working prototype

This commit is contained in:
Henry Jameson 2025-11-24 20:05:38 +02:00
commit 7c57be22e4
15 changed files with 266 additions and 215 deletions

View file

@ -1,14 +1,26 @@
.settings-modal {
overflow: hidden;
h3 {
font-size: 1.4rem;
h2 {
font-size: 1.3rem;
font-weight: 500;
margin-top: 1em;
margin-bottom: 1em;
}
h4 {
h3 {
font-size: 1.2rem;
font-weight: 500;
margin-top: 1em;
margin-bottom: 0.5em;
border-bottom: 1px solid var(--border);
padding-bottom: 0.25em;
box-sizing: border-box;
padding-left: 0.5em;
}
h4 {
font-size: 1.1rem;
margin-top: 1em;
margin-bottom: 0.5em;
}
@ -16,19 +28,21 @@
h5 {
font-size: 1rem;
margin-bottom: 0.5em;
margin-top: 0;
}
.setting-list,
.option-list {
list-style-type: none;
padding-left: 2em;
margin: 0;
.btn:not(.dropdown-button) {
padding: 0 2em;
}
li {
margin-bottom: 0.5em;
margin: 1em 0;
}
.suboptions {
@ -55,7 +69,7 @@
transition: transform;
transition-timing-function: ease-in-out;
transition-duration: 300ms;
width: 1000px;
width: 70em;
max-width: 90vw;
height: 90vh;
@ -90,6 +104,12 @@
}
&.-mobile {
.tabs {
.menu-item {
font-size: 1.2em
}
}
.setting-list,
.option-list {
padding-left: 0.25em;

View file

@ -82,9 +82,6 @@ const SettingsModalContent = {
},
expertLevel () {
return this.$store.state.config.expertLevel
},
isMobileLayout () {
return useInterfaceStore().layoutType === 'mobile'
}
},
data () {
@ -108,21 +105,6 @@ const SettingsModalContent = {
// Clear the state of target tab, so that next time settings is opened
// it doesn't force it.
useInterfaceStore().clearSettingsModalTargetTab()
},
nestedTooBig () {
if (this.navCollapsed) {
this.navCollapsed = false
this.$refs.tabSwitcher.showNav()
}
},
nestedTooSmall () {
if (!this.navCollapsed) {
this.navCollapsed = true
this.$refs.tabSwitcher.hideNav()
}
},
nestedNavSide (side) {
this.navHideHeader = side === 'content'
}
},
mounted () {

View file

@ -1,20 +1,6 @@
.settings_tab-switcher {
height: 100%;
h1 {
margin-bottom: 0.5em;
margin-top: 0.5em;
}
h4 {
margin-bottom: 0;
margin-top: 0.25em;
}
h5 {
margin-top: 0.25em;
}
.setting-item {
border-bottom: 2px solid var(--border);
margin: 1em 1em 1.4em;

View file

@ -8,7 +8,6 @@
:hide-header="navHideHeader"
>
<div
:full-width="true"
:label="$t('settings.general')"
icon="wrench"
data-tab-name="general"
@ -16,16 +15,15 @@
<GeneralTab />
</div>
<div
v-if="isLoggedIn"
:label="$t('settings.profile_tab')"
icon="user"
data-tab-name="profile"
:full-width="true"
:label="$t('settings.posts')"
icon="message"
data-tab-name="posts"
:delay-render="true"
>
<PostsTab />
<ProfileTab />
</div>
<div
:full-width="true"
:label="$t('settings.composing')"
icon="pen-alt"
data-tab-name="composing"
@ -34,13 +32,12 @@
<ComposingTab />
</div>
<div
:full-width="true"
:label="$t('settings.layout')"
icon="table-columns"
data-tab-name="layout"
:label="$t('settings.posts')"
icon="message"
data-tab-name="posts"
:delay-render="true"
>
<LayoutTab />
<PostsTab />
</div>
<div
:full-width="true"
@ -52,39 +49,26 @@
<AppearanceTab />
</div>
<div
v-if="isLoggedIn"
:label="$t('settings.profile_tab')"
icon="user"
data-tab-name="profile"
:full-width="true"
:label="$t('settings.layout')"
icon="table-columns"
data-tab-name="layout"
:delay-render="true"
>
<ProfileTab />
<LayoutTab />
</div>
<div
v-if="isLoggedIn"
:full-width="true"
:label="$t('settings.notifications')"
icon="bell"
data-tab-name="notifications"
>
<NotificationsTab />
</div>
<div
v-if="isLoggedIn"
:label="$t('settings.security_tab')"
icon="lock"
data-tab-name="security"
>
<SecurityTab />
</div>
<div
:label="$t('settings.clutter')"
:fullHeight="true"
icon="broom"
data-tab-name="mutesAndBlocks"
>
<ClutterTab />
</div>
<div
:label="$t('settings.filtering')"
:full-width="true"
icon="filter"
data-tab-name="filtering"
>
@ -99,6 +83,22 @@
>
<MutesAndBlocksTab />
</div>
<div
:label="$t('settings.clutter')"
:fullHeight="true"
icon="broom"
data-tab-name="clutter"
>
<ClutterTab />
</div>
<div
v-if="isLoggedIn"
:label="$t('settings.security_tab')"
icon="lock"
data-tab-name="security"
>
<SecurityTab />
</div>
<div
v-if="isLoggedIn"
:label="$t('settings.data_import_export_tab')"
@ -123,7 +123,7 @@
data-tab-name="theme"
:delay-render="true"
>
<ThemeTab />
<OldThemeTab />
</div>
<div
v-if="expertLevel > 0"

View file

@ -1,4 +1,8 @@
.appearance-tab {
h3 {
border: none
}
.palette,
.theme-notice {
padding: 0.5em;
@ -10,14 +14,6 @@
padding-bottom: 0.5em;
}
h4 {
margin: 2em 0;
line-height: 1.5;
}
h5 {
display: block;
}
input[type="file"] {
padding: 0.25em;

View file

@ -0,0 +1,5 @@
.data-import-export-tab {
h3 {
margin-right: -2em;
}
}

View file

@ -1,9 +1,10 @@
<template>
<div
class="data-import-export-tab"
:label="$t('settings.data_import_export_tab')"
>
<div class="setting-item">
<h2>{{ $t('settings.follow_import') }}</h2>
<h3>{{ $t('settings.follow_import') }}</h3>
<p>{{ $t('settings.import_followers_from_a_csv_file') }}</p>
<Importer
:submit-handler="importFollows"
@ -12,7 +13,7 @@
/>
</div>
<div class="setting-item">
<h2>{{ $t('settings.follow_export') }}</h2>
<h3>{{ $t('settings.follow_export') }}</h3>
<Exporter
:get-content="getFollowsContent"
filename="friends.csv"
@ -20,7 +21,7 @@
/>
</div>
<div class="setting-item">
<h2>{{ $t('settings.block_import') }}</h2>
<h3>{{ $t('settings.block_import') }}</h3>
<p>{{ $t('settings.import_blocks_from_a_csv_file') }}</p>
<Importer
:submit-handler="importBlocks"
@ -29,7 +30,7 @@
/>
</div>
<div class="setting-item">
<h2>{{ $t('settings.block_export') }}</h2>
<h3>{{ $t('settings.block_export') }}</h3>
<Exporter
:get-content="getBlocksContent"
filename="blocks.csv"
@ -37,7 +38,7 @@
/>
</div>
<div class="setting-item">
<h2>{{ $t('settings.mute_import') }}</h2>
<h3>{{ $t('settings.mute_import') }}</h3>
<p>{{ $t('settings.import_mutes_from_a_csv_file') }}</p>
<Importer
:submit-handler="importMutes"
@ -46,7 +47,7 @@
/>
</div>
<div class="setting-item">
<h2>{{ $t('settings.mute_export') }}</h2>
<h3>{{ $t('settings.mute_export') }}</h3>
<Exporter
:get-content="getMutesContent"
filename="mutes.csv"
@ -54,7 +55,7 @@
/>
</div>
<div class="setting-item">
<h2>{{ $t('settings.account_backup') }}</h2>
<h3>{{ $t('settings.account_backup') }}</h3>
<p>{{ $t('settings.account_backup_description') }}</p>
<table>
<thead>
@ -128,4 +129,4 @@
</template>
<script src="./data_import_export_tab.js"></script>
<!-- <style lang="scss" src="./profile.scss"></style> -->
<style lang="scss" src="./data_import_export_tab.scss"></style>

View file

@ -1,3 +1,9 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
import { clearCache, cacheKey, emojiCacheKey } from 'src/services/sw/sw.js'
const pleromaFeCommitUrl = 'https://git.pleroma.social/pleroma/pleroma-fe/commit/'
const VersionTab = {
@ -9,10 +15,14 @@ const VersionTab = {
frontendVersion: instance.frontendVersion
}
},
components: {
BooleanSetting
},
computed: {
frontendVersionLink () {
return pleromaFeCommitUrl + this.frontendVersion
}
},
...SharedComputedObject(),
}
}

View file

@ -0,0 +1,10 @@
.developer-tab {
dt {
font-weight: 900;
}
dd {
margin-top: 0.5em;
margin-bottom: 1em;
}
}

View file

@ -1,32 +1,31 @@
<template>
<div :label="$t('settings.developer')">
<div class="setting-item">
<ul class="setting-list">
<li>
<p>{{ $t('settings.version.backend_version') }}</p>
<ul class="option-list">
<li>
<a
:href="backendRepository"
target="_blank"
>{{ backendVersion }}</a>
</li>
</ul>
</li>
<li>
<p>{{ $t('settings.version.frontend_version') }}</p>
<ul class="option-list">
<li>
<a
:href="frontendVersionLink"
target="_blank"
>{{ frontendVersion }}</a>
</li>
</ul>
</li>
</ul>
</div>
<div
:label="$t('settings.developer')"
class="developer-tab"
>
<div class="setting-item">
<h3>{{ $t('settings.version.title')}}</h3>
<dl class="setting-list">
<dt>{{ $t('settings.version.backend_version') }}</dt>
<dd>
<a
:href="backendRepository"
target="_blank"
>
{{ backendVersion }}
</a>
</dd>
<dt>{{ $t('settings.version.frontend_version') }}</dt>
<dd>
<a
:href="frontendVersionLink"
target="_blank"
>
{{ frontendVersion }}
</a>
</dd>
</dl>
<h3>{{ $t('settings.debug')}}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path="virtualScrolling">
@ -70,3 +69,4 @@
</div>
</template>
<script src="./developer_tab.js" />
<style lang="scss" src="./developer_tab.scss"></style>

View file

@ -233,9 +233,11 @@
{{ $t('settings.use_contain_fit') }}
</BooleanSetting>
</li>
<h3 v-if="expertLevel > 0">
{{ $t('settings.fun') }}
</h3>
</ul>
<h3 v-if="expertLevel > 0">
{{ $t('settings.fun') }}
</h3>
<ul class="setting-list">
<li v-if="user">
<BooleanSetting
path="mentionLinkShowYous"

View file

@ -1,7 +1,7 @@
<template>
<div class="profile-tab">
<div class="setting-item profile-edit">
<h2>{{ $t('settings.account_profile_edit') }}</h2>
<h3>{{ $t('settings.account_profile_edit') }}</h3>
<UserCard
:user-id="user.id"
:editable="true"
@ -9,7 +9,7 @@
/>
</div>
<div class="setting-item">
<h2>{{ $t('settings.account_privacy') }}</h2>
<h3>{{ $t('settings.account_privacy') }}</h3>
<ul class="setting-list">
<li>
<Checkbox v-model="locked">

View file

@ -37,19 +37,13 @@ export default {
required: false,
type: Boolean,
default: null
},
hideHeader: {
required: false,
type: Boolean,
default: null
}
},
data () {
return {
active: findFirstUsable(this.slots()),
resizeHandler: null,
navMode: false,
navSide: 'content'
navSide: 'tabs'
}
},
computed: {
@ -71,16 +65,6 @@ export default {
mobileLayout: store => store.layoutType === 'mobile'
}),
},
created () {
this.resizeHandler = throttle(this.onResize, 200)
window.addEventListener('resize', this.resizeHandler)
},
mounted () {
this.resizeHandler()
},
unmounted () {
window.removeEventListener('resize', this.resizeHandler)
},
beforeUpdate () {
const currentSlot = this.slots()[this.active]
if (!currentSlot.props) {
@ -92,7 +76,6 @@ export default {
return (e) => {
e.preventDefault()
this.setTab(index)
this.onResize()
}
},
setTab (index) {
@ -105,30 +88,6 @@ export default {
changeNavSide (side) {
if (this.navSide !== side) {
this.navSide = side
this.onResize()
}
},
getNavMode () {
return this.navMode
},
onResize () {
// All other tabs are hidden and their width is most likely 0
const activeTab = this.$refs.root.querySelector('.tab-content-wrapper.-active')
const tabContent = activeTab.querySelector('.tab-content')
const tabContentWidth = tabContent.clientWidth
const rootWidth = this.$refs.root.clientWidth
const navWidth = this.$refs.nav.clientWidth
const contentsWidth = rootWidth - navWidth
// if contents takes more space than its container
if (contentsWidth < tabContentWidth) {
this.hideNav()
// If we (theoretically) have enough space to fit it in
} else if (contentsWidth - navWidth >= tabContentWidth){
// First expand the inner layer, then outer
// if use same logic as above order will be reversed
this.showNav()
}
},
// DO NOT put it to computed, it doesn't work (caching?)
@ -145,7 +104,7 @@ export default {
const props = slot.props
if (!props) return
const classesTab = ['vertical-tab', 'menu-item']
if (this.activeIndex === index) {
if (this.activeIndex === index && useInterfaceStore().layoutType !== 'mobile') {
classesTab.push('-active')
}
return (
@ -183,14 +142,21 @@ export default {
: ''
const headerClasses = ['tab-content-label']
if (this.hideHeader === true) {
headerClasses.push('-hidden')
}
const header = (
<h1 class={headerClasses}>
<button type="button" onClick={() => this.changeNavSide('tabs')}>LOL</button>
<h2 class={headerClasses}>
<button
type="button"
onClick={() => this.changeNavSide('tabs')}
class="button-unstyled"
>
<FAIcon
size="lg"
class="back-button-icon"
icon="chevron-left"
/>
</button>
{props.label}
</h1>
</h2>
)
return (
@ -204,13 +170,14 @@ export default {
})
const rootClasses = ['vertical-tab-switcher']
if (this.navMode) {
rootClasses.push('-nav-mode')
if (this.navSide === 'content') {
rootClasses.push('-nav-content')
} else {
rootClasses.push('-nav-tabs')
}
if (useInterfaceStore().layoutType === 'mobile') {
rootClasses.push('-mobile')
}
if (this.navSide === 'tabs') {
rootClasses.push('-nav-tabs')
} else {
rootClasses.push('-nav-contents')
}
return (

View file

@ -1,16 +1,19 @@
.vertical-tab-switcher {
display: flex;
flex-direction: row;
container-type: inline-size;
> .tabs {
flex: 0 0 auto;
flex: 0 0 15em;
flex-direction: column;
overflow: hidden auto;
white-space: nowrap;
text-overflow: ellipsis;
width: 15em;
min-width: 15em;
border-right: 1px solid;
border-right-color: var(--border);
box-sizing: border-box;
> .menu-item {
padding: 0.5em 1em;
@ -23,18 +26,13 @@
}
> .contents {
flex: 1 1 auto;
overflow-x: hidden;
.tab-content-label {
display: none
}
flex: 1 0 35em;
.tab-content {
align-self: center;
&:not(.-full-width) {
width: 30em;
max-width: 40em;
}
&.-full-width {
@ -42,16 +40,23 @@
}
}
.tab-content-label {
box-sizing: border-box;
margin: 0;
border-bottom: 1px solid var(--border);
display: none;
button {
box-sizing: border-box;
padding: 0.5em;
}
}
.tab-content-wrapper {
display: flex;
flex-direction: column;
.tab-content-label {
font-size: 1.5em;
padding: 0.5em 1em;
margin: 0;
border-bottom: 1px solid var(--border);
}
overflow-y: auto;
height: 100%;
&.-hidden {
display: none;
@ -69,38 +74,104 @@
}
}
&.-nav-mode {
&.-nav-tabs {
> .tabs {
width: 100%;
}
> .nav-content {
width: 0%;
&.-mobile {
> .contents {
.tab-content-label {
display: block
}
}
&.-nav-content {
&.-nav-contents {
> .contents {
> .tab-content-wrapper {
> .tab-content-label {
display: block;
display: block;
flex-grow: 1;
}
&.-hidden {
display: none;
}
> .tabs {
display: none;
flex-grow: 0;
flex-shrink: 1;
}
}
&.-nav-tabs {
> .tabs {
display: block;
flex-grow: 1;
}
> .contents {
display: none;
flex-grow: 0;
flex-shrink: 1;
}
}
}
@supports (container-type: inline-size) {
&,
&.-mobile {
&.-nav-contents,
&.-nav-tabs {
/* I THINK it's a false positive and eslint doesn't understand the @-rule */
/* stylelint-disable no-descending-specificity */
> .contents {
display: block;
flex-grow: 1;
}
> .tabs {
display: block;
flex-grow: 0;
}
/* stylelint-enable no-descending-specificity */
}
}
@container (width < 50em) {
> .contents {
.tab-content-label {
display: block
}
}
&.-mobile {
> .contents {
.tab-content-label {
display: block
}
}
}
> .tabs {
position: absolute;
pointer-events: none;
opacity: 0;
}
&,
&.-mobile {
&.-nav-contents {
> .contents {
display: block;
flex-grow: 1;
}
> .nav-content {
width: 100%;
> .tabs {
display: none;
flex-grow: 0;
flex-shrink: 1;
}
}
&.-nav-tabs {
/* stylelint-disable no-descending-specificity */
> .tabs {
display: block;
flex-grow: 1;
}
> .contents {
display: none;
flex-grow: 0;
flex-shrink: 1;
}
/* stylelint-enable no-descending-specificity */
}
}
}
}

View file

@ -405,6 +405,7 @@
"post_look_feel": "Posts Look & Feel",
"posts": "Posts",
"developer": "Developer",
"debug": "Debug",
"mention_links": "Mention links",
"appearance": "Appearance",
"confirm_new_setting": "Confirm new setting?",
@ -418,7 +419,7 @@
"navbar_size": "Top bar size",
"panel_header_size": "Panel header size",
"visual_tweaks": "Minor visual tweaks",
"theme_debug": "Show what background theme engine assumes when dealing with transparancy (DEBUG)",
"theme_debug": "Show what background theme engine assumes when dealing with transparancy",
"scale_and_layout": "Interface scale and layout",
"timelines": "Timelines",
"format_and_language": "Format and Language",
@ -753,7 +754,7 @@
"subject_line_email": "Like email: \"re: subject\"",
"subject_line_mastodon": "Like mastodon: copy as is",
"subject_line_noop": "Do not copy",
"force_theme_recompilation_debug": "Disable theme cahe, force recompile on each boot (DEBUG)",
"force_theme_recompilation_debug": "Disable theme cahe, force recompile on each boot",
"conversation_display": "Conversation display style",
"conversation_display_tree": "Tree-style",
"conversation_display_tree_quick": "Tree view",