@neasg/design-system 0.4.35 → 0.4.113

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +9 -0
  2. package/dist/activity-card.d.ts +31 -0
  3. package/dist/activity-card.js +65 -0
  4. package/dist/alert.d.ts +1 -1
  5. package/dist/autocomplete.d.ts +84 -0
  6. package/dist/autocomplete.js +267 -0
  7. package/dist/avatar.d.ts +3 -1
  8. package/dist/avatar.js +27 -10
  9. package/dist/badge.d.ts +1 -1
  10. package/dist/badge.js +1 -1
  11. package/dist/button.d.ts +2 -2
  12. package/dist/button.js +3 -1
  13. package/dist/calendar.js +1 -1
  14. package/dist/card.d.ts +1 -1
  15. package/dist/card.js +1 -1
  16. package/dist/changelog-action-panel.d.ts +17 -0
  17. package/dist/changelog-action-panel.js +14 -0
  18. package/dist/checklist.d.ts +63 -0
  19. package/dist/checklist.js +30 -0
  20. package/dist/compound-search-input.d.ts +10 -0
  21. package/dist/compound-search-input.js +19 -0
  22. package/dist/date-input.js +3 -3
  23. package/dist/dialog.d.ts +4 -1
  24. package/dist/dialog.js +7 -2
  25. package/dist/draggable-tabs.js +9 -8
  26. package/dist/dropdown-button.js +1 -0
  27. package/dist/editable-table.js +2 -2
  28. package/dist/field-counter.d.ts +7 -0
  29. package/dist/field-counter.js +5 -0
  30. package/dist/index.d.ts +22 -4
  31. package/dist/index.js +12 -2
  32. package/dist/input.d.ts +2 -0
  33. package/dist/input.js +11 -3
  34. package/dist/label-value-grid.js +6 -4
  35. package/dist/layout.d.ts +39 -6
  36. package/dist/layout.js +118 -42
  37. package/dist/lib/utils.js +1 -0
  38. package/dist/message-action-panel.d.ts +12 -0
  39. package/dist/message-action-panel.js +11 -0
  40. package/dist/message-item.d.ts +23 -3
  41. package/dist/message-item.js +115 -16
  42. package/dist/notification.d.ts +14 -3
  43. package/dist/notification.js +36 -7
  44. package/dist/page-section.d.ts +2 -1
  45. package/dist/page-section.js +2 -2
  46. package/dist/popover-menu.d.ts +4 -2
  47. package/dist/popover-menu.js +65 -5
  48. package/dist/popover.d.ts +10 -2
  49. package/dist/popover.js +7 -3
  50. package/dist/rich-text-editor.d.ts +2 -1
  51. package/dist/rich-text-editor.js +29 -6
  52. package/dist/routing-action-panel.d.ts +38 -0
  53. package/dist/routing-action-panel.js +19 -0
  54. package/dist/routing-timeline.d.ts +7 -2
  55. package/dist/routing-timeline.js +67 -29
  56. package/dist/search-input.d.ts +6 -0
  57. package/dist/search-input.js +6 -4
  58. package/dist/select-primitive.js +1 -1
  59. package/dist/select.d.ts +3 -1
  60. package/dist/select.js +9 -4
  61. package/dist/status-dot.d.ts +13 -0
  62. package/dist/status-dot.js +19 -0
  63. package/dist/styles.css +0 -19
  64. package/dist/table-add-row.d.ts +8 -0
  65. package/dist/table-add-row.js +7 -0
  66. package/dist/table-column-filter.js +7 -5
  67. package/dist/table.d.ts +9 -1
  68. package/dist/table.js +25 -11
  69. package/dist/tabs.d.ts +1 -1
  70. package/dist/tabs.js +6 -4
  71. package/dist/tailwind-preset.js +10 -0
  72. package/dist/textarea.d.ts +5 -0
  73. package/dist/textarea.js +18 -3
  74. package/dist/theme-switcher.d.ts +3 -1
  75. package/dist/theme-switcher.js +8 -2
  76. package/package.json +1 -1
@@ -2,6 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { FilterIcon } from "./animated-icons";
5
+ import { buttonVariants } from "./button";
5
6
  import { DateInput } from "./date-input";
6
7
  import { FilterPopover } from "./filter-popover";
7
8
  import { FilterToggle, filterToggleVariants, } from "./filter-toggle";
@@ -29,7 +30,7 @@ function getOptionsFilterSummary(filter) {
29
30
  }
30
31
  return ((_c = (_b = filter.options.find((option) => option.value === filter.values[0])) === null || _b === void 0 ? void 0 : _b.label) !== null && _c !== void 0 ? _c : filter.values[0]);
31
32
  }
