mirror of https://github.com/penpot/penpot.git
✨ Save unfolded tokens path (#7949)
This commit is contained in:
parent
3f4506284b
commit
2240d93069
|
|
@ -11,10 +11,10 @@
|
|||
### :sparkles: New features & Enhancements
|
||||
|
||||
- Remap references when renaming tokens [Taiga #10202](https://tree.taiga.io/project/penpot/us/10202)
|
||||
- Tokens panel nested path view [Taiga #9966](https://tree.taiga.io/project/penpot/us/9966)
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
|
||||
## 2.13.0 (Unreleased)
|
||||
|
||||
### :boom: Breaking changes & Deprecations
|
||||
|
|
@ -46,7 +46,6 @@
|
|||
- Fix dropdown option width in Guides columns dropdown [Taiga #12959](https://tree.taiga.io/project/penpot/issue/12959)
|
||||
- Fix typos on download modal [Taiga #12865](https://tree.taiga.io/project/penpot/issue/12865)
|
||||
|
||||
|
||||
## 2.12.1
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
|
@ -55,7 +54,6 @@
|
|||
- Fix problem with style in fonts input [Taiga #12935](https://tree.taiga.io/project/penpot/issue/12935)
|
||||
- Fix problem with path editor and right click [Github #7917](https://github.com/penpot/penpot/issues/7917)
|
||||
|
||||
|
||||
## 2.12.0
|
||||
|
||||
### :boom: Breaking changes & Deprecations
|
||||
|
|
@ -67,7 +65,6 @@ The backend RPC API URLS are changed from `/api/rpc/command/<name>` to
|
|||
compatibility; however, if you are a user of this API, it is strongly
|
||||
recommended that you adapt your code to use the new PATH.
|
||||
|
||||
|
||||
#### Updated SSO Callback URL
|
||||
|
||||
The OAuth / Single Sign-On (SSO) callback endpoint has changed to
|
||||
|
|
@ -100,7 +97,6 @@ This update standardizes all authentication flows under the single URL
|
|||
and makis it more modular, enabling the ability to configure SSO auth
|
||||
provider dinamically.
|
||||
|
||||
|
||||
#### Changes on default docker compose
|
||||
|
||||
We have updated the `docker/images/docker-compose.yaml` with a small
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
[app.common.files.helpers :as cfh]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.logic.tokens :as clt]
|
||||
[app.common.path-names :as cpn]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.common.uuid :as uuid]
|
||||
|
|
@ -22,6 +23,7 @@
|
|||
[app.main.data.workspace.tokens.propagation :as dwtp]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[beicon.v2.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(declare set-selected-token-set-id)
|
||||
|
|
@ -460,12 +462,35 @@
|
|||
;; TOKEN UI OPS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn set-token-type-section-open
|
||||
[token-type open?]
|
||||
(ptk/reify ::set-token-type-section-open
|
||||
(defn clean-tokens-paths
|
||||
[]
|
||||
(ptk/reify ::clean-tokens-paths
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update-in state [:workspace-tokens :open-status-by-type] assoc token-type open?))))
|
||||
(assoc-in state [:workspace-tokens :unfolded-token-paths] []))))
|
||||
|
||||
(defn toggle-token-path
|
||||
[path]
|
||||
(ptk/reify ::toggle-token-path
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update-in state [:workspace-tokens :unfolded-token-paths]
|
||||
(fn [paths]
|
||||
(let [paths (or paths [])]
|
||||
(if (some #(= % path) paths)
|
||||
(vec (remove #(or (= % path)
|
||||
(str/starts-with? % (str path ".")))
|
||||
paths))
|
||||
(let [split-path (cpn/split-path path :separator ".")
|
||||
partial-paths (reduce
|
||||
(fn [acc segment]
|
||||
(let [new-acc (if (empty? acc)
|
||||
segment
|
||||
(str (last acc) "." segment))]
|
||||
(conj acc new-acc)))
|
||||
[]
|
||||
split-path)]
|
||||
(into paths partial-paths)))))))))
|
||||
|
||||
(defn assign-token-context-menu
|
||||
[{:keys [position] :as params}]
|
||||
|
|
|
|||
|
|
@ -152,7 +152,6 @@
|
|||
(when path-set
|
||||
(ptk/data-event :expand-token-sets {:paths path-set}))
|
||||
(dwtl/set-selected-token-set-id id)
|
||||
(dwtl/set-token-type-section-open :color true)
|
||||
(let [{:keys [modal title]} (get dwta/token-properties :color)
|
||||
window-size (dom/get-window-size)
|
||||
left-sidebar (dom/get-element "left-sidebar-aside")
|
||||
|
|
|
|||
|
|
@ -16,12 +16,8 @@
|
|||
[app.main.ui.workspace.tokens.management.group :refer [token-group*]]
|
||||
[app.util.array :as array]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[okulary.core :as l]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(def ref:token-type-open-status
|
||||
(l/derived (l/key :open-status-by-type) refs/workspace-tokens))
|
||||
|
||||
(defn- get-sorted-token-groups
|
||||
"Separate token-types into groups of `empty` or `filled` depending if
|
||||
tokens exist for that type. Sort each group alphabetically (by their type)."
|
||||
|
|
@ -82,7 +78,6 @@
|
|||
[{:keys [tokens-lib active-tokens resolved-active-tokens]}]
|
||||
(let [objects (mf/deref refs/workspace-page-objects)
|
||||
selected (mf/deref refs/selected-shapes)
|
||||
open-status (mf/deref ref:token-type-open-status)
|
||||
|
||||
selected-shapes
|
||||
(mf/with-memo [selected objects]
|
||||
|
|
@ -123,10 +118,6 @@
|
|||
tokens)]
|
||||
(ctob/group-by-type tokens)))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[empty-group filled-group]
|
||||
(mf/with-memo [tokens-by-type]
|
||||
(get-sorted-token-groups tokens-by-type))]
|
||||
|
|
@ -151,7 +142,6 @@
|
|||
(let [tokens (get tokens-by-type type)]
|
||||
[:> token-group* {:key (name type)
|
||||
:tokens tokens
|
||||
:is-expanded (get open-status type false)
|
||||
:type type
|
||||
:selected-ids selected
|
||||
:selected-shapes selected-shapes
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
(ns app.main.ui.workspace.tokens.management.forms.generic-form
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.files.tokens :as cft]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.types.token :as cto]
|
||||
|
|
@ -180,6 +181,7 @@
|
|||
(mf/deps validate-token token tokens token-type value-subfield type active-tab)
|
||||
(fn [form _event]
|
||||
(let [name (get-in @form [:clean-data :name])
|
||||
path (str (d/name token-type) "." name)
|
||||
description (get-in @form [:clean-data :description])
|
||||
value (get-in @form [:clean-data :value])
|
||||
value-for-validation (get-value-for-validator active-tab value value-subfield type)]
|
||||
|
|
@ -220,6 +222,7 @@
|
|||
{:name name
|
||||
:value (:value valid-token)
|
||||
:description description}))
|
||||
(dwtl/toggle-token-path path)
|
||||
(dwtp/propagate-workspace-tokens)
|
||||
(modal/hide!))))))))))]
|
||||
|
||||
|
|
|
|||
|
|
@ -23,8 +23,11 @@
|
|||
[app.main.ui.workspace.tokens.management.token-tree :refer [token-tree*]]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[okulary.core :as l]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(def ref:unfolded-token-paths
|
||||
(l/derived (l/key :unfolded-token-paths) refs/workspace-tokens))
|
||||
|
||||
(defn token-section-icon
|
||||
[type]
|
||||
|
|
@ -64,14 +67,16 @@
|
|||
|
||||
(mf/defc token-group*
|
||||
{::mf/schema schema:token-group}
|
||||
[{:keys [type tokens selected-shapes is-selected-inside-layout active-theme-tokens selected-token-set-id tokens-lib is-expanded selected-ids]}]
|
||||
[{:keys [type tokens selected-shapes is-selected-inside-layout active-theme-tokens selected-token-set-id tokens-lib selected-ids]}]
|
||||
(let [{:keys [modal title]}
|
||||
(get dwta/token-properties type)
|
||||
|
||||
unfolded-token-paths (mf/deref ref:unfolded-token-paths)
|
||||
is-type-unfolded (contains? (set unfolded-token-paths) (name type))
|
||||
|
||||
editing-ref (mf/deref refs/workspace-editor-state)
|
||||
not-editing? (empty? editing-ref)
|
||||
|
||||
is-expanded (d/nilv is-expanded false)
|
||||
|
||||
can-edit?
|
||||
(mf/use-ctx ctx/can-edit?)
|
||||
|
||||
|
|
@ -95,24 +100,26 @@
|
|||
|
||||
on-toggle-open-click
|
||||
(mf/use-fn
|
||||
(mf/deps is-expanded type)
|
||||
#(st/emit! (dwtl/set-token-type-section-open type (not is-expanded))))
|
||||
(mf/deps type expandable?)
|
||||
(fn []
|
||||
(when expandable?
|
||||
(st/emit! (dwtl/toggle-token-path (name type))))))
|
||||
|
||||
on-popover-open-click
|
||||
(mf/use-fn
|
||||
(mf/deps type title modal)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (dwtl/set-token-type-section-open type true)
|
||||
(let [pos (dom/get-client-position event)]
|
||||
(modal/show (:key modal)
|
||||
{:x (:x pos)
|
||||
:y (:y pos)
|
||||
:position :right
|
||||
:fields (:fields modal)
|
||||
:title title
|
||||
:action "create"
|
||||
:token-type type})))))
|
||||
(st/emit!
|
||||
(let [pos (dom/get-client-position event)]
|
||||
(modal/show (:key modal)
|
||||
{:x (:x pos)
|
||||
:y (:y pos)
|
||||
:position :right
|
||||
:fields (:fields modal)
|
||||
:title title
|
||||
:action "create"
|
||||
:token-type type})))))
|
||||
|
||||
on-token-pill-click
|
||||
(mf/use-fn
|
||||
|
|
@ -127,10 +134,10 @@
|
|||
[:div {:class (stl/css :token-section-wrapper)
|
||||
:data-testid (dm/str "section-" (name type))}
|
||||
[:> layer-button* {:label title
|
||||
:expanded is-expanded
|
||||
:expanded is-type-unfolded
|
||||
:description (when expandable? (dm/str (count tokens)))
|
||||
:is-expandable expandable?
|
||||
:aria-expanded is-expanded
|
||||
:aria-expanded is-type-unfolded
|
||||
:aria-controls (dm/str "token-tree-" (name type))
|
||||
:on-toggle-expand on-toggle-open-click
|
||||
:icon (token-section-icon type)}
|
||||
|
|
@ -141,10 +148,12 @@
|
|||
:variant "ghost"
|
||||
:on-click on-popover-open-click
|
||||
:class (stl/css :token-section-icon)}])]
|
||||
(when is-expanded
|
||||
(when is-type-unfolded
|
||||
[:> token-tree* {:tokens tokens
|
||||
:type type
|
||||
:id (dm/str "token-tree-" (name type))
|
||||
:tokens-lib tokens-lib
|
||||
:unfolded-token-paths unfolded-token-paths
|
||||
:selected-shapes selected-shapes
|
||||
:active-theme-tokens active-theme-tokens
|
||||
:selected-token-set-id selected-token-set-id
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
(:require
|
||||
[app.common.path-names :as cpn]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.main.data.workspace.tokens.library-edit :as dwtl]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.ds.layers.layer-button :refer [layer-button*]]
|
||||
[app.main.ui.workspace.tokens.management.token-pill :refer [token-pill*]]
|
||||
[rumext.v2 :as mf]))
|
||||
|
|
@ -16,6 +18,8 @@
|
|||
(def ^:private schema:folder-node
|
||||
[:map
|
||||
[:node :any]
|
||||
[:type :keyword]
|
||||
[:unfolded-token-paths {:optional true} [:vector :string]]
|
||||
[:selected-shapes :any]
|
||||
[:is-selected-inside-layout {:optional true} :boolean]
|
||||
[:active-theme-tokens {:optional true} :any]
|
||||
|
|
@ -26,18 +30,32 @@
|
|||
|
||||
(mf/defc folder-node*
|
||||
{::mf/schema schema:folder-node}
|
||||
[{:keys [node selected-shapes is-selected-inside-layout active-theme-tokens selected-token-set-id tokens-lib on-token-pill-click on-context-menu]}]
|
||||
(let [expanded* (mf/use-state false)
|
||||
expanded (deref expanded*)
|
||||
swap-folder-expanded #(swap! expanded* not)]
|
||||
[{:keys [node
|
||||
type
|
||||
unfolded-token-paths
|
||||
selected-shapes
|
||||
is-selected-inside-layout
|
||||
active-theme-tokens
|
||||
selected-token-set-id
|
||||
tokens-lib
|
||||
on-token-pill-click
|
||||
on-context-menu]}]
|
||||
(let [full-path (str (name type) "." (:path node))
|
||||
is-folder-expanded (contains? (set (or unfolded-token-paths [])) full-path)
|
||||
|
||||
swap-folder-expanded (mf/use-fn
|
||||
(mf/deps (:path node) type)
|
||||
(fn []
|
||||
(let [path (str (name type) "." (:path node))]
|
||||
(st/emit! (dwtl/toggle-token-path path)))))]
|
||||
[:li {:class (stl/css :folder-node)}
|
||||
[:> layer-button* {:label (:name node)
|
||||
:expanded expanded
|
||||
:aria-expanded expanded
|
||||
:expanded is-folder-expanded
|
||||
:aria-expanded is-folder-expanded
|
||||
:aria-controls (str "folder-children-" (:path node))
|
||||
:is-expandable (not (:leaf node))
|
||||
:on-toggle-expand swap-folder-expanded}]
|
||||
(when expanded
|
||||
(when is-folder-expanded
|
||||
(let [children-fn (:children-fn node)]
|
||||
[:div {:class (stl/css :folder-children-wrapper)
|
||||
:id (str "folder-children-" (:path node))}
|
||||
|
|
@ -47,7 +65,9 @@
|
|||
(if (not (:leaf child))
|
||||
[:ul {:class (stl/css :node-parent)}
|
||||
[:> folder-node* {:key (:path child)
|
||||
:type type
|
||||
:node child
|
||||
:unfolded-token-paths unfolded-token-paths
|
||||
:selected-shapes selected-shapes
|
||||
:is-selected-inside-layout is-selected-inside-layout
|
||||
:active-theme-tokens active-theme-tokens
|
||||
|
|
@ -69,6 +89,8 @@
|
|||
(def ^:private schema:token-tree
|
||||
[:map
|
||||
[:tokens :any]
|
||||
[:type :keyword]
|
||||
[:unfolded-token-paths {:optional true} [:vector :string]]
|
||||
[:selected-shapes :any]
|
||||
[:is-selected-inside-layout {:optional true} :boolean]
|
||||
[:active-theme-tokens {:optional true} :any]
|
||||
|
|
@ -79,7 +101,16 @@
|
|||
|
||||
(mf/defc token-tree*
|
||||
{::mf/schema schema:token-tree}
|
||||
[{:keys [tokens selected-shapes is-selected-inside-layout active-theme-tokens tokens-lib selected-token-set-id on-token-pill-click on-context-menu]}]
|
||||
[{:keys [tokens
|
||||
type
|
||||
unfolded-token-paths
|
||||
selected-shapes
|
||||
is-selected-inside-layout
|
||||
active-theme-tokens
|
||||
tokens-lib
|
||||
selected-token-set-id
|
||||
on-token-pill-click
|
||||
on-context-menu]}]
|
||||
(let [separator "."
|
||||
tree (mf/use-memo
|
||||
(mf/deps tokens)
|
||||
|
|
@ -87,24 +118,26 @@
|
|||
(cpn/build-tree-root tokens separator)))]
|
||||
[:div {:class (stl/css :token-tree-wrapper)}
|
||||
(for [node tree]
|
||||
[:ul {:class (stl/css :node-parent)
|
||||
:key (:path node)
|
||||
:style {:--node-depth (inc (:depth node))}}
|
||||
(if (:leaf node)
|
||||
(let [token (ctob/get-token tokens-lib selected-token-set-id (get-in node [:leaf :id]))]
|
||||
[:> token-pill*
|
||||
{:token token
|
||||
:selected-shapes selected-shapes
|
||||
:is-selected-inside-layout is-selected-inside-layout
|
||||
:active-theme-tokens active-theme-tokens
|
||||
:on-click on-token-pill-click
|
||||
:on-context-menu on-context-menu}])
|
||||
(if (:leaf node)
|
||||
(let [token (ctob/get-token tokens-lib selected-token-set-id (get-in node [:leaf :id]))]
|
||||
[:> token-pill*
|
||||
{:token token
|
||||
:key (:id (:leaf node))
|
||||
:selected-shapes selected-shapes
|
||||
:is-selected-inside-layout is-selected-inside-layout
|
||||
:active-theme-tokens active-theme-tokens
|
||||
:on-click on-token-pill-click
|
||||
:on-context-menu on-context-menu}])
|
||||
;; Render segment folder
|
||||
[:ul {:class (stl/css :node-parent)
|
||||
:key (:path node)}
|
||||
[:> folder-node* {:node node
|
||||
:type type
|
||||
:unfolded-token-paths unfolded-token-paths
|
||||
:selected-shapes selected-shapes
|
||||
:is-selected-inside-layout is-selected-inside-layout
|
||||
:active-theme-tokens active-theme-tokens
|
||||
:on-token-pill-click on-token-pill-click
|
||||
:on-context-menu on-context-menu
|
||||
:tokens-lib tokens-lib
|
||||
:selected-token-set-id selected-token-set-id}])])]))
|
||||
:selected-token-set-id selected-token-set-id}]]))]))
|
||||
|
|
|
|||
|
|
@ -7,24 +7,41 @@
|
|||
@use "ds/_borders.scss" as *;
|
||||
|
||||
.token-tree-wrapper {
|
||||
--node-spacing: var(--sp-s);
|
||||
|
||||
padding-block-end: var(--sp-s);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--sp-s);
|
||||
padding-inline-start: calc(var(--node-spacing));
|
||||
|
||||
& .node-parent {
|
||||
flex: 1 0 100%;
|
||||
|
||||
&:last-of-type + * {
|
||||
margin-block-end: var(--sp-s);
|
||||
}
|
||||
}
|
||||
|
||||
& .token-pill {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.node-parent {
|
||||
--node-spacing: var(--sp-l);
|
||||
--node-depth: 0;
|
||||
|
||||
margin-block-end: 0;
|
||||
padding-inline-start: calc(var(--node-spacing) * var(--node-depth));
|
||||
}
|
||||
|
||||
.folder-children-wrapper:has(> button) {
|
||||
.folder-children-wrapper {
|
||||
margin-inline-start: var(--sp-s);
|
||||
padding-inline-start: var(--sp-s);
|
||||
border-inline-start: $b-2 solid var(--color-background-quaternary);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: var(--sp-xs);
|
||||
|
||||
&:has(> button) {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--sp-xs);
|
||||
}
|
||||
|
||||
& .node-parent {
|
||||
flex: 1 0 100%;
|
||||
|
|
|
|||
Loading…
Reference in New Issue