From e85430904961771b65dc4ebf6d709f4fd1abd564 Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Tue, 19 May 2026 09:47:04 +0200 Subject: [PATCH] :tada: Add typography token row to multiselected texts (#9128) * :bug: Fix text multiselection messages * :recycle: Add tooltip to typography tooltip * :recycle: Improve copy and add detach buttons --- .../ui/specs/multiseleccion.spec.js | 16 +++--- .../data/workspace/tokens/application.cljs | 13 +++++ .../ui/ds/controls/shared/token_option.cljs | 6 ++- .../workspace/colorpicker/color_tokens.cljs | 2 +- .../workspace/sidebar/options/menus/text.cljs | 46 +++++++++++++---- .../workspace/sidebar/options/menus/text.scss | 49 +++++++++++-------- .../sidebar/options/menus/typography.cljs | 7 ++- .../sidebar/options/menus/typography.scss | 4 ++ .../sidebar/options/shapes/multiple.cljs | 2 +- frontend/translations/en.po | 32 ++++++++++++ frontend/translations/es.po | 32 +++++++++++- 11 files changed, 164 insertions(+), 45 deletions(-) diff --git a/frontend/playwright/ui/specs/multiseleccion.spec.js b/frontend/playwright/ui/specs/multiseleccion.spec.js index c81e4ef5f2..3f7b351ce0 100644 --- a/frontend/playwright/ui/specs/multiseleccion.spec.js +++ b/frontend/playwright/ui/specs/multiseleccion.spec.js @@ -250,14 +250,14 @@ test("Multiselection of text and typographies", async ({ page }) => { await expect(textSection).toBeVisible(); await expect( - textSection.getByText("Multiple typographies"), + textSection.getByText("Mixed assets"), ).not.toBeVisible(); // Select two plain text layer with different font family await plainTextLayerTwo.click({ modifiers: ["Control"] }); await expect(textSection).toBeVisible(); await expect( - textSection.getByTitle("Font family").getByText("--"), + textSection.getByTitle("Font family").getByText("Mixed Font Families"), ).toBeVisible(); // Select typography text layer @@ -268,7 +268,7 @@ test("Multiselection of text and typographies", async ({ page }) => { // Select two typography text layer with different typography await typographyTextLayerTwo.click({ modifiers: ["Control"] }); await expect(textSection).toBeVisible(); - await expect(textSection.getByText("Multiple typographies")).toBeVisible(); + await expect(textSection.getByText("Mixed assets")).toBeVisible(); // Select token typography text layer // TODO: CHANGE WHEN TOKEN TYPOGRAPHY ROW IS READY @@ -281,32 +281,32 @@ test("Multiselection of text and typographies", async ({ page }) => { await tokenTypographyTextLayerTwo.click({ modifiers: ["Control"] }); await expect(textSection).toBeVisible(); await expect( - textSection.getByTitle("Font family").getByText("--"), + textSection.getByTitle("Font family").getByText("Mixed Font Families"), ).toBeVisible(); //Select plain text layer and typography text layer together await plainTextLayer.click(); await typographyTextLayerOne.click({ modifiers: ["Control"] }); await expect(textSection).toBeVisible(); - await expect(textSection.getByText("Multiple typographies")).toBeVisible(); + await expect(textSection.getByText("Mixed assets")).toBeVisible(); //Select plain text layer and typography text layer together on reverse order await typographyTextLayerOne.click(); await plainTextLayer.click({ modifiers: ["Control"] }); await expect(textSection).toBeVisible(); - await expect(textSection.getByText("Multiple typographies")).toBeVisible(); + await expect(textSection.getByText("Mixed assets")).toBeVisible(); //Selen token typography text layer and typography text layer together await tokenTypographyTextLayerOne.click(); await typographyTextLayerOne.click({ modifiers: ["Control"] }); await expect(textSection).toBeVisible(); - await expect(textSection.getByText("Multiple typographies")).toBeVisible(); + await expect(textSection.getByText("Mixed assets")).toBeVisible(); //Select token typography text layer and typography text layer together on reverse order await typographyTextLayerOne.click(); await tokenTypographyTextLayerOne.click({ modifiers: ["Control"] }); await expect(textSection).toBeVisible(); - await expect(textSection.getByText("Multiple typographies")).toBeVisible(); + await expect(textSection.getByText("Mixed assets")).toBeVisible(); // Select rectangle and elipse together await rectangleLayer.click(); diff --git a/frontend/src/app/main/data/workspace/tokens/application.cljs b/frontend/src/app/main/data/workspace/tokens/application.cljs index a1d37e0b0d..45950e08f6 100644 --- a/frontend/src/app/main/data/workspace/tokens/application.cljs +++ b/frontend/src/app/main/data/workspace/tokens/application.cljs @@ -777,6 +777,19 @@ (fn [shape] (update shape :applied-tokens remove-token)))))))) +(defn unapply-multiple-tokens + "Removes `attributes` for `shape-ids` without knowing the token, used when a token is deleted." + [{:keys [attributes shape-ids] :as _props}] + + (ptk/reify ::unapply-multiple-tokens + ptk/WatchEvent + (watch [_ _ _] + (rx/of + (dwsh/update-shapes + shape-ids + (fn [shape] + (update shape :applied-tokens #(when % (apply dissoc % attributes))))))))) + (defn toggle-token [{:keys [token attrs shape-ids expand-with-children]}] 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 5e8ca43503..15c1854330 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 @@ -60,8 +60,10 @@ id (d/nilv id internal-id) element-ref (mf/use-ref nil) tooltip-content (if (map? resolved) - (mf/html [:> resolved-value-tooltip* {:token-name name - :resolved-value resolved}]) + (mf/html + [:> resolved-value-tooltip* + {:token-name name + :resolved-value resolved}]) name)] [:li {:value id :class (stl/css-case :token-option true diff --git a/frontend/src/app/main/ui/workspace/colorpicker/color_tokens.cljs b/frontend/src/app/main/ui/workspace/colorpicker/color_tokens.cljs index 89be5ed48f..c06eeaab81 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/color_tokens.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/color_tokens.cljs @@ -294,7 +294,7 @@ sorted-tokens (sort-combined-tokens filtered-combined)] (if (seq combined-tokens) [:div {:class (stl/css :color-tokens-section)} - [:> input* {:placeholder "Search by token name" + [:> input* {:placeholder (tr "workspace.tokens.search-by-token") :icon i/search :max-length max-input-length :variant "comfortable" 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 504856d22e..7b762f68af 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 @@ -292,7 +292,7 @@ (csu/get-token-dropdown-options typography-tokens nil)) selected-token-id* - (mf/use-state #(when current-token-name + (mf/use-state #(when (and (not= :multiple current-token-name) current-token-name) (:id (get-option-by-name dropdown-options current-token-name)))) selected-token-id (deref selected-token-id*) @@ -411,11 +411,19 @@ detach-token (mf/use-fn + (mf/deps ids) (fn [token-name] (st/emit! (dwta/unapply-token {:token-name token-name :attributes #{:typography} :shape-ids ids})))) + handle-detach-all-tokens + (mf/use-fn + (mf/deps ids) + (fn [] + (st/emit! (dwta/unapply-multiple-tokens {:attributes #{:typography} + :shape-ids ids})))) + expand-stream (mf/with-memo [] (->> st/stream (rx/filter (ptk/type? :expand-text-more-options)))) @@ -445,7 +453,7 @@ (mf/with-effect [applied-token-name dropdown-options] (reset! selected-token-id* - (when applied-token-name + (when (and (not= :multiple applied-token-name) applied-token-name) (:id (get-option-by-name dropdown-options applied-token-name))))) (mf/with-effect [token-dropdown-open?] @@ -477,11 +485,36 @@ (when main-menu-open? [:div {:class (stl/css :element-content)} (cond + (and token-typography-row-enabled? (= :multiple current-token-name) (= typography-id :multiple)) + [:div {:class (stl/css :multiple-typography)} + [:span {:class (stl/css :multiple-text)} + (tr "workspace.libraries.text.mixed-tokens-and-assets")]] + + (and token-typography-row-enabled? (= :multiple current-token-name)) + [:div {:class (stl/css :multiple-typography)} + [:span {:class (stl/css :multiple-text)} + (tr "workspace.libraries.text.mixed-tokens")] + [:> icon-button* {:variant "ghost" + :aria-label (tr "workspace.libraries.text.multiple-token-tooltip") + :tooltip-placement "top-left" + :on-click handle-detach-all-tokens + :icon i/detach}]] + (and token-typography-row-enabled? current-token-name) [:> token-typography-row* {:token-name current-token-name :detach-token detach-token :active-tokens (resolve-delay typography-tokens)}] + (= typography-id :multiple) + [:div {:class (stl/css :multiple-typography)} + [:span {:class (stl/css :multiple-text)} + (tr "workspace.libraries.text.mixed-typography")] + [:> icon-button* {:variant "ghost" + :aria-label (tr "workspace.libraries.text.multiple-assets-tooltip") + :on-click handle-detach-typography + :tooltip-placement "top-left" + :icon i/detach}]] + typography [:& typography-entry {:file-id typography-file-id :typography typography @@ -489,13 +522,7 @@ :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* common-props]) @@ -521,4 +548,5 @@ :options (resolve-delay dropdown-options) :selected selected-token-id :align "right" + :placeholder (tr "workspace.tokens.search-by-token") :ref set-option-ref}])])) 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 ca1d49435e..5650b0c68b 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 @@ -4,11 +4,18 @@ // // 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/_utils.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_borders.scss" as *; +@use "ds/spacing.scss" as *; +@use "ds/mixins.scss" as *; .element-set { @include sidebar.option-grid-structure; + + position: relative; } .element-title { @@ -16,37 +23,37 @@ } .element-content { - @include deprecated.flex-column; - + display: flex; + flex-direction: column; + gap: var(--sp-xs); grid-column: span 8; - margin-top: deprecated.$s-4; + margin-block-start: 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; + block-size: $sz-32; + padding-block: var(--sp-s); + padding-inline: var(--sp-s) 0; + background-color: var(--color-background-tertiary); + color: var(--color-foreground-primary); } .multiple-text { - @include deprecated.body-small-typography; + @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-secondary); } .text-align-options { display: flex; - gap: deprecated.$s-4; + gap: var(--sp-xs); } .align-options, @@ -54,10 +61,10 @@ .vertical-align-options, .grow-options, .text-decoration-options { - height: deprecated.$s-32; + block-size: $sz-32; } .text-decoration-options { display: flex; - gap: deprecated.$s-4; + gap: var(--sp-xs); } diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs index 4266716f7b..6b4ded0961 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs @@ -315,7 +315,12 @@ :on-click #(reset! open-selector? true)} (cond (or (= :multiple font-id) (= "mixed" font-id)) - "--" + [:* + [:span {:class (stl/css :font-option-name :font-family-mixed)} + (tr "inspect.attributes.typography.mixed-font-family")] + [:> icon* {:icon-id i/arrow-down + :class (stl/css :dropdown-icon) + :size "s"}]] (some? font) [:* diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.scss index d01a04d186..5e630aea3d 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.scss @@ -51,6 +51,10 @@ visibility: var(--actions-visibility); } +.font-family-mixed { + color: var(--color-foreground-secondary); +} + .typography-selection-wrapper { display: grid; grid-template-columns: $sz-24 auto 1fr; 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 70827abf97..e889f28f9f 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 @@ -274,7 +274,7 @@ (merge-attrs shape-attrs) (merge-attrs content-attrs)) - new-token-acc (merge-token-values token-acc content-attrs applied-tokens)] + new-token-acc (merge-token-values token-acc editable-attrs applied-tokens)] [(conj ids id) new-values new-token-acc]) diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 7f280de647..127693ca6b 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -2015,6 +2015,10 @@ msgstr "Typography" msgid "inspect.attributes.typography.font-family" msgstr "Font Family" +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:308 +msgid "inspect.attributes.typography.mixed-font-family" +msgstr "Mixed Font Families" + #: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:326, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:332 msgid "inspect.attributes.typography.font-size" msgstr "Font Size" @@ -6347,10 +6351,34 @@ msgstr "Connect library" msgid "workspace.libraries.text.multiple-typography" msgstr "Multiple typographies" +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:332 +msgid "workspace.libraries.text.mixed-typography" +msgstr "Mixed assets" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:332 +msgid "workspace.libraries.text.mixed-tokens" +msgstr "Mixed tokens" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:332 +msgid "workspace.libraries.text.mixed-tokens-and-assets" +msgstr "Mixed assets and tokens" + #: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:335 msgid "workspace.libraries.text.multiple-typography-tooltip" msgstr "Unlink all typographies" +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:335 +msgid "workspace.libraries.text.multiple-assets-tooltip" +msgstr "Unlink all assets" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:335 +msgid "workspace.libraries.text.multiple-token-tooltip" +msgstr "Detach all tokens" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:335 +msgid "workspace.libraries.text.multiple-token-and-asset-tooltip" +msgstr "Detach all tokens & unlink all typographies" + #: src/app/main/ui/workspace/libraries.cljs:103, src/app/main/ui/workspace/libraries.cljs:130 msgid "workspace.libraries.typography" msgid_plural "workspace.libraries.typography" @@ -8960,6 +8988,10 @@ msgstr "Duplicate Tokens Group" msgid "workspace.tokens.rename-group-name-hint" msgstr "Your tokens will automatically be renamed to %s.(suffix).(tokenName)" +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs, src/app/main/ui/workspace/colorpicker/color_tokens.cljs +msgid "workspace.tokens.search-by-token" +msgstr "Search by token name" + #: src/app/main/ui/workspace/sidebar.cljs:159, src/app/main/ui/workspace/sidebar.cljs:166 msgid "workspace.toolbar.assets" msgstr "Assets" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 212b919508..f1ee42fc1c 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -1958,6 +1958,10 @@ msgstr "Tipografía" msgid "inspect.attributes.typography.font-family" msgstr "Familia tipográfica" +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:308 +msgid "inspect.attributes.typography.mixed-font-family" +msgstr "Varias Familias tipográficas" + #: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:326, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:332 msgid "inspect.attributes.typography.font-size" msgstr "Tamaño de fuente" @@ -6216,13 +6220,33 @@ msgid "workspace.libraries.shared-library-btn" msgstr "Conectar biblioteca" #: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:332 -msgid "workspace.libraries.text.multiple-typography" -msgstr "Varias tipografías" +msgid "workspace.libraries.text.mixed-typography" +msgstr "Varios recursos" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:332 +msgid "workspace.libraries.text.mixed-tokens" +msgstr "Varios tokens" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:332 +msgid "workspace.libraries.text.mixed-tokens-and-assets" +msgstr "Varios tokens y recursos" #: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:335 msgid "workspace.libraries.text.multiple-typography-tooltip" msgstr "Desvincular todas las tipografías" +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:335 +msgid "workspace.libraries.text.multiple-assets-tooltip" +msgstr "Desvincular todos los recursos" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:335 +msgid "workspace.libraries.text.multiple-token-tooltip" +msgstr "Desvincular todos los tokens" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:335 +msgid "workspace.libraries.text.multiple-token-and-asset-tooltip" +msgstr "Desvincular todos los tokens y recursos" + #: src/app/main/ui/workspace/libraries.cljs:103, src/app/main/ui/workspace/libraries.cljs:130 msgid "workspace.libraries.typography" msgid_plural "workspace.libraries.typography" @@ -8674,6 +8698,10 @@ msgstr "Duplicar grupo de tokens" msgid "workspace.tokens.rename-group-name-hint" msgstr "Tus tokens serán automáticamente renombrados a %s.(sufijo).(token)" +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs, src/app/main/ui/workspace/colorpicker/color_tokens.cljs +msgid "workspace.tokens.search-by-token" +msgstr "Buscar por el nombre del token" + #: src/app/main/ui/workspace/sidebar.cljs:159, src/app/main/ui/workspace/sidebar.cljs:166 msgid "workspace.toolbar.assets" msgstr "Recursos"