Implement drag & drop for set groups

🚧 PR feedback

♻️ Remove unused functions

♻️ Throw on non-allowed changes

🚧 key fixes

🚧 Fix tests

🚧 FMT

🚧 Add set group test

🚧 Remove 'drop' in name

🚧 Add group tests

🚧 FMT

🚧 Always return changes
This commit is contained in:
Florian Schroedl 2025-01-10 13:42:30 +01:00 committed by Andrés Moya
parent 32f0da7514
commit 19daffd1c0
22 changed files with 1015 additions and 346 deletions

View file

@ -15,10 +15,12 @@
[app.main.data.changes :as dch]
[app.main.data.event :as ev]
[app.main.data.helpers :as dsh]
[app.main.data.notifications :as ntf]
[app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.tokens.selected-set :as dwts]
[app.main.store :as st]
[app.main.ui.workspace.tokens.token-set :as wtts]
[app.main.ui.workspace.tokens.update :as wtu]
[app.util.i18n :refer [tr]]
[beicon.v2.core :as rx]
[cuerdas.core :as str]
[potok.v2.core :as ptk]))
@ -53,21 +55,6 @@
;; TOKENS Actions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn set-selected-token-set-path
[full-path]
(ptk/reify ::set-selected-token-set-path
ptk/UpdateEvent
(update [_ state]
(wtts/assoc-selected-token-set-path state full-path))))
(defn set-selected-token-set-path-from-name
[token-set-name]
(ptk/reify ::set-selected-token-set-path-from-name
ptk/UpdateEvent
(update [_ state]
(->> (ctob/set-name-string->prefixed-set-path-string token-set-name)
(wtts/assoc-selected-token-set-path state)))))
(defn create-token-theme [token-theme]
(let [new-token-theme token-theme]
(ptk/reify ::create-token-theme
@ -131,17 +118,16 @@
(let [changes (-> (pcb/empty-changes it)
(pcb/add-token-set new-token-set))]
(rx/of
(set-selected-token-set-path-from-name (:name new-token-set))
(dwts/set-selected-token-set-name (:name new-token-set))
(dch/commit-changes changes)))))))
(defn rename-token-set-group [from-path-str to-path-str]
(defn rename-token-set-group [set-group-path set-group-fname]
(ptk/reify ::rename-token-set-group
ptk/WatchEvent
(watch [it _state _]
(let [changes (-> (pcb/empty-changes it)
(pcb/rename-token-set-group from-path-str to-path-str))]
(pcb/rename-token-set-group set-group-path set-group-fname))]
(rx/of
(set-selected-token-set-path-from-name to-path-str)
(dch/commit-changes changes))))))
(defn update-token-set [set-name token-set]
@ -153,7 +139,7 @@
changes (-> (pcb/empty-changes it)
(pcb/update-token-set token-set prev-token-set))]
(rx/of
(set-selected-token-set-path-from-name (:name token-set))
(dwts/set-selected-token-set-name (:name token-set))
(dch/commit-changes changes))))))
(defn toggle-token-set [{:keys [token-set-name]}]
@ -165,11 +151,11 @@
(dch/commit-changes changes)
(wtu/update-workspace-tokens))))))
(defn toggle-token-set-group [{:keys [prefixed-path-str]}]
(defn toggle-token-set-group [group-path]
(ptk/reify ::toggle-token-set-group
ptk/WatchEvent
(watch [_ state _]
(let [changes (clt/generate-toggle-token-set-group (pcb/empty-changes) (get-tokens-lib state) prefixed-path-str)]
(let [changes (clt/generate-toggle-token-set-group (pcb/empty-changes) (get-tokens-lib state) group-path)]
(rx/of
(dch/commit-changes changes)
(wtu/update-workspace-tokens))))))
@ -183,7 +169,7 @@
(ctob/get-sets)
(first)
(:name)
(set-selected-token-set-path-from-name))
(dwts/set-selected-token-set-name))
changes (-> (pcb/empty-changes it)
(pcb/with-library-data data)
(pcb/set-tokens-lib lib))]
@ -192,39 +178,66 @@
update-token-set-change
(wtu/update-workspace-tokens))))))
(defn delete-token-set-path [prefixed-full-set-path]
(defn delete-token-set-path [group? path]
(ptk/reify ::delete-token-set-path
ptk/WatchEvent
(watch [it state _]
(let [data (dsh/lookup-file-data state)
changes (-> (pcb/empty-changes it)
(pcb/with-library-data data)
(pcb/delete-token-set-path prefixed-full-set-path))]
(pcb/delete-token-set-path group? path))]
(rx/of
(dch/commit-changes changes)
(wtu/update-workspace-tokens))))))
(defn move-token-set [source-set-name dest-set-name position]
(ptk/reify ::move-token-set
(defn drop-error [{:keys [error to-path]}]
(ptk/reify ::drop-error
ptk/WatchEvent
(watch [_ _ _]
(let [content (case error
:path-exists (tr "errors.drag-drop.set-exists" to-path)
:parent-to-child (tr "errors.drag-drop.parent-to-child")
nil)]
(when content
(rx/of
(ntf/show {:content content
:type :toast
:level :error
:timeout 9000})))))))
(defn drop-token-set-group [drop-opts]
(ptk/reify ::drop-token-set-group
ptk/WatchEvent
(watch [it state _]
(let [tokens-lib (get-tokens-lib state)
prev-before-set-name (ctob/get-neighbor-set-name tokens-lib source-set-name 1)
[source-set-name' dest-set-name'] (if (= :top position)
[source-set-name dest-set-name]
[source-set-name (ctob/get-neighbor-set-name tokens-lib dest-set-name 1)])
changes (-> (pcb/empty-changes it)
(pcb/move-token-set-before source-set-name' dest-set-name' prev-before-set-name))]
(rx/of
(dch/commit-changes changes)
(wtu/update-workspace-tokens))))))
(try
(when-let [changes (clt/generate-move-token-set-group (pcb/empty-changes it) (get-tokens-lib state) drop-opts)]
(rx/of
(dch/commit-changes changes)
(wtu/update-workspace-tokens)))
(catch js/Error e
(rx/of
(drop-error (ex-data e))))))))
(defn drop-token-set [drop-opts]
(ptk/reify ::drop-token-set
ptk/WatchEvent
(watch [it state _]
(try
(when-let [changes (clt/generate-move-token-set (pcb/empty-changes it) (get-tokens-lib state) drop-opts)]
(rx/of
(dch/commit-changes changes)
(some-> (get-in changes [:redo-changes 0 :to-path]) (dwts/set-selected-token-set-name))
(wtu/update-workspace-tokens)))
(catch js/Error e
(rx/of
(drop-error (ex-data e))))))))
(defn update-create-token
[{:keys [token prev-token-name]}]
(ptk/reify ::update-create-token
ptk/WatchEvent
(watch [_ state _]
(let [token-set (wtts/get-selected-token-set state)
(let [token-set (dwts/get-selected-token-set state)
token-set-name (or (:name token-set) "Global")
changes (if (not token-set)
;; No set created add a global set
@ -251,7 +264,7 @@
(st/emit! (ptk/event ::ev/event {::ev/name "create-tokens"}))
(pcb/add-token (pcb/empty-changes) (:name token-set) token))))]
(rx/of
(set-selected-token-set-path-from-name token-set-name)
(dwts/set-selected-token-set-name token-set-name)
(dch/commit-changes changes))))))
(defn delete-token
@ -273,8 +286,7 @@
(ptk/reify ::duplicate-token
ptk/WatchEvent
(watch [_ state _]
(when-let [token (some-> (wtts/get-selected-token-set state)
(ctob/get-token token-name)
(when-let [token (some-> (dwts/get-selected-token-set-token state token-name)
(update :name #(str/concat % "-copy")))]
(rx/of
(update-create-token {:token token}))))))
@ -317,19 +329,3 @@
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:workspace-local :token-set-context-menu] nil))))
;; === Import Export Context Menu
(defn show-import-export-context-menu
[{:keys [position] :as params}]
(dm/assert! (gpt/point? position))
(ptk/reify ::show-import-export-context-menu
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:workspace-local :import-export-context-menu] params))))
(def hide-import-export-set-context-menu
(ptk/reify ::hide-import-export-set-context-menu
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:workspace-local :import-export-set-context-menu] nil))))

