mirror of
https://github.com/penpot/penpot.git
synced 2026-07-23 06:28:14 +00:00
✨ Add waitForLayoutUpdate plugin method
This commit is contained in:
parent
c66ee1803f
commit
12189fae5c
@ -743,7 +743,7 @@
|
||||
subtree-ids-by-id]
|
||||
:or {ignore-constraints false ignore-snap-pixel false snap-ignore-axis nil undo-transation? true}
|
||||
:as params}]
|
||||
(ptk/reify ::apply-wasm-modifiesr
|
||||
(ptk/reify ::apply-wasm-modifiers
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [translation?
|
||||
|
||||
@ -119,6 +119,11 @@
|
||||
:undo-group undo-group}))))
|
||||
(rx/empty))))))
|
||||
|
||||
;; Signals that one or more :layout/update events have arrived and are
|
||||
;; still waiting. Read by the plugin API `app.plugins.api/waitForLayoutUpdate`
|
||||
;; to decide whether layout work is still in flight.
|
||||
(defonce layout-pending (atom false))
|
||||
|
||||
(defn initialize-shape-layout
|
||||
[]
|
||||
(ptk/reify ::initialize-shape-layout
|
||||
@ -129,18 +134,21 @@
|
||||
;; FIXME: we don't need use types for simple signaling,
|
||||
;; we can just use a keyword for it
|
||||
(rx/filter (ptk/type? :layout/update))
|
||||
(rx/tap #(reset! layout-pending true))
|
||||
(rx/map deref)
|
||||
;; We buffer the updates to the layout so if there are many changes at the same time
|
||||
;; they are process together. It will get a better performance.
|
||||
(rx/buffer-time 100)
|
||||
(rx/filter #(d/not-empty? %))
|
||||
(rx/tap #(reset! layout-pending false))
|
||||
(rx/mapcat
|
||||
(fn [data]
|
||||
(->> (group-by :page-id data)
|
||||
(map (fn [[page-id items]]
|
||||
(let [ids (reduce #(into %1 (:ids %2)) #{} items)]
|
||||
(update-layout-positions {:page-id page-id :ids ids})))))))
|
||||
(rx/take-until stopper))))))
|
||||
(rx/take-until stopper)
|
||||
(rx/finalize #(reset! layout-pending false)))))))
|
||||
|
||||
(defn finalize-shape-layout
|
||||
[]
|
||||
|
||||
@ -59,6 +59,8 @@
|
||||
(declare v2-update-text-editor-styles)
|
||||
(declare v2-sync-wasm-text-layout)
|
||||
|
||||
(def ^:private ^:const stuck-timeout 10000)
|
||||
|
||||
;; -- Content helpers
|
||||
|
||||
(defn- v2-content-has-text?
|
||||
@ -391,11 +393,18 @@
|
||||
(update-content-range start end attrs))]
|
||||
(assoc shape :content new-content)))
|
||||
|
||||
|
||||
;; Signals that a :font-id change on a text shape (non-wasm render path) is
|
||||
;; still waiting for the new position data to be recalculated and committed.
|
||||
;; Read by the plugin API `app.plugins.api/waitForLayoutUpdate`
|
||||
;; to decide whether layout work is still in flight.
|
||||
(defonce font-pending (atom false))
|
||||
|
||||
(defn update-text-range
|
||||
[id start end attrs]
|
||||
(ptk/reify ::update-text-range
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(watch [_ state stream]
|
||||
(let [objects (dsh/lookup-page-objects state)
|
||||
shape (get objects id)
|
||||
|
||||
@ -412,7 +421,17 @@
|
||||
(rx/of (dwsh/update-shapes shape-ids update-fn))
|
||||
(if (features/active-feature? state "render-wasm/v1")
|
||||
(rx/of (dwwt/resize-wasm-text-debounce id))
|
||||
(rx/empty)))))))
|
||||
(when (contains? attrs :font-id)
|
||||
(reset! font-pending true)
|
||||
(let [stopper (rx/filter (ptk/type? :app.main.data.workspace/finalize) stream)]
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::commit-position-data))
|
||||
(rx/take 1)
|
||||
;; Timeout so the flag cannot get stuck
|
||||
(rx/timeout stuck-timeout (rx/of :timeout))
|
||||
(rx/take-until stopper)
|
||||
(rx/finalize #(reset! font-pending false))
|
||||
(rx/ignore))))))))))
|
||||
|
||||
(defn update-root-attrs
|
||||
[{:keys [id attrs]}]
|
||||
@ -844,7 +863,7 @@
|
||||
(not (features/active-feature? state "text-editor-wasm/v1")))
|
||||
(rx/of (v2-update-text-editor-styles id attrs)))
|
||||
|
||||
(when (features/active-feature? state "render-wasm/v1")
|
||||
(if (features/active-feature? state "render-wasm/v1")
|
||||
(rx/concat
|
||||
;; Apply style to selected spans and sync content
|
||||
(let [has-selection? (wasm.api/text-editor-has-selection?)]
|
||||
@ -858,12 +877,43 @@
|
||||
:update-name? true))))))))
|
||||
;; Resize (with delay for font-id changes)
|
||||
(if (contains? attrs :font-id)
|
||||
(->> stream
|
||||
(rx/filter (font-loaded-event? (:font-id attrs)))
|
||||
(rx/take 1)
|
||||
(rx/observe-on :async)
|
||||
(rx/map #(dwwt/resize-wasm-text id)))
|
||||
(rx/of (dwwt/resize-wasm-text id)))))))))
|
||||
(let [objects (dsh/lookup-page-objects state)
|
||||
shape (get objects id)]
|
||||
(if (and (cfh/text-shape? shape)
|
||||
(not= :fixed (:grow-type shape)))
|
||||
;; Auto-height/auto-width: set resize-pending immediately so
|
||||
;; waitForLayoutUpdate detects pending work. Dispatch resize to
|
||||
;; trigger font loading (set-shape-text-content → store-fonts),
|
||||
;; then re-dispatch after font-loaded for correct final geometry.
|
||||
(do
|
||||
(reset! dwwt/resize-pending true)
|
||||
(rx/concat
|
||||
(rx/of (dwwt/resize-wasm-text id))
|
||||
(let [stopper (rx/filter (ptk/type? :app.main.data.workspace/finalize) stream)]
|
||||
(->> stream
|
||||
(rx/filter (font-loaded-event? (:font-id attrs)))
|
||||
(rx/take 1)
|
||||
;; Timeout if the font-loaded event doesn't arrive eventually
|
||||
(rx/timeout stuck-timeout (rx/of :timeout))
|
||||
(rx/take-until stopper)
|
||||
(rx/observe-on :async)
|
||||
(rx/map #(dwwt/resize-wasm-text id))
|
||||
(rx/finalize #(reset! dwwt/resize-pending false))))))
|
||||
;; Fixed: geometry doesn't change with font; no need to wait.
|
||||
(rx/of (dwwt/resize-wasm-text id))))
|
||||
(rx/of (dwwt/resize-wasm-text id))))
|
||||
|
||||
(when (contains? attrs :font-id)
|
||||
(reset! font-pending true)
|
||||
(let [stopper (rx/filter (ptk/type? :app.main.data.workspace/finalize) stream)]
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::commit-position-data))
|
||||
(rx/take 1)
|
||||
;; Timeout so the flag cannot get stuck
|
||||
(rx/timeout stuck-timeout (rx/of :timeout))
|
||||
(rx/take-until stopper)
|
||||
(rx/finalize #(reset! font-pending false))
|
||||
(rx/ignore)))))))))
|
||||
|
||||
ptk/EffectEvent
|
||||
(effect [_ state _]
|
||||
|
||||
@ -125,6 +125,14 @@
|
||||
:else
|
||||
(rx/empty)))))))
|
||||
|
||||
;; Signals that a wasm text resize is still in flight: set to true when a
|
||||
;; resize enters the debounce window below (or while `update-attrs` waits for
|
||||
;; a font load, see app.main.data.workspace.texts) and reset to false once the
|
||||
;; final resize is dispatched or the wait is cut short. Read by the plugin API
|
||||
;; `waitForLayoutUpdate` (app.plugins.api) to decide whether text geometry
|
||||
;; work is still in flight.
|
||||
(defonce resize-pending (atom false))
|
||||
|
||||
;; This event will debounce the resize events so, if there are many, they
|
||||
;; are processed at the same time and not one-by-one. This will improve
|
||||
;; performance because it's better to make only one layout calculation instead
|
||||
@ -150,13 +158,17 @@
|
||||
(rx/merge
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::resize-wasm-text-debounce-inner))
|
||||
(rx/tap #(reset! resize-pending true))
|
||||
(rx/debounce debounce-resize-text-time)
|
||||
(rx/take 1)
|
||||
(rx/map (fn [evt]
|
||||
(resize-wasm-text-debounce-commit
|
||||
(some-> evt meta :undo-group)
|
||||
(some-> evt meta :undo-id))))
|
||||
(rx/take-until stopper))
|
||||
(rx/take-until stopper)
|
||||
;; Don't leave the flag stuck if the stopper cuts the
|
||||
;; wait short while a resize is still debouncing
|
||||
(rx/finalize #(reset! resize-pending false)))
|
||||
(rx/of (with-meta
|
||||
(resize-wasm-text-debounce-inner id)
|
||||
{:undo-group undo-group :undo-id undo-id})))
|
||||
|
||||
@ -211,6 +211,8 @@
|
||||
;; LOAD API
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def on-loaded-font nil)
|
||||
|
||||
(defn ensure-loaded!
|
||||
([font-id] (ensure-loaded! font-id nil))
|
||||
([font-id variant-id]
|
||||
@ -240,6 +242,8 @@
|
||||
(let [on-load (fn [resolve]
|
||||
(swap! loaded conj font-id)
|
||||
(swap! loading dissoc font-id)
|
||||
(when (fn? on-loaded-font)
|
||||
(on-loaded-font font-id))
|
||||
(resolve font-id))
|
||||
|
||||
load-p (-> (p/create
|
||||
|
||||
@ -27,9 +27,18 @@
|
||||
[app.util.text-editor :as ted]
|
||||
[app.util.text-svg-position :as tsp]
|
||||
[app.util.text.content :as content]
|
||||
[potok.v2.core :as ptk]
|
||||
[promesa.core :as p]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
;; Re-emit browser-side font loads as :font-loaded store events so consumers
|
||||
;; filtering with `font-loaded-event?` (app.main.data.workspace.texts) also
|
||||
;; wake up for fonts loaded through `fonts/ensure-loaded!`, not only for the
|
||||
;; ones loaded by the wasm font store.
|
||||
(set! fonts/on-loaded-font
|
||||
(fn [font-id]
|
||||
(st/emit! (ptk/data-event :font-loaded {:font-id font-id}))))
|
||||
|
||||
(defn fix-position
|
||||
[shape]
|
||||
(if-let [modifiers (:modifiers shape)]
|
||||
|
||||
@ -27,7 +27,10 @@
|
||||
[app.main.data.workspace.colors :as dwc]
|
||||
[app.main.data.workspace.groups :as dwg]
|
||||
[app.main.data.workspace.media :as dwm]
|
||||
[app.main.data.workspace.modifiers :as dwmm]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.shape-layout :as dwsl]
|
||||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.data.workspace.variants :as dwv]
|
||||
[app.main.data.workspace.wasm-text :as dwwt]
|
||||
[app.main.features :as features]
|
||||
@ -54,7 +57,8 @@
|
||||
[app.util.object :as obj]
|
||||
[app.util.theme :as theme]
|
||||
[beicon.v2.core :as rx]
|
||||
[cuerdas.core :as str]))
|
||||
[cuerdas.core :as str]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
;;
|
||||
;; PLUGINS PUBLIC API - The plugins will able to access this functions
|
||||
@ -675,4 +679,67 @@
|
||||
(se/add-event plugin-id)))
|
||||
(shape/shape-proxy plugin-id variant-id))
|
||||
|
||||
(u/not-valid plugin-id :shapes "One of the components is not on the same page or is already a variant")))))))
|
||||
(u/not-valid plugin-id :shapes "One of the components is not on the same page or is already a variant")))))
|
||||
|
||||
:waitForLayoutUpdate
|
||||
(fn [timeout]
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(let [work-pending?
|
||||
(fn []
|
||||
;; `layout-pending`: flex/grid updates buffered in the 100ms
|
||||
;; buffer-time window of `initialize-shape-layout`; cleared
|
||||
;; when the buffer flushes and `update-layout-positions` is
|
||||
;; dispatched.
|
||||
;; `resize-pending`: wasm text resize debouncing or waiting
|
||||
;; for a font load; cleared once the final resize is
|
||||
;; dispatched.
|
||||
;; `font-pending`: non-wasm font change waiting for the next
|
||||
;; `commit-position-data`; cleared when it fires.
|
||||
(boolean (or @dwsl/layout-pending
|
||||
@dwwt/resize-pending
|
||||
@dwt/font-pending)))
|
||||
|
||||
;; Progress signals: events that mark the completion of one of
|
||||
;; the pending work kinds above (plus workspace finalization,
|
||||
;; which clears the flags via the stoppers of their listeners).
|
||||
;; They only trigger a re-check of the flags: several kinds of
|
||||
;; work can be pending at once and one kind can queue another
|
||||
;; (e.g. a text resize triggering a flex layout update), so we
|
||||
;; resolve only when no flag remains set.
|
||||
progress-event?
|
||||
(fn [event]
|
||||
(let [type (ptk/type event)]
|
||||
(or (= type ::dwsl/update-layout-positions)
|
||||
(= type ::dwmm/apply-wasm-modifiers)
|
||||
(= type ::dwt/commit-position-data)
|
||||
(= type :app.main.data.workspace/finalize))))]
|
||||
|
||||
(if-not (work-pending?)
|
||||
;; Fast path: nothing pending, resolve right away.
|
||||
(resolve)
|
||||
(->> (rx/merge
|
||||
;; Optional deadline: if a timeout was requested, emit
|
||||
;; :timeout after `timeout` ms so the promise rejects when
|
||||
;; updates stall.
|
||||
(if timeout
|
||||
(->> (rx/of :timeout)
|
||||
(rx/delay timeout))
|
||||
(rx/empty))
|
||||
|
||||
(->> st/stream
|
||||
(rx/filter progress-event?)
|
||||
;; Defer the re-check so the flag-clearing handlers
|
||||
;; (and any synchronous follow-up work they queue)
|
||||
;; observe the event before we read the flags.
|
||||
(rx/observe-on :async)
|
||||
(rx/filter #(not (work-pending?)))
|
||||
(rx/map (constantly :ok))))
|
||||
|
||||
(rx/take 1)
|
||||
(rx/subs!
|
||||
(fn [value]
|
||||
(if (= value :timeout)
|
||||
(reject (js/Error. "waitForLayoutUpdate timeout"))
|
||||
(resolve)))
|
||||
reject)))))))))
|
||||
|
||||
@ -9,12 +9,17 @@
|
||||
[app.common.math :as m]
|
||||
[app.common.test-helpers.files :as cthf]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.modifiers :as dwmm]
|
||||
[app.main.data.workspace.shape-layout :as dwsl]
|
||||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.data.workspace.wasm-text :as dwwt]
|
||||
[app.main.store :as st]
|
||||
[app.plugins.api :as api]
|
||||
[app.util.object :as obj]
|
||||
[cljs.test :as t :include-macros true]
|
||||
[frontend-tests.helpers.state :as ths]
|
||||
[frontend-tests.helpers.wasm :as thw]))
|
||||
[frontend-tests.helpers.wasm :as thw]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(t/deftest test-common-shape-properties
|
||||
(thw/with-wasm-mocks*
|
||||
@ -370,3 +375,152 @@
|
||||
(t/is (pos? (thw/call-count :clean-modifiers)))
|
||||
(t/is (pos? (thw/call-count :set-structure-modifiers)))
|
||||
(t/is (pos? (thw/call-count :propagate-modifiers))))))))
|
||||
|
||||
;; ---- waitForLayoutUpdate tests ------------------------------------------
|
||||
;;
|
||||
;; Each test sets up a fresh potok store and replaces st/state / st/stream so
|
||||
;; that st/emit! and waitForLayoutUpdate work against the same event bus.
|
||||
;; The fixture below restores the original globals and clears the pending
|
||||
;; flags so no state leaks between tests (or into other test namespaces).
|
||||
|
||||
(def ^:private original-st-state st/state)
|
||||
(def ^:private original-st-stream st/stream)
|
||||
|
||||
(defn- reset-pending-flags!
|
||||
[]
|
||||
(reset! dwsl/layout-pending false)
|
||||
(reset! dwwt/resize-pending false)
|
||||
(reset! dwt/font-pending false))
|
||||
|
||||
(t/use-fixtures :each
|
||||
{:before reset-pending-flags!
|
||||
:after (fn []
|
||||
(reset-pending-flags!)
|
||||
(set! st/state original-st-state)
|
||||
(set! st/stream original-st-stream))})
|
||||
|
||||
(defn- make-test-store
|
||||
"Creates a minimal potok store with an empty state map and installs it as the
|
||||
global st/state and st/stream for the duration of the calling test."
|
||||
[]
|
||||
(let [test-store (ptk/store {:state {} :on-error (fn [e] (js/console.error e))})]
|
||||
(set! st/state test-store)
|
||||
(set! st/stream (ptk/input-stream test-store))
|
||||
test-store))
|
||||
|
||||
(t/deftest test-wait-for-layout-update-no-pending
|
||||
;; When all pending flags are clear the promise should resolve immediately
|
||||
;; via the fast path — no store event is needed.
|
||||
(t/async done
|
||||
(make-test-store)
|
||||
(let [^js ctx (api/create-context "00000000-0000-0000-0000-000000000000")]
|
||||
(-> (.waitForLayoutUpdate ctx)
|
||||
(.then (fn []
|
||||
(t/is true "resolved with no pending work")
|
||||
(done)))
|
||||
(.catch (fn [err]
|
||||
(t/is false (str "unexpected rejection: " err))
|
||||
(done)))))))
|
||||
|
||||
(t/deftest test-wait-for-layout-update-layout-pending
|
||||
;; layout-pending=true signals a flex/grid recalculation in progress.
|
||||
;; The promise should stay pending until ::update-layout-positions fires
|
||||
;; with the flag already cleared (mirroring the real pipeline, where the
|
||||
;; flag is reset when the buffer flushes, just before the event).
|
||||
(t/async done
|
||||
(make-test-store)
|
||||
(reset! dwsl/layout-pending true)
|
||||
(let [^js ctx (api/create-context "00000000-0000-0000-0000-000000000000")]
|
||||
(-> (.waitForLayoutUpdate ctx)
|
||||
(.then (fn []
|
||||
(t/is true "resolved after update-layout-positions event")
|
||||
(done)))
|
||||
(.catch (fn [err]
|
||||
(t/is false (str "unexpected rejection: " err))
|
||||
(done))))
|
||||
;; The Promise constructor ran synchronously so the subscription is
|
||||
;; already live on st/stream. Emitting now triggers the re-check.
|
||||
(reset! dwsl/layout-pending false)
|
||||
(st/emit! (ptk/data-event ::dwsl/update-layout-positions)))))
|
||||
|
||||
(t/deftest test-wait-for-layout-update-resize-pending
|
||||
;; resize-pending=true signals wasm text geometry being recalculated.
|
||||
;; The promise should stay pending until ::apply-wasm-modifiers fires
|
||||
;; with the flag already cleared.
|
||||
(t/async done
|
||||
(make-test-store)
|
||||
(reset! dwwt/resize-pending true)
|
||||
(let [^js ctx (api/create-context "00000000-0000-0000-0000-000000000000")]
|
||||
(-> (.waitForLayoutUpdate ctx)
|
||||
(.then (fn []
|
||||
(t/is true "resolved after apply-wasm-modifiers event")
|
||||
(done)))
|
||||
(.catch (fn [err]
|
||||
(t/is false (str "unexpected rejection: " err))
|
||||
(done))))
|
||||
(reset! dwwt/resize-pending false)
|
||||
(st/emit! (ptk/data-event ::dwmm/apply-wasm-modifiers)))))
|
||||
|
||||
(t/deftest test-wait-for-layout-update-font-pending
|
||||
;; font-pending=true signals a non-wasm CSS font measurement in progress.
|
||||
;; The promise should stay pending until ::commit-position-data fires
|
||||
;; with the flag already cleared.
|
||||
(t/async done
|
||||
(make-test-store)
|
||||
(reset! dwt/font-pending true)
|
||||
(let [^js ctx (api/create-context "00000000-0000-0000-0000-000000000000")]
|
||||
(-> (.waitForLayoutUpdate ctx)
|
||||
(.then (fn []
|
||||
(t/is true "resolved after commit-position-data event")
|
||||
(done)))
|
||||
(.catch (fn [err]
|
||||
(t/is false (str "unexpected rejection: " err))
|
||||
(done))))
|
||||
(reset! dwt/font-pending false)
|
||||
(st/emit! (ptk/data-event ::dwt/commit-position-data)))))
|
||||
|
||||
(t/deftest test-wait-for-layout-update-rechecks-pending
|
||||
;; A completion event must NOT resolve the promise while any pending flag
|
||||
;; is still set. This covers the font-change resize flow, where an early
|
||||
;; ::apply-wasm-modifiers (pre font-load resize) fires while resize-pending
|
||||
;; is still true and only a later one marks the real completion.
|
||||
(t/async done
|
||||
(make-test-store)
|
||||
(reset! dwwt/resize-pending true)
|
||||
(let [^js ctx (api/create-context "00000000-0000-0000-0000-000000000000")
|
||||
resolved (atom false)]
|
||||
(-> (.waitForLayoutUpdate ctx)
|
||||
(.then (fn [] (reset! resolved true)))
|
||||
(.catch (fn [err]
|
||||
(t/is false (str "unexpected rejection: " err)))))
|
||||
;; Early apply-wasm-modifiers: the flag is still set, so the re-check
|
||||
;; must keep the promise pending.
|
||||
(st/emit! (ptk/data-event ::dwmm/apply-wasm-modifiers))
|
||||
(js/setTimeout
|
||||
(fn []
|
||||
(t/is (false? @resolved) "must not resolve while resize-pending is set")
|
||||
;; Final apply-wasm-modifiers with the flag cleared resolves it.
|
||||
(reset! dwwt/resize-pending false)
|
||||
(st/emit! (ptk/data-event ::dwmm/apply-wasm-modifiers))
|
||||
(js/setTimeout
|
||||
(fn []
|
||||
(t/is (true? @resolved) "resolves once all flags are clear")
|
||||
(done))
|
||||
30))
|
||||
30))))
|
||||
|
||||
(t/deftest test-wait-for-layout-update-timeout
|
||||
;; When the optional timeout fires before any completion event the promise
|
||||
;; should reject with an Error whose message mentions "timeout".
|
||||
(t/async done
|
||||
(make-test-store)
|
||||
(reset! dwsl/layout-pending true)
|
||||
(let [^js ctx (api/create-context "00000000-0000-0000-0000-000000000000")]
|
||||
(-> (.waitForLayoutUpdate ctx 20)
|
||||
(.then (fn []
|
||||
(t/is false "expected rejection but promise resolved")
|
||||
(done)))
|
||||
(.catch (fn [^js err]
|
||||
(t/is (instance? js/Error err) "rejection value should be an Error")
|
||||
(t/is (some? (re-find #"timeout" (.-message err))) "error message should mention timeout")
|
||||
(done)))))))
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
- **plugin-types**: Added `textBounds` property for text shapes
|
||||
- **plugin-types**: Added flag `throwValidationErrors` to enable exceptions on validation
|
||||
- **plugin-types**: Fix missing `webp` export format in `Export.type`
|
||||
- **plugin-types**: Added `waitForLayoutUpdate` to wait until pending layout updates have finished
|
||||
|
||||
## 1.4.2 (2026-01-21)
|
||||
|
||||
|
||||
11
plugins/libs/plugin-types/index.d.ts
vendored
11
plugins/libs/plugin-types/index.d.ts
vendored
@ -1330,6 +1330,17 @@ export interface Context {
|
||||
* @return The variant container created
|
||||
*/
|
||||
createVariantFromComponents(shapes: Board[]): VariantContainer;
|
||||
|
||||
/**
|
||||
* This method returns a promise that will be resolved when all the
|
||||
* pending layout updates have finished. If no layout work is pending
|
||||
* the promise resolves immediately.
|
||||
* @param timeout Maximum time to wait, in milliseconds. If the timeout
|
||||
* elapses before the layout settles, the promise is rejected. Without a
|
||||
* timeout the promise waits indefinitely.
|
||||
* @return The promise to be resolved when the layout is updated
|
||||
*/
|
||||
waitForLayoutUpdate(timeout?: number): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -388,6 +388,11 @@ export function createApi(
|
||||
checkPermission('content:write');
|
||||
return plugin.context.createVariantFromComponents(shapes);
|
||||
},
|
||||
|
||||
waitForLayoutUpdate(timeout?: number): Promise<void> {
|
||||
checkPermission('content:read');
|
||||
return plugin.context.waitForLayoutUpdate(timeout);
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user