mirror of
https://github.com/penpot/penpot.git
synced 2025-02-02 04:19:08 -05:00
Merge pull request #231 from tokens-studio/update-token-shapes
Update token shapes
This commit is contained in:
commit
3c7261e75b
7 changed files with 160 additions and 21 deletions
|
@ -672,11 +672,11 @@
|
||||||
(apply-changes-local)))
|
(apply-changes-local)))
|
||||||
|
|
||||||
(defn update-token
|
(defn update-token
|
||||||
[changes token]
|
[changes {:keys [id] :as token} prev-token]
|
||||||
(let [token-id (:id token)]
|
(-> changes
|
||||||
(-> changes
|
(update :redo-changes conj {:type :mod-token :id id :token token})
|
||||||
(update :redo-changes conj {:type :mod-token :id token-id :token token})
|
(update :undo-changes conj {:type :mod-token :id id :token (or prev-token token)})
|
||||||
(apply-changes-local))))
|
(apply-changes-local)))
|
||||||
|
|
||||||
(defn delete-token
|
(defn delete-token
|
||||||
[changes token-id]
|
[changes token-id]
|
||||||
|
|
|
@ -95,14 +95,18 @@
|
||||||
(let [workspace-data (deref refs/workspace-data)]
|
(let [workspace-data (deref refs/workspace-data)]
|
||||||
(get (:tokens workspace-data) id)))
|
(get (:tokens workspace-data) id)))
|
||||||
|
|
||||||
(defn add-token
|
(defn update-create-token
|
||||||
[token]
|
[token]
|
||||||
(let [token (update token :id #(or % (uuid/next)))]
|
(let [token (update token :id #(or % (uuid/next)))]
|
||||||
(ptk/reify ::add-token
|
(ptk/reify ::add-token
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [it _ _]
|
(watch [it _ _]
|
||||||
(let [changes (-> (pcb/empty-changes it)
|
(let [prev-token (get-token-data-from-token-id (:id token))
|
||||||
(pcb/add-token token))]
|
changes (if prev-token
|
||||||
|
(-> (pcb/empty-changes it)
|
||||||
|
(pcb/update-token token prev-token))
|
||||||
|
(-> (pcb/empty-changes it)
|
||||||
|
(pcb/add-token token)))]
|
||||||
(rx/of (dch/commit-changes changes)))))))
|
(rx/of (dch/commit-changes changes)))))))
|
||||||
|
|
||||||
(defn delete-token
|
(defn delete-token
|
||||||
|
@ -122,7 +126,7 @@
|
||||||
(let [new-token (-> (get-token-data-from-token-id id)
|
(let [new-token (-> (get-token-data-from-token-id id)
|
||||||
(dissoc :id)
|
(dissoc :id)
|
||||||
(update :name #(str/concat % "-copy")))]
|
(update :name #(str/concat % "-copy")))]
|
||||||
(add-token new-token)))
|
(update-create-token new-token)))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; TEMP (Move to test)
|
;; TEMP (Move to test)
|
||||||
|
|
|
@ -8,20 +8,17 @@
|
||||||
(:require
|
(:require
|
||||||
[app.common.types.shape.radius :as ctsr]
|
[app.common.types.shape.radius :as ctsr]
|
||||||
[app.common.types.token :as ctt]
|
[app.common.types.token :as ctt]
|
||||||
[app.main.data.tokens :as dt]
|
|
||||||
[app.main.data.workspace :as udw]
|
[app.main.data.workspace :as udw]
|
||||||
[app.main.data.workspace.changes :as dch]
|
[app.main.data.workspace.changes :as dch]
|
||||||
[app.main.data.workspace.shape-layout :as dwsl]
|
[app.main.data.workspace.shape-layout :as dwsl]
|
||||||
[app.main.data.workspace.state-helpers :as wsh]
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
[app.main.data.workspace.transforms :as dwt]
|
[app.main.data.workspace.transforms :as dwt]
|
||||||
[app.main.data.workspace.undo :as dwu]
|
[app.main.data.workspace.undo :as dwu]
|
||||||
[app.main.store :as st]
|
|
||||||
[app.main.ui.workspace.tokens.style-dictionary :as sd]
|
[app.main.ui.workspace.tokens.style-dictionary :as sd]
|
||||||
[app.main.ui.workspace.tokens.token :as wtt]
|
[app.main.ui.workspace.tokens.token :as wtt]
|
||||||
[beicon.v2.core :as rx]
|
[beicon.v2.core :as rx]
|
||||||
[clojure.set :as set]
|
[clojure.set :as set]
|
||||||
[potok.v2.core :as ptk]
|
[potok.v2.core :as ptk]))
|
||||||
[promesa.core :as p]))
|
|
||||||
|
|
||||||
;; Token Updates ---------------------------------------------------------------
|
;; Token Updates ---------------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
(:require-macros [app.main.style :as stl])
|
(:require-macros [app.main.style :as stl])
|
||||||
(:require
|
(:require
|
||||||
["lodash.debounce" :as debounce]
|
["lodash.debounce" :as debounce]
|
||||||
|
[app.main.ui.workspace.tokens.update :as wtu]
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.main.data.modal :as modal]
|
[app.main.data.modal :as modal]
|
||||||
[app.main.data.tokens :as dt]
|
[app.main.data.tokens :as dt]
|
||||||
|
@ -100,7 +101,7 @@ Token names should only contain letters and digits separated by . characters.")}
|
||||||
new-tokens (update tokens token-id merge {:id token-id
|
new-tokens (update tokens token-id merge {:id token-id
|
||||||
:value input
|
:value input
|
||||||
:name token-name})]
|
:name token-name})]
|
||||||
(-> (sd/resolve-tokens+ new-tokens {:debug? true})
|
(-> (sd/resolve-tokens+ new-tokens #_ {:debug? true})
|
||||||
(p/then
|
(p/then
|
||||||
(fn [resolved-tokens]
|
(fn [resolved-tokens]
|
||||||
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens token-id)]
|
(let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens token-id)]
|
||||||
|
@ -240,12 +241,13 @@ Token names should only contain letters and digits separated by . characters.")}
|
||||||
;; The result should be a vector of all resolved validations
|
;; The result should be a vector of all resolved validations
|
||||||
;; We do not handle the error case as it will be handled by the components validations
|
;; We do not handle the error case as it will be handled by the components validations
|
||||||
(when (and (seq result) (not err))
|
(when (and (seq result) (not err))
|
||||||
(let [token (cond-> {:name final-name
|
(let [new-token (cond-> {:name final-name
|
||||||
:type (or (:type token) token-type)
|
:type (or (:type token) token-type)
|
||||||
:value final-value}
|
:value final-value}
|
||||||
final-description (assoc :description final-description)
|
final-description (assoc :description final-description)
|
||||||
(:id token) (assoc :id (:id token)))]
|
(:id token) (assoc :id (:id token)))]
|
||||||
(st/emit! (dt/add-token token))
|
(st/emit! (dt/update-create-token new-token))
|
||||||
|
(st/emit! (wtu/update-workspace-tokens))
|
||||||
(modal/hide!)))))))))]
|
(modal/hide!)))))))))]
|
||||||
[:form
|
[:form
|
||||||
{:on-submit on-submit}
|
{:on-submit on-submit}
|
||||||
|
|
|
@ -111,6 +111,9 @@
|
||||||
|
|
||||||
(defonce !tokens-cache (atom nil))
|
(defonce !tokens-cache (atom nil))
|
||||||
|
|
||||||
|
(defn get-cached-tokens [tokens]
|
||||||
|
(get @!tokens-cache tokens tokens))
|
||||||
|
|
||||||
(defn use-resolved-tokens
|
(defn use-resolved-tokens
|
||||||
"The StyleDictionary process function is async, so we can't use resolved values directly.
|
"The StyleDictionary process function is async, so we can't use resolved values directly.
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,8 @@
|
||||||
:key :rotation}]}}]
|
:key :rotation}]}}]
|
||||||
[:spacing
|
[:spacing
|
||||||
{:title "Spacing"
|
{:title "Spacing"
|
||||||
:attributes ctt/spacing-keys
|
:attributes #{:column-gap :row-gap}
|
||||||
|
:all-attributes ctt/spacing-keys
|
||||||
:on-update-shape wtch/update-layout-spacing
|
:on-update-shape wtch/update-layout-spacing
|
||||||
:modal {:key :tokens/spacing
|
:modal {:key :tokens/spacing
|
||||||
:fields [{:label "Spacing"
|
:fields [{:label "Spacing"
|
||||||
|
|
132
frontend/src/app/main/ui/workspace/tokens/update.cljs
Normal file
132
frontend/src/app/main/ui/workspace/tokens/update.cljs
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
(ns app.main.ui.workspace.tokens.update
|
||||||
|
(:require
|
||||||
|
[app.common.types.token :as ctt]
|
||||||
|
[app.main.data.workspace.shape-layout :as dwsl]
|
||||||
|
[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]
|
||||||
|
[beicon.v2.core :as rx]
|
||||||
|
[clojure.data :as data]
|
||||||
|
[clojure.set :as set]
|
||||||
|
[potok.v2.core :as ptk]))
|
||||||
|
|
||||||
|
;; Constants -------------------------------------------------------------------
|
||||||
|
|
||||||
|
(def filter-existing-values? false)
|
||||||
|
|
||||||
|
(def attributes->shape-update
|
||||||
|
{#{:rx :ry} (fn [v ids _] (wtch/update-shape-radius-all v ids))
|
||||||
|
#{:r1 :r2 :r3 :r4} wtch/update-shape-radius-single-corner
|
||||||
|
ctt/stroke-width-keys wtch/update-stroke-width
|
||||||
|
ctt/sizing-keys wtch/update-shape-dimensions
|
||||||
|
ctt/opacity-keys wtch/update-opacity
|
||||||
|
#{:x :y} wtch/update-shape-position
|
||||||
|
#{:p1 :p2 :p3 :p4} (fn [resolved-value shape-ids attrs]
|
||||||
|
(dwsl/update-layout shape-ids {:layout-padding (zipmap attrs (repeat resolved-value))}))
|
||||||
|
#{:column-gap :row-gap} wtch/update-layout-spacing
|
||||||
|
#{:width :height} wtch/update-shape-dimensions
|
||||||
|
#{:layout-item-min-w :layout-item-min-h :layout-item-max-w :layout-item-max-h} wtch/update-layout-sizing-limits
|
||||||
|
ctt/rotation-keys wtch/update-rotation})
|
||||||
|
|
||||||
|
(def attribute-actions-map
|
||||||
|
(reduce
|
||||||
|
(fn [acc [ks action]]
|
||||||
|
(into acc (map (fn [k] [k action]) ks)))
|
||||||
|
{} attributes->shape-update))
|
||||||
|
|
||||||
|
;; Helpers ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(defn deep-merge
|
||||||
|
"Like d/deep-merge but unions set values."
|
||||||
|
([a b]
|
||||||
|
(cond
|
||||||
|
(map? a) (merge-with deep-merge a b)
|
||||||
|
(set? a) (set/union a b)
|
||||||
|
:else b))
|
||||||
|
([a b & rest]
|
||||||
|
(reduce deep-merge a (cons b rest))))
|
||||||
|
|
||||||
|
;; Data flows ------------------------------------------------------------------
|
||||||
|
|
||||||
|
(defn invert-collect-key-vals
|
||||||
|
[xs resolved-tokens shape]
|
||||||
|
(-> (reduce
|
||||||
|
(fn [acc [k v]]
|
||||||
|
(let [resolved-token (get resolved-tokens v)
|
||||||
|
resolved-value (get resolved-token :resolved-value)
|
||||||
|
skip? (or
|
||||||
|
(not (get resolved-tokens v))
|
||||||
|
(and filter-existing-values? (= (get shape k) resolved-value)))]
|
||||||
|
(if skip?
|
||||||
|
acc
|
||||||
|
(update acc resolved-value (fnil conj #{}) k))))
|
||||||
|
{} xs)))
|
||||||
|
|
||||||
|
(defn split-attribute-groups [attrs-values-map]
|
||||||
|
(reduce
|
||||||
|
(fn [acc [attrs v]]
|
||||||
|
(cond
|
||||||
|
(some attrs #{:rx :ry}) (let [[_ a b] (data/diff #{:rx :ry} attrs)]
|
||||||
|
(cond-> (assoc acc b v)
|
||||||
|
;; Exact match in attrs
|
||||||
|
a (assoc a v)))
|
||||||
|
|
||||||
|
(some attrs #{:widht :height}) (let [[_ a b] (data/diff #{:width :height} attrs)]
|
||||||
|
(cond-> (assoc acc b v)
|
||||||
|
;; Exact match in attrs
|
||||||
|
a (assoc a v)))
|
||||||
|
(some attrs ctt/spacing-keys) (let [[_ rst gap] (data/diff #{:row-gap :column-gap} attrs)
|
||||||
|
[_ position padding] (data/diff #{:p1 :p2 :p3 :p4} rst)]
|
||||||
|
(cond-> acc
|
||||||
|
(seq gap) (assoc gap v)
|
||||||
|
(seq position) (assoc position v)
|
||||||
|
(seq padding) (assoc padding v)))
|
||||||
|
attrs (assoc acc attrs v)))
|
||||||
|
{} attrs-values-map))
|
||||||
|
|
||||||
|
(defn shape-ids-by-values
|
||||||
|
[attrs-values-map object-id]
|
||||||
|
(->> (map (fn [[value attrs]] [attrs {value #{object-id}}]) attrs-values-map)
|
||||||
|
(into {})))
|
||||||
|
|
||||||
|
(defn collect-shapes-update-info [resolved-tokens shapes]
|
||||||
|
(reduce
|
||||||
|
(fn [acc [object-id {:keys [applied-tokens] :as shape}]]
|
||||||
|
(if (seq applied-tokens)
|
||||||
|
(let [applied-tokens (-> (invert-collect-key-vals applied-tokens resolved-tokens shape)
|
||||||
|
(shape-ids-by-values object-id)
|
||||||
|
(split-attribute-groups))]
|
||||||
|
(deep-merge acc applied-tokens))
|
||||||
|
acc))
|
||||||
|
{} shapes))
|
||||||
|
|
||||||
|
(defn actionize-shapes-update-info [shapes-update-info]
|
||||||
|
(mapcat (fn [[attrs update-infos]]
|
||||||
|
(let [action (some attribute-actions-map attrs)]
|
||||||
|
(map
|
||||||
|
(fn [[v shape-ids]]
|
||||||
|
(action v shape-ids attrs))
|
||||||
|
update-infos)))
|
||||||
|
shapes-update-info))
|
||||||
|
|
||||||
|
(defn update-tokens [resolved-tokens]
|
||||||
|
(->> @refs/workspace-page-objects
|
||||||
|
(collect-shapes-update-info resolved-tokens)
|
||||||
|
(actionize-shapes-update-info)))
|
||||||
|
|
||||||
|
(defn update-workspace-tokens []
|
||||||
|
(ptk/reify ::update-workspace-tokens
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state _]
|
||||||
|
(->>
|
||||||
|
(rx/from (wtsd/resolve-tokens+ (get-in state [:workspace-data :tokens])))
|
||||||
|
(rx/mapcat
|
||||||
|
(fn [sd-tokens]
|
||||||
|
(let [undo-id (js/Symbol)]
|
||||||
|
(rx/concat
|
||||||
|
(rx/of (dwu/start-undo-transaction undo-id))
|
||||||
|
(rx/concat
|
||||||
|
(->> (update-tokens sd-tokens)
|
||||||
|
(rx/concat)))
|
||||||
|
(rx/of (dwu/commit-undo-transaction undo-id))))))))))
|
Loading…
Add table
Reference in a new issue