mirror of
https://github.com/penpot/penpot.git
synced 2026-09-09 12:19:58 -04:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7fcac847c | ||
|
|
bb3d2ab5ad | ||
|
|
bc5f45abf7 | ||
|
|
3e12af47f0 |
No files matched your search
@@ -10,11 +10,9 @@
|
||||
// BASE COLORS
|
||||
--canvas-background-color: var(--color-background-primary);
|
||||
--canvas-fill-color: var(--color-canvas);
|
||||
|
||||
--scrollbar-background-color: var(--color-foreground-secondary);
|
||||
--panel-background-color: var(--color-background-primary);
|
||||
--panel-border-color: var(--color-background-quaternary);
|
||||
|
||||
--app-background: var(--color-background-primary);
|
||||
--loader-background: var(--color-background-primary);
|
||||
|
||||
@@ -26,7 +24,6 @@
|
||||
--button-foreground-color-disabled: var(--color-foreground-disabled);
|
||||
--button-background-color-disabled: var(--color-background-quaternary);
|
||||
--button-border-color-disabled: var(--color-background-quaternary);
|
||||
|
||||
--button-primary-background-color-rest: var(--color-accent-primary);
|
||||
--button-primary-border-color-rest: var(--color-accent-primary);
|
||||
--button-primary-foreground-color-rest: var(--color-background-secondary);
|
||||
@@ -39,7 +36,6 @@
|
||||
--button-primary-background-color-focus: var(--color-background-tertiary);
|
||||
--button-primary-border-color-focus: var(--color-accent-primary);
|
||||
--button-primary-foreground-color-focus: var(--color-foreground-secondary);
|
||||
|
||||
--button-secondary-background-color-rest: var(--color-background-tertiary);
|
||||
--button-secondary-border-color-rest: var(--color-background-tertiary);
|
||||
--button-secondary-foreground-color-rest: var(--color-foreground-secondary);
|
||||
@@ -52,7 +48,6 @@
|
||||
--button-secondary-background-color-focus: var(--color-background-tertiary);
|
||||
--button-secondary-border-color-focus: var(--color-accent-primary);
|
||||
--button-secondary-foreground-color-focus: var(--color-foreground-secondary);
|
||||
|
||||
--button-tertiary-foreground-color-rest: var(--color-foreground-secondary);
|
||||
--button-tertiary-background-color-hover: var(--color-background-quaternary);
|
||||
--button-tertiary-border-color-hover: var(--color-background-quaternary);
|
||||
@@ -63,16 +58,13 @@
|
||||
--button-tertiary-background-color-focus: var(--color-background-tertiary);
|
||||
--button-tertiary-border-color-focus: var(--color-accent-primary);
|
||||
--button-tertiary-foreground-color-focus: var(--color-foreground-primary);
|
||||
|
||||
--expand-button-icon-border-width: 0;
|
||||
--expand-button-icon-border-width-selected: 0;
|
||||
|
||||
--button-icon-foreground-color: var(--color-foreground-secondary);
|
||||
--button-icon-foreground-color-hover: var(--color-foreground-secondary);
|
||||
--button-icon-background-color-selected: var(--color-background-quaternary);
|
||||
--button-icon-foreground-color-selected: var(--color-accent-primary);
|
||||
--button-icon-border-color-selected: var(--color-background-quaternary);
|
||||
|
||||
--button-radio-background-color-rest: var(--color-background-tertiary);
|
||||
--button-radio-border-color-rest: var(--color-background-tertiary);
|
||||
--button-radio-foreground-color-rest: var(--color-foreground-secondary);
|
||||
@@ -84,20 +76,16 @@
|
||||
--button-radio-background-color-focus: var(--color-background-tertiary);
|
||||
--button-radio-border-color-focus: var(--color-accent-primary);
|
||||
--button-radio-foreground-color-focus: var(--color-foreground-secondary);
|
||||
|
||||
--button-warning-background-color-rest: var(--status-color-warning-500);
|
||||
--button-warning-border-color-rest: var(--status-color-warning-500);
|
||||
--button-warning-foreground-color-rest: var(--color-background-secondary);
|
||||
|
||||
--button-disabled-background-color-rest: var(--color-background-disabled);
|
||||
--button-disabled-border-color-rest: var(--color-background-disabled);
|
||||
--button-disabled-foreground-color-rest: var(--color-foreground-disabled);
|
||||
|
||||
--button-constraint-background-color-rest: var(--color-foreground-secondary);
|
||||
--button-constraint-border-color-rest: var(--color-background-tertiary);
|
||||
--button-constraint-border-color-hover: var(--color-accent-primary-muted);
|
||||
--button-constraint-background-color-hover: var(--color-accent-primary);
|
||||
|
||||
--constraint-widget-background-color: var(--color-background-tertiary);
|
||||
--constraint-center-area-background-color: var(--color-background-primary);
|
||||
|
||||
@@ -144,7 +132,6 @@
|
||||
--palette-button-shadow-initial: var(--color-background-primary);
|
||||
--palette-button-shadow-final: transparent;
|
||||
--palette-handler-background-color: var(--color-background-quaternary);
|
||||
|
||||
--color-bullet-background-color: var(--app-white); // We don't want this color to change with palette
|
||||
--color-bullet-border-color: var(--color-background-quaternary);
|
||||
--color-bullet-border-color-selected: var(--color-accent-primary);
|
||||
@@ -183,7 +170,6 @@
|
||||
--input-border-color-error: var(--status-color-error-500);
|
||||
--input-border-color-success: var(--color-accent-primary);
|
||||
--input-details-color: var(--color-background-primary);
|
||||
|
||||
--input-checkbox-background-color-rest: var(--color-background-quaternary);
|
||||
--input-checkbox-border-color-rest: var(--color-foreground-secondary);
|
||||
--input-checkbox-border-color-active: var(--color-background-quaternary);
|
||||
@@ -200,7 +186,6 @@
|
||||
--input-checkbox-background-color-active: var(--color-accent-primary);
|
||||
--input-checkbox-foreground-color-active: var(--color-background-primary);
|
||||
--input-checkbox-text-foreground-color: var(--color-foreground-secondary);
|
||||
|
||||
--menu-background-color: var(--color-background-tertiary);
|
||||
--menu-foreground-color: var(--color-foreground-primary);
|
||||
--menu-icon-foreground-color: var(--color-foreground-secondary);
|
||||
@@ -219,7 +204,6 @@
|
||||
--menu-background-color-disabled: var(--color-background-primary);
|
||||
--menu-foreground-color-disabled: var(--color-foreground-secondary);
|
||||
--menu-border-color-disabled: var(--color-background-quaternary);
|
||||
|
||||
--context-menu-background-color: var(--color-background-tertiary);
|
||||
--context-menu-foreground-color: var(--color-foreground-secondary);
|
||||
--context-menu-background-color-selected: var(--color-background-quaternary);
|
||||
@@ -243,34 +227,27 @@
|
||||
--assets-component-border-selected: var(--color-accent-tertiary);
|
||||
--assets-component-second-border-selected: var(--color-background-primary);
|
||||
--assets-component-hightlight: var(--color-accent-secondary);
|
||||
|
||||
--radio-btns-background-color: var(--color-background-tertiary);
|
||||
--radio-btn-background-color-selected: var(--color-background-quaternary);
|
||||
--radio-btn-foreground-color: var(--color-foreground-secondary);
|
||||
--radio-btn-foreground-color-selected: var(--color-accent-primary);
|
||||
--radio-btn-border-color: var(--color-background-tertiary);
|
||||
--radio-btn-border-color-selected: var(--color-background-quaternary);
|
||||
|
||||
--library-name-foreground-color: var(--color-foreground-primary);
|
||||
--library-content-foreground-color: var(--color-foreground-secondary);
|
||||
|
||||
--dropdown-background-color: var(--color-background-tertiary);
|
||||
--dropdown-separator-color: var(--color-background-primary);
|
||||
--profile-drowpdown-background-color: var(--color-background-primary);
|
||||
|
||||
--not-found-background-color: var(--color-background-tertiary);
|
||||
--not-found-foreground-color: var(--color-foreground-secondary);
|
||||
|
||||
--entry-foreground-color: var(--color-foreground-secondary);
|
||||
--entry-background-color: var(--color-background-tertiary);
|
||||
--entry-background-color-disabled: var(--color-background-primary);
|
||||
--entry-border-color-disabled: var(--color-background-quaternary);
|
||||
--entry-foreground-color-hover: var(--color-foreground-primary);
|
||||
--entry-background-color-hover: var(--color-background-quaternary);
|
||||
|
||||
--empty-message-background-color: var(--color-background-tertiary);
|
||||
--empty-message-foreground-color: var(--color-foreground-secondary);
|
||||
|
||||
--user-count-background-color: var(--color-accent-primary);
|
||||
--user-count-foreground-color: var(--color-background-secondary);
|
||||
|
||||
@@ -323,32 +300,25 @@
|
||||
--alert-text-foreground-color-default: var(--color-foreground-primary);
|
||||
--alert-icon-foreground-color-default: var(--color-foreground-primary);
|
||||
--alert-border-color-default: var(--color-background-quaternary);
|
||||
|
||||
--alert-background-color-success: var(--color-background-success);
|
||||
--alert-text-foreground-color-success: var(--color-foreground-primary);
|
||||
--alert-icon-foreground-color-success: var(--color-accent-success);
|
||||
--alert-border-color-success: var(--color-accent-success);
|
||||
|
||||
--alert-background-color-warning: var(--color-background-warning);
|
||||
--alert-text-foreground-color-warning: var(--color-foreground-primary);
|
||||
--alert-icon-foreground-color-warning: var(--color-accent-warning);
|
||||
--alert-border-color-warning: var(--color-accent-warning);
|
||||
|
||||
--alert-background-color-error: var(--color-background-error);
|
||||
--alert-text-foreground-color-error: var(--color-foreground-primary);
|
||||
--alert-icon-foreground-color-error: var(--color-accent-error);
|
||||
--alert-border-color-error: var(--color-accent-error);
|
||||
|
||||
--alert-background-color-info: var(--color-background-info);
|
||||
--alert-text-foreground-color-info: var(--color-foreground-primary);
|
||||
--alert-icon-foreground-color-info: var(--color-accent-info);
|
||||
--alert-border-color-info: var(--color-accent-info);
|
||||
|
||||
--alert-text-foreground-color-focus: var(--color-accent-primary);
|
||||
--alert-border-color-focus: var(--color-accent-primary);
|
||||
|
||||
--notification-foreground-color-default: var(--color-foreground-secondary);
|
||||
|
||||
--element-foreground-warning: var(--status-color-warning-500);
|
||||
--element-foreground-error: var(--status-color-error-500);
|
||||
|
||||
@@ -368,21 +338,16 @@
|
||||
--search-bar-foreground-color: var(--color-foreground-primary);
|
||||
--search-bar-icon-foreground-color: var(--color-foreground-secondary);
|
||||
--search-bar-icon-foreground-color-hover: var(--color-accent-primary);
|
||||
|
||||
--pill-background-color: var(--color-background-tertiary);
|
||||
--pill-foreground-color: var(--color-foreground-primary);
|
||||
|
||||
--link-foreground-color: var(--color-accent-primary);
|
||||
--register-confirmation-color: var(--status-color-success-200); //TODO: review this color
|
||||
|
||||
--register-confirmation-color: var(--status-color-success-200); // TODO: review this color
|
||||
--resize-area-background-color: var(--color-background-primary);
|
||||
--resize-area-border-color: var(--color-background-quaternary);
|
||||
|
||||
--profile-section-background-color: var(--color-background-tertiary);
|
||||
--dashboard-list-background-color: var(--color-background-tertiary);
|
||||
--dashboard-list-foreground-color: var(--color-foreground-primary);
|
||||
--dashboard-list-text-foreground-color: var(--color-foreground-secondary);
|
||||
|
||||
--communication-tag-background-color: var(--color-foreground-primary);
|
||||
--communication-tag-foreground-color: var(--color-background-tertiary);
|
||||
|
||||
@@ -404,7 +369,7 @@
|
||||
// TODO: we should not put these functional tokens here, but rather in the components they belong to
|
||||
--new-team-button-background-color: var(--color-background-primary);
|
||||
|
||||
//DASHBOARD
|
||||
// DASHBOARD
|
||||
--sidebar-element-foreground-color: var(--color-foreground-secondary);
|
||||
--sidebar-element-background-color-hover: var(--color-background-secondary);
|
||||
--sidebar-element-foreground-color-hover: var(--color-accent-primary);
|
||||
@@ -422,21 +387,16 @@
|
||||
--tab-background-color-selected: var(--color-background-primary);
|
||||
--tab-border-color: var(--color-background-tertiary);
|
||||
--tab-border-color-selected: var(--color-background-secondary);
|
||||
|
||||
--radio-btns-background-color: var(--color-background-tertiary);
|
||||
--radio-btn-background-color-selected: var(--color-background-primary);
|
||||
--radio-btn-foreground-color: var(--color-foreground-secondary);
|
||||
--radio-btn-foreground-color-selected: var(--color-accent-primary);
|
||||
--radio-btn-border-color: var(--color-background-tertiary);
|
||||
--radio-btn-border-color-selected: var(--color-background-secondary);
|
||||
|
||||
--button-icon-background-color-selected: var(--color-background-primary);
|
||||
--button-icon-border-color-selected: var(--color-background-secondary);
|
||||
|
||||
--assets-item-name-foreground-color: var(--color-foreground-primary);
|
||||
|
||||
--text-editor-selection-background-color: var(--la-tertiary-70);
|
||||
--expand-button-icon-border-width-selected: 2px;
|
||||
|
||||
--colorpicker-background-color: var(--color-background-primary);
|
||||
}
|
||||
@@ -27,9 +27,12 @@
|
||||
[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.ds.foundations.typography :as t]
|
||||
[app.main.ui.ds.foundations.typography.text :refer [text*]]
|
||||
[app.main.ui.hooks :as hooks]
|
||||
[app.main.ui.workspace.sidebar.options.menus.text-shared :refer [text-options token-text-options*]]
|
||||
[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.sidebar.options.menus.typography :refer [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]]
|
||||
@@ -188,6 +191,82 @@
|
||||
:label (tr "workspace.options.text-options.strikethrough" (sc/get-tooltip :line-through))
|
||||
:icon i/text-stroked}]}]]))
|
||||
|
||||
;; We are temporarily duplicating some components and modifying the copies
|
||||
;; to work under a feature flag. When the flag is set to false, the original
|
||||
;; components are used; when enabled, the new versions are used instead.
|
||||
;;
|
||||
;; This approach introduces some code duplication, but it helps avoid
|
||||
;; scattering conditional (feature flag) logic throughout the codebase,
|
||||
;; keeping both implementations easier to read and maintain during the transition.
|
||||
;;
|
||||
;; Once the feature flag is fully enabled, the old components will be removed
|
||||
;; and the duplicated code will be cleaned up.
|
||||
;;
|
||||
;; Once the feature flag is fully enabled, the old components will be removed
|
||||
;; and the duplicated code will be cleaned up.
|
||||
(mf/defc token-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"
|
||||
:disabled (and token-row (some? token-applied))
|
||||
:options [{:value "none"
|
||||
:id "none-text-decoration"
|
||||
:disabled (and token-row (some? token-applied))
|
||||
:label "Unset"
|
||||
:icon i/remove}
|
||||
{: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-transform-options*
|
||||
[{:keys [values on-change on-blur]}]
|
||||
(let [handle-change
|
||||
(mf/use-fn
|
||||
(mf/deps on-change on-blur)
|
||||
(fn [value]
|
||||
(on-change {:text-transform value})
|
||||
(when (some? on-blur) (on-blur))))]
|
||||
|
||||
[:div {:class (stl/css :text-transform)}
|
||||
[:> radio-buttons* {:selected (:text-transform values)
|
||||
:on-change handle-change
|
||||
:name "text-transform-options"
|
||||
:options [{:value "none"
|
||||
:id "text-transform-none"
|
||||
:label "Unset"
|
||||
:icon i/remove}
|
||||
{:value "uppercase"
|
||||
:id "text-transform-uppercase"
|
||||
:label (tr "workspace.options.text-options.text-transform-uppercase")
|
||||
:icon i/text-uppercase}
|
||||
{:value "capitalize"
|
||||
:id "text-transform-capitalize"
|
||||
:label (tr "workspace.options.text-options.text-transform-capitalize")
|
||||
:icon i/text-mixed}
|
||||
{:value "lowercase"
|
||||
:id "text-transform-lowercase"
|
||||
:label (tr "workspace.options.text-options.text-transform-lowercase")
|
||||
:icon i/text-lowercase}]}]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Helpers
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@@ -223,7 +302,248 @@
|
||||
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
|
||||
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))))
|
||||
|
||||
;; --- 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)))
|
||||
|
||||
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"))
|
||||
;; --- Toggles
|
||||
toggle-main-menu
|
||||
(mf/use-fn
|
||||
(mf/deps main-menu-open?)
|
||||
#(swap! menu-state* update :main-menu not))
|
||||
|
||||
toggle-more-options
|
||||
(mf/use-fn
|
||||
(mf/deps more-options-open?)
|
||||
#(swap! menu-state* update :more-options not))
|
||||
|
||||
;; --- Event handlers
|
||||
|
||||
emit-update!
|
||||
(mf/use-fn
|
||||
(mf/deps values)
|
||||
(fn [ids attrs]
|
||||
(st/emit! (dwt/save-font (-> (merge (txt/get-default-text-attrs) values attrs)
|
||||
(select-keys txt/text-node-attrs)))
|
||||
(dwt/update-all-attrs ids attrs))))
|
||||
|
||||
on-change
|
||||
(mf/use-fn
|
||||
(mf/deps ids emit-update!)
|
||||
(fn [attrs]
|
||||
(emit-update! ids attrs)))
|
||||
|
||||
on-convert-to-typography
|
||||
(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)
|
||||
(ptk/data-event :layout/update {:ids ids})))
|
||||
(ts/schedule #(st/emit! (dwu/commit-undo-transaction uid))))))
|
||||
|
||||
handle-detach-typography
|
||||
(mf/use-fn
|
||||
(mf/deps on-change)
|
||||
#(on-change {:typography-ref-file nil :typography-ref-id nil}))
|
||||
|
||||
handle-change-typography
|
||||
(mf/use-fn
|
||||
(mf/deps typography file-id)
|
||||
(fn [changes]
|
||||
(st/emit! (dwl/update-typography (merge typography changes) file-id))))
|
||||
|
||||
expand-stream
|
||||
(mf/with-memo []
|
||||
(->> st/stream (rx/filter (ptk/type? :expand-text-more-options))))
|
||||
|
||||
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})]
|
||||
|
||||
(hooks/use-stream
|
||||
expand-stream
|
||||
#(swap! menu-state* assoc :more-options true))
|
||||
|
||||
(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?) (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
|
||||
typography
|
||||
[:& 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}]
|
||||
|
||||
(= typography-id :multiple)
|
||||
[:div {:class (stl/css :multiple-typography)}
|
||||
[:span {:class (stl/css :multiple-text)} (tr "workspace.libraries.text.multiple-typography")]
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-label (tr "workspace.libraries.text.multiple-typography-tooltip")
|
||||
:on-click handle-detach-typography
|
||||
:icon i/detach}]]
|
||||
|
||||
:else
|
||||
[:> 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* 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}]]
|
||||
|
||||
(when more-options-open?
|
||||
[: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]])])]))
|
||||
|
||||
;; We are temporarily duplicating some components and modifying the copies
|
||||
;; to work under a feature flag. When the flag is set to false, the original
|
||||
;; components are used; when enabled, the new versions are used instead.
|
||||
;;
|
||||
;; This approach introduces some code duplication, but it helps avoid
|
||||
;; scattering conditional (feature flag) logic throughout the codebase,
|
||||
;; keeping both implementations easier to read and maintain during the transition.
|
||||
;;
|
||||
;; Once the feature flag is fully enabled, the old components will be removed
|
||||
;; and the duplicated code will be cleaned up.
|
||||
;;
|
||||
;; Once the feature flag is fully enabled, the old components will be removed
|
||||
;; and the duplicated code will be cleaned up.
|
||||
|
||||
(mf/defc token-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)
|
||||
|
||||
;; --- UI state
|
||||
menu-state* (mf/use-state {:main-menu true
|
||||
@@ -426,16 +746,15 @@
|
||||
(when token-dropdown-open?
|
||||
(ts/schedule 0 #(some-> (mf/ref-val dropdown-ref) dom/focus!))))
|
||||
|
||||
[:section {:class (stl/css :element-set)
|
||||
[:section {:class (stl/css :text-menu)
|
||||
:aria-label (tr "workspace.options.text-options.text-section")}
|
||||
[:div {:class (stl/css :element-title)}
|
||||
[:div {:class (stl/css :menu-title)}
|
||||
[:> title-bar* {:collapsable true
|
||||
:collapsed (not main-menu-open?)
|
||||
:on-collapsed toggle-main-menu
|
||||
:title label
|
||||
:class (stl/css :title-spacing-text)}
|
||||
:title label}
|
||||
[:*
|
||||
(when (and token-row (some? (resolve-delay typography-tokens)) (not typography))
|
||||
(when (and (some? (resolve-delay typography-tokens)) (not typography))
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-label (tr "ds.inputs.numeric-input.open-token-list-dropdown")
|
||||
:on-click toggle-token-dropdown
|
||||
@@ -451,12 +770,13 @@
|
||||
(when main-menu-open?
|
||||
[:div {:class (stl/css :element-content)}
|
||||
(cond
|
||||
(and token-row current-token-name)
|
||||
current-token-name
|
||||
[:> token-typography-row* {:token-name current-token-name
|
||||
:detach-token detach-token
|
||||
:active-tokens (resolve-delay typography-tokens)}]
|
||||
|
||||
typography
|
||||
;; TODO: Review this component and adapt it to new style
|
||||
[:& typography-entry {:file-id typography-file-id
|
||||
:typography typography
|
||||
:local? (= typography-file-id file-id)
|
||||
@@ -464,25 +784,27 @@
|
||||
: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-row)}
|
||||
[:> text* {:as "span" :typography t/body-small :class (stl/css :multiple-subtitle)}
|
||||
(tr "workspace.libraries.text.multiple-typography")]
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-label (tr "workspace.libraries.text.multiple-typography-tooltip")
|
||||
:on-click handle-detach-typography
|
||||
:tooltip-placement "top-left"
|
||||
:icon i/detach}]]
|
||||
|
||||
:else
|
||||
[:> text-options #js {:ids ids
|
||||
:values values
|
||||
:on-change on-change
|
||||
:show-recent true
|
||||
:on-blur
|
||||
(fn []
|
||||
(ts/schedule
|
||||
100
|
||||
[:> token-text-options* {:ids ids
|
||||
:values values
|
||||
:on-change on-change
|
||||
:show-recent true
|
||||
:on-blur
|
||||
(fn []
|
||||
(when (not= "INPUT" (-> (dom/get-active) dom/get-tag-name))
|
||||
(dom/focus! (txu/get-text-editor-content))))))}])
|
||||
(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* common-props]
|
||||
@@ -494,12 +816,39 @@
|
||||
:icon i/menu}]]
|
||||
|
||||
(when more-options-open?
|
||||
[: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]])])
|
||||
[:div {:class (stl/css :advanced-options)}
|
||||
[:> text* {:as "span" :typography t/headline-small :class (stl/css :advanced-options-title)}
|
||||
"Advanced options"]
|
||||
[:div {:class (stl/css :advanced-option)}
|
||||
[:> text* {:as "span" :typography t/body-small :class (stl/css :advanced-option-title)}
|
||||
"Text Case"]
|
||||
[:> text-transform-options* common-props]
|
||||
[:> icon-button* {:variant "secondary"
|
||||
:aria-label "Open token list"
|
||||
:on-click #()
|
||||
:icon i/tokens}]]
|
||||
|
||||
(when (and token-row token-dropdown-open?)
|
||||
[:div {:class (stl/css :advanced-option)}
|
||||
[:> text* {:as "span" :typography t/body-small :class (stl/css :advanced-option-title)}
|
||||
"Text Decoration"]
|
||||
[:> token-text-decoration-options* (mf/spread-props common-props {:token-applied current-token-name})]
|
||||
[:> icon-button* {:variant "secondary"
|
||||
:aria-label "Open token list"
|
||||
:on-click #()
|
||||
:icon i/tokens}]]
|
||||
|
||||
[:div {:class (stl/css :advanced-option)}
|
||||
[:> text* {:as "span" :typography t/body-small :class (stl/css :advanced-option-title)}
|
||||
"Align"]
|
||||
[:> vertical-align* common-props]]
|
||||
|
||||
|
||||
[:div {:class (stl/css :advanced-option)}
|
||||
[:> text* {:as "span" :typography t/body-small :class (stl/css :advanced-option-title)}
|
||||
"Direction"]
|
||||
[:> text-direction-options* common-props]]])])
|
||||
|
||||
(when token-dropdown-open?
|
||||
[:> searchable-options-dropdown* {:on-click on-option-click
|
||||
:id listbox-id
|
||||
:options (resolve-delay dropdown-options)
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
//
|
||||
// Copyright (c) KALEIDOS INC
|
||||
|
||||
@use "refactor/common-refactor.scss" as deprecated;
|
||||
@use "../../../sidebar/common/sidebar.scss" as sidebar;
|
||||
@use "ds/typography.scss" as t;
|
||||
@use "ds/_borders.scss" as *;
|
||||
@use "ds/_sizes.scss" as *;
|
||||
|
||||
.element-set {
|
||||
@include sidebar.option-grid-structure;
|
||||
@@ -18,37 +20,36 @@
|
||||
}
|
||||
|
||||
.element-content {
|
||||
@include deprecated.flexColumn;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--sp-xs);
|
||||
grid-column: span 8;
|
||||
margin-top: deprecated.$s-4;
|
||||
margin-top: var(--sp-xs);
|
||||
}
|
||||
|
||||
.multiple-typography {
|
||||
@extend %mixed-bar;
|
||||
@include t.use-typography("body-small");
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
border-radius: $br-8;
|
||||
height: $sz-32;
|
||||
padding: var(--sp-s);
|
||||
background-color: var(--color-background-tertiary);
|
||||
color: var(--color-foreground-primary);
|
||||
}
|
||||
|
||||
.multiple-text {
|
||||
@include deprecated.bodySmallTypography;
|
||||
@include t.use-typography("body-small");
|
||||
|
||||
flex-grow: 1;
|
||||
color: var(--input-foreground-color-active);
|
||||
}
|
||||
|
||||
.multiple-typography-button {
|
||||
@extend %button-tertiary;
|
||||
|
||||
height: deprecated.$s-32;
|
||||
width: deprecated.$s-28;
|
||||
|
||||
svg {
|
||||
@extend %button-icon;
|
||||
}
|
||||
color: var(--color-foreground-primary);
|
||||
}
|
||||
|
||||
.text-align-options {
|
||||
display: flex;
|
||||
gap: deprecated.$s-4;
|
||||
gap: var(--sp-xs);
|
||||
}
|
||||
|
||||
.align-options,
|
||||
@@ -56,10 +57,59 @@
|
||||
.vertical-align-options,
|
||||
.grow-options,
|
||||
.text-decoration-options {
|
||||
height: deprecated.$s-32;
|
||||
height: var(--sz-32);
|
||||
}
|
||||
|
||||
.text-decoration-options {
|
||||
display: flex;
|
||||
gap: deprecated.$s-4;
|
||||
gap: var(--sp-xs);
|
||||
}
|
||||
|
||||
|
||||
// This rules are used in token-text-menu
|
||||
.text-menu {
|
||||
@include sidebar.option-grid-structure;
|
||||
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
grid-column: span 8;
|
||||
}
|
||||
|
||||
.multiple-typography-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: $br-8;
|
||||
height: $sz-32;
|
||||
padding: var(--sp-s);
|
||||
background-color: var(--color-background-tertiary);
|
||||
}
|
||||
|
||||
.multiple-subtitle {
|
||||
flex-grow: 1;
|
||||
color: var(--color-foreground-primary);
|
||||
}
|
||||
|
||||
.advanced-options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--sp-xs);
|
||||
padding: var(--sp-m) 0;
|
||||
}
|
||||
|
||||
.advanced-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--sp-xs);
|
||||
}
|
||||
|
||||
.advanced-options-title {
|
||||
color: var(--color-foreground-secondary);
|
||||
padding-block-end: var(--sp-xs);
|
||||
}
|
||||
|
||||
.advanced-option-title {
|
||||
color: var(--color-foreground-secondary);
|
||||
flex-grow: 1;
|
||||
}
|
||||
@@ -0,0 +1,486 @@
|
||||
;; 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.text-shared
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
["react-virtualized" :as rvt]
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.types.text :as txt]
|
||||
[app.main.data.fonts :as fts]
|
||||
[app.main.data.shortcuts :as dsc]
|
||||
[app.main.features :as features]
|
||||
[app.main.fonts :as fonts]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.editable-select :refer [editable-select]]
|
||||
[app.main.ui.components.numeric-input :refer [numeric-input*]]
|
||||
[app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]]
|
||||
[app.main.ui.components.search-bar :refer [search-bar*]]
|
||||
[app.main.ui.components.select :refer [select]]
|
||||
[app.main.ui.context :as ctx]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.icons :as deprecated-icon]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.keyboard :as kbd]
|
||||
[app.util.strings :as ust]
|
||||
[app.util.timers :as tm]
|
||||
[cuerdas.core :as str]
|
||||
[goog.events :as events]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defn- attr->string [value]
|
||||
(if (= value :multiple)
|
||||
""
|
||||
(ust/format-precision value 2)))
|
||||
|
||||
(defn- get-next-font
|
||||
[{:keys [id] :as current} fonts]
|
||||
(if (seq fonts)
|
||||
(let [index (d/index-of-pred fonts #(= (:id %) id))
|
||||
index (or index -1)
|
||||
next (ex/ignoring (nth fonts (inc index)))]
|
||||
(or next (first fonts)))
|
||||
current))
|
||||
|
||||
(defn- get-prev-font
|
||||
[{:keys [id] :as current} fonts]
|
||||
(if (seq fonts)
|
||||
(let [index (d/index-of-pred fonts #(= (:id %) id))
|
||||
next (ex/ignoring (nth fonts (dec index)))]
|
||||
(or next (peek fonts)))
|
||||
current))
|
||||
|
||||
(mf/defc font-item*
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [font is-current on-click style]}]
|
||||
(let [item-ref (mf/use-ref)
|
||||
on-click (mf/use-fn (mf/deps font) #(on-click font))]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps is-current)
|
||||
(fn []
|
||||
(when is-current
|
||||
(let [element (mf/ref-val item-ref)]
|
||||
(when-not (dom/is-in-viewport? element)
|
||||
(dom/scroll-into-view! element))))))
|
||||
|
||||
[:div {:class (stl/css :font-wrapper)
|
||||
:style style
|
||||
:ref item-ref
|
||||
:on-click on-click}
|
||||
[:div {:class (stl/css-case :font-item true
|
||||
:selected is-current)}
|
||||
[:span {:class (stl/css :label)} (:name font)]
|
||||
[:span {:class (stl/css :icon)} (when is-current deprecated-icon/tick)]]]))
|
||||
|
||||
(declare row-renderer)
|
||||
|
||||
(defn filter-fonts
|
||||
[{:keys [term backends]} fonts]
|
||||
(let [term (str/lower term)
|
||||
xform (cond-> (map identity)
|
||||
(seq term)
|
||||
(comp (filter #(str/includes? (str/lower (:name %)) term)))
|
||||
|
||||
(seq backends)
|
||||
(comp (filter #(contains? backends (:backend %)))))]
|
||||
(into [] xform fonts)))
|
||||
|
||||
(mf/defc font-selector*
|
||||
[{:keys [on-select on-close current-font show-recent full-size]}]
|
||||
(let [selected (mf/use-state current-font)
|
||||
state* (mf/use-state
|
||||
#(do {:term "" :backends #{}}))
|
||||
state (deref state*)
|
||||
|
||||
flist (mf/use-ref)
|
||||
input (mf/use-ref)
|
||||
|
||||
fonts (mf/deref fonts/fonts)
|
||||
fonts (mf/with-memo [state fonts]
|
||||
(filter-fonts state fonts))
|
||||
|
||||
recent-fonts (mf/deref refs/recent-fonts)
|
||||
recent-fonts (mf/with-memo [state recent-fonts]
|
||||
(filter-fonts state recent-fonts))
|
||||
|
||||
|
||||
full-size? (boolean (and full-size show-recent))
|
||||
|
||||
select-next
|
||||
(mf/use-fn
|
||||
(mf/deps fonts)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(dom/prevent-default event)
|
||||
(swap! selected get-next-font fonts)))
|
||||
|
||||
select-prev
|
||||
(mf/use-fn
|
||||
(mf/deps fonts)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(dom/prevent-default event)
|
||||
(swap! selected get-prev-font fonts)))
|
||||
|
||||
on-key-down
|
||||
(mf/use-fn
|
||||
(mf/deps fonts)
|
||||
(fn [event]
|
||||
(cond
|
||||
(kbd/up-arrow? event) (select-prev event)
|
||||
(kbd/down-arrow? event) (select-next event)
|
||||
(kbd/esc? event) (on-close)
|
||||
(kbd/enter? event) (on-close)
|
||||
:else (dom/focus! (mf/ref-val input)))))
|
||||
|
||||
on-filter-change
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(swap! state* assoc :term event)))
|
||||
|
||||
on-select-and-close
|
||||
(mf/use-fn
|
||||
(mf/deps on-select on-close)
|
||||
(fn [font]
|
||||
(on-select font)
|
||||
(on-close)))]
|
||||
|
||||
(mf/with-effect [fonts]
|
||||
(let [key (events/listen js/document "keydown" on-key-down)]
|
||||
#(events/unlistenByKey key)))
|
||||
|
||||
(mf/with-effect [@selected]
|
||||
(when-let [inst (mf/ref-val flist)]
|
||||
(when-let [index (:index @selected)]
|
||||
(.scrollToRow ^js inst index))))
|
||||
|
||||
(mf/with-effect [@selected]
|
||||
(on-select @selected))
|
||||
|
||||
(mf/with-effect []
|
||||
(st/emit! (dsc/push-shortcuts :typography {}))
|
||||
(fn []
|
||||
(st/emit! (dsc/pop-shortcuts :typography))))
|
||||
|
||||
(mf/with-effect []
|
||||
(let [index (d/index-of-pred fonts #(= (:id %) (:id current-font)))
|
||||
inst (mf/ref-val flist)]
|
||||
(tm/schedule
|
||||
#(let [offset (.getOffsetForRow ^js inst #js {:alignment "center" :index index})]
|
||||
(.scrollToPosition ^js inst offset)))))
|
||||
|
||||
[:div {:class (stl/css :font-selector)}
|
||||
[:div {:class (stl/css-case :font-selector-dropdown true :font-selector-dropdown-full-size full-size?)}
|
||||
[:div {:class (stl/css :header)}
|
||||
[:> search-bar* {:on-change on-filter-change
|
||||
:value (:term state)
|
||||
:auto-focus true
|
||||
:placeholder (tr "workspace.options.search-font")}]
|
||||
(when (and recent-fonts show-recent)
|
||||
[:section {:class (stl/css :show-recent)}
|
||||
[:p {:class (stl/css :title)} (tr "workspace.options.recent-fonts")]
|
||||
(for [[idx font] (d/enumerate recent-fonts)]
|
||||
[:> font-item* {:key (dm/str "font-" idx)
|
||||
:font font
|
||||
:style {}
|
||||
:on-click on-select-and-close
|
||||
:is-current (= (:id font) (:id @selected))}])])]
|
||||
|
||||
[:div {:class (stl/css-case :fonts-list true
|
||||
:fonts-list-full-size full-size?)}
|
||||
[:> rvt/AutoSizer {}
|
||||
(fn [props]
|
||||
(let [width (unchecked-get props "width")
|
||||
height (unchecked-get props "height")
|
||||
render #(row-renderer fonts @selected on-select-and-close %)]
|
||||
(mf/html
|
||||
[:> rvt/List #js {:height height
|
||||
:ref flist
|
||||
:width width
|
||||
:rowCount (count fonts)
|
||||
:rowHeight 36
|
||||
:rowRenderer render}])))]]]]))
|
||||
|
||||
(defn row-renderer
|
||||
[fonts selected on-select props]
|
||||
(let [index (unchecked-get props "index")
|
||||
key (unchecked-get props "key")
|
||||
style (unchecked-get props "style")
|
||||
font (nth fonts index)]
|
||||
(mf/html
|
||||
[:> font-item* {:key key
|
||||
:font font
|
||||
:style style
|
||||
:on-click on-select
|
||||
:is-current (= (:id font) (:id selected))}])))
|
||||
|
||||
(mf/defc font-options
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [values on-change on-blur show-recent full-size-selector]}]
|
||||
(let [{:keys [font-id font-size font-variant-id]} values
|
||||
|
||||
font-id (or font-id (:font-id txt/default-typography))
|
||||
font-size (or font-size (:font-size txt/default-typography))
|
||||
font-variant-id (or font-variant-id (:font-variant-id txt/default-typography))
|
||||
|
||||
fonts (mf/deref fonts/fontsdb)
|
||||
font (get fonts font-id)
|
||||
|
||||
last-font (mf/use-ref nil)
|
||||
|
||||
open-selector? (mf/use-state false)
|
||||
|
||||
change-font
|
||||
(mf/use-fn
|
||||
(mf/deps on-change fonts)
|
||||
(fn [new-font-id]
|
||||
(let [{:keys [family] :as font} (get fonts new-font-id)
|
||||
{:keys [id name weight style]} (fonts/get-default-variant font)]
|
||||
(on-change {:font-id new-font-id
|
||||
:font-family family
|
||||
:font-variant-id (or id name)
|
||||
:font-weight weight
|
||||
:font-style style})
|
||||
(mf/set-ref-val! last-font font))))
|
||||
|
||||
on-font-size-change
|
||||
(mf/use-fn
|
||||
(mf/deps on-change)
|
||||
(fn [new-font-size]
|
||||
(when-not (str/empty? new-font-size)
|
||||
(on-change {:font-size (str new-font-size)}))))
|
||||
|
||||
on-font-variant-change
|
||||
(mf/use-fn
|
||||
(mf/deps font on-change)
|
||||
(fn [new-variant-id]
|
||||
(let [variant (d/seek #(= new-variant-id (:id %)) (:variants font))]
|
||||
(when-not (nil? variant)
|
||||
(on-change {:font-id (:id font)
|
||||
:font-family (:family font)
|
||||
:font-variant-id new-variant-id
|
||||
:font-weight (:weight variant)
|
||||
:font-style (:style variant)}))
|
||||
;; NOTE: the select component we are using does not fire on-blur event
|
||||
;; so we need to call on-blur manually
|
||||
(when (some? on-blur)
|
||||
(on-blur)))))
|
||||
|
||||
on-font-select
|
||||
(mf/use-fn
|
||||
(mf/deps change-font)
|
||||
(fn [font*]
|
||||
(when (not= font font*)
|
||||
(change-font (:id font*)))
|
||||
|
||||
(when (some? on-blur)
|
||||
(on-blur))))
|
||||
|
||||
on-font-selector-close
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
(reset! open-selector? false)
|
||||
(when (some? on-blur)
|
||||
(on-blur))
|
||||
(when (mf/ref-val last-font)
|
||||
(st/emit! (fts/add-recent-font (mf/ref-val last-font))))))]
|
||||
|
||||
[:*
|
||||
(when @open-selector?
|
||||
[:> font-selector*
|
||||
{:current-font font
|
||||
:on-close on-font-selector-close
|
||||
:on-select on-font-select
|
||||
:full-size full-size-selector
|
||||
:show-recent show-recent}])
|
||||
|
||||
[:div {:class (stl/css :font-option)
|
||||
:title (tr "inspect.attributes.typography.font-family")
|
||||
:on-click #(reset! open-selector? true)}
|
||||
(cond
|
||||
(or (= :multiple font-id) (= "mixed" font-id))
|
||||
"--"
|
||||
|
||||
(some? font)
|
||||
[:*
|
||||
[:span {:class (stl/css :name)}
|
||||
(:name font)]
|
||||
[:span {:class (stl/css :icon)}
|
||||
deprecated-icon/arrow]]
|
||||
|
||||
:else
|
||||
(tr "dashboard.fonts.deleted-placeholder"))]
|
||||
|
||||
[:div {:class (stl/css :font-modifiers)}
|
||||
[:div {:class (stl/css :font-size-options)
|
||||
:title (tr "inspect.attributes.typography.font-size")}
|
||||
(let [size-options [8 9 10 11 12 14 16 18 24 36 48 72]
|
||||
size-options (if (= font-size :multiple) (into [""] size-options) size-options)]
|
||||
[:& editable-select
|
||||
{:value (if (= font-size :multiple) :multiple (attr->string font-size))
|
||||
:class (stl/css :font-size-select)
|
||||
:aria-label (tr "inspect.attributes.typography.font-size")
|
||||
:input-class (stl/css :numeric-input)
|
||||
:options size-options
|
||||
:type "number"
|
||||
:placeholder (tr "settings.multiple")
|
||||
:min 3
|
||||
:max 1000
|
||||
:on-change on-font-size-change
|
||||
:on-blur on-blur}])]
|
||||
|
||||
[:div {:class (stl/css :font-variant-options)
|
||||
:title (tr "inspect.attributes.typography.font-style")}
|
||||
(let [basic-variant-options (->> (:variants font)
|
||||
(map (fn [variant]
|
||||
{:value (:id variant)
|
||||
:key (pr-str variant)
|
||||
:label (:name variant)})))
|
||||
variant-options (if (or (= font-variant-id :multiple) (= font-variant-id "mixed"))
|
||||
(conj basic-variant-options
|
||||
{:value ""
|
||||
:key :multiple-variants
|
||||
:label "--"})
|
||||
basic-variant-options)
|
||||
font-variant-value (attr->string font-variant-id)
|
||||
font-variant-value (if (= font-variant-value "mixed") "" font-variant-value)]
|
||||
|
||||
;; TODO Add disabled mode
|
||||
[:& select
|
||||
{:class (stl/css :font-variant-select)
|
||||
:default-value font-variant-value
|
||||
:options variant-options
|
||||
:on-change on-font-variant-change
|
||||
:on-blur on-blur}])]]]))
|
||||
|
||||
(mf/defc spacing-options
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [values on-change on-blur]}]
|
||||
(let [{:keys [line-height
|
||||
letter-spacing]} values
|
||||
line-height (or line-height "1.2")
|
||||
letter-spacing (or letter-spacing "0")
|
||||
handle-change
|
||||
(fn [value attr]
|
||||
(on-change {attr (str value)}))]
|
||||
|
||||
[:div {:class (stl/css :spacing-options)}
|
||||
[:div {:class (stl/css :line-height)
|
||||
:title (tr "inspect.attributes.typography.line-height")}
|
||||
[:span {:class (stl/css :icon)
|
||||
:alt (tr "workspace.options.text-options.line-height")}
|
||||
deprecated-icon/text-lineheight]
|
||||
[:> numeric-input*
|
||||
{:min -200
|
||||
:max 200
|
||||
:step 0.1
|
||||
:default-value "1.2"
|
||||
:class (stl/css :line-height-input)
|
||||
:aria-label (tr "inspect.attributes.typography.line-height")
|
||||
:value (attr->string line-height)
|
||||
:placeholder (if (= :multiple line-height) (tr "settings.multiple") "--")
|
||||
:nillable (= :multiple line-height)
|
||||
:on-change #(handle-change % :line-height)
|
||||
:on-blur on-blur}]]
|
||||
|
||||
[:div {:class (stl/css :letter-spacing)
|
||||
:title (tr "inspect.attributes.typography.letter-spacing")}
|
||||
[:span
|
||||
{:class (stl/css :icon)
|
||||
:alt (tr "workspace.options.text-options.letter-spacing")}
|
||||
deprecated-icon/text-letterspacing]
|
||||
[:> numeric-input*
|
||||
{:min -200
|
||||
:max 200
|
||||
:step 0.1
|
||||
:default-value "0"
|
||||
:class (stl/css :letter-spacing-input)
|
||||
:aria-label (tr "inspect.attributes.typography.letter-spacing")
|
||||
:value (attr->string letter-spacing)
|
||||
:placeholder (if (= :multiple letter-spacing) (tr "settings.multiple") "--")
|
||||
:on-change #(handle-change % :letter-spacing)
|
||||
:nillable (= :multiple letter-spacing)
|
||||
:on-blur on-blur}]]]))
|
||||
|
||||
(mf/defc text-transform-options
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [values on-change on-blur]}]
|
||||
(let [text-transform (or (:text-transform values) "none")
|
||||
unset-value (if (features/active-feature? @st/state "text-editor/v2") "none" "unset")
|
||||
handle-change
|
||||
(fn [type]
|
||||
(if (= text-transform type)
|
||||
(on-change {:text-transform unset-value})
|
||||
(on-change {:text-transform type}))
|
||||
(when (some? on-blur) (on-blur)))]
|
||||
|
||||
[:div {:class (stl/css :text-transform)}
|
||||
[:& radio-buttons {:selected text-transform
|
||||
:on-change handle-change
|
||||
:name "text-transform"}
|
||||
[:& radio-button {:icon i/text-uppercase
|
||||
:type "checkbox"
|
||||
:title (tr "inspect.attributes.typography.text-transform.uppercase")
|
||||
:value "uppercase"
|
||||
:id "text-transform-uppercase"}]
|
||||
[:& radio-button {:icon i/text-mixed
|
||||
:type "checkbox"
|
||||
:value "capitalize"
|
||||
:title (tr "inspect.attributes.typography.text-transform.capitalize")
|
||||
:id "text-transform-capitalize"}]
|
||||
[:& radio-button {:icon i/text-lowercase
|
||||
:type "checkbox"
|
||||
:title (tr "inspect.attributes.typography.text-transform.lowercase")
|
||||
:value "lowercase"
|
||||
:id "text-transform-lowercase"}]]]))
|
||||
|
||||
(mf/defc text-options
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [ids editor values on-change on-blur show-recent]}]
|
||||
(let [full-size-selector? (and show-recent (= (mf/use-ctx ctx/sidebar) :right))
|
||||
opts #js {:editor editor
|
||||
:ids ids
|
||||
:values values
|
||||
:on-change on-change
|
||||
:on-blur on-blur
|
||||
:show-recent show-recent
|
||||
:full-size-selector full-size-selector?}]
|
||||
[:div {:class (stl/css-case :text-options true
|
||||
:text-options-full-size full-size-selector?)}
|
||||
[:> font-options opts]
|
||||
[:div {:class (stl/css :typography-variations)}
|
||||
[:> spacing-options opts]
|
||||
[:> text-transform-options opts]]]))
|
||||
|
||||
;; We are temporarily duplicating some components and modifying the copies
|
||||
;; to work under a feature flag. When the flag is set to false, the original
|
||||
;; components are used; when enabled, the new versions are used instead.
|
||||
;;
|
||||
;; This approach introduces some code duplication, but it helps avoid
|
||||
;; scattering conditional (feature flag) logic throughout the codebase,
|
||||
;; keeping both implementations easier to read and maintain during the transition.
|
||||
|
||||
(mf/defc token-text-options*
|
||||
[{:keys [ids editor values on-change on-blur show-recent]}]
|
||||
(let [full-size-selector? (and show-recent (= (mf/use-ctx ctx/sidebar) :right))
|
||||
opts #js {:editor editor
|
||||
:ids ids
|
||||
:values values
|
||||
:on-change on-change
|
||||
:on-blur on-blur
|
||||
:show-recent show-recent
|
||||
:full-size-selector full-size-selector?}]
|
||||
[:div {:class (stl/css-case :text-options true
|
||||
:text-options-full-size full-size-selector?)}
|
||||
[:> font-options opts]
|
||||
[:div {:class (stl/css :typography-variations)}
|
||||
[:> spacing-options opts]]]))
|
||||
|
||||
@@ -0,0 +1,546 @@
|
||||
// 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 "refactor/common-refactor.scss" as deprecated;
|
||||
|
||||
.typography-entry {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
height: deprecated.$s-32;
|
||||
width: 100%;
|
||||
border-radius: deprecated.$br-8;
|
||||
background-color: var(--assets-item-background-color);
|
||||
color: var(--assets-item-name-foreground-color-hover);
|
||||
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
background-color: var(--assets-item-background-color-hover);
|
||||
color: var(--assets-item-name-foreground-color-hover);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
border: deprecated.$s-1 solid var(--assets-item-border-color);
|
||||
}
|
||||
|
||||
.element-set-actions {
|
||||
display: flex;
|
||||
visibility: hidden;
|
||||
|
||||
.element-set-actions-button,
|
||||
.menu-btn {
|
||||
@extend %button-tertiary;
|
||||
|
||||
height: deprecated.$s-32;
|
||||
width: deprecated.$s-28;
|
||||
|
||||
svg {
|
||||
@extend %button-icon;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--assets-item-background-color-hover);
|
||||
|
||||
.element-set-actions {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.typography-selection-wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: deprecated.$s-24 auto 1fr;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 0 deprecated.$s-12;
|
||||
|
||||
&.is-selectable {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.typography-sample {
|
||||
@include deprecated.flexCenter;
|
||||
|
||||
min-width: deprecated.$s-24;
|
||||
height: deprecated.$s-32;
|
||||
color: var(--assets-item-name-foreground-color);
|
||||
}
|
||||
|
||||
.typography-name,
|
||||
.typography-font {
|
||||
@include deprecated.bodySmallTypography;
|
||||
@include deprecated.textEllipsis;
|
||||
|
||||
display: block;
|
||||
align-self: center;
|
||||
margin-left: deprecated.$s-6;
|
||||
}
|
||||
|
||||
.typography-name {
|
||||
color: var(--assets-item-name-foreground-color);
|
||||
}
|
||||
|
||||
.typography-font {
|
||||
color: var(--assets-item-name-foreground-color-rest);
|
||||
}
|
||||
|
||||
.font-name-wrapper {
|
||||
@include deprecated.bodySmallTypography;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: deprecated.$s-32;
|
||||
width: 100%;
|
||||
border-radius: deprecated.$br-8;
|
||||
border: deprecated.$s-1 solid transparent;
|
||||
box-sizing: border-box;
|
||||
background-color: var(--assets-item-background-color);
|
||||
margin-bottom: deprecated.$s-4;
|
||||
padding: deprecated.$s-8 deprecated.$s-0 deprecated.$s-8 deprecated.$s-12;
|
||||
|
||||
.typography-sample-input {
|
||||
@include deprecated.flexCenter;
|
||||
|
||||
width: deprecated.$s-24;
|
||||
height: 100%;
|
||||
font-size: deprecated.$fs-16;
|
||||
color: var(--assets-item-name-foreground-color-hover);
|
||||
}
|
||||
|
||||
.adv-typography-name {
|
||||
@include deprecated.removeInputStyle;
|
||||
|
||||
font-size: deprecated.$fs-12;
|
||||
color: var(--input-foreground-color-active);
|
||||
flex-grow: 1;
|
||||
padding-left: deprecated.$s-6;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
@extend %button-tertiary;
|
||||
@include deprecated.flexCenter;
|
||||
|
||||
width: deprecated.$s-28;
|
||||
height: deprecated.$s-28;
|
||||
|
||||
svg {
|
||||
@extend %button-icon-small;
|
||||
|
||||
stroke: var(--icon-foreground);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-within {
|
||||
border: deprecated.$s-1 solid var(--input-border-color-active);
|
||||
|
||||
.adv-typography-name {
|
||||
color: var(--input-foreground-color-active);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--assets-item-background-color-hover);
|
||||
}
|
||||
}
|
||||
|
||||
.advanced-options-wrapper {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: var(--assets-title-background-color);
|
||||
}
|
||||
|
||||
.typography-info-wrapper {
|
||||
@include deprecated.flexColumn;
|
||||
|
||||
margin-bottom: deprecated.$s-12;
|
||||
|
||||
.typography-name-wrapper {
|
||||
@extend %asset-element;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: deprecated.$s-24 auto 1fr deprecated.$s-28;
|
||||
flex: 1;
|
||||
height: deprecated.$s-32;
|
||||
width: 100%;
|
||||
padding: 0 0 0 deprecated.$s-12;
|
||||
background-color: var(--assets-item-background-color-hover);
|
||||
margin-bottom: deprecated.$s-4;
|
||||
|
||||
.typography-sample {
|
||||
@include deprecated.flexCenter;
|
||||
|
||||
min-width: deprecated.$s-24;
|
||||
font-size: deprecated.$fs-16;
|
||||
height: deprecated.$s-32;
|
||||
padding: 0;
|
||||
color: var(--assets-item-name-foreground-color-hover);
|
||||
}
|
||||
|
||||
.typography-name {
|
||||
@include deprecated.bodySmallTypography;
|
||||
@include deprecated.textEllipsis;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
margin-left: deprecated.$s-6;
|
||||
color: var(--assets-item-name-foreground-color-hover);
|
||||
}
|
||||
|
||||
.typography-font {
|
||||
@include deprecated.bodySmallTypography;
|
||||
@include deprecated.textEllipsis;
|
||||
|
||||
margin-left: deprecated.$s-6;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
min-width: 0;
|
||||
color: var(--assets-item-name-foreground-color);
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
@extend %button-tertiary;
|
||||
|
||||
width: deprecated.$s-28;
|
||||
height: deprecated.$s-32;
|
||||
|
||||
svg {
|
||||
@extend %button-icon;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: grid;
|
||||
grid-template-columns: 50% 50%;
|
||||
height: deprecated.$s-32;
|
||||
|
||||
--calculated-width: calc(var(--right-sidebar-width) - deprecated.$s-48);
|
||||
|
||||
padding-left: deprecated.$s-2;
|
||||
|
||||
.info-label {
|
||||
@include deprecated.bodySmallTypography;
|
||||
@include deprecated.textEllipsis;
|
||||
|
||||
width: calc(var(--calculated-width) / 2);
|
||||
padding-top: deprecated.$s-8;
|
||||
color: var(--assets-item-name-foreground-color);
|
||||
}
|
||||
|
||||
.info-content {
|
||||
@include deprecated.bodySmallTypography;
|
||||
@include deprecated.textEllipsis;
|
||||
|
||||
padding-top: deprecated.$s-8;
|
||||
width: calc(var(--calculated-width) / 2);
|
||||
color: var(--assets-item-name-foreground-color-hover);
|
||||
}
|
||||
}
|
||||
|
||||
.link-btn {
|
||||
@include deprecated.uppercaseTitleTipography;
|
||||
@extend %button-secondary;
|
||||
|
||||
width: 100%;
|
||||
height: deprecated.$s-32;
|
||||
border-radius: deprecated.$br-8;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--button-secondary-background-color-hover);
|
||||
color: var(--button-secondary-foreground-color-hover);
|
||||
border: deprecated.$s-1 solid var(--button-secondary-border-color-hover);
|
||||
text-decoration: none;
|
||||
|
||||
svg {
|
||||
stroke: var(--button-secondary-foreground-color-hover);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: var(--button-secondary-background-color-focus);
|
||||
color: var(--button-secondary-foreground-color-focus);
|
||||
border: deprecated.$s-1 solid var(--button-secondary-border-color-focus);
|
||||
|
||||
svg {
|
||||
stroke: var(--button-secondary-foreground-color-focus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.text-options {
|
||||
@include deprecated.flexColumn;
|
||||
|
||||
max-width: var(--options-width);
|
||||
|
||||
&:not(.text-options-full-size) {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.font-option {
|
||||
@include deprecated.bodySmallTypography;
|
||||
@extend %asset-element;
|
||||
|
||||
padding: deprecated.$s-8 0 deprecated.$s-8 deprecated.$s-8;
|
||||
cursor: pointer;
|
||||
|
||||
.name {
|
||||
flex-grow: 1;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.icon {
|
||||
@include deprecated.flexCenter;
|
||||
|
||||
height: deprecated.$s-28;
|
||||
width: deprecated.$s-28;
|
||||
|
||||
svg {
|
||||
@extend %button-icon-small;
|
||||
|
||||
stroke: var(--icon-foreground);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.font-modifiers {
|
||||
display: flex;
|
||||
gap: deprecated.$s-4;
|
||||
|
||||
.font-size-options {
|
||||
@extend %asset-element;
|
||||
@include deprecated.bodySmallTypography;
|
||||
|
||||
flex-grow: 1;
|
||||
width: deprecated.$s-60;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: deprecated.$s-1 solid var(--input-border-color);
|
||||
position: relative;
|
||||
|
||||
.icon {
|
||||
@include deprecated.flexCenter;
|
||||
|
||||
height: deprecated.$s-28;
|
||||
min-width: deprecated.$s-28;
|
||||
|
||||
svg {
|
||||
@extend %button-icon-small;
|
||||
|
||||
stroke: var(--icon-foreground);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.font-variant-options {
|
||||
padding: 0;
|
||||
flex-grow: 2;
|
||||
}
|
||||
}
|
||||
|
||||
.typography-variations {
|
||||
@include deprecated.flexRow;
|
||||
|
||||
.spacing-options {
|
||||
@include deprecated.flexRow;
|
||||
|
||||
.line-height,
|
||||
.letter-spacing {
|
||||
@extend %input-element;
|
||||
@include deprecated.bodySmallTypography;
|
||||
|
||||
.icon {
|
||||
@include deprecated.flexCenter;
|
||||
|
||||
width: deprecated.$s-28;
|
||||
|
||||
svg {
|
||||
@extend %button-icon-small;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.text-transform {
|
||||
@extend %asset-element;
|
||||
|
||||
width: fit-content;
|
||||
padding: 0;
|
||||
background-color: var(--radio-btns-background-color);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--radio-btns-background-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.font-size-select {
|
||||
@include deprecated.removeInputStyle;
|
||||
@include deprecated.bodySmallTypography;
|
||||
|
||||
height: deprecated.$s-32;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: deprecated.$s-8;
|
||||
|
||||
.numeric-input {
|
||||
@extend %input-base;
|
||||
@include deprecated.bodySmallTypography;
|
||||
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.font-selector {
|
||||
@include deprecated.flexCenter;
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
z-index: deprecated.$z-index-4;
|
||||
}
|
||||
|
||||
.show-recent {
|
||||
border-radius: deprecated.$br-8 deprecated.$br-8 0 0;
|
||||
background: var(--dropdown-background-color);
|
||||
border: deprecated.$s-1 solid var(--color-background-quaternary);
|
||||
border-block-end: none;
|
||||
}
|
||||
|
||||
.font-selector-dropdown {
|
||||
width: 100%;
|
||||
|
||||
&:not(.font-selector-dropdown-full-size) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: grid;
|
||||
row-gap: deprecated.$s-2;
|
||||
|
||||
.title {
|
||||
@include deprecated.uppercaseTitleTipography;
|
||||
|
||||
color: var(--title-foreground-color);
|
||||
margin: 0;
|
||||
padding: deprecated.$s-12;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.font-wrapper {
|
||||
padding-bottom: deprecated.$s-4;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.font-item {
|
||||
@extend %asset-element;
|
||||
|
||||
margin-bottom: deprecated.$s-4;
|
||||
border-radius: deprecated.$br-8;
|
||||
display: flex;
|
||||
|
||||
.icon {
|
||||
@include deprecated.flexCenter;
|
||||
|
||||
height: deprecated.$s-28;
|
||||
width: deprecated.$s-28;
|
||||
|
||||
svg {
|
||||
@extend %button-icon-small;
|
||||
|
||||
stroke: var(--icon-foreground);
|
||||
}
|
||||
}
|
||||
|
||||
&.selected {
|
||||
color: var(--assets-item-name-foreground-color-hover);
|
||||
|
||||
.icon {
|
||||
svg {
|
||||
stroke: var(--assets-item-name-foreground-color-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
@include deprecated.bodySmallTypography;
|
||||
@include deprecated.textEllipsis;
|
||||
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.font-selector-dropdown-full-size {
|
||||
height: calc(100vh - 48px); // TODO: ugly hack :( Find a workaround for this.
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
padding: deprecated.$s-2 deprecated.$s-12 deprecated.$s-12 deprecated.$s-12;
|
||||
}
|
||||
|
||||
.fonts-list {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
min-height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: deprecated.$s-2;
|
||||
border-radius: deprecated.$br-8;
|
||||
background-color: var(--dropdown-background-color);
|
||||
overflow: hidden;
|
||||
|
||||
&:not(.fonts-list-full-size) {
|
||||
margin-block-start: deprecated.$s-2;
|
||||
}
|
||||
}
|
||||
|
||||
.fonts-list-full-size {
|
||||
border-start-start-radius: 0;
|
||||
border-start-end-radius: 0;
|
||||
border: deprecated.$s-1 solid var(--color-background-quaternary);
|
||||
|
||||
// TODO: this should belong to typography-entry , but atm we don't have a clear
|
||||
// way of accessing whether we are in fullsize mode or not
|
||||
.selected {
|
||||
padding-inline-end: 0;
|
||||
}
|
||||
}
|
||||
@@ -7,464 +7,24 @@
|
||||
(ns app.main.ui.workspace.sidebar.options.menus.typography
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
["react-virtualized" :as rvt]
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.types.text :as txt]
|
||||
[app.main.constants :refer [max-input-length]]
|
||||
[app.main.data.common :as dcm]
|
||||
[app.main.data.fonts :as fts]
|
||||
[app.main.data.shortcuts :as dsc]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.features :as features]
|
||||
[app.main.fonts :as fonts]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.editable-select :refer [editable-select]]
|
||||
[app.main.ui.components.numeric-input :refer [numeric-input*]]
|
||||
[app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]]
|
||||
[app.main.ui.components.search-bar :refer [search-bar*]]
|
||||
[app.main.ui.components.select :refer [select]]
|
||||
[app.main.ui.context :as ctx]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.icons :as deprecated-icon]
|
||||
[app.main.ui.workspace.sidebar.options.menus.text-shared :refer [text-options]]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.keyboard :as kbd]
|
||||
[app.util.strings :as ust]
|
||||
[app.util.timers :as tm]
|
||||
[cuerdas.core :as str]
|
||||
[goog.events :as events]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defn- attr->string [value]
|
||||
(if (= value :multiple)
|
||||
""
|
||||
(ust/format-precision value 2)))
|
||||
|
||||
(defn- get-next-font
|
||||
[{:keys [id] :as current} fonts]
|
||||
(if (seq fonts)
|
||||
(let [index (d/index-of-pred fonts #(= (:id %) id))
|
||||
index (or index -1)
|
||||
next (ex/ignoring (nth fonts (inc index)))]
|
||||
(or next (first fonts)))
|
||||
current))
|
||||
|
||||
(defn- get-prev-font
|
||||
[{:keys [id] :as current} fonts]
|
||||
(if (seq fonts)
|
||||
(let [index (d/index-of-pred fonts #(= (:id %) id))
|
||||
next (ex/ignoring (nth fonts (dec index)))]
|
||||
(or next (peek fonts)))
|
||||
current))
|
||||
|
||||
(mf/defc font-item*
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [font is-current on-click style]}]
|
||||
(let [item-ref (mf/use-ref)
|
||||
on-click (mf/use-fn (mf/deps font) #(on-click font))]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps is-current)
|
||||
(fn []
|
||||
(when is-current
|
||||
(let [element (mf/ref-val item-ref)]
|
||||
(when-not (dom/is-in-viewport? element)
|
||||
(dom/scroll-into-view! element))))))
|
||||
|
||||
[:div {:class (stl/css :font-wrapper)
|
||||
:style style
|
||||
:ref item-ref
|
||||
:on-click on-click}
|
||||
[:div {:class (stl/css-case :font-item true
|
||||
:selected is-current)}
|
||||
[:span {:class (stl/css :label)} (:name font)]
|
||||
[:span {:class (stl/css :icon)} (when is-current deprecated-icon/tick)]]]))
|
||||
|
||||
(declare row-renderer)
|
||||
|
||||
(defn filter-fonts
|
||||
[{:keys [term backends]} fonts]
|
||||
(let [term (str/lower term)
|
||||
xform (cond-> (map identity)
|
||||
(seq term)
|
||||
(comp (filter #(str/includes? (str/lower (:name %)) term)))
|
||||
|
||||
(seq backends)
|
||||
(comp (filter #(contains? backends (:backend %)))))]
|
||||
(into [] xform fonts)))
|
||||
|
||||
(mf/defc font-selector*
|
||||
[{:keys [on-select on-close current-font show-recent full-size]}]
|
||||
(let [selected (mf/use-state current-font)
|
||||
state* (mf/use-state
|
||||
#(do {:term "" :backends #{}}))
|
||||
state (deref state*)
|
||||
|
||||
flist (mf/use-ref)
|
||||
input (mf/use-ref)
|
||||
|
||||
fonts (mf/deref fonts/fonts)
|
||||
fonts (mf/with-memo [state fonts]
|
||||
(filter-fonts state fonts))
|
||||
|
||||
recent-fonts (mf/deref refs/recent-fonts)
|
||||
recent-fonts (mf/with-memo [state recent-fonts]
|
||||
(filter-fonts state recent-fonts))
|
||||
|
||||
|
||||
full-size? (boolean (and full-size show-recent))
|
||||
|
||||
select-next
|
||||
(mf/use-fn
|
||||
(mf/deps fonts)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(dom/prevent-default event)
|
||||
(swap! selected get-next-font fonts)))
|
||||
|
||||
select-prev
|
||||
(mf/use-fn
|
||||
(mf/deps fonts)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(dom/prevent-default event)
|
||||
(swap! selected get-prev-font fonts)))
|
||||
|
||||
on-key-down
|
||||
(mf/use-fn
|
||||
(mf/deps fonts)
|
||||
(fn [event]
|
||||
(cond
|
||||
(kbd/up-arrow? event) (select-prev event)
|
||||
(kbd/down-arrow? event) (select-next event)
|
||||
(kbd/esc? event) (on-close)
|
||||
(kbd/enter? event) (on-close)
|
||||
:else (dom/focus! (mf/ref-val input)))))
|
||||
|
||||
on-filter-change
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(swap! state* assoc :term event)))
|
||||
|
||||
on-select-and-close
|
||||
(mf/use-fn
|
||||
(mf/deps on-select on-close)
|
||||
(fn [font]
|
||||
(on-select font)
|
||||
(on-close)))]
|
||||
|
||||
(mf/with-effect [fonts]
|
||||
(let [key (events/listen js/document "keydown" on-key-down)]
|
||||
#(events/unlistenByKey key)))
|
||||
|
||||
(mf/with-effect [@selected]
|
||||
(when-let [inst (mf/ref-val flist)]
|
||||
(when-let [index (:index @selected)]
|
||||
(.scrollToRow ^js inst index))))
|
||||
|
||||
(mf/with-effect [@selected]
|
||||
(on-select @selected))
|
||||
|
||||
(mf/with-effect []
|
||||
(st/emit! (dsc/push-shortcuts :typography {}))
|
||||
(fn []
|
||||
(st/emit! (dsc/pop-shortcuts :typography))))
|
||||
|
||||
(mf/with-effect []
|
||||
(let [index (d/index-of-pred fonts #(= (:id %) (:id current-font)))
|
||||
inst (mf/ref-val flist)]
|
||||
(tm/schedule
|
||||
#(let [offset (.getOffsetForRow ^js inst #js {:alignment "center" :index index})]
|
||||
(.scrollToPosition ^js inst offset)))))
|
||||
|
||||
[:div {:class (stl/css :font-selector)}
|
||||
[:div {:class (stl/css-case :font-selector-dropdown true :font-selector-dropdown-full-size full-size?)}
|
||||
[:div {:class (stl/css :header)}
|
||||
[:> search-bar* {:on-change on-filter-change
|
||||
:value (:term state)
|
||||
:auto-focus true
|
||||
:placeholder (tr "workspace.options.search-font")}]
|
||||
(when (and recent-fonts show-recent)
|
||||
[:section {:class (stl/css :show-recent)}
|
||||
[:p {:class (stl/css :title)} (tr "workspace.options.recent-fonts")]
|
||||
(for [[idx font] (d/enumerate recent-fonts)]
|
||||
[:> font-item* {:key (dm/str "font-" idx)
|
||||
:font font
|
||||
:style {}
|
||||
:on-click on-select-and-close
|
||||
:is-current (= (:id font) (:id @selected))}])])]
|
||||
|
||||
[:div {:class (stl/css-case :fonts-list true
|
||||
:fonts-list-full-size full-size?)}
|
||||
[:> rvt/AutoSizer {}
|
||||
(fn [props]
|
||||
(let [width (unchecked-get props "width")
|
||||
height (unchecked-get props "height")
|
||||
render #(row-renderer fonts @selected on-select-and-close %)]
|
||||
(mf/html
|
||||
[:> rvt/List #js {:height height
|
||||
:ref flist
|
||||
:width width
|
||||
:rowCount (count fonts)
|
||||
:rowHeight 36
|
||||
:rowRenderer render}])))]]]]))
|
||||
|
||||
(defn row-renderer
|
||||
[fonts selected on-select props]
|
||||
(let [index (unchecked-get props "index")
|
||||
key (unchecked-get props "key")
|
||||
style (unchecked-get props "style")
|
||||
font (nth fonts index)]
|
||||
(mf/html
|
||||
[:> font-item* {:key key
|
||||
:font font
|
||||
:style style
|
||||
:on-click on-select
|
||||
:is-current (= (:id font) (:id selected))}])))
|
||||
|
||||
(mf/defc font-options
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [values on-change on-blur show-recent full-size-selector]}]
|
||||
(let [{:keys [font-id font-size font-variant-id]} values
|
||||
|
||||
font-id (or font-id (:font-id txt/default-typography))
|
||||
font-size (or font-size (:font-size txt/default-typography))
|
||||
font-variant-id (or font-variant-id (:font-variant-id txt/default-typography))
|
||||
|
||||
fonts (mf/deref fonts/fontsdb)
|
||||
font (get fonts font-id)
|
||||
|
||||
last-font (mf/use-ref nil)
|
||||
|
||||
open-selector? (mf/use-state false)
|
||||
|
||||
change-font
|
||||
(mf/use-fn
|
||||
(mf/deps on-change fonts)
|
||||
(fn [new-font-id]
|
||||
(let [{:keys [family] :as font} (get fonts new-font-id)
|
||||
{:keys [id name weight style]} (fonts/get-default-variant font)]
|
||||
(on-change {:font-id new-font-id
|
||||
:font-family family
|
||||
:font-variant-id (or id name)
|
||||
:font-weight weight
|
||||
:font-style style})
|
||||
(mf/set-ref-val! last-font font))))
|
||||
|
||||
on-font-size-change
|
||||
(mf/use-fn
|
||||
(mf/deps on-change)
|
||||
(fn [new-font-size]
|
||||
(when-not (str/empty? new-font-size)
|
||||
(on-change {:font-size (str new-font-size)}))))
|
||||
|
||||
on-font-variant-change
|
||||
(mf/use-fn
|
||||
(mf/deps font on-change)
|
||||
(fn [new-variant-id]
|
||||
(let [variant (d/seek #(= new-variant-id (:id %)) (:variants font))]
|
||||
(when-not (nil? variant)
|
||||
(on-change {:font-id (:id font)
|
||||
:font-family (:family font)
|
||||
:font-variant-id new-variant-id
|
||||
:font-weight (:weight variant)
|
||||
:font-style (:style variant)}))
|
||||
;; NOTE: the select component we are using does not fire on-blur event
|
||||
;; so we need to call on-blur manually
|
||||
(when (some? on-blur)
|
||||
(on-blur)))))
|
||||
|
||||
on-font-select
|
||||
(mf/use-fn
|
||||
(mf/deps change-font)
|
||||
(fn [font*]
|
||||
(when (not= font font*)
|
||||
(change-font (:id font*)))
|
||||
|
||||
(when (some? on-blur)
|
||||
(on-blur))))
|
||||
|
||||
on-font-selector-close
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
(reset! open-selector? false)
|
||||
(when (some? on-blur)
|
||||
(on-blur))
|
||||
(when (mf/ref-val last-font)
|
||||
(st/emit! (fts/add-recent-font (mf/ref-val last-font))))))]
|
||||
|
||||
[:*
|
||||
(when @open-selector?
|
||||
[:> font-selector*
|
||||
{:current-font font
|
||||
:on-close on-font-selector-close
|
||||
:on-select on-font-select
|
||||
:full-size full-size-selector
|
||||
:show-recent show-recent}])
|
||||
|
||||
[:div {:class (stl/css :font-option)
|
||||
:title (tr "inspect.attributes.typography.font-family")
|
||||
:on-click #(reset! open-selector? true)}
|
||||
(cond
|
||||
(or (= :multiple font-id) (= "mixed" font-id))
|
||||
"--"
|
||||
|
||||
(some? font)
|
||||
[:*
|
||||
[:span {:class (stl/css :name)}
|
||||
(:name font)]
|
||||
[:span {:class (stl/css :icon)}
|
||||
deprecated-icon/arrow]]
|
||||
|
||||
:else
|
||||
(tr "dashboard.fonts.deleted-placeholder"))]
|
||||
|
||||
[:div {:class (stl/css :font-modifiers)}
|
||||
[:div {:class (stl/css :font-size-options)
|
||||
:title (tr "inspect.attributes.typography.font-size")}
|
||||
(let [size-options [8 9 10 11 12 14 16 18 24 36 48 72]
|
||||
size-options (if (= font-size :multiple) (into [""] size-options) size-options)]
|
||||
[:& editable-select
|
||||
{:value (if (= font-size :multiple) :multiple (attr->string font-size))
|
||||
:class (stl/css :font-size-select)
|
||||
:aria-label (tr "inspect.attributes.typography.font-size")
|
||||
:input-class (stl/css :numeric-input)
|
||||
:options size-options
|
||||
:type "number"
|
||||
:placeholder (tr "settings.multiple")
|
||||
:min 3
|
||||
:max 1000
|
||||
:on-change on-font-size-change
|
||||
:on-blur on-blur}])]
|
||||
|
||||
[:div {:class (stl/css :font-variant-options)
|
||||
:title (tr "inspect.attributes.typography.font-style")}
|
||||
(let [basic-variant-options (->> (:variants font)
|
||||
(map (fn [variant]
|
||||
{:value (:id variant)
|
||||
:key (pr-str variant)
|
||||
:label (:name variant)})))
|
||||
variant-options (if (or (= font-variant-id :multiple) (= font-variant-id "mixed"))
|
||||
(conj basic-variant-options
|
||||
{:value ""
|
||||
:key :multiple-variants
|
||||
:label "--"})
|
||||
basic-variant-options)
|
||||
font-variant-value (attr->string font-variant-id)
|
||||
font-variant-value (if (= font-variant-value "mixed") "" font-variant-value)]
|
||||
|
||||
;; TODO Add disabled mode
|
||||
[:& select
|
||||
{:class (stl/css :font-variant-select)
|
||||
:default-value font-variant-value
|
||||
:options variant-options
|
||||
:on-change on-font-variant-change
|
||||
:on-blur on-blur}])]]]))
|
||||
|
||||
(mf/defc spacing-options
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [values on-change on-blur]}]
|
||||
(let [{:keys [line-height
|
||||
letter-spacing]} values
|
||||
line-height (or line-height "1.2")
|
||||
letter-spacing (or letter-spacing "0")
|
||||
handle-change
|
||||
(fn [value attr]
|
||||
(on-change {attr (str value)}))]
|
||||
|
||||
[:div {:class (stl/css :spacing-options)}
|
||||
[:div {:class (stl/css :line-height)
|
||||
:title (tr "inspect.attributes.typography.line-height")}
|
||||
[:span {:class (stl/css :icon)
|
||||
:alt (tr "workspace.options.text-options.line-height")}
|
||||
deprecated-icon/text-lineheight]
|
||||
[:> numeric-input*
|
||||
{:min -200
|
||||
:max 200
|
||||
:step 0.1
|
||||
:default-value "1.2"
|
||||
:class (stl/css :line-height-input)
|
||||
:aria-label (tr "inspect.attributes.typography.line-height")
|
||||
:value (attr->string line-height)
|
||||
:placeholder (if (= :multiple line-height) (tr "settings.multiple") "--")
|
||||
:nillable (= :multiple line-height)
|
||||
:on-change #(handle-change % :line-height)
|
||||
:on-blur on-blur}]]
|
||||
|
||||
[:div {:class (stl/css :letter-spacing)
|
||||
:title (tr "inspect.attributes.typography.letter-spacing")}
|
||||
[:span
|
||||
{:class (stl/css :icon)
|
||||
:alt (tr "workspace.options.text-options.letter-spacing")}
|
||||
deprecated-icon/text-letterspacing]
|
||||
[:> numeric-input*
|
||||
{:min -200
|
||||
:max 200
|
||||
:step 0.1
|
||||
:default-value "0"
|
||||
:class (stl/css :letter-spacing-input)
|
||||
:aria-label (tr "inspect.attributes.typography.letter-spacing")
|
||||
:value (attr->string letter-spacing)
|
||||
:placeholder (if (= :multiple letter-spacing) (tr "settings.multiple") "--")
|
||||
:on-change #(handle-change % :letter-spacing)
|
||||
:nillable (= :multiple letter-spacing)
|
||||
:on-blur on-blur}]]]))
|
||||
|
||||
(mf/defc text-transform-options
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [values on-change on-blur]}]
|
||||
(let [text-transform (or (:text-transform values) "none")
|
||||
unset-value (if (features/active-feature? @st/state "text-editor/v2") "none" "unset")
|
||||
handle-change
|
||||
(fn [type]
|
||||
(if (= text-transform type)
|
||||
(on-change {:text-transform unset-value})
|
||||
(on-change {:text-transform type}))
|
||||
(when (some? on-blur) (on-blur)))]
|
||||
|
||||
[:div {:class (stl/css :text-transform)}
|
||||
[:& radio-buttons {:selected text-transform
|
||||
:on-change handle-change
|
||||
:name "text-transform"}
|
||||
[:& radio-button {:icon i/text-uppercase
|
||||
:type "checkbox"
|
||||
:title (tr "inspect.attributes.typography.text-transform.uppercase")
|
||||
:value "uppercase"
|
||||
:id "text-transform-uppercase"}]
|
||||
[:& radio-button {:icon i/text-mixed
|
||||
:type "checkbox"
|
||||
:value "capitalize"
|
||||
:title (tr "inspect.attributes.typography.text-transform.capitalize")
|
||||
:id "text-transform-capitalize"}]
|
||||
[:& radio-button {:icon i/text-lowercase
|
||||
:type "checkbox"
|
||||
:title (tr "inspect.attributes.typography.text-transform.lowercase")
|
||||
:value "lowercase"
|
||||
:id "text-transform-lowercase"}]]]))
|
||||
|
||||
(mf/defc text-options
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [ids editor values on-change on-blur show-recent]}]
|
||||
(let [full-size-selector? (and show-recent (= (mf/use-ctx ctx/sidebar) :right))
|
||||
opts #js {:editor editor
|
||||
:ids ids
|
||||
:values values
|
||||
:on-change on-change
|
||||
:on-blur on-blur
|
||||
:show-recent show-recent
|
||||
:full-size-selector full-size-selector?}]
|
||||
[:div {:class (stl/css-case :text-options true
|
||||
:text-options-full-size full-size-selector?)}
|
||||
[:> font-options opts]
|
||||
[:div {:class (stl/css :typography-variations)}
|
||||
[:> spacing-options opts]
|
||||
[:> text-transform-options opts]]]))
|
||||
|
||||
(mf/defc typography-advanced-options
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [visible? typography editable? name-input-ref on-close on-change on-name-blur
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.config :as cf]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
||||
[app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu*]]
|
||||
@@ -32,10 +33,11 @@
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [shape shapes-with-children libraries file-id page-id]}]
|
||||
|
||||
(let [id (dm/get-prop shape :id)
|
||||
type (dm/get-prop shape :type)
|
||||
ids (mf/with-memo [id] [id])
|
||||
shapes (mf/with-memo [shape] [shape])
|
||||
(let [id (dm/get-prop shape :id)
|
||||
type (dm/get-prop shape :type)
|
||||
ids (mf/with-memo [id] [id])
|
||||
shapes (mf/with-memo [shape] [shape])
|
||||
token-row (contains? cf/flags :token-typography-row)
|
||||
|
||||
applied-tokens
|
||||
(get shape :applied-tokens)
|
||||
@@ -171,10 +173,29 @@
|
||||
[:& 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
|
||||
:applied-tokens text-tokens}])
|
||||
;; We are temporarily duplicating some components and modifying the copies
|
||||
;; to work under a feature flag. When the flag is set to false, the original
|
||||
;; components are used; when enabled, the new versions are used instead.
|
||||
;;
|
||||
;; This approach introduces some code duplication, but it helps avoid
|
||||
;; scattering conditional (feature flag) logic throughout the codebase,
|
||||
;; keeping both implementations easier to read and maintain during the transition.
|
||||
;;
|
||||
;; Once the feature flag is fully enabled, the old components will be removed
|
||||
;; and the duplicated code will be cleaned up.
|
||||
|
||||
(if token-row
|
||||
[:> ot/token-text-menu*
|
||||
{:ids text-ids
|
||||
:type type
|
||||
:applied-tokens text-tokens
|
||||
:values text-values}]
|
||||
|
||||
[:> ot/text-menu*
|
||||
{:ids text-ids
|
||||
:type type
|
||||
:applied-tokens text-tokens
|
||||
:values text-values}]))
|
||||
|
||||
(when-not (empty? svg-values)
|
||||
[:& svg-attrs-menu {:ids ids :values svg-values}])
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
[app.common.types.text :as txt]
|
||||
[app.common.types.token :as tt]
|
||||
[app.common.weak :as weak]
|
||||
[app.config :as cf]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-attrs blur-menu]]
|
||||
[app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu*]]
|
||||
@@ -326,7 +327,8 @@
|
||||
(mf/defc options*
|
||||
{::mf/wrap [#(mf/memo' % check-options-props)]}
|
||||
[{:keys [shapes shapes-with-children page-id file-id libraries] :as props}]
|
||||
(let [shape-ids
|
||||
(let [token-row (contains? cf/flags :token-typography-row)
|
||||
shape-ids
|
||||
(mf/with-memo [shapes]
|
||||
(into #{} d/xf:map-id shapes))
|
||||
|
||||
@@ -480,12 +482,29 @@
|
||||
(when-not (or (empty? constraint-ids) ^boolean is-layout-child?)
|
||||
[:& constraints-menu {:ids constraint-ids :values constraint-values}])
|
||||
|
||||
;; We are temporarily duplicating some components and modifying the copies
|
||||
;; to work under a feature flag. When the flag is set to false, the original
|
||||
;; components are used; when enabled, the new versions are used instead.
|
||||
;;
|
||||
;; This approach introduces some code duplication, but it helps avoid
|
||||
;; scattering conditional (feature flag) logic throughout the codebase,
|
||||
;; keeping both implementations easier to read and maintain during the transition.
|
||||
;;
|
||||
;; Once the feature flag is fully enabled, the old components will be removed
|
||||
;; and the duplicated code will be cleaned up.
|
||||
(when-not (empty? text-ids)
|
||||
[:> ot/text-menu*
|
||||
{:type type
|
||||
:ids text-ids
|
||||
:values text-values
|
||||
:applied-tokens text-tokens}])
|
||||
(if token-row
|
||||
[:> ot/token-text-menu*
|
||||
{:ids text-ids
|
||||
:type type
|
||||
:applied-tokens text-tokens
|
||||
:values text-values}]
|
||||
|
||||
[:> ot/text-menu*
|
||||
{:ids text-ids
|
||||
:type type
|
||||
:applied-tokens text-tokens
|
||||
:values text-values}]))
|
||||
|
||||
(when-not (empty? fill-ids)
|
||||
[:> fill/fill-menu* {:type type
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.common.types.text :as txt]
|
||||
[app.config :as cf]
|
||||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.features :as features]
|
||||
[app.main.refs :as refs]
|
||||
@@ -26,7 +27,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* token-text-menu*]]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc options*
|
||||
@@ -35,6 +36,7 @@
|
||||
type (dm/get-prop shape :type)
|
||||
ids (mf/with-memo [id] [id])
|
||||
shapes (mf/with-memo [shape] [shape])
|
||||
token-row (contains? cf/flags :token-typography-row)
|
||||
|
||||
applied-tokens
|
||||
(get shape :applied-tokens)
|
||||
@@ -176,11 +178,18 @@
|
||||
{:ids ids
|
||||
:values (select-keys shape constraint-attrs)}])
|
||||
|
||||
[:> text-menu*
|
||||
{:ids ids
|
||||
:type type
|
||||
:applied-tokens applied-tokens
|
||||
:values text-values}]
|
||||
(if token-row
|
||||
[:> token-text-menu*
|
||||
{:ids ids
|
||||
:type type
|
||||
:applied-tokens applied-tokens
|
||||
:values text-values}]
|
||||
|
||||
[:> text-menu*
|
||||
{:ids ids
|
||||
:type type
|
||||
:applied-tokens applied-tokens
|
||||
:values text-values}])
|
||||
|
||||
[:> fill/fill-menu*
|
||||
{:ids ids
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@
|
||||
[app.main.ui.ds.controls.input :refer [input*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.forms :as fc]
|
||||
[app.main.ui.workspace.sidebar.options.menus.typography :refer [font-selector*]]
|
||||
[app.main.ui.workspace.sidebar.options.menus.text-shared :refer [font-selector*]]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.forms :as fm]
|
||||
[app.util.i18n :refer [tr]]
|
||||
|
||||
Reference in new issue
Block a user