View file

@ -0,0 +1,8 @@
(ns app.main.data.workspace.tokens.common
(:require
[app.main.data.helpers :as dsh]))
(defn get-workspace-tokens-lib
[state]
(-> (dsh/lookup-file-data state)
(get :tokens-lib)))

View file

@ -0,0 +1,37 @@
(ns app.main.data.workspace.tokens.selected-set
"The user selected token set in the ui, stored by the `:name` of the set.
Will default to the first set."
(:require
[app.common.types.tokens-lib :as ctob]
[app.main.data.workspace.tokens.common :as dwtc]
[potok.v2.core :as ptk]))
(defn assoc-selected-token-set-name [state set-name]
(assoc-in state [:workspace-local :selected-token-set-name] set-name))
(defn get-selected-token-set-name [state]
(or (get-in state [:workspace-local :selected-token-set-name])
(some-> (dwtc/get-workspace-tokens-lib state)
(ctob/get-sets)
(first)
:name)))
(defn get-selected-token-set [state]
(when-let [set-name (get-selected-token-set-name state)]
(some-> (dwtc/get-workspace-tokens-lib state)
(ctob/get-set set-name))))
(defn get-selected-token-set-token [state token-name]
(some-> (get-selected-token-set state)
(ctob/get-token token-name)))
(defn get-selected-token-set-tokens [state]
(some-> (get-selected-token-set state)
:tokens))
(defn set-selected-token-set-name
[set-name]
(ptk/reify ::set-selected-token-set-path-from-name
ptk/UpdateEvent
(update [_ state]
(assoc-selected-token-set-name state set-name))))

