@multicomma/arkcn 0.1.2 → 0.1.3

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 (148) hide show
  1. package/package.json +1 -1
  2. package/registry/index.json +1 -1
  3. package/registry/items/accordion.json +302 -0
  4. package/registry/items/activity-feed.json +408 -0
  5. package/registry/items/alert-dialog.json +676 -0
  6. package/registry/items/alert.json +113 -0
  7. package/registry/items/angle-slider.json +378 -0
  8. package/registry/items/app-shell.json +2654 -1
  9. package/registry/items/aspect-ratio.json +36 -0
  10. package/registry/items/attachment.json +299 -1
  11. package/registry/items/avatar.json +213 -0
  12. package/registry/items/badge.json +44 -0
  13. package/registry/items/breadcrumb.json +191 -0
  14. package/registry/items/bubble.json +146 -0
  15. package/registry/items/button-group.json +110 -0
  16. package/registry/items/button.json +52 -0
  17. package/registry/items/calendar.json +499 -1
  18. package/registry/items/canvas.json +446 -1
  19. package/registry/items/card.json +192 -0
  20. package/registry/items/carousel.json +208 -1
  21. package/registry/items/cascader.json +797 -1
  22. package/registry/items/chart.json +1764 -1
  23. package/registry/items/chat.json +993 -0
  24. package/registry/items/checkbox.json +503 -0
  25. package/registry/items/clipboard.json +308 -0
  26. package/registry/items/collapsible.json +226 -0
  27. package/registry/items/color-picker.json +1261 -0
  28. package/registry/items/combobox.json +1123 -1
  29. package/registry/items/command.json +837 -1
  30. package/registry/items/comment-thread.json +952 -1
  31. package/registry/items/context-menu.json +1344 -1
  32. package/registry/items/data-grid.json +449 -1
  33. package/registry/items/data-table.json +1310 -1
  34. package/registry/items/date-input.json +476 -0
  35. package/registry/items/dialog.json +592 -0
  36. package/registry/items/direction.json +29 -1
  37. package/registry/items/drawer.json +797 -0
  38. package/registry/items/dropdown-menu.json +1311 -1
  39. package/registry/items/editable.json +503 -0
  40. package/registry/items/empty.json +166 -0
  41. package/registry/items/field.json +286 -1
  42. package/registry/items/file-upload.json +638 -0
  43. package/registry/items/floating-panel.json +720 -0
  44. package/registry/items/floating-toolbar.json +43 -0
  45. package/registry/items/format.json +109 -0
  46. package/registry/items/gantt.json +773 -1
  47. package/registry/items/hooks/use-mobile.json +1 -0
  48. package/registry/items/hotkeys.json +99 -1
  49. package/registry/items/hover-card.json +357 -0
  50. package/registry/items/image-cropper.json +431 -0
  51. package/registry/items/input-group.json +189 -0
  52. package/registry/items/input-otp.json +181 -0
  53. package/registry/items/input.json +28 -0
  54. package/registry/items/item.json +308 -0
  55. package/registry/items/json-tree-view.json +336 -0
  56. package/registry/items/kanban.json +624 -1
  57. package/registry/items/kbd.json +54 -0
  58. package/registry/items/label.json +28 -0
  59. package/registry/items/lib/controllable.json +1 -0
  60. package/registry/items/lib/data-table-adapter.json +1 -0
  61. package/registry/items/lib/drag-preview.json +1 -0
  62. package/registry/items/lib/form.json +1 -0
  63. package/registry/items/lib/history.json +1 -0
  64. package/registry/items/lib/row-layout.json +1 -0
  65. package/registry/items/lib/table.json +1 -0
  66. package/registry/items/lib/time.json +1 -0
  67. package/registry/items/lib/utils.json +1 -0
  68. package/registry/items/live-region.json +43 -0
  69. package/registry/items/marker.json +96 -0
  70. package/registry/items/marquee.json +300 -0
  71. package/registry/items/menubar.json +1282 -1
  72. package/registry/items/message.json +166 -0
  73. package/registry/items/navigation-menu.json +561 -1
  74. package/registry/items/node-graph.json +1037 -1
  75. package/registry/items/number-input.json +519 -0
  76. package/registry/items/pagination.json +244 -1
  77. package/registry/items/password-input.json +311 -0
  78. package/registry/items/popconfirm.json +566 -0
  79. package/registry/items/popover.json +615 -0
  80. package/registry/items/progress.json +516 -0
  81. package/registry/items/qr-code.json +249 -0
  82. package/registry/items/query-builder.json +1368 -1
  83. package/registry/items/radio-group.json +362 -0
  84. package/registry/items/rating-group.json +304 -0
  85. package/registry/items/resizable.json +242 -1
  86. package/registry/items/rich-text-editor.json +934 -1
  87. package/registry/items/scheduler.json +1013 -1
  88. package/registry/items/scroll-area.json +227 -0
  89. package/registry/items/segment-group.json +363 -0
  90. package/registry/items/select.json +956 -0
  91. package/registry/items/separator.json +45 -1
  92. package/registry/items/sheet.json +592 -0
  93. package/registry/items/sidebar.json +785 -1
  94. package/registry/items/signature-pad.json +330 -0
  95. package/registry/items/skeleton.json +28 -0
  96. package/registry/items/slider.json +799 -0
  97. package/registry/items/sonner.json +135 -0
  98. package/registry/items/spinner.json +28 -0
  99. package/registry/items/steps.json +483 -0
  100. package/registry/items/swap.json +107 -0
  101. package/registry/items/switch.json +362 -0
  102. package/registry/items/table.json +210 -0
  103. package/registry/items/tabs.json +326 -0
  104. package/registry/items/tags-input.json +640 -0
  105. package/registry/items/textarea.json +28 -0
  106. package/registry/items/timer.json +289 -0
  107. package/registry/items/toc.json +372 -0
  108. package/registry/items/toggle-group.json +214 -1
  109. package/registry/items/toggle.json +124 -0
  110. package/registry/items/tooltip.json +417 -0
  111. package/registry/items/tour.json +498 -0
  112. package/registry/items/transfer-list.json +609 -1
  113. package/registry/items/tree-select.json +742 -1
  114. package/registry/items/tree-view.json +794 -0
  115. package/registry/items/virtual-list.json +214 -1
  116. package/src/components/ui/app-shell.tsx +11 -0
  117. package/src/components/ui/attachment.tsx +1 -0
  118. package/src/components/ui/calendar.tsx +1 -0
  119. package/src/components/ui/canvas.tsx +8 -0
  120. package/src/components/ui/carousel.tsx +3 -0
  121. package/src/components/ui/cascader.tsx +1 -0
  122. package/src/components/ui/chart.tsx +10 -0
  123. package/src/components/ui/combobox.tsx +3 -0
  124. package/src/components/ui/command.tsx +1 -0
  125. package/src/components/ui/comment-thread.tsx +11 -0
  126. package/src/components/ui/context-menu.tsx +6 -0
  127. package/src/components/ui/data-grid.tsx +8 -0
  128. package/src/components/ui/data-table.tsx +6 -0
  129. package/src/components/ui/direction.tsx +2 -0
  130. package/src/components/ui/dropdown-menu.tsx +6 -0
  131. package/src/components/ui/field.tsx +1 -0
  132. package/src/components/ui/gantt.tsx +15 -0
  133. package/src/components/ui/hotkeys.tsx +3 -0
  134. package/src/components/ui/kanban.tsx +3 -0
  135. package/src/components/ui/menubar.tsx +6 -0
  136. package/src/components/ui/navigation-menu.tsx +1 -0
  137. package/src/components/ui/node-graph.tsx +23 -0
  138. package/src/components/ui/pagination.tsx +1 -0
  139. package/src/components/ui/query-builder.tsx +7 -0
  140. package/src/components/ui/resizable.tsx +1 -0
  141. package/src/components/ui/rich-text-editor.tsx +54 -0
  142. package/src/components/ui/scheduler.tsx +26 -0
  143. package/src/components/ui/separator.tsx +1 -0
  144. package/src/components/ui/sidebar.tsx +5 -0
  145. package/src/components/ui/toggle-group.tsx +3 -0
  146. package/src/components/ui/transfer-list.tsx +11 -0
  147. package/src/components/ui/tree-select.tsx +5 -0
  148. package/src/components/ui/virtual-list.tsx +7 -0
@@ -11,6 +11,219 @@
11
11
  "VirtualListViewport",
12
12
  "useVirtualList"
13
13
  ],
14
+ "parts": [
15
+ {
16
+ "name": "VirtualList",
17
+ "props": [
18
+ {
19
+ "name": "children",
20
+ "type": "ReactNode",
21
+ "required": true,
22
+ "description": "Content rendered inside the part.",
23
+ "from": "arkcn"
24
+ },
25
+ {
26
+ "name": "count",
27
+ "type": "number",
28
+ "required": true,
29
+ "description": "Total number of rows.",
30
+ "from": "arkcn"
31
+ },
32
+ {
33
+ "name": "estimateSize",
34
+ "type": "number | ((index: number) => number)",
35
+ "required": false,
36
+ "default": "36",
37
+ "description": "Row height before measurement (a number, or per index).",
38
+ "from": "arkcn"
39
+ },
40
+ {
41
+ "name": "gap",
42
+ "type": "number",
43
+ "required": false,
44
+ "default": "0",
45
+ "description": "Space between rows in px.",
46
+ "from": "arkcn"
47
+ },
48
+ {
49
+ "name": "getItemKey",
50
+ "type": "((index: number) => Key)",
51
+ "required": false,
52
+ "default": "(index) => index",
53
+ "description": "Stable React key for a row index.",
54
+ "from": "arkcn"
55
+ },
56
+ {
57
+ "name": "onRangeChange",
58
+ "type": "((range: { start: number; end: number; } | null) => void)",
59
+ "required": false,
60
+ "description": "Fires when the rendered window changes.",
61
+ "from": "arkcn"
62
+ },
63
+ {
64
+ "name": "overscan",
65
+ "type": "number",
66
+ "required": false,
67
+ "default": "6",
68
+ "description": "Rows rendered beyond the visible window on each side.",
69
+ "from": "arkcn"
70
+ },
71
+ {
72
+ "name": "paddingEnd",
73
+ "type": "number",
74
+ "required": false,
75
+ "default": "0",
76
+ "description": "Space after the last row in px.",
77
+ "from": "arkcn"
78
+ },
79
+ {
80
+ "name": "paddingStart",
81
+ "type": "number",
82
+ "required": false,
83
+ "default": "0",
84
+ "description": "Space before the first row in px.",
85
+ "from": "arkcn"
86
+ },
87
+ {
88
+ "name": "className",
89
+ "type": "string",
90
+ "required": false,
91
+ "description": "Extra classes merged into the part's own classes with `cn`.",
92
+ "from": "@types/react"
93
+ },
94
+ {
95
+ "name": "style",
96
+ "type": "CSSProperties",
97
+ "required": false,
98
+ "description": "Inline styles applied to the rendered element.",
99
+ "from": "@types/react"
100
+ }
101
+ ]
102
+ },
103
+ {
104
+ "name": "VirtualListContent",
105
+ "props": [
106
+ {
107
+ "name": "children",
108
+ "type": "ReactNode",
109
+ "required": false,
110
+ "description": "Content rendered inside the part.",
111
+ "from": "@types/react"
112
+ },
113
+ {
114
+ "name": "className",
115
+ "type": "string",
116
+ "required": false,
117
+ "description": "Extra classes merged into the part's own classes with `cn`.",
118
+ "from": "@types/react"
119
+ },
120
+ {
121
+ "name": "style",
122
+ "type": "CSSProperties",
123
+ "required": false,
124
+ "description": "Inline styles applied to the rendered element.",
125
+ "from": "@types/react"
126
+ }
127
+ ]
128
+ },
129
+ {
130
+ "name": "VirtualListEmpty",
131
+ "props": [
132
+ {
133
+ "name": "children",
134
+ "type": "ReactNode",
135
+ "required": false,
136
+ "description": "Content rendered inside the part.",
137
+ "from": "@types/react"
138
+ },
139
+ {
140
+ "name": "className",
141
+ "type": "string",
142
+ "required": false,
143
+ "description": "Extra classes merged into the part's own classes with `cn`.",
144
+ "from": "@types/react"
145
+ },
146
+ {
147
+ "name": "style",
148
+ "type": "CSSProperties",
149
+ "required": false,
150
+ "description": "Inline styles applied to the rendered element.",
151
+ "from": "@types/react"
152
+ }
153
+ ]
154
+ },
155
+ {
156
+ "name": "VirtualListItem",
157
+ "props": [
158
+ {
159
+ "name": "index",
160
+ "type": "number",
161
+ "required": true,
162
+ "description": "Position of the item in its list.",
163
+ "from": "arkcn"
164
+ },
165
+ {
166
+ "name": "children",
167
+ "type": "ReactNode",
168
+ "required": false,
169
+ "description": "Content rendered inside the part.",
170
+ "from": "@types/react"
171
+ },
172
+ {
173
+ "name": "className",
174
+ "type": "string",
175
+ "required": false,
176
+ "description": "Extra classes merged into the part's own classes with `cn`.",
177
+ "from": "@types/react"
178
+ },
179
+ {
180
+ "name": "style",
181
+ "type": "CSSProperties",
182
+ "required": false,
183
+ "description": "Inline styles applied to the rendered element.",
184
+ "from": "@types/react"
185
+ }
186
+ ]
187
+ },
188
+ {
189
+ "name": "VirtualListItems",
190
+ "props": [
191
+ {
192
+ "name": "children",
193
+ "type": "(item: VirtualItem) => ReactNode",
194
+ "required": true,
195
+ "description": "Content rendered inside the part.",
196
+ "from": "arkcn"
197
+ }
198
+ ]
199
+ },
200
+ {
201
+ "name": "VirtualListViewport",
202
+ "props": [
203
+ {
204
+ "name": "children",
205
+ "type": "ReactNode",
206
+ "required": false,
207
+ "description": "Content rendered inside the part.",
208
+ "from": "@types/react"
209
+ },
210
+ {
211
+ "name": "className",
212
+ "type": "string",
213
+ "required": false,
214
+ "description": "Extra classes merged into the part's own classes with `cn`.",
215
+ "from": "@types/react"
216
+ },
217
+ {
218
+ "name": "style",
219
+ "type": "CSSProperties",
220
+ "required": false,
221
+ "description": "Inline styles applied to the rendered element.",
222
+ "from": "@types/react"
223
+ }
224
+ ]
225
+ }
226
+ ],
14
227
  "dependencies": {},
