mirror of
https://github.com/penpot/penpot.git
synced 2026-08-06 12:58:55 +00:00
🐛 Fix font selector dropdown takes noticeably long to open when changing font
This commit is contained in:
parent
648c8e2152
commit
b8a87cfea6
@ -137,10 +137,10 @@
|
|||||||
;; uploads, ones that fail to bake) use the runtime fallback.
|
;; uploads, ones that fail to bake) use the runtime fallback.
|
||||||
;;
|
;;
|
||||||
;; The sprite is heavy (~2000 nodes), so we DON'T keep it in the DOM: the fetched
|
;; The sprite is heavy (~2000 nodes), so we DON'T keep it in the DOM: the fetched
|
||||||
;; markup is cached here as a string (`:svg`) and the nodes are materialized only
|
;; markup is cached here as a string (`:svg`) and the node is pre-parsed eagerly
|
||||||
;; while the picker is open (attach/detach below). `:ids` are the font ids it
|
;; (`:node`) so attaching is a cheap appendChild. `:ids` are the font ids it
|
||||||
;; covers, so the UI can pick sprite vs fallback.
|
;; covers (also pre-computed), so the UI can pick sprite vs fallback.
|
||||||
(defonce preview-sprite (l/atom {:status :idle :ids #{} :svg nil}))
|
(defonce preview-sprite (l/atom {:status :idle :ids #{} :svg nil :node nil}))
|
||||||
|
|
||||||
;; Id prefix shared with the generator and the UI's `<use href>`; referenced here
|
;; Id prefix shared with the generator and the UI's `<use href>`; referenced here
|
||||||
;; rather than re-declared so the contract stays in one place.
|
;; rather than re-declared so the contract stays in one place.
|
||||||
@ -162,7 +162,7 @@
|
|||||||
[]
|
[]
|
||||||
;; :error → the UI shows plain names (no previews, no per-font load storm); a
|
;; :error → the UI shows plain names (no previews, no per-font load storm); a
|
||||||
;; later `prefetch-preview-sprite!` call can retry.
|
;; later `prefetch-preview-sprite!` call can retry.
|
||||||
(reset! preview-sprite {:status :error :ids #{} :svg nil}))
|
(reset! preview-sprite {:status :error :ids #{} :svg nil :node nil}))
|
||||||
|
|
||||||
(defn- parse-sprite-svg
|
(defn- parse-sprite-svg
|
||||||
"Parse the cached sprite markup as SVG (not HTML, so no innerHTML injection
|
"Parse the cached sprite markup as SVG (not HTML, so no innerHTML injection
|
||||||
@ -176,10 +176,10 @@
|
|||||||
root)))
|
root)))
|
||||||
|
|
||||||
(defn prefetch-preview-sprite!
|
(defn prefetch-preview-sprite!
|
||||||
"Fetch the font-preview sprite markup and cache it in memory (no DOM yet — see
|
"Fetch the font-preview sprite markup, pre-parse it, and cache both the raw
|
||||||
`attach-preview-sprite!`). Idempotent: fetches only when nothing is cached yet
|
markup and the parsed DOM node (with collected font ids). Idempotent: fetches
|
||||||
(`:idle`) or a previous attempt failed (`:error`); no-op while `:loading` or
|
only when nothing is cached yet (`:idle`) or a previous attempt failed
|
||||||
`:ready`."
|
(`:error`); no-op while `:loading` or `:ready`."
|
||||||
[]
|
[]
|
||||||
(when (and (globals/browser?)
|
(when (and (globals/browser?)
|
||||||
(contains? #{:idle :error} (:status @preview-sprite)))
|
(contains? #{:idle :error} (:status @preview-sprite)))
|
||||||
@ -193,7 +193,20 @@
|
|||||||
;; http/send! doesn't reject on non-2xx; guard so an error body isn't
|
;; http/send! doesn't reject on non-2xx; guard so an error body isn't
|
||||||
;; cached as the sprite.
|
;; cached as the sprite.
|
||||||
(if (http/success? response)
|
(if (http/success? response)
|
||||||
(swap! preview-sprite assoc :status :ready :svg (:body response))
|
(let [svg (:body response)
|
||||||
|
svg-el (parse-sprite-svg svg)]
|
||||||
|
(if-let [node (some-> svg-el (dom/import-node))]
|
||||||
|
(do
|
||||||
|
(dom/set-attribute! node "id" "font-preview-sprite")
|
||||||
|
(let [ids (collect-preview-ids node)]
|
||||||
|
(swap! preview-sprite assoc
|
||||||
|
:status :ready
|
||||||
|
:svg svg
|
||||||
|
:node node
|
||||||
|
:ids ids)))
|
||||||
|
(do
|
||||||
|
(log/wrn :hint "cannot parse font preview sprite")
|
||||||
|
(reset-preview-sprite-error!))))
|
||||||
(do
|
(do
|
||||||
(log/wrn :hint "cannot load font preview sprite" :status (:status response))
|
(log/wrn :hint "cannot load font preview sprite" :status (:status response))
|
||||||
(reset-preview-sprite-error!))))
|
(reset-preview-sprite-error!))))
|
||||||
@ -202,30 +215,22 @@
|
|||||||
(reset-preview-sprite-error!))))))
|
(reset-preview-sprite-error!))))))
|
||||||
|
|
||||||
(defn attach-preview-sprite!
|
(defn attach-preview-sprite!
|
||||||
"Materialize the cached sprite into the DOM (hidden) so rows can reference its
|
"Append the pre-parsed sprite node into the DOM (hidden) so rows can reference
|
||||||
glyph groups via `<use>`, and record the covered font ids. Returns the injected
|
its glyph groups via `<use>`. Returns the node (pass it to
|
||||||
node (pass it to `detach-preview-sprite!` on close), or nil if not ready / the
|
`detach-preview-sprite!` on close), or nil if not ready. Parsing and id
|
||||||
markup is invalid. Parsing happens here, not on prefetch, so the cost is paid
|
collection happen once during `prefetch-preview-sprite!`, so this is just a
|
||||||
only while the picker is open."
|
cheap appendChild."
|
||||||
[]
|
[]
|
||||||
(let [{:keys [status svg]} @preview-sprite]
|
(let [{:keys [status node]} @preview-sprite]
|
||||||
(when (and (globals/browser?) (= :ready status) (some? svg))
|
(when (and (globals/browser?) (= :ready status) (some? node))
|
||||||
(if-let [node (some-> (parse-sprite-svg svg) (dom/import-node))]
|
(when-let [body-el (unchecked-get globals/document "body")]
|
||||||
;; The node already carries display:none + aria-hidden from the generator.
|
(dom/append-child! body-el node))
|
||||||
(do
|
node)))
|
||||||
(dom/set-attribute! node "id" "font-preview-sprite")
|
|
||||||
(when-let [body-el (unchecked-get globals/document "body")]
|
|
||||||
(dom/append-child! body-el node))
|
|
||||||
(swap! preview-sprite assoc :ids (collect-preview-ids node))
|
|
||||||
node)
|
|
||||||
(do
|
|
||||||
(log/wrn :hint "cannot parse font preview sprite")
|
|
||||||
(reset-preview-sprite-error!)
|
|
||||||
nil)))))
|
|
||||||
|
|
||||||
(defn detach-preview-sprite!
|
(defn detach-preview-sprite!
|
||||||
"Remove the sprite node injected by `attach-preview-sprite!` from the DOM. The
|
"Remove the sprite node injected by `attach-preview-sprite!` from the DOM. The
|
||||||
cached markup and `:ids` stay, so reopening re-attaches without a refetch."
|
cached markup, parsed node, and `:ids` stay, so reopening re-attaches without
|
||||||
|
a refetch or re-parse."
|
||||||
[node]
|
[node]
|
||||||
(dom/remove! node))
|
(dom/remove! node))
|
||||||
|
|
||||||
|
|||||||
@ -257,13 +257,20 @@
|
|||||||
|
|
||||||
;; FLAG :font-preview — materialize the preview sprite into the DOM only while
|
;; FLAG :font-preview — materialize the preview sprite into the DOM only while
|
||||||
;; the picker is open (markup is prefetched on workspace load), removing it on
|
;; the picker is open (markup is prefetched on workspace load), removing it on
|
||||||
;; close so its ~2000 nodes aren't kept around idle. Remove the flag clause to
|
;; close so its ~2000 nodes aren't kept around idle. The attachment is deferred
|
||||||
;; drop the feature.
|
;; so the dropdown can paint first with plain names, then the sprite swaps in
|
||||||
|
;; on the next tick. Remove the flag clause to drop the feature.
|
||||||
(mf/with-effect [sprite-status]
|
(mf/with-effect [sprite-status]
|
||||||
(when (and (contains? cf/flags :font-preview)
|
(when (and (contains? cf/flags :font-preview)
|
||||||
(= :ready sprite-status))
|
(= :ready sprite-status))
|
||||||
(let [node (fonts/attach-preview-sprite!)]
|
(let [node* (volatile! nil)
|
||||||
#(fonts/detach-preview-sprite! node))))
|
task (tm/schedule
|
||||||
|
(fn []
|
||||||
|
(vreset! node* (fonts/attach-preview-sprite!))))]
|
||||||
|
(fn []
|
||||||
|
(tm/dispose! task)
|
||||||
|
(when-some [n @node*]
|
||||||
|
(fonts/detach-preview-sprite! n))))))
|
||||||
|
|
||||||
(mf/with-effect [@selected]
|
(mf/with-effect [@selected]
|
||||||
(when-let [inst (mf/ref-val flist)]
|
(when-let [inst (mf/ref-val flist)]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user