32
- const TableFilterTrigger = React.forwardRef(({ active, disabled, label, children, ...props }, ref) => (_jsx(FilterToggle, { ref: ref, type: "button", disabled: disabled, selected: active, "aria-label": `Filter by ${label}${active ? ", active" : ""}`, className: cn("h-9 w-full min-w-0 justify-start overflow-hidden px-2 text-sm font-normal", !active &&
33
+ const TableFilterTrigger = React.forwardRef(({ active, disabled, label, children, ...props }, ref) => (_jsx(FilterToggle, { ref: ref, type: "button", disabled: disabled, selected: active, "aria-label": `Filter by ${label}${active ? ", active" : ""}`, className: cn("h-8 w-full min-w-0 justify-start overflow-hidden px-2 text-sm font-normal", !active &&
33
34
  "bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground"), icon: _jsx(FilterIcon, { "aria-hidden": "true", className: "shrink-0", size: 16 }), ...props, children: _jsx("span", { className: "min-w-0 flex-1 truncate text-left", children: children }) })));
34
35
  TableFilterTrigger.displayName = "TableFilterTrigger";
35
36
  function TextColumnFilter({ filter, label, }) {
@@ -71,15 +72,16 @@ function DateColumnFilter({ filter, label, }) {
71
72
  var _a, _b;
72
73
  const active = isTableColumnFilterActive(filter);
73
74
  const accessibleLabel = (_a = filter.label) !== null && _a !== void 0 ? _a : label;
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 &&
75
- "border-0 bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground") }));
75
+ 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", popoverTitle: `Filter by ${accessibleLabel}`, disabled: filter.disabled, minDate: filter.minDate, maxDate: filter.maxDate, clearable: true, clearLabel: `Clear ${accessibleLabel} filter`, className: "gap-0", triggerClassName: cn(buttonVariants({ variant: "outline", size: "sm" }), "h-8 w-full min-w-0 justify-start font-normal shadow-none", active ? "pl-2 pr-16" : "pl-2 pr-9", active
76
+ ? "border-transparent bg-highlight text-highlight-foreground hover:bg-highlight/80"
77
+ : "bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground") }));
76
78
  }
77
79
  function DateRangeColumnFilter({ filter, label, }) {
78
80
  var _a, _b;
79
81
  const active = isTableColumnFilterActive(filter);
80
82
  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
+ 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", popoverTitle: `Filter by ${accessibleLabel}`, disabled: filter.disabled, minDate: filter.minDate, maxDate: filter.maxDate, clearable: true, clearLabel: `Clear ${accessibleLabel} filter`, className: "gap-0", triggerClassName: cn(filterToggleVariants({ selected: active }), "h-8 w-full min-w-0 justify-start border-0 text-sm font-normal shadow-none", active ? "pl-2 pr-16" : "pl-2 pr-9", !active &&
84
+ "bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground") }));
83
85
  }
84
86
  function TableColumnFilterControl({ filter, label, }) {
85
87
  if (filter.type === "text") {
package/dist/table.d.ts CHANGED
@@ -123,6 +123,12 @@ export interface TableRowExpansionProps<Row> {
123
123
  /** Accessible label for a row's expansion toggle. */
124
124
  getToggleLabel?: (row: Row, rowIndex: number, expanded: boolean) => string;
125
125
  }
126
+ export interface TableRowIndicatorProps<Row> {
127
+ /** Renders a compact status marker in a fixed leading column for each row. */
128
+ render: (row: Row, rowIndex: number) => React.ReactNode;
129
+ /** Accessible name for the otherwise visually empty indicator column header. */
130
+ headerLabel?: string;
131
+ }
126
132
  interface SortableTableHeadProps extends Omit<React.ThHTMLAttributes<HTMLTableCellElement>, "children" | "style"> {
127
133
  field: string;
128
134
  children: React.ReactNode;
@@ -194,6 +200,8 @@ export interface TableProps<Row> extends Omit<React.HTMLAttributes<HTMLDivElemen
194
200
  emptyStateMinHeight?: React.CSSProperties["minHeight"];
195
201
  /** When true, applies alternating row backgrounds for easier scanning. */
196
202
  striped?: boolean;
203
+ /** Adds a compact, non-reorderable status column at the leading edge of every row. */
204
+ rowIndicator?: TableRowIndicatorProps<Row>;
197
205
  /** Adds a disclosure column and full-width expandable content for nested tables or row details. */
198
206
  rowExpansion?: TableRowExpansionProps<Row>;
199
207
  /** When true, non-sticky header cells become draggable for column reordering. */
@@ -201,5 +209,5 @@ export interface TableProps<Row> extends Omit<React.HTMLAttributes<HTMLDivElemen
201
209
  /** Called with the new ordered column key array after a drag-and-drop reorder. */
202
210
  onColumnOrderChange?: (columnKeys: string[]) => void;
203
211
  }
204
- 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;
212
+ 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, rowIndicator, rowExpansion, reorderableColumns, onColumnOrderChange, onScroll, style, ...props }: TableProps<Row>): import("react/jsx-runtime").JSX.Element;
205
213
  export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableRowHeader, TableCaption, SortableTableHead, TableRowSkeleton, };
package/dist/table.js CHANGED
@@ -19,6 +19,7 @@ const DEFAULT_COLUMN_RESIZE_MIN_WIDTH = 64;
19
19
  const DEFAULT_COLUMN_RESIZE_START_WIDTH = 160;
20
20
  const DEFAULT_COLUMN_RESIZE_MAX_WIDTH = 1200;
21
21
  const COLUMN_RESIZE_KEYBOARD_STEP = 16;
22
+ const TABLE_ROW_INDICATOR_COLUMN_WIDTH = 32;
22
23
  const TABLE_EXPANSION_COLUMN_WIDTH = 48;
23
24
  const TABLE_MIN_WIDTH_CSS_VAR = "--table-min-width";
24
25
  const DEFAULT_TABLE_MIN_HEIGHT = "var(--table-min-height, var(--app-table-min-height, 24rem))";
@@ -159,7 +160,7 @@ const TableFooter = React.forwardRef(({ className, ...props }, ref) => (_jsx("tf
159
160
  TableFooter.displayName = "TableFooter";
160
161
  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 })));
161
162
  TableRow.displayName = "TableRow";
