@neasg/design-system 0.4.23 → 0.4.25

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.
@@ -11,7 +11,15 @@ const DialogPortal = DialogPrimitive.Portal;
11
11
  const DialogClose = DialogPrimitive.Close;
12
12
  const DialogOverlay = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Overlay, { ref: ref, "data-slot": "dialog-overlay", className: cn("fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", className), ...props })));
13
13
  DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
14
- const DialogSurface = React.forwardRef(({ className, children, hideCloseButton, ...props }, ref) => (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, {}), _jsxs(DialogPrimitive.Content, { ref: ref, className: cn("fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-3 border bg-background p-4 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-overlay", className), ...props, children: [children, !hideCloseButton ? (_jsxs(DialogPrimitive.Close, { className: "absolute right-2 top-2 flex h-6 w-6 cursor-pointer items-center justify-center rounded-control text-muted-foreground ring-offset-background transition-colors hover:bg-accent hover:text-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none", children: [_jsx(XIcon, { size: 16 }), _jsx("span", { className: "sr-only", children: "Close" })] })) : null] })] })));
14
+ const DialogSurface = React.forwardRef(({ className, children, hideCloseButton, ...props }, ref) => {
15
+ const [mounted, setMounted] = React.useState(false);
16
+ React.useEffect(() => {
17
+ setMounted(true);
18
+ }, []);
19
+ if (!mounted)
20
+ return null;
21
+ return (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, {}), _jsxs(DialogPrimitive.Content, { ref: ref, className: cn("fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-3 border bg-background p-4 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-overlay", className), ...props, children: [children, !hideCloseButton ? (_jsxs(DialogPrimitive.Close, { className: "absolute right-2 top-2 flex h-6 w-6 cursor-pointer items-center justify-center rounded-control text-muted-foreground ring-offset-background transition-colors hover:bg-accent hover:text-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none", children: [_jsx(XIcon, { size: 16 }), _jsx("span", { className: "sr-only", children: "Close" })] })) : null] })] }));
22
+ });
15
23
  DialogSurface.displayName = DialogPrimitive.Content.displayName;
16
24
  const DialogHeader = ({ className, ...props }) => (_jsx("div", { className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className), ...props }));
17
25
  DialogHeader.displayName = "DialogHeader";
package/dist/drawer.js CHANGED
@@ -46,6 +46,12 @@ const drawerSurfaceVariants = cva("fixed z-50 flex flex-col gap-3 overflow-y-aut
46
46
  },
47
47
  });
48
48
  function DrawerSurface({ side, className, children, hideCloseButton, ...props }) {
49
+ const [mounted, setMounted] = React.useState(false);
50
+ React.useEffect(() => {
51
+ setMounted(true);
52
+ }, []);
53
+ if (!mounted)
54
+ return null;
49
55
  return (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, {}), _jsxs(DialogPrimitive.Content, { className: cn(drawerSurfaceVariants({ side }), className), ...props, children: [children, !hideCloseButton ? (_jsxs(DialogPrimitive.Close, { className: "absolute right-2 top-2 flex h-6 w-6 cursor-pointer items-center justify-center rounded-control text-muted-foreground ring-offset-background transition-colors hover:bg-accent hover:text-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none", children: [_jsx(XIcon, { size: 16 }), _jsx("span", { className: "sr-only", children: "Close" })] })) : null] })] }));
50
56
  }
51
57
  function Drawer({ open, defaultOpen, onOpenChange, modal, trigger, title, description, children, footer, primaryAction, secondaryAction, side = "right", contentClassName, bodyClassName, hideCloseButton, }) {
package/dist/index.d.ts CHANGED
@@ -133,7 +133,7 @@ export { useTableSort } from "./use-table-sort";
133
133
  export type { UseTableSortOptions, UseTableSortReturn, } from "./use-table-sort";
134
134
  export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableRowHeader, TableCaption, TableRowSkeleton, TABLE_SKELETON_PRESETS, } from "./table";
135
135
  export type { TableColumn, TableColumnWidths, TableProps, TableResizeMode, TableRowExpansionProps, TableRowTone, TableSortDirection, TableSortingProps, TableSkeletonConfig, TableRowSkeletonProps, } from "./table";
136
- export type { TableColumnFilter, TableColumnFilterOption, TableDateColumnFilter, TableOptionsColumnFilter, TableTextColumnFilter, } from "./table-column-filter";
136
+ export type { TableColumnFilter, TableColumnFilterOption, TableDateColumnFilter, TableDateRangeColumnFilter, TableOptionsColumnFilter, TableTextColumnFilter, } from "./table-column-filter";
137
137
  export { Tabs, SubTabs, TabsRoot, TabWithDropdown, type TabsItem, type TabsItemLabel, type TabsItemLabelProps, type TabsProps, type SubTabsItem, type SubTabsProps, TabsList, TabsTrigger, TabsContent, type TabWithDropdownProps, useTabsContext, type TabDropdownSection, } from "./tabs";
138
138
  export { Tooltip, TooltipRoot, TooltipTrigger, TooltipContent, TooltipProvider, type TooltipProps, } from "./tooltip";
