Compare commits

...
Author SHA1 Message Date
Eva Marco c7fcac847c Update text menu structure 2026-04-15 19:23:32 +02:00
Eva Marco bb3d2ab5ad ♻️ Update text menu scss 2026-04-15 19:23:32 +02:00
Eva Marco bc5f45abf7 ♻️ Divide components to avoid errors when flag is disabled 2026-04-15 19:23:32 +02:00
Eva Marco 3e12af47f0 ♻️ Divide components into different files 2026-04-15 19:23:31 +02:00
10 changed files with 1550 additions and 550 deletions

No files matched your search

@@ -10,11 +10,9 @@
// BASE COLORS
--canvas-background-color: var(--color-background-primary);
--canvas-fill-color: var(--color-canvas);
--scrollbar-background-color: var(--color-foreground-secondary);
--panel-background-color: var(--color-background-primary);
--panel-border-color: var(--color-background-quaternary);
--app-background: var(--color-background-primary);
--loader-background: var(--color-background-primary);
@@ -26,7 +24,6 @@
--button-foreground-color-disabled: var(--color-foreground-disabled);
--button-background-color-disabled: var(--color-background-quaternary);
--button-border-color-disabled: var(--color-background-quaternary);
--button-primary-background-color-rest: var(--color-accent-primary);
--button-primary-border-color-rest: var(--color-accent-primary);
--button-primary-foreground-color-rest: var(--color-background-secondary);
@@ -39,7 +36,6 @@
--button-primary-background-color-focus: var(--color-background-tertiary);
--button-primary-border-color-focus: var(--color-accent-primary);
--button-primary-foreground-color-focus: var(--color-foreground-secondary);
--button-secondary-background-color-rest: var(--color-background-tertiary);
--button-secondary-border-color-rest: var(--color-background-tertiary);
--button-secondary-foreground-color-rest: var(--color-foreground-secondary);
@@ -52,7 +48,6 @@
--button-secondary-background-color-focus: var(--color-background-tertiary);
--button-secondary-border-color-focus: var(--color-accent-primary);
--button-secondary-foreground-color-focus: var(--color-foreground-secondary);
--button-tertiary-foreground-color-rest: var(--color-foreground-secondary);
--button-tertiary-background-color-hover: var(--color-background-quaternary);
--button-tertiary-border-color-hover: var(--color-background-quaternary);
@@ -63,16 +58,13 @@
--button-tertiary-background-color-focus: var(--color-background-tertiary);
--button-tertiary-border-color-focus: var(--color-accent-primary);
--button-tertiary-foreground-color-focus: var(--color-foreground-primary);
--expand-button-icon-border-width: 0;
--expand-button-icon-border-width-selected: 0;
--button-icon-foreground-color: var(--color-foreground-secondary);
--button-icon-foreground-color-hover: var(--color-foreground-secondary);
--button-icon-background-color-selected: var(--color-background-quaternary);
--button-icon-foreground-color-selected: var(--color-accent-primary);
--button-icon-border-color-selected: var(--color-background-quaternary);
--button-radio-background-color-rest: var(--color-background-tertiary);
--button-radio-border-color-rest: var(--color-background-tertiary);
--button-radio-foreground-color-rest: var(--color-foreground-secondary);
@@ -84,20 +76,16 @@
--button-radio-background-color-focus: var(--color-background-tertiary);
--button-radio-border-color-focus: var(--color-accent-primary);
--button-radio-foreground-color-focus: var(--color-foreground-secondary);
--button-warning-background-color-rest: var(--status-color-warning-500);
--button-warning-border-color-rest: var(--status-color-warning-500);
--button-warning-foreground-color-rest: var(--color-background-secondary);
--button-disabled-background-color-rest: var(--color-background-disabled);
--button-disabled-border-color-rest: var(--color-background-disabled);
--button-disabled-foreground-color-rest: var(--color-foreground-disabled);
--button-constraint-background-color-rest: var(--color-foreground-secondary);
--button-constraint-border-color-rest: var(--color-background-tertiary);
--button-constraint-border-color-hover: var(--color-accent-primary-muted);
--button-constraint-background-color-hover: var(--color-accent-primary);
--constraint-widget-background-color: var(--color-background-tertiary);
--constraint-center-area-background-color: var(--color-background-primary);
@@ -144,7 +132,6 @@
--palette-button-shadow-initial: var(--color-background-primary);
--palette-button-shadow-final: transparent;
--palette-handler-background-color: var(--color-background-quaternary);
--color-bullet-background-color: var(--app-white); // We don't want this color to change with palette
--color-bullet-border-color: var(--color-background-quaternary);
--color-bullet-border-color-selected: var(--color-accent-primary);
@@ -183,7 +170,6 @@
--input-border-color-error: var(--status-color-error-500);
--input-border-color-success: var(--color-accent-primary);
--input-details-color: var(--color-background-primary);
--input-checkbox-background-color-rest: var(--color-background-quaternary);
--input-checkbox-border-color-rest: var(--color-foreground-secondary);
--input-checkbox-border-color-active: var(--color-background-quaternary);
@@ -200,7 +186,6 @@
--input-checkbox-background-color-active: var(--color-accent-primary);
--input-checkbox-foreground-color-active: var(--color-background-primary);
--input-checkbox-text-foreground-color: var(--color-foreground-secondary);
--menu-background-color: var(--color-background-tertiary);
--menu-foreground-color: var(--color-foreground-primary);
--menu-icon-foreground-color: var(--color-foreground-secondary);
@@ -219,7 +204,6 @@
--menu-background-color-disabled: var(--color-background-primary);
--menu-foreground-color-disabled: var(--color-foreground-secondary);
--menu-border-color-disabled: var(--color-background-quaternary);
--context-menu-background-color: var(--color-background-tertiary);
--context-menu-foreground-color: var(--color-foreground-secondary);
--context-menu-background-color-selected: var(--color-background-quaternary);
@@ -243,34 +227,27 @@
--assets-component-border-selected: var(--color-accent-tertiary);
--assets-component-second-border-selected: var(--color-background-primary);
--assets-component-hightlight: var(--color-accent-secondary);
--radio-btns-background-color: var(--color-background-tertiary);
--radio-btn-background-color-selected: var(--color-background-quaternary);
--radio-btn-foreground-color: var(--color-foreground-secondary);
--radio-btn-foreground-color-selected: var(--color-accent-primary);
--radio-btn-border-color: var(--color-background-tertiary);
--radio-btn-border-color-selected: var(--color-background-quaternary);
--library-name-foreground-color: var(--color-foreground-primary);
--library-content-foreground-color: var(--color-foreground-secondary);
--dropdown-background-color: var(--color-background-tertiary);
--dropdown-separator-color: var(--color-background-primary);
--profile-drowpdown-background-color: var(--color-background-primary);
--not-found-background-color: var(--color-background-tertiary);
--not-found-foreground-color: var(--color-foreground-secondary);
--entry-foreground-color: var(--color-foreground-secondary);
--entry-background-color: var(--color-background-tertiary);
--entry-background-color-disabled: var(--color-background-primary);
--entry-border-color-disabled: var(--color-background-quaternary);
--entry-foreground-color-hover: var(--color-foreground-primary);
--entry-background-color-hover: var(--color-background-quaternary);
--empty-message-background-color: var(--color-background-tertiary);
--empty-message-foreground-color: var(--color-foreground-secondary);
--user-count-background-color: var(--color-accent-primary);
--user-count-foreground-color: var(--color-background-secondary);
@@ -323,32 +300,25 @@
--alert-text-foreground-color-default: var(--color-foreground-primary);
--alert-icon-foreground-color-default: var(--color-foreground-primary);
--alert-border-color-default: var(--color-background-quaternary);
--alert-background-color-success: var(--color-background-success);
--alert-text-foreground-color-success: var(--color-foreground-primary);
--alert-icon-foreground-color-success: var(--color-accent-success);
--alert-border-color-success: var(--color-accent-success);
--alert-background-color-warning: var(--color-background-warning);
--alert-text-foreground-color-warning: var(--color-foreground-primary);
--alert-icon-foreground-color-warning: var(--color-accent-warning);
--alert-border-color-warning: var(--color-accent-warning);
--alert-background-color-error: var(--color-background-error);
--alert-text-foreground-color-error: var(--color-foreground-primary);
--alert-icon-foreground-color-error: var(--color-accent-error);
--alert-border-color-error: var(--color-accent-error);
--alert-background-color-info: var(--color-background-info);
--alert-text-foreground-color-info: var(--color-foreground-primary);
--alert-icon-foreground-color-info: var(--color-accent-info);
--alert-border-color-info: var(--color-accent-info);
--alert-text-foreground-color-focus: var(--color-accent-primary);
--alert-border-color-focus: var(--color-accent-primary);
--notification-foreground-color-default: var(--color-foreground-secondary);
--element-foreground-warning: var(--status-color-warning-500);
--element-foreground-error: var(--status-color-error-500);
@@ -368,21 +338,16 @@
--search-bar-foreground-color: var(--color-foreground-primary);
--search-bar-icon-foreground-color: var(--color-foreground-secondary);
--search-bar-icon-foreground-color-hover: var(--color-accent-primary);
--pill-background-color: var(--color-background-tertiary);
--pill-foreground-color: var(--color-foreground-primary);
--link-foreground-color: var(--color-accent-primary);
--register-confirmation-color: var(--status-color-success-200); //TODO: review this color
--register-confirmation-color: var(--status-color-success-200); // TODO: review this color
--resize-area-background-color: var(--color-background-primary);
--resize-area-border-color: var(--color-background-quaternary);
--profile-section-background-color: var(--color-background-tertiary);
--dashboard-list-background-color: var(--color-background-tertiary);
--dashboard-list-foreground-color: var(--color-foreground-primary);
--dashboard-list-text-foreground-color: var(--color-foreground-secondary);
--communication-tag-background-color: var(--color-foreground-primary);
--communication-tag-foreground-color: var(--color-background-tertiary);
@@ -404,7 +369,7 @@
// TODO: we should not put these functional tokens here, but rather in the components they belong to
--new-team-button-background-color: var(--color-background-primary);
//DASHBOARD
// DASHBOARD
--sidebar-element-foreground-color: var(--color-foreground-secondary);
--sidebar-element-background-color-hover: var(--color-background-secondary);
--sidebar-element-foreground-color-hover: var(--color-accent-primary);
@@ -422,21 +387,16 @@
--tab-background-color-selected: var(--color-background-primary);
--tab-border-color: var(--color-background-tertiary);
--tab-border-color-selected: var(--color-background-secondary);
--radio-btns-background-color: var(--color-background-tertiary);
--radio-btn-background-color-selected: var(--color-background-primary);
--radio-btn-foreground-color: var(--color-foreground-secondary);
--radio-btn-foreground-color-selected: var(--color-accent-primary);
--radio-btn-border-color: var(--color-background-tertiary);
--radio-btn-border-color-selected: var(--color-background-secondary);
--button-icon-background-color-selected: var(--color-background-primary);
--button-icon-border-color-selected: var(--color-background-secondary);
--assets-item-name-foreground-color: var(--color-foreground-primary);
--text-editor-selection-background-color: var(--la-tertiary-70);
--expand-button-icon-border-width-selected: 2px;
--colorpicker-background-color: var(--color-background-primary);
}
@@ -27,9 +27,12 @@
[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 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 [text-options typography-entry]]
[app.main.ui.workspace.sidebar.options.menus.typography :refer [typography-entry]]
[app.main.ui.workspace.tokens.management.forms.controls.utils :as csu]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
@@ -188,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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -223,7 +302,248 @@
typographies (mf/deref refs/workspace-file-typography)
editor-instance (mf/deref refs/workspace-editor)
libraries (mf/deref refs/files)
token-row (contains? cf/flags :token-typography-row)
;; --- UI state
menu-state* (mf/use-state {:main-menu true
:more-options false})
menu-state (deref menu-state*)
main-menu-open? (:main-menu menu-state)
more-options-open? (:more-options menu-state)
token-dropdown-open* (mf/use-state false)
token-dropdown-open? (deref token-dropdown-open*)
;; --- Applied token
applied-token-name (:typography applied-tokens)
current-token-name* (mf/use-state applied-token-name)
current-token-name (deref current-token-name*)
;; --- Available tokens
active-tokens (mf/use-ctx ctx/active-tokens-by-type)
typography-tokens (mf/with-memo [active-tokens] (csu/filter-tokens-for-input active-tokens :typography))
;; --- Dropdown
dropdown-ref (mf/use-ref nil)
dropdown-options
(mf/with-memo [typography-tokens]
(csu/get-token-dropdown-options typography-tokens nil))
selected-token-id*
(mf/use-state #(when current-token-name
(:id (get-option-by-name dropdown-options current-token-name))))
;; --- Typography
typography-id (:typography-ref-id values)
typography-file-id (:typography-ref-file values)
typography
(mf/with-memo [typography-id typography-file-id file-id libraries]
(cond
(and typography-id
(not= typography-id :multiple)
(not= typography-file-id file-id))
(-> (get-in libraries [typography-file-id :data :typographies typography-id])
(assoc :file-id typography-file-id))
(and typography-id
(not= typography-id :multiple)
(= typography-file-id file-id))
(get typographies typography-id)))
;; --- Helpers
multiple? (->> values vals (d/seek #(= % :multiple)))
label (case type
:multiple (tr "workspace.options.text-options.title-selection")
:group (tr "workspace.options.text-options.title-group")
(tr "workspace.options.text-options.title"))
;; --- Toggles
toggle-main-menu
(mf/use-fn
(mf/deps main-menu-open?)
#(swap! menu-state* update :main-menu not))
toggle-more-options
(mf/use-fn
(mf/deps more-options-open?)
#(swap! menu-state* update :more-options not))
;; --- Event handlers
emit-update!
(mf/use-fn
(mf/deps values)
(fn [ids attrs]
(st/emit! (dwt/save-font (-> (merge (txt/get-default-text-attrs) values attrs)
(select-keys txt/text-node-attrs)))
(dwt/update-all-attrs ids attrs))))
on-change
(mf/use-fn
(mf/deps ids emit-update!)
(fn [attrs]
(emit-update! ids attrs)))
on-convert-to-typography
(mf/use-fn
(mf/deps values ids file-id emit-update!)
(fn [_]
(let [set-values (-> (d/without-nils values)
(select-keys (d/concat-vec txt/text-font-attrs
txt/text-spacing-attrs
txt/text-transform-attrs)))
typography (-> (merge txt/default-typography set-values)
(dwt/generate-typography-name))
id (uuid/next)]
(st/emit! (dwl/add-typography (assoc typography :id id) false))
(emit-update! ids {:typography-ref-id id :typography-ref-file file-id}))))
on-grow-type-change
(mf/use-fn
(mf/deps ids editor-instance)
(fn [{:keys [grow-type]}]
(let [uid (js/Symbol)]
(st/emit! (dwu/start-undo-transaction uid))
(when (features/active-feature? @st/state "text-editor/v2")
(let [content (when editor-instance
(content/dom->cljs (dwt/get-editor-root editor-instance)))]
(when (some? content)
(st/emit! (dwt/v2-update-text-shape-content (first ids) content :finalize? true)))))
(st/emit! (dwsh/update-shapes ids #(assoc % :grow-type grow-type)))
(when (features/active-feature? @st/state "render-wasm/v1")
(st/emit! (dwwt/resize-wasm-text-all ids)
(ptk/data-event :layout/update {:ids ids})))
(ts/schedule #(st/emit! (dwu/commit-undo-transaction uid))))))
handle-detach-typography
(mf/use-fn
(mf/deps on-change)
#(on-change {:typography-ref-file nil :typography-ref-id nil}))
handle-change-typography
(mf/use-fn
(mf/deps typography file-id)
(fn [changes]
(st/emit! (dwl/update-typography (merge typography changes) file-id))))
expand-stream
(mf/with-memo []
(->> st/stream (rx/filter (ptk/type? :expand-text-more-options))))
on-text-blur
(mf/use-fn
(fn []
(ts/schedule
100
(fn []
(when (not= "INPUT" (-> (dom/get-active) dom/get-tag-name))
(dom/focus! (txu/get-text-editor-content)))))))
common-props
(mf/spread-props {} {:values values
:on-change on-change
:on-blur on-text-blur})]
(hooks/use-stream
expand-stream
#(swap! menu-state* assoc :more-options true))
(mf/with-effect [applied-token-name]
(reset! current-token-name* applied-token-name))
(mf/with-effect [applied-token-name dropdown-options]
(reset! selected-token-id*
(when applied-token-name
(:id (get-option-by-name dropdown-options applied-token-name)))))
(mf/with-effect [token-dropdown-open?]
(when token-dropdown-open?
(ts/schedule 0 #(some-> (mf/ref-val dropdown-ref) dom/focus!))))
[:section {:class (stl/css :element-set)
:aria-label (tr "workspace.options.text-options.text-section")}
[:div {:class (stl/css :element-title)}
[:> title-bar* {:collapsable true
:collapsed (not main-menu-open?)
:on-collapsed toggle-main-menu
:title label
:class (stl/css :title-spacing-text)}
(when (and (not typography) (not multiple?) (not applied-token-name))
[:> icon-button* {:variant "ghost"
:aria-label (tr "workspace.options.convert-to-typography")
:on-click on-convert-to-typography
:tooltip-placement "top-left"
:icon i/add}])]]
(when main-menu-open?
[:div {:class (stl/css :element-content)}
(cond
typography
[:& typography-entry {:file-id typography-file-id
:typography typography
:local? (= typography-file-id file-id)
:on-detach handle-detach-typography
: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")]
[:> icon-button* {:variant "ghost"
:aria-label (tr "workspace.libraries.text.multiple-typography-tooltip")
:on-click handle-detach-typography
:icon i/detach}]]
:else
[:> text-options #js {:ids ids
:values values
:on-change on-change
:show-recent true
:on-blur
(fn []
(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]
[:> grow-options* (mf/spread-props common-props {:on-change on-grow-type-change})]
[:> icon-button* {:variant "ghost"
:aria-label (tr "labels.options")
:data-testid "text-align-options-button"
:on-click toggle-more-options
: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]])])]))
;; 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)]}
[{:keys [ids type values applied-tokens]}]
(let [file-id (mf/use-ctx ctx/current-file-id)
typographies (mf/deref refs/workspace-file-typography)
editor-instance (mf/deref refs/workspace-editor)
libraries (mf/deref refs/files)
;; --- UI state
menu-state* (mf/use-state {:main-menu true
@@ -426,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 token-row (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
@@ -451,12 +770,13 @@
(when main-menu-open?
[:div {:class (stl/css :element-content)}
(cond
(and token-row current-token-name)
current-token-name
[:> token-typography-row* {:token-name current-token-name
:detach-token detach-token
: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)
@@ -464,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]
@@ -494,12 +816,39 @@
: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}]]
(when (and token-row token-dropdown-open?)
[: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
:id listbox-id
:options (resolve-delay dropdown-options)
@@ -4,8 +4,10 @@
//
// Copyright (c) KALEIDOS INC
@use "refactor/common-refactor.scss" as deprecated;
@use "../../../sidebar/common/sidebar.scss" as sidebar;
@use "ds/typography.scss" as t;
@use "ds/_borders.scss" as *;
@use "ds/_sizes.scss" as *;
.element-set {
@include sidebar.option-grid-structure;
@@ -18,37 +20,36 @@
}
.element-content {
@include deprecated.flexColumn;
display: flex;
flex-direction: column;
gap: var(--sp-xs);
grid-column: span 8;
margin-top: deprecated.$s-4;
margin-top: var(--sp-xs);
}
.multiple-typography {
@extend %mixed-bar;
@include t.use-typography("body-small");
display: flex;
align-items: center;
flex-grow: 1;
border-radius: $br-8;
height: $sz-32;
padding: var(--sp-s);
background-color: var(--color-background-tertiary);
color: var(--color-foreground-primary);
}
.multiple-text {
@include deprecated.bodySmallTypography;
@include t.use-typography("body-small");
flex-grow: 1;
color: var(--input-foreground-color-active);
}
.multiple-typography-button {
@extend %button-tertiary;
height: deprecated.$s-32;
width: deprecated.$s-28;
svg {
@extend %button-icon;
}
color: var(--color-foreground-primary);
}
.text-align-options {
display: flex;
gap: deprecated.$s-4;
gap: var(--sp-xs);
}
.align-options,
@@ -56,10 +57,59 @@
.vertical-align-options,
.grow-options,
.text-decoration-options {
height: deprecated.$s-32;
height: var(--sz-32);
}
.text-decoration-options {
display: flex;
gap: deprecated.$s-4;
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;
}
@@ -0,0 +1,486 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.main.ui.workspace.sidebar.options.menus.text-shared
(: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]
[app.common.types.text :as txt]
[app.main.data.fonts :as fts]
[app.main.data.shortcuts :as dsc]
[app.main.features :as features]
[app.main.fonts :as fonts]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.editable-select :refer [editable-select]]
[app.main.ui.components.numeric-input :refer [numeric-input*]]
[app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]]
[app.main.ui.components.search-bar :refer [search-bar*]]
[app.main.ui.components.select :refer [select]]
[app.main.ui.context :as ctx]
[app.main.ui.ds.foundations.assets.icon :as i]
[app.main.ui.icons :as deprecated-icon]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as kbd]
[app.util.strings :as ust]
[app.util.timers :as tm]
[cuerdas.core :as str]
[goog.events :as events]
[rumext.v2 :as mf]))
(defn- attr->string [value]
(if (= value :multiple)
""
(ust/format-precision value 2)))
(defn- get-next-font
[{:keys [id] :as current} fonts]
(if (seq fonts)
(let [index (d/index-of-pred fonts #(= (:id %) id))
index (or index -1)
next (ex/ignoring (nth fonts (inc index)))]
(or next (first fonts)))
current))
(defn- get-prev-font
[{:keys [id] :as current} fonts]
(if (seq fonts)
(let [index (d/index-of-pred fonts #(= (:id %) id))
next (ex/ignoring (nth fonts (dec index)))]
(or next (peek fonts)))
current))
(mf/defc font-item*
{::mf/wrap [mf/memo]}
[{:keys [font is-current on-click style]}]
(let [item-ref (mf/use-ref)
on-click (mf/use-fn (mf/deps font) #(on-click font))]
(mf/use-effect
(mf/deps is-current)
(fn []
(when is-current
(let [element (mf/ref-val item-ref)]
(when-not (dom/is-in-viewport? element)
(dom/scroll-into-view! element))))))
[:div {:class (stl/css :font-wrapper)
:style style
:ref item-ref
:on-click on-click}
[:div {:class (stl/css-case :font-item true
:selected is-current)}
[:span {:class (stl/css :label)} (:name font)]
[:span {:class (stl/css :icon)} (when is-current deprecated-icon/tick)]]]))
(declare row-renderer)
(defn filter-fonts
[{:keys [term backends]} fonts]
(let [term (str/lower term)
xform (cond-> (map identity)
(seq term)
(comp (filter #(str/includes? (str/lower (:name %)) term)))
(seq backends)
(comp (filter #(contains? backends (:backend %)))))]
(into [] xform fonts)))
(mf/defc font-selector*
[{:keys [on-select on-close current-font show-recent full-size]}]
(let [selected (mf/use-state current-font)
state* (mf/use-state
#(do {:term "" :backends #{}}))
state (deref state*)
flist (mf/use-ref)
input (mf/use-ref)
fonts (mf/deref fonts/fonts)
fonts (mf/with-memo [state fonts]
(filter-fonts state fonts))
recent-fonts (mf/deref refs/recent-fonts)
recent-fonts (mf/with-memo [state recent-fonts]
(filter-fonts state recent-fonts))
full-size? (boolean (and full-size show-recent))
select-next
(mf/use-fn
(mf/deps fonts)
(fn [event]
(dom/stop-propagation event)
(dom/prevent-default event)
(swap! selected get-next-font fonts)))
select-prev
(mf/use-fn
(mf/deps fonts)
(fn [event]
(dom/stop-propagation event)
(dom/prevent-default event)
(swap! selected get-prev-font fonts)))
on-key-down
(mf/use-fn
(mf/deps fonts)
(fn [event]
(cond
(kbd/up-arrow? event) (select-prev event)
(kbd/down-arrow? event) (select-next event)
(kbd/esc? event) (on-close)
(kbd/enter? event) (on-close)
:else (dom/focus! (mf/ref-val input)))))
on-filter-change
(mf/use-fn
(fn [event]
(swap! state* assoc :term event)))
on-select-and-close
(mf/use-fn
(mf/deps on-select on-close)
(fn [font]
(on-select font)
(on-close)))]
(mf/with-effect [fonts]
(let [key (events/listen js/document "keydown" on-key-down)]
#(events/unlistenByKey key)))
(mf/with-effect [@selected]
(when-let [inst (mf/ref-val flist)]
(when-let [index (:index @selected)]
(.scrollToRow ^js inst index))))
(mf/with-effect [@selected]
(on-select @selected))
(mf/with-effect []
(st/emit! (dsc/push-shortcuts :typography {}))
(fn []
(st/emit! (dsc/pop-shortcuts :typography))))
(mf/with-effect []
(let [index (d/index-of-pred fonts #(= (:id %) (:id current-font)))
inst (mf/ref-val flist)]
(tm/schedule
#(let [offset (.getOffsetForRow ^js inst #js {:alignment "center" :index index})]
(.scrollToPosition ^js inst offset)))))
[:div {:class (stl/css :font-selector)}
[:div {:class (stl/css-case :font-selector-dropdown true :font-selector-dropdown-full-size full-size?)}
[:div {:class (stl/css :header)}
[:> search-bar* {:on-change on-filter-change
:value (:term state)
:auto-focus true
:placeholder (tr "workspace.options.search-font")}]
(when (and recent-fonts show-recent)
[:section {:class (stl/css :show-recent)}
[:p {:class (stl/css :title)} (tr "workspace.options.recent-fonts")]
(for [[idx font] (d/enumerate recent-fonts)]
[:> font-item* {:key (dm/str "font-" idx)
:font font
:style {}
:on-click on-select-and-close
:is-current (= (:id font) (:id @selected))}])])]
[:div {:class (stl/css-case :fonts-list true
:fonts-list-full-size full-size?)}
[:> rvt/AutoSizer {}
(fn [props]
(let [width (unchecked-get props "width")
height (unchecked-get props "height")
render #(row-renderer fonts @selected on-select-and-close %)]
(mf/html
[:> rvt/List #js {:height height
:ref flist
:width width
:rowCount (count fonts)
:rowHeight 36
:rowRenderer render}])))]]]]))
(defn row-renderer
[fonts selected on-select props]
(let [index (unchecked-get props "index")
key (unchecked-get props "key")
style (unchecked-get props "style")
font (nth fonts index)]
(mf/html
[:> font-item* {:key key
:font font
:style style
:on-click on-select
:is-current (= (:id font) (:id selected))}])))
(mf/defc font-options
{::mf/wrap-props false}
[{:keys [values on-change on-blur show-recent full-size-selector]}]
(let [{:keys [font-id font-size font-variant-id]} values
font-id (or font-id (:font-id txt/default-typography))
font-size (or font-size (:font-size txt/default-typography))
font-variant-id (or font-variant-id (:font-variant-id txt/default-typography))
fonts (mf/deref fonts/fontsdb)
font (get fonts font-id)
last-font (mf/use-ref nil)
open-selector? (mf/use-state false)
change-font
(mf/use-fn
(mf/deps on-change fonts)
(fn [new-font-id]
(let [{:keys [family] :as font} (get fonts new-font-id)
{:keys [id name weight style]} (fonts/get-default-variant font)]
(on-change {:font-id new-font-id
:font-family family
:font-variant-id (or id name)
:font-weight weight
:font-style style})
(mf/set-ref-val! last-font font))))
on-font-size-change
(mf/use-fn
(mf/deps on-change)
(fn [new-font-size]
(when-not (str/empty? new-font-size)
(on-change {:font-size (str new-font-size)}))))
on-font-variant-change
(mf/use-fn
(mf/deps font on-change)
(fn [new-variant-id]
(let [variant (d/seek #(= new-variant-id (:id %)) (:variants font))]
(when-not (nil? variant)
(on-change {:font-id (:id font)
:font-family (:family font)
:font-variant-id new-variant-id
:font-weight (:weight variant)
:font-style (:style variant)}))
;; NOTE: the select component we are using does not fire on-blur event
;; so we need to call on-blur manually
(when (some? on-blur)
(on-blur)))))
on-font-select
(mf/use-fn
(mf/deps change-font)
(fn [font*]
(when (not= font font*)
(change-font (:id font*)))
(when (some? on-blur)
(on-blur))))
on-font-selector-close
(mf/use-fn
(fn []
(reset! open-selector? false)
(when (some? on-blur)
(on-blur))
(when (mf/ref-val last-font)
(st/emit! (fts/add-recent-font (mf/ref-val last-font))))))]
[:*
(when @open-selector?
[:> font-selector*
{:current-font font
:on-close on-font-selector-close
:on-select on-font-select
:full-size full-size-selector
:show-recent show-recent}])
[:div {:class (stl/css :font-option)
:title (tr "inspect.attributes.typography.font-family")
:on-click #(reset! open-selector? true)}
(cond
(or (= :multiple font-id) (= "mixed" font-id))
"--"
(some? font)
[:*
[:span {:class (stl/css :name)}
(:name font)]
[:span {:class (stl/css :icon)}
deprecated-icon/arrow]]
:else
(tr "dashboard.fonts.deleted-placeholder"))]
[:div {:class (stl/css :font-modifiers)}
[:div {:class (stl/css :font-size-options)
:title (tr "inspect.attributes.typography.font-size")}
(let [size-options [8 9 10 11 12 14 16 18 24 36 48 72]
size-options (if (= font-size :multiple) (into [""] size-options) size-options)]
[:& editable-select
{:value (if (= font-size :multiple) :multiple (attr->string font-size))
:class (stl/css :font-size-select)
:aria-label (tr "inspect.attributes.typography.font-size")
:input-class (stl/css :numeric-input)
:options size-options
:type "number"
:placeholder (tr "settings.multiple")
:min 3
:max 1000
:on-change on-font-size-change
:on-blur on-blur}])]
[:div {:class (stl/css :font-variant-options)
:title (tr "inspect.attributes.typography.font-style")}
(let [basic-variant-options (->> (:variants font)
(map (fn [variant]
{:value (:id variant)
:key (pr-str variant)
:label (:name variant)})))
variant-options (if (or (= font-variant-id :multiple) (= font-variant-id "mixed"))
(conj basic-variant-options
{:value ""
:key :multiple-variants
:label "--"})
basic-variant-options)
font-variant-value (attr->string font-variant-id)
font-variant-value (if (= font-variant-value "mixed") "" font-variant-value)]
;; TODO Add disabled mode
[:& select
{:class (stl/css :font-variant-select)
:default-value font-variant-value
:options variant-options
:on-change on-font-variant-change
:on-blur on-blur}])]]]))
(mf/defc spacing-options
{::mf/wrap-props false}
[{:keys [values on-change on-blur]}]
(let [{:keys [line-height
letter-spacing]} values
line-height (or line-height "1.2")
letter-spacing (or letter-spacing "0")
handle-change
(fn [value attr]
(on-change {attr (str value)}))]
[:div {:class (stl/css :spacing-options)}
[:div {:class (stl/css :line-height)
:title (tr "inspect.attributes.typography.line-height")}
[:span {:class (stl/css :icon)
:alt (tr "workspace.options.text-options.line-height")}
deprecated-icon/text-lineheight]
[:> numeric-input*
{:min -200
:max 200
:step 0.1
:default-value "1.2"
:class (stl/css :line-height-input)
:aria-label (tr "inspect.attributes.typography.line-height")
:value (attr->string line-height)
:placeholder (if (= :multiple line-height) (tr "settings.multiple") "--")
:nillable (= :multiple line-height)
:on-change #(handle-change % :line-height)
:on-blur on-blur}]]
[:div {:class (stl/css :letter-spacing)
:title (tr "inspect.attributes.typography.letter-spacing")}
[:span
{:class (stl/css :icon)
:alt (tr "workspace.options.text-options.letter-spacing")}
deprecated-icon/text-letterspacing]
[:> numeric-input*
{:min -200
:max 200
:step 0.1
:default-value "0"
:class (stl/css :letter-spacing-input)
:aria-label (tr "inspect.attributes.typography.letter-spacing")
:value (attr->string letter-spacing)
:placeholder (if (= :multiple letter-spacing) (tr "settings.multiple") "--")
:on-change #(handle-change % :letter-spacing)
:nillable (= :multiple letter-spacing)
:on-blur on-blur}]]]))
(mf/defc text-transform-options
{::mf/wrap-props false}
[{:keys [values on-change on-blur]}]
(let [text-transform (or (:text-transform values) "none")
unset-value (if (features/active-feature? @st/state "text-editor/v2") "none" "unset")
handle-change
(fn [type]
(if (= text-transform type)
(on-change {:text-transform unset-value})
(on-change {:text-transform type}))
(when (some? on-blur) (on-blur)))]
[:div {:class (stl/css :text-transform)}
[:& radio-buttons {:selected text-transform
:on-change handle-change
:name "text-transform"}
[:& radio-button {:icon i/text-uppercase
:type "checkbox"
:title (tr "inspect.attributes.typography.text-transform.uppercase")
:value "uppercase"
:id "text-transform-uppercase"}]
[:& radio-button {:icon i/text-mixed
:type "checkbox"
:value "capitalize"
:title (tr "inspect.attributes.typography.text-transform.capitalize")
:id "text-transform-capitalize"}]
[:& radio-button {:icon i/text-lowercase
:type "checkbox"
:title (tr "inspect.attributes.typography.text-transform.lowercase")
:value "lowercase"
:id "text-transform-lowercase"}]]]))
(mf/defc text-options
{::mf/wrap-props false}
[{: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]
[:> 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]]]))
@@ -0,0 +1,546 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
@use "refactor/common-refactor.scss" as deprecated;
.typography-entry {
display: flex;
flex-direction: row;
align-items: center;
height: deprecated.$s-32;
width: 100%;
border-radius: deprecated.$br-8;
background-color: var(--assets-item-background-color);
color: var(--assets-item-name-foreground-color-hover);
&:hover,
&:focus-within {
background-color: var(--assets-item-background-color-hover);
color: var(--assets-item-name-foreground-color-hover);
}
&.selected {
border: deprecated.$s-1 solid var(--assets-item-border-color);
}
.element-set-actions {
display: flex;
visibility: hidden;
.element-set-actions-button,
.menu-btn {
@extend %button-tertiary;
height: deprecated.$s-32;
width: deprecated.$s-28;
svg {
@extend %button-icon;
}
&:active {
background-color: transparent;
}
}
}
&:hover {
background-color: var(--assets-item-background-color-hover);
.element-set-actions {
visibility: visible;
}
}
}
.typography-selection-wrapper {
display: grid;
grid-template-columns: deprecated.$s-24 auto 1fr;
flex: 1;
height: 100%;
width: 100%;
padding: 0 deprecated.$s-12;
&.is-selectable {
cursor: pointer;
}
}
.typography-sample {
@include deprecated.flexCenter;
min-width: deprecated.$s-24;
height: deprecated.$s-32;
color: var(--assets-item-name-foreground-color);
}
.typography-name,
.typography-font {
@include deprecated.bodySmallTypography;
@include deprecated.textEllipsis;
display: block;
align-self: center;
margin-left: deprecated.$s-6;
}
.typography-name {
color: var(--assets-item-name-foreground-color);
}
.typography-font {
color: var(--assets-item-name-foreground-color-rest);
}
.font-name-wrapper {
@include deprecated.bodySmallTypography;
display: flex;
align-items: center;
height: deprecated.$s-32;
width: 100%;
border-radius: deprecated.$br-8;
border: deprecated.$s-1 solid transparent;
box-sizing: border-box;
background-color: var(--assets-item-background-color);
margin-bottom: deprecated.$s-4;
padding: deprecated.$s-8 deprecated.$s-0 deprecated.$s-8 deprecated.$s-12;
.typography-sample-input {
@include deprecated.flexCenter;
width: deprecated.$s-24;
height: 100%;
font-size: deprecated.$fs-16;
color: var(--assets-item-name-foreground-color-hover);
}
.adv-typography-name {
@include deprecated.removeInputStyle;
font-size: deprecated.$fs-12;
color: var(--input-foreground-color-active);
flex-grow: 1;
padding-left: deprecated.$s-6;
margin: 0;
}
.action-btn {
@extend %button-tertiary;
@include deprecated.flexCenter;
width: deprecated.$s-28;
height: deprecated.$s-28;
svg {
@extend %button-icon-small;
stroke: var(--icon-foreground);
}
&:active {
background-color: transparent;
}
}
&:focus-within {
border: deprecated.$s-1 solid var(--input-border-color-active);
.adv-typography-name {
color: var(--input-foreground-color-active);
}
}
&:hover {
background-color: var(--assets-item-background-color-hover);
}
}
.advanced-options-wrapper {
height: 100%;
width: 100%;
background-color: var(--assets-title-background-color);
}
.typography-info-wrapper {
@include deprecated.flexColumn;
margin-bottom: deprecated.$s-12;
.typography-name-wrapper {
@extend %asset-element;
display: grid;
grid-template-columns: deprecated.$s-24 auto 1fr deprecated.$s-28;
flex: 1;
height: deprecated.$s-32;
width: 100%;
padding: 0 0 0 deprecated.$s-12;
background-color: var(--assets-item-background-color-hover);
margin-bottom: deprecated.$s-4;
.typography-sample {
@include deprecated.flexCenter;
min-width: deprecated.$s-24;
font-size: deprecated.$fs-16;
height: deprecated.$s-32;
padding: 0;
color: var(--assets-item-name-foreground-color-hover);
}
.typography-name {
@include deprecated.bodySmallTypography;
@include deprecated.textEllipsis;
display: flex;
align-items: center;
justify-content: flex-start;
margin-left: deprecated.$s-6;
color: var(--assets-item-name-foreground-color-hover);
}
.typography-font {
@include deprecated.bodySmallTypography;
@include deprecated.textEllipsis;
margin-left: deprecated.$s-6;
display: flex;
align-items: center;
justify-content: flex-start;
min-width: 0;
color: var(--assets-item-name-foreground-color);
}
.action-btn {
@extend %button-tertiary;
width: deprecated.$s-28;
height: deprecated.$s-32;
svg {
@extend %button-icon;
}
&:active {
background-color: transparent;
}
}
}
.info-row {
display: grid;
grid-template-columns: 50% 50%;
height: deprecated.$s-32;
--calculated-width: calc(var(--right-sidebar-width) - deprecated.$s-48);
padding-left: deprecated.$s-2;
.info-label {
@include deprecated.bodySmallTypography;
@include deprecated.textEllipsis;
width: calc(var(--calculated-width) / 2);
padding-top: deprecated.$s-8;
color: var(--assets-item-name-foreground-color);
}
.info-content {
@include deprecated.bodySmallTypography;
@include deprecated.textEllipsis;
padding-top: deprecated.$s-8;
width: calc(var(--calculated-width) / 2);
color: var(--assets-item-name-foreground-color-hover);
}
}
.link-btn {
@include deprecated.uppercaseTitleTipography;
@extend %button-secondary;
width: 100%;
height: deprecated.$s-32;
border-radius: deprecated.$br-8;
&:hover {
background-color: var(--button-secondary-background-color-hover);
color: var(--button-secondary-foreground-color-hover);
border: deprecated.$s-1 solid var(--button-secondary-border-color-hover);
text-decoration: none;
svg {
stroke: var(--button-secondary-foreground-color-hover);
}
}
&:focus {
background-color: var(--button-secondary-background-color-focus);
color: var(--button-secondary-foreground-color-focus);
border: deprecated.$s-1 solid var(--button-secondary-border-color-focus);
svg {
stroke: var(--button-secondary-foreground-color-focus);
}
}
}
}
.text-options {
@include deprecated.flexColumn;
max-width: var(--options-width);
&:not(.text-options-full-size) {
position: relative;
}
.font-option {
@include deprecated.bodySmallTypography;
@extend %asset-element;
padding: deprecated.$s-8 0 deprecated.$s-8 deprecated.$s-8;
cursor: pointer;
.name {
flex-grow: 1;
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
}
.icon {
@include deprecated.flexCenter;
height: deprecated.$s-28;
width: deprecated.$s-28;
svg {
@extend %button-icon-small;
stroke: var(--icon-foreground);
transform: rotate(90deg);
}
}
}
.font-modifiers {
display: flex;
gap: deprecated.$s-4;
.font-size-options {
@extend %asset-element;
@include deprecated.bodySmallTypography;
flex-grow: 1;
width: deprecated.$s-60;
margin: 0;
padding: 0;
border: deprecated.$s-1 solid var(--input-border-color);
position: relative;
.icon {
@include deprecated.flexCenter;
height: deprecated.$s-28;
min-width: deprecated.$s-28;
svg {
@extend %button-icon-small;
stroke: var(--icon-foreground);
transform: rotate(90deg);
}
}
}
.font-variant-options {
padding: 0;
flex-grow: 2;
}
}
.typography-variations {
@include deprecated.flexRow;
.spacing-options {
@include deprecated.flexRow;
.line-height,
.letter-spacing {
@extend %input-element;
@include deprecated.bodySmallTypography;
.icon {
@include deprecated.flexCenter;
width: deprecated.$s-28;
svg {
@extend %button-icon-small;
}
}
}
}
.text-transform {
@extend %asset-element;
width: fit-content;
padding: 0;
background-color: var(--radio-btns-background-color);
&:hover {
background-color: var(--radio-btns-background-color);
}
}
}
}
.font-size-select {
@include deprecated.removeInputStyle;
@include deprecated.bodySmallTypography;
height: deprecated.$s-32;
height: 100%;
width: 100%;
margin: 0;
padding: deprecated.$s-8;
.numeric-input {
@extend %input-base;
@include deprecated.bodySmallTypography;
padding: 0;
}
}
.font-selector {
@include deprecated.flexCenter;
position: absolute;
top: 0;
left: 0;
right: 0;
height: 100%;
width: 100%;
z-index: deprecated.$z-index-4;
}
.show-recent {
border-radius: deprecated.$br-8 deprecated.$br-8 0 0;
background: var(--dropdown-background-color);
border: deprecated.$s-1 solid var(--color-background-quaternary);
border-block-end: none;
}
.font-selector-dropdown {
width: 100%;
&:not(.font-selector-dropdown-full-size) {
display: flex;
flex-direction: column;
flex-grow: 1;
height: 100%;
}
.header {
display: grid;
row-gap: deprecated.$s-2;
.title {
@include deprecated.uppercaseTitleTipography;
color: var(--title-foreground-color);
margin: 0;
padding: deprecated.$s-12;
}
}
}
.font-wrapper {
padding-bottom: deprecated.$s-4;
cursor: pointer;
}
.font-item {
@extend %asset-element;
margin-bottom: deprecated.$s-4;
border-radius: deprecated.$br-8;
display: flex;
.icon {
@include deprecated.flexCenter;
height: deprecated.$s-28;
width: deprecated.$s-28;
svg {
@extend %button-icon-small;
stroke: var(--icon-foreground);
}
}
&.selected {
color: var(--assets-item-name-foreground-color-hover);
.icon {
svg {
stroke: var(--assets-item-name-foreground-color-hover);
}
}
}
.label {
@include deprecated.bodySmallTypography;
@include deprecated.textEllipsis;
flex-grow: 1;
min-width: 0;
}
}
.font-selector-dropdown-full-size {
height: calc(100vh - 48px); // TODO: ugly hack :( Find a workaround for this.
display: grid;
grid-template-rows: auto 1fr;
padding: deprecated.$s-2 deprecated.$s-12 deprecated.$s-12 deprecated.$s-12;
}
.fonts-list {
position: relative;
display: flex;
flex-direction: column;
flex: 1 1 auto;
min-height: 100%;
width: 100%;
height: 100%;
padding: deprecated.$s-2;
border-radius: deprecated.$br-8;
background-color: var(--dropdown-background-color);
overflow: hidden;
&:not(.fonts-list-full-size) {
margin-block-start: deprecated.$s-2;
}
}
.fonts-list-full-size {
border-start-start-radius: 0;
border-start-end-radius: 0;
border: deprecated.$s-1 solid var(--color-background-quaternary);
// TODO: this should belong to typography-entry , but atm we don't have a clear
// way of accessing whether we are in fullsize mode or not
.selected {
padding-inline-end: 0;
}
}
@@ -7,464 +7,24 @@
(ns app.main.ui.workspace.sidebar.options.menus.typography
(: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]
[app.common.types.text :as txt]
[app.main.constants :refer [max-input-length]]
[app.main.data.common :as dcm]
[app.main.data.fonts :as fts]
[app.main.data.shortcuts :as dsc]
[app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.undo :as dwu]
[app.main.features :as features]
[app.main.fonts :as fonts]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.editable-select :refer [editable-select]]
[app.main.ui.components.numeric-input :refer [numeric-input*]]
[app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]]
[app.main.ui.components.search-bar :refer [search-bar*]]
[app.main.ui.components.select :refer [select]]
[app.main.ui.context :as ctx]
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
[app.main.ui.ds.foundations.assets.icon :as i]
[app.main.ui.icons :as deprecated-icon]
[app.main.ui.workspace.sidebar.options.menus.text-shared :refer [text-options]]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as kbd]
[app.util.strings :as ust]
[app.util.timers :as tm]
[cuerdas.core :as str]
[goog.events :as events]
[rumext.v2 :as mf]))
(defn- attr->string [value]
(if (= value :multiple)
""
(ust/format-precision value 2)))
(defn- get-next-font
[{:keys [id] :as current} fonts]
(if (seq fonts)
(let [index (d/index-of-pred fonts #(= (:id %) id))
index (or index -1)
next (ex/ignoring (nth fonts (inc index)))]
(or next (first fonts)))
current))
(defn- get-prev-font
[{:keys [id] :as current} fonts]
(if (seq fonts)
(let [index (d/index-of-pred fonts #(= (:id %) id))
next (ex/ignoring (nth fonts (dec index)))]
(or next (peek fonts)))
current))
(mf/defc font-item*
{::mf/wrap [mf/memo]}
[{:keys [font is-current on-click style]}]
(let [item-ref (mf/use-ref)
on-click (mf/use-fn (mf/deps font) #(on-click font))]
(mf/use-effect
(mf/deps is-current)
(fn []
(when is-current
(let [element (mf/ref-val item-ref)]
(when-not (dom/is-in-viewport? element)
(dom/scroll-into-view! element))))))
[:div {:class (stl/css :font-wrapper)
:style style
:ref item-ref
:on-click on-click}
[:div {:class (stl/css-case :font-item true
:selected is-current)}
[:span {:class (stl/css :label)} (:name font)]
[:span {:class (stl/css :icon)} (when is-current deprecated-icon/tick)]]]))
(declare row-renderer)
(defn filter-fonts
[{:keys [term backends]} fonts]
(let [term (str/lower term)
xform (cond-> (map identity)
(seq term)
(comp (filter #(str/includes? (str/lower (:name %)) term)))
(seq backends)
(comp (filter #(contains? backends (:backend %)))))]
(into [] xform fonts)))
(mf/defc font-selector*
[{:keys [on-select on-close current-font show-recent full-size]}]
(let [selected (mf/use-state current-font)
state* (mf/use-state
#(do {:term "" :backends #{}}))
state (deref state*)
flist (mf/use-ref)
input (mf/use-ref)
fonts (mf/deref fonts/fonts)
fonts (mf/with-memo [state fonts]
(filter-fonts state fonts))
recent-fonts (mf/deref refs/recent-fonts)
recent-fonts (mf/with-memo [state recent-fonts]
(filter-fonts state recent-fonts))
full-size? (boolean (and full-size show-recent))
select-next
(mf/use-fn
(mf/deps fonts)
(fn [event]
(dom/stop-propagation event)
(dom/prevent-default event)
(swap! selected get-next-font fonts)))
select-prev
(mf/use-fn
(mf/deps fonts)
(fn [event]
(dom/stop-propagation event)
(dom/prevent-default event)
(swap! selected get-prev-font fonts)))
on-key-down
(mf/use-fn
(mf/deps fonts)
(fn [event]
(cond
(kbd/up-arrow? event) (select-prev event)
(kbd/down-arrow? event) (select-next event)
(kbd/esc? event) (on-close)
(kbd/enter? event) (on-close)
:else (dom/focus! (mf/ref-val input)))))
on-filter-change
(mf/use-fn
(fn [event]
(swap! state* assoc :term event)))
on-select-and-close
(mf/use-fn
(mf/deps on-select on-close)
(fn [font]
(on-select font)
(on-close)))]
(mf/with-effect [fonts]
(let [key (events/listen js/document "keydown" on-key-down)]
#(events/unlistenByKey key)))
(mf/with-effect [@selected]
(when-let [inst (mf/ref-val flist)]
(when-let [index (:index @selected)]
(.scrollToRow ^js inst index))))
(mf/with-effect [@selected]
(on-select @selected))
(mf/with-effect []
(st/emit! (dsc/push-shortcuts :typography {}))
(fn []
(st/emit! (dsc/pop-shortcuts :typography))))
(mf/with-effect []
(let [index (d/index-of-pred fonts #(= (:id %) (:id current-font)))
inst (mf/ref-val flist)]
(tm/schedule
#(let [offset (.getOffsetForRow ^js inst #js {:alignment "center" :index index})]
(.scrollToPosition ^js inst offset)))))
[:div {:class (stl/css :font-selector)}
[:div {:class (stl/css-case :font-selector-dropdown true :font-selector-dropdown-full-size full-size?)}
[:div {:class (stl/css :header)}
[:> search-bar* {:on-change on-filter-change
:value (:term state)
:auto-focus true
:placeholder (tr "workspace.options.search-font")}]
(when (and recent-fonts show-recent)
[:section {:class (stl/css :show-recent)}
[:p {:class (stl/css :title)} (tr "workspace.options.recent-fonts")]
(for [[idx font] (d/enumerate recent-fonts)]
[:> font-item* {:key (dm/str "font-" idx)
:font font
:style {}
:on-click on-select-and-close
:is-current (= (:id font) (:id @selected))}])])]
[:div {:class (stl/css-case :fonts-list true
:fonts-list-full-size full-size?)}
[:> rvt/AutoSizer {}
(fn [props]
(let [width (unchecked-get props "width")
height (unchecked-get props "height")
render #(row-renderer fonts @selected on-select-and-close %)]
(mf/html
[:> rvt/List #js {:height height
:ref flist
:width width
:rowCount (count fonts)
:rowHeight 36
:rowRenderer render}])))]]]]))
(defn row-renderer
[fonts selected on-select props]
(let [index (unchecked-get props "index")
key (unchecked-get props "key")
style (unchecked-get props "style")
font (nth fonts index)]
(mf/html
[:> font-item* {:key key
:font font
:style style
:on-click on-select
:is-current (= (:id font) (:id selected))}])))
(mf/defc font-options
{::mf/wrap-props false}
[{:keys [values on-change on-blur show-recent full-size-selector]}]
(let [{:keys [font-id font-size font-variant-id]} values
font-id (or font-id (:font-id txt/default-typography))
font-size (or font-size (:font-size txt/default-typography))
font-variant-id (or font-variant-id (:font-variant-id txt/default-typography))
fonts (mf/deref fonts/fontsdb)
font (get fonts font-id)
last-font (mf/use-ref nil)
open-selector? (mf/use-state false)
change-font
(mf/use-fn
(mf/deps on-change fonts)
(fn [new-font-id]
(let [{:keys [family] :as font} (get fonts new-font-id)
{:keys [id name weight style]} (fonts/get-default-variant font)]
(on-change {:font-id new-font-id
:font-family family
:font-variant-id (or id name)
:font-weight weight
:font-style style})
(mf/set-ref-val! last-font font))))
on-font-size-change
(mf/use-fn
(mf/deps on-change)
(fn [new-font-size]
(when-not (str/empty? new-font-size)
(on-change {:font-size (str new-font-size)}))))
on-font-variant-change
(mf/use-fn
(mf/deps font on-change)
(fn [new-variant-id]
(let [variant (d/seek #(= new-variant-id (:id %)) (:variants font))]
(when-not (nil? variant)
(on-change {:font-id (:id font)
:font-family (:family font)
:font-variant-id new-variant-id
:font-weight (:weight variant)
:font-style (:style variant)}))
;; NOTE: the select component we are using does not fire on-blur event
;; so we need to call on-blur manually
(when (some? on-blur)
(on-blur)))))
on-font-select
(mf/use-fn
(mf/deps change-font)
(fn [font*]
(when (not= font font*)
(change-font (:id font*)))
(when (some? on-blur)
(on-blur))))
on-font-selector-close
(mf/use-fn
(fn []
(reset! open-selector? false)
(when (some? on-blur)
(on-blur))
(when (mf/ref-val last-font)
(st/emit! (fts/add-recent-font (mf/ref-val last-font))))))]
[:*
(when @open-selector?
[:> font-selector*
{:current-font font
:on-close on-font-selector-close
:on-select on-font-select
:full-size full-size-selector
:show-recent show-recent}])
[:div {:class (stl/css :font-option)
:title (tr "inspect.attributes.typography.font-family")
:on-click #(reset! open-selector? true)}
(cond
(or (= :multiple font-id) (= "mixed" font-id))
"--"
(some? font)
[:*
[:span {:class (stl/css :name)}
(:name font)]
[:span {:class (stl/css :icon)}
deprecated-icon/arrow]]
:else
(tr "dashboard.fonts.deleted-placeholder"))]
[:div {:class (stl/css :font-modifiers)}
[:div {:class (stl/css :font-size-options)
:title (tr "inspect.attributes.typography.font-size")}
(let [size-options [8 9 10 11 12 14 16 18 24 36 48 72]
size-options (if (= font-size :multiple) (into [""] size-options) size-options)]
[:& editable-select
{:value (if (= font-size :multiple) :multiple (attr->string font-size))
:class (stl/css :font-size-select)
:aria-label (tr "inspect.attributes.typography.font-size")
:input-class (stl/css :numeric-input)
:options size-options
:type "number"
:placeholder (tr "settings.multiple")
:min 3
:max 1000
:on-change on-font-size-change
:on-blur on-blur}])]
[:div {:class (stl/css :font-variant-options)
:title (tr "inspect.attributes.typography.font-style")}
(let [basic-variant-options (->> (:variants font)
(map (fn [variant]
{:value (:id variant)
:key (pr-str variant)
:label (:name variant)})))
variant-options (if (or (= font-variant-id :multiple) (= font-variant-id "mixed"))
(conj basic-variant-options
{:value ""
:key :multiple-variants
:label "--"})
basic-variant-options)
font-variant-value (attr->string font-variant-id)
font-variant-value (if (= font-variant-value "mixed") "" font-variant-value)]
;; TODO Add disabled mode
[:& select
{:class (stl/css :font-variant-select)
:default-value font-variant-value
:options variant-options
:on-change on-font-variant-change
:on-blur on-blur}])]]]))
(mf/defc spacing-options
{::mf/wrap-props false}
[{:keys [values on-change on-blur]}]
(let [{:keys [line-height
letter-spacing]} values
line-height (or line-height "1.2")
letter-spacing (or letter-spacing "0")
handle-change
(fn [value attr]
(on-change {attr (str value)}))]
[:div {:class (stl/css :spacing-options)}
[:div {:class (stl/css :line-height)
:title (tr "inspect.attributes.typography.line-height")}
[:span {:class (stl/css :icon)
:alt (tr "workspace.options.text-options.line-height")}
deprecated-icon/text-lineheight]
[:> numeric-input*
{:min -200
:max 200
:step 0.1
:default-value "1.2"
:class (stl/css :line-height-input)
:aria-label (tr "inspect.attributes.typography.line-height")
:value (attr->string line-height)
:placeholder (if (= :multiple line-height) (tr "settings.multiple") "--")
:nillable (= :multiple line-height)
:on-change #(handle-change % :line-height)
:on-blur on-blur}]]
[:div {:class (stl/css :letter-spacing)
:title (tr "inspect.attributes.typography.letter-spacing")}
[:span
{:class (stl/css :icon)
:alt (tr "workspace.options.text-options.letter-spacing")}
deprecated-icon/text-letterspacing]
[:> numeric-input*
{:min -200
:max 200
:step 0.1
:default-value "0"
:class (stl/css :letter-spacing-input)
:aria-label (tr "inspect.attributes.typography.letter-spacing")
:value (attr->string letter-spacing)
:placeholder (if (= :multiple letter-spacing) (tr "settings.multiple") "--")
:on-change #(handle-change % :letter-spacing)
:nillable (= :multiple letter-spacing)
:on-blur on-blur}]]]))
(mf/defc text-transform-options
{::mf/wrap-props false}
[{:keys [values on-change on-blur]}]
(let [text-transform (or (:text-transform values) "none")
unset-value (if (features/active-feature? @st/state "text-editor/v2") "none" "unset")
handle-change
(fn [type]
(if (= text-transform type)
(on-change {:text-transform unset-value})
(on-change {:text-transform type}))
(when (some? on-blur) (on-blur)))]
[:div {:class (stl/css :text-transform)}
[:& radio-buttons {:selected text-transform
:on-change handle-change
:name "text-transform"}
[:& radio-button {:icon i/text-uppercase
:type "checkbox"
:title (tr "inspect.attributes.typography.text-transform.uppercase")
:value "uppercase"
:id "text-transform-uppercase"}]
[:& radio-button {:icon i/text-mixed
:type "checkbox"
:value "capitalize"
:title (tr "inspect.attributes.typography.text-transform.capitalize")
:id "text-transform-capitalize"}]
[:& radio-button {:icon i/text-lowercase
:type "checkbox"
:title (tr "inspect.attributes.typography.text-transform.lowercase")
:value "lowercase"
:id "text-transform-lowercase"}]]]))
(mf/defc text-options
{::mf/wrap-props false}
[{: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]
[:> text-transform-options opts]]]))
(mf/defc typography-advanced-options
{::mf/wrap [mf/memo]}
[{:keys [visible? typography editable? name-input-ref on-close on-change on-name-blur
@@ -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
@@ -10,6 +10,7 @@
[app.common.data.macros :as dm]
[app.common.types.shape.layout :as ctl]
[app.common.types.text :as txt]
[app.config :as cf]
[app.main.data.workspace.texts :as dwt]
[app.main.features :as features]
[app.main.refs :as refs]
@@ -26,7 +27,7 @@
[app.main.ui.workspace.sidebar.options.menus.measures :refer [measure-attrs measures-menu*]]
[app.main.ui.workspace.sidebar.options.menus.shadow :refer [shadow-menu*]]
[app.main.ui.workspace.sidebar.options.menus.stroke :refer [stroke-attrs stroke-menu]]
[app.main.ui.workspace.sidebar.options.menus.text :refer [text-menu*]]
[app.main.ui.workspace.sidebar.options.menus.text :refer [text-menu* token-text-menu*]]
[rumext.v2 :as mf]))
(mf/defc options*
@@ -35,6 +36,7 @@
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)
@@ -176,11 +178,18 @@
{:ids ids
:values (select-keys shape constraint-attrs)}])
[:> text-menu*
{:ids ids
:type type
:applied-tokens applied-tokens
:values text-values}]
(if token-row
[:> token-text-menu*
{:ids ids
:type type
:applied-tokens applied-tokens
:values text-values}]
[:> text-menu*
{:ids ids
:type type
:applied-tokens applied-tokens
:values text-values}])
[:> fill/fill-menu*
{:ids ids
@@ -19,7 +19,7 @@
[app.main.ui.ds.controls.input :refer [input*]]
[app.main.ui.ds.foundations.assets.icon :as i]
[app.main.ui.forms :as fc]
[app.main.ui.workspace.sidebar.options.menus.typography :refer [font-selector*]]
[app.main.ui.workspace.sidebar.options.menus.text-shared :refer [font-selector*]]
[app.util.dom :as dom]
[app.util.forms :as fm]
[app.util.i18n :refer [tr]]