@dxos/react-ui-table 0.6.7-staging.e9eb1ed → 0.6.8-main.046e6cf

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.
Files changed (60) hide show
  1. package/dist/lib/browser/index.mjs +189 -213
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/components/Cell/Cell.d.ts +2 -2
  5. package/dist/types/src/components/Cell/Cell.d.ts.map +1 -1
  6. package/dist/types/src/components/Cell/CellCombobox.d.ts +3 -3
  7. package/dist/types/src/components/Cell/CellCombobox.d.ts.map +1 -1
  8. package/dist/types/src/components/ColumnMenu/ColumnMenu.d.ts +3 -3
  9. package/dist/types/src/components/ColumnMenu/ColumnMenu.d.ts.map +1 -1
  10. package/dist/types/src/components/ColumnMenu/ColumnMenu.stories.d.ts +3 -3
  11. package/dist/types/src/components/ColumnMenu/ColumnMenu.stories.d.ts.map +1 -1
  12. package/dist/types/src/components/ColumnMenu/ColumnSettingsForm.d.ts +2 -2
  13. package/dist/types/src/components/ColumnMenu/ColumnSettingsForm.d.ts.map +1 -1
  14. package/dist/types/src/components/ColumnMenu/ColumnSettingsForm.stories.d.ts +63 -0
  15. package/dist/types/src/components/ColumnMenu/ColumnSettingsForm.stories.d.ts.map +1 -0
  16. package/dist/types/src/components/Table/Table.d.ts +6 -5
  17. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  18. package/dist/types/src/components/Table/Table.stories.d.ts +9 -9
  19. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  20. package/dist/types/src/components/Table/TableBody.d.ts +2 -2
  21. package/dist/types/src/components/Table/TableBody.d.ts.map +1 -1
  22. package/dist/types/src/components/Table/TableContext.d.ts +2 -2
  23. package/dist/types/src/components/Table/TableContext.d.ts.map +1 -1
  24. package/dist/types/src/components/Table/TableHead.d.ts +2 -2
  25. package/dist/types/src/components/Table/TableHead.d.ts.map +1 -1
  26. package/dist/types/src/components/Table/TableRootContext.d.ts +0 -1
  27. package/dist/types/src/components/Table/TableRootContext.d.ts.map +1 -1
  28. package/dist/types/src/components/Table/props.d.ts +0 -1
  29. package/dist/types/src/components/Table/props.d.ts.map +1 -1
  30. package/dist/types/src/helpers.d.ts +1 -1
  31. package/dist/types/src/helpers.d.ts.map +1 -1
  32. package/dist/types/src/hooks/useColumnSizing.d.ts +0 -1
  33. package/dist/types/src/hooks/useColumnSizing.d.ts.map +1 -1
  34. package/dist/types/src/hooks/useColumnSorting.d.ts.map +1 -1
  35. package/dist/types/src/hooks/usePinLastRow.d.ts.map +1 -1
  36. package/dist/types/src/hooks/useRowSelection.d.ts +1 -1
  37. package/dist/types/src/hooks/useRowSelection.d.ts.map +1 -1
  38. package/dist/types/src/schema/SchemaToTable.stories.d.ts +2 -1
  39. package/dist/types/src/schema/SchemaToTable.stories.d.ts.map +1 -1
  40. package/dist/types/src/schema/schema.d.ts.map +1 -1
  41. package/dist/types/src/schema/schemaToColumns.d.ts.map +1 -1
  42. package/dist/types/src/theme.d.ts +6 -10
  43. package/dist/types/src/theme.d.ts.map +1 -1
  44. package/package.json +17 -17
  45. package/src/components/Cell/Cell.tsx +46 -4
  46. package/src/components/ColumnMenu/ColumnMenu.tsx +6 -8
  47. package/src/components/ColumnMenu/ColumnSettingsForm.stories.tsx +104 -0
  48. package/src/components/ColumnMenu/ColumnSettingsForm.tsx +60 -73
  49. package/src/components/Table/Table.stories.tsx +2 -2
  50. package/src/components/Table/Table.tsx +12 -7
  51. package/src/components/Table/TableBody.tsx +2 -1
  52. package/src/components/Table/TableContext.tsx +1 -1
  53. package/src/components/Table/TableHead.tsx +3 -4
  54. package/src/components/Table/props.ts +0 -1
  55. package/src/helpers.tsx +2 -0
  56. package/src/hooks/usePinLastRow.ts +1 -18
  57. package/src/theme.ts +28 -55
  58. package/dist/types/src/components/Table/TableFooter.d.ts +0 -9
  59. package/dist/types/src/components/Table/TableFooter.d.ts.map +0 -1
  60. package/src/components/Table/TableFooter.tsx +0 -45
@@ -1,4 +1,5 @@
1
1
  import '@dxosTheme';
