foldkit 0.85.0 → 0.86.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/task/dom.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,MAAM,EAMP,MAAM,QAAQ,CAAA;AAEf,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAsC5C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,KAAK,GAAI,UAAU,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAKxE,CAAA;AAEJ;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,SAAS,GACpB,UAAU,MAAM,EAChB,UAAU,QAAQ,CAAC;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,KAC7C,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAgDlC,CAAA;AAuBJ;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,GACrB,UAAU,MAAM,KACf,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAclC,CAAA;AAEJ;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GACvB,UAAU,MAAM,KACf,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAKlC,CAAA;AAEJ;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GACzB,UAAU,MAAM,KACf,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAKlC,CAAA;AAEJ,0EAA0E;AAC1E,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,UAAU,CAAA;AAEhD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GACvB,UAAU,MAAM,EAChB,WAAW,cAAc,KACxB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAiClC,CAAA"}
1
+ {"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/task/dom.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,MAAM,EAMP,MAAM,QAAQ,CAAA;AAEf,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AA8B5C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,KAAK,GAAI,UAAU,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAKxE,CAAA;AAEJ;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,SAAS,GACpB,UAAU,MAAM,EAChB,UAAU,QAAQ,CAAC;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,KAC7C,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAgDlC,CAAA;AAuBJ;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,GACrB,UAAU,MAAM,KACf,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAclC,CAAA;AAEJ;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GACvB,UAAU,MAAM,KACf,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAKlC,CAAA;AAEJ;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GACzB,UAAU,MAAM,KACf,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAKlC,CAAA;AAEJ,0EAA0E;AAC1E,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,UAAU,CAAA;AAEhD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GACvB,UAAU,MAAM,EAChB,WAAW,cAAc,KACxB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAiClC,CAAA"}
package/dist/task/dom.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Array, Effect, Equal, Function, Match as M, Number, Option, } from 'effect';
2
2
  import { ElementNotFound } from './error.js';
3
+ import { afterRender } from './timing.js';
3
4
  const BASE_DIALOG_Z_INDEX = 2147483600;
4
5
  let openDialogCount = 0;
5
6
  const dialogCleanups = new WeakMap();
@@ -11,14 +12,6 @@ const FOCUSABLE_SELECTOR = Array.join([
11
12
  'textarea:not([disabled]):not([tabindex="-1"])',
12
13
  '[tabindex]:not([tabindex="-1"])',
13
14
  ], ', ');