139
139
  export { Toaster, toast } from "./toaster";
@@ -136,6 +136,13 @@ function MultiSelect({ label, options, value, onValueChange, placeholder = "Sele
136
136
  closeAndFocusTrigger();
137
137
  return;
138
138
  }
139
+ if (event.key === "Enter" || event.key === " ") {
140
+ event.preventDefault();
141
+ if (currentIndex >= 0) {
142
+ enabledOptions[currentIndex].click();
143
+ }
144
+ return;
145
+ }
139
146
  if (event.key === "Home" || event.key === "End") {
140
147
  event.preventDefault();
141
148
  focusBoundaryOption(event.key === "Home" ? "first" : "last");
@@ -6,6 +6,14 @@ import { cn } from "./lib/utils";
6
6
  const PopoverRoot = PopoverPrimitive.Root;
7
7
  const PopoverTrigger = PopoverPrimitive.Trigger;
8
8
  const PopoverAnchor = PopoverPrimitive.Anchor;
9
- const PopoverContent = React.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => (_jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Content, { ref: ref, align: align, sideOffset: sideOffset, className: cn("shadow-overlay-strong z-50 w-72 rounded-overlay border bg-popover p-4 text-popover-foreground outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", className), ...props }) })));
9
+ const PopoverContent = React.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => {
10
+ const [mounted, setMounted] = React.useState(false);
11
+ React.useEffect(() => {
12
+ setMounted(true);
13
+ }, []);
14
+ if (!mounted)
15
+ return null;
16
+ return (_jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Content, { ref: ref, align: align, sideOffset: sideOffset, className: cn("shadow-overlay-strong z-50 w-72 rounded-overlay border bg-popover p-4 text-popover-foreground outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", className), ...props }) }));
17
+ });
10
18
  PopoverContent.displayName = PopoverPrimitive.Content.displayName;
11
19
  export { PopoverAnchor, PopoverContent, PopoverRoot, PopoverTrigger };
@@ -21,9 +21,17 @@ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
21
21
  const SelectScrollDownButton = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.ScrollDownButton, { ref: ref, "data-slot": "select-scroll-down-button", className: cn("flex cursor-pointer items-center justify-center py-1", className), ...props, children: _jsx(ChevronDown, { className: "h-4 w-4" }) })));
22
22
  SelectScrollDownButton.displayName =
23
23
  SelectPrimitive.ScrollDownButton.displayName;
24
- const SelectContent = React.forwardRef(({ className, children, header, position = "popper", viewportClassName, ...props }, ref) => (_jsx(SelectPrimitive.Portal, { children: _jsxs(SelectPrimitive.Content, { ref: ref, "data-slot": "select-content", className: cn("relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-overlay border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", position === "popper" &&
25
- "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className), position: position, ...props, children: [header ? _jsx("div", { className: "border-b p-1", children: header }) : null, _jsx(SelectScrollUpButton, {}), _jsx(SelectPrimitive.Viewport, { className: cn("p-1", position === "popper" &&
26
- "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]", viewportClassName), children: children }), _jsx(SelectScrollDownButton, {})] }) })));
24
+ const SelectContent = React.forwardRef(({ className, children, header, position = "popper", viewportClassName, ...props }, ref) => {
25
+ const [mounted, setMounted] = React.useState(false);
26
+ React.useEffect(() => {
27
+ setMounted(true);
28
+ }, []);
29
+ if (!mounted)
30
+ return null;
31
+ return (_jsx(SelectPrimitive.Portal, { children: _jsxs(SelectPrimitive.Content, { ref: ref, "data-slot": "select-content", className: cn("relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-overlay border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", position === "popper" &&
32
+ "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className), position: position, ...props, children: [header ? _jsx("div", { className: "border-b p-1", children: header }) : null, _jsx(SelectScrollUpButton, {}), _jsx(SelectPrimitive.Viewport, { className: cn("p-1", position === "popper" &&
33
+ "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]", viewportClassName), children: children }), _jsx(SelectScrollDownButton, {})] }) }));
34
+ });
27
35
  SelectContent.displayName = SelectPrimitive.Content.displayName;
28
36
  const SelectLabel = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.Label, { ref: ref, "data-slot": "select-label", className: cn("px-2 py-1.5 text-xs text-muted-foreground", className), ...props })));
29
37
  SelectLabel.displayName = SelectPrimitive.Label.displayName;
@@ -1,4 +1,5 @@
1
1
  import * as React from "react";
