@neuronection/assistant-ui 0.2.0 → 0.4.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 (57) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +26 -5
  3. package/dist/breadcrumbs.d.ts +24 -0
  4. package/dist/breadcrumbs.js +3 -0
  5. package/dist/breadcrumbs.js.map +1 -0
  6. package/dist/chip-input.d.ts +14 -0
  7. package/dist/chip-input.js +3 -0
  8. package/dist/chip-input.js.map +1 -0
  9. package/dist/chip-list.d.ts +15 -0
  10. package/dist/chip-list.js +3 -0
  11. package/dist/chip-list.js.map +1 -0
  12. package/dist/chunk-5AVBKEDQ.js +74 -0
  13. package/dist/chunk-5AVBKEDQ.js.map +1 -0
  14. package/dist/{chunk-GPIN2ITX.js → chunk-5HXDKOBY.js} +24 -7
  15. package/dist/chunk-5HXDKOBY.js.map +1 -0
  16. package/dist/chunk-5X3RYDPC.js +54 -0
  17. package/dist/chunk-5X3RYDPC.js.map +1 -0
  18. package/dist/chunk-CZ2BL2ZL.js +89 -0
  19. package/dist/chunk-CZ2BL2ZL.js.map +1 -0
  20. package/dist/chunk-MEBOS2FX.js +359 -0
  21. package/dist/chunk-MEBOS2FX.js.map +1 -0
  22. package/dist/chunk-TFS2NUET.js +66 -0
  23. package/dist/chunk-TFS2NUET.js.map +1 -0
  24. package/dist/chunk-TPLQL62V.js +124 -0
  25. package/dist/chunk-TPLQL62V.js.map +1 -0
  26. package/dist/chunk-UIUALJNR.js +110 -0
  27. package/dist/chunk-UIUALJNR.js.map +1 -0
  28. package/dist/chunk-WPO6CDA4.js +92 -0
  29. package/dist/chunk-WPO6CDA4.js.map +1 -0
  30. package/dist/chunk-XLD2BPTT.js +82 -0
  31. package/dist/chunk-XLD2BPTT.js.map +1 -0
  32. package/dist/copy-button.d.ts +14 -0
  33. package/dist/copy-button.js +3 -0
  34. package/dist/copy-button.js.map +1 -0
  35. package/dist/date-picker.d.ts +27 -0
  36. package/dist/date-picker.js +3 -0
  37. package/dist/date-picker.js.map +1 -0
  38. package/dist/form-modal.d.ts +6 -1
  39. package/dist/form-modal.js +1 -1
  40. package/dist/index.d.ts +10 -2
  41. package/dist/index.js +20 -11
  42. package/dist/modal.d.ts +23 -1
  43. package/dist/modal.js +1 -0
  44. package/dist/range-bar.d.ts +15 -0
  45. package/dist/range-bar.js +3 -0
  46. package/dist/range-bar.js.map +1 -0
  47. package/dist/scale-slider.d.ts +18 -0
  48. package/dist/scale-slider.js +3 -0
  49. package/dist/scale-slider.js.map +1 -0
  50. package/dist/styles.css +1 -1
  51. package/dist/table.d.ts +10 -0
  52. package/dist/table.js +3 -0
  53. package/dist/table.js.map +1 -0
  54. package/package.json +36 -2
  55. package/dist/chunk-GPIN2ITX.js.map +0 -1
  56. package/dist/chunk-IFNT6JSQ.js +0 -3
  57. package/dist/chunk-IFNT6JSQ.js.map +0 -1
