0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-20 05:34:23 -05:00

Merge remote-tracking branch 'origin/token-studio-develop' into florian/rename-set-groups

This commit is contained in:
Florian Schroedl 2024-12-10 17:31:38 +01:00
commit 78d743406b
25 changed files with 886 additions and 173 deletions

View file

@ -1166,7 +1166,7 @@
; We need to trigger a sync if the shape has changed any
; attribute that participates in components synchronization.
(and (= (:type operation) :set)
(get ctk/sync-attrs (:attr operation))))
(ctk/component-attr? (:attr operation))))
any-sync? (some need-sync? operations)]
(when any-sync?
(if page-id

View file

@ -27,6 +27,7 @@
[app.common.types.shape-tree :as ctst]
[app.common.types.shape.interactions :as ctsi]
[app.common.types.shape.layout :as ctl]
[app.common.types.token :as cto]
[app.common.types.typography :as cty]
[app.common.uuid :as uuid]
[clojure.set :as set]
@ -1479,6 +1480,44 @@
[{:type :set-remote-synced
:remote-synced (:remote-synced shape)}]}))))))
(defn- update-tokens
"Token synchronization algorithm. Copy the applied tokens that have changed
in the origin shape to the dest shape (applying or removing as necessary).
Only the given token attributes are synced."
[changes container dest-shape orig-shape token-attrs]
(let [orig-tokens (get orig-shape :applied-tokens {})
dest-tokens (get dest-shape :applied-tokens {})
dest-tokens' (reduce (fn [dest-tokens' token-attr]
(let [orig-token (get orig-tokens token-attr)
dest-token (get dest-tokens token-attr)]
(if (= orig-token dest-token)
dest-tokens'
(if (nil? orig-token)
(dissoc dest-tokens' token-attr)
(assoc dest-tokens' token-attr orig-token)))))
dest-tokens
token-attrs)]
(if (= dest-tokens dest-tokens')
changes
(-> changes
(update :redo-changes conj (make-change
container
{:type :mod-obj
:id (:id dest-shape)
:operations [{:type :set
:attr :applied-tokens
:val dest-tokens'
:ignore-touched true}]}))
(update :undo-changes conj (make-change
container
{:type :mod-obj
:id (:id dest-shape)
:operations [{:type :set
:attr :applied-tokens
:val dest-tokens
:ignore-touched true}]}))))))
(defn- update-attrs
"The main function that implements the attribute sync algorithm. Copy
attributes that have changed in the origin shape to the dest shape.
@ -1511,37 +1550,41 @@
(loop [attrs (->> (seq (keys ctk/sync-attrs))
;; We don't update the flex-child attrs
(remove ctk/swap-keep-attrs)
;; We don't do automatic update of the `layout-grid-cells` property.
(remove #(= :layout-grid-cells %)))
applied-tokens #{}
roperations []
uoperations '()]
(let [attr (first attrs)]
(if (nil? attr)
(if (empty? roperations)
(if (and (empty? roperations) (empty? applied-tokens))
changes
(let [all-parents (cfh/get-parent-ids (:objects container)
(:id dest-shape))]
(-> changes
(update :redo-changes conj (make-change
container
{:type :mod-obj
:id (:id dest-shape)
:operations roperations}))
(update :redo-changes conj (make-change
container
{:type :reg-objects
:shapes all-parents}))
(update :undo-changes conj (make-change
container
{:type :mod-obj
:id (:id dest-shape)
:operations (vec uoperations)}))
(update :undo-changes concat [(make-change
container
{:type :reg-objects
:shapes all-parents})]))))
(cond-> changes
(seq roperations)
(-> (update :redo-changes conj (make-change
container
{:type :mod-obj
:id (:id dest-shape)
:operations roperations}))
(update :redo-changes conj (make-change
container
{:type :reg-objects
:shapes all-parents}))
(update :undo-changes conj (make-change
container
{:type :mod-obj
:id (:id dest-shape)
:operations (vec uoperations)}))
(update :undo-changes concat [(make-change
container
{:type :reg-objects
:shapes all-parents})]))
(seq applied-tokens)
(update-tokens container dest-shape origin-shape applied-tokens))))
(let [;; position-data is a special case because can be affected by :geometry-group and :content-group
;; so, if the position-data changes but the geometry is touched we need to reset the position-data
;; so it's calculated again
@ -1564,14 +1607,21 @@
:val (get dest-shape attr)
:ignore-touched true}
attr-group (get ctk/sync-attrs attr)]
attr-group (get ctk/sync-attrs attr)
token-attrs (cto/shape-attr->token-attrs attr)
applied-tokens' (cond-> applied-tokens
(not (and (touched attr-group)
omit-touched?))
(into token-attrs))]
(if (or (= (get origin-shape attr) (get dest-shape attr))
(and (touched attr-group) omit-touched?))
(recur (next attrs)
applied-tokens'
roperations
uoperations)
(recur (next attrs)
applied-tokens'
(conj roperations roperation)
(conj uoperations uoperation)))))))))

View file

@ -0,0 +1,82 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.common.test-helpers.tokens
(:require
[app.common.test-helpers.files :as thf]
[app.common.test-helpers.shapes :as ths]
[app.common.types.container :as ctn]
[app.common.types.file :as ctf]
[app.common.types.pages-list :as ctpl]
[app.common.types.shape-tree :as ctst]
[app.common.types.token :as cto]
[app.common.types.tokens-lib :as ctob]))
(defn add-tokens-lib
[file]
(ctf/update-file-data file #(update % :tokens-lib ctob/ensure-tokens-lib)))
(defn update-tokens-lib
[file f]
(ctf/update-file-data file #(update % :tokens-lib f)))
(defn- set-stroke-width
[shape stroke-width]
(let [strokes (if (seq (:strokes shape))
(:strokes shape)
[{:stroke-style :solid
:stroke-alignment :inner
:stroke-width 1
:stroke-color "#000000"
:stroke-opacity 1}])
new-strokes (update strokes 0 assoc :stroke-width stroke-width)]
(ctn/set-shape-attr shape :strokes new-strokes {:ignore-touched true})))
(defn- set-stroke-color
[shape stroke-color]
(let [strokes (if (seq (:strokes shape))
(:strokes shape)
[{:stroke-style :solid
:stroke-alignment :inner
:stroke-width 1
:stroke-color "#000000"
:stroke-opacity 1}])
new-strokes (update strokes 0 assoc :stroke-color stroke-color)]
(ctn/set-shape-attr shape :strokes new-strokes {:ignore-touched true})))
(defn- set-fill-color
[shape fill-color]
(let [fills (if (seq (:fills shape))
(:fills shape)
[{:fill-color "#000000"
:fill-opacity 1}])
new-fills (update fills 0 assoc :fill-color fill-color)]
(ctn/set-shape-attr shape :fills new-fills {:ignore-touched true})))
(defn apply-token-to-shape
[file shape-label token-name token-attrs shape-attrs resolved-value]
(let [page (thf/current-page file)
shape (ths/get-shape file shape-label)
shape' (as-> shape $
(cto/apply-token-to-shape {:shape $
:token {:name token-name}
:attributes token-attrs})
(reduce (fn [shape attr]
(case attr
:stroke-width (set-stroke-width shape resolved-value)
:stroke-color (set-stroke-color shape resolved-value)
:fill (set-fill-color shape resolved-value)
(ctn/set-shape-attr shape attr resolved-value {:ignore-touched true})))
$
shape-attrs))]
(ctf/update-file-data
file
(fn [file-data]
(ctpl/update-page file-data
(:id page)
#(ctst/set-shape % shape'))))))

View file

@ -140,6 +140,14 @@
:layout-item-z-index
:layout-item-align-self})
(defn component-attr?
"Check if some attribute is one that is involved in component syncrhonization.
Note that design tokens also are involved, although they go by an alternate
route and thus they are not part of :sync-attrs."
[attr]
(or (get sync-attrs attr)
(= :applied-tokens attr)))
(defn instance-root?
"Check if this shape is the head of a top instance."
[shape]

View file

@ -18,6 +18,7 @@
[app.common.types.plugins :as ctpg]
[app.common.types.shape-tree :as ctst]
[app.common.types.shape.layout :as ctl]
[app.common.types.token :as ctt]
[app.common.uuid :as uuid]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -540,14 +541,28 @@
;; --- SHAPE UPDATE
(defn- get-token-groups
[shape new-applied-tokens]
(let [old-applied-tokens (d/nilv (:applied-tokens shape) #{})
changed-token-attrs (filter #(not= (get old-applied-tokens %) (get new-applied-tokens %))
ctt/all-keys)
changed-groups (into #{}
(comp (map ctt/token-attr->shape-attr)
(map #(get ctk/sync-attrs %))
(filter some?))
changed-token-attrs)]
changed-groups))
(defn set-shape-attr
"Assign attribute to shape with touched logic.
The returned shape will contain a metadata associated with it
indicating if shape is touched or not."
[shape attr val & {:keys [ignore-touched ignore-geometry]}]
(let [group (get ctk/sync-attrs attr)
shape-val (get shape attr)
(let [group (get ctk/sync-attrs attr)
token-groups (when (= attr :applied-tokens)
(get-token-groups shape val))
shape-val (get shape attr)
ignore?
(or ignore-touched
@ -585,9 +600,15 @@
;; set the "touched" flag for the group the attribute belongs to.
;; In some cases we need to ignore touched only if the attribute is
;; geometric (position, width or transformation).
(and in-copy? group (not ignore?) (not equal?)
(not (and ignore-geometry is-geometry?)))
(-> (update :touched ctk/set-touched-group group)
(and in-copy?
(or (and group (not equal?)) (seq token-groups))
(not ignore?) (not (and ignore-geometry is-geometry?)))
(-> (update :touched (fn [touched]
(reduce #(ctk/set-touched-group %1 %2)
touched
(if group
(cons group token-groups)
token-groups))))
(dissoc :remote-synced))
(nil? val)

View file

@ -6,8 +6,10 @@
(ns app.common.types.token
(: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]))
@ -150,6 +152,15 @@
(def rotation-keys (schema-keys ::rotation))
(def all-keys (set/union color-keys
border-radius-keys
stroke-width-keys
sizing-keys
opacity-keys
spacing-keys
dimensions-keys
rotation-keys))
(sm/register!
^{::sm/type ::tokens}
[:map {:title "Applied Tokens"}])
@ -163,3 +174,58 @@
::spacing
::rotation
::dimensions])
(defn shape-attr->token-attrs
[shape-attr]
(cond
(= :fills shape-attr) #{:fill}
(= :strokes shape-attr) #{:stroke-color :stroke-width}
(border-radius-keys shape-attr) #{shape-attr}
(sizing-keys shape-attr) #{shape-attr}
(opacity-keys shape-attr) #{shape-attr}
(spacing-keys shape-attr) #{shape-attr}
(rotation-keys shape-attr) #{shape-attr}))
(defn token-attr->shape-attr
[token-attr]
(case token-attr
:fill :fills
:stroke-color :strokes
:stroke-width :strokes
token-attr))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TOKENS IN SHAPES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- toggle-or-apply-token
"Remove any shape attributes from token if they exists.
Othewise apply token attributes."
[shape token]
(let [[shape-leftover token-leftover _matching] (data/diff (:applied-tokens shape) token)]
(merge {} shape-leftover token-leftover)))
(defn- token-from-attributes [token attributes]
(->> (map (fn [attr] [attr (:name token)]) attributes)
(into {})))
(defn- apply-token-to-attributes [{:keys [shape token attributes]}]
(let [token (token-from-attributes token attributes)]
(toggle-or-apply-token shape token)))
(defn apply-token-to-shape
[{:keys [shape token attributes] :as _props}]
(let [applied-tokens (apply-token-to-attributes {:shape shape
:token token
:attributes attributes})]
(update shape :applied-tokens #(merge % applied-tokens))))
(defn maybe-apply-token-to-shape
"When the passed `:token` is non-nil apply it to the `:applied-tokens` on a shape."
[{:keys [shape token _attributes] :as props}]
(if token
(apply-token-to-shape props)
shape))
(defn unapply-token-id [shape attributes]
(update shape :applied-tokens d/without-keys attributes))

View file

@ -634,6 +634,11 @@ When `before-set-name` is nil, move set to bottom")
(delete-token-from-set [_ set-name token-name] "delete a token from a set")
(toggle-set-in-theme [_ group-name theme-name set-name] "toggle a set used / not used in a theme")
(get-active-themes-set-names [_] "set of set names that are active in the the active themes")
(sets-at-path-all-active? [_ prefixed-path] "compute active state for child sets at `prefixed-path`.
Will return a value that matches this schema:
`:none` None of the nested sets are active
`:all` All of the nested sets are active
`:partial` Mixed active state of nested sets")
(get-active-themes-set-tokens [_] "set of set names that are active in the the active themes")
(encode-dtcg [_] "Encodes library to a dtcg compatible json string")
(decode-dtcg-json [_ parsed-json] "Decodes parsed json containing tokens and converts to library")
@ -894,6 +899,19 @@ When `before-set-name` is nil, move set to bottom")
(mapcat :sets)
(get-active-themes this)))
(sets-at-path-all-active? [this prefixed-path]
(let [active-set-names (get-active-themes-set-names this)]
(if (seq active-set-names)
(let [path-active-set-names (->> (get-sets-at-prefix-path this prefixed-path)
(map :name)
(into #{}))
difference (set/difference path-active-set-names active-set-names)]
(cond
(empty? difference) :all
(seq (set/intersection path-active-set-names active-set-names)) :partial
:else :none))
:none)))
(get-active-themes-set-tokens [this]
(let [sets-order (get-ordered-set-names this)
active-themes (get-active-themes this)

View file

@ -193,7 +193,6 @@
(ths/add-sample-shape :free-shape))
page (thf/current-page file)
main-root (ths/get-shape file :main-root)
;; ==== Action
changes1 (cls/generate-relocate (pcb/empty-changes)
@ -203,9 +202,6 @@
0 ; to-index
#{(thi/id :free-shape)}) ; ids
updated-file (thf/apply-changes file changes1)
changes2 (cll/generate-sync-file-changes (pcb/empty-changes)
@ -491,4 +487,4 @@
(t/is (= (:fill-color fill') "#fabada"))
(t/is (= (:fill-opacity fill') 1))
(t/is (= (:touched copy2-root') nil))
(t/is (= (:touched copy2-child') nil))))
(t/is (= (:touched copy2-child') nil))))

View file

@ -417,8 +417,39 @@
expected-tokens (ctob/get-active-themes-set-tokens tokens-lib)
expected-token-names (mapv key expected-tokens)]
(t/is (= '("set-a" "set-b" "inactive-set") expected-order))
(t/is (= ["set-a-token" "set-b-token"] expected-token-names)))))
(t/is (= ["set-a-token" "set-b-token"] expected-token-names))))
(t/testing "sets-at-path-active-state"
(let [tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-set (ctob/make-token-set :name "foo/bar/baz"))
(ctob/add-set (ctob/make-token-set :name "foo/bar/bam"))
(ctob/add-theme (ctob/make-token-theme :name "none"))
(ctob/add-theme (ctob/make-token-theme :name "partial"
:sets #{"foo/bar/baz"}))
(ctob/add-theme (ctob/make-token-theme :name "all"
:sets #{"foo/bar/baz"
"foo/bar/bam"}))
(ctob/add-theme (ctob/make-token-theme :name "invalid"
:sets #{"foo/missing"})))
expected-none (-> tokens-lib
(ctob/set-active-themes #{"/none"})
(ctob/sets-at-path-all-active? "G-foo"))
expected-all (-> tokens-lib
(ctob/set-active-themes #{"/all"})
(ctob/sets-at-path-all-active? "G-foo"))
expected-partial (-> tokens-lib
(ctob/set-active-themes #{"/partial"})
(ctob/sets-at-path-all-active? "G-foo"))
expected-invalid-none (-> tokens-lib
(ctob/set-active-themes #{"/invalid"})
(ctob/sets-at-path-all-active? "G-foo"))]
(t/is (= :none expected-none))
(t/is (= :all expected-all))
(t/is (= :partial expected-partial))
(t/is (= :none expected-invalid-none)))))
(t/deftest token-theme-in-a-lib
(t/testing "add-token-theme"

View file

@ -6,7 +6,6 @@
(ns app.main.data.tokens
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.changes-builder :as pcb]
[app.common.geom.point :as gpt]
@ -15,11 +14,9 @@
[app.main.data.changes :as dch]
[app.main.data.workspace.shapes :as dwsh]
[app.main.refs :as refs]
[app.main.ui.workspace.tokens.token :as wtt]
[app.main.ui.workspace.tokens.token-set :as wtts]
[app.main.ui.workspace.tokens.update :as wtu]
[beicon.v2.core :as rx]
[clojure.data :as data]
[cuerdas.core :as str]
[potok.v2.core :as ptk]))
@ -51,38 +48,6 @@
;; TOKENS Actions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn toggle-or-apply-token
"Remove any shape attributes from token if they exists.
Othewise apply token attributes."
[shape token]
(let [[shape-leftover token-leftover _matching] (data/diff (:applied-tokens shape) token)]
(merge {} shape-leftover token-leftover)))
(defn token-from-attributes [token attributes]
(->> (map (fn [attr] [attr (wtt/token-identifier token)]) attributes)
(into {})))
(defn unapply-token-id [shape attributes]
(update shape :applied-tokens d/without-keys attributes))
(defn apply-token-to-attributes [{:keys [shape token attributes]}]
(let [token (token-from-attributes token attributes)]
(toggle-or-apply-token shape token)))
(defn apply-token-to-shape
[{:keys [shape token attributes] :as _props}]
(let [applied-tokens (apply-token-to-attributes {:shape shape
:token token
:attributes attributes})]
(update shape :applied-tokens #(merge % applied-tokens))))
(defn maybe-apply-token-to-shape
"When the passed `:token` is non-nil apply it to the `:applied-tokens` on a shape."
[{:keys [shape token _attributes] :as props}]
(if token
(apply-token-to-shape props)
shape))
(defn get-token-data-from-token-id
[id]
(let [workspace-data (deref refs/workspace-data)]

View file

@ -82,7 +82,7 @@
(assoc-in [:workspace-global :picked-shift?] shift?)))))
(defn transform-fill
[state ids color transform]
[state ids color transform & options]
(let [objects (wsh/lookup-page-objects state)
is-text? #(= :text (:type (get objects %)))
@ -118,8 +118,8 @@
(rx/concat
(rx/of (dwu/start-undo-transaction undo-id))
(rx/from (map #(dwt/update-text-with-function % transform-attrs) text-ids))
(rx/of (dwsh/update-shapes shape-ids transform-attrs))
(rx/from (map #(apply dwt/update-text-with-function % transform-attrs options) text-ids))
(rx/of (dwsh/update-shapes shape-ids transform-attrs options))
(rx/of (dwu/commit-undo-transaction undo-id)))))
(defn swap-attrs [shape attr index new-index]
@ -146,7 +146,7 @@
(rx/of (dwsh/update-shapes shape-ids transform-attrs)))))))
(defn change-fill
[ids color position]
[ids color position & options]
(ptk/reify ::change-fill
ptk/WatchEvent
(watch [_ state _]
@ -155,18 +155,18 @@
(cond-> (not (contains? shape :fills))
(assoc :fills []))
(assoc-in [:fills position] (into {} attrs))))]
(transform-fill state ids color change-fn)))))
(apply transform-fill state ids color change-fn options)))))
(defn change-fill-and-clear
[ids color]
[ids color & options]
(ptk/reify ::change-fill-and-clear
ptk/WatchEvent
(watch [_ state _]
(let [set (fn [shape attrs] (assoc shape :fills [attrs]))]
(transform-fill state ids color set)))))
(apply transform-fill state ids color set options)))))
(defn add-fill
[ids color]
[ids color & options]
(dm/assert!
"expected a valid color struct"
@ -182,10 +182,10 @@
(let [add (fn [shape attrs]
(-> shape
(update :fills #(into [attrs] %))))]
(transform-fill state ids color add)))))
(apply transform-fill state ids color add options)))))
(defn remove-fill
[ids color position]
[ids color position & options]
(dm/assert!
"expected a valid color struct"
@ -203,10 +203,10 @@
(mapv second)))
remove (fn [shape _] (update shape :fills remove-fill-by-index position))]
(transform-fill state ids color remove)))))
(apply transform-fill state ids color remove options)))))
(defn remove-all-fills
[ids color]
[ids color & options]
(dm/assert!
"expected a valid color struct"
@ -220,7 +220,7 @@
ptk/WatchEvent
(watch [_ state _]
(let [remove-all (fn [shape _] (assoc shape :fills []))]
(transform-fill state ids color remove-all)))))
(apply transform-fill state ids color remove-all options)))))
(defn change-hide-fill-on-export
[ids hide-fill-on-export]
@ -237,7 +237,7 @@
(d/merge shape attrs)
shape))))))))
(defn change-stroke
[ids attrs index]
[ids attrs index & options]
(ptk/reify ::change-stroke
ptk/WatchEvent
(watch [_ _ _]
@ -286,7 +286,8 @@
(assoc :strokes [])
:always
(assoc-in [:strokes index] new-attrs))))))))))
(assoc-in [:strokes index] new-attrs))))
options))))))
(defn change-shadow
[ids attrs index]

View file

@ -786,7 +786,6 @@
(rx/map #(reset-component %) (rx/from ids))
(rx/of (dwu/commit-undo-transaction undo-id)))))))
(defn update-component
"Modify the component linked to the shape with the given id, in the
current page, so that all attributes of its shapes are equal to the

View file

@ -465,8 +465,10 @@
([]
(apply-modifiers nil))
([{:keys [modifiers undo-transation? stack-undo? ignore-constraints ignore-snap-pixel undo-group]
:or {undo-transation? true stack-undo? false ignore-constraints false ignore-snap-pixel false}}]
([{:keys [modifiers undo-transation? stack-undo? ignore-constraints
ignore-snap-pixel ignore-touched undo-group]
:or {undo-transation? true stack-undo? false ignore-constraints false
ignore-snap-pixel false ignore-touched false}}]
(ptk/reify ::apply-modifiers
ptk/WatchEvent
(watch [_ state _]
@ -515,6 +517,7 @@
{:reg-objects? true
:stack-undo? stack-undo?
:ignore-tree ignore-tree
:ignore-touched ignore-touched
:undo-group undo-group
;; Attributes that can change in the transform. This way we don't have to check
;; all the attributes

View file

@ -260,13 +260,13 @@
(rx/of (with-meta event (meta it)))))))))
(defn update-layout
[ids changes]
[ids changes & options]
(ptk/reify ::update-layout
ptk/WatchEvent
(watch [_ _ _]
(let [undo-id (js/Symbol)]
(rx/of (dwu/start-undo-transaction undo-id)
(dwsh/update-shapes ids (d/patch-object changes))
(dwsh/update-shapes ids (d/patch-object changes) options)
(ptk/data-event :layout/update {:ids ids})
(dwu/commit-undo-transaction undo-id))))))
@ -516,7 +516,7 @@
(assoc :layout-item-v-sizing :fix))))
(defn update-layout-child
[ids changes]
[ids changes & options]
(ptk/reify ::update-layout-child
ptk/WatchEvent
(watch [_ state _]
@ -525,8 +525,8 @@
parent-ids (->> ids (map #(cfh/get-parent-id objects %)))
undo-id (js/Symbol)]
(rx/of (dwu/start-undo-transaction undo-id)
(dwsh/update-shapes ids (d/patch-object changes))
(dwsh/update-shapes children-ids (partial fix-child-sizing objects changes))
(dwsh/update-shapes ids (d/patch-object changes) options)
(dwsh/update-shapes children-ids (partial fix-child-sizing objects changes) options)
(dwsh/update-shapes
parent-ids
(fn [parent objects]
@ -534,7 +534,7 @@
(fix-parent-sizing objects (set ids) changes)
(cond-> (ctl/grid-layout? parent)
(ctl/assign-cells objects))))
{:with-objects? true})
(merge options {:with-objects? true}))
(ptk/data-event :layout/update {:ids ids})
(dwu/commit-undo-transaction undo-id))))))

