@dxos/react-ui-table 0.3.9-main.3a0f16f → 0.3.9-main.3fcc0fa

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACzD,cAAc,SAAS,CAAC"}
@@ -4,7 +4,6 @@ export type TableDef = {
4
4
  columns: ColumnProps[];
5
5
  };
6
6
  export type ColumnType = 'number' | 'boolean' | 'date' | 'string' | 'ref';
7
- export declare const columnTypes: Map<ColumnType, string>;
8
7
  export type ColumnProps = {
9
8
  id: string;
10
9
  prop: string;
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/schema.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,QAAQ,GAAG;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE1E,eAAO,MAAM,WAAW,yBAMtB,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IAGf,MAAM,CAAC,EAAE,MAAM,CAAC;IAGhB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/schema.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,QAAQ,GAAG;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE1E,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IAGf,MAAM,CAAC,EAAE,MAAM,CAAC;IAGhB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../../src/theme.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAWzD,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAEvE,eAAO,MAAM,UAAU,wCAAwC,CAAC;AAChE,eAAO,MAAM,WAAW,yCAAyC,CAAC;AAClE,eAAO,MAAM,YAAY,gBAAgB,CAAC;AAC1C,eAAO,MAAM,WAAW,kBAAkB,CAAC;AAC3C,eAAO,MAAM,WAAW,kBAAkB,CAAC;AAE3C,eAAO,MAAM,iBAAiB,sFAAsF,CAAC;AAMrH,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC;AAE9D,eAAO,MAAM,SAAS,EAAE,iBAAiB,CAAC,eAAe,CACN,CAAC;AAEpD,eAAO,MAAM,OAAO,EAAE,iBAAiB,CAAC,eAAe,CACH,CAAC;AAMrD,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAElD,eAAO,MAAM,SAAS,EAAE,iBAAiB,CAAC,eAAe,CAC8B,CAAC;AAExF,eAAO,MAAM,OAAO,EAAE,iBAAiB,CAAC,eAAe,CAA2C,CAAC;AAEnG,eAAO,MAAM,OAAO,EAAE,iBAAiB,CAAC,eAAe,CAOpD,CAAC;AAEJ,eAAO,MAAM,eAAe,EAAE,iBAAiB,CAAC,eAAe,CAI5D,CAAC;AAEJ,eAAO,MAAM,gBAAgB,EAAE,iBAAiB,CAAC,eAAe,CACQ,CAAC;AAMzE,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC;AAC9D,eAAO,MAAM,SAAS,EAAE,iBAAiB,CAAC,eAAe,CAAkC,CAAC;AAE5F,MAAM,MAAM,iBAAiB,GAAG,OAAO,CAAC;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AACpE,eAAO,MAAM,OAAO,EAAE,iBAAiB,CAAC,iBAAiB,CAOtD,CAAC;AAMJ,eAAO,MAAM,MAAM,EAAE,iBAAiB,CAAC,eAAe,CACwD,CAAC;AAC/G,eAAO,MAAM,SAAS,EAAE,iBAAiB,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,CAAkC,CAAC;AAMlG,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC;AAE9D,eAAO,MAAM,SAAS,EAAE,iBAAiB,CAAC,eAAe,CACH,CAAC;AAEvD,eAAO,MAAM,OAAO,EAAE,iBAAiB,CAAC,eAAe,CAAkC,CAAC;AAE1F,eAAO,MAAM,OAAO,EAAE,iBAAiB,CAAC,eAAe,CACQ,CAAC"}
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../../src/theme.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAWzD,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAEvE,eAAO,MAAM,UAAU,wCAAwC,CAAC;AAChE,eAAO,MAAM,WAAW,yCAAyC,CAAC;AAClE,eAAO,MAAM,YAAY,gBAAgB,CAAC;AAC1C,eAAO,MAAM,WAAW,kBAAkB,CAAC;AAC3C,eAAO,MAAM,WAAW,kBAAkB,CAAC;AAE3C,eAAO,MAAM,iBAAiB,sFAAsF,CAAC;AAMrH,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC;AAE9D,eAAO,MAAM,SAAS,EAAE,iBAAiB,CAAC,eAAe,CACN,CAAC;AAEpD,eAAO,MAAM,OAAO,EAAE,iBAAiB,CAAC,eAAe,CACH,CAAC;AAMrD,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAElD,eAAO,MAAM,SAAS,EAAE,iBAAiB,CAAC,eAAe,CAC4D,CAAC;AAEtH,eAAO,MAAM,OAAO,EAAE,iBAAiB,CAAC,eAAe,CAA2C,CAAC;AAEnG,eAAO,MAAM,OAAO,EAAE,iBAAiB,CAAC,eAAe,CAOpD,CAAC;AAEJ,eAAO,MAAM,eAAe,EAAE,iBAAiB,CAAC,eAAe,CAI5D,CAAC;AAEJ,eAAO,MAAM,gBAAgB,EAAE,iBAAiB,CAAC,eAAe,CACQ,CAAC;AAMzE,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC;AAC9D,eAAO,MAAM,SAAS,EAAE,iBAAiB,CAAC,eAAe,CAAkC,CAAC;AAE5F,MAAM,MAAM,iBAAiB,GAAG,OAAO,CAAC;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AACpE,eAAO,MAAM,OAAO,EAAE,iBAAiB,CAAC,iBAAiB,CAOtD,CAAC;AAMJ,eAAO,MAAM,MAAM,EAAE,iBAAiB,CAAC,eAAe,CACwD,CAAC;AAC/G,eAAO,MAAM,SAAS,EAAE,iBAAiB,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,CAAkC,CAAC;AAMlG,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC;AAE9D,eAAO,MAAM,SAAS,EAAE,iBAAiB,CAAC,eAAe,CACH,CAAC;AAEvD,eAAO,MAAM,OAAO,EAAE,iBAAiB,CAAC,eAAe,CAAkC,CAAC;AAE1F,eAAO,MAAM,OAAO,EAAE,iBAAiB,CAAC,eAAe,CACQ,CAAC"}
@@ -3,6 +3,17 @@ declare const _default: {
3
3
  'en-US': {
4
4
  table: {
5
5
  'no selection label': string;
6
+ 'number column type label': string;
7
+ 'boolean column type label': string;
8
+ 'date column type label': string;
9
+ 'string column type label': string;
10
+ 'ref column type label': string;
11
+ 'digits label': string;
12
+ 'save label': string;
13
+ 'delete label': string;
14
+ 'column label label': string;
15
+ 'property key label': string;
16
+ 'column type label': string;
6
17
  };
7
18
  };
8
19
  }[];
@@ -1 +1 @@
1
- {"version":3,"file":"translations.d.ts","sourceRoot":"","sources":["../../../src/translations.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc,UAAU,CAAC;;;;;;;;AAEtC,wBAQE"}
1
+ {"version":3,"file":"translations.d.ts","sourceRoot":"","sources":["../../../src/translations.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc,UAAU,CAAC;;;;;;;;;;;;;;;;;;;AAEtC,wBAmBE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-table",
3
- "version": "0.3.9-main.3a0f16f",
3
+ "version": "0.3.9-main.3fcc0fa",
4
4
  "description": "A table component.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -22,14 +22,14 @@
22
22
  "@tanstack/react-table": "^8.9.3",
23
23
  "date-fns": "^2.29.3",
24
24
  "lodash.defaultsdeep": "^4.6.1",
25
- "@dxos/async": "0.3.9-main.3a0f16f",
26
- "@dxos/invariant": "0.3.9-main.3a0f16f",
27
- "@dxos/keys": "0.3.9-main.3a0f16f",
28
- "@dxos/react-ui": "0.3.9-main.3a0f16f",
29
- "@dxos/react-ui-theme": "0.3.9-main.3a0f16f",
30
- "@dxos/react-ui-searchlist": "0.3.9-main.3a0f16f",
31
- "@dxos/react-ui-types": "0.3.9-main.3a0f16f",
32
- "@dxos/util": "0.3.9-main.3a0f16f"
25
+ "@dxos/async": "0.3.9-main.3fcc0fa",
26
+ "@dxos/keys": "0.3.9-main.3fcc0fa",
27
+ "@dxos/react-ui": "0.3.9-main.3fcc0fa",
28
+ "@dxos/react-ui-searchlist": "0.3.9-main.3fcc0fa",
29
+ "@dxos/react-ui-theme": "0.3.9-main.3fcc0fa",
30
+ "@dxos/react-ui-types": "0.3.9-main.3fcc0fa",
31
+ "@dxos/util": "0.3.9-main.3fcc0fa",
32
+ "@dxos/invariant": "0.3.9-main.3fcc0fa"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@faker-js/faker": "^8.0.2",
@@ -43,7 +43,7 @@
43
43
  "react-dom": "^18.2.0",
44
44
  "vite": "^4.3.9",
45
45
  "vite-plugin-turbosnap": "^1.0.2",
46
- "@dxos/react-ui-theme": "0.3.9-main.3a0f16f"
46
+ "@dxos/react-ui-theme": "0.3.9-main.3fcc0fa"
47
47
  },
48
48
  "peerDependencies": {
49
49
  "@phosphor-icons/react": "^2.0.5",
@@ -6,11 +6,12 @@ import { Check, CaretDown, Trash, X } from '@phosphor-icons/react';
6
6
  import { type HeaderContext, type RowData } from '@tanstack/react-table';
7
7
  import React, { type FC, type PropsWithChildren, useRef, useState } from 'react';
8
8
 
9
- import { Button, DensityProvider, Input, Popover, Select, Separator } from '@dxos/react-ui';
9
+ import { Button, DensityProvider, Input, Popover, Select, Separator, useTranslation } from '@dxos/react-ui';
10
10
  import { getSize, mx } from '@dxos/react-ui-theme';
11
11
  import { safeParseInt } from '@dxos/util';
12
12
 
13
- import { type TableDef, type ColumnProps, columnTypes } from '../schema';
13
+ import { type TableDef, type ColumnProps, type ColumnType } from '../schema';
14
+ import { translationKey } from '../translations';
14
15
 
15
16
  export type ColumnMenuProps<TData extends RowData, TValue> = {
16
17
  context: HeaderContext<TData, TValue>;
@@ -23,13 +24,11 @@ export type ColumnMenuProps<TData extends RowData, TValue> = {
23
24
 
24
25
  export const ColumnMenu = <TData extends RowData, TValue>({ column, ...props }: ColumnMenuProps<TData, TValue>) => {
25
26
  const title = column.label?.length ? column.label : column.id;
26
-
27
27
  return (
28
- <div className='flex grow items-center overflow-hidden'>
29
- <div className='truncate' title={title}>
28
+ <div className='flex items-center gap-2'>
29
+ <div className='flex-1 min-is-0 truncate' title={title}>
30
30
  {title}
31
31
  </div>
32
- <div className='grow' />
33
32
  <div className='flex shrink-0'>
34
33
  <ColumnPanel {...props} column={column} />
35
34
  </div>
@@ -59,6 +58,7 @@ export const ColumnPanel = <TData extends RowData, TValue>({
59
58
  const [label, setLabel] = useState(column.label);
60
59
  const [digits, setDigits] = useState(String(column.digits ?? '0'));
61
60
  const propRef = useRef<HTMLInputElement>(null);
61
+ const { t } = useTranslation(translationKey);
62
62
 
63
63
  const handleCancel = () => {
64
64
  setProp(column.id);
@@ -99,7 +99,7 @@ export const ColumnPanel = <TData extends RowData, TValue>({
99
99
  return (
100
100
  <Popover.Root open={open} onOpenChange={(nextOpen) => setOpen(nextOpen)}>
101
101
  <Popover.Trigger asChild>
102
- <Button variant='ghost' classNames='p-0'>
102
+ <Button variant='ghost' classNames='p-1'>
103
103
  <CaretDown className={getSize(4)} />
104
104
  </Button>
105
105
  </Popover.Trigger>
@@ -110,7 +110,7 @@ export const ColumnPanel = <TData extends RowData, TValue>({
110
110
  <div className='flex flex-col gap-1'>
111
111
  <Section>
112
112
  <Input.Root>
113
- <Input.Label classNames='mbe-1'>Label</Input.Label>
113
+ <Input.Label classNames='mbe-1'>{t('column label label')}</Input.Label>
114
114
  <Input.TextInput
115
115
  placeholder='Column label'
116
116
  value={label ?? ''}
@@ -119,7 +119,7 @@ export const ColumnPanel = <TData extends RowData, TValue>({
119
119
  />
120
120
  </Input.Root>
121
121
  <Input.Root>
122
- <Input.Label classNames='mbe-1 mbs-3'>Property</Input.Label>
122
+ <Input.Label classNames='mbe-1 mbs-3'>{t('property key label')}</Input.Label>
123
123
  <Input.TextInput
124
124
  ref={propRef}
125
125
  placeholder='Property key'
@@ -129,15 +129,15 @@ export const ColumnPanel = <TData extends RowData, TValue>({
129
129
  />
130
130
  </Input.Root>
131
131
  <Input.Root>
132
- <Input.Label classNames='mbe-1 mbs-3'>Type</Input.Label>
132
+ <Input.Label classNames='mbe-1 mbs-3'>{t('column type label')}</Input.Label>
133
133
  <Select.Root value={type} onValueChange={setType}>
134
134
  <Select.TriggerButton placeholder='Type' classNames='is-full' />
135
135
  <Select.Portal>
136
136
  <Select.Content>
137
137
  <Select.Viewport>
138
- {Array.from(columnTypes.entries()).map(([type, label]) => (
138
+ {(['number', 'boolean', 'string', 'ref'] as ColumnType[]).map((type) => (
139
139
  <Select.Option key={type} value={type}>
140
- {label}
140
+ {t(`${type} column type label`)}
141
141
  </Select.Option>
142
142
  ))}
143
143
  </Select.Viewport>
@@ -151,7 +151,7 @@ export const ColumnPanel = <TData extends RowData, TValue>({
151
151
  <>
152
152
  <Section>
153
153
  <Input.Root>
154
- <Input.Label classNames='mbe-1 mbs-3'>Decimal places</Input.Label>
154
+ <Input.Label classNames='mbe-1 mbs-3'>{t('digits label')}</Input.Label>
155
155
  {/* TODO(burdon): Constrain input to numbers. */}
156
156
  <Input.TextInput value={digits ?? ''} onChange={(event) => setDigits(event.target.value)} />
157
157
  </Input.Root>
@@ -213,17 +213,17 @@ export const ColumnPanel = <TData extends RowData, TValue>({
213
213
  <Section className='space-b-1.5'>
214
214
  {/* TODO(burdon): Style as DropdownMenuItem. */}
215
215
  <Button variant='primary' classNames='is-full flex gap-2' onClick={handleSave}>
216
- <span>Save</span>
216
+ <span>{t('save label')}</span>
217
217
  <div className='grow' />
218
218
  <Check className={getSize(5)} />
219
219
  </Button>
220
220
  <Button classNames='is-full flex gap-2' onClick={handleCancel}>
221
- <span>Cancel</span>
221
+ <span>{t('cancel label', { ns: 'os' })}</span>
222
222
  <div className='grow' />
223
223
  <X className={getSize(5)} />
224
224
  </Button>
225
225
  <Button classNames='is-full flex gap-2' onClick={() => onDelete?.(column.id)}>
226
- <span>Delete</span>
226
+ <span>{t('delete label')}</span>
227
227
  <div className='grow' />
228
228
  <Trash className={getSize(5)} />
229
229
  </Button>
@@ -17,6 +17,7 @@ export type TableFlags = Partial<{
17
17
  fullWidth: boolean;
18
18
  debug: boolean;
19
19
  expand: boolean;
20
+ stickyHeader: boolean;
20
21
  rowsSelectable: boolean | 'multi';
21
22
  }>;
22
23
 
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, cell, digits } = cellContext.column.columnDef.meta as ColumnMeta<TData, string>;
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 value = text?.trim().length ? Number(text) : NaN;
149
- onUpdate?.(cellContext.row.original, cellContext.column.id, isNaN(value) ? undefined : value);
150
- setText(undefined);
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 handleCancel = () => {
154
- setText(undefined);
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
- <div
173
- className={mx('is-full text-end font-mono empty:after:content-[" "]', textPadding, cell?.classNames)}
174
- onClick={handleEdit}
175
- >
176
- {value?.toLocaleString(undefined, {
177
- minimumFractionDigits: digits ?? 0,
178
- maximumFractionDigits: digits ?? 2,
179
- })}
180
- </div>
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/index.ts CHANGED
@@ -6,4 +6,5 @@ export * from './components';
6
6
  export * from './helpers';
7
7
  export * from './schema';
8
8
  export * from './theme';
9
+ export { default as translations } from './translations';
9
10
  export * from './types';
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-0 z-10' : '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
 
@@ -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
  },