14
- // NOTE: DOM tasks await one rAF before walking the tree. The runtime defers
15
- // renders to the next animation frame and forks Commands on the microtask
16
- // queue, so without this wait a Task that runs immediately after a dirtying
17
- // Message would query the DOM before the matching VDOM patch has committed.
18
- const awaitNextFrame = Effect.callback(resume => {
19
- const handle = requestAnimationFrame(() => resume(Effect.void));
20
- return Effect.sync(() => cancelAnimationFrame(handle));
21
- });
22
15
  const queryHTMLElement = (selector) => Effect.suspend(() => {
23
16
  const element = document.querySelector(selector);
24
17
  return element instanceof HTMLElement
@@ -49,7 +42,7 @@ const queryHTMLElement = (selector) => Effect.suspend(() => {
49
42
  * ```
50
43
  */
51
44
  export const focus = (selector) => Effect.gen(function* () {
52
- yield* awaitNextFrame;
45
+ yield* afterRender;
53
46
  const element = yield* queryHTMLElement(selector);
54
47
  element.focus();
55
48
  });
@@ -69,7 +62,7 @@ export const focus = (selector) => Effect.gen(function* () {
69
62
  * ```
70
63
  */
71
64
  export const showModal = (selector, options) => Effect.gen(function* () {
72
- yield* awaitNextFrame;
65
+ yield* afterRender;
73
66
  const element = document.querySelector(selector);
74
67
  if (!(element instanceof HTMLDialogElement)) {
75
68
  return yield* Effect.fail(new ElementNotFound({ selector }));
@@ -151,7 +144,7 @@ export const closeModal = (selector) => Effect.suspend(() => {
151
144
  * ```
152
145
  */
153
146
  export const clickElement = (selector) => Effect.gen(function* () {
154
- yield* awaitNextFrame;
147
+ yield* afterRender;
155
148
  const element = yield* queryHTMLElement(selector);
156
149
  element.click();
157
150
  });
@@ -165,7 +158,7 @@ export const clickElement = (selector) => Effect.gen(function* () {
165
158
  * ```
166
159
  */
167
160
  export const scrollIntoView = (selector) => Effect.gen(function* () {
168
- yield* awaitNextFrame;
161
+ yield* afterRender;
169
162
  const element = yield* queryHTMLElement(selector);
170
163
  element.scrollIntoView({ block: 'nearest' });
171
164
  });
@@ -179,7 +172,7 @@ export const scrollIntoView = (selector) => Effect.gen(function* () {
179
172
  * ```
180
173
  */
181
174
  export const advanceFocus = (selector, direction) => Effect.gen(function* () {
182
- yield* awaitNextFrame;
175
+ yield* afterRender;
183
176
  const reference = yield* queryHTMLElement(selector);
184
177
  const focusableElements = Array.fromIterable(document.querySelectorAll(FOCUSABLE_SELECTOR));
185
178
  const referenceElementIndex = Array.findFirstIndex(focusableElements, Equal.equals(reference));
@@ -1 +1 @@
1
- {"version":3,"file":"elementMovement.d.ts","sourceRoot":"","sources":["../../src/task/elementMovement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAI/B;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,qBAAqB,GAAI,UAAU,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAqCvE,CAAA"}
1
+ {"version":3,"file":"elementMovement.d.ts","sourceRoot":"","sources":["../../src/task/elementMovement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAM/B;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,qBAAqB,GAAI,UAAU,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAmCvE,CAAA"}
@@ -1,4 +1,5 @@
1
1
  import { Effect } from 'effect';
2
+ import { afterRender } from './timing.js';
2
3
  const rectToPosition = (rect) => `${rect.x},${rect.y}`;
3
4
  /**
4
5
  * Detects if the element matching the given selector moves in the viewport.
@@ -18,11 +19,9 @@ const rectToPosition = (rect) => `${rect.x},${rect.y}`;
18
19
  * )
19
20
  * ```
20
21
  */
21
- export const detectElementMovement = (selector) => Effect.callback((resume, signal) => {
22
- requestAnimationFrame(() => {
23
- if (signal.aborted) {
24
- return;
25
- }
22
+ export const detectElementMovement = (selector) => Effect.gen(function* () {
23
+ yield* afterRender;
24
+ return yield* Effect.callback((resume, signal) => {
26
25
  const element = document.querySelector(selector);
27
26
  if (!(element instanceof HTMLElement)) {
28
27
  return resume(Effect.void);
@@ -3,7 +3,7 @@ export { getTime, getTimeZone, getZonedTime, getZonedTimeIn } from './time.js';
3
3
  export { advanceFocus, focus, showModal, closeModal, clickElement, scrollIntoView, } from './dom.js';
4
4
  export type { FocusDirection } from './dom.js';
5
5
  export { detectElementMovement } from './elementMovement.js';
6
- export { delay, nextFrame, waitForAnimationSettled } from './timing.js';
6
+ export { afterRender, delay, nextFrame, waitForAnimationSettled, } from './timing.js';
7
7
  export { randomInt, uuid } from './random.js';
8
8
  export { lockScroll, unlockScroll } from './scrollLock.js';
9
9
  export { inertOthers, restoreInert } from './inert.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/task/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC3D,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC9E,OAAO,EACL,YAAY,EACZ,KAAK,EACL,SAAS,EACT,UAAU,EACV,YAAY,EACZ,cAAc,GACf,MAAM,UAAU,CAAA;AACjB,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AACvE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC1D,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/task/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC3D,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC9E,OAAO,EACL,YAAY,EACZ,KAAK,EACL,SAAS,EACT,UAAU,EACV,YAAY,EACZ,cAAc,GACf,MAAM,UAAU,CAAA;AACjB,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,EACL,WAAW,EACX,KAAK,EACL,SAAS,EACT,uBAAuB,GACxB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC1D,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA"}
@@ -2,7 +2,7 @@ export { ElementNotFound, TimeZoneError } from './error.js';
2
2
  export { getTime, getTimeZone, getZonedTime, getZonedTimeIn } from './time.js';
3
3
  export { advanceFocus, focus, showModal, closeModal, clickElement, scrollIntoView, } from './dom.js';
4
4
  export { detectElementMovement } from './elementMovement.js';
5
- export { delay, nextFrame, waitForAnimationSettled } from './timing.js';
5
+ export { afterRender, delay, nextFrame, waitForAnimationSettled, } from './timing.js';
6
6
  export { randomInt, uuid } from './random.js';
7
7
  export { lockScroll, unlockScroll } from './scrollLock.js';
8
8
  export { inertOthers, restoreInert } from './inert.js';
@@ -1,2 +1,2 @@
1
- export { ElementNotFound, TimeZoneError, getTime, getTimeZone, getZonedTime, getZonedTimeIn, focus, showModal, closeModal, clickElement, delay, scrollIntoView, randomInt, uuid, nextFrame, waitForAnimationSettled, } from './index.js';
1
+ export { ElementNotFound, TimeZoneError, afterRender, clickElement, closeModal, delay, focus, getTime, getTimeZone, getZonedTime, getZonedTimeIn, nextFrame, randomInt, scrollIntoView, showModal, uuid, waitForAnimationSettled, } from './index.js';
2
2
  //# sourceMappingURL=public.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/task/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,aAAa,EACb,OAAO,EACP,WAAW,EACX,YAAY,EACZ,cAAc,EACd,KAAK,EACL,SAAS,EACT,UAAU,EACV,YAAY,EACZ,KAAK,EACL,cAAc,EACd,SAAS,EACT,IAAI,EACJ,SAAS,EACT,uBAAuB,GACxB,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/task/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,aAAa,EACb,WAAW,EACX,YAAY,EACZ,UAAU,EACV,KAAK,EACL,KAAK,EACL,OAAO,EACP,WAAW,EACX,YAAY,EACZ,cAAc,EACd,SAAS,EACT,SAAS,EACT,cAAc,EACd,SAAS,EACT,IAAI,EACJ,uBAAuB,GACxB,MAAM,YAAY,CAAA"}
@@ -1 +1 @@
1
- export { ElementNotFound, TimeZoneError, getTime, getTimeZone, getZonedTime, getZonedTimeIn, focus, showModal, closeModal, clickElement, delay, scrollIntoView, randomInt, uuid, nextFrame, waitForAnimationSettled, } from './index.js';
1
+ export { ElementNotFound, TimeZoneError, afterRender, clickElement, closeModal, delay, focus, getTime, getTimeZone, getZonedTime, getZonedTimeIn, nextFrame, randomInt, scrollIntoView, showModal, uuid, waitForAnimationSettled, } from './index.js';
@@ -9,6 +9,29 @@ import { Duration, Effect } from 'effect';
9
9
  * ```
10
10
  */
11
11
  export declare const delay: (duration: Duration.Input) => Effect.Effect<void>;
12
+ /**
13
+ * Completes after the runtime's next render commits. The runtime batches
14
+ * renders to `requestAnimationFrame`, so a Command, Subscription, or other
15
+ * Effect that runs immediately after a dirtying Message would otherwise
16
+ * query the DOM before the matching VDOM patch has applied. Yield this
17
+ * before any DOM read or write whose target was just brought into existence
18
+ * (or moved, or had its attributes changed) by the same Message.
19
+ *
20
+ * The Task DOM helpers (`focus`, `clickElement`, `scrollIntoView`, etc.)
21
+ * already gate themselves with this internally; reach for `afterRender`
22
+ * directly when building custom Commands or DOM-observing Subscriptions
23
+ * that need the same guarantee.
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * Effect.gen(function* () {
28
+ * yield* Task.afterRender
29
+ * const element = document.getElementById(id)
30
+ * // element reflects the post-Message DOM
31
+ * })
32
+ * ```
33
+ */
34
+ export declare const afterRender: Effect.Effect<void>;
12
35
  /**
13
36
  * Completes after two animation frames, ensuring the browser has painted
14
37
  * the current state before proceeding. Used for CSS transition orchestration —
@@ -1 +1 @@
1
- {"version":3,"file":"timing.d.ts","sourceRoot":"","sources":["../../src/task/timing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAEzC;;;;;;;;GAQG;AACH,eAAO,MAAM,KAAK,GAAI,UAAU,QAAQ,CAAC,KAAK,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAC3C,CAAA;AAExB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAMxC,CAAA;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,uBAAuB,GAClC,UAAU,MAAM,KACf,MAAM,CAAC,MAAM,CAAC,IAAI,CAYjB,CAAA"}
1
+ {"version":3,"file":"timing.d.ts","sourceRoot":"","sources":["../../src/task/timing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAEzC;;;;;;;;GAQG;AACH,eAAO,MAAM,KAAK,GAAI,UAAU,QAAQ,CAAC,KAAK,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAC3C,CAAA;AAExB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAK3C,CAAA;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAMxC,CAAA;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,uBAAuB,GAClC,UAAU,MAAM,KACf,MAAM,CAAC,MAAM,CAAC,IAAI,CAYjB,CAAA"}
@@ -9,6 +9,32 @@ import { Effect } from 'effect';
9
9
  * ```
10
10
  */
11
11
  export const delay = (duration) => Effect.sleep(duration);
12
+ /**
13
+ * Completes after the runtime's next render commits. The runtime batches
14
+ * renders to `requestAnimationFrame`, so a Command, Subscription, or other
15
+ * Effect that runs immediately after a dirtying Message would otherwise
16
+ * query the DOM before the matching VDOM patch has applied. Yield this
17
+ * before any DOM read or write whose target was just brought into existence
18
+ * (or moved, or had its attributes changed) by the same Message.
19
+ *
20
+ * The Task DOM helpers (`focus`, `clickElement`, `scrollIntoView`, etc.)
21
+ * already gate themselves with this internally; reach for `afterRender`
22
+ * directly when building custom Commands or DOM-observing Subscriptions
23
+ * that need the same guarantee.
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * Effect.gen(function* () {
28
+ * yield* Task.afterRender
29
+ * const element = document.getElementById(id)
30
+ * // element reflects the post-Message DOM
31
+ * })
32
+ * ```
33
+ */
34
+ export const afterRender = Effect.callback(resume => {
35
+ const handle = requestAnimationFrame(() => resume(Effect.void));
36
+ return Effect.sync(() => cancelAnimationFrame(handle));
37
+ });
12
38
  /**
13
39
  * Completes after two animation frames, ensuring the browser has painted
14
40
  * the current state before proceeding. Used for CSS transition orchestration —
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foldkit",
3
- "version": "0.85.0",
3
+ "version": "0.86.0",
4
4
  "description": "A TypeScript frontend framework, built on Effect and architected like Elm",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",