View file

@ -432,7 +432,7 @@
(txt/transform-nodes (some-fn txt/is-text-node? txt/is-paragraph-node?) migrate-node content))
(defn update-text-with-function
[id update-node-fn]
[id update-node-fn & options]
(ptk/reify ::update-text-with-function
ptk/UpdateEvent
(update [_ state]
@ -464,7 +464,7 @@
(-> shape
(dissoc :fills)
(d/update-when :content update-content)))]
(rx/of (dwsh/update-shapes shape-ids update-shape)))))
(rx/of (dwsh/update-shapes shape-ids update-shape options)))))
ptk/EffectEvent
(effect [_ state _]

View file

@ -301,7 +301,7 @@
(defn update-dimensions
"Change size of shapes, from the sideber options form.
Will ignore pixel snap used in the options side panel"
[ids attr value]
[ids attr value & options]
(dm/assert! (number? value))
(dm/assert!
"expected valid coll of uuids"
@ -324,7 +324,7 @@
ptk/WatchEvent
(watch [_ _ _]
(rx/of (dwm/apply-modifiers)))))
(rx/of (dwm/apply-modifiers options)))))
(defn change-orientation
"Change orientation of shapes, from the sidebar options form.
@ -402,7 +402,7 @@
"Rotate shapes a fixed angle, from a keyboard action."
([ids rotation]
(increase-rotation ids rotation nil))
([ids rotation params]
([ids rotation params & options]
(ptk/reify ::increase-rotation
ptk/WatchEvent
(watch [_ state _]
@ -411,7 +411,7 @@
shapes (->> ids (map #(get objects %)))]
(rx/concat
(rx/of (dwm/set-delta-rotation-modifiers rotation shapes params))
(rx/of (dwm/apply-modifiers))))))))
(rx/of (dwm/apply-modifiers options))))))))
;; -- Move ----------------------------------------------------------