2
+ import { type DateRangeInputValue } from "./date-input";
2
3
  interface TableColumnFilterBase {
3
4
  /** Overrides the column header text in accessible filter labels. */
4
5
  label?: string;
@@ -33,7 +34,14 @@ export interface TableDateColumnFilter extends TableColumnFilterBase {
33
34
  minDate?: Date;
34
35
  maxDate?: Date;
35
36
  }
36
- export type TableColumnFilter = TableTextColumnFilter | TableOptionsColumnFilter | TableDateColumnFilter;
37
+ export interface TableDateRangeColumnFilter extends TableColumnFilterBase {
38
+ type: "dateRange";
39
+ value: DateRangeInputValue;
40
+ onValueChange: (value: DateRangeInputValue) => void;
41
+ minDate?: Date;
42
+ maxDate?: Date;
43
+ }
44
+ export type TableColumnFilter = TableTextColumnFilter | TableOptionsColumnFilter | TableDateColumnFilter | TableDateRangeColumnFilter;
37
45
  interface TableColumnFilterControlProps {
38
46
  filter: TableColumnFilter;
39
47
  label: string;
@@ -9,9 +9,15 @@ import { cn } from "./lib/utils";
9
9
  import { Popover } from "./popover";
10
10
  import { PopoverMenuFooter, PopoverMenuFooterAction, PopoverMenuSearch, } from "./popover-menu";
11
11
  function isTableColumnFilterActive(filter) {
12
- return filter.type === "options"
13
- ? filter.values.length > 0
14
- : filter.value.trim().length > 0;
12
+ switch (filter.type) {
13
+ case "options":
14
+ return filter.values.length > 0;
15
+ case "dateRange":
16
+ return Boolean(filter.value.from || filter.value.to);
17
+ case "text":
18
+ case "date":
19
+ return filter.value.trim().length > 0;
20
+ }
15
21
  }
16
22
  function getOptionsFilterSummary(filter) {
17
23
  var _a, _b, _c;
@@ -68,6 +74,13 @@ function DateColumnFilter({ filter, label, }) {
68
74
  return (_jsx(DateInput, { label: `Filter by ${accessibleLabel}${active ? ", active" : ""}`, hideLabel: true, value: filter.value, onValueChange: filter.onValueChange, placeholder: (_b = filter.placeholder) !== null && _b !== void 0 ? _b : "Pick a date", disabled: filter.disabled, minDate: filter.minDate, maxDate: filter.maxDate, clearable: true, clearLabel: `Clear ${accessibleLabel} filter`, className: "gap-0", triggerClassName: cn(filterToggleVariants({ selected: active }), "h-9 w-full min-w-0 justify-start text-sm font-normal shadow-none", active ? "pl-2 pr-16" : "pl-2 pr-9", !active &&
69
75
  "border-0 bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground") }));
70
76
  }
77
+ function DateRangeColumnFilter({ filter, label, }) {
78
+ var _a, _b;
79
+ const active = isTableColumnFilterActive(filter);
80
+ const accessibleLabel = (_a = filter.label) !== null && _a !== void 0 ? _a : label;
81
+ return (_jsx(DateInput, { mode: "range", label: `Filter by ${accessibleLabel}${active ? ", active" : ""}`, hideLabel: true, value: filter.value, onValueChange: filter.onValueChange, placeholder: (_b = filter.placeholder) !== null && _b !== void 0 ? _b : "Pick date range", disabled: filter.disabled, minDate: filter.minDate, maxDate: filter.maxDate, clearable: true, clearLabel: `Clear ${accessibleLabel} filter`, className: "gap-0", triggerClassName: cn(filterToggleVariants({ selected: active }), "h-9 w-full min-w-0 justify-start text-sm font-normal shadow-none", active ? "pl-2 pr-16" : "pl-2 pr-9", !active &&
82
+ "border-0 bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground") }));
83
+ }
71
84
  function TableColumnFilterControl({ filter, label, }) {
72
85
  if (filter.type === "text") {
73
86
  return _jsx(TextColumnFilter, { filter: filter, label: label });
@@ -75,6 +88,9 @@ function TableColumnFilterControl({ filter, label, }) {
75
88
  if (filter.type === "options") {
76
89
  return _jsx(OptionsColumnFilter, { filter: filter, label: label });
77
90
  }
91
+ if (filter.type === "dateRange") {
92
+ return _jsx(DateRangeColumnFilter, { filter: filter, label: label });
93
+ }
78
94
  return _jsx(DateColumnFilter, { filter: filter, label: label });
79
95
  }
80
96
  export { TableColumnFilterControl };
package/dist/table.d.ts CHANGED
@@ -124,12 +124,14 @@ export interface TableRowExpansionProps<Row> {
124
124
  getToggleLabel?: (row: Row, rowIndex: number, expanded: boolean) => string;
125
125
  }
126
126
  interface SortableTableHeadProps<T extends string> {
127
+ ref?: React.Ref<HTMLTableCellElement>;
127
128
  field: T;
128
129
  children: React.ReactNode;
129
130
  currentSortField: T | null;
130
131
  currentSortDirection: SortDirection;
131
132
  onSort: (field: T) => void;
132
133
  className?: string;
134
+ style?: React.CSSProperties;
133
135
  allowWrap?: boolean;
134
136
  width?: string;
135
137
  minWidth?: string;
@@ -139,8 +141,10 @@ interface SortableTableHeadProps<T extends string> {
139
141
  resizeHandle?: React.ReactNode;
140
142
  treatWidthAsMinWidth?: boolean;
141
143
  containOverflow?: boolean;
144
+ gripIcon?: React.ReactNode;
145
+ [key: string]: unknown;
142
146
  }
143
- declare function SortableTableHead<T extends string>({ field, children, currentSortField, currentSortDirection, onSort, className, allowWrap, width, minWidth, maxWidth, align, sortLabel, resizeHandle, treatWidthAsMinWidth, containOverflow, }: SortableTableHeadProps<T>): import("react/jsx-runtime").JSX.Element;
147
+ declare function SortableTableHead<T extends string>({ ref, field, children, currentSortField, currentSortDirection, onSort, className, style, allowWrap, width, minWidth, maxWidth, align, sortLabel, resizeHandle, treatWidthAsMinWidth, containOverflow, gripIcon, ...rest }: SortableTableHeadProps<T>): import("react/jsx-runtime").JSX.Element;
144
148
  export interface TableColumnResizeHandleRenderProps<Row> {
145
149
  column: TableColumn<Row>;
146
150
  nextColumn: TableColumn<Row>;
@@ -194,6 +198,10 @@ export interface TableProps<Row> extends Omit<React.HTMLAttributes<HTMLDivElemen
194
198
  striped?: boolean;
195
199
  /** Adds a disclosure column and full-width expandable content for nested tables or row details. */
196
200
  rowExpansion?: TableRowExpansionProps<Row>;
201
+ /** When true, non-sticky header cells become draggable for column reordering. */
202
+ reorderableColumns?: boolean;
203
+ /** Called with the new ordered column key array after a drag-and-drop reorder. */
204
+ onColumnOrderChange?: (columnKeys: string[]) => void;
197
205
  }
198
- declare function Table<Row>({ accessibleLabel, columns, rows, getRowKey, emptyState, toolbar, caption, className, containerClassName, tableClassName, rowClassName, rowTone, maxHeight, minHeight, sorting, pagination, loading, loadingLabel, loadingRows, visibleColumnKeys, resizableColumns, columnResizeMode, columnWidths, defaultColumnWidths, onColumnWidthsChange, renderColumnResizeHandle, tableLayout, centerEmptyState, emptyStateMinHeight, striped, rowExpansion, onScroll, style, ...props }: TableProps<Row>): import("react/jsx-runtime").JSX.Element;
206
+ declare function Table<Row>({ accessibleLabel, columns, rows, getRowKey, emptyState, toolbar, caption, className, containerClassName, tableClassName, rowClassName, rowTone, maxHeight, minHeight, sorting, pagination, loading, loadingLabel, loadingRows, visibleColumnKeys, resizableColumns, columnResizeMode, columnWidths, defaultColumnWidths, onColumnWidthsChange, renderColumnResizeHandle, tableLayout, centerEmptyState, emptyStateMinHeight, striped, rowExpansion, reorderableColumns, onColumnOrderChange, onScroll, style, ...props }: TableProps<Row>): import("react/jsx-runtime").JSX.Element;
199
207
  export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableRowHeader, TableCaption, SortableTableHead, TableRowSkeleton, };
package/dist/table.js CHANGED
@@ -1,5 +1,8 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
+ import { DndContext, KeyboardSensor, PointerSensor, closestCenter, useSensor, useSensors, } from "@dnd-kit/core";
4
+ import { SortableContext, arrayMove, horizontalListSortingStrategy, sortableKeyboardCoordinates, useSortable, } from "@dnd-kit/sortable";
5
+ import { CSS } from "@dnd-kit/utilities";
3
6
  import { ArrowUpDown, ChevronRight } from "lucide-react";
4
7
  import { ArrowDownIcon } from "./animated-icons/arrow-down";
5
8
  import { ArrowUpIcon } from "./animated-icons/arrow-up";
@@ -148,7 +151,7 @@ function getColumnHeaderAriaLabel(column) {
148
151
  }
149
152
  return undefined;
150
153
  }
151
- 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 })));
154
+ const TableHeader = React.forwardRef(({ className, ...props }, ref) => (_jsx("thead", { ref: ref, className: cn("sticky top-0 z-10 overflow-visible bg-[hsl(var(--table-header-background))] [&_tr]:border-b [&_tr]:border-[hsl(var(--table-border))] [&>tr]:bg-inherit", className), ...props })));
152
155
  TableHeader.displayName = "TableHeader";
153
156
  const TableBody = React.forwardRef(({ className, ...props }, ref) => (_jsx("tbody", { ref: ref, className: cn("[&_tr:last-child]:border-0", className), ...props })));
154
157
  TableBody.displayName = "TableBody";
@@ -262,19 +265,35 @@ export const TABLE_SKELETON_PRESETS = {
262
265
  })),
263
266
  }),
264
267
  };
265
- function SortableTableHead({ field, children, currentSortField, currentSortDirection, onSort, className, allowWrap = false, width, minWidth, maxWidth, align, sortLabel, resizeHandle, treatWidthAsMinWidth, containOverflow, }) {
268
+ function SortableTableHead({ ref, field, children, currentSortField, currentSortDirection, onSort, className, style, allowWrap = false, width, minWidth, maxWidth, align, sortLabel, resizeHandle, treatWidthAsMinWidth, containOverflow, gripIcon, ...rest }) {
266
269
  const isSorted = currentSortField === field;
267
270
  const resolvedSortLabel = sortLabel !== null && sortLabel !== void 0 ? sortLabel : (typeof children === "string" || typeof children === "number"
268
271
  ? String(children)
269
272
  : field);
270
- return (_jsxs(TableHead, { "data-slot": "table-column-head", scope: "col", "aria-sort": isSorted
273
+ return (_jsxs(TableHead, { ref: ref, "data-slot": "table-column-head", scope: "col", "aria-sort": isSorted
271
274
  ? currentSortDirection === "asc"
272
275
  ? "ascending"
273
276
  : "descending"
274
- : undefined, className: cn(resizeHandle && "relative", alignClassName(align), className), wrap: allowWrap, width: width, minWidth: minWidth, maxWidth: maxWidth, treatWidthAsMinWidth: treatWidthAsMinWidth, containOverflow: containOverflow, 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", containOverflow && "max-w-full overflow-hidden", allowWrap
277
+ : undefined, className: cn(resizeHandle && "relative", alignClassName(align), className), style: style, wrap: allowWrap, width: width, minWidth: minWidth, maxWidth: maxWidth, treatWidthAsMinWidth: treatWidthAsMinWidth, containOverflow: containOverflow, ...rest, children: [gripIcon, _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", containOverflow && "max-w-full overflow-hidden", allowWrap
275
278
  ? "max-w-full whitespace-normal text-left"
276
279
  : "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 ${resolvedSortLabel}`, children: [_jsx("span", { className: cn("min-w-0", containOverflow && !allowWrap && "truncate", containOverflow && allowWrap && "break-words [overflow-wrap:anywhere]"), children: children }), _jsx("span", { "aria-hidden": "true", 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" })) })] }), resizeHandle] }));
