From 5f474f9536fb388cdfca1814b2d85c1e0e18e436 Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Tue, 31 Mar 2026 13:48:49 +0200 Subject: [PATCH] :tada: Add typography token row (#8749) * :wrench: Create flag * :sparkles: Add typography type on tokens by input * :tada: Add typography token row * :recycle: Update sub-components to use new style * :tada: Add disabled option on radio-buttons* component * :tada: Add combobox search in a new component * :tada: Divide components * :bug: Fix placeholder --- common/src/app/common/flags.cljc | 2 + common/src/app/common/types/token.cljc | 3 +- .../main/ui/ds/controls/radio_buttons.cljs | 3 +- .../controls/shared/dropdown_navigation.cljs | 89 +++ .../main/ui/ds/controls/shared/option.scss | 5 + .../ds/controls/shared/options_dropdown.cljs | 77 +-- .../ds/controls/shared/options_dropdown.scss | 22 +- .../ui/ds/controls/shared/render_option.cljs | 67 ++ .../ui/ds/controls/shared/render_option.scss | 40 ++ .../shared/searchable_options_dropdown.cljs | 149 +++++ .../shared/searchable_options_dropdown.scss | 47 ++ .../ui/ds/controls/shared/token_option.cljs | 7 +- .../ui/ds/controls/shared/token_option.scss | 5 + .../ui/ds/controls/utilities/token_field.cljs | 2 +- .../workspace/sidebar/options/menus/text.cljs | 590 +++++++++++------- .../workspace/sidebar/options/menus/text.scss | 5 +- .../options/menus/token_typography_row.cljs | 87 +++ .../options/menus/token_typography_row.scss | 101 +++ .../sidebar/options/rows/color_row.cljs | 10 +- .../sidebar/options/shapes/group.cljs | 7 +- .../sidebar/options/shapes/multiple.cljs | 8 +- .../sidebar/options/shapes/text.cljs | 5 +- frontend/translations/de.po | 4 +- frontend/translations/en.po | 14 +- frontend/translations/es.po | 14 +- frontend/translations/fa.po | 2 +- frontend/translations/fr.po | 4 +- frontend/translations/fr_CA.po | 4 +- frontend/translations/he.po | 4 +- frontend/translations/hi.po | 4 +- frontend/translations/it.po | 4 +- frontend/translations/ko.po | 2 +- frontend/translations/lv.po | 4 +- frontend/translations/nl.po | 4 +- frontend/translations/pt_BR.po | 4 +- frontend/translations/ro.po | 4 +- frontend/translations/ru.po | 4 +- frontend/translations/sv.po | 4 +- frontend/translations/tr.po | 4 +- frontend/translations/zh_CN.po | 2 +- 40 files changed, 1059 insertions(+), 358 deletions(-) create mode 100644 frontend/src/app/main/ui/ds/controls/shared/dropdown_navigation.cljs create mode 100644 frontend/src/app/main/ui/ds/controls/shared/render_option.cljs create mode 100644 frontend/src/app/main/ui/ds/controls/shared/render_option.scss create mode 100644 frontend/src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs create mode 100644 frontend/src/app/main/ui/ds/controls/shared/searchable_options_dropdown.scss create mode 100644 frontend/src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs create mode 100644 frontend/src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.scss diff --git a/common/src/app/common/flags.cljc b/common/src/app/common/flags.cljc index 51832b5e27..aad100c0d9 100644 --- a/common/src/app/common/flags.cljc +++ b/common/src/app/common/flags.cljc @@ -128,6 +128,8 @@ :token-shadow :token-tokenscript :token-import-from-library + :token-typography-row + ;; Only for developtment. :transit-readable-response :user-feedback diff --git a/common/src/app/common/types/token.cljc b/common/src/app/common/types/token.cljc index 2d4b5b0395..c168bfc5a0 100644 --- a/common/src/app/common/types/token.cljc +++ b/common/src/app/common/types/token.cljc @@ -557,7 +557,8 @@ :font-size [:font-size] :letter-spacing [:letter-spacing] :fill [:color] - :stroke-color [:color]}) + :stroke-color [:color] + :typography [:typography]}) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; HELPERS for tokens application diff --git a/frontend/src/app/main/ui/ds/controls/radio_buttons.cljs b/frontend/src/app/main/ui/ds/controls/radio_buttons.cljs index 044fe87bc4..24e4b49453 100644 --- a/frontend/src/app/main/ui/ds/controls/radio_buttons.cljs +++ b/frontend/src/app/main/ui/ds/controls/radio_buttons.cljs @@ -52,6 +52,7 @@ handle-click (mf/use-fn + (mf/deps selected on-change allow-empty) (fn [event] (let [target (dom/get-target event) label (dom/get-parent-with-data target "label") @@ -114,4 +115,4 @@ :name name :disabled (or disabled wrapper-disabled) :value value - :checked checked?}]]))])) \ No newline at end of file + :checked checked?}]]))])) diff --git a/frontend/src/app/main/ui/ds/controls/shared/dropdown_navigation.cljs b/frontend/src/app/main/ui/ds/controls/shared/dropdown_navigation.cljs new file mode 100644 index 0000000000..3f9fc2fa8b --- /dev/null +++ b/frontend/src/app/main/ui/ds/controls/shared/dropdown_navigation.cljs @@ -0,0 +1,89 @@ +;; 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.ds.controls.shared.dropdown-navigation + (:require + [app.util.dom :as dom] + [app.util.keyboard :as kbd] + [app.util.object :as obj] + [rumext.v2 :as mf])) + +(defn use-dropdown-navigation + "Hook for keyboard navigation in dropdowns. + + Options: + - focusable-ids: vector of focusable ids (already filtered) + - nodes-ref: ref to a JS object mapping id -> DOM node + - on-enter: fn called with focused-id when Enter is pressed + - searchable: when true, nil focused-id means search input is focused + - search-input-ref: ref to the search input DOM node + - on-close: optional fn called when Esc/Tab is pressed" + + [{:keys [focusable-ids nodes-ref on-enter searchable search-input-ref on-close]}] + (let [focused-id* (mf/use-state nil) + focused-id (deref focused-id*) + + focus-input! + (mf/use-fn + (mf/deps search-input-ref) + (fn [] + (reset! focused-id* nil) + (when-let [input (mf/ref-val search-input-ref)] + (dom/focus! input)))) + + on-key-down + (mf/use-fn + (mf/deps focused-id focusable-ids searchable) + (fn [event] + (cond + (kbd/down-arrow? event) + (do + (dom/prevent-default event) + (dom/stop-propagation event) + (if (nil? focused-id) + (reset! focused-id* (first focusable-ids)) + (let [idx (or (first (keep-indexed #(when (= %2 focused-id) %1) focusable-ids)) -1) + next-idx (mod (inc idx) (count focusable-ids)) + wrap-to-input? (and ^boolean searchable + (= next-idx 0) + (= idx (dec (count focusable-ids))))] + (if wrap-to-input? + (focus-input!) + (reset! focused-id* (nth focusable-ids next-idx nil)))))) + + (kbd/up-arrow? event) + (do + (dom/prevent-default event) + (dom/stop-propagation event) + (if (nil? focused-id) + (reset! focused-id* (last focusable-ids)) + (let [idx (or (first (keep-indexed #(when (= %2 focused-id) %1) focusable-ids)) 0) + prev-idx (dec idx) + wrap-to-input? (and ^boolean searchable (= prev-idx -1))] + (if wrap-to-input? + (focus-input!) + (reset! focused-id* (nth focusable-ids (mod prev-idx (count focusable-ids)) nil)))))) + + (kbd/enter? event) + (when focused-id + (dom/prevent-default event) + (dom/stop-propagation event) + (on-enter focused-id)) + + (or (kbd/esc? event) (kbd/tab? event)) + (do + (dom/prevent-default event) + (dom/stop-propagation event) + (reset! focused-id* nil) + (when on-close (on-close))))))] + + (mf/with-effect [focused-id] + (when (some? focused-id) + (when-let [node (obj/get (mf/ref-val nodes-ref) focused-id)] + (dom/scroll-into-view-if-needed! node {:block "nearest" :inline "nearest"})))) + + {:focused-id focused-id + :focused-id* focused-id* + :on-key-down on-key-down})) \ No newline at end of file diff --git a/frontend/src/app/main/ui/ds/controls/shared/option.scss b/frontend/src/app/main/ui/ds/controls/shared/option.scss index ae38b73b01..bc693a14d4 100644 --- a/frontend/src/app/main/ui/ds/controls/shared/option.scss +++ b/frontend/src/app/main/ui/ds/controls/shared/option.scss @@ -63,3 +63,8 @@ --options-fg-color: var(--color-accent-primary); --options-icon-fg-color: var(--color-accent-primary); } + +.option-check { + color: var(--token-options-icon-fg-color); + min-width: var(--sp-l); +} diff --git a/frontend/src/app/main/ui/ds/controls/shared/options_dropdown.cljs b/frontend/src/app/main/ui/ds/controls/shared/options_dropdown.cljs index 60de5830a0..e3f7b778d7 100644 --- a/frontend/src/app/main/ui/ds/controls/shared/options_dropdown.cljs +++ b/frontend/src/app/main/ui/ds/controls/shared/options_dropdown.cljs @@ -9,10 +9,8 @@ [app.main.style :as stl]) (:require [app.common.data :as d] - [app.common.weak :refer [weak-key]] - [app.main.ui.ds.controls.shared.option :refer [option*]] - [app.main.ui.ds.controls.shared.token-option :refer [token-option*]] - [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i] + [app.main.ui.ds.controls.shared.render-option :refer [render-option]] + [app.main.ui.ds.foundations.assets.icon :as i] [cuerdas.core :as str] [rumext.v2 :as mf])) @@ -20,6 +18,14 @@ [:and :string [:fn {:error/message "invalid data: invalid icon"} #(contains? i/icon-list %)]]) +(def ^:private + xf:filter-blank-id + (filter #(str/blank? (get % :id)))) + +(def ^:private + xf:filter-non-blank-id + (remove #(str/blank? (get % :id)))) + (def schema:option "A schema for the option data structure expected to receive on props for the `options-dropdown*` component." @@ -33,67 +39,6 @@ [:label {:optional true} :string] [:aria-label {:optional true} :string]]) - - -(def ^:private - xf:filter-blank-id - (filter #(str/blank? (get % :id)))) - -(def ^:private - xf:filter-non-blank-id - (remove #(str/blank? (get % :id)))) - -(defn- render-option - [option ref on-click selected focused] - (let [id (get option :id) - name (get option :name) - type (get option :type)] - - (mf/html - (case type - :group - [:li {:class (stl/css :group-option) - :role "presentation" - :key (weak-key option)} - [:> icon* - {:icon-id i/arrow-down - :size "m" - :class (stl/css :option-check) - :aria-hidden (when name true)}] - (d/name name)] - - :separator - [:hr {:key (weak-key option) :class (stl/css :option-separator)}] - - :empty - [:li {:key (weak-key option) :class (stl/css :option-empty) :role "presentation"} - (get option :label)] - - ;; Token option - :token - [:> token-option* {:selected (= id selected) - :key (weak-key option) - :id id - :name name - :resolved (get option :resolved-value) - :ref ref - :role "option" - :focused (= id focused) - :on-click on-click}] - - ;; Normal option - [:> option* {:selected (= id selected) - :key (weak-key option) - :id id - :label (get option :label) - :aria-label (get option :aria-label) - :icon (get option :icon) - :ref ref - :role "option" - :focused (= id focused) - :dimmed (true? (:dimmed option)) - :on-click on-click}])))) - (def ^:private schema:options-dropdown [:map [:ref {:optional true} fn?] @@ -142,4 +87,4 @@ [:hr {:class (stl/css :option-separator)}]) (for [option options-blank] - (render-option option ref on-click selected focused))])])) + (render-option option ref on-click selected focused))])])) \ No newline at end of file diff --git a/frontend/src/app/main/ui/ds/controls/shared/options_dropdown.scss b/frontend/src/app/main/ui/ds/controls/shared/options_dropdown.scss index 4af9bb4793..0041dc1a9c 100644 --- a/frontend/src/app/main/ui/ds/controls/shared/options_dropdown.scss +++ b/frontend/src/app/main/ui/ds/controls/shared/options_dropdown.scss @@ -28,6 +28,10 @@ overflow: hidden auto; z-index: var(--z-index-dropdown); box-shadow: 0 0 $sz-12 0 var(--color-shadow-dark); + + &:focus { + outline: none; + } } .left-align { @@ -42,21 +46,3 @@ border: $b-1 solid var(--options-dropdown-border-color); margin-block: var(--sp-xs) var(--sp-xs); } - -.group-option, -.option-empty { - @include use-typography("body-small"); - - display: flex; - align-items: center; - gap: var(--sp-xs); - color: var(--color-foreground-secondary); - padding-inline: var(--sp-s); - block-size: var(--sp-xxxl); -} - -.option-empty { - justify-content: center; - text-align: center; - padding: 0 px2rem(40); -} diff --git a/frontend/src/app/main/ui/ds/controls/shared/render_option.cljs b/frontend/src/app/main/ui/ds/controls/shared/render_option.cljs new file mode 100644 index 0000000000..b3dec0b710 --- /dev/null +++ b/frontend/src/app/main/ui/ds/controls/shared/render_option.cljs @@ -0,0 +1,67 @@ +;; 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.ds.controls.shared.render-option + (:require-macros + [app.main.style :as stl]) + (:require + [app.common.data :as d] + [app.common.weak :refer [weak-key]] + [app.main.ui.ds.controls.shared.option :refer [option*]] + [app.main.ui.ds.controls.shared.token-option :refer [token-option*]] + [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i] + [rumext.v2 :as mf])) + +(defn render-option + [option ref on-click selected focused] + (let [id (get option :id) + name (get option :name) + type (get option :type)] + + (mf/html + (case type + :group + [:li {:class (stl/css :group-option) + :role "presentation" + :key (weak-key option)} + [:> icon* + {:icon-id i/arrow-down + :size "m" + :class (stl/css :option-check) + :aria-hidden (when name true)}] + (d/name name)] + + :separator + [:hr {:key (weak-key option) :class (stl/css :option-separator)}] + + :empty + [:li {:key (weak-key option) :class (stl/css :option-empty) :role "presentation"} + (get option :label)] + + ;; Token option + :token + [:> token-option* {:selected (= id selected) + :key (weak-key option) + :id id + :name name + :resolved (get option :resolved-value) + :ref ref + :role "option" + :focused (= id focused) + :on-click on-click}] + + ;; Normal option + [:> option* {:selected (= id selected) + :key (weak-key option) + :id id + :label (get option :label) + :aria-label (get option :aria-label) + :icon (get option :icon) + :ref ref + :role "option" + :focused (= id focused) + :dimmed (true? (:dimmed option)) + :on-click on-click}])))) \ No newline at end of file diff --git a/frontend/src/app/main/ui/ds/controls/shared/render_option.scss b/frontend/src/app/main/ui/ds/controls/shared/render_option.scss new file mode 100644 index 0000000000..232efb42a5 --- /dev/null +++ b/frontend/src/app/main/ui/ds/controls/shared/render_option.scss @@ -0,0 +1,40 @@ +// 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 "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/typography.scss" as *; +@use "ds/_utils.scss" as *; + +.left-align { + inset-inline-start: var(--dropdown-offset, 0); +} + +.right-align { + inset-inline-end: var(--dropdown-offset, 0); +} + +.option-separator { + border: $b-1 solid var(--options-dropdown-border-color); + margin-block: var(--sp-xs) var(--sp-xs); +} + +.group-option, +.option-empty { + @include use-typography("body-small"); + + display: flex; + align-items: center; + gap: var(--sp-xs); + color: var(--color-foreground-secondary); + padding-inline: var(--sp-s); + block-size: var(--sp-xxxl); +} + +.option-check { + color: var(--token-options-icon-fg-color); + min-width: var(--sp-l); +} diff --git a/frontend/src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs b/frontend/src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs new file mode 100644 index 0000000000..6e5d8e7d51 --- /dev/null +++ b/frontend/src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs @@ -0,0 +1,149 @@ +;; 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.ds.controls.shared.searchable-options-dropdown + (:require-macros + [app.main.style :as stl]) + (:require + [app.common.data :as d] + [app.main.ui.ds.controls.input :as ds] + [app.main.ui.ds.controls.shared.dropdown-navigation :refer [use-dropdown-navigation]] + [app.main.ui.ds.controls.shared.render-option :refer [render-option]] + [app.main.ui.ds.foundations.assets.icon :as i] + [app.main.ui.workspace.tokens.management.forms.controls.utils :as csu] + [app.util.dom :as dom] + [app.util.i18n :as i18n :refer [tr]] + [app.util.object :as obj] + [app.util.timers :as ts] + [cuerdas.core :as str] + [rumext.v2 :as mf])) + +(def ^:private schema:icon-list + [:and :string + [:fn {:error/message "invalid data: invalid icon"} #(contains? i/icon-list %)]]) + +(def schema:option + "A schema for the option data structure expected to receive on props + for the `options-dropdown*` component." + [:map + [:id {:optional true} :string] + [:resolved-value {:optional true} + [:or :int :string :float :map]] + [:name {:optional true} :string] + [:value {:optional true} :keyword] + [:icon {:optional true} schema:icon-list] + [:label {:optional true} :string] + [:aria-label {:optional true} :string]]) + +(def ^:private schema:options-dropdown + [:map + [:ref {:optional true} fn?] + [:class {:optional true} :string] + [:wrapper-ref {:optional true} :any] + [:placeholder {:optional true} :string] + [:on-click fn?] + [:options [:vector schema:option]] + [:selected {:optional true} :any] + [:align {:optional true} [:maybe [:enum :left :right]]]]) + +(mf/defc searchable-options-dropdown* + {::mf/schema schema:options-dropdown} + [{:keys [on-click options selected align class placeholder] :rest props}] + (let [align (d/nilv align :left) + + search* (mf/use-state "") + search (deref search*) + search-input-ref (mf/use-ref nil) + + list-ref (mf/use-ref nil) + nodes-ref (mf/use-ref nil) + + filtered-options + (mf/with-memo [options search] + (if (seq search) + (filterv (fn [opt] + (or (not= :token (:type opt)) + (str/includes? (str/lower (:name opt "")) + (str/lower search)))) + options) + options)) + + focusable-ids + (mf/with-memo [filtered-options] + (mapv :id (csu/focusable-options filtered-options))) + + on-search-change + (mf/use-fn + (fn [event] + (reset! search* (dom/get-target-val event)))) + + set-option-ref + (mf/use-fn + (fn [node] + (when node + (let [state (d/nilv (mf/ref-val nodes-ref) #js {}) + id (dom/get-data node "id")] + (mf/set-ref-val! nodes-ref (obj/set! state id node)) + (fn [] + (let [state (d/nilv (mf/ref-val nodes-ref) #js {})] + (mf/set-ref-val! nodes-ref (obj/unset! state id)))))))) + + {:keys [focused-id focused-id* on-key-down]} + (use-dropdown-navigation + {:focusable-ids focusable-ids + :nodes-ref nodes-ref + :on-enter (fn [id] + (when-let [node (obj/get (mf/ref-val nodes-ref) id)] + (.click node))) + :searchable true + :search-input-ref search-input-ref + :on-close nil}) + + on-click-inner + (mf/use-fn + (mf/deps on-click) + (fn [event] + (dom/stop-propagation event) + (on-click event))) + + list-props + (mf/spread-props props + {:class [class (stl/css-case :option-list true + :left-align (= align :left) + :right-align (= align :right))] + :ref list-ref + :tab-index "-1" + :role "listbox" + :on-key-down on-key-down})] + + (mf/with-effect [] + (ts/schedule 0 + #(if (mf/ref-val search-input-ref) + (dom/focus! (mf/ref-val search-input-ref)) + (when-let [list (mf/ref-val list-ref)] + (dom/focus! list))))) + + (mf/with-effect [focused-id] + (when (some? focused-id) + (when-let [list (mf/ref-val list-ref)] + (when-not (dom/active? list) + (dom/focus! list))) + (when-let [node (obj/get (mf/ref-val nodes-ref) focused-id)] + (dom/scroll-into-view-if-needed! node {:block "nearest" :inline "nearest"})))) + + [:> :ul list-props + [:li {:class (stl/css :option-search) + :role "presentation"} + [:> ds/input* {:placeholder (or placeholder (tr "dashboard.search-placeholder")) + :value search + :ref search-input-ref + :variant "comfortable" + :on-change on-search-change + :on-click #(reset! focused-id* nil) + :on-key-down on-key-down}]] + + (for [option filtered-options] + (render-option option set-option-ref on-click-inner selected focused-id))])) diff --git a/frontend/src/app/main/ui/ds/controls/shared/searchable_options_dropdown.scss b/frontend/src/app/main/ui/ds/controls/shared/searchable_options_dropdown.scss new file mode 100644 index 0000000000..cbeae912d8 --- /dev/null +++ b/frontend/src/app/main/ui/ds/controls/shared/searchable_options_dropdown.scss @@ -0,0 +1,47 @@ +// 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 "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/typography.scss" as *; +@use "ds/_utils.scss" as *; + +.option-list { + --options-dropdown-icon-fg-color: var(--color-foreground-secondary); + --options-dropdown-bg-color: var(--color-background-tertiary); + --options-dropdown-outline-color: none; + --options-dropdown-border-color: var(--color-background-quaternary); + + position: absolute; + inset-block-start: $sz-36; + inline-size: var(--dropdown-width, 100%); + transform: translateX(var(--dropdown-translate-distance, 0)); + background-color: var(--options-dropdown-bg-color); + border-radius: $br-8; + border: $b-1 solid var(--options-dropdown-border-color); + padding-block: var(--sp-xs); + margin-block-end: 0; + max-block-size: $sz-400; + overflow: hidden auto; + z-index: var(--z-index-dropdown); + box-shadow: 0 0 $sz-12 0 var(--color-shadow-dark); + + &:focus { + outline: none; + } +} + +.left-align { + inset-inline-start: var(--dropdown-offset, 0); +} + +.right-align { + inset-inline-end: var(--dropdown-offset, 0); +} + +.option-search { + padding: var(--sp-xs); +} diff --git a/frontend/src/app/main/ui/ds/controls/shared/token_option.cljs b/frontend/src/app/main/ui/ds/controls/shared/token_option.cljs index 11667ba8f8..45189a3156 100644 --- a/frontend/src/app/main/ui/ds/controls/shared/token_option.cljs +++ b/frontend/src/app/main/ui/ds/controls/shared/token_option.cljs @@ -18,7 +18,7 @@ [:map [:id {:optiona true} :string] [:ref some?] - [:resolved {:optional true} [:or :int :string :float]] + [:resolved {:optional true} [:or :int :string :float :map]] [:name {:optional true} :string] [:on-click {:optional true} fn?] [:selected {:optional true} :boolean] @@ -55,10 +55,11 @@ :trigger-ref element-ref :id (dm/str id "-name") :class (stl/css :option-text)} - ;; Add ellipsis + [:span {:aria-labelledby (dm/str id "-name") + :class (stl/css :option-name) :ref element-ref} name]] - (when resolved + (when (and resolved (not (map? resolved))) [:> :span {:class (stl/css :option-pill)} resolved])])) diff --git a/frontend/src/app/main/ui/ds/controls/shared/token_option.scss b/frontend/src/app/main/ui/ds/controls/shared/token_option.scss index b05b6e8e66..14f4c6b3a8 100644 --- a/frontend/src/app/main/ui/ds/controls/shared/token_option.scss +++ b/frontend/src/app/main/ui/ds/controls/shared/token_option.scss @@ -7,6 +7,7 @@ @use "ds/_borders.scss" as *; @use "ds/_sizes.scss" as *; @use "ds/typography.scss" as *; +@use "ds/mixins.scss" as *; .token-option { --token-options-fg-color: var(--color-foreground-primary); @@ -78,3 +79,7 @@ color: var(--token-options-icon-fg-color); min-width: var(--sp-l); } + +.option-name { + @include text-ellipsis; +} diff --git a/frontend/src/app/main/ui/ds/controls/utilities/token_field.cljs b/frontend/src/app/main/ui/ds/controls/utilities/token_field.cljs index 8b67fa82f8..04c5900961 100644 --- a/frontend/src/app/main/ui/ds/controls/utilities/token_field.cljs +++ b/frontend/src/app/main/ui/ds/controls/utilities/token_field.cljs @@ -97,5 +97,5 @@ :tooltip-placement tooltip-placement :icon i/broken-link :ref token-detach-btn-ref - :aria-label (tr "ds.inputs.token-field.detach-token") + :aria-label (tr "token-actions.detach-token") :on-click detach-token}])]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs index 75c0344662..ca77bdd320 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs @@ -10,26 +10,30 @@ [app.common.data :as d] [app.common.types.text :as txt] [app.common.uuid :as uuid] + [app.config :as cf] [app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.shapes :as dwsh] [app.main.data.workspace.shortcuts :as sc] [app.main.data.workspace.texts :as dwt] + [app.main.data.workspace.tokens.application :as dwta] [app.main.data.workspace.undo :as dwu] [app.main.data.workspace.wasm-text :as dwwt] [app.main.features :as features] [app.main.refs :as refs] [app.main.store :as st] - [app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]] [app.main.ui.components.title-bar :refer [title-bar*]] [app.main.ui.context :as ctx] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [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.hooks :as hooks] - [app.main.ui.icons :as deprecated-icon] - [app.main.ui.workspace.sidebar.options.menus.typography :refer [text-options - typography-entry]] + [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.tokens.management.forms.controls.utils :as csu] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] + [app.util.object :as obj] [app.util.text.content :as content] [app.util.text.ui :as txu] [app.util.timers :as ts] @@ -37,70 +41,68 @@ [potok.v2.core :as ptk] [rumext.v2 :as mf])) -(mf/defc text-align-options - [{:keys [values on-change on-blur] :as props}] - (let [{:keys [text-align]} values - handle-change +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Sub-components +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(mf/defc text-align-options* + [{:keys [values on-change on-blur]}] + (let [handle-change (mf/use-fn (mf/deps on-change on-blur) (fn [value] (on-change {:text-align value}) (when (some? on-blur) (on-blur))))] - ;; --- Align [:div {:class (stl/css :align-options)} - [:& radio-buttons {:selected text-align - :on-change handle-change - :name "align-text-options"} - [:& radio-button {:value "left" - :id "text-align-left" - :title (tr "workspace.options.text-options.text-align-left") - :icon i/text-align-left}] - [:& radio-button {:value "center" - :id "text-align-center" - :title (tr "workspace.options.text-options.text-align-center") - :icon i/text-align-center}] - [:& radio-button {:value "right" - :id "text-align-right" - :title (tr "workspace.options.text-options.text-align-right") - :icon i/text-align-right}] - [:& radio-button {:value "justify" - :id "text-align-justify" - :title (tr "workspace.options.text-options.text-align-justify") - :icon i/text-justify}]]])) + [:> radio-buttons* {:selected (:text-align values) + :on-change handle-change + :name "align-text-options" + :options [{:value "left" + :id "text-align-left" + :label (tr "workspace.options.text-options.text-align-left") + :icon i/text-align-left} + {:value "center" + :id "text-align-center" + :label (tr "workspace.options.text-options.text-align-center") + :icon i/text-align-center} + {:value "right" + :id "text-align-right" + :label (tr "workspace.options.text-options.text-align-right") + :icon i/text-align-right} + {:value "justify" + :id "text-align-justify" + :label (tr "workspace.options.text-options.text-align-justify") + :icon i/text-justify}]}]])) + +(mf/defc text-direction-options* + [{:keys [values on-change on-blur]}] + (let [direction (:text-direction values) -(mf/defc text-direction-options - [{:keys [values on-change on-blur] :as props}] - (let [direction (:text-direction values) handle-change (mf/use-fn (mf/deps on-change on-blur direction) (fn [value] - (let [dir (if (= value direction) - "none" - value)] - (on-change {:text-direction dir}) - (when (some? on-blur) (on-blur)))))] + (on-change {:text-direction (if (= value direction) "none" value)}) + (when (some? on-blur) (on-blur))))] [:div {:class (stl/css :text-direction-options)} - [:& radio-buttons {:selected direction - :on-change handle-change - :name "text-direction-options"} - [:& radio-button {:value "ltr" - :type "checkbox" - :id "ltr-text-direction" - :title (tr "workspace.options.text-options.direction-ltr") - :icon i/text-ltr}] - [:& radio-button {:value "rtl" - :type "checkbox" - :id "rtl-text-direction" - :title (tr "workspace.options.text-options.direction-rtl") - :icon i/text-rtl}]]])) + [:> radio-buttons* {:selected direction + :on-change handle-change + :name "text-direction-options" + :options [{:value "ltr" + :id "ltr-text-direction" + :label (tr "workspace.options.text-options.direction-ltr") + :icon i/text-ltr} + {:value "rtl" + :id "rtl-text-direction" + :label (tr "workspace.options.text-options.direction-rtl") + :icon i/text-rtl}]}]])) + +(mf/defc vertical-align* + [{:keys [values on-change on-blur]}] + (let [vertical-align (or (:vertical-align values) "top") -(mf/defc vertical-align - [{:keys [values on-change on-blur] :as props}] - (let [{:keys [vertical-align]} values - vertical-align (or vertical-align "top") handle-change (mf/use-fn (mf/deps on-change on-blur) @@ -109,123 +111,223 @@ (when (some? on-blur) (on-blur))))] [:div {:class (stl/css :vertical-align-options)} - [:& radio-buttons {:selected vertical-align - :on-change handle-change - :name "vertical-align-text-options"} - [:& radio-button {:value "top" - :id "vertical-text-align-top" - :title (tr "workspace.options.text-options.align-top") - :icon i/text-top}] - [:& radio-button {:value "center" - :id "vertical-text-align-center" - :title (tr "workspace.options.text-options.align-middle") - :icon i/text-middle}] - [:& radio-button {:value "bottom" - :id "vertical-text-align-bottom" - :title (tr "workspace.options.text-options.align-bottom") - :icon i/text-bottom}]]])) - -(mf/defc grow-options - [{:keys [ids values on-blur] :as props}] - (let [grow-type (:grow-type values) + [:> radio-buttons* {:selected vertical-align + :on-change handle-change + :name "vertical-align-text-options" + :options [{:value "top" + :id "vertical-text-align-top" + :label (tr "workspace.options.text-options.align-top") + :icon i/text-top} + {:value "center" + :id "vertical-text-align-center" + :label (tr "workspace.options.text-options.align-middle") + :icon i/text-middle} + {:value "bottom" + :id "vertical-text-align-bottom" + :label (tr "workspace.options.text-options.align-bottom") + :icon i/text-bottom}]}]])) +(mf/defc grow-options* + [{:keys [ids values on-blur on-change]}] + (let [grow-type (:grow-type values) editor-instance (mf/deref refs/workspace-editor) - handle-change-grow - (mf/use-fn - (mf/deps ids on-blur editor-instance) - (fn [value] - (on-blur) - (let [uid (js/Symbol) - grow-type (keyword value)] - (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))) - ;; We asynchronously commit so every sychronous event is resolved first and inside the transaction - (ts/schedule #(st/emit! (dwu/commit-undo-transaction uid)))) - (when (some? on-blur) (on-blur))))] - - [:div {:class (stl/css :grow-options)} - [:& radio-buttons {:selected (d/name grow-type) - :on-change handle-change-grow - :name "grow-text-options"} - [:& radio-button {:value "fixed" - :id "text-fixed-grow" - :title (tr "workspace.options.text-options.grow-fixed") - :icon i/text-fixed}] - [:& radio-button {:value "auto-width" - :id "text-auto-width-grow" - :title (tr "workspace.options.text-options.grow-auto-width") - :icon i/text-auto-width}] - [:& radio-button {:value "auto-height" - :id "text-auto-height-grow" - :title (tr "workspace.options.text-options.grow-auto-height") - :icon i/text-auto-height}]]])) - -(mf/defc text-decoration-options - [{:keys [values on-change on-blur] :as props}] - (let [text-decoration (or (:text-decoration values) "none") handle-change (mf/use-fn - (mf/deps on-change on-blur text-decoration) + (mf/deps ids on-blur on-change editor-instance) (fn [value] - (let [decoration (if (= value text-decoration) - "none" - value)] - (on-change {:text-decoration decoration}) - (when (some? on-blur) (on-blur)))))] + (on-change {:grow-type (keyword value)}) + (when (some? on-blur) + (on-blur))))] + + [:div {:class (stl/css :grow-options)} + [:> radio-buttons* {:selected (d/name grow-type) + :on-change handle-change + :name "grow-text-options" + :options [{:value "fixed" + :id "text-fixed-grow" + :label (tr "workspace.options.text-options.grow-fixed") + :icon i/text-fixed} + {:value "auto-width" + :id "text-auto-width-grow" + :label (tr "workspace.options.text-options.grow-auto-width") + :icon i/text-auto-width} + {:value "auto-height" + :id "text-auto-height-grow" + :label (tr "workspace.options.text-options.grow-auto-height") + :icon i/text-auto-height}]}]])) + +(mf/defc 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"} - [:& radio-button {:value "underline" - :type "checkbox" - :id "underline-text-decoration" - :title (tr "workspace.options.text-options.underline" (sc/get-tooltip :underline)) - :icon i/text-underlined}] - [:& radio-button {:value "line-through" - :type "checkbox" - :id "line-through-text-decoration" - :title (tr "workspace.options.text-options.strikethrough" (sc/get-tooltip :line-through)) - :icon i/text-stroked}]]])) + [:> radio-buttons* {:selected (if (= text-decoration "none") + nil + text-decoration) + :on-change handle-change + :name "text-decoration-options" + :disabled (and token-row (some? token-applied)) + :allow-empty true + :options [{: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-menu - {::mf/wrap [mf/memo]} - [{:keys [ids type values] :as props}] +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Helpers +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - (let [file-id (mf/use-ctx ctx/current-file-id) - typographies (mf/deref refs/workspace-file-typography) - libraries (mf/deref refs/files) +(defn- get-option-by-name [options name] + (let [options (if (delay? options) (deref options) options)] + (d/seek #(= name (get % :name)) options))) + +(defn- resolve-delay [tokens] + (if (delay? tokens) @tokens tokens)) + +(defn- find-token-by-id [tokens id] + (->> (:typography tokens) + (d/seek #(= (:id %) (uuid/uuid id))))) + +(defn- check-props [n-props o-props] + (and (identical? (unchecked-get n-props "ids") + (unchecked-get o-props "ids")) + (identical? (unchecked-get n-props "appliedTokens") + (unchecked-get o-props "appliedTokens")) + (identical? (unchecked-get n-props "values") + (unchecked-get o-props "values")))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Main component +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(mf/defc 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) + 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 + listbox-id (mf/use-id) + nodes-ref (mf/use-ref nil) + 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)))) + selected-token-id (deref selected-token-id*) + + ;; --- 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))) + + apply-token! + (mf/use-fn + (mf/deps ids typography-tokens) + (fn [id] + (let [token (find-token-by-id (resolve-delay typography-tokens) id)] + (reset! selected-token-id* id) + (reset! token-dropdown-open* false) + (st/emit! + (dwta/apply-token {:shape-ids ids + :attributes #{:typography} + :token token + :on-update-shape dwta/update-typography}))))) 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")) + set-option-ref + (mf/use-fn + (fn [node] + (let [state (d/nilv (mf/ref-val nodes-ref) #js {}) + id (dom/get-data node "id")] + (mf/set-ref-val! nodes-ref (obj/set! state id node)) + (fn [] + (let [state (d/nilv (mf/ref-val nodes-ref) #js {})] + (mf/set-ref-val! nodes-ref (obj/unset! state id))))))) - state* (mf/use-state {:main-menu true - :more-options false}) - state (deref state*) - main-menu-open? (:main-menu state) - more-options-open? (:more-options state) - + ;; --- Toggles toggle-main-menu (mf/use-fn (mf/deps main-menu-open?) - #(swap! state* assoc-in [:main-menu] (not main-menu-open?))) + #(swap! menu-state* update :main-menu not)) toggle-more-options (mf/use-fn (mf/deps more-options-open?) - #(swap! state* assoc-in [:more-options] (not more-options-open?))) + #(swap! menu-state* update :more-options not)) - typography-id (:typography-ref-id values) - typography-file-id (:typography-ref-file values) + toggle-token-dropdown + (mf/use-fn + #(swap! token-dropdown-open* not)) + + ;; --- Event handlers + on-option-click + (mf/use-fn + (mf/deps apply-token!) + (fn [event] + (dom/stop-propagation event) + (let [id (dom/get-data (dom/get-current-target event) "id")] + (apply-token! id)))) emit-update! (mf/use-fn @@ -241,42 +343,40 @@ (fn [attrs] (emit-update! ids attrs))) - typography - (mf/with-memo [values file-id libraries] - (cond - (and typography-id - (not= typography-id :multiple) - (not= typography-file-id file-id)) - (-> libraries - (get-in [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))) - on-convert-to-typography - (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) - typography (dwt/generate-typography-name typography) - 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}))) + (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))) + (ts/schedule #(st/emit! (dwu/commit-undo-transaction uid)))))) handle-detach-typography (mf/use-fn (mf/deps on-change) - (fn [] - (on-change {:typography-ref-file nil - :typography-ref-id nil}))) + #(on-change {:typography-ref-file nil :typography-ref-id nil})) handle-change-typography (mf/use-fn @@ -284,74 +384,124 @@ (fn [changes] (st/emit! (dwl/update-typography (merge typography changes) file-id)))) + detach-token + (mf/use-fn + (fn [token-name] + (st/emit! (dwta/unapply-token {:token-name token-name + :attributes #{:typography} + :shape-ids ids})))) + expand-stream (mf/with-memo [] - (->> st/stream - (rx/filter (ptk/type? :expand-text-more-options)))) + (->> st/stream (rx/filter (ptk/type? :expand-text-more-options)))) - multiple? (->> values vals (d/seek #(= % :multiple))) + 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})] - opts #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))) - (let [node (txu/get-text-editor-content)] - (dom/focus! node))))))}] (hooks/use-stream expand-stream - #(swap! state* assoc-in [:more-options] true)) + #(swap! menu-state* assoc :more-options true)) - [:div {:class (stl/css :element-set)} + (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?)) - [:> icon-button* {:variant "ghost" - :aria-label (tr "labels.options") - :on-click on-convert-to-typography - :icon i/add}])]] + [:* + (when (and token-row (some? typography-tokens) (not typography)) + [:> icon-button* {:variant "ghost" + :aria-label (tr "ds.inputs.numeric-input.open-token-list-dropdown") + :on-click toggle-token-dropdown + :tooltip-placement "top-left" + :icon i/tokens}]) + (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 + (and token-row current-token-name) + [:> token-typography-row* {:token-name current-token-name + :detach-token detach-token + :active-tokens (resolve-delay typography-tokens)}] + typography - [:& typography-entry {:file-id typography-file-id + [:& 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}] + :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")] - [:div {:class (stl/css :multiple-typography-button) - :on-click handle-detach-typography - :title (tr "workspace.libraries.text.multiple-typography-tooltip")} - deprecated-icon/detach]] + [:> icon-button* {:variant "ghost" + :aria-label (tr "workspace.libraries.text.multiple-typography-tooltip") + :on-click handle-detach-typography + :icon i/detach}]] :else - [:> text-options opts]) + [:> 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 opts] - [:> grow-options opts] - [:> icon-button* {:variant "ghost" - :aria-label (tr "labels.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}]] + :on-click toggle-more-options + :icon i/menu}]] (when more-options-open? - [:div {:class (stl/css :text-decoration-options)} - [:> vertical-align opts] - [:> text-decoration-options opts] - [:> text-direction-options opts]])])])) + [: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]])]) + + (when (and token-row token-dropdown-open?) + [:> searchable-options-dropdown* {:on-click on-option-click + :id listbox-id + :options (resolve-delay dropdown-options) + :selected selected-token-id + :align "right" + :ref set-option-ref}])])) \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.scss index 44e721d5b8..8805b83543 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.scss @@ -9,6 +9,8 @@ .element-set { @include sidebar.option-grid-structure; + + position: relative; } .element-title { @@ -16,10 +18,9 @@ } .element-content { - grid-column: span 8; - @include deprecated.flexColumn; + grid-column: span 8; margin-top: deprecated.$s-4; } diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs new file mode 100644 index 0000000000..9d9944a1d2 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs @@ -0,0 +1,87 @@ +;; 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.token-typography-row + (:require-macros [app.main.style :as stl]) + (:require + [app.common.data :as d] + [app.common.data.macros :as dm] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i] + [app.main.ui.ds.tooltip :refer [tooltip*]] + [app.util.i18n :as i18n :refer [tr]] + [cuerdas.core :as str] + [rumext.v2 :as mf])) + +(mf/defc resolved-value-tooltip* + {::mf/private true} + [{:keys [token-name resolved-value]}] + [:* + [:span (dm/str (tr "workspace.tokens.token-name") ": ")] + [:span {:class (stl/css :token-name-tooltip)} token-name] + [:div + [:span (tr "inspect.tabs.styles.token-resolved-value")] + [:ul + (for [[k v] resolved-value] + [:li {:key (d/name k)} + [:span {:class (stl/css :resolved-key)} (str "- " (d/name k) ": ")] + [:span {:class (stl/css :resolved-value)} + (if (sequential? v) + (str/join ", " (map #(dm/str "\"" % "\"") v)) + (dm/str v))]])]]]) + +(mf/defc token-typography-row* + [{:keys [token-name active-tokens detach-token] :rest props}] + (let [element-ref (mf/use-ref nil) + id (mf/use-id) + + token (->> (:typography active-tokens) + (d/seek #(= (:name %) token-name))) + + has-errors (some? (:errors token)) + display-name (or (:name token) token-name) + + resolved-value (:resolved-value token) + not-active (or (nil? token) + (empty? (:typography active-tokens))) + on-detach + (mf/use-fn + (mf/deps display-name) + (fn [] + (detach-token display-name))) + + tooltip-content (cond + not-active + (tr "not-active-token.no-name") + has-errors + (tr "options.deleted-token") + :else + (mf/html [:> resolved-value-tooltip* {:token-name token-name + :resolved-value resolved-value}]))] + + [:div {:class (stl/css-case :token-typography-row true + :token-typography-row-with-errors has-errors + :token-typography-row-not-active not-active)} + (when (or has-errors not-active) + [:div {:class (stl/css :error-dot)}]) + [:> icon* {:icon-id i/text-typography + :class (stl/css :icon)}] + [:> tooltip* {:content tooltip-content + :trigger-ref element-ref + :class (stl/css :token-tooltip) + :id id} + + [:span {:aria-labelledby (dm/str id) + :class (stl/css :token-name) + :ref element-ref} + display-name]] + + [:> icon-button* {:variant "action" + :aria-label (tr "token-actions.detach-token") + :tooltip-class (stl/css :detach-button) + :tooltip-placement "top-left" + :on-click on-detach + :icon i/detach}]])) \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.scss new file mode 100644 index 0000000000..ac89984a55 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.scss @@ -0,0 +1,101 @@ +// 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 "ds/typography.scss" as t; +@use "ds/_sizes.scss" as *; +@use "ds/_borders.scss" as *; +@use "ds/mixins.scss" as *; +@use "ds/_utils.scss" as *; + +.token-typography-row { + --token-typography-row-background-color: var(--color-background-tertiary); + --token-typography-row-foreground-color: var(--color-token-foreground); + --token-typography-row-border-color: var(--color-token-border); + + display: flex; + align-items: center; + position: relative; + gap: var(--sp-xs); + block-size: $sz-32; + min-inline-size: 0; + inline-size: 100%; + padding: var(--sp-s); + margin-inline-end: 0; + background: var(--token-typography-row-background-color); + border: $b-1 solid var(--token-typography-row-border-color); + border-radius: $br-8; + + &:hover { + --token-typography-row-background-color: var(--color-token-background); + --token-typography-row-foreground-color: var(--color-foreground-primary); + --token-typography-row-border-color: var(--color-token-accent); + } +} + +.token-typography-row-with-errors, +.token-typography-row-not-active { + --token-typography-row-background-color: var(--color-background-primary); + --token-typography-row-foreground-color: var(--color-foreground-secondary); + --token-typography-row-border-color: var(--color-token-border); + + &:hover { + --token-typography-row-background-color: var(--color-background-primary); + --token-typography-row-foreground-color: var(--color-foreground-secondary); + --token-typography-row-border-color: var(--color-token-background); + } +} + +.icon { + display: block; + min-inline-size: $sz-16; + color: var(--token-typography-row-foreground-color); +} + +.token-name { + @include t.use-typography("body-small"); + @include text-ellipsis; + + color: var(--token-typography-row-foreground-color); + block-size: $sz-32; + flex: 1; + line-height: $sz-32; +} + +.token-tooltip { + min-inline-size: 0; + inline-size: inherit; +} + +.token-name-tooltip { + color: var(--color-foreground-primary); +} + +.detach-button { + flex-shrink: 0; + inline-size: 0; + max-inline-size: 0; + overflow: hidden; + opacity: 0; + pointer-events: none; +} + +.token-typography-row:hover .detach-button { + inline-size: auto; + opacity: 1; + pointer-events: auto; + max-inline-size: $sz-32; +} + +.error-dot { + inline-size: px2rem(4); + block-size: px2rem(4); + border-radius: 50%; + background-color: var(--color-foreground-error); + margin-inline-start: var(--sp-xs); + position: absolute; + inset-inline-end: px2rem(1); + inset-block-start: px2rem(5); +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs index 281ed7e888..31e98bf832 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs @@ -97,16 +97,16 @@ token-name-ref (mf/use-ref nil) swatch-tooltip-content (cond not-active - (tr "ds.inputs.token-field.no-active-color.token-option") + (tr "not-active-token.no-name") has-errors - (tr "color-row.token-color-row.deleted-token") + (tr "options.deleted-token") :else (tr "workspace.tokens.resolved-value" resolved)) name-tooltip-content (cond not-active - (tr "ds.inputs.token-field.no-active-color.token-option") + (tr "not-active-token.no-name") has-errors - (tr "color-row.token-color-row.deleted-token") + (tr "options.deleted-token") :else #(mf/html [:div @@ -137,7 +137,7 @@ [:div {:class (stl/css :token-actions)} [:> icon-button* {:variant "action" - :aria-label (tr "ds.inputs.token-field.detach-token") + :aria-label (tr "token-actions.detach-token") :on-click on-detach-token :icon i/detach}] [:> icon-button* diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/group.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/group.cljs index eaab775a8e..f99364cbc9 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/group.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/group.cljs @@ -102,7 +102,7 @@ [stroke-ids stroke-values stroke-tokens] (get-attrs shapes objects :stroke) - [text-ids text-values] + [text-ids text-values text-tokens] (get-attrs shapes objects :text) [layout-item-ids layout-item-values] @@ -171,7 +171,10 @@ [:& 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}]) + [:> ot/text-menu* {:type type + :ids text-ids + :values text-values + :applied-tokens text-tokens}]) (when-not (empty? svg-values) [:& svg-attrs-menu {:ids ids :values svg-values}]) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs index f3c6ee2b1e..caff7d7454 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs @@ -385,7 +385,7 @@ [layer-ids layer-values layer-tokens] (get-attrs shapes objects :layer) - [text-ids text-values] + [text-ids text-values text-tokens] (get-attrs shapes objects :text) [constraint-ids constraint-values] @@ -478,7 +478,11 @@ [:& constraints-menu {:ids constraint-ids :values constraint-values}]) (when-not (empty? text-ids) - [:& ot/text-menu {:type type :ids text-ids :values text-values}]) + [:> ot/text-menu* + {:type type + :ids text-ids + :values text-values + :applied-tokens text-tokens}]) (when-not (empty? fill-ids) [:> fill/fill-menu* {:type type diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs index 21cd88b881..acf7c6c61f 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs @@ -25,7 +25,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*]] [rumext.v2 :as mf])) (mf/defc options* @@ -162,9 +162,10 @@ {:ids ids :values (select-keys shape constraint-attrs)}]) - [:& text-menu + [:> text-menu* {:ids ids :type type + :applied-tokens applied-tokens :values text-values}] [:> fill/fill-menu* diff --git a/frontend/translations/de.po b/frontend/translations/de.po index 1a35b21e8e..b1bead45b4 100644 --- a/frontend/translations/de.po +++ b/frontend/translations/de.po @@ -208,7 +208,7 @@ msgid "branding-illustrations-marketing-pieces" msgstr "...Branding, Illustrationen, Marketingmaterialien, usw." #: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108 -msgid "color-row.token-color-row.deleted-token" +msgid "options.deleted-token" msgstr "Dieses Token existiert nicht oder wurde gelöscht." #: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35 @@ -1357,7 +1357,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Token-Liste öffnen" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136 -msgid "ds.inputs.token-field.detach-token" +msgid "token-actions.detach-token" msgstr "Token trennen" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:43, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:99, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:106 diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 9108367da9..10851f8f6b 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -197,7 +197,7 @@ msgid "branding-illustrations-marketing-pieces" msgstr "...branding, illustrations, marketing pieces, etc." #: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108 -msgid "color-row.token-color-row.deleted-token" +msgid "options.deleted-token" msgstr "This token does not exists or has been deleted." #: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35 @@ -1288,7 +1288,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Open token list" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136 -msgid "ds.inputs.token-field.detach-token" +msgid "token-actions.detach-token" msgstr "Detach token" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:43, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:99, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:106 @@ -1296,7 +1296,7 @@ msgid "ds.inputs.token-field.no-active-token-option" msgstr "{%s} is not in any active set or has an invalid value." #: src/app/main/ui/ds/controls/utilities/token_field.cljs -msgid "ds.inputs.token-field.no-active-color.token-option" +msgid "not-active-token.no-name" msgstr "This token is not in any active set or has an invalid value." #: src/app/main/data/auth.cljs:339 @@ -7366,6 +7366,14 @@ msgstr "Title case" msgid "workspace.options.text-options.underline" msgstr "Underline (%s)" +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.convert-to-typography" +msgstr "Create typography style" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.text-section" +msgstr "Text section" + #: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs #, unused msgid "workspace.options.text-options.uppercase" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index c4bd7006d0..74d8ef2b66 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -204,7 +204,7 @@ msgid "branding-illustrations-marketing-pieces" msgstr "diseño de marca, ilustraciones, piezas de marketing..." #: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108 -msgid "color-row.token-color-row.deleted-token" +msgid "options.deleted-token" msgstr "Este token no existe o ha sido borrado." #: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35 @@ -1278,7 +1278,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Abrir lista de tokens" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136 -msgid "ds.inputs.token-field.detach-token" +msgid "token-actions.detach-token" msgstr "Desvincular token" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:43, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:99, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:106 @@ -1286,7 +1286,7 @@ msgid "ds.inputs.token-field.no-active-token-option" msgstr "{%s} no está disponible en ningún set o tiene un valor inválido." #: src/app/main/ui/ds/controls/utilities/token_field.cljs -msgid "ds.inputs.token-field.no-active-color.token-option" +msgid "not-active-token.no-name" msgstr "Este token no está disponible en ningún set o tiene un valor inválido." #: src/app/main/data/auth.cljs:339 @@ -7281,6 +7281,14 @@ msgstr "Título" msgid "workspace.options.text-options.underline" msgstr "Subrayado (%s)" +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.convert-to-typography" +msgstr "Crear estilo de tipografía" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.text-section" +msgstr "Sección de textos" + #: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs #, unused msgid "workspace.options.text-options.uppercase" diff --git a/frontend/translations/fa.po b/frontend/translations/fa.po index 55e50951db..14e02fc6eb 100644 --- a/frontend/translations/fa.po +++ b/frontend/translations/fa.po @@ -199,7 +199,7 @@ msgid "auth.work-email" msgstr "ایمیلِ کار" #: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108 -msgid "color-row.token-color-row.deleted-token" +msgid "options.deleted-token" msgstr "این توکن وجود ندارد یا حذف شده است." #: src/app/main/ui/workspace/libraries.cljs:323 diff --git a/frontend/translations/fr.po b/frontend/translations/fr.po index 29010e9d1c..ef26ef224c 100644 --- a/frontend/translations/fr.po +++ b/frontend/translations/fr.po @@ -206,7 +206,7 @@ msgid "branding-illustrations-marketing-pieces" msgstr "...image de marque, illustrations, supports marketing, etc." #: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108 -msgid "color-row.token-color-row.deleted-token" +msgid "options.deleted-token" msgstr "Ce token n'existe pas ou a été supprimé." #: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35 @@ -1364,7 +1364,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Ouvrir la liste des tokens" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136 -msgid "ds.inputs.token-field.detach-token" +msgid "token-actions.detach-token" msgstr "Détacher le token" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:43, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:99, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:106 diff --git a/frontend/translations/fr_CA.po b/frontend/translations/fr_CA.po index c592b77f2b..c71fb8dff0 100644 --- a/frontend/translations/fr_CA.po +++ b/frontend/translations/fr_CA.po @@ -208,7 +208,7 @@ msgid "branding-illustrations-marketing-pieces" msgstr "images de marque, illustrations, matériel de marketing..." #: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108 -msgid "color-row.token-color-row.deleted-token" +msgid "options.deleted-token" msgstr "Ce token n'existe pas ou a été supprimé." #: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35 @@ -1355,7 +1355,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Ouvrir la liste de tokens" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136 -msgid "ds.inputs.token-field.detach-token" +msgid "token-actions.detach-token" msgstr "Détacher du token" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:43, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:99, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:106 diff --git a/frontend/translations/he.po b/frontend/translations/he.po index a288628e94..395bce7724 100644 --- a/frontend/translations/he.po +++ b/frontend/translations/he.po @@ -194,7 +194,7 @@ msgid "branding-illustrations-marketing-pieces" msgstr "…מיתוג, איורים, חומרים שיווקיים ועוד." #: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108 -msgid "color-row.token-color-row.deleted-token" +msgid "options.deleted-token" msgstr "האסימון הזה לא קיים או שנמחק." #: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35 @@ -1182,7 +1182,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "פתיחת רשימת אסימונים" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136 -msgid "ds.inputs.token-field.detach-token" +msgid "token-actions.detach-token" msgstr "ניתוק אסימון" #: src/app/main/data/auth.cljs:339 diff --git a/frontend/translations/hi.po b/frontend/translations/hi.po index 77507cfd03..54c563fd5d 100644 --- a/frontend/translations/hi.po +++ b/frontend/translations/hi.po @@ -200,7 +200,7 @@ msgid "branding-illustrations-marketing-pieces" msgstr "...ब्रांडिंग, चित्रण, मार्केटिंग सामग्री आदि।" #: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108 -msgid "color-row.token-color-row.deleted-token" +msgid "options.deleted-token" msgstr "यह token मौजूद नहीं है या हटा दिया गया है।" #: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35 @@ -1253,7 +1253,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "token सूची खोलें" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136 -msgid "ds.inputs.token-field.detach-token" +msgid "token-actions.detach-token" msgstr "token अलग करें" #: src/app/main/data/auth.cljs:339 diff --git a/frontend/translations/it.po b/frontend/translations/it.po index cac516f2bb..a6d88a564c 100644 --- a/frontend/translations/it.po +++ b/frontend/translations/it.po @@ -203,7 +203,7 @@ msgid "branding-illustrations-marketing-pieces" msgstr "…branding, illustrazione, materiali di marketing, etc." #: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108 -msgid "color-row.token-color-row.deleted-token" +msgid "options.deleted-token" msgstr "Questo token non esiste o è stato eliminato." #: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35 @@ -1350,7 +1350,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Apri elenco token" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136 -msgid "ds.inputs.token-field.detach-token" +msgid "token-actions.detach-token" msgstr "Scollega token" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:43, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:99, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:106 diff --git a/frontend/translations/ko.po b/frontend/translations/ko.po index f590f97179..f27c397ff8 100644 --- a/frontend/translations/ko.po +++ b/frontend/translations/ko.po @@ -191,7 +191,7 @@ msgid "branding-illustrations-marketing-pieces" msgstr "...브랜딩, 일러스트레이션, 마케팅 자료 등." #: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108 -msgid "color-row.token-color-row.deleted-token" +msgid "options.deleted-token" msgstr "이 토큰은 존재하지 않거나 삭제되었습니다." #: src/app/main/ui/comments.cljs:530 diff --git a/frontend/translations/lv.po b/frontend/translations/lv.po index 48ae267a5a..72fef0e36f 100644 --- a/frontend/translations/lv.po +++ b/frontend/translations/lv.po @@ -205,7 +205,7 @@ msgid "branding-illustrations-marketing-pieces" msgstr "... zīmolrades, ilustrācijām, mārketinga materiāliem utt." #: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108 -msgid "color-row.token-color-row.deleted-token" +msgid "options.deleted-token" msgstr "Šī tekstvienība nepastāv vai ir izdzēsta." #: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35 @@ -1211,7 +1211,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Atvērt tekstvienību sarakstu" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136 -msgid "ds.inputs.token-field.detach-token" +msgid "token-actions.detach-token" msgstr "Atdalīt tekstvienību" #: src/app/main/data/auth.cljs:339 diff --git a/frontend/translations/nl.po b/frontend/translations/nl.po index ea4c4ba523..819e3da13d 100644 --- a/frontend/translations/nl.po +++ b/frontend/translations/nl.po @@ -201,7 +201,7 @@ msgid "branding-illustrations-marketing-pieces" msgstr "…branding, illustraties, marketingstukken, etc." #: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108 -msgid "color-row.token-color-row.deleted-token" +msgid "options.deleted-token" msgstr "Dit token bestaat niet of is verwijderd." #: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35 @@ -1353,7 +1353,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Lijst met tokens openen" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136 -msgid "ds.inputs.token-field.detach-token" +msgid "token-actions.detach-token" msgstr "Token loskoppelen" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:43, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:99, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:106 diff --git a/frontend/translations/pt_BR.po b/frontend/translations/pt_BR.po index 82a5480389..eb14ed44e8 100644 --- a/frontend/translations/pt_BR.po +++ b/frontend/translations/pt_BR.po @@ -202,7 +202,7 @@ msgid "branding-illustrations-marketing-pieces" msgstr "... marca, ilustrações, materiais de marketing, etc." #: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108 -msgid "color-row.token-color-row.deleted-token" +msgid "options.deleted-token" msgstr "Este token não existe ou foi excluído." #: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35 @@ -1182,7 +1182,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Abrir lista de tokens" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136 -msgid "ds.inputs.token-field.detach-token" +msgid "token-actions.detach-token" msgstr "Desvincular token" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:43, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:99, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:106 diff --git a/frontend/translations/ro.po b/frontend/translations/ro.po index 9ec2ddf587..11d1473623 100644 --- a/frontend/translations/ro.po +++ b/frontend/translations/ro.po @@ -208,7 +208,7 @@ msgid "branding-illustrations-marketing-pieces" msgstr "... mărci, ilustrații, piese de marketing, etc." #: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108 -msgid "color-row.token-color-row.deleted-token" +msgid "options.deleted-token" msgstr "Acest token nu există sau a fost șters." #: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35 @@ -1199,7 +1199,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Deschide lista de token-uri" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136 -msgid "ds.inputs.token-field.detach-token" +msgid "token-actions.detach-token" msgstr "Detașează tokenul" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:43, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:99, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:106 diff --git a/frontend/translations/ru.po b/frontend/translations/ru.po index e4d471bde5..95874fe4dc 100644 --- a/frontend/translations/ru.po +++ b/frontend/translations/ru.po @@ -204,7 +204,7 @@ msgid "branding-illustrations-marketing-pieces" msgstr "...брендинг, иллюстрации, маркетинговые материалы и т.д." #: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108 -msgid "color-row.token-color-row.deleted-token" +msgid "options.deleted-token" msgstr "Этот токен не существует или был удален." #: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35 @@ -1181,7 +1181,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Открыть список токенов" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136 -msgid "ds.inputs.token-field.detach-token" +msgid "token-actions.detach-token" msgstr "Отсоединить токен" #: src/app/main/data/auth.cljs:339 diff --git a/frontend/translations/sv.po b/frontend/translations/sv.po index bdf1d0ea0e..810055f737 100644 --- a/frontend/translations/sv.po +++ b/frontend/translations/sv.po @@ -201,7 +201,7 @@ msgid "branding-illustrations-marketing-pieces" msgstr "...varumärkesbyggande, illustrationer, marknadsföringsmaterial, etc." #: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108 -msgid "color-row.token-color-row.deleted-token" +msgid "options.deleted-token" msgstr "Denna token existerar inte eller har raderats." #: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35 @@ -1183,7 +1183,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Öppna token-lista" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136 -msgid "ds.inputs.token-field.detach-token" +msgid "token-actions.detach-token" msgstr "Lösgör token" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:43, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:99, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:106 diff --git a/frontend/translations/tr.po b/frontend/translations/tr.po index f8d364d3bf..c430de809f 100644 --- a/frontend/translations/tr.po +++ b/frontend/translations/tr.po @@ -202,7 +202,7 @@ msgid "branding-illustrations-marketing-pieces" msgstr "...marka çalışması, çizimler, pazarlama materyalleri, vb." #: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108 -msgid "color-row.token-color-row.deleted-token" +msgid "options.deleted-token" msgstr "Bu token yok veya silindi." #: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35 @@ -1352,7 +1352,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Token listesini aç" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136 -msgid "ds.inputs.token-field.detach-token" +msgid "token-actions.detach-token" msgstr "Tokeni ayır" #: src/app/main/data/auth.cljs:339 diff --git a/frontend/translations/zh_CN.po b/frontend/translations/zh_CN.po index 19707c26a6..2eb79194f5 100644 --- a/frontend/translations/zh_CN.po +++ b/frontend/translations/zh_CN.po @@ -1113,7 +1113,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "打开token列表" #: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136 -msgid "ds.inputs.token-field.detach-token" +msgid "token-actions.detach-token" msgstr "分离token" #: src/app/main/data/auth.cljs:339