mirror of
https://github.com/penpot/penpot.git
synced 2025-07-31 10:18:28 +02:00
✨ Implement new token-type :font-families
This commit is contained in:
parent
2cddc6fb5b
commit
d788a4d252
16 changed files with 544 additions and 196 deletions
|
@ -10,6 +10,7 @@
|
|||
[app.common.schema :as sm]
|
||||
[clojure.data :as data]
|
||||
[clojure.set :as set]
|
||||
[cuerdas.core :as str]
|
||||
[malli.util :as mu]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -29,20 +30,21 @@
|
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def token-type->dtcg-token-type
|
||||
{:boolean "boolean"
|
||||
:border-radius "borderRadius"
|
||||
:color "color"
|
||||
:dimensions "dimension"
|
||||
:font-size "fontSizes"
|
||||
{:boolean "boolean"
|
||||
:border-radius "borderRadius"
|
||||
:color "color"
|
||||
:dimensions "dimension"
|
||||
:font-family "fontFamilies"
|
||||
:font-size "fontSizes"
|
||||
:letter-spacing "letterSpacing"
|
||||
:number "number"
|
||||
:opacity "opacity"
|
||||
:other "other"
|
||||
:rotation "rotation"
|
||||
:sizing "sizing"
|
||||
:spacing "spacing"
|
||||
:string "string"
|
||||
:stroke-width "strokeWidth"})
|
||||
: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))
|
||||
|
@ -133,7 +135,13 @@
|
|||
|
||||
(def letter-spacing-keys (schema-keys schema:letter-spacing))
|
||||
|
||||
(def typography-keys (set/union font-size-keys letter-spacing-keys))
|
||||
(def ^:private schema:font-family
|
||||
[:map
|
||||
[:font-family {:optional true} token-name-ref]])
|
||||
|
||||
(def font-family-keys (schema-keys schema:font-family))
|
||||
|
||||
(def typography-keys (set/union font-size-keys letter-spacing-keys font-family-keys))
|
||||
|
||||
;; TODO: Created to extract the font-size feature from the typography feature flag.
|
||||
;; Delete this once the typography feature flag is removed.
|
||||
|
@ -169,6 +177,7 @@
|
|||
schema:number
|
||||
schema:font-size
|
||||
schema:letter-spacing
|
||||
schema:font-family
|
||||
schema:dimensions])
|
||||
|
||||
(defn shape-attr->token-attrs
|
||||
|
@ -198,6 +207,7 @@
|
|||
|
||||
(font-size-keys shape-attr) #{shape-attr}
|
||||
(letter-spacing-keys shape-attr) #{shape-attr}
|
||||
(font-family-keys shape-attr) #{shape-attr}
|
||||
(border-radius-keys shape-attr) #{shape-attr}
|
||||
(sizing-keys shape-attr) #{shape-attr}
|
||||
(opacity-keys shape-attr) #{shape-attr}
|
||||
|
@ -291,3 +301,23 @@
|
|||
|
||||
(defn unapply-token-id [shape attributes]
|
||||
(update shape :applied-tokens d/without-keys attributes))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; TYPOGRAPHY
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn split-font-family
|
||||
"Splits font family `value` string from into vector of font families.
|
||||
|
||||
Doesn't handle possible edge-case of font-families with `,` in their font family name."
|
||||
[font-value]
|
||||
(let [families (str/split font-value ",")
|
||||
xform (comp
|
||||
(map str/trim)
|
||||
(remove str/empty?))]
|
||||
(into [] xform families)))
|
||||
|
||||
(defn join-font-family
|
||||
"Joins font family `value` into a string to be edited with a single input."
|
||||
[font-families]
|
||||
(str/join ", " font-families))
|
||||
|
|
|
@ -1333,13 +1333,25 @@ Will return a value that matches this schema:
|
|||
(walk/postwalk
|
||||
(fn [node]
|
||||
(cond-> node
|
||||
;; Handle sequential values that are objects with type
|
||||
(and (map? node)
|
||||
(contains? node "value")
|
||||
(sequential? (get node "value")))
|
||||
(sequential? (get node "value"))
|
||||
(map? (first (get node "value"))))
|
||||
(update "value"
|
||||
(fn [seq-value]
|
||||
(map #(set/rename-keys % {"type" "$type"}) seq-value)))
|
||||
|
||||
;; Keep array of font families
|
||||
(and (map? node)
|
||||
(contains? node "type")
|
||||
(= "fontFamilies" (get node "type"))
|
||||
(contains? node "value")
|
||||
(sequential? (get node "value"))
|
||||
(not (map? (first (get node "value")))))
|
||||
identity
|
||||
|
||||
;; Rename keys for all token nodes
|
||||
(and (map? node)
|
||||
(and (contains? node "type")
|
||||
(contains? node "value")))
|
||||
|
@ -1371,7 +1383,16 @@ Will return a value that matches this schema:
|
|||
(assoc tokens child-path (make-token
|
||||
:name child-path
|
||||
:type token-type
|
||||
:value (get v "$value")
|
||||
:value (cond-> (get v "$value")
|
||||
;; Split string of font-families
|
||||
(and (= :font-family token-type)
|
||||
(string? (get v "$value")))
|
||||
cto/split-font-family
|
||||
|
||||
;; Keep array of font-families
|
||||
(and (= :font-family token-type)
|
||||
(sequential? (get v "$value")))
|
||||
identity)
|
||||
:description (get v "$description")))
|
||||
;; Discard unknown type tokens
|
||||
tokens)))))
|
||||
|
|
|
@ -62,7 +62,11 @@
|
|||
(ctob/add-token-in-set "test-token-set"
|
||||
(ctob/make-token :name "token-letter-spacing"
|
||||
:type :letter-spacing
|
||||
:value 2))))
|
||||
:value 2))
|
||||
(ctob/add-token-in-set "test-token-set"
|
||||
(ctob/make-token :name "token-font-family"
|
||||
:type :font-family
|
||||
:value ["Helvetica" "Arial" "sans-serif"]))))
|
||||
(tho/add-frame :frame1)
|
||||
(tho/add-text :text1 "Hello World!")))
|
||||
|
||||
|
@ -77,7 +81,8 @@
|
|||
(tht/apply-token-to-shape :frame1 "token-color" [:fill] [:fill] "#00ff00")
|
||||
(tht/apply-token-to-shape :frame1 "token-dimensions" [:width :height] [:width :height] 100)
|
||||
(tht/apply-token-to-shape :text1 "token-font-size" [:font-size] [:font-size] 24)
|
||||
(tht/apply-token-to-shape :text1 "token-letter-spacing" [:letter-spacing] [:letter-spacing] 2)))
|
||||
(tht/apply-token-to-shape :text1 "token-letter-spacing" [:letter-spacing] [:letter-spacing] 2)
|
||||
(tht/apply-token-to-shape :text1 "token-font-family" [:font-family] [:font-family] ["Helvetica" "Arial" "sans-serif"])))
|
||||
|
||||
(t/deftest apply-tokens-to-shape
|
||||
(let [;; ==== Setup
|
||||
|
@ -93,6 +98,7 @@
|
|||
token-dimensions (tht/get-token file "test-token-set" "token-dimensions")
|
||||
token-font-size (tht/get-token file "test-token-set" "token-font-size")
|
||||
token-letter-spacing (tht/get-token file "test-token-set" "token-letter-spacing")
|
||||
token-font-family (tht/get-token file "test-token-set" "token-font-family")
|
||||
|
||||
;; ==== Action
|
||||
changes (-> (-> (pcb/empty-changes nil)
|
||||
|
@ -132,7 +138,10 @@
|
|||
:attributes [:font-size]})
|
||||
(cto/apply-token-to-shape {:token token-letter-spacing
|
||||
:shape $
|
||||
:attributes [:letter-spacing]})))
|
||||
:attributes [:letter-spacing]})
|
||||
(cto/apply-token-to-shape {:token token-font-family
|
||||
:shape $
|
||||
:attributes [:font-family]})))
|
||||
(:objects page)
|
||||
{}))
|
||||
|
||||
|
@ -157,9 +166,10 @@
|
|||
(t/is (= (:fill applied-tokens') "token-color"))
|
||||
(t/is (= (:width applied-tokens') "token-dimensions"))
|
||||
(t/is (= (:height applied-tokens') "token-dimensions"))
|
||||
(t/is (= (count text1-applied-tokens) 2))
|
||||
(t/is (= (count text1-applied-tokens) 3))
|
||||
(t/is (= (:font-size text1-applied-tokens) "token-font-size"))
|
||||
(t/is (= (:letter-spacing text1-applied-tokens) "token-letter-spacing"))))
|
||||
(t/is (= (:letter-spacing text1-applied-tokens) "token-letter-spacing"))
|
||||
(t/is (= (:font-family text1-applied-tokens) "token-font-family"))))
|
||||
|
||||
(t/deftest unapply-tokens-from-shape
|
||||
(let [;; ==== Setup
|
||||
|
@ -189,7 +199,8 @@
|
|||
(fn [shape]
|
||||
(-> shape
|
||||
(cto/unapply-token-id [:font-size])
|
||||
(cto/unapply-token-id [:letter-spacing])))
|
||||
(cto/unapply-token-id [:letter-spacing])
|
||||
(cto/unapply-token-id [:font-family])))
|
||||
(:objects page)
|
||||
{}))
|
||||
|
||||
|
@ -240,7 +251,8 @@
|
|||
d/txt-merge
|
||||
{:fills (ths/sample-fills-color :fill-color "#fabada")
|
||||
:font-size "1"
|
||||
:letter-spacing "0"}))
|
||||
:letter-spacing "0"
|
||||
:font-family "Arial"}))
|
||||
(:objects page)
|
||||
{}))
|
||||
|
||||
|
|
|
@ -195,6 +195,7 @@
|
|||
value (.-value sd-token)
|
||||
has-references? (str/includes? (:value origin-token) "{")
|
||||
parsed-token-value (case (:type origin-token)
|
||||
:font-family {:value (-> (js->clj value) (flatten))}
|
||||
: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?)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
[app.common.types.token :as ctt]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.common.types.typography :as cty]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.data.helpers :as dsh]
|
||||
[app.main.data.style-dictionary :as sd]
|
||||
|
@ -24,6 +25,7 @@
|
|||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.transforms :as dwtr]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.fonts :as fonts]
|
||||
[app.main.store :as st]
|
||||
[beicon.v2.core :as rx]
|
||||
[clojure.set :as set]
|
||||
|
@ -364,6 +366,26 @@
|
|||
{:ignore-touched true
|
||||
:page-id page-id})))))
|
||||
|
||||
(defn update-font-family
|
||||
([value shape-ids attributes] (update-font-family value shape-ids attributes nil))
|
||||
([value shape-ids _attributes page-id]
|
||||
(let [font-family (first value)
|
||||
font (some-> font-family
|
||||
(fonts/find-font-family))
|
||||
text-attrs (if font
|
||||
{:font-id (:id font)
|
||||
:font-family (:family font)}
|
||||
{:font-id (str uuid/zero)
|
||||
:font-family font-family})
|
||||
update-node? (fn [node]
|
||||
(or (txt/is-text-node? node)
|
||||
(txt/is-paragraph-node? node)))]
|
||||
(when text-attrs
|
||||
(dwsh/update-shapes shape-ids
|
||||
#(txt/update-text-content % update-node? d/txt-merge text-attrs)
|
||||
{:ignore-touched true
|
||||
:page-id page-id})))))
|
||||
|
||||
(defn update-font-size
|
||||
([value shape-ids attributes] (update-font-size value shape-ids attributes nil))
|
||||
([value shape-ids _attributes page-id]
|
||||
|
@ -421,6 +443,14 @@
|
|||
:fields [{:label "Letter Spacing"
|
||||
:key :letter-spacing}]}}
|
||||
|
||||
:font-family
|
||||
{:title "Font Family"
|
||||
:attributes ctt/font-family-keys
|
||||
:on-update-shape update-font-family
|
||||
:modal {:key :tokens/font-family
|
||||
:fields [{:label "Font Family"
|
||||
:key :font-family}]}}
|
||||
|
||||
:stroke-width
|
||||
{:title "Stroke Width"
|
||||
:attributes ctt/stroke-width-keys
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#{:line-height} dwta/update-line-height
|
||||
#{:font-size} dwta/update-font-size
|
||||
#{:letter-spacing} dwta/update-letter-spacing
|
||||
#{:font-family} dwta/update-font-family
|
||||
#{:x :y} dwta/update-shape-position
|
||||
#{:p1 :p2 :p3 :p4} dwta/update-layout-padding
|
||||
#{:m1 :m2 :m3 :m4} dwta/update-layout-item-margin
|
||||
|
|
|
@ -78,6 +78,15 @@
|
|||
data))
|
||||
(vals @fontsdb)))
|
||||
|
||||
(defn find-font-family
|
||||
"Case insensitive lookup of font-family."
|
||||
[family]
|
||||
(let [family' (str/lower family)]
|
||||
(d/seek
|
||||
(fn [{:keys [family]}]
|
||||
(= family' (str/lower family)))
|
||||
(vals @fontsdb))))
|
||||
|
||||
(defn resolve-variants
|
||||
[id]
|
||||
(get-in @fontsdb [id :variants]))
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
[:class {:optional true} :string]
|
||||
[:id :string]
|
||||
[:icon {:optional true}
|
||||
[:and :string [:fn #(contains? icon-list %)]]]
|
||||
[:maybe [:and :string [:fn #(contains? icon-list %)]]]]
|
||||
[:has-hint {:optional true} :boolean]
|
||||
[:hint-type {:optional true} [:maybe [:enum "hint" "error" "warning"]]]
|
||||
[:type {:optional true} :string]
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
[app.common.data.macros :as dm]
|
||||
[app.common.files.tokens :as cft]
|
||||
[app.common.types.color :as c]
|
||||
[app.common.types.token :as ctt]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.main.constants :refer [max-input-length]]
|
||||
[app.main.data.modal :as modal]
|
||||
|
@ -21,9 +22,11 @@
|
|||
[app.main.data.workspace.tokens.library-edit :as dwtl]
|
||||
[app.main.data.workspace.tokens.propagation :as dwtp]
|
||||
[app.main.data.workspace.tokens.warnings :as wtw]
|
||||
[app.main.fonts :as fonts]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.ds.buttons.button :refer [button*]]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.controls.input :refer [input*]]
|
||||
[app.main.ui.ds.controls.utilities.hint-message :refer [hint-message*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
|
@ -31,6 +34,8 @@
|
|||
[app.main.ui.ds.notifications.context-notification :refer [context-notification*]]
|
||||
[app.main.ui.workspace.colorpicker :as colorpicker]
|
||||
[app.main.ui.workspace.colorpicker.ramp :refer [ramp-selector*]]
|
||||
[app.main.ui.workspace.sidebar.options.menus.typography :refer [font-selector*]]
|
||||
[app.main.ui.workspace.tokens.management.create.input-token-color-bullet :refer [input-token-color-bullet*]]
|
||||
[app.main.ui.workspace.tokens.management.create.input-tokens-value :refer [input-tokens-value*]]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.functions :as uf]
|
||||
|
@ -94,7 +99,17 @@
|
|||
(defn valid-value? [value]
|
||||
(seq (finalize-value value)))
|
||||
|
||||
;; Component -------------------------------------------------------------------
|
||||
;; Validation ------------------------------------------------------------------
|
||||
|
||||
(defn validate-empty-input [value]
|
||||
(if (sequential? value)
|
||||
(empty? value)
|
||||
(empty? (str/trim value))))
|
||||
|
||||
(defn validate-self-reference? [token-name value]
|
||||
(if (sequential? value)
|
||||
(some #(ctob/token-value-self-reference? token-name %) value)
|
||||
(ctob/token-value-self-reference? token-name value)))
|
||||
|
||||
(defn validate-token-value
|
||||
"Validates token value by resolving the value `input` using `StyleDictionary`.
|
||||
|
@ -104,17 +119,19 @@
|
|||
;; so we use a temporary token name that hopefully doesn't clash with any of the users token names
|
||||
token-name (if (str/empty? name-value) "__TOKEN_STUDIO_SYSTEM.TEMP" name-value)]
|
||||
(cond
|
||||
(empty? (str/trim value))
|
||||
(validate-empty-input value)
|
||||
(rx/throw {:errors [(wte/get-error-code :error.token/empty-input)]})
|
||||
|
||||
(ctob/token-value-self-reference? token-name value)
|
||||
(validate-self-reference? token-name value)
|
||||
(rx/throw {:errors [(wte/get-error-code :error.token/direct-self-reference)]})
|
||||
|
||||
:else
|
||||
(let [tokens' (cond-> tokens
|
||||
;; Remove previous token when renaming a token
|
||||
(not= name-value (:name token)) (dissoc (:name token))
|
||||
:always (update token-name #(ctob/make-token (merge % {:value value
|
||||
:always (update token-name #(ctob/make-token (merge % {:value (cond
|
||||
(= (:type token) :font-family) (ctt/split-font-family value)
|
||||
:else value)
|
||||
:name token-name
|
||||
:type (:type token)}))))]
|
||||
(->> tokens'
|
||||
|
@ -156,59 +173,7 @@
|
|||
|
||||
(defonce form-token-cache-atom (atom nil))
|
||||
|
||||
;; FIXME: this function has confusing name
|
||||
(defn- hex->value
|
||||
[hex]
|
||||
(when-let [tc (tinycolor/valid-color hex)]
|
||||
(let [hex (tinycolor/->hex-string tc)
|
||||
alpha (tinycolor/alpha tc)
|
||||
[r g b] (c/hex->rgb hex)
|
||||
[h s v] (c/hex->hsv hex)]
|
||||
{:hex hex
|
||||
:r r :g g :b b
|
||||
:h h :s s :v v
|
||||
:alpha alpha})))
|
||||
|
||||
(mf/defc ramp*
|
||||
[{:keys [color on-change]}]
|
||||
(let [wrapper-node-ref (mf/use-ref nil)
|
||||
dragging-ref (mf/use-ref false)
|
||||
|
||||
on-start-drag
|
||||
(mf/use-fn #(mf/set-ref-val! dragging-ref true))
|
||||
|
||||
on-finish-drag
|
||||
(mf/use-fn #(mf/set-ref-val! dragging-ref false))
|
||||
|
||||
internal-color*
|
||||
(mf/use-state #(hex->value color))
|
||||
|
||||
internal-color
|
||||
(deref internal-color*)
|
||||
|
||||
on-change'
|
||||
(mf/use-fn
|
||||
(mf/deps on-change)
|
||||
(fn [{:keys [hex alpha] :as selector-color}]
|
||||
(let [dragging? (mf/ref-val dragging-ref)]
|
||||
(when-not (and dragging? hex)
|
||||
(reset! internal-color* selector-color)
|
||||
(on-change hex alpha)))))]
|
||||
(mf/use-effect
|
||||
(mf/deps color)
|
||||
(fn []
|
||||
;; Update internal color when user changes input value
|
||||
(when-let [color (tinycolor/valid-color color)]
|
||||
(when-not (= (tinycolor/->hex-string color) (:hex internal-color))
|
||||
(reset! internal-color* (hex->value color))))))
|
||||
|
||||
(colorpicker/use-color-picker-css-variables! wrapper-node-ref internal-color)
|
||||
[:div {:ref wrapper-node-ref}
|
||||
[:> ramp-selector*
|
||||
{:color internal-color
|
||||
:on-start-drag on-start-drag
|
||||
:on-finish-drag on-finish-drag
|
||||
:on-change on-change'}]]))
|
||||
;; Component -------------------------------------------------------------------
|
||||
|
||||
(mf/defc token-value-hint
|
||||
[{:keys [result]}]
|
||||
|
@ -232,13 +197,11 @@
|
|||
:class (stl/css-case :resolved-value (not (or empty-message? (seq warnings) (seq errors))))
|
||||
:type type}]))
|
||||
|
||||
(mf/defc form
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [token token-type action selected-token-set-name on-display-colorpicker]}]
|
||||
(mf/defc form*
|
||||
[{:keys [token token-type action selected-token-set-name transform-value on-value-resolve custom-input-token-value custom-input-token-value-props]}]
|
||||
(let [create? (not (instance? ctob/Token token))
|
||||
token (or token {:type token-type})
|
||||
token-properties (dwta/get-token-properties token)
|
||||
is-color-token (cft/color-token? token)
|
||||
tokens-in-selected-set (mf/deref refs/workspace-all-tokens-in-selected-set)
|
||||
|
||||
active-theme-tokens (cond-> (mf/deref refs/workspace-active-theme-sets-tokens)
|
||||
|
@ -246,7 +209,11 @@
|
|||
;; even if the name has been overriden by a token with the same name
|
||||
;; in another set below.
|
||||
(and (:name token) (:value token))
|
||||
(assoc (:name token) token))
|
||||
(assoc (:name token) token)
|
||||
|
||||
;; Style dictionary resolver needs font families to be an array of strings
|
||||
(= :font-family (or (:type token) token-type))
|
||||
(update-in [(:name token) :value] ctt/split-font-family))
|
||||
|
||||
resolved-tokens (sd/use-resolved-tokens active-theme-tokens {:cache-atom form-token-cache-atom
|
||||
:interactive? true})
|
||||
|
@ -260,13 +227,6 @@
|
|||
(-> (ctob/tokens-tree tokens-in-selected-set)
|
||||
;; Allow setting editing token to it's own path
|
||||
(d/dissoc-in token-path))))
|
||||
cancel-ref (mf/use-ref nil)
|
||||
|
||||
on-cancel-ref
|
||||
(mf/use-fn
|
||||
(fn [node]
|
||||
(mf/set-ref-val! cancel-ref node)))
|
||||
|
||||
;; Name
|
||||
touched-name* (mf/use-state false)
|
||||
touched-name? (deref touched-name*)
|
||||
|
@ -286,16 +246,13 @@
|
|||
|
||||
on-blur-name
|
||||
(mf/use-fn
|
||||
(mf/deps cancel-ref touched-name? warning-name-change?)
|
||||
(mf/deps touched-name? warning-name-change?)
|
||||
(fn [e]
|
||||
(let [node (dom/get-related-target e)
|
||||
on-cancel-btn (= node (mf/ref-val cancel-ref))]
|
||||
(when-not on-cancel-btn
|
||||
(let [value (dom/get-target-val e)
|
||||
errors (validate-name value)]
|
||||
(when touched-name?
|
||||
(reset! warning-name-change* true))
|
||||
(reset! name-errors errors))))))
|
||||
(let [value (dom/get-target-val e)
|
||||
errors (validate-name value)]
|
||||
(when touched-name?
|
||||
(reset! warning-name-change* true))
|
||||
(reset! name-errors errors))))
|
||||
|
||||
on-update-name-debounced
|
||||
(mf/use-fn
|
||||
|
@ -321,10 +278,6 @@
|
|||
(valid-name? @token-name-ref))
|
||||
|
||||
;; Value
|
||||
color* (mf/use-state (when is-color-token (:value token)))
|
||||
color (deref color*)
|
||||
color-ramp-open* (mf/use-state false)
|
||||
color-ramp-open? (deref color-ramp-open*)
|
||||
value-input-ref (mf/use-ref nil)
|
||||
value-ref (mf/use-ref (:value token))
|
||||
|
||||
|
@ -333,68 +286,46 @@
|
|||
|
||||
set-resolve-value
|
||||
(mf/use-fn
|
||||
(mf/deps on-value-resolve)
|
||||
(fn [token-or-err]
|
||||
(let [error? (:errors token-or-err)
|
||||
warnings? (:warnings token-or-err)
|
||||
v (cond
|
||||
error?
|
||||
token-or-err
|
||||
(do
|
||||
(when on-value-resolve (on-value-resolve nil))
|
||||
token-or-err)
|
||||
|
||||
warnings?
|
||||
(:warnings {:warnings token-or-err})
|
||||
|
||||
:else
|
||||
(:resolved-value token-or-err))]
|
||||
(when is-color-token (reset! color* (if error? nil v)))
|
||||
(cond-> (:resolved-value token-or-err)
|
||||
on-value-resolve on-value-resolve))]
|
||||
(reset! token-resolve-result* v))))
|
||||
|
||||
on-update-value-debounced (use-debonced-resolve-callback token-name-ref token active-theme-tokens set-resolve-value)
|
||||
on-update-value (mf/use-fn
|
||||
(mf/deps on-update-value-debounced)
|
||||
(fn [e]
|
||||
(let [value (dom/get-target-val e)
|
||||
;; Automatically add # for hex values
|
||||
value' (if (and is-color-token (tinycolor/hex-without-hash-prefix? value))
|
||||
(let [hex (dm/str "#" value)]
|
||||
(dom/set-value! (mf/ref-val value-input-ref) hex)
|
||||
hex)
|
||||
value)]
|
||||
(mf/set-ref-val! value-ref value')
|
||||
(on-update-value-debounced value'))))
|
||||
on-update-color (mf/use-fn
|
||||
(mf/deps color on-update-value-debounced)
|
||||
(fn [hex-value alpha]
|
||||
(let [;; StyleDictionary will always convert to hex/rgba, so we take the format from the value input field
|
||||
prev-input-color (some-> (dom/get-value (mf/ref-val value-input-ref))
|
||||
(tinycolor/valid-color))
|
||||
;; If the input is a reference we will take the format from the computed value
|
||||
prev-computed-color (when-not prev-input-color
|
||||
(some-> color (tinycolor/valid-color)))
|
||||
prev-format (some-> (or prev-input-color prev-computed-color)
|
||||
(tinycolor/color-format))
|
||||
to-rgba? (and
|
||||
(< alpha 1)
|
||||
(or (= prev-format "hex") (not prev-format)))
|
||||
to-hex? (and (not prev-format) (= alpha 1))
|
||||
format (cond
|
||||
to-rgba? "rgba"
|
||||
to-hex? "hex"
|
||||
prev-format prev-format
|
||||
:else "hex")
|
||||
color-value (-> (tinycolor/valid-color hex-value)
|
||||
(tinycolor/set-alpha (or alpha 1))
|
||||
(tinycolor/->string format))]
|
||||
(mf/set-ref-val! value-ref color-value)
|
||||
(dom/set-value! (mf/ref-val value-input-ref) color-value)
|
||||
(on-update-value-debounced color-value))))
|
||||
|
||||
on-display-colorpicker'
|
||||
on-update-value
|
||||
(mf/use-fn
|
||||
(mf/deps color-ramp-open? on-display-colorpicker)
|
||||
(fn []
|
||||
(let [open? (not color-ramp-open?)]
|
||||
(reset! color-ramp-open* open?)
|
||||
(on-display-colorpicker open?))))
|
||||
(mf/deps on-update-value-debounced transform-value)
|
||||
(fn [e]
|
||||
(let [value (dom/get-target-val e)
|
||||
value' (if (fn? transform-value)
|
||||
(transform-value value)
|
||||
value)]
|
||||
;; Value got updated in transform, update the dom node
|
||||
(when (not= value value')
|
||||
(dom/set-value! (mf/ref-val value-input-ref) value'))
|
||||
(mf/set-ref-val! value-ref value)
|
||||
(on-update-value-debounced value))))
|
||||
|
||||
on-external-update-value
|
||||
(mf/use-fn
|
||||
(mf/deps on-update-value-debounced)
|
||||
(fn [next-value]
|
||||
(dom/set-value! (mf/ref-val value-input-ref) next-value)
|
||||
(mf/set-ref-val! value-ref next-value)
|
||||
(on-update-value-debounced next-value)))
|
||||
|
||||
value-error? (seq (:errors token-resolve-result))
|
||||
|
||||
|
@ -431,7 +362,6 @@
|
|||
(mf/deps validate-name validate-descripion token active-theme-tokens)
|
||||
(fn [e]
|
||||
(dom/prevent-default e)
|
||||
(mf/set-ref-val! cancel-ref nil)
|
||||
;; We have to re-validate the current form values before submitting
|
||||
;; because the validation is asynchronous/debounced
|
||||
;; and the user might have edited a valid form to make it invalid,
|
||||
|
@ -440,7 +370,11 @@
|
|||
valid-name? (try
|
||||
(not (:errors (validate-name final-name)))
|
||||
(catch js/Error _ nil))
|
||||
final-value (finalize-value (mf/ref-val value-ref))
|
||||
final-value (let [value (mf/ref-val value-ref)
|
||||
font-family? (= :font-family (or (:type token) token-type))]
|
||||
(if font-family?
|
||||
(ctt/split-font-family value)
|
||||
(finalize-value value)))
|
||||
final-description @description-ref
|
||||
valid-description? (if final-description
|
||||
(try
|
||||
|
@ -479,9 +413,9 @@
|
|||
on-cancel
|
||||
(mf/use-fn
|
||||
(fn [e]
|
||||
(mf/set-ref-val! cancel-ref nil)
|
||||
(dom/prevent-default e)
|
||||
(modal/hide!)))
|
||||
|
||||
handle-key-down-delete
|
||||
(mf/use-fn
|
||||
(mf/deps on-delete-token)
|
||||
|
@ -555,20 +489,31 @@
|
|||
{:level :warning :appearance :ghost} (tr "workspace.tokens.warning-name-change")]])]
|
||||
|
||||
[:div {:class (stl/css :input-row)}
|
||||
[:> input-tokens-value*
|
||||
{:placeholder (tr "workspace.tokens.token-value-enter")
|
||||
:label (tr "workspace.tokens.token-value")
|
||||
:default-value (mf/ref-val value-ref)
|
||||
:ref value-input-ref
|
||||
:is-color-token is-color-token
|
||||
:color color
|
||||
:on-change on-update-value
|
||||
:error (not (nil? (:errors token-resolve-result)))
|
||||
:display-colorpicker on-display-colorpicker'
|
||||
:on-blur on-update-value}]
|
||||
(when color-ramp-open?
|
||||
[:> ramp* {:color (some-> color (tinycolor/valid-color))
|
||||
:on-change on-update-color}])
|
||||
(let [placeholder (tr "workspace.tokens.token-value-enter")
|
||||
label (tr "workspace.tokens.token-value")
|
||||
default-value (mf/ref-val value-ref)
|
||||
ref value-input-ref
|
||||
error (not (nil? (:errors token-resolve-result)))
|
||||
on-blur on-update-value]
|
||||
(if (fn? custom-input-token-value)
|
||||
[:> custom-input-token-value
|
||||
{:placeholder placeholder
|
||||
:label label
|
||||
:default-value default-value
|
||||
:input-ref ref
|
||||
:error error
|
||||
:on-blur on-blur
|
||||
:on-update-value on-update-value
|
||||
:on-external-update-value on-external-update-value
|
||||
:custom-input-token-value-props custom-input-token-value-props}]
|
||||
[:> input-tokens-value*
|
||||
{:placeholder placeholder
|
||||
:label label
|
||||
:default-value default-value
|
||||
:ref ref
|
||||
:error error
|
||||
:on-blur on-blur
|
||||
:on-change on-update-value}]))
|
||||
[:& token-value-hint {:result token-resolve-result}]]
|
||||
[:div {:class (stl/css :input-row)}
|
||||
[:> input* {:label (tr "workspace.tokens.token-description")
|
||||
|
@ -593,7 +538,6 @@
|
|||
[:> button* {:on-click on-cancel
|
||||
:on-key-down handle-key-down-cancel
|
||||
:type "button"
|
||||
:on-ref on-cancel-ref
|
||||
:id "token-modal-cancel"
|
||||
:variant "secondary"}
|
||||
(tr "labels.cancel")]
|
||||
|
@ -602,3 +546,247 @@
|
|||
:variant "primary"
|
||||
:disabled disabled?}
|
||||
(tr "labels.save")]]]]))
|
||||
|
||||
;; FIXME: this function has confusing name
|
||||
(defn- hex->value
|
||||
[hex]
|
||||
(when-let [tc (tinycolor/valid-color hex)]
|
||||
(let [hex (tinycolor/->hex-string tc)
|
||||
alpha (tinycolor/alpha tc)
|
||||
[r g b] (c/hex->rgb hex)
|
||||
[h s v] (c/hex->hsv hex)]
|
||||
{:hex hex
|
||||
:r r :g g :b b
|
||||
:h h :s s :v v
|
||||
:alpha alpha})))
|
||||
|
||||
(mf/defc ramp*
|
||||
[{:keys [color on-change]}]
|
||||
(let [wrapper-node-ref (mf/use-ref nil)
|
||||
dragging-ref (mf/use-ref false)
|
||||
|
||||
on-start-drag
|
||||
(mf/use-fn #(mf/set-ref-val! dragging-ref true))
|
||||
|
||||
on-finish-drag
|
||||
(mf/use-fn #(mf/set-ref-val! dragging-ref false))
|
||||
|
||||
internal-color*
|
||||
(mf/use-state #(hex->value color))
|
||||
|
||||
internal-color
|
||||
(deref internal-color*)
|
||||
|
||||
on-change'
|
||||
(mf/use-fn
|
||||
(mf/deps on-change)
|
||||
(fn [{:keys [hex alpha] :as selector-color}]
|
||||
(let [dragging? (mf/ref-val dragging-ref)]
|
||||
(when-not (and dragging? hex)
|
||||
(reset! internal-color* selector-color)
|
||||
(on-change hex alpha)))))]
|
||||
(mf/use-effect
|
||||
(mf/deps color)
|
||||
(fn []
|
||||
;; Update internal color when user changes input value
|
||||
(when-let [color (tinycolor/valid-color color)]
|
||||
(when-not (= (tinycolor/->hex-string color) (:hex internal-color))
|
||||
(reset! internal-color* (hex->value color))))))
|
||||
|
||||
(colorpicker/use-color-picker-css-variables! wrapper-node-ref internal-color)
|
||||
[:div {:ref wrapper-node-ref}
|
||||
[:> ramp-selector*
|
||||
{:color internal-color
|
||||
:on-start-drag on-start-drag
|
||||
:on-finish-drag on-finish-drag
|
||||
:on-change on-change'}]]))
|
||||
|
||||
(mf/defc color-picker*
|
||||
[{:keys [placeholder label default-value input-ref error on-blur on-update-value on-external-update-value custom-input-token-value-props]}]
|
||||
(let [{:keys [color on-display-colorpicker]} custom-input-token-value-props
|
||||
color-ramp-open* (mf/use-state false)
|
||||
color-ramp-open? (deref color-ramp-open*)
|
||||
|
||||
on-click-swatch
|
||||
(mf/use-fn
|
||||
(mf/deps color-ramp-open? on-display-colorpicker)
|
||||
(fn []
|
||||
(let [open? (not color-ramp-open?)]
|
||||
(reset! color-ramp-open* open?)
|
||||
(on-display-colorpicker open?))))
|
||||
|
||||
swatch
|
||||
(mf/html
|
||||
[:> input-token-color-bullet*
|
||||
{:color color
|
||||
:class (stl/css :slot-start)
|
||||
:on-click on-click-swatch}])
|
||||
|
||||
on-change'
|
||||
(mf/use-fn
|
||||
(mf/deps color on-external-update-value)
|
||||
(fn [hex-value alpha]
|
||||
(let [;; StyleDictionary will always convert to hex/rgba, so we take the format from the value input field
|
||||
prev-input-color (some-> (dom/get-value (mf/ref-val input-ref))
|
||||
(tinycolor/valid-color))
|
||||
;; If the input is a reference we will take the format from the computed value
|
||||
prev-computed-color (when-not prev-input-color
|
||||
(some-> color (tinycolor/valid-color)))
|
||||
prev-format (some-> (or prev-input-color prev-computed-color)
|
||||
(tinycolor/color-format))
|
||||
to-rgba? (and
|
||||
(< alpha 1)
|
||||
(or (= prev-format "hex") (not prev-format)))
|
||||
to-hex? (and (not prev-format) (= alpha 1))
|
||||
format (cond
|
||||
to-rgba? "rgba"
|
||||
to-hex? "hex"
|
||||
prev-format prev-format
|
||||
:else "hex")
|
||||
color-value (-> (tinycolor/valid-color hex-value)
|
||||
(tinycolor/set-alpha (or alpha 1))
|
||||
(tinycolor/->string format))]
|
||||
(on-external-update-value color-value))))]
|
||||
|
||||
[:*
|
||||
[:> input-tokens-value*
|
||||
{:placeholder placeholder
|
||||
:label label
|
||||
:default-value default-value
|
||||
:ref input-ref
|
||||
:error error
|
||||
:on-blur on-blur
|
||||
:on-change on-update-value
|
||||
:slot-start swatch}]
|
||||
(when color-ramp-open?
|
||||
[:> ramp*
|
||||
{:color (some-> color (tinycolor/valid-color))
|
||||
:on-change on-change'}])]))
|
||||
|
||||
(mf/defc color-form*
|
||||
[{:keys [token on-display-colorpicker] :rest props}]
|
||||
(let [color* (mf/use-state (:value token))
|
||||
color (deref color*)
|
||||
on-value-resolve (mf/use-fn
|
||||
(mf/deps color)
|
||||
(fn [value]
|
||||
(reset! color* value)
|
||||
value))
|
||||
|
||||
custom-input-token-value-props
|
||||
(mf/use-memo
|
||||
(mf/deps color on-display-colorpicker)
|
||||
(fn []
|
||||
{:color color
|
||||
:on-display-colorpicker on-display-colorpicker}))
|
||||
|
||||
transform-value
|
||||
(mf/use-fn
|
||||
(fn [value]
|
||||
(if (tinycolor/hex-without-hash-prefix? value)
|
||||
(dm/str "#" value)
|
||||
value)))]
|
||||
[:> form*
|
||||
(mf/spread-props props {:token token
|
||||
:transform-value transform-value
|
||||
:on-value-resolve on-value-resolve
|
||||
:custom-input-token-value color-picker*
|
||||
:custom-input-token-value-props custom-input-token-value-props})]))
|
||||
|
||||
(mf/defc font-selector-wrapper*
|
||||
[{:keys [font input-ref on-select-font on-close-font-selector]}]
|
||||
(let [current-font* (mf/use-state (or font
|
||||
(some-> (mf/ref-val input-ref)
|
||||
(dom/get-value)
|
||||
(ctt/split-font-family)
|
||||
(first)
|
||||
(fonts/find-font-family))))
|
||||
current-font (deref current-font*)]
|
||||
[:div {:class (stl/css :font-select-wrapper)}
|
||||
[:> font-selector* {:current-font current-font
|
||||
:on-select on-select-font
|
||||
:on-close on-close-font-selector
|
||||
:full-size true}]]))
|
||||
|
||||
(mf/defc font-picker*
|
||||
[{:keys [default-value input-ref error on-blur on-update-value on-external-update-value]}]
|
||||
(let [font* (mf/use-state (fonts/find-font-family default-value))
|
||||
font (deref font*)
|
||||
set-font (mf/use-fn
|
||||
(mf/deps font)
|
||||
#(reset! font* %))
|
||||
|
||||
font-selector-open* (mf/use-state false)
|
||||
font-selector-open? (deref font-selector-open*)
|
||||
|
||||
on-close-font-selector
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
(reset! font-selector-open* false)))
|
||||
|
||||
on-click-dropdown-button
|
||||
(mf/use-fn
|
||||
(mf/deps font-selector-open?)
|
||||
(fn [e]
|
||||
(dom/prevent-default e)
|
||||
(reset! font-selector-open* (not font-selector-open?))))
|
||||
|
||||
on-select-font
|
||||
(mf/use-fn
|
||||
(mf/deps on-external-update-value set-font)
|
||||
(fn [{:keys [family] :as font}]
|
||||
(when font
|
||||
(set-font font)
|
||||
(on-external-update-value family))))
|
||||
|
||||
on-update-value'
|
||||
(mf/use-fn
|
||||
(mf/deps on-update-value set-font)
|
||||
(fn [value]
|
||||
(set-font nil)
|
||||
(on-update-value value)))
|
||||
|
||||
font-selector-button
|
||||
(mf/html
|
||||
[:> icon-button*
|
||||
{:on-click on-click-dropdown-button
|
||||
:aria-label (tr "workspace.tokens.token-font-family-select")
|
||||
:icon "arrow-down"
|
||||
:variant "action"
|
||||
:type "button"}])]
|
||||
[:*
|
||||
[:> input-tokens-value*
|
||||
{:placeholder (tr "workspace.tokens.token-font-family-value-enter")
|
||||
:label (tr "workspace.tokens.token-font-family-value")
|
||||
:default-value default-value
|
||||
:ref input-ref
|
||||
:error error
|
||||
:on-blur on-blur
|
||||
:on-change on-update-value'
|
||||
:icon "text-font-family"
|
||||
:slot-end font-selector-button}]
|
||||
(when font-selector-open?
|
||||
[:> font-selector-wrapper* {:font font
|
||||
:input-ref input-ref
|
||||
:on-select-font on-select-font
|
||||
:on-close-font-selector on-close-font-selector}])]))
|
||||
|
||||
(mf/defc font-family-form*
|
||||
[{:keys [token] :rest props}]
|
||||
(let [on-value-resolve
|
||||
(mf/use-fn
|
||||
(fn [value]
|
||||
(when value
|
||||
(ctt/join-font-family value))))]
|
||||
[:> form*
|
||||
(mf/spread-props props {:token (when token (update token :value ctt/join-font-family))
|
||||
:custom-input-token-value font-picker*
|
||||
:on-value-resolve on-value-resolve})]))
|
||||
|
||||
(mf/defc form-wrapper*
|
||||
[{:keys [token token-type] :as props}]
|
||||
(let [token-type' (or (:type token) token-type)]
|
||||
(case token-type'
|
||||
:color [:> color-form* props]
|
||||
:font-family [:> font-family-form* props]
|
||||
[:> form* props])))
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
.form-wrapper {
|
||||
width: $s-384;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.button-row {
|
||||
|
@ -64,3 +65,11 @@
|
|||
.form-modal-title {
|
||||
color: var(--color-foreground-primary);
|
||||
}
|
||||
|
||||
.font-select-wrapper {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
// This padding from the modal should be shared as a variable
|
||||
// Need to set this or the font-select will cause scroll
|
||||
bottom: $s-32;
|
||||
}
|
||||
|
|
|
@ -7,11 +7,10 @@
|
|||
(ns app.main.ui.workspace.tokens.management.create.input-tokens-value
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.ui.ds.controls.utilities.input-field :refer [input-field*]]
|
||||
[app.main.ui.ds.controls.utilities.label :refer [label*]]
|
||||
[app.main.ui.workspace.tokens.management.create.input-token-color-bullet :refer [input-token-color-bullet*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :refer [icon-list]]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(def ^:private schema::input-tokens-value
|
||||
|
@ -20,26 +19,18 @@
|
|||
[:placeholder {:optional true} :string]
|
||||
[:value {:optional true} [:maybe :string]]
|
||||
[:class {:optional true} :string]
|
||||
[:is-color-token {:optional true} :boolean]
|
||||
[:color {:optional true} [:maybe :string]]
|
||||
[:display-colorpicker {:optional true} fn?]
|
||||
[:error {:optional true} :boolean]])
|
||||
|
||||
[:error {:optional true} :boolean]
|
||||
[:slot-start {:optional true} [:maybe some?]]
|
||||
[:icon {:optional true}
|
||||
[:maybe [:and :string [:fn #(contains? icon-list %)]]]]])
|
||||
|
||||
(mf/defc input-tokens-value*
|
||||
{::mf/props :obj
|
||||
::mf/forward-ref true
|
||||
::mf/schema schema::input-tokens-value}
|
||||
[{:keys [class label is-color-token placeholder error value color display-colorpicker] :rest props} ref]
|
||||
[{:keys [class label placeholder error value icon slot-start] :rest props} ref]
|
||||
(let [id (mf/use-id)
|
||||
input-ref (mf/use-ref)
|
||||
is-color-token (d/nilv is-color-token false)
|
||||
swatch
|
||||
(mf/html [:> input-token-color-bullet*
|
||||
{:color color
|
||||
:class (stl/css :slot-start)
|
||||
:on-click display-colorpicker}])
|
||||
|
||||
props (mf/spread-props props {:id id
|
||||
:type "text"
|
||||
:class (stl/css :input)
|
||||
|
@ -47,7 +38,8 @@
|
|||
:value value
|
||||
:variant "comfortable"
|
||||
:hint-type (when error "error")
|
||||
:slot-start (when is-color-token swatch)
|
||||
:slot-start slot-start
|
||||
:icon icon
|
||||
:ref (or ref input-ref)})]
|
||||
[:div {:class (dm/str class " " (stl/css-case :wrapper true
|
||||
:input-error error))}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
[app.main.refs :as refs]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.workspace.tokens.management.create.form :refer [form]]
|
||||
[app.main.ui.workspace.tokens.management.create.form :refer [form-wrapper*]]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[okulary.core :as l]
|
||||
[rumext.v2 :as mf]))
|
||||
|
@ -88,11 +88,11 @@
|
|||
:icon i/close
|
||||
:variant "action"
|
||||
:aria-label (tr "labels.close")}]
|
||||
[:& form {:token token
|
||||
:action action
|
||||
:selected-token-set-name selected-token-set-name
|
||||
:token-type token-type
|
||||
:on-display-colorpicker update-modal-size}]]))
|
||||
[:> form-wrapper* {:token token
|
||||
:action action
|
||||
:selected-token-set-name selected-token-set-name
|
||||
:token-type token-type
|
||||
:on-display-colorpicker update-modal-size}]]))
|
||||
|
||||
;; Modals ----------------------------------------------------------------------
|
||||
|
||||
|
@ -191,3 +191,9 @@
|
|||
::mf/register-as :tokens/letter-spacing}
|
||||
[properties]
|
||||
[:& token-update-create-modal properties])
|
||||
|
||||
(mf/defc font-familiy-modal
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :tokens/font-family}
|
||||
[properties]
|
||||
[:& token-update-create-modal properties])
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
:border-radius "corner-radius"
|
||||
:color "drop"
|
||||
:boolean "boolean-difference"
|
||||
:font-family "text-font-family"
|
||||
:font-size "text-font-size"
|
||||
:letter-spacing "text-letterspacing"
|
||||
:opacity "percentage"
|
||||
|
|
|
@ -106,7 +106,9 @@
|
|||
(defn- generate-tooltip
|
||||
"Generates a tooltip for a given token"
|
||||
[is-viewer shape theme-token token half-applied no-valid-value ref-not-in-active-set]
|
||||
(let [{:keys [name value type resolved-value]} token
|
||||
(let [{:keys [name type resolved-value]} token
|
||||
value (cond->> (:value token)
|
||||
(= :font-family type) ctt/join-font-family)
|
||||
resolved-value-theme (:resolved-value theme-token)
|
||||
resolved-value (or resolved-value-theme resolved-value)
|
||||
{:keys [title] :as token-props} (dwta/get-token-properties theme-token)
|
||||
|
|
|
@ -558,6 +558,40 @@
|
|||
(t/is (= (:letter-spacing (:applied-tokens text-1')) (:name token-target')))
|
||||
(t/is (= (:letter-spacing style-text-blocks) "2")))))))))
|
||||
|
||||
(t/deftest test-apply-font-family
|
||||
(t/testing "applies font-family token and updates the text font-family"
|
||||
(t/async
|
||||
done
|
||||
(let [font-family-token {:name "primary-font"
|
||||
:value "Arial"
|
||||
:type :font-family}
|
||||
file (-> (setup-file-with-tokens)
|
||||
(update-in [:data :tokens-lib]
|
||||
#(ctob/add-token-in-set % "Set A" (ctob/make-token font-family-token))))
|
||||
store (ths/setup-store file)
|
||||
text-1 (cths/get-shape file :text-1)
|
||||
events [(dwta/apply-token {:shape-ids [(:id text-1)]
|
||||
:attributes #{:font-family}
|
||||
:token (toht/get-token file "primary-font")
|
||||
:on-update-shape dwta/update-font-family})]]
|
||||
(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' "primary-font")
|
||||
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 (= (:font-family (:applied-tokens text-1')) (:name token-target')))
|
||||
(t/is (= (:font-family style-text-blocks) (:font-id txt/default-text-attrs))))))))))
|
||||
|
||||
(t/deftest test-toggle-token-none
|
||||
(t/testing "should apply token to all selected items, where no item has the token applied"
|
||||
(t/async
|
||||
|
|
|
@ -7528,10 +7528,22 @@ msgstr "Could not resolve reference token with the name: %s"
|
|||
msgid "workspace.tokens.token-value"
|
||||
msgstr "Value"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/form.cljs:560
|
||||
msgid "workspace.tokens.token-font-family-value"
|
||||
msgstr "Font family"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/form.cljs:559
|
||||
msgid "workspace.tokens.token-value-enter"
|
||||
msgstr "Enter a value or alias with {alias}"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/form.cljs:689
|
||||
msgid "workspace.tokens.token-font-family-value-enter"
|
||||
msgstr "Font family or list of fonts separated by comma (,)"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/form.cljs:688
|
||||
msgid "workspace.tokens.token-font-family-select"
|
||||
msgstr "Select font family"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/sidebar.cljs:336
|
||||
msgid "workspace.tokens.tokens-section-title"
|
||||
msgstr "TOKENS - %s"
|
||||
|
@ -7900,4 +7912,4 @@ msgid "labels.sources"
|
|||
msgstr "Sources"
|
||||
|
||||
msgid "labels.pinned-projects"
|
||||
msgstr "Pinned Projects"
|
||||
msgstr "Pinned Projects"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue