@neasg/design-system 0.4.8 → 0.4.10

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 (115) hide show
  1. package/README.md +14 -14
  2. package/dist/alert.d.ts +21 -2
  3. package/dist/alert.js +10 -3
  4. package/dist/avatar.d.ts +5 -2
  5. package/dist/avatar.js +76 -8
  6. package/dist/back-button.d.ts +2 -0
  7. package/dist/back-button.js +1 -1
  8. package/dist/badge.d.ts +3 -2
  9. package/dist/badge.js +9 -3
  10. package/dist/breadcrumb.js +2 -1
  11. package/dist/button.d.ts +1 -1
  12. package/dist/button.js +12 -9
  13. package/dist/calendar.js +10 -9
  14. package/dist/card.d.ts +29 -1
  15. package/dist/card.js +61 -7
  16. package/dist/checkbox.d.ts +2 -0
  17. package/dist/checkbox.js +9 -3
  18. package/dist/close-button.d.ts +6 -0
  19. package/dist/close-button.js +8 -0
  20. package/dist/collapsible.d.ts +7 -1
  21. package/dist/collapsible.js +2 -2
  22. package/dist/command-search.d.ts +16 -1
  23. package/dist/command-search.js +42 -16
  24. package/dist/command.js +3 -3
  25. package/dist/copy-button.d.ts +3 -1
  26. package/dist/copy-button.js +4 -2
  27. package/dist/count-badge.d.ts +3 -1
  28. package/dist/count-badge.js +10 -5
  29. package/dist/date-input.d.ts +18 -3
  30. package/dist/date-input.js +94 -21
  31. package/dist/dialog-primitive.js +4 -3
  32. package/dist/draggable-tabs.d.ts +7 -0
  33. package/dist/draggable-tabs.js +6 -5
  34. package/dist/drawer.js +2 -2
  35. package/dist/dropdown-button.d.ts +18 -0
  36. package/dist/dropdown-button.js +31 -0
  37. package/dist/edit-button.d.ts +10 -0
  38. package/dist/edit-button.js +7 -0
  39. package/dist/editable-table.d.ts +3 -1
  40. package/dist/editable-table.js +25 -12
  41. package/dist/field.d.ts +7 -3
  42. package/dist/field.js +8 -4
  43. package/dist/file-upload-status-toast.d.ts +28 -0
  44. package/dist/file-upload-status-toast.js +136 -0
  45. package/dist/file-upload.js +8 -7
  46. package/dist/filter-popover.d.ts +37 -0
  47. package/dist/filter-popover.js +73 -0
  48. package/dist/filter-toggle.js +2 -2
  49. package/dist/index.d.ts +45 -14
  50. package/dist/index.js +19 -4
  51. package/dist/input-control.d.ts +3 -2
  52. package/dist/input-control.js +3 -2
  53. package/dist/label-value-grid.d.ts +4 -2
  54. package/dist/label-value-grid.js +7 -6
  55. package/dist/label-value.js +3 -1
  56. package/dist/layout-right-panel-edge.d.ts +6 -0
  57. package/dist/layout-right-panel-edge.js +6 -0
  58. package/dist/layout.d.ts +77 -5
  59. package/dist/layout.js +439 -35
  60. package/dist/lib/date-utils.d.ts +10 -2
  61. package/dist/lib/date-utils.js +91 -11
  62. package/dist/lib/utils.js +8 -1
  63. package/dist/link.js +2 -2
  64. package/dist/message-item.d.ts +62 -0
  65. package/dist/message-item.js +148 -0
  66. package/dist/multi-select.js +31 -5
  67. package/dist/notification.d.ts +82 -0
  68. package/dist/notification.js +121 -0
  69. package/dist/otp-input.js +1 -1
  70. package/dist/page-header.js +1 -1
  71. package/dist/page-section.d.ts +2 -1
  72. package/dist/page-section.js +4 -3
  73. package/dist/pagination.js +2 -2
  74. package/dist/popover-menu.d.ts +23 -1
  75. package/dist/popover-menu.js +75 -5
  76. package/dist/popover-primitive.js +1 -1
  77. package/dist/progress.d.ts +5 -2
  78. package/dist/progress.js +6 -3
  79. package/dist/radio-group.d.ts +27 -0
  80. package/dist/radio-group.js +50 -0
  81. package/dist/rich-text-editor.d.ts +56 -0
  82. package/dist/rich-text-editor.js +394 -0
  83. package/dist/routing-timeline.d.ts +22 -0
  84. package/dist/routing-timeline.js +58 -0
  85. package/dist/scroll-hint.d.ts +8 -0
  86. package/dist/scroll-hint.js +7 -0
  87. package/dist/search-input-shell.js +1 -1
  88. package/dist/search-input.js +2 -2
  89. package/dist/section-nav.js +4 -4
  90. package/dist/select-primitive.js +3 -3
  91. package/dist/select.js +1 -1
  92. package/dist/skeleton.js +1 -1
  93. package/dist/styles.css +241 -55
  94. package/dist/table-column-visibility.d.ts +21 -0
  95. package/dist/table-column-visibility.js +74 -0
  96. package/dist/table-toolbar.js +1 -1
  97. package/dist/table.d.ts +12 -1
  98. package/dist/table.js +67 -33
  99. package/dist/tabs.d.ts +14 -3
  100. package/dist/tabs.js +60 -6
  101. package/dist/tailwind-preset.js +46 -0
  102. package/dist/task-mode-shell.d.ts +24 -0
  103. package/dist/task-mode-shell.js +110 -0
  104. package/dist/textarea.js +2 -1
  105. package/dist/theme-switcher.d.ts +28 -0
  106. package/dist/theme-switcher.js +27 -0
  107. package/dist/theme.d.ts +167 -0
  108. package/dist/theme.js +112 -0
  109. package/dist/toaster.js +5 -4
  110. package/dist/tooltip.js +1 -1
  111. package/dist/typography.d.ts +1 -1
  112. package/dist/typography.js +5 -5
  113. package/dist/workspace-header.d.ts +13 -0
  114. package/dist/workspace-header.js +11 -0
  115. package/package.json +18 -2
package/dist/table.js CHANGED
@@ -9,30 +9,36 @@ import { Skeleton } from "./skeleton";
9
9
  import { cn } from "./lib/utils";
10
10
  const TableRoot = React.forwardRef(({ className, wrapperClassName: _wrapperClassName, ...props }, ref) => (_jsx("table", { ref: ref, className: cn("w-full caption-bottom text-sm", className), ...props })));
11
11
  TableRoot.displayName = "TableRoot";
12
- const TableContainer = React.forwardRef(({ className, ...props }, ref) => (_jsx("div", { ref: ref, className: cn("min-h-0 overflow-auto rounded-lg border bg-card", className), ...props })));
12
+ const TableContainer = React.forwardRef(({ className, ...props }, ref) => (_jsx("div", { ref: ref, className: cn("min-h-0 overflow-auto rounded-lg border border-[hsl(var(--table-border))] bg-card text-card-foreground", className), ...props })));
13
13
  TableContainer.displayName = "TableContainer";
