Merge remote-tracking branch 'origin/staging' into develop

This commit is contained in:
Andrey Antukh 2025-04-01 11:02:32 +02:00
commit 076d64df8f
31 changed files with 339 additions and 141 deletions

View file

@ -192,3 +192,5 @@
:height 720}])
(def zoom-half-pixel-precision 8)
(def max-input-length 255)

View file

@ -189,6 +189,8 @@
(assoc :share-links share-links)
(assoc :current-team-id team-id)
(assoc :teams {team-id team})
(assoc :files (-> (d/index-by :id libraries)
(assoc (:id file) file)))
(assoc :viewer {:libraries (d/index-by :id libraries)
:users (d/index-by :id users)
:permissions permissions

View file

@ -10,6 +10,7 @@
[app.main.style :as stl])
(:require
[app.common.data :as d]
[app.main.constants :refer [max-input-length]]
[app.main.ui.ds.controls.shared.options-dropdown :refer [options-dropdown*]]
[app.main.ui.ds.foundations.assets.icon :refer [icon* icon-list] :as i]
[app.util.array :as array]
@ -60,6 +61,7 @@
[:id {:optional true} :string]
[:options [:vector schema:combobox-option]]
[:class {:optional true} :string]
[:max-length {:optional true} :int]
[:placeholder {:optional true} :string]
[:disabled {:optional true} :boolean]
[:default-selected {:optional true} :string]
@ -69,7 +71,7 @@
(mf/defc combobox*
{::mf/props :obj
::mf/schema schema:combobox}
[{:keys [id options class placeholder disabled has-error default-selected on-change] :rest props}]
[{:keys [id options class placeholder disabled has-error default-selected on-change max-length] :rest props}]
(let [open* (mf/use-state false)
open (deref open*)
@ -240,6 +242,7 @@
:aria-activedescendant focused
:class (stl/css :input)
:data-testid "combobox-input"
:maxlength (d/nilv max-length max-input-length)
:disabled disabled
:value selected
:on-change on-input-change

View file

@ -10,6 +10,7 @@
[app.main.style :as stl])
(:require
[app.common.data :as d]
[app.main.constants :refer [max-input-length]]
[app.main.ui.ds.foundations.assets.icon :refer [icon* icon-list]]
[app.util.dom :as dom]
[rumext.v2 :as mf]))
@ -20,13 +21,14 @@
[:icon {:optional true}
[:and :string [:fn #(contains? icon-list %)]]]
[:type {:optional true} :string]
[:max-length {:optional true} :int]
[:variant {:optional true} :string]])
(mf/defc input*
{::mf/props :obj
::mf/forward-ref true
::mf/schema schema:input}
[{:keys [icon class type variant] :rest props} ref]
[{:keys [icon class type max-length variant] :rest props} ref]
(let [ref (or ref (mf/use-ref))
type (d/nilv type "text")
props (mf/spread-props props
@ -34,6 +36,7 @@
:input true
:input-with-icon (some? icon))
:ref ref
:maxlength (d/nilv max-length (str max-input-length))
:type type})
on-icon-click

View file

@ -32,16 +32,11 @@
(get-in state [libraries-place file-id :data :colors]))]
(l/derived get-library st/state)))
(defn- get-colors-library [color]
(let [colors-library-v (-> (mf/use-memo
(mf/deps (:file-id color))
#(make-colors-library-ref :viewer-libraries (:file-id color)))
mf/deref)
colors-library-ws (-> (mf/use-memo
(mf/deps (:file-id color))
#(make-colors-library-ref :libraries (:file-id color)))
mf/deref)]
(or colors-library-v colors-library-ws)))
(defn- use-colors-library [color]
(-> (mf/use-memo
(mf/deps (:file-id color))
#(make-colors-library-ref :files (:file-id color)))
mf/deref))
(defn- get-file-colors []
(or (mf/deref file-colors-ref) (mf/deref refs/workspace-file-colors)))
@ -54,7 +49,7 @@
(str/capital $)))
(mf/defc color-row [{:keys [color format copy-data on-change-format]}]
(let [colors-library (get-colors-library color)
(let [colors-library (use-colors-library color)
file-colors (get-file-colors)
color-library-name (get-in (or colors-library file-colors) [(:id color) :name])
color (assoc color :color-library-name color-library-name)
@ -85,7 +80,8 @@
(when color-library-name
[:div {:class (stl/css :second-row)}
[:div {:class (stl/css :color-name-library)}
[:div {:class (stl/css :color-name-library)
:data-testid "color-library-name"}
color-library-name]])]]
[:div {:class (stl/css :image-download)}
@ -146,6 +142,7 @@
(when color-library-name
[:div {:class (stl/css :second-row)}
[:div {:class (stl/css :color-name-library)}
[:div {:class (stl/css :color-name-library)
:data-testid "color-library-name"}
color-library-name]])]])))

View file

@ -70,36 +70,23 @@
visibility: visible;
}
}
.one-line {
max-height: $s-32;
}
.two-line {
display: grid;
grid-template-rows: 1fr 1fr;
grid-template-rows: auto 1fr;
gap: $s-4;
}
.color-name-wrapper {
@include bodySmallTypography;
@include flexColumn;
padding: $s-8 $s-4 $s-8 $s-8;
height: $s-32;
max-width: $s-80;
&.gradient-color {
color: var(--menu-foreground-color);
max-width: $s-124;
}
.color-name-library {
@include bodySmallTypography;
@include textEllipsis;
text-align: left;
height: $s-16;
color: var(--menu-foreground-color-rest);
}
.color-value-wrapper {
@include bodySmallTypography;
height: $s-16;
color: var(--menu-foreground-color);
}
}
.opacity-info {
@ -152,6 +139,7 @@
.color-name-library {
@include inspectValue;
color: var(--menu-foreground-color-rest);
word-break: break-word;
}
.image-download {

View file

@ -110,7 +110,9 @@
[:div {:class (stl/css :resize-area-horiz)
:on-pointer-down on-pointer-down-pages
:on-lost-pointer-capture on-lost-pointer-capture-pages
:on-pointer-move on-pointer-move-pages}])
:on-pointer-move on-pointer-move-pages}
[:div {:class (stl/css :resize-handle-horiz)}]])
[:& layers-toolbox {:size-parent size
:size size-pages}]])

