basalt-ui 2.6.1 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -51,6 +51,19 @@ export type DataTableColumnPinning<T = unknown> = {
51
51
  start?: DataTableColumnId<T>[];
52
52
  end?: DataTableColumnId<T>[];
53
53
  };
54
+ /** One per-row action: `BarActionItem`'s vocabulary with the row handed to the handler. */
55
+ export type DataTableRowAction<T = unknown> = {
56
+ key: string;
57
+ label: string;
58
+ /** A lone action with an icon renders as that icon button; anything else folds into the menu. */
59
+ icon?: ReactNode;
60
+ onClick: (row: T) => void;
61
+ danger?: boolean;
62
+ /** A disabled action renders inert and never fires. */
63
+ disabled?: (row: T) => boolean;
64
+ /** A loading action shows the busy state (icon button) or sits inert (menu) and never fires. */
65
+ loading?: (row: T) => boolean;
66
+ };
54
67
  /**
55
68
  * The four boxes `BasaltDataTable` paints: `root` is the outer fragment's replacement wrapper,
56
69
  * `toolbar` is the search/facets/actions row, `table` is whichever body renders — the Mantine
@@ -316,6 +329,26 @@ export type BasaltDataTableProps<T extends RowData> = BasaltProps & SlotStylesPr
316
329
  * ]}
317
330
  */
318
331
  bulkActions?: (rows: T[]) => BarAction[];
