Add numeric token type (#6575)

*  Add numeric type token

* 🐛 Fix comments
This commit is contained in:
Eva Marco
2025-06-05 09:33:54 +02:00
committed by GitHub
parent db84eb365b
commit ab0219876e
23 changed files with 331 additions and 180 deletions

View File

@@ -29,6 +29,16 @@
:name "Rect1"}
params)))
(defn add-text
[file text-label content & {:keys [text-params] :as text}]
(let [shape (-> (cts/setup-shape {:type :text :x 0 :y 0})
(txt/change-text content))]
(ths/add-sample-shape file text-label
(merge shape
text-params))))
(defn add-frame
[file frame-label & {:keys [] :as params}]
;; Generated shape tree:

View File

@@ -169,6 +169,13 @@
item)))
root)))
(defn update-text-content
[shape pred-fn update-fn attrs]
(let [update-attrs-fn #(update-fn % attrs)
transform #(transform-nodes pred-fn update-attrs-fn %)]
(-> shape
(update :content transform))))
(defn generate-shape-name
[text]
(subs text 0 (min 280 (count text))))

View File

@@ -228,7 +228,7 @@
[:blur {:optional true} ::ctsb/blur]
[:grow-type {:optional true}
[::sm/one-of grow-types]]
[:applied-tokens {:optional true} ::cto/applied-tokens]
[:applied-tokens {:optional true} cto/schema:applied-tokens]
[:plugin-data {:optional true} ::ctpg/plugin-data]])
(def schema:group-attrs

View File

@@ -8,7 +8,6 @@
(:require
[app.common.data :as d]
[app.common.schema :as sm]
[app.common.schema.registry :as sr]
[clojure.data :as data]
[clojure.set :as set]
[malli.util :as mu]))
@@ -17,16 +16,10 @@
;; HELPERS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn merge-schemas
"Merge registered schemas."
[& schema-keys]
(let [schemas (map #(get @sr/registry %) schema-keys)]
(reduce sm/merge schemas)))
(defn schema-keys
(defn- schema-keys
"Converts registed map schema into set of keys."
[registered-schema]
(->> (get @sr/registry registered-schema)
[schema]
(->> schema
(sm/schema)
(mu/keys)
(into #{})))
@@ -40,7 +33,7 @@
:border-radius "borderRadius"
:color "color"
:dimensions "dimension"
:numeric "numeric"
:number "number"
:opacity "opacity"
:other "other"
:rotation "rotation"
@@ -55,95 +48,86 @@
(def token-types
(into #{} (keys token-type->dtcg-token-type)))
(defn valid-token-type?
[t]
(token-types t))
(def token-name-ref
[:and :string [:re #"^(?!\$)([a-zA-Z0-9-$_]+\.?)*(?<!\.)$"]])
(defn valid-token-name-ref?
[n]
(string? n))
(def ^:private schema:color
[:map
[:fill {:optional true} token-name-ref]
[:stroke-color {:optional true} token-name-ref]])
(sm/register!
^{::sm/type ::color}
[:map
[:fill {:optional true} token-name-ref]
[:stroke-color {:optional true} token-name-ref]])
(def color-keys (schema-keys schema:color))
(def color-keys (schema-keys ::color))
(def ^:private schema:border-radius
[:map
[:r1 {:optional true} token-name-ref]
[:r2 {:optional true} token-name-ref]
[:r3 {:optional true} token-name-ref]
[:r4 {:optional true} token-name-ref]])
(sm/register!
^{::sm/type ::border-radius}
[:map
[:r1 {:optional true} token-name-ref]
[:r2 {:optional true} token-name-ref]
[:r3 {:optional true} token-name-ref]
[:r4 {:optional true} token-name-ref]])
(def border-radius-keys (schema-keys schema:border-radius))
(def border-radius-keys (schema-keys ::border-radius))
(def ^:private schema:stroke-width
[:map
[:stroke-width {:optional true} token-name-ref]])
(sm/register!
^{::sm/type ::stroke-width}
[:map
[:stroke-width {:optional true} token-name-ref]])
(def stroke-width-keys (schema-keys schema:stroke-width))
(def stroke-width-keys (schema-keys ::stroke-width))
(def ^:private schema:sizing
[:map
[:width {:optional true} token-name-ref]
[:height {:optional true} token-name-ref]
[:layout-item-min-w {:optional true} token-name-ref]
[:layout-item-max-w {:optional true} token-name-ref]
[:layout-item-min-h {:optional true} token-name-ref]
[:layout-item-max-h {:optional true} token-name-ref]])
(sm/register!
^{::sm/type ::sizing}
[:map
[:width {:optional true} token-name-ref]
[:height {:optional true} token-name-ref]
[:layout-item-min-w {:optional true} token-name-ref]
[:layout-item-max-w {:optional true} token-name-ref]
[:layout-item-min-h {:optional true} token-name-ref]
[:layout-item-max-h {:optional true} token-name-ref]])
(def sizing-keys (schema-keys schema:sizing))
(def sizing-keys (schema-keys ::sizing))
(def ^:private schema:opacity
[:map
[:opacity {:optional true} token-name-ref]])
(sm/register!
^{::sm/type ::opacity}
[:map
[:opacity {:optional true} token-name-ref]])
(def opacity-keys (schema-keys schema:opacity))
(def opacity-keys (schema-keys ::opacity))
(def ^:private schema:spacing
[:map
[:row-gap {:optional true} token-name-ref]
[:column-gap {:optional true} token-name-ref]
[:p1 {:optional true} token-name-ref]
[:p2 {:optional true} token-name-ref]
[:p3 {:optional true} token-name-ref]
[:p4 {:optional true} token-name-ref]
[:m1 {:optional true} token-name-ref]
[:m2 {:optional true} token-name-ref]
[:m3 {:optional true} token-name-ref]
[:m4 {:optional true} token-name-ref]
[:x {:optional true} token-name-ref]
[:y {:optional true} token-name-ref]])
(sm/register!
^{::sm/type ::spacing}
[:map
[:row-gap {:optional true} token-name-ref]
[:column-gap {:optional true} token-name-ref]
[:p1 {:optional true} token-name-ref]
[:p2 {:optional true} token-name-ref]
[:p3 {:optional true} token-name-ref]
[:p4 {:optional true} token-name-ref]
[:m1 {:optional true} token-name-ref]
[:m2 {:optional true} token-name-ref]
[:m3 {:optional true} token-name-ref]
[:m4 {:optional true} token-name-ref]
[:x {:optional true} token-name-ref]
[:y {:optional true} token-name-ref]])
(def spacing-keys (schema-keys schema:spacing))
(def spacing-keys (schema-keys ::spacing))
(def ^:private schema:dimensions
[:merge
schema:sizing
schema:spacing
schema:stroke-width
schema:border-radius])
(sm/register!
^{::sm/type ::dimensions}
[:merge
::sizing
::spacing
::stroke-width
::border-radius])
(def dimensions-keys (schema-keys schema:dimensions))
(def dimensions-keys (schema-keys ::dimensions))
(def ^:private schema:rotation
[:map
[:rotation {:optional true} token-name-ref]])
(sm/register!
^{::sm/type ::rotation}
[:map
[:rotation {:optional true} token-name-ref]])
(def rotation-keys (schema-keys schema:rotation))
(def rotation-keys (schema-keys ::rotation))
(def ^:private schema:number
[:map
[:rotation {:optional true} token-name-ref]
[:line-height {:optional true} token-name-ref]])
(def number-keys (schema-keys schema:number))
(def all-keys (set/union color-keys
border-radius-keys
@@ -152,21 +136,21 @@
opacity-keys
spacing-keys
dimensions-keys
rotation-keys))
rotation-keys
number-keys))
(sm/register!
^{::sm/type ::tokens}
[:map {:title "Applied Tokens"}])
(def ^:private schema:tokens
[:map {:title "Applied Tokens"}])
(sm/register!
^{::sm/type ::applied-tokens}
[:merge
::tokens
::border-radius
::sizing
::spacing
::rotation
::dimensions])
(def schema:applied-tokens
[:merge
schema:tokens
schema:border-radius
schema:sizing
schema:spacing
schema:rotation
schema:number
schema:dimensions])
(defn shape-attr->token-attrs
([shape-attr] (shape-attr->token-attrs shape-attr nil))
@@ -197,7 +181,8 @@
(sizing-keys shape-attr) #{shape-attr}
(opacity-keys shape-attr) #{shape-attr}
(spacing-keys shape-attr) #{shape-attr}
(rotation-keys shape-attr) #{shape-attr})))
(rotation-keys shape-attr) #{shape-attr}
(number-keys shape-attr) #{shape-attr})))
(defn token-attr->shape-attr
[token-attr]

View File

@@ -29,7 +29,7 @@
:type :boolean
:value true)
token2 (ctob/make-token :name "test-token-2"
:type :numeric
:type :number
:value 66
:description "test description"
:modified-at now)]
@@ -42,7 +42,7 @@
(t/is (ctob/check-token token1))
(t/is (= (:name token2) "test-token-2"))
(t/is (= (:type token2) :numeric))
(t/is (= (:type token2) :number))
(t/is (= (:value token2) 66))
(t/is (= (:description token2) "test description"))
(t/is (= (:modified-at token2) now))

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 16 16">
<path d="M2 4.5h1v7m0 0H2m1 0h1m1-7h4V8H5.5v3.5h4m1-7H14V8m0 0h-3m3 0v3.5h-3.5"/>
</svg>

After

Width:  |  Height:  |  Size: 199 B

View File

@@ -55,8 +55,45 @@
{:value value :unit (tinycolor/color-format tc)}
{:errors [(wte/error-with-value :error.token/invalid-color value)]}))
(defn- parse-sd-token-numeric-value
"Parses `value` of a numeric `sd-token` into a map like `{:value 1 :unit \"px\"}`.
(defn- numeric-string? [s]
(and (string? s)
(re-matches #"^-?\d+(\.\d+)?$" s)))
(defn- with-units [s]
(and (string? s)
(re-matches #"^-?\d+(\.\d+)?(px|rem)$" s)))
;; TODO: After mergin "dimension-tokens" revisit this function to check if it's still
(defn- parse-sd-token-number-value
"Parses `value` of a number `sd-token` into a map like `{:value 1 :unit \"px\"}`.
If the `value` is not parseable and/or has missing references returns a map with `:errors`."
[value]
(let [number? (or (number? value)
(numeric-string? value))
parsed-value (cft/parse-token-value value)
out-of-bounds (or (>= (:value parsed-value) sm/max-safe-int)
(<= (:value parsed-value) sm/min-safe-int))]
(cond
(and parsed-value (not out-of-bounds) number?)
parsed-value
out-of-bounds
{:errors [(wte/error-with-value :error.token/number-too-large value)]}
(seq (ctob/find-token-value-references value))
(let [references (seq (ctob/find-token-value-references value))]
{:errors [(wte/error-with-value :error.style-dictionary/missing-reference references)]
:references references})
(with-units value)
{:errors [(wte/error-with-value :error.style-dictionary/value-with-units value)]}
:else
{:errors [(wte/error-with-value :error.style-dictionary/invalid-token-value value)]})))
(defn- parse-sd-token-general-value
"Parses `value` of a number `sd-token` into a map like `{:value 1 :unit \"px\"}`.
If the `value` is not parseable and/or has missing references returns a map with `:errors`."
[value]
(let [parsed-value (cft/parse-token-value value)
@@ -162,7 +199,8 @@
:color (parse-sd-token-color-value value)
:opacity (parse-sd-token-opacity-value value has-references?)
:stroke-width (parse-sd-token-stroke-width-value value has-references?)
(parse-sd-token-numeric-value value))
:number (parse-sd-token-number-value value)
(parse-sd-token-general-value value))
output-token (cond (:errors parsed-token-value)
(merge origin-token parsed-token-value)

View File

@@ -398,13 +398,6 @@
(rx/of (dwsh/update-shapes shape-ids update-fn))))))
(defn- update-text-content
[shape pred-fn update-fn attrs]
(let [update-attrs-fn #(update-fn % attrs)
transform #(txt/transform-nodes pred-fn update-attrs-fn %)]
(-> shape
(update :content transform))))
(defn update-root-attrs
[{:keys [id attrs]}]
(ptk/reify ::update-root-attrs
@@ -416,7 +409,7 @@
update-fn
(fn [shape]
(if (some? (:content shape))
(update-text-content shape txt/is-root-node? d/txt-merge attrs)
(txt/update-text-content shape txt/is-root-node? d/txt-merge attrs)
(assoc shape :content (d/txt-merge {:type "root"} attrs))))
shape-ids (cond (cfh/text-shape? shape) [id]
@@ -444,7 +437,7 @@
node
attrs))
update-fn #(update-text-content % txt/is-paragraph-node? merge-fn attrs)
update-fn #(txt/update-text-content % txt/is-paragraph-node? merge-fn attrs)
shape-ids (cond
(cfh/text-shape? shape) [id]
(cfh/group-shape? shape) (cfh/get-children-ids objects id))]
@@ -469,7 +462,7 @@
shape-ids (cond
(cfh/text-shape? shape) [id]
(cfh/group-shape? shape) (cfh/get-children-ids objects id))]
(rx/of (dwsh/update-shapes shape-ids #(update-text-content % update-node? d/txt-merge attrs))))))))
(rx/of (dwsh/update-shapes shape-ids #(txt/update-text-content % update-node? d/txt-merge attrs))))))))
(defn migrate-node
[node]

View File

@@ -9,6 +9,7 @@
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.tokens :as cft]
[app.common.text :as txt]
[app.common.types.shape.layout :as ctsl]
[app.common.types.shape.radius :as ctsr]
[app.common.types.token :as ctt]
@@ -42,34 +43,36 @@
(ptk/reify ::apply-token
ptk/WatchEvent
(watch [_ state _]
(when-let [tokens (some-> (dsh/lookup-file-data state)
(get :tokens-lib)
(ctob/get-active-themes-set-tokens))]
(->> (sd/resolve-tokens tokens)
(rx/mapcat
(fn [resolved-tokens]
(let [undo-id (js/Symbol)
objects (dsh/lookup-page-objects state)
;; We do not allow to apply tokens while text editor is open.
(when (empty? (get state :workspace-editor-state))
(when-let [tokens (some-> (dsh/lookup-file-data state)
(get :tokens-lib)
(ctob/get-active-themes-set-tokens))]
(->> (sd/resolve-tokens tokens)
(rx/mapcat
(fn [resolved-tokens]
(let [undo-id (js/Symbol)
objects (dsh/lookup-page-objects state)
shape-ids (or (->> (select-keys objects shape-ids)
(filter (fn [[_ shape]] (not= (:type shape) :group)))
(keys))
[])
shape-ids (or (->> (select-keys objects shape-ids)
(filter (fn [[_ shape]] (not= (:type shape) :group)))
(keys))
[])
resolved-value (get-in resolved-tokens [(cft/token-identifier token) :resolved-value])
tokenized-attributes (cft/attributes-map attributes token)]
(rx/of
(st/emit! (ptk/event ::ev/event {::ev/name "apply-tokens"}))
(dwu/start-undo-transaction undo-id)
(dwsh/update-shapes shape-ids (fn [shape]
(cond-> shape
attributes-to-remove
(update :applied-tokens #(apply (partial dissoc %) attributes-to-remove))
:always
(update :applied-tokens merge tokenized-attributes))))
(when on-update-shape
(on-update-shape resolved-value shape-ids attributes))
(dwu/commit-undo-transaction undo-id))))))))))
resolved-value (get-in resolved-tokens [(cft/token-identifier token) :resolved-value])
tokenized-attributes (cft/attributes-map attributes token)]
(rx/of
(st/emit! (ptk/event ::ev/event {::ev/name "apply-tokens"}))
(dwu/start-undo-transaction undo-id)
(dwsh/update-shapes shape-ids (fn [shape]
(cond-> shape
attributes-to-remove
(update :applied-tokens #(apply (partial dissoc %) attributes-to-remove))
:always
(update :applied-tokens merge tokenized-attributes))))
(when on-update-shape
(on-update-shape resolved-value shape-ids attributes))
(dwu/commit-undo-transaction undo-id)))))))))))
(defn unapply-token
"Removes `attributes` that match `token` for `shape-ids`.
@@ -328,7 +331,22 @@
(dwsl/update-layout-child shape-ids props {:ignore-touched true
:page-id page-id}))))))))
;; Map token types to different properties used along the cokde ---------------------------------------------------------
(defn update-line-height
([value shape-ids attributes] (update-line-height value shape-ids attributes nil))
([value shape-ids _attributes page-id] ; The attributes param is
; needed to have the same
; arity that other update
; functions
(let [update-node? (fn [node]
(or (txt/is-text-node? node)
(txt/is-paragraph-node? node)))]
(when (number? value)
(dwsh/update-shapes shape-ids
#(txt/update-text-content % update-node? d/txt-merge {:line-height value})
{:ignore-touched true
:page-id page-id})))))
;; Map token types to different properties used along the cokde ---------------------------------------------
;; FIXME: the values should be lazy evaluated, probably a function,
;; becasue on future we will need to translate that labels and that
@@ -390,6 +408,15 @@
:fields [{:label "Opacity"
:key :opacity}]}}
:number
{:title "Number"
:attributes ctt/rotation-keys
:all-attributes ctt/number-keys
:on-update-shape update-rotation
:modal {:key :tokens/number
:fields [{:label "Number"
:key :number}]}}
:rotation
{:title "Rotation"
:attributes ctt/rotation-keys

View File

@@ -56,6 +56,10 @@
{:error/code :error.style-dictionary/invalid-token-value
:error/fn #(str (tr "workspace.tokens.invalid-value" %))}
:error.style-dictionary/value-with-units
{:error/code :error.style-dictionary/value-with-units
:error/fn #(str (tr "workspace.tokens.value-with-units"))}
:error.style-dictionary/invalid-token-value-opacity
{:error/code :error.style-dictionary/invalid-token-value-opacity
:error/fn #(str/join "\n" [(str (tr "workspace.tokens.invalid-value" %) ".") (tr "workspace.tokens.opacity-range")])}

View File

@@ -32,6 +32,7 @@
ctt/stroke-width-keys dwta/update-stroke-width
ctt/sizing-keys dwta/update-shape-dimensions
ctt/opacity-keys dwta/update-opacity
#{:line-height} dwta/update-line-height
#{:x :y} dwta/update-shape-position
#{:p1 :p2 :p3 :p4} dwta/update-layout-padding
#{:m1 :m2 :m3 :m4} dwta/update-layout-item-margin
@@ -125,6 +126,7 @@
(defn- actionize-shapes-update-info [page-id shapes-update-info]
(mapcat (fn [[attrs update-infos]]
(let [action (some attribute-actions-map attrs)]
(assert (fn? action) "missing action function on attributes->shape-update")
(map
(fn [[v shape-ids]]
(action v shape-ids attrs page-id))

View File

@@ -12,3 +12,4 @@ $br-4: px2rem(4);
$br-circle: 50%;
$b-1: px2rem(1);
$b-2: px2rem(2);

View File

@@ -205,6 +205,7 @@
(def ^:icon-id msg-neutral "msg-neutral")
(def ^:icon-id msg-success "msg-success")
(def ^:icon-id msg-warning "msg-warning")
(def ^:icon-id number "number")
(def ^:icon-id open-link "open-link")
(def ^:icon-id padding-bottom "padding-bottom")
(def ^:icon-id padding-extended "padding-extended")

View File

@@ -182,6 +182,7 @@
(def ^:icon msg-neutral (icon-xref :msg-neutral))
(def ^:icon msg-success (icon-xref :msg-success))
(def ^:icon msg-warning (icon-xref :msg-warning))
(def ^:icon number (icon-xref :number))
(def ^:icon open-link (icon-xref :open-link))
(def ^:icon oauth-1 (icon-xref :oauth-1))
(def ^:icon oauth-2 (icon-xref :oauth-2))

View File

@@ -37,10 +37,14 @@
:selected-pred #(seq (% ids-by-attributes))}))
(defn generic-attribute-actions [attributes title {:keys [token selected-shapes on-update-shape hint]}]
(let [on-update-shape-fn (or on-update-shape
(-> (dwta/get-token-properties token)
(:on-update-shape)))
{:keys [selected-pred shape-ids]} (attribute-actions token selected-shapes attributes)]
(let [on-update-shape-fn
(or on-update-shape
(-> (dwta/get-token-properties token)
(:on-update-shape)))
{:keys [selected-pred shape-ids]}
(attribute-actions token selected-shapes attributes)]
(map (fn [attribute]
(let [selected? (selected-pred attribute)
props {:attributes #{attribute}
@@ -244,6 +248,9 @@
:sizing sizing-attribute-actions
:rotation (partial generic-attribute-actions #{:rotation} "Rotation")
:opacity (partial generic-attribute-actions #{:opacity} "Opacity")
:number (fn [context-data]
[(generic-attribute-actions #{:rotation} "Rotation" (assoc context-data :on-update-shape dwta/update-rotation))
(generic-attribute-actions #{:line-height} "Line Height" (assoc context-data :on-update-shape dwta/update-line-height))])
:stroke-width stroke-width
:dimensions (fn [context-data]
(concat
@@ -371,9 +378,12 @@
(mf/defc menu-tree
[{:keys [selected-shapes submenu-offset type errors] :as context-data}]
(let [shape-types (into #{} (map :type selected-shapes))
(let [shape-types (into #{} (map :type selected-shapes))
editing-ref (mf/deref refs/workspace-editor-state)
not-editing? (empty? editing-ref)
entries (if (and (not (some? errors))
(seq selected-shapes)
not-editing?
(not= shape-types #{:group}))
(if (some? type)
(submenu-actions-selection-actions context-data)

View File

@@ -138,9 +138,9 @@
[properties]
[:& token-update-create-modal properties])
(mf/defc numeric-modal
(mf/defc number-modal
{::mf/register modal/components
::mf/register-as :tokens/numeric}
::mf/register-as :tokens/number}
[properties]
[:& token-update-create-modal properties])

View File

@@ -56,6 +56,7 @@
:color "drop"
:boolean "boolean-difference"
:opacity "percentage"
:number "number"
:rotation "rotation"
:spacing "padding-extended"
:string "text-mixed"
@@ -70,6 +71,8 @@
[{:keys [type tokens selected-shapes active-theme-tokens is-open]}]
(let [{:keys [modal title]}
(get dwta/token-properties type)
editing-ref (mf/deref refs/workspace-editor-state)
not-editing? (empty? editing-ref)
can-edit?
(mf/use-ctx ctx/can-edit?)
@@ -111,10 +114,10 @@
on-token-pill-click
(mf/use-fn
(mf/deps selected-shapes)
(mf/deps selected-shapes not-editing?)
(fn [event token]
(dom/stop-propagation event)
(when (seq selected-shapes)
(when (and not-editing? (seq selected-shapes))
(st/emit! (dwta/toggle-token {:token token
:shapes selected-shapes})))))]
@@ -129,8 +132,7 @@
[:> icon-button* {:on-click on-popover-open-click
:variant "ghost"
:icon "add"
;; TODO: This needs translation
:aria-label (str "Add token: " title)}])]
:aria-label (tr "workspace.tokens.add-token" title)}])]
(when is-open
[:& cmm/asset-section-block {:role :content}
[:div {:class (stl/css :token-pills-wrapper)}
@@ -145,22 +147,27 @@
(defn- get-sorted-token-groups
"Separate token-types into groups of `empty` or `filled` depending if
tokens exist for that type. Sort each group alphabetically (by
their type)."
tokens exist for that type. Sort each group alphabetically (by their type).
If `:token-units` is not in cf/flags, number tokens are excluded."
[tokens-by-type]
(loop [empty #js []
filled #js []
types (-> dwta/token-properties keys seq)]
(if-let [type (first types)]
(if (not-empty (get tokens-by-type type))
(recur empty
(array/conj! filled type)
(rest types))
(recur (array/conj! empty type)
filled
(rest types)))
[(seq (array/sort! empty))
(seq (array/sort! filled))])))
(let [all-types (-> dwta/token-properties keys seq)
token-units? (contains? cf/flags :token-units)
filtered-types (if token-units?
all-types
(remove #(= % :number) all-types))]
(loop [empty #js []
filled #js []
types filtered-types]
(if-let [type (first types)]
(if (not-empty (get tokens-by-type type))
(recur empty
(array/conj! filled type)
(rest types))
(recur (array/conj! empty type)
filled
(rest types)))
[(seq (array/sort! empty))
(seq (array/sort! filled))]))))
(mf/defc themes-header*
{::mf/private true}

View File

@@ -165,14 +165,14 @@
(mf/defc token-pill*
{::mf/wrap [mf/memo]}
[{:keys [on-click token on-context-menu selected-shapes active-theme-tokens]}]
(let [{:keys [name value errors]} token
(let [{:keys [name value errors type]} token
has-selected? (pos? (count selected-shapes))
is-reference? (cft/is-reference? token)
contains-path? (str/includes? name ".")
{:keys [attributes all-attributes]}
(get dwta/token-properties (:type token))
(get dwta/token-properties type)
full-applied?
(if has-selected?
@@ -204,10 +204,12 @@
color
(when (cft/color-token? token)
(let [theme-token (get active-theme-tokens (:name token))]
(let [theme-token (get active-theme-tokens name)]
(or (dwtc/resolved-token-bullet-color theme-token)
(dwtc/resolved-token-bullet-color token))))
number-token (= type :number)
on-click
(mf/use-fn
(mf/deps errors? on-click token)
@@ -240,18 +242,20 @@
(dom/stop-propagation event)
(when (and can-edit? (not (seq errors)) on-click)
(on-click event))))
on-hover
(mf/use-fn
(mf/deps selected-shapes is-viewer? active-theme-tokens token half-applied? no-valid-value ref-not-in-active-set)
(fn [event]
(let [node (dom/get-current-target event)
theme-token (get active-theme-tokens (:name token))
theme-token (get active-theme-tokens name)
title (generate-tooltip is-viewer? (first selected-shapes) theme-token token
half-applied? no-valid-value ref-not-in-active-set)]
(dom/set-attribute! node "title" title))))]
[:button {:class (stl/css-case
:token-pill true
:token-pill-no-icon (and number-token (not errors?))
:token-pill-default can-edit?
:token-pill-applied (and can-edit? has-selected? (or half-applied? full-applied?))
:token-pill-invalid (and can-edit? errors?)
@@ -276,13 +280,12 @@
{:icon-id "broken-link"
:class (stl/css :token-pill-icon)}]
color
[:& color-bullet {:color color
:mini true}]
:else
[:> token-status-icon*
{:icon-id token-status-id
:class (stl/css :token-pill-icon)}])
(not number-token)
(if color
[:& color-bullet {:color color :mini true}]
[:> token-status-icon*
{:icon-id token-status-id
:class (stl/css :token-pill-icon)}]))
(if contains-path?
(let [[first-part last-part] (cfh/split-by-last-period name)]

View File

@@ -5,6 +5,7 @@
// Copyright (c) KALEIDOS INC
@use "../../ds/typography.scss" as *;
@use "../../ds/borders.scss" as *;
@import "refactor/common-refactor.scss";
.token-pill {
@@ -16,15 +17,20 @@
grid-template-columns: auto 1fr;
align-items: center;
gap: $s-6;
border: $s-1 solid var(--token-pill-border);
outline: $s-2 solid var(--token-pill-outline);
height: $s-24;
border: $b-1 solid var(--token-pill-border);
outline: $b-2 solid var(--token-pill-outline);
height: var(--sp-xxl);
border-radius: $br-8;
padding: $s-2 $s-8 $s-2 $s-4;
padding: var(--sp-xxs) var(--sp-s) var(--sp-xxs) var(--sp-xs);
color: var(--token-pill-foreground);
background: var(--token-pill-background);
}
.token-pill-no-icon {
grid-template-columns: 1fr;
padding: var(--sp-xxs) var(--sp-s);
}
.name-wrapper {
@include use-typography("code-font");
display: block;

View File

@@ -9,9 +9,11 @@
[app.common.test-helpers.compositions :as ctho]
[app.common.test-helpers.files :as cthf]
[app.common.test-helpers.shapes :as cths]
[app.common.text :as txt]
[app.common.types.tokens-lib :as ctob]
[app.main.data.workspace.tokens.application :as dwta]
[cljs.test :as t :include-macros true]
[cuerdas.core :as str]
[frontend-tests.helpers.pages :as thp]
[frontend-tests.helpers.state :as ths]
[frontend-tests.tokens.helpers.state :as tohs]
@@ -39,6 +41,7 @@
(ctho/add-rect :rect-1 rect-1)
(ctho/add-rect :rect-2 rect-2)
(ctho/add-rect :rect-3 rect-3)
(ctho/add-text :text-1 "Hello World!")
(assoc-in [:data :tokens-lib]
(-> (ctob/make-tokens-lib)
(ctob/add-theme (ctob/make-token-theme :name "Theme A" :sets #{"Set A"}))
@@ -443,6 +446,40 @@
(t/is (= (:stroke-width (:applied-tokens rect-without-stroke')) (:name token-target')))
(t/is (empty? (:strokes rect-without-stroke')))))))))))
(t/deftest test-apply-line-height
(t/testing "applies line-height token and updates the text line-height"
(t/async
done
(let [line-height-token {:name "big-height"
:value "1.5"
:type :number}
file (-> (setup-file-with-tokens)
(update-in [:data :tokens-lib]
#(ctob/add-token-in-set % "Set A" (ctob/make-token line-height-token))))
store (ths/setup-store file)
text-1 (cths/get-shape file :text-1)
events [(dwta/apply-token {:shape-ids [(:id text-1)]
:attributes #{:line-height}
:token (toht/get-token file "big-height")
:on-update-shape dwta/update-line-height})]]
(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' "big-height")
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 (= (:line-height (:applied-tokens text-1')) (:name token-target')))
(t/is (= (:line-height style-text-blocks) 1.5)))))))))
(t/deftest test-toggle-token-none
(t/testing "should apply token to all selected items, where no item has the token applied"
(t/async

View File

@@ -7282,6 +7282,10 @@ msgstr ""
msgid "workspace.tokens.invalid-value"
msgstr "Invalid token value: %s"
#: src/app/main/data/workspace/tokens/errors.cljs:57
msgid "workspace.tokens.value-with-units"
msgstr "Invalid value: Units are not allowed."
#: src/app/main/ui/workspace/tokens/modals/themes.cljs:190
msgid "workspace.tokens.label.group"
msgstr "Group"
@@ -7497,6 +7501,10 @@ msgstr "The value is not valid"
msgid "workspace.tokens.warning-name-change"
msgstr "Renaming this token will break any reference to its old name."
#: src/app/main/ui/workspace/tokens/sidebar.cljs
msgid "workspace.tokens.add-token"
msgstr "Add token: %s"
#: src/app/main/ui/workspace/sidebar.cljs:137, src/app/main/ui/workspace/sidebar.cljs:146
msgid "workspace.toolbar.assets"
msgstr "Assets"

View File

@@ -7298,6 +7298,10 @@ msgstr ""
msgid "workspace.tokens.invalid-value"
msgstr "Valor de token no válido: %s"
#: src/app/main/data/workspace/tokens/errors.cljs:57
msgid "workspace.tokens.value-with-units"
msgstr "Valor no válido: No se permiten unidades."
#: src/app/main/ui/workspace/tokens/modals/themes.cljs:190
msgid "workspace.tokens.label.group"
msgstr "Grupo"
@@ -7476,6 +7480,10 @@ msgstr "La importación se ha realizado correctamente pero se omitieron algunos
msgid "workspace.tokens.unknown-token-type-section"
msgstr "El tipo '%s' no está soportado (%s)\n"
#: src/app/main/ui/workspace/tokens/sidebar.cljs
msgid "workspace.tokens.add-token"
msgstr "Añadir token: %s"
#: src/app/main/ui/workspace/sidebar.cljs:137, src/app/main/ui/workspace/sidebar.cljs:146
msgid "workspace.toolbar.assets"
msgstr "Recursos"