View file

@ -88,6 +88,12 @@ $width-settings-bar-max: $s-500;
position: absolute;
left: 0;
width: 100%;
padding: $s-3 0 $s-1 0;
height: $s-6;
cursor: ns-resize;
}
.resize-handle-horiz {
border-bottom: $s-2 solid var(--resize-area-border-color);
cursor: ns-resize;
}

View file

@ -7,7 +7,9 @@
(ns app.main.ui.workspace.tokens.components.controls.input-tokens
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.main.constants :refer [max-input-length]]
[app.main.ui.ds.controls.input :refer [input*]]
[rumext.v2 :as mf]))
@ -18,6 +20,7 @@
[:placeholder {:optional true} :string]
[:default-value {:optional true} [:maybe :string]]
[:class {:optional true} :string]
[:max-length {:optional true} :int]
[:error {:optional true} :boolean]
[:value {:optional true} :string]])
@ -25,12 +28,13 @@
{::mf/props :obj
::mf/forward-ref true
::mf/schema schema::input-tokens}
[{:keys [class label id error value children] :rest props} ref]
[{:keys [class label id max-length error value children] :rest props} ref]
(let [ref (or ref (mf/use-ref))
props (mf/spread-props props {:id id
:type "text"
:class (stl/css :input)
:aria-invalid error
:max-length (d/nilv max-length max-input-length)
:value value
:ref ref})]
[:div {:class (dm/str class " " (stl/css-case :wrapper true

View file

@ -106,9 +106,9 @@
[:> heading* {:level 3
:class (stl/css :theme-group-label)
:typography "body-large"}
[:span {:class (stl/css :group-title) :title (tr "workspace.token.group-name")}
[:> icon* {:icon-id "group"}]
group]])
[:div {:class (stl/css :group-title) :title (str (tr "workspace.token.group-name") ": " group)}
[:> icon* {:icon-id "group" :class (stl/css :group-title-icon)}]
[:> text* {:as "span" :typography "body-medium" :class (stl/css :group-title-name)} group]]])
[:ul {:class (stl/css :theme-group-rows-wrapper)}
(for [[_ {:keys [group name] :as theme}] themes
:let [theme-id (ctob/theme-path theme)
@ -126,7 +126,7 @@
:theme-path [(:id theme) (:group theme) (:name theme)]})))]]
[:li {:key theme-id
:class (stl/css :theme-row)}
[:div {:class (stl/css :theme-row-left)}
[:div {:class (stl/css :theme-switch-row)}
;; FIXME: FIREEEEEEEEEE THIS
[:div {:on-click (fn [e]
@ -135,11 +135,12 @@
(st/emit! (wdt/toggle-token-theme-active? group name)))}
[:& switch {:name (tr "workspace.token.theme-name" name)
:on-change (constantly nil)
:selected? selected?}]]
[:> text* {:as "span" :typography "body-medium" :class (stl/css :theme-name)} name]]
:selected? selected?}]]]
[:div {:class (stl/css :theme-name-row)}
[:> text* {:as "span" :typography "body-medium" :class (stl/css :theme-name) :title name} name]]
[:div {:class (stl/css :theme-row-right)}
[:div {:class (stl/css :theme-actions-row)}
(let [sets-count (some-> theme :sets seq count)]
[:> button* {:class (stl/css-case :sets-count-button sets-count
:sets-count-empty-button (not sets-count))
@ -205,6 +206,7 @@
[:> input-tokens* {:id "theme-input"
:label (tr "workspace.token.label.theme")
:type "text"
:max-length 256
:placeholder (tr "workspace.token.label.theme-placeholder")
:on-change on-update-name
:value (mf/ref-val theme-name-ref)

View file

@ -32,8 +32,8 @@
}
.themes-modal-wrapper {
display: grid;
grid-template-rows: 0fr minmax(0, 1fr);
display: flex;
flex-direction: column;
gap: $s-16;
max-height: $s-688;
}
@ -120,6 +120,15 @@
gap: $s-4;
}
.group-title-icon {
flex-shrink: 0;
}
.group-title-name {
flex-grow: 1;
@include textEllipsis;
}
.theme-group-rows-wrapper {
display: flex;
flex-direction: column;
@ -136,26 +145,26 @@
}
.theme-row {
display: flex;
align-items: center;
gap: $s-12;
display: flex;
justify-content: space-between;
gap: $s-16;
}
.theme-row-left {
display: flex;
align-items: center;
gap: $s-16;
.theme-name-row {
@include textEllipsis;
flex-grow: 1;
}
.theme-name {
color: var(--color-foreground-primary);
}
.theme-row-right {
display: flex;
.theme-actions-row {
align-items: center;
display: flex;
gap: $s-6;
flex-shrink: 0;
}
.sets-count-button {

View file

@ -94,6 +94,7 @@
:type "text"
:on-blur on-submit
:on-key-down on-key-down
:maxlength "256"
:auto-focus true
:placeholder (tr "workspace.token.set-edit-placeholder")
:default-value default-value}]))
@ -226,6 +227,7 @@
:on-submit on-edit-submit'}]
[:*
[:div {:class (stl/css :set-name)
:title label
:on-double-click on-double-click
:id label-id}
label]
@ -498,7 +500,7 @@
(when (fn? on-start-edition)
(on-start-edition v))))]
[:fieldset {:class (stl/css :sets-list)}
[: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.token.no-sets-create")]

View file

@ -70,6 +70,7 @@
}
.icon {
flex-shrink: 0;
display: flex;
align-items: center;
width: $s-20;
@ -82,6 +83,7 @@
}
.checkbox-style {
flex-shrink: 0;
display: flex;
justify-content: center;
align-items: center;

View file

@ -40,6 +40,7 @@
[app.util.i18n :refer [tr]]
[app.util.webapi :as wapi]
[beicon.v2.core :as rx]
[cuerdas.core :as str]
[okulary.core :as l]
[potok.v2.core :as ptk]
[rumext.v2 :as mf]
@ -319,8 +320,7 @@
[:*
[:& token-context-menu]
[:& title-bar {:all-clickable true
:title (tr "workspace.token.tokens-section-title" selected-token-set-name)}]
[:span {:class (stl/css :sets-header)} (tr "workspace.token.tokens-section-title" selected-token-set-name)]
(for [type filled-group]
(let [tokens (get tokens-by-type type)]
@ -368,9 +368,10 @@
(fn [event]
(let [file (-> (dom/get-target event)
(dom/get-files)
(first))]
(first))
file-name (str/replace (.-name file) ".json" "")]
(->> (wapi/read-file-as-text file)
(sd/process-json-stream)
(sd/process-json-stream {:file-name file-name})
(rx/subs! (fn [lib]
(st/emit! (ptk/data-event ::ev/event {::ev/name "import-tokens"})
(dt/import-tokens-lib lib)))
@ -439,6 +440,7 @@
[:div {:class (stl/css :resize-area-horiz)
:on-pointer-down on-pointer-down-pages
:on-lost-pointer-capture on-lost-pointer-capture-pages
:on-pointer-move on-pointer-move-pages}]
:on-pointer-move on-pointer-move-pages}
[:div {:class (stl/css :resize-handle-horiz)}]]
[:> tokens-section* {:tokens-lib tokens-lib}]]
[:> import-export-button*]]))

View file

@ -38,12 +38,17 @@
padding-block-end: $s-16;
}
.themes-header {
.themes-header,
.sets-header {
@include use-typography("headline-small");
display: block;
margin-bottom: $s-8;
padding-left: $s-8;
padding: $s-8;
color: var(--title-foreground-color);
word-break: break-word;
}
.sets-header {
margin-block-start: $s-8;
}
.themes-wrapper {
@ -170,6 +175,12 @@
position: absolute;
left: 0;
width: 100%;
padding: $s-3 0 $s-1 0;
height: $s-6;
cursor: ns-resize;
}
.resize-handle-horiz {
border-bottom: $s-2 solid var(--resize-area-border-color);
cursor: ns-resize;
}

View file

@ -6,6 +6,7 @@
[app.common.schema :as sm]
[app.common.transit :as t]
[app.common.types.tokens-lib :as ctob]
[app.main.refs :as refs]
[app.main.ui.workspace.tokens.errors :as wte]
[app.main.ui.workspace.tokens.tinycolor :as tinycolor]
[app.main.ui.workspace.tokens.token :as wtt]
@ -249,33 +250,51 @@
(= header-2 "Reference Errors:"))
errors)))
(defn process-json-stream [data-stream]
(->> data-stream
(rx/map (fn [data]
(try
(t/decode-str data)
(catch js/Error e
(throw (wte/error-ex-info :error.import/json-parse-error data e))))))
(rx/map (fn [json-data]
(try
(if-not (ctob/has-legacy-format? json-data)
(ctob/decode-dtcg-json (ctob/ensure-tokens-lib nil) json-data)
(ctob/decode-legacy-json (ctob/ensure-tokens-lib nil) json-data))
(catch js/Error e
(throw (wte/error-ex-info :error.import/invalid-json-data json-data e))))))
(rx/mapcat (fn [tokens-lib]
(defn process-json-stream
([data-stream]
(process-json-stream nil data-stream))
([params data-stream]
(let [{:keys [file-name]} params]
(->> data-stream
(rx/map (fn [data]
(try
(-> (ctob/get-all-tokens tokens-lib)
(resolve-tokens-with-errors+)
(p/then (fn [_] tokens-lib))
(p/catch (fn [sd-error]
(let [reference-errors (reference-errors sd-error)
err (if reference-errors
(wte/error-ex-info :error.import/style-dictionary-reference-errors reference-errors sd-error)
(wte/error-ex-info :error.import/style-dictionary-unknown-error sd-error sd-error))]
(throw err)))))
(t/decode-str data)
(catch js/Error e
(p/rejected (wte/error-ex-info :error.import/style-dictionary-unknown-error "" e))))))))
(throw (wte/error-ex-info :error.import/json-parse-error data e))))))
(rx/map (fn [json-data]
(let [single-set? (ctob/single-set? json-data)
json-format (ctob/get-json-format json-data)]
(try
(cond
(and single-set?
(= :json-format/legacy json-format))
(ctob/decode-single-set-legacy-json (ctob/ensure-tokens-lib (deref refs/tokens-lib)) file-name json-data)
(and single-set?
(= :json-format/dtcg json-format))
(ctob/decode-single-set-json (ctob/ensure-tokens-lib (deref refs/tokens-lib)) file-name json-data)
(= :json-format/legacy json-format)
(ctob/decode-legacy-json (ctob/ensure-tokens-lib nil) json-data)
:else
(ctob/decode-dtcg-json (ctob/ensure-tokens-lib nil) json-data))
(catch js/Error e
(throw (wte/error-ex-info :error.import/invalid-json-data json-data e)))))))
(rx/mapcat (fn [tokens-lib]
(try
(-> (ctob/get-all-tokens tokens-lib)
(resolve-tokens-with-errors+)
(p/then (fn [_] tokens-lib))
(p/catch (fn [sd-error]
(let [reference-errors (reference-errors sd-error)
err (if reference-errors
(wte/error-ex-info :error.import/style-dictionary-reference-errors reference-errors sd-error)
(wte/error-ex-info :error.import/style-dictionary-unknown-error sd-error sd-error))]
(throw err)))))
(catch js/Error e
(p/rejected (wte/error-ex-info :error.import/style-dictionary-unknown-error "" e))))))))))
;; === Errors

View file

@ -41,7 +41,7 @@
:sub-item grouped?
:is-selected selected?)
:on-click select-theme}
[:> text* {:as "span" :typography "body-small" :class (stl/css :label)} name]
[:> text* {:as "span" :typography "body-small" :class (stl/css :label) :title name} name]
[:> icon* {:icon-id i/tick
:aria-hidden true
:class (stl/css-case :check-icon true
@ -58,7 +58,7 @@
:aria-labelledby (dm/str group "-label")
:role "group"}
(when (seq group)
[:> text* {:as "span" :typography "headline-small" :class (stl/css :group) :id (dm/str group "-label")} group])
[:> text* {:as "span" :typography "headline-small" :class (stl/css :group) :id (dm/str (str/kebab group) "-label") :title group} group])
[:& themes-list {:themes themes
:active-theme-paths active-theme-paths
:on-close on-close

View file

@ -41,6 +41,7 @@
}
.group {
@include textEllipsis;
display: block;
padding: $s-8;
color: var(--color-foreground-secondary);