@duro-app/ui 0.33.0 → 0.34.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/dist/components/Table/FromTanstack.d.ts +45 -0
- package/dist/components/Table/FromTanstack.d.ts.map +1 -0
- package/dist/components/Table/Table.d.ts +37 -13
- package/dist/components/Table/Table.d.ts.map +1 -1
- package/dist/components/Table/Table.stories.d.ts +2 -0
- package/dist/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/components/Table/styles.css.d.ts +13 -0
- package/dist/components/Table/styles.css.d.ts.map +1 -1
- package/dist/components/Table/tanstack-augmentation.d.ts +13 -0
- package/dist/components/Table/tanstack-augmentation.d.ts.map +1 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2050 -1933
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/Table/FromTanstack.tsx +159 -0
- package/src/components/Table/Table.stories.tsx +164 -0
- package/src/components/Table/Table.tsx +194 -42
- package/src/components/Table/styles.css.ts +25 -0
- package/src/components/Table/tanstack-augmentation.ts +28 -0
- package/src/index.ts +5 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type ReactElement,
|
|
2
3
|
type ReactNode,
|
|
3
4
|
type MutableRefObject,
|
|
4
5
|
createContext,
|
|
@@ -44,13 +45,26 @@ function useTable() {
|
|
|
44
45
|
|
|
45
46
|
const HeaderContext = createContext(false)
|
|
46
47
|
|
|
48
|
+
// --- Dev-only warn registry ---
|
|
49
|
+
//
|
|
50
|
+
// One-shot per warning code per process: long-running dev sessions
|
|
51
|
+
// shouldn't drown the console on every re-render of a misconfigured table.
|
|
52
|
+
const _IS_PROD = typeof process !== 'undefined' && process.env?.NODE_ENV === 'production'
|
|
53
|
+
const _devWarned = new Set<string>()
|
|
54
|
+
function devWarnOnce(code: string, message: string) {
|
|
55
|
+
if (_IS_PROD || _devWarned.has(code)) return
|
|
56
|
+
_devWarned.add(code)
|
|
57
|
+
// eslint-disable-next-line no-console
|
|
58
|
+
console.warn(`[duro-app/ui Table] ${message}`)
|
|
59
|
+
}
|
|
60
|
+
|
|
47
61
|
// --- Container — owns the @container query target ---
|
|
48
62
|
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
63
|
+
// @deprecated Root sets up its own container query and accepts `sortChip`
|
|
64
|
+
// / `pagination` as slot props. Container is kept exported as a passthrough
|
|
65
|
+
// so existing call sites continue to work; new code should use Root alone.
|
|
52
66
|
|
|
53
|
-
function Container({children}: {children: ReactNode}) {
|
|
67
|
+
export function Container({children}: {children: ReactNode}) {
|
|
54
68
|
return <html.div style={styles.container}>{children}</html.div>
|
|
55
69
|
}
|
|
56
70
|
|
|
@@ -62,19 +76,37 @@ function Container({children}: {children: ReactNode}) {
|
|
|
62
76
|
// render — cheap (small N, no DOM) — but worth memoising on `children`
|
|
63
77
|
// reference if profiling flags it.
|
|
64
78
|
|
|
79
|
+
// Shallow string-children extraction. We deliberately do NOT recurse into
|
|
80
|
+
// child elements: an icon, tooltip trigger, or sort indicator nested inside
|
|
81
|
+
// a HeaderCell would otherwise pollute the stack-mode label with its own
|
|
82
|
+
// text content. JSX-only headers must set `label` explicitly.
|
|
65
83
|
function extractText(node: ReactNode): string {
|
|
66
84
|
let out = ''
|
|
67
85
|
Children.forEach(node, (child) => {
|
|
68
86
|
if (typeof child === 'string' || typeof child === 'number') {
|
|
69
87
|
out += String(child)
|
|
70
|
-
} else if (isValidElement(child)) {
|
|
71
|
-
const props = child.props as {children?: ReactNode}
|
|
72
|
-
if (props.children) out += extractText(props.children)
|
|
73
88
|
}
|
|
74
89
|
})
|
|
75
90
|
return out
|
|
76
91
|
}
|
|
77
92
|
|
|
93
|
+
// Unwrap memo(...) / forwardRef(...) wrappers so JSX like
|
|
94
|
+
// `<MemoizedHeaderCell ...>` still identifies as HeaderCell. Both wrappers
|
|
95
|
+
// expose the inner component via a `.type` (memo) or `.render` (forwardRef)
|
|
96
|
+
// property on the element-type descriptor. We walk both fields until we
|
|
97
|
+
// reach a leaf, which is the underlying function/class component.
|
|
98
|
+
function unwrapElementType(t: unknown): unknown {
|
|
99
|
+
let current = t
|
|
100
|
+
// Bounded loop — defensive cap against pathological infinite-nesting.
|
|
101
|
+
for (let i = 0; i < 8 && current && typeof current === 'object'; i++) {
|
|
102
|
+
const next =
|
|
103
|
+
(current as {type?: unknown; render?: unknown}).type ?? (current as {render?: unknown}).render
|
|
104
|
+
if (next == null) break
|
|
105
|
+
current = next
|
|
106
|
+
}
|
|
107
|
+
return current
|
|
108
|
+
}
|
|
109
|
+
|
|
78
110
|
function extractColumnMeta(children: ReactNode): {template: string; labels: string[]} {
|
|
79
111
|
const widths: string[] = []
|
|
80
112
|
const labels: string[] = []
|
|
@@ -83,10 +115,24 @@ function extractColumnMeta(children: ReactNode): {template: string; labels: stri
|
|
|
83
115
|
Children.forEach(node, (child) => {
|
|
84
116
|
if (!isValidElement(child)) return
|
|
85
117
|
const props = child.props as Record<string, any>
|
|
86
|
-
const
|
|
87
|
-
|
|
118
|
+
const unwrappedType = unwrapElementType(child.type)
|
|
119
|
+
const displayName =
|
|
120
|
+
(unwrappedType as {name?: string; displayName?: string} | null)?.displayName ||
|
|
121
|
+
(unwrappedType as {name?: string} | null)?.name ||
|
|
122
|
+
''
|
|
123
|
+
if (unwrappedType === HeaderCell || displayName === 'HeaderCell') {
|
|
88
124
|
widths.push(props.width || '1fr')
|
|
89
|
-
const
|
|
125
|
+
const explicit = typeof props.label === 'string' ? props.label : undefined
|
|
126
|
+
const fallback = extractText(props.children).trim()
|
|
127
|
+
const label = explicit ?? fallback
|
|
128
|
+
if (!explicit && fallback === '' && props.children != null) {
|
|
129
|
+
// Children are JSX with no string content — stack mode will render
|
|
130
|
+
// an unlabeled cell. Tell the developer once.
|
|
131
|
+
devWarnOnce(
|
|
132
|
+
'headerCell-missing-label',
|
|
133
|
+
'Table.HeaderCell with JSX children must set `label` for stack-mode rendering.',
|
|
134
|
+
)
|
|
135
|
+
}
|
|
90
136
|
labels.push(label)
|
|
91
137
|
} else if (props.children) {
|
|
92
138
|
walk(props.children)
|
|
@@ -106,34 +152,67 @@ interface RootProps {
|
|
|
106
152
|
size?: TableSize
|
|
107
153
|
/** Opt out of responsive container-query behavior. Default true. */
|
|
108
154
|
responsive?: boolean
|
|
155
|
+
/**
|
|
156
|
+
* Optional sort UI rendered above the grid. Visible only in stack mode
|
|
157
|
+
* (SortChip carries its own `display: none → inline-flex` rule). Typical
|
|
158
|
+
* value: `<Table.SortChip options={...} value={...} onChange={...} />`.
|
|
159
|
+
*/
|
|
160
|
+
sortChip?: ReactNode
|
|
161
|
+
/**
|
|
162
|
+
* Optional pagination UI rendered below the grid. Typical value:
|
|
163
|
+
* `<Table.Pagination table={tanstackTable} />`.
|
|
164
|
+
*/
|
|
165
|
+
pagination?: ReactNode
|
|
109
166
|
}
|
|
110
167
|
|
|
111
|
-
function Root({
|
|
168
|
+
export function Root({
|
|
169
|
+
children,
|
|
170
|
+
variant = 'default',
|
|
171
|
+
size = 'md',
|
|
172
|
+
responsive = true,
|
|
173
|
+
sortChip,
|
|
174
|
+
pagination,
|
|
175
|
+
}: RootProps) {
|
|
112
176
|
const inferredTemplateRef = useRef<string | null>(null)
|
|
113
177
|
const {template, labels} = extractColumnMeta(children)
|
|
114
178
|
if (template) {
|
|
115
179
|
inferredTemplateRef.current = template
|
|
116
180
|
}
|
|
117
181
|
|
|
182
|
+
const grid = (
|
|
183
|
+
<html.div
|
|
184
|
+
role="table"
|
|
185
|
+
style={[
|
|
186
|
+
styles.root,
|
|
187
|
+
template ? styles.gridColumns(template) : undefined,
|
|
188
|
+
responsive && styles.rootResponsive,
|
|
189
|
+
]}
|
|
190
|
+
>
|
|
191
|
+
{children}
|
|
192
|
+
</html.div>
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
// Slots render unconditionally — `responsive=false` still wants its sort/
|
|
196
|
+
// pagination chrome. Only the containerType:inline-size wrapper is
|
|
197
|
+
// conditional, since it only matters when @container queries fire.
|
|
198
|
+
const body = (
|
|
199
|
+
<>
|
|
200
|
+
{sortChip}
|
|
201
|
+
{grid}
|
|
202
|
+
{pagination}
|
|
203
|
+
</>
|
|
204
|
+
)
|
|
205
|
+
|
|
118
206
|
return (
|
|
119
207
|
<TableContext.Provider value={{variant, size, responsive, labels, inferredTemplateRef}}>
|
|
120
|
-
<html.div
|
|
121
|
-
role="table"
|
|
122
|
-
style={[
|
|
123
|
-
styles.root,
|
|
124
|
-
template ? styles.gridColumns(template) : undefined,
|
|
125
|
-
responsive && styles.rootResponsive,
|
|
126
|
-
]}
|
|
127
|
-
>
|
|
128
|
-
{children}
|
|
129
|
-
</html.div>
|
|
208
|
+
{responsive ? <html.div style={styles.rootContainer}>{body}</html.div> : body}
|
|
130
209
|
</TableContext.Provider>
|
|
131
210
|
)
|
|
132
211
|
}
|
|
133
212
|
|
|
134
213
|
// --- Header ---
|
|
135
214
|
|
|
136
|
-
function Header({children}: {children: ReactNode}) {
|
|
215
|
+
export function Header({children}: {children: ReactNode}) {
|
|
137
216
|
return (
|
|
138
217
|
<HeaderContext.Provider value={true}>
|
|
139
218
|
<html.div role="rowgroup" style={styles.header}>
|
|
@@ -145,22 +224,24 @@ function Header({children}: {children: ReactNode}) {
|
|
|
145
224
|
|
|
146
225
|
// --- Body ---
|
|
147
226
|
|
|
148
|
-
function Body({children}: {children: ReactNode}) {
|
|
227
|
+
export function Body({children}: {children: ReactNode}) {
|
|
149
228
|
const {variant} = useTable()
|
|
150
|
-
const childArray = Children.toArray(children)
|
|
229
|
+
const childArray = Children.toArray(children) as ReactElement[]
|
|
151
230
|
|
|
152
231
|
return (
|
|
153
232
|
<HeaderContext.Provider value={false}>
|
|
154
233
|
<html.div role="rowgroup" style={styles.body}>
|
|
155
234
|
{childArray.map((child, index) => {
|
|
156
|
-
if (variant
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
235
|
+
if (variant !== 'striped') return child
|
|
236
|
+
// Key the provider by the child's React key (Children.toArray
|
|
237
|
+
// guarantees one) so reordered rows carry their providers along
|
|
238
|
+
// with them. Position is passed as the value because striping is
|
|
239
|
+
// a positional concern even when row identity is stable.
|
|
240
|
+
return (
|
|
241
|
+
<RowIndexContext.Provider key={child.key ?? index} value={index}>
|
|
242
|
+
{child}
|
|
243
|
+
</RowIndexContext.Provider>
|
|
244
|
+
)
|
|
164
245
|
})}
|
|
165
246
|
</html.div>
|
|
166
247
|
</HeaderContext.Provider>
|
|
@@ -175,25 +256,81 @@ const RowIndexContext = createContext<number>(-1)
|
|
|
175
256
|
// was only set up for `variant === 'bordered'`, which meant non-bordered
|
|
176
257
|
// tables had every cell reading {index: 0} from the default — fine until
|
|
177
258
|
// we needed to look up labels by column index.
|
|
259
|
+
//
|
|
260
|
+
// Interactive rows: pass `onClick` to make the row a navigation target.
|
|
261
|
+
// The role stays `row` (per ARIA grid pattern) — we don't wrap in an outer
|
|
262
|
+
// element with role=button, which would break the rowgroup → row hierarchy.
|
|
263
|
+
// A click that originates inside an interactive descendant (button, link,
|
|
264
|
+
// input, etc.) is treated as that descendant's click and does not fire
|
|
265
|
+
// onClick — prevents double-fire when a row contains action buttons.
|
|
266
|
+
|
|
267
|
+
interface RowProps {
|
|
268
|
+
children: ReactNode
|
|
269
|
+
/** When set, the row becomes focusable + clickable. */
|
|
270
|
+
onClick?: () => void
|
|
271
|
+
/** Per-row accessible name — required when onClick is set, recommended otherwise. */
|
|
272
|
+
'aria-label'?: string
|
|
273
|
+
}
|
|
178
274
|
|
|
179
|
-
|
|
275
|
+
// Heuristic for "clicking a descendant means the descendant, not the row".
|
|
276
|
+
// Matches the elements the WAI-ARIA grid pattern lists as "widgets" plus the
|
|
277
|
+
// usual native focus-stealers.
|
|
278
|
+
const INTERACTIVE_SELECTOR =
|
|
279
|
+
'button, a, input, select, textarea, [role="button"], [role="link"], [role="checkbox"], [role="menuitem"], [role="switch"], [role="tab"], [contenteditable="true"]'
|
|
280
|
+
|
|
281
|
+
export function Row({children, onClick, 'aria-label': ariaLabel}: RowProps) {
|
|
180
282
|
const {variant} = useTable()
|
|
181
283
|
const isHeader = useContext(HeaderContext)
|
|
182
284
|
const rowIndex = useContext(RowIndexContext)
|
|
183
285
|
const isEvenRow = rowIndex >= 0 && rowIndex % 2 === 1
|
|
184
|
-
const childArray = Children.toArray(children)
|
|
286
|
+
const childArray = Children.toArray(children) as ReactElement[]
|
|
287
|
+
const isClickable = onClick !== undefined && !isHeader
|
|
185
288
|
|
|
186
289
|
return (
|
|
187
290
|
<html.div
|
|
188
291
|
role="row"
|
|
292
|
+
tabIndex={isClickable ? 0 : undefined}
|
|
293
|
+
aria-label={isClickable ? ariaLabel : undefined}
|
|
294
|
+
onClick={
|
|
295
|
+
isClickable
|
|
296
|
+
? (e) => {
|
|
297
|
+
// react-strict-dom's typed click event omits `target`, but the
|
|
298
|
+
// underlying SyntheticEvent still carries it. Cast through so
|
|
299
|
+
// we can detect clicks that originate inside an interactive
|
|
300
|
+
// descendant (action button, link, input) and let them be
|
|
301
|
+
// attributed to that widget instead of the row.
|
|
302
|
+
const target = (e as unknown as {target?: EventTarget | null}).target
|
|
303
|
+
if (target instanceof Element && target.closest(INTERACTIVE_SELECTOR)) {
|
|
304
|
+
return
|
|
305
|
+
}
|
|
306
|
+
onClick()
|
|
307
|
+
}
|
|
308
|
+
: undefined
|
|
309
|
+
}
|
|
310
|
+
onKeyDown={
|
|
311
|
+
isClickable
|
|
312
|
+
? (e) => {
|
|
313
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
314
|
+
onClick()
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
: undefined
|
|
318
|
+
}
|
|
189
319
|
style={[
|
|
190
320
|
styles.row,
|
|
191
321
|
!isHeader && styles.bodyRow,
|
|
192
322
|
!isHeader && variant === 'striped' && isEvenRow && styles.stripedEven,
|
|
323
|
+
isClickable && styles.clickableRow,
|
|
193
324
|
]}
|
|
194
325
|
>
|
|
195
326
|
{childArray.map((child, index) => (
|
|
196
|
-
|
|
327
|
+
// Key by the child's React key (Children.toArray assigns one) so a
|
|
328
|
+
// reordered column carries its CellIndexContext provider along.
|
|
329
|
+
// The positional `index` lives in the provider's value, not its key.
|
|
330
|
+
<CellIndexContext.Provider
|
|
331
|
+
key={child.key ?? index}
|
|
332
|
+
value={{index, total: childArray.length}}
|
|
333
|
+
>
|
|
197
334
|
{child}
|
|
198
335
|
</CellIndexContext.Provider>
|
|
199
336
|
))}
|
|
@@ -205,24 +342,34 @@ const CellIndexContext = createContext<{index: number; total: number}>({index: 0
|
|
|
205
342
|
|
|
206
343
|
// --- HeaderCell ---
|
|
207
344
|
|
|
208
|
-
function HeaderCell({
|
|
345
|
+
export function HeaderCell({
|
|
209
346
|
children,
|
|
210
347
|
width: _width,
|
|
211
348
|
label: _label,
|
|
212
|
-
isActions
|
|
349
|
+
isActions,
|
|
213
350
|
'aria-label': ariaLabel,
|
|
214
351
|
}: {
|
|
215
352
|
children?: ReactNode
|
|
216
353
|
/** Column width: CSS value like '40px', '2fr', 'max-content'. Defaults to '1fr'. */
|
|
217
354
|
width?: string
|
|
218
|
-
/** Stack-mode label string.
|
|
219
|
-
* text
|
|
355
|
+
/** Stack-mode label string. Optional when children is a plain string — the
|
|
356
|
+
* text content is used as the label automatically. Required when children
|
|
357
|
+
* contain JSX (icon + text, sort indicator, etc.). */
|
|
220
358
|
label?: string
|
|
221
|
-
/**
|
|
222
|
-
*
|
|
359
|
+
/**
|
|
360
|
+
* @deprecated Has no effect on HeaderCell. Pass `isActions` on the
|
|
361
|
+
* matching `Table.Cell` instead — that's where the stack-mode footer
|
|
362
|
+
* layout actually applies.
|
|
363
|
+
*/
|
|
223
364
|
isActions?: boolean
|
|
224
365
|
'aria-label'?: string
|
|
225
366
|
}) {
|
|
367
|
+
if (isActions === true) {
|
|
368
|
+
devWarnOnce(
|
|
369
|
+
'headerCell-isActions',
|
|
370
|
+
'HeaderCell `isActions` prop has no effect and is deprecated. Pass `isActions` on the matching Table.Cell instead.',
|
|
371
|
+
)
|
|
372
|
+
}
|
|
226
373
|
const {size, variant} = useTable()
|
|
227
374
|
const {index, total} = useContext(CellIndexContext)
|
|
228
375
|
const isLast = variant === 'bordered' && index === total - 1
|
|
@@ -250,7 +397,7 @@ HeaderCell.displayName = 'HeaderCell'
|
|
|
250
397
|
// container query in non-stack modes. The span is omitted entirely when
|
|
251
398
|
// isActions=true — actions footers don't show a label.
|
|
252
399
|
|
|
253
|
-
function Cell({children, isActions}: {children: ReactNode; isActions?: boolean}) {
|
|
400
|
+
export function Cell({children, isActions}: {children: ReactNode; isActions?: boolean}) {
|
|
254
401
|
const {size, variant, labels, responsive} = useTable()
|
|
255
402
|
const {index, total} = useContext(CellIndexContext)
|
|
256
403
|
const isLast = variant === 'bordered' && index === total - 1
|
|
@@ -277,7 +424,10 @@ function Cell({children, isActions}: {children: ReactNode; isActions?: boolean})
|
|
|
277
424
|
|
|
278
425
|
// --- Export ---
|
|
279
426
|
|
|
427
|
+
import {FromTanstack} from './FromTanstack'
|
|
428
|
+
|
|
280
429
|
export const Table = {
|
|
430
|
+
/** @deprecated Wrap behaviour is built into Table.Root. Drop this wrapper from new code. */
|
|
281
431
|
Container,
|
|
282
432
|
Root,
|
|
283
433
|
Header,
|
|
@@ -289,4 +439,6 @@ export const Table = {
|
|
|
289
439
|
SortIndicator,
|
|
290
440
|
ColumnFilter,
|
|
291
441
|
SortChip,
|
|
442
|
+
/** Renders a styled Table directly from a TanStack table instance. */
|
|
443
|
+
FromTanstack,
|
|
292
444
|
}
|
|
@@ -14,6 +14,9 @@ export const styles = css.create({
|
|
|
14
14
|
// Outer wrapper that hosts the @container query. Wraps SortChip + Root
|
|
15
15
|
// + Pagination so all three react to the same width. Root itself does
|
|
16
16
|
// NOT carry containerType — keeping a single query target per Table.
|
|
17
|
+
//
|
|
18
|
+
// @deprecated Use Table.Root's sortChip/pagination slot props instead.
|
|
19
|
+
// This style backs the deprecated <Table.Container> wrapper.
|
|
17
20
|
container: {
|
|
18
21
|
containerType: 'inline-size',
|
|
19
22
|
display: 'flex',
|
|
@@ -21,6 +24,28 @@ export const styles = css.create({
|
|
|
21
24
|
gap: spacing.sm,
|
|
22
25
|
},
|
|
23
26
|
|
|
27
|
+
// The same containment chrome, but applied by Root when responsive=true.
|
|
28
|
+
// Mirrors `container` so consumers don't need to wrap manually.
|
|
29
|
+
rootContainer: {
|
|
30
|
+
containerType: 'inline-size',
|
|
31
|
+
display: 'flex',
|
|
32
|
+
flexDirection: 'column',
|
|
33
|
+
gap: spacing.sm,
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
// Applied when Row receives an `onClick` handler. Cursor signals clickability;
|
|
37
|
+
// the :focus-visible outline meets WCAG 2.4.7 for keyboard navigation.
|
|
38
|
+
clickableRow: {
|
|
39
|
+
cursor: 'pointer',
|
|
40
|
+
outlineWidth: {
|
|
41
|
+
default: 0,
|
|
42
|
+
':focus-visible': 2,
|
|
43
|
+
},
|
|
44
|
+
outlineStyle: 'solid',
|
|
45
|
+
outlineColor: colors.accent,
|
|
46
|
+
outlineOffset: -2,
|
|
47
|
+
},
|
|
48
|
+
|
|
24
49
|
// Root — the single grid container for the table itself
|
|
25
50
|
root: {
|
|
26
51
|
display: 'grid',
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Side-effect-only module: registers `@duro-app/ui`'s additions to
|
|
2
|
+
// TanStack's `ColumnMeta` so consumers can write
|
|
3
|
+
// meta: { stackLabel: 'X', isActions: true }
|
|
4
|
+
// on column defs and have it type-check, regardless of whether they
|
|
5
|
+
// import from Table/FromTanstack in that particular file.
|
|
6
|
+
//
|
|
7
|
+
// `index.ts` re-exports this module so the augmentation registers as
|
|
8
|
+
// soon as any file in the consumer's TS program imports from
|
|
9
|
+
// `@duro-app/ui`. Keeping the augmentation in a standalone file (instead
|
|
10
|
+
// of co-located with `FromTanstack.tsx`) also keeps it from being
|
|
11
|
+
// tree-shaken out of the published types.
|
|
12
|
+
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
14
|
+
declare module '@tanstack/react-table' {
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
16
|
+
interface ColumnMeta<TData extends unknown, TValue> {
|
|
17
|
+
/** Override the stack-mode label when columnDef.header is JSX (icon + text, etc). */
|
|
18
|
+
stackLabel?: string
|
|
19
|
+
/**
|
|
20
|
+
* Marks this column as the actions column. In stack mode the cells
|
|
21
|
+
* render as a full-width footer instead of a labelled key/value row.
|
|
22
|
+
*/
|
|
23
|
+
isActions?: boolean
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Empty export so this file is treated as a module by TS and ts-up/vite.
|
|
28
|
+
export {}
|
package/src/index.ts
CHANGED
|
@@ -46,6 +46,11 @@ export {
|
|
|
46
46
|
export {Tag, type TagVariant, type TagSize} from './components/Tag/Tag'
|
|
47
47
|
export {TagGroup} from './components/TagGroup/TagGroup'
|
|
48
48
|
export {Table, type TableVariant, type TableSize} from './components/Table/Table'
|
|
49
|
+
// Side-effect import registers our augmentation of TanStack's ColumnMeta
|
|
50
|
+
// (stackLabel / isActions). Standalone module so the augmentation lands
|
|
51
|
+
// whenever this package is in a consumer's TS program, even if FromTanstack
|
|
52
|
+
// itself isn't imported.
|
|
53
|
+
import './components/Table/tanstack-augmentation'
|
|
49
54
|
// TanStack Table: consumers install @tanstack/react-table directly and use
|
|
50
55
|
// Table.Pagination, Table.SortIndicator, Table.ColumnFilter for styled integration
|
|
51
56
|
export {Tabs} from './components/Tabs/Tabs'
|