View file

@ -502,6 +502,14 @@
(def workspace-active-theme-paths
(l/derived (d/nilf ctob/get-active-theme-paths) tokens-lib))
(defn token-sets-at-path-all-active?
[prefixed-path]
(l/derived
(fn [lib]
(when lib
(ctob/sets-at-path-all-active? lib prefixed-path)))
tokens-lib))
(def workspace-active-theme-paths-no-hidden
(l/derived #(disj % ctob/hidden-token-theme-path) workspace-active-theme-paths))

View file

@ -13,9 +13,9 @@
[app.common.types.shape :as cts]
[app.common.types.shape.layout :as ctl]
[app.common.types.shape.radius :as ctsr]
[app.common.types.token :as cto]
[app.common.types.tokens-lib :as ctob]
[app.main.constants :refer [size-presets]]
[app.main.data.tokens :as dt]
[app.main.data.workspace :as udw]
[app.main.data.workspace.interactions :as dwi]
[app.main.data.workspace.shapes :as dwsh]
@ -342,7 +342,7 @@
(let [token-value (wtc/maybe-resolve-token-value token)]
(st/emit!
(change-radius (fn [shape]
(-> (dt/unapply-token-id shape (wtty/token-attributes :border-radius))
(-> (cto/unapply-token-id shape (wtty/token-attributes :border-radius))
(ctsr/set-radius-1 token-value))))))))
on-radius-1-change
@ -352,9 +352,9 @@
(let [token-value (wtc/maybe-resolve-token-value value)]
(st/emit!
(change-radius (fn [shape]
(-> (dt/maybe-apply-token-to-shape {:token (when token-value value)
:shape shape
:attributes (wtty/token-attributes :border-radius)})
(-> (cto/maybe-apply-token-to-shape {:token (when token-value value)
:shape shape
:attributes (wtty/token-attributes :border-radius)})
(ctsr/set-radius-1 (or token-value value)))))))))
on-radius-multi-change

