mirror of
https://github.com/penpot/penpot.git
synced 2025-08-03 22:38:30 +02:00
♻️ Restructure UI files for tokens editor
This commit is contained in:
parent
20b5b7f6e4
commit
6bd3253e5e
20 changed files with 336 additions and 296 deletions
|
@ -36,7 +36,7 @@
|
|||
[app.main.ui.workspace.tokens.export.modal]
|
||||
[app.main.ui.workspace.tokens.import]
|
||||
[app.main.ui.workspace.tokens.import.modal]
|
||||
[app.main.ui.workspace.tokens.modals]
|
||||
[app.main.ui.workspace.tokens.management.create.modals]
|
||||
[app.main.ui.workspace.tokens.settings]
|
||||
[app.main.ui.workspace.tokens.themes.create-modal]
|
||||
[app.main.ui.workspace.viewport :refer [viewport*]]
|
||||
|
|
155
frontend/src/app/main/ui/workspace/tokens/management.cljs
Normal file
155
frontend/src/app/main/ui/workspace/tokens/management.cljs
Normal file
|
@ -0,0 +1,155 @@
|
|||
(ns app.main.ui.workspace.tokens.management
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.config :as cf]
|
||||
[app.main.data.style-dictionary :as sd]
|
||||
[app.main.data.workspace.tokens.application :as dwta]
|
||||
[app.main.data.workspace.tokens.library-edit :as dwtl]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.ds.foundations.typography.text :refer [text*]]
|
||||
[app.main.ui.workspace.tokens.management.context-menu :refer [token-context-menu]]
|
||||
[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).
|
||||
If `:token-units` is not in cf/flags, number tokens are excluded."
|
||||
[tokens-by-type]
|
||||
(let [token-units? (contains? cf/flags :token-units)
|
||||
token-typography-types? (contains? cf/flags :token-typography-types)
|
||||
all-types (cond-> dwta/token-properties
|
||||
(not token-units?) (dissoc :number)
|
||||
(not token-typography-types?) (dissoc :font-size))
|
||||
all-types (-> all-types keys seq)]
|
||||
(loop [empty #js []
|
||||
filled #js []
|
||||
types all-types]
|
||||
(if-let [type (first types)]
|
||||
(if (not-empty (get tokens-by-type type))
|
||||
(recur empty
|
||||
(array/conj! filled type)
|
||||
(rest types))
|
||||
(recur (array/conj! empty type)
|
||||
filled
|
||||
(rest types)))
|
||||
[(seq (array/sort! empty))
|
||||
(seq (array/sort! filled))]))))
|
||||
|
||||
(mf/defc tokens-section*
|
||||
{::mf/private true}
|
||||
[{:keys [tokens-lib]}]
|
||||
(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]
|
||||
(into [] (keep (d/getf objects)) selected))
|
||||
|
||||
active-theme-tokens
|
||||
(mf/with-memo [tokens-lib]
|
||||
(if tokens-lib
|
||||
(ctob/get-tokens-in-active-sets tokens-lib)
|
||||
{}))
|
||||
|
||||
;; Resolve tokens as second step
|
||||
active-theme-tokens'
|
||||
(sd/use-resolved-tokens* active-theme-tokens)
|
||||
|
||||
;; This only checks for the currently explicitly selected set
|
||||
;; name, it is ephimeral and can be nil
|
||||
selected-token-set-name
|
||||
(mf/deref refs/selected-token-set-name)
|
||||
|
||||
selected-token-set
|
||||
(when selected-token-set-name
|
||||
(some-> tokens-lib (ctob/get-set selected-token-set-name)))
|
||||
|
||||
;; If we have not selected any set explicitly we just
|
||||
;; select the first one from the list of sets
|
||||
selected-token-set-tokens
|
||||
(when selected-token-set
|
||||
(get selected-token-set :tokens))
|
||||
|
||||
tokens
|
||||
(mf/with-memo [active-theme-tokens selected-token-set-tokens]
|
||||
(merge active-theme-tokens selected-token-set-tokens))
|
||||
|
||||
tokens
|
||||
(sd/use-resolved-tokens* tokens)
|
||||
|
||||
tokens-by-type
|
||||
(mf/with-memo [tokens selected-token-set-tokens]
|
||||
(let [tokens (reduce-kv (fn [tokens k _]
|
||||
(if (contains? selected-token-set-tokens k)
|
||||
tokens
|
||||
(dissoc tokens k)))
|
||||
tokens
|
||||
tokens)]
|
||||
(ctob/group-by-type tokens)))
|
||||
|
||||
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-sets-names)
|
||||
(fn [name]
|
||||
(contains? active-token-sets-names name)))
|
||||
|
||||
[empty-group filled-group]
|
||||
(mf/with-memo [tokens-by-type]
|
||||
(get-sorted-token-groups tokens-by-type))]
|
||||
|
||||
(mf/with-effect [tokens-lib selected-token-set-name]
|
||||
(when (and tokens-lib
|
||||
(or (nil? selected-token-set-name)
|
||||
(and selected-token-set-name
|
||||
(not (ctob/get-set tokens-lib selected-token-set-name)))))
|
||||
(let [match (->> (ctob/get-sets tokens-lib)
|
||||
(first)
|
||||
(:name))]
|
||||
(st/emit! (dwtl/set-selected-token-set-name match)))))
|
||||
|
||||
[:*
|
||||
[:& token-context-menu]
|
||||
[:div {:class (stl/css :sets-header-container)}
|
||||
[:> text* {:as "span" :typography "headline-small" :class (stl/css :sets-header)} (tr "workspace.tokens.tokens-section-title" selected-token-set-name)]
|
||||
[:div {:class (stl/css :sets-header-status) :title (tr "workspace.tokens.inactive-set-description")}
|
||||
;; NOTE: when no set in tokens-lib, the selected-token-set-name
|
||||
;; will be `nil`, so for properly hide the inactive message we
|
||||
;; check that at least `selected-token-set-name` has a value
|
||||
(when (and (some? selected-token-set-name)
|
||||
(not (token-set-active? selected-token-set-name)))
|
||||
[:*
|
||||
[:> i/icon* {:class (stl/css :sets-header-status-icon) :icon-id i/eye-off}]
|
||||
[:> text* {:as "span" :typography "body-small" :class (stl/css :sets-header-status-text)}
|
||||
(tr "workspace.tokens.inactive-set")]])]]
|
||||
|
||||
(for [type filled-group]
|
||||
(let [tokens (get tokens-by-type type)]
|
||||
[:> token-group* {:key (name type)
|
||||
:is-open (get open-status type false)
|
||||
:type type
|
||||
:selected-shapes selected-shapes
|
||||
:active-theme-tokens active-theme-tokens'
|
||||
:tokens tokens}]))
|
||||
|
||||
(for [type empty-group]
|
||||
[:> token-group* {:key (name type)
|
||||
:type type
|
||||
:selected-shapes selected-shapes
|
||||
:active-theme-tokens active-theme-tokens'
|
||||
:tokens []}])]))
|
36
frontend/src/app/main/ui/workspace/tokens/management.scss
Normal file
36
frontend/src/app/main/ui/workspace/tokens/management.scss
Normal file
|
@ -0,0 +1,36 @@
|
|||
// 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 *;
|
||||
|
||||
.sets-header-container {
|
||||
@include use-typography("headline-small");
|
||||
padding: var(--sp-s);
|
||||
color: var(--title-foreground-color);
|
||||
word-break: break-word;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: var(--sp-xs);
|
||||
margin-block-start: var(--sp-s);
|
||||
}
|
||||
|
||||
.sets-header {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.sets-header-status {
|
||||
@include use-typography("body-small");
|
||||
text-transform: none;
|
||||
color: var(--color-foreground-secondary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--sp-xs);
|
||||
}
|
||||
|
||||
.sets-header-status-text {
|
||||
font-style: italic;
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.main.ui.workspace.tokens.common.context-menu
|
||||
(ns app.main.ui.workspace.tokens.management.context-menu
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
|
@ -4,7 +4,7 @@
|
|||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.main.ui.workspace.tokens.form
|
||||
(ns app.main.ui.workspace.tokens.management.create.form
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.colors :as c]
|
||||
|
@ -31,7 +31,7 @@
|
|||
[app.main.ui.ds.notifications.context-notification :refer [context-notification*]]
|
||||
[app.main.ui.workspace.colorpicker :as colorpicker]
|
||||
[app.main.ui.workspace.colorpicker.ramp :refer [ramp-selector*]]
|
||||
[app.main.ui.workspace.tokens.components.controls.input-tokens-value :refer [input-tokens-value*]]
|
||||
[app.main.ui.workspace.tokens.management.create.input-tokens-value :refer [input-tokens-value*]]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.functions :as uf]
|
||||
[app.util.i18n :refer [tr]]
|
|
@ -4,7 +4,7 @@
|
|||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.main.ui.workspace.tokens.components.controls.input-token-color-bullet
|
||||
(ns app.main.ui.workspace.tokens.management.create.input-token-color-bullet
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.main.data.workspace.tokens.color :as dwtc]
|
|
@ -4,14 +4,14 @@
|
|||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.main.ui.workspace.tokens.components.controls.input-tokens-value
|
||||
(ns app.main.ui.workspace.tokens.management.create.input-tokens-value
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.ui.ds.controls.utilities.input-field :refer [input-field*]]
|
||||
[app.main.ui.ds.controls.utilities.label :refer [label*]]
|
||||
[app.main.ui.workspace.tokens.components.controls.input-token-color-bullet :refer [input-token-color-bullet*]]
|
||||
[app.main.ui.workspace.tokens.management.create.input-token-color-bullet :refer [input-token-color-bullet*]]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(def ^:private schema::input-tokens-value
|
|
@ -4,7 +4,7 @@
|
|||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.main.ui.workspace.tokens.modals
|
||||
(ns app.main.ui.workspace.tokens.management.create.modals
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
|
@ -12,7 +12,7 @@
|
|||
[app.main.refs :as refs]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.workspace.tokens.form :refer [form]]
|
||||
[app.main.ui.workspace.tokens.management.create.form :refer [form]]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[okulary.core :as l]
|
||||
[rumext.v2 :as mf]))
|
||||
|
@ -22,7 +22,7 @@
|
|||
(defn calculate-position
|
||||
"Calculates the style properties for the given coordinates and position"
|
||||
[{vh :height} position x y color?]
|
||||
(let [;; picker height in pixels
|
||||
(let [;; picker height in pixels
|
||||
;; TODO: Revisit these harcoded values
|
||||
h (if color? 610 510)
|
||||
;; Checks for overflow outside the viewport height
|
119
frontend/src/app/main/ui/workspace/tokens/management/group.cljs
Normal file
119
frontend/src/app/main/ui/workspace/tokens/management/group.cljs
Normal file
|
@ -0,0 +1,119 @@
|
|||
;; 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.management.group
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.workspace.tokens.application :as dwta]
|
||||
[app.main.data.workspace.tokens.library-edit :as dwtl]
|
||||
[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.workspace.sidebar.assets.common :as cmm]
|
||||
[app.main.ui.workspace.tokens.management.token-pill :refer [token-pill*]]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defn token-section-icon
|
||||
[type]
|
||||
(case type
|
||||
:border-radius "corner-radius"
|
||||
:color "drop"
|
||||
:boolean "boolean-difference"
|
||||
:font-size "text-font-size"
|
||||
:opacity "percentage"
|
||||
:number "number"
|
||||
:rotation "rotation"
|
||||
:spacing "padding-extended"
|
||||
:string "text-mixed"
|
||||
:stroke-width "stroke-size"
|
||||
:typography "text"
|
||||
:dimensions "expand"
|
||||
:sizing "expand"
|
||||
"add"))
|
||||
|
||||
(mf/defc token-group*
|
||||
{::mf/private true}
|
||||
[{:keys [type tokens selected-shapes active-theme-tokens is-open]}]
|
||||
(let [{:keys [modal title]}
|
||||
(get dwta/token-properties type)
|
||||
editing-ref (mf/deref refs/workspace-editor-state)
|
||||
not-editing? (empty? editing-ref)
|
||||
|
||||
can-edit?
|
||||
(mf/use-ctx ctx/can-edit?)
|
||||
|
||||
tokens
|
||||
(mf/with-memo [tokens]
|
||||
(vec (sort-by :name tokens)))
|
||||
|
||||
on-context-menu
|
||||
(mf/use-fn
|
||||
(fn [event token]
|
||||
(dom/prevent-default event)
|
||||
(st/emit! (dwtl/assign-token-context-menu
|
||||
{:type :token
|
||||
:position (dom/get-client-position event)
|
||||
:errors (:errors token)
|
||||
:token-name (:name token)}))))
|
||||
|
||||
on-toggle-open-click
|
||||
(mf/use-fn
|
||||
(mf/deps is-open type)
|
||||
#(st/emit! (dwtl/set-token-type-section-open type (not is-open))))
|
||||
|
||||
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)
|
||||
;; FIXME: use dom/get-client-position
|
||||
(modal/show (:key modal)
|
||||
{:x (.-clientX ^js event)
|
||||
:y (.-clientY ^js event)
|
||||
:position :right
|
||||
:fields (:fields modal)
|
||||
:title title
|
||||
:action "create"
|
||||
:token-type type}))))
|
||||
|
||||
on-token-pill-click
|
||||
(mf/use-fn
|
||||
(mf/deps selected-shapes not-editing?)
|
||||
(fn [event token]
|
||||
(dom/stop-propagation event)
|
||||
(when (and not-editing? (seq selected-shapes))
|
||||
(st/emit! (dwta/toggle-token {:token token
|
||||
:shapes selected-shapes})))))]
|
||||
|
||||
[:div {:on-click on-toggle-open-click :class (stl/css :token-section-wrapper)}
|
||||
[:& cmm/asset-section {:icon (token-section-icon type)
|
||||
:title title
|
||||
:section :tokens
|
||||
:assets-count (count tokens)
|
||||
:open? is-open}
|
||||
[:& cmm/asset-section-block {:role :title-button}
|
||||
(when can-edit?
|
||||
[:> icon-button* {:on-click on-popover-open-click
|
||||
:variant "ghost"
|
||||
:icon "add"
|
||||
:aria-label (tr "workspace.tokens.add-token" title)}])]
|
||||
(when is-open
|
||||
[:& cmm/asset-section-block {:role :content}
|
||||
[:div {:class (stl/css :token-pills-wrapper)}
|
||||
(for [token tokens]
|
||||
[:> token-pill*
|
||||
{:key (:name token)
|
||||
:token token
|
||||
:selected-shapes selected-shapes
|
||||
:active-theme-tokens active-theme-tokens
|
||||
:on-click on-token-pill-click
|
||||
:on-context-menu on-context-menu}])]])]]))
|
|
@ -0,0 +1,11 @@
|
|||
// 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
|
||||
|
||||
.token-pills-wrapper {
|
||||
display: flex;
|
||||
gap: var(--sp-xs);
|
||||
flex-wrap: wrap;
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.main.ui.workspace.tokens.token-pill
|
||||
(ns app.main.ui.workspace.tokens.management.token-pill
|
||||
(:require-macros
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.style :as stl])
|
|
@ -4,8 +4,8 @@
|
|||
//
|
||||
// Copyright (c) KALEIDOS INC
|
||||
|
||||
@use "../../ds/typography.scss" as *;
|
||||
@use "../../ds/borders.scss" as *;
|
||||
@use "../../../ds/typography.scss" as *;
|
||||
@use "../../../ds/borders.scss" as *;
|
||||
@import "refactor/common-refactor.scss";
|
||||
|
||||
.token-pill {
|
|
@ -7,166 +7,30 @@
|
|||
(ns app.main.ui.workspace.tokens.sidebar
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.config :as cf]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.style-dictionary :as sd]
|
||||
[app.main.data.workspace.tokens.application :as dwta]
|
||||
[app.main.data.workspace.tokens.library-edit :as dwtl]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.dropdown-menu :refer [dropdown-menu
|
||||
dropdown-menu-item*]]
|
||||
[app.main.ui.components.title-bar :refer [title-bar]]
|
||||
[app.main.ui.context :as ctx]
|
||||
[app.main.ui.ds.buttons.button :refer [button*]]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.ds.foundations.typography.text :refer [text*]]
|
||||
[app.main.ui.hooks :as h]
|
||||
[app.main.ui.hooks.resize :refer [use-resize-hook]]
|
||||
[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.management :refer [tokens-section*]]
|
||||
[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.lists :as tsetslist]
|
||||
[app.main.ui.workspace.tokens.themes :refer [themes-header*]]
|
||||
[app.main.ui.workspace.tokens.token-pill :refer [token-pill*]]
|
||||
[app.util.array :as array]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[okulary.core :as l]
|
||||
[rumext.v2 :as mf]
|
||||
[shadow.resource]))
|
||||
|
||||
(def ref:token-type-open-status
|
||||
(l/derived (l/key :open-status-by-type) refs/workspace-tokens))
|
||||
|
||||
;; Components ------------------------------------------------------------------
|
||||
|
||||
(defn token-section-icon
|
||||
[type]
|
||||
(case type
|
||||
:border-radius "corner-radius"
|
||||
:color "drop"
|
||||
:boolean "boolean-difference"
|
||||
:font-size "text-font-size"
|
||||
:opacity "percentage"
|
||||
:number "number"
|
||||
:rotation "rotation"
|
||||
:spacing "padding-extended"
|
||||
:string "text-mixed"
|
||||
:stroke-width "stroke-size"
|
||||
:typography "text"
|
||||
:dimensions "expand"
|
||||
:sizing "expand"
|
||||
"add"))
|
||||
|
||||
(mf/defc token-group*
|
||||
{::mf/private true}
|
||||
[{:keys [type tokens selected-shapes active-theme-tokens is-open]}]
|
||||
(let [{:keys [modal title]}
|
||||
(get dwta/token-properties type)
|
||||
editing-ref (mf/deref refs/workspace-editor-state)
|
||||
not-editing? (empty? editing-ref)
|
||||
|
||||
can-edit?
|
||||
(mf/use-ctx ctx/can-edit?)
|
||||
|
||||
tokens
|
||||
(mf/with-memo [tokens]
|
||||
(vec (sort-by :name tokens)))
|
||||
|
||||
on-context-menu
|
||||
(mf/use-fn
|
||||
(fn [event token]
|
||||
(dom/prevent-default event)
|
||||
(st/emit! (dwtl/assign-token-context-menu
|
||||
{:type :token
|
||||
:position (dom/get-client-position event)
|
||||
:errors (:errors token)
|
||||
:token-name (:name token)}))))
|
||||
|
||||
on-toggle-open-click
|
||||
(mf/use-fn
|
||||
(mf/deps is-open type)
|
||||
#(st/emit! (dwtl/set-token-type-section-open type (not is-open))))
|
||||
|
||||
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)
|
||||
;; FIXME: use dom/get-client-position
|
||||
(modal/show (:key modal)
|
||||
{:x (.-clientX ^js event)
|
||||
:y (.-clientY ^js event)
|
||||
:position :right
|
||||
:fields (:fields modal)
|
||||
:title title
|
||||
:action "create"
|
||||
:token-type type}))))
|
||||
|
||||
on-token-pill-click
|
||||
(mf/use-fn
|
||||
(mf/deps selected-shapes not-editing?)
|
||||
(fn [event token]
|
||||
(dom/stop-propagation event)
|
||||
(when (and not-editing? (seq selected-shapes))
|
||||
(st/emit! (dwta/toggle-token {:token token
|
||||
:shapes selected-shapes})))))]
|
||||
|
||||
[:div {:on-click on-toggle-open-click :class (stl/css :token-section-wrapper)}
|
||||
[:& cmm/asset-section {:icon (token-section-icon type)
|
||||
:title title
|
||||
:section :tokens
|
||||
:assets-count (count tokens)
|
||||
:open? is-open}
|
||||
[:& cmm/asset-section-block {:role :title-button}
|
||||
(when can-edit?
|
||||
[:> icon-button* {:on-click on-popover-open-click
|
||||
:variant "ghost"
|
||||
:icon "add"
|
||||
:aria-label (tr "workspace.tokens.add-token" title)}])]
|
||||
(when is-open
|
||||
[:& cmm/asset-section-block {:role :content}
|
||||
[:div {:class (stl/css :token-pills-wrapper)}
|
||||
(for [token tokens]
|
||||
[:> token-pill*
|
||||
{:key (:name token)
|
||||
:token token
|
||||
:selected-shapes selected-shapes
|
||||
:active-theme-tokens active-theme-tokens
|
||||
:on-click on-token-pill-click
|
||||
:on-context-menu on-context-menu}])]])]]))
|
||||
|
||||
(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).
|
||||
If `:token-units` is not in cf/flags, number tokens are excluded."
|
||||
[tokens-by-type]
|
||||
(let [token-units? (contains? cf/flags :token-units)
|
||||
token-typography-types? (contains? cf/flags :token-typography-types)
|
||||
all-types (cond-> dwta/token-properties
|
||||
(not token-units?) (dissoc :number)
|
||||
(not token-typography-types?) (dissoc :font-size))
|
||||
all-types (-> all-types keys seq)]
|
||||
(loop [empty #js []
|
||||
filled #js []
|
||||
types all-types]
|
||||
(if-let [type (first types)]
|
||||
(if (not-empty (get tokens-by-type type))
|
||||
(recur empty
|
||||
(array/conj! filled type)
|
||||
(rest types))
|
||||
(recur (array/conj! empty type)
|
||||
filled
|
||||
(rest types)))
|
||||
[(seq (array/sort! empty))
|
||||
(seq (array/sort! filled))]))))
|
||||
|
||||
(mf/defc token-sets-list*
|
||||
{::mf/private true}
|
||||
[{:keys [tokens-lib]}]
|
||||
|
@ -218,113 +82,6 @@
|
|||
|
||||
[:> token-sets-list* props]]]))
|
||||
|
||||
(mf/defc tokens-section*
|
||||
[{:keys [tokens-lib]}]
|
||||
(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]
|
||||
(into [] (keep (d/getf objects)) selected))
|
||||
|
||||
active-theme-tokens
|
||||
(mf/with-memo [tokens-lib]
|
||||
(if tokens-lib
|
||||
(ctob/get-tokens-in-active-sets tokens-lib)
|
||||
{}))
|
||||
|
||||
;; Resolve tokens as second step
|
||||
active-theme-tokens'
|
||||
(sd/use-resolved-tokens* active-theme-tokens)
|
||||
|
||||
;; This only checks for the currently explicitly selected set
|
||||
;; name, it is ephimeral and can be nil
|
||||
selected-token-set-name
|
||||
(mf/deref refs/selected-token-set-name)
|
||||
|
||||
selected-token-set
|
||||
(when selected-token-set-name
|
||||
(some-> tokens-lib (ctob/get-set selected-token-set-name)))
|
||||
|
||||
;; If we have not selected any set explicitly we just
|
||||
;; select the first one from the list of sets
|
||||
selected-token-set-tokens
|
||||
(when selected-token-set
|
||||
(get selected-token-set :tokens))
|
||||
|
||||
tokens
|
||||
(mf/with-memo [active-theme-tokens selected-token-set-tokens]
|
||||
(merge active-theme-tokens selected-token-set-tokens))
|
||||
|
||||
tokens
|
||||
(sd/use-resolved-tokens* tokens)
|
||||
|
||||
tokens-by-type
|
||||
(mf/with-memo [tokens selected-token-set-tokens]
|
||||
(let [tokens (reduce-kv (fn [tokens k _]
|
||||
(if (contains? selected-token-set-tokens k)
|
||||
tokens
|
||||
(dissoc tokens k)))
|
||||
tokens
|
||||
tokens)]
|
||||
(ctob/group-by-type tokens)))
|
||||
|
||||
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-sets-names)
|
||||
(fn [name]
|
||||
(contains? active-token-sets-names name)))
|
||||
|
||||
[empty-group filled-group]
|
||||
(mf/with-memo [tokens-by-type]
|
||||
(get-sorted-token-groups tokens-by-type))]
|
||||
|
||||
(mf/with-effect [tokens-lib selected-token-set-name]
|
||||
(when (and tokens-lib
|
||||
(or (nil? selected-token-set-name)
|
||||
(and selected-token-set-name
|
||||
(not (ctob/get-set tokens-lib selected-token-set-name)))))
|
||||
(let [match (->> (ctob/get-sets tokens-lib)
|
||||
(first)
|
||||
(:name))]
|
||||
(st/emit! (dwtl/set-selected-token-set-name match)))))
|
||||
|
||||
[:*
|
||||
[:& token-context-menu]
|
||||
[:div {:class (stl/css :sets-header-container)}
|
||||
[:> text* {:as "span" :typography "headline-small" :class (stl/css :sets-header)} (tr "workspace.tokens.tokens-section-title" selected-token-set-name)]
|
||||
[:div {:class (stl/css :sets-header-status) :title (tr "workspace.tokens.inactive-set-description")}
|
||||
;; NOTE: when no set in tokens-lib, the selected-token-set-name
|
||||
;; will be `nil`, so for properly hide the inactive message we
|
||||
;; check that at least `selected-token-set-name` has a value
|
||||
(when (and (some? selected-token-set-name)
|
||||
(not (token-set-active? selected-token-set-name)))
|
||||
[:*
|
||||
[:> i/icon* {:class (stl/css :sets-header-status-icon) :icon-id i/eye-off}]
|
||||
[:> text* {:as "span" :typography "body-small" :class (stl/css :sets-header-status-text)}
|
||||
(tr "workspace.tokens.inactive-set")]])]]
|
||||
|
||||
(for [type filled-group]
|
||||
(let [tokens (get tokens-by-type type)]
|
||||
[:> token-group* {:key (name type)
|
||||
:is-open (get open-status type false)
|
||||
:type type
|
||||
:selected-shapes selected-shapes
|
||||
:active-theme-tokens active-theme-tokens'
|
||||
:tokens tokens}]))
|
||||
|
||||
(for [type empty-group]
|
||||
[:> token-group* {:key (name type)
|
||||
:type type
|
||||
:selected-shapes selected-shapes
|
||||
:active-theme-tokens active-theme-tokens'
|
||||
:tokens []}])]))
|
||||
|
||||
(mf/defc import-export-button*
|
||||
[]
|
||||
(let [show-menu* (mf/use-state false)
|
||||
|
|
|
@ -36,38 +36,6 @@
|
|||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.sets-header-container {
|
||||
@include use-typography("headline-small");
|
||||
padding: var(--sp-s);
|
||||
color: var(--title-foreground-color);
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.sets-header-container {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: var(--sp-xs);
|
||||
margin-block-start: var(--sp-s);
|
||||
}
|
||||
|
||||
.sets-header {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.sets-header-status {
|
||||
@include use-typography("body-small");
|
||||
text-transform: none;
|
||||
color: var(--color-foreground-secondary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--sp-xs);
|
||||
}
|
||||
|
||||
.sets-header-status-text {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -77,12 +45,6 @@
|
|||
color: var(--layer-row-foreground-color);
|
||||
}
|
||||
|
||||
.token-pills-wrapper {
|
||||
display: flex;
|
||||
gap: var(--sp-xs);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.section-text-icon {
|
||||
font-size: $fs-12;
|
||||
width: var(--sp-l);
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
(ns frontend-tests.tokens.token-form-test
|
||||
(:require
|
||||
[app.main.ui.workspace.tokens.form :as wtf]
|
||||
[app.main.ui.workspace.tokens.management.create.form :as wtf]
|
||||
[cljs.test :as t :include-macros true]
|
||||
[malli.core :as m]))
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue