@elabs-ai/components-ui 4.1.0 → 4.2.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 (193) hide show
  1. package/dist/{chunk-JKPPTL63.js → chunk-FCH4ZN6G.js} +12 -3
  2. package/dist/chunk-FCH4ZN6G.js.map +1 -0
  3. package/dist/form.js +3 -3
  4. package/dist/form.js.map +1 -1
  5. package/dist/index.d.ts +126 -35
  6. package/dist/index.js +3346 -2079
  7. package/dist/index.js.map +1 -1
  8. package/dist/lib/cn.js +10 -1
  9. package/dist/lib/cn.js.map +1 -1
  10. package/package.json +3 -3
  11. package/src/__contract__/attribution-panel.contract.test.tsx +49 -0
  12. package/src/__contract__/badge.contract.test.tsx +49 -0
  13. package/src/__contract__/bounded-number.contract.test.tsx +49 -0
  14. package/src/__contract__/button.contract.test.tsx +49 -0
  15. package/src/__contract__/color-picker.contract.test.tsx +49 -0
  16. package/src/__contract__/combobox.contract.test.tsx +49 -0
  17. package/src/__contract__/command-trigger.contract.test.tsx +49 -0
  18. package/src/__contract__/copyable-value.contract.test.tsx +49 -0
  19. package/src/__contract__/date-picker.contract.test.tsx +49 -0
  20. package/src/__contract__/date-range-picker.contract.test.tsx +49 -0
  21. package/src/__contract__/empty-state.contract.test.tsx +49 -0
  22. package/src/__contract__/error-state.contract.test.tsx +49 -0
  23. package/src/__contract__/icon-button.contract.test.tsx +49 -0
  24. package/src/__contract__/input.contract.test.tsx +49 -0
  25. package/src/__contract__/key-value-editor.contract.test.tsx +49 -0
  26. package/src/__contract__/list-editor.contract.test.tsx +49 -0
  27. package/src/__contract__/loading-state.contract.test.tsx +49 -0
  28. package/src/__contract__/metric-card.contract.test.tsx +49 -0
  29. package/src/__contract__/model-picker.contract.test.tsx +49 -0
  30. package/src/__contract__/nav-notifications.contract.test.tsx +49 -0
  31. package/src/__contract__/number-input.contract.test.tsx +49 -0
  32. package/src/__contract__/rating.contract.test.tsx +49 -0
  33. package/src/__contract__/schema-form.contract.test.tsx +49 -0
  34. package/src/__contract__/section-header.contract.test.tsx +49 -0
  35. package/src/__contract__/segmented-field.contract.test.tsx +49 -0
  36. package/src/__contract__/slider-number.contract.test.tsx +49 -0
  37. package/src/__contract__/status-badge.contract.test.tsx +49 -0
  38. package/src/__contract__/tag-input.contract.test.tsx +49 -0
  39. package/src/__contract__/text.contract.test.tsx +49 -0
  40. package/src/__contract__/textarea.contract.test.tsx +49 -0
  41. package/src/__contract__/timeline.contract.test.tsx +49 -0
  42. package/src/__contract__/toggle.contract.test.tsx +49 -0
  43. package/src/__contract__/transfer.contract.test.tsx +49 -0
  44. package/src/__contract__/tree-select.contract.test.tsx +49 -0
  45. package/src/__contract__/tree.contract.test.tsx +49 -0
  46. package/src/__contract__/virtual-select.contract.test.tsx +49 -0
  47. package/src/__contract__/workspace-picker.contract.test.tsx +49 -0
  48. package/src/components/accordion/accordion.tsx +13 -4
  49. package/src/components/alert/alert.tsx +11 -3
  50. package/src/components/alert-dialog/alert-dialog.test.tsx +32 -0
  51. package/src/components/alert-dialog/alert-dialog.tsx +28 -6
  52. package/src/components/app-shell/app-shell.test.tsx +99 -0
  53. package/src/components/app-shell/app-shell.tsx +29 -10
  54. package/src/components/attribution-panel/attribution-panel.tsx +2 -0
  55. package/src/components/attribution-panel/attributions.generated.ts +2 -2
  56. package/src/components/avatar/avatar.tsx +4 -1
  57. package/src/components/badge/badge.test.tsx +8 -0
  58. package/src/components/badge/badge.tsx +14 -4
  59. package/src/components/bento-grid/bento-grid.test.tsx +23 -2
  60. package/src/components/bento-grid/bento-grid.tsx +10 -0
  61. package/src/components/bounded-number/bounded-number.test.tsx +1 -1
  62. package/src/components/bounded-number/bounded-number.tsx +78 -75
  63. package/src/components/breadcrumb/breadcrumb.tsx +3 -2
  64. package/src/components/button/button.tsx +18 -12
  65. package/src/components/button-group/button-group.tsx +1 -1
  66. package/src/components/calendar/calendar.tsx +3 -2
  67. package/src/components/card/card.test.tsx +24 -2
  68. package/src/components/card/card.tsx +47 -6
  69. package/src/components/carousel/carousel.test.tsx +169 -0
  70. package/src/components/carousel/carousel.tsx +93 -32
  71. package/src/components/change-review/change-review.tsx +38 -29
  72. package/src/components/checkbox/checkbox.tsx +1 -0
  73. package/src/components/collapsible-panel/use-collapsible-panel.ts +0 -3
  74. package/src/components/color-picker/color-picker.tsx +17 -11
  75. package/src/components/combobox/combobox.stories.tsx +10 -0
  76. package/src/components/combobox/combobox.test.tsx +54 -0
  77. package/src/components/combobox/combobox.tsx +25 -11
  78. package/src/components/command/command.test.tsx +15 -0
  79. package/src/components/command/command.tsx +8 -6
  80. package/src/components/context-menu/context-menu.tsx +16 -7
  81. package/src/components/context-rail/context-rail.tsx +4 -4
  82. package/src/components/copyable-value/copyable-value.stories.tsx +10 -0
  83. package/src/components/copyable-value/copyable-value.test.tsx +28 -1
  84. package/src/components/copyable-value/copyable-value.tsx +12 -1
  85. package/src/components/date-picker/date-picker.stories.tsx +9 -0
  86. package/src/components/date-picker/date-picker.test.tsx +52 -0
  87. package/src/components/date-picker/date-picker.tsx +13 -15
  88. package/src/components/date-range-picker/date-range-picker.stories.tsx +9 -0
  89. package/src/components/date-range-picker/date-range-picker.test.tsx +76 -1
  90. package/src/components/date-range-picker/date-range-picker.tsx +39 -28
  91. package/src/components/descriptions/descriptions.tsx +1 -1
  92. package/src/components/dialog/dialog.test.tsx +8 -3
  93. package/src/components/dialog/dialog.tsx +12 -4
  94. package/src/components/drawer/drawer.tsx +23 -6
  95. package/src/components/dropdown-menu/dropdown-menu.tsx +4 -4
  96. package/src/components/field/field.tsx +2 -0
  97. package/src/components/field-row/field-row.tsx +2 -0
  98. package/src/components/file-upload/file-upload.test.tsx +83 -10
  99. package/src/components/file-upload/file-upload.tsx +150 -28
  100. package/src/components/form/form.tsx +4 -2
  101. package/src/components/hover-card/hover-card.tsx +1 -0
  102. package/src/components/icon-button/icon-button.tsx +1 -1
  103. package/src/components/input/input.tsx +7 -3
  104. package/src/components/input-group/input-group.tsx +6 -6
  105. package/src/components/input-otp/input-otp.tsx +3 -1
  106. package/src/components/keyboard-shortcuts/keyboard-shortcuts.tsx +2 -2
  107. package/src/components/label/label.tsx +1 -1
  108. package/src/components/list-editor/list-editor.test.tsx +24 -0
  109. package/src/components/list-editor/list-editor.tsx +126 -52
  110. package/src/components/locale-provider/locale-keys.test.ts +134 -0
  111. package/src/components/locale-provider/messages.ts +321 -0
  112. package/src/components/mention-input/mention-input.stories.tsx +3 -3
  113. package/src/components/mention-input/mention-input.test.tsx +51 -1
  114. package/src/components/mention-input/mention-input.tsx +58 -8
  115. package/src/components/menubar/menubar.tsx +16 -6
  116. package/src/components/nav-main/nav-main.tsx +7 -3
  117. package/src/components/nav-notifications/nav-notifications.test.tsx +11 -0
  118. package/src/components/nav-notifications/nav-notifications.tsx +11 -5
  119. package/src/components/nav-user/nav-user.tsx +5 -5
  120. package/src/components/navigation-menu/navigation-menu.tsx +16 -4
  121. package/src/components/number-input/number-input.stories.tsx +7 -3
  122. package/src/components/number-input/number-input.test.tsx +47 -0
  123. package/src/components/number-input/number-input.tsx +65 -24
  124. package/src/components/pagination/pagination.test.tsx +20 -1
  125. package/src/components/pagination/pagination.tsx +18 -14
  126. package/src/components/popover/popover.tsx +1 -0
  127. package/src/components/progress/progress.tsx +2 -0
  128. package/src/components/rating/rating.tsx +2 -0
  129. package/src/components/resizable/resizable.test.tsx +76 -0
  130. package/src/components/resizable/resizable.tsx +31 -5
  131. package/src/components/revision-timeline/revision-timeline.tsx +7 -2
  132. package/src/components/schema-form/schema-form-store.ts +506 -0
  133. package/src/components/schema-form/schema-form.test.tsx +122 -1
  134. package/src/components/schema-form/schema-form.tsx +163 -327
  135. package/src/components/section-header/section-header.tsx +2 -2
  136. package/src/components/select/select.tsx +7 -5
  137. package/src/components/separator/separator.tsx +1 -0
  138. package/src/components/sheet/sheet.test.tsx +39 -0
  139. package/src/components/sheet/sheet.tsx +40 -9
  140. package/src/components/side-dock/side-dock.test.tsx +26 -0
  141. package/src/components/side-dock/side-dock.tsx +15 -2
  142. package/src/components/sidebar/sidebar.test.tsx +35 -1
  143. package/src/components/sidebar/sidebar.tsx +20 -6
  144. package/src/components/skeleton/skeleton.tsx +1 -0
  145. package/src/components/slider-number/slider-number.stories.tsx +4 -1
  146. package/src/components/slider-number/slider-number.test.tsx +1 -1
  147. package/src/components/sonner/sonner.tsx +2 -0
  148. package/src/components/spinner/spinner.test.tsx +18 -0
  149. package/src/components/spinner/spinner.tsx +14 -4
  150. package/src/components/state-panel/state-panel.test.tsx +3 -3
  151. package/src/components/state-panel/state-panel.tsx +15 -8
  152. package/src/components/switch/switch.tsx +2 -0
  153. package/src/components/table/table.stories.tsx +3 -1
  154. package/src/components/table/table.test.tsx +9 -2
  155. package/src/components/table/table.tsx +18 -8
  156. package/src/components/tabs/index.ts +10 -1
  157. package/src/components/tabs/tabs.stories.tsx +48 -0
  158. package/src/components/tabs/tabs.test.tsx +67 -0
  159. package/src/components/tabs/tabs.tsx +175 -77
  160. package/src/components/tag-input/tag-input.tsx +4 -2
  161. package/src/components/team-switcher/team-switcher.tsx +3 -3
  162. package/src/components/textarea/textarea.tsx +1 -1
  163. package/src/components/theme-switcher/theme-switcher.stories.tsx +76 -0
  164. package/src/components/theme-switcher/theme-switcher.test.tsx +182 -0
  165. package/src/components/theme-switcher/theme-switcher.tsx +134 -10
  166. package/src/components/timeline/timeline.tsx +0 -3
  167. package/src/components/toggle/toggle.tsx +7 -2
  168. package/src/components/toggle-group/toggle-group.tsx +2 -0
  169. package/src/components/tooltip/tooltip.tsx +2 -1
  170. package/src/components/top-nav/top-nav.tsx +1 -1
  171. package/src/components/transfer/transfer.tsx +35 -30
  172. package/src/components/tree/tree.test.tsx +41 -0
  173. package/src/components/tree/tree.tsx +77 -22
  174. package/src/components/tree/use-tree-keyboard.ts +37 -14
  175. package/src/components/tree-select/tree-select.stories.tsx +9 -0
  176. package/src/components/tree-select/tree-select.test.tsx +5 -0
  177. package/src/components/tree-select/tree-select.tsx +11 -14
  178. package/src/components/typography/prose.test.tsx +3 -2
  179. package/src/components/typography/prose.tsx +3 -3
  180. package/src/components/virtual-select/virtual-select.stories.tsx +9 -0
  181. package/src/components/virtual-select/virtual-select.test.tsx +28 -2
  182. package/src/components/virtual-select/virtual-select.tsx +56 -18
  183. package/src/components/wizard/wizard.test.tsx +9 -0
  184. package/src/components/wizard/wizard.tsx +12 -4
  185. package/src/index.ts +5 -0
  186. package/src/lib/cn.ts +9 -0
  187. package/src/lib/csv.ts +48 -0
  188. package/src/lib/diff-rows.ts +2 -0
  189. package/src/lib/merge-refs.ts +2 -0
  190. package/src/lib/use-controllable-state.ts +47 -0
  191. package/src/lib/use-mobile.test.tsx +47 -0
  192. package/src/lib/use-mobile.ts +26 -10
  193. package/dist/chunk-JKPPTL63.js.map +0 -1
