From 7abfddd8a5a5e8c08c1ec198976591a502010cf4 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 6 Jun 2023 21:49:49 +0200 Subject: [PATCH] Change design of dropdowns in web UI (#25107) --- .../mastodon/components/dropdown_menu.jsx | 4 +- .../mastodon/components/status_action_bar.jsx | 14 +- .../features/account/components/header.jsx | 8 +- .../features/status/components/action_bar.jsx | 12 +- .../styles/mastodon/components.scss | 275 ++---------------- app/javascript/styles/mastodon/variables.scss | 7 + 6 files changed, 48 insertions(+), 272 deletions(-) diff --git a/app/javascript/mastodon/components/dropdown_menu.jsx b/app/javascript/mastodon/components/dropdown_menu.jsx index 4cadf907e7..3cfa0ee125 100644 --- a/app/javascript/mastodon/components/dropdown_menu.jsx +++ b/app/javascript/mastodon/components/dropdown_menu.jsx @@ -121,10 +121,10 @@ class DropdownMenu extends PureComponent { return
  • ; } - const { text, href = '#', target = '_blank', method } = option; + const { text, href = '#', target = '_blank', method, dangerous } = option; return ( -
  • +
  • {text} diff --git a/app/javascript/mastodon/components/status_action_bar.jsx b/app/javascript/mastodon/components/status_action_bar.jsx index 936a66080e..8b3c20f824 100644 --- a/app/javascript/mastodon/components/status_action_bar.jsx +++ b/app/javascript/mastodon/components/status_action_bar.jsx @@ -280,8 +280,8 @@ class StatusActionBar extends ImmutablePureComponent { if (writtenByMe) { menu.push({ text: intl.formatMessage(messages.edit), action: this.handleEditClick }); - menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick }); - menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick }); + menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick, dangerous: true }); + menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick, dangerous: true }); } else { menu.push({ text: intl.formatMessage(messages.mention, { name: account.get('username') }), action: this.handleMentionClick }); menu.push({ text: intl.formatMessage(messages.direct, { name: account.get('username') }), action: this.handleDirectClick }); @@ -290,22 +290,22 @@ class StatusActionBar extends ImmutablePureComponent { if (relationship && relationship.get('muting')) { menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get('username') }), action: this.handleMuteClick }); } else { - menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.handleMuteClick }); + menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.handleMuteClick, dangerous: true }); } if (relationship && relationship.get('blocking')) { menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get('username') }), action: this.handleBlockClick }); } else { - menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.handleBlockClick }); + menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.handleBlockClick, dangerous: true }); } if (!this.props.onFilter) { menu.push(null); - menu.push({ text: intl.formatMessage(messages.filter), action: this.handleFilterClick }); + menu.push({ text: intl.formatMessage(messages.filter), action: this.handleFilterClick, dangerous: true }); menu.push(null); } - menu.push({ text: intl.formatMessage(messages.report, { name: account.get('username') }), action: this.handleReport }); + menu.push({ text: intl.formatMessage(messages.report, { name: account.get('username') }), action: this.handleReport, dangerous: true }); if (account.get('acct') !== account.get('username')) { const domain = account.get('acct').split('@')[1]; @@ -315,7 +315,7 @@ class StatusActionBar extends ImmutablePureComponent { if (relationship && relationship.get('domain_blocking')) { menu.push({ text: intl.formatMessage(messages.unblockDomain, { domain }), action: this.handleUnblockDomain }); } else { - menu.push({ text: intl.formatMessage(messages.blockDomain, { domain }), action: this.handleBlockDomain }); + menu.push({ text: intl.formatMessage(messages.blockDomain, { domain }), action: this.handleBlockDomain, dangerous: true }); } } diff --git a/app/javascript/mastodon/features/account/components/header.jsx b/app/javascript/mastodon/features/account/components/header.jsx index e9e5934f46..a206bcc3ba 100644 --- a/app/javascript/mastodon/features/account/components/header.jsx +++ b/app/javascript/mastodon/features/account/components/header.jsx @@ -332,16 +332,16 @@ class Header extends ImmutablePureComponent { if (account.getIn(['relationship', 'muting'])) { menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get('username') }), action: this.props.onMute }); } else { - menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.props.onMute }); + menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.props.onMute, dangerous: true }); } if (account.getIn(['relationship', 'blocking'])) { menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get('username') }), action: this.props.onBlock }); } else { - menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.props.onBlock }); + menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.props.onBlock, dangerous: true }); } - menu.push({ text: intl.formatMessage(messages.report, { name: account.get('username') }), action: this.props.onReport }); + menu.push({ text: intl.formatMessage(messages.report, { name: account.get('username') }), action: this.props.onReport, dangerous: true }); } if (signedIn && isRemote) { @@ -350,7 +350,7 @@ class Header extends ImmutablePureComponent { if (account.getIn(['relationship', 'domain_blocking'])) { menu.push({ text: intl.formatMessage(messages.unblockDomain, { domain: remoteDomain }), action: this.props.onUnblockDomain }); } else { - menu.push({ text: intl.formatMessage(messages.blockDomain, { domain: remoteDomain }), action: this.props.onBlockDomain }); + menu.push({ text: intl.formatMessage(messages.blockDomain, { domain: remoteDomain }), action: this.props.onBlockDomain, dangerous: true }); } } diff --git a/app/javascript/mastodon/features/status/components/action_bar.jsx b/app/javascript/mastodon/features/status/components/action_bar.jsx index 2ce94d9d84..bc90ce592c 100644 --- a/app/javascript/mastodon/features/status/components/action_bar.jsx +++ b/app/javascript/mastodon/features/status/components/action_bar.jsx @@ -219,8 +219,8 @@ class ActionBar extends PureComponent { menu.push({ text: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick }); menu.push(null); menu.push({ text: intl.formatMessage(messages.edit), action: this.handleEditClick }); - menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick }); - menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick }); + menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick, dangerous: true }); + menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick, dangerous: true }); } else { menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick }); menu.push(null); @@ -228,16 +228,16 @@ class ActionBar extends PureComponent { if (relationship && relationship.get('muting')) { menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get('username') }), action: this.handleMuteClick }); } else { - menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.handleMuteClick }); + menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.handleMuteClick, dangerous: true }); } if (relationship && relationship.get('blocking')) { menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get('username') }), action: this.handleBlockClick }); } else { - menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.handleBlockClick }); + menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.handleBlockClick, dangerous: true }); } - menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport }); + menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport, dangerous: true }); if (account.get('acct') !== account.get('username')) { const domain = account.get('acct').split('@')[1]; @@ -247,7 +247,7 @@ class ActionBar extends PureComponent { if (relationship && relationship.get('domain_blocking')) { menu.push({ text: intl.formatMessage(messages.unblockDomain, { domain }), action: this.handleUnblockDomain }); } else { - menu.push({ text: intl.formatMessage(messages.blockDomain, { domain }), action: this.handleBlockDomain }); + menu.push({ text: intl.formatMessage(messages.blockDomain, { domain }), action: this.handleBlockDomain, dangerous: true }); } } diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 8ff0908bf6..a9c19a231f 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1601,105 +1601,6 @@ a .account__avatar { gap: 4px; } -.account__disclaimer { - padding: 10px; - border-top: 1px solid lighten($ui-base-color, 8%); - color: $dark-text-color; - - strong { - font-weight: 500; - - @each $lang in $cjk-langs { - &:lang(#{$lang}) { - font-weight: 700; - } - } - } - - a { - font-weight: 500; - color: inherit; - text-decoration: underline; - - &:hover, - &:focus, - &:active { - text-decoration: none; - } - } -} - -.account__action-bar { - border-top: 1px solid lighten($ui-base-color, 8%); - border-bottom: 1px solid lighten($ui-base-color, 8%); - line-height: 36px; - overflow: hidden; - flex: 0 0 auto; - display: flex; -} - -.account__action-bar-dropdown { - padding: 10px; - - .icon-button { - vertical-align: middle; - } - - .dropdown--active { - .dropdown__content.dropdown__right { - inset-inline-start: 6px; - inset-inline-end: initial; - } - - &::after { - bottom: initial; - margin-inline-start: 11px; - margin-top: -7px; - inset-inline-end: initial; - } - } -} - -.account__action-bar-links { - display: flex; - flex: 1 1 auto; - line-height: 18px; - text-align: center; -} - -.account__action-bar__tab { - text-decoration: none; - overflow: hidden; - flex: 0 1 100%; - border-inline-end: 1px solid lighten($ui-base-color, 8%); - padding: 10px 0; - border-bottom: 4px solid transparent; - - &.active { - border-bottom: 4px solid $ui-highlight-color; - } - - & > span { - display: block; - text-transform: uppercase; - font-size: 11px; - color: $darker-text-color; - } - - strong { - display: block; - font-size: 15px; - font-weight: 500; - color: $primary-text-color; - - @each $lang in $cjk-langs { - &:lang(#{$lang}) { - font-weight: 700; - } - } - } -} - .account-authorize { padding: 14px 10px; @@ -2051,36 +1952,18 @@ a.account__display-name { } .dropdown-animation { - animation: dropdown 300ms cubic-bezier(0.1, 0.7, 0.1, 1); + animation: dropdown 150ms cubic-bezier(0.1, 0.7, 0.1, 1); @keyframes dropdown { from { opacity: 0; - transform: scaleX(0.85) scaleY(0.75); } to { opacity: 1; - transform: scaleX(1) scaleY(1); } } - &.top { - transform-origin: bottom; - } - - &.right { - transform-origin: left; - } - - &.bottom { - transform-origin: top; - } - - &.left { - transform-origin: right; - } - .reduce-motion & { animation: none; } @@ -2096,16 +1979,17 @@ a.account__display-name { } .dropdown-menu__separator { - border-bottom: 1px solid darken($ui-secondary-color, 8%); - margin: 5px 7px 6px; + border-bottom: 1px solid var(--dropdown-border-color); + margin: 5px 0; height: 0; } .dropdown-menu { - background: $ui-secondary-color; - padding: 4px 0; + background: var(--dropdown-background-color); + border: 1px solid var(--dropdown-border-color); + padding: 4px; border-radius: 4px; - box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4); + box-shadow: var(--dropdown-shadow); z-index: 9999; &__text-button { @@ -2126,12 +2010,13 @@ a.account__display-name { &__container { &__header { - border-bottom: 1px solid darken($ui-secondary-color, 8%); - padding: 4px 14px; - padding-bottom: 8px; + border-bottom: 1px solid var(--dropdown-border-color); + padding: 10px 14px; + padding-bottom: 14px; + margin-bottom: 4px; font-size: 13px; line-height: 18px; - color: $inverted-text-color; + color: $darker-text-color; } &__list { @@ -2168,103 +2053,43 @@ a.account__display-name { } } -.dropdown-menu__arrow { - position: absolute; - - &::before { - content: ''; - display: block; - width: 14px; - height: 5px; - background-color: $ui-secondary-color; - mask-image: url("data:image/svg+xml;utf8,"); - } - - &.top { - bottom: -5px; - - &::before { - transform: rotate(180deg); - } - } - - &.right { - inset-inline-start: -9px; - - &::before { - transform: rotate(-90deg); - } - } - - &.bottom { - top: -5px; - } - - &.left { - inset-inline-end: -9px; - - &::before { - transform: rotate(90deg); - } - } -} - .dropdown-menu__item { font-size: 13px; line-height: 18px; + font-weight: 500; display: block; - color: $inverted-text-color; + + &--dangerous { + color: $error-value-color; + } a, button { - font-family: inherit; - font-size: inherit; - line-height: inherit; + font: inherit; display: block; width: 100%; - padding: 4px 14px; + padding: 10px 14px; border: 0; margin: 0; + background: transparent; box-sizing: border-box; text-decoration: none; - background: $ui-secondary-color; color: inherit; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: inherit; + border-radius: 4px; &:focus, &:hover, &:active { - background: $ui-highlight-color; - color: $secondary-text-color; + background: var(--dropdown-border-color); outline: 0; } } } -.dropdown-menu__item--text { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - padding: 4px 14px; -} - -.dropdown-menu__item.edited-timestamp__history__item { - border-bottom: 1px solid darken($ui-secondary-color, 8%); - - &:last-child { - border-bottom: 0; - } - - &.dropdown-menu__item--text, - a, - button { - padding: 8px 14px; - } -} - .inline-account { display: inline-flex; align-items: center; @@ -2280,62 +2105,6 @@ a.account__display-name { } } -.dropdown--active .dropdown__content { - display: block; - line-height: 18px; - max-width: 311px; - inset-inline-end: 0; - text-align: start; - z-index: 9999; - - & > ul { - list-style: none; - background: $ui-secondary-color; - padding: 4px 0; - border-radius: 4px; - box-shadow: 0 0 15px rgba($base-shadow-color, 0.4); - min-width: 140px; - position: relative; - } - - &.dropdown__right { - inset-inline-end: 0; - } - - &.dropdown__left { - & > ul { - inset-inline-start: -98px; - } - } - - & > ul > li > a { - font-size: 13px; - line-height: 18px; - display: block; - padding: 4px 14px; - box-sizing: border-box; - text-decoration: none; - background: $ui-secondary-color; - color: $inverted-text-color; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - - &:focus { - outline: 0; - } - - &:hover { - background: $ui-highlight-color; - color: $secondary-text-color; - } - } -} - -.dropdown__icon { - vertical-align: middle; -} - .columns-area { display: flex; flex: 1 1 auto; diff --git a/app/javascript/styles/mastodon/variables.scss b/app/javascript/styles/mastodon/variables.scss index 7de25f8fd4..d6dda1b3c7 100644 --- a/app/javascript/styles/mastodon/variables.scss +++ b/app/javascript/styles/mastodon/variables.scss @@ -61,3 +61,10 @@ $no-gap-breakpoint: 1175px; $font-sans-serif: 'mastodon-font-sans-serif' !default; $font-display: 'mastodon-font-display' !default; $font-monospace: 'mastodon-font-monospace' !default; + +:root { + --dropdown-border-color: #{lighten($ui-base-color, 12%)}; + --dropdown-background-color: #{lighten($ui-base-color, 4%)}; + --dropdown-shadow: 0 20px 25px -5px #{rgba($base-shadow-color, 0.25)}, + 0 8px 10px -6px #{rgba($base-shadow-color, 0.25)}; +}