diff options
Diffstat (limited to 'src/components')
201 files changed, 3782 insertions, 2183 deletions
diff --git a/src/components/account_actions/account_actions.vue b/src/components/account_actions/account_actions.vue index ce19291a..9a7c6824 100644 --- a/src/components/account_actions/account_actions.vue +++ b/src/components/account_actions/account_actions.vue @@ -11,14 +11,14 @@ <template v-if="relationship.following"> <button v-if="relationship.showing_reblogs" - class="btn button-default dropdown-item" + class="dropdown-item menu-item" @click="hideRepeats" > {{ $t('user_card.hide_repeats') }} </button> <button v-if="!relationship.showing_reblogs" - class="btn button-default dropdown-item" + class="dropdown-item menu-item" @click="showRepeats" > {{ $t('user_card.show_repeats') }} @@ -31,34 +31,34 @@ <UserListMenu :user="user" /> <button v-if="relationship.followed_by" - class="btn button-default btn-block dropdown-item" + class="dropdown-item menu-item" @click="removeUserFromFollowers" > {{ $t('user_card.remove_follower') }} </button> <button v-if="relationship.blocking" - class="btn button-default btn-block dropdown-item" + class="dropdown-item menu-item" @click="unblockUser" > {{ $t('user_card.unblock') }} </button> <button v-else - class="btn button-default btn-block dropdown-item" + class="dropdown-item menu-item" @click="blockUser" > {{ $t('user_card.block') }} </button> <button - class="btn button-default btn-block dropdown-item" + class="dropdown-item menu-item" @click="reportUser" > {{ $t('user_card.report') }} </button> <button v-if="pleromaChatMessagesAvailable" - class="btn button-default btn-block dropdown-item" + class="dropdown-item menu-item" @click="openChat" > {{ $t('user_card.message') }} @@ -122,19 +122,12 @@ <script src="./account_actions.js"></script> <style lang="scss"> -@import "../../variables"; - .AccountActions { .ellipsis-button { width: 2.5em; margin: -0.5em 0; padding: 0.5em 0; text-align: center; - - &:not(:hover) .icon { - color: $fallback--lightText; - color: var(--lightText, $fallback--lightText); - } } } </style> diff --git a/src/components/alert.style.js b/src/components/alert.style.js new file mode 100644 index 00000000..abbeb5ba --- /dev/null +++ b/src/components/alert.style.js @@ -0,0 +1,53 @@ +export default { + name: 'Alert', + selector: '.alert', + validInnerComponents: [ + 'Text', + 'Icon', + 'Link', + 'Border', + 'ButtonUnstyled' + ], + variants: { + normal: '.neutral', + error: '.error', + warning: '.warning', + success: '.success' + }, + defaultRules: [ + { + directives: { + background: '--text', + opacity: 0.5, + blur: '9px' + } + }, + { + parent: { + component: 'Alert' + }, + component: 'Border', + directives: { + textColor: '--parent' + } + }, + { + variant: 'error', + directives: { + background: '--cRed' + } + }, + { + variant: 'warning', + directives: { + background: '--cOrange' + } + }, + { + variant: 'success', + directives: { + background: '--cGreen' + } + } + ] +} diff --git a/src/components/announcement/announcement.vue b/src/components/announcement/announcement.vue index a1c5791e..92880831 100644 --- a/src/components/announcement/announcement.vue +++ b/src/components/announcement/announcement.vue @@ -99,16 +99,14 @@ <script src="./announcement.js"></script> <style lang="scss"> -@import "../../variables"; - .announcement { - border-bottom: 1px solid var(--border, $fallback--border); + border-bottom: 1px solid var(--border); border-radius: 0; - padding: var(--status-margin, $status-margin); + padding: var(--status-margin); .heading, .body { - margin-bottom: var(--status-margin, $status-margin); + margin-bottom: var(--status-margin); } .footer { diff --git a/src/components/announcement_editor/announcement_editor.vue b/src/components/announcement_editor/announcement_editor.vue index 0f29f9f7..c0a3c30a 100644 --- a/src/components/announcement_editor/announcement_editor.vue +++ b/src/components/announcement_editor/announcement_editor.vue @@ -3,7 +3,7 @@ <textarea ref="textarea" v-model="announcement.content" - class="post-textarea" + class="input post-textarea" rows="1" cols="1" :placeholder="$t('announcements.post_placeholder')" @@ -14,6 +14,7 @@ <input id="announcement-start-time" v-model="announcement.startsAt" + class="input" :type="announcement.allDay ? 'date' : 'datetime-local'" :disabled="disabled" > @@ -23,6 +24,7 @@ <input id="announcement-end-time" v-model="announcement.endsAt" + class="input" :type="announcement.allDay ? 'date' : 'datetime-local'" :disabled="disabled" > diff --git a/src/components/announcements_page/announcements_page.vue b/src/components/announcements_page/announcements_page.vue index 78d3ecee..25f830ff 100644 --- a/src/components/announcements_page/announcements_page.vue +++ b/src/components/announcements_page/announcements_page.vue @@ -61,15 +61,13 @@ <script src="./announcements_page.js"></script> <style lang="scss"> -@import "../../variables"; - .announcements-page { .post-form { - padding: var(--status-margin, $status-margin); + padding: var(--status-margin); .heading, .body { - margin-bottom: var(--status-margin, $status-margin); + margin-bottom: var(--status-margin); } .post-button { diff --git a/src/components/attachment/attachment.scss b/src/components/attachment/attachment.scss index 681bab29..13afbe64 100644 --- a/src/components/attachment/attachment.scss +++ b/src/components/attachment/attachment.scss @@ -1,5 +1,3 @@ -@import "../../variables"; - .Attachment { display: inline-flex; flex-direction: column; @@ -9,10 +7,8 @@ height: 100%; border-style: solid; border-width: 1px; - border-radius: $fallback--attachmentRadius; - border-radius: var(--attachmentRadius, $fallback--attachmentRadius); - border-color: $fallback--border; - border-color: var(--border, $fallback--border); + border-radius: var(--roundness); + border-color: var(--border); .attachment-wrapper { flex: 1 1 auto; @@ -84,6 +80,13 @@ } } + .video-container { + border: none; + outline: none; + color: inherit; + background: transparent; + } + .audio-container { display: flex; align-items: flex-end; @@ -126,23 +129,12 @@ .attachment-button { padding: 0; - border-radius: $fallback--tooltipRadius; - border-radius: var(--tooltipRadius, $fallback--tooltipRadius); + border-radius: var(--roundness); text-align: center; width: 2em; height: 2em; margin-left: 0.5em; font-size: 1.25em; - // TODO: theming? hard to theme with unknown background image color - background: rgb(230 230 230 / 70%); - - .svg-inline--fa { - color: rgb(0 0 0 / 60%); - } - - &:hover .svg-inline--fa { - color: rgb(0 0 0 / 90%); - } } } @@ -217,8 +209,7 @@ &.-placeholder { display: inline-block; - color: $fallback--link; - color: var(--postLink, $fallback--link); + color: var(--link); overflow: hidden; white-space: nowrap; height: auto; diff --git a/src/components/attachment/attachment.style.js b/src/components/attachment/attachment.style.js new file mode 100644 index 00000000..5fb4701c --- /dev/null +++ b/src/components/attachment/attachment.style.js @@ -0,0 +1,24 @@ +export default { + name: 'Attachment', + selector: '.Attachment', + validInnerComponents: [ + 'Border', + 'ButtonUnstyled', + 'Input' + ], + defaultRules: [ + { + directives: { + roundness: 3 + } + }, + { + component: 'ButtonUnstyled', + parent: { component: 'Attachment' }, + directives: { + background: '#FFFFFF', + opacity: 0.5 + } + } + ] +} diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue index 79f62806..9abc2627 100644 --- a/src/components/attachment/attachment.vue +++ b/src/components/attachment/attachment.vue @@ -38,7 +38,7 @@ v-if="edit" v-model="localDescription" type="text" - class="description-field" + class="input description-field" :placeholder="$t('post_status.media_description')" @keydown.enter.prevent="" > @@ -175,7 +175,6 @@ :is="videoTag" v-if="type === 'video' && !hidden" class="video-container" - :class="{ 'button-unstyled': 'isModal' }" :href="attachment.url" @click.stop.prevent="openModal" > @@ -253,7 +252,7 @@ v-if="edit" v-model="localDescription" type="text" - class="description-field" + class="input description-field" :placeholder="$t('post_status.media_description')" @keydown.enter.prevent="" > diff --git a/src/components/autosuggest/autosuggest.vue b/src/components/autosuggest/autosuggest.vue index 7b7102fd..04f41801 100644 --- a/src/components/autosuggest/autosuggest.vue +++ b/src/components/autosuggest/autosuggest.vue @@ -1,3 +1,4 @@ +<!-- FIXME THIS NEEDS TO BE REFACTORED TO USE POPOVER --> <template> <div v-click-outside="onClickOutside" @@ -6,12 +7,12 @@ <input v-model="term" :placeholder="placeholder" - class="autosuggest-input" + class="input autosuggest-input" @click="onInputClick" > <div v-if="resultsVisible && filtered.length > 0" - class="autosuggest-results" + class="panel autosuggest-results" > <slot v-for="item in filtered" @@ -24,8 +25,6 @@ <script src="./autosuggest.js"></script> <style lang="scss"> -@import "../../variables"; - .autosuggest { position: relative; @@ -40,18 +39,15 @@ top: 100%; right: 0; max-height: 400px; - background-color: $fallback--bg; - background-color: var(--bg, $fallback--bg); + background-color: var(--bg); border-style: solid; border-width: 1px; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); - border-radius: $fallback--inputRadius; - border-radius: var(--inputRadius, $fallback--inputRadius); + border-color: var(--border); + border-radius: var(--roundness); border-top-left-radius: 0; border-top-right-radius: 0; box-shadow: 1px 1px 4px rgb(0 0 0 / 60%); - box-shadow: var(--panelShadow); + box-shadow: var(--shadow); overflow-y: auto; z-index: 1; } diff --git a/src/components/avatar_list/avatar_list.vue b/src/components/avatar_list/avatar_list.vue index 2d00cb7b..9252ba32 100644 --- a/src/components/avatar_list/avatar_list.vue +++ b/src/components/avatar_list/avatar_list.vue @@ -17,8 +17,6 @@ <script src="./avatar_list.js"></script> <style lang="scss"> -@import "../../variables"; - .avatars { display: flex; margin: 0; @@ -36,8 +34,7 @@ } .avatar-small { - border-radius: $fallback--avatarAltRadius; - border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); + border-radius: var(--roundness); height: 24px; width: 24px; } diff --git a/src/components/badge.style.js b/src/components/badge.style.js new file mode 100644 index 00000000..0697cac6 --- /dev/null +++ b/src/components/badge.style.js @@ -0,0 +1,30 @@ +export default { + name: 'Badge', + selector: '.badge', + validInnerComponents: [ + 'Text', + 'Icon' + ], + variants: { + notification: '.-notification' + }, + defaultRules: [ + { + component: 'Root', + directives: { + '--badgeNotification': 'color | --cRed' + } + }, + { + directives: { + background: '--cGreen' + } + }, + { + variant: 'notification', + directives: { + background: '--cRed' + } + } + ] +} diff --git a/src/components/basic_user_card/basic_user_card.vue b/src/components/basic_user_card/basic_user_card.vue index 705e20f5..9e2b0295 100644 --- a/src/components/basic_user_card/basic_user_card.vue +++ b/src/components/basic_user_card/basic_user_card.vue @@ -47,7 +47,6 @@ display: flex; flex: 1 0; margin: 0; - padding: 0.6em 1em; --emoji-size: 14px; diff --git a/src/components/border.style.js b/src/components/border.style.js new file mode 100644 index 00000000..a87ee9c8 --- /dev/null +++ b/src/components/border.style.js @@ -0,0 +1,13 @@ +export default { + name: 'Border', + selector: '/*border*/', + virtual: true, + defaultRules: [ + { + directives: { + textColor: '$mod(--parent, 10)', + textAuto: 'no-auto' + } + } + ] +} diff --git a/src/components/button.style.js b/src/components/button.style.js new file mode 100644 index 00000000..1bee8f8e --- /dev/null +++ b/src/components/button.style.js @@ -0,0 +1,101 @@ +export default { + name: 'Button', // Name of the component + selector: '.button-default', // CSS selector/prefix + // outOfTreeSelector: '' // out-of-tree selector is used when other components are laid over it but it's not part of the tree, see Underlay component + // States, system witll calculate ALL possible combinations of those and prepend "normal" to them + standalone "normal" state + states: { + // States are a bit expensive - the amount of combinations generated is about (1/6)n^3+n, so adding more state increased number of combination by an order of magnitude! + // All states inherit from "normal" state, there is no other inheirtance, i.e. hover+disabled only inherits from "normal", not from hover nor disabled. + // However, cascading still works, so resulting state will be result of merging of all relevant states/variants + // normal: '' // normal state is implicitly added, it is always included + toggled: '.toggled', + pressed: ':active', + hover: ':hover:not(:disabled)', + focused: ':focus-within', + disabled: ':disabled' + }, + // Variants are mutually exclusive, each component implicitly has "normal" variant, and all other variants inherit from it. + variants: { + // Variants save on computation time since adding new variant just adds one more "set". + // normal: '', // you can override normal variant, it will be appenended to the main class + danger: '.danger' + // Overall the compuation difficulty is N*((1/6)M^3+M) where M is number of distinct states and N is number of variants. + // This (currently) is further multipled by number of places where component can exist. + }, + // This lists all other components that can possibly exist within one. Recursion is currently not supported (and probably won't be supported ever). + validInnerComponents: [ + 'Text', + 'Icon' + ], + // Default rules, used as "default theme", essentially. + defaultRules: [ + { + component: 'Root', + directives: { + '--defaultButtonHoverGlow': 'shadow | 0 0 4 --text', + '--defaultButtonShadow': 'shadow | 0 0 2 #000000', + '--defaultButtonBevel': 'shadow | $borderSide(#FFFFFF, top, 0.2), $borderSide(#000000, bottom, 0.2)', + '--pressedButtonBevel': 'shadow | $borderSide(#FFFFFF, bottom, 0.2), $borderSide(#000000, top, 0.2)' + } + }, + { + // component: 'Button', // no need to specify components every time unless you're specifying how other component should look + // like within it + directives: { + background: '--fg', + shadow: ['--defaultButtonShadow', '--defaultButtonBevel'], + roundness: 3 + } + }, + { + state: ['hover'], + directives: { + shadow: ['--defaultButtonHoverGlow', '--defaultButtonBevel'] + } + }, + { + state: ['pressed'], + directives: { + shadow: ['--defaultButtonShadow', '--pressedButtonBevel'] + } + }, + { + state: ['hover', 'pressed'], + directives: { + shadow: ['--defaultButtonHoverGlow', '--pressedButtonBevel'] + } + }, + { + state: ['toggled'], + directives: { + background: '--inheritedBackground,-14.2', + shadow: ['--defaultButtonShadow', '--pressedButtonBevel'] + } + }, + { + state: ['toggled', 'hover'], + directives: { + background: '--inheritedBackground,-14.2', + shadow: ['--defaultButtonHoverGlow', '--pressedButtonBevel'] + } + }, + { + state: ['disabled'], + directives: { + background: '$blend(--inheritedBackground, 0.25, --parent)', + shadow: ['--defaultButtonBevel'] + } + }, + { + component: 'Text', + parent: { + component: 'Button', + state: ['disabled'] + }, + directives: { + textOpacity: 0.25, + textOpacityMode: 'blend' + } + } + ] +} diff --git a/src/components/button_unstyled.style.js b/src/components/button_unstyled.style.js new file mode 100644 index 00000000..435f9cc6 --- /dev/null +++ b/src/components/button_unstyled.style.js @@ -0,0 +1,95 @@ +export default { + name: 'ButtonUnstyled', + selector: '.button-unstyled', + states: { + toggled: '.toggled', + disabled: ':disabled', + hover: ':hover:not(:disabled)', + focused: ':focus-within' + }, + validInnerComponents: [ + 'Text', + 'Icon', + 'Badge' + ], + defaultRules: [ + { + directives: { + background: '#ffffff', + opacity: 0 + } + }, + { + component: 'Icon', + parent: { + component: 'ButtonUnstyled', + state: ['hover'] + }, + directives: { + textColor: '--parent--text' + } + }, + { + component: 'Icon', + parent: { + component: 'ButtonUnstyled', + state: ['toggled'] + }, + directives: { + textColor: '--parent--text' + } + }, + { + component: 'Icon', + parent: { + component: 'ButtonUnstyled', + state: ['toggled', 'hover'] + }, + directives: { + textColor: '--parent--text' + } + }, + { + component: 'Icon', + parent: { + component: 'ButtonUnstyled', + state: ['toggled', 'focused'] + }, + directives: { + textColor: '--parent--text' + } + }, + { + component: 'Icon', + parent: { + component: 'ButtonUnstyled', + state: ['toggled', 'focused', 'hover'] + }, + directives: { + textColor: '--parent--text' + } + }, + { + component: 'Text', + parent: { + component: 'ButtonUnstyled', + state: ['disabled'] + }, + directives: { + textOpacity: 0.25, + textOpacityMode: 'blend' + } + }, + { + component: 'Icon', + parent: { + component: 'ButtonUnstyled', + state: ['disabled'] + }, + directives: { + textOpacity: 0.25, + textOpacityMode: 'blend' + } + } + ] +} diff --git a/src/components/chat/chat.scss b/src/components/chat/chat.scss index 43e7a5e4..8af710ae 100644 --- a/src/components/chat/chat.scss +++ b/src/components/chat/chat.scss @@ -11,15 +11,15 @@ .chat-view-body { box-sizing: border-box; - background-color: var(--chatBg, $fallback--bg); display: flex; flex-direction: column; width: 100%; overflow: visible; min-height: calc(100vh - var(--navbar-height)); margin: 0; - border-radius: 10px 10px 0 0; - border-radius: var(--panelRadius, 10px) var(--panelRadius, 10px) 0 0; + border-radius: var(--roundness); + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; &::after { border-radius: 0; @@ -37,8 +37,6 @@ .footer { position: sticky; bottom: 0; - background-color: $fallback--bg; - background-color: var(--bg, $fallback--bg); z-index: 1; } @@ -61,8 +59,6 @@ position: absolute; right: 1.3em; top: -3.2em; - background-color: $fallback--fg; - background-color: var(--btn, $fallback--fg); display: flex; justify-content: center; align-items: center; @@ -79,12 +75,6 @@ visibility: visible; } - i { - font-size: 1em; - color: $fallback--text; - color: var(--text, $fallback--text); - } - .unread-message-count { font-size: 0.8em; left: 50%; diff --git a/src/components/chat/chat.style.js b/src/components/chat/chat.style.js new file mode 100644 index 00000000..9ae2b7d7 --- /dev/null +++ b/src/components/chat/chat.style.js @@ -0,0 +1,19 @@ +export default { + name: 'Chat', + selector: '.chat-message-list', + validInnerComponents: [ + 'Text', + 'Link', + 'Icon', + 'Avatar', + 'ChatMessage' + ], + defaultRules: [ + { + directives: { + background: '--bg', + blur: '5px' + } + } + ] +} diff --git a/src/components/chat/chat.vue b/src/components/chat/chat.vue index b1e5468c..6efe576b 100644 --- a/src/components/chat/chat.vue +++ b/src/components/chat/chat.vue @@ -26,7 +26,7 @@ </div> </div> <div - class="message-list" + class="chat-message-list message-list" :style="{ height: scrollableContainerHeight }" > <template v-if="!errorLoadingChat"> @@ -61,7 +61,7 @@ <FAIcon icon="chevron-down" /> <div v-if="newMessageCount" - class="badge badge-notification unread-chat-count unread-message-count" + class="badge -notification unread-chat-count unread-message-count" > {{ newMessageCount }} </div> @@ -95,6 +95,5 @@ <script src="./chat.js"></script> <style lang="scss"> -@import "../../variables"; @import "./chat"; </style> diff --git a/src/components/chat_list/chat_list.vue b/src/components/chat_list/chat_list.vue index 27a475ed..b33ea67e 100644 --- a/src/components/chat_list/chat_list.vue +++ b/src/components/chat_list/chat_list.vue @@ -45,8 +45,6 @@ <script src="./chat_list.js"></script> <style lang="scss"> -@import "../../variables"; - .chat-list { min-height: 25em; margin-bottom: 0; @@ -57,8 +55,7 @@ font-size: 1.2em; display: flex; justify-content: center; - color: $fallback--text; - color: var(--faint, $fallback--text); + color: var(--textFaint); } </style> diff --git a/src/components/chat_list_item/chat_list_item.scss b/src/components/chat_list_item/chat_list_item.scss index 3a84672b..9711b41d 100644 --- a/src/components/chat_list_item/chat_list_item.scss +++ b/src/components/chat_list_item/chat_list_item.scss @@ -1,8 +1,6 @@ .chat-list-item { display: flex; flex-direction: row; - padding: 0.75em; - height: 5em; overflow: hidden; box-sizing: border-box; cursor: pointer; @@ -11,11 +9,6 @@ outline: none; } - &:hover { - background-color: var(--selectedPost, $fallback--lightBg); - box-shadow: 0 0 3px 1px rgb(0 0 0 / 10%); - } - .chat-list-item-left { margin-right: 1em; } @@ -29,7 +22,7 @@ .heading { width: 100%; - display: inline-flex; + display: flex; justify-content: space-between; line-height: 1em; } @@ -47,18 +40,17 @@ } .chat-preview { - display: inline-flex; + display: flex; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; margin: 0.35em 0; - color: $fallback--text; - color: var(--faint, $fallback--text); + color: var(--textFaint); width: 100%; } a { - color: var(--faintLink, $fallback--link); + color: var(--linkFaint); text-decoration: none; pointer-events: none; } @@ -73,11 +65,6 @@ } } - .Avatar { - border-radius: $fallback--avatarAltRadius; - border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); - } - .chat-preview-body { --emoji-size: 1.4em; diff --git a/src/components/chat_list_item/chat_list_item.vue b/src/components/chat_list_item/chat_list_item.vue index 69ad609b..0f9d5c5f 100644 --- a/src/components/chat_list_item/chat_list_item.vue +++ b/src/components/chat_list_item/chat_list_item.vue @@ -36,7 +36,7 @@ /> <div v-if="chat.unread > 0" - class="badge badge-notification unread-chat-count" + class="badge -notification unread-chat-count" > {{ chat.unread }} </div> @@ -48,6 +48,5 @@ <script src="./chat_list_item.js"></script> <style lang="scss"> -@import "../../variables"; @import "./chat_list_item"; </style> diff --git a/src/components/chat_message/chat_message.scss b/src/components/chat_message/chat_message.scss index fd5b7aa4..f7254ea3 100644 --- a/src/components/chat_message/chat_message.scss +++ b/src/components/chat_message/chat_message.scss @@ -1,5 +1,3 @@ -@import "../../variables"; - .chat-message-wrapper { &.hovered-message-chain { .animated.Avatar { @@ -27,12 +25,6 @@ .menu-icon { cursor: pointer; - - &:hover, - .extra-button-popover.open & { - color: $fallback--text; - color: var(--text, $fallback--text); - } } .popover { @@ -61,10 +53,12 @@ } .status { - border-radius: $fallback--chatMessageRadius; - border-radius: var(--chatMessageRadius, $fallback--chatMessageRadius); + background-color: var(--background); + color: var(--text); + border-radius: var(--roundness); display: flex; padding: 0.75em; + border: 1px solid var(--border); } .created-at { @@ -97,8 +91,7 @@ .error { .status-content.media-body, .created-at { - color: $fallback--cRed; - color: var(--badgeNotification, $fallback--cRed); + color: var(--badgeNotification); } } @@ -117,16 +110,6 @@ align-content: end; justify-content: flex-end; - a { - color: var(--chatMessageOutgoingLink, $fallback--link); - } - - .status { - color: var(--chatMessageOutgoingText, $fallback--text); - background-color: var(--chatMessageOutgoingBg, $fallback--lightBg); - border: 1px solid var(--chatMessageOutgoingBorder, --lightBg); - } - .chat-message-inner { align-items: flex-end; } @@ -137,22 +120,6 @@ } .incoming { - a { - color: var(--chatMessageIncomingLink, $fallback--link); - } - - .status { - color: var(--chatMessageIncomingText, $fallback--text); - background-color: var(--chatMessageIncomingBg, $fallback--bg); - border: 1px solid var(--chatMessageIncomingBorder, --border); - } - - .created-at { - a { - color: var(--chatMessageIncomingText, $fallback--text); - } - } - .chat-message-menu { left: 0.4rem; } @@ -176,6 +143,5 @@ margin: 1.4em 0; font-size: 0.9em; user-select: none; - color: $fallback--text; - color: var(--faintedText, $fallback--text); + color: var(--textFaint); } diff --git a/src/components/chat_message/chat_message.style.js b/src/components/chat_message/chat_message.style.js new file mode 100644 index 00000000..9b57ad37 --- /dev/null +++ b/src/components/chat_message/chat_message.style.js @@ -0,0 +1,30 @@ +export default { + name: 'ChatMessage', + selector: '.chat-message', + variants: { + outgoing: '.outgoing' + }, + validInnerComponents: [ + 'Text', + 'Icon', + 'Border', + 'Button', + 'RichContent', + 'Attachment', + 'PollGraph' + ], + defaultRules: [ + { + directives: { + background: '--bg, 2', + backgroundNoCssColor: 'yes' + } + }, + { + variant: 'outgoing', + directives: { + background: '--bg, 5' + } + } + ] +} diff --git a/src/components/chat_message/chat_message.vue b/src/components/chat_message/chat_message.vue index 381574c3..166889d7 100644 --- a/src/components/chat_message/chat_message.vue +++ b/src/components/chat_message/chat_message.vue @@ -53,7 +53,7 @@ <template #content> <div class="dropdown-menu"> <button - class="button-default dropdown-item dropdown-item-icon" + class="menu-item dropdown-item dropdown-item-icon" @click="deleteMessage" > <FAIcon icon="times" /> {{ $t("chats.delete") }} diff --git a/src/components/chat_new/chat_new.scss b/src/components/chat_new/chat_new.scss index b145ecf9..b84e0beb 100644 --- a/src/components/chat_new/chat_new.scss +++ b/src/components/chat_new/chat_new.scss @@ -16,11 +16,6 @@ padding-bottom: 0.7rem; } - .basic-user-card:hover { - cursor: pointer; - background-color: var(--selectedPost, $fallback--lightBg); - } - .go-back-button { text-align: center; line-height: 1; diff --git a/src/components/chat_new/chat_new.vue b/src/components/chat_new/chat_new.vue index 52306c1d..673ba56b 100644 --- a/src/components/chat_new/chat_new.vue +++ b/src/components/chat_new/chat_new.vue @@ -16,27 +16,29 @@ /> </button> </div> - <div class="input-wrap"> - <div class="input-search"> - <FAIcon - class="search-icon fa-scale-110 fa-old-padding" - icon="search" - /> + <div class="panel-body"> + <div class="input-wrap"> + <div class="input-search"> + <FAIcon + class="search-icon fa-scale-110 fa-old-padding" + icon="search" + /> + </div> + <input + ref="search" + v-model="query" + class="input" + placeholder="Search people" + @input="onInput" + > </div> - <input - ref="search" - v-model="query" - placeholder="Search people" - @input="onInput" - > - </div> - <div class="member-list"> - <div - v-for="user in availableUsers" - :key="user.id" - class="member" - > - <div @click.capture.prevent="goToChat(user)"> + <div class="member-list"> + <div + v-for="user in availableUsers" + :key="user.id" + class="list-item" + @click.capture.prevent="goToChat(user)" + > <BasicUserCard :user="user" /> </div> </div> @@ -46,6 +48,5 @@ <script src="./chat_new.js"></script> <style lang="scss"> -@import "../../variables"; @import "./chat_new"; </style> diff --git a/src/components/chat_title/chat_title.vue b/src/components/chat_title/chat_title.vue index 93db4fa7..68ee7a5a 100644 --- a/src/components/chat_title/chat_title.vue +++ b/src/components/chat_title/chat_title.vue @@ -26,8 +26,6 @@ <script src="./chat_title.js"></script> <style lang="scss"> -@import "../../variables"; - .chat-title { display: flex; overflow: hidden; @@ -54,8 +52,7 @@ margin-right: 0.5em; height: 1.5em; width: 1.5em; - border-radius: $fallback--avatarAltRadius; - border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); + border-radius: var(--roundness); &.animated::before { display: none; diff --git a/src/components/checkbox/checkbox.vue b/src/components/checkbox/checkbox.vue index b8b77e7c..6261bf3a 100644 --- a/src/components/checkbox/checkbox.vue +++ b/src/components/checkbox/checkbox.vue @@ -12,7 +12,7 @@ @change="$emit('update:modelValue', $event.target.checked)" > <i - class="checkbox-indicator" + class="input -checkbox checkbox-indicator" :aria-hidden="true" @transitionend.capture="onTransitionEnd" /> @@ -54,7 +54,6 @@ export default { </script> <style lang="scss"> -@import "../../variables"; @import "../../mixins"; .checkbox { @@ -62,9 +61,15 @@ export default { display: inline-block; min-height: 1.2em; - &-indicator { + & > &-indicator { + /* Reset .input stuff */ + padding: 0; + margin: 0; position: relative; + line-height: inherit; + display: inline; padding-left: 1.2em; + box-shadow: none; } &-indicator::before { @@ -76,12 +81,9 @@ export default { transition: color 200ms; width: 1.1em; height: 1.1em; - border-radius: $fallback--checkboxRadius; - border-radius: var(--checkboxRadius, $fallback--checkboxRadius); - box-shadow: 0 0 2px black inset; - box-shadow: var(--inputShadow); - background-color: $fallback--fg; - background-color: var(--input, $fallback--fg); + border-radius: var(--roundness); + box-shadow: var(--shadow); + background-color: var(--background); vertical-align: top; text-align: center; line-height: 1.1em; @@ -98,21 +100,18 @@ export default { } .label { - color: $fallback--faint; - color: var(--faint, $fallback--faint); + color: var(--text); } } input[type="checkbox"] { &:checked + .checkbox-indicator::before { - color: $fallback--text; - color: var(--inputText, $fallback--text); + color: var(--text); } &:indeterminate + .checkbox-indicator::before { content: "–"; - color: $fallback--text; - color: var(--inputText, $fallback--text); + color: var(--text); } } diff --git a/src/components/color_input/color_input.scss b/src/components/color_input/color_input.scss index ca46199a..b0fc879f 100644 --- a/src/components/color_input/color_input.scss +++ b/src/components/color_input/color_input.scss @@ -1,5 +1,3 @@ -@import "../../variables"; - .color-input { display: inline-flex; @@ -11,9 +9,8 @@ padding: 0.2em 8px; input { + color: var(--text); background: none; - color: $fallback--lightText; - color: var(--inputText, $fallback--lightText); border: none; padding: 0; margin: 0; @@ -23,21 +20,38 @@ min-width: 3em; padding: 0; } + } + + .nativeColor { + cursor: pointer; + flex: 0 0 auto; - &.nativeColor { - flex: 0 0 2em; - min-width: 2em; - align-self: stretch; - min-height: 100%; + input { + appearance: none; + max-width: 0; + min-width: 0; + max-height: 0; + /* stylelint-disable-next-line declaration-no-important */ + opacity: 0 !important; } } .computedIndicator, + .validIndicator, + .invalidIndicator, .transparentIndicator { flex: 0 0 2em; + margin: 0 0.5em; min-width: 2em; align-self: stretch; - min-height: 100%; + min-height: 1.5em; + border-radius: var(--roundness); + } + + .invalidIndicator { + background: transparent; + box-sizing: border-box; + border: 2px solid var(--cRed); } .transparentIndicator { @@ -58,11 +72,13 @@ &::after { top: 0; left: 0; + border-top-left-radius: var(--roundness); } &::before { bottom: 0; right: 0; + border-bottom-right-radius: var(--roundness); } } } diff --git a/src/components/color_input/color_input.vue b/src/components/color_input/color_input.vue index dfc084f9..66ee9d53 100644 --- a/src/components/color_input/color_input.vue +++ b/src/components/color_input/color_input.vue @@ -25,30 +25,51 @@ :disabled="!present || disabled" @input="$emit('update:modelValue', $event.target.value)" > - <input + <div v-if="validColor" - :id="name" - class="nativeColor unstyled" - type="color" - :value="modelValue || fallback" - :disabled="!present || disabled" - @input="$emit('update:modelValue', $event.target.value)" - > + class="validIndicator" + :style="{backgroundColor: modelValue || fallback}" + /> <div - v-if="transparentColor" + v-else-if="transparentColor" class="transparentIndicator" /> <div - v-if="computedColor" + v-else-if="computedColor" class="computedIndicator" :style="{backgroundColor: fallback}" /> + <div + v-else + class="invalidIndicator" + /> + <label class="nativeColor"> + <FAIcon icon="eye-dropper" /> + <input + :id="name" + class="unstyled" + type="color" + :value="modelValue || fallback" + :disabled="!present || disabled" + @input="$emit('update:modelValue', $event.target.value)" + > + </label> </div> </div> </template> <script> import Checkbox from '../checkbox/checkbox.vue' import { hex2rgb } from '../../services/color_convert/color_convert.js' + +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faEyeDropper +} from '@fortawesome/free-solid-svg-icons' + +library.add( + faEyeDropper +) + export default { components: { Checkbox @@ -108,12 +129,3 @@ export default { } </script> <style lang="scss" src="./color_input.scss"></style> - -<style lang="scss"> -.color-control { - input.text-input { - max-width: 7em; - flex: 1; - } -} -</style> diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 85e6d8ad..a94d2130 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -56,7 +56,8 @@ const conversation = { expanded: false, threadDisplayStatusObject: {}, // id => 'showing' | 'hidden' statusContentPropertiesObject: {}, - inlineDivePosition: null + inlineDivePosition: null, + loadStatusError: null } }, props: [ @@ -392,11 +393,15 @@ const conversation = { this.setHighlight(this.originalStatusId) }) } else { + this.loadStatusError = null this.$store.state.api.backendInteractor.fetchStatus({ id: this.statusId }) .then((status) => { this.$store.dispatch('addNewStatuses', { statuses: [status] }) this.fetchConversation() }) + .catch((error) => { + this.loadStatusError = error + }) } }, getReplies (id) { diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index 7577129e..526de5c3 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -28,7 +28,27 @@ class="rightside-button" /> </div> - <div class="conversation-body panel-body"> + <div + v-if="isPage && !status" + class="conversation-body" + :class="{ 'panel-body': isExpanded }" + > + <p v-if="!loadStatusError"> + <FAIcon + spin + icon="circle-notch" + /> + {{ $t('status.loading') }} + </p> + <p v-else> + {{ $t('status.load_error', { error: loadStatusError }) }} + </p> + </div> + <div + v-else + class="conversation-body" + :class="{ 'panel-body': isExpanded }" + > <div v-if="isTreeView" class="thread-body" @@ -203,6 +223,7 @@ </div> <div v-else + class="Conversation -hidden" :style="hiddenStyle" /> </template> @@ -210,14 +231,17 @@ <script src="./conversation.js"></script> <style lang="scss"> -@import "../../variables"; - .Conversation { z-index: 1; + &.-hidden { + background: var(--__panel-background); + backdrop-filter: var(--__panel-backdrop-filter); + } + .conversation-dive-to-top-level-box { - padding: var(--status-margin, $status-margin); - border-bottom: 1px solid var(--border, $fallback--border); + padding: var(--status-margin); + border-bottom: 1px solid var(--border); border-radius: 0; /* Make the button stretch along the whole row */ @@ -227,20 +251,22 @@ } .thread-ancestors { - margin-left: var(--status-margin, $status-margin); - border-left: 2px solid var(--border, $fallback--border); + margin-left: var(--status-margin); + border-left: 2px solid var(--border); } - .thread-ancestor.-faded .StatusContent { - --link: var(--faintLink); - --text: var(--faint); - - color: var(--text); + .thread-ancestor.-faded .RichContent { + /* stylelint-disable declaration-no-important */ + --text: var(--textFaint) !important; + --link: var(--linkFaint) !important; + --funtextGreentext: var(--funtextGreentextFaint) !important; + --funtextCyantext: var(--funtextCyantextFaint) !important; + /* stylelint-enable declaration-no-important */ } .thread-ancestor-dive-box { - padding-left: var(--status-margin, $status-margin); - border-bottom: 1px solid var(--border, $fallback--border); + padding-left: var(--status-margin); + border-bottom: 1px solid var(--border); border-radius: 0; /* Make the button stretch along the whole row */ @@ -253,16 +279,17 @@ } .thread-ancestor-dive-box-inner { - padding: var(--status-margin, $status-margin); + padding: var(--status-margin); } .conversation-status { - border-bottom: 1px solid var(--border, $fallback--border); + border-bottom: 1px solid var(--border); border-radius: 0; } .thread-ancestor-has-other-replies .conversation-status, - &:last-child .conversation-status, + &:last-child:not(.-expanded) .conversation-status, + &.-expanded .conversation-status:last-child, .thread-ancestor:last-child .conversation-status, .thread-ancestor:last-child .thread-ancestor-dive-box, &.-expanded .thread-tree .conversation-status { @@ -270,20 +297,36 @@ } .thread-ancestors + .thread-tree > .conversation-status { - border-top: 1px solid var(--border, $fallback--border); + border-top: 1px solid var(--border); } /* expanded conversation in timeline */ &.status-fadein.-expanded .thread-body { - border-left: 4px solid $fallback--cRed; - border-left-color: var(--cRed, $fallback--cRed); - border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius; - border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius); - border-bottom: 1px solid var(--border, $fallback--border); + border-left: 4px solid var(--cRed); + border-radius: var(--roundness); + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom: 1px solid var(--border); } &.-expanded.status-fadein { - margin: calc(var(--status-margin, $status-margin) / 2); + --___margin: calc(var(--status-margin) / 2); + + background: var(--background); + margin: var(--___margin); + + &::before { + z-index: -1; + content: ""; + display: block; + position: absolute; + top: calc(var(--___margin) * -1); + bottom: calc(var(--___margin) * -1); + left: calc(var(--___margin) * -1); + right: calc(var(--___margin) * -1); + background: var(--background); + backdrop-filter: var(--__panel-backdrop-filter); + } } } </style> diff --git a/src/components/desktop_nav/desktop_nav.scss b/src/components/desktop_nav/desktop_nav.scss index c7e02936..61d2541c 100644 --- a/src/components/desktop_nav/desktop_nav.scss +++ b/src/components/desktop_nav/desktop_nav.scss @@ -1,5 +1,3 @@ -@import "../../variables"; - .DesktopNav { width: 100%; z-index: var(--ZI_navbar); @@ -9,7 +7,7 @@ } a { - color: var(--topBarLink, $fallback--link); + color: var(--link); } .inner-nav { @@ -54,27 +52,7 @@ .button-default { &, svg { - color: $fallback--text; - color: var(--btnTopBarText, $fallback--text); - } - - &:active { - background-color: $fallback--fg; - background-color: var(--btnPressedTopBar, $fallback--fg); - color: $fallback--text; - color: var(--btnPressedTopBarText, $fallback--text); - } - - &:disabled { - color: $fallback--text; - color: var(--btnDisabledTopBarText, $fallback--text); - } - - &.toggled { - color: $fallback--text; - color: var(--btnToggledTopBarText, $fallback--text); - background-color: $fallback--fg; - background-color: var(--btnToggledTopBar, $fallback--fg); + color: var(--text); } } @@ -94,8 +72,7 @@ mask-repeat: no-repeat; mask-position: center; mask-size: contain; - background-color: $fallback--fg; - background-color: var(--topBarText, $fallback--fg); + background-color: var(--text); position: absolute; top: 0; bottom: 0; @@ -116,8 +93,7 @@ text-align: center; .svg-inline--fa { - color: $fallback--link; - color: var(--topBarLink, $fallback--link); + color: var(--link); } } diff --git a/src/components/dialog_modal/dialog_modal.vue b/src/components/dialog_modal/dialog_modal.vue index 341cf105..3bceff22 100644 --- a/src/components/dialog_modal/dialog_modal.vue +++ b/src/components/dialog_modal/dialog_modal.vue @@ -12,7 +12,7 @@ <slot name="header" /> </div> </div> - <div class="dialog-modal-content"> + <div class="panel-body dialog-modal-content"> <slot name="default" /> </div> <div class="dialog-modal-footer user-interactions panel-footer"> @@ -25,8 +25,6 @@ <script src="./dialog_modal.js"></script> <style lang="scss"> -@import "../../variables"; - // TODO: unify with other modals. .dark-overlay { &::before { @@ -54,8 +52,6 @@ z-index: 2001; cursor: default; display: block; - background-color: $fallback--bg; - background-color: var(--bg, $fallback--bg); .dialog-modal-heading { .title { @@ -66,18 +62,13 @@ .dialog-modal-content { margin: 0; padding: 1rem; - background-color: $fallback--bg; - background-color: var(--bg, $fallback--bg); white-space: normal; } .dialog-modal-footer { margin: 0; padding: 0.5em; - background-color: $fallback--bg; - background-color: var(--bg, $fallback--bg); - border-top: 1px solid $fallback--border; - border-top: 1px solid var(--border, $fallback--border); + border-top: 1px solid var(--border); display: flex; justify-content: flex-end; diff --git a/src/components/emoji_input/emoji_input.vue b/src/components/emoji_input/emoji_input.vue index 7f9ecc99..9bd5c8f4 100644 --- a/src/components/emoji_input/emoji_input.vue +++ b/src/components/emoji_input/emoji_input.vue @@ -1,7 +1,7 @@ <template> <div ref="root" - class="emoji-input" + class="input emoji-input" :class="{ 'with-picker': !hideEmojiButton }" > <slot @@ -68,9 +68,9 @@ v-for="(suggestion, index) in suggestions" :id="suggestionItemId(index)" :key="index" - class="autocomplete-item" + class="menu-item autocomplete-item" role="option" - :class="{ highlighted: index === highlighted }" + :class="{ '-active': index === highlighted }" :aria-label="autoCompleteItemLabel(suggestion)" :aria-selected="index === highlighted" @click.stop.prevent="onClick($event, suggestion)" @@ -110,9 +110,8 @@ <script src="./emoji_input.js"></script> <style lang="scss"> -@import "../../variables"; - -.emoji-input { +.input.emoji-input { + padding: 0; display: flex; flex-direction: column; position: relative; @@ -127,8 +126,7 @@ line-height: 24px; &:hover i { - color: $fallback--text; - color: var(--text, $fallback--text); + color: var(--text); } } @@ -145,6 +143,12 @@ input, textarea { flex: 1 0 auto; + color: inherit; + /* stylelint-disable-next-line declaration-no-important */ + background: none !important; + box-shadow: none; + border: none; + outline: none; } &.with-picker input { @@ -179,26 +183,28 @@ position: absolute; } - &-item { + &-item.menu-item { display: flex; - cursor: pointer; - padding: 0.2em 0.4em; - border-bottom: 1px solid rgb(0 0 0 / 40%); - height: 32px; + padding-top: 0; + padding-bottom: 0; .image { - width: 32px; - height: 32px; - line-height: 32px; + width: calc(var(--__line-height) + var(--__vertical-gap) * 2); + height: calc(var(--__line-height) + var(--__vertical-gap) * 2); + line-height: var(--__line-height); text-align: center; - font-size: 32px; - margin-right: 4px; + margin-right: var(--__horizontal-gap); img { - width: 32px; - height: 32px; + width: calc(var(--__line-height) + var(--__vertical-gap) * 2); + height: calc(var(--__line-height) + var(--__vertical-gap) * 2); object-fit: contain; } + + span { + font-size: var(--__line-height); + line-height: var(--__line-height); + } } .label { @@ -216,17 +222,6 @@ line-height: 9px; } } - - &.highlighted { - background-color: $fallback--fg; - background-color: var(--selectedMenuPopover, $fallback--fg); - color: var(--selectedMenuPopoverText, $fallback--text); - - --faint: var(--selectedMenuPopoverFaintText, $fallback--faint); - --faintLink: var(--selectedMenuPopoverFaintLink, $fallback--faint); - --lightText: var(--selectedMenuPopoverLightText, $fallback--lightText); - --icon: var(--selectedMenuPopoverIcon, $fallback--icon); - } } } </style> diff --git a/src/components/emoji_picker/emoji_picker.js b/src/components/emoji_picker/emoji_picker.js index eb665c40..9ea5c877 100644 --- a/src/components/emoji_picker/emoji_picker.js +++ b/src/components/emoji_picker/emoji_picker.js @@ -120,6 +120,7 @@ const EmojiPicker = { groupRefs: {}, emojiRefs: {}, filteredEmojiGroups: [], + emojiSize: 0, width: 0 } }, @@ -130,9 +131,28 @@ const EmojiPicker = { Popover }, methods: { + updateEmojiSize () { + const css = window.getComputedStyle(this.$refs.popover.$el) + const emojiSize = css.getPropertyValue('--emojiSize') + const emojiSizeUnit = emojiSize.replace(/[0-9,.]+/, '') + const emojiSizeValue = Number(emojiSize.replace(/[^0-9,.]+/, '')) + const fontSize = css.getPropertyValue('font-size').replace(/[^0-9,.]+/, '') + + let emojiSizeReal + if (emojiSizeUnit.endsWith('em')) { + emojiSizeReal = emojiSizeValue * fontSize + } else { + emojiSizeReal = emojiSizeValue + } + + const fullEmojiSize = emojiSizeReal + (2 * 0.2 * fontSize) + this.emojiSize = fullEmojiSize + }, showPicker () { this.$refs.popover.showPopover() - this.onShowing() + this.$nextTick(() => { + this.onShowing() + }) }, hidePicker () { this.$refs.popover.hidePopover() @@ -224,6 +244,7 @@ const EmojiPicker = { }, onShowing () { const oldContentLoaded = this.contentLoaded + this.updateEmojiSize() this.recalculateItemPerRow() this.$nextTick(() => { this.$refs.search.focus() @@ -266,16 +287,20 @@ const EmojiPicker = { }, computed: { minItemSize () { - return this.emojiHeight + return this.emojiSize }, - emojiHeight () { - return 32 + 4 + // used to watch it + fontSize () { + this.$nextTick(() => { + this.updateEmojiSize() + }) + return this.$store.getters.mergedConfig.fontSize }, - emojiWidth () { - return 32 + 4 + emojiHeight () { + return this.emojiSize }, itemPerRow () { - return this.width ? Math.floor(this.width / this.emojiWidth - 1) : 6 + return this.width ? Math.floor(this.width / this.emojiSize) : 6 }, activeGroupView () { return this.showingStickers ? '' : this.activeGroup diff --git a/src/components/emoji_picker/emoji_picker.scss b/src/components/emoji_picker/emoji_picker.scss index aab9251d..12c09388 100644 --- a/src/components/emoji_picker/emoji_picker.scss +++ b/src/components/emoji_picker/emoji_picker.scss @@ -1,47 +1,35 @@ -@import "../../variables"; - -$emoji-picker-header-height: 36px; -$emoji-picker-header-picture-width: 32px; -$emoji-picker-header-picture-height: 32px; -$emoji-picker-emoji-size: 32px; - .emoji-picker { + --__emoji-picker-header: 2.2em; + width: 25em; max-width: calc(100vw - 20px); // popover gives 10px margin from window edge display: flex; flex-direction: column; - background-color: $fallback--bg; - background-color: var(--popover, $fallback--bg); - color: $fallback--link; - color: var(--popoverText, $fallback--link); - - --faint: var(--popoverFaintText, $fallback--faint); - --faintLink: var(--popoverFaintLink, $fallback--faint); - --lightText: var(--popoverLightText, $fallback--lightText); - --icon: var(--popoverIcon, $fallback--icon); &-header-image { display: inline-flex; justify-content: center; align-items: center; - width: $emoji-picker-header-picture-width; - max-width: $emoji-picker-header-picture-width; - height: $emoji-picker-header-picture-height; - max-height: $emoji-picker-header-picture-height; + width: var(--__emoji-picker-header); + max-width: var(--__emoji-picker-header); + height: var(--__emoji-picker-header); + max-height: var(--__emoji-picker-header); .still-image { - max-width: 100%; - max-height: 100%; - height: 100%; - width: 100%; + width: var(--__emoji-picker-header); + max-width: var(--__emoji-picker-header); + height: var(--__emoji-picker-header); + max-height: var(--__emoji-picker-header); object-fit: contain; + + --_still_image-label-scale: 0.5; } } .keep-open, .too-many-emoji, .hide-custom-emoji { - padding: 7px; + padding: 0.5em; line-height: normal; } @@ -55,13 +43,13 @@ $emoji-picker-emoji-size: 32px; } .keep-open-label { - padding: 0 7px; + padding: 0 0.5em; display: flex; } .heading { display: flex; - padding: 10px 7px 5px; + padding: 0.7em 0.5em 0; } .content { @@ -76,14 +64,14 @@ $emoji-picker-emoji-size: 32px; display: flex; flex-flow: row nowrap; overflow-x: auto; + overflow-y: hidden; } .additional-tabs { display: flex; border-left: 1px solid; - border-left-color: $fallback--icon; - border-left-color: var(--icon, $fallback--icon); - padding-left: 7px; + border-left-color: var(--border); + padding-left: 0.5em; flex: 0 0 auto; } @@ -92,30 +80,29 @@ $emoji-picker-emoji-size: 32px; flex-basis: auto; display: flex; align-content: center; + scrollbar-width: thin; &-item { - padding: 0 7px; + padding: 0 0.5em; cursor: pointer; - font-size: 1.85em; - width: $emoji-picker-header-picture-width; - max-width: $emoji-picker-header-picture-width; - height: $emoji-picker-header-picture-height; - max-height: $emoji-picker-header-picture-height; + width: var(--__emoji-picker-header); + max-width: var(--__emoji-picker-header); + height: var(--__emoji-picker-header); + max-height: var(--__emoji-picker-header); display: flex; align-items: center; + .svg-inline--fa { + font-size: 1.85em; + } + &.disabled { opacity: 0.5; pointer-events: none; } - &.active { - border-bottom: 4px solid; - - svg { - color: $fallback--lightText; - color: var(--lightText, $fallback--lightText); - } + &.toggled { + border-bottom: 0.2em solid; } } } @@ -142,7 +129,7 @@ $emoji-picker-emoji-size: 32px; .emoji { &-search { - padding: 5px; + padding: 0.3em; flex: 0 0 auto; input { @@ -156,6 +143,7 @@ $emoji-picker-emoji-size: 32px; flex: 1 1 1px; position: relative; overflow: auto; + scrollbar-gutter: stable both-edges; user-select: none; mask: linear-gradient(to top, white 0, transparent 100%) bottom no-repeat, @@ -182,13 +170,13 @@ $emoji-picker-emoji-size: 32px; display: flex; align-items: center; flex-wrap: wrap; - padding-left: 5px; justify-content: left; &-title { font-size: 0.85em; width: 100%; margin: 0; + padding-left: 0.3em; &.disabled { display: none; @@ -197,24 +185,28 @@ $emoji-picker-emoji-size: 32px; } &-item { - width: $emoji-picker-emoji-size; - height: $emoji-picker-emoji-size; + width: var(--emoji-size); + height: var(--emoji-size); box-sizing: border-box; display: flex; - line-height: $emoji-picker-emoji-size; + line-height: var(--emoji-size); align-items: center; justify-content: center; - margin: 4px; + margin: 0.2em; cursor: pointer; .emoji-picker-emoji.-custom { object-fit: contain; - max-width: 100%; - max-height: 100%; + width: var(--emoji-size); + max-width: var(--emoji-size); + height: var(--emoji-size); + max-height: var(--emoji-size); + + --_still_image-label-scale: 0.5; } .emoji-picker-emoji.-unicode { - font-size: 24px; + font-size: 1.6em; overflow: hidden; } } diff --git a/src/components/emoji_picker/emoji_picker.vue b/src/components/emoji_picker/emoji_picker.vue index 1231ce2b..a3dc8f9e 100644 --- a/src/components/emoji_picker/emoji_picker.vue +++ b/src/components/emoji_picker/emoji_picker.vue @@ -23,9 +23,9 @@ v-for="group in filteredEmojiGroups" :ref="setGroupRef('group-header-' + group.id)" :key="group.id" - class="emoji-tabs-item" + class="button-unstyled emoji-tabs-item" :class="{ - active: activeGroupView === group.id + toggled: activeGroupView === group.id }" :title="group.text" role="button" @@ -52,8 +52,8 @@ class="additional-tabs" > <span - class="stickers-tab-icon additional-tabs-item" - :class="{active: showingStickers}" + class="button-unstyled stickers-tab-icon additional-tabs-item" + :class="{toggled: showingStickers}" :title="$t('emoji.stickers')" @click.prevent="toggleStickers" > @@ -77,7 +77,7 @@ ref="search" v-model="keyword" type="text" - class="form-control" + class="input form-control" :placeholder="$t('emoji.search_emoji')" @input="$event.target.composing = false" > @@ -89,6 +89,7 @@ class="emoji-groups" :class="groupsScrolledClass" :min-item-size="minItemSize" + :buffer="minItemSize" :items="emojiItems" :emit-update="true" @update="onScroll" diff --git a/src/components/emoji_reactions/emoji_reactions.vue b/src/components/emoji_reactions/emoji_reactions.vue index c11b338e..3ab4c125 100644 --- a/src/components/emoji_reactions/emoji_reactions.vue +++ b/src/components/emoji_reactions/emoji_reactions.vue @@ -72,7 +72,6 @@ <script src="./emoji_reactions.js"></script> <style lang="scss"> -@import "../../variables"; @import "../../mixins"; .EmojiReactions { @@ -80,7 +79,7 @@ margin-top: 0.25em; flex-wrap: wrap; - --emoji-size: calc(1.25em * var(--emojiReactionsScale, 1)); + --emoji-size: calc(var(--emojiSize, 1.25em) * var(--emojiReactionsScale, 1)); .emoji-reaction-container { display: flex; @@ -92,7 +91,6 @@ padding: 0; .emoji-reaction-count-button { - background-color: var(--btn); margin: 0; height: 100%; border-top-left-radius: 0; @@ -102,11 +100,9 @@ display: inline-flex; justify-content: center; align-items: center; - color: $fallback--text; - color: var(--btnText, $fallback--text); &.-picked-reaction { - border: 1px solid var(--accent, $fallback--link); + border: 1px solid var(--accent); margin-right: -1px; } } @@ -149,18 +145,16 @@ } .svg-inline--fa { - color: $fallback--text; - color: var(--btnText, $fallback--text); + color: var(--text); } &.-picked-reaction { - border: 1px solid var(--accent, $fallback--link); + border: 1px solid var(--accent); margin-left: -1px; // offset the border, can't use inset shadows either margin-right: -1px; .svg-inline--fa { - color: $fallback--link; - color: var(--accent, $fallback--link); + color: var(--accent); } } @@ -176,8 +170,7 @@ @include focused-style { .svg-inline--fa { - color: $fallback--link; - color: var(--accent, $fallback--link); + color: var(--accent); } .focus-marker { diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue index b7d3b1d3..7b38d974 100644 --- a/src/components/extra_buttons/extra_buttons.vue +++ b/src/components/extra_buttons/extra_buttons.vue @@ -12,13 +12,13 @@ > <template #content="{close}"> <div + :id="`popup-menu-${randomSeed}`" class="dropdown-menu" role="menu" - :id="`popup-menu-${randomSeed}`" > <button v-if="canMute && !status.thread_muted" - class="button-default dropdown-item dropdown-item-icon" + class="menu-item dropdown-item dropdown-item-icon" role="menuitem" @click.prevent="muteConversation" > @@ -29,7 +29,7 @@ </button> <button v-if="canMute && status.thread_muted" - class="button-default dropdown-item dropdown-item-icon" + class="menu-item dropdown-item dropdown-item-icon" role="menuitem" @click.prevent="unmuteConversation" > @@ -40,7 +40,7 @@ </button> <button v-if="!status.pinned && canPin" - class="button-default dropdown-item dropdown-item-icon" + class="menu-item dropdown-item dropdown-item-icon" role="menuitem" @click.prevent="pinStatus" @click="close" @@ -52,7 +52,7 @@ </button> <button v-if="status.pinned && canPin" - class="button-default dropdown-item dropdown-item-icon" + class="menu-item dropdown-item dropdown-item-icon" role="menuitem" @click.prevent="unpinStatus" @click="close" @@ -65,7 +65,7 @@ <template v-if="canBookmark"> <button v-if="!status.bookmarked" - class="button-default dropdown-item dropdown-item-icon" + class="menu-item dropdown-item dropdown-item-icon" role="menuitem" @click.prevent="bookmarkStatus" @click="close" @@ -77,7 +77,7 @@ </button> <button v-if="status.bookmarked" - class="button-default dropdown-item dropdown-item-icon" + class="menu-item dropdown-item dropdown-item-icon" role="menuitem" @click.prevent="unbookmarkStatus" @click="close" @@ -90,7 +90,7 @@ </template> <button v-if="ownStatus && editingAvailable" - class="button-default dropdown-item dropdown-item-icon" + class="menu-item dropdown-item dropdown-item-icon" role="menuitem" @click.prevent="editStatus" @click="close" @@ -102,7 +102,7 @@ </button> <button v-if="isEdited && editingAvailable" - class="button-default dropdown-item dropdown-item-icon" + class="menu-item dropdown-item dropdown-item-icon" role="menuitem" @click.prevent="showStatusHistory" @click="close" @@ -114,7 +114,7 @@ </button> <button v-if="canDelete" - class="button-default dropdown-item dropdown-item-icon" + class="menu-item dropdown-item dropdown-item-icon" role="menuitem" @click.prevent="deleteStatus" @click="close" @@ -125,7 +125,7 @@ /><span>{{ $t("status.delete") }}</span> </button> <button - class="button-default dropdown-item dropdown-item-icon" + class="menu-item dropdown-item dropdown-item-icon" role="menuitem" @click.prevent="copyLink" @click="close" @@ -137,7 +137,7 @@ </button> <a v-if="!status.is_local" - class="button-default dropdown-item dropdown-item-icon" + class="menu-item dropdown-item dropdown-item-icon" role="menuitem" title="Source" :href="status.external_url" @@ -149,7 +149,7 @@ /><span>{{ $t("status.external_source") }}</span> </a> <button - class="button-default dropdown-item dropdown-item-icon" + class="menu-item dropdown-item dropdown-item-icon" role="menuitem" @click.prevent="reportStatus" @click="close" @@ -201,7 +201,6 @@ <script src="./extra_buttons.js"></script> <style lang="scss"> -@import "../../variables"; @import "../../mixins"; .ExtraButtons { @@ -211,8 +210,7 @@ margin: -10px; &:hover .svg-inline--fa { - color: $fallback--text; - color: var(--text, $fallback--text); + color: var(--text); } } diff --git a/src/components/extra_notifications/extra_notifications.vue b/src/components/extra_notifications/extra_notifications.vue index 6e0456a5..600c99bb 100644 --- a/src/components/extra_notifications/extra_notifications.vue +++ b/src/components/extra_notifications/extra_notifications.vue @@ -80,8 +80,6 @@ <script src="./extra_notifications.js" /> <style lang="scss"> -@import "../../variables"; - .ExtraNotifications { width: 100%; display: flex; @@ -91,8 +89,7 @@ .notification { width: 100%; border-bottom: 1px solid; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); + border-color: var(--border); display: flex; flex-direction: column; align-items: stretch; diff --git a/src/components/favorite_button/favorite_button.vue b/src/components/favorite_button/favorite_button.vue index 8c883c13..2e0dd047 100644 --- a/src/components/favorite_button/favorite_button.vue +++ b/src/components/favorite_button/favorite_button.vue @@ -65,7 +65,6 @@ <script src="./favorite_button.js"></script> <style lang="scss"> -@import "../../variables"; @import "../../mixins"; .FavoriteButton { @@ -88,8 +87,7 @@ &:hover .svg-inline--fa, &.-favorited .svg-inline--fa { - color: $fallback--cOrange; - color: var(--cOrange, $fallback--cOrange); + color: var(--cOrange); } @include unfocused-style { diff --git a/src/components/flash/flash.vue b/src/components/flash/flash.vue index 9f58d314..c9fc730b 100644 --- a/src/components/flash/flash.vue +++ b/src/components/flash/flash.vue @@ -42,8 +42,6 @@ <script src="./flash.js"></script> <style lang="scss"> -@import "../../variables"; - .Flash { display: inline-block; width: 100%; diff --git a/src/components/font_control/font_control.js b/src/components/font_control/font_control.js index 92ee3f30..d9394945 100644 --- a/src/components/font_control/font_control.js +++ b/src/components/font_control/font_control.js @@ -1,63 +1,59 @@ -import { set } from 'lodash' import Select from '../select/select.vue' +import Checkbox from 'src/components/checkbox/checkbox.vue' +import Popover from 'src/components/popover/popover.vue' + +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faExclamationTriangle, + faKeyboard, + faFont +} from '@fortawesome/free-solid-svg-icons' + +library.add( + faExclamationTriangle, + faKeyboard, + faFont +) export default { components: { - Select + Select, + Checkbox, + Popover }, props: [ 'name', 'label', 'modelValue', 'fallback', 'options', 'no-inherit' ], + mounted () { + this.$store.dispatch('queryLocalFonts') + }, emits: ['update:modelValue'], data () { return { - lValue: this.modelValue, + manualEntry: false, availableOptions: [ this.noInherit ? '' : 'inherit', - 'custom', - ...(this.options || []), 'serif', + 'sans-serif', 'monospace', - 'sans-serif' + ...(this.options || []) ].filter(_ => _) } }, - beforeUpdate () { - this.lValue = this.modelValue + methods: { + toggleManualEntry () { + this.manualEntry = !this.manualEntry + } }, computed: { present () { - return typeof this.lValue !== 'undefined' - }, - dValue () { - return this.lValue || this.fallback || {} - }, - family: { - get () { - return this.dValue.family - }, - set (v) { - set(this.lValue, 'family', v) - this.$emit('update:modelValue', this.lValue) - } + return typeof this.modelValue !== 'undefined' }, - isCustom () { - return this.preset === 'custom' + localFontsList () { + return this.$store.state.interface.localFonts }, - preset: { - get () { - if (this.family === 'serif' || - this.family === 'sans-serif' || - this.family === 'monospace' || - this.family === 'inherit') { - return this.family - } else { - return 'custom' - } - }, - set (v) { - this.family = v === 'custom' ? '' : v - } + localFontsSize () { + return this.$store.state.interface.localFonts?.length } } } diff --git a/src/components/font_control/font_control.vue b/src/components/font_control/font_control.vue index e2ba74d1..fca3b360 100644 --- a/src/components/font_control/font_control.vue +++ b/src/components/font_control/font_control.vue @@ -1,6 +1,6 @@ <template> <div - class="font-control style-control" + class="font-control" :class="{ custom: isCustom }" > <label @@ -10,67 +10,137 @@ > {{ label }} </label> - <input + {{ ' ' }} + <Checkbox v-if="typeof fallback !== 'undefined'" :id="name + '-o'" - :aria-labelledby="name + '-label'" - class="opt exlcude-disabled visible-for-screenreader-only" - type="checkbox" - :checked="present" + :modelValue="present" @change="$emit('update:modelValue', typeof modelValue === 'undefined' ? fallback : undefined)" > - <label - v-if="typeof fallback !== 'undefined'" - class="opt-l" - :for="name + '-o'" - :aria-hidden="true" - /> - {{ ' ' }} - <Select - :id="name + '-font-switcher'" - v-model="preset" - :disabled="!present" - class="font-switcher" - > - <option - v-for="option in availableOptions" - :key="option" - :value="option" + {{ $t('settings.style.themes3.define') }} + </Checkbox> + <p v-if="modelValue?.family"> + <label + v-if="manualEntry" + :id="name + '-label'" + :for="preset === 'custom' ? name : name + '-font-switcher'" + class="label" > - {{ option === 'custom' ? $t('settings.style.fonts.custom') : option }} - </option> - </Select> - <input - v-if="isCustom" - :id="name" - v-model="family" - class="custom-font" - type="text" - > + <i18n-t + keypath="settings.style.themes3.font.entry" + tag="span" + > + <template #fontFamily> + <code>font-family</code> + </template> + </i18n-t> + </label> + <label + v-else + :id="name + '-label'" + :for="preset === 'custom' ? name : name + '-font-switcher'" + class="label" + > + {{ $t('settings.style.themes3.font.select') }} + </label> + {{ ' ' }} + <span + v-if="manualEntry" + class="btn-group" + > + <button + class="btn button-default" + @click="toggleManualEntry" + :title="$t('settings.style.themes3.font.lookup_local_fonts')" + > + <FAIcon + fixed-width + icon="font" + /> + </button> + <input + :id="name" + :model-value="modelValue.family" + class="input custom-font" + type="text" + @update:modelValue="$emit('update:modelValue', { ...(modelValue || {}), family: $event.target.value })" + > + </span> + <span + v-else + class="btn-group" + > + <button + class="btn button-default" + @click="toggleManualEntry" + :title="$t('settings.style.themes3.font.enter_manually')" + > + <FAIcon + fixed-width + icon="keyboard" + /> + </button> + <Select + :id="name + '-local-font-switcher'" + :model-value="modelValue?.family" + class="custom-font" + @update:modelValue="v => $emit('update:modelValue', { ...(modelValue || {}), family: v })" + > + <optgroup + :label="$t('settings.style.themes3.font.group-builtin')" + > + <option + v-for="option in availableOptions" + :key="option" + :value="option" + :style="{ fontFamily: option === 'inherit' ? null : option }" + > + {{ $t('settings.style.themes3.font.builtin.' + option) }} + </option> + </optgroup> + <optgroup + v-if="localFontsSize > 0" + :label="$t('settings.style.themes3.font.group-local')" + > + <option + v-for="option in localFontsList" + :key="option" + :value="option" + :style="{ fontFamily: option }" + > + {{ option }} + </option> + </optgroup> + <optgroup + v-else + :label="$t('settings.style.themes3.font.group-local')" + > + <option disabled> + {{ $t('settings.style.themes3.font.local-unavailable1') }} + </option> + <option disabled> + {{ $t('settings.style.themes3.font.local-unavailable2') }} + </option> + </optgroup> + </Select> + </span> + </p> </div> </template> <script src="./font_control.js"></script> <style lang="scss"> -@import "../../variables"; - .font-control { - input.custom-font { - min-width: 10em; + .custom-font { + min-width: 20em; + max-width: 20em; } +} - &.custom { - /* TODO Should make proper joiners... */ - .font-switcher { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } - - .custom-font { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - } +.invalid-tooltip { + margin: 0.5em 1em; + min-width: 10em; + text-align: center; } </style> diff --git a/src/components/fun_text.style.js b/src/components/fun_text.style.js new file mode 100644 index 00000000..2d3ac154 --- /dev/null +++ b/src/components/fun_text.style.js @@ -0,0 +1,40 @@ +export default { + name: 'FunText', + selector: '/*fun-text*/', + virtual: true, + variants: { + greentext: '.greentext', + cyantext: '.cyantext' + }, + states: { + faint: '.faint' + }, + defaultRules: [ + { + directives: { + textColor: '--text', + textAuto: 'preserve' + } + }, + { + state: ['faint'], + directives: { + textOpacity: 0.5 + } + }, + { + variant: 'greentext', + directives: { + textColor: '--cGreen', + textAuto: 'preserve' + } + }, + { + variant: 'cyantext', + directives: { + textColor: '--cBlue', + textAuto: 'preserve' + } + } + ] +} diff --git a/src/components/gallery/gallery.vue b/src/components/gallery/gallery.vue index 96b554e3..40048285 100644 --- a/src/components/gallery/gallery.vue +++ b/src/components/gallery/gallery.vue @@ -87,8 +87,6 @@ <script src='./gallery.js'></script> <style lang="scss"> -@import "../../variables"; - .Gallery { .gallery-rows { display: flex; diff --git a/src/components/global_notice_list/global_notice_list.vue b/src/components/global_notice_list/global_notice_list.vue index 9e9ec7aa..140d0d54 100644 --- a/src/components/global_notice_list/global_notice_list.vue +++ b/src/components/global_notice_list/global_notice_list.vue @@ -4,7 +4,7 @@ v-for="(notice, index) in notices" :key="index" class="alert global-notice" - :class="{ ['global-' + notice.level]: true }" + :class="{ [notice.level]: true }" > <div class="notice-message"> {{ $t(notice.messageKey, notice.messageArgs) }} @@ -25,8 +25,6 @@ <script src="./global_notice_list.js"></script> <style lang="scss"> -@import "../../variables"; - .global-notice-list { position: fixed; top: calc(var(--navbar-height) + 0.5em); @@ -52,48 +50,8 @@ } } - .global-error { - background-color: var(--alertPopupError, $fallback--cRed); - color: var(--alertPopupErrorText, $fallback--text); - - .svg-inline--fa { - color: var(--alertPopupErrorText, $fallback--text); - } - } - - .global-warning { - background-color: var(--alertPopupWarning, $fallback--cOrange); - color: var(--alertPopupWarningText, $fallback--text); - - .svg-inline--fa { - color: var(--alertPopupWarningText, $fallback--text); - } - } - - .global-success { - background-color: var(--alertPopupSuccess, $fallback--cGreen); - color: var(--alertPopupSuccessText, $fallback--text); - - .svg-inline--fa { - color: var(--alertPopupSuccessText, $fallback--text); - } - } - - .global-info { - background-color: var(--alertPopupNeutral, $fallback--fg); - color: var(--alertPopupNeutralText, $fallback--text); - - .svg-inline--fa { - color: var(--alertPopupNeutralText, $fallback--text); - } - } - .close-notice { padding-right: 0.2em; - - .svg-inline--fa:hover { - opacity: 0.6; - } } } </style> diff --git a/src/components/icon.style.js b/src/components/icon.style.js new file mode 100644 index 00000000..6cb9e4e3 --- /dev/null +++ b/src/components/icon.style.js @@ -0,0 +1,14 @@ +export default { + name: 'Icon', + virtual: true, + selector: '.svg-inline--fa', + defaultRules: [ + { + component: 'Icon', + directives: { + textColor: '$blend(--stack, 0.5, --parent--text)', + textAuto: 'no-auto' + } + } + ] +} diff --git a/src/components/image_cropper/image_cropper.vue b/src/components/image_cropper/image_cropper.vue index 8c48a387..8647ed4d 100644 --- a/src/components/image_cropper/image_cropper.vue +++ b/src/components/image_cropper/image_cropper.vue @@ -41,7 +41,7 @@ <input ref="input" type="file" - class="image-cropper-img-input" + class="input image-cropper-img-input" :accept="mimes" > </div> diff --git a/src/components/importer/importer.vue b/src/components/importer/importer.vue index 2a63b31a..12779b45 100644 --- a/src/components/importer/importer.vue +++ b/src/components/importer/importer.vue @@ -3,6 +3,7 @@ <form> <input ref="input" + class="input" type="file" @change="change" > diff --git a/src/components/input.style.js b/src/components/input.style.js new file mode 100644 index 00000000..7302cd6d --- /dev/null +++ b/src/components/input.style.js @@ -0,0 +1,60 @@ +const hoverGlow = { + x: 0, + y: 0, + blur: 4, + spread: 0, + color: '--text', + alpha: 1 +} + +export default { + name: 'Input', + selector: '.input', + variant: { + checkbox: '.-checkbox', + radio: '.-radio' + }, + states: { + disabled: ':disabled', + hover: ':hover:not(:disabled)', + focused: ':focus-within' + }, + validInnerComponents: [ + 'Text' + ], + defaultRules: [ + { + component: 'Root', + directives: { + '--defaultInputBevel': 'shadow | $borderSide(#FFFFFF, bottom, 0.2), $borderSide(#000000, top, 0.2)' + } + }, + { + variant: 'checkbox', + directives: { + roundness: 1 + } + }, + { + directives: { + '--font': 'generic | inherit', + background: '--fg, -5', + roundness: 3, + shadow: [{ + x: 0, + y: 0, + blur: 2, + spread: 0, + color: '#000000', + alpha: 1 + }, '--defaultInputBevel'] + } + }, + { + state: ['hover'], + directives: { + shadow: [hoverGlow, '--defaultInputBevel'] + } + } + ] +} diff --git a/src/components/interactions/interactions.js b/src/components/interactions/interactions.js index 1ae1d01c..fc441b90 100644 --- a/src/components/interactions/interactions.js +++ b/src/components/interactions/interactions.js @@ -3,6 +3,7 @@ import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' const tabModeDict = { mentions: ['mention'], + statuses: ['status'], 'likes+repeats': ['repeat', 'like'], follows: ['follow'], reactions: ['pleroma:emoji_reaction'], diff --git a/src/components/interactions/interactions.vue b/src/components/interactions/interactions.vue index 35d03562..a2c56af4 100644 --- a/src/components/interactions/interactions.vue +++ b/src/components/interactions/interactions.vue @@ -10,10 +10,14 @@ :on-switch="onModeSwitch" > <span - key="mentions" + key="statuses" :label="$t('nav.mentions')" /> <span + key="statuses" + :label="$t('interactions.statuses')" + /> + <span key="likes+repeats" :label="$t('interactions.favs_repeats')" /> diff --git a/src/components/interface_language_switcher/interface_language_switcher.vue b/src/components/interface_language_switcher/interface_language_switcher.vue index 364791a1..f7d5ef7e 100644 --- a/src/components/interface_language_switcher/interface_language_switcher.vue +++ b/src/components/interface_language_switcher/interface_language_switcher.vue @@ -104,8 +104,6 @@ export default { </script> <style lang="scss"> -@import "../../variables"; - .interface-language-switcher { .language-select { margin-right: 1em; diff --git a/src/components/link-preview/link-preview.vue b/src/components/link-preview/link-preview.vue index 09f341ac..8eb5a501 100644 --- a/src/components/link-preview/link-preview.vue +++ b/src/components/link-preview/link-preview.vue @@ -33,8 +33,6 @@ <script src="./link-preview.js"></script> <style lang="scss"> -@import "../../variables"; - .link-preview-card { display: flex; flex-direction: row; @@ -51,8 +49,7 @@ width: 100%; height: 100%; object-fit: cover; - border-radius: $fallback--attachmentRadius; - border-radius: var(--attachmentRadius, $fallback--attachmentRadius); + border-radius: var(--roundness); } } @@ -82,13 +79,10 @@ margin: 2em 0; } - color: $fallback--text; - color: var(--text, $fallback--text); + color: var(--text); border-style: solid; border-width: 1px; - border-radius: $fallback--attachmentRadius; - border-radius: var(--attachmentRadius, $fallback--attachmentRadius); - border-color: $fallback--border; - border-color: var(--border, $fallback--border); + border-radius: var(--roundness); + border-color: var(--border); } </style> diff --git a/src/components/link.style.js b/src/components/link.style.js new file mode 100644 index 00000000..d13cef33 --- /dev/null +++ b/src/components/link.style.js @@ -0,0 +1,24 @@ +export default { + name: 'Link', + selector: 'a', + virtual: true, + states: { + faint: '.faint' + }, + defaultRules: [ + { + component: 'Link', + directives: { + textColor: '--link' + } + }, + { + component: 'Link', + state: ['faint'], + directives: { + textOpacity: 0.5, + textOpacityMode: 'fake' + } + } + ] +} diff --git a/src/components/list/list.vue b/src/components/list/list.vue index a3562c5d..c885bacd 100644 --- a/src/components/list/list.vue +++ b/src/components/list/list.vue @@ -7,6 +7,7 @@ v-for="item in items" :key="getKey(item)" class="list-item" + :class="[getClass(item), nonInteractive ? '-non-interactive' : '']" role="listitem" > <slot @@ -33,24 +34,15 @@ export default { getKey: { type: Function, default: item => item.id + }, + getClass: { + type: Function, + default: item => '' + }, + nonInteractive: { + type: Boolean, + default: false } } } </script> - -<style lang="scss"> -@import "../../variables"; - -.list { - &-item:not(:last-child) { - border-bottom: 1px solid; - border-bottom-color: $fallback--border; - border-bottom-color: var(--border, $fallback--border); - } - - &-empty-content { - text-align: center; - padding: 10px; - } -} -</style> diff --git a/src/components/list/list_item.style.js b/src/components/list/list_item.style.js new file mode 100644 index 00000000..e82a0a83 --- /dev/null +++ b/src/components/list/list_item.style.js @@ -0,0 +1,48 @@ +export default { + name: 'ListItem', + selector: '.list-item', + states: { + active: '.-active', + hover: ':hover:not(.-non-interactive)' + }, + validInnerComponents: [ + 'Text', + 'Link', + 'Icon', + 'Border', + 'Button', + 'ButtonUnstyled', + 'RichContent', + 'Input', + 'Avatar' + ], + defaultRules: [ + { + directives: { + background: '--bg', + opacity: 0 + } + }, + { + state: ['active'], + directives: { + background: '--inheritedBackground, 10', + opacity: 1 + } + }, + { + state: ['hover'], + directives: { + background: '--inheritedBackground, 10', + opacity: 1 + } + }, + { + state: ['hover', 'active'], + directives: { + background: '--inheritedBackground, 20', + opacity: 1 + } + } + ] +} diff --git a/src/components/lists_card/lists_card.vue b/src/components/lists_card/lists_card.vue index 925da3a5..a5dc6371 100644 --- a/src/components/lists_card/lists_card.vue +++ b/src/components/lists_card/lists_card.vue @@ -21,8 +21,6 @@ <script src="./lists_card.js"></script> <style lang="scss"> -@import "../../variables"; - .list-card { display: flex; } @@ -35,18 +33,6 @@ .button-list-edit { margin: 0; padding: 1em; - color: $fallback--link; - color: var(--link, $fallback--link); - - &:hover { - background-color: $fallback--lightBg; - background-color: var(--selectedMenu, $fallback--lightBg); - color: $fallback--link; - color: var(--selectedMenuText, $fallback--link); - - --faint: var(--selectedMenuFaintText, $fallback--faint); - --faintLink: var(--selectedMenuFaintLink, $fallback--faint); - --lightText: var(--selectedMenuLightText, $fallback--lightText); - } + color: var(--link); } </style> diff --git a/src/components/lists_edit/lists_edit.vue b/src/components/lists_edit/lists_edit.vue index eec0f978..3c2066f6 100644 --- a/src/components/lists_edit/lists_edit.vue +++ b/src/components/lists_edit/lists_edit.vue @@ -36,6 +36,7 @@ id="list-edit-title" ref="title" v-model="titleDraft" + class="input" > <button v-if="id" @@ -164,8 +165,6 @@ <script src="./lists_edit.js"></script> <style lang="scss"> -@import "../../variables"; - .ListEdit { --panel-body-padding: 0.5em; diff --git a/src/components/lists_user_search/lists_user_search.vue b/src/components/lists_user_search/lists_user_search.vue index 6ca107e6..0f6ec125 100644 --- a/src/components/lists_user_search/lists_user_search.vue +++ b/src/components/lists_user_search/lists_user_search.vue @@ -10,6 +10,7 @@ <input ref="search" v-model="query" + class="input" :placeholder="$t('lists.search')" @input="onInput" > @@ -27,8 +28,6 @@ <script src="./lists_user_search.js"></script> <style lang="scss"> -@import "../../variables"; - .ListsUserSearch { .input-wrap { display: flex; diff --git a/src/components/login_form/login_form.vue b/src/components/login_form/login_form.vue index 829e88ad..adc2dd5b 100644 --- a/src/components/login_form/login_form.vue +++ b/src/components/login_form/login_form.vue @@ -18,7 +18,7 @@ id="username" v-model="user.username" :disabled="loggingIn" - class="form-control" + class="input form-control" :placeholder="$t('login.placeholder')" > </div> @@ -29,7 +29,7 @@ ref="passwordInput" v-model="user.password" :disabled="loggingIn" - class="form-control" + class="input form-control" type="password" > </div> @@ -93,8 +93,6 @@ <script src="./login_form.js"></script> <style lang="scss"> -@import "../../variables"; - .login-form { display: flex; flex-direction: column; diff --git a/src/components/media_upload/media_upload.vue b/src/components/media_upload/media_upload.vue index 2ea5567a..047e3483 100644 --- a/src/components/media_upload/media_upload.vue +++ b/src/components/media_upload/media_upload.vue @@ -36,8 +36,6 @@ <script src="./media_upload.js"></script> <style lang="scss"> -@import "../../variables"; - .media-upload { .hidden-input-file { display: none; diff --git a/src/components/mention_link/mention_link.scss b/src/components/mention_link/mention_link.scss index 69e9fed1..42f57294 100644 --- a/src/components/mention_link/mention_link.scss +++ b/src/components/mention_link/mention_link.scss @@ -1,10 +1,7 @@ -@import "../../variables"; - .MentionLink { position: relative; white-space: normal; display: inline; - color: var(--link); word-break: normal; & .new, @@ -14,7 +11,7 @@ } .mention-avatar { - border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); + border-radius: var(--roundness); width: 1.5em; height: 1.5em; vertical-align: middle; @@ -61,8 +58,10 @@ } &.-has-selection { - color: var(--alertNeutralText, $fallback--text); - background-color: var(--alertNeutral, $fallback--fg); + --color: var(--selectionText); + --link: var(--selectionText); + + background-color: var(--selectionBackground); } .at { @@ -102,7 +101,7 @@ } .serverName.-faded { - color: var(--faintLink, $fallback--link); + color: var(--linkFaint); } } diff --git a/src/components/mention_link/mention_link.vue b/src/components/mention_link/mention_link.vue index 869a3257..5db837a2 100644 --- a/src/components/mention_link/mention_link.vue +++ b/src/components/mention_link/mention_link.vue @@ -22,7 +22,7 @@ :class="classnames" > <a - class="short button-unstyled" + class="short" :class="{ '-with-tooltip': shouldShowTooltip }" :href="url" @click.prevent="onClick" diff --git a/src/components/mentions_line/mentions_line.vue b/src/components/mentions_line/mentions_line.vue index 64c19bf1..daab64a3 100644 --- a/src/components/mentions_line/mentions_line.vue +++ b/src/components/mentions_line/mentions_line.vue @@ -22,13 +22,13 @@ /> </span><button v-if="!expanded" - class="button-unstyled showMoreLess" + class="button-unstyled -link showMoreLess" @click="toggleShowMore" > {{ $t('status.plus_more', { number: extraMentions.length }) }} </button><button v-if="expanded" - class="button-unstyled showMoreLess" + class="button-unstyled -link showMoreLess" @click="toggleShowMore" > {{ $t('general.show_less') }} diff --git a/src/components/menu_item.style.js b/src/components/menu_item.style.js new file mode 100644 index 00000000..51388155 --- /dev/null +++ b/src/components/menu_item.style.js @@ -0,0 +1,90 @@ +export default { + name: 'MenuItem', + selector: '.menu-item', + validInnerComponents: [ + 'Text', + 'Icon', + 'Input', + 'Border', + 'ButtonUnstyled', + 'Badge', + 'Avatar' + ], + states: { + hover: ':hover', + active: '.-active' + }, + defaultRules: [ + { + directives: { + background: '--bg', + opacity: 0 + } + }, + { + state: ['hover'], + directives: { + background: '$mod(--bg, 5)', + opacity: 1 + } + }, + { + state: ['active'], + directives: { + background: '$mod(--bg, 10)', + opacity: 1 + } + }, + { + state: ['active', 'hover'], + directives: { + background: '$mod(--bg, 15)', + opacity: 1 + } + }, + { + component: 'Text', + parent: { + component: 'MenuItem', + state: ['hover'] + }, + directives: { + textColor: '--link', + textAuto: 'no-preserve' + } + }, + { + component: 'Text', + parent: { + component: 'MenuItem', + state: ['active'] + }, + directives: { + textColor: '--link', + textAuto: 'no-preserve' + } + }, + { + component: 'Icon', + parent: { + component: 'MenuItem', + state: ['active'] + }, + directives: { + textColor: '--link', + textAuto: 'no-preserve' + } + }, + { + component: 'Icon', + parent: { + component: 'MenuItem', + state: ['hover'] + }, + directives: { + textColor: '--link', + textAuto: 'no-preserve' + } + } + ] +} diff --git a/src/components/mfa_form/recovery_form.vue b/src/components/mfa_form/recovery_form.vue index 5988fa51..27184fb4 100644 --- a/src/components/mfa_form/recovery_form.vue +++ b/src/components/mfa_form/recovery_form.vue @@ -16,7 +16,7 @@ <input id="code" v-model="code" - class="form-control" + class="input form-control" > </div> diff --git a/src/components/mfa_form/totp_form.vue b/src/components/mfa_form/totp_form.vue index 709eb9b8..3ec617e9 100644 --- a/src/components/mfa_form/totp_form.vue +++ b/src/components/mfa_form/totp_form.vue @@ -18,7 +18,7 @@ <input id="code" v-model="code" - class="form-control" + class="input form-control" > </div> diff --git a/src/components/mobile_drawer.style.js b/src/components/mobile_drawer.style.js new file mode 100644 index 00000000..398bc186 --- /dev/null +++ b/src/components/mobile_drawer.style.js @@ -0,0 +1,41 @@ +export default { + name: 'MobileDrawer', + selector: '.mobile-drawer', + validInnerComponents: [ + 'Text', + 'Link', + 'Icon', + 'Border', + 'Button', + 'ButtonUnstyled', + 'Input', + 'PanelHeader', + 'MenuItem', + 'Notification', + 'Alert', + 'UserCard' + ], + defaultRules: [ + { + directives: { + background: '--bg', + backgroundNoCssColor: 'yes' + } + }, + { + component: 'PanelHeader', + parent: { component: 'MobileDrawer' }, + directives: { + background: '--fg', + shadow: [{ + x: 0, + y: 0, + blur: 4, + spread: 0, + color: '#000000', + alpha: 0.6 + }] + } + } + ] +} diff --git a/src/components/mobile_nav/mobile_nav.vue b/src/components/mobile_nav/mobile_nav.vue index f20a509d..76a90d3e 100644 --- a/src/components/mobile_nav/mobile_nav.vue +++ b/src/components/mobile_nav/mobile_nav.vue @@ -20,7 +20,7 @@ /> <div v-if="(unreadChatCount && !chatsPinned) || unreadAnnouncementCount" - class="alert-dot" + class="badge -dot -notification" /> </button> <NavigationPins class="pins" /> @@ -37,24 +37,24 @@ /> <div v-if="unseenNotificationsCount" - class="alert-dot" + class="badge -dot -notification" /> </button> </div> </nav> <aside v-if="currentUser" - class="mobile-notifications-drawer" + class="mobile-notifications-drawer mobile-drawer" :class="{ '-closed': !notificationsOpen }" @touchstart.stop="notificationsTouchStart" @touchmove.stop="notificationsTouchMove" > - <div class="mobile-notifications-header"> + <div class="panel-heading mobile-notifications-header"> <span class="title"> {{ $t('notifications.notifications') }} <span v-if="unseenCountBadgeText" - class="badge badge-notification unseen-count" + class="badge -notification unseen-count" >{{ unseenCountBadgeText }}</span> </span> <span class="spacer" /> @@ -123,21 +123,19 @@ <script src="./mobile_nav.js"></script> <style lang="scss"> -@import "../../variables"; - .MobileNav { z-index: var(--ZI_navbar); .mobile-nav { display: grid; line-height: var(--navbar-height); - grid-template-rows: 50px; + grid-template-rows: var(--navbar-height); grid-template-columns: 2fr auto; width: 100%; box-sizing: border-box; a { - color: var(--topBarLink, $fallback--link); + color: var(--link); } } @@ -165,19 +163,6 @@ display: flex; } - .alert-dot { - border-radius: 100%; - height: 8px; - width: 8px; - position: absolute; - left: calc(50% - 4px); - top: calc(50% - 4px); - margin-left: 6px; - margin-top: -6px; - background-color: $fallback--cRed; - background-color: var(--badgeNotification, $fallback--cRed); - } - .mobile-notifications-drawer { width: 100%; height: 100vh; @@ -185,13 +170,13 @@ position: fixed; top: 0; left: 0; - box-shadow: 1px 1px 4px rgb(0 0 0 / 60%); - box-shadow: var(--panelShadow); + box-shadow: var(--shadow); transition-property: transform; transition-duration: 0.25s; transform: translateX(0); z-index: var(--ZI_navbar); -webkit-overflow-scrolling: touch; + background: var(--background); &.-closed { transform: translateX(100%); @@ -205,14 +190,10 @@ justify-content: space-between; z-index: calc(var(--ZI_navbar) + 100); width: 100%; - height: 50px; - line-height: 50px; + height: 3.5em; + line-height: 3.5em; position: absolute; - color: var(--topBarText); - background-color: $fallback--fg; - background-color: var(--topBar, $fallback--fg); - box-shadow: 0 0 4px rgb(0 0 0 / 60%); - box-shadow: var(--topBarShadow); + box-shadow: var(--shadow); .spacer { flex: 1; @@ -233,15 +214,11 @@ } .mobile-notifications { - margin-top: 50px; + margin-top: 3.5em; width: 100vw; height: calc(100vh - var(--navbar-height)); overflow-x: hidden; overflow-y: scroll; - color: $fallback--text; - color: var(--text, $fallback--text); - background-color: $fallback--bg; - background-color: var(--bg, $fallback--bg); .notifications { padding: 0; diff --git a/src/components/mobile_post_status_button/mobile_post_status_button.vue b/src/components/mobile_post_status_button/mobile_post_status_button.vue index ef0f51fe..ba77cb2d 100644 --- a/src/components/mobile_post_status_button/mobile_post_status_button.vue +++ b/src/components/mobile_post_status_button/mobile_post_status_button.vue @@ -13,8 +13,6 @@ <script src="./mobile_post_status_button.js"></script> <style lang="scss"> -@import "../../variables"; - .MobilePostButton { &.button-default { width: 5em; @@ -25,8 +23,6 @@ right: 1.5em; // TODO: this needs its own color, it has to stand out enough and link color // is not very optimal for this particular use. - background-color: $fallback--fg; - background-color: var(--btn, $fallback--fg); display: flex; justify-content: center; align-items: center; @@ -42,8 +38,7 @@ svg { font-size: 1.5em; - color: $fallback--text; - color: var(--text, $fallback--text); + color: var(--text); } } diff --git a/src/components/modal/modals.style.js b/src/components/modal/modals.style.js new file mode 100644 index 00000000..c401a0cd --- /dev/null +++ b/src/components/modal/modals.style.js @@ -0,0 +1,9 @@ +export default { + name: 'Modals', + selector: '.modal-view', + lazy: true, + validInnerComponents: [ + 'Panel' + ], + defaultRules: [] +} diff --git a/src/components/moderation_tools/moderation_tools.vue b/src/components/moderation_tools/moderation_tools.vue index b708cdc8..df3bb655 100644 --- a/src/components/moderation_tools/moderation_tools.vue +++ b/src/components/moderation_tools/moderation_tools.vue @@ -12,13 +12,13 @@ <div class="dropdown-menu"> <span v-if="canGrantRole"> <button - class="button-default dropdown-item" + class="menu-item dropdown-item menu-item" @click="toggleRight("admin")" > {{ $t(!!user.rights.admin ? 'user_card.admin_menu.revoke_admin' : 'user_card.admin_menu.grant_admin') }} </button> <button - class="button-default dropdown-item" + class="menu-item dropdown-item menu-item" @click="toggleRight("moderator")" > {{ $t(!!user.rights.moderator ? 'user_card.admin_menu.revoke_moderator' : 'user_card.admin_menu.grant_moderator') }} @@ -31,14 +31,14 @@ </span> <button v-if="canChangeActivationState" - class="button-default dropdown-item" + class="menu-item dropdown-item menu-item" @click="toggleActivationStatus()" > {{ $t(!!user.deactivated ? 'user_card.admin_menu.activate_account' : 'user_card.admin_menu.deactivate_account') }} </button> <button v-if="canDeleteAccount" - class="button-default dropdown-item" + class="menu-item dropdown-item menu-item" @click="deleteUserDialog(true)" > {{ $t('user_card.admin_menu.delete_account') }} @@ -50,74 +50,74 @@ /> <span v-if="canUseTagPolicy"> <button - class="button-default dropdown-item" + class="menu-item dropdown-item menu-item" @click="toggleTag(tags.FORCE_NSFW)" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': hasTag(tags.FORCE_NSFW) }" /> {{ $t('user_card.admin_menu.force_nsfw') }} </button> <button - class="button-default dropdown-item" + class="menu-item dropdown-item menu-item" @click="toggleTag(tags.STRIP_MEDIA)" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': hasTag(tags.STRIP_MEDIA) }" /> {{ $t('user_card.admin_menu.strip_media') }} </button> <button - class="button-default dropdown-item" + class="menu-item dropdown-item menu-item" @click="toggleTag(tags.FORCE_UNLISTED)" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': hasTag(tags.FORCE_UNLISTED) }" /> {{ $t('user_card.admin_menu.force_unlisted') }} </button> <button - class="button-default dropdown-item" + class="menu-item dropdown-item menu-item" @click="toggleTag(tags.SANDBOX)" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': hasTag(tags.SANDBOX) }" /> {{ $t('user_card.admin_menu.sandbox') }} </button> <button v-if="user.is_local" - class="button-default dropdown-item" + class="menu-item dropdown-item menu-item" @click="toggleTag(tags.DISABLE_REMOTE_SUBSCRIPTION)" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': hasTag(tags.DISABLE_REMOTE_SUBSCRIPTION) }" /> {{ $t('user_card.admin_menu.disable_remote_subscription') }} </button> <button v-if="user.is_local" - class="button-default dropdown-item" + class="menu-item dropdown-item menu-item" @click="toggleTag(tags.DISABLE_ANY_SUBSCRIPTION)" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': hasTag(tags.DISABLE_ANY_SUBSCRIPTION) }" /> {{ $t('user_card.admin_menu.disable_any_subscription') }} </button> <button v-if="user.is_local" - class="button-default dropdown-item" + class="menu-item dropdown-item menu-item" @click="toggleTag(tags.QUARANTINE)" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': hasTag(tags.QUARANTINE) }" /> {{ $t('user_card.admin_menu.quarantine') }} @@ -166,8 +166,6 @@ <script src="./moderation_tools.js"></script> <style lang="scss"> -@import "../../variables"; - .moderation-tools-popover { height: 100%; diff --git a/src/components/mrf_transparency_panel/mrf_transparency_panel.vue b/src/components/mrf_transparency_panel/mrf_transparency_panel.vue index 97af4787..6dc86738 100644 --- a/src/components/mrf_transparency_panel/mrf_transparency_panel.vue +++ b/src/components/mrf_transparency_panel/mrf_transparency_panel.vue @@ -227,6 +227,5 @@ <script src="./mrf_transparency_panel.js"></script> <style lang="scss"> -@import "../../variables"; @import "./mrf_transparency_panel"; </style> diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue index 1a826cc4..bf608936 100644 --- a/src/components/nav_panel/nav_panel.vue +++ b/src/components/nav_panel/nav_panel.vue @@ -37,7 +37,8 @@ </NavigationEntry> <div v-show="showTimelines" - class="timelines-background" + class="timelines-background menu-item-collapsible" + :class="{ '-expanded': showTimelines }" > <div class="timelines"> <NavigationEntry @@ -57,12 +58,11 @@ > <router-link :title="$t('lists.manage_lists')" - class="extra-button" + class="button-unstyled extra-button" :to="{ name: 'lists' }" @click.stop > <FAIcon - class="extra-button" fixed-width icon="wrench" /> @@ -75,7 +75,8 @@ </NavigationEntry> <div v-show="showLists" - class="timelines-background" + class="timelines-background menu-item-collapsible" + :class="{ '-expanded': showLists }" > <ListsMenuContent :show-pin="editMode || forceEditMode" @@ -102,12 +103,10 @@ <script src="./nav_panel.js"></script> <style lang="scss"> -@import "../../variables"; - .NavPanel { .panel { overflow: hidden; - box-shadow: var(--panelShadow); + box-shadow: var(--shadow); } ul { @@ -116,33 +115,6 @@ padding: 0; } - li { - position: relative; - border-bottom: 1px solid; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); - } - - > li { - &:first-child .menu-item { - border-top-right-radius: $fallback--panelRadius; - border-top-right-radius: var(--panelRadius, $fallback--panelRadius); - border-top-left-radius: $fallback--panelRadius; - border-top-left-radius: var(--panelRadius, $fallback--panelRadius); - } - - &:last-child .menu-item { - border-bottom-right-radius: $fallback--panelRadius; - border-bottom-right-radius: var(--panelRadius, $fallback--panelRadius); - border-bottom-left-radius: $fallback--panelRadius; - border-bottom-left-radius: var(--panelRadius, $fallback--panelRadius); - } - } - - li:last-child { - border: none; - } - .navigation-chevron { margin-left: 0.8em; margin-right: 0.8em; @@ -156,16 +128,6 @@ .timelines-background { padding: 0 0 0 0.6em; - background-color: $fallback--lightBg; - background-color: var(--selectedMenu, $fallback--lightBg); - border-bottom: 1px solid; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); - } - - .timelines { - background-color: $fallback--bg; - background-color: var(--bg, $fallback--bg); } .nav-panel-heading { diff --git a/src/components/navigation/navigation_entry.vue b/src/components/navigation/navigation_entry.vue index 411ca536..4ea54ee3 100644 --- a/src/components/navigation/navigation_entry.vue +++ b/src/components/navigation/navigation_entry.vue @@ -1,7 +1,6 @@ <template> <OptionalRouterLink v-slot="{ isActive, href, navigate } = {}" - ass="ass" :to="routeTo" > <li @@ -11,7 +10,7 @@ > <component :is="routeTo ? 'a' : 'button'" - class="main-link button-unstyled" + class="main-link" :href="href" @click="navigate" > @@ -35,7 +34,7 @@ <slot /> <div v-if="item.badgeGetter && getters[item.badgeGetter]" - class="badge badge-notification" + class="badge -notification" > {{ getters[item.badgeGetter] }} </div> @@ -63,73 +62,53 @@ <script src="./navigation_entry.js"></script> <style lang="scss"> -@import "../../variables"; +.NavigationEntry.menu-item { + --__line-height: 2.5em; + --__horizontal-gap: 0.5em; + --__vertical-gap: 0.4em; -.NavigationEntry { + padding: 0; display: flex; - box-sizing: border-box; align-items: baseline; - height: 3.5em; - line-height: 3.5em; - padding: 0 1em; - width: 100%; - color: $fallback--link; - color: var(--link, $fallback--link); - .timelines-chevron { - margin-right: 0; + &[aria-expanded] { + padding-right: var(--__horizontal-gap); } .main-link { + line-height: var(--__line-height); + box-sizing: border-box; flex: 1; + padding: var(--__vertical-gap) var(--__horizontal-gap); } .menu-icon { - margin-right: 0.8em; + line-height: var(--__line-height); + padding: 0; + width: var(--__line-height); + margin-right: var(--__horizontal-gap); + } + + .timelines-chevron { + line-height: var(--__line-height); + padding: 0; + width: var(--__line-height); + margin-right: 0; } .extra-button { - width: 3em; + line-height: var(--__line-height); + padding: 0; + width: var(--__line-height); text-align: center; &:last-child { - margin-right: -0.8em; - } - } - - &:hover { - background-color: $fallback--lightBg; - background-color: var(--selectedMenu, $fallback--lightBg); - color: $fallback--link; - color: var(--selectedMenuText, $fallback--link); - - --faint: var(--selectedMenuFaintText, $fallback--faint); - --faintLink: var(--selectedMenuFaintLink, $fallback--faint); - --lightText: var(--selectedMenuLightText, $fallback--lightText); - - .menu-icon { - --icon: var(--text, $fallback--icon); + margin-right: calc(-1 * var(--__horizontal-gap)); } } - &.-active { - font-weight: bolder; - background-color: $fallback--lightBg; - background-color: var(--selectedMenu, $fallback--lightBg); - color: $fallback--text; - color: var(--selectedMenuText, $fallback--text); - - --faint: var(--selectedMenuFaintText, $fallback--faint); - --faintLink: var(--selectedMenuFaintLink, $fallback--faint); - --lightText: var(--selectedMenuLightText, $fallback--lightText); - - .menu-icon { - --icon: var(--text, $fallback--icon); - } - - &:hover { - text-decoration: underline; - } + .badge { + margin: 0 var(--__horizontal-gap); } } </style> diff --git a/src/components/navigation/navigation_pins.vue b/src/components/navigation/navigation_pins.vue index 4fbb4f95..decd1c04 100644 --- a/src/components/navigation/navigation_pins.vue +++ b/src/components/navigation/navigation_pins.vue @@ -3,7 +3,8 @@ <router-link v-for="item in pinnedList" :key="item.name" - class="pinned-item" + class="button-unstyled pinned-item" + active-class="toggled" :to="getRouteTo(item)" :title="item.labelRaw || $t(item.label)" > @@ -18,7 +19,7 @@ >{{ item.iconLetter }}</span> <div v-if="item.badgeGetter && getters[item.badgeGetter]" - class="alert-dot" + class="badge -dot -notification" /> </router-link> </span> @@ -27,25 +28,12 @@ <script src="./navigation_pins.js"></script> <style lang="scss"> -@import "../../variables"; - .NavigationPins { display: flex; flex-wrap: wrap; overflow: hidden; height: 100%; - .alert-dot { - border-radius: 100%; - height: 0.5em; - width: 0.5em; - position: absolute; - right: calc(50% - 0.75em); - top: calc(50% - 0.5em); - background-color: $fallback--cRed; - background-color: var(--badgeNotification, $fallback--cRed); - } - .pinned-item { position: relative; flex: 1 0 3em; @@ -60,15 +48,9 @@ margin: 0; } - &.router-link-active { - color: $fallback--text; - color: var(--panelText, $fallback--text); + &.toggled { + margin-bottom: -4px; border-bottom: 4px solid; - - & .svg-inline--fa, - & .iconLetter { - color: inherit; - } } } } diff --git a/src/components/notification/notification.scss b/src/components/notification/notification.scss index 654aca3c..2dbced09 100644 --- a/src/components/notification/notification.scss +++ b/src/components/notification/notification.scss @@ -1,13 +1,15 @@ -@import "../../variables"; - // TODO Copypaste from Status, should unify it somehow .Notification { border-bottom: 1px solid; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); + border-color: var(--border); word-wrap: break-word; word-break: break-word; + &.Status { + /* stylelint-disable-next-line declaration-no-important */ + background-color: transparent !important; + } + --emoji-size: 14px; &:hover { @@ -71,28 +73,22 @@ } &.-type--repeat .type-icon { - color: $fallback--cGreen; - color: var(--cGreen, $fallback--cGreen); + color: var(--cGreen); } &.-type--follow .type-icon { - color: $fallback--cBlue; - color: var(--cBlue, $fallback--cBlue); + color: var(--cBlue); } &.-type--follow-request .type-icon { - color: $fallback--cBlue; - color: var(--cBlue, $fallback--cBlue); + color: var(--cBlue); } &.-type--like .type-icon { - color: orange; - color: $fallback--cOrange; - color: var(--cOrange, $fallback--cOrange); + color: var(--cOrange); } &.-type--move .type-icon { - color: $fallback--cBlue; - color: var(--cBlue, $fallback--cBlue); + color: var(--cBlue); } } diff --git a/src/components/notification/notification.style.js b/src/components/notification/notification.style.js new file mode 100644 index 00000000..c6d317d1 --- /dev/null +++ b/src/components/notification/notification.style.js @@ -0,0 +1,18 @@ +export default { + name: 'Notification', + selector: '.Notification', + validInnerComponents: [ + 'Text', + 'Link', + 'Icon', + 'Border', + 'Button', + 'ButtonUnstyled', + 'RichContent', + 'Input', + 'Avatar', + 'Attachment', + 'PollGraph' + ], + defaultRules: [] +} diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index a8eceab0..f84b75de 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -1,6 +1,6 @@ <template> <article - v-if="notification.type === 'mention'" + v-if="notification.type === 'mention' || notification.type === 'status'" > <Status class="Notification" @@ -155,7 +155,7 @@ <router-link v-if="notification.status" :to="{ name: 'conversation', params: { id: notification.status.id } }" - class="timeago-link faint-link" + class="timeago-link faint" > <Timeago :time="notification.created_at" @@ -247,7 +247,6 @@ /> <template v-else> <StatusContent - :class="{ faint: !statusExpanded }" :compact="!statusExpanded" :status="notification.status" /> diff --git a/src/components/notifications/notification_filters.vue b/src/components/notifications/notification_filters.vue index 1315b51a..497a5156 100644 --- a/src/components/notifications/notification_filters.vue +++ b/src/components/notifications/notification_filters.vue @@ -8,65 +8,74 @@ <template #content> <div class="dropdown-menu"> <button - class="button-default dropdown-item" + class="menu-item dropdown-item" @click="toggleNotificationFilter('likes')" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': filters.likes }" />{{ $t('settings.notification_visibility_likes') }} </button> <button - class="button-default dropdown-item" + class="menu-item dropdown-item" @click="toggleNotificationFilter('repeats')" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': filters.repeats }" />{{ $t('settings.notification_visibility_repeats') }} </button> <button - class="button-default dropdown-item" + class="menu-item dropdown-item" @click="toggleNotificationFilter('follows')" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': filters.follows }" />{{ $t('settings.notification_visibility_follows') }} </button> <button - class="button-default dropdown-item" + class="menu-item dropdown-item" @click="toggleNotificationFilter('mentions')" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': filters.mentions }" />{{ $t('settings.notification_visibility_mentions') }} </button> <button - class="button-default dropdown-item" + class="menu-item dropdown-item" + @click="toggleNotificationFilter('statuses')" + > + <span + class="input menu-checkbox" + :class="{ 'menu-checkbox-checked': filters.statuses }" + />{{ $t('settings.notification_visibility_statuses') }} + </button> + <button + class="menu-item dropdown-item" @click="toggleNotificationFilter('emojiReactions')" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': filters.emojiReactions }" />{{ $t('settings.notification_visibility_emoji_reactions') }} </button> <button - class="button-default dropdown-item" + class="menu-item dropdown-item" @click="toggleNotificationFilter('moves')" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': filters.moves }" />{{ $t('settings.notification_visibility_moves') }} </button> <button - class="button-default dropdown-item" + class="menu-item dropdown-item" @click="toggleNotificationFilter('polls')" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': filters.polls }" />{{ $t('settings.notification_visibility_polls') }} </button> diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index a9fa8455..85d3662e 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -33,7 +33,7 @@ const Notifications = { // Disables panel styles, unread mark, potentially other notification-related actions // meant for "Interactions" timeline minimalMode: Boolean, - // Custom filter mode, an array of strings, possible values 'mention', 'repeat', 'like', 'follow', used to override global filter for use in "Interactions" timeline + // Custom filter mode, an array of strings, possible values 'mention', 'status', 'repeat', 'like', 'follow', used to override global filter for use in "Interactions" timeline filterMode: Array, // Do not show extra notifications noExtra: { diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index 5749e430..cfc1f3d6 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -1,5 +1,3 @@ -@import "../../variables"; - .Notifications { &:not(.minimal) { // a bit of a hack to allow scrolling below notifications @@ -7,8 +5,7 @@ } .loadmore-error { - color: $fallback--text; - color: var(--text, $fallback--text); + color: var(--text); } .notification { @@ -25,7 +22,7 @@ &.unseen { .notification-overlay { - background-image: linear-gradient(135deg, var(--badgeNotification, $fallback--cRed) 4px, transparent 10px); + background-image: linear-gradient(135deg, var(--badgeNotification) 4px, transparent 10px); } } } @@ -35,6 +32,11 @@ .notification { box-sizing: border-box; + /* TODO cleanup this */ + .Status { + flex: 1; + } + &:hover .animated.Avatar { canvas { display: none; @@ -60,24 +62,17 @@ width: 32px; height: 32px; } - - .faint { - --link: var(--faintLink); - --text: var(--faint); - } } .follow-request-accept { &:hover { - color: $fallback--text; - color: var(--text, $fallback--text); + color: var(--text); } } .follow-request-reject { &:hover { - color: $fallback--cRed; - color: var(--cRed, $fallback--cRed); + color: var(--cRed); } } @@ -97,11 +92,6 @@ } } - /* TODO cleanup this */ - .Status { - flex: 1; - } - time { white-space: nowrap; } diff --git a/src/components/notifications/notifications.vue b/src/components/notifications/notifications.vue index a0025182..87e7b68d 100644 --- a/src/components/notifications/notifications.vue +++ b/src/components/notifications/notifications.vue @@ -18,7 +18,7 @@ {{ $t('notifications.notifications') }} <span v-if="unseenCountBadgeText" - class="badge badge-notification unseen-count" + class="badge -notification unseen-count" >{{ unseenCountBadgeText }}</span> </div> <div @@ -85,7 +85,7 @@ </div> <button v-else-if="!loading" - class="button-unstyled -link -fullwidth" + class="button-unstyled -link text-center" @click.prevent="fetchOlderNotifications()" > <div class="new-status-notification text-center"> diff --git a/src/components/opacity_input/opacity_input.vue b/src/components/opacity_input/opacity_input.vue index 15d08e04..a45bdd92 100644 --- a/src/components/opacity_input/opacity_input.vue +++ b/src/components/opacity_input/opacity_input.vue @@ -18,7 +18,7 @@ /> <input :id="name" - class="input-number" + class="input input-number" type="number" :value="modelValue || fallback" :disabled="!present || disabled" diff --git a/src/components/panel.style.js b/src/components/panel.style.js new file mode 100644 index 00000000..1bba4766 --- /dev/null +++ b/src/components/panel.style.js @@ -0,0 +1,51 @@ +export default { + name: 'Panel', + selector: '.panel', + validInnerComponents: [ + 'Text', + 'Link', + 'Icon', + 'Border', + 'Button', + 'ButtonUnstyled', + 'Input', + 'PanelHeader', + 'MenuItem', + 'Post', + 'Notification', + 'Alert', + 'UserCard', + 'Chat', + 'Attachment', + 'Tab', + 'ListItem' + ], + validInnerComponentsLite: [ + 'Text', + 'Link', + 'Icon', + 'Border', + 'Button', + 'Input', + 'PanelHeader', + 'Alert' + ], + defaultRules: [ + { + directives: { + backgroundNoCssColor: 'yes', + background: '--bg', + roundness: 3, + blur: '5px', + shadow: [{ + x: 1, + y: 1, + blur: 4, + spread: 0, + color: '#000000', + alpha: 0.6 + }] + } + } + ] +} diff --git a/src/components/panel_header.style.js b/src/components/panel_header.style.js new file mode 100644 index 00000000..010e42cd --- /dev/null +++ b/src/components/panel_header.style.js @@ -0,0 +1,23 @@ +export default { + name: 'PanelHeader', + selector: '.panel-heading', + validInnerComponents: [ + 'Text', + 'Link', + 'Icon', + 'Button', + 'ButtonUnstyled', + 'Badge', + 'Alert', + 'Avatar' + ], + defaultRules: [ + { + component: 'PanelHeader', + directives: { + backgroundNoCssColor: 'yes', + background: '--fg' + } + } + ] +} diff --git a/src/components/panel_loading/panel_loading.vue b/src/components/panel_loading/panel_loading.vue index 17458e49..7a832122 100644 --- a/src/components/panel_loading/panel_loading.vue +++ b/src/components/panel_loading/panel_loading.vue @@ -23,22 +23,18 @@ export default {} </script> <style lang="scss"> -@import "src/variables"; - .panel-loading { display: flex; height: 100%; align-items: center; justify-content: center; font-size: 2em; - color: $fallback--text; - color: var(--text, $fallback--text); + color: var(--text); .loading-text svg { line-height: 0; vertical-align: middle; - color: $fallback--text; - color: var(--text, $fallback--text); + color: var(--text); } } </style> diff --git a/src/components/password_reset/password_reset.vue b/src/components/password_reset/password_reset.vue index d6e10250..037b859e 100644 --- a/src/components/password_reset/password_reset.vue +++ b/src/components/password_reset/password_reset.vue @@ -30,7 +30,7 @@ <div v-else> <p v-if="passwordResetRequested" - class="password-reset-required error" + class="alert password-reset-required error" > {{ $t('password_reset.password_reset_required') }} </p> @@ -43,7 +43,7 @@ v-model="user.email" :disabled="isPending" :placeholder="$t('password_reset.placeholder')" - class="form-control" + class="input form-control" type="input" > </div> @@ -77,8 +77,6 @@ <script src="./password_reset.js"></script> <style lang="scss"> -@import "../../variables"; - .password-reset-form { display: flex; flex-direction: column; @@ -117,11 +115,6 @@ margin: 0.3em 0 1em; } - .password-reset-required { - background-color: var(--alertError, $fallback--alertError); - padding: 10px 0; - } - .notice-dismissible { padding-right: 2rem; } diff --git a/src/components/poll/poll.js b/src/components/poll/poll.js index f6001f56..9ce0e29e 100644 --- a/src/components/poll/poll.js +++ b/src/components/poll/poll.js @@ -38,7 +38,7 @@ export default { return (this.poll && this.poll.options) || [] }, expiresAt () { - return (this.poll && this.poll.expires_at) || 0 + return (this.poll && this.poll.expires_at) || null }, expired () { return (this.poll && this.poll.expired) || false diff --git a/src/components/poll/poll.vue b/src/components/poll/poll.vue index b3a74c49..e12f3e61 100644 --- a/src/components/poll/poll.vue +++ b/src/components/poll/poll.vue @@ -37,12 +37,14 @@ :role="poll.multiple ? 'checkbox' : 'radio'" :aria-labelledby="`option-vote-${randomSeed}-${index}`" :aria-checked="choices[index]" + class="input unstyled" @click="activateOption(index)" > + <!-- TODO: USE CHECKBOX --> <input v-if="poll.multiple" type="checkbox" - class="poll-checkbox" + class="input -checkbox poll-checkbox" :disabled="loading" :value="index" > @@ -51,6 +53,7 @@ type="radio" :disabled="loading" :value="index" + class="input -radio" > <label class="option-vote"> <RichContent @@ -73,15 +76,25 @@ > {{ $t('polls.vote') }} </button> + <span + v-if="poll.pleroma?.non_anonymous" + :title="$t('polls.non_anonymous_title')" + > + {{ $t('polls.non_anonymous') }} + · + </span> <div class="total"> <template v-if="typeof poll.voters_count === 'number'"> - {{ $tc("polls.people_voted_count", poll.voters_count, { count: poll.voters_count }) }} · + {{ $tc("polls.people_voted_count", poll.voters_count, { count: poll.voters_count }) }} </template> <template v-else> - {{ $tc("polls.votes_count", poll.votes_count, { count: poll.votes_count }) }} · + {{ $tc("polls.votes_count", poll.votes_count, { count: poll.votes_count }) }} </template> + <span v-if="expiresAt !== null"> + · + </span> </div> - <span> + <span v-if="expiresAt !== null"> <i18n-t scope="global" :keypath="expired ? 'polls.expired' : 'polls.expires_in'" @@ -100,8 +113,6 @@ <script src="./poll.js"></script> <style lang="scss"> -@import "../../variables"; - .poll { .votes { display: flex; @@ -111,6 +122,10 @@ .poll-option { margin: 0.75em 0.5em; + + .input { + line-height: inherit; + } } .option-result { @@ -118,8 +133,7 @@ display: flex; flex-direction: row; position: relative; - color: $fallback--lightText; - color: var(--lightText, $fallback--lightText); + color: var(--textLight); } .option-result-label { @@ -138,12 +152,7 @@ .result-fill { height: 100%; position: absolute; - color: $fallback--text; - color: var(--pollText, $fallback--text); - background-color: $fallback--lightBg; - background-color: var(--poll, $fallback--lightBg); - border-radius: $fallback--panelRadius; - border-radius: var(--panelRadius, $fallback--panelRadius); + border-radius: var(--roundness); top: 0; left: 0; transition: width 0.5s; diff --git a/src/components/poll/poll_form.vue b/src/components/poll/poll_form.vue index 09d411ca..4eb9d594 100644 --- a/src/components/poll/poll_form.vue +++ b/src/components/poll/poll_form.vue @@ -13,7 +13,7 @@ :id="`poll-${index}`" v-model="options[index]" size="1" - class="poll-option-input" + class="input poll-option-input" type="text" :placeholder="$t('polls.option')" :maxlength="maxLength" @@ -67,7 +67,7 @@ <input v-model="expiryAmount" type="number" - class="expiry-amount hide-number-spinner" + class="input expiry-amount hide-number-spinner" :min="minExpirationInCurrentUnit" :max="maxExpirationInCurrentUnit" @change="expiryAmountChange" @@ -95,8 +95,6 @@ <script src="./poll_form.js"></script> <style lang="scss"> -@import "../../variables"; - .poll-form { display: flex; flex-direction: column; diff --git a/src/components/poll/poll_graph.style.js b/src/components/poll/poll_graph.style.js new file mode 100644 index 00000000..247a266a --- /dev/null +++ b/src/components/poll/poll_graph.style.js @@ -0,0 +1,12 @@ +export default { + name: 'PollGraph', + selector: '.result-fill', + defaultRules: [ + { + directives: { + background: '--accent', + opacity: 0.5 + } + } + ] +} diff --git a/src/components/popover.style.js b/src/components/popover.style.js new file mode 100644 index 00000000..0197271b --- /dev/null +++ b/src/components/popover.style.js @@ -0,0 +1,36 @@ +export default { + name: 'Popover', + selector: '.popover', + lazy: true, + variants: { + modal: '.modal' + }, + validInnerComponents: [ + 'Text', + 'Link', + 'Icon', + 'Border', + 'Button', + 'ButtonUnstyled', + 'Input', + 'MenuItem', + 'Post', + 'UserCard' + ], + defaultRules: [ + { + directives: { + background: '--bg', + blur: '10px', + shadow: [{ + x: 2, + y: 2, + blur: 3, + spread: 0, + color: '#000000', + alpha: 0.5 + }] + } + } + ] +} diff --git a/src/components/popover/popover.vue b/src/components/popover/popover.vue index 1a4bffd9..0c5b372e 100644 --- a/src/components/popover/popover.vue +++ b/src/components/popover/popover.vue @@ -42,8 +42,6 @@ <script src="./popover.js" /> <style lang="scss"> -@import "../../variables"; - .popover-trigger-button { display: inline-block; } @@ -53,81 +51,54 @@ position: fixed; min-width: 0; max-width: calc(100vw - 20px); - box-shadow: 2px 2px 3px rgb(0 0 0 / 50%); - box-shadow: var(--popupShadow); + box-shadow: var(--shadow); } .popover-default { &::after { content: ""; position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - z-index: 3; - box-shadow: 1px 1px 4px rgb(0 0 0 / 60%); - box-shadow: var(--panelShadow); + top: -1px; + bottom: -1px; + left: -1px; + right: -1px; + z-index: -1px; + box-shadow: var(--shadow); pointer-events: none; } - border-radius: $fallback--btnRadius; - border-radius: var(--btnRadius, $fallback--btnRadius); - background-color: $fallback--bg; - background-color: var(--popover, $fallback--bg); - color: $fallback--text; - color: var(--popoverText, $fallback--text); - - --faint: var(--popoverFaintText, $fallback--faint); - --faintLink: var(--popoverFaintLink, $fallback--faint); - --lightText: var(--popoverLightText, $fallback--lightText); - --postLink: var(--popoverPostLink, $fallback--link); - --postFaintLink: var(--popoverPostFaintLink, $fallback--link); - --icon: var(--popoverIcon, $fallback--icon); + border-radius: var(--roundness); + border-color: var(--border); + border-style: solid; + border-width: 1px; + background-color: var(--background); } .dropdown-menu { display: block; - padding: 0.5rem 0; + padding: 0; font-size: 1em; text-align: left; list-style: none; max-width: 100vw; z-index: var(--ZI_popover_override, var(--ZI_popovers)); white-space: nowrap; + background-color: var(--background); .dropdown-divider { height: 0; margin: 0.5rem 0; overflow: hidden; - border-top: 1px solid $fallback--border; - border-top: 1px solid var(--border, $fallback--border); + border-top: 1px solid var(--border); } .dropdown-item { - line-height: 21px; - overflow: hidden; - display: block; - padding: 0.5em 0.75em; - clear: both; - font-weight: 400; - text-align: inherit; - white-space: nowrap; border: none; - border-radius: 0; - background-color: transparent; - box-shadow: none; - width: 100%; - height: 100%; - box-sizing: border-box; - - --btnText: var(--popoverText, $fallback--text); &-icon { svg { - width: 22px; - margin-right: 0.75rem; - color: var(--menuPopoverIcon, $fallback--icon); + width: var(--__line-height); + margin-right: var(--__horizontal-gap); } } @@ -138,40 +109,18 @@ } } - &:active, - &:hover { - background-color: $fallback--lightBg; - background-color: var(--selectedMenuPopover, $fallback--lightBg); - box-shadow: none; - - --btnText: var(--selectedMenuPopoverText, $fallback--link); - --faint: var(--selectedMenuPopoverFaintText, $fallback--faint); - --faintLink: var(--selectedMenuPopoverFaintLink, $fallback--faint); - --lightText: var(--selectedMenuPopoverLightText, $fallback--lightText); - --icon: var(--selectedMenuPopoverIcon, $fallback--icon); - - svg { - color: var(--selectedMenuPopoverIcon, $fallback--icon); - - --icon: var(--selectedMenuPopoverIcon, $fallback--icon); - } - } - .menu-checkbox { display: inline-block; vertical-align: middle; - min-width: 22px; - max-width: 22px; - min-height: 22px; - max-height: 22px; - line-height: 22px; + min-width: calc(var(--__line-height) + 1px); + max-width: calc(var(--__line-height) + 1px); + min-height: calc(var(--__line-height) + 1px); + max-height: calc(var(--__line-height) + 1px); + line-height: var(--__line-height); text-align: center; border-radius: 0; - background-color: $fallback--fg; - background-color: var(--input, $fallback--fg); - box-shadow: 0 0 2px black inset; - box-shadow: var(--inputShadow); - margin-right: 0.75em; + box-shadow: var(--shadow); + margin-right: var(--__horizontal-gap); &.menu-checkbox-checked::after { font-size: 1.25em; @@ -188,30 +137,5 @@ } } } - - .button-default.dropdown-item { - &, - i[class*="icon-"] { - color: $fallback--text; - color: var(--btnText, $fallback--text); - } - - &:active { - background-color: $fallback--lightBg; - background-color: var(--selectedMenuPopover, $fallback--lightBg); - color: $fallback--link; - color: var(--selectedMenuPopoverText, $fallback--link); - } - - &:disabled { - color: $fallback--text; - color: var(--btnDisabledText, $fallback--text); - } - - &.toggled { - color: $fallback--text; - color: var(--btnToggledText, $fallback--text); - } - } } </style> diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 5564b118..563dfb96 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -87,7 +87,8 @@ const PostStatusForm = { 'fileLimit', 'submitOnEnter', 'emojiPickerPlacement', - 'optimisticPosting' + 'optimisticPosting', + 'profileMention' ], emits: [ 'posted', @@ -125,7 +126,7 @@ const PostStatusForm = { const { scopeCopy } = this.$store.getters.mergedConfig - if (this.replyTo) { + if (this.replyTo || this.profileMention) { const currentUser = this.$store.state.users.currentUser statusText = buildMentionsString({ user: this.repliedUser, attentions: this.attentions }, currentUser) } diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 9b108a5a..cf411ff1 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -161,7 +161,7 @@ v-model="newStatus.spoilerText" enable-emoji-picker :suggest="emojiSuggestor" - class="form-control" + class="input form-control" > <template #default="inputProps"> <input @@ -171,7 +171,7 @@ :disabled="posting && !optimisticPosting" v-bind="propsToNative(inputProps)" size="1" - class="form-post-subject" + class="input form-post-subject" > </template> </EmojiInput> @@ -180,7 +180,7 @@ v-model="newStatus.status" :suggest="emojiUserSuggestor" :placement="emojiPickerPlacement" - class="form-control main-input" + class="input form-control main-input" enable-emoji-picker hide-emoji-button :newline-on-ctrl-enter="submitOnEnter" @@ -198,7 +198,7 @@ rows="1" cols="1" :disabled="posting && !optimisticPosting" - class="form-post-body" + class="input form-post-body" :class="{ 'scrollable-form': !!maxHeight }" v-bind="propsToNative(inputProps)" @keydown.exact.enter="submitOnEnter && postStatus($event, newStatus)" @@ -237,7 +237,7 @@ <Select id="post-content-type" v-model="newStatus.contentType" - class="form-control" + class="input form-control" :attrs="{ 'aria-label': $t('post_status.content_type_selection') }" > <option @@ -375,8 +375,6 @@ <script src="./post_status_form.js"></script> <style lang="scss"> -@import "../../variables"; - .post-status-form { position: relative; @@ -437,15 +435,12 @@ .preview-error { font-style: italic; - color: $fallback--faint; - color: var(--faint, $fallback--faint); + color: var(--textFaint); } .preview-status { - border: 1px solid $fallback--border; - border: 1px solid var(--border, $fallback--border); - border-radius: $fallback--tooltipRadius; - border-radius: var(--tooltipRadius, $fallback--tooltipRadius); + border: 1px solid var(--border); + border-radius: var(--roundness); padding: 0.5em; margin: 0; } @@ -456,8 +451,7 @@ .text-format { .only-format { - color: $fallback--faint; - color: var(--faint, $fallback--faint); + color: var(--textFaint); } } @@ -503,31 +497,6 @@ padding: 0 0.1em; display: flex; align-items: center; - - &.selected, - &:hover { - // needs to be specific to override icon default color - svg, - i, - label { - color: $fallback--lightText; - color: var(--lightText, $fallback--lightText); - } - } - - &.disabled { - svg, - i { - cursor: not-allowed; - color: $fallback--icon; - color: var(--btnDisabledText, $fallback--icon); - - &:hover { - color: $fallback--icon; - color: var(--btnDisabledText, $fallback--icon); - } - } - } } .error { @@ -580,7 +549,7 @@ line-height: 1.85; } - .form-post-body { + .input.form-post-body { // TODO: make a resizable textarea component? box-sizing: content-box; // needed for easier computation of dynamic size overflow: hidden; @@ -591,6 +560,7 @@ height: calc(var(--post-line-height) * 1em); min-height: calc(var(--post-line-height) * 1em); resize: none; + background: transparent; &.scrollable-form { overflow-y: auto; @@ -609,8 +579,7 @@ margin: 0 0.5em; &.error { - color: $fallback--cRed; - color: var(--cRed, $fallback--cRed); + color: var(--cRed); } } @@ -633,14 +602,10 @@ align-items: center; justify-content: center; opacity: 0.6; - color: $fallback--text; - color: var(--text, $fallback--text); - background-color: $fallback--bg; - background-color: var(--bg, $fallback--bg); - border-radius: $fallback--tooltipRadius; - border-radius: var(--tooltipRadius, $fallback--tooltipRadius); - border: 2px dashed $fallback--text; - border: 2px dashed var(--text, $fallback--text); + color: var(--text); + background-color: var(--bg); + border-radius: var(--roundness); + border: 2px dashed var(--text); } } </style> diff --git a/src/components/quick_filter_settings/quick_filter_settings.js b/src/components/quick_filter_settings/quick_filter_settings.js index e67e3a4b..0d667df5 100644 --- a/src/components/quick_filter_settings/quick_filter_settings.js +++ b/src/components/quick_filter_settings/quick_filter_settings.js @@ -63,6 +63,13 @@ const QuickFilterSettings = { const value = !this.muteBotStatuses this.$store.dispatch('setOption', { name: 'muteBotStatuses', value }) } + }, + muteSensitiveStatuses: { + get () { return this.mergedConfig.muteSensitiveStatuses }, + set () { + const value = !this.muteSensitiveStatuses + this.$store.dispatch('setOption', { name: 'muteSensitiveStatuses', value }) + } } } } diff --git a/src/components/quick_filter_settings/quick_filter_settings.vue b/src/components/quick_filter_settings/quick_filter_settings.vue index b81215a1..adb96916 100644 --- a/src/components/quick_filter_settings/quick_filter_settings.vue +++ b/src/components/quick_filter_settings/quick_filter_settings.vue @@ -16,39 +16,39 @@ > <button v-if="!conversation" - class="button-default dropdown-item" + class="menu-item dropdown-item" :aria-checked="replyVisibilityAll" role="menuitemradio" @click="replyVisibilityAll = true" > <span - class="menu-checkbox -radio" + class="input menu-checkbox -radio" :class="{ 'menu-checkbox-checked': replyVisibilityAll }" :aria-hidden="true" />{{ $t('settings.reply_visibility_all') }} </button> <button v-if="!conversation" - class="button-default dropdown-item" + class="menu-item dropdown-item" :aria-checked="replyVisibilityFollowing" role="menuitemradio" @click="replyVisibilityFollowing = true" > <span - class="menu-checkbox -radio" + class="input menu-checkbox -radio" :class="{ 'menu-checkbox-checked': replyVisibilityFollowing }" :aria-hidden="true" />{{ $t('settings.reply_visibility_following_short') }} </button> <button v-if="!conversation" - class="button-default dropdown-item" + class="menu-item dropdown-item" :aria-checked="replyVisibilitySelf" role="menuitemradio" @click="replyVisibilitySelf = true" > <span - class="menu-checkbox -radio" + class="input menu-checkbox -radio" :class="{ 'menu-checkbox-checked': replyVisibilitySelf }" :aria-hidden="true" />{{ $t('settings.reply_visibility_self_short') }} @@ -60,43 +60,55 @@ /> </div> <button - class="button-default dropdown-item" + class="menu-item dropdown-item" role="menuitemcheckbox" :aria-checked="muteBotStatuses" @click="muteBotStatuses = !muteBotStatuses" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': muteBotStatuses }" :aria-hidden="true" />{{ $t('settings.mute_bot_posts') }} </button> <button - class="button-default dropdown-item" + class="menu-item dropdown-item" + role="menuitemcheckbox" + :aria-checked="muteSensitiveStatuses" + @click="muteSensitiveStatuses = !muteSensitiveStatuses" + > + <span + class="input menu-checkbox" + :class="{ 'menu-checkbox-checked': muteSensitiveStatuses }" + :aria-hidden="true" + />{{ $t('settings.mute_sensitive_posts') }} + </button> + <button + class="menu-item dropdown-item" role="menuitemcheckbox" :aria-checked="hideMedia" @click="hideMedia = !hideMedia" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': hideMedia }" :aria-hidden="true" />{{ $t('settings.hide_media_previews') }} </button> <button - class="button-default dropdown-item" + class="menu-item dropdown-item" role="menuitemcheckbox" :aria-checked="hideMutedPosts" @click="hideMutedPosts = !hideMutedPosts" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': hideMutedPosts }" :aria-hidden="true" />{{ $t('settings.hide_all_muted_posts') }} </button> <button - class="button-default dropdown-item dropdown-item-icon" + class="menu-item dropdown-item dropdown-item-icon" role="menuitem" @click="openTab('filtering')" > diff --git a/src/components/quick_view_settings/quick_view_settings.js b/src/components/quick_view_settings/quick_view_settings.js index 67aa6713..e35fd579 100644 --- a/src/components/quick_view_settings/quick_view_settings.js +++ b/src/components/quick_view_settings/quick_view_settings.js @@ -61,6 +61,13 @@ const QuickViewSettings = { const value = !this.muteBotStatuses this.$store.dispatch('setOption', { name: 'muteBotStatuses', value }) } + }, + muteSensitiveStatuses: { + get () { return this.mergedConfig.muteSensitiveStatuses }, + set () { + const value = !this.muteSensitiveStatuses + this.$store.dispatch('setOption', { name: 'muteSensitiveStatuses', value }) + } } } } diff --git a/src/components/quick_view_settings/quick_view_settings.vue b/src/components/quick_view_settings/quick_view_settings.vue index 9f5cdabc..e93dd8e0 100644 --- a/src/components/quick_view_settings/quick_view_settings.vue +++ b/src/components/quick_view_settings/quick_view_settings.vue @@ -12,13 +12,13 @@ > <div role="group"> <button - class="button-default dropdown-item" + class="menu-item dropdown-item" :aria-checked="conversationDisplay === 'tree'" role="menuitemradio" @click="conversationDisplay = 'tree'" > <span - class="menu-checkbox -radio" + class="input menu-checkbox -radio" :aria-hidden="true" :class="{ 'menu-checkbox-checked': conversationDisplay === 'tree' }" /><FAIcon @@ -27,13 +27,13 @@ /> {{ $t('settings.conversation_display_tree_quick') }} </button> <button - class="button-default dropdown-item" + class="menu-item dropdown-item" :aria-checked="conversationDisplay === 'linear'" role="menuitemradio" @click="conversationDisplay = 'linear'" > <span - class="menu-checkbox -radio" + class="input menu-checkbox -radio" :class="{ 'menu-checkbox-checked': conversationDisplay === 'linear' }" :aria-hidden="true" /><FAIcon @@ -47,45 +47,45 @@ class="dropdown-divider" /> <button - class="button-default dropdown-item" + class="menu-item dropdown-item" role="menuitemcheckbox" :aria-checked="showUserAvatars" @click="showUserAvatars = !showUserAvatars" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': showUserAvatars }" :aria-hidden="true" />{{ $t('settings.mention_link_show_avatar_quick') }} </button> <button v-if="!conversation" - class="button-default dropdown-item" + class="menu-item dropdown-item" role="menuitemcheckbox" :aria-checked="autoUpdate" @click="autoUpdate = !autoUpdate" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': autoUpdate }" :aria-hidden="true" />{{ $t('settings.auto_update') }} </button> <button v-if="!conversation" - class="button-default dropdown-item" + class="menu-item dropdown-item" role="menuitemcheckbox" :aria-checked="collapseWithSubjects" @click="collapseWithSubjects = !collapseWithSubjects" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': collapseWithSubjects }" :aria-hidden="true" />{{ $t('settings.collapse_subject') }} </button> <button - class="button-default dropdown-item dropdown-item-icon" + class="menu-item dropdown-item dropdown-item-icon" role="menuitem" @click="openTab('general')" > diff --git a/src/components/quotes_timeline/quotes_timeline.js b/src/components/quotes_timeline/quotes_timeline.js new file mode 100644 index 00000000..a5f42da5 --- /dev/null +++ b/src/components/quotes_timeline/quotes_timeline.js @@ -0,0 +1,26 @@ +import Timeline from '../timeline/timeline.vue' + +const QuotesTimeline = { + created () { + this.$store.commit('clearTimeline', { timeline: 'quotes' }) + this.$store.dispatch('startFetchingTimeline', { timeline: 'quotes', statusId: this.statusId }) + }, + components: { + Timeline + }, + computed: { + statusId () { return this.$route.params.id }, + timeline () { return this.$store.state.statuses.timelines.quotes } + }, + watch: { + statusId () { + this.$store.commit('clearTimeline', { timeline: 'quotes' }) + this.$store.dispatch('startFetchingTimeline', { timeline: 'quotes', statusId: this.statusId }) + } + }, + unmounted () { + this.$store.dispatch('stopFetchingTimeline', 'quotes') + } +} + +export default QuotesTimeline diff --git a/src/components/quotes_timeline/quotes_timeline.vue b/src/components/quotes_timeline/quotes_timeline.vue new file mode 100644 index 00000000..835abd12 --- /dev/null +++ b/src/components/quotes_timeline/quotes_timeline.vue @@ -0,0 +1,10 @@ +<template> + <Timeline + :title="$t('nav.quotes')" + :timeline="timeline" + :timeline-name="'quotes'" + :status-id="statusId" + /> +</template> + +<script src='./quotes_timeline.js'></script> diff --git a/src/components/range_input/range_input.vue b/src/components/range_input/range_input.vue index 1e720105..2f8645c0 100644 --- a/src/components/range_input/range_input.vue +++ b/src/components/range_input/range_input.vue @@ -14,7 +14,7 @@ v-if="typeof fallback !== 'undefined'" :id="name + '-o'" :aria-labelledby="name + '-label'" - class="opt visible-for-screenreader-only" + class="input -checkbox opt visible-for-screenreader-only" type="checkbox" :checked="present" @change="$emit('update:modelValue', !present ? fallback : undefined)" @@ -27,7 +27,7 @@ /> <input :id="name" - class="input-number" + class="input input-number" type="range" :value="modelValue || fallback" :disabled="!present || disabled" @@ -38,7 +38,7 @@ > <input :id="name + '-numeric'" - class="input-number" + class="input input-number" type="number" :aria-labelledby="name + '-label'" :value="modelValue || fallback" diff --git a/src/components/react_button/react_button.vue b/src/components/react_button/react_button.vue index 1b0674e6..e648e7e3 100644 --- a/src/components/react_button/react_button.vue +++ b/src/components/react_button/react_button.vue @@ -41,7 +41,6 @@ <script src="./react_button.js"></script> <style lang="scss"> -@import "../../variables"; @import "../../mixins"; .ReactButton { @@ -58,7 +57,7 @@ height: 1px; width: 100%; margin: 0.5em; - background-color: var(--border, $fallback--border); + background-color: var(--border); } .reaction-picker { @@ -99,11 +98,6 @@ padding: 10px; margin: -10px; - &:hover .svg-inline--fa { - color: $fallback--text; - color: var(--text, $fallback--text); - } - @include unfocused-style { .focus-marker { visibility: hidden; diff --git a/src/components/registration/registration.vue b/src/components/registration/registration.vue index 5c913f94..a2008d87 100644 --- a/src/components/registration/registration.vue +++ b/src/components/registration/registration.vue @@ -25,7 +25,7 @@ id="sign-up-username" v-model.trim="v$.user.username.$model" :disabled="isPending" - class="form-control" + class="input form-control" :aria-required="true" :placeholder="$t('registration.username_placeholder')" > @@ -53,7 +53,7 @@ id="sign-up-fullname" v-model.trim="v$.user.fullname.$model" :disabled="isPending" - class="form-control" + class="input form-control" :aria-required="true" :placeholder="$t('registration.fullname_placeholder')" > @@ -81,7 +81,7 @@ id="email" v-model="v$.user.email.$model" :disabled="isPending" - class="form-control" + class="input form-control" type="email" :aria-required="accountActivationRequired" > @@ -106,7 +106,7 @@ id="bio" v-model="user.bio" :disabled="isPending" - class="form-control" + class="input form-control" :placeholder="bioPlaceholder" /> </div> @@ -123,7 +123,7 @@ id="sign-up-password" v-model="user.password" :disabled="isPending" - class="form-control" + class="input form-control" type="password" :aria-required="true" > @@ -151,7 +151,7 @@ id="sign-up-password-confirmation" v-model="user.confirm" :disabled="isPending" - class="form-control" + class="input form-control" type="password" :aria-required="true" > @@ -184,7 +184,7 @@ id="sign-up-birthday" v-model="user.birthday" :disabled="isPending" - class="form-control" + class="input form-control" type="date" :max="birthdayRequired ? birthdayMinAttr : undefined" :aria-required="birthdayRequired" @@ -229,7 +229,7 @@ id="reason" v-model="user.reason" :disabled="isPending" - class="form-control" + class="input form-control" :placeholder="reasonPlaceholder" /> </div> @@ -256,7 +256,7 @@ id="captcha-answer" v-model="captcha.solution" :disabled="isPending" - class="form-control" + class="input form-control" type="text" autocomplete="off" autocorrect="off" @@ -275,7 +275,7 @@ id="token" v-model="token" disabled="true" - class="form-control" + class="input form-control" type="text" > </div> @@ -320,9 +320,6 @@ <script src="./registration.js"></script> <style lang="scss"> -@import "../../variables"; -$validations-cRed: #f04124; - .registration-form { display: flex; flex-direction: column; @@ -369,8 +366,7 @@ $validations-cRed: #f04124; } .form-group--error .form--label { - color: $validations-cRed; - color: var(--cRed, $validations-cRed); + color: var(--cRed); } .form-error { diff --git a/src/components/reply_button/reply_button.vue b/src/components/reply_button/reply_button.vue index 60a40a08..87c06e39 100644 --- a/src/components/reply_button/reply_button.vue +++ b/src/components/reply_button/reply_button.vue @@ -59,7 +59,6 @@ <script src="./reply_button.js"></script> <style lang="scss"> -@import "../../variables"; @import "../../mixins"; .ReplyButton { @@ -78,8 +77,7 @@ .interactive { &:hover .svg-inline--fa, &.-active .svg-inline--fa { - color: $fallback--cBlue; - color: var(--cBlue, $fallback--cBlue); + color: var(--cBlue); } @include unfocused-style { diff --git a/src/components/report/report.scss b/src/components/report/report.scss index 9762400b..4249b850 100644 --- a/src/components/report/report.scss +++ b/src/components/report/report.scss @@ -1,5 +1,3 @@ -@import "../../variables"; - .Report { .report-content { margin: 0.5em 0 1em; @@ -10,12 +8,8 @@ } .reported-status { - border: 1px solid $fallback--faint; - border-color: var(--faint, $fallback--faint); - border-radius: $fallback--inputRadius; - border-radius: var(--inputRadius, $fallback--inputRadius); - color: $fallback--text; - color: var(--text, $fallback--text); + border: 1px solid var(--border); + border-radius: var(--roundness); display: block; padding: 0.5em; margin: 0.5em 0; diff --git a/src/components/report/report.vue b/src/components/report/report.vue index 1f19cc25..32aaaffd 100644 --- a/src/components/report/report.vue +++ b/src/components/report/report.vue @@ -17,7 +17,7 @@ <Select :id="report-state" v-model="state" - class="form-control" + class="input form-control" > <option v-for="state in ['open', 'closed', 'resolved']" diff --git a/src/components/retweet_button/retweet_button.vue b/src/components/retweet_button/retweet_button.vue index e1b6b153..adda9a42 100644 --- a/src/components/retweet_button/retweet_button.vue +++ b/src/components/retweet_button/retweet_button.vue @@ -84,7 +84,6 @@ <script src="./retweet_button.js"></script> <style lang="scss"> -@import "../../variables"; @import "../../mixins"; .RetweetButton { @@ -107,8 +106,7 @@ &:hover .svg-inline--fa, &.-repeated .svg-inline--fa { - color: $fallback--cGreen; - color: var(--cGreen, $fallback--cGreen); + color: var(--cGreen); } @include unfocused-style { diff --git a/src/components/rich_content/rich_content.jsx b/src/components/rich_content/rich_content.jsx index ff14a58a..99d7daca 100644 --- a/src/components/rich_content/rich_content.jsx +++ b/src/components/rich_content/rich_content.jsx @@ -79,6 +79,12 @@ export default { required: false, type: Boolean, default: false + }, + // Faint style (for notifs) + faint: { + required: false, + type: Boolean, + default: false } }, // NEVER EVER TOUCH DATA INSIDE RENDER @@ -277,7 +283,7 @@ export default { // DO NOT USE SLOTS they cause a re-render feedback loop here. // slots updated -> rerender -> emit -> update up the tree -> rerender -> ... // at least until vue3? - const result = <span class="RichContent"> + const result = <span class={['RichContent', this.faint ? '-faint' : '']}> { pass2 } </span> diff --git a/src/components/rich_content/rich_content.scss b/src/components/rich_content/rich_content.scss index e5d353ac..118b6acf 100644 --- a/src/components/rich_content/rich_content.scss +++ b/src/components/rich_content/rich_content.scss @@ -1,10 +1,19 @@ -@import "../../variables"; - .RichContent { + font-family: var(--font); + + &.-faint { + /* stylelint-disable declaration-no-important */ + --text: var(--textFaint) !important; + --link: var(--linkFaint) !important; + --funtextGreentext: var(--funtextGreentextFaint) !important; + --funtextCyantext: var(--funtextCyantextFaint) !important; + /* stylelint-enable declaration-no-important */ + } + blockquote { margin: 0.2em 0 0.2em 0.2em; font-style: italic; - border-left: 0.2em solid var(--faint, $fallback--faint); + border-left: 0.2em solid var(--textFaint); padding-left: 1em; } @@ -17,7 +26,7 @@ kbd, var, pre { - font-family: var(--postCodeFont, monospace); + font-family: var(--monoFont); } p { @@ -65,4 +74,17 @@ vertical-align: middle; object-fit: contain; } + + .greentext { + color: var(--funtextGreentext); + } + + .cyantext { + color: var(--funtextCyantext); + } +} + +a .RichContent { + /* stylelint-disable-next-line declaration-no-important */ + color: var(--link) !important; } diff --git a/src/components/rich_content/rich_content.style.js b/src/components/rich_content/rich_content.style.js new file mode 100644 index 00000000..c8314000 --- /dev/null +++ b/src/components/rich_content/rich_content.style.js @@ -0,0 +1,18 @@ +export default { + name: 'RichContent', + selector: '.RichContent', + validInnerComponents: [ + 'Text', + 'FunText', + 'Link' + ], + defaultRules: [ + { + directives: { + '--font': 'generic | inherit', + '--monoFont': 'generic | monospace', + textNoCssColor: 'yes' + } + } + ] +} diff --git a/src/components/root.style.js b/src/components/root.style.js new file mode 100644 index 00000000..4bd735aa --- /dev/null +++ b/src/components/root.style.js @@ -0,0 +1,49 @@ +export default { + name: 'Root', + selector: ':root', + validInnerComponents: [ + 'Underlay', + 'Modals', + 'Popover', + 'TopBar', + 'Scrollbar', + 'ScrollbarElement', + 'MobileDrawer', + 'Alert', + 'Button' // mobile post button + ], + validInnerComponentsLite: [ + 'Underlay', + 'Scrollbar', + 'ScrollbarElement' + ], + defaultRules: [ + { + directives: { + // These are here just to establish order, + // themes should override those + '--bg': 'color | #121a24', + '--fg': 'color | #182230', + '--text': 'color | #b9b9ba', + '--link': 'color | #d8a070', + '--accent': 'color | #d8a070', + '--cRed': 'color | #FF0000', + '--cBlue': 'color | #0095ff', + '--cGreen': 'color | #0fa00f', + '--cOrange': 'color | #ffa500', + + // Fonts + '--font': 'generic | sans-serif', + '--monoFont': 'generic | monospace', + + // Fallback no-background-image color + // (also useful in some other places like scrollbars) + '--wallpaper': 'color | --bg, -2', + + // Selection colors + '--selectionBackground': 'color | --accent', + '--selectionText': 'color | $textColor(--accent, --text, no-preserve)' + } + } + ] +} diff --git a/src/components/scope_selector/scope_selector.js b/src/components/scope_selector/scope_selector.js index 74bf7284..52cda368 100644 --- a/src/components/scope_selector/scope_selector.js +++ b/src/components/scope_selector/scope_selector.js @@ -44,10 +44,10 @@ const ScopeSelector = { }, css () { return { - public: { selected: this.currentScope === 'public' }, - unlisted: { selected: this.currentScope === 'unlisted' }, - private: { selected: this.currentScope === 'private' }, - direct: { selected: this.currentScope === 'direct' } + public: { toggled: this.currentScope === 'public' }, + unlisted: { toggled: this.currentScope === 'unlisted' }, + private: { toggled: this.currentScope === 'private' }, + direct: { toggled: this.currentScope === 'direct' } } } }, diff --git a/src/components/scope_selector/scope_selector.vue b/src/components/scope_selector/scope_selector.vue index d6e7265b..b90ae020 100644 --- a/src/components/scope_selector/scope_selector.vue +++ b/src/components/scope_selector/scope_selector.vue @@ -64,8 +64,6 @@ <script src="./scope_selector.js"></script> <style lang="scss"> -@import "../../variables"; - .ScopeSelector { .scope { display: inline-block; @@ -73,11 +71,6 @@ min-width: 1.3em; min-height: 1.3em; text-align: center; - - &.selected svg { - color: $fallback--lightText; - color: var(--lightText, $fallback--lightText); - } } } </style> diff --git a/src/components/screen_reader_notice/screen_reader_notice.js b/src/components/screen_reader_notice/screen_reader_notice.js index 3b8eaf37..794b855a 100644 --- a/src/components/screen_reader_notice/screen_reader_notice.js +++ b/src/components/screen_reader_notice/screen_reader_notice.js @@ -2,7 +2,7 @@ const ScreenReaderNotice = { props: { ariaLive: { type: String, - defualt: 'assertive' + default: 'assertive' } }, data () { diff --git a/src/components/scrollbar.style.js b/src/components/scrollbar.style.js new file mode 100644 index 00000000..94e6135d --- /dev/null +++ b/src/components/scrollbar.style.js @@ -0,0 +1,11 @@ +export default { + name: 'Scrollbar', + selector: '::-webkit-scrollbar', + defaultRules: [ + { + directives: { + background: '--wallpaper' + } + } + ] +} diff --git a/src/components/scrollbar_element.style.js b/src/components/scrollbar_element.style.js new file mode 100644 index 00000000..da942ab2 --- /dev/null +++ b/src/components/scrollbar_element.style.js @@ -0,0 +1,101 @@ +const border = (top, shadow) => ({ + x: 0, + y: top ? 1 : -1, + blur: 0, + spread: 0, + color: shadow ? '#000000' : '#FFFFFF', + alpha: 0.2, + inset: true +}) + +const buttonInsetFakeBorders = [border(true, false), border(false, true)] +const inputInsetFakeBorders = [border(true, true), border(false, false)] +const buttonOuterShadow = { + x: 0, + y: 0, + blur: 2, + spread: 0, + color: '#000000', + alpha: 1 +} + +const hoverGlow = { + x: 0, + y: 0, + blur: 4, + spread: 0, + color: '--text', + alpha: 1 +} + +export default { + name: 'ScrollbarElement', + selector: '::-webkit-scrollbar-button', + states: { + pressed: ':active', + hover: ':hover:not(:disabled)', + disabled: ':disabled' + }, + validInnerComponents: [ + 'Text' + ], + defaultRules: [ + { + directives: { + background: '--fg', + shadow: [buttonOuterShadow, ...buttonInsetFakeBorders], + roundness: 3 + } + }, + { + state: ['hover'], + directives: { + shadow: [hoverGlow, ...buttonInsetFakeBorders] + } + }, + { + state: ['pressed'], + directives: { + shadow: [buttonOuterShadow, ...inputInsetFakeBorders] + } + }, + { + state: ['hover', 'pressed'], + directives: { + shadow: [hoverGlow, ...inputInsetFakeBorders] + } + }, + { + state: ['toggled'], + directives: { + background: '--accent,-24.2', + shadow: [buttonOuterShadow, ...inputInsetFakeBorders] + } + }, + { + state: ['toggled', 'hover'], + directives: { + background: '--accent,-24.2', + shadow: [hoverGlow, ...inputInsetFakeBorders] + } + }, + { + state: ['disabled'], + directives: { + background: '$blend(--inheritedBackground, 0.25, --parent)', + shadow: [...buttonInsetFakeBorders] + } + }, + { + component: 'Text', + parent: { + component: 'Button', + state: ['disabled'] + }, + directives: { + textOpacity: 0.25, + textOpacityMode: 'blend' + } + } + ] +} diff --git a/src/components/search/search.vue b/src/components/search/search.vue index 19b9c577..bd384ad3 100644 --- a/src/components/search/search.vue +++ b/src/components/search/search.vue @@ -1,15 +1,15 @@ <template> - <div class="panel panel-default"> + <div class="Search panel panel-default"> <div class="panel-heading"> <div class="title"> {{ $t('nav.search') }} </div> </div> - <div class="search-input-container"> + <div class="panel-body search-input-container"> <input ref="searchInput" v-model="searchTerm" - class="search-input" + class="input search-input" :placeholder="$t('nav.search')" @keyup.enter="newQuery(searchTerm)" > @@ -23,7 +23,7 @@ </div> <div v-if="loading && statusesOffset == 0" - class="text-center loading-icon" + class="panel-body text-center loading-icon" > <FAIcon icon="circle-notch" @@ -67,7 +67,7 @@ /> <button v-if="!loading && loaded && lastStatusFetchCount > 0" - class="more-statuses-button button-unstyled -link -fullwidth" + class="more-statuses-button button-unstyled -link" @click.prevent="search(searchTerm, 'statuses')" > <div class="new-status-notification text-center"> @@ -148,11 +148,8 @@ <script src="./search.js"></script> <style lang="scss"> -@import "../../variables"; - .search-result-heading { - color: $fallback--faint; - color: var(--faint, $fallback--faint); + color: var(--faint); padding: 0.75rem; text-align: center; } @@ -171,17 +168,7 @@ .search-result { box-sizing: border-box; border-bottom: 1px solid; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); -} - -.search-result-footer { - border-width: 1px 0 0; - border-style: solid; - border-color: var(--border, $fallback--border); - padding: 10px; - background-color: $fallback--fg; - background-color: var(--panel, $fallback--fg); + border-color: var(--border); } .search-input-container { @@ -212,8 +199,7 @@ .hashtag { flex: 1 1 auto; - color: $fallback--text; - color: var(--text, $fallback--text); + color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -226,14 +212,14 @@ line-height: 2.25rem; font-weight: 500; text-align: center; - color: $fallback--text; - color: var(--text, $fallback--text); + color: var(--text); } } .more-statuses-button { height: 3.5em; line-height: 3.5em; + width: 100%; } </style> diff --git a/src/components/search_bar/search_bar.vue b/src/components/search_bar/search_bar.vue index 9da2b272..d06b4e77 100644 --- a/src/components/search_bar/search_bar.vue +++ b/src/components/search_bar/search_bar.vue @@ -22,7 +22,7 @@ id="search-bar-input" ref="searchInput" v-model="searchTerm" - class="search-bar-input" + class="input search-bar-input" :placeholder="$t('nav.search')" type="text" @keyup.enter="find(searchTerm)" @@ -60,8 +60,6 @@ <script src="./search_bar.js"></script> <style lang="scss"> -@import "../../variables"; - .SearchBar { display: inline-flex; align-items: baseline; @@ -86,8 +84,7 @@ } .cancel-icon { - color: $fallback--text; - color: var(--btnTopBarText, $fallback--text); + color: var(--text); } } diff --git a/src/components/select/select.vue b/src/components/select/select.vue index 1797afc8..32832126 100644 --- a/src/components/select/select.vue +++ b/src/components/select/select.vue @@ -22,8 +22,6 @@ <script src="./select.js"> </script> <style lang="scss"> -@import "../../variables"; - /* TODO fix order of styles */ label.Select { padding: 0; @@ -32,12 +30,10 @@ label.Select { appearance: none; background: transparent; border: none; - color: $fallback--text; - color: var(--inputText, --text, $fallback--text); + color: var(--text); margin: 0; padding: 0 2em 0 0.2em; - font-family: sans-serif; - font-family: var(--inputFont, sans-serif); + font-family: var(--font); font-size: 1em; width: 100%; z-index: 1; @@ -52,8 +48,7 @@ label.Select { right: 5px; height: 100%; width: 0.875em; - color: $fallback--text; - color: var(--inputText, $fallback--text); + font-family: var(--font); line-height: 2; z-index: 0; pointer-events: none; diff --git a/src/components/selectable_list/selectable_list.vue b/src/components/selectable_list/selectable_list.vue index 14910fc5..3d3a5ff0 100644 --- a/src/components/selectable_list/selectable_list.vue +++ b/src/components/selectable_list/selectable_list.vue @@ -23,16 +23,19 @@ <List :items="items" :get-key="getKey" + :get-class="item => isSelected(item) ? '-active' : ''" > <template #item="{item}"> <div class="selectable-list-item-inner" :class="{ 'selectable-list-item-selected-inner': isSelected(item) }" + @click.stop="toggle(!isSelected(item), item)" > <div class="selectable-list-checkbox-wrapper"> <Checkbox :model-value="isSelected(item)" @update:model-value="checked => toggle(checked, item)" + @click.stop /> </div> <slot @@ -51,9 +54,11 @@ <script src="./selectable_list.js"></script> <style lang="scss"> -@import "../../variables"; - .selectable-list { + --__line-height: 1.5em; + --__horizontal-gap: 0.75em; + --__vertical-gap: 0.5em; + &-item-inner { display: flex; align-items: center; @@ -63,24 +68,12 @@ } } - &-item-selected-inner { - background-color: $fallback--lightBg; - background-color: var(--selectedMenu, $fallback--lightBg); - color: var(--selectedMenuText, $fallback--text); - - --faint: var(--selectedMenuFaintText, $fallback--faint); - --faintLink: var(--selectedMenuFaintLink, $fallback--faint); - --lightText: var(--selectedMenuLightText, $fallback--lightText); - --icon: var(--selectedMenuIcon, $fallback--icon); - } - &-header { display: flex; align-items: center; - padding: 0.6em 0; - border-bottom: 2px solid; - border-bottom-color: $fallback--border; - border-bottom-color: var(--border, $fallback--border); + padding: var(--__vertical-gap) var(--__horizontal-gap); + border-bottom: 1px solid; + border-bottom-color: var(--border); &-actions { flex: 1; @@ -88,7 +81,7 @@ } &-checkbox-wrapper { - padding: 0 10px; + padding-right: var(--__horizontal-gap); flex: none; } } diff --git a/src/components/settings_modal/admin_tabs/emoji_tab.scss b/src/components/settings_modal/admin_tabs/emoji_tab.scss index cc918870..3e77e019 100644 --- a/src/components/settings_modal/admin_tabs/emoji_tab.scss +++ b/src/components/settings_modal/admin_tabs/emoji_tab.scss @@ -1,5 +1,3 @@ -@import "src/variables"; - .emoji-tab { .btn-group .btn:not(:first-child) { margin-left: 0.5em; @@ -25,7 +23,7 @@ } .emoji-unsaved { - box-shadow: 0 3px 5px var(--cBlue, $fallback--cBlue); + box-shadow: 0 3px 5px var(--cBlue); } .emoji-list { @@ -56,6 +54,6 @@ } .warning { - color: var(--cOrange, $fallback--cOrange); + color: var(--cOrange); } } diff --git a/src/components/settings_modal/admin_tabs/emoji_tab.vue b/src/components/settings_modal/admin_tabs/emoji_tab.vue index 5231f860..5742d2ce 100644 --- a/src/components/settings_modal/admin_tabs/emoji_tab.vue +++ b/src/components/settings_modal/admin_tabs/emoji_tab.vue @@ -13,13 +13,15 @@ <button class="button button-default btn" type="button" - @click="reloadEmoji"> + @click="reloadEmoji" + > {{ $t('admin_dash.emoji.reload') }} </button> <button class="button button-default btn" type="button" - @click="importFromFS"> + @click="importFromFS" + > {{ $t('admin_dash.emoji.importFS') }} </button> </li> @@ -28,7 +30,8 @@ <button class="button button-default btn" type="button" - @click="$refs.remotePackPopover.showPopover"> + @click="$refs.remotePackPopover.showPopover" + > {{ $t('admin_dash.emoji.remote_packs') }} <Popover @@ -43,11 +46,16 @@ <template #content> <div class="emoji-tab-popover-input"> <h3>{{ $t('admin_dash.emoji.remote_pack_instance') }}</h3> - <input v-model="remotePackInstance" :placeholder="$t('admin_dash.emoji.remote_pack_instance')"> + <input + v-model="remotePackInstance" + class="input" + :placeholder="$t('admin_dash.emoji.remote_pack_instance')" + > <button class="button button-default btn emoji-tab-popover-button" type="button" - @click="listRemotePacks"> + @click="listRemotePacks" + > {{ $t('admin_dash.emoji.do_list') }} </button> </div> @@ -61,9 +69,22 @@ <li> <h4>{{ $t('admin_dash.emoji.edit_pack') }}</h4> - <Select class="form-control" v-model="packName"> - <option value="" disabled hidden>{{ $t('admin_dash.emoji.emoji_pack') }}</option> - <option v-for="(pack, listPackName) in knownPacks" :label="listPackName" :key="listPackName"> + <Select + v-model="packName" + class="form-control" + > + <option + value="" + disabled + hidden + > + {{ $t('admin_dash.emoji.emoji_pack') }} + </option> + <option + v-for="(pack, listPackName) in knownPacks" + :key="listPackName" + :label="listPackName" + > {{ listPackName }} </option> </Select> @@ -71,7 +92,8 @@ <button class="button button-default btn emoji-tab-popover-button" type="button" - @click="$refs.createPackPopover.showPopover"> + @click="$refs.createPackPopover.showPopover" + > {{ $t('admin_dash.emoji.create_pack') }} </button> <Popover @@ -86,11 +108,16 @@ <template #content> <div class="emoji-tab-popover-input"> <h3>{{ $t('admin_dash.emoji.new_pack_name') }}</h3> - <input v-model="newPackName" :placeholder="$t('admin_dash.emoji.new_pack_name')"> + <input + v-model="newPackName" + :placeholder="$t('admin_dash.emoji.new_pack_name')" + class="input" + > <button class="button button-default btn emoji-tab-popover-button" type="button" - @click="createEmojiPack"> + @click="createEmojiPack" + > {{ $t('admin_dash.emoji.create') }} </button> </div> @@ -105,67 +132,96 @@ <li> <label> {{ $t('admin_dash.emoji.description') }} - <ModifiedIndicator :changed="metaEdited('description')" message-key="admin_dash.emoji.metadata_changed" /> + <ModifiedIndicator + :changed="metaEdited('description')" + message-key="admin_dash.emoji.metadata_changed" + /> <textarea v-model="packMeta.description" :disabled="pack.remote !== undefined" - class="bio resize-height" /> + class="bio resize-height input" + /> </label> </li> <li> <label> {{ $t('admin_dash.emoji.homepage') }} - <ModifiedIndicator :changed="metaEdited('homepage')" message-key="admin_dash.emoji.metadata_changed" /> + <ModifiedIndicator + :changed="metaEdited('homepage')" + message-key="admin_dash.emoji.metadata_changed" + /> - <input - class="emoji-info-input" v-model="packMeta.homepage" - :disabled="pack.remote !== undefined"> + <input + v-model="packMeta.homepage" + class="emoji-info-input input" + :disabled="pack.remote !== undefined" + > </label> </li> <li> <label> {{ $t('admin_dash.emoji.fallback_src') }} - <ModifiedIndicator :changed="metaEdited('fallback-src')" message-key="admin_dash.emoji.metadata_changed" /> + <ModifiedIndicator + :changed="metaEdited('fallback-src')" + message-key="admin_dash.emoji.metadata_changed" + /> - <input class="emoji-info-input" v-model="packMeta['fallback-src']" :disabled="pack.remote !== undefined"> + <input + v-model="packMeta['fallback-src']" + class="emoji-info-input input" + :disabled="pack.remote !== undefined" + > </label> </li> <li> <label> {{ $t('admin_dash.emoji.fallback_sha256') }} - <input :disabled="true" class="emoji-info-input" v-model="packMeta['fallback-src-sha256']"> + <input + v-model="packMeta['fallback-src-sha256']" + :disabled="true" + class="emoji-info-input input" + > </label> </li> <li> - <Checkbox :disabled="pack.remote !== undefined" v-model="packMeta['share-files']"> + <Checkbox + v-model="packMeta['share-files']" + :disabled="pack.remote !== undefined" + > {{ $t('admin_dash.emoji.share') }} </Checkbox> - <ModifiedIndicator :changed="metaEdited('share-files')" message-key="admin_dash.emoji.metadata_changed" /> + <ModifiedIndicator + :changed="metaEdited('share-files')" + message-key="admin_dash.emoji.metadata_changed" + /> </li> <li class="btn-group"> <button + v-if="pack.remote === undefined" class="button button-default btn" type="button" - v-if="pack.remote === undefined" - @click="savePackMetadata"> + @click="savePackMetadata" + > {{ $t('admin_dash.emoji.save_meta') }} </button> <button + v-if="pack.remote === undefined" class="button button-default btn" type="button" - v-if="pack.remote === undefined" - @click="savePackMetadata"> + @click="savePackMetadata" + > {{ $t('admin_dash.emoji.revert_meta') }} </button> <button - class="button button-default btn" v-if="pack.remote === undefined" + class="button button-default btn" type="button" - @click="deleteModalVisible = true"> + @click="deleteModalVisible = true" + > {{ $t('admin_dash.emoji.delete_pack') }} <ConfirmModal @@ -174,16 +230,18 @@ :cancel-text="$t('status.delete_confirm_cancel_button')" :confirm-text="$t('status.delete_confirm_accept_button')" @cancelled="deleteModalVisible = false" - @accepted="deleteEmojiPack" > + @accepted="deleteEmojiPack" + > {{ $t('admin_dash.emoji.delete_confirm', [packName]) }} </ConfirmModal> </button> <button + v-if="pack.remote !== undefined" class="button button-default btn" type="button" - v-if="pack.remote !== undefined" - @click="$refs.dlPackPopover.showPopover"> + @click="$refs.dlPackPopover.showPopover" + > {{ $t('admin_dash.emoji.download_pack') }} <Popover @@ -202,12 +260,17 @@ <div class="emoji-tab-popover-input"> <label> {{ $t('admin_dash.emoji.download_as_name') }} - <input class="emoji-data-input" + <input v-model="remotePackDownloadAs" - :placeholder="$t('admin_dash.emoji.download_as_name_full')"> + class="emoji-data-input input" + :placeholder="$t('admin_dash.emoji.download_as_name_full')" + > </label> - <div v-if="downloadWillReplaceLocal" class="warning"> + <div + v-if="downloadWillReplaceLocal" + class="warning" + > <em>{{ $t('admin_dash.emoji.replace_warning') }}</em> </div> </div> @@ -215,7 +278,8 @@ <button class="button button-default btn" type="button" - @click="downloadRemotePack"> + @click="downloadRemotePack" + > {{ $t('admin_dash.emoji.download') }} </button> </div> @@ -231,31 +295,47 @@ <h4> {{ $t('admin_dash.emoji.files') }} - <ModifiedIndicator v-if="pack" + <ModifiedIndicator + v-if="pack" :changed="$refs.emojiPopovers && $refs.emojiPopovers.some(p => p.isEdited)" - message-key="admin_dash.emoji.emoji_changed"/> + message-key="admin_dash.emoji.emoji_changed" + /> </h4> - <div class="emoji-list" v-if="pack"> + <div + v-if="pack" + class="emoji-list" + > <EmojiEditingPopover v-if="pack.remote === undefined" - placement="bottom" new-upload + placement="bottom" + new-upload :title="$t('admin_dash.emoji.adding_new')" - :packName="packName" - @updatePackFiles="updatePackFiles" @displayError="displayError" + :pack-name="packName" + @updatePackFiles="updatePackFiles" + @displayError="displayError" > <template #trigger> - <FAIcon icon="plus" size="2x" :title="$t('admin_dash.emoji.add_file')" /> + <FAIcon + icon="plus" + size="2x" + :title="$t('admin_dash.emoji.add_file')" + /> </template> </EmojiEditingPopover> <EmojiEditingPopover - placement="top" ref="emojiPopovers" - v-for="(file, shortcode) in pack.files" :key="shortcode" + v-for="(file, shortcode) in pack.files" + ref="emojiPopovers" + :key="shortcode" + placement="top" :title="$t('admin_dash.emoji.editing', [shortcode])" :disabled="pack.remote !== undefined" - :shortcode="shortcode" :file="file" :packName="packName" - @updatePackFiles="updatePackFiles" @displayError="displayError" + :shortcode="shortcode" + :file="file" + :pack-name="packName" + @updatePackFiles="updatePackFiles" + @displayError="displayError" > <template #trigger> <StillImage diff --git a/src/components/settings_modal/admin_tabs/frontends_tab.vue b/src/components/settings_modal/admin_tabs/frontends_tab.vue index 097877bc..8fb3d399 100644 --- a/src/components/settings_modal/admin_tabs/frontends_tab.vue +++ b/src/components/settings_modal/admin_tabs/frontends_tab.vue @@ -6,7 +6,10 @@ <div class="setting-item"> <h2>{{ $t('admin_dash.tabs.frontends') }}</h2> <p>{{ $t('admin_dash.frontend.wip_notice') }}</p> - <ul class="setting-list" v-if="adminDraft"> + <ul + v-if="adminDraft" + class="setting-list" + > <li> <h3>{{ $t('admin_dash.frontend.default_frontend') }}</h3> <p>{{ $t('admin_dash.frontend.default_frontend_tip') }}</p> @@ -23,12 +26,18 @@ </ul> </li> </ul> - <div v-else class="setting-list"> + <div + v-else + class="setting-list" + > {{ $t('admin_dash.frontend.default_frontend_unavail') }} </div> <div class="setting-list relative"> - <PanelLoading class="overlay" v-if="working"/> + <PanelLoading + v-if="working" + class="overlay" + /> <h3>{{ $t('admin_dash.frontend.available_frontends') }}</h3> <ul class="cards-list"> <li @@ -107,7 +116,7 @@ <button v-for="ref in frontend.refs" :key="ref" - class="button-default dropdown-item" + class="menu-item dropdown-item" @click.prevent="update(frontend, ref)" @click="close" > @@ -164,7 +173,7 @@ <button v-for="ref in frontend.installedRefs || frontend.refs" :key="ref" - class="button-default dropdown-item" + class="menu-item dropdown-item" @click.prevent="setDefault(frontend, ref)" @click="close" > diff --git a/src/components/settings_modal/admin_tabs/instance_tab.vue b/src/components/settings_modal/admin_tabs/instance_tab.vue index a0e3351e..32e8df25 100644 --- a/src/components/settings_modal/admin_tabs/instance_tab.vue +++ b/src/components/settings_modal/admin_tabs/instance_tab.vue @@ -8,7 +8,10 @@ </li> <!-- See https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3963 --> <li v-if="adminDraft[':pleroma'][':instance'][':favicon'] !== undefined"> - <AttachmentSetting compact path=":pleroma.:instance.:favicon" /> + <AttachmentSetting + compact + path=":pleroma.:instance.:favicon" + /> </li> <li> <StringSetting path=":pleroma.:instance.:email" /> @@ -20,7 +23,10 @@ <StringSetting path=":pleroma.:instance.:short_description" /> </li> <li> - <AttachmentSetting compact path=":pleroma.:instance.:instance_thumbnail" /> + <AttachmentSetting + compact + path=":pleroma.:instance.:instance_thumbnail" + /> </li> <li> <AttachmentSetting path=":pleroma.:instance.:background_image" /> diff --git a/src/components/settings_modal/helpers/attachment_setting.vue b/src/components/settings_modal/helpers/attachment_setting.vue index b50231f2..96c80ab1 100644 --- a/src/components/settings_modal/helpers/attachment_setting.vue +++ b/src/components/settings_modal/helpers/attachment_setting.vue @@ -29,7 +29,7 @@ <label for="path">{{ $t('settings.url') }}</label> <input :id="path" - class="string-input" + class="input string-input" :disabled="shouldBeDisabled" :value="realDraftMode ? draft : state" @change="update" diff --git a/src/components/settings_modal/helpers/emoji_editing_popover.vue b/src/components/settings_modal/helpers/emoji_editing_popover.vue index cdd3e403..f0465dd5 100644 --- a/src/components/settings_modal/helpers/emoji_editing_popover.vue +++ b/src/components/settings_modal/helpers/emoji_editing_popover.vue @@ -1,10 +1,10 @@ <template> <Popover + ref="emojiPopover" trigger="click" :placement="placement" bound-to-selector=".emoji-list" popover-class="emoji-tab-edit-popover popover-default" - ref="emojiPopover" :bound-to="{ x: 'container' }" :offset="{ y: 5 }" :disabled="disabled" @@ -18,23 +18,36 @@ {{ title }} </h3> - <StillImage class="emoji" v-if="emojiPreview" :src="emojiPreview" /> - <div v-else class="emoji"></div> - - <div class="emoji-tab-popover-input" v-if="newUpload"> + <StillImage + v-if="emojiPreview" + class="emoji" + :src="emojiPreview" + /> + <div + v-else + class="emoji" + /> + + <div + v-if="newUpload" + class="emoji-tab-popover-input" + > <input type="file" accept="image/*" - class="emoji-tab-popover-file" - @change="uploadFile = $event.target.files"> + class="emoji-tab-popover-file input" + @change="uploadFile = $event.target.files" + > </div> <div> <div class="emoji-tab-popover-input"> <label> {{ $t('admin_dash.emoji.shortcode') }} - <input class="emoji-data-input" + <input v-model="editedShortcode" - :placeholder="$t('admin_dash.emoji.new_shortcode')"> + class="emoji-data-input input" + :placeholder="$t('admin_dash.emoji.new_shortcode')" + > </label> </div> @@ -42,9 +55,11 @@ <label> {{ $t('admin_dash.emoji.filename') }} - <input class="emoji-data-input" + <input v-model="editedFile" - :placeholder="$t('admin_dash.emoji.new_filename')"> + class="emoji-data-input input" + :placeholder="$t('admin_dash.emoji.new_filename')" + > </label> </div> @@ -52,7 +67,8 @@ class="button button-default btn" type="button" :disabled="newUpload ? uploadFile.length == 0 : !isEdited" - @click="newUpload ? uploadEmoji() : saveEditedEmoji()"> + @click="newUpload ? uploadEmoji() : saveEditedEmoji()" + > {{ $t('admin_dash.emoji.save') }} </button> @@ -60,13 +76,15 @@ <button class="button button-default btn emoji-tab-popover-button" type="button" - @click="deleteModalVisible = true"> + @click="deleteModalVisible = true" + > {{ $t('admin_dash.emoji.delete') }} </button> <button class="button button-default btn emoji-tab-popover-button" type="button" - @click="revertEmoji"> + @click="revertEmoji" + > {{ $t('admin_dash.emoji.revert') }} </button> <ConfirmModal @@ -75,7 +93,8 @@ :cancel-text="$t('status.delete_confirm_cancel_button')" :confirm-text="$t('status.delete_confirm_accept_button')" @cancelled="deleteModalVisible = false" - @accepted="deleteEmoji" > + @accepted="deleteEmoji" + > {{ $t('admin_dash.emoji.delete_confirm', [shortcode]) }} </ConfirmModal> </template> @@ -91,6 +110,30 @@ import StillImage from 'components/still-image/still-image.vue' export default { components: { Popover, ConfirmModal, StillImage }, + inject: ['emojiAddr'], + props: { + placement: String, + disabled: { + type: Boolean, + default: false + }, + + newUpload: Boolean, + + title: String, + packName: String, + shortcode: { + type: String, + // Only exists when this is not a new upload + default: '' + }, + file: { + type: String, + // Only exists when this is not a new upload + default: '' + } + }, + emits: ['updatePackFiles', 'displayError'], data () { return { uploadFile: [], @@ -113,7 +156,6 @@ export default { return !this.newUpload && (this.editedShortcode !== this.shortcode || this.editedFile !== this.file) } }, - inject: ['emojiAddr'], methods: { saveEditedEmoji () { if (!this.isEdited) return @@ -167,29 +209,6 @@ export default { this.$emit('updatePackFiles', resp) }) } - }, - emits: ['updatePackFiles', 'displayError'], - props: { - placement: String, - disabled: { - type: Boolean, - default: false - }, - - newUpload: Boolean, - - title: String, - packName: String, - shortcode: { - type: String, - // Only exists when this is not a new upload - default: '' - }, - file: { - type: String, - // Only exists when this is not a new upload - default: '' - } } } </script> diff --git a/src/components/settings_modal/helpers/number_setting.vue b/src/components/settings_modal/helpers/number_setting.vue index 93f11331..32dc6f83 100644 --- a/src/components/settings_modal/helpers/number_setting.vue +++ b/src/components/settings_modal/helpers/number_setting.vue @@ -15,9 +15,10 @@ </template> <slot v-else /> </label> + {{ ' ' }} <input :id="path" - class="number-input" + class="input number-input" type="number" :step="step || 1" :disabled="shouldBeDisabled" diff --git a/src/components/settings_modal/helpers/setting.js b/src/components/settings_modal/helpers/setting.js index abf9cfdf..3b3e6268 100644 --- a/src/components/settings_modal/helpers/setting.js +++ b/src/components/settings_modal/helpers/setting.js @@ -48,6 +48,10 @@ export default { draftMode: { type: Boolean, default: undefined + }, + timedApplyMode: { + type: Boolean, + default: false } }, inject: { @@ -161,7 +165,11 @@ export default { case 'admin': return (k, v) => this.$store.dispatch('pushAdminSetting', { path: k, value: v }) default: - return (k, v) => this.$store.dispatch('setOption', { name: k, value: v }) + if (this.timedApplyMode) { + return (k, v) => this.$store.dispatch('setOptionTemporarily', { name: k, value: v }) + } else { + return (k, v) => this.$store.dispatch('setOption', { name: k, value: v }) + } } }, defaultState () { diff --git a/src/components/settings_modal/helpers/size_setting.js b/src/components/settings_modal/helpers/size_setting.js deleted file mode 100644 index 12cef705..00000000 --- a/src/components/settings_modal/helpers/size_setting.js +++ /dev/null @@ -1,40 +0,0 @@ -import Select from 'src/components/select/select.vue' -import Setting from './setting.js' - -export const allCssUnits = ['cm', 'mm', 'in', 'px', 'pt', 'pc', 'em', 'ex', 'ch', 'rem', 'vw', 'vh', 'vmin', 'vmax', '%'] -export const defaultHorizontalUnits = ['px', 'rem', 'vw'] -export const defaultVerticalUnits = ['px', 'rem', 'vh'] - -export default { - ...Setting, - components: { - ...Setting.components, - Select - }, - props: { - ...Setting.props, - min: Number, - units: { - type: Array, - default: () => allCssUnits - } - }, - computed: { - ...Setting.computed, - stateUnit () { - return this.state.replace(/\d+/, '') - }, - stateValue () { - return this.state.replace(/\D+/, '') - } - }, - methods: { - ...Setting.methods, - updateValue (e) { - this.configSink(this.path, parseInt(e.target.value) + this.stateUnit) - }, - updateUnit (e) { - this.configSink(this.path, this.stateValue + e.target.value) - } - } -} diff --git a/src/components/settings_modal/helpers/string_setting.vue b/src/components/settings_modal/helpers/string_setting.vue index 0cfa61ce..7b30d1b9 100644 --- a/src/components/settings_modal/helpers/string_setting.vue +++ b/src/components/settings_modal/helpers/string_setting.vue @@ -17,7 +17,7 @@ </label> <input :id="path" - class="string-input" + class="input string-input" :disabled="shouldBeDisabled" :value="realDraftMode ? draft : state" @change="update" diff --git a/src/components/settings_modal/helpers/unit_setting.js b/src/components/settings_modal/helpers/unit_setting.js new file mode 100644 index 00000000..daeddd81 --- /dev/null +++ b/src/components/settings_modal/helpers/unit_setting.js @@ -0,0 +1,64 @@ +import Select from 'src/components/select/select.vue' +import Setting from './setting.js' + +export const allCssUnits = ['cm', 'mm', 'in', 'px', 'pt', 'pc', 'em', 'ex', 'ch', 'rem', 'vw', 'vh', 'vmin', 'vmax', '%'] +export const defaultHorizontalUnits = ['px', 'rem', 'vw'] +export const defaultVerticalUnits = ['px', 'rem', 'vh'] + +export default { + ...Setting, + components: { + ...Setting.components, + Select + }, + props: { + ...Setting.props, + min: Number, + units: { + type: Array, + default: () => allCssUnits + }, + unitSet: { + type: String, + default: 'none' + }, + step: { + type: Number, + default: 1 + }, + resetDefault: { + type: Object, + default: null + } + }, + computed: { + ...Setting.computed, + stateUnit () { + return typeof this.state === 'string' ? this.state.replace(/[0-9,.]+/, '') : '' + }, + stateValue () { + return typeof this.state === 'string' ? this.state.replace(/[^0-9,.]+/, '') : '' + } + }, + methods: { + ...Setting.methods, + getUnitString (value) { + if (this.unitSet === 'none') return value + return this.$t(['settings', 'units', this.unitSet, value].join('.')) + }, + updateValue (e) { + this.configSink(this.path, parseFloat(e.target.value) + this.stateUnit) + }, + updateUnit (e) { + let value = this.stateValue + const newUnit = e.target.value + if (this.resetDefault) { + const replaceValue = this.resetDefault[newUnit] + if (replaceValue != null) { + value = replaceValue + } + } + this.configSink(this.path, value + newUnit) + } + } +} diff --git a/src/components/settings_modal/helpers/size_setting.vue b/src/components/settings_modal/helpers/unit_setting.vue index 6c3fbaeb..40ab6880 100644 --- a/src/components/settings_modal/helpers/size_setting.vue +++ b/src/components/settings_modal/helpers/unit_setting.vue @@ -1,7 +1,7 @@ <template> <span v-if="matchesExpertLevel" - class="SizeSetting" + class="UnitSetting" > <label :for="path" @@ -9,11 +9,12 @@ > <slot /> </label> + {{ ' ' }} <input :id="path" - class="number-input" + class="input number-input" type="number" - step="1" + :step="step" :disabled="disabled" :min="min || 0" :value="stateValue" @@ -23,7 +24,7 @@ :id="path" :model-value="stateUnit" :disabled="disabled" - class="css-unit-input" + class="unit-input unstyled" @change="updateUnit" > <option @@ -31,7 +32,7 @@ :key="option" :value="option" > - {{ option }} + {{ getUnitString(option) }} </option> </Select> {{ ' ' }} @@ -42,20 +43,19 @@ </span> </template> -<script src="./size_setting.js"></script> +<script src="./unit_setting.js"></script> <style lang="scss"> -.SizeSetting { +.UnitSetting { .number-input { max-width: 6.5em; + text-align: right; } - .css-unit-input, - .css-unit-input select { - margin-left: 0.5em; - width: 4em; - max-width: 4em; + .unit-input, + .unit-input select { min-width: 4em; + width: auto; } } diff --git a/src/components/settings_modal/settings_modal.js b/src/components/settings_modal/settings_modal.js index ff58f2c3..63c9b24a 100644 --- a/src/components/settings_modal/settings_modal.js +++ b/src/components/settings_modal/settings_modal.js @@ -4,6 +4,7 @@ import AsyncComponentError from 'src/components/async_component_error/async_comp import getResettableAsyncComponent from 'src/services/resettable_async_component.js' import Popover from '../popover/popover.vue' import Checkbox from 'src/components/checkbox/checkbox.vue' +import ConfirmModal from 'src/components/confirm_modal/confirm_modal.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { cloneDeep, isEqual } from 'lodash' import { @@ -53,6 +54,7 @@ const SettingsModal = { Modal, Popover, Checkbox, + ConfirmModal, SettingsModalUserContent: getResettableAsyncComponent( () => import('./settings_modal_user_content.vue'), { @@ -165,6 +167,7 @@ const SettingsModal = { }, computed: { currentSaveStateNotice () { + console.log(this.$store.state.interface.settings.currentSaveStateNotice) return this.$store.state.interface.settings.currentSaveStateNotice }, modalActivated () { diff --git a/src/components/settings_modal/settings_modal.scss b/src/components/settings_modal/settings_modal.scss index 6bc9459b..d01553db 100644 --- a/src/components/settings_modal/settings_modal.scss +++ b/src/components/settings_modal/settings_modal.scss @@ -1,5 +1,3 @@ -@import "src/variables"; - .settings-modal { overflow: hidden; diff --git a/src/components/settings_modal/settings_modal.vue b/src/components/settings_modal/settings_modal.vue index 4e7fd931..90dbbde0 100644 --- a/src/components/settings_modal/settings_modal.vue +++ b/src/components/settings_modal/settings_modal.vue @@ -14,7 +14,7 @@ <div v-if="currentSaveStateNotice" class="alert" - :class="{ transparent: !currentSaveStateNotice.error, error: currentSaveStateNotice.error}" + :class="{ success: !currentSaveStateNotice.error, error: currentSaveStateNotice.error}" @click.prevent > {{ currentSaveStateNotice.error ? $t('settings.saving_err') : $t('settings.saving_ok') }} @@ -70,7 +70,7 @@ <template #content="{close}"> <div class="dropdown-menu"> <button - class="button-default dropdown-item dropdown-item-icon" + class="menu-item dropdown-item dropdown-item-icon" @click.prevent="backup" @click="close" > @@ -80,7 +80,7 @@ /><span>{{ $t("settings.file_export_import.backup_settings") }}</span> </button> <button - class="button-default dropdown-item dropdown-item-icon" + class="menu-item dropdown-item dropdown-item-icon" @click.prevent="backupWithTheme" @click="close" > @@ -90,7 +90,7 @@ /><span>{{ $t("settings.file_export_import.backup_settings_theme") }}</span> </button> <button - class="button-default dropdown-item dropdown-item-icon" + class="menu-item dropdown-item dropdown-item-icon" @click.prevent="restore" @click="close" > @@ -147,6 +147,18 @@ </span> </div> </div> + <teleport to="#modal"> + <ConfirmModal + v-if="$store.state.interface.temporaryChangesTimeoutId" + :title="$t('settings.confirm_new_setting')" + :cancel-text="$t('settings.revert')" + :confirm-text="$t('settings.confirm')" + @cancelled="$store.state.interface.temporaryChangesRevert" + @accepted="$store.state.interface.temporaryChangesConfirm" + > + {{ $t('settings.confirm_new_question') }} + </ConfirmModal> + </teleport> </Modal> </template> diff --git a/src/components/settings_modal/settings_modal_admin_content.scss b/src/components/settings_modal/settings_modal_admin_content.scss index c984d703..a5314fe1 100644 --- a/src/components/settings_modal/settings_modal_admin_content.scss +++ b/src/components/settings_modal/settings_modal_admin_content.scss @@ -1,10 +1,8 @@ -@import "src/variables"; - .settings_tab-switcher { height: 100%; .setting-item { - border-bottom: 2px solid var(--fg, $fallback--fg); + border-bottom: 2px solid var(--border); margin: 1em 1em 1.4em; padding-bottom: 1.4em; @@ -33,10 +31,6 @@ margin-bottom: 1em; } - select { - min-width: 10em; - } - textarea { width: 100%; max-width: 100%; @@ -45,8 +39,7 @@ .unavailable, .unavailable svg { - color: var(--cRed, $fallback--cRed); - color: $fallback--cRed; + color: var(--cRed); } } } diff --git a/src/components/settings_modal/settings_modal_user_content.js b/src/components/settings_modal/settings_modal_user_content.js index 9ac0301f..ebd5329f 100644 --- a/src/components/settings_modal/settings_modal_user_content.js +++ b/src/components/settings_modal/settings_modal_user_content.js @@ -7,6 +7,7 @@ import FilteringTab from './tabs/filtering_tab.vue' import SecurityTab from './tabs/security_tab/security_tab.vue' import ProfileTab from './tabs/profile_tab.vue' import GeneralTab from './tabs/general_tab.vue' +import AppearanceTab from './tabs/appearance_tab.vue' import VersionTab from './tabs/version_tab.vue' import ThemeTab from './tabs/theme_tab/theme_tab.vue' @@ -19,7 +20,8 @@ import { faBell, faDownload, faEyeSlash, - faInfo + faInfo, + faWindowRestore } from '@fortawesome/free-solid-svg-icons' library.add( @@ -30,7 +32,8 @@ library.add( faBell, faDownload, faEyeSlash, - faInfo + faInfo, + faWindowRestore ) const SettingsModalContent = { @@ -44,6 +47,7 @@ const SettingsModalContent = { SecurityTab, ProfileTab, GeneralTab, + AppearanceTab, VersionTab, ThemeTab }, diff --git a/src/components/settings_modal/settings_modal_user_content.scss b/src/components/settings_modal/settings_modal_user_content.scss index c984d703..a5314fe1 100644 --- a/src/components/settings_modal/settings_modal_user_content.scss +++ b/src/components/settings_modal/settings_modal_user_content.scss @@ -1,10 +1,8 @@ -@import "src/variables"; - .settings_tab-switcher { height: 100%; .setting-item { - border-bottom: 2px solid var(--fg, $fallback--fg); + border-bottom: 2px solid var(--border); margin: 1em 1em 1.4em; padding-bottom: 1.4em; @@ -33,10 +31,6 @@ margin-bottom: 1em; } - select { - min-width: 10em; - } - textarea { width: 100%; max-width: 100%; @@ -45,8 +39,7 @@ .unavailable, .unavailable svg { - color: var(--cRed, $fallback--cRed); - color: $fallback--cRed; + color: var(--cRed); } } } diff --git a/src/components/settings_modal/settings_modal_user_content.vue b/src/components/settings_modal/settings_modal_user_content.vue index 0221cccb..1441d892 100644 --- a/src/components/settings_modal/settings_modal_user_content.vue +++ b/src/components/settings_modal/settings_modal_user_content.vue @@ -14,6 +14,20 @@ <GeneralTab /> </div> <div + :label="$t('settings.appearance')" + icon="window-restore" + data-tab-name="appearance" + > + <AppearanceTab /> + </div> + <div + :label="$t('settings.theme')" + icon="paint-brush" + data-tab-name="theme" + > + <ThemeTab /> + </div> + <div v-if="isLoggedIn" :label="$t('settings.profile_tab')" icon="user" @@ -23,6 +37,14 @@ </div> <div v-if="isLoggedIn" + :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" @@ -37,19 +59,13 @@ <FilteringTab /> </div> <div - :label="$t('settings.theme')" - icon="paint-brush" - data-tab-name="theme" - > - <ThemeTab /> - </div> - <div v-if="isLoggedIn" - :label="$t('settings.notifications')" - icon="bell" - data-tab-name="notifications" + :label="$t('settings.mutes_and_blocks')" + :fullHeight="true" + icon="eye-slash" + data-tab-name="mutesAndBlocks" > - <NotificationsTab /> + <MutesAndBlocksTab /> </div> <div v-if="isLoggedIn" @@ -60,15 +76,6 @@ <DataImportExportTab /> </div> <div - v-if="isLoggedIn" - :label="$t('settings.mutes_and_blocks')" - :fullHeight="true" - icon="eye-slash" - data-tab-name="mutesAndBlocks" - > - <MutesAndBlocksTab /> - </div> - <div :label="$t('settings.version.title')" icon="info" data-tab-name="version" diff --git a/src/components/settings_modal/tabs/appearance_tab.js b/src/components/settings_modal/tabs/appearance_tab.js new file mode 100644 index 00000000..b5fd6c4c --- /dev/null +++ b/src/components/settings_modal/tabs/appearance_tab.js @@ -0,0 +1,195 @@ +import BooleanSetting from '../helpers/boolean_setting.vue' +import ChoiceSetting from '../helpers/choice_setting.vue' +import IntegerSetting from '../helpers/integer_setting.vue' +import FloatSetting from '../helpers/float_setting.vue' +import UnitSetting, { defaultHorizontalUnits } from '../helpers/unit_setting.vue' + +import FontControl from 'src/components/font_control/font_control.vue' + +import { normalizeThemeData } from 'src/modules/interface' + +import { + getThemes +} from 'src/services/style_setter/style_setter.js' +import { convertTheme2To3 } from 'src/services/theme_data/theme2_to_theme3.js' +import { init } from 'src/services/theme_data/theme_data_3.service.js' +import { + getCssRules, + getScopedVersion +} from 'src/services/theme_data/css_utils.js' + +import SharedComputedObject from '../helpers/shared_computed_object.js' +import ProfileSettingIndicator from '../helpers/profile_setting_indicator.vue' +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faGlobe +} from '@fortawesome/free-solid-svg-icons' + +import Preview from './theme_tab/theme_preview.vue' + +library.add( + faGlobe +) + +const AppearanceTab = { + data () { + return { + availableStyles: [], + intersectionObserver: null, + thirdColumnModeOptions: ['none', 'notifications', 'postform'].map(mode => ({ + key: mode, + value: mode, + label: this.$t(`settings.third_column_mode_${mode}`) + })), + forcedRoundnessOptions: ['disabled', 'sharp', 'nonsharp', 'round'].map((mode, i) => ({ + key: mode, + value: i - 1, + label: this.$t(`settings.style.themes3.hacks.forced_roundness_mode_${mode}`) + })), + underlayOverrideModes: ['none', 'opaque', 'transparent'].map((mode, i) => ({ + key: mode, + value: mode, + label: this.$t(`settings.style.themes3.hacks.underlay_override_mode_${mode}`) + })) + } + }, + components: { + BooleanSetting, + ChoiceSetting, + IntegerSetting, + FloatSetting, + UnitSetting, + ProfileSettingIndicator, + FontControl, + Preview + }, + mounted () { + getThemes() + .then((promises) => { + return Promise.all( + Object.entries(promises) + .map(([k, v]) => v.then(res => [k, res])) + ) + }) + .then(themes => themes.reduce((acc, [k, v]) => { + if (v) { + return [ + ...acc, + { + name: v.name || v[0], + key: k, + data: v + } + ] + } else { + return acc + } + }, [])) + .then((themesComplete) => { + this.availableStyles = themesComplete + }) + + if (window.IntersectionObserver) { + this.intersectionObserver = new IntersectionObserver((entries, observer) => { + entries.forEach(({ target, isIntersecting }) => { + if (!isIntersecting) return + const theme = this.availableStyles.find(x => x.key === target.dataset.themeKey) + this.$nextTick(() => { + if (theme) theme.ready = true + }) + observer.unobserve(target) + }) + }, { + root: this.$refs.themeList + }) + } + }, + updated () { + this.$nextTick(() => { + this.$refs.themeList.querySelectorAll('.theme-preview').forEach(node => { + this.intersectionObserver.observe(node) + }) + }) + }, + computed: { + noIntersectionObserver () { + return !window.IntersectionObserver + }, + horizontalUnits () { + return defaultHorizontalUnits + }, + fontsOverride () { + return this.$store.getters.mergedConfig.fontsOverride + }, + columns () { + const mode = this.$store.getters.mergedConfig.thirdColumnMode + + const notif = mode === 'none' ? [] : ['notifs'] + + if (this.$store.getters.mergedConfig.sidebarRight || mode === 'postform') { + return [...notif, 'content', 'sidebar'] + } else { + return ['sidebar', 'content', ...notif] + } + }, + instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel }, + instanceWallpaperUsed () { + return this.$store.state.instance.background && + !this.$store.state.users.currentUser.background_image + }, + instanceShoutboxPresent () { return this.$store.state.instance.shoutAvailable }, + language: { + get: function () { return this.$store.getters.mergedConfig.interfaceLanguage }, + set: function (val) { + this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val }) + } + }, + isCustomThemeUsed () { + const { theme } = this.mergedConfig + return theme === 'custom' || theme === null + }, + ...SharedComputedObject() + }, + methods: { + updateFont (key, value) { + console.log(key, value) + this.$store.dispatch('setOption', { + name: 'theme3hacks', + value: { + ...this.mergedConfig.theme3hacks, + fonts: { + ...this.mergedConfig.theme3hacks.fonts, + [key]: value + } + } + }) + }, + isThemeActive (key) { + const { theme } = this.mergedConfig + return key === theme + }, + setTheme (name) { + this.$store.dispatch('setTheme', { themeName: name, saveData: true, recompile: true }) + }, + previewTheme (key, input) { + const style = normalizeThemeData(input) + const x = 2 + if (x === 1) return + const theme2 = convertTheme2To3(style) + const theme3 = init({ + inputRuleset: theme2, + ultimateBackgroundColor: '#000000', + liteMode: true, + debug: true, + onlyNormalState: true + }) + + return getScopedVersion( + getCssRules(theme3.eager), + '#theme-preview-' + key + ).join('\n') + } + } +} + +export default AppearanceTab diff --git a/src/components/settings_modal/tabs/appearance_tab.vue b/src/components/settings_modal/tabs/appearance_tab.vue new file mode 100644 index 00000000..de6eb8e7 --- /dev/null +++ b/src/components/settings_modal/tabs/appearance_tab.vue @@ -0,0 +1,313 @@ +<template> + <div class="appearance-tab" :label="$t('settings.general')"> + <div class="setting-item"> + <h2>{{ $t('settings.theme') }}</h2> + <ul + class="theme-list" + ref="themeList" + > + <button + v-if="isCustomThemeUsed" + disabled + class="button-default theme-preview" + > + <preview /> + <h4 class="theme-name">{{ $t('settings.style.custom_theme_used') }}</h4> + </button> + <button + v-for="style in availableStyles" + :data-theme-key="style.key" + :key="style.key" + class="button-default theme-preview" + :class="{ toggled: isThemeActive(style.key) }" + @click="setTheme(style.key)" + > + <!-- eslint-disable vue/no-v-text-v-html-on-component --> + <component + :is="'style'" + v-if="style.ready || noIntersectionObserver" + v-html="previewTheme(style.key, style.data)" + /> + <!-- eslint-enable vue/no-v-text-v-html-on-component --> + <preview :class="{ placeholder: ready }" :id="'theme-preview-' + style.key"/> + <h4 class="theme-name">{{ style.name }}</h4> + </button> + </ul> + </div> + <div class="alert neutral theme-notice"> + {{ $t("settings.style.appearance_tab_note") }} + </div> + <div class="setting-item"> + <h2>{{ $t('settings.scale_and_layout') }}</h2> + <ul class="setting-list"> + <li> + <UnitSetting + path="textSize" + step="0.1" + :units="['px', 'rem']" + :reset-default="{ 'px': 14, 'rem': 1 }" + timed-apply-mode + > + {{ $t('settings.text_size') }} + </UnitSetting> + <div> + <small> + <i18n-t + scope="global" + keypath="settings.text_size_tip" + tag="span" + > + <code>px</code> + <code>rem</code> + </i18n-t> + <br/> + <i18n-t + scope="global" + keypath="settings.text_size_tip2" + tag="span" + > + <code>14px</code> + </i18n-t> + </small> + </div> + </li> + <li> + <h3>{{ $t('settings.style.interface_font_user_override') }}</h3> + <ul class="setting-list"> + <li> + <FontControl + :model-value="mergedConfig.theme3hacks.fonts.interface" + name="ui" + :label="$t('settings.style.fonts.components.interface')" + :fallback="{ family: 'sans-serif' }" + no-inherit="1" + @update:modelValue="v => updateFont('interface', v)" + /> + </li> + <li> + <FontControl + v-if="expertLevel > 0" + :model-value="mergedConfig.theme3hacks.fonts.input" + name="input" + :fallback="{ family: 'inherit' }" + :label="$t('settings.style.fonts.components.input')" + @update:modelValue="v => updateFont('input', v)" + /> + </li> + <li> + <FontControl + v-if="expertLevel > 0" + :model-value="mergedConfig.theme3hacks.fonts.post" + name="post" + :fallback="{ family: 'inherit' }" + :label="$t('settings.style.fonts.components.post')" + @update:modelValue="v => updateFont('post', v)" + /> + </li> + <li> + <FontControl + v-if="expertLevel > 0" + :model-value="mergedConfig.theme3hacks.fonts.monospace" + name="postCode" + :fallback="{ family: 'monospace' }" + :label="$t('settings.style.fonts.components.monospace')" + @update:modelValue="v => updateFont('monospace', v)" + /> + </li> + </ul> + </li> + <li> + <UnitSetting + path="emojiSize" + step="0.1" + :units="['px', 'rem']" + :reset-default="{ 'px': 32, 'rem': 2.2 }" + > + {{ $t('settings.emoji_size') }} + </UnitSetting> + <ul + class="setting-list suboptions" + > + <li> + <FloatSetting + v-if="user" + path="emojiReactionsScale" + expert="1" + > + {{ $t('settings.emoji_reactions_scale') }} + </FloatSetting> + </li> + </ul> + </li> + <li> + <UnitSetting + path="navbarSize" + step="0.1" + :units="['px', 'rem']" + :reset-default="{ 'px': 55, 'rem': 3.5 }" + > + {{ $t('settings.navbar_size') }} + </UnitSetting> + </li> + <h3>{{ $t('settings.columns') }}</h3> + <li> + <UnitSetting + path="panelHeaderSize" + step="0.1" + :units="['px', 'rem']" + :reset-default="{ 'px': 52, 'rem': 3.2 }" + timed-apply-mode + > + {{ $t('settings.panel_header_size') }} + </UnitSetting> + </li> + <li> + <BooleanSetting path="sidebarRight"> + {{ $t('settings.right_sidebar') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting path="navbarColumnStretch"> + {{ $t('settings.navbar_column_stretch') }} + </BooleanSetting> + </li> + <li> + <ChoiceSetting + v-if="user" + id="thirdColumnMode" + path="thirdColumnMode" + :options="thirdColumnModeOptions" + > + {{ $t('settings.third_column_mode') }} + </ChoiceSetting> + </li> + <li v-if="expertLevel > 0"> + {{ $t('settings.column_sizes') }} + <div class="column-settings"> + <UnitSetting + v-for="column in columns" + :key="column" + :path="column + 'ColumnWidth'" + :units="horizontalUnits" + expert="1" + > + {{ $t('settings.column_sizes_' + column) }} + </UnitSetting> + </div> + </li> + <li> + <BooleanSetting path="disableStickyHeaders"> + {{ $t('settings.disable_sticky_headers') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting path="showScrollbars"> + {{ $t('settings.show_scrollbars') }} + </BooleanSetting> + </li> + </ul> + </div> + <div class="setting-item"> + <h2>{{ $t('settings.visual_tweaks') }}</h2> + <ul class="setting-list"> + <li> + <ChoiceSetting + id="forcedRoundness" + path="forcedRoundness" + :options="forcedRoundnessOptions" + > + {{ $t('settings.style.themes3.hacks.force_interface_roundness') }} + </ChoiceSetting> + </li> + <li> + <ChoiceSetting + id="underlayOverride" + path="theme3hacks.underlay" + :options="underlayOverrideModes" + > + {{ $t('settings.style.themes3.hacks.underlay_overrides') }} + </ChoiceSetting> + </li> + <li v-if="instanceWallpaperUsed"> + <BooleanSetting path="hideInstanceWallpaper"> + {{ $t('settings.hide_wallpaper') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting + path="forceThemeRecompilation" + :expert="1" + > + {{ $t('settings.force_theme_recompilation_debug') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting + path="themeDebug" + :expert="1" + > + {{ $t('settings.theme_debug') }} + </BooleanSetting> + </li> + </ul> + </div> + </div> +</template> + +<script src="./appearance_tab.js"></script> + +<style lang="scss"> +.appearance-tab { + .theme-notice { + padding: 0.5em; + margin: 1em; + } + + .column-settings { + display: flex; + justify-content: space-evenly; + flex-wrap: wrap; + } + + .column-settings .size-label { + display: block; + margin-bottom: 0.5em; + margin-top: 0.5em; + } + + .theme-list { + list-style: none; + display: flex; + flex-wrap: wrap; + margin: -0.5em 0; + height: 25em; + overflow-x: hidden; + overflow-y: auto; + scrollbar-gutter: stable; + border-radius: var(--roundness); + border: 1px solid var(--border); + padding: 0; + + .theme-preview { + font-size: 1rem; // fix for firefox + width: 19rem; + display: flex; + flex-direction: column; + align-items: center; + margin: 0.5em; + + &.placeholder { + opacity: 0.2; + } + + .theme-preview-container { + pointer-events: none; + zoom: 0.5; + border: none; + border-radius: var(--roundness); + text-align: left; + } + } + } +} +</style> diff --git a/src/components/settings_modal/tabs/filtering_tab.js b/src/components/settings_modal/tabs/filtering_tab.js index 7c37f0bc..fbace15d 100644 --- a/src/components/settings_modal/tabs/filtering_tab.js +++ b/src/components/settings_modal/tabs/filtering_tab.js @@ -1,6 +1,7 @@ import { filter, trim, debounce } from 'lodash' import BooleanSetting from '../helpers/boolean_setting.vue' import ChoiceSetting from '../helpers/choice_setting.vue' +import UnitSetting from '../helpers/unit_setting.vue' import IntegerSetting from '../helpers/integer_setting.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' @@ -19,6 +20,7 @@ const FilteringTab = { components: { BooleanSetting, ChoiceSetting, + UnitSetting, IntegerSetting }, computed: { diff --git a/src/components/settings_modal/tabs/filtering_tab.vue b/src/components/settings_modal/tabs/filtering_tab.vue index 9e82fcfd..c86810d5 100644 --- a/src/components/settings_modal/tabs/filtering_tab.vue +++ b/src/components/settings_modal/tabs/filtering_tab.vue @@ -45,6 +45,11 @@ </BooleanSetting> </li> <li> + <BooleanSetting path="muteSensitiveStatuses"> + {{ $t('settings.mute_sensitive_posts') }} + </BooleanSetting> + </li> + <li> <BooleanSetting path="hidePostStats"> {{ $t('settings.hide_post_stats') }} </BooleanSetting> @@ -67,7 +72,7 @@ <textarea id="muteWords" v-model="muteWordsString" - class="resize-height" + class="input resize-height" /> <div>{{ $t('settings.filtering_explanation') }}</div> </li> @@ -96,6 +101,17 @@ {{ $t('settings.hide_scrobbles') }} </BooleanSetting> </li> + <li> + <UnitSetting + key="hideScrobblesAfter" + path="hideScrobblesAfter" + :units="['m', 'h', 'd']" + unitSet="time" + expert="1" + > + {{ $t('settings.hide_scrobbles_after') }} + </UnitSetting> + </li> </ul> </div> <div diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js index 3f2bcb13..e9e8fa72 100644 --- a/src/components/settings_modal/tabs/general_tab.js +++ b/src/components/settings_modal/tabs/general_tab.js @@ -3,7 +3,7 @@ import ChoiceSetting from '../helpers/choice_setting.vue' import ScopeSelector from 'src/components/scope_selector/scope_selector.vue' import IntegerSetting from '../helpers/integer_setting.vue' import FloatSetting from '../helpers/float_setting.vue' -import SizeSetting, { defaultHorizontalUnits } from '../helpers/size_setting.vue' +import UnitSetting from '../helpers/unit_setting.vue' import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' @@ -40,11 +40,6 @@ const GeneralTab = { value: mode, label: this.$t(`settings.mention_link_display_${mode}`) })), - thirdColumnModeOptions: ['none', 'notifications', 'postform'].map(mode => ({ - key: mode, - value: mode, - label: this.$t(`settings.third_column_mode_${mode}`) - })), userPopoverAvatarActionOptions: ['close', 'zoom', 'open'].map(mode => ({ key: mode, value: mode, @@ -64,15 +59,12 @@ const GeneralTab = { ChoiceSetting, IntegerSetting, FloatSetting, - SizeSetting, + UnitSetting, InterfaceLanguageSwitcher, ScopeSelector, ProfileSettingIndicator }, computed: { - horizontalUnits () { - return defaultHorizontalUnits - }, postFormats () { return this.$store.state.instance.postFormats || [] }, @@ -83,23 +75,6 @@ const GeneralTab = { label: this.$t(`post_status.content_type["${format}"]`) })) }, - columns () { - const mode = this.$store.getters.mergedConfig.thirdColumnMode - - const notif = mode === 'none' ? [] : ['notifs'] - - if (this.$store.getters.mergedConfig.sidebarRight || mode === 'postform') { - return [...notif, 'content', 'sidebar'] - } else { - return ['sidebar', 'content', ...notif] - } - }, - instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel }, - instanceWallpaperUsed () { - return this.$store.state.instance.background && - !this.$store.state.users.currentUser.background_image - }, - instanceShoutboxPresent () { return this.$store.state.instance.shoutAvailable }, language: { get: function () { return this.$store.getters.mergedConfig.interfaceLanguage }, set: function (val) { diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue index f56fa8e0..82d5da89 100644 --- a/src/components/settings_modal/tabs/general_tab.vue +++ b/src/components/settings_modal/tabs/general_tab.vue @@ -15,11 +15,6 @@ {{ $t('settings.hide_isp') }} </BooleanSetting> </li> - <li v-if="instanceWallpaperUsed"> - <BooleanSetting path="hideInstanceWallpaper"> - {{ $t('settings.hide_wallpaper') }} - </BooleanSetting> - </li> <li> <BooleanSetting path="stopGifs"> {{ $t('settings.stop_gifs') }} @@ -98,53 +93,6 @@ {{ $t('settings.hide_shoutbox') }} </BooleanSetting> </li> - <li> - <h3>{{ $t('settings.columns') }}</h3> - </li> - <li> - <BooleanSetting path="disableStickyHeaders"> - {{ $t('settings.disable_sticky_headers') }} - </BooleanSetting> - </li> - <li> - <BooleanSetting path="showScrollbars"> - {{ $t('settings.show_scrollbars') }} - </BooleanSetting> - </li> - <li> - <BooleanSetting path="sidebarRight"> - {{ $t('settings.right_sidebar') }} - </BooleanSetting> - </li> - <li> - <BooleanSetting path="navbarColumnStretch"> - {{ $t('settings.navbar_column_stretch') }} - </BooleanSetting> - </li> - <li> - <ChoiceSetting - v-if="user" - id="thirdColumnMode" - path="thirdColumnMode" - :options="thirdColumnModeOptions" - > - {{ $t('settings.third_column_mode') }} - </ChoiceSetting> - </li> - <li v-if="expertLevel > 0"> - {{ $t('settings.column_sizes') }} - <div class="column-settings"> - <SizeSetting - v-for="column in columns" - :key="column" - :path="column + 'ColumnWidth'" - :units="horizontalUnits" - expert="1" - > - {{ $t('settings.column_sizes_' + column) }} - </SizeSetting> - </div> - </li> <li class="select-multiple"> <span class="label">{{ $t('settings.confirm_dialogs') }}</span> <ul class="option-list"> @@ -270,14 +218,28 @@ </BooleanSetting> </li> <li> - <FloatSetting - v-if="user" - path="emojiReactionsScale" + <BooleanSetting + path="useAbsoluteTimeFormat" expert="1" > - {{ $t('settings.emoji_reactions_scale') }} - </FloatSetting> + {{ $t('settings.absolute_time_format') }} + </BooleanSetting> </li> + <ul + class="setting-list suboptions" + v-if="mergedConfig.useAbsoluteTimeFormat" + > + <li> + <UnitSetting + path="absoluteTimeFormatMinAge" + unit-set="time" + :units="['s', 'm', 'h', 'd']" + :min="0" + > + {{ $t('settings.absolute_time_format_min_age') }} + </UnitSetting> + </li> + </ul> <h3>{{ $t('settings.attachments') }}</h3> <li> <BooleanSetting @@ -520,17 +482,3 @@ </template> <script src="./general_tab.js"></script> - -<style lang="scss"> -.column-settings { - display: flex; - justify-content: space-evenly; - flex-wrap: wrap; -} - -.column-settings .size-label { - display: block; - margin-bottom: 0.5em; - margin-top: 0.5em; -} -</style> diff --git a/src/components/settings_modal/tabs/notifications_tab.vue b/src/components/settings_modal/tabs/notifications_tab.vue index 9ace4c36..10228888 100644 --- a/src/components/settings_modal/tabs/notifications_tab.vue +++ b/src/components/settings_modal/tabs/notifications_tab.vue @@ -19,7 +19,10 @@ </div> </li> <li> - <BooleanSetting path="unseenAtTop" expert="1"> + <BooleanSetting + path="unseenAtTop" + expert="1" + > {{ $t('settings.notification_setting_unseen_at_top') }} </BooleanSetting> </li> @@ -38,7 +41,9 @@ </li> <li> <h3> {{ $t('settings.notification_visibility') }}</h3> - <p v-if="expertLevel > 0">{{ $t('settings.notification_setting_filters_chrome_push') }}</p> + <p v-if="expertLevel > 0"> + {{ $t('settings.notification_setting_filters_chrome_push') }} + </p> <ul class="setting-list two-column"> <li> <h4> {{ $t('settings.notification_visibility_mentions') }}</h4> @@ -56,6 +61,21 @@ </ul> </li> <li> + <h4> {{ $t('settings.notification_visibility_statuses') }}</h4> + <ul class="setting-list"> + <li> + <BooleanSetting path="notificationVisibility.statuses"> + {{ $t('settings.notification_visibility_in_column') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting path="notificationNative.statuses"> + {{ $t('settings.notification_visibility_native_notifications') }} + </BooleanSetting> + </li> + </ul> + </li> + <li> <h4> {{ $t('settings.notification_visibility_likes') }}</h4> <ul class="setting-list"> <li> diff --git a/src/components/settings_modal/tabs/profile_tab.scss b/src/components/settings_modal/tabs/profile_tab.scss index ee253ffe..7eda943b 100644 --- a/src/components/settings_modal/tabs/profile_tab.scss +++ b/src/components/settings_modal/tabs/profile_tab.scss @@ -1,5 +1,3 @@ -@import "../../../variables"; - .profile-tab { .bio { margin: 0; @@ -43,16 +41,14 @@ display: block; width: 100%; height: 100%; - border-radius: $fallback--avatarRadius; - border-radius: var(--avatarRadius, $fallback--avatarRadius); + border-radius: var(--roundness); } .reset-button { position: absolute; top: 0.2em; right: 0.2em; - border-radius: $fallback--tooltipRadius; - border-radius: var(--tooltipRadius, $fallback--tooltipRadius); + border-radius: var(--roundness); background-color: rgb(0 0 0 / 60%); opacity: 0.7; width: 1.5em; diff --git a/src/components/settings_modal/tabs/profile_tab.vue b/src/components/settings_modal/tabs/profile_tab.vue index de5219a7..034034a1 100644 --- a/src/components/settings_modal/tabs/profile_tab.vue +++ b/src/components/settings_modal/tabs/profile_tab.vue @@ -12,7 +12,7 @@ <input id="username" v-model="newName" - class="name-changer" + class="input name-changer" v-bind="propsToNative(inputProps)" > </template> @@ -26,7 +26,7 @@ <template #default="inputProps"> <textarea v-model="newBio" - class="bio resize-height" + class="input bio resize-height" v-bind="propsToNative(inputProps)" /> </template> @@ -47,7 +47,7 @@ id="birthday" v-model="newBirthday" type="date" - class="birthday-input" + class="input birthday-input" > <Checkbox v-model="showBirthday"> {{ $t('settings.birthday.show_birthday') }} @@ -71,6 +71,7 @@ v-model="newFields[i].name" :placeholder="$t('settings.profile_fields.name')" v-bind="propsToNative(inputProps)" + class="input" > </template> </EmojiInput> @@ -85,6 +86,7 @@ v-model="newFields[i].value" :placeholder="$t('settings.profile_fields.value')" v-bind="propsToNative(inputProps)" + class="input" > </template> </EmojiInput> @@ -205,6 +207,7 @@ <div> <input type="file" + class="input" @change="uploadFile('banner', $event)" > </div> @@ -247,6 +250,7 @@ <div> <input type="file" + class="input" @change="uploadFile('background', $event)" > </div> diff --git a/src/components/settings_modal/tabs/security_tab/mfa.vue b/src/components/settings_modal/tabs/security_tab/mfa.vue index ee5b6b13..9421b16e 100644 --- a/src/components/settings_modal/tabs/security_tab/mfa.vue +++ b/src/components/settings_modal/tabs/security_tab/mfa.vue @@ -99,12 +99,14 @@ <input v-model="otpConfirmToken" type="text" + class="input" > <p>{{ $t('settings.enter_current_password_to_confirm') }}:</p> <input v-model="currentPassword" type="password" + class="input" > <div class="confirm-otp-actions"> <button @@ -137,8 +139,6 @@ <script src="./mfa.js"></script> <style lang="scss"> -@import "../../../../variables"; - .mfa-settings { .mfa-heading, .method-item { @@ -149,8 +149,7 @@ } .warning { - color: $fallback--cOrange; - color: var(--cOrange, $fallback--cOrange); + color: var(--cOrange); } .setup-otp { diff --git a/src/components/settings_modal/tabs/security_tab/mfa_backup_codes.vue b/src/components/settings_modal/tabs/security_tab/mfa_backup_codes.vue index 923161b2..32a8a759 100644 --- a/src/components/settings_modal/tabs/security_tab/mfa_backup_codes.vue +++ b/src/components/settings_modal/tabs/security_tab/mfa_backup_codes.vue @@ -21,16 +21,13 @@ </template> <script src="./mfa_backup_codes.js"></script> <style lang="scss"> -@import "../../../../variables"; - .mfa-backup-codes { .warning { - color: $fallback--cOrange; - color: var(--cOrange, $fallback--cOrange); + color: var(--cOrange); } .backup-codes { - font-family: var(--postCodeFont, monospace); + font-family: var(--monoFont); } } </style> diff --git a/src/components/settings_modal/tabs/security_tab/mfa_totp.vue b/src/components/settings_modal/tabs/security_tab/mfa_totp.vue index 8e767bd0..99b66818 100644 --- a/src/components/settings_modal/tabs/security_tab/mfa_totp.vue +++ b/src/components/settings_modal/tabs/security_tab/mfa_totp.vue @@ -30,6 +30,7 @@ <input v-model="currentPassword" type="password" + class="input" > </confirm> <div diff --git a/src/components/settings_modal/tabs/security_tab/security_tab.vue b/src/components/settings_modal/tabs/security_tab/security_tab.vue index d36d478f..74103f6f 100644 --- a/src/components/settings_modal/tabs/security_tab/security_tab.vue +++ b/src/components/settings_modal/tabs/security_tab/security_tab.vue @@ -8,6 +8,7 @@ v-model="newEmail" type="email" autocomplete="email" + class="input" > </div> <div> @@ -16,6 +17,7 @@ v-model="changeEmailPassword" type="password" autocomplete="current-password" + class="input" > </div> <button @@ -40,6 +42,7 @@ <input v-model="changePasswordInputs[0]" type="password" + class="input" > </div> <div> @@ -47,6 +50,7 @@ <input v-model="changePasswordInputs[1]" type="password" + class="input" > </div> <div> @@ -54,6 +58,7 @@ <input v-model="changePasswordInputs[2]" type="password" + class="input" > </div> <button @@ -155,6 +160,7 @@ </i18n-t> <input v-model="addAliasTarget" + class="input" > </div> <button @@ -187,6 +193,7 @@ </i18n-t> <input v-model="moveAccountTarget" + class="input" > </div> <div> @@ -195,6 +202,7 @@ v-model="moveAccountPassword" type="password" autocomplete="current-password" + class="input" > </div> <button @@ -222,6 +230,7 @@ <input v-model="deleteAccountConfirmPasswordInput" type="password" + class="input" > <button class="btn button-default" diff --git a/src/components/settings_modal/tabs/theme_tab/preview.vue b/src/components/settings_modal/tabs/theme_tab/theme_preview.vue index d755279a..dbaecee4 100644 --- a/src/components/settings_modal/tabs/theme_tab/preview.vue +++ b/src/components/settings_modal/tabs/theme_tab/theme_preview.vue @@ -1,11 +1,11 @@ <template> - <div class="preview-container"> + <div class="theme-preview-container"> <div class="underlay underlay-preview" /> <div class="panel dummy"> <div class="panel-heading"> <div class="title"> {{ $t('settings.style.preview.header') }} - <span class="badge badge-notification"> + <span class="badge -notification"> 99 </span> </div> @@ -81,7 +81,7 @@ class="faint" scope="global" > - <a style="color: var(--faintLink);"> + <a style="color: var(--linkFaint);"> {{ $t('settings.style.preview.faint_link') }} </a> </i18n-t> @@ -95,17 +95,13 @@ <input :value="$t('settings.style.preview.input')" type="text" + class="input" > <div class="actions"> - <span class="checkbox"> - <input - id="preview_checkbox" - checked="very yes" - type="checkbox" - > - <label for="preview_checkbox">{{ $t('settings.style.preview.checkbox') }}</label> - </span> + <Checkbox> + {{ $t('settings.style.preview.checkbox') }} + </Checkbox> <button class="btn button-default"> {{ $t('settings.style.preview.button') }} </button> @@ -116,6 +112,7 @@ </template> <script> +import Checkbox from 'src/components/checkbox/checkbox.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faTimes, @@ -131,19 +128,123 @@ library.add( faReply ) -export default {} +export default { + components: { + Checkbox + } +} </script> <style lang="scss"> -.preview-container { +.theme-preview-container { position: relative; -} + border-top: 1px dashed; + border-bottom: 1px dashed; + border-color: var(--border); + margin: 1em 0; + padding: 1em; + background-color: var(--wallpaper); + background-image: var(--body-background-image); + background-size: cover; + background-position: 50% 50%; + + .theme-preview-content { + padding: 20px; + } + + .dummy { + .post { + font-family: var(--postFont); + display: flex; + + .content { + flex: 1; + + h4 { + margin-bottom: 0.25em; + } + + .icons { + margin-top: 0.5em; + display: flex; + + i { + margin-right: 1em; + } + } + } + } + + .after-post { + margin-top: 1em; + display: flex; + align-items: center; + } + + .avatar, + .avatar-alt { + background: + linear-gradient( + 135deg, + #b8e1fc 0%, + #a9d2f3 10%, + #90bae4 25%, + #90bcea 37%, + #90bff0 50%, + #6ba8e5 51%, + #a2daf5 83%, + #bdf3fd 100% + ); + color: black; + font-family: sans-serif; + text-align: center; + margin-right: 1em; + } + + .avatar-alt { + flex: 0 auto; + margin-left: 28px; + font-size: 12px; + min-width: 20px; + min-height: 20px; + line-height: 20px; + } + + .avatar { + flex: 0 auto; + width: 48px; + height: 48px; + font-size: 14px; + line-height: 48px; + } + + .actions { + display: flex; + align-items: baseline; + + .checkbox { + margin-right: 1em; + flex: 1; + } + } + + .separator { + margin: 1em; + border-bottom: 1px solid; + border-color: var(--border); + } + + .btn { + min-width: 3em; + } + } -.underlay-preview { - position: absolute; - top: 0; - bottom: 0; - left: 10px; - right: 10px; + .underlay-preview { + position: absolute; + top: 0; + bottom: 0; + left: 10px; + right: 10px; + } } -</style> + </style> diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.js b/src/components/settings_modal/tabs/theme_tab/theme_tab.js index 58f8d44a..25836559 100644 --- a/src/components/settings_modal/tabs/theme_tab/theme_tab.js +++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.js @@ -1,18 +1,11 @@ import { rgb2hex, hex2rgb, - getContrastRatioLayers + getContrastRatioLayers, + relativeLuminance } from 'src/services/color_convert/color_convert.js' import { - DEFAULT_SHADOWS, - generateColors, - generateShadows, - generateRadii, - generateFonts, - composePreset, - getThemes, - shadows2to3, - colors2to3 + getThemes } from 'src/services/style_setter/style_setter.js' import { newImporter, @@ -25,8 +18,23 @@ import { CURRENT_VERSION, OPACITIES, getLayers, - getOpacitySlot + getOpacitySlot, + DEFAULT_SHADOWS, + generateColors, + generateShadows, + generateRadii, + generateFonts, + shadows2to3, + colors2to3 } from 'src/services/theme_data/theme_data.service.js' + +import { convertTheme2To3 } from 'src/services/theme_data/theme2_to_theme3.js' +import { init } from 'src/services/theme_data/theme_data_3.service.js' +import { + getCssRules, + getScopedVersion +} from 'src/services/theme_data/css_utils.js' + import ColorInput from 'src/components/color_input/color_input.vue' import RangeInput from 'src/components/range_input/range_input.vue' import OpacityInput from 'src/components/opacity_input/opacity_input.vue' @@ -37,7 +45,7 @@ import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' import Checkbox from 'src/components/checkbox/checkbox.vue' import Select from 'src/components/select/select.vue' -import Preview from './preview.vue' +import Preview from './theme_preview.vue' // List of color values used in v1 const v1OnlyNames = [ @@ -62,6 +70,7 @@ const colorConvert = (color) => { export default { data () { return { + themeV3Preview: [], themeImporter: newImporter({ validator: this.importValidator, onImport: this.onImport, @@ -78,10 +87,7 @@ export default { tempImportFile: undefined, engineVersion: 0, - previewShadows: {}, - previewColors: {}, - previewRadii: {}, - previewFonts: {}, + previewTheme: {}, shadowsInvalid: true, colorsInvalid: true, @@ -232,13 +238,6 @@ export default { chatMessage: this.chatMessageRadiusLocal } }, - preview () { - return composePreset(this.previewColors, this.previewRadii, this.previewShadows, this.previewFonts) - }, - previewTheme () { - if (!this.preview.theme.colors) return { colors: {}, opacity: {}, radii: {}, shadows: {}, fonts: {} } - return this.preview.theme - }, // This needs optimization maybe previewContrast () { try { @@ -306,14 +305,6 @@ export default { return {} } }, - previewRules () { - if (!this.preview.rules) return '' - return [ - ...Object.values(this.preview.rules), - 'color: var(--text)', - 'font-family: var(--interfaceFont, sans-serif)' - ].join(';') - }, shadowsAvailable () { return Object.keys(DEFAULT_SHADOWS).sort() }, @@ -511,16 +502,15 @@ export default { } }, setCustomTheme () { - this.$store.dispatch('setOption', { - name: 'customTheme', - value: { + this.$store.dispatch('setThemeV2', { + customTheme: { + ignore: true, + themeFileVersion: this.selectedVersion, themeEngineVersion: CURRENT_VERSION, ...this.previewTheme - } - }) - this.$store.dispatch('setOption', { - name: 'customThemeSource', - value: { + }, + customThemeSource: { + themeFileVersion: this.selectedVersion, themeEngineVersion: CURRENT_VERSION, shadows: this.shadowsLocal, fonts: this.fontsLocal, @@ -530,16 +520,24 @@ export default { } }) }, - updatePreviewColorsAndShadows () { - this.previewColors = generateColors({ + updatePreviewColors () { + const result = generateColors({ opacity: this.currentOpacity, colors: this.currentColors }) - this.previewShadows = generateShadows( - { shadows: this.shadowsLocal, opacity: this.previewTheme.opacity, themeEngineVersion: this.engineVersion }, - this.previewColors.theme.colors, - this.previewColors.mod - ) + this.previewTheme.colors = result.theme.colors + this.previewTheme.opacity = result.theme.opacity + }, + updatePreviewShadows () { + this.previewTheme.shadows = generateShadows( + { + shadows: this.shadowsLocal, + opacity: this.previewTheme.opacity, + themeEngineVersion: this.engineVersion + }, + this.previewTheme.colors, + relativeLuminance(this.previewTheme.colors.bg) < 0.5 ? 1 : -1 + ).theme.shadows }, importTheme () { this.themeImporter.importData() }, exportTheme () { this.themeExporter.exportData() }, @@ -608,7 +606,7 @@ export default { normalizeLocalState (theme, version = 0, source, forceSource = false) { let input if (typeof source !== 'undefined') { - if (forceSource || source.themeEngineVersion === CURRENT_VERSION) { + if (forceSource || source?.themeEngineVersion === CURRENT_VERSION) { input = source version = source.themeEngineVersion } else { @@ -690,6 +688,8 @@ export default { } else { this.shadowsLocal = shadows } + this.updatePreviewColors() + this.updatePreviewShadows() this.shadowSelected = this.shadowsAvailable[0] } @@ -697,12 +697,25 @@ export default { this.clearFonts() this.fontsLocal = fonts } + }, + updateTheme3Preview () { + const theme2 = convertTheme2To3(this.previewTheme) + const theme3 = init({ + inputRuleset: theme2, + ultimateBackgroundColor: '#000000', + liteMode: true + }) + + this.themeV3Preview = getScopedVersion( + getCssRules(theme3.eager), + '#theme-preview' + ).join('\n') } }, watch: { currentRadii () { try { - this.previewRadii = generateRadii({ radii: this.currentRadii }) + this.previewTheme.radii = generateRadii({ radii: this.currentRadii }).theme.radii this.radiiInvalid = false } catch (e) { this.radiiInvalid = true @@ -711,9 +724,8 @@ export default { }, shadowsLocal: { handler () { - if (Object.getOwnPropertyNames(this.previewColors).length === 1) return try { - this.updatePreviewColorsAndShadows() + this.updatePreviewShadows() this.shadowsInvalid = false } catch (e) { this.shadowsInvalid = true @@ -725,7 +737,7 @@ export default { fontsLocal: { handler () { try { - this.previewFonts = generateFonts({ fonts: this.fontsLocal }) + this.previewTheme.fonts = generateFonts({ fonts: this.fontsLocal }).theme.fonts this.fontsInvalid = false } catch (e) { this.fontsInvalid = true @@ -736,18 +748,16 @@ export default { }, currentColors () { try { - this.updatePreviewColorsAndShadows() + this.updatePreviewColors() this.colorsInvalid = false - this.shadowsInvalid = false } catch (e) { this.colorsInvalid = true - this.shadowsInvalid = true console.warn(e) } }, currentOpacity () { try { - this.updatePreviewColorsAndShadows() + this.updatePreviewColors() } catch (e) { console.warn(e) } diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.scss b/src/components/settings_modal/tabs/theme_tab/theme_tab.scss index 9935c2e7..84933fb8 100644 --- a/src/components/settings_modal/tabs/theme_tab/theme_tab.scss +++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.scss @@ -1,6 +1,9 @@ -@import "src/variables"; - .theme-tab { + .deprecation-warning { + padding: 0.5em; + margin: 2em; + } + padding-bottom: 2em; .preset-switcher { @@ -12,6 +15,10 @@ margin-right: 0.25em; } + .btn-group .btn { + margin: 0; + } + .style-control { display: flex; align-items: baseline; @@ -159,111 +166,6 @@ } } - .preview-container { - border-top: 1px dashed; - border-bottom: 1px dashed; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); - margin: 1em 0; - padding: 1em; - background-color: var(--wallpaper); - background-image: var(--body-background-image); - background-size: cover; - background-position: 50% 50%; - - .dummy { - .post { - font-family: var(--postFont); - display: flex; - - .content { - flex: 1; - - h4 { - margin-bottom: 0.25em; - } - - .icons { - margin-top: 0.5em; - display: flex; - - i { - margin-right: 1em; - } - } - } - } - - .after-post { - margin-top: 1em; - display: flex; - align-items: center; - } - - .avatar, - .avatar-alt { - background: - linear-gradient( - 135deg, - #b8e1fc 0%, - #a9d2f3 10%, - #90bae4 25%, - #90bcea 37%, - #90bff0 50%, - #6ba8e5 51%, - #a2daf5 83%, - #bdf3fd 100% - ); - color: black; - font-family: sans-serif; - text-align: center; - margin-right: 1em; - } - - .avatar-alt { - flex: 0 auto; - margin-left: 28px; - font-size: 12px; - min-width: 20px; - min-height: 20px; - line-height: 20px; - border-radius: $fallback--avatarAltRadius; - border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); - } - - .avatar { - flex: 0 auto; - width: 48px; - height: 48px; - font-size: 14px; - line-height: 48px; - } - - .actions { - display: flex; - align-items: baseline; - - .checkbox { - display: inline-flex; - align-items: baseline; - margin-right: 1em; - flex: 1; - } - } - - .separator { - margin: 1em; - border-bottom: 1px solid; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); - } - - .btn { - min-width: 3em; - } - } - } - .radius-item { flex-basis: auto; } @@ -296,7 +198,7 @@ border: 0; box-shadow: none; background: transparent; - color: var(--faint, $fallback--faint); + color: var(--textFaint); align-self: stretch; } @@ -316,10 +218,6 @@ max-width: 50em; } - .theme-preview-content { - padding: 20px; - } - .theme-warning { display: flex; align-items: baseline; diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.vue b/src/components/settings_modal/tabs/theme_tab/theme_tab.vue index ff2fece9..4498c143 100644 --- a/src/components/settings_modal/tabs/theme_tab/theme_tab.vue +++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.vue @@ -1,5 +1,8 @@ <template> <div class="theme-tab"> + <div class="alert warning deprecation-warning"> + {{ $t("settings.style.themes2_outdated") }} + </div> <div class="presets-container"> <div class="save-load"> <div @@ -120,7 +123,19 @@ </div> </div> - <preview :style="previewRules" /> + <!-- eslint-disable vue/no-v-text-v-html-on-component --> + <component :is="'style'" v-html="themeV3Preview"/> + <!-- eslint-enable vue/no-v-text-v-html-on-component --> + <preview id="theme-preview"/> + + <div> + <button + class="btn button-default" + @click="updateTheme3Preview" + > + {{ $t("settings.style.update_preview") }} + </button> + </div> <keep-alive> <tab-switcher key="style-tweak"> @@ -156,7 +171,7 @@ <OpacityInput v-model="bgOpacityLocal" name="bgOpacity" - :fallback="previewTheme.opacity.bg" + :fallback="previewTheme.opacity?.bg" /> <ColorInput v-model="textColorLocal" @@ -167,14 +182,14 @@ <ColorInput v-model="accentColorLocal" name="accentColor" - :fallback="previewTheme.colors.link" + :fallback="previewTheme.colors?.link" :label="$t('settings.accent')" :show-optional-tickbox="typeof linkColorLocal !== 'undefined'" /> <ColorInput v-model="linkColorLocal" name="linkColor" - :fallback="previewTheme.colors.accent" + :fallback="previewTheme.colors?.accent" :label="$t('settings.links')" :show-optional-tickbox="typeof accentColorLocal !== 'undefined'" /> @@ -190,13 +205,13 @@ v-model="fgTextColorLocal" name="fgTextColor" :label="$t('settings.text')" - :fallback="previewTheme.colors.fgText" + :fallback="previewTheme.colors?.fgText" /> <ColorInput v-model="fgLinkColorLocal" name="fgLinkColor" :label="$t('settings.links')" - :fallback="previewTheme.colors.fgLink" + :fallback="previewTheme.colors?.fgLink" /> <p>{{ $t('settings.style.common_colors.foreground_hint') }}</p> </div> @@ -256,14 +271,14 @@ <ColorInput v-model="postLinkColorLocal" name="postLinkColor" - :fallback="previewTheme.colors.accent" + :fallback="previewTheme.colors?.accent" :label="$t('settings.links')" /> <ContrastRatio :contrast="previewContrast.postLink" /> <ColorInput v-model="postGreentextColorLocal" name="postGreentextColor" - :fallback="previewTheme.colors.cGreen" + :fallback="previewTheme.colors?.cGreen" :label="$t('settings.greentext')" /> <ContrastRatio :contrast="previewContrast.postGreentext" /> @@ -272,13 +287,13 @@ v-model="alertErrorColorLocal" name="alertError" :label="$t('settings.style.advanced_colors.alert_error')" - :fallback="previewTheme.colors.alertError" + :fallback="previewTheme.colors?.alertError" /> <ColorInput v-model="alertErrorTextColorLocal" name="alertErrorText" :label="$t('settings.text')" - :fallback="previewTheme.colors.alertErrorText" + :fallback="previewTheme.colors?.alertErrorText" /> <ContrastRatio :contrast="previewContrast.alertErrorText" @@ -288,13 +303,13 @@ v-model="alertWarningColorLocal" name="alertWarning" :label="$t('settings.style.advanced_colors.alert_warning')" - :fallback="previewTheme.colors.alertWarning" + :fallback="previewTheme.colors?.alertWarning" /> <ColorInput v-model="alertWarningTextColorLocal" name="alertWarningText" :label="$t('settings.text')" - :fallback="previewTheme.colors.alertWarningText" + :fallback="previewTheme.colors?.alertWarningText" /> <ContrastRatio :contrast="previewContrast.alertWarningText" @@ -304,13 +319,13 @@ v-model="alertNeutralColorLocal" name="alertNeutral" :label="$t('settings.style.advanced_colors.alert_neutral')" - :fallback="previewTheme.colors.alertNeutral" + :fallback="previewTheme.colors?.alertNeutral" /> <ColorInput v-model="alertNeutralTextColorLocal" name="alertNeutralText" :label="$t('settings.text')" - :fallback="previewTheme.colors.alertNeutralText" + :fallback="previewTheme.colors?.alertNeutralText" /> <ContrastRatio :contrast="previewContrast.alertNeutralText" @@ -319,7 +334,7 @@ <OpacityInput v-model="alertOpacityLocal" name="alertOpacity" - :fallback="previewTheme.opacity.alert" + :fallback="previewTheme.opacity?.alert" /> </div> <div class="color-item"> @@ -328,13 +343,13 @@ v-model="badgeNotificationColorLocal" name="badgeNotification" :label="$t('settings.style.advanced_colors.badge_notification')" - :fallback="previewTheme.colors.badgeNotification" + :fallback="previewTheme.colors?.badgeNotification" /> <ColorInput v-model="badgeNotificationTextColorLocal" name="badgeNotificationText" :label="$t('settings.text')" - :fallback="previewTheme.colors.badgeNotificationText" + :fallback="previewTheme.colors?.badgeNotificationText" /> <ContrastRatio :contrast="previewContrast.badgeNotificationText" @@ -346,19 +361,19 @@ <ColorInput v-model="panelColorLocal" name="panelColor" - :fallback="previewTheme.colors.panel" + :fallback="previewTheme.colors?.panel" :label="$t('settings.background')" /> <OpacityInput v-model="panelOpacityLocal" name="panelOpacity" - :fallback="previewTheme.opacity.panel" + :fallback="previewTheme.opacity?.panel" :disabled="panelColorLocal === 'transparent'" /> <ColorInput v-model="panelTextColorLocal" name="panelTextColor" - :fallback="previewTheme.colors.panelText" + :fallback="previewTheme.colors?.panelText" :label="$t('settings.text')" /> <ContrastRatio @@ -368,7 +383,7 @@ <ColorInput v-model="panelLinkColorLocal" name="panelLinkColor" - :fallback="previewTheme.colors.panelLink" + :fallback="previewTheme.colors?.panelLink" :label="$t('settings.links')" /> <ContrastRatio @@ -381,20 +396,20 @@ <ColorInput v-model="topBarColorLocal" name="topBarColor" - :fallback="previewTheme.colors.topBar" + :fallback="previewTheme.colors?.topBar" :label="$t('settings.background')" /> <ColorInput v-model="topBarTextColorLocal" name="topBarTextColor" - :fallback="previewTheme.colors.topBarText" + :fallback="previewTheme.colors?.topBarText" :label="$t('settings.text')" /> <ContrastRatio :contrast="previewContrast.topBarText" /> <ColorInput v-model="topBarLinkColorLocal" name="topBarLinkColor" - :fallback="previewTheme.colors.topBarLink" + :fallback="previewTheme.colors?.topBarLink" :label="$t('settings.links')" /> <ContrastRatio :contrast="previewContrast.topBarLink" /> @@ -404,19 +419,19 @@ <ColorInput v-model="inputColorLocal" name="inputColor" - :fallback="previewTheme.colors.input" + :fallback="previewTheme.colors?.input" :label="$t('settings.background')" /> <OpacityInput v-model="inputOpacityLocal" name="inputOpacity" - :fallback="previewTheme.opacity.input" + :fallback="previewTheme.opacity?.input" :disabled="inputColorLocal === 'transparent'" /> <ColorInput v-model="inputTextColorLocal" name="inputTextColor" - :fallback="previewTheme.colors.inputText" + :fallback="previewTheme.colors?.inputText" :label="$t('settings.text')" /> <ContrastRatio :contrast="previewContrast.inputText" /> @@ -426,33 +441,33 @@ <ColorInput v-model="btnColorLocal" name="btnColor" - :fallback="previewTheme.colors.btn" + :fallback="previewTheme.colors?.btn" :label="$t('settings.background')" /> <OpacityInput v-model="btnOpacityLocal" name="btnOpacity" - :fallback="previewTheme.opacity.btn" + :fallback="previewTheme.opacity?.btn" :disabled="btnColorLocal === 'transparent'" /> <ColorInput v-model="btnTextColorLocal" name="btnTextColor" - :fallback="previewTheme.colors.btnText" + :fallback="previewTheme.colors?.btnText" :label="$t('settings.text')" /> <ContrastRatio :contrast="previewContrast.btnText" /> <ColorInput v-model="btnPanelTextColorLocal" name="btnPanelTextColor" - :fallback="previewTheme.colors.btnPanelText" + :fallback="previewTheme.colors?.btnPanelText" :label="$t('settings.style.advanced_colors.panel_header')" /> <ContrastRatio :contrast="previewContrast.btnPanelText" /> <ColorInput v-model="btnTopBarTextColorLocal" name="btnTopBarTextColor" - :fallback="previewTheme.colors.btnTopBarText" + :fallback="previewTheme.colors?.btnTopBarText" :label="$t('settings.style.advanced_colors.top_bar')" /> <ContrastRatio :contrast="previewContrast.btnTopBarText" /> @@ -460,27 +475,27 @@ <ColorInput v-model="btnPressedColorLocal" name="btnPressedColor" - :fallback="previewTheme.colors.btnPressed" + :fallback="previewTheme.colors?.btnPressed" :label="$t('settings.background')" /> <ColorInput v-model="btnPressedTextColorLocal" name="btnPressedTextColor" - :fallback="previewTheme.colors.btnPressedText" + :fallback="previewTheme.colors?.btnPressedText" :label="$t('settings.text')" /> <ContrastRatio :contrast="previewContrast.btnPressedText" /> <ColorInput v-model="btnPressedPanelTextColorLocal" name="btnPressedPanelTextColor" - :fallback="previewTheme.colors.btnPressedPanelText" + :fallback="previewTheme.colors?.btnPressedPanelText" :label="$t('settings.style.advanced_colors.panel_header')" /> <ContrastRatio :contrast="previewContrast.btnPressedPanelText" /> <ColorInput v-model="btnPressedTopBarTextColorLocal" name="btnPressedTopBarTextColor" - :fallback="previewTheme.colors.btnPressedTopBarText" + :fallback="previewTheme.colors?.btnPressedTopBarText" :label="$t('settings.style.advanced_colors.top_bar')" /> <ContrastRatio :contrast="previewContrast.btnPressedTopBarText" /> @@ -488,52 +503,52 @@ <ColorInput v-model="btnDisabledColorLocal" name="btnDisabledColor" - :fallback="previewTheme.colors.btnDisabled" + :fallback="previewTheme.colors?.btnDisabled" :label="$t('settings.background')" /> <ColorInput v-model="btnDisabledTextColorLocal" name="btnDisabledTextColor" - :fallback="previewTheme.colors.btnDisabledText" + :fallback="previewTheme.colors?.btnDisabledText" :label="$t('settings.text')" /> <ColorInput v-model="btnDisabledPanelTextColorLocal" name="btnDisabledPanelTextColor" - :fallback="previewTheme.colors.btnDisabledPanelText" + :fallback="previewTheme.colors?.btnDisabledPanelText" :label="$t('settings.style.advanced_colors.panel_header')" /> <ColorInput v-model="btnDisabledTopBarTextColorLocal" name="btnDisabledTopBarTextColor" - :fallback="previewTheme.colors.btnDisabledTopBarText" + :fallback="previewTheme.colors?.btnDisabledTopBarText" :label="$t('settings.style.advanced_colors.top_bar')" /> <h5>{{ $t('settings.style.advanced_colors.toggled') }}</h5> <ColorInput v-model="btnToggledColorLocal" name="btnToggledColor" - :fallback="previewTheme.colors.btnToggled" + :fallback="previewTheme.colors?.btnToggled" :label="$t('settings.background')" /> <ColorInput v-model="btnToggledTextColorLocal" name="btnToggledTextColor" - :fallback="previewTheme.colors.btnToggledText" + :fallback="previewTheme.colors?.btnToggledText" :label="$t('settings.text')" /> <ContrastRatio :contrast="previewContrast.btnToggledText" /> <ColorInput v-model="btnToggledPanelTextColorLocal" name="btnToggledPanelTextColor" - :fallback="previewTheme.colors.btnToggledPanelText" + :fallback="previewTheme.colors?.btnToggledPanelText" :label="$t('settings.style.advanced_colors.panel_header')" /> <ContrastRatio :contrast="previewContrast.btnToggledPanelText" /> <ColorInput v-model="btnToggledTopBarTextColorLocal" name="btnToggledTopBarTextColor" - :fallback="previewTheme.colors.btnToggledTopBarText" + :fallback="previewTheme.colors?.btnToggledTopBarText" :label="$t('settings.style.advanced_colors.top_bar')" /> <ContrastRatio :contrast="previewContrast.btnToggledTopBarText" /> @@ -543,20 +558,20 @@ <ColorInput v-model="tabColorLocal" name="tabColor" - :fallback="previewTheme.colors.tab" + :fallback="previewTheme.colors?.tab" :label="$t('settings.background')" /> <ColorInput v-model="tabTextColorLocal" name="tabTextColor" - :fallback="previewTheme.colors.tabText" + :fallback="previewTheme.colors?.tabText" :label="$t('settings.text')" /> <ContrastRatio :contrast="previewContrast.tabText" /> <ColorInput v-model="tabActiveTextColorLocal" name="tabActiveTextColor" - :fallback="previewTheme.colors.tabActiveText" + :fallback="previewTheme.colors?.tabActiveText" :label="$t('settings.text')" /> <ContrastRatio :contrast="previewContrast.tabActiveText" /> @@ -566,13 +581,13 @@ <ColorInput v-model="borderColorLocal" name="borderColor" - :fallback="previewTheme.colors.border" + :fallback="previewTheme.colors?.border" :label="$t('settings.style.common.color')" /> <OpacityInput v-model="borderOpacityLocal" name="borderOpacity" - :fallback="previewTheme.opacity.border" + :fallback="previewTheme.opacity?.border" :disabled="borderColorLocal === 'transparent'" /> </div> @@ -581,25 +596,25 @@ <ColorInput v-model="faintColorLocal" name="faintColor" - :fallback="previewTheme.colors.faint" + :fallback="previewTheme.colors?.faint" :label="$t('settings.text')" /> <ColorInput v-model="faintLinkColorLocal" name="faintLinkColor" - :fallback="previewTheme.colors.faintLink" + :fallback="previewTheme.colors?.faintLink" :label="$t('settings.links')" /> <ColorInput v-model="panelFaintColorLocal" name="panelFaintColor" - :fallback="previewTheme.colors.panelFaint" + :fallback="previewTheme.colors?.panelFaint" :label="$t('settings.style.advanced_colors.panel_header')" /> <OpacityInput v-model="faintOpacityLocal" name="faintOpacity" - :fallback="previewTheme.opacity.faint" + :fallback="previewTheme.opacity?.faint" /> </div> <div class="color-item"> @@ -608,12 +623,12 @@ v-model="underlayColorLocal" name="underlay" :label="$t('settings.style.advanced_colors.underlay')" - :fallback="previewTheme.colors.underlay" + :fallback="previewTheme.colors?.underlay" /> <OpacityInput v-model="underlayOpacityLocal" name="underlayOpacity" - :fallback="previewTheme.opacity.underlay" + :fallback="previewTheme.opacity?.underlay" :disabled="underlayOpacityLocal === 'transparent'" /> </div> @@ -623,7 +638,7 @@ v-model="wallpaperColorLocal" name="wallpaper" :label="$t('settings.style.advanced_colors.wallpaper')" - :fallback="previewTheme.colors.wallpaper" + :fallback="previewTheme.colors?.wallpaper" /> </div> <div class="color-item"> @@ -632,13 +647,13 @@ v-model="pollColorLocal" name="poll" :label="$t('settings.background')" - :fallback="previewTheme.colors.poll" + :fallback="previewTheme.colors?.poll" /> <ColorInput v-model="pollTextColorLocal" name="pollText" :label="$t('settings.text')" - :fallback="previewTheme.colors.pollText" + :fallback="previewTheme.colors?.pollText" /> </div> <div class="color-item"> @@ -647,7 +662,7 @@ v-model="iconColorLocal" name="icon" :label="$t('settings.style.advanced_colors.icons')" - :fallback="previewTheme.colors.icon" + :fallback="previewTheme.colors?.icon" /> </div> <div class="color-item"> @@ -656,20 +671,20 @@ v-model="highlightColorLocal" name="highlight" :label="$t('settings.background')" - :fallback="previewTheme.colors.highlight" + :fallback="previewTheme.colors?.highlight" /> <ColorInput v-model="highlightTextColorLocal" name="highlightText" :label="$t('settings.text')" - :fallback="previewTheme.colors.highlightText" + :fallback="previewTheme.colors?.highlightText" /> <ContrastRatio :contrast="previewContrast.highlightText" /> <ColorInput v-model="highlightLinkColorLocal" name="highlightLink" :label="$t('settings.links')" - :fallback="previewTheme.colors.highlightLink" + :fallback="previewTheme.colors?.highlightLink" /> <ContrastRatio :contrast="previewContrast.highlightLink" /> </div> @@ -679,26 +694,26 @@ v-model="popoverColorLocal" name="popover" :label="$t('settings.background')" - :fallback="previewTheme.colors.popover" + :fallback="previewTheme.colors?.popover" /> <OpacityInput v-model="popoverOpacityLocal" name="popoverOpacity" - :fallback="previewTheme.opacity.popover" + :fallback="previewTheme.opacity?.popover" :disabled="popoverOpacityLocal === 'transparent'" /> <ColorInput v-model="popoverTextColorLocal" name="popoverText" :label="$t('settings.text')" - :fallback="previewTheme.colors.popoverText" + :fallback="previewTheme.colors?.popoverText" /> <ContrastRatio :contrast="previewContrast.popoverText" /> <ColorInput v-model="popoverLinkColorLocal" name="popoverLink" :label="$t('settings.links')" - :fallback="previewTheme.colors.popoverLink" + :fallback="previewTheme.colors?.popoverLink" /> <ContrastRatio :contrast="previewContrast.popoverLink" /> </div> @@ -708,20 +723,20 @@ v-model="selectedPostColorLocal" name="selectedPost" :label="$t('settings.background')" - :fallback="previewTheme.colors.selectedPost" + :fallback="previewTheme.colors?.selectedPost" /> <ColorInput v-model="selectedPostTextColorLocal" name="selectedPostText" :label="$t('settings.text')" - :fallback="previewTheme.colors.selectedPostText" + :fallback="previewTheme.colors?.selectedPostText" /> <ContrastRatio :contrast="previewContrast.selectedPostText" /> <ColorInput v-model="selectedPostLinkColorLocal" name="selectedPostLink" :label="$t('settings.links')" - :fallback="previewTheme.colors.selectedPostLink" + :fallback="previewTheme.colors?.selectedPostLink" /> <ContrastRatio :contrast="previewContrast.selectedPostLink" /> </div> @@ -731,20 +746,20 @@ v-model="selectedMenuColorLocal" name="selectedMenu" :label="$t('settings.background')" - :fallback="previewTheme.colors.selectedMenu" + :fallback="previewTheme.colors?.selectedMenu" /> <ColorInput v-model="selectedMenuTextColorLocal" name="selectedMenuText" :label="$t('settings.text')" - :fallback="previewTheme.colors.selectedMenuText" + :fallback="previewTheme.colors?.selectedMenuText" /> <ContrastRatio :contrast="previewContrast.selectedMenuText" /> <ColorInput v-model="selectedMenuLinkColorLocal" name="selectedMenuLink" :label="$t('settings.links')" - :fallback="previewTheme.colors.selectedMenuLink" + :fallback="previewTheme.colors?.selectedMenuLink" /> <ContrastRatio :contrast="previewContrast.selectedMenuLink" /> </div> @@ -753,57 +768,57 @@ <ColorInput v-model="chatBgColorLocal" name="chatBgColor" - :fallback="previewTheme.colors.bg" + :fallback="previewTheme.colors?.bg" :label="$t('settings.background')" /> <h5>{{ $t('settings.style.advanced_colors.chat.incoming') }}</h5> <ColorInput v-model="chatMessageIncomingBgColorLocal" name="chatMessageIncomingBgColor" - :fallback="previewTheme.colors.bg" + :fallback="previewTheme.colors?.bg" :label="$t('settings.background')" /> <ColorInput v-model="chatMessageIncomingTextColorLocal" name="chatMessageIncomingTextColor" - :fallback="previewTheme.colors.text" + :fallback="previewTheme.colors?.text" :label="$t('settings.text')" /> <ColorInput v-model="chatMessageIncomingLinkColorLocal" name="chatMessageIncomingLinkColor" - :fallback="previewTheme.colors.link" + :fallback="previewTheme.colors?.link" :label="$t('settings.links')" /> <ColorInput v-model="chatMessageIncomingBorderColorLocal" name="chatMessageIncomingBorderLinkColor" - :fallback="previewTheme.colors.fg" + :fallback="previewTheme.colors?.fg" :label="$t('settings.style.advanced_colors.chat.border')" /> <h5>{{ $t('settings.style.advanced_colors.chat.outgoing') }}</h5> <ColorInput v-model="chatMessageOutgoingBgColorLocal" name="chatMessageOutgoingBgColor" - :fallback="previewTheme.colors.bg" + :fallback="previewTheme.colors?.bg" :label="$t('settings.background')" /> <ColorInput v-model="chatMessageOutgoingTextColorLocal" name="chatMessageOutgoingTextColor" - :fallback="previewTheme.colors.text" + :fallback="previewTheme.colors?.text" :label="$t('settings.text')" /> <ColorInput v-model="chatMessageOutgoingLinkColorLocal" name="chatMessageOutgoingLinkColor" - :fallback="previewTheme.colors.link" + :fallback="previewTheme.colors?.link" :label="$t('settings.links')" /> <ColorInput v-model="chatMessageOutgoingBorderColorLocal" name="chatMessageOutgoingBorderLinkColor" - :fallback="previewTheme.colors.bg" + :fallback="previewTheme.colors?.bg" :label="$t('settings.style.advanced_colors.chat.border')" /> </div> @@ -826,7 +841,7 @@ v-model="btnRadiusLocal" name="btnRadius" :label="$t('settings.btnRadius')" - :fallback="previewTheme.radii.btn" + :fallback="previewTheme.radii?.btn" max="16" hard-min="0" /> @@ -834,7 +849,7 @@ v-model="inputRadiusLocal" name="inputRadius" :label="$t('settings.inputRadius')" - :fallback="previewTheme.radii.input" + :fallback="previewTheme.radii?.input" max="9" hard-min="0" /> @@ -842,7 +857,7 @@ v-model="checkboxRadiusLocal" name="checkboxRadius" :label="$t('settings.checkboxRadius')" - :fallback="previewTheme.radii.checkbox" + :fallback="previewTheme.radii?.checkbox" max="16" hard-min="0" /> @@ -850,7 +865,7 @@ v-model="panelRadiusLocal" name="panelRadius" :label="$t('settings.panelRadius')" - :fallback="previewTheme.radii.panel" + :fallback="previewTheme.radii?.panel" max="50" hard-min="0" /> @@ -858,7 +873,7 @@ v-model="avatarRadiusLocal" name="avatarRadius" :label="$t('settings.avatarRadius')" - :fallback="previewTheme.radii.avatar" + :fallback="previewTheme.radii?.avatar" max="28" hard-min="0" /> @@ -866,7 +881,7 @@ v-model="avatarAltRadiusLocal" name="avatarAltRadius" :label="$t('settings.avatarAltRadius')" - :fallback="previewTheme.radii.avatarAlt" + :fallback="previewTheme.radii?.avatarAlt" max="28" hard-min="0" /> @@ -874,7 +889,7 @@ v-model="attachmentRadiusLocal" name="attachmentRadius" :label="$t('settings.attachmentRadius')" - :fallback="previewTheme.radii.attachment" + :fallback="previewTheme.radii?.attachment" max="50" hard-min="0" /> @@ -882,7 +897,7 @@ v-model="tooltipRadiusLocal" name="tooltipRadius" :label="$t('settings.tooltipRadius')" - :fallback="previewTheme.radii.tooltip" + :fallback="previewTheme.radii?.tooltip" max="50" hard-min="0" /> @@ -890,7 +905,7 @@ v-model="chatMessageRadiusLocal" name="chatMessageRadius" :label="$t('settings.chatMessageRadius')" - :fallback="previewTheme.radii.chatMessage || 2" + :fallback="previewTheme.radii?.chatMessage || 2" max="50" hard-min="0" /> @@ -996,26 +1011,26 @@ v-model="fontsLocal.interface" name="ui" :label="$t('settings.style.fonts.components.interface')" - :fallback="previewTheme.fonts.interface" + :fallback="previewTheme.fonts?.interface" no-inherit="1" /> <FontControl v-model="fontsLocal.input" name="input" :label="$t('settings.style.fonts.components.input')" - :fallback="previewTheme.fonts.input" + :fallback="previewTheme.fonts?.input" /> <FontControl v-model="fontsLocal.post" name="post" :label="$t('settings.style.fonts.components.post')" - :fallback="previewTheme.fonts.post" + :fallback="previewTheme.fonts?.post" /> <FontControl v-model="fontsLocal.postCode" name="postCode" :label="$t('settings.style.fonts.components.postCode')" - :fallback="previewTheme.fonts.postCode" + :fallback="previewTheme.fonts?.postCode" /> </div> </tab-switcher> diff --git a/src/components/shadow_control/shadow_control.js b/src/components/shadow_control/shadow_control.js index a1d1012b..f8e12dbf 100644 --- a/src/components/shadow_control/shadow_control.js +++ b/src/components/shadow_control/shadow_control.js @@ -1,7 +1,7 @@ import ColorInput from '../color_input/color_input.vue' import OpacityInput from '../opacity_input/opacity_input.vue' import Select from '../select/select.vue' -import { getCssShadow } from '../../services/style_setter/style_setter.js' +import { getCssShadow } from '../../services/theme_data/theme_data.service.js' import { hex2rgb } from '../../services/color_convert/color_convert.js' import { library } from '@fortawesome/fontawesome-svg-core' import { diff --git a/src/components/shadow_control/shadow_control.vue b/src/components/shadow_control/shadow_control.vue index 1f3c26aa..c3b956cd 100644 --- a/src/components/shadow_control/shadow_control.vue +++ b/src/components/shadow_control/shadow_control.vue @@ -11,14 +11,14 @@ <input v-model="selected.y" :disabled="!present" - class="input-number" + class="input input-number" type="number" > <div class="wrap"> <input v-model="selected.y" :disabled="!present" - class="input-range" + class="input input-range" type="range" max="20" min="-20" @@ -38,14 +38,14 @@ <input v-model="selected.x" :disabled="!present" - class="input-number" + class="input input-number" type="number" > <div class="wrap"> <input v-model="selected.x" :disabled="!present" - class="input-range" + class="input input-range" type="range" max="20" min="-20" @@ -129,7 +129,7 @@ v-model="selected.inset" :disabled="!present" name="inset" - class="input-inset visible-for-screenreader-only" + class="input -checkbox input-inset visible-for-screenreader-only" type="checkbox" > <label @@ -153,7 +153,7 @@ v-model="selected.blur" :disabled="!present" name="blur" - class="input-range" + class="input input-range" type="range" max="20" min="0" @@ -161,7 +161,7 @@ <input v-model="selected.blur" :disabled="!present" - class="input-number" + class="input input-number" type="number" min="0" > @@ -181,7 +181,7 @@ v-model="selected.spread" :disabled="!present" name="spread" - class="input-range" + class="input input-range" type="range" max="20" min="-20" @@ -189,7 +189,7 @@ <input v-model="selected.spread" :disabled="!present" - class="input-number" + class="input input-number" type="number" > </div> @@ -219,8 +219,6 @@ <script src="./shadow_control.js"></script> <style lang="scss"> -@import "../../variables"; - .shadow-control { display: flex; flex-wrap: wrap; @@ -237,8 +235,6 @@ display: flex; flex-wrap: wrap; - $side: 15em; - input[type="number"] { width: 5em; min-width: 2em; @@ -261,7 +257,7 @@ .x-shift-control .wrap, input[type="range"] { margin: 0; - width: $side; + width: 15em; height: 2em; } @@ -271,7 +267,7 @@ .wrap { width: 2em; - height: $side; + height: 15em; } input[type="range"] { @@ -293,16 +289,12 @@ linear-gradient(-45deg, transparent 75%, #666 75%); background-size: 20px 20px; background-position: 0 0, 0 10px, 10px -10px, -10px 0; - border-radius: $fallback--inputRadius; - border-radius: var(--inputRadius, $fallback--inputRadius); + border-radius: var(--roundness); .preview-block { width: 33%; height: 33%; - background-color: $fallback--bg; - background-color: var(--bg, $fallback--bg); - border-radius: $fallback--panelRadius; - border-radius: var(--panelRadius, $fallback--panelRadius); + border-radius: var(--roundness); } } } diff --git a/src/components/shout_panel/shout_panel.vue b/src/components/shout_panel/shout_panel.vue index a7013469..9113211e 100644 --- a/src/components/shout_panel/shout_panel.vue +++ b/src/components/shout_panel/shout_panel.vue @@ -5,7 +5,7 @@ > <div class="panel panel-default"> <div - class="panel-heading timeline-heading" + class="panel-heading" :class="{ 'shout-heading': floating }" @click.stop.prevent="togglePanel" > @@ -18,7 +18,7 @@ /> </div> </div> - <div class="shout-window"> + <div class="panel-body shout-window"> <div v-for="message in messages" :key="message.id" @@ -41,10 +41,10 @@ </div> </div> </div> - <div class="shout-input"> + <div class="panel-body shout-input"> <textarea v-model="currentMessage" - class="shout-input-textarea" + class="shout-input-textarea input" rows="1" @keyup.enter="submit(currentMessage)" /> @@ -75,8 +75,6 @@ <script src="./shout_panel.js"></script> <style lang="scss"> -@import "../../variables"; - .floating-shout { position: fixed; bottom: 0.5em; @@ -97,8 +95,7 @@ cursor: pointer; .icon { - color: $fallback--text; - color: var(--panelText, $fallback--text); + color: var(--text); margin-right: 0.5em; } @@ -128,8 +125,7 @@ img { height: 24px; width: 24px; - border-radius: $fallback--avatarRadius; - border-radius: var(--avatarRadius, $fallback--avatarRadius); + border-radius: var(--roundness); margin-right: 0.5em; margin-top: 0.25em; } diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue index 09588767..608b5608 100644 --- a/src/components/side_drawer/side_drawer.vue +++ b/src/components/side_drawer/side_drawer.vue @@ -1,6 +1,6 @@ <template> <div - class="side-drawer-container" + class="side-drawer-container mobile-drawer" :class="{ 'side-drawer-container-closed': closed, 'side-drawer-container-open': !closed }" > <div @@ -35,7 +35,10 @@ v-if="!currentUser" @click="toggleDrawer" > - <router-link :to="{ name: 'login' }"> + <router-link + :to="{ name: 'login' }" + class="menu-item" + > <FAIcon fixed-width class="fa-scale-110 fa-old-padding" @@ -47,7 +50,10 @@ v-if="currentUser || !privateMode" @click="toggleDrawer" > - <router-link :to="timelinesRoute"> + <router-link + :to="timelinesRoute" + class="menu-item" + > <FAIcon fixed-width class="fa-scale-110 fa-old-padding" @@ -59,7 +65,10 @@ v-if="currentUser" @click="toggleDrawer" > - <router-link :to="{ name: 'lists' }"> + <router-link + :to="{ name: 'lists' }" + class="menu-item" + > <FAIcon fixed-width class="fa-scale-110 fa-old-padding" @@ -74,6 +83,7 @@ <router-link :to="{ name: 'chats', params: { username: currentUser.screen_name } }" style="position: relative;" + class="menu-item" > <FAIcon fixed-width @@ -82,7 +92,7 @@ /> {{ $t("nav.chats") }} <span v-if="unreadChatCount" - class="badge badge-notification" + class="badge -notification" > {{ unreadChatCount }} </span> @@ -91,7 +101,10 @@ </ul> <ul v-if="currentUser"> <li @click="toggleDrawer"> - <router-link :to="{ name: 'interactions', params: { username: currentUser.screen_name } }"> + <router-link + :to="{ name: 'interactions', params: { username: currentUser.screen_name } }" + class="menu-item" + > <FAIcon fixed-width class="fa-scale-110 fa-old-padding" @@ -103,7 +116,10 @@ v-if="currentUser.locked" @click="toggleDrawer" > - <router-link to="/friend-requests"> + <router-link + to="/friend-requests" + class="menu-item" + > <FAIcon fixed-width class="fa-scale-110 fa-old-padding" @@ -111,7 +127,7 @@ /> {{ $t("nav.friend_requests") }} <span v-if="followRequestCount > 0" - class="badge badge-notification" + class="badge -notification" > {{ followRequestCount }} </span> @@ -121,7 +137,10 @@ v-if="shout" @click="toggleDrawer" > - <router-link :to="{ name: 'shout-panel' }"> + <router-link + :to="{ name: 'shout-panel' }" + class="menu-item" + > <FAIcon fixed-width class="fa-scale-110 fa-old-padding" @@ -135,7 +154,10 @@ v-if="currentUser || !privateMode" @click="toggleDrawer" > - <router-link :to="{ name: 'search' }"> + <router-link + :to="{ name: 'search' }" + class="menu-item" + > <FAIcon fixed-width class="fa-scale-110 fa-old-padding" @@ -147,7 +169,10 @@ v-if="currentUser && suggestionsEnabled" @click="toggleDrawer" > - <router-link :to="{ name: 'who-to-follow' }"> + <router-link + :to="{ name: 'who-to-follow' }" + class="menu-item" + > <FAIcon fixed-width class="fa-scale-110 fa-old-padding" @@ -157,7 +182,7 @@ </li> <li @click="toggleDrawer"> <button - class="button-unstyled -link -fullwidth" + class="menu-item" @click="openSettingsModal" > <FAIcon @@ -168,7 +193,10 @@ </button> </li> <li @click="toggleDrawer"> - <router-link :to="{ name: 'about'}"> + <router-link + :to="{ name: 'about'}" + class="menu-item" + > <FAIcon fixed-width class="fa-scale-110 fa-old-padding" @@ -181,7 +209,7 @@ @click="toggleDrawer" > <button - class="button-unstyled -link -fullwidth" + class="menu-item" @click.stop="openAdminModal" > <FAIcon @@ -197,6 +225,7 @@ > <router-link :to="{ name: 'announcements' }" + class="menu-item" > <FAIcon fixed-width @@ -205,7 +234,7 @@ /> {{ $t("nav.announcements") }} <span v-if="unreadAnnouncementCount" - class="badge badge-notification" + class="badge -notification" > {{ unreadAnnouncementCount }} </span> @@ -215,7 +244,10 @@ v-if="currentUser" @click="toggleDrawer" > - <router-link :to="{ name: 'edit-navigation' }"> + <router-link + :to="{ name: 'edit-navigation' }" + class="menu-item" + > <FAIcon fixed-width class="fa-scale-110 fa-old-padding" @@ -228,7 +260,7 @@ @click="toggleDrawer" > <button - class="button-unstyled -link -fullwidth" + class="menu-item" @click="doLogout" > <FAIcon @@ -251,8 +283,6 @@ <script src="./side_drawer.js"></script> <style lang="scss"> -@import "../../variables"; - .side-drawer-container { position: fixed; z-index: var(--ZI_navbar); @@ -305,17 +335,8 @@ width: 80%; max-width: 20em; flex: 0 0 80%; - box-shadow: 1px 1px 4px rgb(0 0 0 / 60%); - box-shadow: var(--panelShadow); - background-color: $fallback--bg; - background-color: var(--popover, $fallback--bg); - color: $fallback--link; - color: var(--popoverText, $fallback--link); - - --faint: var(--popoverFaintText, $fallback--faint); - --faintLink: var(--popoverFaintLink, $fallback--faint); - --lightText: var(--popoverLightText, $fallback--lightText); - --icon: var(--popoverIcon, $fallback--icon); + box-shadow: var(--shadow); + background-color: var(--background); .badge { margin-left: 10px; @@ -362,8 +383,7 @@ margin: 0; padding: 0; border-bottom: 1px solid; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); + border-color: var(--border); } .side-drawer ul:last-child { @@ -380,18 +400,6 @@ height: 3em; line-height: 3em; padding: 0 0.7em; - - &:hover { - background-color: $fallback--lightBg; - background-color: var(--selectedMenuPopover, $fallback--lightBg); - color: $fallback--text; - color: var(--selectedMenuPopoverText, $fallback--text); - - --faint: var(--selectedMenuPopoverFaintText, $fallback--faint); - --faintLink: var(--selectedMenuPopoverFaintLink, $fallback--faint); - --lightText: var(--selectedMenuPopoverLightText, $fallback--lightText); - --icon: var(--selectedMenuPopoverIcon, $fallback--icon); - } } } </style> diff --git a/src/components/status/post.style.js b/src/components/status/post.style.js new file mode 100644 index 00000000..d0038424 --- /dev/null +++ b/src/components/status/post.style.js @@ -0,0 +1,42 @@ +export default { + name: 'Post', + selector: '.Status', + states: { + selected: '.-focused' + }, + validInnerComponents: [ + 'Text', + 'Link', + 'Icon', + 'Border', + 'Button', + 'ButtonUnstyled', + 'RichContent', + 'Input', + 'Avatar', + 'Attachment', + 'PollGraph' + ], + validInnerComponentsLite: [ + 'Text', + 'Link', + 'Icon', + 'Border', + 'ButtonUnstyled', + 'RichContent', + 'Avatar' + ], + defaultRules: [ + { + directives: { + background: '--bg' + } + }, + { + state: ['selected'], + directives: { + background: '--inheritedBackground, 10' + } + } + ] +} diff --git a/src/components/status/status.js b/src/components/status/status.js index 8f22b708..bf4e4275 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -238,6 +238,9 @@ const Status = { showActorTypeIndicator () { return !this.hideBotIndication }, + sensitiveStatus () { + return this.status.nsfw + }, mentionsLine () { if (!this.headTailLinks) return [] const writtenSet = new Set(this.headTailLinks.writtenMentions.map(_ => _.url)) @@ -265,7 +268,9 @@ const Status = { // Wordfiltered this.muteWordHits.length > 0 || // bot status - (this.muteBotStatuses && this.botStatus && !this.compact) + (this.muteBotStatuses && this.botStatus && !this.compact) || + // sensitive status + (this.muteSensitiveStatuses && this.sensitiveStatus && !this.compact) return !this.unmuted && !this.shouldNotMute && reasonsToMute }, userIsMuted () { @@ -368,9 +373,15 @@ const Status = { hidePostStats () { return this.mergedConfig.hidePostStats }, + shouldDisplayFavsAndRepeats () { + return !this.hidePostStats && this.isFocused && (this.combinedFavsAndRepeatsUsers.length > 0 || this.statusFromGlobalRepository.quotes_count) + }, muteBotStatuses () { return this.mergedConfig.muteBotStatuses }, + muteSensitiveStatuses () { + return this.mergedConfig.muteSensitiveStatuses + }, hideBotIndication () { return this.mergedConfig.hideBotIndication }, @@ -414,7 +425,27 @@ const Status = { return this.quotedStatus && this.displayQuote }, scrobblePresent () { - return !this.mergedConfig.hideScrobbles && this.status.user.latestScrobble && this.status.user.latestScrobble.artist + if (this.mergedConfig.hideScrobbles) return false + if (!this.status.user.latestScrobble) return false + const value = this.mergedConfig.hideScrobblesAfter.match(/\d+/gs)[0] + const unit = this.mergedConfig.hideScrobblesAfter.match(/\D+/gs)[0] + let multiplier = 60 * 1000 // minutes is smallest unit + switch (unit) { + case 'm': + break + case 'h': + multiplier *= 60 // hour + break + case 'd': + multiplier *= 60 // hour + multiplier *= 24 // day + break + } + const maxAge = Number(value) * multiplier + const createdAt = Date.parse(this.status.user.latestScrobble.created_at) + const age = Date.now() - createdAt + if (age > maxAge) return false + return this.status.user.latestScrobble.artist }, scrobble () { return this.status.user.latestScrobble diff --git a/src/components/status/status.scss b/src/components/status/status.scss index 760c6ac1..63809ff2 100644 --- a/src/components/status/status.scss +++ b/src/components/status/status.scss @@ -1,5 +1,3 @@ -@import "../../variables"; - .Status { min-width: 0; white-space: normal; @@ -12,24 +10,8 @@ --_still-image-label-visibility: hidden; } - &.-focused { - background-color: $fallback--lightBg; - background-color: var(--selectedPost, $fallback--lightBg); - color: $fallback--text; - color: var(--selectedPostText, $fallback--text); - - --lightText: var(--selectedPostLightText, $fallback--light); - --faint: var(--selectedPostFaintText, $fallback--faint); - --faintLink: var(--selectedPostFaintLink, $fallback--faint); - --postLink: var(--selectedPostPostLink, $fallback--faint); - --postFaintLink: var(--selectedPostFaintPostLink, $fallback--faint); - --icon: var(--selectedPostIcon, $fallback--icon); - } - .gravestone { - padding: var(--status-margin, $status-margin); - color: $fallback--faint; - color: var(--faint, $fallback--faint); + padding: var(--status-margin); display: flex; .deleted-text { @@ -40,7 +22,7 @@ .status-container { display: flex; - padding: var(--status-margin, $status-margin); + padding: var(--status-margin); > * { min-width: 0; @@ -52,7 +34,7 @@ } .pin { - padding: var(--status-margin, $status-margin) var(--status-margin, $status-margin) 0; + padding: var(--status-margin) var(--status-margin) 0; display: flex; align-items: center; justify-content: flex-end; @@ -68,7 +50,7 @@ } .left-side { - margin-right: var(--status-margin, $status-margin); + margin-right: var(--status-margin); } .right-side { @@ -77,7 +59,7 @@ } .usercard { - margin-bottom: var(--status-margin, $status-margin); + margin-bottom: var(--status-margin); } .status-username { @@ -135,11 +117,6 @@ .button-unstyled { padding: 5px; margin: -5px; - - &:hover svg { - color: $fallback--lightText; - color: var(--lightText, $fallback--lightText); - } } .svg-inline--fa { @@ -243,16 +220,15 @@ } .repeat-info { - padding: 0.4em var(--status-margin, $status-margin); + padding: 0.4em var(--status-margin); .repeat-icon { - color: $fallback--cGreen; - color: var(--cGreen, $fallback--cGreen); + color: var(--cGreen); } } .repeater-avatar { - border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); + border-radius: var(--roundness); margin-left: 28px; width: 20px; height: 20px; @@ -289,7 +265,7 @@ position: relative; width: 100%; display: flex; - margin-top: var(--status-margin, $status-margin); + margin-top: var(--status-margin); > * { max-width: 4em; @@ -357,7 +333,7 @@ } .favs-repeated-users { - margin-top: var(--status-margin, $status-margin); + margin-top: var(--status-margin); } .stats { @@ -368,10 +344,10 @@ .avatar-row { flex: 1; - overflow: hidden; position: relative; display: flex; align-items: center; + overflow: hidden; &::before { content: ""; @@ -379,16 +355,16 @@ height: 100%; width: 1px; left: 0; - background-color: var(--faint, $fallback--faint); + background-color: var(--textFaint); } } .stat-count { - margin-right: var(--status-margin, $status-margin); + margin-right: var(--status-margin); user-select: none; .stat-title { - color: var(--faint, $fallback--faint); + color: var(--textFaint); font-size: 0.85em; text-transform: uppercase; position: relative; @@ -398,6 +374,7 @@ font-weight: bolder; font-size: 1.1em; line-height: 1em; + color: var(--text); } &:hover .stat-title { @@ -425,8 +402,8 @@ .quoted-status { margin-top: 0.5em; - border: 1px solid var(--border, $fallback--border); - border-radius: var(--attachmentRadius, $fallback--attachmentRadius); + border: 1px solid var(--border); + border-radius: var(--roundness); &.-unavailable-prompt { padding: 0.5em; diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 1c91c36c..61a58cda 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -31,6 +31,12 @@ /> </small> <small + v-if="muteSensitiveStatuses && status.nsfw" + class="mute-thread" + > + {{ $t('status.sensitive_muted') }} + </small> + <small v-if="showReasonMutedThread" class="mute-thread" > @@ -180,7 +186,7 @@ <span class="heading-right"> <router-link - class="timeago faint-link" + class="timeago faint" :to="{ name: 'conversation', params: { id: status.id } }" > <Timeago @@ -450,7 +456,7 @@ > <button v-if="showOtherRepliesAsButton && replies.length > 1" - class="button-unstyled -link faint" + class="button-unstyled -link" :title="$tc('status.ancestor_follow', replies.length - 1, { numReplies: replies.length - 1 })" @click.prevent="dive" > @@ -478,7 +484,7 @@ <transition name="fade"> <div - v-if="!hidePostStats && isFocused && combinedFavsAndRepeatsUsers.length > 0" + v-if="shouldDisplayFavsAndRepeats" class="favs-repeated-users" > <div class="stats"> @@ -506,6 +512,19 @@ </div> </div> </UserListPopover> + <router-link + v-if="statusFromGlobalRepository.quotes_count > 0" + :to="{ name: 'quotes', params: { id: status.id } }" + > + <div + class="stat-count" + > + <a class="stat-title">{{ $t('status.quotes') }}</a> + <div class="stat-number"> + {{ statusFromGlobalRepository.quotes_count }} + </div> + </div> + </router-link> <div class="avatar-row"> <AvatarList :users="combinedFavsAndRepeatsUsers" /> </div> diff --git a/src/components/status_body/status_body.scss b/src/components/status_body/status_body.scss index 8fd60afc..0a467b4f 100644 --- a/src/components/status_body/status_body.scss +++ b/src/components/status_body/status_body.scss @@ -1,5 +1,3 @@ -@import "../../variables"; - .StatusBody { display: flex; flex-direction: column; @@ -14,7 +12,6 @@ & .text, & .summary { - font-family: var(--postFont, sans-serif); white-space: pre-wrap; overflow-wrap: break-word; word-wrap: break-word; @@ -41,7 +38,7 @@ margin-bottom: 0.5em; border-style: solid; border-width: 0 0 1px; - border-color: var(--border, $fallback--border); + border-color: var(--border); flex-grow: 0; &.-tall { @@ -112,15 +109,6 @@ } } - .greentext { - color: $fallback--cGreen; - color: var(--postGreentext, $fallback--cGreen); - } - - .cyantext { - color: var(--postCyantext, $fallback--cBlue); - } - &.-compact { align-items: top; flex-direction: row; diff --git a/src/components/status_body/status_body.vue b/src/components/status_body/status_body.vue index fb356360..f21bcc80 100644 --- a/src/components/status_body/status_body.vue +++ b/src/components/status_body/status_body.vue @@ -11,6 +11,7 @@ > <RichContent class="media-body summary" + :faint="compact" :html="status.summary_raw_html" :emoji="status.emojis" /> @@ -48,6 +49,7 @@ :html="status.raw_html" :emoji="status.emojis" :handle-links="true" + :faint="compact" :greentext="mergedConfig.greentext" :attentions="status.attentions" @parseReady="onParseReady" diff --git a/src/components/status_popover/status_popover.vue b/src/components/status_popover/status_popover.vue index 311ca099..839f82e9 100644 --- a/src/components/status_popover/status_popover.vue +++ b/src/components/status_popover/status_popover.vue @@ -40,19 +40,14 @@ <script src="./status_popover.js"></script> <style lang="scss"> -@import "../../variables"; - /* popover styles load on-demand, so we need to override */ .status-popover.popover { font-size: 1rem; min-width: 15em; max-width: 95%; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); + border-color: var(--border); border-style: solid; border-width: 1px; - border-radius: $fallback--tooltipRadius; - border-radius: var(--tooltipRadius, $fallback--tooltipRadius); /* TODO cleanup this */ .Status.Status { diff --git a/src/components/sticker_picker/sticker_picker.vue b/src/components/sticker_picker/sticker_picker.vue index 904853c0..6678132e 100644 --- a/src/components/sticker_picker/sticker_picker.vue +++ b/src/components/sticker_picker/sticker_picker.vue @@ -32,8 +32,6 @@ <script src="./sticker_picker.js"></script> <style lang="scss"> -@import "../../variables"; - .sticker-picker { width: 100%; @@ -56,7 +54,7 @@ height: 100%; &:hover { - filter: drop-shadow(0 0 5px var(--accent, $fallback--link)); + filter: drop-shadow(0 0 5px var(--accent)); } } } diff --git a/src/components/still-image/still-image.vue b/src/components/still-image/still-image.vue index fc46fbe6..57633827 100644 --- a/src/components/still-image/still-image.vue +++ b/src/components/still-image/still-image.vue @@ -28,8 +28,6 @@ <script src="./still-image.js"></script> <style lang="scss"> -@import "../../variables"; - .still-image { position: relative; line-height: 0; @@ -68,8 +66,7 @@ color: #fff; display: block; padding: 2px 4px; - border-radius: $fallback--tooltipRadius; - border-radius: var(--tooltipRadius, $fallback--tooltipRadius); + border-radius: var(--roundness); z-index: 2; visibility: var(--_still-image-label-visibility, visible); } diff --git a/src/components/tab_switcher/tab.style.js b/src/components/tab_switcher/tab.style.js new file mode 100644 index 00000000..eac8aaeb --- /dev/null +++ b/src/components/tab_switcher/tab.style.js @@ -0,0 +1,78 @@ +export default { + name: 'Tab', // Name of the component + selector: '.tab', // CSS selector/prefix + states: { + active: '.active', + hover: ':hover:not(.disabled)', + disabled: '.disabled' + }, + validInnerComponents: [ + 'Text', + 'Icon' + ], + defaultRules: [ + { + directives: { + background: '--fg', + shadow: ['--defaultButtonShadow', '--defaultButtonBevel'], + roundness: 3 + } + }, + { + state: ['hover'], + directives: { + shadow: ['--defaultButtonHoverGlow', '--defaultButtonBevel'] + } + }, + { + state: ['active'], + directives: { + opacity: 0 + } + }, + { + state: ['hover', 'active'], + directives: { + shadow: ['--defaultButtonShadow', '--defaultButtonBevel'] + } + }, + { + state: ['disabled'], + directives: { + background: '$blend(--inheritedBackground, 0.25, --parent)', + shadow: ['--defaultButtonBevel'] + } + }, + { + component: 'Text', + parent: { + component: 'Tab', + state: ['disabled'] + }, + directives: { + textOpacity: 0.25, + textOpacityMode: 'blend' + } + }, + { + component: 'Icon', + parent: { + component: 'Tab', + state: ['active'] + }, + directives: { + textColor: '--text' + } + }, + { + component: 'Icon', + parent: { + component: 'Tab', + state: ['active', 'hover'] + }, + directives: { + textColor: '--text' + } + } + ] +} diff --git a/src/components/tab_switcher/tab_switcher.jsx b/src/components/tab_switcher/tab_switcher.jsx index b444da43..027a380a 100644 --- a/src/components/tab_switcher/tab_switcher.jsx +++ b/src/components/tab_switcher/tab_switcher.jsx @@ -97,7 +97,7 @@ export default { .map((slot, index) => { const props = slot.props if (!props) return - const classesTab = ['tab', 'button-default'] + const classesTab = ['tab'] const classesWrapper = ['tab-wrapper'] if (this.activeIndex === index) { classesTab.push('active') diff --git a/src/components/tab_switcher/tab_switcher.scss b/src/components/tab_switcher/tab_switcher.scss index 705925c8..4e242b91 100644 --- a/src/components/tab_switcher/tab_switcher.scss +++ b/src/components/tab_switcher/tab_switcher.scss @@ -1,5 +1,3 @@ -@import "../../variables"; - /* stylelint-disable no-descending-specificity */ .tab-switcher { display: flex; @@ -25,8 +23,7 @@ content: ""; flex: 1 1 auto; border-bottom: 1px solid; - border-bottom-color: $fallback--border; - border-bottom-color: var(--border, $fallback--border); + border-bottom-color: var(--border); } .tab-wrapper { @@ -37,8 +34,7 @@ right: 0; bottom: 0; border-bottom: 1px solid; - border-bottom-color: $fallback--border; - border-bottom-color: var(--border, $fallback--border); + border-bottom-color: var(--border); } } @@ -80,8 +76,7 @@ flex-basis: 0.5em; content: ""; border-right: 1px solid; - border-right-color: $fallback--border; - border-right-color: var(--border, $fallback--border); + border-right-color: var(--border); } &::after { @@ -106,16 +101,14 @@ right: 0; bottom: 0; border-right: 1px solid; - border-right-color: $fallback--border; - border-right-color: var(--border, $fallback--border); + border-right-color: var(--border); } &::before { flex: 0 0 6px; content: ""; border-right: 1px solid; - border-right-color: $fallback--border; - border-right-color: var(--border, $fallback--border); + border-right-color: var(--border); } &:last-child .tab { @@ -173,6 +166,15 @@ } .tab { + user-select: none; + color: var(--text); + border: none; + cursor: pointer; + box-shadow: var(--shadow); + font-size: 1em; + font-family: var(--font); + border-radius: var(--roundness); + background-color: var(--background); position: relative; white-space: nowrap; padding: 6px 1em; @@ -188,8 +190,6 @@ &.active { background: transparent; z-index: 5; - color: $fallback--text; - color: var(--tabActiveText, $fallback--text); } img { @@ -231,7 +231,7 @@ margin-top: 0.5em; margin-left: 0.2em; margin-bottom: 0.25em; - border-bottom: 1px solid var(--border, $fallback--border); + border-bottom: 1px solid var(--border); @media all and (min-width: 800px) { display: none; diff --git a/src/components/text.style.js b/src/components/text.style.js new file mode 100644 index 00000000..a254ceb4 --- /dev/null +++ b/src/components/text.style.js @@ -0,0 +1,22 @@ +export default { + name: 'Text', + selector: '/*text*/', + virtual: true, + states: { + faint: '.faint' + }, + defaultRules: [ + { + directives: { + textColor: '--text', + textAuto: 'no-preserve' + } + }, + { + state: ['faint'], + directives: { + textOpacity: 0.5 + } + } + ] +} diff --git a/src/components/thread_tree/thread_tree.vue b/src/components/thread_tree/thread_tree.vue index 04727278..971b7001 100644 --- a/src/components/thread_tree/thread_tree.vue +++ b/src/components/thread_tree/thread_tree.vue @@ -119,15 +119,13 @@ <script src="./thread_tree.js"></script> <style lang="scss"> -@import "../../variables"; - .thread-tree-replies { - margin-left: var(--status-margin, $status-margin); - border-left: 2px solid var(--border, $fallback--border); + margin-left: var(--status-margin); + border-left: 2px solid var(--border); } .thread-tree-replies-hidden { - padding: var(--status-margin, $status-margin); + padding: var(--status-margin); /* Make the button stretch along the whole row */ display: flex; diff --git a/src/components/timeago/timeago.vue b/src/components/timeago/timeago.vue index b5f49515..bf918441 100644 --- a/src/components/timeago/timeago.vue +++ b/src/components/timeago/timeago.vue @@ -3,7 +3,7 @@ :datetime="time" :title="localeDateString" > - {{ relativeTimeString }} + {{ relativeOrAbsoluteTimeString }} </time> </template> @@ -16,16 +16,28 @@ export default { props: ['time', 'autoUpdate', 'longFormat', 'nowThreshold', 'templateKey'], data () { return { + relativeTimeMs: 0, relativeTime: { key: 'time.now', num: 0 }, interval: null } }, computed: { - localeDateString () { - const browserLocale = localeService.internalToBrowserLocale(this.$i18n.locale) + shouldUseAbsoluteTimeFormat () { + if (!this.$store.getters.mergedConfig.useAbsoluteTimeFormat) { + return false + } + return DateUtils.durationStrToMs(this.$store.getters.mergedConfig.absoluteTimeFormatMinAge) <= this.relativeTimeMs + }, + browserLocale () { + return localeService.internalToBrowserLocale(this.$i18n.locale) + }, + timeAsDate () { return typeof this.time === 'string' - ? new Date(Date.parse(this.time)).toLocaleString(browserLocale) - : this.time.toLocaleString(browserLocale) + ? new Date(Date.parse(this.time)) + : this.time + }, + localeDateString () { + return this.timeAsDate.toLocaleString(this.browserLocale) }, relativeTimeString () { const timeString = this.$i18n.tc(this.relativeTime.key, this.relativeTime.num, [this.relativeTime.num]) @@ -35,6 +47,40 @@ export default { } return timeString + }, + absoluteTimeString () { + if (this.longFormat) { + return this.localeDateString + } + const now = new Date() + const formatter = (() => { + if (DateUtils.isSameDay(this.timeAsDate, now)) { + return new Intl.DateTimeFormat(this.browserLocale, { + minute: 'numeric', + hour: 'numeric' + }) + } else if (DateUtils.isSameMonth(this.timeAsDate, now)) { + return new Intl.DateTimeFormat(this.browserLocale, { + hour: 'numeric', + day: 'numeric' + }) + } else if (DateUtils.isSameYear(this.timeAsDate, now)) { + return new Intl.DateTimeFormat(this.browserLocale, { + month: 'short', + day: 'numeric' + }) + } else { + return new Intl.DateTimeFormat(this.browserLocale, { + year: 'numeric', + month: 'short' + }) + } + })() + + return formatter.format(this.timeAsDate) + }, + relativeOrAbsoluteTimeString () { + return this.shouldUseAbsoluteTimeFormat ? this.absoluteTimeString : this.relativeTimeString } }, watch: { @@ -54,6 +100,7 @@ export default { methods: { refreshRelativeTimeObject () { const nowThreshold = typeof this.nowThreshold === 'number' ? this.nowThreshold : 1 + this.relativeTimeMs = DateUtils.relativeTimeMs(this.time) this.relativeTime = this.longFormat ? DateUtils.relativeTime(this.time, nowThreshold) : DateUtils.relativeTimeShort(this.time, nowThreshold) diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js index 1050b87a..59170f49 100644 --- a/src/components/timeline/timeline.js +++ b/src/components/timeline/timeline.js @@ -25,6 +25,7 @@ const Timeline = { 'title', 'userId', 'listId', + 'statusId', 'tag', 'embedded', 'count', @@ -77,13 +78,13 @@ const Timeline = { } }, classes () { - let rootClasses = !this.embedded ? ['panel', 'panel-default'] : ['-nonpanel'] + let rootClasses = !this.embedded ? ['panel', 'panel-default'] : ['-embedded'] if (this.blockingClicks) rootClasses = rootClasses.concat(['-blocked', '_misclick-prevention']) return { root: rootClasses, - header: ['timeline-heading'].concat(!this.embedded ? ['panel-heading', '-sticky'] : []), - body: ['timeline-body'].concat(!this.embedded ? ['panel-body'] : []), - footer: ['timeline-footer'].concat(!this.embedded ? ['panel-footer'] : []) + header: ['timeline-heading'].concat(!this.embedded ? ['panel-heading', '-sticky'] : ['panel-body']), + body: ['timeline-body'].concat(!this.embedded ? ['panel-body'] : ['panel-body']), + footer: ['timeline-footer'].concat(!this.embedded ? ['panel-footer'] : ['panel-body']) } }, // id map of statuses which need to be hidden in the main list due to pinning logic @@ -121,6 +122,7 @@ const Timeline = { showImmediately, userId: this.userId, listId: this.listId, + statusId: this.statusId, tag: this.tag }) }, @@ -183,6 +185,7 @@ const Timeline = { showImmediately: true, userId: this.userId, listId: this.listId, + statusId: this.statusId, tag: this.tag }).then(({ statuses }) => { if (statuses && statuses.length === 0) { diff --git a/src/components/timeline/timeline.scss b/src/components/timeline/timeline.scss index 4371947d..2dd66328 100644 --- a/src/components/timeline/timeline.scss +++ b/src/components/timeline/timeline.scss @@ -1,31 +1,20 @@ -@import "../../variables"; - .Timeline { - .alert-dot { - border-radius: 100%; - height: 8px; - width: 8px; - position: absolute; - left: calc(50% - 4px); - top: calc(50% - 4px); - margin-left: 6px; - margin-top: -6px; - background-color: var(--badgeNeutral); + .timeline-body { + background: none; + backdrop-filter: none; } .alert-badge { font-size: 0.75em; line-height: 1; text-align: right; - border-radius: var(--tooltipRadius); + border-radius: var(--roundness); position: absolute; left: calc(50% - 0.5em); top: calc(50% - 0.4em); padding: 0.2em; margin-left: 0.7em; margin-top: -1em; - background-color: var(--badgeNeutral); - color: var(--badgeNeutralText); } .loadmore-button { @@ -37,16 +26,21 @@ } .conversation-heading { - top: calc(var(--__panel-heading-height) * var(--currentPanelStack, 2)); + top: calc(var(--__panel-heading-height) * var(--currentPanelStack, 1) + var(--navbar-height)); z-index: 2; } - &.-nonpanel { + &.-embedded { .timeline-heading { text-align: center; line-height: 2.75em; padding: 0 0.5em; + // Override the shrug empty filler + &:empty::before { + content: initial; + } + .button-default, .alert { line-height: 2em; diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue index 2279f21a..862a1972 100644 --- a/src/components/timeline/timeline.vue +++ b/src/components/timeline/timeline.vue @@ -38,7 +38,7 @@ fixed-width icon="circle-plus" /> - <div class="alert-badge"> + <div class="badge -counter"> {{ mobileLoadButtonString }} </div> </button> diff --git a/src/components/timeline_menu/timeline_menu.js b/src/components/timeline_menu/timeline_menu.js index 5a2a86c2..c4586b32 100644 --- a/src/components/timeline_menu/timeline_menu.js +++ b/src/components/timeline_menu/timeline_menu.js @@ -19,7 +19,8 @@ export const timelineNames = () => { bookmarks: 'nav.bookmarks', dms: 'nav.dms', 'public-timeline': 'nav.public_tl', - 'public-external-timeline': 'nav.twkn' + 'public-external-timeline': 'nav.twkn', + quotes: 'nav.quotes' } } diff --git a/src/components/timeline_menu/timeline_menu.vue b/src/components/timeline_menu/timeline_menu.vue index 5f1da1f7..d755b9dd 100644 --- a/src/components/timeline_menu/timeline_menu.vue +++ b/src/components/timeline_menu/timeline_menu.vue @@ -45,8 +45,6 @@ <script src="./timeline_menu.js"></script> <style lang="scss"> -@import "../../variables"; - .timeline-menu-popover { min-width: 24rem; max-width: 100vw; @@ -60,65 +58,6 @@ margin: 0; padding: 0; } - - a { - display: block; - padding: 0 0.65em; - height: 3.5em; - line-height: 3.5em; - - &:hover { - background-color: $fallback--lightBg; - background-color: var(--selectedMenu, $fallback--lightBg); - color: $fallback--link; - color: var(--selectedMenuText, $fallback--link); - - --faint: var(--selectedMenuFaintText, $fallback--faint); - --faintLink: var(--selectedMenuFaintLink, $fallback--faint); - --lightText: var(--selectedMenuLightText, $fallback--lightText); - --icon: var(--selectedMenuIcon, $fallback--icon); - } - - &.router-link-active { - font-weight: bolder; - background-color: $fallback--lightBg; - background-color: var(--selectedMenu, $fallback--lightBg); - color: $fallback--text; - color: var(--selectedMenuText, $fallback--text); - - --faint: var(--selectedMenuFaintText, $fallback--faint); - --faintLink: var(--selectedMenuFaintLink, $fallback--faint); - --lightText: var(--selectedMenuLightText, $fallback--lightText); - --icon: var(--selectedMenuIcon, $fallback--icon); - - &:hover { - text-decoration: underline; - } - } - - svg { - margin-right: 0.4em; - margin-left: -0.2em; - } - } - - li { - border-bottom: 1px solid; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); - padding: 0; - - &:last-child a { - border-bottom-right-radius: $fallback--panelRadius; - border-bottom-right-radius: var(--panelRadius, $fallback--panelRadius); - border-bottom-left-radius: $fallback--panelRadius; - border-bottom-left-radius: var(--panelRadius, $fallback--panelRadius); - } - - &:last-child { - border: none; - } - } } .TimelineMenu { @@ -159,8 +98,6 @@ } &.open .timeline-menu-title svg { - color: $fallback--text; - color: var(--panelText, $fallback--text); transform: rotate(180deg); } diff --git a/src/components/top_bar.style.js b/src/components/top_bar.style.js new file mode 100644 index 00000000..46b3fb56 --- /dev/null +++ b/src/components/top_bar.style.js @@ -0,0 +1,28 @@ +export default { + name: 'TopBar', + selector: 'nav', + validInnerComponents: [ + 'Link', + 'Text', + 'Icon', + 'Button', + 'ButtonUnstyled', + 'Input', + 'Badge' + ], + defaultRules: [ + { + directives: { + background: '--fg', + shadow: [{ + x: 0, + y: 0, + blur: 4, + spread: 0, + color: '#000000', + alpha: 0.6 + }] + } + } + ] +} diff --git a/src/components/underlay.style.js b/src/components/underlay.style.js new file mode 100644 index 00000000..3e0e1bf1 --- /dev/null +++ b/src/components/underlay.style.js @@ -0,0 +1,19 @@ +export default { + name: 'Underlay', + selector: '#content', + // Out of tree selector: Most components are laid over underlay, but underlay itself is not part of the DOM tree, + // i.e. it's a separate absolutely-positioned component, so we need to treat it differently depending on whether + // we are searching for underlay specifically or for whatever is laid on top of it. + outOfTreeSelector: '.underlay', + validInnerComponents: [ + 'Panel' + ], + defaultRules: [ + { + directives: { + background: '#000000', + opacity: 0.2 + } + } + ] +} diff --git a/src/components/update_notification/update_notification.scss b/src/components/update_notification/update_notification.scss index 4337acc4..fb2edaa4 100644 --- a/src/components/update_notification/update_notification.scss +++ b/src/components/update_notification/update_notification.scss @@ -1,5 +1,3 @@ -@import "src/variables"; - .UpdateNotification { overflow: hidden; } @@ -48,7 +46,7 @@ .panel-body { border-width: 0 0 1px; border-style: solid; - border-color: var(--border, $fallback--border); + border-color: var(--border); } .panel-footer { diff --git a/src/components/user_avatar/avatar.style.js b/src/components/user_avatar/avatar.style.js new file mode 100644 index 00000000..812d45a4 --- /dev/null +++ b/src/components/user_avatar/avatar.style.js @@ -0,0 +1,22 @@ +export default { + name: 'Avatar', + selector: '.Avatar', + variants: { + compact: '.-compact' + }, + defaultRules: [ + { + directives: { + roundness: 3, + shadow: [{ + x: 0, + y: 1, + blur: 8, + spread: 0, + color: '#000000', + alpha: 0.7 + }] + } + } + ] +} diff --git a/src/components/user_avatar/user_avatar.vue b/src/components/user_avatar/user_avatar.vue index 3cbccec3..83608c50 100644 --- a/src/components/user_avatar/user_avatar.vue +++ b/src/components/user_avatar/user_avatar.vue @@ -32,12 +32,10 @@ <script src="./user_avatar.js"></script> <style lang="scss"> -@import "../../variables"; - .Avatar { - --_avatarShadowBox: var(--avatarStatusShadow); - --_avatarShadowFilter: var(--avatarStatusShadowFilter); - --_avatarShadowInset: var(--avatarStatusShadowInset); + --_avatarShadowBox: var(--shadow); + --_avatarShadowFilter: var(--shadowFilter); + --_avatarShadowInset: var(--shadowInset); --_still-image-label-visibility: hidden; display: inline-block; @@ -48,16 +46,14 @@ &.-compact { width: 32px; height: 32px; - border-radius: $fallback--avatarAltRadius; - border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); + border-radius: var(--roundness); } .avatar { width: 100%; height: 100%; box-shadow: var(--_avatarShadowBox); - border-radius: $fallback--avatarRadius; - border-radius: var(--avatarRadius, $fallback--avatarRadius); + border-radius: var(--roundness); &.-better-shadow { box-shadow: var(--_avatarShadowInset); @@ -69,13 +65,11 @@ } &.-compact { - border-radius: $fallback--avatarAltRadius; - border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); + border-radius: var(--roundness); } &.-placeholder { - background-color: $fallback--fg; - background-color: var(--fg, $fallback--fg); + background-color: var(--background); } } @@ -92,7 +86,7 @@ padding: 0.2em; background: rgb(127 127 127 / 50%); color: #fff; - border-radius: var(--tooltipRadius); + border-radius: var(--roundness); } } </style> diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js index e17bf8eb..b1fe2e8f 100644 --- a/src/components/user_card/user_card.js +++ b/src/components/user_card/user_card.js @@ -225,7 +225,7 @@ export default { this.$store.dispatch('setCurrentMedia', attachment) }, mentionUser () { - this.$store.dispatch('openPostStatusModal', { replyTo: true, repliedUser: this.user }) + this.$store.dispatch('openPostStatusModal', { profileMention: true, repliedUser: this.user }) }, onAvatarClickHandler (e) { if (this.onAvatarClick) { diff --git a/src/components/user_card/user_card.scss b/src/components/user_card/user_card.scss index 4ab93a8a..70a76d54 100644 --- a/src/components/user_card/user_card.scss +++ b/src/components/user_card/user_card.scss @@ -1,5 +1,3 @@ -@import "../../variables"; - .user-card { position: relative; z-index: 1; @@ -21,14 +19,6 @@ position: relative; } - .panel-body { - word-wrap: break-word; - border-bottom-right-radius: inherit; - border-bottom-left-radius: inherit; - // create new stacking context - position: relative; - } - .background-image { position: absolute; top: 0; @@ -62,11 +52,6 @@ padding: 1em; margin: 0; - a { - color: $fallback--link; - color: var(--postLink, $fallback--link); - } - img { object-fit: contain; vertical-align: middle; @@ -76,53 +61,37 @@ } &.-rounded-t { - border-top-left-radius: $fallback--panelRadius; - border-top-left-radius: var(--panelRadius, $fallback--panelRadius); - border-top-right-radius: $fallback--panelRadius; - border-top-right-radius: var(--panelRadius, $fallback--panelRadius); + border-top-left-radius: var(--roundness); + border-top-right-radius: var(--roundness); - --__roundnessTop: var(--panelRadius); + --__roundnessTop: var(--roundness); --__roundnessBottom: 0; } &.-rounded { - border-radius: $fallback--panelRadius; - border-radius: var(--panelRadius, $fallback--panelRadius); + border-radius: var(--roundness); - --__roundnessTop: var(--panelRadius); - --__roundnessBottom: var(--panelRadius); + --__roundnessTop: var(--roundness); + --__roundnessBottom: var(--roundness); } &.-popover { - border-radius: $fallback--tooltipRadius; - border-radius: var(--tooltipRadius, $fallback--tooltipRadius); + border-radius: var(--roundness); - --__roundnessTop: var(--tooltipRadius); - --__roundnessBottom: var(--tooltipRadius); + --__roundnessTop: var(--roundness); + --__roundnessBottom: var(--roundness); } &.-bordered { border-width: 1px; border-style: solid; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); + border-color: var(--border); } } .user-info { - color: $fallback--lightText; - color: var(--lightText, $fallback--lightText); padding: 0 26px; - a { - color: $fallback--lightText; - color: var(--lightText, $fallback--lightText); - - &:hover { - color: var(--icon); - } - } - .container { min-width: 0; padding: 16px 0 6px; @@ -164,8 +133,7 @@ display: flex; justify-content: center; align-items: center; - border-radius: $fallback--avatarRadius; - border-radius: var(--avatarRadius, $fallback--avatarRadius); + border-radius: var(--roundness); opacity: 0; transition: opacity 0.2s ease; @@ -188,8 +156,7 @@ padding: 0.5em 0; &:not(:hover) .icon { - color: $fallback--lightText; - color: var(--lightText, $fallback--lightText); + color: var(--lightText); } } @@ -203,6 +170,7 @@ } .user-screen-name { + color: var(--text); min-width: 1px; flex: 0 1 auto; text-overflow: ellipsis; @@ -214,16 +182,11 @@ flex: 0 0 auto; margin-left: 1em; font-size: 0.7em; - color: $fallback--text; - color: var(--text, $fallback--text); + color: var(--text); } .user-role { flex: none; - color: $fallback--text; - color: var(--alertNeutralText, $fallback--text); - background-color: $fallback--fg; - background-color: var(--alertNeutral, $fallback--fg); } } @@ -241,6 +204,11 @@ --emoji-size: 1.7em; + .RichContent { + /* stylelint-disable-next-line declaration-no-important */ + --link: var(--text) !important; + } + .top-line, .bottom-line { display: flex; @@ -334,8 +302,6 @@ padding: 0.5em 1.5em 0; text-align: center; justify-content: space-between; - color: $fallback--lightText; - color: var(--lightText, $fallback--lightText); flex-wrap: wrap; } diff --git a/src/components/user_card/user_card.style.js b/src/components/user_card/user_card.style.js new file mode 100644 index 00000000..34eaa176 --- /dev/null +++ b/src/components/user_card/user_card.style.js @@ -0,0 +1,41 @@ +export default { + name: 'UserCard', + selector: '.user-card', + validInnerComponents: [ + 'Text', + 'Link', + 'Icon', + 'Button', + 'ButtonUnstyled', + 'Input', + 'RichContent', + 'Alert' + ], + defaultRules: [ + { + directives: { + background: '--bg', + opacity: 0, + roundness: 3, + shadow: [{ + x: 1, + y: 1, + blur: 4, + spread: 0, + color: '#000000', + alpha: 0.6 + }], + '--profileTint': 'color | $alpha(--background, 0.5)' + } + }, + { + parent: { + component: 'UserCard' + }, + component: 'RichContent', + directives: { + opacity: 0 + } + } + ] +} diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue index 2c76a220..70c4f67d 100644 --- a/src/components/user_card/user_card.vue +++ b/src/components/user_card/user_card.vue @@ -113,19 +113,19 @@ <template v-if="!hideBio"> <span v-if="user.deactivated" - class="alert user-role" + class="alert neutral user-role" > {{ $t('user_card.deactivated') }} </span> <span v-if="!!visibleRole" - class="alert user-role" + class="alert neutral user-role" > {{ $t(`general.role.${visibleRole}`) }} </span> <span v-if="user.actor_type === 'Service'" - class="alert user-role" + class="alert neutral user-role" > {{ $t('user_card.bot') }} </span> @@ -166,14 +166,14 @@ v-if="userHighlightType !== 'disabled'" :id="'userHighlightColorTx'+user.id" v-model="userHighlightColor" - class="userHighlightText" + class="input userHighlightText" type="text" > <input v-if="userHighlightType !== 'disabled'" :id="'userHighlightColor'+user.id" v-model="userHighlightColor" - class="userHighlightCl" + class="input userHighlightCl" type="color" > {{ ' ' }} @@ -282,10 +282,7 @@ /> </div> </div> - <div - v-if="!hideBio" - class="panel-body" - > + <div v-if="!hideBio"> <div v-if="!mergedConfig.hideUserStats && switcher" class="user-counts" diff --git a/src/components/user_list_menu/user_list_menu.vue b/src/components/user_list_menu/user_list_menu.vue index 06947ab7..39ee2451 100644 --- a/src/components/user_list_menu/user_list_menu.vue +++ b/src/components/user_list_menu/user_list_menu.vue @@ -10,11 +10,11 @@ <button v-for="list in lists" :key="list.id" - class="button-default dropdown-item" + class="menu-item dropdown-item" @click="toggleList(list.id)" > <span - class="menu-checkbox" + class="input menu-checkbox" :class="{ 'menu-checkbox-checked': list.inList }" /> {{ list.title }} @@ -22,7 +22,7 @@ </div> </template> <template #trigger> - <button class="btn button-default dropdown-item -has-submenu"> + <button class="menu-item dropdown-item -has-submenu"> {{ $t('lists.manage_lists') }} <FAIcon class="chevron-icon" diff --git a/src/components/user_list_popover/user_list_popover.vue b/src/components/user_list_popover/user_list_popover.vue index 8307cc8a..cd134453 100644 --- a/src/components/user_list_popover/user_list_popover.vue +++ b/src/components/user_list_popover/user_list_popover.vue @@ -48,8 +48,6 @@ <script src="./user_list_popover.js"></script> <style lang="scss"> -@import "../../variables"; - .user-list-popover { padding: 0.5em; diff --git a/src/components/user_note/user_note.vue b/src/components/user_note/user_note.vue index 4e05951f..e175ced0 100644 --- a/src/components/user_note/user_note.vue +++ b/src/components/user_note/user_note.vue @@ -33,7 +33,7 @@ <textarea v-show="editing" v-model="localNote" - class="note-text" + class="input note-text" /> <span v-show="!editing" @@ -48,8 +48,6 @@ <script src="./user_note.js"></script> <style lang="scss"> -@import "../../variables"; - .user-note { display: flex; flex-direction: column; @@ -82,7 +80,7 @@ .note-text.-blank { font-style: italic; - color: var(--faint, $fallback--faint); + color: var(--textFaint); } } </style> diff --git a/src/components/user_panel/user_panel.vue b/src/components/user_panel/user_panel.vue index 95ec97af..9323b65e 100644 --- a/src/components/user_panel/user_panel.vue +++ b/src/components/user_panel/user_panel.vue @@ -22,8 +22,15 @@ <script src="./user_panel.js"></script> <style lang="scss"> -.user-panel .signed-in { - overflow: visible; - z-index: 10; +.user-panel { + .panel { + background: var(--background); + backdrop-filter: var(--backdrop-filter); + } + + .signed-in { + overflow: visible; + z-index: 10; + } } </style> diff --git a/src/components/user_popover/user_popover.vue b/src/components/user_popover/user_popover.vue index 3b2bbc45..413b254c 100644 --- a/src/components/user_popover/user_popover.vue +++ b/src/components/user_popover/user_popover.vue @@ -24,8 +24,6 @@ <script src="./user_popover.js"></script> <style lang="scss"> -@import "../../variables"; - /* popover styles load on-demand, so we need to override */ /* stylelint-disable block-no-empty */ .user-popover.popover { diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index d0618dbb..1ec12d0c 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -4,52 +4,54 @@ v-if="user" class="user-profile panel panel-default" > - <UserCard - :user-id="userId" - :switcher="true" - :selected="timeline.viewing" - avatar-action="zoom" - rounded="top" - :has-note-editor="true" - /> - <span - v-if="!!user.birthday" - class="user-birthday" - > - <FAIcon - class="fa-old-padding" - icon="birthday-cake" + <div class="panel-body"> + <UserCard + :user-id="userId" + :switcher="true" + :selected="timeline.viewing" + avatar-action="zoom" + rounded="top" + :has-note-editor="true" /> - {{ $t('user_card.birthday', { birthday: formattedBirthday }) }} - </span> - <div - v-if="user.fields_html && user.fields_html.length > 0" - class="user-profile-fields" - > - <dl - v-for="(field, index) in user.fields_html" - :key="index" - class="user-profile-field" + <span + v-if="!!user.birthday" + class="user-birthday" > - <dt - :title="user.fields_text[index].name" - class="user-profile-field-name" - > - <RichContent - :html="field.name" - :emoji="user.emoji" - /> - </dt> - <dd - :title="user.fields_text[index].value" - class="user-profile-field-value" + <FAIcon + class="fa-old-padding" + icon="birthday-cake" + /> + {{ $t('user_card.birthday', { birthday: formattedBirthday }) }} + </span> + <div + v-if="user.fields_html && user.fields_html.length > 0" + class="user-profile-fields" + > + <dl + v-for="(field, index) in user.fields_html" + :key="index" + class="user-profile-field" > - <RichContent - :html="field.value" - :emoji="user.emoji" - /> - </dd> - </dl> + <dt + :title="user.fields_text[index].name" + class="user-profile-field-name" + > + <RichContent + :html="field.name" + :emoji="user.emoji" + /> + </dt> + <dd + :title="user.fields_text[index].value" + class="user-profile-field-value" + > + <RichContent + :html="field.value" + :emoji="user.emoji" + /> + </dd> + </dl> + </div> </div> <tab-switcher :active-tab="tab" @@ -72,10 +74,14 @@ <div v-if="followsTabVisible" key="followees" + class="panel-body" :label="$t('user_card.followees')" :disabled="!user.friends_count" > - <FriendList :user-id="userId"> + <FriendList + :user-id="userId" + :non-interactive="true" + > <template #item="{item}"> <FollowCard :user="item" /> </template> @@ -84,10 +90,14 @@ <div v-if="followersTabVisible" key="followers" + class="panel-body" :label="$t('user_card.followers')" :disabled="!user.followers_count" > - <FollowerList :user-id="userId"> + <FollowerList + :user-id="userId" + :non-interactive="true" + > <template #item="{item}"> <FollowCard :user="item" @@ -117,7 +127,7 @@ :title="$t('user_card.favorites')" timeline-name="favorites" :timeline="favorites" - :user-id="userId" + :user-id="isUs ? undefined : userId" :in-profile="true" :footer-slipgate="footerRef" /> @@ -136,7 +146,7 @@ {{ $t('settings.profile_tab') }} </div> </div> - <div class="panel-body"> + <div> <span v-if="error">{{ error }}</span> <FAIcon v-else @@ -151,8 +161,6 @@ <script src="./user_profile.js"></script> <style lang="scss"> -@import "../../variables"; - .user-profile { flex: 2; flex-basis: 500px; @@ -182,9 +190,8 @@ .user-profile-field { display: flex; margin: 0.25em; - border: 1px solid var(--border, $fallback--border); - border-radius: $fallback--inputRadius; - border-radius: var(--inputRadius, $fallback--inputRadius); + border: 1px solid var(--border); + border-radius: var(--roundness); .user-profile-field-name { flex: 0 1 30%; @@ -192,7 +199,7 @@ text-align: right; color: var(--lightText); min-width: 120px; - border-right: 1px solid var(--border, $fallback--border); + border-right: 1px solid var(--border); } .user-profile-field-value { @@ -229,4 +236,5 @@ padding: 7em; } } + </style> diff --git a/src/components/user_reporting_modal/user_reporting_modal.vue b/src/components/user_reporting_modal/user_reporting_modal.vue index 092c514e..2bda509a 100644 --- a/src/components/user_reporting_modal/user_reporting_modal.vue +++ b/src/components/user_reporting_modal/user_reporting_modal.vue @@ -19,7 +19,7 @@ <p>{{ $t('user_reporting.add_comment_description') }}</p> <textarea v-model="comment" - class="form-control" + class="input form-control" :placeholder="$t('user_reporting.additional_comments')" rows="1" @input="resize" @@ -72,8 +72,6 @@ <script src="./user_reporting_modal.js"></script> <style lang="scss"> -@import "../../variables"; - .user-reporting-panel { width: 90vw; max-width: 700px; @@ -84,8 +82,7 @@ display: flex; flex-direction: column-reverse; border-top: 1px solid; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); + border-color: var(--border); overflow: hidden; } @@ -155,8 +152,7 @@ width: 50%; max-width: 320px; border-right: 1px solid; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); + border-color: var(--border); padding: 1.1em; > div { |
