mirror of
https://github.com/penpot/penpot.git
synced 2026-04-25 11:18:36 +00:00
🎉 Add typography token row (#8749)
* 🔧 Create flag * ✨ Add typography type on tokens by input * 🎉 Add typography token row * ♻️ Update sub-components to use new style * 🎉 Add disabled option on radio-buttons* component * 🎉 Add combobox search in a new component * 🎉 Divide components * 🐛 Fix placeholder
This commit is contained in:
parent
27313e6add
commit
5f474f9536
@ -128,6 +128,8 @@
|
||||
:token-shadow
|
||||
:token-tokenscript
|
||||
:token-import-from-library
|
||||
:token-typography-row
|
||||
|
||||
;; Only for developtment.
|
||||
:transit-readable-response
|
||||
:user-feedback
|
||||
|
||||
@ -557,7 +557,8 @@
|
||||
:font-size [:font-size]
|
||||
:letter-spacing [:letter-spacing]
|
||||
:fill [:color]
|
||||
:stroke-color [:color]})
|
||||
:stroke-color [:color]
|
||||
:typography [:typography]})
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; HELPERS for tokens application
|
||||
|
||||
@ -52,6 +52,7 @@
|
||||
|
||||
handle-click
|
||||
(mf/use-fn
|
||||
(mf/deps selected on-change allow-empty)
|
||||
(fn [event]
|
||||
(let [target (dom/get-target event)
|
||||
label (dom/get-parent-with-data target "label")
|
||||
@ -114,4 +115,4 @@
|
||||
:name name
|
||||
:disabled (or disabled wrapper-disabled)
|
||||
:value value
|
||||
:checked checked?}]]))]))
|
||||
:checked checked?}]]))]))
|
||||
|
||||
@ -0,0 +1,89 @@
|
||||
;; 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.ds.controls.shared.dropdown-navigation
|
||||
(:require
|
||||
[app.util.dom :as dom]
|
||||
[app.util.keyboard :as kbd]
|
||||
[app.util.object :as obj]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defn use-dropdown-navigation
|
||||
"Hook for keyboard navigation in dropdowns.
|
||||
|
||||
Options:
|
||||
- focusable-ids: vector of focusable ids (already filtered)
|
||||
- nodes-ref: ref to a JS object mapping id -> DOM node
|
||||
- on-enter: fn called with focused-id when Enter is pressed
|
||||
- searchable: when true, nil focused-id means search input is focused
|
||||
- search-input-ref: ref to the search input DOM node
|
||||
- on-close: optional fn called when Esc/Tab is pressed"
|
||||
|
||||
[{:keys [focusable-ids nodes-ref on-enter searchable search-input-ref on-close]}]
|
||||
(let [focused-id* (mf/use-state nil)
|
||||
focused-id (deref focused-id*)
|
||||
|
||||
focus-input!
|
||||
(mf/use-fn
|
||||
(mf/deps search-input-ref)
|
||||
(fn []
|
||||
(reset! focused-id* nil)
|
||||
(when-let [input (mf/ref-val search-input-ref)]
|
||||
(dom/focus! input))))
|
||||
|
||||
on-key-down
|
||||
(mf/use-fn
|
||||
(mf/deps focused-id focusable-ids searchable)
|
||||
(fn [event]
|
||||
(cond
|
||||
(kbd/down-arrow? event)
|
||||
(do
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(if (nil? focused-id)
|
||||
(reset! focused-id* (first focusable-ids))
|
||||
(let [idx (or (first (keep-indexed #(when (= %2 focused-id) %1) focusable-ids)) -1)
|
||||
next-idx (mod (inc idx) (count focusable-ids))
|
||||
wrap-to-input? (and ^boolean searchable
|
||||
(= next-idx 0)
|
||||
(= idx (dec (count focusable-ids))))]
|
||||
(if wrap-to-input?
|
||||
(focus-input!)
|
||||
(reset! focused-id* (nth focusable-ids next-idx nil))))))
|
||||
|
||||
(kbd/up-arrow? event)
|
||||
(do
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(if (nil? focused-id)
|
||||
(reset! focused-id* (last focusable-ids))
|
||||
(let [idx (or (first (keep-indexed #(when (= %2 focused-id) %1) focusable-ids)) 0)
|
||||
prev-idx (dec idx)
|
||||
wrap-to-input? (and ^boolean searchable (= prev-idx -1))]
|
||||
(if wrap-to-input?
|
||||
(focus-input!)
|
||||
(reset! focused-id* (nth focusable-ids (mod prev-idx (count focusable-ids)) nil))))))
|
||||
|
||||
(kbd/enter? event)
|
||||
(when focused-id
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(on-enter focused-id))
|
||||
|
||||
(or (kbd/esc? event) (kbd/tab? event))
|
||||
(do
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(reset! focused-id* nil)
|
||||
(when on-close (on-close))))))]
|
||||
|
||||
(mf/with-effect [focused-id]
|
||||
(when (some? focused-id)
|
||||
(when-let [node (obj/get (mf/ref-val nodes-ref) focused-id)]
|
||||
(dom/scroll-into-view-if-needed! node {:block "nearest" :inline "nearest"}))))
|
||||
|
||||
{:focused-id focused-id
|
||||
:focused-id* focused-id*
|
||||
:on-key-down on-key-down}))
|
||||
@ -63,3 +63,8 @@
|
||||
--options-fg-color: var(--color-accent-primary);
|
||||
--options-icon-fg-color: var(--color-accent-primary);
|
||||
}
|
||||
|
||||
.option-check {
|
||||
color: var(--token-options-icon-fg-color);
|
||||
min-width: var(--sp-l);
|
||||
}
|
||||
|
||||
@ -9,10 +9,8 @@
|
||||
[app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.weak :refer [weak-key]]
|
||||
[app.main.ui.ds.controls.shared.option :refer [option*]]
|
||||
[app.main.ui.ds.controls.shared.token-option :refer [token-option*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
||||
[app.main.ui.ds.controls.shared.render-option :refer [render-option]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
@ -20,6 +18,14 @@
|
||||
[:and :string
|
||||
[:fn {:error/message "invalid data: invalid icon"} #(contains? i/icon-list %)]])
|
||||
|
||||
(def ^:private
|
||||
xf:filter-blank-id
|
||||
(filter #(str/blank? (get % :id))))
|
||||
|
||||
(def ^:private
|
||||
xf:filter-non-blank-id
|
||||
(remove #(str/blank? (get % :id))))
|
||||
|
||||
(def schema:option
|
||||
"A schema for the option data structure expected to receive on props
|
||||
for the `options-dropdown*` component."
|
||||
@ -33,67 +39,6 @@
|
||||
[:label {:optional true} :string]
|
||||
[:aria-label {:optional true} :string]])
|
||||
|
||||
|
||||
|
||||
(def ^:private
|
||||
xf:filter-blank-id
|
||||
(filter #(str/blank? (get % :id))))
|
||||
|
||||
(def ^:private
|
||||
xf:filter-non-blank-id
|
||||
(remove #(str/blank? (get % :id))))
|
||||
|
||||
(defn- render-option
|
||||
[option ref on-click selected focused]
|
||||
(let [id (get option :id)
|
||||
name (get option :name)
|
||||
type (get option :type)]
|
||||
|
||||
(mf/html
|
||||
(case type
|
||||
:group
|
||||
[:li {:class (stl/css :group-option)
|
||||
:role "presentation"
|
||||
:key (weak-key option)}
|
||||
[:> icon*
|
||||
{:icon-id i/arrow-down
|
||||
:size "m"
|
||||
:class (stl/css :option-check)
|
||||
:aria-hidden (when name true)}]
|
||||
(d/name name)]
|
||||
|
||||
:separator
|
||||
[:hr {:key (weak-key option) :class (stl/css :option-separator)}]
|
||||
|
||||
:empty
|
||||
[:li {:key (weak-key option) :class (stl/css :option-empty) :role "presentation"}
|
||||
(get option :label)]
|
||||
|
||||
;; Token option
|
||||
:token
|
||||
[:> token-option* {:selected (= id selected)
|
||||
:key (weak-key option)
|
||||
:id id
|
||||
:name name
|
||||
:resolved (get option :resolved-value)
|
||||
:ref ref
|
||||
:role "option"
|
||||
:focused (= id focused)
|
||||
:on-click on-click}]
|
||||
|
||||
;; Normal option
|
||||
[:> option* {:selected (= id selected)
|
||||
:key (weak-key option)
|
||||
:id id
|
||||
:label (get option :label)
|
||||
:aria-label (get option :aria-label)
|
||||
:icon (get option :icon)
|
||||
:ref ref
|
||||
:role "option"
|
||||
:focused (= id focused)
|
||||
:dimmed (true? (:dimmed option))
|
||||
:on-click on-click}]))))
|
||||
|
||||
(def ^:private schema:options-dropdown
|
||||
[:map
|
||||
[:ref {:optional true} fn?]
|
||||
@ -142,4 +87,4 @@
|
||||
[:hr {:class (stl/css :option-separator)}])
|
||||
|
||||
(for [option options-blank]
|
||||
(render-option option ref on-click selected focused))])]))
|
||||
(render-option option ref on-click selected focused))])]))
|
||||
@ -28,6 +28,10 @@
|
||||
overflow: hidden auto;
|
||||
z-index: var(--z-index-dropdown);
|
||||
box-shadow: 0 0 $sz-12 0 var(--color-shadow-dark);
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.left-align {
|
||||
@ -42,21 +46,3 @@
|
||||
border: $b-1 solid var(--options-dropdown-border-color);
|
||||
margin-block: var(--sp-xs) var(--sp-xs);
|
||||
}
|
||||
|
||||
.group-option,
|
||||
.option-empty {
|
||||
@include use-typography("body-small");
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--sp-xs);
|
||||
color: var(--color-foreground-secondary);
|
||||
padding-inline: var(--sp-s);
|
||||
block-size: var(--sp-xxxl);
|
||||
}
|
||||
|
||||
.option-empty {
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: 0 px2rem(40);
|
||||
}
|
||||
|
||||
@ -0,0 +1,67 @@
|
||||
;; 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.ds.controls.shared.render-option
|
||||
(:require-macros
|
||||
[app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.weak :refer [weak-key]]
|
||||
[app.main.ui.ds.controls.shared.option :refer [option*]]
|
||||
[app.main.ui.ds.controls.shared.token-option :refer [token-option*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defn render-option
|
||||
[option ref on-click selected focused]
|
||||
(let [id (get option :id)
|
||||
name (get option :name)
|
||||
type (get option :type)]
|
||||
|
||||
(mf/html
|
||||
(case type
|
||||
:group
|
||||
[:li {:class (stl/css :group-option)
|
||||
:role "presentation"
|
||||
:key (weak-key option)}
|
||||
[:> icon*
|
||||
{:icon-id i/arrow-down
|
||||
:size "m"
|
||||
:class (stl/css :option-check)
|
||||
:aria-hidden (when name true)}]
|
||||
(d/name name)]
|
||||
|
||||
:separator
|
||||
[:hr {:key (weak-key option) :class (stl/css :option-separator)}]
|
||||
|
||||
:empty
|
||||
[:li {:key (weak-key option) :class (stl/css :option-empty) :role "presentation"}
|
||||
(get option :label)]
|
||||
|
||||
;; Token option
|
||||
:token
|
||||
[:> token-option* {:selected (= id selected)
|
||||
:key (weak-key option)
|
||||
:id id
|
||||
:name name
|
||||
:resolved (get option :resolved-value)
|
||||
:ref ref
|
||||
:role "option"
|
||||
:focused (= id focused)
|
||||
:on-click on-click}]
|
||||
|
||||
;; Normal option
|
||||
[:> option* {:selected (= id selected)
|
||||
:key (weak-key option)
|
||||
:id id
|
||||
:label (get option :label)
|
||||
:aria-label (get option :aria-label)
|
||||
:icon (get option :icon)
|
||||
:ref ref
|
||||
:role "option"
|
||||
:focused (= id focused)
|
||||
:dimmed (true? (:dimmed option))
|
||||
:on-click on-click}]))))
|
||||
@ -0,0 +1,40 @@
|
||||
// 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/_borders.scss" as *;
|
||||
@use "ds/_sizes.scss" as *;
|
||||
@use "ds/typography.scss" as *;
|
||||
@use "ds/_utils.scss" as *;
|
||||
|
||||
.left-align {
|
||||
inset-inline-start: var(--dropdown-offset, 0);
|
||||
}
|
||||
|
||||
.right-align {
|
||||
inset-inline-end: var(--dropdown-offset, 0);
|
||||
}
|
||||
|
||||
.option-separator {
|
||||
border: $b-1 solid var(--options-dropdown-border-color);
|
||||
margin-block: var(--sp-xs) var(--sp-xs);
|
||||
}
|
||||
|
||||
.group-option,
|
||||
.option-empty {
|
||||
@include use-typography("body-small");
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--sp-xs);
|
||||
color: var(--color-foreground-secondary);
|
||||
padding-inline: var(--sp-s);
|
||||
block-size: var(--sp-xxxl);
|
||||
}
|
||||
|
||||
.option-check {
|
||||
color: var(--token-options-icon-fg-color);
|
||||
min-width: var(--sp-l);
|
||||
}
|
||||
@ -0,0 +1,149 @@
|
||||
;; 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.ds.controls.shared.searchable-options-dropdown
|
||||
(:require-macros
|
||||
[app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.main.ui.ds.controls.input :as ds]
|
||||
[app.main.ui.ds.controls.shared.dropdown-navigation :refer [use-dropdown-navigation]]
|
||||
[app.main.ui.ds.controls.shared.render-option :refer [render-option]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.workspace.tokens.management.forms.controls.utils :as csu]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.object :as obj]
|
||||
[app.util.timers :as ts]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(def ^:private schema:icon-list
|
||||
[:and :string
|
||||
[:fn {:error/message "invalid data: invalid icon"} #(contains? i/icon-list %)]])
|
||||
|
||||
(def schema:option
|
||||
"A schema for the option data structure expected to receive on props
|
||||
for the `options-dropdown*` component."
|
||||
[:map
|
||||
[:id {:optional true} :string]
|
||||
[:resolved-value {:optional true}
|
||||
[:or :int :string :float :map]]
|
||||
[:name {:optional true} :string]
|
||||
[:value {:optional true} :keyword]
|
||||
[:icon {:optional true} schema:icon-list]
|
||||
[:label {:optional true} :string]
|
||||
[:aria-label {:optional true} :string]])
|
||||
|
||||
(def ^:private schema:options-dropdown
|
||||
[:map
|
||||
[:ref {:optional true} fn?]
|
||||
[:class {:optional true} :string]
|
||||
[:wrapper-ref {:optional true} :any]
|
||||
[:placeholder {:optional true} :string]
|
||||
[:on-click fn?]
|
||||
[:options [:vector schema:option]]
|
||||
[:selected {:optional true} :any]
|
||||
[:align {:optional true} [:maybe [:enum :left :right]]]])
|
||||
|
||||
(mf/defc searchable-options-dropdown*
|
||||
{::mf/schema schema:options-dropdown}
|
||||
[{:keys [on-click options selected align class placeholder] :rest props}]
|
||||
(let [align (d/nilv align :left)
|
||||
|
||||
search* (mf/use-state "")
|
||||
search (deref search*)
|
||||
search-input-ref (mf/use-ref nil)
|
||||
|
||||
list-ref (mf/use-ref nil)
|
||||
nodes-ref (mf/use-ref nil)
|
||||
|
||||
filtered-options
|
||||
(mf/with-memo [options search]
|
||||
(if (seq search)
|
||||
(filterv (fn [opt]
|
||||
(or (not= :token (:type opt))
|
||||
(str/includes? (str/lower (:name opt ""))
|
||||
(str/lower search))))
|
||||
options)
|
||||
options))
|
||||
|
||||
focusable-ids
|
||||
(mf/with-memo [filtered-options]
|
||||
(mapv :id (csu/focusable-options filtered-options)))
|
||||
|
||||
on-search-change
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(reset! search* (dom/get-target-val event))))
|
||||
|
||||
set-option-ref
|
||||
(mf/use-fn
|
||||
(fn [node]
|
||||
(when node
|
||||
(let [state (d/nilv (mf/ref-val nodes-ref) #js {})
|
||||
id (dom/get-data node "id")]
|
||||
(mf/set-ref-val! nodes-ref (obj/set! state id node))
|
||||
(fn []
|
||||
(let [state (d/nilv (mf/ref-val nodes-ref) #js {})]
|
||||
(mf/set-ref-val! nodes-ref (obj/unset! state id))))))))
|
||||
|
||||
{:keys [focused-id focused-id* on-key-down]}
|
||||
(use-dropdown-navigation
|
||||
{:focusable-ids focusable-ids
|
||||
:nodes-ref nodes-ref
|
||||
:on-enter (fn [id]
|
||||
(when-let [node (obj/get (mf/ref-val nodes-ref) id)]
|
||||
(.click node)))
|
||||
:searchable true
|
||||
:search-input-ref search-input-ref
|
||||
:on-close nil})
|
||||
|
||||
on-click-inner
|
||||
(mf/use-fn
|
||||
(mf/deps on-click)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(on-click event)))
|
||||
|
||||
list-props
|
||||
(mf/spread-props props
|
||||
{:class [class (stl/css-case :option-list true
|
||||
:left-align (= align :left)
|
||||
:right-align (= align :right))]
|
||||
:ref list-ref
|
||||
:tab-index "-1"
|
||||
:role "listbox"
|
||||
:on-key-down on-key-down})]
|
||||
|
||||
(mf/with-effect []
|
||||
(ts/schedule 0
|
||||
#(if (mf/ref-val search-input-ref)
|
||||
(dom/focus! (mf/ref-val search-input-ref))
|
||||
(when-let [list (mf/ref-val list-ref)]
|
||||
(dom/focus! list)))))
|
||||
|
||||
(mf/with-effect [focused-id]
|
||||
(when (some? focused-id)
|
||||
(when-let [list (mf/ref-val list-ref)]
|
||||
(when-not (dom/active? list)
|
||||
(dom/focus! list)))
|
||||
(when-let [node (obj/get (mf/ref-val nodes-ref) focused-id)]
|
||||
(dom/scroll-into-view-if-needed! node {:block "nearest" :inline "nearest"}))))
|
||||
|
||||
[:> :ul list-props
|
||||
[:li {:class (stl/css :option-search)
|
||||
:role "presentation"}
|
||||
[:> ds/input* {:placeholder (or placeholder (tr "dashboard.search-placeholder"))
|
||||
:value search
|
||||
:ref search-input-ref
|
||||
:variant "comfortable"
|
||||
:on-change on-search-change
|
||||
:on-click #(reset! focused-id* nil)
|
||||
:on-key-down on-key-down}]]
|
||||
|
||||
(for [option filtered-options]
|
||||
(render-option option set-option-ref on-click-inner selected focused-id))]))
|
||||
@ -0,0 +1,47 @@
|
||||
// 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/_borders.scss" as *;
|
||||
@use "ds/_sizes.scss" as *;
|
||||
@use "ds/typography.scss" as *;
|
||||
@use "ds/_utils.scss" as *;
|
||||
|
||||
.option-list {
|
||||
--options-dropdown-icon-fg-color: var(--color-foreground-secondary);
|
||||
--options-dropdown-bg-color: var(--color-background-tertiary);
|
||||
--options-dropdown-outline-color: none;
|
||||
--options-dropdown-border-color: var(--color-background-quaternary);
|
||||
|
||||
position: absolute;
|
||||
inset-block-start: $sz-36;
|
||||
inline-size: var(--dropdown-width, 100%);
|
||||
transform: translateX(var(--dropdown-translate-distance, 0));
|
||||
background-color: var(--options-dropdown-bg-color);
|
||||
border-radius: $br-8;
|
||||
border: $b-1 solid var(--options-dropdown-border-color);
|
||||
padding-block: var(--sp-xs);
|
||||
margin-block-end: 0;
|
||||
max-block-size: $sz-400;
|
||||
overflow: hidden auto;
|
||||
z-index: var(--z-index-dropdown);
|
||||
box-shadow: 0 0 $sz-12 0 var(--color-shadow-dark);
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.left-align {
|
||||
inset-inline-start: var(--dropdown-offset, 0);
|
||||
}
|
||||
|
||||
.right-align {
|
||||
inset-inline-end: var(--dropdown-offset, 0);
|
||||
}
|
||||
|
||||
.option-search {
|
||||
padding: var(--sp-xs);
|
||||
}
|
||||
@ -18,7 +18,7 @@
|
||||
[:map
|
||||
[:id {:optiona true} :string]
|
||||
[:ref some?]
|
||||
[:resolved {:optional true} [:or :int :string :float]]
|
||||
[:resolved {:optional true} [:or :int :string :float :map]]
|
||||
[:name {:optional true} :string]
|
||||
[:on-click {:optional true} fn?]
|
||||
[:selected {:optional true} :boolean]
|
||||
@ -55,10 +55,11 @@
|
||||
:trigger-ref element-ref
|
||||
:id (dm/str id "-name")
|
||||
:class (stl/css :option-text)}
|
||||
;; Add ellipsis
|
||||
|
||||
[:span {:aria-labelledby (dm/str id "-name")
|
||||
:class (stl/css :option-name)
|
||||
:ref element-ref}
|
||||
name]]
|
||||
(when resolved
|
||||
(when (and resolved (not (map? resolved)))
|
||||
[:> :span {:class (stl/css :option-pill)}
|
||||
resolved])]))
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
@use "ds/_borders.scss" as *;
|
||||
@use "ds/_sizes.scss" as *;
|
||||
@use "ds/typography.scss" as *;
|
||||
@use "ds/mixins.scss" as *;
|
||||
|
||||
.token-option {
|
||||
--token-options-fg-color: var(--color-foreground-primary);
|
||||
@ -78,3 +79,7 @@
|
||||
color: var(--token-options-icon-fg-color);
|
||||
min-width: var(--sp-l);
|
||||
}
|
||||
|
||||
.option-name {
|
||||
@include text-ellipsis;
|
||||
}
|
||||
|
||||
@ -97,5 +97,5 @@
|
||||
:tooltip-placement tooltip-placement
|
||||
:icon i/broken-link
|
||||
:ref token-detach-btn-ref
|
||||
:aria-label (tr "ds.inputs.token-field.detach-token")
|
||||
:aria-label (tr "token-actions.detach-token")
|
||||
:on-click detach-token}])]]))
|
||||
|
||||
@ -10,26 +10,30 @@
|
||||
[app.common.data :as d]
|
||||
[app.common.types.text :as txt]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.shortcuts :as sc]
|
||||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.data.workspace.tokens.application :as dwta]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.data.workspace.wasm-text :as dwwt]
|
||||
[app.main.features :as features]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]]
|
||||
[app.main.ui.components.title-bar :refer [title-bar*]]
|
||||
[app.main.ui.context :as ctx]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.controls.radio-buttons :refer [radio-buttons*]]
|
||||
[app.main.ui.ds.controls.shared.searchable-options-dropdown :refer [searchable-options-dropdown*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.hooks :as hooks]
|
||||
[app.main.ui.icons :as deprecated-icon]
|
||||
[app.main.ui.workspace.sidebar.options.menus.typography :refer [text-options
|
||||
typography-entry]]
|
||||
[app.main.ui.workspace.sidebar.options.menus.token-typography-row :refer [token-typography-row*]]
|
||||
[app.main.ui.workspace.sidebar.options.menus.typography :refer [text-options typography-entry]]
|
||||
[app.main.ui.workspace.tokens.management.forms.controls.utils :as csu]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.object :as obj]
|
||||
[app.util.text.content :as content]
|
||||
[app.util.text.ui :as txu]
|
||||
[app.util.timers :as ts]
|
||||
@ -37,70 +41,68 @@
|
||||
[potok.v2.core :as ptk]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc text-align-options
|
||||
[{:keys [values on-change on-blur] :as props}]
|
||||
(let [{:keys [text-align]} values
|
||||
handle-change
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Sub-components
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(mf/defc text-align-options*
|
||||
[{:keys [values on-change on-blur]}]
|
||||
(let [handle-change
|
||||
(mf/use-fn
|
||||
(mf/deps on-change on-blur)
|
||||
(fn [value]
|
||||
(on-change {:text-align value})
|
||||
(when (some? on-blur) (on-blur))))]
|
||||
|
||||
;; --- Align
|
||||
[:div {:class (stl/css :align-options)}
|
||||
[:& radio-buttons {:selected text-align
|
||||
:on-change handle-change
|
||||
:name "align-text-options"}
|
||||
[:& radio-button {:value "left"
|
||||
:id "text-align-left"
|
||||
:title (tr "workspace.options.text-options.text-align-left")
|
||||
:icon i/text-align-left}]
|
||||
[:& radio-button {:value "center"
|
||||
:id "text-align-center"
|
||||
:title (tr "workspace.options.text-options.text-align-center")
|
||||
:icon i/text-align-center}]
|
||||
[:& radio-button {:value "right"
|
||||
:id "text-align-right"
|
||||
:title (tr "workspace.options.text-options.text-align-right")
|
||||
:icon i/text-align-right}]
|
||||
[:& radio-button {:value "justify"
|
||||
:id "text-align-justify"
|
||||
:title (tr "workspace.options.text-options.text-align-justify")
|
||||
:icon i/text-justify}]]]))
|
||||
[:> radio-buttons* {:selected (:text-align values)
|
||||
:on-change handle-change
|
||||
:name "align-text-options"
|
||||
:options [{:value "left"
|
||||
:id "text-align-left"
|
||||
:label (tr "workspace.options.text-options.text-align-left")
|
||||
:icon i/text-align-left}
|
||||
{:value "center"
|
||||
:id "text-align-center"
|
||||
:label (tr "workspace.options.text-options.text-align-center")
|
||||
:icon i/text-align-center}
|
||||
{:value "right"
|
||||
:id "text-align-right"
|
||||
:label (tr "workspace.options.text-options.text-align-right")
|
||||
:icon i/text-align-right}
|
||||
{:value "justify"
|
||||
:id "text-align-justify"
|
||||
:label (tr "workspace.options.text-options.text-align-justify")
|
||||
:icon i/text-justify}]}]]))
|
||||
|
||||
(mf/defc text-direction-options*
|
||||
[{:keys [values on-change on-blur]}]
|
||||
(let [direction (:text-direction values)
|
||||
|
||||
(mf/defc text-direction-options
|
||||
[{:keys [values on-change on-blur] :as props}]
|
||||
(let [direction (:text-direction values)
|
||||
handle-change
|
||||
(mf/use-fn
|
||||
(mf/deps on-change on-blur direction)
|
||||
(fn [value]
|
||||
(let [dir (if (= value direction)
|
||||
"none"
|
||||
value)]
|
||||
(on-change {:text-direction dir})
|
||||
(when (some? on-blur) (on-blur)))))]
|
||||
(on-change {:text-direction (if (= value direction) "none" value)})
|
||||
(when (some? on-blur) (on-blur))))]
|
||||
|
||||
[:div {:class (stl/css :text-direction-options)}
|
||||
[:& radio-buttons {:selected direction
|
||||
:on-change handle-change
|
||||
:name "text-direction-options"}
|
||||
[:& radio-button {:value "ltr"
|
||||
:type "checkbox"
|
||||
:id "ltr-text-direction"
|
||||
:title (tr "workspace.options.text-options.direction-ltr")
|
||||
:icon i/text-ltr}]
|
||||
[:& radio-button {:value "rtl"
|
||||
:type "checkbox"
|
||||
:id "rtl-text-direction"
|
||||
:title (tr "workspace.options.text-options.direction-rtl")
|
||||
:icon i/text-rtl}]]]))
|
||||
[:> radio-buttons* {:selected direction
|
||||
:on-change handle-change
|
||||
:name "text-direction-options"
|
||||
:options [{:value "ltr"
|
||||
:id "ltr-text-direction"
|
||||
:label (tr "workspace.options.text-options.direction-ltr")
|
||||
:icon i/text-ltr}
|
||||
{:value "rtl"
|
||||
:id "rtl-text-direction"
|
||||
:label (tr "workspace.options.text-options.direction-rtl")
|
||||
:icon i/text-rtl}]}]]))
|
||||
|
||||
(mf/defc vertical-align*
|
||||
[{:keys [values on-change on-blur]}]
|
||||
(let [vertical-align (or (:vertical-align values) "top")
|
||||
|
||||
(mf/defc vertical-align
|
||||
[{:keys [values on-change on-blur] :as props}]
|
||||
(let [{:keys [vertical-align]} values
|
||||
vertical-align (or vertical-align "top")
|
||||
handle-change
|
||||
(mf/use-fn
|
||||
(mf/deps on-change on-blur)
|
||||
@ -109,123 +111,223 @@
|
||||
(when (some? on-blur) (on-blur))))]
|
||||
|
||||
[:div {:class (stl/css :vertical-align-options)}
|
||||
[:& radio-buttons {:selected vertical-align
|
||||
:on-change handle-change
|
||||
:name "vertical-align-text-options"}
|
||||
[:& radio-button {:value "top"
|
||||
:id "vertical-text-align-top"
|
||||
:title (tr "workspace.options.text-options.align-top")
|
||||
:icon i/text-top}]
|
||||
[:& radio-button {:value "center"
|
||||
:id "vertical-text-align-center"
|
||||
:title (tr "workspace.options.text-options.align-middle")
|
||||
:icon i/text-middle}]
|
||||
[:& radio-button {:value "bottom"
|
||||
:id "vertical-text-align-bottom"
|
||||
:title (tr "workspace.options.text-options.align-bottom")
|
||||
:icon i/text-bottom}]]]))
|
||||
|
||||
(mf/defc grow-options
|
||||
[{:keys [ids values on-blur] :as props}]
|
||||
(let [grow-type (:grow-type values)
|
||||
[:> radio-buttons* {:selected vertical-align
|
||||
:on-change handle-change
|
||||
:name "vertical-align-text-options"
|
||||
:options [{:value "top"
|
||||
:id "vertical-text-align-top"
|
||||
:label (tr "workspace.options.text-options.align-top")
|
||||
:icon i/text-top}
|
||||
{:value "center"
|
||||
:id "vertical-text-align-center"
|
||||
:label (tr "workspace.options.text-options.align-middle")
|
||||
:icon i/text-middle}
|
||||
{:value "bottom"
|
||||
:id "vertical-text-align-bottom"
|
||||
:label (tr "workspace.options.text-options.align-bottom")
|
||||
:icon i/text-bottom}]}]]))
|
||||
|
||||
(mf/defc grow-options*
|
||||
[{:keys [ids values on-blur on-change]}]
|
||||
(let [grow-type (:grow-type values)
|
||||
editor-instance (mf/deref refs/workspace-editor)
|
||||
|
||||
handle-change-grow
|
||||
(mf/use-fn
|
||||
(mf/deps ids on-blur editor-instance)
|
||||
(fn [value]
|
||||
(on-blur)
|
||||
(let [uid (js/Symbol)
|
||||
grow-type (keyword value)]
|
||||
(st/emit! (dwu/start-undo-transaction uid))
|
||||
(when (features/active-feature? @st/state "text-editor/v2")
|
||||
(let [content (when editor-instance
|
||||
(content/dom->cljs (dwt/get-editor-root editor-instance)))]
|
||||
(when (some? content)
|
||||
(st/emit! (dwt/v2-update-text-shape-content (first ids) content :finalize? true)))))
|
||||
(st/emit! (dwsh/update-shapes ids #(assoc % :grow-type grow-type)))
|
||||
|
||||
(when (features/active-feature? @st/state "render-wasm/v1")
|
||||
(st/emit! (dwwt/resize-wasm-text-all ids)))
|
||||
;; We asynchronously commit so every sychronous event is resolved first and inside the transaction
|
||||
(ts/schedule #(st/emit! (dwu/commit-undo-transaction uid))))
|
||||
(when (some? on-blur) (on-blur))))]
|
||||
|
||||
[:div {:class (stl/css :grow-options)}
|
||||
[:& radio-buttons {:selected (d/name grow-type)
|
||||
:on-change handle-change-grow
|
||||
:name "grow-text-options"}
|
||||
[:& radio-button {:value "fixed"
|
||||
:id "text-fixed-grow"
|
||||
:title (tr "workspace.options.text-options.grow-fixed")
|
||||
:icon i/text-fixed}]
|
||||
[:& radio-button {:value "auto-width"
|
||||
:id "text-auto-width-grow"
|
||||
:title (tr "workspace.options.text-options.grow-auto-width")
|
||||
:icon i/text-auto-width}]
|
||||
[:& radio-button {:value "auto-height"
|
||||
:id "text-auto-height-grow"
|
||||
:title (tr "workspace.options.text-options.grow-auto-height")
|
||||
:icon i/text-auto-height}]]]))
|
||||
|
||||
(mf/defc text-decoration-options
|
||||
[{:keys [values on-change on-blur] :as props}]
|
||||
(let [text-decoration (or (:text-decoration values) "none")
|
||||
handle-change
|
||||
(mf/use-fn
|
||||
(mf/deps on-change on-blur text-decoration)
|
||||
(mf/deps ids on-blur on-change editor-instance)
|
||||
(fn [value]
|
||||
(let [decoration (if (= value text-decoration)
|
||||
"none"
|
||||
value)]
|
||||
(on-change {:text-decoration decoration})
|
||||
(when (some? on-blur) (on-blur)))))]
|
||||
(on-change {:grow-type (keyword value)})
|
||||
(when (some? on-blur)
|
||||
(on-blur))))]
|
||||
|
||||
[:div {:class (stl/css :grow-options)}
|
||||
[:> radio-buttons* {:selected (d/name grow-type)
|
||||
:on-change handle-change
|
||||
:name "grow-text-options"
|
||||
:options [{:value "fixed"
|
||||
:id "text-fixed-grow"
|
||||
:label (tr "workspace.options.text-options.grow-fixed")
|
||||
:icon i/text-fixed}
|
||||
{:value "auto-width"
|
||||
:id "text-auto-width-grow"
|
||||
:label (tr "workspace.options.text-options.grow-auto-width")
|
||||
:icon i/text-auto-width}
|
||||
{:value "auto-height"
|
||||
:id "text-auto-height-grow"
|
||||
:label (tr "workspace.options.text-options.grow-auto-height")
|
||||
:icon i/text-auto-height}]}]]))
|
||||
|
||||
(mf/defc text-decoration-options*
|
||||
[{:keys [values on-change on-blur token-applied]}]
|
||||
(let [token-row (contains? cf/flags :token-typography-row)
|
||||
text-decoration (some-> (:text-decoration values) d/name)
|
||||
handle-change
|
||||
(mf/use-fn
|
||||
(mf/deps on-change on-blur)
|
||||
(fn [value]
|
||||
(on-change {:text-decoration value})
|
||||
(when (some? on-blur)
|
||||
(on-blur))))]
|
||||
|
||||
[:div {:class (stl/css :text-decoration-options)}
|
||||
[:& radio-buttons {:selected text-decoration
|
||||
:on-change handle-change
|
||||
:name "text-decoration-options"}
|
||||
[:& radio-button {:value "underline"
|
||||
:type "checkbox"
|
||||
:id "underline-text-decoration"
|
||||
:title (tr "workspace.options.text-options.underline" (sc/get-tooltip :underline))
|
||||
:icon i/text-underlined}]
|
||||
[:& radio-button {:value "line-through"
|
||||
:type "checkbox"
|
||||
:id "line-through-text-decoration"
|
||||
:title (tr "workspace.options.text-options.strikethrough" (sc/get-tooltip :line-through))
|
||||
:icon i/text-stroked}]]]))
|
||||
[:> radio-buttons* {:selected (if (= text-decoration "none")
|
||||
nil
|
||||
text-decoration)
|
||||
:on-change handle-change
|
||||
:name "text-decoration-options"
|
||||
:disabled (and token-row (some? token-applied))
|
||||
:allow-empty true
|
||||
:options [{:value "underline"
|
||||
:id "underline-text-decoration"
|
||||
:disabled (and token-row (some? token-applied))
|
||||
:label (tr "workspace.options.text-options.underline" (sc/get-tooltip :underline))
|
||||
:icon i/text-underlined}
|
||||
{:value "line-through"
|
||||
:id "line-through-text-decoration"
|
||||
:disabled (and token-row (some? token-applied))
|
||||
:label (tr "workspace.options.text-options.strikethrough" (sc/get-tooltip :line-through))
|
||||
:icon i/text-stroked}]}]]))
|
||||
|
||||
(mf/defc text-menu
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [ids type values] :as props}]
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Helpers
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(let [file-id (mf/use-ctx ctx/current-file-id)
|
||||
typographies (mf/deref refs/workspace-file-typography)
|
||||
libraries (mf/deref refs/files)
|
||||
(defn- get-option-by-name [options name]
|
||||
(let [options (if (delay? options) (deref options) options)]
|
||||
(d/seek #(= name (get % :name)) options)))
|
||||
|
||||
(defn- resolve-delay [tokens]
|
||||
(if (delay? tokens) @tokens tokens))
|
||||
|
||||
(defn- find-token-by-id [tokens id]
|
||||
(->> (:typography tokens)
|
||||
(d/seek #(= (:id %) (uuid/uuid id)))))
|
||||
|
||||
(defn- check-props [n-props o-props]
|
||||
(and (identical? (unchecked-get n-props "ids")
|
||||
(unchecked-get o-props "ids"))
|
||||
(identical? (unchecked-get n-props "appliedTokens")
|
||||
(unchecked-get o-props "appliedTokens"))
|
||||
(identical? (unchecked-get n-props "values")
|
||||
(unchecked-get o-props "values"))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Main component
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(mf/defc text-menu*
|
||||
{::mf/wrap [#(mf/memo' % check-props)]}
|
||||
[{:keys [ids type values applied-tokens]}]
|
||||
|
||||
(let [file-id (mf/use-ctx ctx/current-file-id)
|
||||
typographies (mf/deref refs/workspace-file-typography)
|
||||
editor-instance (mf/deref refs/workspace-editor)
|
||||
libraries (mf/deref refs/files)
|
||||
token-row (contains? cf/flags :token-typography-row)
|
||||
|
||||
;; --- UI state
|
||||
menu-state* (mf/use-state {:main-menu true
|
||||
:more-options false})
|
||||
menu-state (deref menu-state*)
|
||||
main-menu-open? (:main-menu menu-state)
|
||||
more-options-open? (:more-options menu-state)
|
||||
|
||||
token-dropdown-open* (mf/use-state false)
|
||||
token-dropdown-open? (deref token-dropdown-open*)
|
||||
|
||||
;; --- Applied token
|
||||
applied-token-name (:typography applied-tokens)
|
||||
current-token-name* (mf/use-state applied-token-name)
|
||||
current-token-name (deref current-token-name*)
|
||||
|
||||
;; --- Available tokens
|
||||
active-tokens (mf/use-ctx ctx/active-tokens-by-type)
|
||||
typography-tokens (mf/with-memo [active-tokens] (csu/filter-tokens-for-input active-tokens :typography))
|
||||
|
||||
;; --- Dropdown
|
||||
listbox-id (mf/use-id)
|
||||
nodes-ref (mf/use-ref nil)
|
||||
dropdown-ref (mf/use-ref nil)
|
||||
|
||||
dropdown-options
|
||||
(mf/with-memo [typography-tokens]
|
||||
(csu/get-token-dropdown-options typography-tokens nil))
|
||||
|
||||
selected-token-id*
|
||||
(mf/use-state #(when current-token-name
|
||||
(:id (get-option-by-name dropdown-options current-token-name))))
|
||||
selected-token-id (deref selected-token-id*)
|
||||
|
||||
;; --- Typography
|
||||
typography-id (:typography-ref-id values)
|
||||
typography-file-id (:typography-ref-file values)
|
||||
|
||||
typography
|
||||
(mf/with-memo [typography-id typography-file-id file-id libraries]
|
||||
(cond
|
||||
(and typography-id
|
||||
(not= typography-id :multiple)
|
||||
(not= typography-file-id file-id))
|
||||
(-> (get-in libraries [typography-file-id :data :typographies typography-id])
|
||||
(assoc :file-id typography-file-id))
|
||||
|
||||
(and typography-id
|
||||
(not= typography-id :multiple)
|
||||
(= typography-file-id file-id))
|
||||
(get typographies typography-id)))
|
||||
|
||||
;; --- Helpers
|
||||
multiple? (->> values vals (d/seek #(= % :multiple)))
|
||||
|
||||
apply-token!
|
||||
(mf/use-fn
|
||||
(mf/deps ids typography-tokens)
|
||||
(fn [id]
|
||||
(let [token (find-token-by-id (resolve-delay typography-tokens) id)]
|
||||
(reset! selected-token-id* id)
|
||||
(reset! token-dropdown-open* false)
|
||||
(st/emit!
|
||||
(dwta/apply-token {:shape-ids ids
|
||||
:attributes #{:typography}
|
||||
:token token
|
||||
:on-update-shape dwta/update-typography})))))
|
||||
label (case type
|
||||
:multiple (tr "workspace.options.text-options.title-selection")
|
||||
:group (tr "workspace.options.text-options.title-group")
|
||||
(tr "workspace.options.text-options.title"))
|
||||
set-option-ref
|
||||
(mf/use-fn
|
||||
(fn [node]
|
||||
(let [state (d/nilv (mf/ref-val nodes-ref) #js {})
|
||||
id (dom/get-data node "id")]
|
||||
(mf/set-ref-val! nodes-ref (obj/set! state id node))
|
||||
(fn []
|
||||
(let [state (d/nilv (mf/ref-val nodes-ref) #js {})]
|
||||
(mf/set-ref-val! nodes-ref (obj/unset! state id)))))))
|
||||
|
||||
state* (mf/use-state {:main-menu true
|
||||
:more-options false})
|
||||
state (deref state*)
|
||||
main-menu-open? (:main-menu state)
|
||||
more-options-open? (:more-options state)
|
||||
|
||||
;; --- Toggles
|
||||
toggle-main-menu
|
||||
(mf/use-fn
|
||||
(mf/deps main-menu-open?)
|
||||
#(swap! state* assoc-in [:main-menu] (not main-menu-open?)))
|
||||
#(swap! menu-state* update :main-menu not))
|
||||
|
||||
toggle-more-options
|
||||
(mf/use-fn
|
||||
(mf/deps more-options-open?)
|
||||
#(swap! state* assoc-in [:more-options] (not more-options-open?)))
|
||||
#(swap! menu-state* update :more-options not))
|
||||
|
||||
typography-id (:typography-ref-id values)
|
||||
typography-file-id (:typography-ref-file values)
|
||||
toggle-token-dropdown
|
||||
(mf/use-fn
|
||||
#(swap! token-dropdown-open* not))
|
||||
|
||||
;; --- Event handlers
|
||||
on-option-click
|
||||
(mf/use-fn
|
||||
(mf/deps apply-token!)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(let [id (dom/get-data (dom/get-current-target event) "id")]
|
||||
(apply-token! id))))
|
||||
|
||||
emit-update!
|
||||
(mf/use-fn
|
||||
@ -241,42 +343,40 @@
|
||||
(fn [attrs]
|
||||
(emit-update! ids attrs)))
|
||||
|
||||
typography
|
||||
(mf/with-memo [values file-id libraries]
|
||||
(cond
|
||||
(and typography-id
|
||||
(not= typography-id :multiple)
|
||||
(not= typography-file-id file-id))
|
||||
(-> libraries
|
||||
(get-in [typography-file-id :data :typographies typography-id])
|
||||
(assoc :file-id typography-file-id))
|
||||
|
||||
(and typography-id
|
||||
(not= typography-id :multiple)
|
||||
(= typography-file-id file-id))
|
||||
(get typographies typography-id)))
|
||||
|
||||
on-convert-to-typography
|
||||
(fn [_]
|
||||
(let [set-values (-> (d/without-nils values)
|
||||
(select-keys
|
||||
(d/concat-vec txt/text-font-attrs
|
||||
txt/text-spacing-attrs
|
||||
txt/text-transform-attrs)))
|
||||
typography (merge txt/default-typography set-values)
|
||||
typography (dwt/generate-typography-name typography)
|
||||
id (uuid/next)]
|
||||
(st/emit! (dwl/add-typography (assoc typography :id id) false))
|
||||
(emit-update! ids
|
||||
{:typography-ref-id id
|
||||
:typography-ref-file file-id})))
|
||||
(mf/use-fn
|
||||
(mf/deps values ids file-id emit-update!)
|
||||
(fn [_]
|
||||
(let [set-values (-> (d/without-nils values)
|
||||
(select-keys (d/concat-vec txt/text-font-attrs
|
||||
txt/text-spacing-attrs
|
||||
txt/text-transform-attrs)))
|
||||
typography (-> (merge txt/default-typography set-values)
|
||||
(dwt/generate-typography-name))
|
||||
id (uuid/next)]
|
||||
(st/emit! (dwl/add-typography (assoc typography :id id) false))
|
||||
(emit-update! ids {:typography-ref-id id :typography-ref-file file-id}))))
|
||||
|
||||
on-grow-type-change
|
||||
(mf/use-fn
|
||||
(mf/deps ids editor-instance)
|
||||
(fn [{:keys [grow-type]}]
|
||||
(let [uid (js/Symbol)]
|
||||
(st/emit! (dwu/start-undo-transaction uid))
|
||||
(when (features/active-feature? @st/state "text-editor/v2")
|
||||
(let [content (when editor-instance
|
||||
(content/dom->cljs (dwt/get-editor-root editor-instance)))]
|
||||
(when (some? content)
|
||||
(st/emit! (dwt/v2-update-text-shape-content (first ids) content :finalize? true)))))
|
||||
(st/emit! (dwsh/update-shapes ids #(assoc % :grow-type grow-type)))
|
||||
(when (features/active-feature? @st/state "render-wasm/v1")
|
||||
(st/emit! (dwwt/resize-wasm-text-all ids)))
|
||||
(ts/schedule #(st/emit! (dwu/commit-undo-transaction uid))))))
|
||||
|
||||
handle-detach-typography
|
||||
(mf/use-fn
|
||||
(mf/deps on-change)
|
||||
(fn []
|
||||
(on-change {:typography-ref-file nil
|
||||
:typography-ref-id nil})))
|
||||
#(on-change {:typography-ref-file nil :typography-ref-id nil}))
|
||||
|
||||
handle-change-typography
|
||||
(mf/use-fn
|
||||
@ -284,74 +384,124 @@
|
||||
(fn [changes]
|
||||
(st/emit! (dwl/update-typography (merge typography changes) file-id))))
|
||||
|
||||
detach-token
|
||||
(mf/use-fn
|
||||
(fn [token-name]
|
||||
(st/emit! (dwta/unapply-token {:token-name token-name
|
||||
:attributes #{:typography}
|
||||
:shape-ids ids}))))
|
||||
|
||||
expand-stream
|
||||
(mf/with-memo []
|
||||
(->> st/stream
|
||||
(rx/filter (ptk/type? :expand-text-more-options))))
|
||||
(->> st/stream (rx/filter (ptk/type? :expand-text-more-options))))
|
||||
|
||||
multiple? (->> values vals (d/seek #(= % :multiple)))
|
||||
on-text-blur
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
(ts/schedule
|
||||
100
|
||||
(fn []
|
||||
(when (not= "INPUT" (-> (dom/get-active) dom/get-tag-name))
|
||||
(dom/focus! (txu/get-text-editor-content)))))))
|
||||
|
||||
common-props
|
||||
(mf/spread-props {} {:values values
|
||||
:on-change on-change
|
||||
:on-blur on-text-blur})]
|
||||
|
||||
opts #js {:ids ids
|
||||
:values values
|
||||
:on-change on-change
|
||||
:show-recent true
|
||||
:on-blur
|
||||
(fn []
|
||||
(ts/schedule
|
||||
100
|
||||
(fn []
|
||||
(when (not= "INPUT" (-> (dom/get-active) (dom/get-tag-name)))
|
||||
(let [node (txu/get-text-editor-content)]
|
||||
(dom/focus! node))))))}]
|
||||
(hooks/use-stream
|
||||
expand-stream
|
||||
#(swap! state* assoc-in [:more-options] true))
|
||||
#(swap! menu-state* assoc :more-options true))
|
||||
|
||||
[:div {:class (stl/css :element-set)}
|
||||
(mf/with-effect [applied-token-name]
|
||||
(reset! current-token-name* applied-token-name))
|
||||
|
||||
(mf/with-effect [applied-token-name dropdown-options]
|
||||
(reset! selected-token-id*
|
||||
(when applied-token-name
|
||||
(:id (get-option-by-name dropdown-options applied-token-name)))))
|
||||
|
||||
(mf/with-effect [token-dropdown-open?]
|
||||
(when token-dropdown-open?
|
||||
(ts/schedule 0 #(some-> (mf/ref-val dropdown-ref) dom/focus!))))
|
||||
|
||||
[:section {:class (stl/css :element-set)
|
||||
:aria-label (tr "workspace.options.text-options.text-section")}
|
||||
[:div {:class (stl/css :element-title)}
|
||||
[:> title-bar* {:collapsable true
|
||||
:collapsed (not main-menu-open?)
|
||||
:on-collapsed toggle-main-menu
|
||||
:title label
|
||||
:class (stl/css :title-spacing-text)}
|
||||
(when (and (not typography) (not multiple?))
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-label (tr "labels.options")
|
||||
:on-click on-convert-to-typography
|
||||
:icon i/add}])]]
|
||||
[:*
|
||||
(when (and token-row (some? typography-tokens) (not typography))
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-label (tr "ds.inputs.numeric-input.open-token-list-dropdown")
|
||||
:on-click toggle-token-dropdown
|
||||
:tooltip-placement "top-left"
|
||||
:icon i/tokens}])
|
||||
(when (and (not typography) (not multiple?) (not applied-token-name))
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-label (tr "workspace.options.convert-to-typography")
|
||||
:on-click on-convert-to-typography
|
||||
:tooltip-placement "top-left"
|
||||
:icon i/add}])]]]
|
||||
|
||||
(when main-menu-open?
|
||||
[:div {:class (stl/css :element-content)}
|
||||
(cond
|
||||
(and token-row current-token-name)
|
||||
[:> token-typography-row* {:token-name current-token-name
|
||||
:detach-token detach-token
|
||||
:active-tokens (resolve-delay typography-tokens)}]
|
||||
|
||||
typography
|
||||
[:& typography-entry {:file-id typography-file-id
|
||||
[:& typography-entry {:file-id typography-file-id
|
||||
:typography typography
|
||||
:local? (= typography-file-id file-id)
|
||||
:on-detach handle-detach-typography
|
||||
:on-change handle-change-typography}]
|
||||
:local? (= typography-file-id file-id)
|
||||
:on-detach handle-detach-typography
|
||||
:on-change handle-change-typography}]
|
||||
|
||||
(= typography-id :multiple)
|
||||
[:div {:class (stl/css :multiple-typography)}
|
||||
[:span {:class (stl/css :multiple-text)} (tr "workspace.libraries.text.multiple-typography")]
|
||||
[:div {:class (stl/css :multiple-typography-button)
|
||||
:on-click handle-detach-typography
|
||||
:title (tr "workspace.libraries.text.multiple-typography-tooltip")}
|
||||
deprecated-icon/detach]]
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-label (tr "workspace.libraries.text.multiple-typography-tooltip")
|
||||
:on-click handle-detach-typography
|
||||
:icon i/detach}]]
|
||||
|
||||
:else
|
||||
[:> text-options opts])
|
||||
[:> text-options #js {:ids ids
|
||||
:values values
|
||||
:on-change on-change
|
||||
:show-recent true
|
||||
:on-blur
|
||||
(fn []
|
||||
(ts/schedule
|
||||
100
|
||||
(fn []
|
||||
(when (not= "INPUT" (-> (dom/get-active) dom/get-tag-name))
|
||||
(dom/focus! (txu/get-text-editor-content))))))}])
|
||||
|
||||
[:div {:class (stl/css :text-align-options)}
|
||||
[:> text-align-options opts]
|
||||
[:> grow-options opts]
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-label (tr "labels.options")
|
||||
[:> text-align-options* common-props]
|
||||
[:> grow-options* (mf/spread-props common-props {:on-change on-grow-type-change})]
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-label (tr "labels.options")
|
||||
:data-testid "text-align-options-button"
|
||||
:on-click toggle-more-options
|
||||
:icon i/menu}]]
|
||||
:on-click toggle-more-options
|
||||
:icon i/menu}]]
|
||||
|
||||
(when more-options-open?
|
||||
[:div {:class (stl/css :text-decoration-options)}
|
||||
[:> vertical-align opts]
|
||||
[:> text-decoration-options opts]
|
||||
[:> text-direction-options opts]])])]))
|
||||
[:div {:class (stl/css :text-decoration-options)}
|
||||
[:> vertical-align* common-props]
|
||||
[:> text-decoration-options* (mf/spread-props common-props {:token-applied current-token-name})]
|
||||
[:> text-direction-options* common-props]])])
|
||||
|
||||
(when (and token-row token-dropdown-open?)
|
||||
[:> searchable-options-dropdown* {:on-click on-option-click
|
||||
:id listbox-id
|
||||
:options (resolve-delay dropdown-options)
|
||||
:selected selected-token-id
|
||||
:align "right"
|
||||
:ref set-option-ref}])]))
|
||||
@ -9,6 +9,8 @@
|
||||
|
||||
.element-set {
|
||||
@include sidebar.option-grid-structure;
|
||||
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.element-title {
|
||||
@ -16,10 +18,9 @@
|
||||
}
|
||||
|
||||
.element-content {
|
||||
grid-column: span 8;
|
||||
|
||||
@include deprecated.flexColumn;
|
||||
|
||||
grid-column: span 8;
|
||||
margin-top: deprecated.$s-4;
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,87 @@
|
||||
;; 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.sidebar.options.menus.token-typography-row
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
||||
[app.main.ui.ds.tooltip :refer [tooltip*]]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc resolved-value-tooltip*
|
||||
{::mf/private true}
|
||||
[{:keys [token-name resolved-value]}]
|
||||
[:*
|
||||
[:span (dm/str (tr "workspace.tokens.token-name") ": ")]
|
||||
[:span {:class (stl/css :token-name-tooltip)} token-name]
|
||||
[:div
|
||||
[:span (tr "inspect.tabs.styles.token-resolved-value")]
|
||||
[:ul
|
||||
(for [[k v] resolved-value]
|
||||
[:li {:key (d/name k)}
|
||||
[:span {:class (stl/css :resolved-key)} (str "- " (d/name k) ": ")]
|
||||
[:span {:class (stl/css :resolved-value)}
|
||||
(if (sequential? v)
|
||||
(str/join ", " (map #(dm/str "\"" % "\"") v))
|
||||
(dm/str v))]])]]])
|
||||
|
||||
(mf/defc token-typography-row*
|
||||
[{:keys [token-name active-tokens detach-token] :rest props}]
|
||||
(let [element-ref (mf/use-ref nil)
|
||||
id (mf/use-id)
|
||||
|
||||
token (->> (:typography active-tokens)
|
||||
(d/seek #(= (:name %) token-name)))
|
||||
|
||||
has-errors (some? (:errors token))
|
||||
display-name (or (:name token) token-name)
|
||||
|
||||
resolved-value (:resolved-value token)
|
||||
not-active (or (nil? token)
|
||||
(empty? (:typography active-tokens)))
|
||||
on-detach
|
||||
(mf/use-fn
|
||||
(mf/deps display-name)
|
||||
(fn []
|
||||
(detach-token display-name)))
|
||||
|
||||
tooltip-content (cond
|
||||
not-active
|
||||
(tr "not-active-token.no-name")
|
||||
has-errors
|
||||
(tr "options.deleted-token")
|
||||
:else
|
||||
(mf/html [:> resolved-value-tooltip* {:token-name token-name
|
||||
:resolved-value resolved-value}]))]
|
||||
|
||||
[:div {:class (stl/css-case :token-typography-row true
|
||||
:token-typography-row-with-errors has-errors
|
||||
:token-typography-row-not-active not-active)}
|
||||
(when (or has-errors not-active)
|
||||
[:div {:class (stl/css :error-dot)}])
|
||||
[:> icon* {:icon-id i/text-typography
|
||||
:class (stl/css :icon)}]
|
||||
[:> tooltip* {:content tooltip-content
|
||||
:trigger-ref element-ref
|
||||
:class (stl/css :token-tooltip)
|
||||
:id id}
|
||||
|
||||
[:span {:aria-labelledby (dm/str id)
|
||||
:class (stl/css :token-name)
|
||||
:ref element-ref}
|
||||
display-name]]
|
||||
|
||||
[:> icon-button* {:variant "action"
|
||||
:aria-label (tr "token-actions.detach-token")
|
||||
:tooltip-class (stl/css :detach-button)
|
||||
:tooltip-placement "top-left"
|
||||
:on-click on-detach
|
||||
:icon i/detach}]]))
|
||||
@ -0,0 +1,101 @@
|
||||
// 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 t;
|
||||
@use "ds/_sizes.scss" as *;
|
||||
@use "ds/_borders.scss" as *;
|
||||
@use "ds/mixins.scss" as *;
|
||||
@use "ds/_utils.scss" as *;
|
||||
|
||||
.token-typography-row {
|
||||
--token-typography-row-background-color: var(--color-background-tertiary);
|
||||
--token-typography-row-foreground-color: var(--color-token-foreground);
|
||||
--token-typography-row-border-color: var(--color-token-border);
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
gap: var(--sp-xs);
|
||||
block-size: $sz-32;
|
||||
min-inline-size: 0;
|
||||
inline-size: 100%;
|
||||
padding: var(--sp-s);
|
||||
margin-inline-end: 0;
|
||||
background: var(--token-typography-row-background-color);
|
||||
border: $b-1 solid var(--token-typography-row-border-color);
|
||||
border-radius: $br-8;
|
||||
|
||||
&:hover {
|
||||
--token-typography-row-background-color: var(--color-token-background);
|
||||
--token-typography-row-foreground-color: var(--color-foreground-primary);
|
||||
--token-typography-row-border-color: var(--color-token-accent);
|
||||
}
|
||||
}
|
||||
|
||||
.token-typography-row-with-errors,
|
||||
.token-typography-row-not-active {
|
||||
--token-typography-row-background-color: var(--color-background-primary);
|
||||
--token-typography-row-foreground-color: var(--color-foreground-secondary);
|
||||
--token-typography-row-border-color: var(--color-token-border);
|
||||
|
||||
&:hover {
|
||||
--token-typography-row-background-color: var(--color-background-primary);
|
||||
--token-typography-row-foreground-color: var(--color-foreground-secondary);
|
||||
--token-typography-row-border-color: var(--color-token-background);
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: block;
|
||||
min-inline-size: $sz-16;
|
||||
color: var(--token-typography-row-foreground-color);
|
||||
}
|
||||
|
||||
.token-name {
|
||||
@include t.use-typography("body-small");
|
||||
@include text-ellipsis;
|
||||
|
||||
color: var(--token-typography-row-foreground-color);
|
||||
block-size: $sz-32;
|
||||
flex: 1;
|
||||
line-height: $sz-32;
|
||||
}
|
||||
|
||||
.token-tooltip {
|
||||
min-inline-size: 0;
|
||||
inline-size: inherit;
|
||||
}
|
||||
|
||||
.token-name-tooltip {
|
||||
color: var(--color-foreground-primary);
|
||||
}
|
||||
|
||||
.detach-button {
|
||||
flex-shrink: 0;
|
||||
inline-size: 0;
|
||||
max-inline-size: 0;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.token-typography-row:hover .detach-button {
|
||||
inline-size: auto;
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
max-inline-size: $sz-32;
|
||||
}
|
||||
|
||||
.error-dot {
|
||||
inline-size: px2rem(4);
|
||||
block-size: px2rem(4);
|
||||
border-radius: 50%;
|
||||
background-color: var(--color-foreground-error);
|
||||
margin-inline-start: var(--sp-xs);
|
||||
position: absolute;
|
||||
inset-inline-end: px2rem(1);
|
||||
inset-block-start: px2rem(5);
|
||||
}
|
||||
@ -97,16 +97,16 @@
|
||||
token-name-ref (mf/use-ref nil)
|
||||
swatch-tooltip-content (cond
|
||||
not-active
|
||||
(tr "ds.inputs.token-field.no-active-color.token-option")
|
||||
(tr "not-active-token.no-name")
|
||||
has-errors
|
||||
(tr "color-row.token-color-row.deleted-token")
|
||||
(tr "options.deleted-token")
|
||||
:else
|
||||
(tr "workspace.tokens.resolved-value" resolved))
|
||||
name-tooltip-content (cond
|
||||
not-active
|
||||
(tr "ds.inputs.token-field.no-active-color.token-option")
|
||||
(tr "not-active-token.no-name")
|
||||
has-errors
|
||||
(tr "color-row.token-color-row.deleted-token")
|
||||
(tr "options.deleted-token")
|
||||
:else
|
||||
#(mf/html
|
||||
[:div
|
||||
@ -137,7 +137,7 @@
|
||||
[:div {:class (stl/css :token-actions)}
|
||||
[:> icon-button*
|
||||
{:variant "action"
|
||||
:aria-label (tr "ds.inputs.token-field.detach-token")
|
||||
:aria-label (tr "token-actions.detach-token")
|
||||
:on-click on-detach-token
|
||||
:icon i/detach}]
|
||||
[:> icon-button*
|
||||
|
||||
@ -102,7 +102,7 @@
|
||||
[stroke-ids stroke-values stroke-tokens]
|
||||
(get-attrs shapes objects :stroke)
|
||||
|
||||
[text-ids text-values]
|
||||
[text-ids text-values text-tokens]
|
||||
(get-attrs shapes objects :text)
|
||||
|
||||
[layout-item-ids layout-item-values]
|
||||
@ -171,7 +171,10 @@
|
||||
[:& blur-menu {:type type :ids blur-ids :values blur-values}])
|
||||
|
||||
(when-not (empty? text-ids)
|
||||
[:& ot/text-menu {:type type :ids text-ids :values text-values}])
|
||||
[:> ot/text-menu* {:type type
|
||||
:ids text-ids
|
||||
:values text-values
|
||||
:applied-tokens text-tokens}])
|
||||
|
||||
(when-not (empty? svg-values)
|
||||
[:& svg-attrs-menu {:ids ids :values svg-values}])
|
||||
|
||||
@ -385,7 +385,7 @@
|
||||
[layer-ids layer-values layer-tokens]
|
||||
(get-attrs shapes objects :layer)
|
||||
|
||||
[text-ids text-values]
|
||||
[text-ids text-values text-tokens]
|
||||
(get-attrs shapes objects :text)
|
||||
|
||||
[constraint-ids constraint-values]
|
||||
@ -478,7 +478,11 @@
|
||||
[:& constraints-menu {:ids constraint-ids :values constraint-values}])
|
||||
|
||||
(when-not (empty? text-ids)
|
||||
[:& ot/text-menu {:type type :ids text-ids :values text-values}])
|
||||
[:> ot/text-menu*
|
||||
{:type type
|
||||
:ids text-ids
|
||||
:values text-values
|
||||
:applied-tokens text-tokens}])
|
||||
|
||||
(when-not (empty? fill-ids)
|
||||
[:> fill/fill-menu* {:type type
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
[app.main.ui.workspace.sidebar.options.menus.measures :refer [measure-attrs measures-menu*]]
|
||||
[app.main.ui.workspace.sidebar.options.menus.shadow :refer [shadow-menu*]]
|
||||
[app.main.ui.workspace.sidebar.options.menus.stroke :refer [stroke-attrs stroke-menu]]
|
||||
[app.main.ui.workspace.sidebar.options.menus.text :refer [text-menu]]
|
||||
[app.main.ui.workspace.sidebar.options.menus.text :refer [text-menu*]]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc options*
|
||||
@ -162,9 +162,10 @@
|
||||
{:ids ids
|
||||
:values (select-keys shape constraint-attrs)}])
|
||||
|
||||
[:& text-menu
|
||||
[:> text-menu*
|
||||
{:ids ids
|
||||
:type type
|
||||
:applied-tokens applied-tokens
|
||||
:values text-values}]
|
||||
|
||||
[:> fill/fill-menu*
|
||||
|
||||
@ -208,7 +208,7 @@ msgid "branding-illustrations-marketing-pieces"
|
||||
msgstr "...Branding, Illustrationen, Marketingmaterialien, usw."
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108
|
||||
msgid "color-row.token-color-row.deleted-token"
|
||||
msgid "options.deleted-token"
|
||||
msgstr "Dieses Token existiert nicht oder wurde gelöscht."
|
||||
|
||||
#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35
|
||||
@ -1357,7 +1357,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "Token-Liste öffnen"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136
|
||||
msgid "ds.inputs.token-field.detach-token"
|
||||
msgid "token-actions.detach-token"
|
||||
msgstr "Token trennen"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:43, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:99, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:106
|
||||
|
||||
@ -197,7 +197,7 @@ msgid "branding-illustrations-marketing-pieces"
|
||||
msgstr "...branding, illustrations, marketing pieces, etc."
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108
|
||||
msgid "color-row.token-color-row.deleted-token"
|
||||
msgid "options.deleted-token"
|
||||
msgstr "This token does not exists or has been deleted."
|
||||
|
||||
#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35
|
||||
@ -1288,7 +1288,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "Open token list"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136
|
||||
msgid "ds.inputs.token-field.detach-token"
|
||||
msgid "token-actions.detach-token"
|
||||
msgstr "Detach token"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:43, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:99, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:106
|
||||
@ -1296,7 +1296,7 @@ msgid "ds.inputs.token-field.no-active-token-option"
|
||||
msgstr "{%s} is not in any active set or has an invalid value."
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs
|
||||
msgid "ds.inputs.token-field.no-active-color.token-option"
|
||||
msgid "not-active-token.no-name"
|
||||
msgstr "This token is not in any active set or has an invalid value."
|
||||
|
||||
#: src/app/main/data/auth.cljs:339
|
||||
@ -7366,6 +7366,14 @@ msgstr "Title case"
|
||||
msgid "workspace.options.text-options.underline"
|
||||
msgstr "Underline (%s)"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs
|
||||
msgid "workspace.options.convert-to-typography"
|
||||
msgstr "Create typography style"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs
|
||||
msgid "workspace.options.text-options.text-section"
|
||||
msgstr "Text section"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs
|
||||
#, unused
|
||||
msgid "workspace.options.text-options.uppercase"
|
||||
|
||||
@ -204,7 +204,7 @@ msgid "branding-illustrations-marketing-pieces"
|
||||
msgstr "diseño de marca, ilustraciones, piezas de marketing..."
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108
|
||||
msgid "color-row.token-color-row.deleted-token"
|
||||
msgid "options.deleted-token"
|
||||
msgstr "Este token no existe o ha sido borrado."
|
||||
|
||||
#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35
|
||||
@ -1278,7 +1278,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "Abrir lista de tokens"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136
|
||||
msgid "ds.inputs.token-field.detach-token"
|
||||
msgid "token-actions.detach-token"
|
||||
msgstr "Desvincular token"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:43, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:99, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:106
|
||||
@ -1286,7 +1286,7 @@ msgid "ds.inputs.token-field.no-active-token-option"
|
||||
msgstr "{%s} no está disponible en ningún set o tiene un valor inválido."
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs
|
||||
msgid "ds.inputs.token-field.no-active-color.token-option"
|
||||
msgid "not-active-token.no-name"
|
||||
msgstr "Este token no está disponible en ningún set o tiene un valor inválido."
|
||||
|
||||
#: src/app/main/data/auth.cljs:339
|
||||
@ -7281,6 +7281,14 @@ msgstr "Título"
|
||||
msgid "workspace.options.text-options.underline"
|
||||
msgstr "Subrayado (%s)"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs
|
||||
msgid "workspace.options.convert-to-typography"
|
||||
msgstr "Crear estilo de tipografía"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs
|
||||
msgid "workspace.options.text-options.text-section"
|
||||
msgstr "Sección de textos"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs
|
||||
#, unused
|
||||
msgid "workspace.options.text-options.uppercase"
|
||||
|
||||
@ -199,7 +199,7 @@ msgid "auth.work-email"
|
||||
msgstr "ایمیلِ کار"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108
|
||||
msgid "color-row.token-color-row.deleted-token"
|
||||
msgid "options.deleted-token"
|
||||
msgstr "این توکن وجود ندارد یا حذف شده است."
|
||||
|
||||
#: src/app/main/ui/workspace/libraries.cljs:323
|
||||
|
||||
@ -206,7 +206,7 @@ msgid "branding-illustrations-marketing-pieces"
|
||||
msgstr "...image de marque, illustrations, supports marketing, etc."
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108
|
||||
msgid "color-row.token-color-row.deleted-token"
|
||||
msgid "options.deleted-token"
|
||||
msgstr "Ce token n'existe pas ou a été supprimé."
|
||||
|
||||
#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35
|
||||
@ -1364,7 +1364,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "Ouvrir la liste des tokens"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136
|
||||
msgid "ds.inputs.token-field.detach-token"
|
||||
msgid "token-actions.detach-token"
|
||||
msgstr "Détacher le token"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:43, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:99, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:106
|
||||
|
||||
@ -208,7 +208,7 @@ msgid "branding-illustrations-marketing-pieces"
|
||||
msgstr "images de marque, illustrations, matériel de marketing..."
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108
|
||||
msgid "color-row.token-color-row.deleted-token"
|
||||
msgid "options.deleted-token"
|
||||
msgstr "Ce token n'existe pas ou a été supprimé."
|
||||
|
||||
#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35
|
||||
@ -1355,7 +1355,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "Ouvrir la liste de tokens"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136
|
||||
msgid "ds.inputs.token-field.detach-token"
|
||||
msgid "token-actions.detach-token"
|
||||
msgstr "Détacher du token"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:43, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:99, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:106
|
||||
|
||||
@ -194,7 +194,7 @@ msgid "branding-illustrations-marketing-pieces"
|
||||
msgstr "…מיתוג, איורים, חומרים שיווקיים ועוד."
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108
|
||||
msgid "color-row.token-color-row.deleted-token"
|
||||
msgid "options.deleted-token"
|
||||
msgstr "האסימון הזה לא קיים או שנמחק."
|
||||
|
||||
#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35
|
||||
@ -1182,7 +1182,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "פתיחת רשימת אסימונים"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136
|
||||
msgid "ds.inputs.token-field.detach-token"
|
||||
msgid "token-actions.detach-token"
|
||||
msgstr "ניתוק אסימון"
|
||||
|
||||
#: src/app/main/data/auth.cljs:339
|
||||
|
||||
@ -200,7 +200,7 @@ msgid "branding-illustrations-marketing-pieces"
|
||||
msgstr "...ब्रांडिंग, चित्रण, मार्केटिंग सामग्री आदि।"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108
|
||||
msgid "color-row.token-color-row.deleted-token"
|
||||
msgid "options.deleted-token"
|
||||
msgstr "यह token मौजूद नहीं है या हटा दिया गया है।"
|
||||
|
||||
#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35
|
||||
@ -1253,7 +1253,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "token सूची खोलें"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136
|
||||
msgid "ds.inputs.token-field.detach-token"
|
||||
msgid "token-actions.detach-token"
|
||||
msgstr "token अलग करें"
|
||||
|
||||
#: src/app/main/data/auth.cljs:339
|
||||
|
||||
@ -203,7 +203,7 @@ msgid "branding-illustrations-marketing-pieces"
|
||||
msgstr "…branding, illustrazione, materiali di marketing, etc."
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108
|
||||
msgid "color-row.token-color-row.deleted-token"
|
||||
msgid "options.deleted-token"
|
||||
msgstr "Questo token non esiste o è stato eliminato."
|
||||
|
||||
#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35
|
||||
@ -1350,7 +1350,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "Apri elenco token"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136
|
||||
msgid "ds.inputs.token-field.detach-token"
|
||||
msgid "token-actions.detach-token"
|
||||
msgstr "Scollega token"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:43, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:99, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:106
|
||||
|
||||
@ -191,7 +191,7 @@ msgid "branding-illustrations-marketing-pieces"
|
||||
msgstr "...브랜딩, 일러스트레이션, 마케팅 자료 등."
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108
|
||||
msgid "color-row.token-color-row.deleted-token"
|
||||
msgid "options.deleted-token"
|
||||
msgstr "이 토큰은 존재하지 않거나 삭제되었습니다."
|
||||
|
||||
#: src/app/main/ui/comments.cljs:530
|
||||
|
||||
@ -205,7 +205,7 @@ msgid "branding-illustrations-marketing-pieces"
|
||||
msgstr "... zīmolrades, ilustrācijām, mārketinga materiāliem utt."
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108
|
||||
msgid "color-row.token-color-row.deleted-token"
|
||||
msgid "options.deleted-token"
|
||||
msgstr "Šī tekstvienība nepastāv vai ir izdzēsta."
|
||||
|
||||
#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35
|
||||
@ -1211,7 +1211,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "Atvērt tekstvienību sarakstu"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136
|
||||
msgid "ds.inputs.token-field.detach-token"
|
||||
msgid "token-actions.detach-token"
|
||||
msgstr "Atdalīt tekstvienību"
|
||||
|
||||
#: src/app/main/data/auth.cljs:339
|
||||
|
||||
@ -201,7 +201,7 @@ msgid "branding-illustrations-marketing-pieces"
|
||||
msgstr "…branding, illustraties, marketingstukken, etc."
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108
|
||||
msgid "color-row.token-color-row.deleted-token"
|
||||
msgid "options.deleted-token"
|
||||
msgstr "Dit token bestaat niet of is verwijderd."
|
||||
|
||||
#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35
|
||||
@ -1353,7 +1353,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "Lijst met tokens openen"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136
|
||||
msgid "ds.inputs.token-field.detach-token"
|
||||
msgid "token-actions.detach-token"
|
||||
msgstr "Token loskoppelen"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:43, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:99, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:106
|
||||
|
||||
@ -202,7 +202,7 @@ msgid "branding-illustrations-marketing-pieces"
|
||||
msgstr "... marca, ilustrações, materiais de marketing, etc."
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108
|
||||
msgid "color-row.token-color-row.deleted-token"
|
||||
msgid "options.deleted-token"
|
||||
msgstr "Este token não existe ou foi excluído."
|
||||
|
||||
#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35
|
||||
@ -1182,7 +1182,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "Abrir lista de tokens"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136
|
||||
msgid "ds.inputs.token-field.detach-token"
|
||||
msgid "token-actions.detach-token"
|
||||
msgstr "Desvincular token"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:43, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:99, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:106
|
||||
|
||||
@ -208,7 +208,7 @@ msgid "branding-illustrations-marketing-pieces"
|
||||
msgstr "... mărci, ilustrații, piese de marketing, etc."
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108
|
||||
msgid "color-row.token-color-row.deleted-token"
|
||||
msgid "options.deleted-token"
|
||||
msgstr "Acest token nu există sau a fost șters."
|
||||
|
||||
#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35
|
||||
@ -1199,7 +1199,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "Deschide lista de token-uri"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136
|
||||
msgid "ds.inputs.token-field.detach-token"
|
||||
msgid "token-actions.detach-token"
|
||||
msgstr "Detașează tokenul"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:43, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:99, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:106
|
||||
|
||||
@ -204,7 +204,7 @@ msgid "branding-illustrations-marketing-pieces"
|
||||
msgstr "...брендинг, иллюстрации, маркетинговые материалы и т.д."
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108
|
||||
msgid "color-row.token-color-row.deleted-token"
|
||||
msgid "options.deleted-token"
|
||||
msgstr "Этот токен не существует или был удален."
|
||||
|
||||
#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35
|
||||
@ -1181,7 +1181,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "Открыть список токенов"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136
|
||||
msgid "ds.inputs.token-field.detach-token"
|
||||
msgid "token-actions.detach-token"
|
||||
msgstr "Отсоединить токен"
|
||||
|
||||
#: src/app/main/data/auth.cljs:339
|
||||
|
||||
@ -201,7 +201,7 @@ msgid "branding-illustrations-marketing-pieces"
|
||||
msgstr "...varumärkesbyggande, illustrationer, marknadsföringsmaterial, etc."
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108
|
||||
msgid "color-row.token-color-row.deleted-token"
|
||||
msgid "options.deleted-token"
|
||||
msgstr "Denna token existerar inte eller har raderats."
|
||||
|
||||
#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35
|
||||
@ -1183,7 +1183,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "Öppna token-lista"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136
|
||||
msgid "ds.inputs.token-field.detach-token"
|
||||
msgid "token-actions.detach-token"
|
||||
msgstr "Lösgör token"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:43, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:99, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:106
|
||||
|
||||
@ -202,7 +202,7 @@ msgid "branding-illustrations-marketing-pieces"
|
||||
msgstr "...marka çalışması, çizimler, pazarlama materyalleri, vb."
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108
|
||||
msgid "color-row.token-color-row.deleted-token"
|
||||
msgid "options.deleted-token"
|
||||
msgstr "Bu token yok veya silindi."
|
||||
|
||||
#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35
|
||||
@ -1352,7 +1352,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "Token listesini aç"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136
|
||||
msgid "ds.inputs.token-field.detach-token"
|
||||
msgid "token-actions.detach-token"
|
||||
msgstr "Tokeni ayır"
|
||||
|
||||
#: src/app/main/data/auth.cljs:339
|
||||
|
||||
@ -1113,7 +1113,7 @@ msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "打开token列表"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:91, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136
|
||||
msgid "ds.inputs.token-field.detach-token"
|
||||
msgid "token-actions.detach-token"
|
||||
msgstr "分离token"
|
||||
|
||||
#: src/app/main/data/auth.cljs:339
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user