foldkit 0.19.0 → 0.21.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.
Files changed (61) hide show
  1. package/README.md +3 -3
  2. package/dist/command/index.d.ts +4 -0
  3. package/dist/command/index.d.ts.map +1 -0
  4. package/dist/command/index.js +1 -0
  5. package/dist/command/public.d.ts +2 -0
  6. package/dist/command/public.d.ts.map +1 -0
  7. package/dist/command/public.js +1 -0
  8. package/dist/html/index.d.ts +14 -0
  9. package/dist/html/index.d.ts.map +1 -1
  10. package/dist/html/index.js +5 -1
  11. package/dist/index.d.ts +1 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/runtime/public.d.ts +2 -2
  14. package/dist/runtime/public.d.ts.map +1 -1
  15. package/dist/runtime/public.js +1 -1
  16. package/dist/runtime/runtime.d.ts +18 -19
  17. package/dist/runtime/runtime.d.ts.map +1 -1
  18. package/dist/runtime/runtime.js +9 -9
  19. package/dist/task/dom.d.ts +26 -27
  20. package/dist/task/dom.d.ts.map +1 -1
  21. package/dist/task/dom.js +36 -37
  22. package/dist/task/elementMovement.d.ts +21 -0
  23. package/dist/task/elementMovement.d.ts.map +1 -0
  24. package/dist/task/elementMovement.js +49 -0
  25. package/dist/task/error.d.ts +18 -0
  26. package/dist/task/error.d.ts.map +1 -0
  27. package/dist/task/error.js +7 -0
  28. package/dist/task/index.d.ts +2 -0
  29. package/dist/task/index.d.ts.map +1 -1
  30. package/dist/task/index.js +2 -0
  31. package/dist/task/inert.d.ts +12 -11
  32. package/dist/task/inert.d.ts.map +1 -1
  33. package/dist/task/inert.js +12 -13
  34. package/dist/task/public.d.ts +1 -1
  35. package/dist/task/public.d.ts.map +1 -1
  36. package/dist/task/public.js +1 -1
  37. package/dist/task/random.d.ts +3 -4
  38. package/dist/task/random.d.ts.map +1 -1
  39. package/dist/task/random.js +3 -4
  40. package/dist/task/scrollLock.d.ts +9 -9
  41. package/dist/task/scrollLock.d.ts.map +1 -1
  42. package/dist/task/scrollLock.js +9 -11
  43. package/dist/task/time.d.ts +17 -16
  44. package/dist/task/time.d.ts.map +1 -1
  45. package/dist/task/time.js +20 -21
  46. package/dist/task/timing.d.ts +14 -15
  47. package/dist/task/timing.d.ts.map +1 -1
  48. package/dist/task/timing.js +16 -20
  49. package/dist/ui/dialog/index.d.ts +1 -1
  50. package/dist/ui/dialog/index.d.ts.map +1 -1
  51. package/dist/ui/dialog/index.js +5 -11
  52. package/dist/ui/disclosure/index.d.ts +1 -1
  53. package/dist/ui/disclosure/index.d.ts.map +1 -1
  54. package/dist/ui/disclosure/index.js +5 -8
  55. package/dist/ui/menu/index.d.ts +7 -3
  56. package/dist/ui/menu/index.d.ts.map +1 -1
  57. package/dist/ui/menu/index.js +45 -43
  58. package/dist/ui/tabs/index.d.ts +4 -5
  59. package/dist/ui/tabs/index.d.ts.map +1 -1
  60. package/dist/ui/tabs/index.js +6 -8
  61. package/package.json +9 -1
package/dist/task/dom.js CHANGED
@@ -1,113 +1,112 @@
1
1
  import { Effect } from 'effect';
2
+ import { ElementNotFound } from './error';
2
3
  /**
3
- * Creates a command that focuses an element by selector and passes the result to a message constructor.
4
- * Passes true if the element was found and focused, false otherwise.
5
- * Uses requestAnimationFrame to ensure the DOM tree is updated and nodes exist before attempting to focus.
6
- * This follows the same approach as Elm's Browser.Dom.focus.
4
+ * Focuses an element matching the given selector.
5
+ * Uses requestAnimationFrame to ensure the DOM is updated before attempting to focus.
6
+ * Fails with `ElementNotFound` if the selector does not match an `HTMLElement`.
7
7
  *
8
8
  * @example
9
9
  * ```typescript
10
- * Task.focus('#email-input', success => InputFocused({ success }))
10
+ * Task.focus('#email-input').pipe(Effect.ignore, Effect.as(NoOp()))
11
11
  * ```
12
12
  */