15
228
  "registryDependencies": [
16
229
  "lib/utils"
@@ -18,7 +231,7 @@
18
231
  "files": [
19
232
  {
20
233
  "path": "components/ui/virtual-list.tsx",
21
- "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { cn } from \"@/lib/utils\"\n\n/* -------------------------------- context -------------------------------- */\n\ntype VirtualItem = {\n index: number\n key: React.Key\n /** Offset from the top of the content, in px. */\n start: number\n size: number\n end: number\n /** False until the row's real height has been measured. */\n measured: boolean\n}\n\ntype ScrollAlign = \"auto\" | \"start\" | \"center\" | \"end\"\n\ntype VirtualListContextValue = {\n count: number\n items: VirtualItem[]\n totalSize: number\n /** First and last rendered index (overscan included), or null when empty. */\n range: { start: number; end: number } | null\n measure: (index: number, size: number) => void\n scrollToIndex: (index: number, options?: { align?: ScrollAlign; behavior?: ScrollBehavior }) => void\n scrollToOffset: (offset: number, behavior?: ScrollBehavior) => void\n viewportRef: React.RefObject<HTMLDivElement | null>\n getItemKey: (index: number) => React.Key\n}\n\nconst VirtualListContext = React.createContext<VirtualListContextValue | null>(null)\n\nfunction useVirtualList() {\n const ctx = React.useContext(VirtualListContext)\n if (!ctx) throw new Error(\"VirtualList parts must be used inside <VirtualList>\")\n return ctx\n}\n\n/* -------------------------------- helpers -------------------------------- */\n\n/** Largest index whose start is <= offset. */\nfunction findIndex(starts: Float64Array, count: number, offset: number) {\n let lo = 0\n let hi = count - 1\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1\n if (starts[mid] <= offset) lo = mid\n else hi = mid - 1\n }\n return lo\n}\n\n/* ---------------------------------- root --------------------------------- */\n\ntype VirtualListProps = Omit<React.ComponentProps<\"div\">, \"children\"> & {\n count: number\n /** Row height before measurement (a number, or per index). */\n estimateSize?: number | ((index: number) => number)\n /** Rows rendered beyond the visible window on each side. */\n overscan?: number\n gap?: number\n paddingStart?: number\n paddingEnd?: number\n getItemKey?: (index: number) => React.Key\n /** Fires when the rendered window changes. */\n onRangeChange?: (range: { start: number; end: number } | null) => void\n children: React.ReactNode\n}\n\nfunction VirtualList({\n count,\n estimateSize = 36,\n overscan = 6,\n gap = 0,\n paddingStart = 0,\n paddingEnd = 0,\n getItemKey = (index) => index,\n onRangeChange,\n className,\n children,\n ...props\n}: VirtualListProps) {\n const viewportRef = React.useRef<HTMLDivElement | null>(null)\n const sizes = React.useRef(new Map<number, number>())\n const [version, setVersion] = React.useState(0)\n const [scrollTop, setScrollTop] = React.useState(0)\n const [viewportSize, setViewportSize] = React.useState(0)\n const estimate = React.useCallback(\n (index: number) => (typeof estimateSize === \"function\" ? estimateSize(index) : estimateSize),\n [estimateSize]\n )\n\n // Prefix sums of row starts; recomputed when a measurement or the count changes.\n const layout = React.useMemo(() => {\n const starts = new Float64Array(count)\n let offset = paddingStart\n for (let i = 0; i < count; i++) {\n starts[i] = offset\n offset += (sizes.current.get(i) ?? estimate(i)) + (i < count - 1 ? gap : 0)\n }\n return { starts, totalSize: offset + paddingEnd }\n // `version` is the measurement tick.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [count, estimate, gap, paddingStart, paddingEnd, version])\n\n const range = React.useMemo(() => {\n if (count === 0 || viewportSize === 0) return count === 0 ? null : { start: 0, end: Math.min(count - 1, overscan) }\n const first = findIndex(layout.starts, count, scrollTop)\n const last = findIndex(layout.starts, count, scrollTop + viewportSize)\n return { start: Math.max(0, first - overscan), end: Math.min(count - 1, last + overscan) }\n }, [count, viewportSize, scrollTop, layout, overscan])\n\n const items = React.useMemo(() => {\n if (!range) return []\n const out: VirtualItem[] = []\n for (let i = range.start; i <= range.end; i++) {\n const measured = sizes.current.has(i)\n const size = sizes.current.get(i) ?? estimate(i)\n out.push({ index: i, key: getItemKey(i), start: layout.starts[i], size, end: layout.starts[i] + size, measured })\n }\n return out\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [range, layout, estimate, getItemKey, version])\n\n const onRangeRef = React.useRef(onRangeChange)\n onRangeRef.current = onRangeChange\n const lastRange = React.useRef<string>(\"\")\n React.useEffect(() => {\n const key = range ? `${range.start}-${range.end}` : \"none\"\n if (key === lastRange.current) return\n lastRange.current = key\n onRangeRef.current?.(range)\n }, [range])\n\n // Drop stale measurements when the list shrinks.\n React.useEffect(() => {\n let dirty = false\n for (const index of sizes.current.keys()) {\n if (index >= count) {\n sizes.current.delete(index)\n dirty = true\n }\n }\n if (dirty) setVersion((v) => v + 1)\n }, [count])\n\n const measure = React.useCallback((index: number, size: number) => {\n const prev = sizes.current.get(index)\n if (prev !== undefined && Math.abs(prev - size) < 0.5) return\n sizes.current.set(index, size)\n setVersion((v) => v + 1)\n }, [])\n\n React.useLayoutEffect(() => {\n const el = viewportRef.current\n if (!el) return\n const update = () => setViewportSize(el.clientHeight)\n update()\n const ro = new ResizeObserver(update)\n ro.observe(el)\n return () => ro.disconnect()\n }, [])\n\n const scrollToOffset = React.useCallback((offset: number, behavior: ScrollBehavior = \"auto\") => {\n viewportRef.current?.scrollTo({ top: offset, behavior })\n }, [])\n\n const pending = React.useRef<{ index: number; align: ScrollAlign; tries: number } | null>(null)\n const offsetFor = React.useCallback(\n (index: number, align: ScrollAlign) => {\n const el = viewportRef.current\n if (!el) return 0\n const start = layout.starts[index] ?? 0\n const size = sizes.current.get(index) ?? estimate(index)\n const height = el.clientHeight\n let resolved = align\n if (align === \"auto\") {\n if (start < el.scrollTop) resolved = \"start\"\n else if (start + size > el.scrollTop + height) resolved = \"end\"\n else return el.scrollTop\n }\n if (resolved === \"start\") return start\n if (resolved === \"end\") return start + size - height\n return start + size / 2 - height / 2\n },\n [layout, estimate]\n )\n\n const scrollToIndex = React.useCallback(\n (index: number, options?: { align?: ScrollAlign; behavior?: ScrollBehavior }) => {\n const clamped = Math.max(0, Math.min(count - 1, index))\n const align = options?.align ?? \"auto\"\n pending.current = { index: clamped, align, tries: 0 }\n scrollToOffset(offsetFor(clamped, align), options?.behavior)\n },\n [count, offsetFor, scrollToOffset]\n )\n\n // Estimated rows shift as they get measured: re-aim at the target a few times.\n React.useLayoutEffect(() => {\n const p = pending.current\n const el = viewportRef.current\n if (!p || !el) return\n if (p.tries >= 4 || !sizes.current.has(p.index)) {\n if (p.tries >= 4) pending.current = null\n return\n }\n const target = offsetFor(p.index, p.align === \"auto\" ? \"start\" : p.align)\n if (Math.abs(el.scrollTop - target) > 1) {\n p.tries += 1\n el.scrollTop = target\n } else pending.current = null\n })\n\n const ctx: VirtualListContextValue = {\n count,\n items,\n totalSize: layout.totalSize,\n range,\n measure,\n scrollToIndex,\n scrollToOffset,\n viewportRef,\n getItemKey,\n }\n\n return (\n <VirtualListContext.Provider value={ctx}>\n <VirtualListScroll onScroll={setScrollTop} />\n <div data-slot=\"virtual-list\" className={cn(\"flex min-h-0 flex-col\", className)} {...props}>\n {children}\n </div>\n </VirtualListContext.Provider>\n )\n}\n\n/** Attaches the scroll listener once the viewport mounts (kept out of the root's render). */\nfunction VirtualListScroll({ onScroll }: { onScroll: (top: number) => void }) {\n const ctx = useVirtualList()\n React.useEffect(() => {\n const el = ctx.viewportRef.current\n if (!el) return\n const handler = () => onScroll(el.scrollTop)\n el.addEventListener(\"scroll\", handler, { passive: true })\n return () => el.removeEventListener(\"scroll\", handler)\n }, [ctx.viewportRef, onScroll])\n return null\n}\n\n/* --------------------------------- parts --------------------------------- */\n\nfunction VirtualListViewport({ className, ...props }: React.ComponentProps<\"div\">) {\n const ctx = useVirtualList()\n return (\n <div\n ref={ctx.viewportRef}\n data-slot=\"virtual-list-viewport\"\n className={cn(\"relative min-h-0 overflow-y-auto overscroll-contain\", className)}\n {...props}\n />\n )\n}\n\n/** Sized to the whole list so the scrollbar is right; rows position inside it. */\nfunction VirtualListContent({ className, style, ...props }: React.ComponentProps<\"div\">) {\n const ctx = useVirtualList()\n return (\n <div\n data-slot=\"virtual-list-content\"\n className={cn(\"relative w-full\", className)}\n style={{ height: ctx.totalSize, ...style }}\n {...props}\n />\n )\n}\n\n/** Renders the visible window. The child renders one row and must return a `VirtualListItem`. */\nfunction VirtualListItems({ children }: { children: (item: VirtualItem) => React.ReactNode }) {\n const ctx = useVirtualList()\n return (\n <>\n {ctx.items.map((item) => (\n <React.Fragment key={item.key}>{children(item)}</React.Fragment>\n ))}\n </>\n )\n}\n\nfunction VirtualListItem({ index, className, style, ...props }: React.ComponentProps<\"div\"> & { index: number }) {\n const { items, measure } = useVirtualList()\n const item = items.find((i) => i.index === index)\n const ref = React.useCallback(\n (el: HTMLDivElement | null) => {\n if (!el) return\n const run = () => measure(index, el.getBoundingClientRect().height)\n run()\n const ro = new ResizeObserver(run)\n ro.observe(el)\n // React 19 ref cleanup\n return () => ro.disconnect()\n },\n [measure, index]\n )\n return (\n <div\n ref={ref}\n data-slot=\"virtual-list-item\"\n data-index={index}\n className={cn(\"absolute inset-x-0 top-0\", className)}\n style={{ transform: `translateY(${item?.start ?? 0}px)`, ...style }}\n {...props}\n />\n )\n}\n\nfunction VirtualListEmpty({ className, children, ...props }: React.ComponentProps<\"div\">) {\n const ctx = useVirtualList()\n if (ctx.count > 0) return null\n return (\n <div\n data-slot=\"virtual-list-empty\"\n className={cn(\"flex flex-1 items-center justify-center py-8 text-sm text-muted-foreground\", className)}\n {...props}\n >\n {children ?? \"Nothing to show\"}\n </div>\n )\n}\n\nexport {\n VirtualList,\n VirtualListContent,\n VirtualListEmpty,\n VirtualListItem,\n VirtualListItems,\n VirtualListViewport,\n useVirtualList,\n type VirtualItem,\n type VirtualListProps,\n}\n"
234
+ "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { cn } from \"@/lib/utils\"\n\n/* -------------------------------- context -------------------------------- */\n\ntype VirtualItem = {\n index: number\n key: React.Key\n /** Offset from the top of the content, in px. */\n start: number\n size: number\n end: number\n /** False until the row's real height has been measured. */\n measured: boolean\n}\n\ntype ScrollAlign = \"auto\" | \"start\" | \"center\" | \"end\"\n\ntype VirtualListContextValue = {\n /** Total number of rows. */\n count: number\n items: VirtualItem[]\n totalSize: number\n /** First and last rendered index (overscan included), or null when empty. */\n range: { start: number; end: number } | null\n measure: (index: number, size: number) => void\n scrollToIndex: (index: number, options?: { align?: ScrollAlign; behavior?: ScrollBehavior }) => void\n scrollToOffset: (offset: number, behavior?: ScrollBehavior) => void\n viewportRef: React.RefObject<HTMLDivElement | null>\n /** Stable React key for a row index. */\n getItemKey: (index: number) => React.Key\n}\n\nconst VirtualListContext = React.createContext<VirtualListContextValue | null>(null)\n\nfunction useVirtualList() {\n const ctx = React.useContext(VirtualListContext)\n if (!ctx) throw new Error(\"VirtualList parts must be used inside <VirtualList>\")\n return ctx\n}\n\n/* -------------------------------- helpers -------------------------------- */\n\n/** Largest index whose start is <= offset. */\nfunction findIndex(starts: Float64Array, count: number, offset: number) {\n let lo = 0\n let hi = count - 1\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1\n if (starts[mid] <= offset) lo = mid\n else hi = mid - 1\n }\n return lo\n}\n\n/* ---------------------------------- root --------------------------------- */\n\ntype VirtualListProps = Omit<React.ComponentProps<\"div\">, \"children\"> & {\n /** Total number of rows. */\n count: number\n /** Row height before measurement (a number, or per index). */\n estimateSize?: number | ((index: number) => number)\n /** Rows rendered beyond the visible window on each side. */\n overscan?: number\n /** Space between rows in px. */\n gap?: number\n /** Space before the first row in px. */\n paddingStart?: number\n /** Space after the last row in px. */\n paddingEnd?: number\n /** Stable React key for a row index. */\n getItemKey?: (index: number) => React.Key\n /** Fires when the rendered window changes. */\n onRangeChange?: (range: { start: number; end: number } | null) => void\n children: React.ReactNode\n}\n\nfunction VirtualList({\n count,\n estimateSize = 36,\n overscan = 6,\n gap = 0,\n paddingStart = 0,\n paddingEnd = 0,\n getItemKey = (index) => index,\n onRangeChange,\n className,\n children,\n ...props\n}: VirtualListProps) {\n const viewportRef = React.useRef<HTMLDivElement | null>(null)\n const sizes = React.useRef(new Map<number, number>())\n const [version, setVersion] = React.useState(0)\n const [scrollTop, setScrollTop] = React.useState(0)\n const [viewportSize, setViewportSize] = React.useState(0)\n const estimate = React.useCallback(\n (index: number) => (typeof estimateSize === \"function\" ? estimateSize(index) : estimateSize),\n [estimateSize]\n )\n\n // Prefix sums of row starts; recomputed when a measurement or the count changes.\n const layout = React.useMemo(() => {\n const starts = new Float64Array(count)\n let offset = paddingStart\n for (let i = 0; i < count; i++) {\n starts[i] = offset\n offset += (sizes.current.get(i) ?? estimate(i)) + (i < count - 1 ? gap : 0)\n }\n return { starts, totalSize: offset + paddingEnd }\n // `version` is the measurement tick.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [count, estimate, gap, paddingStart, paddingEnd, version])\n\n const range = React.useMemo(() => {\n if (count === 0 || viewportSize === 0) return count === 0 ? null : { start: 0, end: Math.min(count - 1, overscan) }\n const first = findIndex(layout.starts, count, scrollTop)\n const last = findIndex(layout.starts, count, scrollTop + viewportSize)\n return { start: Math.max(0, first - overscan), end: Math.min(count - 1, last + overscan) }\n }, [count, viewportSize, scrollTop, layout, overscan])\n\n const items = React.useMemo(() => {\n if (!range) return []\n const out: VirtualItem[] = []\n for (let i = range.start; i <= range.end; i++) {\n const measured = sizes.current.has(i)\n const size = sizes.current.get(i) ?? estimate(i)\n out.push({ index: i, key: getItemKey(i), start: layout.starts[i], size, end: layout.starts[i] + size, measured })\n }\n return out\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [range, layout, estimate, getItemKey, version])\n\n const onRangeRef = React.useRef(onRangeChange)\n onRangeRef.current = onRangeChange\n const lastRange = React.useRef<string>(\"\")\n React.useEffect(() => {\n const key = range ? `${range.start}-${range.end}` : \"none\"\n if (key === lastRange.current) return\n lastRange.current = key\n onRangeRef.current?.(range)\n }, [range])\n\n // Drop stale measurements when the list shrinks.\n React.useEffect(() => {\n let dirty = false\n for (const index of sizes.current.keys()) {\n if (index >= count) {\n sizes.current.delete(index)\n dirty = true\n }\n }\n if (dirty) setVersion((v) => v + 1)\n }, [count])\n\n const measure = React.useCallback((index: number, size: number) => {\n const prev = sizes.current.get(index)\n if (prev !== undefined && Math.abs(prev - size) < 0.5) return\n sizes.current.set(index, size)\n setVersion((v) => v + 1)\n }, [])\n\n React.useLayoutEffect(() => {\n const el = viewportRef.current\n if (!el) return\n const update = () => setViewportSize(el.clientHeight)\n update()\n const ro = new ResizeObserver(update)\n ro.observe(el)\n return () => ro.disconnect()\n }, [])\n\n const scrollToOffset = React.useCallback((offset: number, behavior: ScrollBehavior = \"auto\") => {\n viewportRef.current?.scrollTo({ top: offset, behavior })\n }, [])\n\n const pending = React.useRef<{ index: number; align: ScrollAlign; tries: number } | null>(null)\n const offsetFor = React.useCallback(\n (index: number, align: ScrollAlign) => {\n const el = viewportRef.current\n if (!el) return 0\n const start = layout.starts[index] ?? 0\n const size = sizes.current.get(index) ?? estimate(index)\n const height = el.clientHeight\n let resolved = align\n if (align === \"auto\") {\n if (start < el.scrollTop) resolved = \"start\"\n else if (start + size > el.scrollTop + height) resolved = \"end\"\n else return el.scrollTop\n }\n if (resolved === \"start\") return start\n if (resolved === \"end\") return start + size - height\n return start + size / 2 - height / 2\n },\n [layout, estimate]\n )\n\n const scrollToIndex = React.useCallback(\n (index: number, options?: { align?: ScrollAlign; behavior?: ScrollBehavior }) => {\n const clamped = Math.max(0, Math.min(count - 1, index))\n const align = options?.align ?? \"auto\"\n pending.current = { index: clamped, align, tries: 0 }\n scrollToOffset(offsetFor(clamped, align), options?.behavior)\n },\n [count, offsetFor, scrollToOffset]\n )\n\n // Estimated rows shift as they get measured: re-aim at the target a few times.\n React.useLayoutEffect(() => {\n const p = pending.current\n const el = viewportRef.current\n if (!p || !el) return\n if (p.tries >= 4 || !sizes.current.has(p.index)) {\n if (p.tries >= 4) pending.current = null\n return\n }\n const target = offsetFor(p.index, p.align === \"auto\" ? \"start\" : p.align)\n if (Math.abs(el.scrollTop - target) > 1) {\n p.tries += 1\n el.scrollTop = target\n } else pending.current = null\n })\n\n const ctx: VirtualListContextValue = {\n count,\n items,\n totalSize: layout.totalSize,\n range,\n measure,\n scrollToIndex,\n scrollToOffset,\n viewportRef,\n getItemKey,\n }\n\n return (\n <VirtualListContext.Provider value={ctx}>\n <VirtualListScroll onScroll={setScrollTop} />\n <div data-slot=\"virtual-list\" className={cn(\"flex min-h-0 flex-col\", className)} {...props}>\n {children}\n </div>\n </VirtualListContext.Provider>\n )\n}\n\n/** Attaches the scroll listener once the viewport mounts (kept out of the root's render). */\nfunction VirtualListScroll({ onScroll }: { onScroll: (top: number) => void }) {\n const ctx = useVirtualList()\n React.useEffect(() => {\n const el = ctx.viewportRef.current\n if (!el) return\n const handler = () => onScroll(el.scrollTop)\n el.addEventListener(\"scroll\", handler, { passive: true })\n return () => el.removeEventListener(\"scroll\", handler)\n }, [ctx.viewportRef, onScroll])\n return null\n}\n\n/* --------------------------------- parts --------------------------------- */\n\nfunction VirtualListViewport({ className, ...props }: React.ComponentProps<\"div\">) {\n const ctx = useVirtualList()\n return (\n <div\n ref={ctx.viewportRef}\n data-slot=\"virtual-list-viewport\"\n className={cn(\"relative min-h-0 overflow-y-auto overscroll-contain\", className)}\n {...props}\n />\n )\n}\n\n/** Sized to the whole list so the scrollbar is right; rows position inside it. */\nfunction VirtualListContent({ className, style, ...props }: React.ComponentProps<\"div\">) {\n const ctx = useVirtualList()\n return (\n <div\n data-slot=\"virtual-list-content\"\n className={cn(\"relative w-full\", className)}\n style={{ height: ctx.totalSize, ...style }}\n {...props}\n />\n )\n}\n\n/** Renders the visible window. The child renders one row and must return a `VirtualListItem`. */\nfunction VirtualListItems({ children }: { children: (item: VirtualItem) => React.ReactNode }) {\n const ctx = useVirtualList()\n return (\n <>\n {ctx.items.map((item) => (\n <React.Fragment key={item.key}>{children(item)}</React.Fragment>\n ))}\n </>\n )\n}\n\nfunction VirtualListItem({ index, className, style, ...props }: React.ComponentProps<\"div\"> & { index: number }) {\n const { items, measure } = useVirtualList()\n const item = items.find((i) => i.index === index)\n const ref = React.useCallback(\n (el: HTMLDivElement | null) => {\n if (!el) return\n const run = () => measure(index, el.getBoundingClientRect().height)\n run()\n const ro = new ResizeObserver(run)\n ro.observe(el)\n // React 19 ref cleanup\n return () => ro.disconnect()\n },\n [measure, index]\n )\n return (\n <div\n ref={ref}\n data-slot=\"virtual-list-item\"\n data-index={index}\n className={cn(\"absolute inset-x-0 top-0\", className)}\n style={{ transform: `translateY(${item?.start ?? 0}px)`, ...style }}\n {...props}\n />\n )\n}\n\nfunction VirtualListEmpty({ className, children, ...props }: React.ComponentProps<\"div\">) {\n const ctx = useVirtualList()\n if (ctx.count > 0) return null\n return (\n <div\n data-slot=\"virtual-list-empty\"\n className={cn(\"flex flex-1 items-center justify-center py-8 text-sm text-muted-foreground\", className)}\n {...props}\n >\n {children ?? \"Nothing to show\"}\n </div>\n )\n}\n\nexport {\n VirtualList,\n VirtualListContent,\n VirtualListEmpty,\n VirtualListItem,\n VirtualListItems,\n VirtualListViewport,\n useVirtualList,\n type VirtualItem,\n type VirtualListProps,\n}\n"
22
235
  }
23
236
  ],
24
237
  "docs": "## Virtual list\n`src/components/ui/virtual-list.tsx` is a dependency-free windowed list. `VirtualList { count,\nestimateSize (number or per index), overscan, gap, paddingStart/End, getItemKey, onRangeChange }`\nowns scroll offset, viewport size, and measured row heights (prefix sums + binary search; rows\nmeasure themselves with a ResizeObserver and estimates are replaced as rows appear). Parts:\n`VirtualListViewport` (the scroll container; **the consumer gives it a height**, it must never be\n`flex-1` in an auto-height column or it grows to the whole content and renders every row) >\n`VirtualListContent` (sized to `totalSize`) > `VirtualListItems` (render-prop child gets\n`{ index, key, start, size, measured }` and returns a `VirtualListItem index=`, absolutely\npositioned via translateY, `data-index`), `VirtualListEmpty`. `useVirtualList()` exposes `items`,\n`range`, `totalSize`, `scrollToIndex(index, { align: auto|start|center|end })` (re-aims a few times\nwhile estimated rows get measured) and `scrollToOffset`. Keyboard is the consumer's: keep focus on\nthe viewport with `aria-activedescendant` and call `scrollToIndex` for the active row, as the\ncontacts listbox in `src/demo/virtual-list/` does (`/audit-log`, \"Audit log\": a 100k-line log with mixed\nheights and go-to-line, and a 10k-contact listbox with search)."
@@ -60,6 +60,7 @@ type SidebarCollapsible = "offcanvas" | "icon" | "none"
60
60
  type AppShellContextValue = {
61
61
  variant: SidebarVariant
62
62
  setVariant: (variant: SidebarVariant) => void
63
+ /** Controlled sidebar collapse mode: `offcanvas`, `icon`, or `none`. */
63
64
  collapsible: SidebarCollapsible
64
65
  setCollapsible: (collapsible: SidebarCollapsible) => void
65
66
  searchOpen: boolean
@@ -91,10 +92,15 @@ function AppShell({
91
92
  ...sidebarProviderProps
92
93
  }: Omit<React.ComponentProps<typeof SidebarProvider>, "children"> & {
93
94
  variant?: SidebarVariant
95
+ /** Initial sidebar variant when uncontrolled. */
94
96
  defaultVariant?: SidebarVariant
97
+ /** Called when the sidebar variant changes. */
95
98
  onVariantChange?: (variant: SidebarVariant) => void
99
+ /** Controlled sidebar collapse mode: `offcanvas`, `icon`, or `none`. */
96
100
  collapsible?: SidebarCollapsible
101
+ /** Initial collapse mode when uncontrolled. */
97
102
  defaultCollapsible?: SidebarCollapsible
103
+ /** Called when the collapse mode changes. */
98
104
  onCollapsibleChange?: (collapsible: SidebarCollapsible) => void
99
105
  /** Toggle the search dialog with Cmd/Ctrl+K. */
100
106
  searchShortcut?: boolean
@@ -308,6 +314,7 @@ function AppShellNavCollapsible({
308
314
  active?: boolean
309
315
  defaultOpen?: boolean
310
316
  tooltip?: string
317
+ /** Element rendered as the trigger for the collapsible group. */
311
318
  trigger: React.ReactNode
312
319
  /** Rendered inside the flyout when the sidebar is collapsed to icons. */
313
320
  menu?: React.ReactNode
@@ -674,6 +681,7 @@ function AppShellNotificationsTrigger({
674
681
  }: React.ComponentProps<typeof Button> & {
675
682
  /** Unread count shown as a badge; hidden when 0. */
676
683
  count?: number
684
+ /** Cap for the badge count; larger counts render as `max+`. */
677
685
  max?: number
678
686
  }) {
679
687
  const label = count > max ? `${max}+` : String(count)
@@ -763,6 +771,7 @@ function AppShellNotificationItem({
763
771
  children,
764
772
  ...props
765
773
  }: React.ComponentProps<"li"> & {
774
+ /** Mark the notification unread; sets `data-unread` and shows the indicator. */
766
775
  unread?: boolean
767
776
  /** Render the item's child (e.g. an anchor or button) as the interactive surface. */
768
777
  asChild?: boolean
@@ -894,7 +903,9 @@ function AppShellThemeToggle({
894
903
  className,
895
904
  ...props
896
905
  }: Omit<React.ComponentProps<typeof Button>, "onChange"> & {
906
+ /** Current theme: `light`, `dark`, or `system`. */
897
907
  theme: Theme
908
+ /** Called with the next theme when the toggle is used. */
898
909
  onThemeChange: (theme: Theme) => void
899
910
  }) {
900
911
  return (
@@ -31,6 +31,7 @@ function Attachment({
31
31
  ...props
32
32
  }: React.ComponentProps<"div"> &
33
33
  VariantProps<typeof attachmentVariants> & {
34
+ /** Upload state: `idle`, `uploading`, `processing`, `error`, or `done`; sets `data-state`. */
34
35
  state?: "idle" | "uploading" | "processing" | "error" | "done"
35
36
  }) {
36
37
  return (
@@ -18,6 +18,7 @@ function Calendar({
18
18
  components,
19
19
  ...props
20
20
  }: React.ComponentProps<typeof DayPicker> & {
21
+ /** Button variant used for the navigation buttons. */
21
22
  buttonVariant?: React.ComponentProps<typeof Button>["variant"]
22
23
  }) {
23
24
  const defaultClassNames = getDefaultClassNames()
@@ -154,6 +154,7 @@ function Canvas({
154
154
  const data = source.data as DragData
155
155
  setDragging(data.type === "node" ? { type: "node", id: data.id } : { type: "palette" })
156
156
  },
157
+ /** Called with `{ source, target }` for every drop: a palette item or node onto a node edge or the empty area. */
157
158
  onDrop: ({ source, location }) => {
158
159
  setDragging(null)
159
160
  const data = source.data as DragData
@@ -257,6 +258,7 @@ function CanvasPaletteItem({ data, className, onKeyDown, ...props }: React.Compo
257
258
  getInitialData: (): DragData => ({ instanceId, type: "palette", data: dataRef.current }),
258
259
  onGenerateDragPreview: (args) => cloneDragPreview(args),
259
260
  onDragStart: () => setIsDragging(true),
261
+ /** Called with `{ source, target }` for every drop: a palette item or node onto a node edge or the empty area. */
260
262
  onDrop: () => setIsDragging(false),
261
263
  })
262
264
  }, [instanceId])
@@ -310,6 +312,7 @@ function CanvasArea({ className, ...props }: React.ComponentProps<"div">) {
310
312
  setIsOver((prev) => (prev === over ? prev : over))
311
313
  },
312
314
  onDragLeave: () => setIsOver(false),
315
+ /** Called with `{ source, target }` for every drop: a palette item or node onto a node edge or the empty area. */
313
316
  onDrop: () => setIsOver(false),
314
317
  }),
315
318
  autoScrollForElements({ element: el, canScroll: ({ source }) => isOwnData(instanceId, source.data) })
@@ -437,6 +440,7 @@ function CanvasNode({
437
440
  setClosestEdge((prev) => (prev === edge ? prev : edge))
438
441
  },
439
442
  onDragLeave: () => setClosestEdge(null),
443
+ /** Called with `{ source, target }` for every drop: a palette item or node onto a node edge or the empty area. */
440
444
  onDrop: () => setClosestEdge(null),
441
445
  }),
442
446
  ]
@@ -448,6 +452,7 @@ function CanvasNode({
448
452
  getInitialData: (): DragData => ({ instanceId, type: "node", id: value }),
449
453
  onGenerateDragPreview: (args) => cloneDragPreview(args),
450
454
  onDragStart: () => setIsDragging(true),
455
+ /** Called with `{ source, target }` for every drop: a palette item or node onto a node edge or the empty area. */
451
456
  onDrop: () => setIsDragging(false),
452
457
  })
453
458
  )
@@ -602,8 +607,11 @@ function CanvasResizeHandle({
602
607
  className,
603
608
  ...props
604
609
  }: Omit<React.ComponentProps<"div">, "onResize"> & {
610
+ /** Called while dragging with the width change as a fraction of the row width. */
605
611
  onResize: (deltaFraction: number) => void
612
+ /** Called once when the resize drag starts. */
606
613
  onResizeStart?: () => void
614
+ /** Called once when the resize drag ends. */
607
615
  onResizeEnd?: () => void
608
616
  }) {
609
617
  const row = React.useContext(RowContext)
@@ -11,9 +11,12 @@ type CarouselOptions = UseCarouselParameters[0]
11
11
  type CarouselPlugin = UseCarouselParameters[1]
12
12
 
13
13
  type CarouselProps = {
14
+ /** Embla carousel options. */
14
15
  opts?: CarouselOptions
16
+ /** Embla plugins. */
15
17
  plugins?: CarouselPlugin
16
18
  orientation?: "horizontal" | "vertical"
19
+ /** Receives the Embla API once the carousel mounts. */
17
20
  setApi?: (api: CarouselApi) => void
18
21
  }
19
22
 
@@ -410,6 +410,7 @@ function CascaderValue<T extends TreeNode>({
410
410
  ...props
411
411
  }: Omit<React.ComponentProps<"span">, "children"> & {
412
412
  placeholder?: React.ReactNode
413
+ /** Rendered between path segments. */
413
414
  separator?: React.ReactNode
414
415
  children?: (path: T[]) => React.ReactNode
415
416
  }) {
@@ -20,6 +20,7 @@ export type ChartConfig = Record<
20
20
  >
21
21
 
22
22
  type ChartContextProps = {
23
+ /** Series config: label, icon, and color (or theme colors) per data key; exposed as `--color-<key>`. */
23
24
  config: ChartConfig
24
25
  }
25
26
 
@@ -43,8 +44,10 @@ function ChartContainer({
43
44
  initialDimension = INITIAL_DIMENSION,
44
45
  ...props
45
46
  }: React.ComponentProps<"div"> & {
47
+ /** Series config: label, icon, and color (or theme colors) per data key; exposed as `--color-<key>`. */
46
48
  config: ChartConfig
47
49
  children: React.ComponentProps<typeof RechartsPrimitive.ResponsiveContainer>["children"]
50
+ /** Width and height used before the container measures itself. */
48
51
  initialDimension?: {
49
52
  width: number
50
53
  height: number
@@ -120,10 +123,15 @@ function ChartTooltipContent({
120
123
  labelKey,
121
124
  }: React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
122
125
  React.ComponentProps<"div"> & {
126
+ /** Hide the tooltip label. */
123
127
  hideLabel?: boolean
128
+ /** Hide the color indicator in each row. */
124
129
  hideIndicator?: boolean
130
+ /** Indicator style: `dot`, `line`, or `dashed`. */
125
131
  indicator?: "line" | "dot" | "dashed"
132
+ /** Data key used for the series name. */
126
133
  nameKey?: string
134
+ /** Data key used for the tooltip label. */
127
135
  labelKey?: string
128
136
  } & Omit<RechartsPrimitive.DefaultTooltipContentProps<TooltipValueType, TooltipNameType>, "accessibilityLayer">) {
129
137
  const { config } = useChart()
@@ -238,7 +246,9 @@ function ChartLegendContent({
238
246
  verticalAlign = "bottom",
239
247
  nameKey,
240
248
  }: React.ComponentProps<"div"> & {
249
+ /** Hide the series icon. */
241
250
  hideIcon?: boolean
251
+ /** Data key used for the series name. */
242
252
  nameKey?: string
243
253
  } & RechartsPrimitive.DefaultLegendContentProps) {
244
254
  const { config } = useChart()
@@ -120,7 +120,9 @@ function ComboboxInput({
120
120
  showClear = false,
121
121
  ...props
122
122
  }: Omit<React.ComponentProps<typeof ComboboxPrimitive.Input>, "id"> & {
123
+ /** Render the open/close trigger in the control. */
123
124
  showTrigger?: boolean
125
+ /** Render the clear button in the control. */
124
126
  showClear?: boolean
125
127
  }) {
126
128
  return (
@@ -316,6 +318,7 @@ function ComboboxChip({
316
318
  }: React.ComponentProps<"div"> & {
317
319
  /** The selected value this chip represents. */
318
320
  value: string
321
+ /** Render the remove button on the chip. */
319
322
  showRemove?: boolean
320
323
  }) {
321
324
  const combobox = useComboboxContext()
@@ -167,6 +167,7 @@ function CommandGroup({
167
167
  children,
168
168
  ...props
169
169
  }: React.ComponentProps<typeof CommandPrimitive.ItemGroup> & {
170
+ /** Group heading text. */
170
171
  heading?: React.ReactNode
171
172
  }) {
172
173
  return (
@@ -35,8 +35,11 @@ import { useControllable } from "@/lib/controllable"
35
35
  type Person = { id: string; name: string; initials?: string; avatar?: string }
36
36
 
37
37
  type ThreadContextValue = {
38
+ /** People who can be mentioned and whose names and initials are rendered. */
38
39
  people: Person[]
40
+ /** Id of the signed-in person; their comments get `data-own` and mentions of them `data-self`. */
39
41
  currentUserId?: string
42
+ /** Id of the comment being replied to (controlled). */
40
43
  replyTo: string | null
41
44
  setReplyTo: (id: string | null) => void
42
45
  editing: string | null
@@ -77,9 +80,13 @@ function CommentThread({
77
80
  className,
78
81
  ...props
79
82
  }: React.ComponentProps<"div"> & {
83
+ /** People who can be mentioned and whose names and initials are rendered. */
80
84
  people?: Person[]
85
+ /** Id of the signed-in person; their comments get `data-own` and mentions of them `data-self`. */
81
86
  currentUserId?: string
87
+ /** Id of the comment being replied to (controlled). */
82
88
  replyTo?: string | null
89
+ /** Called when the reply target changes. */
83
90
  onReplyToChange?: (id: string | null) => void
84
91
  }) {
85
92
  const [replyTo, setReplyTo] = useControllable<string | null>(replyToProp, null, onReplyToChange)
@@ -383,6 +390,7 @@ function CommentEmpty({ className, ...props }: React.ComponentProps<"div">) {
383
390
  type MentionState = { query: string; start: number; index: number } | null
384
391
 
385
392
  type ComposerContextValue = {
393
+ /** People who can be mentioned and whose names and initials are rendered. */
386
394
  people: Person[]
387
395
  value: string
388
396
  setValue: (value: string) => void
@@ -424,13 +432,16 @@ function CommentComposer({
424
432
  children,
425
433
  ...props
426
434
  }: Omit<React.ComponentProps<"div">, "onSubmit" | "defaultValue"> & {
435
+ /** People who can be mentioned and whose names and initials are rendered. */
427
436
  people?: Person[]
428
437
  value?: string
429
438
  defaultValue?: string
430
439
  onValueChange?: (value: string) => void
431
440
  /** Called with the trimmed text and the ids of mentioned people. */
432
441
  onSubmit?: (comment: { text: string; mentions: string[] }) => void
442
+ /** Called when the composer is cancelled with Escape or the cancel trigger. */
433
443
  onCancel?: () => void
444
+ /** Maximum number of mention suggestions shown. */
434
445
  maxSuggestions?: number
435
446
  }) {
436
447
  const [value, setValue] = useControllable(valueProp, defaultValue, onValueChange)
@@ -70,6 +70,7 @@ function ContextMenuItem({
70
70
  ...props
71
71
  }: Omit<React.ComponentProps<typeof ContextMenuPrimitive.Item>, "value"> & {
72
72
  value?: string
73
+ /** Indent the item to align with items that have an indicator. */
73
74
  inset?: boolean
74
75
  variant?: "default" | "destructive"
75
76
  }) {
@@ -117,8 +118,10 @@ function ContextMenuCheckboxItem({
117
118
  value,
118
119
  ...props
119
120
  }: Omit<React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>, "checked" | "value"> & {
121
+ /** Checked state of the item. */
120
122
  checked?: boolean
121
123
  value?: string
124
+ /** Indent the item to align with items that have an indicator. */
122
125
  inset?: boolean
123
126
  }) {
124
127
  const id = React.useId()
@@ -149,6 +152,7 @@ function ContextMenuRadioItem({
149
152
  inset,
150
153
  ...props
151
154
  }: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem> & {
155
+ /** Indent the item to align with items that have an indicator. */
152
156
  inset?: boolean
153
157
  }) {
154
158
  return (
@@ -171,6 +175,7 @@ function ContextMenuLabel({
171
175
  inset,
172
176
  ...props
173
177
  }: React.ComponentProps<typeof ContextMenuPrimitive.ItemGroupLabel> & {
178
+ /** Indent the item to align with items that have an indicator. */
174
179
  inset?: boolean
175
180
  }) {
176
181
  return (
@@ -220,6 +225,7 @@ function ContextMenuSubTrigger({
220
225
  children,
221
226
  ...props
222
227
  }: React.ComponentProps<typeof ContextMenuPrimitive.TriggerItem> & {
228
+ /** Indent the item to align with items that have an indicator. */
223
229
  inset?: boolean
224
230
  }) {
225
231
  return (
@@ -102,6 +102,7 @@ type DataGridContextValue<TData = unknown> = {
102
102
  layoutById: Map<string, ColumnLayout>
103
103
  templateColumns: string
104
104
  totalWidth: number
105
+ /** Fixed height of every row in px. */
105
106
  rowHeight: number
106
107
  setWidth: (columnId: string, width: number) => void
107
108
  focus: CellAddress | null
@@ -166,12 +167,19 @@ function DataGrid<TData>({
166
167
  ...props
167
168
  }: Omit<React.ComponentProps<"div">, "children"> & {
168
169
  table: DataTableInstance<TData>
170
+ /** Per-column grid behaviour keyed by column id: editor type, width, pinning, options, format and parse. */
169
171
  columns?: DataGridColumnConfig[]
172
+ /** Fixed height of every row in px. */
170
173
  rowHeight?: number
174
+ /** Rows rendered beyond the visible window on each side. */
171
175
  overscan?: number
176
+ /** Called with `{ row, columnId, value, previous }` when a cell edit commits; the consumer applies it. */
172
177
  onCellChange?: (change: DataGridCellChange<TData>) => void
178
+ /** Controlled column widths by column id. */
173
179
  columnSizing?: Record<string, number>
180
+ /** Initial column widths when uncontrolled. */
174
181
  defaultColumnSizing?: Record<string, number>
182
+ /** Called when a column is resized. */
175
183
  onColumnSizingChange?: (sizing: Record<string, number>) => void
176
184
  children?: React.ReactNode
177
185
  }) {