14
- const TableHeader = React.forwardRef(({ className, ...props }, ref) => (_jsx("thead", { ref: ref, className: cn("sticky top-0 z-10 bg-muted [&_tr]:border-b [&>tr]:bg-inherit", className), ...props })));
14
+ function getTableColumnSizingStyle({ style, width, minWidth, maxWidth, }) {
15
+ const resolvedMinWidth = minWidth !== null && minWidth !== void 0 ? minWidth : width;
16
+ return {
17
+ ...style,
18
+ ...(width ? { width } : {}),
19
+ ...(resolvedMinWidth ? { minWidth: resolvedMinWidth } : {}),
20
+ ...(maxWidth ? { maxWidth } : {}),
21
+ };
22
+ }
23
+ const TableHeader = React.forwardRef(({ className, ...props }, ref) => (_jsx("thead", { ref: ref, className: cn("sticky top-0 z-10 bg-[hsl(var(--table-header-background))] [&_tr]:border-b [&_tr]:border-[hsl(var(--table-border))] [&>tr]:bg-inherit", className), ...props })));
15
24
  TableHeader.displayName = "TableHeader";
16
25
  const TableBody = React.forwardRef(({ className, ...props }, ref) => (_jsx("tbody", { ref: ref, className: cn("[&_tr:last-child]:border-0", className), ...props })));
17
26
  TableBody.displayName = "TableBody";
18
- const TableFooter = React.forwardRef(({ className, ...props }, ref) => (_jsx("tfoot", { ref: ref, className: cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className), ...props })));
27
+ const TableFooter = React.forwardRef(({ className, ...props }, ref) => (_jsx("tfoot", { ref: ref, className: cn("border-t border-[hsl(var(--table-border))] bg-card font-medium [&>tr]:last:border-b-0", className), ...props })));
19
28
  TableFooter.displayName = "TableFooter";
20
- const TableRow = React.forwardRef(({ className, ...props }, ref) => (_jsx("tr", { ref: ref, className: cn("border-b bg-card transition-colors data-[state=selected]:bg-muted", className), ...props })));
29
+ const TableRow = React.forwardRef(({ className, ...props }, ref) => (_jsx("tr", { ref: ref, className: cn("border-b border-[hsl(var(--table-border))] bg-card text-card-foreground transition-colors data-[state=selected]:bg-muted", className), ...props })));
21
30
  TableRow.displayName = "TableRow";
22
- const TableHead = React.forwardRef(({ className, style, width, minWidth, maxWidth, wrap = false, ...props }, ref) => (_jsx("th", { ref: ref, className: cn("h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0", wrap ? "whitespace-normal" : "whitespace-nowrap", className), style: {
23
- ...style,
24
- fontWeight: 500,
25
- ...(width ? { width } : {}),
26
- ...(minWidth ? { minWidth } : {}),
27
- ...(maxWidth ? { maxWidth } : {}),
28
- }, ...props })));
31
+ const TableHead = React.forwardRef(({ className, style, width, minWidth, maxWidth, wrap = false, ...props }, ref) => (_jsx("th", { ref: ref, className: cn("h-11 px-4 py-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0", wrap ? "whitespace-normal" : "whitespace-nowrap", className), style: getTableColumnSizingStyle({
32
+ style: {
33
+ ...style,
34
+ fontWeight: 500,
35
+ },
36
+ width,
37
+ minWidth,
38
+ maxWidth,
39
+ }), ...props })));
29
40
  TableHead.displayName = "TableHead";
30
- const TableCell = React.forwardRef(({ className, style, width, minWidth, maxWidth, wrap = false, ...props }, ref) => (_jsx("td", { ref: ref, className: cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", wrap ? "whitespace-normal" : "whitespace-nowrap", className), style: {
31
- ...style,
32
- ...(width ? { width } : {}),
33
- ...(minWidth ? { minWidth } : {}),
34
- ...(maxWidth ? { maxWidth } : {}),
35
- }, ...props })));
41
+ const TableCell = React.forwardRef(({ className, style, width, minWidth, maxWidth, wrap = false, ...props }, ref) => (_jsx("td", { ref: ref, className: cn("h-12 px-4 py-2 align-middle text-card-foreground [&:has([role=checkbox])]:pr-0", wrap ? "whitespace-normal" : "whitespace-nowrap", className), style: getTableColumnSizingStyle({ style, width, minWidth, maxWidth }), ...props })));
36
42
  TableCell.displayName = "TableCell";
37
43
  const TableCaption = React.forwardRef(({ className, ...props }, ref) => (_jsx("caption", { ref: ref, className: cn("mt-4 text-sm text-muted-foreground", className), ...props })));
38
44
  TableCaption.displayName = "TableCaption";
@@ -43,11 +49,10 @@ function stickyClassName(sticky, kind) {
43
49
  const shadow = sticky === "left"
44
50
  ? "shadow-[inset_-1px_0_0_0_var(--color-border)]"
45
51
  : "shadow-[inset_1px_0_0_0_var(--color-border)]";
46
- // bg-inherit follows the row's current bg (default/hover/striped/header),
47
- // so sticky cells stay in sync with the rest of the row.
52
+ const background = kind === "head" ? "bg-[hsl(var(--table-header-background))]" : "bg-card";
48
53
  // z-20 on header cells so they layer above sticky body cells on the other axis.
49
54
  const z = kind === "head" ? "z-20" : "z-10";
50
- return cn("sticky bg-inherit", side, z, shadow);
55
+ return cn("sticky", background, side, z, shadow);
51
56
  }