332
+ /**
333
+ * Per-row actions as typed data. The table renders them itself in a trailing, pinned,
334
+ * header-less column: an icon button for one action that carries an `icon`, an overflow menu
335
+ * for anything else. Clicking either never fires `onRowActivate`. Never put a `Menu` or
336
+ * `ActionIcon` in a `display` / `cell` renderer (`basalt/control-outside-home`).
337
+ *
338
+ * Pass a STABLE reference — a module constant or a `useMemo` — because the column is rebuilt
339
+ * whenever the array identity changes. A `renderCard` projection owns its own card, so the
340
+ * column renders in the table body only.
341
+ *
342
+ * @example
343
+ * const ROW_ACTIONS: DataTableRowAction<Row>[] = [
344
+ * { key: 'edit', label: 'Edit', icon: <IconEdit />, onClick: (row) => edit(row) },
345
+ * { key: 'delete', label: 'Delete', danger: true, onClick: (row) => remove(row) },
346
+ * ]
347
+ * <BasaltDataTable rowActions={ROW_ACTIONS} rowLabel={(row) => row.name} … />
348
+ */
349
+ rowActions?: DataTableRowAction<T>[];
350
+ /** Names a row for assistive tech in its action controls; default `Row N actions`. */
351
+ rowLabel?: (row: T) => string;
319
352
  /**
320
353
  * Enables the bottom pagination bar ("Showing X–Y of N", rows-per-page `Select`, Mantine
321
354
  * `Pagination`) wired to TanStack's `pagination` state. Client-side by default — see
@@ -1 +1 @@
1
- {"version":3,"file":"data-table.d.ts","sourceRoot":"","sources":["../../src/table/data-table.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"data-table.d.ts","sourceRoot":"","sources":["../../src/table/data-table.tsx"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import {
3
+ ActionIcon,
3
4
  Box,
4
5
  Checkbox,
5
6
  Group,
@@ -21,10 +22,11 @@ import { HIT_COARSE } from "../common/hit-floor.js";
21
22
  import { useMeasuredWidths } from "../common/use-measured-widths.js";
22
23
  import { assertRequiredProps, useValidateProps } from "../common/validate.js";
23
24
  import { FilterSet } from "../controls/index.js";
24
- import { ActionGroup, BarActionSlot } from "../controls/actions.js";
25
+ import { ActionGroup, BarActionSlot, OverflowMenu } from "../controls/actions.js";
25
26
  import { EnumFilter } from "../controls/enum-filter.js";
26
27
  import { MultiSelectFilter } from "../controls/multi-select-filter.js";
27
28
  import { CtlSlot } from "../theme/index.js";
29
+ import { IconSlot } from "../theme/icon-slot.js";
28
30
  import { alpha, VX } from "../tokens/index.js";
29
31
  import { WidgetHeader } from "../dashboard/index.js";
30
32
  import { ErrorState } from "../dashboard/query-state.js";
@@ -148,6 +150,74 @@ function selectionColumn() {
148
150
  )
149
151
  };
150
152
  }
153
+ const ACTIONS_COLUMN_ID = "__basalt_actions__";
154
+ const ACTIONS_COLUMN_SIZE = 56;
155
+ function stopsRowActivation(cell) {
156
+ if (cell.column.id === ACTIONS_COLUMN_ID) return true;
157
+ return cell.column.id === SELECT_COLUMN_ID && cell.row.getCanSelect();
158
+ }
159
+ function RowActionCell({
160
+ actions,
161
+ row,
162
+ rowIndex,
163
+ rowLabel
164
+ }) {
165
+ const named = rowLabel?.(row);
166
+ const [only] = actions;
167
+ if (actions.length === 1 && only?.icon !== void 0) {
168
+ const inert = only.disabled?.(row) === true || only.loading?.(row) === true;
169
+ return /* @__PURE__ */ jsx(
170
+ ActionIcon,
171
+ {
172
+ variant: "subtle",
173
+ size: "ctl",
174
+ "aria-label": named === void 0 ? only.label : `${only.label}: ${named}`,
175
+ disabled: only.disabled?.(row) === true,
176
+ loading: only.loading?.(row) === true,
177
+ ...only.danger === true && { color: "red" },
178
+ onClick: () => {
179
+ if (!inert) only.onClick(row);
180
+ },
181
+ children: /* @__PURE__ */ jsx(IconSlot, { children: only.icon })
182
+ }
183
+ );
184
+ }
185
+ const items = actions.map((action) => ({
186
+ key: action.key,
187
+ label: action.label,
188
+ ...action.icon !== void 0 && { icon: action.icon },
189
+ ...action.danger === true && { danger: true },
190
+ // The menu has no busy state of its own, so loading reads as inert there.
191
+ disabled: action.disabled?.(row) === true || action.loading?.(row) === true,
192
+ onClick: () => action.onClick(row)
193
+ }));
194
+ return /* @__PURE__ */ jsx(
195
+ OverflowMenu,
196
+ {
197
+ actions: items,
198
+ trigger: "kebab",
199
+ label: named === void 0 ? `Row ${rowIndex + 1} actions` : `Actions for ${named}`
200
+ }
201
+ );
202
+ }
203
+ function rowActionsColumn(actions, rowLabel) {
204
+ return {
205
+ id: ACTIONS_COLUMN_ID,
206
+ size: ACTIONS_COLUMN_SIZE,
207
+ enableSorting: false,
208
+ enableHiding: false,
209
+ header: () => /* @__PURE__ */ jsx("span", { className: classes.srOnly, children: "Actions" }),
210
+ cell: ({ row }) => /* @__PURE__ */ jsx(
211
+ RowActionCell,
212
+ {
213
+ actions,
214
+ row: row.original,
215
+ rowIndex: row.index,
216
+ rowLabel
217
+ }
218
+ )
219
+ };
220
+ }
151
221
  function BulkActionBar({ count, actions }) {
152
222
  return /* @__PURE__ */ jsxs(Group, { className: classes.bulkBar, gap: "xs", align: "center", wrap: "wrap", mb: "xs", children: [
153
223
  /* @__PURE__ */ jsxs(Text, { className: classes.bulkCount, children: [
@@ -409,6 +479,8 @@ function BasaltDataTable(props) {
409
479
  enableRowSelection = false,
410
480
  getRowId,
411
481
  bulkActions,
482
+ rowActions,
483
+ rowLabel,
412
484
  enablePagination = false,
413
485
  pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS,
414
486
  manualPagination = false,
@@ -461,9 +533,14 @@ function BasaltDataTable(props) {
461
533
  return { ...column, filterFn };
462
534
  });
463
535
  }, [columns, facets]);
536
+ const hasRowActions = rowActions !== void 0 && rowActions.length > 0;
464
537
  const tableColumns = useMemo(
465
- () => enableRowSelection ? [selectionColumn(), ...facetColumns] : facetColumns,
466
- [facetColumns, enableRowSelection]
538
+ () => [
539
+ ...enableRowSelection ? [selectionColumn()] : [],
540
+ ...facetColumns,
541
+ ...hasRowActions ? [rowActionsColumn(rowActions, rowLabel)] : []
542
+ ],
543
+ [facetColumns, enableRowSelection, hasRowActions, rowActions, rowLabel]
467
544
  );
468
545
  const hasFacets = Boolean(facets && facets.length > 0);
469
546
  const breaches = manualPaginationBreaches({
@@ -512,6 +589,11 @@ function BasaltDataTable(props) {
512
589
  );
513
590
  const hasFolded = foldedColumns.size > 0;
514
591
  const foldRows = useRowDisclosure({ data, getRowId });
592
+ const pinningActive = enablePinning || hasRowActions;
593
+ const tablePinning = useMemo(
594
+ () => hasRowActions ? { ...columnPinning, end: [...columnPinning.end, ACTIONS_COLUMN_ID] } : columnPinning,
595
+ [columnPinning, hasRowActions]
596
+ );
515
597
  const table = useTable({
516
598
  features: basaltTableFeatures,
517
599
  data,
@@ -520,7 +602,7 @@ function BasaltDataTable(props) {
520
602
  sorting: tableState.sorting,
521
603
  globalFilter,
522
604
  columnFilters: tableState.columnFilters,
523
- columnPinning,
605
+ columnPinning: tablePinning,
524
606
  columnVisibility,
525
607
  ...enablePagination && { pagination: tableState.pagination },
526
608
  ...enableRowSelection && { rowSelection: tableState.rowSelection }
@@ -549,7 +631,7 @@ function BasaltDataTable(props) {
549
631
  ...manualPagination && pageCount !== void 0 && { pageCount }
550
632
  },
551
633
  enableSorting: sortingEnabled,
552
- enableColumnPinning: enablePinning
634
+ enableColumnPinning: pinningActive
553
635
  });
554
636
  const showSearch = enableGlobalFilter && filteringEnabled;
555
637
  const showFacets = hasFacets && filteringEnabled;
@@ -557,7 +639,7 @@ function BasaltDataTable(props) {
557
639
  const branch = query === void 0 ? void 0 : dataQueryBranch("BasaltDataTable", query);
558
640
  const showSkeleton = branch === void 0 ? isLoading : branch === "pending";
559
641
  const queryError = branch === "error" && query !== void 0 ? query : void 0;
560
- const columnCount = columns.length + (enableRowSelection ? 1 : 0) + (hasFolded ? 1 : 0) - foldedColumns.size;
642
+ const columnCount = columns.length + (enableRowSelection ? 1 : 0) + (hasRowActions ? 1 : 0) + (hasFolded ? 1 : 0) - foldedColumns.size;
561
643
  const selectedModelRows = enableRowSelection ? table.getSelectedRowModel().rows : void 0;
562
644
  const selectedRows = useMemo(
563
645
  () => selectedModelRows?.map((row) => row.original) ?? [],
@@ -567,7 +649,7 @@ function BasaltDataTable(props) {
567
649
  () => bulkActions === void 0 || selectedRows.length === 0 ? void 0 : bulkActions(selectedRows),
568
650
  [bulkActions, selectedRows]
569
651
  );
570
- const headerGroups = enablePinning ? getOrderedHeaderGroups(table) : table.getHeaderGroups();
652
+ const headerGroups = pinningActive ? getOrderedHeaderGroups(table) : table.getHeaderGroups();
571
653
  const rows = table.getRowModel().rows;
572
654
  const pageStickyHeader = !scrolls && stickyHeader === true;
573
655
  const { wrapperRef: containmentRef, contained } = useMeasuredContainment(
@@ -610,14 +692,15 @@ function BasaltDataTable(props) {
610
692
  const canSort = sortingEnabled && header.column.getCanSort();
611
693
  const sorted = header.column.getIsSorted();
612
694
  const toggleSorting = header.column.getToggleSortingHandler();
613
- const pinnedStyle = enablePinning ? getPinnedCellStyle(table, header.column) : void 0;
695
+ const pinnedStyle = pinningActive ? getPinnedCellStyle(table, header.column) : void 0;
614
696
  const align = resolveAlign(header.column);
615
697
  const isSelectColumn = header.column.id === SELECT_COLUMN_ID;
698
+ const isFoldCandidate = !isSelectColumn && header.column.id !== ACTIONS_COLUMN_ID;
616
699
  return /* @__PURE__ */ jsxs(
617
700
  Table.Th,
618
701
  {
619
702
  ...isSelectColumn && { className: classes.selectCell },
620
- ...!isSelectColumn && { [FOLD_ID_ATTR]: header.column.id },
703
+ ...isFoldCandidate && { [FOLD_ID_ATTR]: header.column.id },
621
704
  onClick: canSort ? toggleSorting : void 0,
622
705
  onKeyDown: canSort ? (event) => {
623
706
  if (event.key !== "Enter" && event.key !== " ") return;
@@ -641,7 +724,7 @@ function BasaltDataTable(props) {
641
724
  })
642
725
  ] }, headerGroup.id)) }),
643
726
  /* @__PURE__ */ jsx(Table.Tbody, { children: queryError !== void 0 ? /* @__PURE__ */ jsx(Table.Tr, { children: /* @__PURE__ */ jsx(Table.Td, { colSpan: columnCount, children: errorNode }) }) : showSkeleton ? Array.from({ length: skeletonRows }, (_, rowIndex) => /* @__PURE__ */ jsx(Table.Tr, { children: Array.from({ length: columnCount }, (__, colIndex) => /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Skeleton, { height: 16, radius: "sm" }) }, `skeleton-${rowIndex}-${colIndex}`)) }, `skeleton-${rowIndex}`)) : rows.length === 0 ? /* @__PURE__ */ jsx(Table.Tr, { children: /* @__PURE__ */ jsx(Table.Td, { colSpan: columnCount, children: emptyNode }) }) : rows.map((row) => {
644
- const cells = enablePinning ? [
727
+ const cells = pinningActive ? [
645
728
  ...row.getStartVisibleCells(),
646
729
  ...row.getCenterVisibleCells(),
647
730
  ...row.getEndVisibleCells()
@@ -663,18 +746,18 @@ function BasaltDataTable(props) {
663
746
  }
664
747
  ) }),
665
748
  cells.map((cell) => {
666
- const pinnedStyle = enablePinning ? getPinnedCellStyle(table, cell.column) : void 0;
749
+ const pinnedStyle = pinningActive ? getPinnedCellStyle(table, cell.column) : void 0;
667
750
  const align = resolveAlign(cell.column);
668
751
  return /* @__PURE__ */ jsx(
669
752
  Table.Td,
670
753
  {
671
754
  ...cell.column.id === SELECT_COLUMN_ID && {
672
- className: classes.selectCell,
673
- // The label's ::after fills the cell, so a tap on it bubbles to the row —
674
- // stop it only where a tick is possible; a disabled row's tap is a row tap.
675
- ...cell.row.getCanSelect() && {
676
- onClick: (event) => event.stopPropagation()
677
- }
755
+ className: classes.selectCell
756
+ },
757
+ ...stopsRowActivation(cell) && {
758
+ // The label's ::after fills the select cell, so a tap on it bubbles to the
759
+ // row; the actions menu portals out but React bubbles through the portal.
760
+ onClick: (event) => event.stopPropagation()
678
761
  },
679
762
  style: {
680
763
  ...typeof cell.getValue() === "number" && cell.column.columnDef.meta?.numeral !== false ? NUMERIC_CELL_STYLE : void 0,
package/llms.txt CHANGED
@@ -1,4 +1,4 @@
1
- # basalt-ui v2.6.1
1
+ # basalt-ui v2.7.0
2
2
  # Opinionated framework for Mantine-based React apps — theme, app shell, TanStack Charts, toolchain presets
3
3
  #
4
4
  # Entry points: use the exact import specifier below; do not guess.
@@ -13,7 +13,7 @@
13
13
  # A subpath's `Install:` line is its required peers; `OptionalPeers:` are import()-only, install on use.
14
14
 
15
15
  Package: basalt-ui
16
- Version: 2.6.1
16
+ Version: 2.7.0
17
17
 
18
18
  ## basalt-ui
19
19
  Import: import { ... } from 'basalt-ui'
@@ -54,16 +54,206 @@ Description: CartesianChart (the TanStack Charts rung), the Lines and Bars kinds
54
54
  Layer: headless
55
55
  Rule: basalt-charts
56
56
  Signature: BandStrip(props: BandStripProps<T, X>): ReactElement<unknown, string | JSXElementConstructor<any>>
57
+ className?: string
58
+ style?: CSSProperties
59
+ ariaLabel: string
60
+ data: readonly T[]
61
+ x: (d: T) => X
62
+ xTicks?: number | readonly X[]
63
+ formatX?: ((x: X) => string)
64
+ legend?: false | ChartLegendConfig
65
+ height?: number | ResponsiveChartHeight
66
+ state?: ChartState
67
+ cursorGroup?: string
68
+ cursorResolution?: CursorResolution
69
+ tooltip?: false | ChartTooltipConfig<T, X>
70
+ states: readonly BandStripSeries<T>[]
71
+ getBand: (d: T) => BandSpan
72
+ fold?: BandFold<T>
57
73
  Signature: Bars(props: BarsProps<T, X>): ReactElement<unknown, string | JSXElementConstructor<any>>
74
+ className?: string
75
+ style?: CSSProperties
76
+ ariaLabel: string
77
+ data: readonly T[]
78
+ x: (d: T) => X
79
+ y?: AxisConfig<T>
80
+ y2?: AxisConfig<T>
81
+ xTicks?: number | readonly X[]
82
+ formatX?: ((x: X) => string)
83
+ zones?: readonly ZoneSpec[]
84
+ xZones?: readonly XZoneSpec<X>[]
85
+ refLines?: readonly RefLine[]
86
+ legend?: false | ChartLegendConfig
87
+ height?: number | ResponsiveChartHeight
88
+ state?: ChartState
89
+ cursorGroup?: string
90
+ cursorResolution?: CursorResolution
91
+ tooltip?: false | ChartTooltipConfig<T, X>
92
+ bars: readonly BarsBar<T>[]
93
+ lines?: readonly BarsLine<T>[]
94
+ layout?: "stacked" | "grouped" | "normalized"
95
+ barOpacity?: ((d: T, key: string) => number)
58
96
  Signature: CartesianChart(props: CartesianChartProps<T, X>): ReactElement<unknown, string | JSXElementConstructor<any>>
97
+ className?: string
98
+ style?: CSSProperties
99
+ ariaLabel: string
100
+ data: readonly T[]
101
+ x: (d: T) => X
102
+ series: readonly ChartSeries<T>[]
103
+ marks: (plot: PlotContext<T, X>) => ChartMarks
104
+ y?: AxisConfig<T>
105
+ y2?: AxisConfig<T>
106
+ xTicks?: number | readonly X[]
107
+ formatX?: ((x: X) => string)
108
+ zones?: readonly ZoneSpec[]
109
+ xZones?: readonly XZoneSpec<X>[]
110
+ refLines?: readonly RefLine[]
111
+ legend?: false | ChartLegendConfig
112
+ height?: number | ResponsiveChartHeight
113
+ state?: ChartState
114
+ cursorGroup?: string
115
+ cursorResolution?: CursorResolution
116
+ tooltip?: false | ChartTooltipConfig<T, X>
59
117
  Signature: ChartCard(props: ChartCardProps): ReactElement<unknown, string | JSXElementConstructor<any>>
118
+ className?: string
119
+ style?: CSSProperties
120
+ classNames?: Partial<Record<ChartCardSlot, string>>
121
+ title?: string
122
+ icon?: ReactNode
123
+ subtitle?: string
124
+ info?: string
125
+ value?: string
126
+ delta?: number
127
+ deltaPeriod?: string
128
+ deltaPolarity?: DeltaPolarity
129
+ count?: number
130
+ actions?: ReactNode
131
+ state?: ChartState
132
+ placeholderHeight?: number | ResponsiveChartHeight
133
+ stateAction?: ReactNode
134
+ children?: ReactNode
60
135
  Signature: Donut(props: DonutProps<K>): ReactElement<unknown, string | JSXElementConstructor<any>>
136
+ className?: string
137
+ style?: CSSProperties
138
+ ariaLabel: string
139
+ data: readonly DonutDatum<K>[]
140
+ colorForKey: (key: K) => string
141
+ formatValue: (v: number) => string
142
+ seriesLabel?: ((key: K) => string)
143
+ centerLabel?: string
144
+ centerSubLabel?: string
145
+ height?: number | ResponsiveChartHeight
146
+ state?: ChartState
61
147
  Signature: DualPanel(props: DualPanelProps<T, X>): ReactElement<unknown, string | JSXElementConstructor<any>>
148
+ className?: string
149
+ style?: CSSProperties
150
+ ariaLabel: string
151
+ data: readonly T[]
152
+ x: (d: T) => X
153
+ formatX?: ((x: X) => string)
154
+ legend?: false | ChartLegendConfig
155
+ height?: number | ResponsiveChartHeight
156
+ state?: ChartState
157
+ cursorGroup?: string
158
+ cursorResolution?: CursorResolution
159
+ tooltip?: false | ChartTooltipConfig<T, X>
160
+ series: readonly DualPanelLine<T>[]
161
+ top?: { domain?: readonly [number, number] | "auto" | undefined; …
162
+ fillBetween?: { from: string; to: string; fill: string; aboveFill?: strin…
163
+ bar: DualPanelBar<T>
164
+ topFraction?: number
62
165
  Signature: Heatmap(props: HeatmapProps<T>): ReactElement<unknown, string | JSXElementConstructor<any>>
166
+ className?: string
167
+ style?: CSSProperties
168
+ ariaLabel: string
169
+ data: readonly T[]
170
+ getRow: (d: T) => string
171
+ getCol: (d: T) => string
172
+ getValue: (d: T) => number | null
173
+ rows?: readonly string[]
174
+ cols?: readonly string[]
175
+ color?: string
176
+ domain?: readonly [number, number]
177
+ formatValue?: ((v: number) => string)
178
+ formatRow?: ((row: string) => string)
179
+ formatCol?: ((col: string) => string)
180
+ legend?: false | { label?: string | undefined; }
181
+ tooltip?: false | { rows?: ((d: T) => readonly ChartTooltipRow[]) | u…
182
+ height?: number | ResponsiveChartHeight
183
+ matchHeight?: boolean
184
+ state?: ChartState
63
185
  Signature: Lines(props: LinesProps<T, X>): ReactElement<unknown, string | JSXElementConstructor<any>>
186
+ className?: string
187
+ style?: CSSProperties
188
+ ariaLabel: string
189
+ data: readonly T[]
190
+ x: (d: T) => X
191
+ series: readonly ChartSeries<T>[]
192
+ y?: AxisConfig<T>
193
+ y2?: AxisConfig<T>
194
+ xTicks?: number | readonly X[]
195
+ formatX?: ((x: X) => string)
196
+ zones?: readonly ZoneSpec[]
197
+ xZones?: readonly XZoneSpec<X>[]
198
+ refLines?: readonly RefLine[]
199
+ legend?: false | ChartLegendConfig
200
+ height?: number | ResponsiveChartHeight
201
+ state?: ChartState
202
+ cursorGroup?: string
203
+ cursorResolution?: CursorResolution
204
+ tooltip?: false | ChartTooltipConfig<T, X>
205
+ thresholds?: [X] extends [string] ? never : readonly Threshold[]
64
206
  Signature: MirroredBars(props: MirroredBarsProps<T, X>): ReactElement<unknown, string | JSXElementConstructor<any>>
207
+ className?: string
208
+ style?: CSSProperties
209
+ ariaLabel: string
210
+ data: readonly T[]
211
+ x: (d: T) => X
212
+ formatX?: ((x: X) => string)
213
+ legend?: false | ChartLegendConfig
214
+ height?: number | ResponsiveChartHeight
215
+ state?: ChartState
216
+ cursorGroup?: string
217
+ tooltip?: false | ChartTooltipConfig<T, X>
218
+ series: readonly MirroredBarsSeries<T>[]
219
+ up: MirroredBarPane
220
+ down: MirroredBarPane
221
+ upFraction?: number
222
+ getAbsentFraction?: ((d: T) => number)
223
+ getBarOpacity?: ((d: T) => number)
224
+ fold?: { minBandPx?: number | undefined; merge: (group: T[]) => T;…
225
+ cursorResolution?: CursorResolution
65
226
  Signature: Sparkline(props: SparklineProps): ReactElement<unknown, string | JSXElementConstructor<any>>
227
+ className?: string
228
+ style?: CSSProperties
229
+ ariaLabel: string
230
+ data: readonly number[]
231
+ height: number
232
+ width?: number
233
+ mark?: "line" | "bar"
234
+ color?: string
235
+ emphasizeLast?: boolean
236
+ emphasisColor?: string
237
+ state?: ChartState
66
238
  Signature: StackedArea(props: StackedAreaProps<T, X>): ReactElement<unknown, string | JSXElementConstructor<any>>
239
+ className?: string
240
+ style?: CSSProperties
241
+ ariaLabel: string
242
+ data: readonly T[]
243
+ x: (d: T) => X
244
+ xTicks?: number | readonly X[]
245
+ formatX?: ((x: X) => string)
246
+ zones?: readonly ZoneSpec[]
247
+ xZones?: readonly XZoneSpec<X>[]
248
+ refLines?: readonly RefLine[]
249
+ legend?: false | ChartLegendConfig
250
+ height?: number | ResponsiveChartHeight
251
+ state?: ChartState
252
+ cursorGroup?: string
253
+ cursorResolution?: CursorResolution
254
+ tooltip?: false | ChartTooltipConfig<T, X>
255
+ series: readonly StackedAreaBand<T>[]
256
+ y?: Omit<AxisConfig<T>, "scale">
67
257
  Signature: chartCursor(group: string): ChartCursorController<ChartValue, ChartValue>
68
258
  Signature: chartCurve(name: CurveName): ChartCurve
69
259
  Install: bun add @tanstack/charts@1.1.0 d3-scale@4.0.2
@@ -113,8 +303,10 @@ Description: Mantine form layer: useBasaltForm + inputProps/fieldKey (two calls
113
303
  Layer: mantine-coupled
114
304
  Rule: basalt-batteries
115
305
  Signature: fieldKey(form: UseFormReturnType<Values>, path: Path): string
116
- Signature: inputProps(form: UseFormReturnType<Values>, path: Path): GetInputPropsReturnType
306
+ Signature: inputProps(form: UseFormReturnType<Values>, path: Path): InputProps
117
307
  Signature: useBasaltForm(opts: UseBasaltFormOptions<Values>): UseFormReturnType<Values>
308
+ mode?: "controlled" | "uncontrolled"
309
+ schema?: StandardSchemaV1<unknown, Values>
118
310
  Install: bun add @mantine/form@^9.7.0
119
311
 
120
312
  ## basalt-ui/commands
@@ -139,6 +331,47 @@ Description: BasaltDataTable and createColumnHelper: a data table over TanStack
139
331
  Layer: mantine-coupled
140
332
  Rule: basalt-batteries
141
333
  Signature: BasaltDataTable(props: BasaltDataTableProps<T>): JSX.Element
334
+ className?: string
335
+ style?: CSSProperties
336
+ classNames?: Partial<Record<BasaltDataTableSlot, string>>
337
+ title?: string
338
+ icon?: ReactNode
339
+ subtitle?: string
340
+ data: T[]
341
+ columns: BasaltColumnDef<T>[]
342
+ enableSorting?: boolean
343
+ manualSorting?: boolean
344
+ striped?: boolean | "odd" | "even"
345
+ highlightOnHover?: boolean
346
+ emptyState?: ReactNode
347
+ isLoading?: boolean
348
+ query?: QueryStateLike<unknown>
349
+ skeletonRows?: number
350
+ initialSorting?: SortingState
351
+ onSortingChange?: (sorting: SortingState) => void
352
+ enableGlobalFilter?: boolean
353
+ globalFilterPlaceholder?: string
354
+ searchIcon?: ReactNode
355
+ initialGlobalFilter?: string
356
+ onGlobalFilterChange?: (value: string) => void
357
+ facets?: DataTableFacet<T>[]
358
+ onColumnFiltersChange?: (filters: ColumnFiltersState) => void
359
+ manualFiltering?: boolean
360
+ actions?: SlotActions
361
+ onRowActivate?: (row: T) => void
362
+ renderCard?: (row: T) => ReactNode
363
+ enableRowSelection?: boolean
364
+ rowSelection?: RowSelectionState
365
+ onRowSelectionChange?: (selection: RowSelectionState) => void
366
+ getRowId?: (row: T, index: number) => string
367
+ bulkActions?: (rows: T[]) => BarAction[]
368
+ rowActions?: DataTableRowAction<T>[]
369
+ rowLabel?: (row: T) => string
370
+ enablePagination?: boolean
371
+ pageSizeOptions?: number[]
372
+ initialPagination?: PaginationState
373
+ onPaginationChange?: (pagination: PaginationState) => void
374
+ … +13 more
142
375
  Signature: createColumnHelper(): ColumnHelper<{ rowSortingFeature: TableFeature; columnFilteringFeatur…
143
376
  Install: bun add @tanstack/react-table@9.2.8
144
377
 
@@ -176,7 +409,7 @@ OptionalPeers: shiki@^4.5.0, @shikijs/langs@^4.5.0, @shikijs/themes@^4.5.0, beau
176
409
 
177
410
  ## basalt-ui/controls
178
411
  Import: import { ... } from 'basalt-ui/controls'
179
- Description: The control tier (docs/CONTROLS-SPEC.md §3): FilterSet (nowrap row + measured +N fold + the mobile Filters (n) sheet), RangeFilter/CompareFilter/SelectFilter/MultiSelectFilter/NumberFilter/SearchFilter/ToggleFilter (each bound to a FieldHandle — no value/onChange/size, law C2/C5; NumberFilter is the field.number lane, a radio list over `options` or a stepper without), ViewTabs, the action/sync family (ActionGroup, OverflowMenu, SyncButton, BarAction/GlobalAction), and the form layout FormSection/FormRow/FormGroup/FormActions (moved here from ./forms in 1.36.0 — FormRow is law C1’s form row and needs no @mantine/form). THREE surfaces, picked by the home and never by a prop (useFilterSurface): pill in a PageBar row, sheet in the mobile Filters (n) drawer, panel in a PageAside body — where every control renders an inspector/facet ROW instead of a chip (docs/ASIDE-SPEC.md §3). PanelRow is that row primitive (label above, hint, mono readout, an `end` control on the label line) and SliderControl is the one bound control with no pill form at all; the counts/max props on MultiSelectFilter render the Foundry facet list there. Every control owns its own desktop/mobile swap in CSS (C9) and renders size="ctl" internally. Resolves and renders with NO @mantine/dates installed — the custom date picker is injected through RangeFilter.customPicker from ./controls-dates.
412
+ Description: The control tier (docs/CONTROLS-SPEC.md §3). Exports: `FilterSet` (nowrap row + measured +N fold + the mobile Filters (n) sheet), `RangeFilter`/`SelectFilter`/`MultiSelectFilter`/`NumberFilter`/`SearchFilter`/`ToggleFilter` (each bound to a FieldHandle — no value/onChange/size, law C2/C5; `NumberFilter` is the field.number lane, a radio list over `options` or a stepper without), `ViewTabs`, `SyncButton` with the `BarAction`/`GlobalAction` types, and the form layout `FormRow`/`FormGroup` (moved here from ./forms in 1.36.0 — `FormRow` is law C1’s form row and needs no @mantine/form). THREE surfaces, picked by the home and never by a prop: pill in a PageBar row, sheet in the mobile Filters (n) drawer, panel in a PageAside body — where every control renders an inspector/facet ROW instead of a chip (docs/ASIDE-SPEC.md §3); the counts/max props on `MultiSelectFilter` render the Foundry facet list there. Every control owns its own desktop/mobile swap in CSS (C9) and renders size="ctl" internally. Resolves and renders with NO @mantine/dates installed — the custom date picker is injected through RangeFilter.customPicker from ./controls-dates.
180
413
  Layer: mantine-coupled
181
414
  Rule: basalt-controls
182
415
  Signature: FilterSet(props: FilterSetProps): ReactNode
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "basalt-ui",
3
- "version": "2.6.1",
3
+ "version": "2.7.0",
4
4
  "description": "Opinionated framework for Mantine-based React apps — theme, app shell, TanStack Charts, toolchain presets",
5
5
  "keywords": [
6
6
  "charts",
@@ -137,9 +137,10 @@ export type FormRowProps = BasaltProps &
137
137
  hint?: string
138
138
  /**
139
139
  * The field's error, rendered under the control. Falsy renders nothing. For a Mantine input
140
- * wired through `inputProps(form, path)`, Mantine's own `Input.Wrapper` already renders
141
- * `form.errors[path]` — passing it here too would render it twice. Reserve this prop for
142
- * children that render no error of their own: a raw input, a custom control.
140
+ * wired through `inputProps(form, path)`, Mantine's own `Input.Wrapper` already renders the
141
+ * field error — passing it here too would render it twice. Reserve this prop for children that
142
+ * render no error of their own (a raw input, a custom control) and read it typed:
143
+ * `error={inputProps(form, path).error}`.
143
144
  */
144
145
  error?: ReactNode
145
146
  /** Marks the field required: an accent `*` for sighted readers, `(required)` for assistive tech. */
@@ -172,7 +173,7 @@ export type FormRowProps = BasaltProps &
172
173
  *
173
174
  * @example
174
175
  * // A raw control renders no error of its own — pass it explicitly.
175
- * <FormRow label="Email" required error={form.errors['email']}>
176
+ * <FormRow label="Email" required error={inputProps(form, 'email').error}>
176
177
  * <input value={form.values.email} onChange={(e) => form.setFieldValue('email', e.target.value)} />
177
178
  * </FormRow>
178
179
  */
@@ -239,8 +240,16 @@ export type FormGroupProps = BasaltProps & {
239
240
  *
240
241
  * @example
241
242
  * <FormGroup label="Notify me about" direction="row">
242
- * <Checkbox key={fieldKey(form, 'onDeploy')} {...inputProps(form, 'onDeploy')} label="Deploys" />
243
- * <Checkbox key={fieldKey(form, 'onError')} {...inputProps(form, 'onError')} label="Errors" />
243
+ * <Checkbox
244
+ * key={fieldKey(form, 'onDeploy')}
245
+ * {...inputProps(form, 'onDeploy', { type: 'checkbox' })}
246
+ * label="Deploys"
247
+ * />
248
+ * <Switch
249
+ * key={fieldKey(form, 'onError')}
250
+ * {...inputProps(form, 'onError', { type: 'checkbox' })}
251
+ * label="Errors"
252
+ * />
244
253
  * </FormGroup>
245
254
  */
246
255
  export function FormGroup(props: FormGroupProps) {