mirror of
https://github.com/penpot/penpot.git
synced 2025-07-11 16:17:16 +02:00
♻️ Restructure UI files for token sets
This commit is contained in:
parent
e258030bc0
commit
ce59070fd1
10 changed files with 719 additions and 537 deletions
|
@ -99,7 +99,7 @@ export class WorkspacePage extends BaseWebSocketPage {
|
||||||
this.tokenThemeUpdateCreateModal = page.getByTestId(
|
this.tokenThemeUpdateCreateModal = page.getByTestId(
|
||||||
"token-theme-update-create-modal",
|
"token-theme-update-create-modal",
|
||||||
);
|
);
|
||||||
this.tokenThemesSetsSidebar = page.getByTestId("token-themes-sets-sidebar");
|
this.tokenThemesSetsSidebar = page.getByTestId("token-management-sidebar");
|
||||||
this.tokensSidebar = page.getByTestId("tokens-sidebar");
|
this.tokensSidebar = page.getByTestId("tokens-sidebar");
|
||||||
this.tokenSetItems = page.getByTestId("tokens-set-item");
|
this.tokenSetItems = page.getByTestId("tokens-set-item");
|
||||||
this.tokenSetGroupItems = page.getByTestId("tokens-set-group-item");
|
this.tokenSetGroupItems = page.getByTestId("tokens-set-group-item");
|
||||||
|
|
|
@ -5,33 +5,18 @@
|
||||||
;; Copyright (c) KALEIDOS INC
|
;; Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
(ns app.main.ui.workspace.tokens.sets
|
(ns app.main.ui.workspace.tokens.sets
|
||||||
(:require-macros [app.main.style :as stl])
|
|
||||||
(:require
|
(:require
|
||||||
[app.common.data.macros :as dm]
|
|
||||||
[app.common.types.tokens-lib :as ctob]
|
[app.common.types.tokens-lib :as ctob]
|
||||||
[app.main.data.event :as ev]
|
|
||||||
[app.main.data.workspace.tokens.library-edit :as dwtl]
|
[app.main.data.workspace.tokens.library-edit :as dwtl]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.context :as ctx]
|
[app.main.ui.context :as ctx]
|
||||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
[app.main.ui.workspace.tokens.sets.helpers :as sets-helpers]
|
||||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as ic]
|
[app.main.ui.workspace.tokens.sets.lists :refer [controlled-sets-list*]]
|
||||||
[app.main.ui.ds.foundations.typography.text :refer [text*]]
|
|
||||||
[app.main.ui.hooks :as h]
|
|
||||||
[app.util.dom :as dom]
|
|
||||||
[app.util.i18n :refer [tr]]
|
|
||||||
[app.util.keyboard :as kbd]
|
|
||||||
[cuerdas.core :as str]
|
|
||||||
[potok.v2.core :as ptk]
|
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
(defn- on-select-token-set-click [name]
|
||||||
;; HELPERS
|
(st/emit! (dwtl/set-selected-token-set-name name)))
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
(defn- on-start-creation
|
|
||||||
[]
|
|
||||||
(st/emit! (dwtl/start-token-set-creation [])))
|
|
||||||
|
|
||||||
(defn- on-toggle-token-set-click [name]
|
(defn- on-toggle-token-set-click [name]
|
||||||
(st/emit! (dwtl/toggle-token-set name)))
|
(st/emit! (dwtl/toggle-token-set name)))
|
||||||
|
@ -39,498 +24,6 @@
|
||||||
(defn- on-toggle-token-set-group-click [path]
|
(defn- on-toggle-token-set-group-click [path]
|
||||||
(st/emit! (dwtl/toggle-token-set-group path)))
|
(st/emit! (dwtl/toggle-token-set-group path)))
|
||||||
|
|
||||||
(defn- on-select-token-set-click [name]
|
|
||||||
(st/emit! (dwtl/set-selected-token-set-name name)))
|
|
||||||
|
|
||||||
(defn on-update-token-set
|
|
||||||
[token-set name]
|
|
||||||
(st/emit! (dwtl/clear-token-set-edition)
|
|
||||||
(dwtl/update-token-set token-set name)))
|
|
||||||
|
|
||||||
(defn- on-update-token-set-group
|
|
||||||
[path name]
|
|
||||||
(st/emit! (dwtl/clear-token-set-edition)
|
|
||||||
(dwtl/rename-token-set-group path name)))
|
|
||||||
|
|
||||||
(defn- on-create-token-set
|
|
||||||
[parent-set name]
|
|
||||||
(let [;; FIXME: this code should be reusable under helper under
|
|
||||||
;; common types namespace
|
|
||||||
name
|
|
||||||
(if-let [parent-path (ctob/get-set-path parent-set)]
|
|
||||||
(->> (concat parent-path (ctob/split-set-name name))
|
|
||||||
(ctob/join-set-path))
|
|
||||||
(ctob/normalize-set-name name))]
|
|
||||||
|
|
||||||
(st/emit! (ptk/data-event ::ev/event {::ev/name "create-token-set" :name name})
|
|
||||||
(dwtl/create-token-set name))))
|
|
||||||
|
|
||||||
(defn group-edition-id
|
|
||||||
"Prefix editing groups `edition-id` so it can be differentiated from sets with the same id."
|
|
||||||
[edition-id]
|
|
||||||
(str "group-" edition-id))
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;; COMPONENTS
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
(mf/defc editing-label*
|
|
||||||
{::mf/private true}
|
|
||||||
[{:keys [default-value on-cancel on-submit]}]
|
|
||||||
(let [on-submit
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps on-cancel on-submit default-value)
|
|
||||||
(fn [event]
|
|
||||||
(let [value (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 on-cancel)
|
|
||||||
(fn [event]
|
|
||||||
(cond
|
|
||||||
(kbd/enter? event) (on-submit event)
|
|
||||||
(kbd/esc? event) (on-cancel))))]
|
|
||||||
[:input
|
|
||||||
{:class (stl/css :editing-node)
|
|
||||||
:type "text"
|
|
||||||
:on-blur on-submit
|
|
||||||
:on-key-down on-key-down
|
|
||||||
:maxlength "256"
|
|
||||||
:auto-focus true
|
|
||||||
:placeholder (tr "workspace.tokens.set-edit-placeholder")
|
|
||||||
:default-value default-value}]))
|
|
||||||
|
|
||||||
(mf/defc checkbox*
|
|
||||||
[{:keys [checked aria-label on-click disabled]}]
|
|
||||||
(let [all? (true? checked)
|
|
||||||
mixed? (= checked "mixed")
|
|
||||||
checked? (or all? mixed?)]
|
|
||||||
|
|
||||||
[:div {:role "checkbox"
|
|
||||||
:aria-checked (dm/str checked)
|
|
||||||
:disabled disabled
|
|
||||||
:title (when disabled (tr "workspace.tokens.no-permisions-set"))
|
|
||||||
:tab-index 0
|
|
||||||
:class (stl/css-case :checkbox-style true
|
|
||||||
:checkbox-checked-style checked?
|
|
||||||
:checkbox-disabled-checked (and checked? disabled)
|
|
||||||
:checkbox-disabled disabled)
|
|
||||||
:on-click (when-not disabled on-click)}
|
|
||||||
|
|
||||||
(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 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.tokens.no-sets-yet")]
|
|
||||||
[:button {:on-click on-start-creation
|
|
||||||
:class (stl/css :create-set-button)}
|
|
||||||
(tr "workspace.tokens.create-one")]]
|
|
||||||
[:div {:class (stl/css :empty-sets-wrapper)}
|
|
||||||
[:> text* {:as "span" :typography "body-small" :class (stl/css :empty-state-message)}
|
|
||||||
(tr "workspace.tokens.no-sets-yet")]])))
|
|
||||||
|
|
||||||
(mf/defc add-button*
|
|
||||||
[]
|
|
||||||
[:> icon-button* {:variant "ghost"
|
|
||||||
:icon "add"
|
|
||||||
:on-click on-start-creation
|
|
||||||
:aria-label (tr "workspace.tokens.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 is-editing id tree-path can-edit?)
|
|
||||||
(fn [event]
|
|
||||||
(dom/prevent-default event)
|
|
||||||
(dom/stop-propagation event)
|
|
||||||
(when (and can-edit? (not is-editing))
|
|
||||||
(st/emit! (dwtl/assign-token-set-context-menu
|
|
||||||
{:position (dom/get-client-position event)
|
|
||||||
:is-group true
|
|
||||||
:id id
|
|
||||||
:edition-id (group-edition-id id)
|
|
||||||
:path tree-path})))))
|
|
||||||
|
|
||||||
on-collapse-click
|
|
||||||
(mf/use-fn
|
|
||||||
(fn [event]
|
|
||||||
(dom/prevent-default event)
|
|
||||||
(dom/stop-propagation event)
|
|
||||||
(on-toggle-collapse tree-path)))
|
|
||||||
|
|
||||||
on-double-click
|
|
||||||
(mf/use-fn (mf/deps id) #(on-start-edition (group-edition-id id)))
|
|
||||||
|
|
||||||
on-checkbox-click
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps on-toggle tree-path can-edit?)
|
|
||||||
#(on-toggle tree-path))
|
|
||||||
|
|
||||||
on-edit-submit'
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps tree-path on-edit-submit can-edit?)
|
|
||||||
#(on-edit-submit tree-path %))
|
|
||||||
|
|
||||||
on-drop
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps tree-index on-drop)
|
|
||||||
(fn [position data]
|
|
||||||
(on-drop tree-index position data)))
|
|
||||||
|
|
||||||
[dprops dref]
|
|
||||||
(h/use-sortable
|
|
||||||
:data-type "penpot/token-set"
|
|
||||||
:on-drop on-drop
|
|
||||||
:data {:index tree-index
|
|
||||||
:is-group true}
|
|
||||||
:detect-center? true
|
|
||||||
:draggable? is-draggable)]
|
|
||||||
|
|
||||||
[:div {:ref dref
|
|
||||||
: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 is-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
|
|
||||||
:data-testid "tokens-set-group-collapse"
|
|
||||||
:aria-label (tr "labels.collapse")
|
|
||||||
:icon (if is-collapsed "arrow-right" "arrow-down")
|
|
||||||
:variant "action"}]
|
|
||||||
(if is-editing
|
|
||||||
[:> editing-label*
|
|
||||||
{:default-value label
|
|
||||||
:on-cancel on-reset-edition
|
|
||||||
:on-submit on-edit-submit'}]
|
|
||||||
[:*
|
|
||||||
[:div {:class (stl/css :set-name)
|
|
||||||
:role "button"
|
|
||||||
:title label
|
|
||||||
:tab-index 0
|
|
||||||
:on-double-click on-double-click
|
|
||||||
:id label-id}
|
|
||||||
label]
|
|
||||||
[:> checkbox*
|
|
||||||
{:on-click on-checkbox-click
|
|
||||||
:disabled (not can-edit?)
|
|
||||||
:checked (case is-active
|
|
||||||
:all true
|
|
||||||
:partial "mixed"
|
|
||||||
:none false)
|
|
||||||
:arial-label (tr "workspace.tokens.select-set")}]])]))
|
|
||||||
|
|
||||||
(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 is-editing tree-path)
|
|
||||||
(fn [event]
|
|
||||||
(dom/stop-propagation event)
|
|
||||||
(when-not is-editing
|
|
||||||
(when (fn? on-select)
|
|
||||||
(on-select set-name)))))
|
|
||||||
|
|
||||||
on-context-menu
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps is-editing id tree-path can-edit?)
|
|
||||||
(fn [event]
|
|
||||||
(dom/prevent-default event)
|
|
||||||
(dom/stop-propagation event)
|
|
||||||
(when (and can-edit? (not is-editing))
|
|
||||||
(st/emit! (dwtl/assign-token-set-context-menu
|
|
||||||
{:position (dom/get-client-position event)
|
|
||||||
:is-group false
|
|
||||||
:id id
|
|
||||||
:edition-id id
|
|
||||||
:path tree-path})))))
|
|
||||||
|
|
||||||
on-double-click
|
|
||||||
(mf/use-fn (mf/deps id) #(on-start-edition id))
|
|
||||||
|
|
||||||
on-checkbox-click
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps set-name on-toggle)
|
|
||||||
(fn [event]
|
|
||||||
(dom/stop-propagation event)
|
|
||||||
(when (fn? on-toggle)
|
|
||||||
(on-toggle set-name))))
|
|
||||||
|
|
||||||
on-edit-submit'
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps set on-edit-submit)
|
|
||||||
#(on-edit-submit set %))
|
|
||||||
|
|
||||||
on-drag
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps tree-path)
|
|
||||||
(fn [_]
|
|
||||||
(when-not is-selected
|
|
||||||
(on-select tree-path))))
|
|
||||||
|
|
||||||
on-drop
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps tree-index on-drop)
|
|
||||||
(fn [position data]
|
|
||||||
(on-drop tree-index position data)))
|
|
||||||
|
|
||||||
[dprops dref]
|
|
||||||
(h/use-sortable
|
|
||||||
:data-type "penpot/token-set"
|
|
||||||
:on-drag on-drag
|
|
||||||
:on-drop on-drop
|
|
||||||
:data {:index tree-index
|
|
||||||
: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 is-selected
|
|
||||||
:dnd-over (= drop-over :center)
|
|
||||||
:dnd-over-top (= drop-over :top)
|
|
||||||
:dnd-over-bot (= drop-over :bot))
|
|
||||||
:on-click on-click
|
|
||||||
:on-double-click on-double-click
|
|
||||||
:on-context-menu on-context-menu
|
|
||||||
:aria-checked is-active}
|
|
||||||
|
|
||||||
[:> icon*
|
|
||||||
{:icon-id "document"
|
|
||||||
:class (stl/css-case :icon true
|
|
||||||
:root-icon (not tree-depth))}]
|
|
||||||
(if is-editing
|
|
||||||
[:> editing-label*
|
|
||||||
{:default-value label
|
|
||||||
:on-cancel on-reset-edition
|
|
||||||
:on-submit on-edit-submit'}]
|
|
||||||
[:*
|
|
||||||
[:div {:class (stl/css :set-name)}
|
|
||||||
label]
|
|
||||||
[:> checkbox*
|
|
||||||
{:on-click on-checkbox-click
|
|
||||||
:disabled (not can-edit?)
|
|
||||||
:arial-label (tr "workspace.tokens.select-set")
|
|
||||||
:checked is-active}]])]))
|
|
||||||
|
|
||||||
(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
|
|
||||||
token-sets
|
|
||||||
new-path
|
|
||||||
edition-id]}]
|
|
||||||
|
|
||||||
(let [collapsed-paths* (mf/use-state #{})
|
|
||||||
collapsed-paths (deref collapsed-paths*)
|
|
||||||
|
|
||||||
collapsed?
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps collapsed-paths)
|
|
||||||
(partial contains? collapsed-paths))
|
|
||||||
|
|
||||||
on-drop
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps collapsed-paths)
|
|
||||||
(fn [tree-index position data]
|
|
||||||
(let [params {:from-index (:index data)
|
|
||||||
:to-index tree-index
|
|
||||||
:position position
|
|
||||||
:collapsed-paths collapsed-paths}]
|
|
||||||
(if (:is-group data)
|
|
||||||
(st/emit! (dwtl/drop-token-set-group params))
|
|
||||||
(st/emit! (dwtl/drop-token-set params))))))
|
|
||||||
|
|
||||||
on-toggle-collapse
|
|
||||||
(mf/use-fn
|
|
||||||
(fn [path]
|
|
||||||
(swap! collapsed-paths* #(if (contains? % path)
|
|
||||||
(disj % path)
|
|
||||||
(conj % path)))))]
|
|
||||||
|
|
||||||
(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 (group-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
|
|
||||||
[:> 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/props :obj}
|
|
||||||
[{:keys [token-sets
|
|
||||||
selected
|
|
||||||
on-update-token-set
|
|
||||||
on-update-token-set-group
|
|
||||||
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
|
|
||||||
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))))]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :sets-list)}
|
|
||||||
(if ^boolean empty-state?
|
|
||||||
[:> text* {:as "span" :typography "body-small" :class (stl/css :empty-state-message-sets)}
|
|
||||||
(tr "workspace.tokens.no-sets-create")]
|
|
||||||
|
|
||||||
[:> token-sets-tree*
|
|
||||||
{:is-draggable draggable?
|
|
||||||
:new-path new-path
|
|
||||||
:edition-id edition-id
|
|
||||||
:token-sets token-sets
|
|
||||||
:selected selected
|
|
||||||
:on-select on-select
|
|
||||||
: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
|
|
||||||
:on-create-token-set on-create-token-set
|
|
||||||
: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*
|
(mf/defc sets-list*
|
||||||
[{:keys [tokens-lib selected new-path edition-id]}]
|
[{:keys [tokens-lib selected new-path edition-id]}]
|
||||||
|
|
||||||
|
@ -589,6 +82,6 @@
|
||||||
|
|
||||||
:on-toggle-token-set on-toggle-token-set-click
|
:on-toggle-token-set on-toggle-token-set-click
|
||||||
:on-toggle-token-set-group on-toggle-token-set-group-click
|
:on-toggle-token-set-group on-toggle-token-set-group-click
|
||||||
:on-update-token-set on-update-token-set
|
:on-update-token-set sets-helpers/on-update-token-set
|
||||||
:on-update-token-set-group on-update-token-set-group
|
:on-update-token-set-group sets-helpers/on-update-token-set-group
|
||||||
:on-create-token-set on-create-token-set}]))
|
:on-create-token-set sets-helpers/on-create-token-set}]))
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) KALEIDOS INC
|
;; Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
(ns app.main.ui.workspace.tokens.sets-context-menu
|
(ns app.main.ui.workspace.tokens.sets.context-menu
|
||||||
(:require-macros [app.main.style :as stl])
|
(:require-macros [app.main.style :as stl])
|
||||||
(:require
|
(:require
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
|
@ -4,7 +4,7 @@
|
||||||
//
|
//
|
||||||
// Copyright (c) KALEIDOS INC
|
// Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
@use "../../ds/typography.scss" as t;
|
@use "../../../ds/typography.scss" as t;
|
||||||
@import "refactor/common-refactor.scss";
|
@import "refactor/common-refactor.scss";
|
||||||
|
|
||||||
.token-set-context-menu {
|
.token-set-context-menu {
|
36
frontend/src/app/main/ui/workspace/tokens/sets/helpers.cljs
Normal file
36
frontend/src/app/main/ui/workspace/tokens/sets/helpers.cljs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
(ns app.main.ui.workspace.tokens.sets.helpers
|
||||||
|
(:require
|
||||||
|
[app.common.types.tokens-lib :as ctob]
|
||||||
|
[app.main.data.event :as ev]
|
||||||
|
[app.main.data.workspace.tokens.library-edit :as dwtl]
|
||||||
|
[app.main.store :as st]
|
||||||
|
[potok.v2.core :as ptk]))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; HELPERS - Shared functions for token sets management
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(defn on-update-token-set
|
||||||
|
[token-set name]
|
||||||
|
(st/emit! (dwtl/clear-token-set-edition)
|
||||||
|
(dwtl/update-token-set token-set name)))
|
||||||
|
|
||||||
|
(defn on-update-token-set-group
|
||||||
|
[path name]
|
||||||
|
(st/emit! (dwtl/clear-token-set-edition)
|
||||||
|
(dwtl/rename-token-set-group path name)))
|
||||||
|
|
||||||
|
(defn on-create-token-set
|
||||||
|
[parent-set name]
|
||||||
|
(let [;; FIXME: this code should be reusable under helper under
|
||||||
|
;; common types namespace
|
||||||
|
name
|
||||||
|
(if-let [parent-path (ctob/get-set-path parent-set)]
|
||||||
|
(->> (concat parent-path (ctob/split-set-name name))
|
||||||
|
(ctob/join-set-path))
|
||||||
|
(ctob/normalize-set-name name))]
|
||||||
|
|
||||||
|
(st/emit! (ptk/data-event ::ev/event {::ev/name "create-token-set" :name name})
|
||||||
|
(dwtl/create-token-set name))))
|
||||||
|
|
||||||
|
|
492
frontend/src/app/main/ui/workspace/tokens/sets/lists.cljs
Normal file
492
frontend/src/app/main/ui/workspace/tokens/sets/lists.cljs
Normal file
|
@ -0,0 +1,492 @@
|
||||||
|
;; 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.lists
|
||||||
|
(:require-macros [app.main.style :as stl])
|
||||||
|
(:require
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.types.tokens-lib :as ctob]
|
||||||
|
[app.main.data.workspace.tokens.library-edit :as dwtl]
|
||||||
|
[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.helpers :as sets-helpers]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[app.util.i18n :refer [tr]]
|
||||||
|
[app.util.keyboard :as kbd]
|
||||||
|
[cuerdas.core :as str]
|
||||||
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
(defn- on-start-creation
|
||||||
|
[]
|
||||||
|
(st/emit! (dwtl/start-token-set-creation [])))
|
||||||
|
|
||||||
|
(defn- group-edition-id
|
||||||
|
"Prefix editing groups `edition-id` so it can be differentiated from sets with the same id."
|
||||||
|
[edition-id]
|
||||||
|
(str "group-" edition-id))
|
||||||
|
|
||||||
|
|
||||||
|
(mf/defc editing-label*
|
||||||
|
{::mf/private true}
|
||||||
|
[{:keys [default-value on-cancel on-submit]}]
|
||||||
|
(let [on-submit
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps on-cancel on-submit default-value)
|
||||||
|
(fn [event]
|
||||||
|
(let [value (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 on-cancel)
|
||||||
|
(fn [event]
|
||||||
|
(cond
|
||||||
|
(kbd/enter? event) (on-submit event)
|
||||||
|
(kbd/esc? event) (on-cancel))))]
|
||||||
|
[:input
|
||||||
|
{:class (stl/css :editing-node)
|
||||||
|
:type "text"
|
||||||
|
:on-blur on-submit
|
||||||
|
:on-key-down on-key-down
|
||||||
|
:maxlength "256"
|
||||||
|
:auto-focus true
|
||||||
|
:placeholder (tr "workspace.tokens.set-edit-placeholder")
|
||||||
|
:default-value default-value}]))
|
||||||
|
|
||||||
|
(mf/defc checkbox*
|
||||||
|
[{:keys [checked aria-label on-click disabled]}]
|
||||||
|
(let [all? (true? checked)
|
||||||
|
mixed? (= checked "mixed")
|
||||||
|
checked? (or all? mixed?)]
|
||||||
|
|
||||||
|
[:div {:role "checkbox"
|
||||||
|
:aria-checked (dm/str checked)
|
||||||
|
:disabled disabled
|
||||||
|
:title (when disabled (tr "workspace.tokens.no-permisions-set"))
|
||||||
|
:tab-index 0
|
||||||
|
:class (stl/css-case :checkbox-style true
|
||||||
|
:checkbox-checked-style checked?
|
||||||
|
:checkbox-disabled-checked (and checked? disabled)
|
||||||
|
:checkbox-disabled disabled)
|
||||||
|
:on-click (when-not disabled on-click)}
|
||||||
|
|
||||||
|
(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 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.tokens.no-sets-yet")]
|
||||||
|
[:button {:on-click on-start-creation
|
||||||
|
:class (stl/css :create-set-button)}
|
||||||
|
(tr "workspace.tokens.create-one")]]
|
||||||
|
[:div {:class (stl/css :empty-sets-wrapper)}
|
||||||
|
[:> text* {:as "span" :typography "body-small" :class (stl/css :empty-state-message)}
|
||||||
|
(tr "workspace.tokens.no-sets-yet")]])))
|
||||||
|
|
||||||
|
(mf/defc add-button*
|
||||||
|
[]
|
||||||
|
[:> icon-button* {:variant "ghost"
|
||||||
|
:icon "add"
|
||||||
|
:on-click on-start-creation
|
||||||
|
:aria-label (tr "workspace.tokens.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 is-editing id tree-path can-edit?)
|
||||||
|
(fn [event]
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(dom/stop-propagation event)
|
||||||
|
(when (and can-edit? (not is-editing))
|
||||||
|
(st/emit! (dwtl/assign-token-set-context-menu
|
||||||
|
{:position (dom/get-client-position event)
|
||||||
|
:is-group true
|
||||||
|
:id id
|
||||||
|
:edition-id (group-edition-id id)
|
||||||
|
:path tree-path})))))
|
||||||
|
|
||||||
|
on-collapse-click
|
||||||
|
(mf/use-fn
|
||||||
|
(fn [event]
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(dom/stop-propagation event)
|
||||||
|
(on-toggle-collapse tree-path)))
|
||||||
|
|
||||||
|
on-double-click
|
||||||
|
(mf/use-fn (mf/deps id) #(on-start-edition (group-edition-id id)))
|
||||||
|
|
||||||
|
on-checkbox-click
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps on-toggle tree-path can-edit?)
|
||||||
|
#(on-toggle tree-path))
|
||||||
|
|
||||||
|
on-edit-submit'
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps tree-path on-edit-submit can-edit?)
|
||||||
|
#(on-edit-submit tree-path %))
|
||||||
|
|
||||||
|
on-drop
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps tree-index on-drop)
|
||||||
|
(fn [position data]
|
||||||
|
(on-drop tree-index position data)))
|
||||||
|
|
||||||
|
[dprops dref]
|
||||||
|
(h/use-sortable
|
||||||
|
:data-type "penpot/token-set"
|
||||||
|
:on-drop on-drop
|
||||||
|
:data {:index tree-index
|
||||||
|
:is-group true}
|
||||||
|
:detect-center? true
|
||||||
|
:draggable? is-draggable)]
|
||||||
|
|
||||||
|
[:div {:ref dref
|
||||||
|
: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 is-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
|
||||||
|
:data-testid "tokens-set-group-collapse"
|
||||||
|
:aria-label (tr "labels.collapse")
|
||||||
|
:icon (if is-collapsed "arrow-right" "arrow-down")
|
||||||
|
:variant "action"}]
|
||||||
|
(if is-editing
|
||||||
|
[:> editing-label*
|
||||||
|
{:default-value label
|
||||||
|
:on-cancel on-reset-edition
|
||||||
|
:on-submit on-edit-submit'}]
|
||||||
|
[:*
|
||||||
|
[:div {:class (stl/css :set-name)
|
||||||
|
:role "button"
|
||||||
|
:title label
|
||||||
|
:tab-index 0
|
||||||
|
:on-double-click on-double-click
|
||||||
|
:id label-id}
|
||||||
|
label]
|
||||||
|
[:> checkbox*
|
||||||
|
{:on-click on-checkbox-click
|
||||||
|
:disabled (not can-edit?)
|
||||||
|
:checked (case is-active
|
||||||
|
:all true
|
||||||
|
:partial "mixed"
|
||||||
|
:none false)
|
||||||
|
:arial-label (tr "workspace.tokens.select-set")}]])]))
|
||||||
|
|
||||||
|
(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 is-editing tree-path)
|
||||||
|
(fn [event]
|
||||||
|
(dom/stop-propagation event)
|
||||||
|
(when-not is-editing
|
||||||
|
(when (fn? on-select)
|
||||||
|
(on-select set-name)))))
|
||||||
|
|
||||||
|
on-context-menu
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps is-editing id tree-path can-edit?)
|
||||||
|
(fn [event]
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(dom/stop-propagation event)
|
||||||
|
(when (and can-edit? (not is-editing))
|
||||||
|
(st/emit! (dwtl/assign-token-set-context-menu
|
||||||
|
{:position (dom/get-client-position event)
|
||||||
|
:is-group false
|
||||||
|
:id id
|
||||||
|
:edition-id id
|
||||||
|
:path tree-path})))))
|
||||||
|
|
||||||
|
on-double-click
|
||||||
|
(mf/use-fn (mf/deps id) #(on-start-edition id))
|
||||||
|
|
||||||
|
on-checkbox-click
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps set-name on-toggle)
|
||||||
|
(fn [event]
|
||||||
|
(dom/stop-propagation event)
|
||||||
|
(when (fn? on-toggle)
|
||||||
|
(on-toggle set-name))))
|
||||||
|
|
||||||
|
on-edit-submit'
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps set on-edit-submit)
|
||||||
|
#(on-edit-submit set %))
|
||||||
|
|
||||||
|
on-drag
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps tree-path)
|
||||||
|
(fn [_]
|
||||||
|
(when-not is-selected
|
||||||
|
(on-select tree-path))))
|
||||||
|
|
||||||
|
on-drop
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps tree-index on-drop)
|
||||||
|
(fn [position data]
|
||||||
|
(on-drop tree-index position data)))
|
||||||
|
|
||||||
|
[dprops dref]
|
||||||
|
(h/use-sortable
|
||||||
|
:data-type "penpot/token-set"
|
||||||
|
:on-drag on-drag
|
||||||
|
:on-drop on-drop
|
||||||
|
:data {:index tree-index
|
||||||
|
: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 is-selected
|
||||||
|
:dnd-over (= drop-over :center)
|
||||||
|
:dnd-over-top (= drop-over :top)
|
||||||
|
:dnd-over-bot (= drop-over :bot))
|
||||||
|
:on-click on-click
|
||||||
|
:on-double-click on-double-click
|
||||||
|
:on-context-menu on-context-menu
|
||||||
|
:aria-checked is-active}
|
||||||
|
|
||||||
|
[:> icon*
|
||||||
|
{:icon-id "document"
|
||||||
|
:class (stl/css-case :icon true
|
||||||
|
:root-icon (not tree-depth))}]
|
||||||
|
(if is-editing
|
||||||
|
[:> editing-label*
|
||||||
|
{:default-value label
|
||||||
|
:on-cancel on-reset-edition
|
||||||
|
:on-submit on-edit-submit'}]
|
||||||
|
[:*
|
||||||
|
[:div {:class (stl/css :set-name)}
|
||||||
|
label]
|
||||||
|
[:> checkbox*
|
||||||
|
{:on-click on-checkbox-click
|
||||||
|
:disabled (not can-edit?)
|
||||||
|
:arial-label (tr "workspace.tokens.select-set")
|
||||||
|
:checked is-active}]])]))
|
||||||
|
|
||||||
|
(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
|
||||||
|
token-sets
|
||||||
|
new-path
|
||||||
|
edition-id]}]
|
||||||
|
|
||||||
|
(let [collapsed-paths* (mf/use-state #{})
|
||||||
|
collapsed-paths (deref collapsed-paths*)
|
||||||
|
|
||||||
|
collapsed?
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps collapsed-paths)
|
||||||
|
(partial contains? collapsed-paths))
|
||||||
|
|
||||||
|
on-drop
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps collapsed-paths)
|
||||||
|
(fn [tree-index position data]
|
||||||
|
(let [params {:from-index (:index data)
|
||||||
|
:to-index tree-index
|
||||||
|
:position position
|
||||||
|
:collapsed-paths collapsed-paths}]
|
||||||
|
(if (:is-group data)
|
||||||
|
(st/emit! (dwtl/drop-token-set-group params))
|
||||||
|
(st/emit! (dwtl/drop-token-set params))))))
|
||||||
|
|
||||||
|
on-toggle-collapse
|
||||||
|
(mf/use-fn
|
||||||
|
(fn [path]
|
||||||
|
(swap! collapsed-paths* #(if (contains? % path)
|
||||||
|
(disj % path)
|
||||||
|
(conj % path)))))]
|
||||||
|
|
||||||
|
(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 (group-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 sets-helpers/on-create-token-set}]
|
||||||
|
|
||||||
|
:else
|
||||||
|
[:> 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/props :obj}
|
||||||
|
[{:keys [token-sets
|
||||||
|
selected
|
||||||
|
on-update-token-set
|
||||||
|
on-update-token-set-group
|
||||||
|
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
|
||||||
|
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))))]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :sets-list)}
|
||||||
|
(if ^boolean empty-state?
|
||||||
|
[:> text* {:as "span" :typography "body-small" :class (stl/css :empty-state-message-sets)}
|
||||||
|
(tr "workspace.tokens.no-sets-create")]
|
||||||
|
|
||||||
|
[:> token-sets-tree*
|
||||||
|
{:is-draggable draggable?
|
||||||
|
:new-path new-path
|
||||||
|
:edition-id edition-id
|
||||||
|
:token-sets token-sets
|
||||||
|
:selected selected
|
||||||
|
:on-select on-select
|
||||||
|
: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
|
||||||
|
:on-create-token-set on-create-token-set
|
||||||
|
: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}])]))
|
164
frontend/src/app/main/ui/workspace/tokens/sets/lists.scss
Normal file
164
frontend/src/app/main/ui/workspace/tokens/sets/lists.scss
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
@use "../../../ds/typography.scss" as *;
|
||||||
|
@import "refactor/common-refactor.scss";
|
||||||
|
|
||||||
|
.sets-list {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 0;
|
||||||
|
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;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
min-height: $s-32;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--layer-row-foreground-color);
|
||||||
|
padding-left: calc($s-24 * var(--tree-depth, 0) + $s-8);
|
||||||
|
border: $s-2 solid transparent;
|
||||||
|
gap: $s-2;
|
||||||
|
|
||||||
|
&.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-group {
|
||||||
|
cursor: unset;
|
||||||
|
padding-left: calc($s-24 * var(--tree-depth, 0));
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.set-item-group-collapse-button {
|
||||||
|
cursor: pointer;
|
||||||
|
width: auto;
|
||||||
|
height: $s-28;
|
||||||
|
}
|
||||||
|
|
||||||
|
.set-name {
|
||||||
|
@include textEllipsis;
|
||||||
|
flex-grow: 1;
|
||||||
|
padding-left: $s-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: $s-20;
|
||||||
|
height: $s-20;
|
||||||
|
padding-right: $s-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-icon {
|
||||||
|
margin-left: $s-8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-style {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: $s-16;
|
||||||
|
height: $s-16;
|
||||||
|
margin-inline: $s-6;
|
||||||
|
background-color: var(--input-checkbox-background-color-rest);
|
||||||
|
border: $s-1 solid var(--input-checkbox-border-color-rest);
|
||||||
|
border-radius: $s-4;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-checked-style {
|
||||||
|
background-color: var(--input-border-color-active);
|
||||||
|
color: var(--color-background-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-disabled {
|
||||||
|
border: $s-1 solid var(--color-background-quaternary);
|
||||||
|
background-color: var(--color-background-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-disabled-checked {
|
||||||
|
background-color: var(--color-accent-primary-muted);
|
||||||
|
color: var(--color-background-quaternary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-icon {
|
||||||
|
color: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.set-item-container:hover {
|
||||||
|
background-color: var(--layer-row-background-color-hover);
|
||||||
|
color: var(--layer-row-foreground-color-hover);
|
||||||
|
box-shadow: -100px 0 0 0 var(--layer-row-background-color-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state-message-sets {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
padding: $s-12;
|
||||||
|
color: var(--color-foreground-secondary);
|
||||||
|
}
|
||||||
|
.selected-set {
|
||||||
|
background-color: var(--layer-row-background-color-selected);
|
||||||
|
color: var(--layer-row-foreground-color-selected);
|
||||||
|
box-shadow: -100px 0 0 0 var(--layer-row-background-color-selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsabled-icon {
|
||||||
|
@include buttonStyle;
|
||||||
|
@include flexCenter;
|
||||||
|
height: $s-24;
|
||||||
|
border-radius: $br-8;
|
||||||
|
&:hover {
|
||||||
|
color: var(--title-foreground-color-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.editing-node {
|
||||||
|
@include textEllipsis;
|
||||||
|
@include bodySmallTypography;
|
||||||
|
@include removeInputStyle;
|
||||||
|
|
||||||
|
border: $s-1 solid var(--input-border-color-focus);
|
||||||
|
border-radius: $br-8;
|
||||||
|
color: var(--layer-row-foreground-color-focus);
|
||||||
|
flex-grow: 1;
|
||||||
|
height: $s-28;
|
||||||
|
margin: 0;
|
||||||
|
padding-left: $s-6;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: var(--layer-row-foreground-color-placeholder);
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,7 +29,8 @@
|
||||||
[app.main.ui.workspace.sidebar.assets.common :as cmm]
|
[app.main.ui.workspace.sidebar.assets.common :as cmm]
|
||||||
[app.main.ui.workspace.tokens.common.context-menu :refer [token-context-menu]]
|
[app.main.ui.workspace.tokens.common.context-menu :refer [token-context-menu]]
|
||||||
[app.main.ui.workspace.tokens.sets :as tsets]
|
[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.sets.context-menu :refer [token-set-context-menu*]]
|
||||||
|
[app.main.ui.workspace.tokens.sets.lists :as tsetslist]
|
||||||
[app.main.ui.workspace.tokens.themes :refer [themes-header*]]
|
[app.main.ui.workspace.tokens.themes :refer [themes-header*]]
|
||||||
[app.main.ui.workspace.tokens.token-pill :refer [token-pill*]]
|
[app.main.ui.workspace.tokens.token-pill :refer [token-pill*]]
|
||||||
[app.util.array :as array]
|
[app.util.array :as array]
|
||||||
|
@ -188,7 +189,7 @@
|
||||||
(not token-set-new-path))
|
(not token-set-new-path))
|
||||||
|
|
||||||
(when-not token-set-new-path
|
(when-not token-set-new-path
|
||||||
[:> tsets/inline-add-button*])
|
[:> tsetslist/inline-add-button*])
|
||||||
|
|
||||||
[:> h/sortable-container {}
|
[:> h/sortable-container {}
|
||||||
[:> tsets/sets-list*
|
[:> tsets/sets-list*
|
||||||
|
@ -197,7 +198,7 @@
|
||||||
:edition-id token-set-edition-id
|
:edition-id token-set-edition-id
|
||||||
:selected selected-token-set-name}]])))
|
:selected selected-token-set-name}]])))
|
||||||
|
|
||||||
(mf/defc token-sets-section*
|
(mf/defc token-management-section*
|
||||||
{::mf/private true}
|
{::mf/private true}
|
||||||
[{:keys [resize-height] :as props}]
|
[{:keys [resize-height] :as props}]
|
||||||
|
|
||||||
|
@ -206,17 +207,16 @@
|
||||||
|
|
||||||
[:*
|
[:*
|
||||||
[:> token-set-context-menu*]
|
[:> token-set-context-menu*]
|
||||||
[:article {:data-testid "token-themes-sets-sidebar"
|
[:section {:data-testid "token-management-sidebar"
|
||||||
:class (stl/css :sets-section-wrapper)
|
:class (stl/css :token-management-section-wrapper)
|
||||||
:style {"--resize-height" (str resize-height "px")}}
|
:style {"--resize-height" (str resize-height "px")}}
|
||||||
[:div {:class (stl/css :sets-sidebar)}
|
[:> themes-header*]
|
||||||
[:> themes-header*]
|
[:div {:class (stl/css :sidebar-header)}
|
||||||
[:div {:class (stl/css :sidebar-header)}
|
[:& title-bar {:title (tr "labels.sets")}
|
||||||
[:& title-bar {:title (tr "labels.sets")}
|
(when can-edit?
|
||||||
(when can-edit?
|
[:> tsetslist/add-button*])]]
|
||||||
[:> tsets/add-button*])]]
|
|
||||||
|
|
||||||
[:> token-sets-list* props]]]]))
|
[:> token-sets-list* props]]]))
|
||||||
|
|
||||||
(mf/defc tokens-section*
|
(mf/defc tokens-section*
|
||||||
[{:keys [tokens-lib]}]
|
[{:keys [tokens-lib]}]
|
||||||
|
@ -398,7 +398,7 @@
|
||||||
(mf/deref refs/tokens-lib)]
|
(mf/deref refs/tokens-lib)]
|
||||||
|
|
||||||
[:div {:class (stl/css :sidebar-wrapper)}
|
[:div {:class (stl/css :sidebar-wrapper)}
|
||||||
[:> token-sets-section*
|
[:> token-management-section*
|
||||||
{:resize-height size-pages-opened
|
{:resize-height size-pages-opened
|
||||||
:tokens-lib tokens-lib}]
|
:tokens-lib tokens-lib}]
|
||||||
[:article {:class (stl/css :tokens-section-wrapper)
|
[:article {:class (stl/css :tokens-section-wrapper)
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sets-section-wrapper {
|
.token-management-section-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
@ -25,6 +25,8 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
scrollbar-gutter: stable;
|
scrollbar-gutter: stable;
|
||||||
|
position: relative;
|
||||||
|
padding-block-end: var(--sp-l);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tokens-section-wrapper {
|
.tokens-section-wrapper {
|
||||||
|
@ -34,11 +36,6 @@
|
||||||
scrollbar-gutter: stable;
|
scrollbar-gutter: stable;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sets-sidebar {
|
|
||||||
position: relative;
|
|
||||||
padding-block-end: var(--sp-l);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sets-header-container {
|
.sets-header-container {
|
||||||
@include use-typography("headline-small");
|
@include use-typography("headline-small");
|
||||||
padding: var(--sp-s);
|
padding: var(--sp-s);
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
[app.main.ui.ds.foundations.typography.heading :refer [heading*]]
|
[app.main.ui.ds.foundations.typography.heading :refer [heading*]]
|
||||||
[app.main.ui.ds.foundations.typography.text :refer [text*]]
|
[app.main.ui.ds.foundations.typography.text :refer [text*]]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.main.ui.workspace.tokens.sets :as wts]
|
[app.main.ui.workspace.tokens.sets.lists :as wts]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
[app.util.keyboard :as k]
|
[app.util.keyboard :as k]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue