foldkit 0.111.0 → 0.112.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 (222) hide show
  1. package/README.md +2 -2
  2. package/dist/devTools/host.d.ts +6 -0
  3. package/dist/devTools/host.d.ts.map +1 -0
  4. package/dist/devTools/host.js +4 -0
  5. package/dist/dom/dom.d.ts +6 -1
  6. package/dist/dom/dom.d.ts.map +1 -1
  7. package/dist/dom/dom.js +24 -1
  8. package/dist/html/index.d.ts +4 -3
  9. package/dist/html/index.d.ts.map +1 -1
  10. package/dist/html/index.js +4 -3
  11. package/dist/html/submodel.d.ts +2 -2
  12. package/dist/html/submodel.d.ts.map +1 -1
  13. package/dist/index.d.ts +0 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +0 -1
  16. package/dist/runtime/public.d.ts +1 -1
  17. package/dist/runtime/public.d.ts.map +1 -1
  18. package/dist/runtime/runtime.d.ts +14 -1
  19. package/dist/runtime/runtime.d.ts.map +1 -1
  20. package/dist/runtime/runtime.js +7 -4
  21. package/dist/runtime/subscription.d.ts +1 -1
  22. package/dist/subscription/public.d.ts +1 -1
  23. package/dist/subscription/public.d.ts.map +1 -1
  24. package/package.json +12 -93
  25. package/dist/devTools/overlay-styles.d.ts +0 -3
  26. package/dist/devTools/overlay-styles.d.ts.map +0 -1
  27. package/dist/devTools/overlay-styles.js +0 -706
  28. package/dist/devTools/overlay.d.ts +0 -63
  29. package/dist/devTools/overlay.d.ts.map +0 -1
  30. package/dist/devTools/overlay.js +0 -1244
  31. package/dist/test/apps/disabledButton.d.ts +0 -38
  32. package/dist/test/apps/disabledButton.d.ts.map +0 -1
  33. package/dist/test/apps/disabledButton.js +0 -71
  34. package/dist/ui/anchor.d.ts +0 -38
  35. package/dist/ui/anchor.d.ts.map +0 -1
  36. package/dist/ui/anchor.js +0 -142
  37. package/dist/ui/animation/index.d.ts +0 -49
  38. package/dist/ui/animation/index.d.ts.map +0 -1
  39. package/dist/ui/animation/index.js +0 -74
  40. package/dist/ui/animation/public.d.ts +0 -3
  41. package/dist/ui/animation/public.d.ts.map +0 -1
  42. package/dist/ui/animation/public.js +0 -1
  43. package/dist/ui/animation/schema.d.ts +0 -43
  44. package/dist/ui/animation/schema.d.ts.map +0 -1
  45. package/dist/ui/animation/schema.js +0 -41
  46. package/dist/ui/animation/update.d.ts +0 -24
  47. package/dist/ui/animation/update.d.ts.map +0 -1
  48. package/dist/ui/animation/update.js +0 -67
  49. package/dist/ui/button/index.d.ts +0 -17
  50. package/dist/ui/button/index.d.ts.map +0 -1
  51. package/dist/ui/button/index.js +0 -22
  52. package/dist/ui/button/public.d.ts +0 -3
  53. package/dist/ui/button/public.d.ts.map +0 -1
  54. package/dist/ui/button/public.js +0 -1
  55. package/dist/ui/calendar/index.d.ts +0 -462
  56. package/dist/ui/calendar/index.d.ts.map +0 -1
  57. package/dist/ui/calendar/index.js +0 -824
  58. package/dist/ui/calendar/public.d.ts +0 -3
  59. package/dist/ui/calendar/public.d.ts.map +0 -1
  60. package/dist/ui/calendar/public.js +0 -1
  61. package/dist/ui/checkbox/index.d.ts +0 -119
  62. package/dist/ui/checkbox/index.d.ts.map +0 -1
  63. package/dist/ui/checkbox/index.js +0 -110
  64. package/dist/ui/checkbox/public.d.ts +0 -3
  65. package/dist/ui/checkbox/public.d.ts.map +0 -1
  66. package/dist/ui/checkbox/public.js +0 -1
  67. package/dist/ui/combobox/multi.d.ts +0 -184
  68. package/dist/ui/combobox/multi.d.ts.map +0 -1
  69. package/dist/ui/combobox/multi.js +0 -81
  70. package/dist/ui/combobox/multiPublic.d.ts +0 -3
  71. package/dist/ui/combobox/multiPublic.d.ts.map +0 -1
  72. package/dist/ui/combobox/multiPublic.js +0 -1
  73. package/dist/ui/combobox/public.d.ts +0 -7
  74. package/dist/ui/combobox/public.d.ts.map +0 -1
  75. package/dist/ui/combobox/public.js +0 -3
  76. package/dist/ui/combobox/shared.d.ts +0 -422
  77. package/dist/ui/combobox/shared.d.ts.map +0 -1
  78. package/dist/ui/combobox/shared.js +0 -707
  79. package/dist/ui/combobox/single.d.ts +0 -199
  80. package/dist/ui/combobox/single.d.ts.map +0 -1
  81. package/dist/ui/combobox/single.js +0 -106
  82. package/dist/ui/datePicker/index.d.ts +0 -457
  83. package/dist/ui/datePicker/index.d.ts.map +0 -1
  84. package/dist/ui/datePicker/index.js +0 -317
  85. package/dist/ui/datePicker/public.d.ts +0 -3
  86. package/dist/ui/datePicker/public.d.ts.map +0 -1
  87. package/dist/ui/datePicker/public.js +0 -1
  88. package/dist/ui/dialog/index.d.ts +0 -155
  89. package/dist/ui/dialog/index.d.ts.map +0 -1
  90. package/dist/ui/dialog/index.js +0 -208
  91. package/dist/ui/dialog/public.d.ts +0 -3
  92. package/dist/ui/dialog/public.d.ts.map +0 -1
  93. package/dist/ui/dialog/public.js +0 -1
  94. package/dist/ui/disclosure/index.d.ts +0 -110
  95. package/dist/ui/disclosure/index.d.ts.map +0 -1
  96. package/dist/ui/disclosure/index.js +0 -110
  97. package/dist/ui/disclosure/public.d.ts +0 -3
  98. package/dist/ui/disclosure/public.d.ts.map +0 -1
  99. package/dist/ui/disclosure/public.js +0 -1
  100. package/dist/ui/dragAndDrop/index.d.ts +0 -540
  101. package/dist/ui/dragAndDrop/index.d.ts.map +0 -1
  102. package/dist/ui/dragAndDrop/index.js +0 -535
  103. package/dist/ui/dragAndDrop/public.d.ts +0 -3
  104. package/dist/ui/dragAndDrop/public.d.ts.map +0 -1
  105. package/dist/ui/dragAndDrop/public.js +0 -1
  106. package/dist/ui/fieldset/index.d.ts +0 -21
  107. package/dist/ui/fieldset/index.d.ts.map +0 -1
  108. package/dist/ui/fieldset/index.js +0 -25
  109. package/dist/ui/fieldset/public.d.ts +0 -3
  110. package/dist/ui/fieldset/public.d.ts.map +0 -1
  111. package/dist/ui/fieldset/public.js +0 -1
  112. package/dist/ui/fileDrop/index.d.ts +0 -109
  113. package/dist/ui/fileDrop/index.d.ts.map +0 -1
  114. package/dist/ui/fileDrop/index.js +0 -126
  115. package/dist/ui/fileDrop/public.d.ts +0 -3
  116. package/dist/ui/fileDrop/public.d.ts.map +0 -1
  117. package/dist/ui/fileDrop/public.js +0 -1
  118. package/dist/ui/group.d.ts +0 -8
  119. package/dist/ui/group.d.ts.map +0 -1
  120. package/dist/ui/group.js +0 -13
  121. package/dist/ui/index.d.ts +0 -25
  122. package/dist/ui/index.d.ts.map +0 -1
  123. package/dist/ui/index.js +0 -24
  124. package/dist/ui/input/index.d.ts +0 -26
  125. package/dist/ui/input/index.d.ts.map +0 -1
  126. package/dist/ui/input/index.js +0 -43
  127. package/dist/ui/input/public.d.ts +0 -3
  128. package/dist/ui/input/public.d.ts.map +0 -1
  129. package/dist/ui/input/public.js +0 -1
  130. package/dist/ui/keyboard.d.ts +0 -6
  131. package/dist/ui/keyboard.d.ts.map +0 -1
  132. package/dist/ui/keyboard.js +0 -9
  133. package/dist/ui/listbox/multi.d.ts +0 -190
  134. package/dist/ui/listbox/multi.d.ts.map +0 -1
  135. package/dist/ui/listbox/multi.js +0 -65
  136. package/dist/ui/listbox/multiPublic.d.ts +0 -3
  137. package/dist/ui/listbox/multiPublic.d.ts.map +0 -1
  138. package/dist/ui/listbox/multiPublic.js +0 -1
  139. package/dist/ui/listbox/public.d.ts +0 -7
  140. package/dist/ui/listbox/public.d.ts.map +0 -1
  141. package/dist/ui/listbox/public.js +0 -3
  142. package/dist/ui/listbox/shared.d.ts +0 -431
  143. package/dist/ui/listbox/shared.d.ts.map +0 -1
  144. package/dist/ui/listbox/shared.js +0 -669
  145. package/dist/ui/listbox/single.d.ts +0 -208
  146. package/dist/ui/listbox/single.d.ts.map +0 -1
  147. package/dist/ui/listbox/single.js +0 -73
  148. package/dist/ui/menu/index.d.ts +0 -367
  149. package/dist/ui/menu/index.d.ts.map +0 -1
  150. package/dist/ui/menu/index.js +0 -681
  151. package/dist/ui/menu/public.d.ts +0 -4
  152. package/dist/ui/menu/public.d.ts.map +0 -1
  153. package/dist/ui/menu/public.js +0 -1
  154. package/dist/ui/popover/index.d.ts +0 -267
  155. package/dist/ui/popover/index.d.ts.map +0 -1
  156. package/dist/ui/popover/index.js +0 -345
  157. package/dist/ui/popover/public.d.ts +0 -4
  158. package/dist/ui/popover/public.d.ts.map +0 -1
  159. package/dist/ui/popover/public.js +0 -1
  160. package/dist/ui/radioGroup/index.d.ts +0 -169
  161. package/dist/ui/radioGroup/index.d.ts.map +0 -1
  162. package/dist/ui/radioGroup/index.js +0 -196
  163. package/dist/ui/radioGroup/public.d.ts +0 -3
  164. package/dist/ui/radioGroup/public.d.ts.map +0 -1
  165. package/dist/ui/radioGroup/public.js +0 -1
  166. package/dist/ui/select/index.d.ts +0 -24
  167. package/dist/ui/select/index.d.ts.map +0 -1
  168. package/dist/ui/select/index.js +0 -40
  169. package/dist/ui/select/public.d.ts +0 -3
  170. package/dist/ui/select/public.d.ts.map +0 -1
  171. package/dist/ui/select/public.js +0 -1
  172. package/dist/ui/slider/index.d.ts +0 -318
  173. package/dist/ui/slider/index.d.ts.map +0 -1
  174. package/dist/ui/slider/index.js +0 -336
  175. package/dist/ui/slider/public.d.ts +0 -3
  176. package/dist/ui/slider/public.d.ts.map +0 -1
  177. package/dist/ui/slider/public.js +0 -1
  178. package/dist/ui/switch/index.d.ts +0 -99
  179. package/dist/ui/switch/index.d.ts.map +0 -1
  180. package/dist/ui/switch/index.js +0 -106
  181. package/dist/ui/switch/public.d.ts +0 -3
  182. package/dist/ui/switch/public.d.ts.map +0 -1
  183. package/dist/ui/switch/public.js +0 -1
  184. package/dist/ui/tabs/index.d.ts +0 -155
  185. package/dist/ui/tabs/index.d.ts.map +0 -1
  186. package/dist/ui/tabs/index.js +0 -184
  187. package/dist/ui/tabs/public.d.ts +0 -3
  188. package/dist/ui/tabs/public.d.ts.map +0 -1
  189. package/dist/ui/tabs/public.js +0 -1
  190. package/dist/ui/textarea/index.d.ts +0 -26
  191. package/dist/ui/textarea/index.d.ts.map +0 -1
  192. package/dist/ui/textarea/index.js +0 -44
  193. package/dist/ui/textarea/public.d.ts +0 -3
  194. package/dist/ui/textarea/public.d.ts.map +0 -1
  195. package/dist/ui/textarea/public.js +0 -1
  196. package/dist/ui/toast/index.d.ts +0 -608
  197. package/dist/ui/toast/index.d.ts.map +0 -1
  198. package/dist/ui/toast/index.js +0 -145
  199. package/dist/ui/toast/public.d.ts +0 -4
  200. package/dist/ui/toast/public.d.ts.map +0 -1
  201. package/dist/ui/toast/public.js +0 -1
  202. package/dist/ui/toast/schema.d.ts +0 -154
  203. package/dist/ui/toast/schema.d.ts.map +0 -1
  204. package/dist/ui/toast/schema.js +0 -93
  205. package/dist/ui/toast/update.d.ts +0 -510
  206. package/dist/ui/toast/update.d.ts.map +0 -1
  207. package/dist/ui/toast/update.js +0 -225
  208. package/dist/ui/tooltip/index.d.ts +0 -170
  209. package/dist/ui/tooltip/index.d.ts.map +0 -1
  210. package/dist/ui/tooltip/index.js +0 -252
  211. package/dist/ui/tooltip/public.d.ts +0 -4
  212. package/dist/ui/tooltip/public.d.ts.map +0 -1
  213. package/dist/ui/tooltip/public.js +0 -1
  214. package/dist/ui/typeahead.d.ts +0 -4
  215. package/dist/ui/typeahead.d.ts.map +0 -1
  216. package/dist/ui/typeahead.js +0 -14
  217. package/dist/ui/virtualList/index.d.ts +0 -202
  218. package/dist/ui/virtualList/index.d.ts.map +0 -1
  219. package/dist/ui/virtualList/index.js +0 -391
  220. package/dist/ui/virtualList/public.d.ts +0 -3
  221. package/dist/ui/virtualList/public.d.ts.map +0 -1
  222. package/dist/ui/virtualList/public.js +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"typeahead.d.ts","sourceRoot":"","sources":["../../src/ui/typeahead.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAuB,MAAM,QAAQ,CAAA;AAI3D,oOAAoO;AACpO,eAAO,MAAM,qBAAqB,GAAI,IAAI,EACxC,OAAO,aAAa,CAAC,IAAI,CAAC,EAC1B,OAAO,MAAM,EACb,sBAAsB,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAC3C,YAAY,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,EACtC,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,EACvD,cAAc,OAAO,KACpB,MAAM,CAAC,MAAM,CAAC,MAAM,CA4BtB,CAAA"}
@@ -1,14 +0,0 @@
1
- import { Array, Option, String as Str, pipe } from 'effect';
2
- import { wrapIndex } from './keyboard.js';
3
- /** Finds the first enabled item whose search text starts with the query, searching forward from the active item and wrapping around. On a fresh search, starts after the active item; on a refinement, includes the active item. */
4
- export const resolveTypeaheadMatch = (items, query, maybeActiveItemIndex, isDisabled, itemToSearchText, isRefinement) => {
5
- const lowerQuery = Str.toLowerCase(query);
6
- const offset = isRefinement ? 0 : 1;
7
- const startIndex = Option.match(maybeActiveItemIndex, {
8
- onNone: () => 0,
9
- onSome: index => index + offset,
10
- });
11
- const isEnabledMatch = (index) => !isDisabled(index) &&
12
- pipe(items, Array.get(index), Option.exists(item => pipe(itemToSearchText(item, index), Str.toLowerCase, Str.startsWith(lowerQuery))));
13
- return pipe(items, Array.length, Array.makeBy(step => wrapIndex(startIndex + step, items.length)), Array.findFirst(isEnabledMatch));
14
- };
@@ -1,202 +0,0 @@
1
- import { Effect, Option, Schema as S } from 'effect';
2
- import * as Command from '../../command/index.js';
3
- import { type ChildAttribute, type Html, type SubmodelView, type TagName } from '../../html/index.js';
4
- import * as Subscription from '../../runtime/subscription.js';
5
- /** Schema for the virtual list's state. Tracks scroll position, container
6
- * measurement, and any in-flight programmatic scroll. */
7
- export declare const Model: S.Struct<{
8
- readonly id: S.String;
9
- readonly rowHeightPx: S.Number;
10
- readonly scrollTop: S.Number;
11
- readonly measurement: S.Union<readonly [import("../../schema/index.js").CallableTaggedStruct<"Unmeasured", {}>, import("../../schema/index.js").CallableTaggedStruct<"Measured", {
12
- containerHeight: S.Number;
13
- }>]>;
14
- readonly pendingScroll: S.Union<readonly [import("../../schema/index.js").CallableTaggedStruct<"Idle", {}>, import("../../schema/index.js").CallableTaggedStruct<"ScrollingToIndex", {
15
- index: S.Number;
16
- version: S.Number;
17
- }>]>;
18
- readonly pendingScrollVersion: S.Number;
19
- }>;
20
- export type Model = typeof Model.Type;
21
- /** Sent when the user scrolls the container. Carries the new scroll position
22
- * read from the scroll event. */
23
- export declare const ScrolledContainer: import("../../schema/index.js").CallableTaggedStruct<"ScrolledContainer", {
24
- scrollTop: S.Number;
25
- }>;
26
- /** Sent when the container resizes. Carries the new container height read
27
- * from the `ResizeObserver` entry. */
28
- export declare const MeasuredContainer: import("../../schema/index.js").CallableTaggedStruct<"MeasuredContainer", {
29
- containerHeight: S.Number;
30
- }>;
31
- /** Sent when a `scrollToIndex` Command completes. Carries the version it was
32
- * issued with so the update can ignore stale completions. */
33
- export declare const CompletedApplyScroll: import("../../schema/index.js").CallableTaggedStruct<"CompletedApplyScroll", {
34
- version: S.Number;
35
- }>;
36
- /** Union of all messages the virtual list component can produce. */
37
- export declare const Message: S.Union<[
38
- typeof ScrolledContainer,
39
- typeof MeasuredContainer,
40
- typeof CompletedApplyScroll
41
- ]>;
42
- export type ScrolledContainer = typeof ScrolledContainer.Type;
43
- export type MeasuredContainer = typeof MeasuredContainer.Type;
44
- export type Message = typeof Message.Type;
45
- /** Configuration for creating a virtual list model with `init`. */
46
- export type InitConfig = Readonly<{
47
- id: string;
48
- rowHeightPx: number;
49
- initialScrollTop?: number;
50
- }>;
51
- /** Creates an initial virtual list model from a config. The container starts
52
- * in `Unmeasured` state. The first `ResizeObserver` entry transitions it to
53
- * `Measured`. */
54
- export declare const init: (config: InitConfig) => Model;
55
- export declare const ApplyScroll: Command.CommandDefinitionWithArgs<"ApplyScroll", {
56
- id: S.String;
57
- scrollTop: S.Number;
58
- version: S.Number;
59
- }, Effect.Effect<{
60
- readonly _tag: "CompletedApplyScroll";
61
- readonly version: number;
62
- }, never, never>>;
63
- /** Processes a virtual list message and returns the next model and commands. */
64
- export declare const update: (model: Model, message: Message) => readonly [Model, ReadonlyArray<Command.Command<Message>>];
65
- /** Programmatically scrolls the container so the row at `index` is visible.
66
- * Returns the next model and a Command that mutates `element.scrollTop`. The
67
- * natural scroll event then flows back through `ScrolledContainer` and the
68
- * component re-renders the new visible slice.
69
- *
70
- * Uses version-based cancellation: each call increments
71
- * `pendingScrollVersion` so a stale `CompletedApplyScroll` (e.g. from a
72
- * previous in-flight scroll) is ignored when its version no longer matches.
73
- *
74
- * Should be called after the container has rendered. If the container is not
75
- * yet in the DOM the Command silently no-ops (the model still transitions
76
- * through `ScrollingToIndex` → `Idle` via the version-matched completion).
77
- *
78
- * Assumes uniform row heights: target scroll position is computed as
79
- * `index * model.rowHeightPx`. For variable-height rows, use
80
- * `scrollToIndexVariable`. */
81
- export declare const scrollToIndex: (model: Model, index: number) => readonly [Model, ReadonlyArray<Command.Command<Message>>];
82
- /** Variable-height counterpart of `scrollToIndex`. Walks the heights of items
83
- * before `index` to compute the target `scrollTop`. Use this when rendering
84
- * the list with `itemToRowHeightPx`; use `scrollToIndex` for uniform heights.
85
- *
86
- * Out-of-range indices clamp to the corresponding edge: negative or zero
87
- * scrolls to the top, indices past the end scroll past the last row.
88
- *
89
- * Note: when restoring `initialScrollTop` on the first measurement of a
90
- * variable-height list, the runtime falls back to uniform-height math (using
91
- * `model.rowHeightPx`) because items aren't reachable from the `update`
92
- * function. Consumers who need an accurate initial scroll on a
93
- * variable-height list should call `scrollToIndexVariable` after the first
94
- * `MeasuredContainer` arrives. */
95
- export declare const scrollToIndexVariable: <Item>(model: Model, items: ReadonlyArray<Item>, itemToRowHeightPx: (item: Item, index: number) => number, index: number) => readonly [Model, ReadonlyArray<Command.Command<Message>>];
96
- /** Slice of the data array that the view should render, plus the spacer
97
- * heights that keep the scrollbar physically correct. The first row in the
98
- * slice corresponds to data index `startIndex`. */
99
- export type VisibleWindow = Readonly<{
100
- startIndex: number;
101
- endIndex: number;
102
- topSpacerHeight: number;
103
- bottomSpacerHeight: number;
104
- }>;
105
- /** Computes the visible slice of a data array given the current scroll
106
- * position, container height, row height, and an overscan buffer.
107
- *
108
- * Assumes uniform row heights via `model.rowHeightPx`. For variable-height
109
- * rows, use `visibleWindowVariable`.
110
- *
111
- * Returns `Option.none()` when the container has not yet been measured;
112
- * callers should render a placeholder (or `Html.empty`) and wait for the
113
- * first `MeasuredContainer` message. */
114
- export declare const visibleWindow: (model: Model, itemCount: number, overscan: number) => Option.Option<VisibleWindow>;
115
- /** Variable-height counterpart of `visibleWindow`. Walks the heights of every
116
- * item to build a prefix-sum array, then locates the visible slice with two
117
- * linear searches.
118
- *
119
- * Cost is O(N) per call, walking the whole `items` array once to build the
120
- * prefix sums. For lists in the 10k-item range, this comfortably fits inside
121
- * a 60Hz scroll budget. Larger lists or hotter scroll paths can layer a
122
- * prefix-sum cache invalidated when items change; that lives behind the same
123
- * return shape so consumers don't have to know.
124
- *
125
- * Returns `Option.none()` when the container has not yet been measured. */
126
- export declare const visibleWindowVariable: <Item>(model: Model, items: ReadonlyArray<Item>, itemToRowHeightPx: (item: Item, index: number) => number, overscan: number) => Option.Option<VisibleWindow>;
127
- /** Subscriptions that track the container's scroll position and size.
128
- *
129
- * - **scroll**: listens for `scroll` events on the container element and
130
- * emits `ScrolledContainer` with the new `scrollTop`.
131
- * - **resize**: observes the container with `ResizeObserver` and emits
132
- * `MeasuredContainer` with the new height.
133
- *
134
- * A `MutationObserver` watches the document for the container element
135
- * appearing and disappearing, so the listeners attach the moment the
136
- * element is inserted into the DOM and clean up when it is removed. This
137
- * makes the subscription robust across SPA route changes: navigating to a
138
- * page that mounts the list, away, and back all reattach correctly without
139
- * the consumer having to teach the framework about navigation. */
140
- export declare const subscriptions: {
141
- readonly containerEvents: Subscription.EntryWithoutKeepAlive<{
142
- readonly id: string;
143
- readonly rowHeightPx: number;
144
- readonly scrollTop: number;
145
- readonly measurement: {
146
- readonly _tag: "Unmeasured";
147
- } | {
148
- readonly _tag: "Measured";
149
- readonly containerHeight: number;
150
- };
151
- readonly pendingScroll: {
152
- readonly _tag: "Idle";
153
- } | {
154
- readonly _tag: "ScrollingToIndex";
155
- readonly index: number;
156
- readonly version: number;
157
- };
158
- readonly pendingScrollVersion: number;
159
- }, {
160
- readonly _tag: "ScrolledContainer";
161
- readonly scrollTop: number;
162
- } | {
163
- readonly _tag: "MeasuredContainer";
164
- readonly containerHeight: number;
165
- } | {
166
- readonly _tag: "CompletedApplyScroll";
167
- readonly version: number;
168
- }, {
169
- readonly id: string;
170
- }, never> & {
171
- readonly __subscription: never;
172
- };
173
- };
174
- /** Per-render view inputs passed to `view` via `h.submodel`'s `viewInputs` field.
175
- *
176
- * VirtualList does not surface event handlers in the view. All input
177
- * (scroll events and resize observations) flows through the
178
- * `containerEvents` Subscription. The consumer wraps that
179
- * Subscription's stream into their parent Message in their own
180
- * `subscriptions` definition. */
181
- export type ViewInputs<Item> = Readonly<{
182
- items: ReadonlyArray<Item>;
183
- itemToKey: (item: Item, index: number) => string;
184
- itemToView: (item: Item, index: number) => Html;
185
- itemToRowHeightPx?: (item: Item, index: number) => number;
186
- overscan?: number;
187
- rowElement?: TagName;
188
- containerClassName?: string;
189
- containerAttributes?: ReadonlyArray<ChildAttribute>;
190
- }>;
191
- /** Renders a virtualized list. Only items inside the viewport (plus an
192
- * overscan buffer) are mounted; spacer elements above and below the
193
- * slice keep the scrollbar's apparent total height correct.
194
- *
195
- * Generic over `Item`: call as `Ui.VirtualList.view<MyItem>()` at the
196
- * embed site to get a `SubmodelView` typed for your item type. The
197
- * underlying view implementation is shared; the call only narrows the
198
- * type. */
199
- type ViewForItem<Item> = SubmodelView<Model, Message, ViewInputs<Item>>;
200
- export declare const view: <Item>() => ViewForItem<Item>;
201
- export {};
202
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/virtualList/index.ts"],"names":[],"mappings":"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"}
@@ -1,391 +0,0 @@
1
- import { Array, Effect, Match as M, Number, Option, Queue, Schema as S, Stream, pipe, } from 'effect';
2
- import * as Command from '../../command/index.js';
3
- import { childAttributes, defineView, html, } from '../../html/index.js';
4
- import { m } from '../../message/index.js';
5
- import * as Subscription from '../../runtime/subscription.js';
6
- import { ts } from '../../schema/index.js';
7
- import { evo } from '../../struct/index.js';
8
- // MODEL
9
- const Unmeasured = ts('Unmeasured');
10
- const Measured = ts('Measured', { containerHeight: S.Number });
11
- /** Measurement state of the virtual list's scrollable container.
12
- *
13
- * Before the container's `ResizeObserver` fires for the first time we don't
14
- * know its height and cannot compute a visible slice. The view must handle
15
- * `Unmeasured` explicitly, typically by rendering a placeholder until the
16
- * first measurement arrives.
17
- */
18
- const Measurement = S.Union([Unmeasured, Measured]);
19
- const Idle = ts('Idle');
20
- const ScrollingToIndex = ts('ScrollingToIndex', {
21
- index: S.Number,
22
- version: S.Number,
23
- });
24
- /** State of a programmatic scroll initiated by `scrollToIndex`. */
25
- const PendingScroll = S.Union([Idle, ScrollingToIndex]);
26
- /** Schema for the virtual list's state. Tracks scroll position, container
27
- * measurement, and any in-flight programmatic scroll. */
28
- export const Model = S.Struct({
29
- id: S.String,
30
- rowHeightPx: S.Number,
31
- scrollTop: S.Number,
32
- measurement: Measurement,
33
- pendingScroll: PendingScroll,
34
- pendingScrollVersion: S.Number,
35
- });
36
- // MESSAGE
37
- /** Sent when the user scrolls the container. Carries the new scroll position
38
- * read from the scroll event. */
39
- export const ScrolledContainer = m('ScrolledContainer', {
40
- scrollTop: S.Number,
41
- });
42
- /** Sent when the container resizes. Carries the new container height read
43
- * from the `ResizeObserver` entry. */
44
- export const MeasuredContainer = m('MeasuredContainer', {
45
- containerHeight: S.Number,
46
- });
47
- /** Sent when a `scrollToIndex` Command completes. Carries the version it was
48
- * issued with so the update can ignore stale completions. */
49
- export const CompletedApplyScroll = m('CompletedApplyScroll', {
50
- version: S.Number,
51
- });
52
- /** Union of all messages the virtual list component can produce. */
53
- export const Message = S.Union([ScrolledContainer, MeasuredContainer, CompletedApplyScroll]);
54
- /** Creates an initial virtual list model from a config. The container starts
55
- * in `Unmeasured` state. The first `ResizeObserver` entry transitions it to
56
- * `Measured`. */
57
- export const init = (config) => ({
58
- id: config.id,
59
- rowHeightPx: config.rowHeightPx,
60
- scrollTop: config.initialScrollTop ?? 0,
61
- measurement: Unmeasured(),
62
- pendingScroll: Idle(),
63
- pendingScrollVersion: 0,
64
- });
65
- // UPDATE
66
- export const ApplyScroll = Command.define('ApplyScroll', { id: S.String, scrollTop: S.Number, version: S.Number }, CompletedApplyScroll)(({ id, scrollTop, version }) => Effect.sync(() => {
67
- const element = document.getElementById(id);
68
- if (element !== null) {
69
- element.scrollTop = scrollTop;
70
- }
71
- return CompletedApplyScroll({ version });
72
- }));
73
- /** Processes a virtual list message and returns the next model and commands. */
74
- export const update = (model, message) => M.value(message).pipe(M.withReturnType(), M.tagsExhaustive({
75
- ScrolledContainer: ({ scrollTop }) => [
76
- evo(model, { scrollTop: () => scrollTop }),
77
- [],
78
- ],
79
- MeasuredContainer: ({ containerHeight }) => {
80
- const wasUnmeasured = model.measurement._tag === 'Unmeasured';
81
- const needsInitialApply = wasUnmeasured && model.scrollTop !== 0;
82
- if (needsInitialApply) {
83
- const nextVersion = Number.increment(model.pendingScrollVersion);
84
- return [
85
- evo(model, {
86
- measurement: () => Measured({ containerHeight }),
87
- pendingScrollVersion: () => nextVersion,
88
- pendingScroll: () => ScrollingToIndex({
89
- index: Math.floor(model.scrollTop / model.rowHeightPx),
90
- version: nextVersion,
91
- }),
92
- }),
93
- [
94
- ApplyScroll({
95
- id: model.id,
96
- scrollTop: model.scrollTop,
97
- version: nextVersion,
98
- }),
99
- ],
100
- ];
101
- }
102
- else {
103
- return [
104
- evo(model, { measurement: () => Measured({ containerHeight }) }),
105
- [],
106
- ];
107
- }
108
- },
109
- CompletedApplyScroll: ({ version }) => {
110
- if (version !== model.pendingScrollVersion) {
111
- return [model, []];
112
- }
113
- else {
114
- return [evo(model, { pendingScroll: () => Idle() }), []];
115
- }
116
- },
117
- }));
118
- const buildScrollToIndex = (model, index, targetScrollTop) => {
119
- const nextVersion = Number.increment(model.pendingScrollVersion);
120
- return [
121
- evo(model, {
122
- pendingScrollVersion: () => nextVersion,
123
- pendingScroll: () => ScrollingToIndex({ index, version: nextVersion }),
124
- }),
125
- [
126
- ApplyScroll({
127
- id: model.id,
128
- scrollTop: targetScrollTop,
129
- version: nextVersion,
130
- }),
131
- ],
132
- ];
133
- };
134
- /** Programmatically scrolls the container so the row at `index` is visible.
135
- * Returns the next model and a Command that mutates `element.scrollTop`. The
136
- * natural scroll event then flows back through `ScrolledContainer` and the
137
- * component re-renders the new visible slice.
138
- *
139
- * Uses version-based cancellation: each call increments
140
- * `pendingScrollVersion` so a stale `CompletedApplyScroll` (e.g. from a
141
- * previous in-flight scroll) is ignored when its version no longer matches.
142
- *
143
- * Should be called after the container has rendered. If the container is not
144
- * yet in the DOM the Command silently no-ops (the model still transitions
145
- * through `ScrollingToIndex` → `Idle` via the version-matched completion).
146
- *
147
- * Assumes uniform row heights: target scroll position is computed as
148
- * `index * model.rowHeightPx`. For variable-height rows, use
149
- * `scrollToIndexVariable`. */
150
- export const scrollToIndex = (model, index) => buildScrollToIndex(model, index, index * model.rowHeightPx);
151
- /** Variable-height counterpart of `scrollToIndex`. Walks the heights of items
152
- * before `index` to compute the target `scrollTop`. Use this when rendering
153
- * the list with `itemToRowHeightPx`; use `scrollToIndex` for uniform heights.
154
- *
155
- * Out-of-range indices clamp to the corresponding edge: negative or zero
156
- * scrolls to the top, indices past the end scroll past the last row.
157
- *
158
- * Note: when restoring `initialScrollTop` on the first measurement of a
159
- * variable-height list, the runtime falls back to uniform-height math (using
160
- * `model.rowHeightPx`) because items aren't reachable from the `update`
161
- * function. Consumers who need an accurate initial scroll on a
162
- * variable-height list should call `scrollToIndexVariable` after the first
163
- * `MeasuredContainer` arrives. */
164
- export const scrollToIndexVariable = (model, items, itemToRowHeightPx, index) => {
165
- const cumulativeOffsets = prefixSum(items, itemToRowHeightPx);
166
- const targetScrollTop = pipe(cumulativeOffsets, Array.get(Math.max(0, index)), Option.getOrElse(() => lastOrZero(cumulativeOffsets)));
167
- return buildScrollToIndex(model, index, targetScrollTop);
168
- };
169
- const clampIndex = (index, itemCount) => Math.max(0, Math.min(index, itemCount));
170
- const prefixSum = (items, itemToRowHeightPx) => {
171
- const heights = Array.map(items, itemToRowHeightPx);
172
- return Array.scan(heights, 0, (cumulative, height) => cumulative + height);
173
- };
174
- const lastOrZero = (values) => pipe(values, Array.last, Option.getOrElse(() => 0));
175
- /** Computes the visible slice of a data array given the current scroll
176
- * position, container height, row height, and an overscan buffer.
177
- *
178
- * Assumes uniform row heights via `model.rowHeightPx`. For variable-height
179
- * rows, use `visibleWindowVariable`.
180
- *
181
- * Returns `Option.none()` when the container has not yet been measured;
182
- * callers should render a placeholder (or `Html.empty`) and wait for the
183
- * first `MeasuredContainer` message. */
184
- export const visibleWindow = (model, itemCount, overscan) => M.value(model.measurement).pipe(M.withReturnType(), M.tagsExhaustive({
185
- Unmeasured: () => Option.none(),
186
- Measured: ({ containerHeight }) => {
187
- const firstVisibleIndex = Math.floor(model.scrollTop / model.rowHeightPx);
188
- const lastVisibleIndex = Math.ceil((model.scrollTop + containerHeight) / model.rowHeightPx);
189
- const startIndex = clampIndex(firstVisibleIndex - overscan, itemCount);
190
- const endIndex = clampIndex(lastVisibleIndex + overscan, itemCount);
191
- const topSpacerHeight = startIndex * model.rowHeightPx;
192
- const bottomSpacerHeight = (itemCount - endIndex) * model.rowHeightPx;
193
- return Option.some({
194
- startIndex,
195
- endIndex,
196
- topSpacerHeight,
197
- bottomSpacerHeight,
198
- });
199
- },
200
- }));
201
- /** Variable-height counterpart of `visibleWindow`. Walks the heights of every
202
- * item to build a prefix-sum array, then locates the visible slice with two
203
- * linear searches.
204
- *
205
- * Cost is O(N) per call, walking the whole `items` array once to build the
206
- * prefix sums. For lists in the 10k-item range, this comfortably fits inside
207
- * a 60Hz scroll budget. Larger lists or hotter scroll paths can layer a
208
- * prefix-sum cache invalidated when items change; that lives behind the same
209
- * return shape so consumers don't have to know.
210
- *
211
- * Returns `Option.none()` when the container has not yet been measured. */
212
- export const visibleWindowVariable = (model, items, itemToRowHeightPx, overscan) => M.value(model.measurement).pipe(M.withReturnType(), M.tagsExhaustive({
213
- Unmeasured: () => Option.none(),
214
- Measured: ({ containerHeight }) => {
215
- const itemCount = items.length;
216
- const cumulativeOffsets = prefixSum(items, itemToRowHeightPx);
217
- const totalHeight = lastOrZero(cumulativeOffsets);
218
- const firstVisibleIndex = pipe(cumulativeOffsets, Array.findFirstIndex(Number.isGreaterThan(model.scrollTop)), Option.match({
219
- onNone: () => itemCount,
220
- onSome: index => Math.max(0, index - 1),
221
- }));
222
- const lastVisibleIndex = pipe(cumulativeOffsets, Array.findFirstIndex(Number.isGreaterThanOrEqualTo(model.scrollTop + containerHeight)), Option.getOrElse(() => itemCount));
223
- const startIndex = clampIndex(firstVisibleIndex - overscan, itemCount);
224
- const endIndex = clampIndex(lastVisibleIndex + overscan, itemCount);
225
- const topSpacerHeight = pipe(cumulativeOffsets, Array.get(startIndex), Option.getOrElse(() => 0));
226
- const offsetAtEnd = pipe(cumulativeOffsets, Array.get(endIndex), Option.getOrElse(() => totalHeight));
227
- const bottomSpacerHeight = totalHeight - offsetAtEnd;
228
- return Option.some({
229
- startIndex,
230
- endIndex,
231
- topSpacerHeight,
232
- bottomSpacerHeight,
233
- });
234
- },
235
- }));
236
- // SUBSCRIPTION
237
- const containerElement = (id) => Option.fromNullishOr(document.getElementById(id));
238
- /** Subscriptions that track the container's scroll position and size.
239
- *
240
- * - **scroll**: listens for `scroll` events on the container element and
241
- * emits `ScrolledContainer` with the new `scrollTop`.
242
- * - **resize**: observes the container with `ResizeObserver` and emits
243
- * `MeasuredContainer` with the new height.
244
- *
245
- * A `MutationObserver` watches the document for the container element
246
- * appearing and disappearing, so the listeners attach the moment the
247
- * element is inserted into the DOM and clean up when it is removed. This
248
- * makes the subscription robust across SPA route changes: navigating to a
249
- * page that mounts the list, away, and back all reattach correctly without
250
- * the consumer having to teach the framework about navigation. */
251
- export const subscriptions = Subscription.make()(entry => ({
252
- containerEvents: entry({ id: S.String }, {
253
- modelToDependencies: model => ({ id: model.id }),
254
- dependenciesToStream: ({ id }) => Stream.callback(queue => Effect.acquireRelease(Effect.sync(() => {
255
- const state = {
256
- scrollListener: null,
257
- resizeObserver: null,
258
- observedElement: null,
259
- pendingFrame: null,
260
- };
261
- const detach = () => {
262
- if (state.resizeObserver !== null) {
263
- state.resizeObserver.disconnect();
264
- state.resizeObserver = null;
265
- }
266
- if (state.observedElement !== null &&
267
- state.scrollListener !== null) {
268
- state.observedElement.removeEventListener('scroll', state.scrollListener);
269
- }
270
- state.observedElement = null;
271
- state.scrollListener = null;
272
- };
273
- const attach = (element) => {
274
- const listener = () => Queue.offerUnsafe(queue, ScrolledContainer({ scrollTop: element.scrollTop }));
275
- element.addEventListener('scroll', listener, { passive: true });
276
- state.scrollListener = listener;
277
- state.observedElement = element;
278
- state.resizeObserver = new ResizeObserver(entries => {
279
- const lastEntry = Array.last(entries);
280
- if (Option.isSome(lastEntry)) {
281
- Queue.offerUnsafe(queue, MeasuredContainer({
282
- containerHeight: lastEntry.value.contentRect.height,
283
- }));
284
- }
285
- });
286
- state.resizeObserver.observe(element);
287
- };
288
- const reconcile = () => {
289
- const maybeElement = containerElement(id);
290
- if (Option.isNone(maybeElement)) {
291
- if (state.observedElement !== null) {
292
- detach();
293
- }
294
- return;
295
- }
296
- if (state.observedElement === maybeElement.value) {
297
- return;
298
- }
299
- detach();
300
- attach(maybeElement.value);
301
- };
302
- reconcile();
303
- // NOTE: observes the entire document subtree because the container
304
- // can be inserted/removed by any parent the consumer chooses (route
305
- // changes, conditional renders, modal mounts), and the framework
306
- // has no way to know that hierarchy in advance. Reconcile is gated
307
- // by rAF and short-circuits when the cached observedElement is
308
- // still in the DOM, so per-mutation cost stays low even with
309
- // subtree: true.
310
- const mutationObserver = new MutationObserver(() => {
311
- if (state.pendingFrame !== null) {
312
- return;
313
- }
314
- state.pendingFrame = requestAnimationFrame(() => {
315
- state.pendingFrame = null;
316
- reconcile();
317
- });
318
- });
319
- mutationObserver.observe(document.body, {
320
- childList: true,
321
- subtree: true,
322
- });
323
- return { state, detach, mutationObserver };
324
- }), ({ state, detach, mutationObserver }) => Effect.sync(() => {
325
- mutationObserver.disconnect();
326
- if (state.pendingFrame !== null) {
327
- cancelAnimationFrame(state.pendingFrame);
328
- }
329
- detach();
330
- })).pipe(Effect.flatMap(() => Effect.never))),
331
- }),
332
- }));
333
- // VIEW
334
- const DEFAULT_OVERSCAN = 5;
335
- export const view = () =>
336
- /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
337
- viewImpl;
338
- const viewImpl = defineView((model, viewInputs) => {
339
- const h = html();
340
- const { items, itemToKey, itemToView, itemToRowHeightPx, overscan = DEFAULT_OVERSCAN, rowElement = 'li', containerClassName, containerAttributes = [], } = viewInputs;
341
- const baseContainerAttributes = [
342
- h.Id(model.id),
343
- h.Role('list'),
344
- h.DataAttribute('virtual-list-id', model.id),
345
- h.Style({
346
- overflow: 'auto',
347
- 'list-style': 'none',
348
- margin: '0',
349
- padding: '0',
350
- }),
351
- ...(containerClassName !== undefined
352
- ? [h.Class(containerClassName)]
353
- : []),
354
- ];
355
- const allContainerAttributes = [
356
- ...childAttributes(baseContainerAttributes),
357
- ...containerAttributes,
358
- ];
359
- const renderContainer = (children) => h.keyed('ul')(model.id, allContainerAttributes, children);
360
- const maybeWindow = itemToRowHeightPx !== undefined
361
- ? visibleWindowVariable(model, items, itemToRowHeightPx, overscan)
362
- : visibleWindow(model, items.length, overscan);
363
- const rowHeightFor = (item, dataIndex) => itemToRowHeightPx !== undefined
364
- ? itemToRowHeightPx(item, dataIndex)
365
- : model.rowHeightPx;
366
- return Option.match(maybeWindow, {
367
- onNone: () => renderContainer([]),
368
- onSome: ({ startIndex, endIndex, topSpacerHeight, bottomSpacerHeight, }) => {
369
- const visibleItems = items.slice(startIndex, endIndex);
370
- const topSpacer = h.keyed('li')(`${model.id}-top-spacer`, [h.Role('presentation'), h.Style({ height: `${topSpacerHeight}px` })], []);
371
- const bottomSpacer = h.keyed('li')(`${model.id}-bottom-spacer`, [
372
- h.Role('presentation'),
373
- h.Style({ height: `${bottomSpacerHeight}px` }),
374
- ], []);
375
- const renderedRows = Array.map(visibleItems, (item, sliceIndex) => {
376
- const dataIndex = startIndex + sliceIndex;
377
- return h.keyed(rowElement)(itemToKey(item, dataIndex), [
378
- h.Role('listitem'),
379
- h.DataAttribute('virtual-list-item-index', String(dataIndex)),
380
- h.AriaSetsize(items.length),
381
- h.AriaPosinset(dataIndex + 1),
382
- h.Style({
383
- height: `${rowHeightFor(item, dataIndex)}px`,
384
- display: 'grid',
385
- }),
386
- ], [itemToView(item, dataIndex)]);
387
- });
388
- return renderContainer([topSpacer, ...renderedRows, bottomSpacer]);
389
- },
390
- });
391
- });
@@ -1,3 +0,0 @@
1
- export { init, update, scrollToIndex, scrollToIndexVariable, view, subscriptions, visibleWindow, visibleWindowVariable, Model, Message, ScrolledContainer, MeasuredContainer, CompletedApplyScroll, } from './index.js';
2
- export type { InitConfig, ViewInputs, VisibleWindow } from './index.js';
3
- //# sourceMappingURL=public.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/ui/virtualList/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,MAAM,EACN,aAAa,EACb,qBAAqB,EACrB,IAAI,EACJ,aAAa,EACb,aAAa,EACb,qBAAqB,EACrB,KAAK,EACL,OAAO,EACP,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,YAAY,CAAA;AAEnB,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA"}
@@ -1 +0,0 @@
1
- export { init, update, scrollToIndex, scrollToIndexVariable, view, subscriptions, visibleWindow, visibleWindowVariable, Model, Message, ScrolledContainer, MeasuredContainer, CompletedApplyScroll, } from './index.js';