View file

@ -95,20 +95,9 @@
(when (ctsr/has-radius? shape)
(ctsr/set-radius-1 shape value)))
{:reg-objects? true
:ignore-touched true
:attrs ctt/border-radius-keys}))
(defn update-opacity [value shape-ids]
(when (<= 0 value 1)
(dwsh/update-shapes shape-ids #(assoc % :opacity value))))
(defn update-rotation [value shape-ids]
(ptk/reify ::update-shape-rotation
ptk/WatchEvent
(watch [_ _ _]
(rx/of
(udw/trigger-bounding-box-cloaking shape-ids)
(udw/increase-rotation shape-ids value)))))
(defn update-shape-radius-single-corner [value shape-ids attributes]
(dwsh/update-shapes shape-ids
(fn [shape]
@ -117,8 +106,23 @@
(:rx shape) (ctsr/switch-to-radius-4)
:always (ctsr/set-radius-4 (first attributes) value))))
{:reg-objects? true
:ignore-touched true
:attrs [:rx :ry :r1 :r2 :r3 :r4]}))
(defn update-opacity [value shape-ids]
(when (<= 0 value 1)
(dwsh/update-shapes shape-ids
#(assoc % :opacity value)
{:ignore-touched true})))
(defn update-rotation [value shape-ids]
(ptk/reify ::update-shape-rotation
ptk/WatchEvent
(watch [_ _ _]
(rx/of
(udw/trigger-bounding-box-cloaking shape-ids)
(udw/increase-rotation shape-ids value nil :ignore-touched true)))))
(defn update-stroke-width
[value shape-ids]
(dwsh/update-shapes shape-ids
@ -126,6 +130,7 @@
(when (seq (:strokes shape))
(assoc-in shape [:strokes 0 :stroke-width] value)))
{:reg-objects? true
:ignore-touched true
:attrs [:strokes]}))
(defn update-color [f value shape-ids]
@ -133,7 +138,7 @@
(tinycolor/valid-color)
(tinycolor/->hex)
(str "#"))]
(f shape-ids {:color color} 0)))
(apply f shape-ids {:color color} 0 [:ignore-touched true])))
(defn update-fill
[value shape-ids]
@ -156,8 +161,8 @@
ptk/WatchEvent
(watch [_ _ _]
(rx/of
(when (:width attributes) (dwt/update-dimensions shape-ids :width value))
(when (:height attributes) (dwt/update-dimensions shape-ids :height value))))))
(when (:width attributes) (dwt/update-dimensions shape-ids :width value :ignore-touched true))
(when (:height attributes) (dwt/update-dimensions shape-ids :height value :ignore-touched true))))))
(defn- attributes->layout-gap [attributes value]
(let [layout-gap (-> (set/intersection attributes #{:column-gap :row-gap})
@ -165,7 +170,9 @@
{:layout-gap layout-gap}))
(defn update-layout-padding [value shape-ids attrs]
(dwsl/update-layout shape-ids {:layout-padding (zipmap attrs (repeat value))}))
(dwsl/update-layout shape-ids
{:layout-padding (zipmap attrs (repeat value))}
:ignore-touched true))
(defn update-layout-spacing [value shape-ids attributes]
(ptk/reify ::update-layout-spacing
@ -177,7 +184,9 @@
(map :id)))
layout-attributes (attributes->layout-gap attributes value)]
(rx/of
(dwsl/update-layout layout-shape-ids layout-attributes))))))
(dwsl/update-layout layout-shape-ids
layout-attributes
:ignore-touched true))))))
(defn update-shape-position [value shape-ids attributes]
(ptk/reify ::update-shape-position
@ -195,4 +204,4 @@
:layout-item-max-w value
:layout-item-max-h value}
(select-keys attributes))]
(dwsl/update-layout-child shape-ids props)))))
(dwsl/update-layout-child shape-ids props :ignore-touched true)))))

