mirror of
https://github.com/penpot/penpot.git
synced 2026-09-07 12:40:12 +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.controls.shared.searchable-options-dropdown :refer [searchable-options-dropdown*]]
|
||||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||||
[app.main.ui.hooks :as hooks]
|
[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.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.main.ui.workspace.tokens.management.forms.controls.utils :as csu]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[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
|
(ns app.main.ui.workspace.sidebar.options.menus.typography
|
||||||
(:require-macros [app.main.style :as stl])
|
(:require-macros [app.main.style :as stl])
|
||||||
(:require
|
(: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.constants :refer [max-input-length]]
|
||||||
[app.main.data.common :as dcm]
|
[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.libraries :as dwl]
|
||||||
[app.main.data.workspace.undo :as dwu]
|
[app.main.data.workspace.undo :as dwu]
|
||||||
[app.main.features :as features]
|
|
||||||
[app.main.fonts :as fonts]
|
[app.main.fonts :as fonts]
|
||||||
[app.main.refs :as refs]
|
|
||||||
[app.main.store :as st]
|
[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.context :as ctx]
|
||||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||||
[app.main.ui.icons :as deprecated-icon]
|
[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.dom :as dom]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
[app.util.keyboard :as kbd]
|
[app.util.keyboard :as kbd]
|
||||||
[app.util.strings :as ust]
|
|
||||||
[app.util.timers :as tm]
|
[app.util.timers :as tm]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[goog.events :as events]
|
|
||||||
[rumext.v2 :as mf]))
|
[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/defc typography-advanced-options
|
||||||
{::mf/wrap [mf/memo]}
|
{::mf/wrap [mf/memo]}
|
||||||
[{:keys [visible? typography editable? name-input-ref on-close on-change on-name-blur
|
[{: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.controls.input :refer [input*]]
|
||||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||||
[app.main.ui.forms :as fc]
|
[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.dom :as dom]
|
||||||
[app.util.forms :as fm]
|
[app.util.forms :as fm]
|
||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user