package/CHANGELOG.md ADDED
@@ -0,0 +1,56 @@
1
+ # @neuronection/assistant-ui
2
+
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`5b2ff4c`](https://github.com/neuronection/assistant-ui/commit/5b2ff4cc6304c90fa8b15497fd554fd0a14ced21) Thanks [@constLiakos](https://github.com/constLiakos)! - Phase-3 groundwork for health-assistant adoption: `FormModal` gains
8
+ `headerActions`, `onReject`/`rejectLabel`, `hideFooter` and `bodyClassName`
9
+ (health's HITL flows need them); new `CopyButton` (clipboard with legacy
10
+ fallback, copied state, `onCopied`/`onCopyError` callbacks — apps own
11
+ toasts/i18n) and `Breadcrumbs` (router-free: `linkComponent` prop for SPA
12
+ links); `themes/health.css` now matches health's real blue-600 accent with a
13
+ `.dark` token block for its class-based dark mode. New `PanelModal`:
14
+ header (icon/title/actions/close) + scrollable body + sticky footer,
15
+ full-screen on mobile — the shared shape both career and health used as
16
+ their hand-rolled `Modal`. `DatePicker` gains `variant="unstyled"` for
17
+ inline dense usage.
18
+
19
+ ## 0.3.0
20
+
21
+ ### Minor Changes
22
+
23
+ - [`e8a56d3`](https://github.com/neuronection/assistant-ui/commit/e8a56d33427fc2e8e03784fe7959037f24efb95a) Thanks [@constLiakos](https://github.com/constLiakos)! - Add the six components career-assistant's Phase-2 adoption needs, all shared
24
+ with health-assistant (catalog T1): `ChipInput`, `ChipList` (superset with
25
+ variants + clickable chips), `Table` (tokenized, with empty state), `RangeBar`
26
+ (with optional `label`/`valueLabel`), `ScaleSlider` (+ `scaleColorForValue`),
27
+ and `DatePicker` (Radix popover calendar with days/months/years views, min/max,
28
+ allowClear, and a roving-tabindex arrow-key day grid — an a11y upgrade over
29
+ both app copies). New dependency: `date-fns`.
30
+
31
+ ### Patch Changes
32
+
33
+ - [`e5252f2`](https://github.com/neuronection/assistant-ui/commit/e5252f26d6f8732a2f5ba4a3fe849b4df50b30c2) Thanks [@constLiakos](https://github.com/constLiakos)! - Ship CHANGELOG.md inside the published package so consuming apps (and their agents) can read what changed without visiting GitHub.
34
+
35
+ ## 0.2.0
36
+
37
+ ### Minor Changes
38
+
39
+ - [`ec2a8bf`](https://github.com/neuronection/assistant-ui/commit/ec2a8bf7d49bb96435990e9b8cca4d74fc31748b) Thanks [@constLiakos](https://github.com/constLiakos)! - Phase 1: 16 new modules for study-assistant full adoption
40
+
41
+ - PopoverButton: self-contained trigger+panel popover on Radix (hover-open, lazy children, closeSignal, controlled mode)
42
+ - InfoButton + FieldLabel: info affordances with popover help
43
+ - Menu: Radix DropdownMenu primitives (Menu, MenuItem, MenuContent…) + items-driven ActionMenu
44
+ - ContextMenu: coordinate-anchored menu with full Radix keyboard/typeahead semantics
45
+ - Combobox + ComboboxMulti: WAI-ARIA listbox pattern, grouping, async mode (onSearchChange + loading), multi-select, clearable
46
+ - FormModal: form-in-modal shell (submit/cancel/busy/disabled) on Modal primitives
47
+ - Wizard + Stepper: wizard state machine with per-step validation gates, modal and drawer variants; dots/labels steppers
48
+ - Ported from study-assistant ui/: CheckIndicator, SearchInput, ExpandableSearch, SelectionBar, ViewToggle, ErrorBanner, UndoNotice, Marquee (useMarquee/MarqueeBand/MarqueeSurface)
49
+
50
+ All components: forwardRef where DOM-emitting, semantic --as-\* tokens only, data-as hooks, English-default label props, keyboard-nav + axe tests.
51
+
52
+ ## 0.1.0
53
+
54
+ ### Minor Changes
55
+
56
+ - [`a211003`](https://github.com/neuronection/assistant-ui/commit/a211003348a5248f65a7995806500325408b6f92) Thanks [@constLiakos](https://github.com/constLiakos)! - Initial release (Phase 0): Button, Badge, Card, Modal, Popover, Tooltip + InfoTooltip, Input, Spinner, EmptyState, ConfirmationModal, Portal, ThemeScope and `cn()` — CSS-variable design tokens, precompiled `styles.css`, React 18/19 and Tailwind 3/4 compatible.
package/README.md CHANGED
@@ -65,11 +65,32 @@ without configuration. Do **not** add this package to your Tailwind
65
65
 
66
66
  ## Components
67
67
 
68
- Button, Badge, Card (+Header/Title/Description/Content/Footer), Modal
69
- (+Trigger/Header/Title/Description/Footer/Close), Popover, Tooltip +
70
- InfoTooltip, Input, Spinner, EmptyState, ConfirmationModal, Portal,
71
- ThemeScope, `cn()`. Full props in the
72
- [gallery](https://neuronection.github.io/assistant-ui/) (Ladle build).
68
+ Every module has its own entry point (`import { Menu } from
69
+ '@neuronection/assistant-ui/menu'`) and everything is re-exported from the
70
+ package root. Authoritative props for your installed version: the `.d.ts`
71
+ files in the package's `dist/`. Visual reference:
72
+ [gallery](https://neuronection.github.io/assistant-ui/).
73
+
74
+ | Module | Exports |
75
+ |---|---|
76
+ | `button` / `badge` / `card` | `Button`, `buttonVariants` · `Badge`, `badgeVariants` · `Card`, `CardHeader/Title/Description/Content/Footer` |
77
+ | `modal` / `confirmation-modal` / `form-modal` | `Modal` + parts, `modalContentVariants` · `ConfirmationModal` · `FormModal` (form-in-modal shell) |
78
+ | `popover` / `popover-button` | compound Radix `Popover` + parts · `PopoverButton` (self-contained trigger + panel, hover-open, lazy children, `closeSignal`) |
79
+ | `menu` / `context-menu` | `Menu`, `MenuTrigger/Content/Item/Separator/Label`, `ActionMenu` (items-driven) · `ContextMenu` (coordinate-anchored, `{x, y, items, onClose}`) |
80
+ | `combobox` | `Combobox`, `ComboboxMulti` — async mode (`onSearchChange` + `loading`), grouping, full keyboard nav |
81
+ | `tooltip` | `Tooltip` + parts, `InfoTooltip` |
82
+ | `wizard` | `Wizard` (steps config + `renderStep`, per-step validation gates, modal/drawer variants), `Stepper` (dots/labels) |
83
+ | `input` / `search-input` / `expandable-search` | `Input` (label/hint/error wiring) · `SearchInput` · `ExpandableSearch` |
84
+ | `chip-input` / `chip-list` | `ChipInput` (Enter/comma commit, paste-splits, Backspace-removes) · `ChipList` (variant pills, clickable, removable) |
85
+ | `date-picker` | `DatePicker` (popover calendar: days/months/years views, min/max, `allowClear`, arrow-key day grid) |
86
+ | `range-bar` / `scale-slider` | `RangeBar` (low–high band + value dot) · `ScaleSlider` (+ `scaleColorForValue`) |
87
+ | `table` | `Table` (headers/rows, built-in empty state) |
88
+ | `check-indicator` / `selection-bar` / `view-toggle` | `CheckIndicator` (tri-state) · `SelectionBar` (bulk-select bar) · `ViewToggle` (grid/list) |
89
+ | `error-banner` / `undo-notice` / `empty-state` / `spinner` | presentational feedback: alert banner with `action` slot · undo toast · `EmptyState` · `Spinner` |
90
+ | `info-button` / `field-label` | `InfoButton` (info popover) · `FieldLabel` (label + info affordance) |
91
+ | `copy-button` / `breadcrumbs` | `CopyButton` (clipboard + copied state) · `Breadcrumbs` (SPA links via `linkComponent`) |
92
+ | `marquee` | `useMarquee`, `MarqueeSurface`, `MarqueeBand` (rubber-band selection) |
93
+ | `portal` / `theme-scope` / `tokens` | `Portal` · `ThemeScope` · token name lists + types |
73
94
 
74
95
  API rules: controlled-first, `className` merges (never replaces), `asChild`
75
96
  where it makes sense, refs forwarded everywhere, English label props with
@@ -0,0 +1,24 @@
1
+ import * as React from 'react';
2
+
3
+ interface BreadcrumbItem {
4
+ label: string;
5
+ href?: string;
6
+ icon?: React.ReactNode;
7
+ }
8
+ interface BreadcrumbLinkProps {
9
+ href: string;
10
+ className?: string;
11
+ title?: string;
12
+ 'aria-label'?: string;
13
+ children: React.ReactNode;
14
+ }
15
+ interface BreadcrumbsProps extends React.ComponentProps<'nav'> {
16
+ items?: BreadcrumbItem[];
17
+ currentLabel?: string;
18
+ homeHref?: string;
19
+ homeLabel?: string;
20
+ linkComponent?: React.ComponentType<BreadcrumbLinkProps>;
21
+ }
22
+ declare const Breadcrumbs: React.ForwardRefExoticComponent<Omit<BreadcrumbsProps, "ref"> & React.RefAttributes<HTMLElement>>;
23
+
24
+ export { type BreadcrumbItem, type BreadcrumbLinkProps, Breadcrumbs, type BreadcrumbsProps };
@@ -0,0 +1,3 @@
1
+ export { Breadcrumbs } from './chunk-TFS2NUET.js';
2
+ //# sourceMappingURL=breadcrumbs.js.map
3
+ //# sourceMappingURL=breadcrumbs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"breadcrumbs.js"}
@@ -0,0 +1,14 @@
1
+ import * as React from 'react';
2
+
3
+ interface ChipInputProps extends Omit<React.ComponentProps<'div'>, 'onChange'> {
4
+ value: string[];
5
+ onChange: (value: string[]) => void;
6
+ placeholder?: string;
7
+ separators?: string[];
8
+ inputLabel?: string;
9
+ removeLabel?: string;
10
+ disabled?: boolean;
11
+ }
12
+ declare const ChipInput: React.ForwardRefExoticComponent<Omit<ChipInputProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
13
+
14
+ export { ChipInput, type ChipInputProps };
@@ -0,0 +1,3 @@
1
+ export { ChipInput } from './chunk-TPLQL62V.js';
2
+ //# sourceMappingURL=chip-input.js.map
3
+ //# sourceMappingURL=chip-input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"chip-input.js"}
@@ -0,0 +1,15 @@
1
+ import * as React from 'react';
2
+
3
+ type ChipVariant = 'neutral' | 'primary' | 'success' | 'warning' | 'danger';
4
+ interface ChipListProps extends React.ComponentProps<'div'> {
5
+ items: ReadonlyArray<string | null | undefined>;
6
+ onRemove?: (item: string) => void;
7
+ onItemClick?: (value: string, index: number) => void;
8
+ variant?: ChipVariant;
9
+ emptyText?: string;
10
+ showChevron?: boolean;
11
+ removeLabel?: string;
12
+ }
13
+ declare const ChipList: React.ForwardRefExoticComponent<Omit<ChipListProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
14
+
15
+ export { ChipList, type ChipListProps, type ChipVariant };
@@ -0,0 +1,3 @@
1
+ export { ChipList } from './chunk-XLD2BPTT.js';
2
+ //# sourceMappingURL=chip-list.js.map
3
+ //# sourceMappingURL=chip-list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"chip-list.js"}
@@ -0,0 +1,74 @@
1
+ import { cn } from './chunk-BLJWR4ZV.js';
2
+ import * as React from 'react';
3
+ import { jsxs, jsx } from 'react/jsx-runtime';
4
+
5
+ function pct(value, domainMin, domainMax) {
6
+ if (domainMax === domainMin) return 0;
7
+ return (value - domainMin) / (domainMax - domainMin) * 100;
8
+ }
9
+ function fmt(value) {
10
+ return Number.isInteger(value) ? value.toLocaleString() : value.toFixed(1).replace(/\.0$/, "");
11
+ }
12
+ var RangeBar = React.forwardRef(
13
+ function RangeBar2({ low, high, min, max, value, unit, label, valueLabel = "You", className, ...props }, ref) {
14
+ if (!Number.isFinite(low) || !Number.isFinite(high) || high <= low) {
15
+ return null;
16
+ }
17
+ const span = high - low;
18
+ let domainMin = min ?? low - span;
19
+ let domainMax = max ?? high + span;
20
+ if (domainMin < 0 && low >= 0) {
21
+ const shift = -domainMin;
22
+ domainMin = 0;
23
+ domainMax += shift;
24
+ }
25
+ if (domainMax <= domainMin) domainMax = domainMin + span;
26
+ const left = Math.max(0, Math.min(100, pct(low, domainMin, domainMax)));
27
+ const right = Math.max(0, Math.min(100, pct(high, domainMin, domainMax)));
28
+ const valuePct = value !== null && value !== void 0 && Number.isFinite(value) ? Math.max(0, Math.min(100, pct(value, domainMin, domainMax))) : null;
29
+ return /* @__PURE__ */ jsxs(
30
+ "div",
31
+ {
32
+ ref,
33
+ "data-as": "range-bar",
34
+ className: cn("w-full", className),
35
+ ...props,
36
+ children: [
37
+ label ? /* @__PURE__ */ jsx("p", { className: "mb-1 text-xs text-[var(--as-muted-fg)]", children: label }) : null,
38
+ /* @__PURE__ */ jsxs("div", { className: "relative h-3.5 w-full rounded-full bg-[var(--as-muted)]", children: [
39
+ /* @__PURE__ */ jsx(
40
+ "div",
41
+ {
42
+ className: "absolute h-full rounded-full bg-[color-mix(in_srgb,var(--as-primary)_30%,transparent)]",
43
+ style: { left: `${left}%`, width: `${Math.max(0, right - left)}%` },
44
+ title: `${label ? `${label}: ` : ""}${fmt(low)} \u2013 ${fmt(high)}${unit ? ` ${unit}` : ""}`
45
+ }
46
+ ),
47
+ valuePct !== null ? /* @__PURE__ */ jsx(
48
+ "div",
49
+ {
50
+ className: "absolute top-1/2 size-2.5 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-[var(--as-surface-raised)] bg-[var(--as-primary)] shadow-[var(--as-shadow-1)]",
51
+ style: { left: `${valuePct}%` },
52
+ title: `${valueLabel}: ${fmt(value)}${unit ? ` ${unit}` : ""}`
53
+ }
54
+ ) : null
55
+ ] }),
56
+ /* @__PURE__ */ jsxs("div", { className: "mt-0.5 flex justify-between text-[10px] text-[var(--as-muted-fg)]", children: [
57
+ /* @__PURE__ */ jsx("span", { children: fmt(domainMin) }),
58
+ /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
59
+ fmt(low),
60
+ " \u2013 ",
61
+ fmt(high),
62
+ unit ? ` ${unit}` : ""
63
+ ] }),
64
+ /* @__PURE__ */ jsx("span", { children: fmt(domainMax) })
65
+ ] })
66
+ ]
67
+ }
68
+ );
69
+ }
70
+ );
71
+
72
+ export { RangeBar };
73
+ //# sourceMappingURL=chunk-5AVBKEDQ.js.map
74
+ //# sourceMappingURL=chunk-5AVBKEDQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/range-bar/RangeBar.tsx"],"names":["RangeBar"],"mappings":"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","file":"chunk-5AVBKEDQ.js","sourcesContent":["import * as React from 'react'\nimport { cn } from '../../lib/utils'\n\nexport interface RangeBarProps extends React.ComponentProps<'div'> {\n low: number\n high: number\n min?: number\n max?: number\n value?: number | null\n unit?: string\n label?: string\n valueLabel?: string\n}\n\nfunction pct(value: number, domainMin: number, domainMax: number): number {\n if (domainMax === domainMin) return 0\n return ((value - domainMin) / (domainMax - domainMin)) * 100\n}\n\nfunction fmt(value: number): string {\n return Number.isInteger(value)\n ? value.toLocaleString()\n : value.toFixed(1).replace(/\\.0$/, '')\n}\n\nexport const RangeBar = React.forwardRef<HTMLDivElement, RangeBarProps>(\n function RangeBar(\n { low, high, min, max, value, unit, label, valueLabel = 'You', className, ...props },\n ref,\n ) {\n if (!Number.isFinite(low) || !Number.isFinite(high) || high <= low) {\n return null\n }\n\n const span = high - low\n let domainMin = min ?? low - span\n let domainMax = max ?? high + span\n if (domainMin < 0 && low >= 0) {\n const shift = -domainMin\n domainMin = 0\n domainMax += shift\n }\n if (domainMax <= domainMin) domainMax = domainMin + span\n\n const left = Math.max(0, Math.min(100, pct(low, domainMin, domainMax)))\n const right = Math.max(0, Math.min(100, pct(high, domainMin, domainMax)))\n const valuePct =\n value !== null && value !== undefined && Number.isFinite(value)\n ? Math.max(0, Math.min(100, pct(value, domainMin, domainMax)))\n : null\n\n return (\n <div\n ref={ref}\n data-as=\"range-bar\"\n className={cn('w-full', className)}\n {...props}\n >\n {label ? (\n <p className=\"mb-1 text-xs text-[var(--as-muted-fg)]\">{label}</p>\n ) : null}\n <div className=\"relative h-3.5 w-full rounded-full bg-[var(--as-muted)]\">\n <div\n className=\"absolute h-full rounded-full bg-[color-mix(in_srgb,var(--as-primary)_30%,transparent)]\"\n style={{ left: `${left}%`, width: `${Math.max(0, right - left)}%` }}\n title={`${label ? `${label}: ` : ''}${fmt(low)} – ${fmt(high)}${unit ? ` ${unit}` : ''}`}\n />\n {valuePct !== null ? (\n <div\n className=\"absolute top-1/2 size-2.5 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-[var(--as-surface-raised)] bg-[var(--as-primary)] shadow-[var(--as-shadow-1)]\"\n style={{ left: `${valuePct}%` }}\n title={`${valueLabel}: ${fmt(value as number)}${unit ? ` ${unit}` : ''}`}\n />\n ) : null}\n </div>\n <div className=\"mt-0.5 flex justify-between text-[10px] text-[var(--as-muted-fg)]\">\n <span>{fmt(domainMin)}</span>\n <span className=\"font-medium\">\n {fmt(low)} – {fmt(high)}\n {unit ? ` ${unit}` : ''}\n </span>\n <span>{fmt(domainMax)}</span>\n </div>\n </div>\n )\n },\n)\n"]}
@@ -9,6 +9,7 @@ function FormModal({
9
9
  title,
10
10
  description,
11
11
  icon: Icon,
12
+ headerActions,
12
13
  children,
13
14
  onSubmit,
14
15
  submitting = false,
@@ -18,7 +19,11 @@ function FormModal({
18
19
  closeLabel = "Close",
19
20
  size = "md",
20
21
  contentClassName,
21
- footer
22
+ bodyClassName,
23
+ footer,
24
+ hideFooter = false,
25
+ onReject,
26
+ rejectLabel = "Reject"
22
27
  }) {
23
28
  return /* @__PURE__ */ jsx(Modal, { open, onOpenChange, children: /* @__PURE__ */ jsx(ModalContent, { size, closeLabel, "aria-describedby": void 0, children: /* @__PURE__ */ jsxs(
24
29
  "form",
@@ -35,12 +40,24 @@ function FormModal({
35
40
  /* @__PURE__ */ jsxs(ModalHeader, { children: [
36
41
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
37
42
  Icon ? /* @__PURE__ */ jsx("span", { className: "rounded-[var(--as-radius)] bg-[var(--as-secondary)] p-2 text-[var(--as-fg)]", children: /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true", className: "size-5" }) }) : null,
38
- /* @__PURE__ */ jsx(ModalTitle, { children: title })
43
+ /* @__PURE__ */ jsx(ModalTitle, { children: title }),
44
+ headerActions ? /* @__PURE__ */ jsx("span", { className: "ml-auto flex items-center gap-1.5", children: headerActions }) : null
39
45
  ] }),
40
46
  description ? /* @__PURE__ */ jsx(ModalDescription, { children: description }) : null
41
47
  ] }),
42
- /* @__PURE__ */ jsx("div", { className: "px-6 pb-2", children }),
43
- footer ?? /* @__PURE__ */ jsxs(ModalFooter, { children: [
48
+ /* @__PURE__ */ jsx("div", { className: cn("px-6 pb-2", bodyClassName), children }),
49
+ footer ?? (hideFooter ? null : /* @__PURE__ */ jsxs(ModalFooter, { children: [
50
+ onReject ? /* @__PURE__ */ jsx(
51
+ Button,
52
+ {
53
+ type: "button",
54
+ variant: "destructive",
55
+ onClick: onReject,
56
+ disabled: submitting,
57
+ className: "sm:mr-auto",
58
+ children: rejectLabel
59
+ }
60
+ ) : null,
44
61
  /* @__PURE__ */ jsx(
45
62
  Button,
46
63
  {
@@ -52,12 +69,12 @@ function FormModal({
52
69
  }
53
70
  ),
54
71
  onSubmit ? /* @__PURE__ */ jsx(Button, { type: "submit", disabled: submitDisabled || submitting, loading: submitting, children: submitLabel }) : null
55
- ] })
72
+ ] }))
56
73
  ]
57
74
  }
58
75
  ) }) });
59
76
  }
60
77
 
61
78
  export { FormModal };
62
- //# sourceMappingURL=chunk-GPIN2ITX.js.map
63
- //# sourceMappingURL=chunk-GPIN2ITX.js.map
79
+ //# sourceMappingURL=chunk-5HXDKOBY.js.map
80
+ //# sourceMappingURL=chunk-5HXDKOBY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/form-modal/FormModal.tsx"],"names":[],"mappings":";;;;;AAoCO,SAAS,SAAA,CAAU;AAAA,EACxB,IAAA;AAAA,EACA,YAAA;AAAA,EACA,KAAA;AAAA,EACA,WAAA;AAAA,EACA,IAAA,EAAM,IAAA;AAAA,EACN,aAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA,UAAA,GAAa,KAAA;AAAA,EACb,cAAA,GAAiB,KAAA;AAAA,EACjB,WAAA,GAAc,MAAA;AAAA,EACd,WAAA,GAAc,QAAA;AAAA,EACd,UAAA,GAAa,OAAA;AAAA,EACb,IAAA,GAAO,IAAA;AAAA,EACP,gBAAA;AAAA,EACA,aAAA;AAAA,EACA,MAAA;AAAA,EACA,UAAA,GAAa,KAAA;AAAA,EACb,QAAA;AAAA,EACA,WAAA,GAAc;AAChB,CAAA,EAAmB;AACjB,EAAA,uBACE,GAAA,CAAC,SAAM,IAAA,EAAY,YAAA,EACjB,8BAAC,YAAA,EAAA,EAAa,IAAA,EAAY,UAAA,EAAwB,kBAAA,EAAkB,MAAA,EAClE,QAAA,kBAAA,IAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAQ,YAAA;AAAA,MACR,SAAA,EAAW,EAAA,CAAG,UAAA,EAAY,gBAAgB,CAAA;AAAA,MAC1C,QAAA,EAAU,CAAC,KAAA,KAAU;AACnB,QAAA,KAAA,CAAM,cAAA,EAAe;AACrB,QAAA,IAAI,CAAC,UAAA,IAAc,CAAC,cAAA,EAAgB;AAClC,UAAA,QAAA,IAAW;AAAA,QACb;AAAA,MACF,CAAA;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAA,IAAA,CAAC,WAAA,EAAA,EACC,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,yBAAA,EACZ,QAAA,EAAA;AAAA,YAAA,IAAA,mBACC,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,6EAAA,EACd,QAAA,kBAAA,GAAA,CAAC,IAAA,EAAA,EAAK,aAAA,EAAY,MAAA,EAAO,SAAA,EAAU,QAAA,EAAS,CAAA,EAC9C,CAAA,GACE,IAAA;AAAA,4BACJ,GAAA,CAAC,cAAY,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,YAClB,gCACC,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,mCAAA,EAAqC,yBAAc,CAAA,GACjE;AAAA,WAAA,EACN,CAAA;AAAA,UACC,WAAA,mBAAc,GAAA,CAAC,gBAAA,EAAA,EAAkB,QAAA,EAAA,WAAA,EAAY,CAAA,GAAsB;AAAA,SAAA,EACtE,CAAA;AAAA,4BACC,KAAA,EAAA,EAAI,SAAA,EAAW,GAAG,WAAA,EAAa,aAAa,GAAI,QAAA,EAAS,CAAA;AAAA,QACzD,MAAA,KAAW,UAAA,GAAa,IAAA,mBACvB,IAAA,CAAC,WAAA,EAAA,EACE,QAAA,EAAA;AAAA,UAAA,QAAA,mBACC,GAAA;AAAA,YAAC,MAAA;AAAA,YAAA;AAAA,cACC,IAAA,EAAK,QAAA;AAAA,cACL,OAAA,EAAQ,aAAA;AAAA,cACR,OAAA,EAAS,QAAA;AAAA,cACT,QAAA,EAAU,UAAA;AAAA,cACV,SAAA,EAAU,YAAA;AAAA,cAET,QAAA,EAAA;AAAA;AAAA,WACH,GACE,IAAA;AAAA,0BACJ,GAAA;AAAA,YAAC,MAAA;AAAA,YAAA;AAAA,cACC,IAAA,EAAK,QAAA;AAAA,cACL,OAAA,EAAQ,SAAA;AAAA,cACR,OAAA,EAAS,MAAM,YAAA,CAAa,KAAK,CAAA;AAAA,cACjC,QAAA,EAAU,UAAA;AAAA,cAET,QAAA,EAAA;AAAA;AAAA,WACH;AAAA,UACC,QAAA,mBACC,GAAA,CAAC,MAAA,EAAA,EAAO,IAAA,EAAK,QAAA,EAAS,QAAA,EAAU,cAAA,IAAkB,UAAA,EAAY,OAAA,EAAS,UAAA,EACpE,QAAA,EAAA,WAAA,EACH,CAAA,GACE;AAAA,SAAA,EACN,CAAA;AAAA;AAAA;AAAA,KAGN,CAAA,EACF,CAAA;AAEJ","file":"chunk-5HXDKOBY.js","sourcesContent":["import * as React from 'react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '../../lib/utils'\nimport { Button } from '../button/Button'\nimport {\n Modal,\n ModalContent,\n ModalDescription,\n ModalFooter,\n ModalHeader,\n ModalTitle,\n} from '../modal/Modal'\n\nexport interface FormModalProps {\n open: boolean\n onOpenChange: (open: boolean) => void\n title: React.ReactNode\n description?: React.ReactNode\n icon?: LucideIcon\n headerActions?: React.ReactNode\n children: React.ReactNode\n onSubmit?: () => void\n submitting?: boolean\n submitDisabled?: boolean\n submitLabel?: string\n cancelLabel?: string\n closeLabel?: string\n size?: 'sm' | 'md' | 'lg' | 'xl'\n contentClassName?: string\n bodyClassName?: string\n footer?: React.ReactNode\n hideFooter?: boolean\n onReject?: () => void\n rejectLabel?: string\n}\n\nexport function FormModal({\n open,\n onOpenChange,\n title,\n description,\n icon: Icon,\n headerActions,\n children,\n onSubmit,\n submitting = false,\n submitDisabled = false,\n submitLabel = 'Save',\n cancelLabel = 'Cancel',\n closeLabel = 'Close',\n size = 'md',\n contentClassName,\n bodyClassName,\n footer,\n hideFooter = false,\n onReject,\n rejectLabel = 'Reject',\n}: FormModalProps) {\n return (\n <Modal open={open} onOpenChange={onOpenChange}>\n <ModalContent size={size} closeLabel={closeLabel} aria-describedby={undefined}>\n <form\n data-as=\"form-modal\"\n className={cn('contents', contentClassName)}\n onSubmit={(event) => {\n event.preventDefault()\n if (!submitting && !submitDisabled) {\n onSubmit?.()\n }\n }}\n >\n <ModalHeader>\n <div className=\"flex items-center gap-3\">\n {Icon ? (\n <span className=\"rounded-[var(--as-radius)] bg-[var(--as-secondary)] p-2 text-[var(--as-fg)]\">\n <Icon aria-hidden=\"true\" className=\"size-5\" />\n </span>\n ) : null}\n <ModalTitle>{title}</ModalTitle>\n {headerActions ? (\n <span className=\"ml-auto flex items-center gap-1.5\">{headerActions}</span>\n ) : null}\n </div>\n {description ? <ModalDescription>{description}</ModalDescription> : null}\n </ModalHeader>\n <div className={cn('px-6 pb-2', bodyClassName)}>{children}</div>\n {footer ?? (hideFooter ? null : (\n <ModalFooter>\n {onReject ? (\n <Button\n type=\"button\"\n variant=\"destructive\"\n onClick={onReject}\n disabled={submitting}\n className=\"sm:mr-auto\"\n >\n {rejectLabel}\n </Button>\n ) : null}\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => onOpenChange(false)}\n disabled={submitting}\n >\n {cancelLabel}\n </Button>\n {onSubmit ? (\n <Button type=\"submit\" disabled={submitDisabled || submitting} loading={submitting}>\n {submitLabel}\n </Button>\n ) : null}\n </ModalFooter>\n ))}\n </form>\n </ModalContent>\n </Modal>\n )\n}\n"]}
@@ -0,0 +1,54 @@
1
+ import { cn } from './chunk-BLJWR4ZV.js';
2
+ import * as React from 'react';
3
+ import { jsx, jsxs } from 'react/jsx-runtime';
4
+
5
+ var Table = React.forwardRef(
6
+ function Table2({ headers, rows, emptyText = "Nothing here yet", className, ...props }, ref) {
7
+ return /* @__PURE__ */ jsx(
8
+ "div",
9
+ {
10
+ ref,
11
+ "data-as": "table",
12
+ className: cn("overflow-x-auto", className),
13
+ ...props,
14
+ children: /* @__PURE__ */ jsxs("table", { className: "min-w-full divide-y divide-[var(--as-border)]", children: [
15
+ /* @__PURE__ */ jsx("thead", { className: "bg-[var(--as-muted)]", children: /* @__PURE__ */ jsx("tr", { children: headers.map((header, index) => /* @__PURE__ */ jsx(
16
+ "th",
17
+ {
18
+ scope: "col",
19
+ className: "px-4 py-2.5 text-left text-xs font-medium uppercase tracking-wider text-[var(--as-muted-fg)]",
20
+ children: header
21
+ },
22
+ index
23
+ )) }) }),
24
+ /* @__PURE__ */ jsx("tbody", { className: "divide-y divide-[color-mix(in_srgb,var(--as-border)_55%,transparent)] bg-[var(--as-surface-raised)]", children: rows.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
25
+ "td",
26
+ {
27
+ colSpan: headers.length,
28
+ className: "px-4 py-6 text-center text-sm text-[var(--as-muted-fg)]",
29
+ children: emptyText
30
+ }
31
+ ) }) : rows.map((row, rowIndex) => /* @__PURE__ */ jsx(
32
+ "tr",
33
+ {
34
+ className: "transition-colors hover:bg-[color-mix(in_srgb,var(--as-muted)_60%,transparent)]",
35
+ children: row.map((cell, cellIndex) => /* @__PURE__ */ jsx(
36
+ "td",
37
+ {
38
+ className: "whitespace-nowrap px-4 py-3 text-sm text-[var(--as-fg)]",
39
+ children: cell
40
+ },
41
+ cellIndex
42
+ ))
43
+ },
44
+ rowIndex
45
+ )) })
46
+ ] })
47
+ }
48
+ );
49
+ }
50
+ );
51
+
52
+ export { Table };
53
+ //# sourceMappingURL=chunk-5X3RYDPC.js.map
54
+ //# sourceMappingURL=chunk-5X3RYDPC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/table/Table.tsx"],"names":["Table"],"mappings":";;;;AASO,IAAM,KAAA,GAAc,KAAA,CAAA,UAAA;AAAA,EACzB,SAASA,MAAAA,CAAM,EAAE,OAAA,EAAS,IAAA,EAAM,SAAA,GAAY,kBAAA,EAAoB,SAAA,EAAW,GAAG,KAAA,EAAM,EAAG,GAAA,EAAK;AAC1F,IAAA,uBACE,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,SAAA,EAAQ,OAAA;AAAA,QACR,SAAA,EAAW,EAAA,CAAG,iBAAA,EAAmB,SAAS,CAAA;AAAA,QACzC,GAAG,KAAA;AAAA,QAEJ,QAAA,kBAAA,IAAA,CAAC,OAAA,EAAA,EAAM,SAAA,EAAU,+CAAA,EACf,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,OAAA,EAAA,EAAM,WAAU,sBAAA,EACf,QAAA,kBAAA,GAAA,CAAC,QACE,QAAA,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAC,MAAA,EAAQ,KAAA,qBACpB,GAAA;AAAA,YAAC,IAAA;AAAA,YAAA;AAAA,cAEC,KAAA,EAAM,KAAA;AAAA,cACN,SAAA,EAAU,8FAAA;AAAA,cAET,QAAA,EAAA;AAAA,aAAA;AAAA,YAJI;AAAA,WAMR,GACH,CAAA,EACF,CAAA;AAAA,0BACA,GAAA,CAAC,WAAM,SAAA,EAAU,qGAAA,EACd,eAAK,MAAA,KAAW,CAAA,uBACd,IAAA,EAAA,EACC,QAAA,kBAAA,GAAA;AAAA,YAAC,IAAA;AAAA,YAAA;AAAA,cACC,SAAS,OAAA,CAAQ,MAAA;AAAA,cACjB,SAAA,EAAU,yDAAA;AAAA,cAET,QAAA,EAAA;AAAA;AAAA,aAEL,CAAA,GAEA,IAAA,CAAK,GAAA,CAAI,CAAC,KAAK,QAAA,qBACb,GAAA;AAAA,YAAC,IAAA;AAAA,YAAA;AAAA,cAEC,SAAA,EAAU,iFAAA;AAAA,cAET,QAAA,EAAA,GAAA,CAAI,GAAA,CAAI,CAAC,IAAA,EAAM,SAAA,qBACd,GAAA;AAAA,gBAAC,IAAA;AAAA,gBAAA;AAAA,kBAEC,SAAA,EAAU,yDAAA;AAAA,kBAET,QAAA,EAAA;AAAA,iBAAA;AAAA,gBAHI;AAAA,eAKR;AAAA,aAAA;AAAA,YAVI;AAAA,WAYR,CAAA,EAEL;AAAA,SAAA,EACF;AAAA;AAAA,KACF;AAAA,EAEJ;AACF","file":"chunk-5X3RYDPC.js","sourcesContent":["import * as React from 'react'\nimport { cn } from '../../lib/utils'\n\nexport interface TableProps extends React.ComponentProps<'div'> {\n headers: string[]\n rows: React.ReactNode[][]\n emptyText?: string\n}\n\nexport const Table = React.forwardRef<HTMLDivElement, TableProps>(\n function Table({ headers, rows, emptyText = 'Nothing here yet', className, ...props }, ref) {\n return (\n <div\n ref={ref}\n data-as=\"table\"\n className={cn('overflow-x-auto', className)}\n {...props}\n >\n <table className=\"min-w-full divide-y divide-[var(--as-border)]\">\n <thead className=\"bg-[var(--as-muted)]\">\n <tr>\n {headers.map((header, index) => (\n <th\n key={index}\n scope=\"col\"\n className=\"px-4 py-2.5 text-left text-xs font-medium uppercase tracking-wider text-[var(--as-muted-fg)]\"\n >\n {header}\n </th>\n ))}\n </tr>\n </thead>\n <tbody className=\"divide-y divide-[color-mix(in_srgb,var(--as-border)_55%,transparent)] bg-[var(--as-surface-raised)]\">\n {rows.length === 0 ? (\n <tr>\n <td\n colSpan={headers.length}\n className=\"px-4 py-6 text-center text-sm text-[var(--as-muted-fg)]\"\n >\n {emptyText}\n </td>\n </tr>\n ) : (\n rows.map((row, rowIndex) => (\n <tr\n key={rowIndex}\n className=\"transition-colors hover:bg-[color-mix(in_srgb,var(--as-muted)_60%,transparent)]\"\n >\n {row.map((cell, cellIndex) => (\n <td\n key={cellIndex}\n className=\"whitespace-nowrap px-4 py-3 text-sm text-[var(--as-fg)]\"\n >\n {cell}\n </td>\n ))}\n </tr>\n ))\n )}\n </tbody>\n </table>\n </div>\n )\n },\n)\n"]}
@@ -0,0 +1,89 @@
1
+ import { cn } from './chunk-BLJWR4ZV.js';
2
+ import * as React from 'react';
3
+ import { Check, Copy } from 'lucide-react';
4
+ import { jsx } from 'react/jsx-runtime';
5
+
6
+ function writeClipboard(value) {
7
+ if (navigator?.clipboard?.writeText) {
8
+ return navigator.clipboard.writeText(value);
9
+ }
10
+ return new Promise((resolve, reject) => {
11
+ try {
12
+ const textarea = document.createElement("textarea");
13
+ textarea.value = value;
14
+ textarea.style.position = "fixed";
15
+ textarea.style.opacity = "0";
16
+ document.body.appendChild(textarea);
17
+ textarea.select();
18
+ document.execCommand("copy");
19
+ document.body.removeChild(textarea);
20
+ resolve();
21
+ } catch (error) {
22
+ reject(error);
23
+ }
24
+ });
25
+ }
26
+ var CopyButton = React.forwardRef(
27
+ function CopyButton2({
28
+ value,
29
+ label = "Copy",
30
+ size = 14,
31
+ hideWhenEmpty = true,
32
+ copiedDuration = 1500,
33
+ onCopied,
34
+ onCopyError,
35
+ className,
36
+ onClick,
37
+ ...props
38
+ }, ref) {
39
+ const [copied, setCopied] = React.useState(false);
40
+ const timer = React.useRef(null);
41
+ React.useEffect(
42
+ () => () => {
43
+ if (timer.current) clearTimeout(timer.current);
44
+ },
45
+ []
46
+ );
47
+ if (hideWhenEmpty && !value) return null;
48
+ const Icon = copied ? Check : Copy;
49
+ return /* @__PURE__ */ jsx(
50
+ "button",
51
+ {
52
+ ref,
53
+ type: "button",
54
+ "data-as": "copy-button",
55
+ "data-copied": copied || void 0,
56
+ "aria-label": label,
57
+ title: label,
58
+ onClick: (event) => {
59
+ onClick?.(event);
60
+ if (event.defaultPrevented || !value) return;
61
+ event.preventDefault();
62
+ event.stopPropagation();
63
+ writeClipboard(value).then(
64
+ () => {
65
+ setCopied(true);
66
+ if (timer.current) clearTimeout(timer.current);
67
+ timer.current = setTimeout(() => setCopied(false), copiedDuration);
68
+ onCopied?.();
69
+ },
70
+ (error) => {
71
+ onCopyError?.(error);
72
+ }
73
+ );
74
+ },
75
+ className: cn(
76
+ "inline-flex cursor-pointer items-center justify-center rounded-[var(--as-radius-sm)] p-0.5 text-[var(--as-muted-fg)] transition-colors hover:text-[var(--as-primary)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
77
+ copied && "text-[var(--as-success)] hover:text-[var(--as-success)]",
78
+ className
79
+ ),
80
+ ...props,
81
+ children: /* @__PURE__ */ jsx(Icon, { style: { width: size, height: size }, "aria-hidden": true })
82
+ }
83
+ );
84
+ }
85
+ );
86
+
87
+ export { CopyButton };
88
+ //# sourceMappingURL=chunk-CZ2BL2ZL.js.map
89
+ //# sourceMappingURL=chunk-CZ2BL2ZL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/copy-button/CopyButton.tsx"],"names":["CopyButton"],"mappings":"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","file":"chunk-CZ2BL2ZL.js","sourcesContent":["import * as React from 'react'\nimport { Check, Copy } from 'lucide-react'\nimport { cn } from '../../lib/utils'\n\nexport interface CopyButtonProps\n extends Omit<React.ComponentProps<'button'>, 'value' | 'children'> {\n value: string\n label?: string\n size?: number\n hideWhenEmpty?: boolean\n copiedDuration?: number\n onCopied?: () => void\n onCopyError?: (error: unknown) => void\n}\n\nfunction writeClipboard(value: string): Promise<void> {\n if (navigator?.clipboard?.writeText) {\n return navigator.clipboard.writeText(value)\n }\n return new Promise((resolve, reject) => {\n try {\n const textarea = document.createElement('textarea')\n textarea.value = value\n textarea.style.position = 'fixed'\n textarea.style.opacity = '0'\n document.body.appendChild(textarea)\n textarea.select()\n document.execCommand('copy')\n document.body.removeChild(textarea)\n resolve()\n } catch (error) {\n reject(error)\n }\n })\n}\n\nexport const CopyButton = React.forwardRef<HTMLButtonElement, CopyButtonProps>(\n function CopyButton(\n {\n value,\n label = 'Copy',\n size = 14,\n hideWhenEmpty = true,\n copiedDuration = 1500,\n onCopied,\n onCopyError,\n className,\n onClick,\n ...props\n },\n ref,\n ) {\n const [copied, setCopied] = React.useState(false)\n const timer = React.useRef<ReturnType<typeof setTimeout> | null>(null)\n\n React.useEffect(\n () => () => {\n if (timer.current) clearTimeout(timer.current)\n },\n [],\n )\n\n if (hideWhenEmpty && !value) return null\n\n const Icon = copied ? Check : Copy\n\n return (\n <button\n ref={ref}\n type=\"button\"\n data-as=\"copy-button\"\n data-copied={copied || undefined}\n aria-label={label}\n title={label}\n onClick={(event) => {\n onClick?.(event)\n if (event.defaultPrevented || !value) return\n event.preventDefault()\n event.stopPropagation()\n writeClipboard(value).then(\n () => {\n setCopied(true)\n if (timer.current) clearTimeout(timer.current)\n timer.current = setTimeout(() => setCopied(false), copiedDuration)\n onCopied?.()\n },\n (error) => {\n onCopyError?.(error)\n },\n )\n }}\n className={cn(\n 'inline-flex cursor-pointer items-center justify-center rounded-[var(--as-radius-sm)] p-0.5 text-[var(--as-muted-fg)] transition-colors hover:text-[var(--as-primary)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]',\n copied && 'text-[var(--as-success)] hover:text-[var(--as-success)]',\n className,\n )}\n {...props}\n >\n <Icon style={{ width: size, height: size }} aria-hidden />\n </button>\n )\n },\n)\n"]}