277
280
  }
281
+ const DragGripIcon = (_jsx("span", { "aria-hidden": "true", className: "pointer-events-none absolute bottom-0 left-1/2 z-20 -translate-x-1/2 translate-y-1/2 rounded border border-border bg-background px-1.5 py-0.5 text-muted-foreground opacity-0 shadow-sm transition-opacity group-hover/drag-col:opacity-100", children: _jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { transform: "rotate(90deg)" }, children: [_jsx("circle", { cx: "9", cy: "5", r: "1" }), _jsx("circle", { cx: "15", cy: "5", r: "1" }), _jsx("circle", { cx: "9", cy: "12", r: "1" }), _jsx("circle", { cx: "15", cy: "12", r: "1" }), _jsx("circle", { cx: "9", cy: "19", r: "1" }), _jsx("circle", { cx: "15", cy: "19", r: "1" })] }) }));
282
+ function DraggableColumnHead({ columnKey, disabled, children, }) {
283
+ const { attributes, listeners, setNodeRef, transform, transition, isDragging, } = useSortable({ id: columnKey, disabled });
284
+ return children({
285
+ ref: setNodeRef,
286
+ style: {
287
+ transform: CSS.Translate.toString(transform),
288
+ transition,
289
+ zIndex: isDragging ? 30 : undefined,
290
+ },
291
+ className: cn(!disabled &&
292
+ "group/drag-col relative !overflow-visible cursor-grab active:cursor-grabbing before:pointer-events-none before:absolute before:left-0 before:top-1/2 before:z-[1] before:h-6 before:-translate-y-1/2 before:w-px before:rounded-full before:bg-transparent before:transition-colors hover:before:bg-[hsl(var(--table-border))] after:pointer-events-none after:absolute after:right-0 after:top-1/2 after:z-[1] after:h-6 after:-translate-y-1/2 after:w-px after:rounded-full after:bg-transparent after:transition-colors hover:after:bg-[hsl(var(--table-border))]", isDragging && "z-10 opacity-90 shadow-lg"),
293
+ gripIcon: disabled ? null : DragGripIcon,
294
+ dragHandleProps: disabled ? {} : { ...attributes, ...listeners },
295
+ });
296
+ }
278
297
  const TableColumnResizeHandle = React.forwardRef(function TableColumnResizeHandle({ column, nextColumn, currentWidth, minWidth, maxWidth, onResizeStart, onResizeByKeyboard, className, onClick, ...props }, ref) {
279
298
  const label = getColumnResizeLabel(column);
280
299
  return (_jsx("div", { ...props, ref: ref, role: "separator", tabIndex: 0, "aria-orientation": "vertical", "aria-label": `Resize ${label} column`, title: `Resize ${label} column`, "aria-valuenow": currentWidth, "aria-valuemin": minWidth, "aria-valuemax": maxWidth, "aria-valuetext": currentWidth === undefined ? undefined : `${currentWidth} pixels`, onPointerDown: (event) => onResizeStart(column, nextColumn, event), onClick: (event) => {
@@ -365,7 +384,7 @@ function getColumnPairResizeRange(column, nextColumn, columnWidths) {
365
384
  maxWidth: Math.round(currentWidth + maxDelta),
366
385
  };
367
386
  }
368
- function Table({ accessibleLabel, 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, minHeight, sorting, pagination, loading = false, loadingLabel = "Loading table data", loadingRows = 5, visibleColumnKeys, resizableColumns = false, columnResizeMode = "pair", columnWidths, defaultColumnWidths, onColumnWidthsChange, renderColumnResizeHandle, tableLayout, centerEmptyState = false, emptyStateMinHeight = DEFAULT_CENTERED_EMPTY_STATE_MIN_HEIGHT, striped = false, rowExpansion, onScroll, style, ...props }) {
387
+ function Table({ accessibleLabel, 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, minHeight, sorting, pagination, loading = false, loadingLabel = "Loading table data", loadingRows = 5, visibleColumnKeys, resizableColumns = false, columnResizeMode = "pair", columnWidths, defaultColumnWidths, onColumnWidthsChange, renderColumnResizeHandle, tableLayout, centerEmptyState = false, emptyStateMinHeight = DEFAULT_CENTERED_EMPTY_STATE_MIN_HEIGHT, striped = false, rowExpansion, reorderableColumns = false, onColumnOrderChange, onScroll, style, ...props }) {
369
388
  const visibleColumns = getVisibleColumns(columns, visibleColumnKeys);
370
389
  const hasExpansionColumn = Boolean(rowExpansion);
371
390
  const hasColumnFilters = visibleColumns.some((column) => column.filter);
@@ -433,6 +452,24 @@ function Table({ accessibleLabel, columns, rows, getRowKey, emptyState = (_jsx(E
433
452
  var _a;
434
453
  (_a = resizeCleanupRef.current) === null || _a === void 0 ? void 0 : _a.call(resizeCleanupRef);
435
454
  }, []);
455
+ const columnReorderSensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 8 } }), useSensor(KeyboardSensor, {
456
+ coordinateGetter: sortableKeyboardCoordinates,
457
+ }));
458
+ const handleColumnDragEnd = React.useCallback((event) => {
459
+ const { active, over } = event;
460
+ if (!over || active.id === over.id || !onColumnOrderChange) {
461
+ return;
462
+ }
463
+ const currentKeys = visibleColumns.map((col) => col.key);
464
+ const oldIndex = currentKeys.indexOf(String(active.id));
465
+ const newIndex = currentKeys.indexOf(String(over.id));
466
+ if (oldIndex >= 0 && newIndex >= 0) {
467
+ onColumnOrderChange(arrayMove(currentKeys, oldIndex, newIndex));
468
+ }
469
+ }, [visibleColumns, onColumnOrderChange]);
470
+ const reorderableColumnIds = React.useMemo(() => reorderableColumns
471
+ ? visibleColumns.filter((col) => !col.sticky).map((col) => col.key)
472
+ : [], [reorderableColumns, visibleColumns]);
436
473
  const commitColumnWidths = React.useCallback((updater) => {
437
474
  const nextColumnWidths = updater(columnWidthsRef.current);
438
475
  columnWidthsRef.current = nextColumnWidths;
@@ -584,6 +621,33 @@ function Table({ accessibleLabel, columns, rows, getRowKey, emptyState = (_jsx(E
584
621
  }
585
622
  (_a = rowExpansion === null || rowExpansion === void 0 ? void 0 : rowExpansion.onExpandedRowKeysChange) === null || _a === void 0 ? void 0 : _a.call(rowExpansion, Array.from(nextExpandedRowKeys));
586
623
  }, [controlledExpandedRowKeys, rowExpansion, uncontrolledExpandedRowKeys]);
624
+ function renderColumnHead(column, columnIndex, dragProps) {
625
+ var _a;
626
+ const sortField = (_a = column.sortKey) !== null && _a !== void 0 ? _a : column.key;
627
+ const isSortable = column.sortable && sorting;
628
+ const columnWidth = getRenderedColumnWidth({
629
+ column,
630
+ columnWidths: resolvedColumnWidths,
631
+ resizing: isColumnResizingEnabled,
632
+ });
633
+ const resizeHandle = renderResizeHandle(column, columnIndex);
634
+ if (isSortable) {
635
+ return (_jsx(SortableTableHead, { ref: dragProps === null || dragProps === void 0 ? void 0 : dragProps.ref, field: sortField, currentSortField: sorting.field, currentSortDirection: sorting.direction, onSort: sorting.onSort, className: cn(stickyClassName(column.sticky, "head", getStickyShadowVisibility(column.sticky, stickyScrollState)), column.headerClassName, dragProps === null || dragProps === void 0 ? void 0 : dragProps.className), style: dragProps === null || dragProps === void 0 ? void 0 : dragProps.style, allowWrap: column.wrap, width: columnWidth, minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled), maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled), align: column.align, sortLabel: column.sortLabel, resizeHandle: resizeHandle, gripIcon: dragProps === null || dragProps === void 0 ? void 0 : dragProps.gripIcon, treatWidthAsMinWidth: !isColumnResizingEnabled, containOverflow: isColumnResizingEnabled, ...dragProps === null || dragProps === void 0 ? void 0 : dragProps.dragHandleProps, children: column.header }, column.key));
636
+ }
637
+ return (_jsxs(TableHead, { ref: dragProps === null || dragProps === void 0 ? void 0 : dragProps.ref, "data-slot": "table-column-head", "aria-label": getColumnHeaderAriaLabel(column), scope: "col", width: columnWidth, minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled), maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled), wrap: column.wrap, treatWidthAsMinWidth: !isColumnResizingEnabled, containOverflow: isColumnResizingEnabled, className: cn(resizeHandle && "relative", alignClassName(column.align), stickyClassName(column.sticky, "head", getStickyShadowVisibility(column.sticky, stickyScrollState)), column.headerClassName, dragProps === null || dragProps === void 0 ? void 0 : dragProps.className), style: dragProps === null || dragProps === void 0 ? void 0 : dragProps.style, ...dragProps === null || dragProps === void 0 ? void 0 : dragProps.dragHandleProps, children: [dragProps === null || dragProps === void 0 ? void 0 : dragProps.gripIcon, column.header, resizeHandle] }, column.key));
638
+ }
639
+ function renderColumnHeaders(cols) {
640
+ const headers = cols.map((column, columnIndex) => {
641
+ if (!reorderableColumns) {
642
+ return renderColumnHead(column, columnIndex);
643
+ }
644
+ return (_jsx(DraggableColumnHead, { columnKey: column.key, disabled: Boolean(column.sticky), children: (dragProps) => renderColumnHead(column, columnIndex, dragProps) }, column.key));
645
+ });
646
+ if (!reorderableColumns) {
647
+ return headers;
648
+ }
649
+ return (_jsx(DndContext, { sensors: columnReorderSensors, collisionDetection: closestCenter, onDragEnd: handleColumnDragEnd, children: _jsx(SortableContext, { items: reorderableColumnIds, strategy: horizontalListSortingStrategy, children: headers }) }));
650
+ }
587
651
  const resolvedEmptyState = centerEmptyState ? (_jsx("div", { "data-slot": "table-empty-state", className: "flex w-full min-w-0 items-center justify-center whitespace-normal text-center [&>*]:mx-auto", style: { minHeight: emptyStateMinHeight }, children: emptyState })) : (emptyState);
588
652
  const container = (_jsxs(TableContainer, { ref: tableContainerRef, role: stickyScrollState.scrollable ? "region" : undefined, "aria-label": stickyScrollState.scrollable
589
653
  ? `${accessibleLabel} scroll area`
@@ -610,18 +674,7 @@ function Table({ accessibleLabel, columns, rows, getRowKey, emptyState = (_jsx(E
610
674
  minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled),
611
675
  maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled),
612
676
  treatWidthAsMinWidth: !isColumnResizingEnabled,
613
- }) }, column.key)))] }), _jsxs(TableHeader, { children: [_jsxs(TableRow, { children: [hasExpansionColumn ? (_jsx(TableHead, { "data-slot": "table-expansion-head", "aria-label": "Expand rows", scope: "col", className: "w-12 px-2" })) : null, visibleColumns.map((column, columnIndex) => {
614
- var _a;
615
- const sortField = (_a = column.sortKey) !== null && _a !== void 0 ? _a : column.key;
616
- const isSortable = column.sortable && sorting;
617
- const columnWidth = getRenderedColumnWidth({
618
- column,
619
- columnWidths: resolvedColumnWidths,
620
- resizing: isColumnResizingEnabled,
621
- });
622
- const resizeHandle = renderResizeHandle(column, columnIndex);
623
- return (isSortable ? (_jsx(SortableTableHead, { field: sortField, currentSortField: sorting.field, currentSortDirection: sorting.direction, onSort: sorting.onSort, className: cn(stickyClassName(column.sticky, "head", getStickyShadowVisibility(column.sticky, stickyScrollState)), column.headerClassName), allowWrap: column.wrap, width: columnWidth, minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled), maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled), align: column.align, sortLabel: column.sortLabel, resizeHandle: resizeHandle, treatWidthAsMinWidth: !isColumnResizingEnabled, containOverflow: isColumnResizingEnabled, children: column.header }, column.key)) : (_jsxs(TableHead, { "data-slot": "table-column-head", "aria-label": getColumnHeaderAriaLabel(column), scope: "col", width: columnWidth, minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled), maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled), wrap: column.wrap, treatWidthAsMinWidth: !isColumnResizingEnabled, containOverflow: isColumnResizingEnabled, className: cn(resizeHandle && "relative", alignClassName(column.align), stickyClassName(column.sticky, "head", getStickyShadowVisibility(column.sticky, stickyScrollState)), column.headerClassName), children: [column.header, resizeHandle] }, column.key)));
624
- })] }), hasColumnFilters ? (_jsxs(TableRow, { "data-slot": "table-filter-row", className: "!bg-card", children: [hasExpansionColumn ? (_jsx(TableCell, { "data-slot": "table-expansion-filter-cell", "aria-hidden": "true", className: "w-12 px-2" })) : null, visibleColumns.map((column) => {
677
+ }) }, column.key)))] }), _jsxs(TableHeader, { children: [_jsxs(TableRow, { children: [hasExpansionColumn ? (_jsx(TableHead, { "data-slot": "table-expansion-head", "aria-label": "Expand rows", scope: "col", className: "w-12 px-2" })) : null, renderColumnHeaders(visibleColumns)] }), hasColumnFilters ? (_jsxs(TableRow, { "data-slot": "table-filter-row", className: "!bg-card", children: [hasExpansionColumn ? (_jsx(TableCell, { "data-slot": "table-expansion-filter-cell", "aria-hidden": "true", className: "w-12 px-2" })) : null, visibleColumns.map((column) => {
625
678
  const columnWidth = getRenderedColumnWidth({
626
679
  column,
627
680
  columnWidths: resolvedColumnWidths,
package/dist/toaster.js CHANGED
@@ -32,7 +32,7 @@ function Toaster({ position = "top-right", visibleToasts = 3, closeButton = true
32
32
  style: toastLayoutStyle,
33
33
  classNames: {
34
34
  toast: "group relative !grid w-[var(--width)] !grid-cols-[auto_var(--space-2)_auto_minmax(0,1fr)] !items-start !gap-x-0 !gap-y-2 !rounded-overlay !border !border-border/80 !bg-popover !p-3 !pr-10 !text-popover-foreground !shadow-overlay-strong [&_[data-action]]:col-start-1 [&_[data-button]]:row-start-2 [&_[data-button]]:!ml-0 [&_[data-button]]:!mr-0 [&_[data-cancel]]:col-start-1 [&_[data-content]]:col-span-full [&_[data-content]]:col-start-1 [&_[data-content]]:row-start-1 [&_[data-icon]]:col-start-1 [&_[data-icon]]:row-start-1 [&:has([data-cancel])_[data-action]]:col-start-3 [&:has([data-icon])]:!grid-cols-[auto_var(--space-3)_auto_var(--space-2)_auto_minmax(0,1fr)] [&:has([data-icon])_[data-action]]:col-start-3 [&:has([data-icon])_[data-cancel]]:col-start-3 [&:has([data-icon])_[data-content]]:col-start-3 [&:has([data-icon]):has([data-cancel])_[data-action]]:col-start-5",
35
- content: "flex min-w-0 flex-col gap-1",
35
+ content: "flex min-w-0 flex-col gap-1 [&:not(:has([data-description]))]:min-h-8 [&:not(:has([data-description]))]:justify-center",
36
36
  title: cn(typographyVariants({ variant: "bodySm" }), "!font-medium !text-popover-foreground"),
37
37
  description: cn(typographyVariants({ variant: "bodySm" }), "!text-muted-foreground"),
38
38
  icon: "!mx-0 flex !h-8 !w-8 shrink-0 !items-center !justify-center self-start rounded-control !border !border-border !bg-muted !text-muted-foreground [&>svg]:!m-0 [&>svg]:!h-4 [&>svg]:!w-4",
package/dist/tooltip.js CHANGED
@@ -8,7 +8,15 @@ function TooltipProvider({ delayDuration = 100, ...props }) {
8
8
  }
9
9
  const TooltipRoot = TooltipPrimitive.Root;
10
10
  const TooltipTrigger = TooltipPrimitive.Trigger;
11
- const TooltipContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => (_jsx(TooltipPrimitive.Portal, { children: _jsx(TooltipPrimitive.Content, { ref: ref, sideOffset: sideOffset, className: cn("z-50 overflow-hidden rounded-overlay bg-[hsl(var(--tooltip-background))] px-3 py-1.5 text-sm text-[hsl(var(--tooltip-foreground))] shadow-[var(--overlay-shadow)] animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", className), ...props }) })));
11
+ const TooltipContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => {
12
+ const [mounted, setMounted] = React.useState(false);
13
+ React.useEffect(() => {
14
+ setMounted(true);
15
+ }, []);
16
+ if (!mounted)
17
+ return null;
18
+ return (_jsx(TooltipPrimitive.Portal, { children: _jsx(TooltipPrimitive.Content, { ref: ref, sideOffset: sideOffset, className: cn("z-50 overflow-hidden rounded-overlay bg-[hsl(var(--tooltip-background))] px-3 py-1.5 text-sm text-[hsl(var(--tooltip-foreground))] shadow-[var(--overlay-shadow)] animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", className), ...props }) }));
19
+ });
12
20
  TooltipContent.displayName = TooltipPrimitive.Content.displayName;
13
21
  function Tooltip({ content, children, delayDuration = 100, side = "top", sideOffset = 4, contentClassName, open, defaultOpen, onOpenChange, }) {
14
22
  return (_jsx(TooltipProvider, { delayDuration: delayDuration, children: _jsxs(TooltipRoot, { open: open, defaultOpen: defaultOpen, onOpenChange: onOpenChange, children: [_jsx(TooltipTrigger, { asChild: true, children: children }), _jsx(TooltipContent, { side: side, sideOffset: sideOffset, className: contentClassName, children: content })] }) }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neasg/design-system",
3
- "version": "0.4.23",
3
+ "version": "0.4.25",
4
4
  "description": "NEA shared design system primitives, theme tokens, and Storybook docs.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",