@solidrt/core 0.0.51 → 0.0.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/window.ts CHANGED
@@ -208,14 +208,49 @@ export function keyboardHeight(): number {
208
208
  return keyboardHeightAccessor()
209
209
  }
210
210
 
211
+ // Post-layout handlers run in registration order from one bus subscription,
212
+ // then pending reactive writes are flushed once. The postLayout emit is a JS
213
+ // entry inside the frame (layout has run, paint has not), and the runtime's
214
+ // microtask checkpoint only comes after the whole frame closure, so a signal
215
+ // write made by a handler would otherwise reach its node a frame late. The
216
+ // drain lives here, the way runFrame drains before renderFrame, so that no
217
+ // handler has to flush for itself. A throwing handler skips neither the
218
+ // handlers after it nor the flush.
219
+ let layoutHandlers: (() => void)[] = []
220
+ let layoutSubscribed = false
221
+
222
+ function runLayoutHandlers() {
223
+ for (let fn of [...layoutHandlers]) {
224
+ try {
225
+ fn()
226
+ } catch (err) {
227
+ console.error("Error in onLayout handler:", err)
228
+ }
229
+ }
230
+ try {
231
+ flush()
232
+ } catch (err) {
233
+ console.error("Error in reactive flush:", err)
234
+ }
235
+ }
236
+
211
237
  /**
212
238
  * Fires after layout has been computed for the current frame but before paint.
213
- * Setting properties that affect layout from this callback will be picked up
214
- * by a re-layout pass before painting (one extra pass; cascades beyond that
215
- * paint stale).
239
+ * Property writes made from this callback, direct or through signals (pending
240
+ * reactive writes are flushed once every handler has run), are picked up by a
241
+ * re-layout pass before painting (one extra pass; cascades beyond that paint
242
+ * stale).
216
243
  */
217
244
  export function onLayout(fn: () => void) {
218
- let unsubscribe = on("postLayout", fn)
245
+ if (!layoutSubscribed) {
246
+ layoutSubscribed = true
247
+ on("postLayout", runLayoutHandlers)
248
+ }
249
+ layoutHandlers.push(fn)
250
+ let unsubscribe = () => {
251
+ let i = layoutHandlers.indexOf(fn)
252
+ if (i >= 0) layoutHandlers.splice(i, 1)
253
+ }
219
254
  onCleanup(unsubscribe)
220
255
  return unsubscribe
221
256
  }
@@ -274,10 +309,20 @@ export function onBack(fn: (e: BackEvent) => void) {
274
309
 
275
310
  // ------ Window ----------------
276
311
 
277
- export function attachWindow(nodeId: number) {
312
+ // The window root's id: the key-routing fallback target and the pointer
313
+ // interest root. Set by attachWindow, moved by setWindowRoot when render()'s
314
+ // error boundary swaps the app's window for the error window and back.
315
+ let windowRootId = 0
316
+
317
+ export function setWindowRoot(nodeId: number) {
318
+ windowRootId = nodeId
278
319
  // The root carries the ambient move-interest bit for global onPointerMove
279
320
  // subscribers (it is on every hit path); see core.setInterestRoot.
280
321
  setInterestRoot(nodeId)
322
+ }
323
+
324
+ export function attachWindow(nodeId: number) {
325
+ setWindowRoot(nodeId)
281
326
  let unsubscribe: () => void = null!
282
327
  let unsubDown: () => void = null!
283
328
  let unsubUp: () => void = null!
@@ -426,13 +471,13 @@ export function attachWindow(nodeId: number) {
426
471
  // at dispatch time from current focus (nothing to freeze: keyup follows
427
472
  // focus, as in the DOM).
428
473
  let dispatchKey = (raw: any, handler: string) => {
429
- let target = focusedNode() ?? nodeId
474
+ let target = focusedNode() ?? windowRootId
430
475
  let stopped = false
431
476
  let e = { ...raw, target, stopPropagation: () => (stopped = true) }
432
477
  let path = getNodePath(target)
433
478
  // A focused node detached this tick has no chain to the root; the
434
479
  // window root must still hear the key.
435
- if (path[path.length - 1] !== nodeId) path.push(nodeId)
480
+ if (path[path.length - 1] !== windowRootId) path.push(windowRootId)
436
481
  for (let id of path) {
437
482
  e.currentTarget = id
438
483
  getEventHandler(id, handler)?.(e)