mirror of
https://github.com/penpot/penpot.git
synced 2026-04-25 11:18:36 +00:00
♻️ Divide components into different files
This commit is contained in:
parent
c10f945473
commit
3e12af47f0
@ -28,8 +28,9 @@
|
||||
[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.workspace.sidebar.options.menus.text-shared :refer [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]]
|
||||
|
||||
@ -0,0 +1,463 @@
|
||||
;; 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]]]))
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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]]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user