foldkit 0.24.0 → 0.26.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 (94) hide show
  1. package/README.md +70 -55
  2. package/dist/fieldValidation/index.d.ts +39 -30
  3. package/dist/fieldValidation/index.d.ts.map +1 -1
  4. package/dist/fieldValidation/index.js +25 -30
  5. package/dist/fieldValidation/public.d.ts +2 -2
  6. package/dist/fieldValidation/public.d.ts.map +1 -1
  7. package/dist/fieldValidation/public.js +1 -1
  8. package/dist/html/index.d.ts +44 -9
  9. package/dist/html/index.d.ts.map +1 -1
  10. package/dist/html/index.js +15 -3
  11. package/dist/html/lazy.d.ts +12 -0
  12. package/dist/html/lazy.d.ts.map +1 -0
  13. package/dist/html/lazy.js +35 -0
  14. package/dist/html/public.d.ts +2 -1
  15. package/dist/html/public.d.ts.map +1 -1
  16. package/dist/html/public.js +1 -0
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +2 -0
  20. package/dist/managedResource/index.d.ts +38 -0
  21. package/dist/managedResource/index.d.ts.map +1 -0
  22. package/dist/managedResource/index.js +20 -0
  23. package/dist/managedResource/public.d.ts +5 -0
  24. package/dist/managedResource/public.d.ts.map +1 -0
  25. package/dist/managedResource/public.js +2 -0
  26. package/dist/runtime/managedResource.d.ts +114 -0
  27. package/dist/runtime/managedResource.d.ts.map +1 -0
  28. package/dist/runtime/managedResource.js +92 -0
  29. package/dist/runtime/public.d.ts +2 -2
  30. package/dist/runtime/public.d.ts.map +1 -1
  31. package/dist/runtime/public.js +1 -1
  32. package/dist/runtime/runtime.d.ts +79 -90
  33. package/dist/runtime/runtime.d.ts.map +1 -1
  34. package/dist/runtime/runtime.js +95 -19
  35. package/dist/runtime/subscription.d.ts +25 -0
  36. package/dist/runtime/subscription.d.ts.map +1 -0
  37. package/dist/runtime/subscription.js +7 -0
  38. package/dist/struct/index.d.ts +2 -0
  39. package/dist/struct/index.d.ts.map +1 -1
  40. package/dist/struct/index.js +4 -0
  41. package/dist/struct/public.d.ts +1 -1
  42. package/dist/struct/public.d.ts.map +1 -1
  43. package/dist/struct/public.js +1 -1
  44. package/dist/subscription/public.d.ts +3 -0
  45. package/dist/subscription/public.d.ts.map +1 -0
  46. package/dist/subscription/public.js +1 -0
  47. package/dist/ui/anchor.d.ts +19 -0
  48. package/dist/ui/anchor.d.ts.map +1 -0
  49. package/dist/ui/{menu/anchor.js → anchor.js} +3 -2
  50. package/dist/ui/disclosure/index.d.ts.map +1 -1
  51. package/dist/ui/disclosure/index.js +3 -2
  52. package/dist/ui/index.d.ts +2 -0
  53. package/dist/ui/index.d.ts.map +1 -1
  54. package/dist/ui/index.js +2 -0
  55. package/dist/ui/listbox/multi.d.ts +172 -0
  56. package/dist/ui/listbox/multi.d.ts.map +1 -0
  57. package/dist/ui/listbox/multi.js +25 -0
  58. package/dist/ui/listbox/multiPublic.d.ts +3 -0
  59. package/dist/ui/listbox/multiPublic.d.ts.map +1 -0
  60. package/dist/ui/listbox/multiPublic.js +1 -0
  61. package/dist/ui/listbox/public.d.ts +7 -3
  62. package/dist/ui/listbox/public.d.ts.map +1 -1
  63. package/dist/ui/listbox/public.js +4 -1
  64. package/dist/ui/listbox/{index.d.ts → shared.d.ts} +78 -27
  65. package/dist/ui/listbox/shared.d.ts.map +1 -0
  66. package/dist/ui/listbox/{index.js → shared.js} +208 -199
  67. package/dist/ui/listbox/single.d.ts +172 -0
  68. package/dist/ui/listbox/single.d.ts.map +1 -0
  69. package/dist/ui/listbox/single.js +29 -0
  70. package/dist/ui/menu/index.d.ts +1 -4
  71. package/dist/ui/menu/index.d.ts.map +1 -1
  72. package/dist/ui/menu/index.js +2 -3
  73. package/dist/ui/menu/public.d.ts +3 -2
  74. package/dist/ui/menu/public.d.ts.map +1 -1
  75. package/dist/ui/menu/public.js +2 -1
  76. package/dist/ui/popover/index.d.ts +75 -0
  77. package/dist/ui/popover/index.d.ts.map +1 -0
  78. package/dist/ui/popover/index.js +237 -0
  79. package/dist/ui/popover/public.d.ts +5 -0
  80. package/dist/ui/popover/public.d.ts.map +1 -0
  81. package/dist/ui/popover/public.js +2 -0
  82. package/dist/ui/switch/index.d.ts +47 -0
  83. package/dist/ui/switch/index.d.ts.map +1 -0
  84. package/dist/ui/switch/index.js +66 -0
  85. package/dist/ui/switch/public.d.ts +3 -0
  86. package/dist/ui/switch/public.d.ts.map +1 -0
  87. package/dist/ui/switch/public.js +1 -0
  88. package/dist/ui/transition.d.ts +5 -0
  89. package/dist/ui/transition.d.ts.map +1 -0
  90. package/dist/ui/transition.js +3 -0
  91. package/package.json +17 -1
  92. package/dist/ui/listbox/index.d.ts.map +0 -1
  93. package/dist/ui/menu/anchor.d.ts +0 -18
  94. package/dist/ui/menu/anchor.d.ts.map +0 -1
