mirror of
https://github.com/penpot/penpot.git
synced 2025-06-05 23:21:38 +02:00
🎉 Token Sets dnd re-ordering
This commit is contained in:
parent
a85a7d2b2f
commit
85fee87bc4
8 changed files with 169 additions and 28 deletions
|
@ -295,6 +295,12 @@
|
||||||
[:name :string]
|
[:name :string]
|
||||||
[:token-set ::ctot/token-set]]]
|
[:token-set ::ctot/token-set]]]
|
||||||
|
|
||||||
|
[:move-token-set-before
|
||||||
|
[:map {:title "MoveTokenSetBefore"}
|
||||||
|
[:type [:= :move-token-set-before]]
|
||||||
|
[:set-name :string]
|
||||||
|
[:before-set-name [:maybe :string]]]]
|
||||||
|
|
||||||
[:del-token-set
|
[:del-token-set
|
||||||
[:map {:title "DelTokenSetChange"}
|
[:map {:title "DelTokenSetChange"}
|
||||||
[:type [:= :del-token-set]]
|
[:type [:= :del-token-set]]
|
||||||
|
@ -865,6 +871,12 @@
|
||||||
(cond-> lib'
|
(cond-> lib'
|
||||||
path-changed? (ctob/update-set-name name (:name token-set)))))))
|
path-changed? (ctob/update-set-name name (:name token-set)))))))
|
||||||
|
|
||||||
|
(defmethod process-change :move-token-set-before
|
||||||
|
[data {:keys [set-name before-set-name]}]
|
||||||
|
(update data :tokens-lib #(-> %
|
||||||
|
(ctob/ensure-tokens-lib)
|
||||||
|
(ctob/move-set-before set-name before-set-name))))
|
||||||
|
|
||||||
(defmethod process-change :del-token-set
|
(defmethod process-change :del-token-set
|
||||||
[data {:keys [name]}]
|
[data {:keys [name]}]
|
||||||
(update data :tokens-lib #(-> %
|
(update data :tokens-lib #(-> %
|
||||||
|
|
|
@ -764,6 +764,13 @@
|
||||||
(update :undo-changes conj {:type :add-token-set :token-set prev-token-theme})
|
(update :undo-changes conj {:type :add-token-set :token-set prev-token-theme})
|
||||||
(apply-changes-local))))
|
(apply-changes-local))))
|
||||||
|
|
||||||
|
(defn move-token-set-before
|
||||||
|
[changes set-name before-set-name prev-before-set-name]
|
||||||
|
(-> changes
|
||||||
|
(update :redo-changes conj {:type :move-token-set-before :set-name set-name :before-set-name before-set-name})
|
||||||
|
(update :undo-changes conj {:type :move-token-set-before :set-name set-name :before-set-name prev-before-set-name})
|
||||||
|
(apply-changes-local)))
|
||||||
|
|
||||||
(defn add-token
|
(defn add-token
|
||||||
[changes set-name token]
|
[changes set-name token]
|
||||||
(-> changes
|
(-> changes
|
||||||
|
|
|
@ -167,6 +167,17 @@
|
||||||
|
|
||||||
;; === Token Set
|
;; === Token Set
|
||||||
|
|
||||||
|
(def set-separator "/")
|
||||||
|
|
||||||
|
(defn get-token-set-path [path]
|
||||||
|
(get-path path set-separator))
|
||||||
|
|
||||||
|
(defn get-token-set-group-str [path]
|
||||||
|
(get-groups-str path set-separator))
|
||||||
|
|
||||||
|
(defn split-token-set-path [path]
|
||||||
|
(split-path path set-separator))
|
||||||
|
|
||||||
(defprotocol ITokenSet
|
(defprotocol ITokenSet
|
||||||
(add-token [_ token] "add a token at the end of the list")
|
(add-token [_ token] "add a token at the end of the list")
|
||||||
(update-token [_ token-name f] "update a token in the list")
|
(update-token [_ token-name f] "update a token in the list")
|
||||||
|
@ -257,11 +268,14 @@
|
||||||
(add-set [_ token-set] "add a set to the library, at the end")
|
(add-set [_ token-set] "add a set to the library, at the end")
|
||||||
(update-set [_ set-name f] "modify a set in the ilbrary")
|
(update-set [_ set-name f] "modify a set in the ilbrary")
|
||||||
(delete-set [_ set-name] "delete a set in the library")
|
(delete-set [_ set-name] "delete a set in the library")
|
||||||
|
(move-set-before [_ set-name before-set-name] "move a set with `set-name` before a set with `before-set-name` in the library.
|
||||||
|
When `before-set-name` is nil, move set to bottom")
|
||||||
(set-count [_] "get the total number if sets in the library")
|
(set-count [_] "get the total number if sets in the library")
|
||||||
(get-set-tree [_] "get a nested tree of all sets in the library")
|
(get-set-tree [_] "get a nested tree of all sets in the library")
|
||||||
(get-sets [_] "get an ordered sequence of all sets in the library")
|
(get-sets [_] "get an ordered sequence of all sets in the library")
|
||||||
(get-ordered-set-names [_] "get an ordered sequence of all sets names in the library")
|
(get-ordered-set-names [_] "get an ordered sequence of all sets names in the library")
|
||||||
(get-set [_ set-name] "get one set looking for name")
|
(get-set [_ set-name] "get one set looking for name")
|
||||||
|
(get-neighbor-set-name [_ set-name index-offset] "get neighboring set name offset by `index-offset`")
|
||||||
(get-set-group [_ set-group-path] "get the attributes of a set group"))
|
(get-set-group [_ set-group-path] "get the attributes of a set group"))
|
||||||
|
|
||||||
(def schema:token-set-node
|
(def schema:token-set-node
|
||||||
|
@ -462,8 +476,8 @@
|
||||||
ITokenSets
|
ITokenSets
|
||||||
(add-set [_ token-set]
|
(add-set [_ token-set]
|
||||||
(dm/assert! "expected valid token set" (check-token-set! token-set))
|
(dm/assert! "expected valid token set" (check-token-set! token-set))
|
||||||
(let [path (get-path token-set "/")
|
(let [path (get-token-set-path token-set)
|
||||||
groups-str (get-groups-str token-set "/")]
|
groups-str (get-token-set-group-str token-set)]
|
||||||
(TokensLib. (d/oassoc-in sets path token-set)
|
(TokensLib. (d/oassoc-in sets path token-set)
|
||||||
(cond-> set-groups
|
(cond-> set-groups
|
||||||
(not (str/empty? groups-str))
|
(not (str/empty? groups-str))
|
||||||
|
@ -472,7 +486,7 @@
|
||||||
active-themes)))
|
active-themes)))
|
||||||
|
|
||||||
(update-set [this set-name f]
|
(update-set [this set-name f]
|
||||||
(let [path (split-path set-name "/")
|
(let [path (split-token-set-path set-name)
|
||||||
set (get-in sets path)]
|
set (get-in sets path)]
|
||||||
(if set
|
(if set
|
||||||
(let [set' (-> (make-token-set (f set))
|
(let [set' (-> (make-token-set (f set))
|
||||||
|
@ -490,12 +504,30 @@
|
||||||
this)))
|
this)))
|
||||||
|
|
||||||
(delete-set [_ set-name]
|
(delete-set [_ set-name]
|
||||||
(let [path (split-path set-name "/")]
|
(let [path (split-token-set-path set-name)]
|
||||||
(TokensLib. (d/dissoc-in sets path)
|
(TokensLib. (d/dissoc-in sets path)
|
||||||
set-groups ;; TODO remove set-group if needed
|
set-groups ;; TODO remove set-group if needed
|
||||||
themes
|
themes
|
||||||
active-themes)))
|
active-themes)))
|
||||||
|
|
||||||
|
;; TODO Handle groups and nesting
|
||||||
|
(move-set-before [this set-name before-set-name]
|
||||||
|
(let [source-path (split-token-set-path set-name)
|
||||||
|
token-set (-> (get-set this set-name)
|
||||||
|
(assoc :modified-at (dt/now)))
|
||||||
|
target-path (split-token-set-path before-set-name)]
|
||||||
|
(if before-set-name
|
||||||
|
(TokensLib. (d/oassoc-in-before sets target-path source-path token-set)
|
||||||
|
set-groups ;; TODO remove set-group if needed
|
||||||
|
themes
|
||||||
|
active-themes)
|
||||||
|
(TokensLib. (-> sets
|
||||||
|
(d/dissoc-in source-path)
|
||||||
|
(d/oassoc-in source-path token-set))
|
||||||
|
set-groups ;; TODO remove set-group if needed
|
||||||
|
themes
|
||||||
|
active-themes))))
|
||||||
|
|
||||||
(get-set-tree [_]
|
(get-set-tree [_]
|
||||||
sets)
|
sets)
|
||||||
|
|
||||||
|
@ -513,6 +545,13 @@
|
||||||
(let [path (split-path set-name "/")]
|
(let [path (split-path set-name "/")]
|
||||||
(get-in sets path)))
|
(get-in sets path)))
|
||||||
|
|
||||||
|
(get-neighbor-set-name [this set-name index-offset]
|
||||||
|
(let [sets (get-ordered-set-names this)
|
||||||
|
index (d/index-of sets set-name)
|
||||||
|
neighbor-set-name (when index
|
||||||
|
(nth sets (+ index-offset index) nil))]
|
||||||
|
neighbor-set-name))
|
||||||
|
|
||||||
(get-set-group [_ set-group-path]
|
(get-set-group [_ set-group-path]
|
||||||
(get set-groups set-group-path))
|
(get set-groups set-group-path))
|
||||||
|
|
||||||
|
|
|
@ -79,8 +79,32 @@
|
||||||
(let [args {:name 777
|
(let [args {:name 777
|
||||||
:description 999}]
|
:description 999}]
|
||||||
(t/is (thrown-with-msg? Exception #"expected valid token set"
|
(t/is (thrown-with-msg? Exception #"expected valid token set"
|
||||||
(apply ctob/make-token-set args))))))
|
(apply ctob/make-token-set args)))))
|
||||||
|
|
||||||
|
(t/deftest move-token-set
|
||||||
|
(let [tokens-lib (-> (ctob/make-tokens-lib)
|
||||||
|
(ctob/add-set (ctob/make-token-set :name "A"))
|
||||||
|
(ctob/add-set (ctob/make-token-set :name "B"))
|
||||||
|
(ctob/add-set (ctob/make-token-set :name "Move")))
|
||||||
|
original-order (into [] (ctob/get-ordered-set-names tokens-lib))
|
||||||
|
move (fn [set-name before-set-name]
|
||||||
|
(->> (ctob/move-set-before tokens-lib set-name before-set-name)
|
||||||
|
(ctob/get-ordered-set-names)
|
||||||
|
(into [])))]
|
||||||
|
;; TODO Nested moving doesn't work as expected
|
||||||
|
(t/testing "regular moving"
|
||||||
|
(t/is (= ["A" "Move" "B"] (move "Move" "B")))
|
||||||
|
(t/is (= ["B" "A" "Move"] (move "A" "Move"))))
|
||||||
|
|
||||||
|
(t/testing "move to bottom"
|
||||||
|
(t/is (= ["B" "Move" "A"] (move "A" nil))))
|
||||||
|
|
||||||
|
(t/testing "no move expected"
|
||||||
|
(t/is (= original-order (move "Move" "Move"))))
|
||||||
|
|
||||||
|
(t/testing "ignore invalid moves"
|
||||||
|
(t/is (= original-order (move "A" "foo/bar/baz")))
|
||||||
|
(t/is (= original-order (move "Missing" "Move")))))))
|
||||||
|
|
||||||
(t/testing "token-theme"
|
(t/testing "token-theme"
|
||||||
(t/deftest make-token-theme
|
(t/deftest make-token-theme
|
||||||
|
|
|
@ -204,6 +204,21 @@
|
||||||
(dch/commit-changes changes)
|
(dch/commit-changes changes)
|
||||||
(wtu/update-workspace-tokens))))))
|
(wtu/update-workspace-tokens))))))
|
||||||
|
|
||||||
|
(defn move-token-set [source-set-name dest-set-name position]
|
||||||
|
(ptk/reify ::move-token-set
|
||||||
|
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))))))
|
||||||
|
|
||||||
(defn update-create-token
|
(defn update-create-token
|
||||||
[{:keys [token prev-token-name]}]
|
[{:keys [token prev-token-name]}]
|
||||||
(ptk/reify ::update-create-token
|
(ptk/reify ::update-create-token
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
[app.main.data.tokens :as wdt]
|
[app.main.data.tokens :as wdt]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
|
[app.main.ui.hooks :as h]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.main.ui.workspace.tokens.sets-context :as sets-context]
|
[app.main.ui.workspace.tokens.sets-context :as sets-context]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
|
@ -79,26 +80,56 @@
|
||||||
set? true #_(= type :set)
|
set? true #_(= type :set)
|
||||||
group? false #_(= type :group)
|
group? false #_(= type :group)
|
||||||
editing-node? (editing? name)
|
editing-node? (editing? name)
|
||||||
on-select (mf/use-callback
|
|
||||||
(mf/deps editing-node?)
|
on-click
|
||||||
(fn [event]
|
(mf/use-callback
|
||||||
(dom/stop-propagation event)
|
(mf/deps editing-node?)
|
||||||
(when-not editing-node?
|
(fn [event]
|
||||||
(on-select name))))
|
(dom/stop-propagation event)
|
||||||
on-context-menu (mf/use-callback
|
(when-not editing-node?
|
||||||
(mf/deps editing-node? name)
|
(on-select name))))
|
||||||
(fn [event]
|
|
||||||
(dom/prevent-default event)
|
on-context-menu
|
||||||
(dom/stop-propagation event)
|
(mf/use-callback
|
||||||
(when-not editing-node?
|
(mf/deps editing-node? name)
|
||||||
(st/emit!
|
(fn [event]
|
||||||
(wdt/show-token-set-context-menu
|
(dom/prevent-default event)
|
||||||
{:position (dom/get-client-position event)
|
(dom/stop-propagation event)
|
||||||
:token-set-name name})))))]
|
(when-not editing-node?
|
||||||
[:div {:class (stl/css :set-item-container)
|
(st/emit!
|
||||||
:on-click on-select
|
(wdt/show-token-set-context-menu
|
||||||
|
{:position (dom/get-client-position event)
|
||||||
|
:token-set-name name})))))
|
||||||
|
|
||||||
|
on-drag
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps name)
|
||||||
|
(fn [_]
|
||||||
|
(when-not selected?
|
||||||
|
(on-select name))))
|
||||||
|
|
||||||
|
on-drop
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps name)
|
||||||
|
(fn [position data]
|
||||||
|
(st/emit! (wdt/move-token-set (:name data) name position))))
|
||||||
|
|
||||||
|
[dprops dref]
|
||||||
|
(h/use-sortable
|
||||||
|
:data-type "penpot/token-set"
|
||||||
|
:on-drag on-drag
|
||||||
|
:on-drop on-drop
|
||||||
|
:data {:name name}
|
||||||
|
:draggable? true)]
|
||||||
|
[:div {:ref dref
|
||||||
|
:class (stl/css-case :set-item-container true
|
||||||
|
:dnd-over (= (:over dprops) :center)
|
||||||
|
:dnd-over-top (= (:over dprops) :top)
|
||||||
|
:dnd-over-bot (= (:over dprops) :bot))
|
||||||
|
:on-click on-click
|
||||||
:on-double-click #(on-edit name)
|
:on-double-click #(on-edit name)
|
||||||
:on-context-menu on-context-menu}
|
:on-context-menu on-context-menu
|
||||||
|
:data-name name}
|
||||||
[:div {:class (stl/css-case :set-item-group group?
|
[:div {:class (stl/css-case :set-item-group group?
|
||||||
:set-item-set set?
|
:set-item-set set?
|
||||||
:selected-set selected?)}
|
:selected-set selected?)}
|
||||||
|
@ -191,7 +222,7 @@
|
||||||
(let [token-sets (mf/deref refs/workspace-ordered-token-sets)
|
(let [token-sets (mf/deref refs/workspace-ordered-token-sets)
|
||||||
selected-token-set-id (mf/deref refs/workspace-selected-token-set-id)
|
selected-token-set-id (mf/deref refs/workspace-selected-token-set-id)
|
||||||
token-set-selected? (mf/use-callback
|
token-set-selected? (mf/use-callback
|
||||||
(mf/deps selected-token-set-id)
|
(mf/deps token-sets selected-token-set-id)
|
||||||
(fn [set-name]
|
(fn [set-name]
|
||||||
(= set-name selected-token-set-id)))
|
(= set-name selected-token-set-id)))
|
||||||
active-token-set-ids (mf/deref refs/workspace-active-set-names)
|
active-token-set-ids (mf/deref refs/workspace-active-set-names)
|
||||||
|
|
|
@ -17,6 +17,17 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: var(--layer-row-foreground-color);
|
color: var(--layer-row-foreground-color);
|
||||||
padding-left: $s-20;
|
padding-left: $s-20;
|
||||||
|
border: $s-2 solid transparent;
|
||||||
|
|
||||||
|
&.dnd-over-bot {
|
||||||
|
border-bottom: $s-2 solid var(--layer-row-foreground-color-hover);
|
||||||
|
}
|
||||||
|
&.dnd-over-top {
|
||||||
|
border-top: $s-2 solid var(--layer-row-foreground-color-hover);
|
||||||
|
}
|
||||||
|
&.dnd-over {
|
||||||
|
border: $s-2 solid var(--layer-row-foreground-color-hover);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.set-item-set,
|
.set-item-set,
|
||||||
|
|
|
@ -8,12 +8,14 @@
|
||||||
(:require-macros [app.main.style :as stl])
|
(:require-macros [app.main.style :as stl])
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
|
[app.common.types.tokens-lib :as ctob]
|
||||||
[app.main.data.modal :as modal]
|
[app.main.data.modal :as modal]
|
||||||
[app.main.data.tokens :as dt]
|
[app.main.data.tokens :as dt]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.color-bullet :refer [color-bullet]]
|
[app.main.ui.components.color-bullet :refer [color-bullet]]
|
||||||
[app.main.ui.components.title-bar :refer [title-bar]]
|
[app.main.ui.components.title-bar :refer [title-bar]]
|
||||||
|
[app.main.ui.hooks :as h]
|
||||||
[app.main.ui.hooks.resize :refer [use-resize-hook]]
|
[app.main.ui.hooks.resize :refer [use-resize-hook]]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.main.ui.workspace.sidebar.assets.common :as cmm]
|
[app.main.ui.workspace.sidebar.assets.common :as cmm]
|
||||||
|
@ -32,8 +34,7 @@
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[okulary.core :as l]
|
[okulary.core :as l]
|
||||||
[rumext.v2 :as mf]
|
[rumext.v2 :as mf]
|
||||||
[shadow.resource]
|
[shadow.resource]))
|
||||||
[app.common.types.tokens-lib :as ctob]))
|
|
||||||
|
|
||||||
(def lens:token-type-open-status
|
(def lens:token-type-open-status
|
||||||
(l/derived (l/in [:workspace-tokens :open-status]) st/state))
|
(l/derived (l/in [:workspace-tokens :open-status]) st/state))
|
||||||
|
@ -205,7 +206,8 @@
|
||||||
:on-collapsed #(swap! open? not)}
|
:on-collapsed #(swap! open? not)}
|
||||||
[:& add-set-button {:on-open on-open}]]]
|
[:& add-set-button {:on-open on-open}]]]
|
||||||
(when @open?
|
(when @open?
|
||||||
[:& sets-list])]]))
|
[:& h/sortable-container {}
|
||||||
|
[:& sets-list]])]]))
|
||||||
|
|
||||||
(mf/defc tokens-explorer
|
(mf/defc tokens-explorer
|
||||||
[_props]
|
[_props]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue