@foldkit/ui 0.127.0 → 0.128.1

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 (79) hide show
  1. package/README.md +2 -2
  2. package/dist/calendar/index.d.ts +17 -23
  3. package/dist/calendar/index.d.ts.map +1 -1
  4. package/dist/calendar/index.js +16 -25
  5. package/dist/calendar/public.d.ts +1 -1
  6. package/dist/calendar/public.d.ts.map +1 -1
  7. package/dist/calendar/public.js +1 -1
  8. package/dist/checkbox/index.d.ts +47 -104
  9. package/dist/checkbox/index.d.ts.map +1 -1
  10. package/dist/checkbox/index.js +48 -87
  11. package/dist/checkbox/public.d.ts +2 -2
  12. package/dist/checkbox/public.d.ts.map +1 -1
  13. package/dist/checkbox/public.js +1 -1
  14. package/dist/combobox/multi.d.ts +19 -30
  15. package/dist/combobox/multi.d.ts.map +1 -1
  16. package/dist/combobox/multi.js +32 -53
  17. package/dist/combobox/public.d.ts +2 -2
  18. package/dist/combobox/public.d.ts.map +1 -1
  19. package/dist/combobox/public.js +1 -1
  20. package/dist/combobox/shared.d.ts +59 -35
  21. package/dist/combobox/shared.d.ts.map +1 -1
  22. package/dist/combobox/shared.js +58 -45
  23. package/dist/combobox/single.d.ts +36 -48
  24. package/dist/combobox/single.d.ts.map +1 -1
  25. package/dist/combobox/single.js +49 -76
  26. package/dist/datePicker/index.d.ts +33 -41
  27. package/dist/datePicker/index.d.ts.map +1 -1
  28. package/dist/datePicker/index.js +31 -40
  29. package/dist/datePicker/public.d.ts +1 -1
  30. package/dist/datePicker/public.d.ts.map +1 -1
  31. package/dist/datePicker/public.js +1 -1
  32. package/dist/disclosure/index.d.ts +49 -113
  33. package/dist/disclosure/index.d.ts.map +1 -1
  34. package/dist/disclosure/index.js +35 -92
  35. package/dist/disclosure/public.d.ts +2 -2
  36. package/dist/disclosure/public.d.ts.map +1 -1
  37. package/dist/disclosure/public.js +1 -1
  38. package/dist/listbox/multi.d.ts +7 -21
  39. package/dist/listbox/multi.d.ts.map +1 -1
  40. package/dist/listbox/multi.js +12 -36
  41. package/dist/listbox/public.d.ts +1 -1
  42. package/dist/listbox/public.d.ts.map +1 -1
  43. package/dist/listbox/shared.d.ts +28 -20
  44. package/dist/listbox/shared.d.ts.map +1 -1
  45. package/dist/listbox/shared.js +6 -7
  46. package/dist/listbox/single.d.ts +17 -27
  47. package/dist/listbox/single.d.ts.map +1 -1
  48. package/dist/listbox/single.js +18 -30
  49. package/dist/radioGroup/index.d.ts +57 -142
  50. package/dist/radioGroup/index.d.ts.map +1 -1
  51. package/dist/radioGroup/index.js +44 -119
  52. package/dist/radioGroup/public.d.ts +2 -2
  53. package/dist/radioGroup/public.d.ts.map +1 -1
  54. package/dist/radioGroup/public.js +1 -1
  55. package/dist/slider/index.d.ts +51 -27
  56. package/dist/slider/index.d.ts.map +1 -1
  57. package/dist/slider/index.js +66 -66
  58. package/dist/slider/public.d.ts +1 -1
  59. package/dist/slider/public.d.ts.map +1 -1
  60. package/dist/slider/public.js +1 -1
  61. package/dist/switch/index.d.ts +45 -92
  62. package/dist/switch/index.d.ts.map +1 -1
  63. package/dist/switch/index.js +36 -81
  64. package/dist/switch/public.d.ts +2 -2
  65. package/dist/switch/public.d.ts.map +1 -1
  66. package/dist/switch/public.js +1 -1
  67. package/dist/tabs/index.d.ts +33 -33
  68. package/dist/tabs/index.d.ts.map +1 -1
  69. package/dist/tabs/index.js +36 -49
  70. package/dist/toast/index.d.ts +1 -0
  71. package/dist/toast/index.d.ts.map +1 -1
  72. package/dist/toast/index.js +1 -0
  73. package/dist/toast/public.d.ts +2 -0
  74. package/dist/toast/public.d.ts.map +1 -1
  75. package/dist/toast/public.js +1 -0
  76. package/dist/toast/test.d.ts +41 -0
  77. package/dist/toast/test.d.ts.map +1 -0
  78. package/dist/toast/test.js +36 -0
  79. package/package.json +5 -5
@@ -1,7 +1,7 @@
1
1
  import { Effect, Option, Schema as S } from 'effect';
2
2
  import * as Command from 'foldkit/command';
3
3
  import { type ChildAttribute, type Html } from 'foldkit/html';
4
- import { type Reflect2, type View as SubmodelView } from 'foldkit/submodel';
4
+ import { type View as SubmodelView } from 'foldkit/submodel';
5
5
  export { wrapIndex, findFirstEnabledIndex, keyToIndex } from '../keyboard.js';
6
6
  /** Controls the tab list layout direction and which arrow keys navigate between tabs. */
7
7
  export declare const Orientation: S.Literals<readonly ["Horizontal", "Vertical"]>;
@@ -9,15 +9,19 @@ export type Orientation = typeof Orientation.Type;
9
9
  /** Controls whether tabs activate on focus (`Automatic`) or require an explicit selection (`Manual`). */
10
10
  export declare const ActivationMode: S.Literals<readonly ["Automatic", "Manual"]>;
11
11
  export type ActivationMode = typeof ActivationMode.Type;
12
- /** Schema for the tabs component's state, tracking active/focused indices and activation mode. */
12
+ /** Schema for the tabs component's private interaction state. The active
13
+ * tab is owned by the parent and passed in via `ViewInputs.selectedValue`,
14
+ * so it is not stored here. `maybeFocusedIndex` is the roving-tabindex
15
+ * cursor: `None` means keyboard focus follows the selected tab, and `Manual`
16
+ * activation stores `Some(index)` while focus diverges from the selection. */
13
17
  export declare const Model: S.Struct<{
14
18
  readonly id: S.String;
15
- readonly activeIndex: S.Number;
16
- readonly focusedIndex: S.Number;
19
+ readonly maybeFocusedIndex: S.Option<S.Number>;
17
20
  readonly activationMode: S.Literals<readonly ["Automatic", "Manual"]>;
18
21
  }>;
19
22
  export type Model = typeof Model.Type;
20
- /** Sent when a tab is selected via click or keyboard. Updates both the active and focused indices. */
23
+ /** Sent when a tab is selected via click or keyboard. Commits the tab as the
24
+ * new selection and moves focus onto it. */
21
25
  export declare const SelectedTab: import("foldkit/schema").CallableTaggedStruct<"SelectedTab", {
22
26
  index: S.Number;
23
27
  value: S.String;
@@ -59,10 +63,11 @@ export type OutMessage<Value extends string = string> = Selected<Value>;
59
63
  /** Configuration for creating a tabs model with `init`. */
60
64
  export type InitConfig = Readonly<{
61
65
  id: string;
62
- activeIndex?: number;
63
66
  activationMode?: ActivationMode;
64
67
  }>;
65
- /** Creates an initial tabs model from a config. Defaults to first tab and automatic activation. */
68
+ /** Creates an initial tabs model from a config. Focus follows the selected
69
+ * tab until the user navigates in `Manual` mode, so `maybeFocusedIndex`
70
+ * starts `None`. Defaults to automatic activation. */
66
71
  export declare const init: (config: InitConfig) => Model;
67
72
  /** Moves focus to the tab at the given index. */
68
73
  export declare const FocusTab: Command.CommandDefinitionWithArgs<"FocusTab", {
@@ -76,20 +81,11 @@ type UpdateReturn = readonly [
76
81
  ReadonlyArray<Command.Command<Message>>,
77
82
  Option.Option<OutMessage>
78
83
  ];
79
- /** Processes a tabs message and returns the next model, commands, and an optional OutMessage. `Selected` fires when a tab is committed via click or keyboard. */
84
+ /** Processes a tabs message and returns the next model, commands, and an
85
+ * optional OutMessage. `Selected` fires when a tab is committed via click or
86
+ * keyboard; the parent stores the new value and passes it back in as
87
+ * `selectedValue`. */
80
88
  export declare const update: (model: Model, message: Message) => UpdateReturn;
81
- /** Programmatically selects a tab. Emits a `Selected` OutMessage. */
82
- export declare const selectTab: (model: Model, value: string, index: number) => UpdateReturn;
83
- /** Reflects an externally-sourced active tab onto the model without
84
- * emitting an OutMessage or running the focus command. Use this to mirror
85
- * external truth (a deep link, restored storage) onto the active tab.
86
- * Contrast with `selectTab`, which represents a user or programmatic
87
- * *choice*: it focuses the tab and emits `Selected`. Takes the tab `value`
88
- * plus the `options` list (mirroring `RadioGroup.select`) because Tabs
89
- * stores the active *index* internally, so the value is resolved to an
90
- * index. A value not present in `options` is a no-op. Returns the model
91
- * directly because it produces no commands and no OutMessage. */
92
- export declare const reflectSelectedTab: Reflect2<Model, string, ReadonlyArray<string>>;
93
89
  /** Per-tab render info passed to the consumer's `toView`. Generic over
94
90
  * `Value extends string`: when `Tabs.create<MyUnion>()` is declared,
95
91
  * `tab.value` is typed `MyUnion` so the consumer can switch on it without
@@ -109,9 +105,9 @@ export type TabInfo<Value extends string = string> = Readonly<{
109
105
  * - `tabs`: one entry per tab in `viewInputs.tabs`, in the same order, with
110
106
  * the tab button's attribute bundle, the panel's attribute bundle,
111
107
  * and derived state.
112
- * - `activeIndex`: the currently-active tab index, convenient when the
113
- * consumer wants to render only the active panel (vs all panels with
114
- * `Hidden` for transitions). */
108
+ * - `activeIndex`: the index of `viewInputs.selectedValue` within
109
+ * `viewInputs.tabs`, convenient when the consumer wants to render only the
110
+ * active panel (vs all panels with `Hidden` for transitions). */
115
111
  export type RenderInfo<Value extends string = string> = Readonly<{
116
112
  tablist: ReadonlyArray<ChildAttribute>;
117
113
  tabs: ReadonlyArray<TabInfo<Value>>;
@@ -121,26 +117,32 @@ export type RenderInfo<Value extends string = string> = Readonly<{
121
117
  * Generic over `Value extends string` so consumers using
122
118
  * `Tabs.create<MyUnion>()` receive `tab.value: MyUnion` in `toView`
123
119
  * and `(value: MyUnion, index) => boolean` in `isTabDisabled`, without
124
- * casting. */
120
+ * casting.
121
+ *
122
+ * - `selectedValue`: the active tab, read straight from the parent Model.
123
+ * `aria-selected`, the `data-selected` marker, and which panel is active
124
+ * all derive from it. */
125
125
  export type ViewInputs<Value extends string = string> = Readonly<{
126
126
  tabs: ReadonlyArray<Value>;
127
+ selectedValue: Value;
127
128
  ariaLabel: string;
128
129
  toView: (render: RenderInfo<Value>) => Html;
129
130
  isTabDisabled?: (value: Value, index: number) => boolean;
130
131
  orientation?: Orientation;
131
132
  }>;
132
- /** Pairs the tabs `view`, `update`, and `selectTab` behind a single
133
- * Value-typed entry point. Declare once at module scope so consumers
134
- * receive `tab.value: Value` in `toView` without an `as` cast:
133
+ /** Pairs the tabs `view` and `update` behind a single Value-typed entry
134
+ * point. Declare once at module scope so consumers receive
135
+ * `tab.value: Value` in `toView` and the `Selected` OutMessage without an
136
+ * `as` cast:
135
137
  *
136
138
  * ```ts
137
139
  * const DemoTabs = Tabs.create<DemoTab>()
138
140
  *
139
- * // In view:
140
- * h.submodel({ view: DemoTabs.view, ... })
141
+ * // In view (selectedValue is the parent-owned active tab):
142
+ * h.submodel({ view: DemoTabs.view, viewInputs: { selectedValue, ... }, ... })
141
143
  *
142
- * // In update:
143
- * const [next, commands] = DemoTabs.update(model, message)
144
+ * // In update, fold the Selected OutMessage into your Model:
145
+ * const [next, commands, maybeOutMessage] = DemoTabs.update(model, message)
144
146
  * ```
145
147
  *
146
148
  * The internal view stays typed `ReadonlyArray<string>`; consumers can
@@ -149,7 +151,5 @@ export type ViewInputs<Value extends string = string> = Readonly<{
149
151
  export declare const create: <Value extends string = string>() => Readonly<{
150
152
  view: SubmodelView<Model, Message, ViewInputs<Value>>;
151
153
  update: (model: Model, message: Message) => readonly [Model, ReadonlyArray<Command.Command<Message>>, Option.Option<OutMessage<Value>>];
152
- selectTab: (model: Model, value: Value, index: number) => readonly [Model, ReadonlyArray<Command.Command<Message>>, Option.Option<OutMessage<Value>>];
153
- reflectSelectedTab: Reflect2<Model, Value, ReadonlyArray<Value>>;
154
154
  }>;
155
155
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tabs/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,MAAM,EAGN,MAAM,EACN,MAAM,IAAI,CAAC,EAGZ,MAAM,QAAQ,CAAA;AACf,OAAO,KAAK,OAAO,MAAM,iBAAiB,CAAA;AAE1C,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,IAAI,EAGV,MAAM,cAAc,CAAA;AAGrB,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,IAAI,IAAI,YAAY,EAE1B,MAAM,kBAAkB,CAAA;AAKzB,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAI7E,yFAAyF;AACzF,eAAO,MAAM,WAAW,iDAAyC,CAAA;AACjE,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAC,IAAI,CAAA;AAEjD,yGAAyG;AACzG,eAAO,MAAM,cAAc,8CAAsC,CAAA;AACjE,MAAM,MAAM,cAAc,GAAG,OAAO,cAAc,CAAC,IAAI,CAAA;AAEvD,kGAAkG;AAClG,eAAO,MAAM,KAAK;;;;;EAKhB,CAAA;AAEF,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAIrC,sGAAsG;AACtG,eAAO,MAAM,WAAW;;;EAGtB,CAAA;AACF,wFAAwF;AACxF,eAAO,MAAM,UAAU;;EAAuC,CAAA;AAC9D,iDAAiD;AACjD,eAAO,MAAM,iBAAiB,wEAAyB,CAAA;AAEvD,4DAA4D;AAC5D,eAAO,MAAM,OAAO,EAAE,CAAC,CAAC,KAAK,CAC3B;IAAC,OAAO,WAAW;IAAE,OAAO,UAAU;IAAE,OAAO,iBAAiB;CAAC,CACV,CAAA;AAEzD,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAC,IAAI,CAAA;AACjD,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAC,IAAI,CAAA;AAE/C,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIzC,kRAAkR;AAClR,eAAO,MAAM,QAAQ;;;EAGnB,CAAA;AAEF,MAAM,MAAM,QAAQ,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IAC7D,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB,CAAC,CAAA;AAEF,wJAAwJ;AACxJ,eAAO,MAAM,UAAU;;;IAAsB,CAAA;AAE7C;;mDAEmD;AACnD,MAAM,MAAM,UAAU,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAA;AAIvE,2DAA2D;AAC3D,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,cAAc,CAAC,EAAE,cAAc,CAAA;CAChC,CAAC,CAAA;AAEF,mGAAmG;AACnG,eAAO,MAAM,IAAI,GAAI,QAAQ,UAAU,KAAG,KASzC,CAAA;AAQD,iDAAiD;AACjD,eAAO,MAAM,QAAQ;;;;;iBASpB,CAAA;AAED,KAAK,YAAY,GAAG,SAAS;IAC3B,KAAK;IACL,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC;CAC1B,CAAA;AAED,iKAAiK;AACjK,eAAO,MAAM,MAAM,GAAI,OAAO,KAAK,EAAE,SAAS,OAAO,KAAG,YAmBrD,CAAA;AAEH,qEAAqE;AACrE,eAAO,MAAM,SAAS,GACpB,OAAO,KAAK,EACZ,OAAO,MAAM,EACb,OAAO,MAAM,KACZ,YAA4D,CAAA;AAE/D;;;;;;;;kEAQkE;AAClE,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CACvC,KAAK,EACL,MAAM,EACN,aAAa,CAAC,MAAM,CAAC,CAatB,CAAA;AAID;;;eAGe;AACf,MAAM,MAAM,OAAO,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IAC5D,KAAK,EAAE,KAAK,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,OAAO,CAAA;IACjB,SAAS,EAAE,OAAO,CAAA;IAClB,UAAU,EAAE,OAAO,CAAA;IACnB,GAAG,EAAE,aAAa,CAAC,cAAc,CAAC,CAAA;IAClC,KAAK,EAAE,aAAa,CAAC,cAAc,CAAC,CAAA;CACrC,CAAC,CAAA;AAEF;;;;;;;;mCAQmC;AACnC,MAAM,MAAM,UAAU,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IAC/D,OAAO,EAAE,aAAa,CAAC,cAAc,CAAC,CAAA;IACtC,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;IACnC,WAAW,EAAE,MAAM,CAAA;CACpB,CAAC,CAAA;AAEF;;;;eAIe;AACf,MAAM,MAAM,UAAU,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IAC/D,IAAI,EAAE,aAAa,CAAC,KAAK,CAAC,CAAA;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,KAAK,CAAC,KAAK,IAAI,CAAA;IAC3C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAA;IACxD,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B,CAAC,CAAA;AAmJF;;;;;;;;;;;;;;;;mDAgBmD;AACnD,eAAO,MAAM,MAAM,GAAI,KAAK,SAAS,MAAM,GAAG,MAAM,OAAK,QAAQ,CAAC;IAChE,IAAI,EAAE,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAA;IACrD,MAAM,EAAE,CACN,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,OAAO,KACb,SAAS,CACZ,KAAK,EACL,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,EACvC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CACjC,CAAA;IACD,SAAS,EAAE,CACT,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM,KACV,SAAS,CACZ,KAAK,EACL,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,EACvC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CACjC,CAAA;IACD,kBAAkB,EAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,CAAA;CACjE,CA0BA,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tabs/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,MAAM,EAEN,MAAM,EACN,MAAM,IAAI,CAAC,EAGZ,MAAM,QAAQ,CAAA;AACf,OAAO,KAAK,OAAO,MAAM,iBAAiB,CAAA;AAE1C,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,IAAI,EAGV,MAAM,cAAc,CAAA;AAGrB,OAAO,EAAE,KAAK,IAAI,IAAI,YAAY,EAAc,MAAM,kBAAkB,CAAA;AAKxE,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAI7E,yFAAyF;AACzF,eAAO,MAAM,WAAW,iDAAyC,CAAA;AACjE,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAC,IAAI,CAAA;AAEjD,yGAAyG;AACzG,eAAO,MAAM,cAAc,8CAAsC,CAAA;AACjE,MAAM,MAAM,cAAc,GAAG,OAAO,cAAc,CAAC,IAAI,CAAA;AAEvD;;;;+EAI+E;AAC/E,eAAO,MAAM,KAAK;;;;EAIhB,CAAA;AAEF,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAIrC;6CAC6C;AAC7C,eAAO,MAAM,WAAW;;;EAGtB,CAAA;AACF,wFAAwF;AACxF,eAAO,MAAM,UAAU;;EAAuC,CAAA;AAC9D,iDAAiD;AACjD,eAAO,MAAM,iBAAiB,wEAAyB,CAAA;AAEvD,4DAA4D;AAC5D,eAAO,MAAM,OAAO,EAAE,CAAC,CAAC,KAAK,CAC3B;IAAC,OAAO,WAAW;IAAE,OAAO,UAAU;IAAE,OAAO,iBAAiB;CAAC,CACV,CAAA;AAEzD,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAC,IAAI,CAAA;AACjD,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAC,IAAI,CAAA;AAE/C,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIzC,kRAAkR;AAClR,eAAO,MAAM,QAAQ;;;EAGnB,CAAA;AAEF,MAAM,MAAM,QAAQ,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IAC7D,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB,CAAC,CAAA;AAEF,wJAAwJ;AACxJ,eAAO,MAAM,UAAU;;;IAAsB,CAAA;AAE7C;;mDAEmD;AACnD,MAAM,MAAM,UAAU,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAA;AAIvE,2DAA2D;AAC3D,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,cAAc,CAAC,EAAE,cAAc,CAAA;CAChC,CAAC,CAAA;AAEF;;uDAEuD;AACvD,eAAO,MAAM,IAAI,GAAI,QAAQ,UAAU,KAAG,KAIxC,CAAA;AAQF,iDAAiD;AACjD,eAAO,MAAM,QAAQ;;;;;iBASpB,CAAA;AAED,KAAK,YAAY,GAAG,SAAS;IAC3B,KAAK;IACL,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC;CAC1B,CAAA;AAED;;;uBAGuB;AACvB,eAAO,MAAM,MAAM,GAAI,OAAO,KAAK,EAAE,SAAS,OAAO,KAAG,YAgBrD,CAAA;AAIH;;;eAGe;AACf,MAAM,MAAM,OAAO,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IAC5D,KAAK,EAAE,KAAK,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,OAAO,CAAA;IACjB,SAAS,EAAE,OAAO,CAAA;IAClB,UAAU,EAAE,OAAO,CAAA;IACnB,GAAG,EAAE,aAAa,CAAC,cAAc,CAAC,CAAA;IAClC,KAAK,EAAE,aAAa,CAAC,cAAc,CAAC,CAAA;CACrC,CAAC,CAAA;AAEF;;;;;;;;oEAQoE;AACpE,MAAM,MAAM,UAAU,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IAC/D,OAAO,EAAE,aAAa,CAAC,cAAc,CAAC,CAAA;IACtC,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;IACnC,WAAW,EAAE,MAAM,CAAA;CACpB,CAAC,CAAA;AAEF;;;;;;;;4BAQ4B;AAC5B,MAAM,MAAM,UAAU,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IAC/D,IAAI,EAAE,aAAa,CAAC,KAAK,CAAC,CAAA;IAC1B,aAAa,EAAE,KAAK,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,KAAK,CAAC,KAAK,IAAI,CAAA;IAC3C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAA;IACxD,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B,CAAC,CAAA;AAgKF;;;;;;;;;;;;;;;;;mDAiBmD;AACnD,eAAO,MAAM,MAAM,GAAI,KAAK,SAAS,MAAM,GAAG,MAAM,OAAK,QAAQ,CAAC;IAChE,IAAI,EAAE,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAA;IACrD,MAAM,EAAE,CACN,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,OAAO,KACb,SAAS,CACZ,KAAK,EACL,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,EACvC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CACjC,CAAA;CACF,CAmBA,CAAA"}
@@ -1,10 +1,10 @@
1
- import { Array, Effect, Function, Match as M, Option, Schema as S, String, pipe, } from 'effect';
1
+ import { Array, Effect, Match as M, Option, Schema as S, String, pipe, } from 'effect';
2
2
  import * as Command from 'foldkit/command';
3
3
  import * as Dom from 'foldkit/dom';
4
4
  import { childAttributes, html, } from 'foldkit/html';
5
5
  import { m } from 'foldkit/message';
6
6
  import { evo } from 'foldkit/struct';
7
- import { defineView, } from 'foldkit/submodel';
7
+ import { defineView } from 'foldkit/submodel';
8
8
  import { idSelector } from '../internal/selectors.js';
9
9
  import { keyToIndex } from '../keyboard.js';
10
10
  export { wrapIndex, findFirstEnabledIndex, keyToIndex } from '../keyboard.js';
@@ -13,15 +13,19 @@ export { wrapIndex, findFirstEnabledIndex, keyToIndex } from '../keyboard.js';
13
13
  export const Orientation = S.Literals(['Horizontal', 'Vertical']);
14
14
  /** Controls whether tabs activate on focus (`Automatic`) or require an explicit selection (`Manual`). */
15
15
  export const ActivationMode = S.Literals(['Automatic', 'Manual']);
16
- /** Schema for the tabs component's state, tracking active/focused indices and activation mode. */
16
+ /** Schema for the tabs component's private interaction state. The active
17
+ * tab is owned by the parent and passed in via `ViewInputs.selectedValue`,
18
+ * so it is not stored here. `maybeFocusedIndex` is the roving-tabindex
19
+ * cursor: `None` means keyboard focus follows the selected tab, and `Manual`
20
+ * activation stores `Some(index)` while focus diverges from the selection. */
17
21
  export const Model = S.Struct({
18
22
  id: S.String,
19
- activeIndex: S.Number,
20
- focusedIndex: S.Number,
23
+ maybeFocusedIndex: S.Option(S.Number),
21
24
  activationMode: ActivationMode,
22
25
  });
23
26
  // MESSAGE
24
- /** Sent when a tab is selected via click or keyboard. Updates both the active and focused indices. */
27
+ /** Sent when a tab is selected via click or keyboard. Commits the tab as the
28
+ * new selection and moves focus onto it. */
25
29
  export const SelectedTab = m('SelectedTab', {
26
30
  index: S.Number,
27
31
  value: S.String,
@@ -40,57 +44,42 @@ export const Selected = m('Selected', {
40
44
  });
41
45
  /** Union of out-messages the tabs component can produce. Surfaced as the third element of `update`'s return tuple and pattern-matched by the parent. */
42
46
  export const OutMessage = S.Union([Selected]);
43
- /** Creates an initial tabs model from a config. Defaults to first tab and automatic activation. */
44
- export const init = (config) => {
45
- const activeIndex = config.activeIndex ?? 0;
46
- return {
47
- id: config.id,
48
- activeIndex,
49
- focusedIndex: activeIndex,
50
- activationMode: config.activationMode ?? 'Automatic',
51
- };
52
- };
47
+ /** Creates an initial tabs model from a config. Focus follows the selected
48
+ * tab until the user navigates in `Manual` mode, so `maybeFocusedIndex`
49
+ * starts `None`. Defaults to automatic activation. */
50
+ export const init = (config) => ({
51
+ id: config.id,
52
+ maybeFocusedIndex: Option.none(),
53
+ activationMode: config.activationMode ?? 'Automatic',
54
+ });
53
55
  // UPDATE
54
56
  const tabId = (id, index) => `${id}-tab-${index}`;
55
57
  const tabPanelId = (id, index) => `${id}-panel-${index}`;
56
58
  /** Moves focus to the tab at the given index. */
57
59
  export const FocusTab = Command.define('FocusTab', { id: S.String, index: S.Number }, CompletedFocusTab)(({ id, index }) => Dom.focus(idSelector(tabId(id, index))).pipe(Effect.ignore, Effect.as(CompletedFocusTab())));
58
- /** Processes a tabs message and returns the next model, commands, and an optional OutMessage. `Selected` fires when a tab is committed via click or keyboard. */
60
+ /** Processes a tabs message and returns the next model, commands, and an
61
+ * optional OutMessage. `Selected` fires when a tab is committed via click or
62
+ * keyboard; the parent stores the new value and passes it back in as
63
+ * `selectedValue`. */
59
64
  export const update = (model, message) => M.value(message).pipe(M.withReturnType(), M.tagsExhaustive({
60
65
  SelectedTab: ({ index, value }) => [
61
- evo(model, {
62
- activeIndex: () => index,
63
- focusedIndex: () => index,
64
- }),
66
+ evo(model, { maybeFocusedIndex: () => Option.none() }),
65
67
  [FocusTab({ id: model.id, index })],
66
68
  Option.some(Selected({ value, index })),
67
69
  ],
68
70
  FocusedTab: ({ index }) => [
69
- evo(model, { focusedIndex: () => index }),
71
+ evo(model, { maybeFocusedIndex: () => Option.some(index) }),
70
72
  [FocusTab({ id: model.id, index })],
71
73
  Option.none(),
72
74
  ],
73
75
  CompletedFocusTab: () => [model, [], Option.none()],
74
76
  }));
75
- /** Programmatically selects a tab. Emits a `Selected` OutMessage. */
76
- export const selectTab = (model, value, index) => update(model, SelectedTab({ index, value }));
77
- /** Reflects an externally-sourced active tab onto the model without
78
- * emitting an OutMessage or running the focus command. Use this to mirror
79
- * external truth (a deep link, restored storage) onto the active tab.
80
- * Contrast with `selectTab`, which represents a user or programmatic
81
- * *choice*: it focuses the tab and emits `Selected`. Takes the tab `value`
82
- * plus the `options` list (mirroring `RadioGroup.select`) because Tabs
83
- * stores the active *index* internally, so the value is resolved to an
84
- * index. A value not present in `options` is a no-op. Returns the model
85
- * directly because it produces no commands and no OutMessage. */
86
- export const reflectSelectedTab = Function.dual(3, (model, value, options) => pipe(options, Array.findFirstIndex(option => option === value), Option.match({
87
- onNone: () => model,
88
- onSome: index => evo(model, { activeIndex: () => index, focusedIndex: () => index }),
89
- })));
90
77
  const internalView = defineView((model, viewInputs) => {
91
78
  const h = html();
92
- const { id, activationMode, focusedIndex, activeIndex } = model;
93
- const { tabs, ariaLabel, toView, isTabDisabled, orientation = 'Horizontal', } = viewInputs;
79
+ const { id, activationMode, maybeFocusedIndex } = model;
80
+ const { tabs, selectedValue, ariaLabel, toView, isTabDisabled, orientation = 'Horizontal', } = viewInputs;
81
+ const activeIndex = pipe(tabs, Array.findFirstIndex(tab => tab === selectedValue), Option.getOrElse(() => 0));
82
+ const focusedIndex = pipe(maybeFocusedIndex, Option.filter(index => index < tabs.length), Option.getOrElse(() => activeIndex));
94
83
  const isDisabled = (index) => !!isTabDisabled &&
95
84
  pipe(tabs, Array.get(index), Option.exists(tab => isTabDisabled(tab, index)));
96
85
  const { nextKey, previousKey } = M.value(orientation).pipe(M.when('Horizontal', () => ({
@@ -154,18 +143,19 @@ const internalView = defineView((model, viewInputs) => {
154
143
  activeIndex,
155
144
  });
156
145
  });
157
- /** Pairs the tabs `view`, `update`, and `selectTab` behind a single
158
- * Value-typed entry point. Declare once at module scope so consumers
159
- * receive `tab.value: Value` in `toView` without an `as` cast:
146
+ /** Pairs the tabs `view` and `update` behind a single Value-typed entry
147
+ * point. Declare once at module scope so consumers receive
148
+ * `tab.value: Value` in `toView` and the `Selected` OutMessage without an
149
+ * `as` cast:
160
150
  *
161
151
  * ```ts
162
152
  * const DemoTabs = Tabs.create<DemoTab>()
163
153
  *
164
- * // In view:
165
- * h.submodel({ view: DemoTabs.view, ... })
154
+ * // In view (selectedValue is the parent-owned active tab):
155
+ * h.submodel({ view: DemoTabs.view, viewInputs: { selectedValue, ... }, ... })
166
156
  *
167
- * // In update:
168
- * const [next, commands] = DemoTabs.update(model, message)
157
+ * // In update, fold the Selected OutMessage into your Model:
158
+ * const [next, commands, maybeOutMessage] = DemoTabs.update(model, message)
169
159
  * ```
170
160
  *
171
161
  * The internal view stays typed `ReadonlyArray<string>`; consumers can
@@ -179,8 +169,5 @@ export const create = () => {
179
169
  /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
180
170
  view: internalView,
181
171
  update: (model, message) => cast(update(model, message)),
182
- selectTab: (model, value, index) => cast(selectTab(model, value, index)),
183
- /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
184
- reflectSelectedTab: reflectSelectedTab,
185
172
  };
186
173
  };
@@ -4,6 +4,7 @@ import { Dismissed, DismissedAll, ElapsedDuration, GotAnimationMessage, HoveredE
4
4
  import { DismissAfter } from './update.js';
5
5
  export type { InitConfig } from './schema.js';
6
6
  export type { ShowInput } from './update.js';
7
+ export * as test from './test.js';
7
8
  export { Variant, Position, Dismissed, DismissedAll, ElapsedDuration, HoveredEntry, LeftEntry, GotAnimationMessage, DismissAfter, };
8
9
  /** Handlers passed to `entryToView`. Spread `dismiss` onto a close
9
10
  * button's attribute array (typically inside `h.button([...dismiss])`)
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/toast/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAChD,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,IAAI,EAGV,MAAM,cAAc,CAAA;AAGrB,OAAO,EACL,SAAS,EACT,YAAY,EACZ,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,OAAO,EACR,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,YAAY,EAAe,MAAM,aAAa,CAAA;AAEvD,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAC7C,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE5C,OAAO,EACL,OAAO,EACP,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,SAAS,EACT,mBAAmB,EACnB,YAAY,GACb,CAAA;AA0ED;;;;qDAIqD;AACrD,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC;IACnC,OAAO,EAAE,aAAa,CAAC,cAAc,CAAC,CAAA;CACvC,CAAC,CAAA;AAIF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,IAAI,GAAI,CAAC,EAAE,CAAC,EAAE,eAAe,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAUzC,QAAQ;qBACL,CAAC,KAAK;;;;;;;;;;;;kBAAO,EAAE,QAAQ,EAAE,aAAa,KAAK,IAAI;oBAChD,MAAM;6BACG,MAAM;yBACV,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2F1B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/toast/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAChD,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,IAAI,EAGV,MAAM,cAAc,CAAA;AAGrB,OAAO,EACL,SAAS,EACT,YAAY,EACZ,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,OAAO,EACR,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,YAAY,EAAe,MAAM,aAAa,CAAA;AAEvD,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAC7C,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE5C,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AAEjC,OAAO,EACL,OAAO,EACP,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,SAAS,EACT,mBAAmB,EACnB,YAAY,GACb,CAAA;AA0ED;;;;qDAIqD;AACrD,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC;IACnC,OAAO,EAAE,aAAa,CAAC,cAAc,CAAC,CAAA;CACvC,CAAC,CAAA;AAIF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,IAAI,GAAI,CAAC,EAAE,CAAC,EAAE,eAAe,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAUzC,QAAQ;qBACL,CAAC,KAAK;;;;;;;;;;;;kBAAO,EAAE,QAAQ,EAAE,aAAa,KAAK,IAAI;oBAChD,MAAM;6BACG,MAAM;yBACV,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2F1B,CAAA"}
@@ -3,6 +3,7 @@ import { childAttributes, html, } from 'foldkit/html';
3
3
  import { defineView } from 'foldkit/submodel';
4
4
  import { Dismissed, DismissedAll, ElapsedDuration, GotAnimationMessage, HoveredEntry, LeftEntry, Position, Variant, } from './schema.js';
5
5
  import { DismissAfter, makeRuntime } from './update.js';
6
+ export * as test from './test.js';
6
7
  export { Variant, Position, Dismissed, DismissedAll, ElapsedDuration, HoveredEntry, LeftEntry, GotAnimationMessage, DismissAfter, };
7
8
  const variantToRole = (variant) => M.value(variant).pipe(M.withReturnType(), M.when('Info', () => 'status'), M.when('Success', () => 'status'), M.when('Warning', () => 'alert'), M.when('Error', () => 'alert'), M.exhaustive);
8
9
  const positionToContainerStyle = (position) => {
@@ -1,4 +1,6 @@
1
1
  export { make, Variant, Position, Dismissed, DismissedAll, ElapsedDuration, HoveredEntry, LeftEntry, GotAnimationMessage, DismissAfter, } from './index.js';
2
+ export * as test from './test.js';
2
3
  export type { EntryHandlers } from './index.js';
3
4
  export type { InitConfig, ShowInput } from './index.js';
5
+ export type { DrainEntryInput } from './test.js';
4
6
  //# sourceMappingURL=public.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/toast/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,SAAS,EACT,mBAAmB,EACnB,YAAY,GACb,MAAM,YAAY,CAAA;AAEnB,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC/C,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/toast/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,SAAS,EACT,mBAAmB,EACnB,YAAY,GACb,MAAM,YAAY,CAAA;AAEnB,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AAEjC,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC/C,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACvD,YAAY,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA"}
@@ -1 +1,2 @@
1
1
  export { make, Variant, Position, Dismissed, DismissedAll, ElapsedDuration, HoveredEntry, LeftEntry, GotAnimationMessage, DismissAfter, } from './index.js';
2
+ export * as test from './test.js';
@@ -0,0 +1,41 @@
1
+ import * as Story from 'foldkit/story';
2
+ /** Input for {@link drainEntry}. `entryId` selects the entry whose lifecycle
3
+ * to drain. `version` is the auto-dismiss timer version echoed back by
4
+ * `ElapsedDuration`; it defaults to `0`, the version a freshly shown entry
5
+ * carries. */
6
+ export type DrainEntryInput = Readonly<{
7
+ entryId: string;
8
+ version?: number;
9
+ }>;
10
+ /** Builds a {@link Story.Command.resolveAll} step that drains a single toast
11
+ * entry's full animation and dismiss lifecycle. Resolving these Commands in
12
+ * order takes a freshly shown entry from its enter animation through
13
+ * auto-dismiss and its exit animation, ending with the entry removed from the
14
+ * stack and the `DismissedToast` OutMessage emitted.
15
+ *
16
+ * Showing a toast via `Toast.show` emits a multi-step lifecycle that a Story
17
+ * test must resolve in full or the story fails on leftover Commands. The
18
+ * steps are:
19
+ *
20
+ * - enter animation: `RequestFrame` then `AdvancedAnimationFrame`
21
+ * - enter settle: `WaitForAnimationSettled` then `EndedAnimation`
22
+ * - auto-dismiss: `DismissAfter` then `ElapsedDuration`
23
+ * - exit animation: `RequestFrame` then `AdvancedAnimationFrame`
24
+ * - exit settle: `WaitForAnimationSettled` then `EndedAnimation`
25
+ *
26
+ * Each step resolves with the child's raw result Message. `resolveAll` replays
27
+ * the matched Command's own recorded wrapping, so a parent that embeds the
28
+ * toast Submodel drains the same way without restating its `Got*` lift.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * Story.story(
33
+ * update,
34
+ * Story.with(model),
35
+ * Story.message(ClickedSave()),
36
+ * Toast.test.drainEntry({ entryId: 'toast-entry-0' }),
37
+ * )
38
+ * ```
39
+ */
40
+ export declare const drainEntry: ({ entryId, version, }: DrainEntryInput) => <Model, Message, OutMessage = undefined>(simulation: Story.StorySimulation<Model, Message, OutMessage>) => Story.StorySimulation<Model, Message, OutMessage>;
41
+ //# sourceMappingURL=test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../src/toast/test.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,eAAe,CAAA;AAWtC;;;eAGe;AACf,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IACrC,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAC,CAAA;AAIF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,UAAU,GAAI,uBAGxB,eAAe,iKAOf,CAAA"}
@@ -0,0 +1,36 @@
1
+ import * as Story from 'foldkit/story';
2
+ import { AdvancedAnimationFrame, EndedAnimation, RequestFrame, WaitForAnimationSettled, } from '../animation/index.js';
3
+ import { ElapsedDuration } from './schema.js';
4
+ import { DismissAfter } from './update.js';
5
+ const DEFAULT_VERSION = 0;
6
+ /** Builds a {@link Story.Command.resolveAll} step that drains a single toast
7
+ * entry's full animation and dismiss lifecycle. Resolving these Commands in
8
+ * order takes a freshly shown entry from its enter animation through
9
+ * auto-dismiss and its exit animation, ending with the entry removed from the
10
+ * stack and the `DismissedToast` OutMessage emitted.
11
+ *
12
+ * Showing a toast via `Toast.show` emits a multi-step lifecycle that a Story
13
+ * test must resolve in full or the story fails on leftover Commands. The
14
+ * steps are:
15
+ *
16
+ * - enter animation: `RequestFrame` then `AdvancedAnimationFrame`
17
+ * - enter settle: `WaitForAnimationSettled` then `EndedAnimation`
18
+ * - auto-dismiss: `DismissAfter` then `ElapsedDuration`
19
+ * - exit animation: `RequestFrame` then `AdvancedAnimationFrame`
20
+ * - exit settle: `WaitForAnimationSettled` then `EndedAnimation`
21
+ *
22
+ * Each step resolves with the child's raw result Message. `resolveAll` replays
23
+ * the matched Command's own recorded wrapping, so a parent that embeds the
24
+ * toast Submodel drains the same way without restating its `Got*` lift.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * Story.story(
29
+ * update,
30
+ * Story.with(model),
31
+ * Story.message(ClickedSave()),
32
+ * Toast.test.drainEntry({ entryId: 'toast-entry-0' }),
33
+ * )
34
+ * ```
35
+ */
36
+ export const drainEntry = ({ entryId, version = DEFAULT_VERSION, }) => Story.Command.resolveAll([RequestFrame, AdvancedAnimationFrame()], [WaitForAnimationSettled, EndedAnimation()], [DismissAfter, ElapsedDuration({ entryId, version })], [RequestFrame, AdvancedAnimationFrame()], [WaitForAnimationSettled, EndedAnimation()]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foldkit/ui",
3
- "version": "0.127.0",
3
+ "version": "0.128.1",
4
4
  "description": "Headless, accessible UI components for Foldkit",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -113,21 +113,21 @@
113
113
  "dist"
114
114
  ],
115
115
  "peerDependencies": {
116
- "effect": "4.0.0-beta.88",
116
+ "effect": "4.0.0-beta.97",
117
117
  "foldkit": "^0"
118
118
  },
119
119
  "dependencies": {
120
120
  "@floating-ui/dom": "^1.7.6"
121
121
  },
122
122
  "devDependencies": {
123
- "@effect/vitest": "4.0.0-beta.88",
124
- "effect": "4.0.0-beta.88",
123
+ "@effect/vitest": "4.0.0-beta.97",
124
+ "effect": "4.0.0-beta.97",
125
125
  "happy-dom": "^20.10.4",
126
126
  "rimraf": "^6.1.3",
127
127
  "typedoc": "^0.28.19",
128
128
  "typescript": "^6.0.3",
129
129
  "vitest": "^4.1.9",
130
- "foldkit": "0.127.0"
130
+ "foldkit": "0.128.1"
131
131
  },
132
132
  "keywords": [
133
133
  "foldkit",