162
- const TableHead = React.forwardRef(({ className, style, width, minWidth, maxWidth, wrap = false, treatWidthAsMinWidth, containOverflow, ...props }, ref) => (_jsx("th", { ref: ref, className: cn("h-11 px-4 py-2 text-left align-middle font-medium text-instructional [&:has([role=checkbox])]:pr-0", wrap ? "whitespace-normal" : "whitespace-nowrap", overflowContainmentClassName({ containOverflow, wrap }), className), style: getTableColumnSizingStyle({
163
+ const TableHead = React.forwardRef(({ className, style, width, minWidth, maxWidth, wrap = false, treatWidthAsMinWidth, containOverflow, ...props }, ref) => (_jsx("th", { ref: ref, className: cn("h-11 px-4 py-[4px] text-left align-middle font-medium text-instructional [&:has([role=checkbox])]:pr-0", wrap ? "whitespace-normal" : "whitespace-nowrap", overflowContainmentClassName({ containOverflow, wrap }), className), style: getTableColumnSizingStyle({
163
164
  style: {
164
165
  ...style,
165
166
  fontWeight: 500,
@@ -170,7 +171,7 @@ const TableHead = React.forwardRef(({ className, style, width, minWidth, maxWidt
170
171
  treatWidthAsMinWidth,
171
172
  }), ...props })));
172
173
  TableHead.displayName = "TableHead";
173
- const TableCell = React.forwardRef(({ className, style, width, minWidth, maxWidth, wrap = false, treatWidthAsMinWidth, containOverflow, ...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", overflowContainmentClassName({ containOverflow, wrap }), className), style: getTableColumnSizingStyle({
174
+ const TableCell = React.forwardRef(({ className, style, width, minWidth, maxWidth, wrap = false, treatWidthAsMinWidth, containOverflow, ...props }, ref) => (_jsx("td", { ref: ref, className: cn("h-12 px-4 py-[4px] align-middle text-card-foreground [&:has([role=checkbox])]:pr-0 [&_[data-slot=button]]:px-0", wrap ? "whitespace-normal" : "whitespace-nowrap", overflowContainmentClassName({ containOverflow, wrap }), className), style: getTableColumnSizingStyle({
174
175
  style,
175
176
  width,
176
177
  minWidth,
@@ -178,7 +179,7 @@ const TableCell = React.forwardRef(({ className, style, width, minWidth, maxWidt
178
179
  treatWidthAsMinWidth,
179
180
  }), ...props })));
180
181
  TableCell.displayName = "TableCell";
181
- const TableRowHeader = React.forwardRef(({ className, style, width, minWidth, maxWidth, wrap = false, treatWidthAsMinWidth, containOverflow, scope = "row", ...props }, ref) => (_jsx("th", { ref: ref, scope: scope, className: cn("h-12 px-4 py-2 text-left align-middle font-medium text-card-foreground [&:has([role=checkbox])]:pr-0", wrap ? "whitespace-normal" : "whitespace-nowrap", overflowContainmentClassName({ containOverflow, wrap }), className), style: getTableColumnSizingStyle({
182
+ const TableRowHeader = React.forwardRef(({ className, style, width, minWidth, maxWidth, wrap = false, treatWidthAsMinWidth, containOverflow, scope = "row", ...props }, ref) => (_jsx("th", { ref: ref, scope: scope, className: cn("h-12 px-4 py-[4px] text-left align-middle font-medium text-card-foreground [&:has([role=checkbox])]:pr-0", wrap ? "whitespace-normal" : "whitespace-nowrap", overflowContainmentClassName({ containOverflow, wrap }), className), style: getTableColumnSizingStyle({
182
183
  style,
183
184
  width,
184
185
  minWidth,
@@ -240,7 +241,7 @@ function SkeletonCell({ config }) {
240
241
  return (_jsx(TableCell, { children: _jsx(SkeletonBlock, { width: config.width, height: config.height, badge: config.badge }) }));
241
242
  }
242
243
  function TableRowSkeleton({ columns, rows = 1 }) {
243
- return (_jsx(_Fragment, { children: Array.from({ length: rows }).map((_, rowIndex) => (_jsx(TableRow, { children: columns.map((config, columnIndex) => (_jsx(SkeletonCell, { config: config }, columnIndex))) }, rowIndex))) }));
244
+ return (_jsx(_Fragment, { children: Array.from({ length: rows }).map((_, rowIndex) => (_jsx(TableRow, { "data-slot": "table-skeleton-row", className: "h-12", children: columns.map((config, columnIndex) => (_jsx(SkeletonCell, { config: config }, columnIndex))) }, rowIndex))) }));
244
245
  }
245
246
  export const TABLE_SKELETON_PRESETS = {
246
247
  text: (width = "8rem", height = "1rem") => ({
@@ -278,7 +279,7 @@ const SortableTableHead = React.forwardRef(function SortableTableHead({ field, c
278
279
  ? currentSortDirection === "asc"
279
280
  ? "ascending"
280
281
  : "descending"
281
- : 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
282
+ : 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-auto min-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
282
283
  ? "max-w-full whitespace-normal text-left"
283
284
  : "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 &&
284
285
  allowWrap &&
@@ -398,8 +399,10 @@ function getColumnPairResizeRange(column, nextColumn, columnWidths) {
398
399
  maxWidth: Math.round(currentWidth + maxDelta),
399
400
  };
400
401
  }
401
- 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 }) {
402
+ 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, rowIndicator, rowExpansion, reorderableColumns = false, onColumnOrderChange, onScroll, style, ...props }) {
403
+ var _a;
402
404
  const visibleColumns = getVisibleColumns(columns, visibleColumnKeys);
405
+ const hasRowIndicatorColumn = Boolean(rowIndicator);
403
406
  const hasExpansionColumn = Boolean(rowExpansion);
404
407
  const hasColumnFilters = visibleColumns.some((column) => column.filter);
405
408
  const isGrowResizeMode = columnResizeMode === "grow";
@@ -413,6 +416,7 @@ function Table({ accessibleLabel, columns, rows, getRowKey, emptyState = (_jsx(E
413
416
  const hasResolvedColumnWidths = hasColumnWidthOverrides(resolvedColumnWidths);
414
417
  const growTableMinWidth = isGrowResizeMode
415
418
  ? getTableMinWidth(visibleColumns, resolvedColumnWidths) +
419
+ (hasRowIndicatorColumn ? TABLE_ROW_INDICATOR_COLUMN_WIDTH : 0) +
416
420
  (hasExpansionColumn ? TABLE_EXPANSION_COLUMN_WIDTH : 0)
417
421
  : undefined;
418
422
  const resolvedMinHeight = minHeight !== null && minHeight !== void 0 ? minHeight : (centerEmptyState ? DEFAULT_TABLE_MIN_HEIGHT : undefined);
@@ -673,7 +677,11 @@ function Table({ accessibleLabel, columns, rows, getRowKey, emptyState = (_jsx(E
673
677
  }, ...props, children: [_jsxs(TableRoot, { "aria-busy": loading, className: cn(shouldUseFixedLayout && "table-fixed", isGrowResizeMode &&
674
678
  (hasResolvedColumnWidths
675
679
  ? "w-[var(--table-min-width)]"
676
- : "min-w-[var(--table-min-width)]"), tableClassName), children: [_jsx(TableCaption, { className: caption ? undefined : "sr-only !m-0", children: caption !== null && caption !== void 0 ? caption : accessibleLabel }), _jsxs("colgroup", { children: [hasExpansionColumn ? (_jsx("col", { style: {
680
+ : "min-w-[var(--table-min-width)]"), tableClassName), children: [_jsx(TableCaption, { className: caption ? undefined : "sr-only !m-0", children: caption !== null && caption !== void 0 ? caption : accessibleLabel }), _jsxs("colgroup", { children: [hasRowIndicatorColumn ? (_jsx("col", { style: {
681
+ width: `${TABLE_ROW_INDICATOR_COLUMN_WIDTH}px`,
682
+ minWidth: `${TABLE_ROW_INDICATOR_COLUMN_WIDTH}px`,
683
+ maxWidth: `${TABLE_ROW_INDICATOR_COLUMN_WIDTH}px`,
684
+ } })) : null, hasExpansionColumn ? (_jsx("col", { style: {
677
685
  width: `${TABLE_EXPANSION_COLUMN_WIDTH}px`,
678
686
  minWidth: `${TABLE_EXPANSION_COLUMN_WIDTH}px`,
679
687
  maxWidth: `${TABLE_EXPANSION_COLUMN_WIDTH}px`,
@@ -686,14 +694,17 @@ function Table({ accessibleLabel, columns, rows, getRowKey, emptyState = (_jsx(E
686
694
  minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled),
687
695
  maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled),
688
696
  treatWidthAsMinWidth: !isColumnResizingEnabled,
689
- }) }, 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) => {
697
+ }) }, column.key)))] }), _jsxs(TableHeader, { children: [_jsxs(TableRow, { children: [hasRowIndicatorColumn ? (_jsx(TableHead, { "data-slot": "table-row-indicator-head", "aria-label": (_a = rowIndicator === null || rowIndicator === void 0 ? void 0 : rowIndicator.headerLabel) !== null && _a !== void 0 ? _a : "Row status", scope: "col", className: "w-8 px-2" })) : null, 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: [hasRowIndicatorColumn ? (_jsx(TableCell, { "data-slot": "table-row-indicator-filter-cell", "aria-hidden": "true", className: "h-11 w-8 px-2 py-[4px]" })) : null, hasExpansionColumn ? (_jsx(TableCell, { "data-slot": "table-expansion-filter-cell", "aria-hidden": "true", className: "h-11 w-12 px-2 py-[4px]" })) : null, visibleColumns.map((column) => {
690
698
  const columnWidth = getRenderedColumnWidth({
691
699
  column,
692
700
  columnWidths: resolvedColumnWidths,
693
701
  resizing: isColumnResizingEnabled,
694
702
  });
695
- return (_jsx(TableCell, { "data-slot": "table-filter-cell", width: columnWidth, minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled), maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled), treatWidthAsMinWidth: !isColumnResizingEnabled, containOverflow: isColumnResizingEnabled, className: cn("relative h-11 px-2 py-1 font-normal", stickyClassName(column.sticky, "filter", getStickyShadowVisibility(column.sticky, stickyScrollState)), column.filterClassName), children: column.filter ? (_jsx("div", { "data-slot": "table-filter-control", className: "absolute inset-x-2 inset-y-1 min-w-0", children: _jsx(TableColumnFilterControl, { filter: column.filter, label: getColumnResizeLabel(column) }) })) : null }, column.key));
703
+ return (_jsx(TableCell, { "data-slot": "table-filter-cell", width: columnWidth, minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled), maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled), treatWidthAsMinWidth: !isColumnResizingEnabled, containOverflow: isColumnResizingEnabled, className: cn("relative h-11 px-2 py-[4px] font-normal", stickyClassName(column.sticky, "filter", getStickyShadowVisibility(column.sticky, stickyScrollState)), column.filterClassName), children: column.filter ? (_jsx("div", { "data-slot": "table-filter-control", className: "absolute inset-x-2 inset-y-[4px] min-w-0", children: _jsx(TableColumnFilterControl, { filter: column.filter, label: getColumnResizeLabel(column) }) })) : null }, column.key));
696
704
  })] })) : null] }), _jsx(TableBody, { "aria-hidden": loading ? "true" : undefined, children: loading ? (_jsx(TableRowSkeleton, { rows: loadingRows, columns: [
705
+ ...(hasRowIndicatorColumn
706
+ ? [{ width: "0.5rem", height: "0.5rem", badge: true }]
707
+ : []),
697
708
  ...(hasExpansionColumn
698
709
  ? [{ width: "1rem", height: "1rem" }]
699
710
  : []),
@@ -715,7 +726,7 @@ function Table({ accessibleLabel, columns, rows, getRowKey, emptyState = (_jsx(E
715
726
  !resolvedTone &&
716
727
  "bg-[hsl(var(--table-row-striped-background))]", tableRowToneClassName(resolvedTone), typeof rowClassName === "function"
717
728
  ? rowClassName(row, rowIndex)
718
- : rowClassName), children: [hasExpansionColumn ? (_jsx(TableCell, { className: "w-12 px-2 text-center", children: isRowExpandable ? (_jsx("button", { type: "button", "aria-expanded": isExpanded, "aria-controls": isExpanded ? expandedContentId : undefined, "aria-label": (_d = (_c = rowExpansion === null || rowExpansion === void 0 ? void 0 : rowExpansion.getToggleLabel) === null || _c === void 0 ? void 0 : _c.call(rowExpansion, row, rowIndex, isExpanded)) !== null && _d !== void 0 ? _d : `${isExpanded ? "Collapse" : "Expand"} row ${rowIndex + 1}`, onClick: (event) => {
729
+ : rowClassName), children: [hasRowIndicatorColumn ? (_jsx(TableCell, { "data-slot": "table-row-indicator-cell", className: "w-8 px-2 text-center", children: rowIndicator === null || rowIndicator === void 0 ? void 0 : rowIndicator.render(row, rowIndex) })) : null, hasExpansionColumn ? (_jsx(TableCell, { className: "w-12 px-2 text-center", children: isRowExpandable ? (_jsx("button", { type: "button", "aria-expanded": isExpanded, "aria-controls": isExpanded ? expandedContentId : undefined, "aria-label": (_d = (_c = rowExpansion === null || rowExpansion === void 0 ? void 0 : rowExpansion.getToggleLabel) === null || _c === void 0 ? void 0 : _c.call(rowExpansion, row, rowIndex, isExpanded)) !== null && _d !== void 0 ? _d : `${isExpanded ? "Collapse" : "Expand"} row ${rowIndex + 1}`, onClick: (event) => {
719
730
  event.stopPropagation();
720
731
  toggleExpandedRow(rowKey);
721
732
  }, className: "inline-flex h-8 w-8 cursor-pointer items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", children: _jsx(ChevronRight, { "aria-hidden": "true", className: cn("h-4 w-4 transition-transform duration-150", isExpanded && "rotate-90") }) })) : null })) : null, visibleColumns.map((column) => {
@@ -733,8 +744,11 @@ function Table({ accessibleLabel, columns, rows, getRowKey, emptyState = (_jsx(E
733
744
  : TableCell;
734
745
  return (_jsx(Cell, { width: columnWidth, minWidth: getRenderedColumnMinWidth(column, isColumnResizingEnabled), maxWidth: getRenderedColumnMaxWidth(column, isColumnResizingEnabled), wrap: column.wrap, treatWidthAsMinWidth: !isColumnResizingEnabled, containOverflow: isColumnResizingEnabled, className: cn(alignClassName(column.align), stickyClassName(column.sticky, "cell", getStickyShadowVisibility(column.sticky, stickyScrollState)), column.cellClassName), children: value }, column.key));
735
746
  })] }), isExpanded ? (_jsx(TableRow, { id: expandedContentId, "data-slot": "table-expanded-row", className: "bg-[hsl(var(--table-row-striped-background))] hover:bg-[hsl(var(--table-row-striped-background))]", children: _jsx(TableCell, { colSpan: Math.max(visibleColumns.length +
747
+ (hasRowIndicatorColumn ? 1 : 0) +
736
748
  (hasExpansionColumn ? 1 : 0), 1), className: "h-auto whitespace-normal p-4", children: rowExpansion === null || rowExpansion === void 0 ? void 0 : rowExpansion.renderExpandedContent(row, rowIndex) }) })) : null] }, rowKey));
737
- })) : (_jsx(TableRow, { className: "cursor-default hover:bg-card", children: _jsx(TableCell, { colSpan: Math.max(visibleColumns.length + (hasExpansionColumn ? 1 : 0), 1), className: "p-6", children: resolvedEmptyState }) })) })] }), _jsx("div", { role: "status", className: "sr-only", children: loading ? loadingLabel : null })] }));
749
+ })) : (_jsx(TableRow, { className: "cursor-default hover:bg-card", children: _jsx(TableCell, { colSpan: Math.max(visibleColumns.length +
750
+ (hasRowIndicatorColumn ? 1 : 0) +
751
+ (hasExpansionColumn ? 1 : 0), 1), className: "p-6", children: resolvedEmptyState }) })) })] }), _jsx("div", { role: "status", className: "sr-only", children: loading ? loadingLabel : null })] }));
738
752
  const reorderableContainer = reorderableColumns ? (_jsx(DndContext, { sensors: columnReorderSensors, collisionDetection: closestCenter, onDragEnd: handleColumnDragEnd, children: _jsx(SortableContext, { items: reorderableColumnIds, strategy: horizontalListSortingStrategy, children: container }) })) : (container);
739
753
  if (!pagination) {
740
754
  return (_jsxs("div", { className: "space-y-4", children: [toolbar, reorderableContainer] }));
package/dist/tabs.d.ts CHANGED
@@ -65,7 +65,7 @@ export interface TabsProps extends Omit<React.ComponentPropsWithoutRef<typeof Ta
65
65
  defaultValue?: string;
66
66
  value?: string;
67
67
  onValueChange?: (value: string) => void;
68
- size?: "default" | "compact";
68
+ size?: "default" | "compact" | "comfortable";
69
69
  renderPanels?: boolean;
70
70
  listWrapperClassName?: string;
71
71
  listClassName?: string;
package/dist/tabs.js CHANGED
@@ -144,13 +144,15 @@ function Tabs({ items, defaultValue, value, onValueChange, size = "default", ren
144
144
  if (!items.length) {
145
145
  return null;
146
146
  }
147
- 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, suppressAriaControls: !renderPanels, triggerClassName: cn(size === "compact" &&
148
- "min-h-7 px-2.5 py-0.5 data-[state=active]:shadow-sm", triggerClassName) }, item.value)) : (_jsx(TabTriggerItem, { item: item, suppressAriaControls: !renderPanels, triggerClassName: cn(size === "compact" &&
149
- "min-h-7 px-2.5 py-0.5 data-[state=active]:shadow-sm", triggerClassName) }, item.value))) }) }), renderPanels
147
+ 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", size === "comfortable" && "rounded-[10px]", listClassName), children: items.map((item) => item.sections && item.sections.length > 0 ? (_jsx(TabTriggerWithDropdown, { item: item, suppressAriaControls: !renderPanels, triggerClassName: cn(size === "compact" &&
148
+ "min-h-7 px-2.5 py-0.5 data-[state=active]:shadow-sm", size === "comfortable" &&
149
+ "min-h-[40px] rounded-md px-[12px] py-[8px] text-[16px]", triggerClassName) }, item.value)) : (_jsx(TabTriggerItem, { item: item, suppressAriaControls: !renderPanels, triggerClassName: cn(size === "compact" &&
150
+ "min-h-7 px-2.5 py-0.5 data-[state=active]:shadow-sm", size === "comfortable" &&
151
+ "min-h-[40px] rounded-md px-[12px] py-[8px] text-[16px]", triggerClassName) }, item.value))) }) }), renderPanels
150
152
  ? items.map((item) => (_jsx(TabsContent, { value: item.value, className: cn("pt-4", panelClassName), children: item.content }, item.value)))
151
153
  : null] }));
152
154
  }
153
155
  function SubTabs({ className, listWrapperClassName, listClassName, triggerClassName, panelClassName, ...props }) {
154
- return (_jsx(Tabs, { className: cn("space-y-0", className), listWrapperClassName: cn("min-w-0 overflow-x-auto", listWrapperClassName), listClassName: cn("w-full min-w-max justify-start overflow-visible rounded-none border-0 border-b border-[hsl(var(--tab-list-border))] bg-transparent p-0", listClassName), triggerClassName: cn("min-h-9 rounded-none border-b-2 border-transparent bg-transparent px-3 py-2 text-[hsl(var(--tab-trigger-foreground))] shadow-none focus-visible:ring-inset focus-visible:ring-offset-0 data-[state=inactive]:hover:bg-[hsl(var(--tab-trigger-hover-background))] data-[state=active]:border-[hsl(var(--tab-indicator))] data-[state=active]:bg-transparent data-[state=active]:text-[hsl(var(--tab-trigger-active-foreground))] data-[state=active]:shadow-none", triggerClassName), panelClassName: cn("pt-4", panelClassName), ...props }));
156
+ return (_jsx(Tabs, { className: cn("space-y-0", className), listWrapperClassName: cn("min-w-0 overflow-x-auto", listWrapperClassName), listClassName: cn("w-full min-w-max justify-start overflow-visible rounded-none border-0 border-b border-[hsl(var(--tab-list-border))] bg-transparent p-0", listClassName), triggerClassName: cn("min-h-9 rounded-none border-b-[3px] border-transparent bg-transparent px-3 py-2 text-[hsl(var(--tab-trigger-foreground))] shadow-none focus-visible:ring-inset focus-visible:ring-offset-0 data-[state=inactive]:hover:bg-[hsl(var(--tab-trigger-hover-background))] data-[state=active]:border-[hsl(var(--tab-indicator))] data-[state=active]:bg-transparent data-[state=active]:text-[hsl(var(--tab-trigger-active-foreground))] data-[state=active]:shadow-none", triggerClassName), panelClassName: cn("pt-4", panelClassName), ...props }));
155
157
  }
156
158
  export { Tabs, SubTabs, TabsRoot, TabsList, TabsTrigger, TabsContent, TabWithDropdown, };
@@ -147,6 +147,14 @@ export const neaTailwindPreset = {
147
147
  12: "var(--space-12)",
148
148
  },
149
149
  keyframes: {
150
+ "collapsible-down": {
151
+ from: { height: "0" },
152
+ to: { height: "var(--radix-collapsible-content-height)" },
153
+ },
154
+ "collapsible-up": {
155
+ from: { height: "var(--radix-collapsible-content-height)" },
156
+ to: { height: "0" },
157
+ },
150
158
  "accordion-down": {
151
159
  from: { height: "0" },
152
160
  to: { height: "var(--radix-accordion-content-height)" },
@@ -157,6 +165,8 @@ export const neaTailwindPreset = {
157
165
  },
158
166
  },
159
167
  animation: {
168
+ "collapsible-down": "collapsible-down 0.2s ease-out",
169
+ "collapsible-up": "collapsible-up 0.2s ease-out",
160
170
  "accordion-down": "accordion-down 0.2s ease-out",
161
171
  "accordion-up": "accordion-up 0.2s ease-out",
162
172
  },
@@ -2,7 +2,12 @@ import * as React from "react";
2
2
  declare const TextareaControl: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.TextareaHTMLAttributes<HTMLTextAreaElement>, HTMLTextAreaElement>, "ref"> & React.RefAttributes<HTMLTextAreaElement>>;
3
3
  export interface TextareaProps extends Omit<React.ComponentProps<typeof TextareaControl>, "className"> {
4
4
  label: React.ReactNode;
5
+ size?: "default" | "comfortable";
5
6
  description?: React.ReactNode;
7
+ /** Show a word counter and validate this limit without truncating the entered text. */
8
+ maxWords?: number;
9
+ /** Display a character counter for maxLength; maxWords takes precedence. */
10
+ showCount?: boolean;
6
11
  error?: React.ReactNode;
7
12
  invalid?: boolean;
8
13
  hideLabel?: boolean;
package/dist/textarea.js CHANGED
@@ -2,12 +2,22 @@ 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
4
  import { inputControlPaddingClassName } from "./input-control";
5
+ import { FieldCounter } from "./field-counter";
5
6
  import { cn } from "./lib/utils";
6
7
  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 })));
7
8
  TextareaControl.displayName = "TextareaControl";
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) => {
9
+ const Textarea = React.forwardRef(({ label, size = "default", description, maxWords, showCount = false, value, defaultValue, onChange, error: suppliedError, invalid = false, hideLabel = false, className, textareaClassName, labelClassName, descriptionClassName, errorClassName, id, disabled, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, ...props }, ref) => {
10
+ const [uncontrolledValue, setUncontrolledValue] = React.useState(defaultValue !== null && defaultValue !== void 0 ? defaultValue : "");
11
+ const text = String(value !== null && value !== void 0 ? value : uncontrolledValue);
12
+ const words = text.trim() ? text.trim().split(/\s+/).length : 0;
13
+ const hasWordLimit = maxWords !== undefined;
14
+ const error = suppliedError || (hasWordLimit && words > maxWords
15
+ ? `Use ${maxWords} words or fewer.`
16
+ : undefined);
9
17
  const generatedId = React.useId();
10
18
  const textareaId = id !== null && id !== void 0 ? id : `textarea-${generatedId}`;
19
+ const limit = maxWords !== null && maxWords !== void 0 ? maxWords : (showCount ? props.maxLength : undefined);
20
+ const wordLimitId = limit !== undefined ? `${textareaId}-word-limit` : undefined;
11
21
  const errorId = error ? `${textareaId}-error` : undefined;
12
22
  const showDescription = Boolean(description) && !error;
13
23
  const descriptionId = showDescription ? `${textareaId}-description` : undefined;
@@ -15,10 +25,15 @@ const Textarea = React.forwardRef(({ label, description, error, invalid = false,
15
25
  ariaInvalid === true ||
16
26
  ariaInvalid === "true" ||
17
27
  Boolean(error);
18
- const describedBy = [ariaDescribedBy, descriptionId, errorId]
28
+ const describedBy = [ariaDescribedBy, descriptionId, errorId, wordLimitId]
19
29
  .filter(Boolean)
20
30
  .join(" ") || undefined;
21
- return (_jsxs(Field, { className: className, "data-disabled": disabled ? "true" : undefined, "data-invalid": isInvalid ? "true" : undefined, children: [_jsx(FieldLabel, { htmlFor: textareaId, className: cn(hideLabel && "sr-only", labelClassName), children: label }), _jsx(TextareaControl, { ref: ref, id: textareaId, disabled: disabled, "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": describedBy, className: textareaClassName, ...props }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: descriptionClassName, children: description })) : null, _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
31
+ return (_jsxs(Field, { className: cn(size === "comfortable" && "gap-[8px]", className), "data-disabled": disabled ? "true" : undefined, "data-invalid": isInvalid ? "true" : undefined, children: [_jsx(FieldLabel, { htmlFor: textareaId, className: cn(size === "comfortable" && "text-[16px] leading-[24px]", hideLabel && "sr-only", labelClassName), children: label }), _jsx(TextareaControl, { ref: ref, id: textareaId, disabled: disabled, "aria-invalid": isInvalid ? "true" : undefined, "aria-describedby": describedBy, className: cn(size === "comfortable" &&
32
+ "min-h-[76px] px-[16px] py-[12px] text-[16px] leading-[24px]", textareaClassName), ...props, value: value, defaultValue: defaultValue, onChange: (event) => {
33
+ if (value === undefined)
34
+ setUncontrolledValue(event.target.value);
35
+ onChange === null || onChange === void 0 ? void 0 : onChange(event);
36
+ } }), showDescription ? (_jsx(FieldDescription, { id: descriptionId, className: cn(size === "comfortable" && "text-[14px] leading-[20px]", descriptionClassName), children: description })) : null, limit !== undefined && _jsx(FieldCounter, { id: wordLimitId, count: hasWordLimit ? words : text.length, limit: limit, unit: hasWordLimit ? "words" : "characters" }), _jsx(FieldError, { id: errorId, className: errorClassName, children: error })] }));
22
37
  });
23
38
  Textarea.displayName = "Textarea";
24
39
  export { Textarea };
@@ -9,6 +9,8 @@ export interface ThemeSwitcherProps extends Omit<SwitchProps, "checked" | "defau
9
9
  onCheckedChange?: (checked: boolean) => void;
10
10
  /** Icon-only mode for collapsed sidebars. */
11
11
  isCollapsed?: boolean;
12
+ /** Animates between expanded and icon-only presentations. */
13
+ animateModeChange?: boolean;
12
14
  /** Text shown before the switch. Defaults to the active theme label. */
13
15
  label?: React.ReactNode;
14
16
  /** Label shown when light theme is active and no `label` is provided. */
@@ -21,7 +23,7 @@ export interface ThemeSwitcherProps extends Omit<SwitchProps, "checked" | "defau
21
23
  useLightLabel?: string;
22
24
  className?: string;
23
25
  }
24
- declare function ThemeSwitcher({ checked, defaultChecked, onCheckedChange, isCollapsed, label, lightLabel, darkLabel, useDarkLabel, useLightLabel, className, disabled, tabIndex, ...props }: ThemeSwitcherProps): import("react/jsx-runtime").JSX.Element;
26
+ declare function ThemeSwitcher({ checked, defaultChecked, onCheckedChange, isCollapsed, animateModeChange, label, lightLabel, darkLabel, useDarkLabel, useLightLabel, className, disabled, tabIndex, ...props }: ThemeSwitcherProps): import("react/jsx-runtime").JSX.Element;
25
27
  declare namespace ThemeSwitcher {
26
28
  var displayName: string;
27
29
  }
@@ -5,7 +5,7 @@ import { Moon, Sun } from "lucide-react";
5
5
  import { Button } from "./button";
6
6
  import { cn } from "./lib/utils";
7
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, tabIndex, ...props }) {
8
+ function ThemeSwitcher({ checked, defaultChecked = false, onCheckedChange, isCollapsed = false, animateModeChange = true, label, lightLabel = "Light theme", darkLabel = "Dark theme", useDarkLabel = "Use dark theme", useLightLabel = "Use light theme", className, disabled, tabIndex, ...props }) {
9
9
  const isControlled = checked !== undefined;
10
10
  const [uncontrolledChecked, setUncontrolledChecked] = React.useState(defaultChecked);
11
11
  const isDark = isControlled ? checked : uncontrolledChecked;
@@ -17,7 +17,13 @@ function ThemeSwitcher({ checked, defaultChecked = false, onCheckedChange, isCol
17
17
  }
18
18
  onCheckedChange === null || onCheckedChange === void 0 ? void 0 : onCheckedChange(nextChecked);
19
19
  }, [isControlled, onCheckedChange]);
20
- return (_jsxs("div", { "data-slot": "theme-switcher", className: cn("relative w-full overflow-hidden py-1 transition-[padding] duration-300 ease-in-out", isCollapsed ? "px-0" : "px-1.5", className), children: [_jsx(Button, { type: "button", variant: "ghost", size: "icon", disabled: disabled, "aria-hidden": isCollapsed ? undefined : true, "aria-label": isDark ? useLightLabel : useDarkLabel, "aria-pressed": isDark, tabIndex: isCollapsed ? tabIndex : -1, className: cn("absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 transition-opacity duration-150", isCollapsed ? "opacity-100" : "pointer-events-none opacity-0"), onClick: () => handleCheckedChange(!isDark), children: _jsx(ActiveIcon, { "aria-hidden": "true", className: "h-4 w-4" }) }), _jsx("div", { "aria-hidden": isCollapsed ? true : undefined, className: cn("transition-[opacity,transform] duration-150", isCollapsed
20
+ return (_jsxs("div", { "data-slot": "theme-switcher", className: cn("relative w-full overflow-hidden py-1", animateModeChange
21
+ ? "transition-[padding] duration-300 ease-in-out"
22
+ : "transition-none", isCollapsed ? "px-0" : "px-1.5", className), children: [_jsx(Button, { type: "button", variant: "ghost", size: "icon", disabled: disabled, "aria-hidden": isCollapsed ? undefined : true, "aria-label": isDark ? useLightLabel : useDarkLabel, "aria-pressed": isDark, tabIndex: isCollapsed ? tabIndex : -1, className: cn("absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2", animateModeChange
23
+ ? "transition-opacity duration-150"
24
+ : "transition-none", isCollapsed ? "opacity-100" : "pointer-events-none opacity-0"), onClick: () => handleCheckedChange(!isDark), children: _jsx(ActiveIcon, { "aria-hidden": "true", className: "h-4 w-4" }) }), _jsx("div", { "aria-hidden": isCollapsed ? true : undefined, className: cn(animateModeChange
25
+ ? "transition-[opacity,transform] duration-150"
26
+ : "transition-none", isCollapsed
21
27
  ? "pointer-events-none translate-x-1 opacity-0"
22
28
  : "translate-x-0 opacity-100"), children: _jsx(Switch, { checked: isDark, onCheckedChange: handleCheckedChange, disabled: disabled, tabIndex: isCollapsed ? -1 : tabIndex, 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: "w-full flex-row-reverse justify-between rounded-control px-3 py-2 text-muted-foreground transition-colors hover:bg-card hover:text-foreground", ...props }) })] }));
23
29
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neasg/design-system",
3
- "version": "0.4.35",
3
+ "version": "0.4.113",
4
4
  "description": "NEA shared design system primitives, theme tokens, and Storybook docs.",
5
5
  "repository": {
6
6
  "type": "git",