13
- export const focus = (selector, f) => Effect.async(resume => {
13
+ export const focus = (selector) => Effect.async(resume => {
14
14
  requestAnimationFrame(() => {
15
15
  const element = document.querySelector(selector);
16
16
  if (element instanceof HTMLElement) {
17
17
  element.focus();
18
- resume(Effect.succeed(f(true)));
18
+ resume(Effect.void);
19
19
  }
20
20
  else {
21
- resume(Effect.succeed(f(false)));
21
+ resume(Effect.fail(new ElementNotFound({ selector })));
22
22
  }
23
23
  });
24
24
  });
25
25
  /**
26
- * Creates a command that opens a dialog element as a modal using `showModal()`.
27
- * Passes true if the element was found and opened, false otherwise.
28
- * Uses requestAnimationFrame to ensure the DOM tree is updated and nodes exist before attempting to show.
26
+ * Opens a dialog element as a modal using `showModal()`.
27
+ * Uses requestAnimationFrame to ensure the DOM is updated before attempting to show.
28
+ * Fails with `ElementNotFound` if the selector does not match an `HTMLDialogElement`.
29
29
  *
30
30
  * @example
31
31
  * ```typescript
32
- * Task.showModal('#my-dialog', success => ModalOpened({ success }))
32
+ * Task.showModal('#my-dialog').pipe(Effect.ignore, Effect.as(NoOp()))
33
33
  * ```
34
34
  */
35
- export const showModal = (selector, f) => Effect.async(resume => {
35
+ export const showModal = (selector) => Effect.async(resume => {
36
36
  requestAnimationFrame(() => {
37
37
  const element = document.querySelector(selector);
38
38
  if (element instanceof HTMLDialogElement) {
39
39
  element.showModal();
40
- resume(Effect.succeed(f(true)));
40
+ resume(Effect.void);
41
41
  }
42
42
  else {
43
- resume(Effect.succeed(f(false)));
43
+ resume(Effect.fail(new ElementNotFound({ selector })));
44
44
  }
45
45
  });
46
46
  });
47
47
  /**
48
- * Creates a command that closes a dialog element using `.close()`.
49
- * Passes true if the element was found and closed, false otherwise.
50
- * Uses requestAnimationFrame to ensure the DOM tree is updated and nodes exist before attempting to close.
48
+ * Closes a dialog element using `.close()`.
49
+ * Uses requestAnimationFrame to ensure the DOM is updated before attempting to close.
50
+ * Fails with `ElementNotFound` if the selector does not match an `HTMLDialogElement`.
51
51
  *
52
52
  * @example
53
53
  * ```typescript
54
- * Task.closeModal('#my-dialog', success => ModalClosed({ success }))
54
+ * Task.closeModal('#my-dialog').pipe(Effect.ignore, Effect.as(NoOp()))
55
55
  * ```
56
56
  */
57
- export const closeModal = (selector, f) => Effect.async(resume => {
57
+ export const closeModal = (selector) => Effect.async(resume => {
58
58
  requestAnimationFrame(() => {
59
59
  const element = document.querySelector(selector);
60
60
  if (element instanceof HTMLDialogElement) {
61
61
  element.close();
62
- resume(Effect.succeed(f(true)));
62
+ resume(Effect.void);
63
63
  }
64
64
  else {
65
- resume(Effect.succeed(f(false)));
65
+ resume(Effect.fail(new ElementNotFound({ selector })));
66
66
  }
67
67
  });
68
68
  });
69
69
  /**
70
- * Creates a command that programmatically clicks an element by selector and passes the result to a message constructor.
71
- * Passes true if the element was found and clicked, false otherwise.
72
- * Uses requestAnimationFrame to ensure the DOM tree is updated and nodes exist before attempting to click.
70
+ * Programmatically clicks an element matching the given selector.
71
+ * Uses requestAnimationFrame to ensure the DOM is updated before attempting to click.
72
+ * Fails with `ElementNotFound` if the selector does not match an `HTMLElement`.
73
73
  *
74
74
  * @example
75
75
  * ```typescript
76
- * Task.clickElement('#menu-item-2', success => ItemClicked({ success }))
76
+ * Task.clickElement('#menu-item-2').pipe(Effect.ignore, Effect.as(NoOp()))
77
77
  * ```
78
78
  */
79
- export const clickElement = (selector, f) => Effect.async(resume => {
79
+ export const clickElement = (selector) => Effect.async(resume => {
80
80
  requestAnimationFrame(() => {
81
81
  const element = document.querySelector(selector);
82
82
  if (element instanceof HTMLElement) {
83
83
  element.click();
84
- resume(Effect.succeed(f(true)));
84
+ resume(Effect.void);
85
85
  }
86
86
  else {
87
- resume(Effect.succeed(f(false)));
87
+ resume(Effect.fail(new ElementNotFound({ selector })));
88
88
  }
89
89
  });
90
90
  });
91
91
  /**
92
- * Creates a command that scrolls an element into view by selector and passes the result to a message constructor.
93
- * Passes true if the element was found and scrolled, false otherwise.
94
- * Uses requestAnimationFrame to ensure the DOM tree is updated and nodes exist before attempting to scroll.
95
- * Uses `{ block: 'nearest' }` to avoid unnecessary scrolling when the element is already visible.
92
+ * Scrolls an element into view by selector using `{ block: 'nearest' }`.
93
+ * Uses requestAnimationFrame to ensure the DOM is updated before attempting to scroll.
94
+ * Fails with `ElementNotFound` if the selector does not match an `HTMLElement`.
96
95
  *
97
96
  * @example
98
97
  * ```typescript
99
- * Task.scrollIntoView('#active-item', success => ItemScrolled({ success }))
98
+ * Task.scrollIntoView('#active-item').pipe(Effect.ignore, Effect.as(NoOp()))
100
99
  * ```
101
100
  */
102
- export const scrollIntoView = (selector, f) => Effect.async(resume => {
101
+ export const scrollIntoView = (selector) => Effect.async(resume => {
103
102
  requestAnimationFrame(() => {
104
103
  const element = document.querySelector(selector);
105
104
  if (element instanceof HTMLElement) {
106
105
  element.scrollIntoView({ block: 'nearest' });
107
- resume(Effect.succeed(f(true)));
106
+ resume(Effect.void);
108
107
  }
109
108
  else {
110
- resume(Effect.succeed(f(false)));
109
+ resume(Effect.fail(new ElementNotFound({ selector })));
111
110
  }
112
111
  });
113
112
  });
@@ -0,0 +1,21 @@
1
+ import { Effect } from 'effect';
2
+ /**
3
+ * Detects if the element matching the given selector moves in the viewport.
4
+ * Snapshots the element's position via `getBoundingClientRect` and watches for
5
+ * changes using a `ResizeObserver` plus window `scroll` and `resize` listeners.
6
+ * Resolves when movement is detected. Falls back to completing immediately if
7
+ * the element is missing.
8
+ *
9
+ * Cleanup runs automatically when the fiber is interrupted (e.g. by
10
+ * `Effect.raceFirst`), removing the observer and event listeners via
11
+ * `AbortSignal`.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * Task.detectElementMovement('#menu-button').pipe(
16
+ * Effect.as(DetectedButtonMovement()),
17
+ * )
18
+ * ```
19
+ */
20
+ export declare const detectElementMovement: (selector: string) => Effect.Effect<void>;
21
+ //# sourceMappingURL=elementMovement.d.ts.map
@@ -0,0 +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,CAsCvE,CAAA"}
@@ -0,0 +1,49 @@
1
+ import { Effect } from 'effect';
2
+ const rectToPosition = (rect) => `${rect.x},${rect.y}`;
3
+ /**
4
+ * Detects if the element matching the given selector moves in the viewport.
5
+ * Snapshots the element's position via `getBoundingClientRect` and watches for
6
+ * changes using a `ResizeObserver` plus window `scroll` and `resize` listeners.
7
+ * Resolves when movement is detected. Falls back to completing immediately if
8
+ * the element is missing.
9
+ *
10
+ * Cleanup runs automatically when the fiber is interrupted (e.g. by
11
+ * `Effect.raceFirst`), removing the observer and event listeners via
12
+ * `AbortSignal`.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * Task.detectElementMovement('#menu-button').pipe(
17
+ * Effect.as(DetectedButtonMovement()),
18
+ * )
19
+ * ```
20
+ */
21
+ export const detectElementMovement = (selector) => Effect.async((resume, signal) => {
22
+ requestAnimationFrame(() => {
23
+ if (signal.aborted) {
24
+ return;
25
+ }
26
+ const element = document.querySelector(selector);
27
+ if (!(element instanceof HTMLElement)) {
28
+ resume(Effect.void);
29
+ return;
30
+ }
31
+ const initialPosition = rectToPosition(element.getBoundingClientRect());
32
+ const cleanup = () => {
33
+ observer.disconnect();
34
+ window.removeEventListener('scroll', check, { capture: true });
35
+ window.removeEventListener('resize', check);
36
+ };
37
+ const check = () => {
38
+ if (rectToPosition(element.getBoundingClientRect()) !== initialPosition) {
39
+ cleanup();
40
+ resume(Effect.void);
41
+ }
42
+ };
43
+ const observer = new ResizeObserver(check);
44
+ observer.observe(element);
45
+ window.addEventListener('scroll', check, { passive: true, capture: true });
46
+ window.addEventListener('resize', check);
47
+ signal.addEventListener('abort', cleanup);
48
+ });
49
+ });
@@ -0,0 +1,18 @@
1
+ declare const ElementNotFound_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
2
+ readonly _tag: "ElementNotFound";
3
+ } & Readonly<A>;
4
+ /** Error indicating that a DOM element matching the given selector was not found. */
5
+ export declare class ElementNotFound extends ElementNotFound_base<{
6
+ readonly selector: string;
7
+ }> {
8
+ }
9
+ declare const TimeZoneError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
10
+ readonly _tag: "TimeZoneError";
11
+ } & Readonly<A>;
12
+ /** Error indicating that the given timezone ID is invalid. */
13
+ export declare class TimeZoneError extends TimeZoneError_base<{
14
+ readonly zoneId: string;
15
+ }> {
16
+ }
17
+ export {};
18
+ //# sourceMappingURL=error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/task/error.ts"],"names":[],"mappings":";;;AAEA,qFAAqF;AACrF,qBAAa,eAAgB,SAAQ,qBAAoC;IACvE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC1B,CAAC;CAAG;;;;AAEL,8DAA8D;AAC9D,qBAAa,aAAc,SAAQ,mBAAkC;IACnE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB,CAAC;CAAG"}
@@ -0,0 +1,7 @@
1
+ import { Data } from 'effect';
2
+ /** Error indicating that a DOM element matching the given selector was not found. */
3
+ export class ElementNotFound extends Data.TaggedError('ElementNotFound') {
4
+ }
5
+ /** Error indicating that the given timezone ID is invalid. */
6
+ export class TimeZoneError extends Data.TaggedError('TimeZoneError') {
7
+ }
@@ -1,5 +1,7 @@
1
+ export { ElementNotFound, TimeZoneError } from './error';
1
2
  export { getTime, getTimeZone, getZonedTime, getZonedTimeIn } from './time';
2
3
  export { focus, showModal, closeModal, clickElement, scrollIntoView, } from './dom';
4
+ export { detectElementMovement } from './elementMovement';
3
5
  export { delay, nextFrame, waitForTransitions } from './timing';
4
6
  export { randomInt } from './random';
5
7
  export { lockScroll, unlockScroll } from './scrollLock';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/task/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAC3E,OAAO,EACL,KAAK,EACL,SAAS,EACT,UAAU,EACV,YAAY,EACZ,cAAc,GACf,MAAM,OAAO,CAAA;AACd,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACpC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AACvD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/task/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACxD,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAC3E,OAAO,EACL,KAAK,EACL,SAAS,EACT,UAAU,EACV,YAAY,EACZ,cAAc,GACf,MAAM,OAAO,CAAA;AACd,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAA;AACzD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACpC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AACvD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA"}
@@ -1,5 +1,7 @@
1
+ export { ElementNotFound, TimeZoneError } from './error';
1
2
  export { getTime, getTimeZone, getZonedTime, getZonedTimeIn } from './time';
2
3
  export { focus, showModal, closeModal, clickElement, scrollIntoView, } from './dom';
4
+ export { detectElementMovement } from './elementMovement';
3
5
  export { delay, nextFrame, waitForTransitions } from './timing';
4
6
  export { randomInt } from './random';
5
7
  export { lockScroll, unlockScroll } from './scrollLock';
@@ -1,25 +1,26 @@
1
1
  import { Effect } from 'effect';
2
2
  /**
3
- * Creates a command that marks all DOM elements outside the given selectors as
4
- * `inert` and `aria-hidden="true"`. Walks each allowed element up to
5
- * `document.body`, marking siblings that don't contain an allowed element.
6
- * Uses reference counting so nested calls are safe.
3
+ * Marks all DOM elements outside the given selectors as `inert` and
4
+ * `aria-hidden="true"`. Walks each allowed element up to `document.body`,
5
+ * marking siblings that don't contain an allowed element. Uses reference
6
+ * counting so nested calls are safe.
7
7
  *
8
8
  * @example
9
9
  * ```typescript
10
- * Task.inertOthers('my-menu', ['#menu-button', '#menu-items'], () => NoOp())
10
+ * Task.inertOthers('my-menu', ['#menu-button', '#menu-items']).pipe(
11
+ * Effect.as(NoOp()),
12
+ * )
11
13
  * ```
12
14
  */
13
- export declare const inertOthers: <Message>(id: string, allowedSelectors: ReadonlyArray<string>, f: () => Message) => Effect.Effect<Message>;
15
+ export declare const inertOthers: (id: string, allowedSelectors: ReadonlyArray<string>) => Effect.Effect<void>;
14
16
  /**
15
- * Creates a command that restores all elements previously marked inert by
16
- * `inertOthers` for the given ID. Safe to call without a preceding
17
- * `inertOthers` — acts as a no-op in that case.
17
+ * Restores all elements previously marked inert by `inertOthers` for the
18
+ * given ID. Safe to call without a preceding `inertOthers` — acts as a no-op.
18
19
  *
19
20
  * @example
20
21
  * ```typescript
21
- * Task.restoreInert('my-menu', () => NoOp())
22
+ * Task.restoreInert('my-menu').pipe(Effect.as(NoOp()))
22
23
  * ```
23
24
  */
24
- export declare const restoreInert: <Message>(id: string, f: () => Message) => Effect.Effect<Message>;
25
+ export declare const restoreInert: (id: string) => Effect.Effect<void>;
25
26
  //# sourceMappingURL=inert.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"inert.d.ts","sourceRoot":"","sources":["../../src/task/inert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAmC,MAAM,QAAQ,CAAA;AAoFvE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,EACjC,IAAI,MAAM,EACV,kBAAkB,aAAa,CAAC,MAAM,CAAC,EACvC,GAAG,MAAM,OAAO,KACf,MAAM,CAAC,MAAM,CAAC,OAAO,CAepB,CAAA;AAEJ;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,EAClC,IAAI,MAAM,EACV,GAAG,MAAM,OAAO,KACf,MAAM,CAAC,MAAM,CAAC,OAAO,CAUpB,CAAA"}
1
+ {"version":3,"file":"inert.d.ts","sourceRoot":"","sources":["../../src/task/inert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAmC,MAAM,QAAQ,CAAA;AAoFvE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,WAAW,GACtB,IAAI,MAAM,EACV,kBAAkB,aAAa,CAAC,MAAM,CAAC,KACtC,MAAM,CAAC,MAAM,CAAC,IAAI,CAajB,CAAA;AAEJ;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GAAI,IAAI,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAQxD,CAAA"}
@@ -52,37 +52,36 @@ const inertableSiblings = (parent, allowedElements) => pipe(parent.children, Arr
52
52
  ? Option.some(child)
53
53
  : Option.none()));
54
54
  /**
55
- * Creates a command that marks all DOM elements outside the given selectors as
56
- * `inert` and `aria-hidden="true"`. Walks each allowed element up to
57
- * `document.body`, marking siblings that don't contain an allowed element.
58
- * Uses reference counting so nested calls are safe.
55
+ * Marks all DOM elements outside the given selectors as `inert` and
56
+ * `aria-hidden="true"`. Walks each allowed element up to `document.body`,
57
+ * marking siblings that don't contain an allowed element. Uses reference
58
+ * counting so nested calls are safe.
59
59
  *
60
60
  * @example
61
61
  * ```typescript
62
- * Task.inertOthers('my-menu', ['#menu-button', '#menu-items'], () => NoOp())
62
+ * Task.inertOthers('my-menu', ['#menu-button', '#menu-items']).pipe(
63
+ * Effect.as(NoOp()),
64
+ * )
63
65
  * ```
64
66
  */
65
- export const inertOthers = (id, allowedSelectors, f) => Effect.sync(() => {
67
+ export const inertOthers = (id, allowedSelectors) => Effect.sync(() => {
66
68
  const allowedElements = resolveElements(allowedSelectors);
67
69
  const cleanupFunctions = pipe(allowedElements, Array.flatMap(ancestorsUpToBody), Array.flatMap(ancestor => Array.map(inertableSiblings(ancestor, allowedElements), markInert)));
68
70
  inertState.cleanups.set(id, cleanupFunctions);
69
- return f();
70
71
  });
71
72
  /**
72
- * Creates a command that restores all elements previously marked inert by
73
- * `inertOthers` for the given ID. Safe to call without a preceding
74
- * `inertOthers` — acts as a no-op in that case.
73
+ * Restores all elements previously marked inert by `inertOthers` for the
74
+ * given ID. Safe to call without a preceding `inertOthers` — acts as a no-op.
75
75
  *
76
76
  * @example
77
77
  * ```typescript
78
- * Task.restoreInert('my-menu', () => NoOp())
78
+ * Task.restoreInert('my-menu').pipe(Effect.as(NoOp()))
79
79
  * ```
80
80
  */
81
- export const restoreInert = (id, f) => Effect.sync(() => {
81
+ export const restoreInert = (id) => Effect.sync(() => {
82
82
  const cleanupFunctions = inertState.cleanups.get(id);
83
83
  if (cleanupFunctions) {
84
84
  Array.forEach(cleanupFunctions, cleanup => cleanup());
85
85
  inertState.cleanups.delete(id);
86
86
  }
87
- return f();
88
87
  });
@@ -1,2 +1,2 @@
1
- export { getTime, getTimeZone, getZonedTime, getZonedTimeIn, focus, showModal, closeModal, clickElement, delay, scrollIntoView, randomInt, nextFrame, waitForTransitions, } from './index';
1
+ export { ElementNotFound, TimeZoneError, getTime, getTimeZone, getZonedTime, getZonedTimeIn, focus, showModal, closeModal, clickElement, delay, scrollIntoView, randomInt, nextFrame, waitForTransitions, } from './index';
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,OAAO,EACP,WAAW,EACX,YAAY,EACZ,cAAc,EACd,KAAK,EACL,SAAS,EACT,UAAU,EACV,YAAY,EACZ,KAAK,EACL,cAAc,EACd,SAAS,EACT,SAAS,EACT,kBAAkB,GACnB,MAAM,SAAS,CAAA"}
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,SAAS,EACT,kBAAkB,GACnB,MAAM,SAAS,CAAA"}
@@ -1 +1 @@
1
- export { getTime, getTimeZone, getZonedTime, getZonedTimeIn, focus, showModal, closeModal, clickElement, delay, scrollIntoView, randomInt, nextFrame, waitForTransitions, } from './index';
1
+ export { ElementNotFound, TimeZoneError, getTime, getTimeZone, getZonedTime, getZonedTimeIn, focus, showModal, closeModal, clickElement, delay, scrollIntoView, randomInt, nextFrame, waitForTransitions, } from './index';
@@ -1,12 +1,11 @@
1
1
  import { Effect } from 'effect';
2
2
  /**
3
- * Creates a command that generates a random integer between min (inclusive) and max (exclusive)
4
- * and passes it to a message constructor.
3
+ * Generates a random integer between min (inclusive) and max (exclusive).
5
4
  *
6
5
  * @example
7
6
  * ```typescript
8
- * Task.randomInt(0, 100, value => GotRandom({ value }))
7
+ * Task.randomInt(1, 7).pipe(Effect.map(value => GotDiceRoll({ value })))
9
8
  * ```
10
9
  */
11
- export declare const randomInt: <Message>(min: number, max: number, f: (value: number) => Message) => Effect.Effect<Message>;
10
+ export declare const randomInt: (min: number, max: number) => Effect.Effect<number>;
12
11
  //# sourceMappingURL=random.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"random.d.ts","sourceRoot":"","sources":["../../src/task/random.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE/B;;;;;;;;GAQG;AACH,eAAO,MAAM,SAAS,GAAI,OAAO,EAC/B,KAAK,MAAM,EACX,KAAK,MAAM,EACX,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,KAC5B,MAAM,CAAC,MAAM,CAAC,OAAO,CAC6C,CAAA"}
1
+ {"version":3,"file":"random.d.ts","sourceRoot":"","sources":["../../src/task/random.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE/B;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,GAAI,KAAK,MAAM,EAAE,KAAK,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CACP,CAAA"}
@@ -1,11 +1,10 @@
1
1
  import { Effect } from 'effect';
2
2
  /**
3
- * Creates a command that generates a random integer between min (inclusive) and max (exclusive)
4
- * and passes it to a message constructor.
3
+ * Generates a random integer between min (inclusive) and max (exclusive).
5
4
  *
6
5
  * @example
7
6
  * ```typescript
8
- * Task.randomInt(0, 100, value => GotRandom({ value }))
7
+ * Task.randomInt(1, 7).pipe(Effect.map(value => GotDiceRoll({ value })))
9
8
  * ```
10
9
  */
11
- export const randomInt = (min, max, f) => Effect.sync(() => f(Math.floor(Math.random() * (max - min)) + min));
10
+ export const randomInt = (min, max) => Effect.sync(() => Math.floor(Math.random() * (max - min)) + min);
@@ -1,24 +1,24 @@
1
1
  import { Effect } from 'effect';
2
2
  /**
3
- * Creates a command that locks page scroll by setting `overflow: hidden` on the
4
- * document element. Compensates for scrollbar width with padding to prevent layout
5
- * shift. Uses reference counting so nested locks are safe — the page only unlocks
3
+ * Locks page scroll by setting `overflow: hidden` on the document element.
4
+ * Compensates for scrollbar width with padding to prevent layout shift.
5
+ * Uses reference counting so nested locks are safe — the page only unlocks
6
6
  * when every lock has been released.
7
7
  *
8
8
  * @example
9
9
  * ```typescript
10
- * Task.lockScroll(() => NoOp())
10
+ * Task.lockScroll.pipe(Effect.as(NoOp()))
11
11
  * ```
12
12
  */
13
- export declare const lockScroll: <Message>(f: () => Message) => Effect.Effect<Message>;
13
+ export declare const lockScroll: Effect.Effect<void>;
14
14
  /**
15
- * Creates a command that releases one scroll lock. When the last lock is released,
16
- * restores the original `overflow` and `padding-right` on the document element.
15
+ * Releases one scroll lock. When the last lock is released, restores the
16
+ * original `overflow` and `padding-right` on the document element.
17
17
  *
18
18
  * @example
19
19
  * ```typescript
20
- * Task.unlockScroll(() => NoOp())
20
+ * Task.unlockScroll.pipe(Effect.as(NoOp()))
21
21
  * ```
22
22
  */
23
- export declare const unlockScroll: <Message>(f: () => Message) => Effect.Effect<Message>;
23
+ export declare const unlockScroll: Effect.Effect<void>;
24
24
  //# sourceMappingURL=scrollLock.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scrollLock.d.ts","sourceRoot":"","sources":["../../src/task/scrollLock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAA;AAQvC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,EAAE,GAAG,MAAM,OAAO,KAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAqBxE,CAAA;AAEJ;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,EAClC,GAAG,MAAM,OAAO,KACf,MAAM,CAAC,MAAM,CAAC,OAAO,CAapB,CAAA"}
1
+ {"version":3,"file":"scrollLock.d.ts","sourceRoot":"","sources":["../../src/task/scrollLock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAA;AAQvC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAkBzC,CAAA;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAU3C,CAAA"}
@@ -5,17 +5,17 @@ const scrollLockState = {
5
5
  paddingRight: '',
6
6
  };
7
7
  /**
8
- * Creates a command that locks page scroll by setting `overflow: hidden` on the
9
- * document element. Compensates for scrollbar width with padding to prevent layout
10
- * shift. Uses reference counting so nested locks are safe — the page only unlocks
8
+ * Locks page scroll by setting `overflow: hidden` on the document element.
9
+ * Compensates for scrollbar width with padding to prevent layout shift.
10
+ * Uses reference counting so nested locks are safe — the page only unlocks
11
11
  * when every lock has been released.
12
12
  *
13
13
  * @example
14
14
  * ```typescript
15
- * Task.lockScroll(() => NoOp())
15
+ * Task.lockScroll.pipe(Effect.as(NoOp()))
16
16
  * ```
17
17
  */
18
- export const lockScroll = (f) => Effect.sync(() => {
18
+ export const lockScroll = Effect.sync(() => {
19
19
  const { documentElement, documentElement: { style }, } = document;
20
20
  if (scrollLockState.count === 0) {
21
21
  scrollLockState.overflow = style.overflow;
@@ -26,23 +26,21 @@ export const lockScroll = (f) => Effect.sync(() => {
26
26
  scrollbarWidth > 0 ? `${scrollbarWidth}px` : style.paddingRight;
27
27
  }
28
28
  scrollLockState.count++;
29
- return f();
30
29
  });
31
30
  /**
32
- * Creates a command that releases one scroll lock. When the last lock is released,
33
- * restores the original `overflow` and `padding-right` on the document element.
31
+ * Releases one scroll lock. When the last lock is released, restores the
32
+ * original `overflow` and `padding-right` on the document element.
34
33
  *
35
34
  * @example
36
35
  * ```typescript
37
- * Task.unlockScroll(() => NoOp())
36
+ * Task.unlockScroll.pipe(Effect.as(NoOp()))
38
37
  * ```
39
38
  */
40
- export const unlockScroll = (f) => Effect.sync(() => {
39
+ export const unlockScroll = Effect.sync(() => {
41
40
  scrollLockState.count = Math.max(0, Number.decrement(scrollLockState.count));
42
41
  if (scrollLockState.count === 0) {
43
42
  const { documentElement: { style }, } = document;
44
43
  style.overflow = scrollLockState.overflow;
45
44
  style.paddingRight = scrollLockState.paddingRight;
46
45
  }
47
- return f();
48
46
  });
@@ -1,42 +1,43 @@
1
1
  import { DateTime, Effect } from 'effect';
2
+ import { TimeZoneError } from './error';
2
3
  /**
3
- * Creates a command that gets the current UTC time and passes it to a message constructor.
4
- * This is similar to Elm's `Task.perform` with `Time.now`.
4
+ * Gets the current UTC time.
5
5
  *
6
6
  * @example
7
7
  * ```typescript
8
- * Task.getTime(utc => GotTime({ utc }))
8
+ * Task.getTime.pipe(Effect.map(utc => GotTime({ utc })))
9
9
  * ```
10
10
  */
11
- export declare const getTime: <Message>(f: (utc: DateTime.Utc) => Message) => Effect.Effect<Message>;
11
+ export declare const getTime: Effect.Effect<DateTime.Utc>;
12
12
  /**
13
- * Creates a command that gets the system timezone and passes it to a message constructor.
14
- * This is similar to Elm's `Task.perform` with `Time.here`.
13
+ * Gets the system timezone.
15
14
  *
16
15
  * @example
17
16
  * ```typescript
18
- * Task.getTimeZone(zone => GotTimeZone({ zone }))
17
+ * Task.getTimeZone.pipe(Effect.map(zone => GotTimeZone({ zone })))
19
18
  * ```
20
19
  */
21
- export declare const getTimeZone: <Message>(f: (zone: DateTime.TimeZone) => Message) => Effect.Effect<Message>;
20
+ export declare const getTimeZone: Effect.Effect<DateTime.TimeZone>;
22
21
  /**
23
- * Creates a command that gets the current time in the system timezone and passes it to a message constructor.
24
- * This combines both time and timezone in a single task.
22
+ * Gets the current time in the system timezone.
25
23
  *
26
24
  * @example
27
25
  * ```typescript
28
- * Task.getZonedTime(zoned => GotTime({ zoned }))
26
+ * Task.getZonedTime.pipe(Effect.map(zoned => GotTime({ zoned })))
29
27
  * ```
30
28
  */
31
- export declare const getZonedTime: <Message>(f: (zoned: DateTime.Zoned) => Message) => Effect.Effect<Message>;
29
+ export declare const getZonedTime: Effect.Effect<DateTime.Zoned>;
32
30
  /**
33
- * Creates a command that gets the current time in a specific timezone and passes it to a message constructor.
34
- * If the timezone is invalid, the effect will fail with an error string.
31
+ * Gets the current time in a specific timezone.
32
+ * Fails with `TimeZoneError` if the timezone ID is invalid.
35
33
  *
36
34
  * @example
37
35
  * ```typescript
38
- * Task.getZonedTimeIn('America/New_York', zoned => GotNYTime({ zoned }))
36
+ * Task.getZonedTimeIn('America/New_York').pipe(
37
+ * Effect.map(zoned => GotNYTime({ zoned })),
38
+ * Effect.catchAll(() => Effect.succeed(FailedTimeZone())),
39
+ * )
39
40
  * ```
40
41
  */
41
- export declare const getZonedTimeIn: <Message>(zoneId: string, f: (zoned: DateTime.Zoned) => Message) => Effect.Effect<Message, string>;
42
+ export declare const getZonedTimeIn: (zoneId: string) => Effect.Effect<DateTime.Zoned, TimeZoneError>;
42
43
  //# sourceMappingURL=time.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../src/task/time.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAA;AAEjD;;;;;;;;GAQG;AACH,eAAO,MAAM,OAAO,GAAI,OAAO,EAC7B,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,GAAG,KAAK,OAAO,KAChC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAgC,CAAA;AAExD;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,EACjC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,QAAQ,KAAK,OAAO,KACtC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAmD,CAAA;AAE3E;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,EAClC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,KAAK,OAAO,KACpC,MAAM,CAAC,MAAM,CAAC,OAAO,CAMpB,CAAA;AAEJ;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GAAI,OAAO,EACpC,QAAQ,MAAM,EACd,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,KAAK,OAAO,KACpC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAS5B,CAAA"}
1
+ {"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../src/task/time.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAA;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAEvC;;;;;;;GAOG;AACH,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAgB,CAAA;AAEhE;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAExD,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAMtD,CAAA;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc,GACzB,QAAQ,MAAM,KACb,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAQ1C,CAAA"}