View file

@ -15,8 +15,8 @@
[app.common.types.tokens-lib :as ctob]
[app.config :as cf]
[app.main.data.helpers :as dsh]
[app.main.data.workspace.tokens.selected-set :as dwts]
[app.main.store :as st]
[app.main.ui.workspace.tokens.token-set :as wtts]
[okulary.core :as l]))
;; ---- Global refs
@ -450,11 +450,8 @@
(def workspace-token-themes-no-hidden
(l/derived #(remove ctob/hidden-temporary-theme? %) workspace-token-themes))
(def workspace-selected-token-set-path
(l/derived wtts/get-selected-token-set-path st/state))
(def workspace-token-set-group-selected?
(l/derived wtts/token-group-selected? st/state))
(def workspace-selected-token-set-name
(l/derived dwts/get-selected-token-set-name st/state))
(def workspace-ordered-token-sets
(l/derived #(or (some-> % ctob/get-sets) []) tokens-lib))
@ -466,11 +463,11 @@
(l/derived (d/nilf ctob/get-active-theme-paths) tokens-lib))
(defn token-sets-at-path-all-active
[prefixed-path]
[group-path]
(l/derived
(fn [lib]
(when lib
(ctob/sets-at-path-all-active? lib prefixed-path)))
(ctob/sets-at-path-all-active? lib group-path)))
tokens-lib))
(def workspace-active-theme-paths-no-hidden
@ -485,12 +482,12 @@
(def workspace-selected-token-set-token
(fn [token-name]
(l/derived
#(some-> (wtts/get-selected-token-set %)
(ctob/get-token token-name))
#(dwts/get-selected-token-set-token % token-name)
st/state)))
(def workspace-selected-token-set-tokens
(l/derived #(or (wtts/get-selected-token-set-tokens %) {}) st/state))
(l/derived #(or (dwts/get-selected-token-set-tokens %) {}) st/state))
(def plugins-permissions-peek
(l/derived (fn [state]

View file

@ -207,7 +207,7 @@
(generic-attribute-actions #{:x} "X" (assoc context-data :on-update-shape wtch/update-shape-position))
(generic-attribute-actions #{:y} "Y" (assoc context-data :on-update-shape wtch/update-shape-position))))}))
(defn default-actions [{:keys [token selected-token-set-path]}]
(defn default-actions [{:keys [token selected-token-set-name]}]
(let [{:keys [modal]} (wtty/get-token-properties token)]
[{:title (tr "workspace.token.edit")
:no-selectable true
@ -220,16 +220,16 @@
:position :right
:fields fields
:action "edit"
:selected-token-set-path selected-token-set-path
:selected-token-set-name selected-token-set-name
:token token})))}
{:title (tr "workspace.token.duplicate")
:no-selectable true
:action #(st/emit! (dt/duplicate-token (:name token)))}
{:title (tr "workspace.token.delete")
:no-selectable true
:action #(st/emit! (-> selected-token-set-path
ctob/prefixed-set-path-string->set-name-string
(dt/delete-token (:name token))))}]))
:action #(st/emit! (dt/delete-token
(ctob/prefixed-set-path-string->set-name-string selected-token-set-name)
(:name token)))}]))
(defn selection-actions [{:keys [type token] :as context-data}]
(let [with-actions (get shape-attribute-actions-map (or type (:type token)))
@ -350,13 +350,13 @@
selected-shapes (into [] (keep (d/getf objects)) selected)
token-name (:token-name mdata)
token (mf/deref (refs/workspace-selected-token-set-token token-name))
selected-token-set-path (mf/deref refs/workspace-selected-token-set-path)]
selected-token-set-name (mf/deref refs/workspace-selected-token-set-name)]
[:ul {:class (stl/css :context-list)}
[:& menu-tree {:submenu-offset width
:submenu-direction direction
:token token
:errors errors
:selected-token-set-path selected-token-set-path
:selected-token-set-name selected-token-set-name
:selected-shapes selected-shapes}]]))
(mf/defc token-context-menu

View file

@ -214,7 +214,7 @@
(mf/defc form
{::mf/wrap-props false}
[{:keys [token token-type action selected-token-set-path]}]
[{:keys [token token-type action selected-token-set-name]}]
(let [token (or token {:type token-type})
token-properties (wtty/get-token-properties token)
color? (wtt/color-token? token)
@ -376,11 +376,11 @@
(modal/hide!))))))))
on-delete-token
(mf/use-fn
(mf/deps selected-token-set-path)
(mf/deps selected-token-set-name)
(fn [e]
(dom/prevent-default e)
(modal/hide!)
(st/emit! (dt/delete-token (ctob/prefixed-set-path-string->set-name-string selected-token-set-path) (:name token)))))
(st/emit! (dt/delete-token (ctob/prefixed-set-path-string->set-name-string selected-token-set-name) (:name token)))))
on-cancel
(mf/use-fn

View file

@ -42,7 +42,7 @@
(mf/defc token-update-create-modal
{::mf/wrap-props false}
[{:keys [x y position token token-type action selected-token-set-path] :as _args}]
[{:keys [x y position token token-type action selected-token-set-name] :as _args}]
(let [wrapper-style (use-viewport-position-style x y position)
close-modal (mf/use-fn
(fn []
@ -57,7 +57,7 @@
:aria-label (tr "labels.close")}]
[:& form {:token token
:action action
:selected-token-set-path selected-token-set-path
:selected-token-set-name selected-token-set-name
:token-type token-type}]]))
;; Modals ----------------------------------------------------------------------

View file

@ -8,6 +8,7 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.data.macros :as dm]
[app.common.logic.tokens :as clt]
[app.common.types.tokens-lib :as ctob]
[app.main.data.event :as ev]
[app.main.data.modal :as modal]
@ -308,8 +309,8 @@
token-set-group-active?
(mf/use-callback
(mf/deps theme-state)
(fn [prefixed-path]
(ctob/sets-at-path-all-active? lib prefixed-path)))
(fn [group-path]
(ctob/sets-at-path-all-active? lib group-path)))
token-set-active?
(mf/use-callback
@ -323,6 +324,12 @@
(fn [set-name]
(swap! theme-state #(ctob/toggle-set % set-name))))
on-toggle-token-set-group
(mf/use-callback
(mf/deps theme-state)
(fn [group-path]
(swap! theme-state #(clt/toggle-token-set-group group-path lib %))))
on-click-token-set
(mf/use-callback
(mf/deps on-toggle-token-set)
@ -358,6 +365,7 @@
:token-set-group-active? token-set-group-active?
:on-select on-click-token-set
:on-toggle-token-set on-toggle-token-set
:on-toggle-token-set-group on-toggle-token-set-group
:origin "theme-modal"
:context sets-context/static-context}]]
@ -400,4 +408,5 @@
:aria-label (tr "labels.close")
:variant "action"
:icon "close"}]
[:& themes-modal-body]]]))
[:& sets-context/provider {}
[:& themes-modal-body]]]]))