52
57
  function alignClassName(align) {
53
58
  switch (align) {
@@ -104,8 +109,8 @@ export const TABLE_SKELETON_PRESETS = {
104
109
  };
105
110
  function SortableTableHead({ field, children, currentSortField, currentSortDirection, onSort, className, allowWrap = false, width, minWidth, maxWidth, align, sortLabel, }) {
106
111
  const isSorted = currentSortField === field;
107
- return (_jsx(TableHead, { className: cn(alignClassName(align), className), wrap: allowWrap, width: width, minWidth: minWidth, maxWidth: maxWidth, children: _jsxs("button", { type: "button", onClick: () => onSort(field), className: cn("flex cursor-pointer items-center gap-1 rounded px-2 py-1 font-medium transition-colors hover:text-foreground -ml-2", allowWrap
108
- ? "max-w-full items-start whitespace-normal text-left"
112
+ return (_jsx(TableHead, { className: cn(alignClassName(align), className), wrap: allowWrap, width: width, minWidth: minWidth, maxWidth: maxWidth, children: _jsxs("button", { type: "button", onClick: () => onSort(field), className: cn("-ml-1.5 flex h-7 cursor-pointer items-center gap-1 rounded px-1.5 py-0 font-medium transition-colors hover:text-foreground", allowWrap
113
+ ? "max-w-full whitespace-normal text-left"
109
114
  : "whitespace-nowrap", align === "right" && "ml-auto justify-end", align === "center" && "mx-auto justify-center"), style: { font: "inherit", color: "inherit", fontWeight: 500 }, "aria-label": `Sort by ${sortLabel !== null && sortLabel !== void 0 ? sortLabel : field}`, children: [children, _jsx("span", { className: "inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center", children: isSorted ? (currentSortDirection === "asc" ? (_jsx(ArrowUpIcon, { className: "shrink-0", size: 14 })) : (_jsx(ArrowDownIcon, { className: "shrink-0", size: 14 }))) : (_jsx(ArrowUpDown, { className: "h-3.5 w-3.5 shrink-0 opacity-50" })) })] }) }));
110
115
  }
111
116
  function getDefaultSkeleton(column) {
@@ -115,27 +120,56 @@ function getDefaultSkeleton(column) {
115
120
  height: "1rem",
116
121
  };
117
122
  }
118
- function Table({ columns, rows, getRowKey, emptyState = (_jsx(EmptyState, { message: "No data available", description: "There are no rows to display in this table yet.", className: "border-0 bg-transparent px-0 py-6" })), toolbar, caption, className, containerClassName, tableClassName, rowClassName, maxHeight, sorting, pagination, loading = false, loadingRows = 5, striped = false, onRowClick, style, ...props }) {
123
+ function tableRowToneClassName(tone) {
124
+ switch (tone) {
125
+ case "muted":
126
+ return "bg-card";
127
+ case "warning":
128
+ return "bg-card";
129
+ default:
130
+ return undefined;
131
+ }
132
+ }
133
+ function Table({ columns, rows, getRowKey, emptyState = (_jsx(EmptyState, { message: "No data available", description: "There are no rows to display in this table yet.", className: "border-0 bg-transparent px-0 py-6" })), toolbar, caption, className, containerClassName, tableClassName, rowClassName, rowTone, maxHeight, sorting, pagination, loading = false, loadingRows = 5, visibleColumnKeys, striped = false, onRowClick, style, ...props }) {
119
134
  // Use auto layout so the browser can grow a column to contain its (nowrap)
120
135
  // header. Caller can opt back into fixed layout via `tableClassName="table-fixed"`.
121
136
  const shouldUseFixedLayout = false;
137
+ const visibleColumnSet = visibleColumnKeys
138
+ ? new Set(visibleColumnKeys)
139
+ : undefined;
140
+ const visibleColumns = visibleColumnSet
141
+ ? columns.filter((column) => visibleColumnSet.has(column.key))
142
+ : columns;
122
143
  const container = (_jsx(TableContainer, { className: cn("w-full", loading && "pointer-events-none select-none", className, containerClassName), style: {
123
144
  ...style,
124
145
  ...(maxHeight !== undefined ? { maxHeight } : {}),
125
- }, ...props, children: _jsxs(TableRoot, { className: cn(shouldUseFixedLayout && "table-fixed", tableClassName), children: [caption ? _jsx(TableCaption, { children: caption }) : null, _jsx(TableHeader, { children: _jsx(TableRow, { children: columns.map((column) => {
146
+ }, ...props, children: _jsxs(TableRoot, { className: cn(shouldUseFixedLayout && "table-fixed", tableClassName), children: [caption ? _jsx(TableCaption, { children: caption }) : null, _jsx("colgroup", { children: visibleColumns.map((column) => (_jsx("col", { style: getTableColumnSizingStyle({
147
+ width: column.width,
148
+ minWidth: column.minWidth,
149
+ maxWidth: column.maxWidth,
150
+ }) }, column.key))) }), _jsx(TableHeader, { children: _jsx(TableRow, { children: visibleColumns.map((column) => {
126
151
  var _a;
127
152
  const sortField = (_a = column.sortKey) !== null && _a !== void 0 ? _a : column.key;
128
153
  const isSortable = column.sortable && sorting;
129
154
  return (isSortable ? (_jsx(SortableTableHead, { field: sortField, currentSortField: sorting.field, currentSortDirection: sorting.direction, onSort: sorting.onSort, className: cn(stickyClassName(column.sticky, "head"), column.headerClassName), allowWrap: column.wrap, width: column.width, minWidth: column.minWidth, maxWidth: column.maxWidth, align: column.align, sortLabel: column.sortLabel, children: column.header }, column.key)) : (_jsx(TableHead, { width: column.width, minWidth: column.minWidth, maxWidth: column.maxWidth, wrap: column.wrap, className: cn(alignClassName(column.align), stickyClassName(column.sticky, "head"), column.headerClassName), children: column.header }, column.key)));
130
- }) }) }), _jsx(TableBody, { className: cn(striped && "[&>tr:nth-child(even)]:bg-muted/60"), children: loading ? (_jsx(TableRowSkeleton, { rows: loadingRows, columns: columns.map((column) => { var _a; return (_a = column.skeleton) !== null && _a !== void 0 ? _a : getDefaultSkeleton(column); }) })) : rows.length ? (rows.map((row, rowIndex) => (_jsx(TableRow, { onClick: onRowClick ? () => onRowClick(row, rowIndex) : undefined, className: cn(onRowClick && "cursor-pointer hover:bg-muted", typeof rowClassName === "function"
131
- ? rowClassName(row, rowIndex)
132
- : rowClassName), children: columns.map((column) => {
133
- var _a, _b;
134
- const value = (_b = (_a = column.cell) === null || _a === void 0 ? void 0 : _a.call(column, row, rowIndex)) !== null && _b !== void 0 ? _b : (column.accessorKey !== undefined
135
- ? row[column.accessorKey]
136
- : null);
137
- return (_jsx(TableCell, { width: column.width, minWidth: column.minWidth, maxWidth: column.maxWidth, wrap: column.wrap, className: cn(alignClassName(column.align), stickyClassName(column.sticky, "cell"), column.cellClassName), children: value }, column.key));
138
- }) }, getRowKey(row, rowIndex))))) : (_jsx(TableRow, { className: "cursor-default hover:bg-card", children: _jsx(TableCell, { colSpan: columns.length, className: "p-6", children: emptyState }) })) })] }) }));
155
+ }) }) }), _jsx(TableBody, { children: loading ? (_jsx(TableRowSkeleton, { rows: loadingRows, columns: visibleColumns.map((column) => { var _a; return (_a = column.skeleton) !== null && _a !== void 0 ? _a : getDefaultSkeleton(column); }) })) : rows.length ? (rows.map((row, rowIndex) => {
156
+ const resolvedTone = typeof rowTone === "function"
157
+ ? rowTone(row, rowIndex)
158
+ : rowTone;
159
+ return (_jsx(TableRow, { onClick: onRowClick ? () => onRowClick(row, rowIndex) : undefined, className: cn(onRowClick &&
160
+ "cursor-pointer hover:bg-muted", striped &&
161
+ rowIndex % 2 === 1 &&
162
+ !resolvedTone &&
163
+ "bg-[hsl(var(--table-row-striped-background))]", tableRowToneClassName(resolvedTone), typeof rowClassName === "function"
164
+ ? rowClassName(row, rowIndex)
165
+ : rowClassName), children: visibleColumns.map((column) => {
166
+ var _a, _b;
167
+ const value = (_b = (_a = column.cell) === null || _a === void 0 ? void 0 : _a.call(column, row, rowIndex)) !== null && _b !== void 0 ? _b : (column.accessorKey !== undefined
168
+ ? row[column.accessorKey]
169
+ : null);
170
+ return (_jsx(TableCell, { width: column.width, minWidth: column.minWidth, maxWidth: column.maxWidth, wrap: column.wrap, className: cn(alignClassName(column.align), stickyClassName(column.sticky, "cell"), column.cellClassName), children: value }, column.key));
171
+ }) }, getRowKey(row, rowIndex)));
172
+ })) : (_jsx(TableRow, { className: "cursor-default hover:bg-card", children: _jsx(TableCell, { colSpan: Math.max(visibleColumns.length, 1), className: "p-6", children: emptyState }) })) })] }) }));
139
173
  if (!pagination) {
140
174
  return (_jsxs("div", { className: "space-y-4", children: [toolbar, container] }));
141
175
  }
package/dist/tabs.d.ts CHANGED
@@ -24,11 +24,19 @@ export interface TabDropdownSection {
24
24
  id: string;
25
25
  label: React.ReactNode;
26
26
  }
27
+ export interface TabsItemLabelProps {
28
+ active: boolean;
29
+ }
30
+ export type TabsItemLabel = React.ReactNode | ((props: TabsItemLabelProps) => React.ReactNode);
27
31
  export interface TabsItem {
28
32
  value: string;
29
- label: React.ReactNode;
33
+ label: TabsItemLabel;
30
34
  content: React.ReactNode;
31
35
  disabled?: boolean;
36
+ /** Renders a close control next to the tab trigger. */
37
+ onClose?: () => void;
38
+ /** Accessible label for the close control. */
39
+ closeLabel?: string;
32
40
  /** When provided, the tab shows a chevron and opens a dropdown menu of in-page sections. */
33
41
  sections?: TabDropdownSection[];
34
42
  /** Scroll offset in pixels when scrolling to a section (default 200). */
@@ -40,7 +48,7 @@ export interface TabsItem {
40
48
  }
41
49
  export interface TabWithDropdownProps {
42
50
  value: string;
43
- label: React.ReactNode;
51
+ label: TabsItemLabel;
44
52
  sections?: TabDropdownSection[];
45
53
  disabled?: boolean;
46
54
  scrollOffset?: number;
@@ -54,9 +62,12 @@ export interface TabsProps extends Omit<React.ComponentPropsWithoutRef<typeof Ta
54
62
  defaultValue?: string;
55
63
  value?: string;
56
64
  onValueChange?: (value: string) => void;
65
+ size?: "default" | "compact";
66
+ renderPanels?: boolean;
67
+ listWrapperClassName?: string;
57
68
  listClassName?: string;
58
69
  triggerClassName?: string;
59
70
  panelClassName?: string;
60
71
  }
61
- declare function Tabs({ items, defaultValue, value, onValueChange, className, listClassName, triggerClassName, panelClassName, ...props }: TabsProps): import("react/jsx-runtime").JSX.Element | null;
72
+ declare function Tabs({ items, defaultValue, value, onValueChange, size, renderPanels, className, listWrapperClassName, listClassName, triggerClassName, panelClassName, ...props }: TabsProps): import("react/jsx-runtime").JSX.Element | null;
62
73
  export { Tabs, TabsRoot, TabsList, TabsTrigger, TabsContent, TabWithDropdown };
package/dist/tabs.js CHANGED
@@ -3,10 +3,17 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import * as TabsPrimitive from "@radix-ui/react-tabs";
5
5
  import { ChevronDownIcon } from "./animated-icons/chevron-down";
6
+ import { XIcon } from "./animated-icons/x";
6
7
  import { cn } from "./lib/utils";
7
8
  import { Popover } from "./popover";
8
9
  import { PopoverMenuItem, PopoverMenuSection } from "./popover-menu";
9
10
  const TabsContext = React.createContext(null);
11
+ const getTabValueSelector = (value) => {
12
+ const escapedValue = typeof CSS !== "undefined" && typeof CSS.escape === "function"
13
+ ? CSS.escape(value)
14
+ : value.replace(/["\\]/g, "\\$&");
15
+ return `[data-tab-value="${escapedValue}"]`;
16
+ };
10
17
  export function useTabsContext() {
11
18
  return React.useContext(TabsContext);
12
19
  }
@@ -24,12 +31,15 @@ const TabsRoot = React.forwardRef(({ defaultValue = "", value: valueProp, onValu
24
31
  return (_jsx(TabsContext.Provider, { value: contextValue, children: _jsx(TabsPrimitive.Root, { ref: ref, value: value, onValueChange: setValue, className: cn(className), ...props, children: children }) }));
25
32
  });
26
33
  TabsRoot.displayName = TabsPrimitive.Root.displayName;
27
- const TabsList = React.forwardRef(({ className, ...props }, ref) => (_jsx(TabsPrimitive.List, { ref: ref, className: cn("inline-flex h-fit items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground", className), ...props })));
34
+ const TabsList = React.forwardRef(({ className, ...props }, ref) => (_jsx(TabsPrimitive.List, { ref: ref, className: cn("inline-flex h-fit items-stretch justify-center overflow-hidden rounded-control border border-[hsl(var(--tab-list-border))] bg-[hsl(var(--tab-list-background))] p-1 text-[hsl(var(--tab-trigger-foreground))]", className), ...props })));
28
35
  TabsList.displayName = TabsPrimitive.List.displayName;
29
- const TabsTrigger = React.forwardRef(({ className, value, ...props }, ref) => (_jsx(TabsPrimitive.Trigger, { ref: ref, value: value, className: cn("inline-flex cursor-pointer items-center justify-center whitespace-nowrap rounded-md px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=inactive]:hover:bg-background/50 data-[state=inactive]:hover:text-foreground data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm", className), ...props })));
36
+ const TabsTrigger = React.forwardRef(({ className, value, ...props }, ref) => (_jsx(TabsPrimitive.Trigger, { ref: ref, value: value, className: cn("inline-flex min-h-8 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-sm bg-[hsl(var(--tab-list-background))] px-3 py-1 text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-[hsl(var(--tab-list-background))] disabled:pointer-events-none disabled:opacity-50 data-[state=inactive]:hover:bg-[hsl(var(--tab-trigger-hover-background))] data-[state=inactive]:hover:text-[hsl(var(--tab-trigger-active-foreground))] data-[state=active]:bg-[hsl(var(--tab-trigger-active-background))] data-[state=active]:text-[hsl(var(--tab-trigger-active-foreground))] data-[state=active]:shadow-[0_1px_3px_rgb(15_23_42_/_0.12)]", className), ...props })));
30
37
  TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
31
- const TabsContent = React.forwardRef(({ className, value, ...props }, ref) => (_jsx(TabsPrimitive.Content, { ref: ref, value: value, className: cn("focus-visible:outline-none", className), ...props })));
38
+ const TabsContent = React.forwardRef(({ className, value, ...props }, ref) => (_jsx(TabsPrimitive.Content, { ref: ref, value: value, className: cn("focus-visible:outline-none data-[state=inactive]:hidden", className), ...props })));
32
39
  TabsContent.displayName = TabsPrimitive.Content.displayName;
40
+ function renderTabsItemLabel(label, active) {
41
+ return typeof label === "function" ? label({ active }) : label;
42
+ }
33
43
  function TabTriggerWithDropdown({ item, triggerClassName, }) {
34
44
  var _a, _b, _c, _d, _e;
35
45
  const [open, setOpen] = React.useState(false);
@@ -71,7 +81,7 @@ function TabTriggerWithDropdown({ item, triggerClassName, }) {
71
81
  setInternalActiveSection((_b = (_a = sections[0]) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : null);
72
82
  }
73
83
  }, [isTabSelected, item.activeSection, sections]);
74
- return (_jsx(Popover, { open: open, onOpenChange: setOpen, surface: "menu", align: "start", contentClassName: "min-w-[12rem]", trigger: _jsxs(TabsTrigger, { value: item.value, disabled: item.disabled, "data-state": isTabSelected ? "active" : "inactive", className: cn("flex items-center gap-1", triggerClassName), children: [item.label, _jsx(ChevronDownIcon, { size: 12, className: cn("transition-transform duration-200", open && "rotate-180") })] }), children: _jsx(PopoverMenuSection, { children: sections.map((section) => (_jsx(PopoverMenuItem, { active: isTabSelected && activeSection === section.id, className: "py-2 pl-2 pr-8", onClick: () => handleSectionClick(section.id), children: section.label }, section.id))) }) }));
84
+ return (_jsx(Popover, { open: open, onOpenChange: setOpen, surface: "menu", align: "start", contentClassName: "min-w-[12rem]", trigger: _jsxs(TabsTrigger, { value: item.value, disabled: item.disabled, "data-state": isTabSelected ? "active" : "inactive", "data-tab-value": item.value, className: cn("flex items-center gap-1", triggerClassName), children: [renderTabsItemLabel(item.label, isTabSelected), _jsx(ChevronDownIcon, { size: 12, className: cn("transition-transform duration-200", open && "rotate-180") })] }), children: _jsx(PopoverMenuSection, { children: sections.map((section) => (_jsx(PopoverMenuItem, { active: isTabSelected && activeSection === section.id, className: "py-2 pl-2 pr-8", onClick: () => handleSectionClick(section.id), children: section.label }, section.id))) }) }));
75
85
  }
76
86
  function TabWithDropdown({ value, label, sections, disabled, scrollOffset, onSectionSelect, activeSection, className, }) {
77
87
  return (_jsx(TabTriggerWithDropdown, { item: {
@@ -85,12 +95,56 @@ function TabWithDropdown({ value, label, sections, disabled, scrollOffset, onSec
85
95
  activeSection,
86
96
  }, triggerClassName: className }));
87
97
  }
88
- function Tabs({ items, defaultValue, value, onValueChange, className, listClassName, triggerClassName, panelClassName, ...props }) {
98
+ function TabTriggerItem({ item, triggerClassName }) {
99
+ var _a;
100
+ const tabsContext = useTabsContext();
101
+ const isSelected = tabsContext ? tabsContext.value === item.value : false;
102
+ const label = renderTabsItemLabel(item.label, isSelected);
103
+ if (!item.onClose) {
104
+ return (_jsx(TabsTrigger, { value: item.value, disabled: item.disabled, "data-tab-value": item.value, className: triggerClassName, children: label }));
105
+ }
106
+ return (_jsxs("div", { className: "inline-flex min-w-max shrink-0 items-stretch whitespace-nowrap", "data-tab-value": item.value, children: [_jsx(TabsTrigger, { value: item.value, disabled: item.disabled, className: cn("shrink-0 rounded-r-none pr-1 data-[state=active]:shadow-none", triggerClassName), children: label }), _jsx("button", { type: "button", "aria-label": (_a = item.closeLabel) !== null && _a !== void 0 ? _a : "Close tab", "data-state": isSelected ? "active" : "inactive", className: cn("inline-flex min-h-8 cursor-pointer items-center justify-center rounded-r-sm bg-[hsl(var(--tab-list-background))] px-2 text-muted-foreground transition-all hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-[hsl(var(--tab-list-background))] disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-[hsl(var(--tab-trigger-active-background))] data-[state=active]:text-[hsl(var(--tab-trigger-active-foreground))]", item.disabled && "cursor-not-allowed opacity-50"), disabled: item.disabled, onClick: (event) => {
107
+ var _a;
108
+ event.stopPropagation();
109
+ (_a = item.onClose) === null || _a === void 0 ? void 0 : _a.call(item);
110
+ }, children: _jsx(XIcon, { size: 12 }) })] }));
111
+ }
112
+ function Tabs({ items, defaultValue, value, onValueChange, size = "default", renderPanels = true, className, listWrapperClassName, listClassName, triggerClassName, panelClassName, ...props }) {
89
113
  var _a, _b;
90
114
  const fallbackValue = (_b = defaultValue !== null && defaultValue !== void 0 ? defaultValue : (_a = items[0]) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : "";
115
+ const [activeValue, setActiveValue] = React.useState(value !== null && value !== void 0 ? value : fallbackValue);
116
+ const listWrapperRef = React.useRef(null);
117
+ const selectedValue = value !== null && value !== void 0 ? value : activeValue;
118
+ const handleValueChange = React.useCallback((nextValue) => {
119
+ setActiveValue(nextValue);
120
+ onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(nextValue);
121
+ }, [onValueChange]);
122
+ React.useEffect(() => {
123
+ if (value !== undefined) {
124
+ setActiveValue(value);
125
+ }
126
+ }, [value]);
127
+ React.useEffect(() => {
128
+ const listWrapper = listWrapperRef.current;
129
+ if (!listWrapper || !selectedValue) {
130
+ return;
131
+ }
132
+ const selectedTab = listWrapper.querySelector(getTabValueSelector(selectedValue));
133
+ if (typeof (selectedTab === null || selectedTab === void 0 ? void 0 : selectedTab.scrollIntoView) !== "function") {
134
+ return;
135
+ }
136
+ selectedTab.scrollIntoView({
137
+ block: "nearest",
138
+ inline: "nearest",
139
+ });
140
+ }, [items.length, selectedValue]);
91
141
  if (!items.length) {
92
142
  return null;
93
143
  }
94
- return (_jsxs(TabsRoot, { defaultValue: fallbackValue, value: value, onValueChange: onValueChange, className: cn("space-y-4", className), ...props, children: [_jsx(TabsList, { className: listClassName, children: items.map((item) => item.sections && item.sections.length > 0 ? (_jsx(TabTriggerWithDropdown, { item: item, triggerClassName: triggerClassName }, item.value)) : (_jsx(TabsTrigger, { value: item.value, disabled: item.disabled, className: triggerClassName, children: item.label }, item.value))) }), items.map((item) => (_jsx(TabsContent, { value: item.value, className: cn("pt-4", panelClassName), children: item.content }, item.value)))] }));
144
+ return (_jsxs(TabsRoot, { defaultValue: fallbackValue, value: value, onValueChange: handleValueChange, className: cn("space-y-4", className), ...props, children: [_jsx("div", { ref: listWrapperRef, className: listWrapperClassName, children: _jsx(TabsList, { className: cn(size === "compact" && "p-0.5", listClassName), children: items.map((item) => item.sections && item.sections.length > 0 ? (_jsx(TabTriggerWithDropdown, { item: item, triggerClassName: cn(size === "compact" &&
145
+ "min-h-7 px-2.5 py-0.5 data-[state=active]:shadow-sm", triggerClassName) }, item.value)) : (_jsx(TabTriggerItem, { item: item, triggerClassName: cn(size === "compact" &&
146
+ "min-h-7 px-2.5 py-0.5 data-[state=active]:shadow-sm", triggerClassName) }, item.value))) }) }), renderPanels
147
+ ? items.map((item) => (_jsx(TabsContent, { value: item.value, className: cn("pt-4", panelClassName), children: item.content }, item.value)))
148
+ : null] }));
95
149
  }
96
150
  export { Tabs, TabsRoot, TabsList, TabsTrigger, TabsContent, TabWithDropdown };
@@ -12,6 +12,21 @@ export const neaTailwindPreset = {
12
12
  "2xl": "1400px",
13
13
  },
14
14
  },
15
+ fontSize: {
16
+ xs: ["0.9rem", { lineHeight: "1.125rem" }],
17
+ sm: ["1rem", { lineHeight: "1.375rem" }],
18
+ base: ["1.12rem", { lineHeight: "1.5rem" }],
19
+ lg: ["1.25rem", { lineHeight: "1.75rem" }],
20
+ xl: ["1.375rem", { lineHeight: "1.875rem" }],
21
+ "2xl": ["1.5rem", { lineHeight: "2rem" }],
22
+ "3xl": ["1.875rem", { lineHeight: "2.25rem" }],
23
+ "4xl": ["2.25rem", { lineHeight: "2.5rem" }],
24
+ "5xl": ["3rem", { lineHeight: "1" }],
25
+ "6xl": ["3.75rem", { lineHeight: "1" }],
26
+ "7xl": ["4.5rem", { lineHeight: "1" }],
27
+ "8xl": ["6rem", { lineHeight: "1" }],
28
+ "9xl": ["8rem", { lineHeight: "1" }],
29
+ },
15
30
  extend: {
16
31
  colors: {
17
32
  border: "hsl(var(--border))",
@@ -47,12 +62,37 @@ export const neaTailwindPreset = {
47
62
  DEFAULT: "hsl(var(--card))",
48
63
  foreground: "hsl(var(--card-foreground))",
49
64
  },
65
+ status: {
66
+ success: {
67
+ DEFAULT: "hsl(var(--status-success))",
68
+ foreground: "hsl(var(--status-success-foreground))",
69
+ },
70
+ warning: {
71
+ DEFAULT: "hsl(var(--status-warning))",
72
+ foreground: "hsl(var(--status-warning-foreground))",
73
+ },
74
+ info: {
75
+ DEFAULT: "hsl(var(--status-info))",
76
+ foreground: "hsl(var(--status-info-foreground))",
77
+ },
78
+ accent: {
79
+ DEFAULT: "hsl(var(--status-accent))",
80
+ foreground: "hsl(var(--status-accent-foreground))",
81
+ },
82
+ destructive: {
83
+ DEFAULT: "hsl(var(--status-destructive))",
84
+ foreground: "hsl(var(--status-destructive-foreground))",
85
+ },
86
+ },
50
87
  },
51
88
  borderRadius: {
52
89
  lg: "var(--radius-lg)",
53
90
  md: "var(--radius-md)",
54
91
  sm: "var(--radius-sm)",
55
92
  xl: "var(--radius-xl)",
93
+ control: "var(--radius-control)",
94
+ surface: "var(--radius-surface)",
95
+ overlay: "var(--radius-overlay)",
56
96
  },
57
97
  fontFamily: {
58
98
  sans: ["var(--font-sans)"],
@@ -70,6 +110,12 @@ export const neaTailwindPreset = {
70
110
  width: {
71
111
  control: "var(--size-control)",
72
112
  },
113
+ minWidth: {
114
+ control: "var(--size-control)",
115
+ },
116
+ maxWidth: {
117
+ control: "var(--size-control)",
118
+ },
73
119
  minHeight: {
74
120
  control: "var(--size-control)",
75
121
  },
@@ -0,0 +1,24 @@
1
+ import * as React from "react";
2
+ export interface TaskModeShellProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
3
+ header: React.ReactNode;
4
+ children: React.ReactNode;
5
+ loadingProgress?: number;
6
+ showLoadingBar?: boolean;
7
+ loadingLabel?: string;
8
+ ready?: boolean;
9
+ exiting?: boolean;
10
+ headerClassName?: string;
11
+ contentOffsetClassName?: string;
12
+ bodyClassName?: string;
13
+ mainPanelClassName?: string;
14
+ sidePanel?: React.ReactNode;
15
+ sidePanelOpen?: boolean;
16
+ sidePanelClassName?: string;
17
+ sidePanelAriaLabel?: string;
18
+ sidePanelResizable?: boolean;
19
+ sidePanelDefaultSize?: number;
20
+ sidePanelMinSize?: number;
21
+ sidePanelMaxSize?: number;
22
+ splitDividerClassName?: string;
23
+ }
24
+ export declare const TaskModeShell: React.ForwardRefExoticComponent<TaskModeShellProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,110 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { GripVertical } from "lucide-react";
5
+ import { cn } from "./lib/utils";
6
+ import { rightPanelEdgeClassName, rightPanelEdgeClosedClassName, rightPanelEdgeDividerClassName, rightPanelEdgeOpenClassName, rightPanelResizeHandleSurfaceClassName, } from "./layout-right-panel-edge";
7
+ import { Progress } from "./progress";
8
+ const TASK_MODE_SIDE_PANEL_DEFAULT_SIZE = 50;
9
+ const TASK_MODE_SIDE_PANEL_MIN_SIZE = 28;
10
+ const TASK_MODE_SIDE_PANEL_MAX_SIZE = 72;
11
+ const TASK_MODE_SIDE_PANEL_SIZE_CSS_VAR = "--task-mode-side-panel-size";
12
+ function clampSidePanelSize(size, minSize, maxSize) {
13
+ return Math.min(Math.max(size, minSize), maxSize);
14
+ }
15
+ function getResizableSplitTemplate() {
16
+ return `minmax(0, calc(100% - var(${TASK_MODE_SIDE_PANEL_SIZE_CSS_VAR}))) minmax(0, var(${TASK_MODE_SIDE_PANEL_SIZE_CSS_VAR}))`;
17
+ }
18
+ export const TaskModeShell = React.forwardRef(({ className, header, children, loadingProgress = 0, showLoadingBar = false, loadingLabel = "Loading task", ready = true, exiting = false, headerClassName, contentOffsetClassName = "pt-16", bodyClassName, mainPanelClassName, sidePanel, sidePanelOpen = false, sidePanelClassName, sidePanelAriaLabel, sidePanelResizable = false, sidePanelDefaultSize = TASK_MODE_SIDE_PANEL_DEFAULT_SIZE, sidePanelMinSize = TASK_MODE_SIDE_PANEL_MIN_SIZE, sidePanelMaxSize = TASK_MODE_SIDE_PANEL_MAX_SIZE, splitDividerClassName, ...props }, ref) => {
19
+ const hasSidePanel = sidePanel !== undefined && sidePanel !== null;
20
+ const splitOpen = hasSidePanel && sidePanelOpen;
21
+ const splitContainerRef = React.useRef(null);
22
+ const resolvedSidePanelMinSize = Math.min(sidePanelMinSize, sidePanelMaxSize);
23
+ const resolvedSidePanelMaxSize = Math.max(sidePanelMinSize, sidePanelMaxSize);
24
+ const [sidePanelSize, setSidePanelSize] = React.useState(() => clampSidePanelSize(sidePanelDefaultSize, resolvedSidePanelMinSize, resolvedSidePanelMaxSize));
25
+ const sidePanelSizeRef = React.useRef(sidePanelSize);
26
+ React.useEffect(() => {
27
+ sidePanelSizeRef.current = sidePanelSize;
28
+ }, [sidePanelSize]);
29
+ React.useEffect(() => {
30
+ if (!sidePanelResizable) {
31
+ return;
32
+ }
33
+ setSidePanelSize((size) => clampSidePanelSize(size, resolvedSidePanelMinSize, resolvedSidePanelMaxSize));
34
+ }, [
35
+ resolvedSidePanelMaxSize,
36
+ resolvedSidePanelMinSize,
37
+ sidePanelResizable,
38
+ ]);
39
+ const splitGridTemplateColumns = React.useMemo(() => {
40
+ if (!splitOpen) {
41
+ return "minmax(0, 100%) minmax(0, 0%)";
42
+ }
43
+ if (sidePanelResizable) {
44
+ return getResizableSplitTemplate();
45
+ }
46
+ return "minmax(0, 50%) minmax(0, 50%)";
47
+ }, [sidePanelResizable, splitOpen]);
48
+ const splitStyle = React.useMemo(() => ({
49
+ [TASK_MODE_SIDE_PANEL_SIZE_CSS_VAR]: `${sidePanelSize}%`,
50
+ gridTemplateColumns: splitGridTemplateColumns,
51
+ }), [sidePanelSize, splitGridTemplateColumns]);
52
+ const handleSidePanelResizePointerDown = React.useCallback((event) => {
53
+ var _a;
54
+ if (!sidePanelResizable || !splitOpen || event.button !== 0) {
55
+ return;
56
+ }
57
+ const splitContainer = splitContainerRef.current;
58
+ const splitContainerWidth = (_a = splitContainer === null || splitContainer === void 0 ? void 0 : splitContainer.getBoundingClientRect().width) !== null && _a !== void 0 ? _a : 0;
59
+ if (!splitContainer || splitContainerWidth <= 0) {
60
+ return;
61
+ }
62
+ event.preventDefault();
63
+ const startX = event.clientX;
64
+ const startSize = sidePanelSizeRef.current;
65
+ const previousCursor = document.body.style.cursor;
66
+ const previousUserSelect = document.body.style.userSelect;
67
+ const previousTransition = splitContainer.style.transition;
68
+ document.body.style.cursor = "col-resize";
69
+ document.body.style.userSelect = "none";
70
+ splitContainer.style.transition = "none";
71
+ let pendingSize = startSize;
72
+ const applyPendingSize = () => {
73
+ splitContainer.style.setProperty(TASK_MODE_SIDE_PANEL_SIZE_CSS_VAR, `${pendingSize}%`);
74
+ };
75
+ const handlePointerMove = (moveEvent) => {
76
+ const nextSize = startSize +
77
+ ((startX - moveEvent.clientX) / splitContainerWidth) * 100;
78
+ pendingSize = clampSidePanelSize(nextSize, resolvedSidePanelMinSize, resolvedSidePanelMaxSize);
79
+ applyPendingSize();
80
+ };
81
+ const finishResize = () => {
82
+ document.body.style.cursor = previousCursor;
83
+ document.body.style.userSelect = previousUserSelect;
84
+ splitContainer.style.transition = previousTransition;
85
+ window.removeEventListener("pointermove", handlePointerMove);
86
+ window.removeEventListener("pointerup", finishResize);
87
+ window.removeEventListener("pointercancel", finishResize);
88
+ setSidePanelSize(pendingSize);
89
+ };
90
+ window.addEventListener("pointermove", handlePointerMove);
91
+ window.addEventListener("pointerup", finishResize, { once: true });
92
+ window.addEventListener("pointercancel", finishResize, { once: true });
93
+ }, [
94
+ resolvedSidePanelMaxSize,
95
+ resolvedSidePanelMinSize,
96
+ sidePanelResizable,
97
+ splitOpen,
98
+ ]);
99
+ return (_jsxs("div", { ref: ref, className: cn("relative min-h-screen w-full bg-background text-foreground", className), ...props, children: [showLoadingBar || loadingProgress > 0 ? (_jsx(Progress, { value: loadingProgress, size: "xs", shape: "square", label: loadingLabel, className: cn("absolute left-0 top-0 z-50 transition-opacity duration-300 ease-out", showLoadingBar ? "opacity-100" : "pointer-events-none opacity-0"), trackClassName: "bg-transparent", indicatorClassName: "bg-emerald-600" })) : null, _jsxs("div", { className: cn("transition-all duration-[250ms] ease-out", ready && !exiting
100
+ ? "translate-y-0 opacity-100"
101
+ : "translate-y-4 opacity-0"), children: [_jsx("header", { className: cn("fixed left-0 right-0 top-0 z-30 border-b bg-background", headerClassName), children: header }), _jsx("main", { className: cn("w-full", contentOffsetClassName), children: hasSidePanel ? (_jsxs("div", { ref: splitContainerRef, className: cn("relative grid min-h-0 w-full overflow-hidden transition-[grid-template-columns] duration-300 ease-in-out", bodyClassName), style: splitStyle, children: [_jsx("section", { className: cn("min-h-0 min-w-0", mainPanelClassName), children: children }), _jsx("aside", { "aria-hidden": !splitOpen, "aria-label": sidePanelAriaLabel, className: cn("min-h-0 min-w-0 bg-[hsl(var(--layout-right-panel-background))] transition-[border-color,box-shadow,opacity] duration-300 ease-in-out", rightPanelEdgeClassName, sidePanelClassName, splitOpen
102
+ ? ["opacity-100", rightPanelEdgeOpenClassName]
103
+ : [
104
+ "pointer-events-none overflow-hidden opacity-0",
105
+ rightPanelEdgeClosedClassName,
106
+ ]), children: sidePanel }), splitOpen && sidePanelResizable ? (_jsxs("button", { type: "button", "aria-label": "Resize side panel", "data-slot": "task-mode-shell-split-divider", className: "group absolute inset-y-0 z-40 flex w-6 -translate-x-1/2 cursor-col-resize touch-none items-center justify-center rounded-none outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background", style: {
107
+ left: `calc(100% - var(${TASK_MODE_SIDE_PANEL_SIZE_CSS_VAR}))`,
108
+ }, onPointerDown: handleSidePanelResizePointerDown, children: [_jsx("span", { "aria-hidden": "true", className: cn("pointer-events-none absolute inset-y-0 left-1/2 w-px -translate-x-px", rightPanelEdgeDividerClassName, splitDividerClassName) }), _jsx("span", { "aria-hidden": "true", className: cn("pointer-events-none flex h-10 w-4 items-center justify-center rounded-full border text-muted-foreground/80 shadow-sm transition-[border-color,color,box-shadow] group-hover:border-ring group-hover:text-foreground group-hover:shadow-md group-focus-visible:border-ring group-focus-visible:text-foreground group-focus-visible:shadow-md", rightPanelResizeHandleSurfaceClassName), children: _jsx(GripVertical, { className: "h-3.5 w-3.5 stroke-[2.25]" }) })] })) : splitOpen ? (_jsx("div", { "aria-hidden": "true", "data-slot": "task-mode-shell-split-divider", className: cn("pointer-events-none absolute inset-y-0 left-1/2 z-40 w-px -translate-x-px", rightPanelEdgeDividerClassName, splitDividerClassName) })) : null] })) : (children) })] })] }));
109
+ });
110
+ TaskModeShell.displayName = "TaskModeShell";
package/dist/textarea.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { Field, FieldDescription, FieldError, FieldLabel } from "./field";
4
+ import { inputControlPaddingClassName } from "./input-control";
4
5
  import { cn } from "./lib/utils";
5
- const TextareaControl = React.forwardRef(({ className, ...props }, ref) => (_jsx("textarea", { ref: ref, "data-slot": "textarea", className: cn("flex min-h-20 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-[invalid=true]:border-destructive aria-[invalid=true]:ring-destructive/20", className), ...props })));
6
+ const TextareaControl = React.forwardRef(({ className, ...props }, ref) => (_jsx("textarea", { ref: ref, "data-slot": "textarea", className: cn("flex min-h-16 w-full rounded-control border border-input bg-background text-sm transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-[invalid=true]:border-destructive aria-[invalid=true]:ring-destructive/20", inputControlPaddingClassName, className), ...props })));
6
7
  TextareaControl.displayName = "TextareaControl";
7
8
  const Textarea = React.forwardRef(({ label, description, error, invalid = false, hideLabel = false, className, textareaClassName, labelClassName, descriptionClassName, errorClassName, id, disabled, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, ...props }, ref) => {
8
9
  const generatedId = React.useId();
@@ -0,0 +1,28 @@
1
+ import * as React from "react";
2
+ import { type SwitchProps } from "./switch";
3
+ export interface ThemeSwitcherProps extends Omit<SwitchProps, "checked" | "defaultChecked" | "onCheckedChange" | "label" | "description" | "wrapperClassName" | "className"> {
4
+ /** Controlled state. `true` means dark theme is selected. */
5
+ checked?: boolean;
6
+ /** Uncontrolled initial state. `true` means dark theme is selected. */
7
+ defaultChecked?: boolean;
8
+ /** Fires when the selected theme changes. */
9
+ onCheckedChange?: (checked: boolean) => void;
10
+ /** Icon-only mode for collapsed sidebars. */
11
+ isCollapsed?: boolean;
12
+ /** Text shown before the switch. Defaults to the active theme label. */
13
+ label?: React.ReactNode;
14
+ /** Label shown when light theme is active and no `label` is provided. */
15
+ lightLabel?: React.ReactNode;
16
+ /** Label shown when dark theme is active and no `label` is provided. */
17
+ darkLabel?: React.ReactNode;
18
+ /** Accessible action label for the collapsed button when light theme is active. */
19
+ useDarkLabel?: string;
20
+ /** Accessible action label for the collapsed button when dark theme is active. */
21
+ useLightLabel?: string;
22
+ className?: string;
23
+ }
24
+ declare function ThemeSwitcher({ checked, defaultChecked, onCheckedChange, isCollapsed, label, lightLabel, darkLabel, useDarkLabel, useLightLabel, className, disabled, ...props }: ThemeSwitcherProps): import("react/jsx-runtime").JSX.Element;
25
+ declare namespace ThemeSwitcher {
26
+ var displayName: string;
27
+ }
28
+ export { ThemeSwitcher };
@@ -0,0 +1,27 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Moon, Sun } from "lucide-react";
5
+ import { Button } from "./button";
6
+ import { cn } from "./lib/utils";
7
+ import { Switch } from "./switch";
8
+ function ThemeSwitcher({ checked, defaultChecked = false, onCheckedChange, isCollapsed = false, label, lightLabel = "Light theme", darkLabel = "Dark theme", useDarkLabel = "Use dark theme", useLightLabel = "Use light theme", className, disabled, ...props }) {
9
+ const isControlled = checked !== undefined;
10
+ const [uncontrolledChecked, setUncontrolledChecked] = React.useState(defaultChecked);
11
+ const isDark = isControlled ? checked : uncontrolledChecked;
12
+ const activeLabel = label !== null && label !== void 0 ? label : (isDark ? darkLabel : lightLabel);
13
+ const ActiveIcon = isDark ? Moon : Sun;
14
+ const CollapsedIcon = isDark ? Sun : Moon;
15
+ const handleCheckedChange = React.useCallback((nextChecked) => {
16
+ if (!isControlled) {
17
+ setUncontrolledChecked(nextChecked);
18
+ }
19
+ onCheckedChange === null || onCheckedChange === void 0 ? void 0 : onCheckedChange(nextChecked);
20
+ }, [isControlled, onCheckedChange]);
21
+ if (isCollapsed) {
22
+ return (_jsx("div", { "data-slot": "theme-switcher", className: cn("flex w-full justify-center py-1", className), children: _jsx(Button, { type: "button", variant: "ghost", size: "icon", disabled: disabled, "aria-label": isDark ? useLightLabel : useDarkLabel, "aria-pressed": isDark, onClick: () => handleCheckedChange(!isDark), children: _jsx(CollapsedIcon, { "aria-hidden": "true", className: "h-4 w-4" }) }) }));
23
+ }
24
+ return (_jsx("div", { "data-slot": "theme-switcher", className: "px-1.5 py-1", children: _jsx(Switch, { checked: isDark, onCheckedChange: handleCheckedChange, disabled: disabled, label: _jsxs("span", { className: "inline-flex min-w-0 items-center gap-2", children: [_jsx(ActiveIcon, { "aria-hidden": "true", className: "h-4 w-4 shrink-0" }), _jsx("span", { className: "truncate", children: activeLabel })] }), wrapperClassName: cn("w-full flex-row-reverse justify-between rounded-control px-3 py-2 text-muted-foreground transition-colors hover:bg-card hover:text-foreground", className), ...props }) }));
25
+ }
26
+ ThemeSwitcher.displayName = "ThemeSwitcher";
27
+ export { ThemeSwitcher };