mirror of
https://github.com/penpot/penpot.git
synced 2025-01-20 05:34:23 -05:00
✨ Add token status pills
This commit is contained in:
parent
2a766a7190
commit
d899fd687f
27 changed files with 390 additions and 139 deletions
3
frontend/resources/images/icons/broken-link.svg
Normal file
3
frontend/resources/images/icons/broken-link.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M6.5 10.5h-1a2.5 2.5 0 0 1 0-5m4 0h1a2.5 2.5 0 0 1 2 4M6 8h2M3 3l10 10"/>
|
||||
</svg>
|
After Width: | Height: | Size: 235 B |
8
frontend/resources/images/icons/token-status-full.svg
Normal file
8
frontend/resources/images/icons/token-status-full.svg
Normal file
|
@ -0,0 +1,8 @@
|
|||
<svg width="14" xmlns="http://www.w3.org/2000/svg" height="14">
|
||||
<g fill="none">
|
||||
<rect rx="0" ry="0" width="14" height="14"/>
|
||||
</g>
|
||||
<g>
|
||||
<path d="M7 2.443A4.56 4.56 0 0 1 11.557 7 4.56 4.56 0 0 1 7 11.557 4.56 4.56 0 0 1 2.443 7 4.56 4.56 0 0 1 7 2.443ZM7 4.25a2.751 2.751 0 0 0 0 5.5 2.751 2.751 0 0 0 0-5.5Z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 353 B |
|
@ -0,0 +1,8 @@
|
|||
<svg width="14" xmlns="http://www.w3.org/2000/svg" height="14" >
|
||||
<g fill="none">
|
||||
<rect rx="0" ry="0" width="14" height="14"/>
|
||||
</g>
|
||||
<g class="frame-children">
|
||||
<circle cx="7" cy="7" r="4"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 231 B |
8
frontend/resources/images/icons/token-status-partial.svg
Normal file
8
frontend/resources/images/icons/token-status-partial.svg
Normal file
|
@ -0,0 +1,8 @@
|
|||
<svg width="14" xmlns="http://www.w3.org/2000/svg" height="14">
|
||||
<g fill="none">
|
||||
<rect rx="0" ry="0" width="14" height="14"/>
|
||||
</g>
|
||||
<g>
|
||||
<path d="M7 4.25a2.751 2.751 0 0 0-1.711 4.903l-1.282 1.282A4.548 4.548 0 0 1 2.443 7 4.56 4.56 0 0 1 7 2.443c1.37 0 2.599.606 3.435 1.564L9.153 5.289A2.747 2.747 0 0 0 7 4.25Z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 357 B |
|
@ -180,7 +180,7 @@
|
|||
[:& bc/color-bullet {:color {:color (:color color)
|
||||
:id (:id color)
|
||||
:opacity (:opacity color)}
|
||||
:mini? true}]
|
||||
:mini true}]
|
||||
[:div {:class (stl/css :name-block)}
|
||||
[:span {:class (stl/css :color-name)} (:name color)]
|
||||
(when-not (= (:name color) default-name)
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
[app.main.ui.ds.foundations.typography :refer [typography-list]]
|
||||
[app.main.ui.ds.foundations.typography.heading :refer [heading*]]
|
||||
[app.main.ui.ds.foundations.typography.text :refer [text*]]
|
||||
[app.main.ui.ds.foundations.utilities.token.token-status :refer [token-status-icon* token-status-list]]
|
||||
[app.main.ui.ds.layout.tab-switcher :refer [tab-switcher*]]
|
||||
[app.main.ui.ds.notifications.toast :refer [toast*]]
|
||||
[app.main.ui.ds.product.empty-placeholder :refer [empty-placeholder*]]
|
||||
|
@ -40,8 +41,10 @@
|
|||
:Text text*
|
||||
:TabSwitcher tab-switcher*
|
||||
:Toast toast*
|
||||
:TokenStatusIcon token-status-icon*
|
||||
;; meta / misc
|
||||
:meta #js {:icons (clj->js (sort icon-list))
|
||||
:tokenStatus (clj->js (sort token-status-list))
|
||||
:svgs (clj->js (sort raw-svg-list))
|
||||
:typography (clj->js typography-list)}
|
||||
:storybook #js {:StoryGrid sb/story-grid*
|
||||
|
|
|
@ -22,6 +22,7 @@ $orange-950: #440806;
|
|||
|
||||
$red-200: #ffcada;
|
||||
$red-400: #c80857;
|
||||
$red-500: #ff3277;
|
||||
$red-950: #500124;
|
||||
|
||||
$pink-400: #ff6fe0;
|
||||
|
@ -33,6 +34,16 @@ $purple-700: #6911d4;
|
|||
$purple-600-10: #8c33eb1a;
|
||||
$purple-700-60: #6911d499;
|
||||
|
||||
$aqua-200: #ddf7ff;
|
||||
$aqua-400: #77e1f3;
|
||||
$aqua-600: #59acbb;
|
||||
$aqua-800: #1d4464;
|
||||
|
||||
$violet-300: #a7a9ff;
|
||||
$violet-600: #6c6dad;
|
||||
$violet-700: #484c74;
|
||||
$violet-800: #272941;
|
||||
|
||||
$blue-200: #bae3fd;
|
||||
$blue-500: #0e9be9;
|
||||
$blue-950: #082c49;
|
||||
|
@ -72,6 +83,7 @@ $grayish-red: #bfbfbf;
|
|||
--color-background-warning: #{$orange-200};
|
||||
--color-accent-error: #{$red-400};
|
||||
--color-background-error: #{$red-200};
|
||||
--color-foreground-error: #{$red-500};
|
||||
--color-accent-info: #{$blue-500};
|
||||
--color-background-info: #{$blue-200};
|
||||
|
||||
|
@ -87,6 +99,11 @@ $grayish-red: #bfbfbf;
|
|||
--color-overlay-default: #{$white-60};
|
||||
--color-overlay-onboarding: #{$white-90};
|
||||
--color-canvas: #{$grayish-red};
|
||||
|
||||
--color-token-background: #{$aqua-200};
|
||||
--color-token-border: #{$aqua-400};
|
||||
--color-token-accent: #{$aqua-600};
|
||||
--color-token-foreground: #{$aqua-800};
|
||||
}
|
||||
|
||||
:global(.default) {
|
||||
|
@ -104,6 +121,7 @@ $grayish-red: #bfbfbf;
|
|||
--color-background-warning: #{$orange-950};
|
||||
--color-accent-error: #{$red-400};
|
||||
--color-background-error: #{$red-950};
|
||||
--color-foreground-error: #{$red-500};
|
||||
--color-accent-info: #{$blue-500};
|
||||
--color-background-info: #{$blue-950};
|
||||
|
||||
|
@ -119,4 +137,9 @@ $grayish-red: #bfbfbf;
|
|||
--color-overlay-default: #{$gray-950-60};
|
||||
--color-overlay-onboarding: #{$gray-950-90};
|
||||
--color-canvas: #{$grayish-red};
|
||||
|
||||
--color-token-background: #{$violet-800};
|
||||
--color-token-border: #{$violet-700};
|
||||
--color-token-accent: #{$violet-600};
|
||||
--color-token-foreground: #{$violet-300};
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
(def ^:icon-id boolean-flatten "boolean-flatten")
|
||||
(def ^:icon-id boolean-intersection "boolean-intersection")
|
||||
(def ^:icon-id boolean-union "boolean-union")
|
||||
(def ^:icon-id broken-link "broken-link")
|
||||
(def ^:icon-id bug "bug")
|
||||
(def ^:icon-id character-a "character-a")
|
||||
(def ^:icon-id character-b "character-b")
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
(ns app.main.ui.ds.foundations.utilities.token.token-status
|
||||
(:require-macros
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.style :as stl])
|
||||
(:require
|
||||
[app.main.ui.ds.foundations.assets.icon :refer [collect-icons]]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(def ^:icon-id token-status-partial "token-status-partial")
|
||||
(def ^:icon-id token-status-full "token-status-full")
|
||||
(def ^:icon-id token-status-non-applied "token-status-non-applied")
|
||||
|
||||
(def token-status-list "A collection of all status" (collect-icons))
|
||||
|
||||
(def ^:private schema:token-status-icon
|
||||
[:map
|
||||
[:class {:optional true} :string]
|
||||
[:id [:and :string [:fn #(contains? token-status-list %)]]]])
|
||||
|
||||
(mf/defc token-status-icon*
|
||||
{::mf/props :obj
|
||||
::mf/schema schema:token-status-icon}
|
||||
[{:keys [id class] :rest props}]
|
||||
(let [class (dm/str (or class "") " " (stl/css :token-icon))
|
||||
props (mf/spread-props props {:class class :width "14px" :height "14px"})
|
||||
offset 0]
|
||||
[:> "svg" props
|
||||
[:use {:href (dm/str "#icon-" id) :width "14px" :height "14px" :x offset :y offset}]]))
|
|
@ -0,0 +1,31 @@
|
|||
import { Canvas, Meta } from '@storybook/blocks';
|
||||
import * as TokenStatusIconStories from "./token_status.stories"
|
||||
|
||||
<Meta of={TokenStatusIconStories} />
|
||||
|
||||
# Token status icons
|
||||
|
||||
## Technical notes
|
||||
|
||||
There are some SVG that are not regular icons, and that are only
|
||||
meant to be used on token components.
|
||||
|
||||
They represent the applied status of a token over a shape.
|
||||
|
||||
The assets are located in the `frontend/resources/images/icons` folder.
|
||||
|
||||
### Using asset IDs
|
||||
|
||||
For convenience, icons IDs are available in the component namespace.
|
||||
|
||||
```clj
|
||||
(ns app.main.ui.foo
|
||||
(:require
|
||||
[app.main.ui.ds.foundations.utilities.token.token-status :refer [token-status-icon*] :as ts]))
|
||||
```
|
||||
|
||||
```clj
|
||||
[:> token-status-icon*
|
||||
{:id ts/token-status-partial
|
||||
:class (stl/css :token-pill-icon)}]
|
||||
```
|
|
@ -0,0 +1,10 @@
|
|||
// 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-icon {
|
||||
fill: currentColor;
|
||||
stroke: none;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import * as React from "react";
|
||||
import Components from "@target/components";
|
||||
|
||||
const { TokenStatusIcon } = Components;
|
||||
const { tokenStatus } = Components.meta;
|
||||
|
||||
export default {
|
||||
title: "Foundations/Utilities/TokenStatus",
|
||||
component: TokenStatusIcon,
|
||||
argTypes: {
|
||||
id: {
|
||||
options: tokenStatus,
|
||||
control: { type: "select" },
|
||||
},
|
||||
},
|
||||
render: ({ ...args }) => <TokenStatusIcon {...args} />,
|
||||
};
|
||||
|
||||
export const Default = {
|
||||
args: {
|
||||
id: "token-status-full",
|
||||
},
|
||||
};
|
|
@ -70,7 +70,7 @@
|
|||
[:div {:class (stl/css :bullet-wrapper)
|
||||
:style #js {"--bullet-size" "16px"}}
|
||||
[:& cb/color-bullet {:color color
|
||||
:mini? true}]]
|
||||
:mini true}]]
|
||||
|
||||
[:div {:class (stl/css :format-wrapper)}
|
||||
[:div {:class (stl/css :image-format)}
|
||||
|
@ -102,7 +102,7 @@
|
|||
[:div {:class (stl/css :bullet-wrapper)
|
||||
:style #js {"--bullet-size" "16px"}}
|
||||
[:& cb/color-bullet {:color color
|
||||
:mini? true}]]
|
||||
:mini true}]]
|
||||
|
||||
[:div {:class (stl/css :format-wrapper)}
|
||||
(when-not (and on-change-format (or (:gradient color) image))
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
:style #js {"--bullet-size" "20px"}}
|
||||
(for [[i {:keys [color id gradient]}] (map-indexed vector (take 7 colors))]
|
||||
[:& cb/color-bullet {:key (dm/str "color-" i)
|
||||
:mini? true
|
||||
:mini true
|
||||
:color {:color color :id id :gradient gradient}}])]]]))
|
||||
|
||||
[:li {:class (stl/css-case :file-library true
|
||||
|
@ -68,7 +68,7 @@
|
|||
:style #js {"--bullet-size" "20px"}}
|
||||
(for [[i color] (map-indexed vector (take 7 (vals file-colors)))]
|
||||
[:& cb/color-bullet {:key (dm/str "color-" i)
|
||||
:mini? true
|
||||
:mini true
|
||||
:color color}])]]]
|
||||
|
||||
[:li {:class (stl/css :recent-colors true
|
||||
|
@ -90,5 +90,5 @@
|
|||
:style #js {"--bullet-size" "20px"}}
|
||||
(for [[idx color] (map-indexed vector (take 7 (reverse recent-colors)))]
|
||||
[:& cb/color-bullet {:key (str "color-" idx)
|
||||
:mini? true
|
||||
:mini true
|
||||
:color color}])]]]]]))
|
||||
|
|
|
@ -211,7 +211,7 @@
|
|||
|
||||
[:div {:class (stl/css :bullet-block)}
|
||||
[:& cb/color-bullet {:color color
|
||||
:mini? true}]]
|
||||
:mini true}]]
|
||||
|
||||
(if ^boolean editing?
|
||||
[:input
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
[app.main.ui.components.context-menu-a11y :refer [context-menu*]]
|
||||
[app.main.ui.components.title-bar :refer [title-bar]]
|
||||
[app.main.ui.context :as ctx]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*]]
|
||||
[app.util.array :as array]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.dom.dnd :as dnd]
|
||||
|
@ -119,14 +119,13 @@
|
|||
:left (:left state)
|
||||
:options options}])
|
||||
|
||||
(mf/defc section-icon
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [section]}]
|
||||
(defn section-icon
|
||||
[section]
|
||||
(case section
|
||||
:colors i/drop-icon
|
||||
:components i/component
|
||||
:typographies i/text-palette
|
||||
i/add))
|
||||
:colors "drop"
|
||||
:components "component"
|
||||
:typographies "text-palette"
|
||||
"add"))
|
||||
|
||||
(mf/defc asset-section
|
||||
{::mf/wrap-props false}
|
||||
|
@ -151,7 +150,7 @@
|
|||
(mf/html
|
||||
[:span {:class (stl/css :title-name)}
|
||||
[:span {:class (stl/css :section-icon)}
|
||||
[:& (or icon section-icon) {:section section}]]
|
||||
[:> icon* {:id (or icon (section-icon section)) :size "s"}]]
|
||||
[:span {:class (stl/css :section-name)}
|
||||
title]
|
||||
|
||||
|
@ -167,10 +166,12 @@
|
|||
:all-clickable true
|
||||
:on-collapsed on-collapsed
|
||||
:add-icon-gap (= 0 assets-count)
|
||||
:class (stl/css-case :title-spacing open?)
|
||||
:title title}
|
||||
buttons]
|
||||
(when ^boolean open? content)]))
|
||||
(when ^boolean (and (< 0 assets-count)
|
||||
open?)
|
||||
[:div {:class (stl/css-case :title-spacing open?)}
|
||||
content])]))
|
||||
|
||||
(mf/defc asset-section-block
|
||||
{::mf/wrap-props false}
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
}
|
||||
|
||||
.title-spacing {
|
||||
margin-bottom: $s-4;
|
||||
padding-block-start: $s-4;
|
||||
}
|
||||
|
||||
.asset-section.opened {
|
||||
|
|
|
@ -208,7 +208,7 @@
|
|||
(nil? color-name) (assoc
|
||||
:id nil
|
||||
:file-id nil))
|
||||
:mini? true
|
||||
:mini true
|
||||
:on-click handle-click-color}]]
|
||||
(cond
|
||||
;; Rendering a color with ID
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
(:require
|
||||
[app.common.colors :as c]
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.tokens :as dt]
|
||||
|
@ -191,10 +190,10 @@ Token names should only contain letters and digits separated by . characters.")}
|
|||
empty-message? (or (nil? result-or-errors)
|
||||
(wte/has-error-code? :error/empty-input errors))
|
||||
message (cond
|
||||
empty-message? (dm/str (tr "workspace.token.resolved-value") "-")
|
||||
empty-message? (tr "workspace.token.resolved-value" "-")
|
||||
errors (->> (wte/humanize-errors errors)
|
||||
(str/join "\n"))
|
||||
:else (dm/str (tr "workspace.token.resolved-value") result-or-errors))]
|
||||
:else (tr "workspace.token.resolved-value" result-or-errors))]
|
||||
[:> text* {:as "p"
|
||||
:typography "body-small"
|
||||
:class (stl/css-case :resolved-value true
|
||||
|
|
|
@ -158,7 +158,6 @@
|
|||
}
|
||||
|
||||
.sets-count-button {
|
||||
text-transform: lowercase;
|
||||
padding: $s-6;
|
||||
padding-left: $s-12;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
[app.main.data.tokens :as dt]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.color-bullet :refer [color-bullet]]
|
||||
[app.main.ui.components.dropdown-menu :refer [dropdown-menu dropdown-menu-item*]]
|
||||
[app.main.ui.components.title-bar :refer [title-bar]]
|
||||
[app.main.ui.ds.buttons.button :refer [button*]]
|
||||
|
@ -22,7 +21,6 @@
|
|||
[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.icons :as i]
|
||||
[app.main.ui.workspace.sidebar.assets.common :as cmm]
|
||||
[app.main.ui.workspace.tokens.changes :as wtch]
|
||||
[app.main.ui.workspace.tokens.context-menu :refer [token-context-menu]]
|
||||
|
@ -33,12 +31,12 @@
|
|||
[app.main.ui.workspace.tokens.style-dictionary :as sd]
|
||||
[app.main.ui.workspace.tokens.theme-select :refer [theme-select]]
|
||||
[app.main.ui.workspace.tokens.token :as wtt]
|
||||
[app.main.ui.workspace.tokens.token-pill :refer [token-pill]]
|
||||
[app.main.ui.workspace.tokens.token-types :as wtty]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[app.util.webapi :as wapi]
|
||||
[beicon.v2.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[okulary.core :as l]
|
||||
[rumext.v2 :as mf]
|
||||
[shadow.resource]))
|
||||
|
@ -46,57 +44,30 @@
|
|||
(def lens:token-type-open-status
|
||||
(l/derived (l/in [:workspace-tokens :open-status]) st/state))
|
||||
|
||||
(def ^:private download-icon
|
||||
(i/icon-xref :download (stl/css :download-icon)))
|
||||
|
||||
;; Components ------------------------------------------------------------------
|
||||
|
||||
(mf/defc token-pill
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [on-click token theme-token highlighted? on-context-menu]}]
|
||||
(let [{:keys [name value resolved-value errors]} token
|
||||
errors? (and (seq errors) (seq (:errors theme-token)))]
|
||||
[:button
|
||||
{:class (stl/css-case :token-pill true
|
||||
:token-pill-highlighted highlighted?
|
||||
:token-pill-invalid errors?)
|
||||
:title (cond
|
||||
errors? (sd/humanize-errors token)
|
||||
:else (->> [(str "Token: " name)
|
||||
(str (tr "workspace.token.original-value") value)
|
||||
(str (tr "workspace.token.resolved-value") resolved-value)]
|
||||
(str/join "\n")))
|
||||
:on-click on-click
|
||||
:on-context-menu on-context-menu
|
||||
:disabled errors?}
|
||||
(when-let [color (if (seq (ctob/find-token-value-references (:value token)))
|
||||
(or
|
||||
(wtt/resolved-value-hex theme-token)
|
||||
;; Fallback when the current set is inactive and has a reference that resolves in this inactive set
|
||||
(wtt/resolved-value-hex token))
|
||||
(wtt/resolved-value-hex token))]
|
||||
[:& color-bullet {:color color
|
||||
:mini? true}])
|
||||
name]))
|
||||
|
||||
(mf/defc token-section-icon
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [type]}]
|
||||
(defn token-section-icon
|
||||
[type]
|
||||
(case type
|
||||
:border-radius i/corner-radius
|
||||
:numeric [:span {:class (stl/css :section-text-icon)} "123"]
|
||||
:color i/drop-icon
|
||||
:boolean i/boolean-difference
|
||||
:opacity [:span {:class (stl/css :section-text-icon)} "%"]
|
||||
:rotation i/rotation
|
||||
:spacing i/padding-extended
|
||||
:string i/text-mixed
|
||||
:stroke-width i/stroke-size
|
||||
:typography i/text
|
||||
;; TODO: Add diagonal icon here when it's available
|
||||
:dimensions [:div {:style {:rotate "45deg"}} i/constraint-horizontal]
|
||||
:sizing [:div {:style {:rotate "45deg"}} i/constraint-horizontal]
|
||||
i/add))
|
||||
:border-radius "corner-radius"
|
||||
:color "drop"
|
||||
:boolean "boolean-difference"
|
||||
:opacity "percentage"
|
||||
:rotation "rotation"
|
||||
:spacing "padding-extended"
|
||||
:string "text-mixed"
|
||||
:stroke-width "stroke-size"
|
||||
:typography "text"
|
||||
:dimensions "expand"
|
||||
:sizing "expand"
|
||||
"add"))
|
||||
|
||||
(defn attribute-actions [token selected-shapes attributes]
|
||||
(let [ids-by-attributes (wtt/shapes-ids-by-applied-attributes token selected-shapes attributes)
|
||||
shape-ids (into #{} (map :id selected-shapes))]
|
||||
{:all-selected? (wtt/shapes-applied-all? ids-by-attributes shape-ids attributes)
|
||||
:shape-ids shape-ids
|
||||
:selected-pred #(seq (% ids-by-attributes))}))
|
||||
|
||||
(mf/defc token-component
|
||||
[{:keys [type tokens selected-shapes token-type-props active-theme-tokens]}]
|
||||
|
@ -140,29 +111,37 @@
|
|||
:token-type-props token-type-props})))))
|
||||
tokens-count (count tokens)]
|
||||
[:div {:on-click on-toggle-open-click}
|
||||
[:& cmm/asset-section {:icon (mf/fnc icon-wrapper []
|
||||
[:div {:class (stl/css :section-icon)}
|
||||
[:& token-section-icon {:type type}]])
|
||||
[:& cmm/asset-section {:icon (token-section-icon type)
|
||||
:title title
|
||||
:assets-count tokens-count
|
||||
:open? open?}
|
||||
[:& cmm/asset-section-block {:role :title-button}
|
||||
[:button {:class (stl/css :action-button)
|
||||
:on-click on-popover-open-click
|
||||
:title (str "Add token: " title)}
|
||||
i/add]]
|
||||
[:> icon-button* {:on-click on-popover-open-click
|
||||
:variant "ghost"
|
||||
:icon "add"
|
||||
:aria-label (str "Add token: " title)}]]
|
||||
(when open?
|
||||
[:& cmm/asset-section-block {:role :content}
|
||||
[:div {:class (stl/css :token-pills-wrapper)}
|
||||
(for [token (sort-by :name tokens)]
|
||||
(let [theme-token (get active-theme-tokens (wtt/token-identifier token))]
|
||||
(let [theme-token (get active-theme-tokens (wtt/token-identifier token))
|
||||
multiple-selection (< 1 (count selected-shapes))
|
||||
full-applied (:all-selected? (attribute-actions token selected-shapes (or all-attributes attributes)))
|
||||
applied (wtt/shapes-token-applied? token selected-shapes (or all-attributes attributes))
|
||||
on-token-click (fn [e]
|
||||
(on-token-pill-click e token))
|
||||
on-context-menu (fn [e] (on-context-menu e token))]
|
||||
[:& token-pill
|
||||
{:key (:name token)
|
||||
:token token
|
||||
:theme-token theme-token
|
||||
:highlighted? (wtt/shapes-token-applied? token selected-shapes (or all-attributes attributes))
|
||||
:on-click #(on-token-pill-click % token)
|
||||
:on-context-menu #(on-context-menu % token)}]))]])]]))
|
||||
:half-applied (and applied (not full-applied))
|
||||
;; Multiple selected shapes behavior should be reviewed after MVP
|
||||
:full-applied (if multiple-selection
|
||||
false
|
||||
applied)
|
||||
:on-click on-token-click
|
||||
:on-context-menu on-context-menu}]))]])]]))
|
||||
|
||||
(defn sorted-token-groups
|
||||
"Separate token-types into groups of `:empty` or `:filled` depending if tokens exist for that type.
|
||||
|
@ -270,24 +249,13 @@
|
|||
[:& token-context-menu]
|
||||
[:& title-bar {:all-clickable true
|
||||
:title "TOKENS"}]
|
||||
[:div.assets-bar
|
||||
(for [{:keys [token-key token-type-props tokens]} (concat (:filled token-groups)
|
||||
(:empty token-groups))]
|
||||
[:& token-component {:key token-key
|
||||
:type token-key
|
||||
:selected-shapes selected-shapes
|
||||
:active-theme-tokens active-theme-tokens
|
||||
:tokens tokens
|
||||
:token-type-props token-type-props}])]]))
|
||||
|
||||
(mf/defc json-import-button []
|
||||
(let []
|
||||
[:div
|
||||
|
||||
[:button {:class (stl/css :download-json-button)
|
||||
:on-click #(.click (js/document.getElementById "file-input"))}
|
||||
download-icon
|
||||
"Import JSON"]]))
|
||||
(for [{:keys [token-key token-type-props tokens]} (concat (:filled token-groups) (:empty token-groups))]
|
||||
[:& token-component {:key token-key
|
||||
:type token-key
|
||||
:selected-shapes selected-shapes
|
||||
:active-theme-tokens active-theme-tokens
|
||||
:tokens tokens
|
||||
:token-type-props token-type-props}])]))
|
||||
|
||||
(mf/defc import-export-button
|
||||
{::mf/wrap-props false}
|
||||
|
|
|
@ -80,25 +80,6 @@
|
|||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.token-pill {
|
||||
@extend .button-secondary;
|
||||
gap: $s-8;
|
||||
padding: $s-4 $s-8;
|
||||
border-radius: $br-6;
|
||||
font-size: $fs-14;
|
||||
|
||||
&.token-pill-highlighted {
|
||||
color: var(--button-primary-foreground-color-rest);
|
||||
background: var(--button-primary-background-color-rest);
|
||||
}
|
||||
|
||||
&.token-pill-invalid {
|
||||
background-color: var(--button-secondary-background-color-rest);
|
||||
color: var(--status-color-error-500);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.section-text-icon {
|
||||
font-size: $fs-12;
|
||||
width: 16px;
|
||||
|
|
|
@ -47,12 +47,12 @@
|
|||
(= (token-identifier token) id)))
|
||||
|
||||
(defn token-applied?
|
||||
"Test if `token` is applied to a `shape` with at least one of the one of the given `token-attributes`."
|
||||
"Test if `token` is applied to a `shape` with at least one of the given `token-attributes`."
|
||||
[token shape token-attributes]
|
||||
(some #(token-attribute-applied? token shape %) token-attributes))
|
||||
|
||||
(defn shapes-token-applied?
|
||||
"Test if `token` is applied to to any of `shapes` with at least one of the one of the given `token-attributes`."
|
||||
"Test if `token` is applied to to any of `shapes` with at least one of the given `token-attributes`."
|
||||
[token shapes token-attributes]
|
||||
(some #(token-applied? token % token-attributes) shapes))
|
||||
|
||||
|
|
59
frontend/src/app/main/ui/workspace/tokens/token_pill.cljs
Normal file
59
frontend/src/app/main/ui/workspace/tokens/token_pill.cljs
Normal file
|
@ -0,0 +1,59 @@
|
|||
(ns app.main.ui.workspace.tokens.token-pill
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.main.ui.components.color-bullet :refer [color-bullet]]
|
||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*]]
|
||||
[app.main.ui.ds.foundations.utilities.token.token-status :refer [token-status-icon*]]
|
||||
[app.main.ui.workspace.tokens.style-dictionary :as sd]
|
||||
[app.main.ui.workspace.tokens.token :as wtt]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc token-pill
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [on-click token theme-token full-applied on-context-menu half-applied]}]
|
||||
(let [{:keys [name value resolved-value errors]} token
|
||||
errors? (or (nil? theme-token) (and (seq errors) (seq (:errors theme-token))))
|
||||
|
||||
color (when (seq (ctob/find-token-value-references value))
|
||||
(wtt/resolved-value-hex theme-token))
|
||||
|
||||
color (or color (wtt/resolved-value-hex token))
|
||||
|
||||
token-status-id (cond
|
||||
half-applied
|
||||
"token-status-partial"
|
||||
full-applied
|
||||
"token-status-full"
|
||||
:else
|
||||
"token-status-non-applied")]
|
||||
[:button {:class (stl/css-case :token-pill true
|
||||
:token-pill-applied full-applied
|
||||
:token-pill-invalid errors?
|
||||
:token-pill-invalid-applied (and full-applied errors?))
|
||||
:type "button"
|
||||
:title (cond
|
||||
errors? (sd/humanize-errors token)
|
||||
:else (->> [(str "Token: " name)
|
||||
(tr "workspace.token.original-value" value)
|
||||
(tr "workspace.token.resolved-value" resolved-value)]
|
||||
(str/join "\n")))
|
||||
:on-click on-click
|
||||
:on-context-menu on-context-menu
|
||||
:disabled errors?}
|
||||
(cond
|
||||
color
|
||||
[:& color-bullet {:color color
|
||||
:mini true}]
|
||||
errors?
|
||||
[:> icon*
|
||||
{:id "broken-link"
|
||||
:class (stl/css :token-pill-icon)}]
|
||||
|
||||
:else
|
||||
[:> token-status-icon*
|
||||
{:id token-status-id
|
||||
:class (stl/css :token-pill-icon)}])
|
||||
name]))
|
106
frontend/src/app/main/ui/workspace/tokens/token_pill.scss
Normal file
106
frontend/src/app/main/ui/workspace/tokens/token_pill.scss
Normal file
|
@ -0,0 +1,106 @@
|
|||
// 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";
|
||||
@import "./common.scss";
|
||||
|
||||
.token-pill {
|
||||
--token-pill-background: var(--color-background-tertiary);
|
||||
--token-pill-foreground: var(--color-foreground-secondary);
|
||||
--token-pill-border: var(--color-background-tertiary);
|
||||
--token-pill-outline: none;
|
||||
--token-pill-accent: var(--color-background-quaternary);
|
||||
|
||||
@include use-typography("code-font");
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
align-items: center;
|
||||
gap: $s-6;
|
||||
border: $s-1 solid var(--token-pill-border);
|
||||
outline: $s-2 solid var(--token-pill-outline);
|
||||
height: $s-24;
|
||||
border-radius: $br-8;
|
||||
padding: $s-3 $s-8;
|
||||
color: var(--token-pill-foreground);
|
||||
background: var(--token-pill-background);
|
||||
|
||||
&:hover {
|
||||
--token-pill-background: var(--color-token-background);
|
||||
--token-pill-foreground: var(--color-foreground-primary);
|
||||
--token-pill-border: var(--color-token-background);
|
||||
--token-pill-outline: none;
|
||||
--token-pill-accent: var(--color-background-quaternary);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
--token-pill-outline: var(--color-background-primary);
|
||||
--token-pill-border: var(--color-accent-primary);
|
||||
outline-offset: -3px;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
--token-pill-background: var(--color-background-primary);
|
||||
--token-pill-foreground: var(--color-foreground-secondary);
|
||||
--token-pill-border: var(--color-background-tertiary);
|
||||
--token-pill-outline: none;
|
||||
--token-pill-accent: var(--color-background-tertiary);
|
||||
}
|
||||
}
|
||||
|
||||
.token-pill-applied {
|
||||
--token-pill-background: var(--color-token-background);
|
||||
--token-pill-foreground: var(--color-token-foreground);
|
||||
--token-pill-border: var(--color-token-border);
|
||||
--token-pill-accent: var(--color-token-accent);
|
||||
|
||||
&:hover {
|
||||
--token-pill-background: var(--color-token-background);
|
||||
--token-pill-foreground: var(--color-foreground-primary);
|
||||
--token-pill-border: var(--color-token-foreground);
|
||||
--token-pill-accent: var(--color-token-accent);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
--token-pill-background: var(--color-token-background);
|
||||
--token-pill-foreground: var(--color-token-foreground);
|
||||
--token-pill-outline: var(--color-accent-primary);
|
||||
--token-pill-border: var(--color-token-background);
|
||||
--token-pill-accent: var(--color-token-accent);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
--token-pill-background: var(--color-background-primary);
|
||||
--token-pill-foreground: var(--color-token-foreground);
|
||||
--token-pill-border: var(--color-token-accent);
|
||||
--token-pill-outline: none;
|
||||
--token-pill-accent: var(--color-token-accent);
|
||||
}
|
||||
}
|
||||
|
||||
.token-pill-invalid,
|
||||
.token-pill-invalid-applied {
|
||||
--token-pill-background: var(--color-background-tertiary);
|
||||
--token-pill-foreground: var(--color-foreground-error);
|
||||
--token-pill-border: var(--color-background-tertiary);
|
||||
--token-pill-accent: var(--color-foreground-error);
|
||||
|
||||
&:hover,
|
||||
&:focus-visible,
|
||||
&:disabled {
|
||||
--token-pill-background: var(--color-background-tertiary);
|
||||
--token-pill-foreground: var(--color-foreground-error);
|
||||
--token-pill-border: var(--color-background-tertiary);
|
||||
--token-pill-accent: var(--color-foreground-error);
|
||||
}
|
||||
}
|
||||
|
||||
.token-pill-icon {
|
||||
color: var(--token-pill-accent);
|
||||
}
|
|
@ -6240,10 +6240,6 @@ msgstr "%s sets"
|
|||
msgid "workspace.token.original-value"
|
||||
msgstr "Original value: "
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/form.cljs:193, src/app/main/ui/workspace/tokens/form.cljs:196, src/app/main/ui/workspace/tokens/sidebar.cljs:67
|
||||
msgid "workspace.token.resolved-value"
|
||||
msgstr "Resolved value: "
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/modals/themes.cljs:208
|
||||
msgid "workspace.token.save-theme"
|
||||
msgstr "Save theme"
|
||||
|
@ -6573,9 +6569,9 @@ msgstr "Create new %s token"
|
|||
msgid "workspace.token.edit-token"
|
||||
msgstr "Edit token"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/form.cljs
|
||||
#: src/app/main/ui/workspace/tokens/form.cljs, src/app/main/ui/workspace/tokens/token_pill.cljs
|
||||
msgid "workspace.token.resolved-value"
|
||||
msgstr "Resolved value: "
|
||||
msgstr "Resolved value: %s"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/form.cljs
|
||||
msgid "workspace.token.token-name"
|
||||
|
@ -6603,7 +6599,7 @@ msgstr "Add a description (optional)"
|
|||
|
||||
#: src/app/main/ui/workspace/tokens/sidebar.cljs
|
||||
msgid "workspace.token.original-value"
|
||||
msgstr "Original value: "
|
||||
msgstr "Original value: %s"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/sidebar.cljs
|
||||
msgid "workspace.token.no-themes"
|
||||
|
|
|
@ -6242,10 +6242,6 @@ msgstr "%s sets"
|
|||
msgid "workspace.token.original-value"
|
||||
msgstr "Valor original: "
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/form.cljs:193, src/app/main/ui/workspace/tokens/form.cljs:196, src/app/main/ui/workspace/tokens/sidebar.cljs:67
|
||||
msgid "workspace.token.resolved-value"
|
||||
msgstr "Valor resuelto: "
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/modals/themes.cljs:208
|
||||
msgid "workspace.token.save-theme"
|
||||
msgstr "Guardar tema"
|
||||
|
@ -6573,9 +6569,9 @@ msgstr "Crear un token de %s"
|
|||
msgid "workspace.token.edit-token"
|
||||
msgstr "Editar token"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/form.cljs
|
||||
#: src/app/main/ui/workspace/tokens/form.cljs ,src/app/main/ui/workspace/tokens/token_pill.cljs
|
||||
msgid "workspace.token.resolved-value"
|
||||
msgstr "Valor resuelto: "
|
||||
msgstr "Valor resuelto: %s"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/form.cljs
|
||||
msgid "workspace.token.token-name"
|
||||
|
@ -6603,7 +6599,7 @@ msgstr "Añade una Descripción (opcional)"
|
|||
|
||||
#: src/app/main/ui/workspace/tokens/sidebar.cljs
|
||||
msgid "workspace.token.original-value"
|
||||
msgstr "Valor original: "
|
||||
msgstr "Valor original: %s"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/sidebar.cljs
|
||||
msgid "workspace.token.no-themes"
|
||||
|
|
Loading…
Add table
Reference in a new issue