@dxos/react-ui-table 0.3.9-main.14901ff → 0.3.9-main.16d375f
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/lib/browser/index.mjs +195 -146
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/components/Cell/CellCombobox.d.ts.map +1 -1
- package/dist/types/src/components/ColumnMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/props.d.ts +4 -2
- package/dist/types/src/components/Table/props.d.ts.map +1 -1
- package/dist/types/src/helpers.d.ts.map +1 -1
- package/dist/types/src/schema.d.ts +0 -1
- package/dist/types/src/schema.d.ts.map +1 -1
- package/dist/types/src/theme.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +11 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/package.json +11 -12
- package/src/components/Cell/CellCombobox.tsx +4 -1
- package/src/components/ColumnMenu.tsx +16 -16
- package/src/components/Table/Table.stories.tsx +21 -12
- package/src/components/Table/Table.tsx +86 -25
- package/src/components/Table/props.ts +24 -16
- package/src/helpers.tsx +67 -54
- package/src/schema.ts +0 -8
- package/src/theme.ts +2 -2
- package/src/translations.ts +11 -0
package/src/helpers.tsx
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { useFocusFinders } from '@fluentui/react-tabster';
|
|
5
6
|
import { Check, ClipboardText, type Icon, X } from '@phosphor-icons/react';
|
|
6
7
|
import {
|
|
7
8
|
createColumnHelper,
|
|
@@ -13,7 +14,7 @@ import {
|
|
|
13
14
|
} from '@tanstack/react-table';
|
|
14
15
|
import format from 'date-fns/format';
|
|
15
16
|
import formatDistanceToNow from 'date-fns/formatDistanceToNow';
|
|
16
|
-
import React, { useRef, useState } from 'react';
|
|
17
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
17
18
|
|
|
18
19
|
import { type PublicKey } from '@dxos/keys';
|
|
19
20
|
import { type ClassNameValue, Input, Tooltip } from '@dxos/react-ui';
|
|
@@ -132,10 +133,27 @@ const StringBuilderCell = <TData extends RowData>(cellContext: CellContext<TData
|
|
|
132
133
|
);
|
|
133
134
|
};
|
|
134
135
|
|
|
136
|
+
const parseNumber = (text?: string) => (text?.trim().length ? Number(text) : NaN);
|
|
137
|
+
|
|
138
|
+
const displayNumber = (value?: number, digits?: number) =>
|
|
139
|
+
value || value === 0
|
|
140
|
+
? value.toLocaleString(undefined, {
|
|
141
|
+
minimumFractionDigits: digits ?? 0,
|
|
142
|
+
maximumFractionDigits: digits ?? 2,
|
|
143
|
+
})
|
|
144
|
+
: '';
|
|
145
|
+
|
|
135
146
|
const NumberBuilderCell = <TData extends RowData>(cellContext: CellContext<TData, number>) => {
|
|
136
|
-
const { onUpdate,
|
|
147
|
+
const { onUpdate, digits } = cellContext.column.columnDef.meta as ColumnMeta<TData, string>;
|
|
137
148
|
const value = cellContext.getValue();
|
|
138
|
-
const [text, setText] = useState<string>();
|
|
149
|
+
const [text, setText] = useState<string>(displayNumber(value, digits));
|
|
150
|
+
const { findPrevFocusable } = useFocusFinders();
|
|
151
|
+
const ref = useRef<HTMLInputElement | null>(null);
|
|
152
|
+
|
|
153
|
+
// Update display value if `digits` changes and input is not in focus.
|
|
154
|
+
useEffect(() => {
|
|
155
|
+
(!document.activeElement || document.activeElement !== ref.current) && setText(displayNumber(value, digits));
|
|
156
|
+
}, [digits, value]);
|
|
139
157
|
|
|
140
158
|
const handleEdit = () => {
|
|
141
159
|
if (onUpdate) {
|
|
@@ -145,39 +163,34 @@ const NumberBuilderCell = <TData extends RowData>(cellContext: CellContext<TData
|
|
|
145
163
|
|
|
146
164
|
// TODO(burdon): Property is encoded as float (e.g., 6.1 => 6.099)
|
|
147
165
|
const handleSave = () => {
|
|
148
|
-
const
|
|
149
|
-
onUpdate?.(cellContext.row.original, cellContext.column.id, isNaN(
|
|
150
|
-
setText(
|
|
166
|
+
const nextValue = parseNumber(text);
|
|
167
|
+
onUpdate?.(cellContext.row.original, cellContext.column.id, isNaN(nextValue) ? undefined : nextValue);
|
|
168
|
+
setText(displayNumber(nextValue, digits));
|
|
169
|
+
// Shift focus only if input is focused on save (e.g. on `Enter`).
|
|
170
|
+
ref.current && document.activeElement === ref.current && findPrevFocusable(ref.current)?.focus();
|
|
151
171
|
};
|
|
152
172
|
|
|
153
|
-
const
|
|
154
|
-
setText(
|
|
173
|
+
const _handleCancel = () => {
|
|
174
|
+
setText(displayNumber(value, digits));
|
|
175
|
+
ref.current && findPrevFocusable(ref.current)?.focus();
|
|
155
176
|
};
|
|
156
177
|
|
|
157
|
-
if (text !== undefined) {
|
|
158
|
-
return (
|
|
159
|
-
<Input.Root>
|
|
160
|
-
<Input.TextInput
|
|
161
|
-
value={text}
|
|
162
|
-
classNames={['is-full text-end font-mono']}
|
|
163
|
-
onBlur={handleSave}
|
|
164
|
-
onChange={(event) => setText(event.target.value)}
|
|
165
|
-
onKeyDown={(event) => (event.key === 'Escape' && handleCancel()) || (event.key === 'Enter' && handleSave())}
|
|
166
|
-
/>
|
|
167
|
-
</Input.Root>
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
178
|
return (
|
|
172
|
-
<
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
179
|
+
<Input.Root>
|
|
180
|
+
<Input.TextInput
|
|
181
|
+
ref={ref}
|
|
182
|
+
variant='subdued'
|
|
183
|
+
value={text}
|
|
184
|
+
classNames={['is-full text-end font-mono', textPadding]}
|
|
185
|
+
onBlur={handleSave}
|
|
186
|
+
onFocus={handleEdit}
|
|
187
|
+
onChange={({ target: { value } }) => setText(value)}
|
|
188
|
+
onKeyDown={(event) => {
|
|
189
|
+
// TODO(thure): `Escape` is reserved for blurring the input and moving focus to the cell
|
|
190
|
+
return /* (event.key === 'Escape' && handleCancel()) || */ event.key === 'Enter' && handleSave();
|
|
191
|
+
}}
|
|
192
|
+
/>
|
|
193
|
+
</Input.Root>
|
|
181
194
|
);
|
|
182
195
|
};
|
|
183
196
|
|
|
@@ -238,14 +251,7 @@ export class ColumnBuilder<TData extends RowData> {
|
|
|
238
251
|
...props
|
|
239
252
|
}: ComboboxColumnOptions<TData>): Partial<ColumnDef<TData, any>> {
|
|
240
253
|
return {
|
|
241
|
-
...props,
|
|
242
254
|
minSize: 100,
|
|
243
|
-
meta: {
|
|
244
|
-
...props.meta,
|
|
245
|
-
model,
|
|
246
|
-
onUpdate,
|
|
247
|
-
cell: { classNames, ...props.meta?.cell },
|
|
248
|
-
},
|
|
249
255
|
header: (column) => {
|
|
250
256
|
return label ?? column.header.id;
|
|
251
257
|
},
|
|
@@ -256,6 +262,13 @@ export class ColumnBuilder<TData extends RowData> {
|
|
|
256
262
|
const cellValue = cellContext.getValue();
|
|
257
263
|
return <div className={mx('truncate', classNames)}>{cellValue ? model?.getText(cellValue) : ''}</div>;
|
|
258
264
|
},
|
|
265
|
+
...props,
|
|
266
|
+
meta: {
|
|
267
|
+
...props.meta,
|
|
268
|
+
model,
|
|
269
|
+
onUpdate,
|
|
270
|
+
cell: { classNames, ...props.meta?.cell },
|
|
271
|
+
},
|
|
259
272
|
};
|
|
260
273
|
}
|
|
261
274
|
|
|
@@ -266,13 +279,7 @@ export class ColumnBuilder<TData extends RowData> {
|
|
|
266
279
|
ColumnDef<TData, string>
|
|
267
280
|
> {
|
|
268
281
|
return {
|
|
269
|
-
...props,
|
|
270
282
|
minSize: 100,
|
|
271
|
-
meta: {
|
|
272
|
-
...props.meta,
|
|
273
|
-
onUpdate,
|
|
274
|
-
cell: { ...props.meta?.cell, classNames },
|
|
275
|
-
},
|
|
276
283
|
// TODO(burdon): Default.
|
|
277
284
|
header: (column) => {
|
|
278
285
|
return label ?? column.header.id;
|
|
@@ -283,6 +290,12 @@ export class ColumnBuilder<TData extends RowData> {
|
|
|
283
290
|
const value = cell.getValue();
|
|
284
291
|
return <div className={mx('truncate', textPadding, classNames)}>{value}</div>;
|
|
285
292
|
},
|
|
293
|
+
...props,
|
|
294
|
+
meta: {
|
|
295
|
+
...props.meta,
|
|
296
|
+
onUpdate,
|
|
297
|
+
cell: { ...props.meta?.cell, classNames },
|
|
298
|
+
},
|
|
286
299
|
};
|
|
287
300
|
}
|
|
288
301
|
|
|
@@ -293,9 +306,11 @@ export class ColumnBuilder<TData extends RowData> {
|
|
|
293
306
|
ColumnDef<TData, number>
|
|
294
307
|
> {
|
|
295
308
|
return {
|
|
296
|
-
...props,
|
|
297
309
|
size: 100,
|
|
298
310
|
minSize: 100,
|
|
311
|
+
header: (cell) => label ?? cell.header.id,
|
|
312
|
+
cell: NumberBuilderCell,
|
|
313
|
+
...props,
|
|
299
314
|
meta: {
|
|
300
315
|
...props.meta,
|
|
301
316
|
onUpdate,
|
|
@@ -303,8 +318,6 @@ export class ColumnBuilder<TData extends RowData> {
|
|
|
303
318
|
header: { classNames: 'text-end', ...props.meta?.header },
|
|
304
319
|
cell: { ...props.meta?.cell, classNames },
|
|
305
320
|
},
|
|
306
|
-
header: (cell) => label ?? cell.header.id,
|
|
307
|
-
cell: NumberBuilderCell,
|
|
308
321
|
};
|
|
309
322
|
}
|
|
310
323
|
|
|
@@ -316,10 +329,8 @@ export class ColumnBuilder<TData extends RowData> {
|
|
|
316
329
|
ColumnDef<TData, Date>
|
|
317
330
|
> {
|
|
318
331
|
return {
|
|
319
|
-
...props,
|
|
320
332
|
size: 220, // TODO(burdon): Depends on format.
|
|
321
333
|
minSize: 100,
|
|
322
|
-
meta: { ...props.meta, cell: { ...props.meta?.cell, classNames } },
|
|
323
334
|
header: (cell) => label ?? cell.header.id,
|
|
324
335
|
cell: (cell) => {
|
|
325
336
|
const value = cell.getValue();
|
|
@@ -339,6 +350,8 @@ export class ColumnBuilder<TData extends RowData> {
|
|
|
339
350
|
return null;
|
|
340
351
|
}
|
|
341
352
|
},
|
|
353
|
+
...props,
|
|
354
|
+
meta: { ...props.meta, cell: { ...props.meta?.cell, classNames } },
|
|
342
355
|
};
|
|
343
356
|
}
|
|
344
357
|
|
|
@@ -347,10 +360,8 @@ export class ColumnBuilder<TData extends RowData> {
|
|
|
347
360
|
*/
|
|
348
361
|
key({ label, tooltip, classNames, ...props }: KeyColumnOptions<TData> = {}): Partial<ColumnDef<TData, PublicKey>> {
|
|
349
362
|
return {
|
|
350
|
-
...props,
|
|
351
363
|
size: 94,
|
|
352
364
|
minSize: 94,
|
|
353
|
-
meta: { ...props.meta, cell: { ...props.meta?.cell, classNames: ['font-mono', textPadding, classNames] } },
|
|
354
365
|
header: (cell) => label ?? cell.header.id,
|
|
355
366
|
cell: (cell) => {
|
|
356
367
|
const value = cell.getValue();
|
|
@@ -383,6 +394,8 @@ export class ColumnBuilder<TData extends RowData> {
|
|
|
383
394
|
</Tooltip.Provider>
|
|
384
395
|
);
|
|
385
396
|
},
|
|
397
|
+
...props,
|
|
398
|
+
meta: { ...props.meta, cell: { ...props.meta?.cell, classNames: ['font-mono', textPadding, classNames] } },
|
|
386
399
|
};
|
|
387
400
|
}
|
|
388
401
|
|
|
@@ -423,12 +436,12 @@ export class ColumnBuilder<TData extends RowData> {
|
|
|
423
436
|
ColumnDef<TData, boolean>
|
|
424
437
|
> {
|
|
425
438
|
return {
|
|
426
|
-
...props,
|
|
427
439
|
size: 50,
|
|
428
440
|
minSize: 50,
|
|
429
|
-
meta: { ...props.meta, onUpdate, cell: { ...props.meta?.cell, classNames: [textPadding, classNames] } },
|
|
430
441
|
header: (column) => label ?? column.header.id,
|
|
431
442
|
cell: SwitchBuilderCell,
|
|
443
|
+
...props,
|
|
444
|
+
meta: { ...props.meta, onUpdate, cell: { ...props.meta?.cell, classNames: [textPadding, classNames] } },
|
|
432
445
|
};
|
|
433
446
|
}
|
|
434
447
|
|
|
@@ -439,7 +452,6 @@ export class ColumnBuilder<TData extends RowData> {
|
|
|
439
452
|
const IconOn = on?.Icon ?? Check;
|
|
440
453
|
const IconOff = off?.Icon ?? X;
|
|
441
454
|
return {
|
|
442
|
-
...props,
|
|
443
455
|
size: size ?? 32,
|
|
444
456
|
header: (column) => <div className={'justify-center'}>{label ?? column.header.id}</div>,
|
|
445
457
|
cell: (cell) => {
|
|
@@ -452,6 +464,7 @@ export class ColumnBuilder<TData extends RowData> {
|
|
|
452
464
|
return null;
|
|
453
465
|
}
|
|
454
466
|
},
|
|
467
|
+
...props,
|
|
455
468
|
};
|
|
456
469
|
}
|
|
457
470
|
}
|
package/src/schema.ts
CHANGED
|
@@ -10,14 +10,6 @@ export type TableDef = {
|
|
|
10
10
|
|
|
11
11
|
export type ColumnType = 'number' | 'boolean' | 'date' | 'string' | 'ref';
|
|
12
12
|
|
|
13
|
-
export const columnTypes = new Map<ColumnType, string>([
|
|
14
|
-
['string', 'Text'],
|
|
15
|
-
['boolean', 'Checkbox'],
|
|
16
|
-
['number', 'Number'],
|
|
17
|
-
['date', 'Date'],
|
|
18
|
-
['ref', 'Reference'],
|
|
19
|
-
]);
|
|
20
|
-
|
|
21
13
|
export type ColumnProps = {
|
|
22
14
|
id: string;
|
|
23
15
|
prop: string;
|
package/src/theme.ts
CHANGED
|
@@ -43,8 +43,8 @@ export const groupTh: ComponentFunction<TableStyleProps> = (_props, ...etc) =>
|
|
|
43
43
|
|
|
44
44
|
export type TheadStyleProps = Partial<TableFlags>;
|
|
45
45
|
|
|
46
|
-
export const theadRoot: ComponentFunction<TheadStyleProps> = ({ header }, ...etc) =>
|
|
47
|
-
mx(header ? 'sticky block-start-[--sticky-top] z-[1]' : 'collapse', header && fixedSurface, ...etc);
|
|
46
|
+
export const theadRoot: ComponentFunction<TheadStyleProps> = ({ header, stickyHeader }, ...etc) =>
|
|
47
|
+
mx(header ? stickyHeader && 'sticky block-start-[--sticky-top] z-[1]' : 'collapse', header && fixedSurface, ...etc);
|
|
48
48
|
|
|
49
49
|
export const theadTr: ComponentFunction<TheadStyleProps> = (_props, ...etc) => mx('group', ...etc);
|
|
50
50
|
|
package/src/translations.ts
CHANGED
|
@@ -9,6 +9,17 @@ export default [
|
|
|
9
9
|
'en-US': {
|
|
10
10
|
[translationKey]: {
|
|
11
11
|
'no selection label': ' ',
|
|
12
|
+
'number column type label': 'Number',
|
|
13
|
+
'boolean column type label': 'Toggle',
|
|
14
|
+
'date column type label': 'Date',
|
|
15
|
+
'string column type label': 'Text',
|
|
16
|
+
'ref column type label': 'Reference',
|
|
17
|
+
'digits label': 'Decimal places',
|
|
18
|
+
'save label': 'Save',
|
|
19
|
+
'delete label': 'Delete',
|
|
20
|
+
'column label label': 'Label',
|
|
21
|
+
'property key label': 'Property',
|
|
22
|
+
'column type label': 'Type',
|
|
12
23
|
},
|
|
13
24
|
},
|
|
14
25
|
},
|