mirror of
https://github.com/penpot/penpot.git
synced 2025-05-21 02:26:11 +02:00
🎉 Add new font selector to workspace.
This commit is contained in:
parent
8831f3241c
commit
2ea200be78
19 changed files with 800 additions and 199 deletions
|
@ -13,6 +13,7 @@
|
|||
[app.main.repo :as rp]
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.users :as du]
|
||||
[app.main.data.fonts :as df]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.router :as rt]
|
||||
[app.util.time :as dt]
|
||||
|
@ -86,6 +87,7 @@
|
|||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(rx/merge
|
||||
(ptk/watch (df/load-team-fonts id) state stream)
|
||||
(ptk/watch (fetch-projects) state stream)
|
||||
(ptk/watch (du/fetch-teams) state stream)
|
||||
(ptk/watch (du/fetch-users {:team-id id}) state stream)))))
|
||||
|
|
57
frontend/src/app/main/data/fonts.cljs
Normal file
57
frontend/src/app/main/data/fonts.cljs
Normal file
|
@ -0,0 +1,57 @@
|
|||
;; 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) UXBOX Labs SL
|
||||
|
||||
(ns app.main.data.fonts
|
||||
(:require
|
||||
[app.common.media :as cm]
|
||||
[app.main.fonts :as fonts]
|
||||
[app.main.repo :as rp]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[beicon.core :as rx]
|
||||
[cljs.spec.alpha :as s]
|
||||
[cuerdas.core :as str]
|
||||
[potok.core :as ptk]))
|
||||
|
||||
(defn prepare-font-variant
|
||||
[item]
|
||||
{:id (str (:font-style item) "-" (:font-weight item))
|
||||
:name (str (cm/font-weight->name (:font-weight item)) " "
|
||||
(str/capital (:font-style item)))
|
||||
:style (:font-style item)
|
||||
:weight (str (:font-weight item))
|
||||
::fonts/woff1-file-id (:woff1-file-id item)
|
||||
::fonts/woff2-file-id (:woff2-file-id item)
|
||||
::fonts/ttf-file-id (:ttf-file-id item)
|
||||
::fonts/otf-file-id (:otf-file-id item)})
|
||||
|
||||
(defn prepare-font
|
||||
[[id [item :as items]]]
|
||||
{:id id
|
||||
:name (:font-family item)
|
||||
:family (:font-family item)
|
||||
:variants (mapv prepare-font-variant items)})
|
||||
|
||||
(defn team-fonts-loaded
|
||||
[fonts]
|
||||
(ptk/reify ::team-fonts-loaded
|
||||
ptk/EffectEvent
|
||||
(effect [_ state stream]
|
||||
(let [fonts (->> (group-by :font-id fonts)
|
||||
(mapv prepare-font))]
|
||||
(fonts/register! :custom fonts)))))
|
||||
|
||||
(defn load-team-fonts
|
||||
[team-id]
|
||||
(ptk/reify ::load-team-fonts
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(->> (rp/query :team-font-variants {:team-id team-id})
|
||||
(rx/map team-fonts-loaded)))))
|
||||
|
||||
|
||||
(defn get-fonts
|
||||
[backend]
|
||||
(get @fonts/fonts backend []))
|
|
@ -1258,6 +1258,15 @@
|
|||
(rx/of ::dwp/force-persist
|
||||
(rt/nav :dashboard-projects {:team-id team-id})))))))
|
||||
|
||||
(defn go-to-dashboard-fonts
|
||||
[]
|
||||
(ptk/reify ::go-to-dashboard
|
||||
ptk/WatchEvent
|
||||
(watch [it state stream]
|
||||
(let [team-id (:current-team-id state)]
|
||||
(rx/of ::dwp/force-persist
|
||||
(rt/nav :dashboard-fonts {:team-id team-id}))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Context Menu
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
[app.common.uuid :as uuid]
|
||||
[app.main.data.dashboard :as dd]
|
||||
[app.main.data.media :as di]
|
||||
[app.main.data.fonts :as df]
|
||||
[app.main.data.messages :as dm]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
|
@ -270,7 +271,8 @@
|
|||
:project project
|
||||
:libraries libraries}))
|
||||
(rx/mapcat (fn [{:keys [project] :as bundle}]
|
||||
(rx/of (ptk/data-event ::bundle-fetched bundle))))))))
|
||||
(rx/of (ptk/data-event ::bundle-fetched bundle)
|
||||
(df/load-team-fonts (:team-id project)))))))))
|
||||
|
||||
;; --- Set File shared
|
||||
|
||||
|
|
|
@ -8,16 +8,22 @@
|
|||
"Fonts management and loading logic."
|
||||
(:require-macros [app.main.fonts :refer [preload-gfonts]])
|
||||
(:require
|
||||
[app.config :as cf]
|
||||
[app.common.data :as d]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.object :as obj]
|
||||
[app.util.timers :as ts]
|
||||
[app.util.logging :as log]
|
||||
[lambdaisland.uri :as u]
|
||||
[goog.events :as gev]
|
||||
[beicon.core :as rx]
|
||||
[clojure.set :as set]
|
||||
[cuerdas.core :as str]
|
||||
[okulary.core :as l]
|
||||
[promesa.core :as p]))
|
||||
|
||||
(log/set-level! :trace)
|
||||
|
||||
(def google-fonts
|
||||
(preload-gfonts "fonts/gfonts.2020.04.23.json"))
|
||||
|
||||
|
@ -38,22 +44,27 @@
|
|||
{:id "blackitalic" :name "black (italic)" :weight "900" :style "italic"}]}])
|
||||
|
||||
(defonce fontsdb (l/atom {}))
|
||||
(defonce fontsview (l/atom {}))
|
||||
(defonce fonts (l/atom []))
|
||||
|
||||
(defn- materialize-fontsview
|
||||
[db]
|
||||
(reset! fontsview (reduce-kv (fn [acc k v]
|
||||
(assoc acc k (sort-by :name v)))
|
||||
{}
|
||||
(group-by :backend (vals db)))))
|
||||
(add-watch fontsdb "main"
|
||||
(fn [_ _ _ db]
|
||||
(ts/schedule #(materialize-fontsview db))))
|
||||
(->> (vals db)
|
||||
(sort-by :name)
|
||||
(map-indexed #(assoc %2 :index %1))
|
||||
(vec)
|
||||
(reset! fonts))))
|
||||
|
||||
(defn- remove-fonts
|
||||
[db backend]
|
||||
(reduce-kv #(cond-> %1 (= backend (:backend %3)) (dissoc %2)) db db))
|
||||
|
||||
(defn register!
|
||||
[backend fonts]
|
||||
(let [fonts (map #(assoc % :backend backend) fonts)]
|
||||
(swap! fontsdb #(merge % (d/index-by :id fonts)))))
|
||||
(swap! fontsdb
|
||||
(fn [db]
|
||||
(let [db (reduce-kv #(cond-> %1 (= backend (:backend %3)) (dissoc %2)) db db)
|
||||
fonts (map #(assoc % :backend backend) fonts)]
|
||||
(merge db (d/index-by :id fonts))))))
|
||||
|
||||
(register! :builtin local-fonts)
|
||||
(register! :google google-fonts)
|
||||
|
@ -67,13 +78,15 @@
|
|||
|
||||
(defn resolve-fonts
|
||||
[backend]
|
||||
(get @fontsview backend))
|
||||
(get @fonts backend))
|
||||
|
||||
;; --- Fonts Loader
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; FONTS LOADING
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defonce loaded (l/atom #{}))
|
||||
|
||||
(defn- create-link-node
|
||||
(defn- create-link-element
|
||||
[uri]
|
||||
(let [node (.createElement js/document "link")]
|
||||
(unchecked-set node "href" uri)
|
||||
|
@ -81,32 +94,107 @@
|
|||
(unchecked-set node "type" "text/css")
|
||||
node))
|
||||
|
||||
(defn gfont-url [family variants]
|
||||
|
||||
(defn- create-style-element
|
||||
[css]
|
||||
(let [node (.createElement js/document "style")]
|
||||
(unchecked-set node "innerHTML" css)
|
||||
node))
|
||||
|
||||
(defn- load-font-css!
|
||||
"Creates a link element and attaches it to the dom for correctly
|
||||
load external css resource."
|
||||
[url on-loaded]
|
||||
(let [node (create-link-element url)
|
||||
head (.-head ^js js/document)]
|
||||
(gev/listenOnce node "load" (fn [event]
|
||||
(when (fn? on-loaded)
|
||||
(on-loaded))))
|
||||
(dom/append-child! head node)))
|
||||
|
||||
(defn- add-font-css!
|
||||
"Creates a style element and attaches it to the dom."
|
||||
[css]
|
||||
(let [head (.-head ^js js/document)]
|
||||
(->> (create-style-element css)
|
||||
(dom/append-child! head))))
|
||||
|
||||
;; --- LOADER: BUILTIN
|
||||
|
||||
(defmulti ^:private load-font :backend)
|
||||
|
||||
(defmethod load-font :default
|
||||
[{:keys [backend] :as font}]
|
||||
(log/warn :msg "no implementation found for" :backend backend))
|
||||
|
||||
(defmethod load-font :builtin
|
||||
[{:keys [id ::on-loaded] :as font}]
|
||||
(log/debug :action "load-font" :font-id id :backend "builtin")
|
||||
;; (js/console.log "[debug:fonts]: loading builtin font" id)
|
||||
(when (fn? on-loaded)
|
||||
(on-loaded id)))
|
||||
|
||||
;; --- LOADER: GOOGLE
|
||||
|
||||
(defn generate-gfonts-url
|
||||
[{:keys [family variants]}]
|
||||
(let [base (str "https://fonts.googleapis.com/css?family=" family)
|
||||
variants (str/join "," (map :id variants))]
|
||||
(str base ":" variants "&display=block")))
|
||||
|
||||
(defmulti ^:private load-font :backend)
|
||||
|
||||
(defmethod load-font :builtin
|
||||
[{:keys [id ::on-loaded] :as font}]
|
||||
(js/console.log "[debug:fonts]: loading builtin font" id)
|
||||
(when (fn? on-loaded)
|
||||
(on-loaded id)))
|
||||
|
||||
(defmethod load-font :google
|
||||
[{:keys [id family variants ::on-loaded] :as font}]
|
||||
(when (exists? js/window)
|
||||
(js/console.log "[debug:fonts]: loading google font" id)
|
||||
(let [node (create-link-node (gfont-url family variants))]
|
||||
(.addEventListener node "load" (fn [event] (when (fn? on-loaded)
|
||||
(on-loaded id))))
|
||||
(.append (.-head js/document) node)
|
||||
(log/debug :action "load-font" :font-id id :backend "google")
|
||||
(let [url (generate-gfonts-url font)]
|
||||
(load-font-css! url (partial on-loaded id))
|
||||
nil)))
|
||||
|
||||
(defmethod load-font :default
|
||||
[{:keys [backend] :as font}]
|
||||
(js/console.warn "no implementation found for" backend))
|
||||
;; --- LOADER: CUSTOM
|
||||
|
||||
(def font-css-template
|
||||
"@font-face {
|
||||
font-family: '%(family)s';
|
||||
font-style: %(style)s;
|
||||
font-weight: %(weight)s;
|
||||
font-display: block;
|
||||
src: url(%(woff2-uri)s) format('woff2'),
|
||||
url(%(woff1-uri)s) format('woff'),
|
||||
url(%(ttf-uri)s) format('ttf'),
|
||||
url(%(otf-uri)s) format('otf');
|
||||
}")
|
||||
|
||||
(defn- font-id->uri
|
||||
[font-id]
|
||||
(str (u/join cf/public-uri "assets/by-id/" font-id)))
|
||||
|
||||
(defn generate-custom-font-variant-css
|
||||
[family variant]
|
||||
(str/fmt font-css-template
|
||||
{:family family
|
||||
:style (:style variant)
|
||||
:weight (:weight variant)
|
||||
:woff2-uri (font-id->uri (::woff2-file-id variant))
|
||||
:woff1-uri (font-id->uri (::woff1-file-id variant))
|
||||
:ttf-uri (font-id->uri (::ttf-file-id variant))
|
||||
:otf-uri (font-id->uri (::otf-file-id variant))}))
|
||||
|
||||
(defn- generate-custom-font-css
|
||||
[{:keys [family variants] :as font}]
|
||||
(->> variants
|
||||
(map #(generate-custom-font-variant-css family %))
|
||||
(str/join "\n")))
|
||||
|
||||
(defmethod load-font :custom
|
||||
[{:keys [id family variants ::on-loaded] :as font}]
|
||||
(when (exists? js/window)
|
||||
(js/console.log "[debug:fonts]: loading google font" id)
|
||||
(let [css (generate-custom-font-css font)]
|
||||
(add-font-css! css))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; LOAD API
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn ensure-loaded!
|
||||
([id]
|
||||
|
@ -119,7 +207,8 @@
|
|||
(load-font (assoc font ::on-loaded on-loaded))
|
||||
(swap! loaded conj id)))))
|
||||
|
||||
(defn ready [cb]
|
||||
(defn ready
|
||||
[cb]
|
||||
(-> (obj/get-in js/document ["fonts" "ready"])
|
||||
(p/then cb)))
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
[props]
|
||||
(let [children (gobj/get props "children")
|
||||
on-close (gobj/get props "on-close")
|
||||
ref (gobj/get props "container")
|
||||
ref (gobj/get props "container")
|
||||
|
||||
on-click
|
||||
(fn [event]
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
"Given a font and the variant-id, retrieves the style CSS for it."
|
||||
[{:keys [id backend family variants] :as font} font-variant-id]
|
||||
(if (= :google backend)
|
||||
(let [uri (fonts/gfont-url family [{:id font-variant-id}])]
|
||||
(let [uri (fonts/generate-gfonts-url {:family family :variants [{:id font-variant-id}]})]
|
||||
(->> (http/send! {:method :get
|
||||
:mode :cors
|
||||
:omit-default-headers true
|
||||
|
|
|
@ -128,7 +128,6 @@
|
|||
:on-click #(handle-change % "rtl")}
|
||||
i/text-direction-rtl]]))
|
||||
|
||||
|
||||
(mf/defc vertical-align
|
||||
[{:keys [shapes ids values on-change] :as props}]
|
||||
(let [{:keys [vertical-align]} values
|
||||
|
@ -225,63 +224,73 @@
|
|||
(tr "workspace.options.text-options.title"))
|
||||
|
||||
emit-update!
|
||||
(fn [id attrs]
|
||||
(let [attrs (select-keys attrs root-attrs)]
|
||||
(when-not (empty? attrs)
|
||||
(st/emit! (dwt/update-root-attrs {:id id :attrs attrs}))))
|
||||
(mf/use-callback
|
||||
(fn [id attrs]
|
||||
(let [attrs (select-keys attrs root-attrs)]
|
||||
(when-not (empty? attrs)
|
||||
(st/emit! (dwt/update-root-attrs {:id id :attrs attrs}))))
|
||||
|
||||
(let [attrs (select-keys attrs paragraph-attrs)]
|
||||
(when-not (empty? attrs)
|
||||
(st/emit! (dwt/update-paragraph-attrs {:id id :attrs attrs}))))
|
||||
(let [attrs (select-keys attrs paragraph-attrs)]
|
||||
(when-not (empty? attrs)
|
||||
(st/emit! (dwt/update-paragraph-attrs {:id id :attrs attrs}))))
|
||||
|
||||
(let [attrs (select-keys attrs text-attrs)]
|
||||
(when-not (empty? attrs)
|
||||
(st/emit! (dwt/update-text-attrs {:id id :attrs attrs})))))
|
||||
(let [attrs (select-keys attrs text-attrs)]
|
||||
(when-not (empty? attrs)
|
||||
(st/emit! (dwt/update-text-attrs {:id id :attrs attrs}))))))
|
||||
|
||||
on-change
|
||||
(mf/use-callback
|
||||
(mf/deps ids)
|
||||
(fn [attrs]
|
||||
(run! #(emit-update! % attrs) ids)))
|
||||
|
||||
typography
|
||||
(cond
|
||||
(and (:typography-ref-id values)
|
||||
(not= (:typography-ref-id values) :multiple)
|
||||
(not= (:typography-ref-file values) file-id))
|
||||
(-> shared-libs
|
||||
(get-in [(:typography-ref-file values) :data :typographies (:typography-ref-id values)])
|
||||
(assoc :file-id (:typography-ref-file values)))
|
||||
(mf/use-memo
|
||||
(mf/deps values file-id shared-libs)
|
||||
(fn []
|
||||
(cond
|
||||
(and (:typography-ref-id values)
|
||||
(not= (:typography-ref-id values) :multiple)
|
||||
(not= (:typography-ref-file values) file-id))
|
||||
(-> shared-libs
|
||||
(get-in [(:typography-ref-file values) :data :typographies (:typography-ref-id values)])
|
||||
(assoc :file-id (:typography-ref-file values)))
|
||||
|
||||
(and (:typography-ref-id values)
|
||||
(not= (:typography-ref-id values) :multiple)
|
||||
(= (:typography-ref-file values) file-id))
|
||||
(get typographies (:typography-ref-id values)))
|
||||
(and (:typography-ref-id values)
|
||||
(not= (:typography-ref-id values) :multiple)
|
||||
(= (:typography-ref-file values) file-id))
|
||||
(get typographies (:typography-ref-id values)))))
|
||||
|
||||
on-convert-to-typography
|
||||
(mf/use-callback
|
||||
(mf/deps values)
|
||||
(fn [event]
|
||||
(let [setted-values (-> (d/without-nils values)
|
||||
(select-keys
|
||||
(d/concat text-font-attrs
|
||||
text-spacing-attrs
|
||||
text-transform-attrs)))
|
||||
typography (merge txt/default-typography setted-values)
|
||||
typography (generate-typography-name typography)]
|
||||
(let [id (uuid/next)]
|
||||
(st/emit! (dwl/add-typography (assoc typography :id id) false))
|
||||
(run! #(emit-update! % {:typography-ref-id id
|
||||
:typography-ref-file file-id}) ids)))))
|
||||
(fn [event]
|
||||
(let [setted-values (-> (d/without-nils values)
|
||||
(select-keys
|
||||
(d/concat text-font-attrs
|
||||
text-spacing-attrs
|
||||
text-transform-attrs)))
|
||||
typography (merge txt/default-typography setted-values)
|
||||
typography (generate-typography-name typography)]
|
||||
(let [id (uuid/next)]
|
||||
(st/emit! (dwl/add-typography (assoc typography :id id) false))
|
||||
(run! #(emit-update! % {:typography-ref-id id
|
||||
:typography-ref-file file-id}) ids))))
|
||||
|
||||
handle-detach-typography
|
||||
(fn []
|
||||
(run! #(emit-update! % {:typography-ref-file nil
|
||||
:typography-ref-id nil})
|
||||
ids))
|
||||
(mf/use-callback
|
||||
(mf/deps on-change)
|
||||
(fn []
|
||||
(on-change {:typography-ref-file nil
|
||||
:typography-ref-id nil})))
|
||||
|
||||
handle-change-typography
|
||||
(fn [changes]
|
||||
(st/emit! (dwl/update-typography (merge typography changes) file-id)))
|
||||
(mf/use-callback
|
||||
(mf/deps typography file-id)
|
||||
(fn [changes]
|
||||
(st/emit! (dwl/update-typography (merge typography changes) file-id))))
|
||||
|
||||
opts #js {:ids ids
|
||||
:values values
|
||||
:on-change (fn [attrs]
|
||||
(run! #(emit-update! % attrs) ids))}]
|
||||
:on-change on-change}]
|
||||
|
||||
[:div.element-set
|
||||
[:div.element-set-title
|
||||
|
|
|
@ -6,20 +6,30 @@
|
|||
|
||||
(ns app.main.ui.workspace.sidebar.options.menus.typography
|
||||
(:require
|
||||
["react-virtualized" :as rvt]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.data :as d]
|
||||
[app.common.pages :as cp]
|
||||
[app.common.text :as txt]
|
||||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.data.shortcuts :as dsc]
|
||||
[app.main.data.fonts :as df]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.fonts :as fonts]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||
[app.main.ui.components.editable-select :refer [editable-select]]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.workspace.sidebar.options.common :refer [advanced-options]]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [t]]
|
||||
[app.util.object :as obj]
|
||||
[app.util.timers :as tm]
|
||||
[app.util.keyboard :as kbd]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.router :as rt]
|
||||
[app.util.timers :as ts]
|
||||
[goog.events :as events]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
|
@ -28,74 +38,279 @@
|
|||
""
|
||||
(str value)))
|
||||
|
||||
(mf/defc font-select-optgroups
|
||||
(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 [locale] :as props}]
|
||||
[:*
|
||||
[:optgroup {:label (t locale "workspace.options.text-options.preset")}
|
||||
(for [font fonts/local-fonts]
|
||||
[:option {:value (:id font)
|
||||
:key (:id font)}
|
||||
(:name font)])]
|
||||
[:optgroup {:label (t locale "workspace.options.text-options.google")}
|
||||
(for [font (fonts/resolve-fonts :google)]
|
||||
[:option {:value (:id font)
|
||||
:key (:id font)}
|
||||
(:name font)])]])
|
||||
[{:keys [font current? on-click style]}]
|
||||
(let [item-ref (mf/use-ref)
|
||||
on-click (mf/use-callback (mf/deps font) #(on-click font))]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps current?)
|
||||
(fn []
|
||||
(when current?
|
||||
(let [element (mf/ref-val item-ref)]
|
||||
(when-not (dom/is-in-viewport? element)
|
||||
(dom/scroll-into-view! element))))))
|
||||
|
||||
[:div.font-item {:ref item-ref
|
||||
:style style
|
||||
:class (when current? "selected")
|
||||
:on-click on-click}
|
||||
[:span.icon (when current? i/tick)]
|
||||
[:span.label (:name font)]]))
|
||||
|
||||
(declare row-renderer)
|
||||
|
||||
(defn filter-fonts
|
||||
[{:keys [term backends]} fonts]
|
||||
(let [xform (cond-> (map identity)
|
||||
(seq term)
|
||||
(comp (filter #(str/includes? (str/lower (:name %)) term)))
|
||||
|
||||
(seq backends)
|
||||
(comp (filter #(contains? backends (:backend %)))))]
|
||||
(into [] xform fonts)))
|
||||
|
||||
(defn- toggle-backend
|
||||
[backends id]
|
||||
(if (contains? backends id)
|
||||
(disj backends id)
|
||||
(conj backends id)))
|
||||
|
||||
(mf/defc font-selector
|
||||
[{:keys [on-select on-close current-font] :as props}]
|
||||
(let [selected (mf/use-state current-font)
|
||||
state (mf/use-state {:term "" :backends #{}})
|
||||
|
||||
flist (mf/use-ref)
|
||||
input (mf/use-ref)
|
||||
ddown (mf/use-ref)
|
||||
|
||||
fonts (mf/use-memo (mf/deps @state) #(filter-fonts @state @fonts/fonts))
|
||||
|
||||
select-next
|
||||
(mf/use-callback
|
||||
(mf/deps fonts)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(dom/prevent-default event)
|
||||
(swap! selected get-next-font fonts)))
|
||||
|
||||
select-prev
|
||||
(mf/use-callback
|
||||
(mf/deps fonts)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(dom/prevent-default event)
|
||||
(swap! selected get-prev-font fonts)))
|
||||
|
||||
on-key-down
|
||||
(mf/use-callback
|
||||
(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-callback
|
||||
(mf/deps)
|
||||
(fn [event]
|
||||
(let [value (dom/get-target-val event)]
|
||||
(swap! state assoc :term value))))
|
||||
|
||||
on-select-and-close
|
||||
(mf/use-callback
|
||||
(mf/deps on-select on-close)
|
||||
(fn [font]
|
||||
(on-select font)
|
||||
(on-close)))
|
||||
]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps fonts)
|
||||
(fn []
|
||||
(let [key (events/listen js/document "keydown" on-key-down)]
|
||||
#(events/unlistenByKey key))))
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps @selected)
|
||||
(fn []
|
||||
(when-let [inst (mf/ref-val flist)]
|
||||
(when-let [index (:index @selected)]
|
||||
(.scrollToRow ^js inst index)))))
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps @selected)
|
||||
(fn []
|
||||
(on-select @selected)))
|
||||
|
||||
(mf/use-effect
|
||||
(fn []
|
||||
(st/emit! (dsc/push-shortcuts :typography {}))
|
||||
(fn []
|
||||
(st/emit! (dsc/pop-shortcuts :typography)))))
|
||||
|
||||
(mf/use-effect
|
||||
(fn []
|
||||
(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.font-selector
|
||||
[:div.font-selector-dropdown
|
||||
[:header
|
||||
[:input {:placeholder "Search font"
|
||||
:value (:term @state)
|
||||
:ref input
|
||||
:spell-check false
|
||||
:on-change on-filter-change}]
|
||||
|
||||
#_[:div.options
|
||||
{:on-click #(swap! state assoc :show-options true)
|
||||
:class (when (seq (:backends @state)) "active")}
|
||||
i/picker-hsv]
|
||||
|
||||
#_[:& dropdown {:show (:show-options @state false)
|
||||
:on-close #(swap! state dissoc :show-options)}
|
||||
(let [backends (:backends @state)]
|
||||
[:div.backend-filters.dropdown {:ref ddown}
|
||||
[:div.backend-filter
|
||||
{:class (when (backends :custom) "selected")
|
||||
:on-click #(swap! state update :backends toggle-backend :custom)}
|
||||
[:div.checkbox-icon i/tick]
|
||||
[:div.backend-name (tr "labels.custom-fonts")]]
|
||||
[:div.backend-filter
|
||||
{:class (when (backends :google) "selected")
|
||||
:on-click #(swap! state update :backends toggle-backend :google)}
|
||||
[:div.checkbox-icon i/tick]
|
||||
[:div.backend-name "Google Fonts"]]])]]
|
||||
|
||||
[:hr]
|
||||
|
||||
[:div.fonts-list
|
||||
[:> rvt/AutoSizer {}
|
||||
(fn [props]
|
||||
(let [width (obj/get props "width")
|
||||
height (obj/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 32
|
||||
:rowRenderer render}])))]]]]))
|
||||
(defn row-renderer
|
||||
[fonts selected on-select props]
|
||||
(let [index (obj/get props "index")
|
||||
key (obj/get props "key")
|
||||
style (obj/get props "style")
|
||||
font (nth fonts index)]
|
||||
(mf/html
|
||||
[:& font-item {:key key
|
||||
:font font
|
||||
:style style
|
||||
:on-click on-select
|
||||
:current? (= (:id font) (:id selected))}])))
|
||||
|
||||
(mf/defc font-options
|
||||
[{:keys [editor ids values locale on-change] :as props}]
|
||||
(let [{:keys [font-id
|
||||
font-size
|
||||
font-variant-id]} values
|
||||
[{:keys [editor ids values on-change] :as props}]
|
||||
(let [{:keys [font-id font-size font-variant-id]} values
|
||||
|
||||
font-id (or font-id (:font-id txt/default-text-attrs))
|
||||
font-size (or font-size (:font-size txt/default-text-attrs))
|
||||
font-id (or font-id (:font-id txt/default-text-attrs))
|
||||
font-size (or font-size (:font-size txt/default-text-attrs))
|
||||
font-variant-id (or font-variant-id (:font-variant-id txt/default-text-attrs))
|
||||
|
||||
fonts (mf/deref fonts/fontsdb)
|
||||
font (get fonts font-id)
|
||||
fonts (mf/deref fonts/fontsdb)
|
||||
font (get fonts font-id)
|
||||
|
||||
open-selector? (mf/use-state false)
|
||||
|
||||
change-font
|
||||
(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/use-callback
|
||||
(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}))))
|
||||
|
||||
on-font-family-change
|
||||
(fn [event]
|
||||
(let [new-font-id (dom/get-target-val event)]
|
||||
(when-not (str/empty? new-font-id)
|
||||
(let [font (get fonts new-font-id)]
|
||||
(fonts/ensure-loaded! new-font-id (partial change-font new-font-id))))))
|
||||
(mf/use-callback
|
||||
(mf/deps fonts change-font)
|
||||
(fn [event]
|
||||
(let [new-font-id (dom/get-target-val event)]
|
||||
(when-not (str/empty? new-font-id)
|
||||
(let [font (get fonts new-font-id)]
|
||||
(fonts/ensure-loaded! new-font-id (partial change-font new-font-id)))))))
|
||||
|
||||
on-font-size-change
|
||||
(fn [new-font-size]
|
||||
(when-not (str/empty? new-font-size)
|
||||
(on-change {:font-size (str new-font-size)})))
|
||||
(mf/use-callback
|
||||
(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
|
||||
(fn [event]
|
||||
(let [new-variant-id (dom/get-target-val event)
|
||||
variant (d/seek #(= new-variant-id (:id %)) (:variants font))]
|
||||
(on-change {:font-id (:id font)
|
||||
:font-family (:family font)
|
||||
:font-variant-id new-variant-id
|
||||
:font-weight (:weight variant)
|
||||
:font-style (:style variant)})))]
|
||||
(mf/use-callback
|
||||
(mf/deps font on-change)
|
||||
(fn [event]
|
||||
(let [new-variant-id (dom/get-target-val event)
|
||||
variant (d/seek #(= new-variant-id (:id %)) (:variants font))]
|
||||
(on-change {:font-id (:id font)
|
||||
:font-family (:family font)
|
||||
:font-variant-id new-variant-id
|
||||
:font-weight (:weight variant)
|
||||
:font-style (:style variant)}))))
|
||||
|
||||
on-font-select
|
||||
(mf/use-callback
|
||||
(mf/deps change-font)
|
||||
(fn [font*]
|
||||
(when (not= font font*)
|
||||
(change-font (:id font*)))))
|
||||
|
||||
on-font-selector-close
|
||||
(mf/use-callback
|
||||
#(reset! open-selector? false))]
|
||||
|
||||
[:*
|
||||
(when @open-selector?
|
||||
[:& font-selector
|
||||
{:current-font font
|
||||
:on-close on-font-selector-close
|
||||
:on-select on-font-select}])
|
||||
|
||||
[:div.row-flex
|
||||
[:select.input-select.font-option
|
||||
{:value (attr->string font-id)
|
||||
:on-change on-font-family-change}
|
||||
(when (= font-id :multiple)
|
||||
[:option {:value ""} (t locale "settings.multiple")])
|
||||
[:& font-select-optgroups {:locale locale}]]]
|
||||
[:div.input-select.font-option
|
||||
{:on-click #(reset! open-selector? true)}
|
||||
(:name font)]]
|
||||
|
||||
[:div.row-flex
|
||||
(let [size-options [8 9 10 11 12 14 18 24 36 48 72]
|
||||
|
@ -121,7 +336,7 @@
|
|||
|
||||
|
||||
(mf/defc spacing-options
|
||||
[{:keys [editor ids values locale on-change] :as props}]
|
||||
[{:keys [editor ids values on-change] :as props}]
|
||||
(let [{:keys [line-height
|
||||
letter-spacing]} values
|
||||
|
||||
|
@ -136,7 +351,7 @@
|
|||
[:div.spacing-options
|
||||
[:div.input-icon
|
||||
[:span.icon-before.tooltip.tooltip-bottom
|
||||
{:alt (t locale "workspace.options.text-options.line-height")}
|
||||
{:alt (tr "workspace.options.text-options.line-height")}
|
||||
i/line-height]
|
||||
[:input.input-text
|
||||
{:type "number"
|
||||
|
@ -144,12 +359,12 @@
|
|||
:min "0"
|
||||
:max "200"
|
||||
:value (attr->string line-height)
|
||||
:placeholder (t locale "settings.multiple")
|
||||
:placeholder (tr "settings.multiple")
|
||||
:on-change #(handle-change % :line-height)}]]
|
||||
|
||||
[:div.input-icon
|
||||
[:span.icon-before.tooltip.tooltip-bottom
|
||||
{:alt (t locale "workspace.options.text-options.letter-spacing")}
|
||||
{:alt (tr "workspace.options.text-options.letter-spacing")}
|
||||
i/letter-spacing]
|
||||
[:input.input-text
|
||||
{:type "number"
|
||||
|
@ -157,11 +372,11 @@
|
|||
:min "0"
|
||||
:max "200"
|
||||
:value (attr->string letter-spacing)
|
||||
:placeholder (t locale "settings.multiple")
|
||||
:placeholder (tr "settings.multiple")
|
||||
:on-change #(handle-change % :letter-spacing)}]]]))
|
||||
|
||||
(mf/defc text-transform-options
|
||||
[{:keys [editor ids values locale on-change] :as props}]
|
||||
[{:keys [editor ids values on-change] :as props}]
|
||||
(let [{:keys [text-transform]} values
|
||||
|
||||
text-transform (or text-transform "none")
|
||||
|
@ -171,35 +386,32 @@
|
|||
(on-change {:text-transform type}))]
|
||||
[:div.align-icons
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt (t locale "workspace.options.text-options.none")
|
||||
{:alt (tr "workspace.options.text-options.none")
|
||||
:class (dom/classnames :current (= "none" text-transform))
|
||||
:on-click #(handle-change % "none")}
|
||||
i/minus]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt (t locale "workspace.options.text-options.uppercase")
|
||||
{:alt (tr "workspace.options.text-options.uppercase")
|
||||
:class (dom/classnames :current (= "uppercase" text-transform))
|
||||
:on-click #(handle-change % "uppercase")}
|
||||
i/uppercase]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt (t locale "workspace.options.text-options.lowercase")
|
||||
{:alt (tr "workspace.options.text-options.lowercase")
|
||||
:class (dom/classnames :current (= "lowercase" text-transform))
|
||||
:on-click #(handle-change % "lowercase")}
|
||||
i/lowercase]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt (t locale "workspace.options.text-options.titlecase")
|
||||
{:alt (tr "workspace.options.text-options.titlecase")
|
||||
:class (dom/classnames :current (= "capitalize" text-transform))
|
||||
:on-click #(handle-change % "capitalize")}
|
||||
i/titlecase]]))
|
||||
|
||||
(mf/defc typography-options
|
||||
[{:keys [ids editor values on-change]}]
|
||||
(let [locale (mf/deref i18n/locale)
|
||||
opts #js {:editor editor
|
||||
(let [opts #js {:editor editor
|
||||
:ids ids
|
||||
:values values
|
||||
:locale locale
|
||||
:on-change on-change}]
|
||||
|
||||
[:div.element-set-content
|
||||
[:> font-options opts]
|
||||
[:div.row-flex
|
||||
|
@ -209,8 +421,7 @@
|
|||
|
||||
(mf/defc typography-entry
|
||||
[{:keys [typography read-only? selected? on-click on-change on-detach on-context-menu editting? focus-name? file]}]
|
||||
(let [locale (mf/deref i18n/locale)
|
||||
open? (mf/use-state editting?)
|
||||
(let [open? (mf/use-state editting?)
|
||||
hover-detach (mf/use-state false)
|
||||
name-input-ref (mf/use-ref nil)
|
||||
value (mf/use-state (cp/merge-path-item (:path typography) (:name typography)))
|
||||
|
@ -255,7 +466,7 @@
|
|||
{:style {:font-family (:font-family typography)
|
||||
:font-weight (:font-weight typography)
|
||||
:font-style (:font-style typography)}}
|
||||
(t locale "workspace.assets.typography.sample")]
|
||||
(tr "workspace.assets.typography.sample")]
|
||||
[:div.typography-name (:name typography)]]
|
||||
[:div.element-set-actions
|
||||
(when on-detach
|
||||
|
@ -277,32 +488,32 @@
|
|||
[:span (:name typography)]]
|
||||
|
||||
[:div.row-flex
|
||||
[:span.label (t locale "workspace.assets.typography.font-id")]
|
||||
[:span.label (tr "workspace.assets.typography.font-id")]
|
||||
[:span (:font-id typography)]]
|
||||
|
||||
[:div.row-flex
|
||||
[:span.label (t locale "workspace.assets.typography.font-variant-id")]
|
||||
[:span.label (tr "workspace.assets.typography.font-variant-id")]
|
||||
[:span (:font-variant-id typography)]]
|
||||
|
||||
[:div.row-flex
|
||||
[:span.label (t locale "workspace.assets.typography.font-size")]
|
||||
[:span.label (tr "workspace.assets.typography.font-size")]
|
||||
[:span (:font-size typography)]]
|
||||
|
||||
[:div.row-flex
|
||||
[:span.label (t locale "workspace.assets.typography.line-height")]
|
||||
[:span.label (tr "workspace.assets.typography.line-height")]
|
||||
[:span (:line-height typography)]]
|
||||
|
||||
[:div.row-flex
|
||||
[:span.label (t locale "workspace.assets.typography.letter-spacing")]
|
||||
[:span.label (tr "workspace.assets.typography.letter-spacing")]
|
||||
[:span (:letter-spacing typography)]]
|
||||
|
||||
[:div.row-flex
|
||||
[:span.label (t locale "workspace.assets.typography.text-transform")]
|
||||
[:span.label (tr "workspace.assets.typography.text-transform")]
|
||||
[:span (:text-transform typography)]]
|
||||
|
||||
[:div.go-to-lib-button
|
||||
{:on-click handle-go-to-edit}
|
||||
(t locale "workspace.assets.typography.go-to-edit")]]
|
||||
(tr "workspace.assets.typography.go-to-edit")]]
|
||||
|
||||
[:*
|
||||
[:div.element-set-content
|
||||
|
|
|
@ -190,7 +190,6 @@
|
|||
(defn setup-shortcuts
|
||||
[path-editing? drawing-path?]
|
||||
(hooks/use-shortcuts ::workspace wsc/shortcuts)
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps path-editing? drawing-path?)
|
||||
(fn []
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue