mirror of
https://github.com/penpot/penpot.git
synced 2026-04-25 11:18:36 +00:00
✨ Update text menu structure
This commit is contained in:
parent
bb3d2ab5ad
commit
c7fcac847c
@ -27,8 +27,10 @@
|
||||
[app.main.ui.ds.controls.radio-buttons :refer [radio-buttons*]]
|
||||
[app.main.ui.ds.controls.shared.searchable-options-dropdown :refer [searchable-options-dropdown*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.ds.foundations.typography :as t]
|
||||
[app.main.ui.ds.foundations.typography.text :refer [text*]]
|
||||
[app.main.ui.hooks :as hooks]
|
||||
[app.main.ui.workspace.sidebar.options.menus.text-shared :refer [text-options]]
|
||||
[app.main.ui.workspace.sidebar.options.menus.text-shared :refer [text-options token-text-options*]]
|
||||
[app.main.ui.workspace.sidebar.options.menus.token-typography-row :refer [token-typography-row*]]
|
||||
[app.main.ui.workspace.sidebar.options.menus.typography :refer [typography-entry]]
|
||||
[app.main.ui.workspace.tokens.management.forms.controls.utils :as csu]
|
||||
@ -189,6 +191,82 @@
|
||||
:label (tr "workspace.options.text-options.strikethrough" (sc/get-tooltip :line-through))
|
||||
:icon i/text-stroked}]}]]))
|
||||
|
||||
;; We are temporarily duplicating some components and modifying the copies
|
||||
;; to work under a feature flag. When the flag is set to false, the original
|
||||
;; components are used; when enabled, the new versions are used instead.
|
||||
;;
|
||||
;; This approach introduces some code duplication, but it helps avoid
|
||||
;; scattering conditional (feature flag) logic throughout the codebase,
|
||||
;; keeping both implementations easier to read and maintain during the transition.
|
||||
;;
|
||||
;; Once the feature flag is fully enabled, the old components will be removed
|
||||
;; and the duplicated code will be cleaned up.
|
||||
;;
|
||||
;; Once the feature flag is fully enabled, the old components will be removed
|
||||
;; and the duplicated code will be cleaned up.
|
||||
(mf/defc token-text-decoration-options*
|
||||
[{:keys [values on-change on-blur token-applied]}]
|
||||
(let [token-row (contains? cf/flags :token-typography-row)
|
||||
text-decoration (some-> (:text-decoration values) d/name)
|
||||
handle-change
|
||||
(mf/use-fn
|
||||
(mf/deps on-change on-blur)
|
||||
(fn [value]
|
||||
(on-change {:text-decoration value})
|
||||
(when (some? on-blur)
|
||||
(on-blur))))]
|
||||
|
||||
[:div {:class (stl/css :text-decoration-options)}
|
||||
[:> radio-buttons* {:selected text-decoration
|
||||
:on-change handle-change
|
||||
:name "text-decoration-options"
|
||||
:disabled (and token-row (some? token-applied))
|
||||
:options [{:value "none"
|
||||
:id "none-text-decoration"
|
||||
:disabled (and token-row (some? token-applied))
|
||||
:label "Unset"
|
||||
:icon i/remove}
|
||||
{:value "underline"
|
||||
:id "underline-text-decoration"
|
||||
:disabled (and token-row (some? token-applied))
|
||||
:label (tr "workspace.options.text-options.underline" (sc/get-tooltip :underline))
|
||||
:icon i/text-underlined}
|
||||
{:value "line-through"
|
||||
:id "line-through-text-decoration"
|
||||
:disabled (and token-row (some? token-applied))
|
||||
:label (tr "workspace.options.text-options.strikethrough" (sc/get-tooltip :line-through))
|
||||
:icon i/text-stroked}]}]]))
|
||||
|
||||
(mf/defc text-transform-options*
|
||||
[{:keys [values on-change on-blur]}]
|
||||
(let [handle-change
|
||||
(mf/use-fn
|
||||
(mf/deps on-change on-blur)
|
||||
(fn [value]
|
||||
(on-change {:text-transform value})
|
||||
(when (some? on-blur) (on-blur))))]
|
||||
|
||||
[:div {:class (stl/css :text-transform)}
|
||||
[:> radio-buttons* {:selected (:text-transform values)
|
||||
:on-change handle-change
|
||||
:name "text-transform-options"
|
||||
:options [{:value "none"
|
||||
:id "text-transform-none"
|
||||
:label "Unset"
|
||||
:icon i/remove}
|
||||
{:value "uppercase"
|
||||
:id "text-transform-uppercase"
|
||||
:label (tr "workspace.options.text-options.text-transform-uppercase")
|
||||
:icon i/text-uppercase}
|
||||
{:value "capitalize"
|
||||
:id "text-transform-capitalize"
|
||||
:label (tr "workspace.options.text-options.text-transform-capitalize")
|
||||
:icon i/text-mixed}
|
||||
{:value "lowercase"
|
||||
:id "text-transform-lowercase"
|
||||
:label (tr "workspace.options.text-options.text-transform-lowercase")
|
||||
:icon i/text-lowercase}]}]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Helpers
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@ -216,9 +294,6 @@
|
||||
;; Main component
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; This component is being duplicated, when text token row is enabled,
|
||||
;; this duplication will be removed and we will use token-text-menu*.
|
||||
|
||||
(mf/defc text-menu*
|
||||
{::mf/wrap [#(mf/memo' % check-props)]}
|
||||
[{:keys [ids type values applied-tokens]}]
|
||||
@ -447,8 +522,19 @@
|
||||
[:> text-decoration-options* (mf/spread-props common-props {:token-applied current-token-name})]
|
||||
[:> text-direction-options* common-props]])])]))
|
||||
|
||||
;; This component is being duplicated, when text token row is enabled,
|
||||
;; this duplication will be removed and we will use this component but renaming it to text-menu*.
|
||||
;; We are temporarily duplicating some components and modifying the copies
|
||||
;; to work under a feature flag. When the flag is set to false, the original
|
||||
;; components are used; when enabled, the new versions are used instead.
|
||||
;;
|
||||
;; This approach introduces some code duplication, but it helps avoid
|
||||
;; scattering conditional (feature flag) logic throughout the codebase,
|
||||
;; keeping both implementations easier to read and maintain during the transition.
|
||||
;;
|
||||
;; Once the feature flag is fully enabled, the old components will be removed
|
||||
;; and the duplicated code will be cleaned up.
|
||||
;;
|
||||
;; Once the feature flag is fully enabled, the old components will be removed
|
||||
;; and the duplicated code will be cleaned up.
|
||||
|
||||
(mf/defc token-text-menu*
|
||||
{::mf/wrap [#(mf/memo' % check-props)]}
|
||||
@ -660,16 +746,15 @@
|
||||
(when token-dropdown-open?
|
||||
(ts/schedule 0 #(some-> (mf/ref-val dropdown-ref) dom/focus!))))
|
||||
|
||||
[:section {:class (stl/css :element-set)
|
||||
[:section {:class (stl/css :text-menu)
|
||||
:aria-label (tr "workspace.options.text-options.text-section")}
|
||||
[:div {:class (stl/css :element-title)}
|
||||
[:div {:class (stl/css :menu-title)}
|
||||
[:> title-bar* {:collapsable true
|
||||
:collapsed (not main-menu-open?)
|
||||
:on-collapsed toggle-main-menu
|
||||
:title label
|
||||
:class (stl/css :title-spacing-text)}
|
||||
:title label}
|
||||
[:*
|
||||
(when (and (some? (resolve-delay typography-tokens)) (not typography) )
|
||||
(when (and (some? (resolve-delay typography-tokens)) (not typography))
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-label (tr "ds.inputs.numeric-input.open-token-list-dropdown")
|
||||
:on-click toggle-token-dropdown
|
||||
@ -691,6 +776,7 @@
|
||||
:active-tokens (resolve-delay typography-tokens)}]
|
||||
|
||||
typography
|
||||
;; TODO: Review this component and adapt it to new style
|
||||
[:& typography-entry {:file-id typography-file-id
|
||||
:typography typography
|
||||
:local? (= typography-file-id file-id)
|
||||
@ -698,25 +784,27 @@
|
||||
:on-change handle-change-typography}]
|
||||
|
||||
(= typography-id :multiple)
|
||||
[:div {:class (stl/css :multiple-typography)}
|
||||
[:span {:class (stl/css :multiple-text)} (tr "workspace.libraries.text.multiple-typography")]
|
||||
[:div {:class (stl/css :multiple-typography-row)}
|
||||
[:> text* {:as "span" :typography t/body-small :class (stl/css :multiple-subtitle)}
|
||||
(tr "workspace.libraries.text.multiple-typography")]
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-label (tr "workspace.libraries.text.multiple-typography-tooltip")
|
||||
:on-click handle-detach-typography
|
||||
:tooltip-placement "top-left"
|
||||
:icon i/detach}]]
|
||||
|
||||
:else
|
||||
[:> text-options #js {:ids ids
|
||||
:values values
|
||||
:on-change on-change
|
||||
:show-recent true
|
||||
:on-blur
|
||||
(fn []
|
||||
(ts/schedule
|
||||
100
|
||||
[:> token-text-options* {:ids ids
|
||||
:values values
|
||||
:on-change on-change
|
||||
:show-recent true
|
||||
:on-blur
|
||||
(fn []
|
||||
(when (not= "INPUT" (-> (dom/get-active) dom/get-tag-name))
|
||||
(dom/focus! (txu/get-text-editor-content))))))}])
|
||||
(ts/schedule
|
||||
100
|
||||
(fn []
|
||||
(when (not= "INPUT" (-> (dom/get-active) dom/get-tag-name))
|
||||
(dom/focus! (txu/get-text-editor-content))))))}])
|
||||
|
||||
[:div {:class (stl/css :text-align-options)}
|
||||
[:> text-align-options* common-props]
|
||||
@ -728,10 +816,37 @@
|
||||
:icon i/menu}]]
|
||||
|
||||
(when more-options-open?
|
||||
[:div {:class (stl/css :text-decoration-options)}
|
||||
[:> vertical-align* common-props]
|
||||
[:> text-decoration-options* (mf/spread-props common-props {:token-applied current-token-name})]
|
||||
[:> text-direction-options* common-props]])])
|
||||
[:div {:class (stl/css :advanced-options)}
|
||||
[:> text* {:as "span" :typography t/headline-small :class (stl/css :advanced-options-title)}
|
||||
"Advanced options"]
|
||||
[:div {:class (stl/css :advanced-option)}
|
||||
[:> text* {:as "span" :typography t/body-small :class (stl/css :advanced-option-title)}
|
||||
"Text Case"]
|
||||
[:> text-transform-options* common-props]
|
||||
[:> icon-button* {:variant "secondary"
|
||||
:aria-label "Open token list"
|
||||
:on-click #()
|
||||
:icon i/tokens}]]
|
||||
|
||||
[:div {:class (stl/css :advanced-option)}
|
||||
[:> text* {:as "span" :typography t/body-small :class (stl/css :advanced-option-title)}
|
||||
"Text Decoration"]
|
||||
[:> token-text-decoration-options* (mf/spread-props common-props {:token-applied current-token-name})]
|
||||
[:> icon-button* {:variant "secondary"
|
||||
:aria-label "Open token list"
|
||||
:on-click #()
|
||||
:icon i/tokens}]]
|
||||
|
||||
[:div {:class (stl/css :advanced-option)}
|
||||
[:> text* {:as "span" :typography t/body-small :class (stl/css :advanced-option-title)}
|
||||
"Align"]
|
||||
[:> vertical-align* common-props]]
|
||||
|
||||
|
||||
[:div {:class (stl/css :advanced-option)}
|
||||
[:> text* {:as "span" :typography t/body-small :class (stl/css :advanced-option-title)}
|
||||
"Direction"]
|
||||
[:> text-direction-options* common-props]]])])
|
||||
|
||||
(when token-dropdown-open?
|
||||
[:> searchable-options-dropdown* {:on-click on-option-click
|
||||
|
||||
@ -64,3 +64,52 @@
|
||||
display: flex;
|
||||
gap: var(--sp-xs);
|
||||
}
|
||||
|
||||
|
||||
// This rules are used in token-text-menu
|
||||
.text-menu {
|
||||
@include sidebar.option-grid-structure;
|
||||
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
grid-column: span 8;
|
||||
}
|
||||
|
||||
.multiple-typography-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: $br-8;
|
||||
height: $sz-32;
|
||||
padding: var(--sp-s);
|
||||
background-color: var(--color-background-tertiary);
|
||||
}
|
||||
|
||||
.multiple-subtitle {
|
||||
flex-grow: 1;
|
||||
color: var(--color-foreground-primary);
|
||||
}
|
||||
|
||||
.advanced-options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--sp-xs);
|
||||
padding: var(--sp-m) 0;
|
||||
}
|
||||
|
||||
.advanced-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--sp-xs);
|
||||
}
|
||||
|
||||
.advanced-options-title {
|
||||
color: var(--color-foreground-secondary);
|
||||
padding-block-end: var(--sp-xs);
|
||||
}
|
||||
|
||||
.advanced-option-title {
|
||||
color: var(--color-foreground-secondary);
|
||||
flex-grow: 1;
|
||||
}
|
||||
@ -8,7 +8,6 @@
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
["react-virtualized" :as rvt]
|
||||
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.exceptions :as ex]
|
||||
@ -461,3 +460,27 @@
|
||||
[:> spacing-options opts]
|
||||
[:> text-transform-options opts]]]))
|
||||
|
||||
;; We are temporarily duplicating some components and modifying the copies
|
||||
;; to work under a feature flag. When the flag is set to false, the original
|
||||
;; components are used; when enabled, the new versions are used instead.
|
||||
;;
|
||||
;; This approach introduces some code duplication, but it helps avoid
|
||||
;; scattering conditional (feature flag) logic throughout the codebase,
|
||||
;; keeping both implementations easier to read and maintain during the transition.
|
||||
|
||||
(mf/defc token-text-options*
|
||||
[{:keys [ids editor values on-change on-blur show-recent]}]
|
||||
(let [full-size-selector? (and show-recent (= (mf/use-ctx ctx/sidebar) :right))
|
||||
opts #js {:editor editor
|
||||
:ids ids
|
||||
:values values
|
||||
:on-change on-change
|
||||
:on-blur on-blur
|
||||
:show-recent show-recent
|
||||
:full-size-selector full-size-selector?}]
|
||||
[:div {:class (stl/css-case :text-options true
|
||||
:text-options-full-size full-size-selector?)}
|
||||
[:> font-options opts]
|
||||
[:div {:class (stl/css :typography-variations)}
|
||||
[:> spacing-options opts]]]))
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.config :as cf]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
||||
[app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu*]]
|
||||
@ -32,10 +33,11 @@
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [shape shapes-with-children libraries file-id page-id]}]
|
||||
|
||||
(let [id (dm/get-prop shape :id)
|
||||
type (dm/get-prop shape :type)
|
||||
ids (mf/with-memo [id] [id])
|
||||
shapes (mf/with-memo [shape] [shape])
|
||||
(let [id (dm/get-prop shape :id)
|
||||
type (dm/get-prop shape :type)
|
||||
ids (mf/with-memo [id] [id])
|
||||
shapes (mf/with-memo [shape] [shape])
|
||||
token-row (contains? cf/flags :token-typography-row)
|
||||
|
||||
applied-tokens
|
||||
(get shape :applied-tokens)
|
||||
@ -171,10 +173,29 @@
|
||||
[:& blur-menu {:type type :ids blur-ids :values blur-values}])
|
||||
|
||||
(when-not (empty? text-ids)
|
||||
[:> ot/text-menu* {:type type
|
||||
:ids text-ids
|
||||
:values text-values
|
||||
:applied-tokens text-tokens}])
|
||||
;; We are temporarily duplicating some components and modifying the copies
|
||||
;; to work under a feature flag. When the flag is set to false, the original
|
||||
;; components are used; when enabled, the new versions are used instead.
|
||||
;;
|
||||
;; This approach introduces some code duplication, but it helps avoid
|
||||
;; scattering conditional (feature flag) logic throughout the codebase,
|
||||
;; keeping both implementations easier to read and maintain during the transition.
|
||||
;;
|
||||
;; Once the feature flag is fully enabled, the old components will be removed
|
||||
;; and the duplicated code will be cleaned up.
|
||||
|
||||
(if token-row
|
||||
[:> ot/token-text-menu*
|
||||
{:ids text-ids
|
||||
:type type
|
||||
:applied-tokens text-tokens
|
||||
:values text-values}]
|
||||
|
||||
[:> ot/text-menu*
|
||||
{:ids text-ids
|
||||
:type type
|
||||
:applied-tokens text-tokens
|
||||
:values text-values}]))
|
||||
|
||||
(when-not (empty? svg-values)
|
||||
[:& svg-attrs-menu {:ids ids :values svg-values}])
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
[app.common.types.text :as txt]
|
||||
[app.common.types.token :as tt]
|
||||
[app.common.weak :as weak]
|
||||
[app.config :as cf]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-attrs blur-menu]]
|
||||
[app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu*]]
|
||||
@ -326,7 +327,8 @@
|
||||
(mf/defc options*
|
||||
{::mf/wrap [#(mf/memo' % check-options-props)]}
|
||||
[{:keys [shapes shapes-with-children page-id file-id libraries] :as props}]
|
||||
(let [shape-ids
|
||||
(let [token-row (contains? cf/flags :token-typography-row)
|
||||
shape-ids
|
||||
(mf/with-memo [shapes]
|
||||
(into #{} d/xf:map-id shapes))
|
||||
|
||||
@ -480,12 +482,29 @@
|
||||
(when-not (or (empty? constraint-ids) ^boolean is-layout-child?)
|
||||
[:& constraints-menu {:ids constraint-ids :values constraint-values}])
|
||||
|
||||
;; We are temporarily duplicating some components and modifying the copies
|
||||
;; to work under a feature flag. When the flag is set to false, the original
|
||||
;; components are used; when enabled, the new versions are used instead.
|
||||
;;
|
||||
;; This approach introduces some code duplication, but it helps avoid
|
||||
;; scattering conditional (feature flag) logic throughout the codebase,
|
||||
;; keeping both implementations easier to read and maintain during the transition.
|
||||
;;
|
||||
;; Once the feature flag is fully enabled, the old components will be removed
|
||||
;; and the duplicated code will be cleaned up.
|
||||
(when-not (empty? text-ids)
|
||||
[:> ot/text-menu*
|
||||
{:type type
|
||||
:ids text-ids
|
||||
:values text-values
|
||||
:applied-tokens text-tokens}])
|
||||
(if token-row
|
||||
[:> ot/token-text-menu*
|
||||
{:ids text-ids
|
||||
:type type
|
||||
:applied-tokens text-tokens
|
||||
:values text-values}]
|
||||
|
||||
[:> ot/text-menu*
|
||||
{:ids text-ids
|
||||
:type type
|
||||
:applied-tokens text-tokens
|
||||
:values text-values}]))
|
||||
|
||||
(when-not (empty? fill-ids)
|
||||
[:> fill/fill-menu* {:type type
|
||||
|
||||
@ -179,7 +179,6 @@
|
||||
:values (select-keys shape constraint-attrs)}])
|
||||
|
||||
(if token-row
|
||||
|
||||
[:> token-text-menu*
|
||||
{:ids ids
|
||||
:type type
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user