@adia-ai/web-components 0.6.33 → 0.6.34

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 (157) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/components/accordion/accordion.css +2 -2
  3. package/components/action-list/action-list.css +2 -2
  4. package/components/agent-artifact/agent-artifact.css +31 -31
  5. package/components/agent-feedback-bar/agent-feedback-bar.css +10 -10
  6. package/components/agent-questions/agent-questions.css +57 -57
  7. package/components/agent-reasoning/agent-reasoning.css +62 -62
  8. package/components/agent-suggestions/agent-suggestions.css +4 -4
  9. package/components/agent-trace/agent-trace.css +53 -53
  10. package/components/alert/alert.css +41 -41
  11. package/components/avatar/avatar.css +27 -27
  12. package/components/badge/badge.css +27 -27
  13. package/components/block/block.css +16 -16
  14. package/components/breadcrumb/breadcrumb.css +23 -23
  15. package/components/button/button.css +101 -91
  16. package/components/calendar-grid/calendar-grid.a2ui.json +136 -0
  17. package/components/calendar-grid/calendar-grid.css +226 -0
  18. package/components/calendar-grid/calendar-grid.d.ts +37 -0
  19. package/components/calendar-grid/calendar-grid.js +17 -0
  20. package/components/calendar-grid/calendar-grid.yaml +116 -0
  21. package/components/calendar-grid/class.js +300 -0
  22. package/components/calendar-picker/calendar-picker.css +139 -139
  23. package/components/canvas/canvas.css +12 -12
  24. package/components/card/card.css +83 -83
  25. package/components/chart/chart.css +224 -224
  26. package/components/chart-legend/chart-legend.css +26 -26
  27. package/components/check/check.css +40 -40
  28. package/components/code/code.css +125 -125
  29. package/components/col/col.css +15 -15
  30. package/components/color-picker/color-picker.css +55 -55
  31. package/components/combobox/class.js +861 -0
  32. package/components/combobox/combobox.a2ui.json +363 -0
  33. package/components/combobox/combobox.css +244 -0
  34. package/components/combobox/combobox.d.ts +113 -0
  35. package/components/combobox/combobox.examples.md +59 -0
  36. package/components/combobox/combobox.js +17 -0
  37. package/components/combobox/combobox.test.js +181 -0
  38. package/components/combobox/combobox.yaml +369 -0
  39. package/components/command/command.css +90 -90
  40. package/components/date-range-picker/class.js +775 -0
  41. package/components/date-range-picker/date-range-picker.a2ui.json +300 -0
  42. package/components/date-range-picker/date-range-picker.css +178 -0
  43. package/components/date-range-picker/date-range-picker.d.ts +82 -0
  44. package/components/date-range-picker/date-range-picker.examples.md +37 -0
  45. package/components/date-range-picker/date-range-picker.js +17 -0
  46. package/components/date-range-picker/date-range-picker.test.js +387 -0
  47. package/components/date-range-picker/date-range-picker.yaml +285 -0
  48. package/components/datetime-picker/class.js +706 -0
  49. package/components/datetime-picker/datetime-picker.a2ui.json +334 -0
  50. package/components/datetime-picker/datetime-picker.css +150 -0
  51. package/components/datetime-picker/datetime-picker.d.ts +86 -0
  52. package/components/datetime-picker/datetime-picker.examples.md +46 -0
  53. package/components/datetime-picker/datetime-picker.js +17 -0
  54. package/components/datetime-picker/datetime-picker.test.js +454 -0
  55. package/components/datetime-picker/datetime-picker.yaml +332 -0
  56. package/components/demo-toggle/demo-toggle.css +27 -27
  57. package/components/description-list/description-list.css +18 -18
  58. package/components/divider/divider.css +24 -24
  59. package/components/embed/embed.css +6 -6
  60. package/components/empty-state/empty-state.css +27 -27
  61. package/components/feed/feed.css +12 -12
  62. package/components/field/field.css +28 -28
  63. package/components/fields/fields.css +5 -5
  64. package/components/grid/grid.css +5 -5
  65. package/components/heatmap/heatmap.css +63 -63
  66. package/components/icon/icon.css +12 -12
  67. package/components/image/image.css +14 -14
  68. package/components/index.js +8 -0
  69. package/components/input/input.css +66 -66
  70. package/components/inspector/inspector.css +6 -6
  71. package/components/integration-card/class.js +410 -0
  72. package/components/integration-card/integration-card.a2ui.json +268 -0
  73. package/components/integration-card/integration-card.css +169 -0
  74. package/components/integration-card/integration-card.d.ts +63 -0
  75. package/components/integration-card/integration-card.examples.md +41 -0
  76. package/components/integration-card/integration-card.js +17 -0
  77. package/components/integration-card/integration-card.test.js +306 -0
  78. package/components/integration-card/integration-card.yaml +280 -0
  79. package/components/kbd/kbd.css +32 -32
  80. package/components/link/link.css +12 -12
  81. package/components/list/list.css +8 -8
  82. package/components/list-window/class.js +688 -0
  83. package/components/list-window/list-window.a2ui.json +277 -0
  84. package/components/list-window/list-window.css +124 -0
  85. package/components/list-window/list-window.d.ts +84 -0
  86. package/components/list-window/list-window.examples.md +73 -0
  87. package/components/list-window/list-window.js +17 -0
  88. package/components/list-window/list-window.test.js +303 -0
  89. package/components/list-window/list-window.yaml +270 -0
  90. package/components/menu/menu.css +8 -8
  91. package/components/modal/modal.css +43 -43
  92. package/components/nav/nav.css +40 -40
  93. package/components/nav-group/nav-group.css +52 -52
  94. package/components/nav-item/nav-item.css +44 -44
  95. package/components/noodles/noodles.css +31 -31
  96. package/components/option-card/option-card.css +69 -69
  97. package/components/otp-input/otp-input.css +30 -30
  98. package/components/page/page.css +18 -18
  99. package/components/pagination/pagination.css +61 -61
  100. package/components/pane/pane.css +57 -57
  101. package/components/pipeline-status/pipeline-status.css +65 -65
  102. package/components/popover/popover.css +17 -17
  103. package/components/progress/progress.css +23 -23
  104. package/components/progress-row/progress-row.css +17 -17
  105. package/components/radio/radio.css +39 -39
  106. package/components/range/range.css +55 -55
  107. package/components/rating/rating.css +28 -28
  108. package/components/richtext/richtext.css +133 -133
  109. package/components/row/row.css +19 -19
  110. package/components/search/search.css +5 -5
  111. package/components/segment/segment.css +24 -24
  112. package/components/segmented/segmented.css +25 -25
  113. package/components/select/select.css +84 -84
  114. package/components/skeleton/skeleton.css +14 -14
  115. package/components/slider/slider.css +46 -46
  116. package/components/spinner/class.js +69 -0
  117. package/components/spinner/spinner.a2ui.json +197 -0
  118. package/components/spinner/spinner.css +165 -0
  119. package/components/spinner/spinner.d.ts +26 -0
  120. package/components/spinner/spinner.examples.md +26 -0
  121. package/components/spinner/spinner.js +17 -0
  122. package/components/spinner/spinner.test.js +234 -0
  123. package/components/spinner/spinner.yaml +230 -0
  124. package/components/stack/stack.css +11 -11
  125. package/components/stat/stat.css +25 -25
  126. package/components/step-progress/step-progress.css +20 -20
  127. package/components/stepper/stepper.css +29 -29
  128. package/components/stream/stream.css +12 -12
  129. package/components/swatch/swatch.css +68 -68
  130. package/components/swiper/swiper.css +57 -57
  131. package/components/switch/switch.css +52 -52
  132. package/components/table/table.css +162 -162
  133. package/components/table-toolbar/table-toolbar.css +32 -32
  134. package/components/tabs/tabs.css +51 -51
  135. package/components/tag/tag.css +48 -48
  136. package/components/text/text.css +44 -44
  137. package/components/textarea/textarea.css +46 -46
  138. package/components/time-picker/class.js +693 -0
  139. package/components/time-picker/time-picker.a2ui.json +267 -0
  140. package/components/time-picker/time-picker.css +122 -0
  141. package/components/time-picker/time-picker.d.ts +75 -0
  142. package/components/time-picker/time-picker.examples.md +35 -0
  143. package/components/time-picker/time-picker.js +17 -0
  144. package/components/time-picker/time-picker.test.js +287 -0
  145. package/components/time-picker/time-picker.yaml +256 -0
  146. package/components/timeline/timeline.css +50 -50
  147. package/components/toast/toast.css +58 -58
  148. package/components/toggle-group/toggle-group.css +6 -6
  149. package/components/toggle-scheme/toggle-scheme.css +2 -2
  150. package/components/toolbar/toolbar.css +17 -17
  151. package/components/tooltip/tooltip.css +2 -2
  152. package/components/tree/tree.css +37 -37
  153. package/components/upload/upload.css +49 -49
  154. package/dist/web-components.min.css +1 -1
  155. package/dist/web-components.min.js +121 -83
  156. package/package.json +1 -1
  157. package/styles/components.css +8 -0
