basalt-ui 2.6.0 → 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.
- package/AGENTS.md +15 -15
- package/CHANGELOG.md +24 -0
- package/MIGRATING.md +50 -12
- package/agent/rules/basalt-batteries.md +2 -2
- package/agent/rules/basalt-controls.md +1 -1
- package/agent/skills/basalt-app/SKILL.md +2 -3
- package/agent/skills/basalt-charts/SKILL.md +1 -1
- package/agent/skills/basalt-design/SKILL.md +1 -1
- package/configs/oxlint-plugin.js +48 -0
- package/configs/oxlint-plugin.test.ts +82 -0
- package/dist/charts/Bars.d.ts +1 -1
- package/dist/charts/Bars.d.ts.map +1 -1
- package/dist/charts/Bars.js +9 -2
- package/dist/charts/legend.d.ts.map +1 -1
- package/dist/charts/legend.js +9 -1
- package/dist/controls/form-layout.d.ts +15 -6
- package/dist/controls/form-layout.d.ts.map +1 -1
- package/dist/forms/field.d.ts +42 -2
- package/dist/forms/field.d.ts.map +1 -1
- package/dist/forms/field.js +2 -2
- package/dist/forms/index.d.ts +4 -5
- package/dist/forms/index.d.ts.map +1 -1
- package/dist/surfaces.d.ts +1 -1
- package/dist/surfaces.js +1 -1
- package/dist/table/data-table.d.ts +33 -0
- package/dist/table/data-table.d.ts.map +1 -1
- package/dist/table/data-table.js +100 -17
- package/dist/telemetry.d.ts +5 -0
- package/dist/telemetry.d.ts.map +1 -1
- package/llms.txt +343 -11
- package/package.json +1 -1
- package/src/charts/Bars.tsx +13 -3
- package/src/charts/legend.tsx +9 -2
- package/src/controls/form-layout.tsx +15 -6
- package/src/forms/field.ts +78 -3
- package/src/forms/index.ts +4 -5
- package/src/surfaces.ts +1 -1
- package/src/table/data-table.tsx +171 -17
- package/src/telemetry.ts +5 -0
package/src/forms/index.ts
CHANGED
|
@@ -6,14 +6,13 @@
|
|
|
6
6
|
* `basalt-ui`. Same reason `./charts` and `./tokens` stay off the root barrel, one optional peer
|
|
7
7
|
* over. Import everything below from `basalt-ui/forms`.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* `basalt-ui/controls` (it needs no `@mantine/form`); `useForm`/`schemaResolver` come from
|
|
13
|
-
* `@mantine/form` directly. Both stopped being re-exported here in 1.39.0.
|
|
9
|
+
* One piece: the form (`useBasaltForm` + `inputProps`/`fieldKey`). The layout (FormRow /
|
|
10
|
+
* FormGroup) lives in `basalt-ui/controls` (it needs no `@mantine/form`); the form hook and
|
|
11
|
+
* schema resolvers come from `@mantine/form` directly.
|
|
14
12
|
*
|
|
15
13
|
* Optional peer: @mantine/form ^9.3.0 — install with: bun add @mantine/form
|
|
16
14
|
*/
|
|
17
15
|
|
|
18
16
|
export { useBasaltForm } from './create-form'
|
|
19
17
|
export { inputProps, fieldKey } from './field'
|
|
18
|
+
export type { InputProps } from './field'
|
package/src/surfaces.ts
CHANGED
|
@@ -502,7 +502,7 @@ export const SURFACES = {
|
|
|
502
502
|
'C12 — one shape for refresh/sync (SyncButton); only the alias table sees a renamed copy',
|
|
503
503
|
],
|
|
504
504
|
description:
|
|
505
|
-
'The control tier (docs/CONTROLS-SPEC.md §3): FilterSet (nowrap row + measured +N fold + the mobile Filters (n) sheet), RangeFilter
|
|
505
|
+
'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.',
|
|
506
506
|
optionalPeers: [],
|
|
507
507
|
// `@mantine/dates` is an optional peer basaltViteConfig pre-bundles the whole @mantine scope
|
|
508
508
|
// for — a consumer without the peer (linewatch) must never resolve it through this surface.
|
package/src/table/data-table.tsx
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
* />
|
|
33
33
|
*/
|
|
34
34
|
import {
|
|
35
|
+
ActionIcon,
|
|
35
36
|
Box,
|
|
36
37
|
Checkbox,
|
|
37
38
|
Group,
|
|
@@ -46,6 +47,7 @@ import {
|
|
|
46
47
|
} from '@mantine/core'
|
|
47
48
|
import type { MantineSpacing } from '@mantine/core'
|
|
48
49
|
import type {
|
|
50
|
+
Cell,
|
|
49
51
|
Column,
|
|
50
52
|
ColumnDef,
|
|
51
53
|
ColumnFiltersState,
|
|
@@ -69,8 +71,8 @@ import { HIT_COARSE } from '../common/hit-floor'
|
|
|
69
71
|
import { useMeasuredWidths } from '../common/use-measured-widths'
|
|
70
72
|
import { assertRequiredProps, useValidateProps } from '../common/validate'
|
|
71
73
|
import { FilterSet } from '../controls'
|
|
72
|
-
import { ActionGroup, BarActionSlot } from '../controls/actions'
|
|
73
|
-
import type { BarAction, SlotActions } from '../controls/actions'
|
|
74
|
+
import { ActionGroup, BarActionSlot, OverflowMenu } from '../controls/actions'
|
|
75
|
+
import type { BarAction, BarActionItem, SlotActions } from '../controls/actions'
|
|
74
76
|
// `EnumFilter` is deliberately NOT on the `./controls` barrel (`docs/CONTROLS-SPEC.md` §3 —
|
|
75
77
|
// reaching for it directly is hand-rolling a filter, `basalt/hand-rolled-filter`); a facet column
|
|
76
78
|
// is the one place inside the framework itself that legitimately builds a `FieldHandle` over
|
|
@@ -78,6 +80,7 @@ import type { BarAction, SlotActions } from '../controls/actions'
|
|
|
78
80
|
import { EnumFilter } from '../controls/enum-filter'
|
|
79
81
|
import { MultiSelectFilter } from '../controls/multi-select-filter'
|
|
80
82
|
import { CtlSlot } from '../theme'
|
|
83
|
+
import { IconSlot } from '../theme/icon-slot'
|
|
81
84
|
import { alpha, VX } from '../tokens'
|
|
82
85
|
import type { EnumField, FieldHandle, MultiField } from '../state/fields'
|
|
83
86
|
import { WidgetHeader } from '../dashboard'
|
|
@@ -358,6 +361,113 @@ function selectionColumn<T extends RowData>(): ColumnDef<BasaltTableFeatures, T,
|
|
|
358
361
|
}
|
|
359
362
|
}
|
|
360
363
|
|
|
364
|
+
// ── Row actions ───────────────────────────────────────────────────────────────
|
|
365
|
+
|
|
366
|
+
/** One per-row action: `BarActionItem`'s vocabulary with the row handed to the handler. */
|
|
367
|
+
export type DataTableRowAction<T = unknown> = {
|
|
368
|
+
key: string
|
|
369
|
+
label: string
|
|
370
|
+
/** A lone action with an icon renders as that icon button; anything else folds into the menu. */
|
|
371
|
+
icon?: ReactNode
|
|
372
|
+
onClick: (row: T) => void
|
|
373
|
+
danger?: boolean
|
|
374
|
+
/** A disabled action renders inert and never fires. */
|
|
375
|
+
disabled?: (row: T) => boolean
|
|
376
|
+
/** A loading action shows the busy state (icon button) or sits inert (menu) and never fires. */
|
|
377
|
+
loading?: (row: T) => boolean
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/** The synthetic trailing column `rowActions` appends — namespaced like {@link SELECT_COLUMN_ID}. */
|
|
381
|
+
const ACTIONS_COLUMN_ID = '__basalt_actions__'
|
|
382
|
+
|
|
383
|
+
/** Wide enough for one 30px control plus the cell's horizontal padding. */
|
|
384
|
+
const ACTIONS_COLUMN_SIZE = 56
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* The cells whose own controls must not read as "activate the row": a tickable selection cell (a
|
|
388
|
+
* disabled row's tap IS a row tap) and every actions cell.
|
|
389
|
+
*/
|
|
390
|
+
function stopsRowActivation<T extends RowData>(
|
|
391
|
+
cell: Cell<BasaltTableFeatures, T, unknown>,
|
|
392
|
+
): boolean {
|
|
393
|
+
if (cell.column.id === ACTIONS_COLUMN_ID) return true
|
|
394
|
+
return cell.column.id === SELECT_COLUMN_ID && cell.row.getCanSelect()
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function RowActionCell<T>({
|
|
398
|
+
actions,
|
|
399
|
+
row,
|
|
400
|
+
rowIndex,
|
|
401
|
+
rowLabel,
|
|
402
|
+
}: {
|
|
403
|
+
actions: readonly DataTableRowAction<T>[]
|
|
404
|
+
row: T
|
|
405
|
+
rowIndex: number
|
|
406
|
+
rowLabel: ((row: T) => string) | undefined
|
|
407
|
+
}): ReactNode {
|
|
408
|
+
const named = rowLabel?.(row)
|
|
409
|
+
const [only] = actions
|
|
410
|
+
if (actions.length === 1 && only?.icon !== undefined) {
|
|
411
|
+
const inert = only.disabled?.(row) === true || only.loading?.(row) === true
|
|
412
|
+
return (
|
|
413
|
+
<ActionIcon
|
|
414
|
+
variant="subtle"
|
|
415
|
+
size="ctl"
|
|
416
|
+
aria-label={named === undefined ? only.label : `${only.label}: ${named}`}
|
|
417
|
+
disabled={only.disabled?.(row) === true}
|
|
418
|
+
loading={only.loading?.(row) === true}
|
|
419
|
+
{...(only.danger === true && { color: 'red' })}
|
|
420
|
+
onClick={() => {
|
|
421
|
+
if (!inert) only.onClick(row)
|
|
422
|
+
}}
|
|
423
|
+
>
|
|
424
|
+
<IconSlot>{only.icon}</IconSlot>
|
|
425
|
+
</ActionIcon>
|
|
426
|
+
)
|
|
427
|
+
}
|
|
428
|
+
const items: BarActionItem[] = actions.map((action) => ({
|
|
429
|
+
key: action.key,
|
|
430
|
+
label: action.label,
|
|
431
|
+
...(action.icon !== undefined && { icon: action.icon }),
|
|
432
|
+
...(action.danger === true && { danger: true }),
|
|
433
|
+
// The menu has no busy state of its own, so loading reads as inert there.
|
|
434
|
+
disabled: action.disabled?.(row) === true || action.loading?.(row) === true,
|
|
435
|
+
onClick: () => action.onClick(row),
|
|
436
|
+
}))
|
|
437
|
+
return (
|
|
438
|
+
<OverflowMenu
|
|
439
|
+
actions={items}
|
|
440
|
+
trigger="kebab"
|
|
441
|
+
label={named === undefined ? `Row ${rowIndex + 1} actions` : `Actions for ${named}`}
|
|
442
|
+
/>
|
|
443
|
+
)
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Header-less: the `<th>` carries a screen-reader-only name and nothing sorts across it. Built per
|
|
448
|
+
* render for the same reason as {@link selectionColumn} (`ColumnDef` is generic in `T`).
|
|
449
|
+
*/
|
|
450
|
+
function rowActionsColumn<T extends RowData>(
|
|
451
|
+
actions: readonly DataTableRowAction<T>[],
|
|
452
|
+
rowLabel: ((row: T) => string) | undefined,
|
|
453
|
+
): ColumnDef<BasaltTableFeatures, T, unknown> {
|
|
454
|
+
return {
|
|
455
|
+
id: ACTIONS_COLUMN_ID,
|
|
456
|
+
size: ACTIONS_COLUMN_SIZE,
|
|
457
|
+
enableSorting: false,
|
|
458
|
+
enableHiding: false,
|
|
459
|
+
header: () => <span className={classes.srOnly}>Actions</span>,
|
|
460
|
+
cell: ({ row }) => (
|
|
461
|
+
<RowActionCell
|
|
462
|
+
actions={actions}
|
|
463
|
+
row={row.original}
|
|
464
|
+
rowIndex={row.index}
|
|
465
|
+
rowLabel={rowLabel}
|
|
466
|
+
/>
|
|
467
|
+
),
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
361
471
|
/**
|
|
362
472
|
* The bar a selection raises, above the table and below its header — the count, then the actions,
|
|
363
473
|
* through the SAME `BarAction[]` vocabulary `PageBar` uses, so the ≤3-inline fold and the mobile
|
|
@@ -661,6 +771,27 @@ export type BasaltDataTableProps<T extends RowData> = BasaltProps &
|
|
|
661
771
|
*/
|
|
662
772
|
bulkActions?: (rows: T[]) => BarAction[]
|
|
663
773
|
|
|
774
|
+
/**
|
|
775
|
+
* Per-row actions as typed data. The table renders them itself in a trailing, pinned,
|
|
776
|
+
* header-less column: an icon button for one action that carries an `icon`, an overflow menu
|
|
777
|
+
* for anything else. Clicking either never fires `onRowActivate`. Never put a `Menu` or
|
|
778
|
+
* `ActionIcon` in a `display` / `cell` renderer (`basalt/control-outside-home`).
|
|
779
|
+
*
|
|
780
|
+
* Pass a STABLE reference — a module constant or a `useMemo` — because the column is rebuilt
|
|
781
|
+
* whenever the array identity changes. A `renderCard` projection owns its own card, so the
|
|
782
|
+
* column renders in the table body only.
|
|
783
|
+
*
|
|
784
|
+
* @example
|
|
785
|
+
* const ROW_ACTIONS: DataTableRowAction<Row>[] = [
|
|
786
|
+
* { key: 'edit', label: 'Edit', icon: <IconEdit />, onClick: (row) => edit(row) },
|
|
787
|
+
* { key: 'delete', label: 'Delete', danger: true, onClick: (row) => remove(row) },
|
|
788
|
+
* ]
|
|
789
|
+
* <BasaltDataTable rowActions={ROW_ACTIONS} rowLabel={(row) => row.name} … />
|
|
790
|
+
*/
|
|
791
|
+
rowActions?: DataTableRowAction<T>[]
|
|
792
|
+
/** Names a row for assistive tech in its action controls; default `Row N actions`. */
|
|
793
|
+
rowLabel?: (row: T) => string
|
|
794
|
+
|
|
664
795
|
// ── Pagination ────────────────────────────────────────────────────────────────
|
|
665
796
|
|
|
666
797
|
/**
|
|
@@ -1354,6 +1485,8 @@ export function BasaltDataTable<T extends RowData>(props: BasaltDataTableProps<T
|
|
|
1354
1485
|
enableRowSelection = false,
|
|
1355
1486
|
getRowId,
|
|
1356
1487
|
bulkActions,
|
|
1488
|
+
rowActions,
|
|
1489
|
+
rowLabel,
|
|
1357
1490
|
enablePagination = false,
|
|
1358
1491
|
pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS,
|
|
1359
1492
|
manualPagination = false,
|
|
@@ -1443,9 +1576,14 @@ export function BasaltDataTable<T extends RowData>(props: BasaltDataTableProps<T
|
|
|
1443
1576
|
// The checkbox column is PREPENDED here rather than asked of the caller, so `columns` stays the
|
|
1444
1577
|
// caller's own data columns and `meta.align` / `numeral` never have to reason about a synthetic
|
|
1445
1578
|
// one. Everything counting columns downstream reads `columnCount`, below.
|
|
1579
|
+
const hasRowActions = rowActions !== undefined && rowActions.length > 0
|
|
1446
1580
|
const tableColumns = useMemo(
|
|
1447
|
-
() =>
|
|
1448
|
-
|
|
1581
|
+
() => [
|
|
1582
|
+
...(enableRowSelection ? [selectionColumn<T>()] : []),
|
|
1583
|
+
...facetColumns,
|
|
1584
|
+
...(hasRowActions ? [rowActionsColumn<T>(rowActions, rowLabel)] : []),
|
|
1585
|
+
],
|
|
1586
|
+
[facetColumns, enableRowSelection, hasRowActions, rowActions, rowLabel],
|
|
1449
1587
|
)
|
|
1450
1588
|
|
|
1451
1589
|
// Enforced before the table is built, so the degraded options below are the ones TanStack gets —
|
|
@@ -1519,6 +1657,17 @@ export function BasaltDataTable<T extends RowData>(props: BasaltDataTableProps<T
|
|
|
1519
1657
|
const hasFolded = foldedColumns.size > 0
|
|
1520
1658
|
const foldRows = useRowDisclosure({ data, getRowId })
|
|
1521
1659
|
|
|
1660
|
+
// The actions column always sticks to the end edge, behind any end-pinned columns the caller
|
|
1661
|
+
// asked for — so it is pinned through TanStack's own state, which also keeps their offsets right.
|
|
1662
|
+
const pinningActive = enablePinning || hasRowActions
|
|
1663
|
+
const tablePinning = useMemo(
|
|
1664
|
+
() =>
|
|
1665
|
+
hasRowActions
|
|
1666
|
+
? { ...columnPinning, end: [...columnPinning.end, ACTIONS_COLUMN_ID] }
|
|
1667
|
+
: columnPinning,
|
|
1668
|
+
[columnPinning, hasRowActions],
|
|
1669
|
+
)
|
|
1670
|
+
|
|
1522
1671
|
const table = useTable({
|
|
1523
1672
|
features: basaltTableFeatures,
|
|
1524
1673
|
data,
|
|
@@ -1527,7 +1676,7 @@ export function BasaltDataTable<T extends RowData>(props: BasaltDataTableProps<T
|
|
|
1527
1676
|
sorting: tableState.sorting,
|
|
1528
1677
|
globalFilter,
|
|
1529
1678
|
columnFilters: tableState.columnFilters,
|
|
1530
|
-
columnPinning,
|
|
1679
|
+
columnPinning: tablePinning,
|
|
1531
1680
|
columnVisibility,
|
|
1532
1681
|
...(enablePagination && { pagination: tableState.pagination }),
|
|
1533
1682
|
...(enableRowSelection && { rowSelection: tableState.rowSelection }),
|
|
@@ -1556,7 +1705,7 @@ export function BasaltDataTable<T extends RowData>(props: BasaltDataTableProps<T
|
|
|
1556
1705
|
...(manualPagination && pageCount !== undefined && { pageCount }),
|
|
1557
1706
|
}),
|
|
1558
1707
|
enableSorting: sortingEnabled,
|
|
1559
|
-
enableColumnPinning:
|
|
1708
|
+
enableColumnPinning: pinningActive,
|
|
1560
1709
|
})
|
|
1561
1710
|
|
|
1562
1711
|
const showSearch = enableGlobalFilter && filteringEnabled
|
|
@@ -1575,7 +1724,11 @@ export function BasaltDataTable<T extends RowData>(props: BasaltDataTableProps<T
|
|
|
1575
1724
|
// the prepended checkbox column, one more again for the fold-disclosure toggle once anything has
|
|
1576
1725
|
// folded — a stale count left the empty state and the error row short of the header.
|
|
1577
1726
|
const columnCount =
|
|
1578
|
-
columns.length +
|
|
1727
|
+
columns.length +
|
|
1728
|
+
(enableRowSelection ? 1 : 0) +
|
|
1729
|
+
(hasRowActions ? 1 : 0) +
|
|
1730
|
+
(hasFolded ? 1 : 0) -
|
|
1731
|
+
foldedColumns.size
|
|
1579
1732
|
|
|
1580
1733
|
// `getSelectedRowModel()` is TanStack's own memo, so its `rows` array keeps its identity until
|
|
1581
1734
|
// the selection or the row model actually moves — which is what makes both memos below hold.
|
|
@@ -1594,7 +1747,7 @@ export function BasaltDataTable<T extends RowData>(props: BasaltDataTableProps<T
|
|
|
1594
1747
|
: bulkActions(selectedRows),
|
|
1595
1748
|
[bulkActions, selectedRows],
|
|
1596
1749
|
)
|
|
1597
|
-
const headerGroups =
|
|
1750
|
+
const headerGroups = pinningActive ? getOrderedHeaderGroups(table) : table.getHeaderGroups()
|
|
1598
1751
|
// The rows actually rendered, not `data` — a filter or a page index that matches nothing leaves
|
|
1599
1752
|
// `data` non-empty while the body has nothing in it, and a blank body reads as a broken table.
|
|
1600
1753
|
const rows = table.getRowModel().rows
|
|
@@ -1672,18 +1825,19 @@ export function BasaltDataTable<T extends RowData>(props: BasaltDataTableProps<T
|
|
|
1672
1825
|
const canSort = sortingEnabled && header.column.getCanSort()
|
|
1673
1826
|
const sorted = header.column.getIsSorted()
|
|
1674
1827
|
const toggleSorting = header.column.getToggleSortingHandler()
|
|
1675
|
-
const pinnedStyle =
|
|
1828
|
+
const pinnedStyle = pinningActive
|
|
1676
1829
|
? getPinnedCellStyle(table, header.column)
|
|
1677
1830
|
: undefined
|
|
1678
1831
|
const align = resolveAlign(header.column)
|
|
1679
1832
|
const isSelectColumn = header.column.id === SELECT_COLUMN_ID
|
|
1833
|
+
const isFoldCandidate = !isSelectColumn && header.column.id !== ACTIONS_COLUMN_ID
|
|
1680
1834
|
return (
|
|
1681
1835
|
<Table.Th
|
|
1682
1836
|
key={header.id}
|
|
1683
1837
|
{...(isSelectColumn && { className: classes.selectCell })}
|
|
1684
1838
|
// The measured fold's own read (`useColumnFold`) — never present on the
|
|
1685
1839
|
// selection column, which is never a fold candidate.
|
|
1686
|
-
{...(
|
|
1840
|
+
{...(isFoldCandidate && { [FOLD_ID_ATTR]: header.column.id })}
|
|
1687
1841
|
onClick={canSort ? toggleSorting : undefined}
|
|
1688
1842
|
onKeyDown={
|
|
1689
1843
|
canSort
|
|
@@ -1731,7 +1885,7 @@ export function BasaltDataTable<T extends RowData>(props: BasaltDataTableProps<T
|
|
|
1731
1885
|
</Table.Tr>
|
|
1732
1886
|
) : (
|
|
1733
1887
|
rows.map((row) => {
|
|
1734
|
-
const cells =
|
|
1888
|
+
const cells = pinningActive
|
|
1735
1889
|
? [
|
|
1736
1890
|
...row.getStartVisibleCells(),
|
|
1737
1891
|
...row.getCenterVisibleCells(),
|
|
@@ -1755,7 +1909,7 @@ export function BasaltDataTable<T extends RowData>(props: BasaltDataTableProps<T
|
|
|
1755
1909
|
</Table.Td>
|
|
1756
1910
|
)}
|
|
1757
1911
|
{cells.map((cell) => {
|
|
1758
|
-
const pinnedStyle =
|
|
1912
|
+
const pinnedStyle = pinningActive
|
|
1759
1913
|
? getPinnedCellStyle(table, cell.column)
|
|
1760
1914
|
: undefined
|
|
1761
1915
|
const align = resolveAlign(cell.column)
|
|
@@ -1764,11 +1918,11 @@ export function BasaltDataTable<T extends RowData>(props: BasaltDataTableProps<T
|
|
|
1764
1918
|
key={cell.id}
|
|
1765
1919
|
{...(cell.column.id === SELECT_COLUMN_ID && {
|
|
1766
1920
|
className: classes.selectCell,
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1921
|
+
})}
|
|
1922
|
+
{...(stopsRowActivation(cell) && {
|
|
1923
|
+
// The label's ::after fills the select cell, so a tap on it bubbles to the
|
|
1924
|
+
// row; the actions menu portals out but React bubbles through the portal.
|
|
1925
|
+
onClick: (event: ReactMouseEvent) => event.stopPropagation(),
|
|
1772
1926
|
})}
|
|
1773
1927
|
style={{
|
|
1774
1928
|
...(typeof cell.getValue() === 'number' &&
|
package/src/telemetry.ts
CHANGED
|
@@ -99,6 +99,11 @@ export type ChatTurnTrace = {
|
|
|
99
99
|
* else. A sink without the hook, or no sink, changes nothing on the wire. basalt has no OTel
|
|
100
100
|
* dependency; the hook is where the consumer's SDK does the work.
|
|
101
101
|
*
|
|
102
|
+
* A fetch instrumentation (OTel web, HyperDX) overwrites the returned `traceparent` on same-origin
|
|
103
|
+
* requests with its own fetch span, so the API span lands in a separate trace. Either exclude the
|
|
104
|
+
* chat URL from the instrumentation (`ignoreUrls` in HyperDX and in OTel's fetch instrumentation),
|
|
105
|
+
* or make the turn span the active context around the request.
|
|
106
|
+
*
|
|
102
107
|
* @example
|
|
103
108
|
* export const telemetry: Telemetry = Object.assign(onEvent, {
|
|
104
109
|
* startChatTurn({ threadId }) {
|