@@ -0,0 +1,172 @@
1
+ import { Option, Schema as S } from 'effect';
2
+ import { type BaseInitConfig, type BaseViewConfig } from './shared';
3
+ /** Schema for the listbox component's state, tracking open/closed status, active item, selected item, activation trigger, and typeahead search. */
4
+ export declare const Model: S.extend<S.Struct<{
5
+ id: typeof S.String;
6
+ isOpen: typeof S.Boolean;
7
+ isAnimated: typeof S.Boolean;
8
+ isModal: typeof S.Boolean;
9
+ orientation: S.Literal<["Vertical", "Horizontal"]>;
10
+ transitionState: S.Literal<["Idle", "EnterStart", "EnterAnimating", "LeaveStart", "LeaveAnimating"]>;
11
+ maybeActiveItemIndex: S.OptionFromSelf<typeof S.Number>;
12
+ activationTrigger: S.Literal<["Pointer", "Keyboard"]>;
13
+ searchQuery: typeof S.String;
14
+ searchVersion: typeof S.Number;
15
+ maybeLastPointerPosition: S.OptionFromSelf<S.Struct<{
16
+ screenX: typeof S.Number;
17
+ screenY: typeof S.Number;
18
+ }>>;
19
+ maybeLastButtonPointerType: S.OptionFromSelf<typeof S.String>;
20
+ }>, S.Struct<{
21
+ maybeSelectedItem: S.OptionFromSelf<typeof S.String>;
22
+ }>>;
23
+ export type Model = typeof Model.Type;
24
+ /** Configuration for creating a single-select listbox model with `init`. `isAnimated` enables CSS transition coordination (default `false`). `isModal` locks page scroll and inerts other elements when open (default `false`). `selectedItem` sets the initial selection (default none). */
25
+ export type InitConfig = BaseInitConfig & Readonly<{
26
+ selectedItem?: string;
27
+ }>;
28
+ /** Creates an initial single-select listbox model from a config. Defaults to closed with no active item and no selection. */
29
+ export declare const init: (config: InitConfig) => Model;
30
+ /** Processes a listbox message and returns the next model and commands. Closes the listbox on selection (single-select behavior). */
31
+ export declare const update: (model: {
32
+ readonly id: string;
33
+ readonly isOpen: boolean;
34
+ readonly isAnimated: boolean;
35
+ readonly isModal: boolean;
36
+ readonly orientation: "Vertical" | "Horizontal";
37
+ readonly transitionState: "Idle" | "EnterStart" | "EnterAnimating" | "LeaveStart" | "LeaveAnimating";
38
+ readonly maybeActiveItemIndex: Option.Option<number>;
39
+ readonly activationTrigger: "Pointer" | "Keyboard";
40
+ readonly searchQuery: string;
41
+ readonly searchVersion: number;
42
+ readonly maybeLastPointerPosition: Option.Option<{
43
+ readonly screenX: number;
44
+ readonly screenY: number;
45
+ }>;
46
+ readonly maybeLastButtonPointerType: Option.Option<string>;
47
+ } & {
48
+ readonly maybeSelectedItem: Option.Option<string>;
49
+ }, message: import("./shared").Message) => [{
50
+ readonly id: string;
51
+ readonly isOpen: boolean;
52
+ readonly isAnimated: boolean;
53
+ readonly isModal: boolean;
54
+ readonly orientation: "Vertical" | "Horizontal";
55
+ readonly transitionState: "Idle" | "EnterStart" | "EnterAnimating" | "LeaveStart" | "LeaveAnimating";
56
+ readonly maybeActiveItemIndex: Option.Option<number>;
57
+ readonly activationTrigger: "Pointer" | "Keyboard";
58
+ readonly searchQuery: string;
59
+ readonly searchVersion: number;
60
+ readonly maybeLastPointerPosition: Option.Option<{
61
+ readonly screenX: number;
62
+ readonly screenY: number;
63
+ }>;
64
+ readonly maybeLastButtonPointerType: Option.Option<string>;
65
+ } & {
66
+ readonly maybeSelectedItem: Option.Option<string>;
67
+ }, readonly import("../../command").Command<{
68
+ readonly _tag: "Closed";
69
+ } | {
70
+ readonly _tag: "NoOp";
71
+ } | {
72
+ readonly _tag: "Opened";
73
+ readonly maybeActiveItemIndex: Option.Option<number>;
74
+ } | {
75
+ readonly _tag: "ClosedByTab";
76
+ } | {
77
+ readonly _tag: "ActivatedItem";
78
+ readonly activationTrigger: "Pointer" | "Keyboard";
79
+ readonly index: number;
80
+ } | {
81
+ readonly _tag: "DeactivatedItem";
82
+ } | {
83
+ readonly _tag: "SelectedItem";
84
+ readonly item: string;
85
+ } | {
86
+ readonly _tag: "MovedPointerOverItem";
87
+ readonly screenX: number;
88
+ readonly screenY: number;
89
+ readonly index: number;
90
+ } | {
91
+ readonly _tag: "RequestedItemClick";
92
+ readonly index: number;
93
+ } | {
94
+ readonly _tag: "Searched";
95
+ readonly key: string;
96
+ readonly maybeTargetIndex: Option.Option<number>;
97
+ } | {
98
+ readonly _tag: "ClearedSearch";
99
+ readonly version: number;
100
+ } | {
101
+ readonly _tag: "AdvancedTransitionFrame";
102
+ } | {
103
+ readonly _tag: "EndedTransition";
104
+ } | {
105
+ readonly _tag: "DetectedButtonMovement";
106
+ } | {
107
+ readonly _tag: "PressedPointerOnButton";
108
+ readonly button: number;
109
+ readonly pointerType: string;
110
+ }>[]];
111
+ /** Configuration for rendering a single-select listbox with `view`. */
112
+ export type ViewConfig<Message, Item> = BaseViewConfig<Message, Item, Model>;
113
+ /** Renders a headless single-select listbox with typeahead search, keyboard navigation, selection tracking, and aria-activedescendant focus management. */
114
+ export declare const view: <Message, Item>(config: Readonly<{
115
+ model: {
116
+ readonly id: string;
117
+ readonly isOpen: boolean;
118
+ readonly isAnimated: boolean;
119
+ readonly isModal: boolean;
120
+ readonly orientation: "Vertical" | "Horizontal";
121
+ readonly transitionState: "Idle" | "EnterStart" | "EnterAnimating" | "LeaveStart" | "LeaveAnimating";
122
+ readonly maybeActiveItemIndex: Option.Option<number>;
123
+ readonly activationTrigger: "Pointer" | "Keyboard";
124
+ readonly searchQuery: string;
125
+ readonly searchVersion: number;
126
+ readonly maybeLastPointerPosition: Option.Option<{
127
+ readonly screenX: number;
128
+ readonly screenY: number;
129
+ }>;
130
+ readonly maybeLastButtonPointerType: Option.Option<string>;
131
+ } & {
132
+ readonly maybeSelectedItem: Option.Option<string>;
133
+ };
134
+ toMessage: (message: import("./shared").Opened | import("./shared").Closed | import("./shared").ClosedByTab | import("./shared").ActivatedItem | import("./shared").DeactivatedItem | import("./shared").SelectedItem | import("./shared").MovedPointerOverItem | import("./shared").RequestedItemClick | import("./shared").Searched | import("./shared").PressedPointerOnButton | import("./shared").NoOp) => Message;
135
+ items: readonly Item[];
136
+ itemToConfig: (item: Item, context: Readonly<{
137
+ isActive: boolean;
138
+ isDisabled: boolean;
139
+ isSelected: boolean;
140
+ }>) => Readonly<{
141
+ className: string;
142
+ content: import("../../html").Html;
143
+ }>;
144
+ isItemDisabled?: (item: Item, index: number) => boolean;
145
+ itemToSearchText?: (item: Item, index: number) => string;
146
+ itemToValue?: (item: Item) => string;
147
+ isButtonDisabled?: boolean;
148
+ buttonContent: import("../../html").Html;
149
+ buttonClassName: string;
150
+ itemsClassName: string;
151
+ backdropClassName: string;
152
+ className?: string;
153
+ itemGroupKey?: (item: Item, index: number) => string;
154
+ groupToHeading?: (groupKey: string) => Readonly<{
155
+ content: import("../../html").Html;
156
+ className: string;
157
+ }> | undefined;
158
+ groupClassName?: string;
159
+ separatorClassName?: string;
160
+ anchor?: Readonly<{
161
+ placement?: import("@floating-ui/dom").Placement;
162
+ gap?: number;
163
+ offset?: number;
164
+ padding?: number;
165
+ portal?: boolean;
166
+ }>;
167
+ name?: string;
168
+ form?: string;
169
+ isDisabled?: boolean;
170
+ isInvalid?: boolean;
171
+ }>) => import("../../html").Html;
172
+ //# sourceMappingURL=single.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"single.d.ts","sourceRoot":"","sources":["../../../src/ui/listbox/single.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAE,MAAM,IAAI,CAAC,EAAQ,MAAM,QAAQ,CAAA;AAGzD,OAAO,EACL,KAAK,cAAc,EAEnB,KAAK,cAAc,EAKpB,MAAM,UAAU,CAAA;AAIjB,mJAAmJ;AACnJ,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;GAEjB,CAAA;AAED,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAIrC,6RAA6R;AAC7R,MAAM,MAAM,UAAU,GAAG,cAAc,GACrC,QAAQ,CAAC;IACP,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAC,CAAA;AAEJ,6HAA6H;AAC7H,eAAO,MAAM,IAAI,GAAI,QAAQ,UAAU,KAAG,KAGxC,CAAA;AAIF,qIAAqI;AACrI,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAYjB,CAAA;AAIF,uEAAuE;AACvE,MAAM,MAAM,UAAU,CAAC,OAAO,EAAE,IAAI,IAAI,cAAc,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;AAE5E,2JAA2J;AAC3J,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAWf,CAAA"}
@@ -0,0 +1,29 @@
1
+ import { Array, Option, Schema as S, pipe } from 'effect';
2
+ import { evo } from '../../struct';
3
+ import { BaseModel, baseInit, closedModel, makeUpdate, makeView, } from './shared';
4
+ // MODEL
5
+ /** Schema for the listbox component's state, tracking open/closed status, active item, selected item, activation trigger, and typeahead search. */
6
+ export const Model = BaseModel.pipe(S.extend(S.Struct({ maybeSelectedItem: S.OptionFromSelf(S.String) })));
7
+ /** Creates an initial single-select listbox model from a config. Defaults to closed with no active item and no selection. */
8
+ export const init = (config) => ({
9
+ ...baseInit(config),
10
+ maybeSelectedItem: Option.fromNullable(config.selectedItem),
11
+ });
12
+ // UPDATE
13
+ /** Processes a listbox message and returns the next model and commands. Closes the listbox on selection (single-select behavior). */
14
+ export const update = makeUpdate((model, item, context) => [
15
+ evo(closedModel(model), {
16
+ maybeSelectedItem: () => Option.some(item),
17
+ }),
18
+ pipe(Array.getSomes([
19
+ context.maybeNextFrame,
20
+ context.maybeUnlockScroll,
21
+ context.maybeRestoreInert,
22
+ ]), Array.prepend(context.focusButton)),
23
+ ]);
24
+ /** Renders a headless single-select listbox with typeahead search, keyboard navigation, selection tracking, and aria-activedescendant focus management. */
25
+ export const view = makeView({
26
+ isItemSelected: (model, itemValue) => Option.exists(model.maybeSelectedItem, selectedItem => selectedItem === itemValue),
27
+ selectedItemIndex: (model, items, itemToValue) => Option.flatMap(model.maybeSelectedItem, selectedItem => Array.findFirstIndex(items, item => itemToValue(item) === selectedItem)),
28
+ ariaMultiSelectable: false,
29
+ });
@@ -1,15 +1,12 @@
1
1
  import { Schema as S } from 'effect';
2
2
  import type { Command } from '../../command';
3
3
  import { type Html } from '../../html';
4
+ import type { AnchorConfig } from '../anchor';
4
5
  import { groupContiguous } from '../group';
5
6
  import { resolveTypeaheadMatch } from '../typeahead';
6
- import type { AnchorConfig } from './anchor';
7
7
  /** Schema for the activation trigger — whether the user interacted via mouse or keyboard. */
8
8
  export declare const ActivationTrigger: S.Literal<["Pointer", "Keyboard"]>;
9
9
  export type ActivationTrigger = typeof ActivationTrigger.Type;
10
- /** Schema for the transition animation state, tracking enter/leave phases for CSS transition coordination. */
11
- export declare const TransitionState: S.Literal<["Idle", "EnterStart", "EnterAnimating", "LeaveStart", "LeaveAnimating"]>;
12
- export type TransitionState = typeof TransitionState.Type;
13
10
  /** Schema for the menu component's state, tracking open/closed status, active item, activation trigger, and typeahead search. */
14
11
  export declare const Model: S.Struct<{
15
12
  id: typeof S.String;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/menu/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/menu/index.ts"],"names":[],"mappings":"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"}
@@ -4,15 +4,14 @@ import { html } from '../../html';
4
4
  import { m } from '../../message';
5
5
  import { evo } from '../../struct';
6
6
  import * as Task from '../../task';
7
+ import { anchorHooks } from '../anchor';
7
8
  import { groupContiguous } from '../group';
8
9
  import { findFirstEnabledIndex, isPrintableKey, keyToIndex } from '../keyboard';
10
+ import { TransitionState } from '../transition';
9
11
  import { resolveTypeaheadMatch } from '../typeahead';
10
- import { anchorHooks } from './anchor';
11
12
  // MODEL
12
13
  /** Schema for the activation trigger — whether the user interacted via mouse or keyboard. */
13
14
  export const ActivationTrigger = S.Literal('Pointer', 'Keyboard');
14
- /** Schema for the transition animation state, tracking enter/leave phases for CSS transition coordination. */
15
- export const TransitionState = S.Literal('Idle', 'EnterStart', 'EnterAnimating', 'LeaveStart', 'LeaveAnimating');
16
15
  const PointerOrigin = S.Struct({
17
16
  screenX: S.Number,
18
17
  screenY: S.Number,
@@ -1,4 +1,5 @@
1
- export { init, update, view, Model, Message, TransitionState } from './index';
1
+ export { init, update, view, Model, Message } from './index';
2
+ export { TransitionState } from '../transition';
2
3
  export type { ActivationTrigger, Opened, Closed, ClosedByTab, ActivatedItem, DeactivatedItem, SelectedItem, MovedPointerOverItem, Searched, ClearedSearch, NoOp, AdvancedTransitionFrame, EndedTransition, InitConfig, ViewConfig, ItemConfig, GroupHeading, } from './index';
3
- export type { AnchorConfig } from './anchor';
4
+ export type { AnchorConfig } from '../anchor';
4
5
  //# sourceMappingURL=public.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/ui/menu/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAE7E,YAAY,EACV,iBAAiB,EACjB,MAAM,EACN,MAAM,EACN,WAAW,EACX,aAAa,EACb,eAAe,EACf,YAAY,EACZ,oBAAoB,EACpB,QAAQ,EACR,aAAa,EACb,IAAI,EACJ,uBAAuB,EACvB,eAAe,EACf,UAAU,EACV,UAAU,EACV,UAAU,EACV,YAAY,GACb,MAAM,SAAS,CAAA;AAEhB,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA"}
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/ui/menu/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAE5D,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAE/C,YAAY,EACV,iBAAiB,EACjB,MAAM,EACN,MAAM,EACN,WAAW,EACX,aAAa,EACb,eAAe,EACf,YAAY,EACZ,oBAAoB,EACpB,QAAQ,EACR,aAAa,EACb,IAAI,EACJ,uBAAuB,EACvB,eAAe,EACf,UAAU,EACV,UAAU,EACV,UAAU,EACV,YAAY,GACb,MAAM,SAAS,CAAA;AAEhB,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA"}
@@ -1 +1,2 @@
1
- export { init, update, view, Model, Message, TransitionState } from './index';
1
+ export { init, update, view, Model, Message } from './index';
2
+ export { TransitionState } from '../transition';
@@ -0,0 +1,75 @@
1
+ import { Schema as S } from 'effect';
2
+ import type { Command } from '../../command';
3
+ import { type Html } from '../../html';
4
+ import type { AnchorConfig } from '../anchor';
5
+ /** Schema for the popover component's state, tracking open/closed status and transition animation. */
6
+ export declare const Model: S.Struct<{
7
+ id: typeof S.String;
8
+ isOpen: typeof S.Boolean;
9
+ isAnimated: typeof S.Boolean;
10
+ isModal: typeof S.Boolean;
11
+ transitionState: S.Literal<["Idle", "EnterStart", "EnterAnimating", "LeaveStart", "LeaveAnimating"]>;
12
+ maybeLastButtonPointerType: S.OptionFromSelf<typeof S.String>;
13
+ }>;
14
+ export type Model = typeof Model.Type;
15
+ /** Sent when the popover opens via button click or keyboard activation. */
16
+ export declare const Opened: import("../../schema").CallableTaggedStruct<"Opened", {}>;
17
+ /** Sent when the popover closes via Escape key or backdrop click. Returns focus to the button. */
18
+ export declare const Closed: import("../../schema").CallableTaggedStruct<"Closed", {}>;
19
+ /** Sent when focus leaves the popover panel via Tab key. Does NOT return focus to the button. */
20
+ export declare const ClosedByTab: import("../../schema").CallableTaggedStruct<"ClosedByTab", {}>;
21
+ /** Sent when the user presses a pointer device on the popover button. Records pointer type and toggles for mouse. */
22
+ export declare const PressedPointerOnButton: import("../../schema").CallableTaggedStruct<"PressedPointerOnButton", {
23
+ pointerType: typeof S.String;
24
+ button: typeof S.Number;
25
+ }>;
26
+ /** Placeholder message used when no action is needed. */
27
+ export declare const NoOp: import("../../schema").CallableTaggedStruct<"NoOp", {}>;
28
+ /** Sent internally when a double-rAF completes, advancing the transition to its animating phase. */
29
+ export declare const AdvancedTransitionFrame: import("../../schema").CallableTaggedStruct<"AdvancedTransitionFrame", {}>;
30
+ /** Sent internally when all CSS transitions on the popover panel have completed. */
31
+ export declare const EndedTransition: import("../../schema").CallableTaggedStruct<"EndedTransition", {}>;
32
+ /** Sent internally when the popover button moves in the viewport during a leave transition, cancelling the animation. */
33
+ export declare const DetectedButtonMovement: import("../../schema").CallableTaggedStruct<"DetectedButtonMovement", {}>;
34
+ /** Union of all messages the popover component can produce. */
35
+ export declare const Message: S.Union<[import("../../schema").CallableTaggedStruct<"Opened", {}>, import("../../schema").CallableTaggedStruct<"Closed", {}>, import("../../schema").CallableTaggedStruct<"ClosedByTab", {}>, import("../../schema").CallableTaggedStruct<"PressedPointerOnButton", {
36
+ pointerType: typeof S.String;
37
+ button: typeof S.Number;
38
+ }>, import("../../schema").CallableTaggedStruct<"NoOp", {}>, import("../../schema").CallableTaggedStruct<"AdvancedTransitionFrame", {}>, import("../../schema").CallableTaggedStruct<"EndedTransition", {}>, import("../../schema").CallableTaggedStruct<"DetectedButtonMovement", {}>]>;
39
+ export type Opened = typeof Opened.Type;
40
+ export type Closed = typeof Closed.Type;
41
+ export type ClosedByTab = typeof ClosedByTab.Type;
42
+ export type PressedPointerOnButton = typeof PressedPointerOnButton.Type;
43
+ export type NoOp = typeof NoOp.Type;
44
+ export type AdvancedTransitionFrame = typeof AdvancedTransitionFrame.Type;
45
+ export type EndedTransition = typeof EndedTransition.Type;
46
+ export type DetectedButtonMovement = typeof DetectedButtonMovement.Type;
47
+ export type Message = typeof Message.Type;
48
+ /** Configuration for creating a popover model with `init`. `isAnimated` enables CSS transition coordination (default `false`). `isModal` locks page scroll and inerts other elements when open (default `false`). */
49
+ export type InitConfig = Readonly<{
50
+ id: string;
51
+ isAnimated?: boolean;
52
+ isModal?: boolean;
53
+ }>;
54
+ /** Creates an initial popover model from a config. Defaults to closed. */
55
+ export declare const init: (config: InitConfig) => Model;
56
+ type UpdateReturn = [Model, ReadonlyArray<Command<Message>>];
57
+ /** Processes a popover message and returns the next model and commands. */
58
+ export declare const update: (model: Model, message: Message) => UpdateReturn;
59
+ /** Configuration for rendering a popover with `view`. */
60
+ export type ViewConfig<Message> = Readonly<{
61
+ model: Model;
62
+ toMessage: (message: Opened | Closed | ClosedByTab | PressedPointerOnButton | NoOp) => Message;
63
+ anchor: AnchorConfig;
64
+ buttonContent: Html;
65
+ buttonClassName: string;
66
+ panelContent: Html;
67
+ panelClassName: string;
68
+ backdropClassName: string;
69
+ isDisabled?: boolean;
70
+ className?: string;
71
+ }>;
72
+ /** Renders a headless popover with a trigger button and a floating panel. Uses the disclosure ARIA pattern (aria-expanded + aria-controls) with no role on the panel. */
73
+ export declare const view: <Message>(config: ViewConfig<Message>) => Html;
74
+ export {};
75
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/popover/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,237 @@
1
+ import { Array, Effect, Match as M, Option, Schema as S, pipe } from 'effect';
2
+ import { OptionExt } from '../../effectExtensions';
3
+ import { html } from '../../html';
4
+ import { m } from '../../message';
5
+ import { evo } from '../../struct';
6
+ import * as Task from '../../task';
7
+ import { anchorHooks } from '../anchor';
8
+ import { TransitionState } from '../transition';
9
+ // MODEL
10
+ /** Schema for the popover component's state, tracking open/closed status and transition animation. */
11
+ export const Model = S.Struct({
12
+ id: S.String,
13
+ isOpen: S.Boolean,
14
+ isAnimated: S.Boolean,
15
+ isModal: S.Boolean,
16
+ transitionState: TransitionState,
17
+ maybeLastButtonPointerType: S.OptionFromSelf(S.String),
18
+ });
19
+ // MESSAGE
20
+ /** Sent when the popover opens via button click or keyboard activation. */
21
+ export const Opened = m('Opened');
22
+ /** Sent when the popover closes via Escape key or backdrop click. Returns focus to the button. */
23
+ export const Closed = m('Closed');
24
+ /** Sent when focus leaves the popover panel via Tab key. Does NOT return focus to the button. */
25
+ export const ClosedByTab = m('ClosedByTab');
26
+ /** Sent when the user presses a pointer device on the popover button. Records pointer type and toggles for mouse. */
27
+ export const PressedPointerOnButton = m('PressedPointerOnButton', {
28
+ pointerType: S.String,
29
+ button: S.Number,
30
+ });
31
+ /** Placeholder message used when no action is needed. */
32
+ export const NoOp = m('NoOp');
33
+ /** Sent internally when a double-rAF completes, advancing the transition to its animating phase. */
34
+ export const AdvancedTransitionFrame = m('AdvancedTransitionFrame');
35
+ /** Sent internally when all CSS transitions on the popover panel have completed. */
36
+ export const EndedTransition = m('EndedTransition');
37
+ /** Sent internally when the popover button moves in the viewport during a leave transition, cancelling the animation. */
38
+ export const DetectedButtonMovement = m('DetectedButtonMovement');
39
+ /** Union of all messages the popover component can produce. */
40
+ export const Message = S.Union(Opened, Closed, ClosedByTab, PressedPointerOnButton, NoOp, AdvancedTransitionFrame, EndedTransition, DetectedButtonMovement);
41
+ // INIT
42
+ const LEFT_MOUSE_BUTTON = 0;
43
+ /** Creates an initial popover model from a config. Defaults to closed. */
44
+ export const init = (config) => ({
45
+ id: config.id,
46
+ isOpen: false,
47
+ isAnimated: config.isAnimated ?? false,
48
+ isModal: config.isModal ?? false,
49
+ transitionState: 'Idle',
50
+ maybeLastButtonPointerType: Option.none(),
51
+ });
52
+ // UPDATE
53
+ const closedModel = (model) => evo(model, {
54
+ isOpen: () => false,
55
+ transitionState: () => (model.isAnimated ? 'LeaveStart' : 'Idle'),
56
+ maybeLastButtonPointerType: () => Option.none(),
57
+ });
58
+ const buttonSelector = (id) => `#${id}-button`;
59
+ const panelSelector = (id) => `#${id}-panel`;
60
+ const withUpdateReturn = M.withReturnType();
61
+ /** Processes a popover message and returns the next model and commands. */
62
+ export const update = (model, message) => {
63
+ const maybeNextFrame = OptionExt.when(model.isAnimated, Task.nextFrame.pipe(Effect.as(AdvancedTransitionFrame())));
64
+ const maybeLockScroll = OptionExt.when(model.isModal, Task.lockScroll.pipe(Effect.as(NoOp())));
65
+ const maybeUnlockScroll = OptionExt.when(model.isModal, Task.unlockScroll.pipe(Effect.as(NoOp())));
66
+ const maybeInertOthers = OptionExt.when(model.isModal, Task.inertOthers(model.id, [
67
+ buttonSelector(model.id),
68
+ panelSelector(model.id),
69
+ ]).pipe(Effect.as(NoOp())));
70
+ const maybeRestoreInert = OptionExt.when(model.isModal, Task.restoreInert(model.id).pipe(Effect.as(NoOp())));
71
+ return M.value(message).pipe(withUpdateReturn, M.tagsExhaustive({
72
+ Opened: () => {
73
+ const nextModel = evo(model, {
74
+ isOpen: () => true,
75
+ transitionState: () => (model.isAnimated ? 'EnterStart' : 'Idle'),
76
+ });
77
+ return [
78
+ nextModel,
79
+ pipe(Array.getSomes([maybeNextFrame, maybeLockScroll, maybeInertOthers]), Array.prepend(Task.focus(panelSelector(model.id)).pipe(Effect.ignore, Effect.as(NoOp())))),
80
+ ];
81
+ },
82
+ Closed: () => [
83
+ closedModel(model),
84
+ pipe(Array.getSomes([
85
+ maybeNextFrame,
86
+ maybeUnlockScroll,
87
+ maybeRestoreInert,
88
+ ]), Array.prepend(Task.focus(buttonSelector(model.id)).pipe(Effect.ignore, Effect.as(NoOp())))),
89
+ ],
90
+ ClosedByTab: () => [
91
+ closedModel(model),
92
+ Array.getSomes([maybeNextFrame, maybeUnlockScroll, maybeRestoreInert]),
93
+ ],
94
+ PressedPointerOnButton: ({ pointerType, button }) => {
95
+ const withPointerType = evo(model, {
96
+ maybeLastButtonPointerType: () => Option.some(pointerType),
97
+ });
98
+ if (pointerType !== 'mouse' || button !== LEFT_MOUSE_BUTTON) {
99
+ return [withPointerType, []];
100
+ }
101
+ if (model.isOpen) {
102
+ return [
103
+ closedModel(withPointerType),
104
+ pipe(Array.getSomes([
105
+ maybeNextFrame,
106
+ maybeUnlockScroll,
107
+ maybeRestoreInert,
108
+ ]), Array.prepend(Task.focus(buttonSelector(model.id)).pipe(Effect.ignore, Effect.as(NoOp())))),
109
+ ];
110
+ }
111
+ const nextModel = evo(withPointerType, {
112
+ isOpen: () => true,
113
+ transitionState: () => (model.isAnimated ? 'EnterStart' : 'Idle'),
114
+ });
115
+ return [
116
+ nextModel,
117
+ pipe(Array.getSomes([maybeNextFrame, maybeLockScroll, maybeInertOthers]), Array.prepend(Task.focus(panelSelector(model.id)).pipe(Effect.ignore, Effect.as(NoOp())))),
118
+ ];
119
+ },
120
+ AdvancedTransitionFrame: () => M.value(model.transitionState).pipe(withUpdateReturn, M.when('EnterStart', () => [
121
+ evo(model, { transitionState: () => 'EnterAnimating' }),
122
+ [
123
+ Task.waitForTransitions(panelSelector(model.id)).pipe(Effect.as(EndedTransition())),
124
+ ],
125
+ ]), M.when('LeaveStart', () => [
126
+ evo(model, { transitionState: () => 'LeaveAnimating' }),
127
+ [
128
+ Effect.raceFirst(Task.detectElementMovement(buttonSelector(model.id)).pipe(Effect.as(DetectedButtonMovement())), Task.waitForTransitions(panelSelector(model.id)).pipe(Effect.as(EndedTransition()))),
129
+ ],
130
+ ]), M.orElse(() => [model, []])),
131
+ EndedTransition: () => M.value(model.transitionState).pipe(withUpdateReturn, M.whenOr('EnterAnimating', 'LeaveAnimating', () => [
132
+ evo(model, { transitionState: () => 'Idle' }),
133
+ [],
134
+ ]), M.orElse(() => [model, []])),
135
+ DetectedButtonMovement: () => M.value(model.transitionState).pipe(withUpdateReturn, M.when('LeaveAnimating', () => [
136
+ evo(model, { transitionState: () => 'Idle' }),
137
+ [],
138
+ ]), M.orElse(() => [model, []])),
139
+ NoOp: () => [model, []],
140
+ }));
141
+ };
142
+ /** Renders a headless popover with a trigger button and a floating panel. Uses the disclosure ARIA pattern (aria-expanded + aria-controls) with no role on the panel. */
143
+ export const view = (config) => {
144
+ const { div, AriaControls, AriaDisabled, AriaExpanded, Class, DataAttribute, Id, OnBlur, OnClick, OnDestroy, OnInsert, OnKeyDownPreventDefault, OnKeyUpPreventDefault, OnPointerDown, Style, Tabindex, Type, keyed, } = html();
145
+ const { model: { id, isOpen, transitionState, maybeLastButtonPointerType }, toMessage, anchor, buttonContent, buttonClassName, panelContent, panelClassName, backdropClassName, isDisabled, className, } = config;
146
+ const isLeaving = transitionState === 'LeaveStart' || transitionState === 'LeaveAnimating';
147
+ const isVisible = isOpen || isLeaving;
148
+ const transitionAttributes = M.value(transitionState).pipe(M.when('EnterStart', () => [
149
+ DataAttribute('closed', ''),
150
+ DataAttribute('enter', ''),
151
+ DataAttribute('transition', ''),
152
+ ]), M.when('EnterAnimating', () => [
153
+ DataAttribute('enter', ''),
154
+ DataAttribute('transition', ''),
155
+ ]), M.when('LeaveStart', () => [
156
+ DataAttribute('leave', ''),
157
+ DataAttribute('transition', ''),
158
+ ]), M.when('LeaveAnimating', () => [
159
+ DataAttribute('closed', ''),
160
+ DataAttribute('leave', ''),
161
+ DataAttribute('transition', ''),
162
+ ]), M.orElse(() => []));
163
+ const handleButtonKeyDown = (key) => M.value(key).pipe(M.whenOr('Enter', ' ', 'ArrowDown', () => Option.some(toMessage(isOpen ? Closed() : Opened()))), M.orElse(() => Option.none()));
164
+ const handleButtonPointerDown = (pointerType, button) => Option.some(toMessage(PressedPointerOnButton({
165
+ pointerType,
166
+ button,
167
+ })));
168
+ const handleButtonClick = () => {
169
+ const isMouse = Option.exists(maybeLastButtonPointerType, type => type === 'mouse');
170
+ if (isMouse) {
171
+ return toMessage(NoOp());
172
+ }
173
+ else if (isOpen) {
174
+ return toMessage(Closed());
175
+ }
176
+ else {
177
+ return toMessage(Opened());
178
+ }
179
+ };
180
+ const handleSpaceKeyUp = (key) => OptionExt.when(key === ' ', toMessage(NoOp()));
181
+ const handlePanelKeyDown = (key) => M.value(key).pipe(M.when('Escape', () => Option.some(toMessage(Closed()))), M.orElse(() => Option.none()));
182
+ const buttonAttributes = [
183
+ Id(`${id}-button`),
184
+ Type('button'),
185
+ Class(buttonClassName),
186
+ AriaExpanded(isVisible),
187
+ AriaControls(`${id}-panel`),
188
+ ...(isDisabled
189
+ ? [AriaDisabled(true), DataAttribute('disabled', '')]
190
+ : [
191
+ OnPointerDown(handleButtonPointerDown),
192
+ OnKeyDownPreventDefault(handleButtonKeyDown),
193
+ OnKeyUpPreventDefault(handleSpaceKeyUp),
194
+ OnClick(handleButtonClick()),
195
+ ]),
196
+ ...(isVisible ? [DataAttribute('open', '')] : []),
197
+ ];
198
+ const hooks = anchorHooks({
199
+ buttonId: `${id}-button`,
200
+ anchor,
201
+ interceptTab: false,
202
+ });
203
+ const anchorAttributes = [
204
+ Style({ position: 'absolute', margin: '0', visibility: 'hidden' }),
205
+ OnInsert(hooks.onInsert),
206
+ OnDestroy(hooks.onDestroy),
207
+ ];
208
+ const panelAttributes = [
209
+ Id(`${id}-panel`),
210
+ Tabindex(0),
211
+ Class(panelClassName),
212
+ ...anchorAttributes,
213
+ ...transitionAttributes,
214
+ ...(isLeaving
215
+ ? []
216
+ : [
217
+ OnKeyDownPreventDefault(handlePanelKeyDown),
218
+ OnBlur(toMessage(ClosedByTab())),
219
+ ]),
220
+ ];
221
+ const backdrop = keyed('div')(`${id}-backdrop`, [
222
+ Class(backdropClassName),
223
+ ...(isLeaving ? [] : [OnClick(toMessage(Closed()))]),
224
+ ], []);
225
+ const visibleContent = [
226
+ backdrop,
227
+ keyed('div')(`${id}-panel-container`, panelAttributes, [panelContent]),
228
+ ];
229
+ const wrapperAttributes = [
230
+ ...(className ? [Class(className)] : []),
231
+ ...(isVisible ? [DataAttribute('open', '')] : []),
232
+ ];
233
+ return div(wrapperAttributes, [
234
+ keyed('button')(`${id}-button`, buttonAttributes, [buttonContent]),
235
+ ...(isVisible ? visibleContent : []),
236
+ ]);
237
+ };
@@ -0,0 +1,5 @@
1
+ export { init, update, view, Model, Message } from './index';
2
+ export { TransitionState } from '../transition';
3
+ export type { Opened, Closed, ClosedByTab, PressedPointerOnButton, NoOp, AdvancedTransitionFrame, EndedTransition, DetectedButtonMovement, InitConfig, ViewConfig, } from './index';
4
+ export type { AnchorConfig } from '../anchor';
5
+ //# sourceMappingURL=public.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/ui/popover/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAE5D,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAE/C,YAAY,EACV,MAAM,EACN,MAAM,EACN,WAAW,EACX,sBAAsB,EACtB,IAAI,EACJ,uBAAuB,EACvB,eAAe,EACf,sBAAsB,EACtB,UAAU,EACV,UAAU,GACX,MAAM,SAAS,CAAA;AAEhB,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA"}
@@ -0,0 +1,2 @@
1
+ export { init, update, view, Model, Message } from './index';
2
+ export { TransitionState } from '../transition';