View file

@ -254,41 +254,41 @@
[{:keys [state set-state]}]
(let [{:keys [theme-path]} @state
[_ theme-group theme-name] theme-path
ordered-token-sets (mf/deref refs/workspace-ordered-token-sets)
token-sets (mf/deref refs/workspace-token-sets-tree)
theme (mf/deref (refs/workspace-token-theme theme-group theme-name))
theme-state (mf/use-state theme)
lib (-> (ctob/make-tokens-lib)
(ctob/add-theme @theme-state)
(ctob/add-sets ordered-token-sets)
(ctob/activate-theme (:group @theme-state) (:name @theme-state)))
;; Form / Modal handlers
on-back #(set-state (constantly {:type :themes-overview}))
on-submit #(st/emit! (wdt/update-token-theme [(:group theme) (:name theme)] %))
{:keys [dropdown-open? _on-open-dropdown on-close-dropdown on-toggle-dropdown]} (wtco/use-dropdown-open-state)
theme-state (mf/use-state theme)
disabled? (-> (:name @theme-state)
(str/trim)
(str/empty?))
token-set-active? (mf/use-callback
(mf/deps theme-state)
(fn [set-name]
(get-in @theme-state [:sets set-name])))
on-toggle-token-set (mf/use-callback
(mf/deps theme-state)
(fn [set-name]
(swap! theme-state #(ctob/toggle-set % set-name))))
on-click-token-set (mf/use-callback
(mf/deps on-toggle-token-set)
(fn [prefixed-set-path-str]
(let [set-name (ctob/prefixed-set-path-string->set-name-string prefixed-set-path-str)]
(on-toggle-token-set set-name))))
on-change-field (fn [field value]
(swap! theme-state #(assoc % field value)))
on-save-form (mf/use-callback
(mf/deps theme-state on-submit)
(fn [e]
(dom/prevent-default e)
(let [theme (-> @theme-state
(update :name str/trim)
(update :group str/trim)
(update :description str/trim))]
(when-not (str/empty? (:name theme))
(on-submit theme)))
(on-back)))
on-change-field
(mf/use-fn
(fn [field value]
(swap! theme-state #(assoc % field value))))
on-save-form
(mf/use-callback
(mf/deps theme-state on-submit)
(fn [e]
(dom/prevent-default e)
(let [theme (-> @theme-state
(update :name str/trim)
(update :group str/trim)
(update :description str/trim))]
(when-not (str/empty? (:name theme))
(on-submit theme)))
(on-back)))
close-modal
(mf/use-fn
(fn [e]
@ -300,7 +300,33 @@
(mf/deps theme on-back)
(fn []
(st/emit! (wdt/delete-token-theme (:group theme) (:name theme)))
(on-back)))]
(on-back)))
;; Sets tree handlers
token-set-group-active?
(mf/use-callback
(mf/deps theme-state)
(fn [prefixed-path]
(ctob/sets-at-path-all-active? lib prefixed-path)))
token-set-active?
(mf/use-callback
(mf/deps theme-state)
(fn [set-name]
(get-in @theme-state [:sets set-name])))
on-toggle-token-set
(mf/use-callback
(mf/deps theme-state)
(fn [set-name]
(swap! theme-state #(ctob/toggle-set % set-name))))
on-click-token-set
(mf/use-callback
(mf/deps on-toggle-token-set)
(fn [prefixed-set-path-str]
(let [set-name (ctob/prefixed-set-path-string->set-name-string prefixed-set-path-str)]
(on-toggle-token-set set-name))))]
[:div {:class (stl/css :themes-modal-wrapper)}
[:> heading* {:level 2 :typography "headline-medium" :class (stl/css :themes-modal-title)}
@ -327,6 +353,7 @@
{:token-sets token-sets
:token-set-selected? (constantly false)
:token-set-active? token-set-active?
:token-set-group-active? token-set-group-active?
:on-select on-click-token-set
:on-toggle-token-set on-toggle-token-set
:origin "theme-modal"

View file

@ -7,6 +7,7 @@
(ns app.main.ui.workspace.tokens.sets
(:require-macros [app.main.style :as stl])
(:require
[app.common.data.macros :as dm]
[app.common.types.tokens-lib :as ctob]
[app.main.data.tokens :as wdt]
[app.main.refs :as refs]
@ -68,9 +69,28 @@
:auto-focus true
:default-value default-value}]))
(mf/defc checkbox
[{:keys [checked aria-label on-click]}]
(let [all? (true? checked)
mixed? (= checked "mixed")
checked? (or all? mixed?)]
[:div {:role "checkbox"
:aria-checked (dm/str checked)
:tabindex 0
:class (stl/css-case :checkbox-style true
:checkbox-checked-style checked?)
:on-click on-click}
(when checked?
[:> icon*
{:aria-label aria-label
:class (stl/css :check-icon)
:size "s"
:id (if mixed? ic/remove ic/tick)}])]))
(mf/defc sets-tree-set-group
[{:keys [label tree-depth tree-path selected? collapsed? editing? on-edit on-edit-reset on-edit-submit]}]
[{:keys [label tree-depth tree-path active? selected? collapsed? editing? on-edit on-edit-reset on-edit-submit]}]
(let [editing?' (editing? tree-path)
active?' (active? tree-path)
on-context-menu
(mf/use-fn
(mf/deps editing? tree-path)
@ -114,9 +134,16 @@
:on-cancel on-edit-reset
:on-create on-edit-reset
:on-submit on-edit-submit'}]
[:div {:class (stl/css :set-name)
:on-double-click on-double-click}
label])]))
[:*
[:div {:class (stl/css :set-name)
:on-double-click on-double-click}
label]
[:& checkbox
{:checked (case active?'
:all true
:partial "mixed"
:none false)
:arial-label (tr "workspace.token.select-set")}]])]))
(mf/defc sets-tree-set
[{:keys [set label tree-depth tree-path selected? on-select active? on-toggle editing? on-edit on-edit-reset on-edit-submit]}]
@ -173,18 +200,14 @@
[:div {:class (stl/css :set-name)
:on-double-click on-double-click}
label]
[:button {:type "button"
:on-click on-checkbox-click
:class (stl/css-case :checkbox-style true
:checkbox-checked-style active?')}
(when active?'
[:> icon* {:aria-label (tr "workspace.token.select-set")
:class (stl/css :check-icon)
:size "s"
:id ic/tick}])]])]))
[:& checkbox
{:on-click on-checkbox-click
:arial-label (tr "workspace.token.select-set")
:checked active?'}]])]))
(mf/defc sets-tree
[{:keys [active?
group-active?
editing?
on-edit
on-edit-reset
@ -227,6 +250,7 @@
set-group?
[:& sets-tree-set-group
{:selected? (selected? tree-path)
:active? group-active?
:on-select on-select
:label set-fname
:collapsed? collapsed?
@ -249,6 +273,7 @@
:selected? selected?
:on-toggle on-toggle
:active? active?
:group-active? group-active?
:editing? editing?
:on-edit on-edit
:on-edit-reset on-edit-reset
@ -261,6 +286,7 @@
on-update-token-set-group
token-set-selected?
token-set-active?
token-set-group-active?
on-create-token-set
on-toggle-token-set
origin
@ -268,10 +294,9 @@
context]
:as _props}]
(let [{:keys [editing? new? on-edit on-reset] :as ctx} (or context (sets-context/use-context))]
[:ul {:class (stl/css :sets-list)}
(if (and
(= origin "theme-modal")
(empty? token-sets))
[:fieldset {:class (stl/css :sets-list)}
(if (and (= origin "theme-modal")
(empty? token-sets))
[:> text* {:as "span" :typography "body-small" :class (stl/css :empty-state-message-sets)}
(tr "workspace.token.no-sets-create")]
(if (and (= origin "theme-modal")
@ -284,6 +309,7 @@
:selected? token-set-selected?
:on-select on-select
:active? token-set-active?
:group-active? token-set-group-active?
:on-toggle on-toggle-token-set
:editing? editing?
:on-edit on-edit
@ -314,11 +340,15 @@
token-set-active? (mf/use-fn
(mf/deps active-token-set-names)
(fn [set-name]
(get active-token-set-names set-name)))]
(get active-token-set-names set-name)))
token-set-group-active? (mf/use-fn
(fn [prefixed-path]
@(refs/token-sets-at-path-all-active? prefixed-path)))]
[:& controlled-sets-list
{:token-sets token-sets
:token-set-selected? token-set-selected?
:token-set-active? token-set-active?
:token-set-group-active? token-set-group-active?
:on-select on-select-token-set-click
:origin "set-panel"
:on-toggle-token-set on-toggle-token-set-click

View file

@ -2,8 +2,8 @@
(:require
[app.common.types.token :as ctt]
[app.main.data.workspace.shape-layout :as dwsl]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.undo :as dwu]
[app.main.refs :as refs]
[app.main.ui.workspace.tokens.changes :as wtch]
[app.main.ui.workspace.tokens.style-dictionary :as wtsd]
[app.main.ui.workspace.tokens.token-set :as wtts]
@ -112,8 +112,8 @@
update-infos)))
shapes-update-info))
(defn update-tokens [resolved-tokens]
(->> @refs/workspace-page-objects
(defn update-tokens [state resolved-tokens]
(->> (wsh/lookup-page-objects state)
(collect-shapes-update-info resolved-tokens)
(actionize-shapes-update-info)))
@ -131,5 +131,5 @@
(let [undo-id (js/Symbol)]
(rx/concat
(rx/of (dwu/start-undo-transaction undo-id))
(update-tokens sd-tokens)
(update-tokens state sd-tokens)
(rx/of (dwu/commit-undo-transaction undo-id))))))))))

View file

@ -57,7 +57,7 @@
(fn [cause]
(js/console.log "[error]:" cause))
(fn [_]
(js/console.log "[complete]"))))
#_(js/console.debug "[complete]"))))
(doseq [event events]
(ptk/emit! store event))

View file

@ -0,0 +1,397 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns frontend-tests.logic.components-and-tokens
(:require
[app.common.geom.point :as geom]
[app.common.math :as mth]
[app.common.test-helpers.components :as cthc]
[app.common.test-helpers.compositions :as ctho]
[app.common.test-helpers.files :as cthf]
[app.common.test-helpers.ids-map :as cthi]
[app.common.test-helpers.shapes :as cths]
[app.common.test-helpers.tokens :as ctht]
[app.common.types.tokens-lib :as ctob]
[app.main.data.tokens :as dt]
[app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.selection :as dws]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.ui.workspace.tokens.changes :as wtch]
[app.main.ui.workspace.tokens.update :as wtu]
[cljs.test :as t :include-macros true]
[frontend-tests.helpers.pages :as thp]
[frontend-tests.helpers.state :as ths]
[frontend-tests.tokens.helpers.state :as tohs]
[frontend-tests.tokens.helpers.tokens :as toht]))
(t/use-fixtures :each
{:before thp/reset-idmap!})
(defn- setup-base-file
[]
(-> (cthf/sample-file :file1)
(ctht/add-tokens-lib)
(ctht/update-tokens-lib #(-> %
(ctob/add-set (ctob/make-token-set :name "test-token-set"))
(ctob/add-theme (ctob/make-token-theme :name "test-theme"
:sets #{"test-token-set"}))
(ctob/set-active-themes #{"/test-theme"})
(ctob/add-token-in-set "test-token-set"
(ctob/make-token :name "test-token-1"
:type :border-radius
:value 25))
(ctob/add-token-in-set "test-token-set"
(ctob/make-token :name "test-token-2"
:type :border-radius
:value 50))
(ctob/add-token-in-set "test-token-set"
(ctob/make-token :name "test-token-3"
:type :border-radius
:value 75))))
(ctho/add-frame :frame1)
(ctht/apply-token-to-shape :frame1 "test-token-1" [:rx :ry] [:rx :ry] 25)))
(defn- setup-file-with-main
[]
(-> (setup-base-file)
(cthc/make-component :component1 :frame1)))
(defn- setup-file-with-copy
[]
(-> (setup-file-with-main)
(cthc/instantiate-component :component1 :c-frame1)))
(t/deftest create-component-with-token
(t/async
done
(let [;; ==== Setup
file (setup-base-file)
store (ths/setup-store file)
;; ==== Action
events
[(dws/select-shape (cthi/id :frame1))
(dwl/add-component)]]
(ths/run-store
store done events
(fn [new-state]
(let [;; ==== Get
file' (ths/get-file-from-store new-state)
frame1' (cths/get-shape file' :frame1)
tokens-frame1' (:applied-tokens frame1')]
;; ==== Check
(t/is (= (count tokens-frame1') 2))
(t/is (= (get tokens-frame1' :rx) "test-token-1"))
(t/is (= (get tokens-frame1' :ry) "test-token-1"))
(t/is (= (get frame1' :rx) 25))
(t/is (= (get frame1' :ry) 25))))))))
(t/deftest create-copy-with-token
(t/async
done
(let [;; ==== Setup
file (setup-file-with-main)
store (ths/setup-store file)
;; ==== Action
events
[(dwl/instantiate-component (:id file)
(cthi/id :component1)
(geom/point 0 0))]]
(ths/run-store
store done events
(fn [new-state]
(let [;; ==== Get
selected (wsh/lookup-selected new-state)
c-frame1' (wsh/lookup-shape new-state (first selected))
tokens-frame1' (:applied-tokens c-frame1')]
;; ==== Check
(t/is (= (count tokens-frame1') 2))
(t/is (= (get tokens-frame1' :rx) "test-token-1"))
(t/is (= (get tokens-frame1' :ry) "test-token-1"))
(t/is (= (get c-frame1' :rx) 25))
(t/is (= (get c-frame1' :ry) 25))))))))
(t/deftest change-token-in-main
(t/async
done
(let [;; ==== Setup
file (setup-file-with-copy)
store (ths/setup-store file)
;; ==== Action
events [(wtch/apply-token {:shape-ids [(cthi/id :frame1)]
:attributes #{:rx :ry}
:token (toht/get-token file "test-token-2")
:on-update-shape wtch/update-shape-radius-all})]
step2 (fn [_]
(let [events2 [(dwl/sync-file (:id file) (:id file))]]
(ths/run-store
store done events2
(fn [new-state]
(let [;; ==== Get
file' (ths/get-file-from-store new-state)
c-frame1' (cths/get-shape file' :c-frame1)
tokens-frame1' (:applied-tokens c-frame1')]
;; ==== Check
(t/is (= (count tokens-frame1') 2))
(t/is (= (get tokens-frame1' :rx) "test-token-2"))
(t/is (= (get tokens-frame1' :ry) "test-token-2"))
(t/is (= (get c-frame1' :rx) 50))
(t/is (= (get c-frame1' :ry) 50)))))))]
(tohs/run-store-async
store step2 events identity))))
(t/deftest remove-token-in-main
(t/async
done
(let [;; ==== Setup
file (setup-file-with-copy)
store (ths/setup-store file)
;; ==== Action
events [(wtch/unapply-token {:shape-ids [(cthi/id :frame1)]
:attributes #{:rx :ry}
:token (toht/get-token file "test-token-1")})]
step2 (fn [_]
(let [events2 [(dwl/sync-file (:id file) (:id file))]]
(ths/run-store
store done events2
(fn [new-state]
(let [;; ==== Get
file' (ths/get-file-from-store new-state)
c-frame1' (cths/get-shape file' :c-frame1)
tokens-frame1' (:applied-tokens c-frame1')]
;; ==== Check
(t/is (= (count tokens-frame1') 0))
(t/is (= (get c-frame1' :rx) 25))
(t/is (= (get c-frame1' :ry) 25)))))))]
(tohs/run-store-async
store step2 events identity))))
(t/deftest modify-token
(t/async
done
(let [;; ==== Setup
file (setup-file-with-copy)
store (ths/setup-store file)
;; ==== Action
events [(dt/update-create-token {:token (ctob/make-token :name "test-token-1"
:type :border-radius
:value 66)
:prev-token-name "test-token-1"})]
step2 (fn [_]
(let [events2 [(wtu/update-workspace-tokens)
(dwl/sync-file (:id file) (:id file))]]
(tohs/run-store-async
store done events2
(fn [new-state]
(let [;; ==== Get
file' (ths/get-file-from-store new-state)
c-frame1' (cths/get-shape file' :c-frame1)
tokens-frame1' (:applied-tokens c-frame1')]
;; ==== Check
(t/is (= (count tokens-frame1') 2))
(t/is (= (get tokens-frame1' :rx) "test-token-1"))
(t/is (= (get tokens-frame1' :ry) "test-token-1"))
(t/is (= (get c-frame1' :rx) 66))
(t/is (= (get c-frame1' :ry) 66)))))))]
(tohs/run-store-async
store step2 events identity))))
(t/deftest change-token-in-copy-then-change-main
(t/async
done
(let [;; ==== Setup
file (setup-file-with-copy)
store (ths/setup-store file)
;; ==== Action
events [(wtch/apply-token {:shape-ids [(cthi/id :c-frame1)]
:attributes #{:rx :ry}
:token (toht/get-token file "test-token-2")
:on-update-shape wtch/update-shape-radius-all})
(wtch/apply-token {:shape-ids [(cthi/id :frame1)]
:attributes #{:rx :ry}
:token (toht/get-token file "test-token-3")
:on-update-shape wtch/update-shape-radius-all})]
step2 (fn [_]
(let [events2 [(dwl/sync-file (:id file) (:id file))]]
(ths/run-store
store done events2
(fn [new-state]
(let [;; ==== Get
file' (ths/get-file-from-store new-state)
c-frame1' (cths/get-shape file' :c-frame1)
tokens-frame1' (:applied-tokens c-frame1')]
;; ==== Check
(t/is (= (count tokens-frame1') 2))
(t/is (= (get tokens-frame1' :rx) "test-token-2"))
(t/is (= (get tokens-frame1' :ry) "test-token-2"))
(t/is (= (get c-frame1' :rx) 50))
(t/is (= (get c-frame1' :ry) 50)))))))]
(tohs/run-store-async
store step2 events identity))))
(t/deftest remove-token-in-copy-then-change-main
(t/async
done
(let [;; ==== Setup
file (setup-file-with-copy)
store (ths/setup-store file)
;; ==== Action
events [(wtch/unapply-token {:shape-ids [(cthi/id :c-frame1)]
:attributes #{:rx :ry}
:token (toht/get-token file "test-token-1")})
(wtch/apply-token {:shape-ids [(cthi/id :frame1)]
:attributes #{:rx :ry}
:token (toht/get-token file "test-token-3")
:on-update-shape wtch/update-shape-radius-all})]
step2 (fn [_]
(let [events2 [(dwl/sync-file (:id file) (:id file))]]
(ths/run-store
store done events2
(fn [new-state]
(let [;; ==== Get
file' (ths/get-file-from-store new-state)
c-frame1' (cths/get-shape file' :c-frame1)
tokens-frame1' (:applied-tokens c-frame1')]
;; ==== Check
(t/is (= (count tokens-frame1') 0))
(t/is (= (get c-frame1' :rx) 25))
(t/is (= (get c-frame1' :ry) 25)))))))]
(tohs/run-store-async
store step2 events identity))))
(t/deftest modify-token-all-types
(t/async
done
(let [;; ==== Setup
file (-> (cthf/sample-file :file1)
(ctht/add-tokens-lib)
(ctht/update-tokens-lib #(-> %
(ctob/add-set (ctob/make-token-set :name "test-token-set"))
(ctob/add-theme (ctob/make-token-theme :name "test-theme"
:sets #{"test-token-set"}))
(ctob/set-active-themes #{"/test-theme"})
(ctob/add-token-in-set "token-radius"
(ctob/make-token :name "token-radius"
:type :border-radius
:value 10))
(ctob/add-token-in-set "token-rotation"
(ctob/make-token :name "token-rotation"
:type :rotation
:value 30))
(ctob/add-token-in-set "token-opacity"
(ctob/make-token :name "token-opacity"
:type :opacity
:value 0.7))
(ctob/add-token-in-set "token-stroke-width"
(ctob/make-token :name "token-stroke-width"
:type :stroke-width
:value 2))
(ctob/add-token-in-set "token-color"
(ctob/make-token :name "token-color"
:type :color
:value "#00ff00"))
(ctob/add-token-in-set "token-dimensions"
(ctob/make-token :name "token-dimensions"
:type :dimensions
:value 100))))
(ctho/add-frame :frame1)
(ctht/apply-token-to-shape :frame1 "token-radius" [:rx :ry] [:rx :ry] 10)
(ctht/apply-token-to-shape :frame1 "token-rotation" [:rotation] [:rotation] 30)
(ctht/apply-token-to-shape :frame1 "token-opacity" [:opacity] [:opacity] 0.7)
(ctht/apply-token-to-shape :frame1 "token-stroke-width" [:stroke-width] [:stroke-width] 2)
(ctht/apply-token-to-shape :frame1 "token-color" [:stroke-color] [:stroke-color] "#00ff00")
(ctht/apply-token-to-shape :frame1 "token-color" [:fill] [:fill] "#00ff00")
(ctht/apply-token-to-shape :frame1 "token-dimensions" [:width :height] [:width :height] 100)
(cthc/make-component :component1 :frame1)
(cthc/instantiate-component :component1 :c-frame1))
store (ths/setup-store file)
;; ==== Action
events [(dt/update-create-token {:token (ctob/make-token :name "token-radius"
:type :border-radius
:value 30)
:prev-token-name "token-radius"})
(dt/update-create-token {:token (ctob/make-token :name "token-rotation"
:type :rotation
:value 45)
:prev-token-name "token-rotation"})
(dt/update-create-token {:token (ctob/make-token :name "token-opacity"
:type :opacity
:value 0.9)
:prev-token-name "token-opacity"})
(dt/update-create-token {:token (ctob/make-token :name "token-stroke-width"
:type :stroke-width
:value 8)
:prev-token-name "token-stroke-width"})
(dt/update-create-token {:token (ctob/make-token :name "token-color"
:type :color
:value "#ff0000")
:prev-token-name "token-color"})
(dt/update-create-token {:token (ctob/make-token :name "token-dimensions"
:type :dimensions
:value 200)
:prev-token-name "token-dimensions"})]
step2 (fn [_]
(let [events2 [(wtu/update-workspace-tokens)
(dwl/sync-file (:id file) (:id file))]]
(tohs/run-store-async
store done events2
(fn [new-state]
(let [;; ==== Get
file' (ths/get-file-from-store new-state)
c-frame1' (cths/get-shape file' :c-frame1)
tokens-frame1' (:applied-tokens c-frame1')]
;; ==== Check
(t/is (= (count tokens-frame1') 9))
(t/is (= (get tokens-frame1' :rx) "token-radius"))
(t/is (= (get tokens-frame1' :ry) "token-radius"))
(t/is (= (get tokens-frame1' :rotation) "token-rotation"))
(t/is (= (get tokens-frame1' :opacity) "token-opacity"))
(t/is (= (get tokens-frame1' :stroke-width) "token-stroke-width"))
(t/is (= (get tokens-frame1' :stroke-color) "token-color"))
(t/is (= (get tokens-frame1' :fill) "token-color"))
(t/is (= (get tokens-frame1' :width) "token-dimensions"))
(t/is (= (get tokens-frame1' :height) "token-dimensions"))
(t/is (= (get c-frame1' :rx) 30))
(t/is (= (get c-frame1' :ry) 30))
(t/is (= (get c-frame1' :rotation) 45))
(t/is (= (get c-frame1' :opacity) 0.9))
(t/is (= (get-in c-frame1' [:strokes 0 :stroke-width]) 8))
(t/is (= (get-in c-frame1' [:strokes 0 :stroke-color]) "#ff0000"))
(t/is (= (get-in c-frame1' [:fills 0 :fill-color]) "#ff0000"))
(t/is (mth/close? (get c-frame1' :width) 200))
(t/is (mth/close? (get c-frame1' :height) 200))
(t/is (empty? (:touched c-frame1'))))))))]
(tohs/run-store-async
store step2 events identity))))

View file

@ -4,6 +4,7 @@
[frontend-tests.basic-shapes-test]
[frontend-tests.helpers-shapes-test]
[frontend-tests.logic.comp-remove-swap-slots-test]
[frontend-tests.logic.components-and-tokens]
[frontend-tests.logic.copying-and-duplicating-test]
[frontend-tests.logic.frame-guides-test]
[frontend-tests.logic.groups-test]
@ -28,6 +29,7 @@
(t/run-tests
'frontend-tests.helpers-shapes-test
'frontend-tests.logic.comp-remove-swap-slots-test
'frontend-tests.logic.components-and-tokens
'frontend-tests.logic.copying-and-duplicating-test
'frontend-tests.logic.frame-guides-test
'frontend-tests.logic.groups-test