mirror of
https://github.com/penpot/penpot.git
synced 2025-07-03 08:37:21 +02:00
♻️ Remove sets context abstraction
This commit is contained in:
parent
b228438127
commit
e8c85d13ff
12 changed files with 579 additions and 478 deletions
|
@ -691,6 +691,7 @@ used for managing active sets without a user created theme.")
|
|||
(filter some?)
|
||||
first))))
|
||||
|
||||
;; DEPRECATED
|
||||
(defn walk-sets-tree-seq
|
||||
"Walk sets tree as a flat list.
|
||||
|
||||
|
@ -739,6 +740,67 @@ used for managing active sets without a user created theme.")
|
|||
(cons item (mapcat #(walk % (assoc opts :parent path :depth (inc depth))) v'))))))))))]
|
||||
(walk (or nodes (d/ordered-map)) nil)))
|
||||
|
||||
|
||||
(defn sets-tree-seq
|
||||
"Get tokens sets tree as a flat list
|
||||
|
||||
Options:
|
||||
`:skip-children-pred`: predicate to skip iterating over a set groups children by checking the path of the set group
|
||||
`:new-editing-set-path`: append a an item with `:new?` at the given path"
|
||||
|
||||
[tree & {:keys [skip-children-pred new-at-path]
|
||||
:or {skip-children-pred (constantly false)}}]
|
||||
(let [walk (fn walk [[k v :as node] parent depth]
|
||||
(lazy-seq
|
||||
(cond
|
||||
;; New set
|
||||
(= :is-new k)
|
||||
(let [tset (make-token-set :name (if (empty? parent)
|
||||
""
|
||||
(join-set-path parent)))]
|
||||
[{:is-new true
|
||||
:is-group false
|
||||
:id ""
|
||||
:parent-path parent
|
||||
:token-set tset
|
||||
:depth depth}])
|
||||
|
||||
;; Set
|
||||
(and v (instance? TokenSet v))
|
||||
(let [name (:name v)]
|
||||
[{:is-group false
|
||||
:path (split-token-set-name name)
|
||||
:id name
|
||||
:parent-path parent
|
||||
:depth depth
|
||||
:token-set v}])
|
||||
|
||||
;; Set group
|
||||
(and v (d/ordered-map? v))
|
||||
(let [unprefixed-path (last (split-set-str-path-prefix k))
|
||||
path (conj parent unprefixed-path)
|
||||
item {:is-group true
|
||||
:path path
|
||||
:id (join-set-path path)
|
||||
:parent-path parent
|
||||
:depth depth}]
|
||||
|
||||
(if (skip-children-pred path)
|
||||
[item]
|
||||
(let [v (cond-> v
|
||||
(= path new-at-path)
|
||||
(assoc :is-new true))]
|
||||
(cons item
|
||||
(mapcat #(walk % path (inc depth)) v))))))))
|
||||
|
||||
tree (cond-> tree
|
||||
(= [] new-at-path)
|
||||
(assoc :is-new true))]
|
||||
(->> tree
|
||||
(mapcat #(walk % [] 0))
|
||||
(map-indexed (fn [index item]
|
||||
(assoc item :index index))))))
|
||||
|
||||
(defn flatten-nested-tokens-json
|
||||
"Recursively flatten the dtcg token structure, joining keys with '.'."
|
||||
[tokens token-path]
|
||||
|
|
|
@ -19,13 +19,23 @@
|
|||
[app.main.data.notifications :as ntf]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.tokens.selected-set :as dwts]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[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]))
|
||||
|
||||
(declare set-selected-token-set-name)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; TOKENS Getters
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn get-tokens-lib
|
||||
[state]
|
||||
(-> (dsh/lookup-file-data state)
|
||||
(get :tokens-lib)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Helpers
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -43,15 +53,6 @@
|
|||
(watch [_ _ _]
|
||||
(rx/of (dwsh/update-shapes [id] #(merge % attrs))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; TOKENS Getters
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn get-tokens-lib
|
||||
[state]
|
||||
(-> (dsh/lookup-file-data state)
|
||||
(get :tokens-lib)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; TOKENS Actions
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -108,19 +109,21 @@
|
|||
(dch/commit-changes changes)
|
||||
(wtu/update-workspace-tokens))))))
|
||||
|
||||
(declare set-selected-token-set-name)
|
||||
(defn create-token-set
|
||||
[set-name token-set]
|
||||
(ptk/reify ::create-token-set
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
;; Clear possible local state
|
||||
(update state :workspace-tokens dissoc :token-set-new-path))
|
||||
|
||||
(defn create-token-set [set-name token-set]
|
||||
(let [new-token-set (-> token-set
|
||||
(update :name #(if (empty? %) set-name (ctob/join-set-path [% set-name]))))]
|
||||
(ptk/reify ::create-token-set
|
||||
ptk/WatchEvent
|
||||
(watch [it _ _]
|
||||
(let [changes (-> (pcb/empty-changes it)
|
||||
(pcb/add-token-set new-token-set))]
|
||||
(rx/of
|
||||
(set-selected-token-set-name (:name new-token-set))
|
||||
(dch/commit-changes changes)))))))
|
||||
ptk/WatchEvent
|
||||
(watch [it _ _]
|
||||
(let [token-set (update token-set :name #(if (empty? %) set-name (ctob/join-set-path [% set-name])))
|
||||
changes (-> (pcb/empty-changes it)
|
||||
(pcb/add-token-set token-set))]
|
||||
(rx/of (set-selected-token-set-name (:name token-set))
|
||||
(dch/commit-changes changes))))))
|
||||
|
||||
(defn rename-token-set-group [set-group-path set-group-fname]
|
||||
(ptk/reify ::rename-token-set-group
|
||||
|
@ -143,14 +146,18 @@
|
|||
(set-selected-token-set-name (:name token-set))
|
||||
(dch/commit-changes changes))))))
|
||||
|
||||
(defn toggle-token-set [{:keys [token-set-name]}]
|
||||
(defn toggle-token-set
|
||||
[name]
|
||||
(assert (string? name) "expected a string for `name`")
|
||||
(ptk/reify ::toggle-token-set
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [changes (clt/generate-toggle-token-set (pcb/empty-changes) (get-tokens-lib state) token-set-name)]
|
||||
(rx/of
|
||||
(dch/commit-changes changes)
|
||||
(wtu/update-workspace-tokens))))))
|
||||
(let [tlib (get-tokens-lib state)
|
||||
changes (-> (pcb/empty-changes)
|
||||
(clt/generate-toggle-token-set tlib name))]
|
||||
|
||||
(rx/of (dch/commit-changes changes)
|
||||
(wtu/update-workspace-tokens))))))
|
||||
|
||||
(defn toggle-token-set-group [group-path]
|
||||
(ptk/reify ::toggle-token-set-group
|
||||
|
@ -224,6 +231,7 @@
|
|||
(rx/of
|
||||
(drop-error (ex-data e))))))))
|
||||
|
||||
;; FIXME: the the name is very confusing
|
||||
(defn update-create-token
|
||||
[{:keys [token prev-token-name]}]
|
||||
(ptk/reify ::update-create-token
|
||||
|
@ -241,13 +249,15 @@
|
|||
add-to-hidden-theme? (= active-theme-paths #{ctob/hidden-token-theme-path})
|
||||
base-changes (pcb/add-token-set (pcb/empty-changes) token-set)]
|
||||
(cond
|
||||
(not tokens-lib) (-> base-changes
|
||||
(pcb/add-token-theme hidden-theme)
|
||||
(pcb/update-active-token-themes #{ctob/hidden-token-theme-path} #{}))
|
||||
(not tokens-lib)
|
||||
(-> base-changes
|
||||
(pcb/add-token-theme hidden-theme)
|
||||
(pcb/update-active-token-themes #{ctob/hidden-token-theme-path} #{}))
|
||||
|
||||
add-to-hidden-theme? (let [prev-hidden-theme (ctob/get-theme tokens-lib ctob/hidden-token-theme-group ctob/hidden-token-theme-name)]
|
||||
(-> base-changes
|
||||
(pcb/update-token-theme (ctob/toggle-set prev-hidden-theme ctob/hidden-token-theme-path) prev-hidden-theme)))
|
||||
add-to-hidden-theme?
|
||||
(let [prev-hidden-theme (ctob/get-theme tokens-lib ctob/hidden-token-theme-group ctob/hidden-token-theme-name)]
|
||||
(-> base-changes
|
||||
(pcb/update-token-theme (ctob/toggle-set prev-hidden-theme ctob/hidden-token-theme-path) prev-hidden-theme)))
|
||||
|
||||
:else base-changes))
|
||||
(-> (pcb/empty-changes it)
|
||||
|
@ -295,49 +305,81 @@
|
|||
(update-create-token
|
||||
{:token (assoc token :name copy-name)})))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; TOKEN UI OPS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn set-token-type-section-open
|
||||
[token-type open?]
|
||||
(ptk/reify ::set-token-type-section-open
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:workspace-local :token-type-open-status token-type] open?))))
|
||||
(update-in state [:workspace-tokens :open-status-by-type] assoc token-type open?))))
|
||||
|
||||
;; === Token Context Menu
|
||||
(defn assign-token-context-menu
|
||||
[{:keys [position] :as params}]
|
||||
|
||||
(when params
|
||||
(assert (gpt/point? position) "expected a point instance for `position` param"))
|
||||
|
||||
(defn show-token-context-menu
|
||||
[{:keys [position _token-name] :as params}]
|
||||
(dm/assert! (gpt/point? position))
|
||||
(ptk/reify ::show-token-context-menu
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:workspace-local :token-context-menu] params))))
|
||||
(if params
|
||||
(update state :workspace-tokens assoc :token-context-menu params)
|
||||
(update state :workspace-tokens dissoc :token-context-menu)))))
|
||||
|
||||
(def hide-token-context-menu
|
||||
(ptk/reify ::hide-token-context-menu
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; TOKEN-SET UI OPS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn assign-token-set-context-menu
|
||||
[{:keys [position] :as params}]
|
||||
(when params
|
||||
(assert (gpt/point? position) "expected valid point for `position` param"))
|
||||
|
||||
(ptk/reify ::assign-token-set-context-menu
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:workspace-local :token-context-menu] nil))))
|
||||
|
||||
;; === Token Set Context Menu
|
||||
|
||||
(defn show-token-set-context-menu
|
||||
[{:keys [position _token-set-name] :as params}]
|
||||
(dm/assert! (gpt/point? position))
|
||||
(ptk/reify ::show-token-set-context-menu
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:workspace-local :token-set-context-menu] params))))
|
||||
|
||||
(def hide-token-set-context-menu
|
||||
(ptk/reify ::hide-token-set-context-menu
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:workspace-local :token-set-context-menu] nil))))
|
||||
(if params
|
||||
(update state :workspace-tokens assoc :token-set-context-menu params)
|
||||
(update state :workspace-tokens dissoc :token-set-context-menu)))))
|
||||
|
||||
(defn set-selected-token-set-name
|
||||
[name]
|
||||
(ptk/reify ::set-selected-token-set-name
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :workspace-local assoc :selected-token-set-name name))))
|
||||
(update state :workspace-tokens assoc :selected-token-set-name name))))
|
||||
|
||||
(defn start-token-set-edition
|
||||
[edition-id]
|
||||
(assert (string? edition-id) "expected a string for `edition-id`")
|
||||
|
||||
(ptk/reify ::start-token-set-edition
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :workspace-tokens assoc :token-set-edition-id edition-id))))
|
||||
|
||||
(defn start-token-set-creation
|
||||
[path]
|
||||
(assert (vector? path) "expected a vector for `path`")
|
||||
|
||||
(ptk/reify ::start-token-set-creation
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :workspace-tokens assoc :token-set-new-path path))))
|
||||
|
||||
(defn clear-token-set-edition
|
||||
[]
|
||||
(ptk/reify ::clear-token-set-edition
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :workspace-tokens dissoc :token-set-edition-id))))
|
||||
|
||||
(defn clear-token-set-creation
|
||||
[]
|
||||
(ptk/reify ::clear-token-set-creation
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :workspace-tokens dissoc :token-set-new-path))))
|
||||
|
|
|
@ -399,6 +399,7 @@
|
|||
:workspace-media-objects
|
||||
:workspace-persistence
|
||||
:workspace-presence
|
||||
:workspace-tokens
|
||||
:workspace-ready
|
||||
:workspace-undo)
|
||||
(update :workspace-global dissoc :read-only?)
|
||||
|
|
|
@ -128,6 +128,10 @@
|
|||
(def workspace-selrect-transform
|
||||
(l/derived :workspace-selrect-transform st/state))
|
||||
|
||||
(def workspace-tokens
|
||||
"All tokens related ephimeral state"
|
||||
(l/derived :workspace-tokens st/state))
|
||||
|
||||
;; TODO: rename to workspace-selected (?)
|
||||
;; Don't use directly from components, this is a proxy to improve performance of selected-shapes
|
||||
(def ^:private selected-shapes-data
|
||||
|
@ -453,12 +457,8 @@
|
|||
(def workspace-token-themes-no-hidden
|
||||
(l/derived #(remove ctob/hidden-temporary-theme? %) workspace-token-themes))
|
||||
|
||||
;; FIXME: deprecated
|
||||
(def workspace-selected-token-set-name
|
||||
(l/derived dwts/get-selected-token-set-name st/state))
|
||||
|
||||
(def selected-token-set-name
|
||||
(l/derived (l/key :selected-token-set-name) workspace-local))
|
||||
(l/derived (l/key :selected-token-set-name) workspace-tokens))
|
||||
|
||||
(def workspace-ordered-token-sets
|
||||
(l/derived #(or (some-> % ctob/get-sets) []) tokens-lib))
|
||||
|
@ -480,10 +480,7 @@
|
|||
(def workspace-active-theme-paths-no-hidden
|
||||
(l/derived #(disj % ctob/hidden-token-theme-path) workspace-active-theme-paths))
|
||||
|
||||
(def workspace-active-set-names
|
||||
(l/derived (d/nilf ctob/get-active-themes-set-names) tokens-lib))
|
||||
|
||||
;; FIXME: deprecated, it should not be implemented with ref
|
||||
;; FIXME: deprecated, it should not be implemented with ref (still used in form)
|
||||
(def workspace-active-theme-sets-tokens
|
||||
(l/derived #(or (some-> % ctob/get-active-themes-set-tokens) {}) tokens-lib))
|
||||
|
||||
|
@ -496,7 +493,6 @@
|
|||
(def workspace-selected-token-set-tokens
|
||||
(l/derived #(or (dwts/get-selected-token-set-tokens %) {}) st/state))
|
||||
|
||||
|
||||
(def plugins-permissions-peek
|
||||
(l/derived (fn [state]
|
||||
(dm/get-in state [:plugins-permissions-peek :data]))
|
||||
|
|
|
@ -218,15 +218,15 @@
|
|||
:no-selectable true
|
||||
:action (fn [event]
|
||||
(let [{:keys [key fields]} modal]
|
||||
(st/emit! dt/hide-token-context-menu)
|
||||
(dom/stop-propagation event)
|
||||
(modal/show! key {:x (.-clientX ^js event)
|
||||
:y (.-clientY ^js event)
|
||||
:position :right
|
||||
:fields fields
|
||||
:action "edit"
|
||||
:selected-token-set-name selected-token-set-name
|
||||
:token token})))}
|
||||
(st/emit! (dt/assign-token-set-context-menu nil)
|
||||
(modal/show key {:x (.-clientX ^js event)
|
||||
:y (.-clientY ^js event)
|
||||
:position :right
|
||||
:fields fields
|
||||
:action "edit"
|
||||
: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)))}
|
||||
|
@ -252,8 +252,8 @@
|
|||
|
||||
;; Components ------------------------------------------------------------------
|
||||
|
||||
(def tokens-menu-ref
|
||||
(l/derived :token-context-menu refs/workspace-local))
|
||||
(def ^:private tokens-menu-ref
|
||||
(l/derived :token-context-menu refs/workspace-tokens))
|
||||
|
||||
(defn- prevent-default
|
||||
[event]
|
||||
|
@ -355,7 +355,7 @@
|
|||
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-name (mf/deref refs/workspace-selected-token-set-name)]
|
||||
selected-token-set-name (mf/deref refs/selected-token-set-name)]
|
||||
[:ul {:class (stl/css :context-list)}
|
||||
[:& menu-tree {:submenu-offset width
|
||||
:submenu-direction direction
|
||||
|
@ -394,8 +394,9 @@
|
|||
(reset! dropdown-direction* (if is-outside? "up" "down"))
|
||||
(mf/set-ref-val! dropdown-direction-change* (inc (mf/ref-val dropdown-direction-change*)))))))
|
||||
|
||||
;; FIXME: perf optimization
|
||||
[:& dropdown {:show is-open?
|
||||
:on-close #(st/emit! dt/hide-token-context-menu)}
|
||||
:on-close #(st/emit! (dt/assign-token-context-menu nil))}
|
||||
[:div {:class (stl/css :token-context-menu)
|
||||
:data-testid "tokens-context-menu-for-token"
|
||||
:ref dropdown-ref
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.workspace.tokens.components.controls.input-tokens :refer [input-tokens*]]
|
||||
[app.main.ui.workspace.tokens.sets :as wts]
|
||||
[app.main.ui.workspace.tokens.sets-context :as sets-context]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[app.util.keyboard :as k]
|
||||
|
@ -339,8 +338,8 @@
|
|||
token-set-active?
|
||||
(mf/use-fn
|
||||
(mf/deps theme-state)
|
||||
(fn [set-name]
|
||||
(get-in theme-state [:sets set-name])))
|
||||
(fn [name]
|
||||
(contains? (:sets theme-state) name)))
|
||||
|
||||
on-toggle-token-set
|
||||
(mf/use-fn
|
||||
|
@ -381,16 +380,15 @@
|
|||
(tr "workspace.token.set-selection-theme")]
|
||||
[:div {:class (stl/css :sets-list-wrapper)}
|
||||
|
||||
[:& wts/controlled-sets-list
|
||||
[:> wts/controlled-sets-list*
|
||||
{:token-sets token-sets
|
||||
:token-set-selected? (constantly false)
|
||||
:token-set-active? token-set-active?
|
||||
:token-set-group-active? token-set-group-active?
|
||||
:is-token-set-active token-set-active?
|
||||
:is-token-set-group-active token-set-group-active?
|
||||
:on-select on-click-token-set
|
||||
:can-edit false
|
||||
: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}]]
|
||||
:origin "theme-modal"}]]
|
||||
|
||||
[:div {:class (stl/css :edit-theme-footer)}
|
||||
[:> button* {:variant "secondary"
|
||||
|
@ -432,5 +430,4 @@
|
|||
:aria-label (tr "labels.close")
|
||||
:variant "action"
|
||||
:icon "close"}]
|
||||
[:& sets-context/provider {}
|
||||
[:& themes-modal-body]]]]))
|
||||
[:& themes-modal-body]]]))
|
||||
|
|
|
@ -7,18 +7,17 @@
|
|||
(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.tokens :as dt]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.context :as ctx]
|
||||
[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]]
|
||||
[app.util.keyboard :as kbd]
|
||||
|
@ -26,42 +25,59 @@
|
|||
[potok.v2.core :as ptk]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defn on-toggle-token-set-click [token-set-name]
|
||||
(st/emit! (wdt/toggle-token-set {:token-set-name token-set-name})))
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; HELPERS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn on-toggle-token-set-group-click [group-path]
|
||||
(st/emit! (wdt/toggle-token-set-group group-path)))
|
||||
(defn- on-start-creation
|
||||
[]
|
||||
(st/emit! (dt/start-token-set-creation [])))
|
||||
|
||||
(defn on-select-token-set-click [set-name]
|
||||
(st/emit! (wdt/set-selected-token-set-name set-name)))
|
||||
(defn- on-toggle-token-set-click [name]
|
||||
(st/emit! (dt/toggle-token-set name)))
|
||||
|
||||
(defn- on-toggle-token-set-group-click [group-path]
|
||||
(st/emit! (dt/toggle-token-set-group group-path)))
|
||||
|
||||
(defn- on-select-token-set-click [set-name]
|
||||
(st/emit! (dt/set-selected-token-set-name set-name)))
|
||||
|
||||
(defn on-update-token-set [set-name token-set]
|
||||
(st/emit! (wdt/update-token-set (:name token-set) (ctob/update-name token-set set-name))))
|
||||
(st/emit! (dt/update-token-set (:name token-set) (ctob/update-name token-set set-name))))
|
||||
|
||||
(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-update-token-set-group [path name]
|
||||
(st/emit! (dt/rename-token-set-group path name)))
|
||||
|
||||
(defn on-create-token-set [set-name token-set]
|
||||
(st/emit! (ptk/event ::ev/event {::ev/name "create-tokens-set"}))
|
||||
(st/emit! (wdt/create-token-set set-name token-set)))
|
||||
(defn- on-create-token-set [name token-set]
|
||||
(st/emit! (ptk/data-event ::ev/event {::ev/name "create-token-set" :name name})
|
||||
(dt/create-token-set name token-set)))
|
||||
|
||||
(mf/defc editing-label
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; COMPONENTS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(mf/defc editing-label*
|
||||
{::mf/private true}
|
||||
[{:keys [default-value on-cancel on-submit]}]
|
||||
(let [ref (mf/use-ref)
|
||||
on-submit-valid (mf/use-fn
|
||||
(fn [event]
|
||||
(let [value (str/trim (dom/get-target-val event))]
|
||||
(if (or (str/empty? value)
|
||||
(= value default-value))
|
||||
(on-cancel)
|
||||
(do
|
||||
(on-submit value)
|
||||
(on-cancel))))))
|
||||
on-key-down (mf/use-fn
|
||||
(fn [event]
|
||||
(cond
|
||||
(kbd/enter? event) (on-submit-valid event)
|
||||
(kbd/esc? event) (on-cancel))))]
|
||||
|
||||
on-submit-valid
|
||||
(mf/use-fn
|
||||
(mf/deps on-cancel on-submit default-value)
|
||||
(fn [event]
|
||||
(let [value (str/trim (dom/get-target-val event))]
|
||||
(if (or (str/empty? value)
|
||||
(= value default-value))
|
||||
(on-cancel)
|
||||
(on-submit value)))))
|
||||
|
||||
on-key-down
|
||||
(mf/use-fn
|
||||
(mf/deps on-submit-valid on-cancel)
|
||||
(fn [event]
|
||||
(cond
|
||||
(kbd/enter? event) (on-submit-valid event)
|
||||
(kbd/esc? event) (on-cancel))))]
|
||||
[:input
|
||||
{:class (stl/css :editing-node)
|
||||
:type "text"
|
||||
|
@ -71,17 +87,12 @@
|
|||
:auto-focus true
|
||||
:default-value default-value}]))
|
||||
|
||||
(mf/defc checkbox
|
||||
(mf/defc checkbox*
|
||||
[{:keys [checked aria-label on-click disabled]}]
|
||||
(let [all? (true? checked)
|
||||
mixed? (= checked "mixed")
|
||||
checked? (or all? mixed?)
|
||||
on-click
|
||||
(mf/use-fn
|
||||
(mf/deps disabled)
|
||||
(fn [e]
|
||||
(when-not disabled
|
||||
(on-click e))))]
|
||||
(let [all? (true? checked)
|
||||
mixed? (= checked "mixed")
|
||||
checked? (or all? mixed?)]
|
||||
|
||||
[:div {:role "checkbox"
|
||||
:aria-checked (dm/str checked)
|
||||
:disabled disabled
|
||||
|
@ -90,37 +101,59 @@
|
|||
:class (stl/css-case :checkbox-style true
|
||||
:checkbox-checked-style checked?
|
||||
:checkbox-disabled-checked (and checked? disabled)
|
||||
:checkbox-disabled disabled)
|
||||
:on-click on-click}
|
||||
:checkbox-disabled disabled)
|
||||
:on-click (when-not disabled on-click)}
|
||||
|
||||
(when checked?
|
||||
(when ^boolean checked?
|
||||
[:> icon*
|
||||
{:aria-label aria-label
|
||||
:class (stl/css :check-icon)
|
||||
:size "s"
|
||||
:icon-id (if mixed? ic/remove ic/tick)}])]))
|
||||
|
||||
(mf/defc sets-tree-set-group
|
||||
[{: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))
|
||||
;; Used by playwright to get the correct item by label
|
||||
label-id (str editing-id "-label")
|
||||
(mf/defc inline-add-button*
|
||||
[]
|
||||
(let [can-edit? (mf/use-ctx ctx/can-edit?)]
|
||||
(if can-edit?
|
||||
[:div {:class (stl/css :empty-sets-wrapper)}
|
||||
[:> text* {:as "span" :typography "body-small" :class (stl/css :empty-state-message)}
|
||||
(tr "workspace.token.no-sets-yet")]
|
||||
[:button {:on-click on-start-creation
|
||||
:class (stl/css :create-set-button)}
|
||||
(tr "workspace.token.create-one")]]
|
||||
[:div {:class (stl/css :empty-sets-wrapper)}
|
||||
[:> text* {:as "span" :typography "body-small" :class (stl/css :empty-state-message)}
|
||||
(tr "workspace.token.no-sets-yet")]])))
|
||||
|
||||
(mf/defc add-button*
|
||||
[]
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:icon "add"
|
||||
:on-click on-start-creation
|
||||
:aria-label (tr "workspace.token.add set")}])
|
||||
|
||||
(mf/defc sets-tree-set-group*
|
||||
{::mf/private true}
|
||||
[{:keys [id label tree-depth tree-path is-active is-selected is-draggable is-collapsed tree-index on-drop
|
||||
on-toggle-collapse on-toggle is-editing on-start-edition on-reset-edition on-edit-submit]}]
|
||||
|
||||
(let [can-edit?
|
||||
(mf/use-ctx ctx/can-edit?)
|
||||
|
||||
label-id
|
||||
(str id "-label")
|
||||
|
||||
on-context-menu
|
||||
(mf/use-fn
|
||||
(mf/deps editing?' editing-id can-edit?)
|
||||
(mf/deps is-editing can-edit?)
|
||||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(when-not editing?'
|
||||
(st/emit!
|
||||
(wdt/show-token-set-context-menu
|
||||
{:position (dom/get-client-position event)
|
||||
:is-group true
|
||||
:path tree-path})))))
|
||||
(when-not is-editing
|
||||
(st/emit! (dt/assign-token-set-context-menu
|
||||
{:position (dom/get-client-position event)
|
||||
:is-group true
|
||||
:path tree-path})))))
|
||||
|
||||
on-collapse-click
|
||||
(mf/use-fn
|
||||
|
@ -129,9 +162,7 @@
|
|||
(on-toggle-collapse tree-path)))
|
||||
|
||||
on-double-click
|
||||
(mf/use-fn
|
||||
(mf/deps editing-id can-edit?)
|
||||
#(on-edit editing-id))
|
||||
(mf/use-fn (mf/deps id) #(on-start-edition id))
|
||||
|
||||
on-checkbox-click
|
||||
(mf/use-fn
|
||||
|
@ -145,24 +176,18 @@
|
|||
|
||||
on-drop
|
||||
(mf/use-fn
|
||||
(mf/deps tree-index collapsed-paths)
|
||||
(mf/deps tree-index on-drop)
|
||||
(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))))))
|
||||
(on-drop tree-index position data)))
|
||||
|
||||
[dprops dref]
|
||||
(h/use-sortable
|
||||
:data-type "penpot/token-set"
|
||||
:on-drop on-drop
|
||||
:data {:index tree-index
|
||||
:group? true}
|
||||
:is-group true}
|
||||
:detect-center? true
|
||||
:draggable? draggable?)]
|
||||
:draggable? is-draggable)]
|
||||
|
||||
[:div {:ref dref
|
||||
:role "button"
|
||||
|
@ -171,7 +196,7 @@
|
|||
:style {"--tree-depth" tree-depth}
|
||||
:class (stl/css-case :set-item-container true
|
||||
:set-item-group true
|
||||
:selected-set selected?
|
||||
:selected-set is-selected
|
||||
:dnd-over (= (:over dprops) :center)
|
||||
:dnd-over-top (= (:over dprops) :top)
|
||||
:dnd-over-bot (= (:over dprops) :bot))
|
||||
|
@ -180,68 +205,64 @@
|
|||
{: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 is-collapsed "arrow-right" "arrow-down")
|
||||
:variant "action"}]
|
||||
(if editing?'
|
||||
[:& editing-label
|
||||
(if is-editing
|
||||
[:> editing-label*
|
||||
{:default-value label
|
||||
:on-cancel on-edit-reset
|
||||
:on-create on-edit-reset
|
||||
:on-cancel on-reset-edition
|
||||
:on-submit on-edit-submit'}]
|
||||
[:*
|
||||
[:div {:class (stl/css :set-name)
|
||||
:on-double-click on-double-click
|
||||
:id label-id}
|
||||
label]
|
||||
[:& checkbox
|
||||
[:> checkbox*
|
||||
{:on-click on-checkbox-click
|
||||
:disabled (not can-edit?)
|
||||
:checked (case active?'
|
||||
:checked (case is-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 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))
|
||||
(mf/defc sets-tree-set*
|
||||
[{:keys [id set label tree-depth tree-path tree-index is-selected is-active is-draggable is-editing
|
||||
on-select on-drop on-toggle on-start-edition on-reset-edition on-edit-submit]}]
|
||||
(let [set-name (get set :name)
|
||||
can-edit? (mf/use-ctx ctx/can-edit?)
|
||||
|
||||
on-click
|
||||
(mf/use-fn
|
||||
(mf/deps editing?' tree-path)
|
||||
(mf/deps is-editing tree-path)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(when-not editing?'
|
||||
(on-select set-name))))
|
||||
(when-not is-editing
|
||||
(when (fn? on-select)
|
||||
(on-select set-name)))))
|
||||
|
||||
on-context-menu
|
||||
(mf/use-fn
|
||||
(mf/deps editing?' tree-path can-edit?)
|
||||
(mf/deps is-editing tree-path can-edit?)
|
||||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(when (and can-edit? (not editing?'))
|
||||
(st/emit!
|
||||
(wdt/show-token-set-context-menu
|
||||
{:position (dom/get-client-position event)
|
||||
:is-group false
|
||||
:path tree-path})))))
|
||||
(when (and can-edit? (not is-editing))
|
||||
(st/emit! (dt/assign-token-set-context-menu
|
||||
{:position (dom/get-client-position event)
|
||||
:is-group false
|
||||
:path tree-path})))))
|
||||
|
||||
on-double-click
|
||||
(mf/use-fn
|
||||
(mf/deps editing-id)
|
||||
(fn []
|
||||
(on-edit editing-id)))
|
||||
(mf/use-fn (mf/deps id) #(on-start-edition id))
|
||||
|
||||
on-checkbox-click
|
||||
(mf/use-fn
|
||||
(mf/deps set-name)
|
||||
(mf/deps set-name on-toggle)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(on-toggle set-name)))
|
||||
(when (fn? on-toggle)
|
||||
(on-toggle set-name))))
|
||||
|
||||
on-edit-submit'
|
||||
(mf/use-fn
|
||||
|
@ -252,20 +273,14 @@
|
|||
(mf/use-fn
|
||||
(mf/deps tree-path)
|
||||
(fn [_]
|
||||
(when-not selected?
|
||||
(when-not is-selected
|
||||
(on-select tree-path))))
|
||||
|
||||
on-drop
|
||||
(mf/use-fn
|
||||
(mf/deps tree-index collapsed-paths)
|
||||
(mf/deps tree-index on-drop)
|
||||
(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))))))
|
||||
(on-drop tree-index position data)))
|
||||
|
||||
[dprops dref]
|
||||
(h/use-sortable
|
||||
|
@ -273,210 +288,280 @@
|
|||
:on-drag on-drag
|
||||
:on-drop on-drop
|
||||
:data {:index tree-index
|
||||
:group? false}
|
||||
:draggable? draggable?)]
|
||||
:is-group false}
|
||||
:draggable? is-draggable)
|
||||
|
||||
drop-over
|
||||
(get dprops :over)]
|
||||
|
||||
[: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?
|
||||
:dnd-over (= (:over dprops) :center)
|
||||
:dnd-over-top (= (:over dprops) :top)
|
||||
:dnd-over-bot (= (:over dprops) :bot))
|
||||
:selected-set is-selected
|
||||
:dnd-over (= drop-over :center)
|
||||
:dnd-over-top (= drop-over :top)
|
||||
:dnd-over-bot (= drop-over :bot))
|
||||
:on-click on-click
|
||||
:on-context-menu on-context-menu
|
||||
:aria-checked active?'}
|
||||
:aria-checked is-active}
|
||||
|
||||
[:> icon*
|
||||
{:icon-id "document"
|
||||
:class (stl/css-case :icon true
|
||||
:root-icon (not tree-depth))}]
|
||||
(if editing?'
|
||||
[:& editing-label
|
||||
(if is-editing
|
||||
[:> editing-label*
|
||||
{:default-value label
|
||||
:on-cancel on-edit-reset
|
||||
:on-create on-edit-reset
|
||||
:on-cancel on-reset-edition
|
||||
:on-submit on-edit-submit'}]
|
||||
[:*
|
||||
[:div {:class (stl/css :set-name)
|
||||
:on-double-click on-double-click}
|
||||
label]
|
||||
[:& checkbox
|
||||
[:> checkbox*
|
||||
{:on-click on-checkbox-click
|
||||
:disabled (not can-edit?)
|
||||
:arial-label (tr "workspace.token.select-set")
|
||||
:checked active?'}]])]))
|
||||
:checked is-active}]])]))
|
||||
|
||||
(mf/defc sets-tree
|
||||
[{:keys [draggable?
|
||||
active?
|
||||
selected?
|
||||
group-active?
|
||||
editing?
|
||||
on-edit-reset
|
||||
(mf/defc token-sets-tree*
|
||||
[{:keys [is-draggable
|
||||
selected
|
||||
is-token-set-group-active
|
||||
is-token-set-active
|
||||
on-start-edition
|
||||
on-reset-edition
|
||||
on-edit-submit-set
|
||||
on-edit-submit-group
|
||||
on-select
|
||||
on-toggle-set
|
||||
on-toggle-set-group
|
||||
set-node]
|
||||
:as props}]
|
||||
(let [{:keys [on-edit new-path] :as ctx} (sets-context/use-context)
|
||||
collapsed-paths (mf/use-state #{})
|
||||
token-sets
|
||||
new-path
|
||||
edition-id]}]
|
||||
|
||||
(let [collapsed-paths* (mf/use-state #{})
|
||||
collapsed-paths (deref collapsed-paths*)
|
||||
|
||||
collapsed?
|
||||
(mf/use-fn
|
||||
#(contains? @collapsed-paths %))
|
||||
(mf/deps collapsed-paths)
|
||||
(partial contains? collapsed-paths))
|
||||
|
||||
on-drop
|
||||
(mf/use-fn
|
||||
(mf/deps collapsed-paths)
|
||||
(fn [tree-index position data]
|
||||
(let [props {:from-index (:index data)
|
||||
:to-index tree-index
|
||||
:position position
|
||||
:collapsed-paths collapsed-paths}]
|
||||
(if (:is-group data)
|
||||
(st/emit! (dt/drop-token-set-group props))
|
||||
(st/emit! (dt/drop-token-set props))))))
|
||||
|
||||
on-toggle-collapse
|
||||
(mf/use-fn
|
||||
(fn [path]
|
||||
(swap! collapsed-paths #(if (contains? % path)
|
||||
(disj % path)
|
||||
(conj % path)))))]
|
||||
(for [[index {:keys [new? group? path parent-path depth] :as node}]
|
||||
(d/enumerate (ctob/walk-sets-tree-seq set-node {:skip-children-pred #(contains? @collapsed-paths %)
|
||||
:new-editing-set-path new-path}))]
|
||||
(cond
|
||||
group?
|
||||
(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}])
|
||||
(swap! collapsed-paths* #(if (contains? % path)
|
||||
(disj % path)
|
||||
(conj % path)))))]
|
||||
|
||||
new?
|
||||
(let [editing-id (sets-context/set-path->id path)]
|
||||
[:& sets-tree-set
|
||||
{:key editing-id
|
||||
:set (ctob/make-token-set :name (if (empty? parent-path)
|
||||
""
|
||||
(ctob/join-set-path parent-path)))
|
||||
:label ""
|
||||
:active? (constantly true)
|
||||
:selected? (constantly true)
|
||||
:on-select (constantly nil)
|
||||
:tree-path path
|
||||
:tree-depth depth
|
||||
:tree-index index
|
||||
:tree-parent-path parent-path
|
||||
:on-toggle (constantly nil)
|
||||
:editing-id editing-id
|
||||
:editing? (constantly true)
|
||||
:on-edit-reset on-edit-reset
|
||||
:on-edit-submit on-create-token-set}])
|
||||
(for [{:keys [id token-set index is-new is-group path parent-path depth] :as node}
|
||||
(ctob/sets-tree-seq token-sets
|
||||
{:skip-children-pred collapsed?
|
||||
:new-at-path new-path})]
|
||||
(cond
|
||||
^boolean is-group
|
||||
[:> sets-tree-set-group*
|
||||
{:key index
|
||||
:label (peek path)
|
||||
:id id
|
||||
:is-active (is-token-set-group-active path)
|
||||
:is-selected false
|
||||
:is-draggable is-draggable
|
||||
:is-editing (= edition-id id)
|
||||
:is-collapsed (collapsed? path)
|
||||
:on-select on-select
|
||||
|
||||
:tree-path path
|
||||
:tree-depth depth
|
||||
:tree-index index
|
||||
:tree-parent-path parent-path
|
||||
|
||||
:on-drop on-drop
|
||||
:on-start-edition on-start-edition
|
||||
:on-reset-edition on-reset-edition
|
||||
:on-edit-submit on-edit-submit-group
|
||||
:on-toggle-collapse on-toggle-collapse
|
||||
:on-toggle on-toggle-set-group}]
|
||||
|
||||
^boolean is-new
|
||||
[:> sets-tree-set*
|
||||
{:key index
|
||||
:set token-set
|
||||
:label ""
|
||||
:id id
|
||||
:is-editing true
|
||||
:is-active true
|
||||
:is-selected true
|
||||
|
||||
:tree-path path
|
||||
:tree-depth depth
|
||||
:tree-index index
|
||||
:tree-parent-path parent-path
|
||||
|
||||
:on-drop on-drop
|
||||
:on-reset-edition on-reset-edition
|
||||
:on-edit-submit on-create-token-set}]
|
||||
|
||||
:else
|
||||
(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}])))))
|
||||
[:> sets-tree-set*
|
||||
{:key index
|
||||
:set token-set
|
||||
:id id
|
||||
:label (peek path)
|
||||
:is-editing (= edition-id id)
|
||||
:is-active (is-token-set-active id)
|
||||
:is-selected (= selected id)
|
||||
:is-draggable is-draggable
|
||||
:on-select on-select
|
||||
:tree-path path
|
||||
:tree-depth depth
|
||||
:tree-index index
|
||||
:tree-parent-path parent-path
|
||||
:on-toggle on-toggle-set
|
||||
:edition-id edition-id
|
||||
:on-start-edition on-start-edition
|
||||
:on-drop on-drop
|
||||
:on-reset-edition on-reset-edition
|
||||
:on-edit-submit on-edit-submit-set}]))))
|
||||
|
||||
(mf/defc controlled-sets-list
|
||||
(mf/defc controlled-sets-list*
|
||||
{::mf/props :obj}
|
||||
[{:keys [token-sets
|
||||
selected
|
||||
on-update-token-set
|
||||
on-update-token-set-group
|
||||
token-set-selected?
|
||||
token-set-active?
|
||||
token-set-group-active?
|
||||
is-token-set-active
|
||||
is-token-set-group-active
|
||||
on-create-token-set
|
||||
on-toggle-token-set
|
||||
on-toggle-token-set-group
|
||||
on-start-edition
|
||||
on-reset-edition
|
||||
origin
|
||||
on-select
|
||||
context]
|
||||
:as _props}]
|
||||
(let [{:keys [editing? on-edit on-reset new-path] :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?)]
|
||||
new-path
|
||||
edition-id]}]
|
||||
|
||||
(assert (fn? is-token-set-group-active) "expected a function for `is-token-set-group-active` prop")
|
||||
(assert (fn? is-token-set-active) "expected a function for `is-token-set-active` prop")
|
||||
|
||||
(let [theme-modal? (= origin "theme-modal")
|
||||
can-edit? (mf/use-ctx ctx/can-edit?)
|
||||
draggable? (and (not theme-modal?) can-edit?)
|
||||
empty-state? (and theme-modal?
|
||||
(empty? token-sets)
|
||||
(not new-path))
|
||||
|
||||
;; NOTE: on-reset-edition and on-start-edition function can
|
||||
;; come as nil, in this case we need to provide a safe
|
||||
;; fallback for them
|
||||
on-reset-edition
|
||||
(mf/use-fn
|
||||
(mf/deps on-reset-edition)
|
||||
(fn [v]
|
||||
(when (fn? on-reset-edition)
|
||||
(on-reset-edition v))))
|
||||
|
||||
on-start-edition
|
||||
(mf/use-fn
|
||||
(mf/deps on-start-edition)
|
||||
(fn [v]
|
||||
(when (fn? on-start-edition)
|
||||
(on-start-edition v))))]
|
||||
|
||||
[:fieldset {:class (stl/css :sets-list)}
|
||||
(if (and theme-modal?
|
||||
(empty? token-sets)
|
||||
(not new-path))
|
||||
(if ^boolean empty-state?
|
||||
[:> text* {:as "span" :typography "body-small" :class (stl/css :empty-state-message-sets)}
|
||||
(tr "workspace.token.no-sets-create")]
|
||||
[:& sets-tree
|
||||
{:draggable? draggable?
|
||||
:set-node token-sets
|
||||
:selected? token-set-selected?
|
||||
|
||||
[:> token-sets-tree*
|
||||
{:is-draggable draggable?
|
||||
:new-path new-path
|
||||
:edition-id edition-id
|
||||
:token-sets token-sets
|
||||
:selected selected
|
||||
:on-select on-select
|
||||
:active? token-set-active?
|
||||
:group-active? token-set-group-active?
|
||||
:is-token-set-active is-token-set-active
|
||||
:is-token-set-group-active is-token-set-group-active
|
||||
:on-toggle-set on-toggle-token-set
|
||||
:on-toggle-set-group on-toggle-token-set-group
|
||||
:editing? editing?
|
||||
:on-create-token-set on-create-token-set
|
||||
:on-edit on-edit
|
||||
:on-edit-reset on-reset
|
||||
:on-start-edition on-start-edition
|
||||
:on-reset-edition on-reset-edition
|
||||
:on-edit-submit-set on-update-token-set
|
||||
:on-edit-submit-group on-update-token-set-group}])]))
|
||||
|
||||
(mf/defc sets-list*
|
||||
[{:keys [tokens-lib selected-token-set-name]}]
|
||||
[{:keys [tokens-lib selected new-path edition-id]}]
|
||||
|
||||
(let [token-sets
|
||||
(some-> tokens-lib (ctob/get-set-tree))
|
||||
|
||||
token-set-selected?
|
||||
(mf/use-fn
|
||||
(mf/deps token-sets selected-token-set-name)
|
||||
(fn [set-name]
|
||||
(= set-name selected-token-set-name)))
|
||||
can-edit?
|
||||
(mf/use-ctx ctx/can-edit?)
|
||||
|
||||
active-token-set-names
|
||||
(mf/deref refs/workspace-active-set-names)
|
||||
active-token-sets-names
|
||||
(mf/with-memo [tokens-lib]
|
||||
(some-> tokens-lib (ctob/get-active-themes-set-names)))
|
||||
|
||||
token-set-active?
|
||||
(mf/use-fn
|
||||
(mf/deps active-token-set-names)
|
||||
(fn [set-name]
|
||||
(get active-token-set-names set-name)))
|
||||
(mf/deps active-token-sets-names)
|
||||
(fn [name]
|
||||
(contains? active-token-sets-names name)))
|
||||
|
||||
token-set-group-active?
|
||||
(mf/use-fn
|
||||
(fn [group-path]
|
||||
@(refs/token-sets-at-path-all-active group-path)))]
|
||||
;; FIXME
|
||||
@(refs/token-sets-at-path-all-active group-path)))
|
||||
|
||||
[:& controlled-sets-list
|
||||
on-reset-edition
|
||||
(mf/use-fn
|
||||
(mf/deps can-edit?)
|
||||
(fn [_]
|
||||
(when can-edit?
|
||||
(st/emit! (dt/clear-token-set-edition)
|
||||
(dt/clear-token-set-creation)))))
|
||||
|
||||
on-start-edition
|
||||
(mf/use-fn
|
||||
(mf/deps can-edit?)
|
||||
(fn [id]
|
||||
(when can-edit?
|
||||
(st/emit! (dt/start-token-set-edition id)))))]
|
||||
|
||||
[:> 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?
|
||||
|
||||
:is-token-set-active token-set-active?
|
||||
:is-token-set-group-active token-set-group-active?
|
||||
:on-select on-select-token-set-click
|
||||
|
||||
:selected selected
|
||||
:new-path new-path
|
||||
:edition-id edition-id
|
||||
|
||||
:origin "set-panel"
|
||||
:can-edit can-edit?
|
||||
:on-start-edition on-start-edition
|
||||
:on-reset-edition on-reset-edition
|
||||
|
||||
:on-toggle-token-set on-toggle-token-set-click
|
||||
:on-toggle-token-set-group on-toggle-token-set-group-click
|
||||
:on-update-token-set on-update-token-set
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Copyright (c) KALEIDOS INC
|
||||
|
||||
@use "../../ds/typography.scss" as *;
|
||||
@import "refactor/common-refactor.scss";
|
||||
|
||||
.sets-list {
|
||||
|
@ -12,6 +13,21 @@
|
|||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.empty-sets-wrapper {
|
||||
padding: $s-12;
|
||||
padding-inline-start: $s-24;
|
||||
color: var(--color-foreground-secondary);
|
||||
}
|
||||
|
||||
.create-set-button {
|
||||
@include use-typography("body-small");
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
appearance: none;
|
||||
color: var(--color-accent-primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.set-item-container {
|
||||
@include bodySmallTypography;
|
||||
display: flex;
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
;; 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.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 {})
|
||||
|
||||
(def context (mf/create-context initial))
|
||||
|
||||
(def static-context
|
||||
{:editing? (constantly false)
|
||||
:on-edit (constantly nil)
|
||||
:on-create (constantly nil)
|
||||
:on-reset (constantly nil)})
|
||||
|
||||
(mf/defc provider
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [children (unchecked-get props "children")
|
||||
state (mf/use-state initial)]
|
||||
[:& (mf/provider context) {:value state}
|
||||
children]))
|
||||
|
||||
(defn use-context []
|
||||
(let [ctx (mf/use-ctx context)
|
||||
{:keys [editing-id new-path]} @ctx
|
||||
editing? (mf/use-callback
|
||||
(mf/deps editing-id)
|
||||
#(= editing-id %))
|
||||
on-edit (mf/use-fn
|
||||
(fn [editing-id]
|
||||
(reset! ctx (assoc @ctx :editing-id editing-id))))
|
||||
on-create (mf/use-fn
|
||||
(fn [path]
|
||||
(swap! ctx assoc :editing-id (random-uuid) :new-path path)))
|
||||
on-reset (mf/use-fn
|
||||
#(reset! ctx initial))]
|
||||
{:editing? editing?
|
||||
:new-path new-path
|
||||
:on-edit on-edit
|
||||
:on-create on-create
|
||||
:on-reset on-reset}))
|
|
@ -8,18 +8,18 @@
|
|||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.data.tokens :as wdt]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.main.data.tokens :as dt]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||
[app.main.ui.workspace.tokens.sets-context :as sets-context]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[okulary.core :as l]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(def ^:private ref:tokens-sets-context-menu
|
||||
(l/derived :token-set-context-menu refs/workspace-local))
|
||||
(def ^:private ref:token-sets-context-menu
|
||||
(l/derived :token-set-context-menu refs/workspace-tokens))
|
||||
|
||||
(defn- prevent-default
|
||||
[event]
|
||||
|
@ -34,27 +34,22 @@
|
|||
[:span {:class (stl/css :title)} title]])
|
||||
|
||||
(mf/defc menu*
|
||||
{::mf/private true}
|
||||
[{:keys [is-group path]}]
|
||||
(let [{:keys [on-create on-edit]} (sets-context/use-context)
|
||||
|
||||
create-set-at-path
|
||||
(mf/use-fn
|
||||
(mf/deps path on-create)
|
||||
#(on-create path))
|
||||
(let [create-set-at-path
|
||||
(mf/use-fn (mf/deps path) #(st/emit! (dt/start-token-set-creation path)))
|
||||
|
||||
edit-name
|
||||
(mf/use-fn
|
||||
(mf/deps is-group on-edit)
|
||||
(mf/deps path)
|
||||
(fn []
|
||||
(let [path (if is-group
|
||||
(sets-context/set-group-path->id path)
|
||||
(sets-context/set-path->id path))]
|
||||
(on-edit path))))
|
||||
(let [name (ctob/join-set-path path)]
|
||||
(st/emit! (dt/start-token-set-edition name)))))
|
||||
|
||||
delete-set
|
||||
(mf/use-fn
|
||||
(mf/deps is-group path)
|
||||
#(st/emit! (wdt/delete-token-set-path is-group path)))]
|
||||
#(st/emit! (dt/delete-token-set-path is-group path)))]
|
||||
|
||||
[:ul {:class (stl/css :context-list)}
|
||||
(when is-group
|
||||
|
@ -62,10 +57,10 @@
|
|||
[:> menu-entry* {:title (tr "labels.rename") :on-click edit-name}]
|
||||
[:> menu-entry* {:title (tr "labels.delete") :on-click delete-set}]]))
|
||||
|
||||
(mf/defc sets-context-menu*
|
||||
(mf/defc token-set-context-menu*
|
||||
[]
|
||||
(let [{:keys [position is-group path]}
|
||||
(mf/deref ref:tokens-sets-context-menu)
|
||||
(mf/deref ref:token-sets-context-menu)
|
||||
|
||||
position-top
|
||||
(+ (dm/get-prop position :y) 5)
|
||||
|
@ -74,8 +69,7 @@
|
|||
(+ (dm/get-prop position :x) 5)
|
||||
|
||||
on-close
|
||||
(mf/use-fn
|
||||
#(st/emit! wdt/hide-token-set-context-menu))]
|
||||
(mf/use-fn #(st/emit! (dt/assign-token-set-context-menu nil)))]
|
||||
|
||||
[:& dropdown {:show (some? position)
|
||||
:on-close on-close}
|
||||
|
|
|
@ -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.json :as json]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.main.data.event :as ev]
|
||||
|
@ -30,9 +29,8 @@
|
|||
[app.main.ui.workspace.tokens.changes :as wtch]
|
||||
[app.main.ui.workspace.tokens.context-menu :refer [token-context-menu]]
|
||||
[app.main.ui.workspace.tokens.errors :as wte]
|
||||
[app.main.ui.workspace.tokens.sets :refer [sets-list*]]
|
||||
[app.main.ui.workspace.tokens.sets-context :as sets-context]
|
||||
[app.main.ui.workspace.tokens.sets-context-menu :refer [sets-context-menu*]]
|
||||
[app.main.ui.workspace.tokens.sets :as tsets]
|
||||
[app.main.ui.workspace.tokens.sets-context-menu :refer [token-set-context-menu*]]
|
||||
[app.main.ui.workspace.tokens.style-dictionary :as sd]
|
||||
[app.main.ui.workspace.tokens.theme-select :refer [theme-select]]
|
||||
[app.main.ui.workspace.tokens.token-pill :refer [token-pill*]]
|
||||
|
@ -47,7 +45,7 @@
|
|||
[shadow.resource]))
|
||||
|
||||
(def ref:token-type-open-status
|
||||
(l/derived #(dm/get-in % [:workspace-local :token-type-open-status]) st/state))
|
||||
(l/derived (l/key :open-status-by-type) refs/workspace-tokens))
|
||||
|
||||
;; Components ------------------------------------------------------------------
|
||||
|
||||
|
@ -85,7 +83,7 @@
|
|||
(fn [event token]
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (dt/show-token-context-menu
|
||||
(st/emit! (dt/assign-token-context-menu
|
||||
{:type :token
|
||||
:position (dom/get-client-position event)
|
||||
:errors (:errors token)
|
||||
|
@ -200,43 +198,7 @@
|
|||
(tr "workspace.token.no-permission-themes"))}
|
||||
[:& theme-select]]))]))
|
||||
|
||||
(mf/defc add-set-button*
|
||||
{::mf/private true}
|
||||
[{:keys [style]}]
|
||||
(let [{:keys [on-create new-path]}
|
||||
(sets-context/use-context)
|
||||
|
||||
permissions
|
||||
(mf/use-ctx ctx/permissions)
|
||||
|
||||
can-edit?
|
||||
(get permissions :can-edit)
|
||||
|
||||
on-click
|
||||
(mf/use-fn
|
||||
(mf/deps on-create)
|
||||
(fn []
|
||||
(on-create [])))]
|
||||
|
||||
(if (= style "inline")
|
||||
(when-not new-path
|
||||
(if can-edit?
|
||||
[:div {:class (stl/css :empty-sets-wrapper)}
|
||||
[:> text* {:as "span" :typography "body-small" :class (stl/css :empty-state-message)}
|
||||
(tr "workspace.token.no-sets-yet")]
|
||||
[:button {:on-click on-click
|
||||
:class (stl/css :create-theme-button)}
|
||||
(tr "workspace.token.create-one")]]
|
||||
[:div {:class (stl/css :empty-sets-wrapper)}
|
||||
[:> text* {:as "span" :typography "body-small" :class (stl/css :empty-state-message)}
|
||||
(tr "workspace.token.no-sets-yet")]]))
|
||||
(when can-edit?
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:icon "add"
|
||||
:on-click on-click
|
||||
:aria-label (tr "workspace.token.add set")}]))))
|
||||
|
||||
(mf/defc theme-sets-list*
|
||||
(mf/defc token-sets-list*
|
||||
{::mf/private true}
|
||||
[{:keys [tokens-lib]}]
|
||||
(let [;; FIXME: This is an inneficient operation just for being
|
||||
|
@ -250,27 +212,31 @@
|
|||
selected-token-set-name
|
||||
(mf/deref refs/selected-token-set-name)
|
||||
|
||||
{:keys [new-path] :as ctx}
|
||||
(sets-context/use-context)]
|
||||
{:keys [token-set-edition-id
|
||||
token-set-new-path]}
|
||||
(mf/deref refs/workspace-tokens)]
|
||||
|
||||
(if (and (empty? token-sets)
|
||||
(not new-path))
|
||||
[:> add-set-button* {:style "inline"}]
|
||||
[:& h/sortable-container {}
|
||||
[:> sets-list* {:tokens-lib tokens-lib
|
||||
:selected-token-set-name selected-token-set-name}]])))
|
||||
(not token-set-new-path))
|
||||
|
||||
(mf/defc themes-sets-tab*
|
||||
(when-not token-set-new-path
|
||||
[:> tsets/inline-add-button*])
|
||||
|
||||
[:> h/sortable-container {}
|
||||
[:> tsets/sets-list*
|
||||
{:tokens-lib tokens-lib
|
||||
:new-path token-set-new-path
|
||||
:edition-id token-set-edition-id
|
||||
:selected selected-token-set-name}]])))
|
||||
|
||||
(mf/defc token-sets-section*
|
||||
{::mf/private true}
|
||||
[{:keys [resize-height] :as props}]
|
||||
(let [permissions
|
||||
(mf/use-ctx ctx/permissions)
|
||||
(let [can-edit?
|
||||
(mf/use-ctx ctx/can-edit?)]
|
||||
|
||||
can-edit?
|
||||
(get permissions :can-edit)]
|
||||
|
||||
[:& sets-context/provider {}
|
||||
[:> sets-context-menu*]
|
||||
[:*
|
||||
[:> token-set-context-menu*]
|
||||
[:article {:data-testid "token-themes-sets-sidebar"
|
||||
:class (stl/css :sets-section-wrapper)
|
||||
:style {"--resize-height" (str resize-height "px")}}
|
||||
|
@ -279,11 +245,11 @@
|
|||
[:div {:class (stl/css :sidebar-header)}
|
||||
[:& title-bar {:title (tr "labels.sets")}
|
||||
(when can-edit?
|
||||
[:> add-set-button* {:style "header"}])]]
|
||||
[:> tsets/add-button*])]]
|
||||
|
||||
[:> theme-sets-list* props]]]]))
|
||||
[:> token-sets-list* props]]]]))
|
||||
|
||||
(mf/defc tokens-tab*
|
||||
(mf/defc tokens-section*
|
||||
[{:keys [tokens-lib]}]
|
||||
(let [objects (mf/deref refs/workspace-page-objects)
|
||||
selected (mf/deref refs/selected-shapes)
|
||||
|
@ -475,13 +441,14 @@
|
|||
(mf/deref refs/tokens-lib)]
|
||||
|
||||
[:div {:class (stl/css :sidebar-wrapper)}
|
||||
[:> themes-sets-tab* {:resize-height size-pages-opened
|
||||
:tokens-lib tokens-lib}]
|
||||
[:> token-sets-section*
|
||||
{:resize-height size-pages-opened
|
||||
:tokens-lib tokens-lib}]
|
||||
[:article {:class (stl/css :tokens-section-wrapper)
|
||||
:data-testid "tokens-sidebar"}
|
||||
[:div {:class (stl/css :resize-area-horiz)
|
||||
:on-pointer-down on-pointer-down-pages
|
||||
:on-lost-pointer-capture on-lost-pointer-capture-pages
|
||||
:on-pointer-move on-pointer-move-pages}]
|
||||
[:> tokens-tab* {:tokens-lib tokens-lib}]]
|
||||
[:> tokens-section* {:tokens-lib tokens-lib}]]
|
||||
[:> import-export-button*]]))
|
||||
|
|
|
@ -54,12 +54,6 @@
|
|||
color: var(--color-foreground-secondary);
|
||||
}
|
||||
|
||||
.empty-sets-wrapper {
|
||||
padding: $s-12;
|
||||
padding-inline-start: $s-24;
|
||||
color: var(--color-foreground-secondary);
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue