diff --git a/common/src/app/common/types/token.cljc b/common/src/app/common/types/token.cljc index 707796e4f8..f35d5145c7 100644 --- a/common/src/app/common/types/token.cljc +++ b/common/src/app/common/types/token.cljc @@ -30,22 +30,23 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (def token-type->dtcg-token-type - {:boolean "boolean" - :border-radius "borderRadius" - :color "color" - :dimensions "dimension" - :font-family "fontFamilies" - :font-size "fontSizes" - :letter-spacing "letterSpacing" - :text-case "textCase" - :number "number" - :opacity "opacity" - :other "other" - :rotation "rotation" - :sizing "sizing" - :spacing "spacing" - :string "string" - :stroke-width "strokeWidth"}) + {:boolean "boolean" + :border-radius "borderRadius" + :color "color" + :dimensions "dimension" + :font-family "fontFamilies" + :font-size "fontSizes" + :letter-spacing "letterSpacing" + :text-case "textCase" + :text-decoration "textDecoration" + :number "number" + :opacity "opacity" + :other "other" + :rotation "rotation" + :sizing "sizing" + :spacing "spacing" + :string "string" + :stroke-width "strokeWidth"}) (def dtcg-token-type->token-type (set/map-invert token-type->dtcg-token-type)) @@ -170,10 +171,17 @@ (def text-case-keys (schema-keys schema:text-case)) +(def ^:private schema:text-decoration + [:map + [:text-decoration {:optional true} token-name-ref]]) + +(def text-decoration-keys (schema-keys schema:text-decoration)) + (def typography-keys (set/union font-size-keys letter-spacing-keys font-family-keys - text-case-keys)) + text-case-keys + text-decoration-keys)) ;; TODO: Created to extract the font-size feature from the typography feature flag. ;; Delete this once the typography feature flag is removed. @@ -212,6 +220,7 @@ schema:letter-spacing schema:font-family schema:text-case + schema:text-decoration schema:dimensions]) (defn shape-attr->token-attrs @@ -243,6 +252,7 @@ (letter-spacing-keys shape-attr) #{shape-attr} (font-family-keys shape-attr) #{shape-attr} (text-case-keys shape-attr) #{shape-attr} + (text-decoration-keys shape-attr) #{shape-attr} (border-radius-keys shape-attr) #{shape-attr} (sizing-keys shape-attr) #{shape-attr} (opacity-keys shape-attr) #{shape-attr} diff --git a/frontend/src/app/main/data/style_dictionary.cljs b/frontend/src/app/main/data/style_dictionary.cljs index 9363419c30..bc016d2b92 100644 --- a/frontend/src/app/main/data/style_dictionary.cljs +++ b/frontend/src/app/main/data/style_dictionary.cljs @@ -175,6 +175,24 @@ :else {:errors [(wte/error-with-value :error.style-dictionary/invalid-token-value-text-case value)]}))) +(defn- parse-sd-token-text-decoration-value + "Parses `value` of a text-decoration `sd-token` into a map like `{:value \"underline\"}`. + If the `value` is not parseable and/or has missing references returns a map with `:errors`." + [value] + (let [normalized-value (str/lower (str/trim value)) + valid? (contains? #{"none" "underline" "strike-through"} normalized-value) + references (seq (ctob/find-token-value-references value))] + (cond + valid? + {:value normalized-value} + + references + {:errors [(wte/error-with-value :error.style-dictionary/missing-reference references)] + :references references} + + :else + {:errors [(wte/error-with-value :error.style-dictionary/invalid-token-value-text-decoration value)]}))) + (defn process-sd-tokens "Converts a StyleDictionary dictionary with resolved tokens (aka `sd-tokens`) back to clojure. The `get-origin-token` argument should be a function that takes an @@ -218,6 +236,7 @@ :opacity (parse-sd-token-opacity-value value has-references?) :stroke-width (parse-sd-token-stroke-width-value value has-references?) :text-case (parse-sd-token-text-case-value value) + :text-decoration (parse-sd-token-text-decoration-value value) :number (parse-sd-token-number-value value) (parse-sd-token-general-value value)) output-token (cond (:errors parsed-token-value) diff --git a/frontend/src/app/main/data/workspace/tokens/application.cljs b/frontend/src/app/main/data/workspace/tokens/application.cljs index f6277f24ff..f6a44a8c56 100644 --- a/frontend/src/app/main/data/workspace/tokens/application.cljs +++ b/frontend/src/app/main/data/workspace/tokens/application.cljs @@ -307,6 +307,15 @@ (when (string? value) (generate-text-shape-update {:text-transform value} shape-ids page-id)))) +(defn update-text-decoration + ([value shape-ids attributes] (update-text-decoration value shape-ids attributes nil)) + ([value shape-ids _attributes page-id] + (when (string? value) + (let [css-value (case value + "strike-through" "line-through" + value)] + (generate-text-shape-update {:text-decoration css-value} shape-ids page-id))))) + ;; Events to apply / unapply tokens to shapes ------------------------------------------------------------ (defn apply-token @@ -481,6 +490,14 @@ :fields [{:label "Text Case" :key :text-case}]}} + :text-decoration + {:title "Text Decoration" + :attributes ctt/text-decoration-keys + :on-update-shape update-text-decoration + :modal {:key :tokens/text-decoration + :fields [{:label "Text Decoration" + :key :text-decoration}]}} + :stroke-width {:title "Stroke Width" :attributes ctt/stroke-width-keys diff --git a/frontend/src/app/main/data/workspace/tokens/errors.cljs b/frontend/src/app/main/data/workspace/tokens/errors.cljs index 52402da22c..85bb653281 100644 --- a/frontend/src/app/main/data/workspace/tokens/errors.cljs +++ b/frontend/src/app/main/data/workspace/tokens/errors.cljs @@ -76,6 +76,10 @@ {:error/code :error.style-dictionary/invalid-token-value-text-case :error/fn #(tr "workspace.tokens.invalid-text-case-token-value" %)} + :error.style-dictionary/invalid-token-value-text-decoration + {:error/code :error.style-dictionary/invalid-token-value-text-decoration + :error/fn #(tr "workspace.tokens.invalid-text-decoration-token-value" %)} + :error/unknown {:error/code :error/unknown :error/fn #(tr "labels.unknown-error")}}) diff --git a/frontend/src/app/main/data/workspace/tokens/propagation.cljs b/frontend/src/app/main/data/workspace/tokens/propagation.cljs index d892404de2..0f5cc1c001 100644 --- a/frontend/src/app/main/data/workspace/tokens/propagation.cljs +++ b/frontend/src/app/main/data/workspace/tokens/propagation.cljs @@ -37,6 +37,7 @@ #{:letter-spacing} dwta/update-letter-spacing #{:font-family} dwta/update-font-family #{:text-case} dwta/update-text-case + #{:text-decoration} dwta/update-text-decoration #{:x :y} dwta/update-shape-position #{:p1 :p2 :p3 :p4} dwta/update-layout-padding #{:m1 :m2 :m3 :m4} dwta/update-layout-item-margin diff --git a/frontend/src/app/main/ui/workspace/tokens/management/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/management/context_menu.cljs index 1b08b6e6ca..f4c103a7bf 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/context_menu.cljs @@ -271,6 +271,7 @@ font-family (partial generic-attribute-actions #{:font-family} "Font Family") line-height #(generic-attribute-actions #{:line-height} "Line Height" (assoc % :on-update-shape dwta/update-line-height)) text-case (partial generic-attribute-actions #{:text-case} "Text Case") + text-decoration (partial generic-attribute-actions #{:text-decoration} "Text Decoration") border-radius (partial all-or-separate-actions {:attribute-labels {:r1 "Top Left" :r2 "Top Right" :r4 "Bottom Left" @@ -298,6 +299,7 @@ :line-height line-height :letter-spacing letter-spacing :text-case text-case + :text-decoration text-decoration :dimensions (fn [context-data] (-> (concat (when (seq (sizing-attribute-actions context-data)) [{:title "Sizing" :submenu :sizing}]) diff --git a/frontend/src/app/main/ui/workspace/tokens/management/create/form.cljs b/frontend/src/app/main/ui/workspace/tokens/management/create/form.cljs index dc33e9ce4a..4b332f9934 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/create/form.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/create/form.cljs @@ -794,6 +794,13 @@ (mf/spread-props props {:token token :input-placeholder placeholder})])) +(mf/defc text-decoration-form* + [{:keys [token] :rest props}] + (let [placeholder (tr "workspace.tokens.text-decoration-value-enter")] + [:> form* + (mf/spread-props props {:token token + :input-placeholder placeholder})])) + (mf/defc form-wrapper* [{:keys [token token-type] :as props}] (let [token-type' (or (:type token) token-type)] @@ -801,4 +808,5 @@ :color [:> color-form* props] :font-family [:> font-family-form* props] :text-case [:> text-case-form* props] + :text-decoration [:> text-decoration-form* props] [:> form* props]))) diff --git a/frontend/src/app/main/ui/workspace/tokens/management/create/modals.cljs b/frontend/src/app/main/ui/workspace/tokens/management/create/modals.cljs index a0316ef76f..aa910ffb59 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/create/modals.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/create/modals.cljs @@ -203,3 +203,9 @@ ::mf/register-as :tokens/text-case} [properties] [:& token-update-create-modal properties]) + +(mf/defc text-decoration-modal + {::mf/register modal/components + ::mf/register-as :tokens/text-decoration} + [properties] + [:& token-update-create-modal properties]) diff --git a/frontend/src/app/main/ui/workspace/tokens/management/group.cljs b/frontend/src/app/main/ui/workspace/tokens/management/group.cljs index f14b4b8c59..462c63caf4 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/group.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/group.cljs @@ -31,6 +31,7 @@ :font-size "text-font-size" :letter-spacing "text-letterspacing" :text-case "text-mixed" + :text-decoration "text-underlined" :opacity "percentage" :number "number" :rotation "rotation" diff --git a/frontend/src/app/main/ui/workspace/tokens/management/token_pill.cljs b/frontend/src/app/main/ui/workspace/tokens/management/token_pill.cljs index 639d51c707..52d630bc80 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/token_pill.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/token_pill.cljs @@ -176,7 +176,7 @@ selected-shapes))) (def token-types-with-status-icon - #{:color :border-radius :rotation :sizing :dimensions :opacity :spacing :stroke-width :text-case}) + #{:color :border-radius :rotation :sizing :dimensions :opacity :spacing :stroke-width}) (mf/defc token-pill* {::mf/wrap [mf/memo]} diff --git a/frontend/test/frontend_tests/tokens/logic/token_actions_test.cljs b/frontend/test/frontend_tests/tokens/logic/token_actions_test.cljs index 3181d95cb5..d9e7c9d93c 100644 --- a/frontend/test/frontend_tests/tokens/logic/token_actions_test.cljs +++ b/frontend/test/frontend_tests/tokens/logic/token_actions_test.cljs @@ -626,6 +626,40 @@ (t/is (= (:text-case (:applied-tokens text-1')) (:name token-target'))) (t/is (= (:text-transform style-text-blocks) "uppercase"))))))))) +(t/deftest test-apply-text-decoration + (t/testing "applies text-decoration token and updates the text decoration" + (t/async + done + (let [text-decoration-token {:name "underline-decoration" + :value "underline" + :type :text-decoration} + file (-> (setup-file-with-tokens) + (update-in [:data :tokens-lib] + #(ctob/add-token-in-set % "Set A" (ctob/make-token text-decoration-token)))) + store (ths/setup-store file) + text-1 (cths/get-shape file :text-1) + events [(dwta/apply-token {:shape-ids [(:id text-1)] + :attributes #{:text-decoration} + :token (toht/get-token file "underline-decoration") + :on-update-shape dwta/update-text-decoration})]] + (tohs/run-store-async + store done events + (fn [new-state] + (let [file' (ths/get-file-from-state new-state) + token-target' (toht/get-token file' "underline-decoration") + text-1' (cths/get-shape file' :text-1) + style-text-blocks (->> (:content text-1') + (txt/content->text+styles) + (remove (fn [[_ text]] (str/empty? (str/trim text)))) + (mapv (fn [[style text]] + {:styles (merge txt/default-text-attrs style) + :text-content text})) + (first) + (:styles))] + (t/is (some? (:applied-tokens text-1'))) + (t/is (= (:text-decoration (:applied-tokens text-1')) (:name token-target'))) + (t/is (= (:text-decoration style-text-blocks) "underline"))))))))) + (t/deftest test-toggle-token-none (t/testing "should apply token to all selected items, where no item has the token applied" (t/async diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 59387046e2..01b3dd4091 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -7290,6 +7290,14 @@ msgstr "Enter text case: none | Uppercase | Lowercase | Capitalize" msgid "workspace.tokens.invalid-text-case-token-value" msgstr "Invalid token value: only none, Uppercase, Lowercase or Capitalize are accepted" +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:799 +msgid "workspace.tokens.text-decoration-value-enter" +msgstr "Enter text decoration: none | underline | strike-through" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:81 +msgid "workspace.tokens.invalid-text-decoration-token-value" +msgstr "Invalid token value: only none, underline and strike-through are accepted" + #: src/app/main/ui/workspace/tokens/modals/export.cljs:33 msgid "workspace.tokens.export.preview" msgstr "Preview:" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 296fc7cbea..0261ad00c2 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -7236,6 +7236,10 @@ msgstr "No existen tokens, temas o sets para exportar." msgid "workspace.tokens.text-case-value-enter" msgstr "Introduce una capitalización: none | Uppercase | Lowercase | Capitalize" +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:799 +msgid "workspace.tokens.text-decoration-value-enter" +msgstr "Introduce text decoration: none | underline | strike-through" + #: src/app/main/ui/workspace/tokens/modals/export.cljs:33 msgid "workspace.tokens.export.preview" msgstr "Previsualizar:"