@@ -0,0 +1,277 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://adiaui.dev/a2ui/v0_9/components/ListWindow.json",
4
+ "title": "ListWindow",
5
+ "description": "Virtualized / windowed list primitive. Renders only the visible slice of a large items[] array (chat threads, feeds, log streams, nav lists, search-result panes) — typically ≤50 DOM rows regardless of the underlying collection size. Composes a `render`-function prop OR a slotted <template> for row materialization; ships a fixed-size fast-path (constant-time index→offset math) and a variable-size measurement fallback. Distinct from <list-ui> (renders every child, preferred for short lists < 50 items) and <table-ui> (tabular data with columns). Use list-window-ui when items.length is large enough that rendering every row would block the main thread or stutter scroll.",
6
+ "type": "object",
7
+ "allOf": [
8
+ {
9
+ "$ref": "common_types.json#/$defs/ComponentCommon"
10
+ },
11
+ {
12
+ "$ref": "common_types.json#/$defs/CatalogComponentCommon"
13
+ }
14
+ ],
15
+ "properties": {
16
+ "items": {
17
+ "description": "The items to virtualize. Required for prop-driven authoring; ignored when data-stream-src is set.",
18
+ "type": "array",
19
+ "default": []
20
+ },
21
+ "component": {
22
+ "const": "ListWindow"
23
+ },
24
+ "direction": {
25
+ "description": "Scroll axis — vertical (default) or horizontal carousel.",
26
+ "type": "string",
27
+ "enum": [
28
+ "vertical",
29
+ "horizontal"
30
+ ],
31
+ "default": "vertical"
32
+ },
33
+ "estimatedSize": {
34
+ "description": "Initial guess for variable-height rows. Used until the first measurement pass refines the offset cache.",
35
+ "type": "number",
36
+ "default": 48
37
+ },
38
+ "itemSize": {
39
+ "description": "Fixed item height in pixels. When > 0, uses the constant-time fast path (avoids per-row measurement).",
40
+ "type": "number",
41
+ "default": 0
42
+ },
43
+ "itemSizeRem": {
44
+ "description": "Fixed item height in rem. Useful for typographic-scale rows that should track the body type.",
45
+ "type": "number",
46
+ "default": 0
47
+ },
48
+ "loading": {
49
+ "description": "Render skeleton rows in the visible window. Sets aria-busy=\"true\" on the host.",
50
+ "type": "boolean",
51
+ "default": false
52
+ },
53
+ "overscan": {
54
+ "description": "Rows to render above + below the visible window. 0–20 is reasonable; > 50 negates the windowing benefit.",
55
+ "type": "number",
56
+ "default": 3
57
+ },
58
+ "pinBottom": {
59
+ "description": "When appending items, keep scroll pinned to the bottom (chat-thread / log-tail pattern).",
60
+ "type": "boolean",
61
+ "default": false
62
+ },
63
+ "startIndex": {
64
+ "description": "Index to scroll to on mount. Useful for restoring scroll position on remount.",
65
+ "type": "number",
66
+ "default": 0
67
+ }
68
+ },
69
+ "required": [
70
+ "component"
71
+ ],
72
+ "unevaluatedProperties": false,
73
+ "x-adiaui": {
74
+ "anti_patterns": [
75
+ {
76
+ "fix": "{ \"component\": \"ListWindow\", \"items\": [/* 10000 items */], \"item-size\": 48 }\n",
77
+ "why": "Rendering 10k items into <list-ui> blows up the DOM and main thread. Scroll\njank, mount lag, and memory pressure all degrade.\n",
78
+ "wrong": "{ \"component\": \"List\", \"items\": [/* 10000 items */] }\n"
79
+ },
80
+ {
81
+ "fix": "Wrap in a container with a height, or set style=\"height:480px\" on the\nListWindow itself.\n",
82
+ "why": "Without a height bound, the scroll container has no viewport, so the windowing\nmath reports \"all items visible\" and the whole list mounts.\n",
83
+ "wrong": "{ \"component\": \"ListWindow\", \"items\": [/* … */] }\n(with no parent height + no item-size + no estimated-size)\n"
84
+ },
85
+ {
86
+ "fix": "{ \"component\": \"ListWindow\", \"overscan\": 5, \"items\": [/* … */] }\n",
87
+ "why": "Overscan=200 materializes 200 rows above + below the viewport. That defeats\nthe entire point of windowing.\n",
88
+ "wrong": "{ \"component\": \"ListWindow\", \"overscan\": 200, \"items\": [/* … */] }\n"
89
+ }
90
+ ],
91
+ "category": "display",
92
+ "composes": [
93
+ "skeleton-ui"
94
+ ],
95
+ "events": {
96
+ "item-click": {
97
+ "description": "Fired when a rendered row is clicked.",
98
+ "detail": {
99
+ "index": {
100
+ "description": "Item index in the full items[] array.",
101
+ "type": "number"
102
+ },
103
+ "item": {
104
+ "description": "The clicked item (full item-shape from items[])."
105
+ }
106
+ }
107
+ },
108
+ "measure": {
109
+ "description": "Fired when a variable-height row is measured. Useful for instrumenting the offset cache.",
110
+ "detail": {
111
+ "height": {
112
+ "description": "Measured row height in pixels.",
113
+ "type": "number"
114
+ },
115
+ "index": {
116
+ "description": "Index of the row that was measured.",
117
+ "type": "number"
118
+ }
119
+ }
120
+ },
121
+ "range-change": {
122
+ "description": "Fired when the visible row range (start/end indices) changes due to scroll.",
123
+ "detail": {
124
+ "items": {
125
+ "description": "The items currently materialized in the window.",
126
+ "type": "array"
127
+ },
128
+ "endIndex": {
129
+ "description": "Last rendered row index (exclusive).",
130
+ "type": "number"
131
+ },
132
+ "startIndex": {
133
+ "description": "First rendered row index.",
134
+ "type": "number"
135
+ }
136
+ }
137
+ },
138
+ "scroll-end": {
139
+ "description": "Fired when the user scrolls to the bottom (within 1 viewport). Use for infinite-load patterns.",
140
+ "detail": {
141
+ "index": {
142
+ "description": "Last visible row index.",
143
+ "type": "number"
144
+ }
145
+ }
146
+ },
147
+ "scroll-start": {
148
+ "description": "Fired when the user scrolls to the top (within 1 viewport). Use for \"load older\" patterns.",
149
+ "detail": {
150
+ "index": {
151
+ "description": "First visible row index.",
152
+ "type": "number"
153
+ }
154
+ }
155
+ }
156
+ },
157
+ "examples": [
158
+ {
159
+ "description": "Virtualized chat-thread message list with declarative <template> rows.",
160
+ "a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"Card\",\n \"children\": [\"thread\"]\n },\n {\n \"id\": \"thread\",\n \"component\": \"ListWindow\",\n \"item-size\": 56,\n \"overscan\": 5,\n \"pin-bottom\": true\n }\n]",
161
+ "name": "chat-thread-list"
162
+ },
163
+ {
164
+ "description": "SSE-streamed JSONL log tail with sticky-bottom pin.",
165
+ "a2ui": "[\n {\n \"id\": \"logs\",\n \"component\": \"ListWindow\",\n \"item-size\": 24,\n \"pin-bottom\": true\n }\n]",
166
+ "name": "log-tail-stream"
167
+ }
168
+ ],
169
+ "keywords": [
170
+ "list-window",
171
+ "virtualized",
172
+ "windowed",
173
+ "virtual-scroll",
174
+ "infinite-scroll",
175
+ "large-list",
176
+ "feed",
177
+ "chat-thread",
178
+ "log-stream",
179
+ "10k-rows"
180
+ ],
181
+ "name": "UIListWindow",
182
+ "related": [
183
+ "List",
184
+ "ListItem",
185
+ "Feed",
186
+ "ChatThread",
187
+ "Table"
188
+ ],
189
+ "slots": {
190
+ "default": {
191
+ "description": "A single <template> element used to clone rows (declarative-template authoring). Mutually exclusive with the render prop."
192
+ },
193
+ "after": {
194
+ "description": "Sticky-bottom content (composer, \"load older\" button)."
195
+ },
196
+ "before": {
197
+ "description": "Sticky-top content (filter chips, summary stat)."
198
+ },
199
+ "empty": {
200
+ "description": "Custom empty-state content when items.length === 0."
201
+ },
202
+ "loading": {
203
+ "description": "Custom skeleton row template; falls back to <skeleton-ui> when omitted."
204
+ }
205
+ },
206
+ "states": [
207
+ {
208
+ "description": "Default — rendering and reconciling normally.",
209
+ "name": "idle"
210
+ },
211
+ {
212
+ "description": "Skeleton rows; data fetch in flight.",
213
+ "attribute": "loading",
214
+ "name": "loading"
215
+ },
216
+ {
217
+ "description": "items.length === 0.",
218
+ "attribute": "empty",
219
+ "name": "empty"
220
+ },
221
+ {
222
+ "description": "First-mount measurement pass on variable-height rows; suppresses scroll-end events.",
223
+ "attribute": "measuring",
224
+ "name": "measuring"
225
+ },
226
+ {
227
+ "description": "Pointer events blocked.",
228
+ "attribute": "disabled",
229
+ "name": "disabled"
230
+ }
231
+ ],
232
+ "status": "stable",
233
+ "synonyms": {
234
+ "infinite-scroll": [
235
+ "virtualized",
236
+ "virtual-scroll",
237
+ "list-window"
238
+ ],
239
+ "large-list": [
240
+ "virtualized",
241
+ "list-window"
242
+ ],
243
+ "virtual-scroll": [
244
+ "virtualized",
245
+ "windowed",
246
+ "list-window"
247
+ ],
248
+ "virtualized": [
249
+ "virtual-scroll",
250
+ "windowed",
251
+ "list-window"
252
+ ],
253
+ "windowed": [
254
+ "virtualized",
255
+ "virtual-scroll",
256
+ "list-window"
257
+ ]
258
+ },
259
+ "tag": "list-window-ui",
260
+ "tokens": {
261
+ "--list-window-bg": {
262
+ "description": "Host background (defaults to --a-bg)."
263
+ },
264
+ "--list-window-overscan-bg": {
265
+ "description": "Visible buffer rows background — transparent by default."
266
+ },
267
+ "--list-window-row-gap": {
268
+ "description": "Row spacing in the visible window (defaults to --a-space-1)."
269
+ },
270
+ "--list-window-sentinel-size": {
271
+ "description": "Top + bottom IntersectionObserver-target sentinel size (defaults to --a-space-2)."
272
+ }
273
+ },
274
+ "traits": [],
275
+ "version": 1
276
+ }
277
+ }
@@ -0,0 +1,124 @@
1
+ @scope (list-window-ui) {
2
+ /* ── Block 1 — TOKENS (zero-specificity, exposable for theming) ── */
3
+ :where(:scope) {
4
+ --list-window-bg-default: var(--a-bg);
5
+ --list-window-row-gap-default: var(--a-space-1);
6
+ --list-window-overscan-bg-default: transparent;
7
+ --list-window-sentinel-size-default: var(--a-space-2);
8
+ }
9
+
10
+ /* ── Block 2 — BASE styles consuming the tokens above ── */
11
+ :scope {
12
+ box-sizing: border-box;
13
+ display: block;
14
+ position: relative;
15
+ overflow: auto;
16
+ contain: strict;
17
+ background: var(--list-window-bg, var(--list-window-bg-default));
18
+ isolation: isolate;
19
+ outline: none;
20
+ }
21
+
22
+ :scope:focus-visible {
23
+ outline: 2px solid var(--a-accent-strong);
24
+ outline-offset: -2px;
25
+ }
26
+
27
+ /* Phantom spacer reserves total scroll height = sum of all row sizes.
28
+ Width pinned to 1px so it never contributes horizontal scroll in
29
+ vertical mode (and vice versa). */
30
+ :scope > [data-phantom] {
31
+ position: relative;
32
+ pointer-events: none;
33
+ }
34
+
35
+ /* The visible-rows container is absolutely positioned and translated
36
+ to align with the current scroll window. flex-column lays rows
37
+ vertically; gap controls row spacing without margin-collapse
38
+ surprises. */
39
+ :scope > [data-window] {
40
+ position: absolute;
41
+ top: 0;
42
+ inset-inline-start: 0;
43
+ inset-inline-end: 0;
44
+ display: flex;
45
+ flex-direction: column;
46
+ gap: var(--list-window-row-gap, var(--list-window-row-gap-default));
47
+ will-change: transform;
48
+ }
49
+
50
+ /* Each row is its own block — height defined by content (variable
51
+ mode) or the item-size prop (fixed mode). min-width:0 prevents
52
+ overflow:hidden flex children from blowing out the host. */
53
+ :scope > [data-window] > [data-row] {
54
+ flex: 0 0 auto;
55
+ min-width: 0;
56
+ }
57
+
58
+ /* Sentinel nodes are tiny invisible markers tracked by the host's
59
+ IntersectionObserver. The top sentinel sits at the top of the
60
+ phantom; the bottom sentinel at the bottom. They emit
61
+ scroll-start / scroll-end when intersecting the host viewport. */
62
+ :scope > [data-sentinel] {
63
+ position: absolute;
64
+ inset-inline-start: 0;
65
+ inset-inline-end: 0;
66
+ height: var(--list-window-sentinel-size, var(--list-window-sentinel-size-default));
67
+ pointer-events: none;
68
+ }
69
+ :scope > [data-sentinel="top"] { top: 0; }
70
+ :scope > [data-sentinel="bottom"] { bottom: 0; }
71
+
72
+ /* Sticky slots — sit at the top/bottom of the scroll container so they
73
+ stay visible while the window scrolls. */
74
+ :scope > [slot="before"] {
75
+ position: sticky;
76
+ top: 0;
77
+ z-index: 1;
78
+ background: var(--list-window-bg, var(--list-window-bg-default));
79
+ }
80
+ :scope > [slot="after"] {
81
+ position: sticky;
82
+ bottom: 0;
83
+ z-index: 1;
84
+ background: var(--list-window-bg, var(--list-window-bg-default));
85
+ }
86
+
87
+ /* ── Horizontal mode ── */
88
+ :scope[direction="horizontal"] > [data-window] {
89
+ flex-direction: row;
90
+ }
91
+ :scope[direction="horizontal"] > [data-sentinel] {
92
+ width: var(--list-window-sentinel-size, var(--list-window-sentinel-size-default));
93
+ height: auto;
94
+ top: 0;
95
+ bottom: 0;
96
+ inset-inline-start: auto;
97
+ inset-inline-end: auto;
98
+ }
99
+ :scope[direction="horizontal"] > [data-sentinel="top"] { inset-inline-start: 0; }
100
+ :scope[direction="horizontal"] > [data-sentinel="bottom"] { inset-inline-end: 0; }
101
+
102
+ /* ── States ── */
103
+ :scope[empty] > [data-window] {
104
+ display: none;
105
+ }
106
+ :scope[empty] > [slot="empty"] {
107
+ display: block;
108
+ }
109
+
110
+ :scope:not([empty]) > [slot="empty"] {
111
+ display: none;
112
+ }
113
+
114
+ :scope[measuring] > [data-window] {
115
+ /* Suppress row-gap during initial measurement pass to avoid
116
+ layout shift between estimated and measured offsets. */
117
+ gap: 0;
118
+ }
119
+
120
+ :scope[disabled] {
121
+ pointer-events: none;
122
+ opacity: 0.5;
123
+ }
124
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * `<list-window-ui>` — Virtualized / windowed list primitive. Renders only the visible slice of a large items[] array (chat threads, feeds, log streams, nav lists, search-result panes) — typically ≤50 DOM rows regardless of the underlying collection size. Composes a `render`-function prop OR a slotted <template> for row materialization; ships a fixed-size fast-path (constant-time index→offset math) and a variable-size measurement fallback. Distinct from <list-ui> (renders every child, preferred for short lists < 50 items) and <table-ui> (tabular data with columns). Use list-window-ui when items.length is large enough that rendering every row would block the main thread or stutter scroll.
3
+ *
4
+ * @see https://ui-kit.exe.xyz/site/components/list-window
5
+ *
6
+ * Type declarations generated by scripts/build/dts-codegen.mjs from
7
+ * the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
8
+ * run `npm run build:components`, then `npm run codegen:dts` to
9
+ * regenerate; or hand-author this file fully if rich event types are
10
+ * needed beyond what the yaml `events:` block can express.
11
+ */
12
+
13
+ import { UIElement } from '../../core/element.js';
14
+
15
+ export interface ListWindowItemClickEventDetail {
16
+ /** Item index in the full items[] array. */
17
+ index: number;
18
+ /** The clicked item (full item-shape from items[]). */
19
+ item: string;
20
+ }
21
+
22
+ export type ListWindowItemClickEvent = CustomEvent<ListWindowItemClickEventDetail>;
23
+ export interface ListWindowMeasureEventDetail {
24
+ /** Measured row height in pixels. */
25
+ height: number;
26
+ /** Index of the row that was measured. */
27
+ index: number;
28
+ }
29
+
30
+ export type ListWindowMeasureEvent = CustomEvent<ListWindowMeasureEventDetail>;
31
+ export interface ListWindowRangeChangeEventDetail {
32
+ /** The items currently materialized in the window. */
33
+ items: unknown[];
34
+ /** Last rendered row index (exclusive). */
35
+ endIndex: number;
36
+ /** First rendered row index. */
37
+ startIndex: number;
38
+ }
39
+
40
+ export type ListWindowRangeChangeEvent = CustomEvent<ListWindowRangeChangeEventDetail>;
41
+ export interface ListWindowScrollEndEventDetail {
42
+ /** Last visible row index. */
43
+ index: number;
44
+ }
45
+
46
+ export type ListWindowScrollEndEvent = CustomEvent<ListWindowScrollEndEventDetail>;
47
+ export interface ListWindowScrollStartEventDetail {
48
+ /** First visible row index. */
49
+ index: number;
50
+ }
51
+
52
+ export type ListWindowScrollStartEvent = CustomEvent<ListWindowScrollStartEventDetail>;
53
+
54
+ export class UIListWindow extends UIElement {
55
+ /** The items to virtualize. Required for prop-driven authoring; ignored when data-stream-src is set. */
56
+ items: unknown[];
57
+ /** Scroll axis — vertical (default) or horizontal carousel. */
58
+ direction: 'vertical' | 'horizontal';
59
+ /** Initial guess for variable-height rows. Used until the first measurement pass refines the offset cache. */
60
+ estimatedSize: number;
61
+ /** Fixed item height in pixels. When > 0, uses the constant-time fast path (avoids per-row measurement). */
62
+ itemSize: number;
63
+ /** Fixed item height in rem. Useful for typographic-scale rows that should track the body type. */
64
+ itemSizeRem: number;
65
+ /** Render skeleton rows in the visible window. Sets aria-busy="true" on the host. */
66
+ loading: boolean;
67
+ /** Rows to render above + below the visible window. 0–20 is reasonable; > 50 negates the windowing benefit. */
68
+ overscan: number;
69
+ /** When appending items, keep scroll pinned to the bottom (chat-thread / log-tail pattern). */
70
+ pinBottom: boolean;
71
+ /** Index to scroll to on mount. Useful for restoring scroll position on remount. */
72
+ startIndex: number;
73
+
74
+ addEventListener<K extends keyof HTMLElementEventMap>(
75
+ type: K,
76
+ listener: (this: UIListWindow, ev: HTMLElementEventMap[K]) => unknown,
77
+ options?: boolean | AddEventListenerOptions,
78
+ ): void;
79
+ addEventListener(type: 'item-click', listener: (ev: ListWindowItemClickEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
80
+ addEventListener(type: 'measure', listener: (ev: ListWindowMeasureEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
81
+ addEventListener(type: 'range-change', listener: (ev: ListWindowRangeChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
82
+ addEventListener(type: 'scroll-end', listener: (ev: ListWindowScrollEndEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
83
+ addEventListener(type: 'scroll-start', listener: (ev: ListWindowScrollStartEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
84
+ }
@@ -0,0 +1,73 @@
1
+ # list-window — Examples
2
+
3
+ ## basic — fixed-size rows
4
+
5
+ ```html
6
+ <list-window-ui item-size="40" overscan="3" style="height:320px">
7
+ </list-window-ui>
8
+ <script>
9
+ const el = document.querySelector('list-window-ui');
10
+ el.renderRow = (item) => {
11
+ const row = document.createElement('list-item-ui');
12
+ row.setAttribute('text', item.text);
13
+ return row;
14
+ };
15
+ el.items = Array.from({ length: 1000 }, (_, i) => ({
16
+ id: `b-${i}`,
17
+ text: `Row ${i + 1}`,
18
+ }));
19
+ </script>
20
+ ```
21
+
22
+ ## 10,000 rows
23
+
24
+ ```html
25
+ <list-window-ui item-size="32" overscan="5" style="height:320px">
26
+ </list-window-ui>
27
+ <script>
28
+ const el = document.querySelector('list-window-ui');
29
+ el.items = Array.from({ length: 10_000 }, (_, i) => ({
30
+ id: `lg-${i}`,
31
+ text: `Log line ${i}`,
32
+ }));
33
+ </script>
34
+ ```
35
+
36
+ ## pin-bottom — chat-thread / log-tail
37
+
38
+ ```html
39
+ <list-window-ui item-size="28" pin-bottom style="height:240px">
40
+ </list-window-ui>
41
+ <script>
42
+ const el = document.querySelector('list-window-ui');
43
+ el.items = initialMessages;
44
+ // Appending keeps scroll pinned at the bottom.
45
+ el.items = [...el.items, ...newMessages];
46
+ </script>
47
+ ```
48
+
49
+ ## variable-height — feed cards
50
+
51
+ ```html
52
+ <list-window-ui estimated-size="120" overscan="2" style="height:360px">
53
+ </list-window-ui>
54
+ <script>
55
+ const el = document.querySelector('list-window-ui');
56
+ el.renderRow = (item) => buildCardElement(item);
57
+ el.items = cards;
58
+ </script>
59
+ ```
60
+
61
+ ## loading state
62
+
63
+ ```html
64
+ <list-window-ui item-size="48" loading style="height:240px"></list-window-ui>
65
+ ```
66
+
67
+ ## empty state
68
+
69
+ ```html
70
+ <list-window-ui item-size="40" style="height:160px">
71
+ <div slot="empty">No items to display.</div>
72
+ </list-window-ui>
73
+ ```
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `<list-window-ui>` — auto-registers the tag on import.
3
+ *
4
+ * For non-side-effect class import (test isolation, tag override), use
5
+ * the `class` subpath:
6
+ *
7
+ * import { UIListWindow } from '@adia-ai/web-components/components/list-window/class';
8
+ *
9
+ * @see ../../USAGE.md#registration--auto-vs-explicit
10
+ */
11
+
12
+ import { defineIfFree } from '../../core/register.js';
13
+ import { UIListWindow } from './class.js';
14
+
15
+ defineIfFree('list-window-ui', UIListWindow);
16
+
17
+ export { UIListWindow };