@@ -36,11 +36,13 @@ export const Progress = forwardRef<ElementRef<typeof ProgressPrimitive.Root>, Pr
36
36
  return (
37
37
  <ProgressPrimitive.Root
38
38
  ref={ref}
39
+ data-slot="progress"
39
40
  value={value}
40
41
  className={cn("relative h-2 w-full overflow-hidden rounded-full bg-muted", className)}
41
42
  {...props}
42
43
  >
43
44
  <ProgressPrimitive.Indicator
45
+ data-slot="progress-indicator"
44
46
  className={progressIndicatorVariants({ variant })}
45
47
  style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
46
48
  />
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  // a2ui.exposed: yes
2
4
  import {
3
5
  forwardRef,
@@ -0,0 +1,76 @@
1
+ import { createRef } from "react";
2
+ import { describe, expect, it, vi } from "vitest";
3
+ import { render } from "@testing-library/react";
4
+ import type * as ResizablePrimitiveModule from "react-resizable-panels";
5
+ import type { ImperativePanelGroupHandle } from "react-resizable-panels";
6
+
7
+ const capturedProps: Record<string, unknown>[] = [];
8
+
9
+ vi.mock("react-resizable-panels", async (importOriginal) => {
10
+ const actual = await importOriginal<typeof ResizablePrimitiveModule>();
11
+ return {
12
+ ...actual,
13
+ PanelResizeHandle: (props: Record<string, unknown>) => {
14
+ capturedProps.push(props);
15
+ return actual.PanelResizeHandle(props as never);
16
+ },
17
+ };
18
+ });
19
+
20
+ // Imported AFTER the mock so the module picked up above is the one in use.
21
+ const { ResizableHandle, ResizablePanel, ResizablePanelGroup } = await import("./resizable");
22
+
23
+ describe("Resizable — handle hit area is expanded without changing the visible line", () => {
24
+ it("keeps the visible line at 1px (w-px) while widening the pointer hit area via hitAreaMargins", () => {
25
+ capturedProps.length = 0;
26
+ const { container } = render(
27
+ <ResizablePanelGroup direction="horizontal" className="h-48 max-w-xl">
28
+ <ResizablePanel defaultSize={40}>List</ResizablePanel>
29
+ <ResizableHandle aria-label="Resize" />
30
+ <ResizablePanel defaultSize={60}>Detail</ResizablePanel>
31
+ </ResizablePanelGroup>,
32
+ );
33
+ const handle = container.querySelector('[role="separator"]');
34
+ expect(handle).not.toBeNull();
35
+ // Visual line unchanged — still the 1px hairline, not a wider drawn box.
36
+ expect(handle!.className).toMatch(/\bw-px\b/);
37
+
38
+ // The DRAG hit area comes from the primitive's own pointer-distance
39
+ // config, not from CSS: at least 16px combined margin around the line
40
+ // (>= 8px each side), satisfying the ≥16px minimum without widening
41
+ // anything visible.
42
+ const props = capturedProps.at(-1);
43
+ const margins = props?.hitAreaMargins as { coarse: number; fine: number } | undefined;
44
+ expect(margins).toBeDefined();
45
+ expect(margins!.fine).toBeGreaterThanOrEqual(8);
46
+ expect(margins!.coarse).toBeGreaterThanOrEqual(8);
47
+ });
48
+
49
+ it("lets a caller override hitAreaMargins explicitly", () => {
50
+ capturedProps.length = 0;
51
+ render(
52
+ <ResizablePanelGroup direction="horizontal" className="h-48 max-w-xl">
53
+ <ResizablePanel defaultSize={40}>List</ResizablePanel>
54
+ <ResizableHandle aria-label="Resize" hitAreaMargins={{ coarse: 20, fine: 20 }} />
55
+ <ResizablePanel defaultSize={60}>Detail</ResizablePanel>
56
+ </ResizablePanelGroup>,
57
+ );
58
+ const props = capturedProps.at(-1);
59
+ expect(props?.hitAreaMargins).toEqual({ coarse: 20, fine: 20 });
60
+ });
61
+ });
62
+
63
+ describe("Resizable — forwardRef", () => {
64
+ it("ResizablePanelGroup forwards its ref to the imperative panel-group handle", () => {
65
+ const ref = createRef<ImperativePanelGroupHandle>();
66
+ render(
67
+ <ResizablePanelGroup ref={ref} direction="horizontal" className="h-48 max-w-xl">
68
+ <ResizablePanel defaultSize={40}>List</ResizablePanel>
69
+ <ResizableHandle aria-label="Resize" />
70
+ <ResizablePanel defaultSize={60}>Detail</ResizablePanel>
71
+ </ResizablePanelGroup>,
72
+ );
73
+ expect(ref.current).not.toBeNull();
74
+ expect(typeof ref.current?.getLayout).toBe("function");
75
+ });
76
+ });
@@ -1,28 +1,54 @@
1
+ import { forwardRef, type ForwardRefExoticComponent, type RefAttributes } from "react";
1
2
  import { GripVertical } from "lucide-react";
2
3
  import * as ResizablePrimitive from "react-resizable-panels";
3
4
  import { cn } from "../../lib/cn";
4
5
 
5
- export function ResizablePanelGroup({
6
- className,
7
- ...props
8
- }: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) {
6
+ export const ResizablePanelGroup: ForwardRefExoticComponent<
7
+ React.ComponentProps<typeof ResizablePrimitive.PanelGroup> &
8
+ RefAttributes<ResizablePrimitive.ImperativePanelGroupHandle>
9
+ > = forwardRef<
10
+ ResizablePrimitive.ImperativePanelGroupHandle,
11
+ React.ComponentProps<typeof ResizablePrimitive.PanelGroup>
12
+ >(function ResizablePanelGroup({ className, ...props }, ref) {
9
13
  return (
10
14
  <ResizablePrimitive.PanelGroup
15
+ ref={ref}
11
16
  className={cn("flex size-full data-[panel-group-direction=vertical]:flex-col", className)}
12
17
  {...props}
13
18
  />
14
19
  );
15
- }
20
+ });
16
21
 
17
22
  export const ResizablePanel = ResizablePrimitive.Panel;
18
23
 
24
+ /**
25
+ * The visible line stays 1px (`w-px`); the DRAG hit area is expanded via the
26
+ * primitive's own `hitAreaMargins` (pointer-distance based, not a CSS trick),
27
+ * so pointer/touch users get a ≥16px target without widening what's drawn.
28
+ * Callers can still override `hitAreaMargins` explicitly.
29
+ */
30
+ const DEFAULT_HIT_AREA_MARGINS: ResizablePrimitive.PointerHitAreaMargins = {
31
+ coarse: 15,
32
+ fine: 8,
33
+ };
34
+
35
+ /**
36
+ * NOT wrapped in `forwardRef`: `react-resizable-panels`' `PanelResizeHandle`
37
+ * is a plain function component that hardcodes its own internal element ref
38
+ * and does not accept `ref` as a prop at all (neither in its types nor at
39
+ * runtime — verified: a `ref` passed to it resolves to `null`). There is no
40
+ * DOM node here for a wrapper ref to attach to without changing what render
41
+ * onto the actual drag surface, so adding `forwardRef` would be a dead prop.
42
+ */
19
43
  export function ResizableHandle({
20
44
  withHandle,
21
45
  className,
46
+ hitAreaMargins,
22
47
  ...props
23
48
  }: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & { withHandle?: boolean }) {
24
49
  return (
25
50
  <ResizablePrimitive.PanelResizeHandle
51
+ hitAreaMargins={hitAreaMargins ?? DEFAULT_HIT_AREA_MARGINS}
26
52
  className={cn(
27
53
  "relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-ring",
28
54
  "data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0",
@@ -73,6 +73,7 @@ import {
73
73
  import { cva, type VariantProps } from "class-variance-authority";
74
74
  import { ChevronRight, GitBranch, GitCommitHorizontal, GitMerge, Plus, Minus } from "lucide-react";
75
75
  import { cn } from "../../lib/cn";
76
+ import { useLocale } from "../locale-provider";
76
77
 
77
78
  // ─── Public data types ────────────────────────────────────────────────────────
78
79
 
@@ -937,6 +938,7 @@ export const RevisionTimeline = forwardRef<HTMLDivElement, RevisionTimelineProps
937
938
  },
938
939
  ref,
939
940
  ) {
941
+ const { t } = useLocale();
940
942
  const resolvedDensity: RevisionTimelineDensity = density ?? "comfortable";
941
943
 
942
944
  // Collapse/expand mode is opt-in: active only when `branches` are supplied.
@@ -1059,7 +1061,7 @@ export const RevisionTimeline = forwardRef<HTMLDivElement, RevisionTimelineProps
1059
1061
  const from = summaryOf.get(e.from) ?? e.from;
1060
1062
  const to = summaryOf.get(e.to) ?? e.to;
1061
1063
  if (from === to) continue; // collapsed into a single node
1062
- const key = `${from}${to}`;
1064
+ const key = `${from}\u0000${to}`;
1063
1065
  if (seen.has(key)) continue; // de-dup coincident remaps
1064
1066
  seen.add(key);
1065
1067
  out.push({ from, to });
@@ -1102,7 +1104,10 @@ export const RevisionTimeline = forwardRef<HTMLDivElement, RevisionTimelineProps
1102
1104
  {groups.map((group) => (
1103
1105
  <div key={group.dayKey}>
1104
1106
  {groupBy === "day" && <DayHeader label={group.dayLabel} />}
1105
- <ol role="list" aria-label={groupBy === "day" ? group.dayLabel : "Revisions"}>
1107
+ <ol
1108
+ role="list"
1109
+ aria-label={groupBy === "day" ? group.dayLabel : t("ui.revisionTimeline.label")}
1110
+ >
1106
1111
  {group.items.map((it) => {
1107
1112
  if (it.kind === "summary") {
1108
1113
  return (