View file

@ -7,15 +7,18 @@
(ns app.main.ui.workspace.tokens.sets
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.types.tokens-lib :as ctob]
[app.main.data.event :as ev]
[app.main.data.tokens :as wdt]
[app.main.data.workspace.tokens.selected-set :as dwts]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as ic]
[app.main.ui.ds.foundations.typography.text :refer [text*]]
[app.main.ui.hooks :as h]
[app.main.ui.workspace.tokens.sets-context :as sets-context]
[app.util.dom :as dom]
[app.util.i18n :refer [tr]]
@ -27,23 +30,17 @@
(defn on-toggle-token-set-click [token-set-name]
(st/emit! (wdt/toggle-token-set {:token-set-name token-set-name})))
(defn on-toggle-token-set-group-click [prefixed-path-str]
(st/emit! (wdt/toggle-token-set-group {:prefixed-path-str prefixed-path-str})))
(defn on-toggle-token-set-group-click [group-path]
(st/emit! (wdt/toggle-token-set-group group-path)))
(defn on-select-token-set-click [tree-path]
(st/emit! (wdt/set-selected-token-set-path tree-path)))
(defn on-select-token-set-click [set-name]
(st/emit! (dwts/set-selected-token-set-name set-name)))
(defn on-update-token-set [set-name token-set]
(st/emit! (wdt/update-token-set set-name token-set)))
(defn on-update-token-set-group [from-prefixed-path-str to-path-str]
(st/emit!
(wdt/rename-token-set-group
(ctob/prefixed-set-path-string->set-name-string from-prefixed-path-str)
(-> (ctob/prefixed-set-path-string->set-path from-prefixed-path-str)
(butlast)
(ctob/join-set-path)
(ctob/join-set-path-str to-path-str)))))
(defn on-update-token-set-group [set-group-path set-group-fname]
(st/emit! (wdt/rename-token-set-group set-group-path set-group-fname)))
(defn on-create-token-set [_ token-set]
(st/emit! (ptk/event ::ev/event {::ev/name "create-tokens-set"}))
@ -105,58 +102,83 @@
:icon-id (if mixed? ic/remove ic/tick)}])]))
(mf/defc sets-tree-set-group
[{:keys [label tree-depth tree-path active? selected? collapsed? editing? on-toggle on-edit on-edit-reset on-edit-submit]}]
(let [editing?' (editing? tree-path)
active?' (active? tree-path)
[{:keys [label tree-depth tree-path active? selected? draggable? on-toggle-collapse on-toggle editing-id editing? on-edit on-edit-reset on-edit-submit collapsed-paths tree-index]}]
(let [active?' (active? tree-path)
editing?' (editing? editing-id)
collapsed? (some? (get @collapsed-paths tree-path))
can-edit? (:can-edit (deref refs/permissions))
on-context-menu
(mf/use-fn
(mf/deps editing? tree-path can-edit?)
(mf/deps editing?' editing-id can-edit?)
(fn [event]
(dom/prevent-default event)
(dom/stop-propagation event)
(when (and can-edit? (not editing?'))
(when-not editing?'
(st/emit!
(wdt/show-token-set-context-menu
{:position (dom/get-client-position event)
:prefixed-set-path tree-path})))))
:group? true
:path tree-path})))))
on-collapse-click
(mf/use-fn
(fn [event]
(dom/stop-propagation event)
(swap! collapsed? not)))
(on-toggle-collapse tree-path)))
on-double-click
(mf/use-fn
(mf/deps tree-path can-edit?)
(fn []
(when can-edit?
(on-edit tree-path))))
(mf/deps editing-id can-edit?)
#(on-edit editing-id))
on-checkbox-click
(mf/use-fn
(mf/deps on-toggle tree-path can-edit?)
(fn []
(when can-edit?
(on-toggle tree-path))))
#(on-toggle tree-path))
on-edit-submit'
(mf/use-fn
(mf/deps tree-path on-edit-submit can-edit?)
(fn [e]
(when can-edit? (on-edit-submit tree-path e))))]
[:div {:role "button"
#(on-edit-submit tree-path %))
on-drop
(mf/use-fn
(mf/deps tree-index collapsed-paths)
(fn [position data]
(let [props {:from-index (:index data)
:to-index tree-index
:position position
:collapsed-paths @collapsed-paths}]
(if (:group? data)
(st/emit! (wdt/drop-token-set-group props))
(st/emit! (wdt/drop-token-set props))))))
[dprops dref]
(h/use-sortable
:data-type "penpot/token-set"
:on-drop on-drop
:data {:index tree-index
:group? true}
:detect-center? true
:draggable? draggable?)]
[:div {:ref dref
:role "button"
:data-testid "tokens-set-group-item"
:style {"--tree-depth" tree-depth}
:class (stl/css-case :set-item-container true
:set-item-group true
:selected-set selected?)
:selected-set selected?
:dnd-over (= (:over dprops) :center)
:dnd-over-top (= (:over dprops) :top)
:dnd-over-bot (= (:over dprops) :bot))
:on-context-menu on-context-menu}
[:> icon-button*
{:class (stl/css :set-item-group-collapse-button)
:on-click on-collapse-click
:aria-label (tr "labels.collapse")
:icon (if @collapsed? "arrow-right" "arrow-down")
:icon (if collapsed? "arrow-right" "arrow-down")
:variant "action"}]
(if editing?'
[:& editing-label
@ -178,18 +200,20 @@
: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]}]
(let [set-name (.-name set)
editing?' (editing? tree-path)
active?' (some? (active? set-name))
[{:keys [set label tree-depth tree-path tree-index selected? on-select active? draggable? on-toggle editing-id editing? on-edit on-edit-reset on-edit-submit collapsed-paths]}]
(let [set-name (.-name set)
editing?' (editing? editing-id)
active?' (some? (active? set-name))
can-edit? (:can-edit (deref refs/permissions))
on-click
(mf/use-fn
(mf/deps editing?' tree-path)
(fn [event]
(dom/stop-propagation event)
(when-not editing?'
(on-select tree-path))))
(on-select set-name))))
on-context-menu
(mf/use-fn
(mf/deps editing?' tree-path can-edit?)
@ -200,23 +224,64 @@
(st/emit!
(wdt/show-token-set-context-menu
{:position (dom/get-client-position event)
:prefixed-set-path tree-path})))))
on-double-click (mf/use-fn
(mf/deps tree-path)
#(on-edit tree-path))
on-checkbox-click (mf/use-fn
(mf/deps set-name)
(fn [event]
(dom/stop-propagation event)
(on-toggle set-name)))
on-edit-submit' (mf/use-fn
(mf/deps set on-edit-submit)
#(on-edit-submit set-name (ctob/update-name set %)))]
[:div {:role "button"
:group? false
:path tree-path})))))
on-double-click
(mf/use-fn
(mf/deps editing-id)
(fn []
(on-edit editing-id)))
on-checkbox-click
(mf/use-fn
(mf/deps set-name)
(fn [event]
(dom/stop-propagation event)
(on-toggle set-name)))
on-edit-submit'
(mf/use-fn
(mf/deps set on-edit-submit)
#(on-edit-submit set-name (ctob/update-name set %)))
on-drag
(mf/use-fn
(mf/deps tree-path)
(fn [_]
(when-not selected?
(on-select tree-path))))
on-drop
(mf/use-fn
(mf/deps tree-index collapsed-paths)
(fn [position data]
(let [props {:from-index (:index data)
:to-index tree-index
:position position
:collapsed-paths @collapsed-paths}]
(if (:group? data)
(st/emit! (wdt/drop-token-set-group props))
(st/emit! (wdt/drop-token-set props))))))
[dprops dref]
(h/use-sortable
:data-type "penpot/token-set"
:on-drag on-drag
:on-drop on-drop
:data {:index tree-index
:group? false}
:draggable? draggable?)]
[:div {:ref dref
:role "button"
:data-testid "tokens-set-item"
:style {"--tree-depth" tree-depth}
:class (stl/css-case :set-item-container true
:selected-set selected?)
:selected-set selected?
:dnd-over (= (:over dprops) :center)
:dnd-over-top (= (:over dprops) :top)
:dnd-over-bot (= (:over dprops) :bot))
:on-click on-click
:on-context-menu on-context-menu
:aria-checked active?'}
@ -241,82 +306,75 @@
:checked active?'}]])]))
(mf/defc sets-tree
[{:keys [active?
[{:keys [draggable?
active?
selected?
group-active?
editing?
on-edit
on-edit-reset
on-edit-submit-set
on-edit-submit-group
on-select
on-toggle-set
on-toggle-set-group
selected?
set-node
set-path
tree-depth
tree-path]
:or {tree-depth 0}
set-node]
:as props}]
(let [[set-path-prefix set-fname] (some-> set-path (ctob/split-set-str-path-prefix))
set? (instance? ctob/TokenSet set-node)
set-group? (= ctob/set-group-prefix set-path-prefix)
root? (= tree-depth 0)
collapsed? (mf/use-state false)
children? (and
(or root? set-group?)
(not @collapsed?))]
[:*
(cond
root? nil
set?
[:& sets-tree-set
{:set set-node
:active? active?
:selected? (selected? tree-path)
:on-select on-select
:label set-fname
:tree-path (or tree-path set-path)
:tree-depth tree-depth
:editing? editing?
:on-toggle on-toggle-set
:on-edit on-edit
:on-edit-reset on-edit-reset
:on-edit-submit on-edit-submit-set}]
set-group?
[:& sets-tree-set-group
{:selected? (selected? tree-path)
:active? group-active?
:on-select on-select
:label set-fname
:collapsed? collapsed?
:tree-path (or tree-path set-path)
:tree-depth tree-depth
:editing? editing?
:on-toggle on-toggle-set-group
:on-edit on-edit
:on-edit-reset on-edit-reset
:on-edit-submit on-edit-submit-group}])
(when children?
(for [[set-path set-node] set-node
:let [tree-path' (ctob/join-set-path-str tree-path set-path)]]
[:& sets-tree
{:key tree-path'
:set-path set-path
:set-node set-node
:tree-depth (when-not root? (inc tree-depth))
:tree-path tree-path'
:on-select on-select
:selected? selected?
:on-toggle-set on-toggle-set
:on-toggle-set-group on-toggle-set-group
:active? active?
:group-active? group-active?
:editing? editing?
:on-edit on-edit
:on-edit-reset on-edit-reset
:on-edit-submit-set on-edit-submit-set
:on-edit-submit-group on-update-token-set-group}]))]))
(let [{:keys [on-edit] :as ctx} (sets-context/use-context)
collapsed-paths (mf/use-state #{})
collapsed?
(mf/use-fn
#(contains? @collapsed-paths %))
on-toggle-collapse
(mf/use-fn
(fn [path]
(swap! collapsed-paths #(if (contains? % path)
(disj % path)
(conj % path)))))]
(for [[index {:keys [group? path parent-path depth] :as node}]
(d/enumerate (ctob/walk-sets-tree-seq set-node :walk-children? #(contains? @collapsed-paths %)))]
(if (not group?)
(let [editing-id (sets-context/set-path->id path)]
[:& sets-tree-set
{:key editing-id
:set (:set node)
:label (last path)
:active? active?
:selected? (selected? (get-in node [:set :name]))
:draggable? draggable?
:on-select on-select
:tree-path path
:tree-depth depth
:tree-index index
:tree-parent-path parent-path
:on-toggle on-toggle-set
:editing-id editing-id
:editing? editing?
:on-edit on-edit
:on-edit-reset on-edit-reset
:on-edit-submit on-edit-submit-set
:collapsed-paths collapsed-paths}])
(let [editing-id (sets-context/set-group-path->id path)]
[:& sets-tree-set-group
{:key editing-id
:label (last path)
:active? group-active?
:selected? false
:draggable? draggable?
:on-select on-select
:tree-path path
:tree-depth depth
:tree-index index
:tree-parent-path parent-path
:editing-id editing-id
:editing? editing?
:on-edit on-edit
:on-edit-reset on-edit-reset
:on-edit-submit on-edit-submit-group
:collapsed? (collapsed? path)
:on-toggle-collapse on-toggle-collapse
:on-toggle on-toggle-set-group
:collapsed-paths collapsed-paths}])))))
(mf/defc controlled-sets-list
[{:keys [token-sets
@ -332,19 +390,23 @@
on-select
context]
:as _props}]
(let [{:keys [editing? new? on-edit on-reset] :as ctx} (or context (sets-context/use-context))]
(let [{:keys [editing? new? on-edit on-reset] :as ctx} (or context (sets-context/use-context))
theme-modal? (= origin "theme-modal")
can-edit? (:can-edit (deref refs/permissions))
draggable? (and (not theme-modal?) can-edit?)]
[:fieldset {:class (stl/css :sets-list)}
(if (and (= origin "theme-modal")
(if (and 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")
(if (and theme-modal?
(empty? token-sets))
[:> text* {:as "span" :typography "body-small" :class (stl/css :empty-state-message-sets)}
(tr "workspace.token.no-sets-create")]
[:*
[:& sets-tree
{:set-node token-sets
{:draggable? draggable?
:set-node token-sets
:selected? token-set-selected?
:on-select on-select
:active? token-set-active?
@ -368,22 +430,23 @@
:on-edit-reset on-reset
:on-edit-submit on-create-token-set}])]))]))
(mf/defc sets-list
[{:keys []}]
(let [token-sets (mf/deref refs/workspace-token-sets-tree)
selected-token-set-path (mf/deref refs/workspace-selected-token-set-path)
selected-token-set-name (mf/deref refs/workspace-selected-token-set-name)
token-set-selected? (mf/use-fn
(mf/deps token-sets selected-token-set-path)
(fn [tree-path]
(= tree-path selected-token-set-path)))
(mf/deps token-sets selected-token-set-name)
(fn [set-name]
(= set-name selected-token-set-name)))
active-token-set-names (mf/deref refs/workspace-active-set-names)
token-set-active? (mf/use-fn
(mf/deps active-token-set-names)
(fn [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)))]
(fn [group-path]
@(refs/token-sets-at-path-all-active group-path)))]
[:& controlled-sets-list
{:token-sets token-sets
:token-set-selected? token-set-selected?

View file

@ -6,8 +6,15 @@
(ns app.main.ui.workspace.tokens.sets-context
(:require
[app.common.data.macros :as dm]
[rumext.v2 :as mf]))
(defn set-group-path->id [set-group-path]
(dm/str "group-" set-group-path))
(defn set-path->id [set-path]
(dm/str "set-" set-path))
(def initial {:editing-id nil
:new? false})
@ -35,7 +42,8 @@
(mf/deps editing-id)
#(= editing-id %))
on-edit (mf/use-fn
#(swap! ctx assoc :editing-id %))
(fn [editing-id]
(reset! ctx (assoc @ctx :editing-id editing-id))))
on-create (mf/use-fn
#(swap! ctx assoc :editing-id (random-uuid) :new? true))
on-reset (mf/use-fn

View file

@ -7,7 +7,6 @@
(ns app.main.ui.workspace.tokens.sets-context-menu
(:require-macros [app.main.style :as stl])
(:require
[app.common.types.tokens-lib :as ctob]
[app.main.data.tokens :as wdt]
[app.main.refs :as refs]
[app.main.store :as st]
@ -35,13 +34,20 @@
[:span {:class (stl/css :title)} title]])
(mf/defc menu
[{:keys [prefixed-set-path]}]
[{:keys [group? path]}]
(let [{:keys [on-edit]} (sets-context/use-context)
edit-name (mf/use-fn #(on-edit prefixed-set-path))
delete-set (mf/use-fn #(st/emit! (wdt/delete-token-set-path prefixed-set-path)))]
edit-name (mf/use-fn
(mf/deps group?)
(fn []
(let [path (if group?
(sets-context/set-group-path->id path)
(sets-context/set-path->id path))]
(on-edit path))))
delete-set (mf/use-fn #(st/emit! (wdt/delete-token-set-path group? path)))]
[:ul {:class (stl/css :context-list)}
(when (ctob/prefixed-set-path-final-group? prefixed-set-path)
[:& menu-entry {:title "Add set to this group" :on-click js/console.log}])
;; TODO Implement
;; (when (ctob/prefixed-set-path-final-group? prefixed-set-path)
;; [:& menu-entry {:title "Add set to this group" :on-click js/console.log}])
[:& menu-entry {:title (tr "labels.rename") :on-click edit-name}]
[:& menu-entry {:title (tr "labels.delete") :on-click delete-set}]]))
@ -63,4 +69,5 @@
:ref dropdown-ref
:style {:top top :left left}
:on-context-menu prevent-default}
[:& menu {:prefixed-set-path (:prefixed-set-path mdata)}]]]))
[:& menu {:group? (:group? mdata)
:path (:path mdata)}]]]))

View file

@ -8,7 +8,6 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.types.tokens-lib :as ctob]
[app.main.data.event :as ev]
[app.main.data.modal :as modal]
@ -269,7 +268,7 @@
selected-token-set-tokens (mf/deref refs/workspace-selected-token-set-tokens)
selected-token-set-path (mf/deref refs/workspace-selected-token-set-path)
selected-token-set-name (mf/deref refs/workspace-selected-token-set-name)
token-groups (mf/with-memo [tokens selected-token-set-tokens]
(-> (select-keys tokens (keys selected-token-set-tokens))
@ -277,7 +276,7 @@
[:*
[:& token-context-menu]
[:& title-bar {:all-clickable true
:title (dm/str "TOKENS - " (ctob/set-path->set-name selected-token-set-path))}]
:title (tr "workspace.token.tokens-section-title" selected-token-set-name)}]
(for [{:keys [token-key token-type-props tokens]} (concat (:filled token-groups)
(:empty token-groups))]

View file

@ -43,38 +43,3 @@
(defn get-active-theme-sets-tokens-names-map [state]
(when-let [lib (get-workspace-tokens-lib state)]
(ctob/get-active-themes-set-tokens lib)))
;; === Set selection
(defn get-selected-token-set-path [state]
(or (get-in state [:workspace-local :selected-token-set-path])
(some-> (get-workspace-tokens-lib state)
(ctob/get-sets)
(first)
(ctob/get-set-prefixed-path-string))))
(defn get-selected-token-set-node [state]
(when-let [path (some-> (get-selected-token-set-path state)
(ctob/split-token-set-path))]
(some-> (get-workspace-tokens-lib state)
(ctob/get-in-set-tree path))))
(defn get-selected-token-set [state]
(let [set-node (get-selected-token-set-node state)]
(when (instance? ctob/TokenSet set-node)
set-node)))
(defn get-selected-token-set-group [state]
(let [set-node (get-selected-token-set-node state)]
(when (and set-node (not (instance? ctob/TokenSet set-node)))
set-node)))
(defn get-selected-token-set-tokens [state]
(some-> (get-selected-token-set state)
:tokens))
(defn token-group-selected? [state]
(some? (get-selected-token-set-group state)))
(defn assoc-selected-token-set-path [state id]
(assoc-in state [:workspace-local :selected-token-set-path] id))