@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.
- package/README.md +14 -14
- package/dist/alert.d.ts +21 -2
- package/dist/alert.js +10 -3
- package/dist/avatar.d.ts +5 -2
- package/dist/avatar.js +76 -8
- package/dist/back-button.d.ts +2 -0
- package/dist/back-button.js +1 -1
- package/dist/badge.d.ts +3 -2
- package/dist/badge.js +9 -3
- package/dist/breadcrumb.js +2 -1
- package/dist/button.d.ts +1 -1
- package/dist/button.js +12 -9
- package/dist/calendar.js +10 -9
- package/dist/card.d.ts +29 -1
- package/dist/card.js +61 -7
- package/dist/checkbox.d.ts +2 -0
- package/dist/checkbox.js +9 -3
- package/dist/close-button.d.ts +6 -0
- package/dist/close-button.js +8 -0
- package/dist/collapsible.d.ts +7 -1
- package/dist/collapsible.js +2 -2
- package/dist/command-search.d.ts +16 -1
- package/dist/command-search.js +42 -16
- package/dist/command.js +3 -3
- package/dist/copy-button.d.ts +3 -1
- package/dist/copy-button.js +4 -2
- package/dist/count-badge.d.ts +3 -1
- package/dist/count-badge.js +10 -5
- package/dist/date-input.d.ts +18 -3
- package/dist/date-input.js +94 -21
- package/dist/dialog-primitive.js +4 -3
- package/dist/draggable-tabs.d.ts +7 -0
- package/dist/draggable-tabs.js +6 -5
- package/dist/drawer.js +2 -2
- package/dist/dropdown-button.d.ts +18 -0
- package/dist/dropdown-button.js +31 -0
- package/dist/edit-button.d.ts +10 -0
- package/dist/edit-button.js +7 -0
- package/dist/editable-table.d.ts +3 -1
- package/dist/editable-table.js +25 -12
- package/dist/field.d.ts +7 -3
- package/dist/field.js +8 -4
- package/dist/file-upload-status-toast.d.ts +28 -0
- package/dist/file-upload-status-toast.js +136 -0
- package/dist/file-upload.js +8 -7
- package/dist/filter-popover.d.ts +37 -0
- package/dist/filter-popover.js +73 -0
- package/dist/filter-toggle.js +2 -2
- package/dist/index.d.ts +45 -14
- package/dist/index.js +19 -4
- package/dist/input-control.d.ts +3 -2
- package/dist/input-control.js +3 -2
- package/dist/label-value-grid.d.ts +4 -2
- package/dist/label-value-grid.js +7 -6
- package/dist/label-value.js +3 -1
- package/dist/layout-right-panel-edge.d.ts +6 -0
- package/dist/layout-right-panel-edge.js +6 -0
- package/dist/layout.d.ts +77 -5
- package/dist/layout.js +439 -35
- package/dist/lib/date-utils.d.ts +10 -2
- package/dist/lib/date-utils.js +91 -11
- package/dist/lib/utils.js +8 -1
- package/dist/link.js +2 -2
- package/dist/message-item.d.ts +62 -0
- package/dist/message-item.js +148 -0
- package/dist/multi-select.js +31 -5
- package/dist/notification.d.ts +82 -0
- package/dist/notification.js +121 -0
- package/dist/otp-input.js +1 -1
- package/dist/page-header.js +1 -1
- package/dist/page-section.d.ts +2 -1
- package/dist/page-section.js +4 -3
- package/dist/pagination.js +2 -2
- package/dist/popover-menu.d.ts +23 -1
- package/dist/popover-menu.js +75 -5
- package/dist/popover-primitive.js +1 -1
- package/dist/progress.d.ts +5 -2
- package/dist/progress.js +6 -3
- package/dist/radio-group.d.ts +27 -0
- package/dist/radio-group.js +50 -0
- package/dist/rich-text-editor.d.ts +56 -0
- package/dist/rich-text-editor.js +394 -0
- package/dist/routing-timeline.d.ts +22 -0
- package/dist/routing-timeline.js +58 -0
- package/dist/scroll-hint.d.ts +8 -0
- package/dist/scroll-hint.js +7 -0
- package/dist/search-input-shell.js +1 -1
- package/dist/search-input.js +2 -2
- package/dist/section-nav.js +4 -4
- package/dist/select-primitive.js +3 -3
- package/dist/select.js +1 -1
- package/dist/skeleton.js +1 -1
- package/dist/styles.css +241 -55
- package/dist/table-column-visibility.d.ts +21 -0
- package/dist/table-column-visibility.js +74 -0
- package/dist/table-toolbar.js +1 -1
- package/dist/table.d.ts +12 -1
- package/dist/table.js +67 -33
- package/dist/tabs.d.ts +14 -3
- package/dist/tabs.js +60 -6
- package/dist/tailwind-preset.js +46 -0
- package/dist/task-mode-shell.d.ts +24 -0
- package/dist/task-mode-shell.js +110 -0
- package/dist/textarea.js +2 -1
- package/dist/theme-switcher.d.ts +28 -0
- package/dist/theme-switcher.js +27 -0
- package/dist/theme.d.ts +167 -0
- package/dist/theme.js +112 -0
- package/dist/toaster.js +5 -4
- package/dist/tooltip.js +1 -1
- package/dist/typography.d.ts +1 -1
- package/dist/typography.js +5 -5
- package/dist/workspace-header.d.ts +13 -0
- package/dist/workspace-header.js +11 -0
- 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
|
-
|
|
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-
|
|
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-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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("
|
|
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
|
-
|
|
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
|
|
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-
|
|
108
|
-
? "max-w-full
|
|
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
|
|
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(
|
|
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, {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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:
|
|
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:
|
|
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-
|
|
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-
|
|
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
|
|
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:
|
|
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 };
|
package/dist/tailwind-preset.js
CHANGED
|
@@ -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-
|
|
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 };
|