2
+ import React from 'react';
2
3
  declare const _default: {
3
4
  title: string;
4
5
  args: {};
@@ -6,6 +7,6 @@ declare const _default: {
6
7
  };
7
8
  export default _default;
8
9
  export declare const SchemaTable: {
9
- render: () => JSX.Element;
10
+ render: () => React.JSX.Element;
10
11
  };
11
12
  //# sourceMappingURL=SchemaToTable.stories.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SchemaToTable.stories.d.ts","sourceRoot":"","sources":["../../../../src/schema/SchemaToTable.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,YAAY,CAAC;;;;;;AAapB,wBAWE;AAuBF,eAAO,MAAM,WAAW;;CAmBvB,CAAC"}
1
+ {"version":3,"file":"SchemaToTable.stories.d.ts","sourceRoot":"","sources":["../../../../src/schema/SchemaToTable.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,YAAY,CAAC;AAIpB,OAAO,KAAK,MAAM,OAAO,CAAC;;;;;;AAS1B,wBAWE;AAuBF,eAAO,MAAM,WAAW;;CAmBvB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/schema/schema.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,CAAC,MAAM,uBAAuB,CAAC;AAEhD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAG1C,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG,SAAS,CAAC;AAG1D,eAAO,MAAM,wBAAwB,WAAY,EAAE,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,KAAG,CAAC,MAAM,EAAE,oBAAoB,CAAC,EAwBnG,CAAC"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/schema/schema.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,CAAC,MAAM,uBAAuB,CAAC;AAEhD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAG1C,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG,SAAS,CAAC;AAG1D,eAAO,MAAM,wBAAwB,WAAY,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,KAAG,CAAC,MAAM,EAAE,oBAAoB,CAAC,EAwBnG,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"schemaToColumns.d.ts","sourceRoot":"","sources":["../../../../src/schema/schemaToColumns.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,mBAAmB,CAAC;AAK3C,eAAO,MAAM,kBAAkB,cAAe,EAAE,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,KAAG,UAAU,CAAC,EAAE,GAAG,CAAC,EAoCjF,CAAC"}
1
+ {"version":3,"file":"schemaToColumns.d.ts","sourceRoot":"","sources":["../../../../src/schema/schemaToColumns.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,mBAAmB,CAAC;AAK3C,eAAO,MAAM,kBAAkB,GAAI,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,KAAG,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,EAoCjF,CAAC"}
@@ -1,12 +1,11 @@
1
- import { type CellContext } from '@tanstack/react-table';
2
1
  import { type ComponentFunction } from '@dxos/react-ui-types';
3
2
  import { type TableContextValue, type TableFlags } from './components';
4
3
  export declare const currentRow = "!bg-neutral-75 !dark:bg-neutral-850";
5
4
  export declare const selectedRow = "!bg-primary-100 dark:!bg-primary-700";
6
5
  export declare const flushPadding = "pli-0 plb-0";
7
- export declare const textPadding = "pli-2 plb-0.5";
8
- export declare const headPadding = "pli-2 plb-1";
9
- export declare const gridCellFocusRing = "focus-within:outline outline-2 outline-primary-500 dark:outline-primary-400";
6
+ export declare const textPadding = "pli-2 plb-0";
7
+ export declare const headPadding = "pli-2 plb-0";
8
+ export declare const gridCellFocusRing = "relative focus-within:outline focus-within:outline-1 outline-primary-500 outline-offset-[-1px]";
10
9
  export type TableStyleProps = Partial<TableContextValue<any>>;
11
10
  export declare const tableRoot: ComponentFunction<TableStyleProps>;
12
11
  export declare const groupTh: ComponentFunction<TableStyleProps>;
@@ -24,10 +23,7 @@ export type TbodyTrStyleProps = Partial<{
24
23
  isPinned?: boolean;
25
24
  }>;
26
25
  export declare const tbodyTr: ComponentFunction<TbodyTrStyleProps>;
27
- export declare const tdRoot: ComponentFunction<TbodyStyleProps>;
28
- export declare const tdContent: ComponentFunction<CellContext<any, any>>;
29
- export type TfootStyleProps = Partial<TableContextValue<any>>;
30
- export declare const tfootRoot: ComponentFunction<TfootStyleProps>;
31
- export declare const tfootTr: ComponentFunction<TfootStyleProps>;
32
- export declare const tfootTh: ComponentFunction<TfootStyleProps>;
26
+ export declare const tdRoot: ComponentFunction<TbodyStyleProps & {
27
+ pinned?: boolean;
28
+ }>;
33
29
  //# sourceMappingURL=theme.d.ts.map
@@ -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;AAGzD,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,gBAAgB,CAAC;AAEzC,eAAO,MAAM,iBAAiB,gFAAgF,CAAC;AAM/G,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,CAMtD,CAAC;AAEJ,eAAO,MAAM,OAAO,EAAE,iBAAiB,CAAC,eAAe,CAA2C,CAAC;AAEnG,eAAO,MAAM,OAAO,EAAE,iBAAiB,CAAC,eAAe,CAQpD,CAAC;AAEJ,eAAO,MAAM,eAAe,EAAE,iBAAiB,CAAC;IAAE,UAAU,EAAE,OAAO,CAAA;CAAE,CAUtE,CAAC;AAMF,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,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AACxF,eAAO,MAAM,OAAO,EAAE,iBAAiB,CAAC,iBAAiB,CAQtD,CAAC;AAMJ,eAAO,MAAM,MAAM,EAAE,iBAAiB,CAAC,eAAe,CACwD,CAAC;AAE/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,CACJ,CAAC;AAEtD,eAAO,MAAM,OAAO,EAAE,iBAAiB,CAAC,eAAe,CAAkC,CAAC;AAE1F,eAAO,MAAM,OAAO,EAAE,iBAAiB,CAAC,eAAe,CACO,CAAC"}
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../../src/theme.ts"],"names":[],"mappings":"AAKA,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,gBAAgB,CAAC;AACzC,eAAO,MAAM,WAAW,gBAAgB,CAAC;AAEzC,eAAO,MAAM,iBAAiB,mGACoE,CAAC;AAInG,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC;AAE9D,eAAO,MAAM,SAAS,EAAE,iBAAiB,CAAC,eAAe,CACoC,CAAC;AAE9F,eAAO,MAAM,OAAO,EAAE,iBAAiB,CAAC,eAAe,CACH,CAAC;AAErD,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAElD,eAAO,MAAM,SAAS,EAAE,iBAAiB,CAAC,eAAe,CACqD,CAAC;AAE/G,eAAO,MAAM,OAAO,EAAE,iBAAiB,CAAC,eAAe,CAA2C,CAAC;AAEnG,eAAO,MAAM,OAAO,EAAE,iBAAiB,CAAC,eAAe,CAQpD,CAAC;AAEJ,eAAO,MAAM,eAAe,EAAE,iBAAiB,CAAC;IAAE,UAAU,EAAE,OAAO,CAAA;CAAE,CAUtE,CAAC;AAEF,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,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AACxF,eAAO,MAAM,OAAO,EAAE,iBAAiB,CAAC,iBAAiB,CAStD,CAAC;AAEJ,eAAO,MAAM,MAAM,EAAE,iBAAiB,CAAC,eAAe,GAAG;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,CAS1E,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-table",
3
- "version": "0.6.7-staging.e9eb1ed",
3
+ "version": "0.6.8-main.046e6cf",
4
4
  "description": "A table component.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -31,29 +31,29 @@
31
31
  "@tanstack/react-virtual": "^3.9.0",
32
32
  "date-fns": "^3.3.1",
33
33
  "effect": "^3.2.7",
34
- "@dxos/invariant": "0.6.7-staging.e9eb1ed",
35
- "@dxos/async": "0.6.7-staging.e9eb1ed",
36
- "@dxos/keys": "0.6.7-staging.e9eb1ed",
37
- "@dxos/log": "0.6.7-staging.e9eb1ed",
38
- "@dxos/react-ui": "0.6.7-staging.e9eb1ed",
39
- "@dxos/react-ui-searchlist": "0.6.7-staging.e9eb1ed",
40
- "@dxos/react-ui-theme": "0.6.7-staging.e9eb1ed",
41
- "@dxos/react-ui-types": "0.6.7-staging.e9eb1ed",
42
- "@dxos/util": "0.6.7-staging.e9eb1ed"
34
+ "@dxos/async": "0.6.8-main.046e6cf",
35
+ "@dxos/invariant": "0.6.8-main.046e6cf",
36
+ "@dxos/keys": "0.6.8-main.046e6cf",
37
+ "@dxos/react-ui": "0.6.8-main.046e6cf",
38
+ "@dxos/log": "0.6.8-main.046e6cf",
39
+ "@dxos/react-ui-searchlist": "0.6.8-main.046e6cf",
40
+ "@dxos/react-ui-theme": "0.6.8-main.046e6cf",
41
+ "@dxos/react-ui-types": "0.6.8-main.046e6cf",
42
+ "@dxos/util": "0.6.8-main.046e6cf"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@phosphor-icons/react": "^2.1.5",
46
- "@types/react": "^18.0.21",
47
- "@types/react-dom": "^18.0.6",
46
+ "@types/react": "~18.2.0",
47
+ "@types/react-dom": "~18.2.0",
48
48
  "fast-check": "^3.19.0",
49
49
  "react": "~18.2.0",
50
50
  "react-dom": "~18.2.0",
51
51
  "vite": "^5.3.4",
52
- "@dxos/echo-schema": "0.6.7-staging.e9eb1ed",
53
- "@dxos/random": "0.6.7-staging.e9eb1ed",
54
- "@dxos/react-ui-theme": "0.6.7-staging.e9eb1ed",
55
- "@dxos/echo-signals": "0.6.7-staging.e9eb1ed",
56
- "@dxos/storybook-utils": "0.6.7-staging.e9eb1ed"
52
+ "@dxos/echo-signals": "0.6.8-main.046e6cf",
53
+ "@dxos/echo-schema": "0.6.8-main.046e6cf",
54
+ "@dxos/react-ui-theme": "0.6.8-main.046e6cf",
55
+ "@dxos/random": "0.6.8-main.046e6cf",
56
+ "@dxos/storybook-utils": "0.6.8-main.046e6cf"
57
57
  },
58
58
  "peerDependencies": {
59
59
  "@phosphor-icons/react": "^2.1.5",
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { useFocusableGroup } from '@fluentui/react-tabster';
6
6
  import { type Cell as CellType, type RowData } from '@tanstack/react-table';
7
- import React, { type PropsWithChildren } from 'react';
7
+ import React, { useCallback, type PropsWithChildren, type KeyboardEvent } from 'react';
8
8
 
9
9
  import { tdRoot } from '../../theme';
10
10
  import { useTableContext } from '../Table/TableContext';
@@ -19,10 +19,48 @@ const FocusableCell = <TData extends RowData, TValue>({ cell, children }: CellPr
19
19
  const tableContext = useTableContext();
20
20
  const domAttributes = useFocusableGroup({ tabBehavior: 'limited' });
21
21
 
22
- const className = tdRoot(tableContext, cell.column.columnDef.meta?.cell?.classNames);
22
+ const pinned = tableContext.table.getState().rowPinning?.bottom?.includes(cell.row.id);
23
+ const className = tdRoot({ ...tableContext, pinned }, cell.column.columnDef.meta?.cell?.classNames);
24
+
25
+ const handleKeyDown = useCallback(({ key, currentTarget }: KeyboardEvent<HTMLTableCellElement>) => {
26
+ if (key !== 'Enter') {
27
+ return;
28
+ }
29
+
30
+ const currentRow = currentTarget.closest('tr');
31
+ if (!currentRow) {
32
+ return;
33
+ }
34
+
35
+ const cellIndex = Array.from(currentRow.cells || []).indexOf(currentTarget);
36
+
37
+ const nextSibling = currentRow.nextElementSibling;
38
+ if (!(nextSibling instanceof HTMLTableRowElement)) {
39
+ let depth = 0;
40
+ // Poll for the new 'add row' row to be added to the DOM.
41
+ const pollForNewRow = () => {
42
+ const newNextSibling = currentRow.nextElementSibling;
43
+ if (newNextSibling instanceof HTMLTableRowElement) {
44
+ const nextCell = newNextSibling.cells[cellIndex];
45
+ // Find the input within next cell and focus it.
46
+ const input = nextCell.querySelector('input');
47
+ if (input) {
48
+ input.focus();
49
+ }
50
+
51
+ // TODO(Zan): Use the same utility that sheets uses.
52
+ // Scroll current row into view after it's been hidden behind the sticky add-row row.
53
+ currentRow.scrollIntoView({ behavior: 'smooth', block: 'center' });
54
+ } else if (depth++ < 100) {
55
+ requestAnimationFrame(pollForNewRow);
56
+ }
57
+ };
58
+ requestAnimationFrame(pollForNewRow);
59
+ }
60
+ }, []);
23
61
 
24
62
  return (
25
- <td tabIndex={0} {...domAttributes} className={className}>
63
+ <td data-testid='table.cell' tabIndex={0} {...domAttributes} className={className} onKeyDown={handleKeyDown}>
26
64
  {children}
27
65
  </td>
28
66
  );
@@ -30,7 +68,11 @@ const FocusableCell = <TData extends RowData, TValue>({ cell, children }: CellPr
30
68
 
31
69
  const StaticCell = <TData extends RowData, TValue>({ cell, children }: CellProps<TData, TValue>) => {
32
70
  const tableContext = useTableContext();
33
- return <td className={tdRoot(tableContext, cell.column.columnDef.meta?.cell?.classNames)}>{children}</td>;
71
+ return (
72
+ <td data-testid='table.cell' className={tdRoot(tableContext, cell.column.columnDef.meta?.cell?.classNames)}>
73
+ {children}
74
+ </td>
75
+ );
34
76
  };
35
77
 
36
78
  const Cell = <TData extends RowData, TValue>({ cell, children }: CellProps<TData, TValue>) => {
@@ -6,7 +6,7 @@ import { X, GearSix, CaretDown, ArrowDown, ArrowUp, ArrowRight, ArrowLeft } from
6
6
  import { type SortDirection, type HeaderContext, type RowData } from '@tanstack/react-table';
7
7
  import React, { useRef, useState, useCallback } from 'react';
8
8
 
9
- import { Button, DensityProvider, Popover, DropdownMenu } from '@dxos/react-ui';
9
+ import { Button, Popover, DropdownMenu } from '@dxos/react-ui';
10
10
  import { getSize, mx } from '@dxos/react-ui-theme';
11
11
 
12
12
  import { ColumnSettingsForm } from './ColumnSettingsForm';
@@ -59,7 +59,7 @@ export const ColumnMenu = <TData extends RowData, TValue>({
59
59
  );
60
60
 
61
61
  return (
62
- <div className='flex items-center justify-center justify-between'>
62
+ <div className='flex items-center justify-between'>
63
63
  <div className='flex items-center gap-1 truncate' title={title}>
64
64
  <SortIndicator direction={sortDirection} onClick={onToggleSort} />
65
65
  <div className='truncate'>{title}</div>
@@ -69,7 +69,7 @@ export const ColumnMenu = <TData extends RowData, TValue>({
69
69
  <Popover.Root open={isColumnSettingsOpen} onOpenChange={setIsColumnSettingsOpen} modal={false}>
70
70
  <DropdownMenu.Trigger ref={columnSettingsAnchorRef} asChild>
71
71
  <Popover.Anchor asChild>
72
- <Button variant='ghost'>
72
+ <Button variant='ghost' data-testid='table.column-menu'>
73
73
  <CaretDown className={getSize(4)} />
74
74
  </Button>
75
75
  </Popover.Anchor>
@@ -124,7 +124,7 @@ export const ColumnMenu = <TData extends RowData, TValue>({
124
124
  </>
125
125
  )}
126
126
 
127
- <DropdownMenu.Item onClick={onOpenColumnSettings}>
127
+ <DropdownMenu.Item onClick={onOpenColumnSettings} data-testid='table.open-column-settings'>
128
128
  <span className='grow'>Column settings</span>
129
129
  <span className='opacity-50'>
130
130
  <GearSix className={mx(getSize(4), 'rotate-90')} />
@@ -137,10 +137,8 @@ export const ColumnMenu = <TData extends RowData, TValue>({
137
137
 
138
138
  <Popover.Portal>
139
139
  <Popover.Content>
140
- <Popover.Viewport classNames='w-60'>
141
- <DensityProvider density='fine'>
142
- <ColumnSettingsForm {...props} column={column} onClose={() => setIsColumnSettingsOpen(false)} />
143
- </DensityProvider>
140
+ <Popover.Viewport>
141
+ <ColumnSettingsForm {...props} column={column} onClose={() => setIsColumnSettingsOpen(false)} />
144
142
  </Popover.Viewport>
145
143
  <Popover.Arrow />
146
144
  </Popover.Content>
@@ -0,0 +1,104 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import '@dxosTheme';
6
+ import React from 'react';
7
+
8
+ import { withTheme } from '@dxos/storybook-utils';
9
+
10
+ import { ColumnSettingsForm, type ColumnSettingsFormProps } from './ColumnSettingsForm';
11
+ import { type TableDef } from '../../schema';
12
+
13
+ export default {
14
+ title: 'react-ui-table/ColumnSettingsForm',
15
+ component: ColumnSettingsForm,
16
+ decorators: [withTheme],
17
+ };
18
+
19
+ const sampleTableDef: TableDef = {
20
+ id: '1',
21
+ name: 'Sample Table',
22
+ columns: [
23
+ { id: 'col-1', prop: 'name', type: 'string', label: 'Name' },
24
+ { id: 'col-2', prop: 'age', type: 'number', label: 'Age', digits: 0 },
25
+ ],
26
+ };
27
+
28
+ const sampleTablesToReference: TableDef[] = [
29
+ sampleTableDef,
30
+ {
31
+ id: '2',
32
+ name: 'Reference Table',
33
+ columns: [
34
+ { id: 'ref-col-1', prop: 'id', type: 'string', label: 'ID' },
35
+ { id: 'ref-col-2', prop: 'description', type: 'string', label: 'Description' },
36
+ ],
37
+ },
38
+ ];
39
+
40
+ const defaultArgs: ColumnSettingsFormProps = {
41
+ column: {
42
+ id: 'col-1',
43
+ prop: 'name',
44
+ type: 'string',
45
+ label: 'Name',
46
+ },
47
+ tableDef: sampleTableDef,
48
+ tablesToReference: sampleTablesToReference,
49
+ onUpdate: (id, column) => console.log('onUpdate', { id, column }),
50
+ onDelete: (id) => console.log('onDelete', id),
51
+ onClose: () => console.log('onClose'),
52
+ };
53
+
54
+ export const Default = {
55
+ args: defaultArgs,
56
+ };
57
+
58
+ export const NumberColumn = {
59
+ args: {
60
+ ...defaultArgs,
61
+ column: {
62
+ id: 'col-2',
63
+ prop: 'age',
64
+ type: 'number',
65
+ label: 'Age',
66
+ digits: 0,
67
+ },
68
+ },
69
+ };
70
+
71
+ export const ReferenceColumn = {
72
+ args: {
73
+ ...defaultArgs,
74
+ column: {
75
+ id: 'col-3',
76
+ prop: 'reference',
77
+ type: 'ref',
78
+ label: 'Reference',
79
+ refTable: '2',
80
+ refProp: 'ref-col-1',
81
+ },
82
+ },
83
+ };
84
+
85
+ export const NewColumn = {
86
+ args: {
87
+ ...defaultArgs,
88
+ column: {
89
+ id: 'new-col',
90
+ prop: '',
91
+ type: 'string',
92
+ label: '',
93
+ },
94
+ },
95
+ };
96
+
97
+ const Template = (args: ColumnSettingsFormProps) => (
98
+ <div className='p-4 max-is-64'>
99
+ <ColumnSettingsForm {...args} />
100
+ </div>
101
+ );
102
+
103
+ export const Interactive = Template.bind({});
104
+ (Interactive as any).args = defaultArgs;
@@ -1,22 +1,14 @@
1
1
  //
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
- import { Check, Trash, X } from '@phosphor-icons/react';
5
- import React, { type FC, type PropsWithChildren, useRef, useState } from 'react';
4
+ import React, { useRef, useState } from 'react';
6
5
 
7
- import { Button, Input, Select, Separator, useTranslation } from '@dxos/react-ui';
8
- import { getSize, mx } from '@dxos/react-ui-theme';
6
+ import { Button, Input, Select, useTranslation } from '@dxos/react-ui';
9
7
  import { safeParseInt } from '@dxos/util';
10
8
 
11
9
  import { type TableDef, type ColumnProps, type ColumnType } from '../../schema';
12
10
  import { translationKey } from '../../translations';
13
11
 
14
- const Section: FC<PropsWithChildren & { className?: string }> = ({ children, className }) => (
15
- <div role='none' className={mx('p-2', className)}>
16
- {children}
17
- </div>
18
- );
19
-
20
12
  export type ColumnSettingsFormProps = {
21
13
  column: ColumnProps;
22
14
  tableDef: TableDef;
@@ -46,10 +38,6 @@ export const ColumnSettingsForm = ({
46
38
  const propRef = useRef<HTMLInputElement>(null);
47
39
  const { t } = useTranslation(translationKey);
48
40
 
49
- const handleCancel = () => {
50
- onClose?.();
51
- };
52
-
53
41
  const handleSave = () => {
54
42
  const { prop, refTable, refProp, type, label, digits } = formState;
55
43
 
@@ -88,19 +76,22 @@ export const ColumnSettingsForm = ({
88
76
  };
89
77
 
90
78
  return (
91
- <div className='flex flex-col gap-1'>
92
- <Section>
79
+ <div className='p-1 space-y-2'>
80
+ <div className='space-y-1'>
93
81
  <Input.Root>
94
- <Input.Label classNames='mbe-1'>{t('column label label')}</Input.Label>
82
+ <Input.Label>{t('column label label')}</Input.Label>
95
83
  <Input.TextInput
96
84
  placeholder='Column label'
97
85
  value={formState.label ?? ''}
98
86
  onChange={(event) => setFormState((prevState) => ({ ...prevState, label: event.target.value }))}
99
87
  autoFocus
88
+ data-testid='table.column-settings.label'
100
89
  />
101
90
  </Input.Root>
91
+ </div>
92
+ <div className='space-y-1'>
102
93
  <Input.Root>
103
- <Input.Label classNames='mbe-1 mbs-3'>{t('property key label')}</Input.Label>
94
+ <Input.Label>{t('property key label')}</Input.Label>
104
95
  <Input.TextInput
105
96
  ref={propRef}
106
97
  placeholder='Property key'
@@ -110,13 +101,15 @@ export const ColumnSettingsForm = ({
110
101
  }
111
102
  />
112
103
  </Input.Root>
104
+ </div>
105
+ <div className='space-y-1'>
113
106
  <Input.Root>
114
- <Input.Label classNames='mbe-1 mbs-3'>{t('column type label')}</Input.Label>
107
+ <Input.Label>{t('column type label')}</Input.Label>
115
108
  <Select.Root
116
109
  value={formState.type}
117
110
  onValueChange={(value) => setFormState((prevState) => ({ ...prevState, type: value }))}
118
111
  >
119
- <Select.TriggerButton placeholder='Type' classNames='is-full' />
112
+ <Select.TriggerButton classNames='is-full' placeholder='Type' />
120
113
  <Select.Portal>
121
114
  <Select.Content>
122
115
  <Select.Viewport>
@@ -130,60 +123,37 @@ export const ColumnSettingsForm = ({
130
123
  </Select.Portal>
131
124
  </Select.Root>
132
125
  </Input.Root>
133
- </Section>
126
+ </div>
134
127
  {formState.type === 'number' && (
135
- <Section>
128
+ <div className='space-y-1'>
136
129
  <Input.Root>
137
- <Input.Label classNames='mbe-1 mbs-3'>{t('digits label')}</Input.Label>
130
+ <Input.Label>{t('digits label')}</Input.Label>
138
131
  <Input.TextInput
139
132
  value={formState.digits ?? ''}
140
133
  onChange={(event) => setFormState((prevState) => ({ ...prevState, digits: event.target.value }))}
141
134
  />
142
135
  </Input.Root>
143
- </Section>
136
+ </div>
144
137
  )}
145
138
 
146
139
  {formState.type === 'ref' && (
147
- <Section>
148
- <Input.Root>
149
- <Input.Label classNames='mbe-1 mbs-3'>Table</Input.Label>
150
- <Select.Root
151
- value={formState.refTable}
152
- onValueChange={(value) => setFormState((prevState) => ({ ...prevState, refTable: value }))}
153
- >
154
- <Select.TriggerButton placeholder='Table' classNames='is-full' />
155
- <Select.Portal>
156
- <Select.Content>
157
- <Select.Viewport>
158
- {tablesToReference
159
- .filter((t) => t.id !== tableDef.id)
160
- .map(({ id, name }) => (
161
- <Select.Option key={id} value={id}>
162
- {name ?? id}
163
- </Select.Option>
164
- ))}
165
- </Select.Viewport>
166
- </Select.Content>
167
- </Select.Portal>
168
- </Select.Root>
169
- </Input.Root>
170
-
171
- {formState.refTable && (
140
+ <>
141
+ <div className='space-y-1'>
172
142
  <Input.Root>
173
- <Input.Label classNames='mbe-1 mbs-3'>Table property</Input.Label>
143
+ <Input.Label>Table</Input.Label>
174
144
  <Select.Root
175
- value={formState.refProp}
176
- onValueChange={(value) => setFormState((prevState) => ({ ...prevState, refProp: value }))}
145
+ value={formState.refTable}
146
+ onValueChange={(value) => setFormState((prevState) => ({ ...prevState, refTable: value }))}
177
147
  >
178
- <Select.TriggerButton placeholder='Property' classNames='is-full' />
148
+ <Select.TriggerButton placeholder='Table' classNames='is-full' />
179
149
  <Select.Portal>
180
150
  <Select.Content>
181
151
  <Select.Viewport>
182
152
  {tablesToReference
183
- .find((tableDef) => tableDef.id === formState.refTable)
184
- ?.columns.map(({ id, label }) => (
153
+ .filter((t) => t.id !== tableDef.id)
154
+ .map(({ id, name }) => (
185
155
  <Select.Option key={id} value={id}>
186
- {label ?? id}
156
+ {name ?? id}
187
157
  </Select.Option>
188
158
  ))}
189
159
  </Select.Viewport>
@@ -191,29 +161,46 @@ export const ColumnSettingsForm = ({
191
161
  </Select.Portal>
192
162
  </Select.Root>
193
163
  </Input.Root>
164
+ </div>
165
+ {formState.refTable && (
166
+ <div className='space-y-1'>
167
+ <Input.Root>
168
+ <Input.Label>Table property</Input.Label>
169
+ <Select.Root
170
+ value={formState.refProp}
171
+ onValueChange={(value) => setFormState((prevState) => ({ ...prevState, refProp: value }))}
172
+ >
173
+ <Select.TriggerButton placeholder='Property' />
174
+ <Select.Portal>
175
+ <Select.Content>
176
+ <Select.Viewport>
177
+ {tablesToReference
178
+ .find((tableDef) => tableDef.id === formState.refTable)
179
+ ?.columns.map(({ id, label }) => (
180
+ <Select.Option key={id} value={id}>
181
+ {label ?? id}
182
+ </Select.Option>
183
+ ))}
184
+ </Select.Viewport>
185
+ </Select.Content>
186
+ </Select.Portal>
187
+ </Select.Root>
188
+ </Input.Root>
189
+ </div>
194
190
  )}
195
- </Section>
191
+ </>
196
192
  )}
197
193
 
198
- <Separator classNames='mli-2' />
194
+ <div role='none' className='h-1' />
199
195
 
200
- <Section className='space-b-1.5'>
201
- <Button variant='primary' classNames='is-full flex gap-2' onClick={handleSave}>
202
- <span>{t('save label')}</span>
203
- <div className='grow' />
204
- <Check className={getSize(5)} />
205
- </Button>
206
- <Button classNames='is-full flex gap-2' onClick={handleCancel}>
207
- <span>{t('cancel label', { ns: 'os' })}</span>
208
- <div className='grow' />
209
- <X className={getSize(5)} />
196
+ <div className='flex flex-row justify-end gap-1'>
197
+ <Button onClick={handleDelete} data-testid='table.column-settings.delete'>
198
+ {t('delete label')}
210
199
  </Button>
211
- <Button classNames='is-full flex gap-2' onClick={handleDelete}>
212
- <span>{t('delete label')}</span>
213
- <div className='grow' />
214
- <Trash className={getSize(5)} />
200
+ <Button variant='primary' onClick={handleSave} data-testid='table.column-settings.save'>
201
+ {t('save label')}
215
202
  </Button>
216
- </Section>
203
+ </div>
217
204
  </div>
218
205
  );
219
206
  };
@@ -323,7 +323,7 @@ export const InsertDelete = {
323
323
  };
324
324
 
325
325
  return (
326
- <div>
326
+ <div className='space-y-4'>
327
327
  <div className='flex flex-row gap-2'>
328
328
  <Button onClick={onInsertFirst}>Insert first</Button>
329
329
  <Button onClick={onInsertLast}>Insert last</Button>
@@ -331,7 +331,7 @@ export const InsertDelete = {
331
331
  <Button onClick={onDeleteLast}>Delete last</Button>
332
332
  </div>
333
333
  <Table.Root>
334
- <Table.Viewport classNames='fixed inset-0'>
334
+ <Table.Viewport>
335
335
  <Table.Main<Item>
336
336
  role='grid'
337
337
  rowsSelectable='multi'
@@ -25,12 +25,11 @@ import React, {
25
25
  memo,
26
26
  } from 'react';
27
27
 
28
- import { type ThemedClassName, useDefaultValue } from '@dxos/react-ui';
28
+ import { DensityProvider, type ThemedClassName, useDefaultValue } from '@dxos/react-ui';
29
29
  import { mx } from '@dxos/react-ui-theme';
30
30
 
31
31
  import { TableBody } from './TableBody';
32
32
  import { type TypedTableProvider, TableProvider as UntypedTableProvider, useTableContext } from './TableContext';
33
- import { TableFooter } from './TableFooter';
34
33
  import { TableHead } from './TableHead';
35
34
  import { TableRootContext, useTableRootContext } from './TableRootContext';
36
35
  import { type TableProps } from './props';
@@ -41,7 +40,11 @@ type TableRootProps = { children: ReactNode };
41
40
 
42
41
  const TableRoot = ({ children }: TableRootProps) => {
43
42
  const contextValue = useTableRootContext();
44
- return <TableRootContext.Provider value={contextValue}>{children}</TableRootContext.Provider>;
43
+ return (
44
+ <TableRootContext.Provider value={contextValue}>
45
+ <DensityProvider density='fine'>{children}</DensityProvider>
46
+ </TableRootContext.Provider>
47
+ );
45
48
  };
46
49
 
47
50
  type TableViewportProps = ThemedClassName<ComponentPropsWithoutRef<typeof Primitive.div>> & {
@@ -175,7 +178,7 @@ export const TablePrimitive = <TData extends RowData>(props: TableProps<TData>)
175
178
  * Pure implementation of table outside of context set-up.
176
179
  */
177
180
  const TableImpl = <TData extends RowData>(props: TableProps<TData>) => {
178
- const { debug, classNames, role, footer, grouping, fullWidth } = props;
181
+ const { debug, classNames, role, grouping, fullWidth } = props;
179
182
  const { table } = useTableContext<TData>();
180
183
 
181
184
  if (debug) {
@@ -194,13 +197,15 @@ const TableImpl = <TData extends RowData>(props: TableProps<TData>) => {
194
197
  <table
195
198
  role={role}
196
199
  className={tableRoot(props, classNames)}
197
- {...(!fullWidth && { style: { width: table.getTotalSize() } })}
200
+ style={{
201
+ // borderCollapse: 'separate',
202
+ borderSpacing: 0,
203
+ ...(!fullWidth && { width: table.getTotalSize() }),
204
+ }}
198
205
  >
199
206
  <TableHead />
200
207
 
201
208
  {grouping?.length !== 0 ? <TableComponent /> : <GroupedTableContent />}
202
-
203
- {footer && <TableFooter />}
204
209
  </table>
205
210
  );
206
211
  };