@dxos/react-ui-grid 0.8.4-main.dedc0f3 → 0.8.4-main.dfabb4ec29

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/package.json CHANGED
@@ -1,58 +1,61 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-grid",
3
- "version": "0.8.4-main.dedc0f3",
3
+ "version": "0.8.4-main.dfabb4ec29",
4
4
  "description": "React component which manages a `dx-grid` Lit web component.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/dxos/dxos"
10
+ },
7
11
  "license": "MIT",
8
12
  "author": "DXOS.org",
9
- "sideEffects": true,
13
+ "sideEffects": false,
10
14
  "type": "module",
11
15
  "exports": {
12
16
  ".": {
13
17
  "source": "./src/index.ts",
14
18
  "types": "./dist/types/src/index.d.ts",
15
- "browser": "./dist/lib/browser/index.mjs"
19
+ "browser": "./dist/lib/browser/index.mjs",
20
+ "node": "./dist/lib/browser/index.mjs"
16
21
  }
17
22
  },
18
23
  "types": "dist/types/src/index.d.ts",
19
- "typesVersions": {
20
- "*": {}
21
- },
22
24
  "files": [
23
25
  "dist",
24
26
  "src"
25
27
  ],
26
28
  "dependencies": {
27
- "@codemirror/autocomplete": "^6.18.7",
29
+ "@codemirror/autocomplete": "^6.19.0",
28
30
  "@codemirror/state": "^6.5.2",
29
- "@codemirror/view": "^6.38.2",
30
- "@lit/react": "^1.0.5",
31
- "@preact-signals/safe-react": "^0.9.0",
31
+ "@codemirror/view": "^6.38.5",
32
+ "@lit/react": "^1.0.8",
32
33
  "@radix-ui/react-context": "1.1.1",
33
34
  "@radix-ui/react-popper": "1.2.2",
34
35
  "@radix-ui/react-use-controllable-state": "1.1.0",
35
- "@dxos/react-ui-editor": "0.8.4-main.dedc0f3",
36
- "@dxos/lit-grid": "0.8.4-main.dedc0f3",
37
- "@dxos/util": "0.8.4-main.dedc0f3"
36
+ "@dxos/lit-grid": "0.8.4-main.dfabb4ec29",
37
+ "@dxos/ui-editor": "0.8.4-main.dfabb4ec29",
38
+ "@dxos/util": "0.8.4-main.dfabb4ec29",
39
+ "@dxos/react-ui-editor": "0.8.4-main.dfabb4ec29"
38
40
  },
39
41
  "devDependencies": {
40
- "@types/react": "~18.2.0",
41
- "@types/react-dom": "~18.2.0",
42
- "react": "~18.2.0",
43
- "react-dom": "~18.2.0",
44
- "vite": "7.1.1",
45
- "@dxos/random": "0.8.4-main.dedc0f3",
46
- "@dxos/react-ui-searchlist": "0.8.4-main.dedc0f3",
47
- "@dxos/react-ui-theme": "0.8.4-main.dedc0f3",
48
- "@dxos/storybook-utils": "0.8.4-main.dedc0f3",
49
- "@dxos/react-ui": "0.8.4-main.dedc0f3"
42
+ "@types/react": "~19.2.7",
43
+ "@types/react-dom": "~19.2.3",
44
+ "react": "~19.2.3",
45
+ "react-dom": "~19.2.3",
46
+ "vite": "^8.0.10",
47
+ "@dxos/random": "0.8.4-main.dfabb4ec29",
48
+ "@dxos/react-ui": "0.8.4-main.dfabb4ec29",
49
+ "@dxos/storybook-utils": "0.8.4-main.dfabb4ec29",
50
+ "@dxos/ui-theme": "0.8.4-main.dfabb4ec29",
51
+ "@dxos/react-ui-search": "0.8.4-main.dfabb4ec29",
52
+ "@dxos/react-ui-list": "0.8.4-main.dfabb4ec29"
50
53
  },
51
54
  "peerDependencies": {
52
- "react": "~18.2.0",
53
- "react-dom": "~18.2.0",
54
- "@dxos/react-ui": "0.8.4-main.dedc0f3",
55
- "@dxos/react-ui-theme": "0.8.4-main.dedc0f3"
55
+ "react": "~19.2.3",
56
+ "react-dom": "~19.2.3",
57
+ "@dxos/ui-theme": "0.8.4-main.dfabb4ec29",
58
+ "@dxos/react-ui": "0.8.4-main.dfabb4ec29"
56
59
  },
57
60
  "publishConfig": {
58
61
  "access": "public"
@@ -2,12 +2,10 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React, { useState } from 'react';
9
7
 
10
- import { withTheme } from '@dxos/storybook-utils';
8
+ import { withTheme } from '@dxos/react-ui/testing';
11
9
 
12
10
  import { CellEditor, type CellEditorProps, type EditorKeyEvent, editorKeys } from './CellEditor';
13
11
 
@@ -72,7 +70,7 @@ const meta = {
72
70
  title: 'ui/react-ui-grid/CellEditor',
73
71
  component: CellEditor,
74
72
  render: DefaultStory,
75
- decorators: [withTheme],
73
+ decorators: [withTheme()],
76
74
  parameters: {
77
75
  layout: 'centered',
78
76
  },
@@ -8,15 +8,14 @@ import { EditorView, keymap } from '@codemirror/view';
8
8
  import React, { type KeyboardEvent } from 'react';
9
9
 
10
10
  import { useThemeContext } from '@dxos/react-ui';
11
+ import { type UseTextEditorProps, useTextEditor } from '@dxos/react-ui-editor';
11
12
  import {
12
13
  type ThemeExtensionsOptions,
13
- type UseTextEditorProps,
14
14
  createBasicExtensions,
15
15
  createThemeExtensions,
16
- preventNewline,
17
- useTextEditor,
18
- } from '@dxos/react-ui-editor';
19
- import { mx } from '@dxos/react-ui-theme';
16
+ filterChars,
17
+ } from '@dxos/ui-editor';
18
+ import { mx } from '@dxos/ui-theme';
20
19
 
21
20
  import { type GridEditBox } from '../Grid';
22
21
 
@@ -123,7 +122,7 @@ export type CellEditorProps = {
123
122
  } & Pick<UseTextEditorProps, 'autoFocus'> &
124
123
  Pick<ThemeExtensionsOptions, 'slots'>;
125
124
 
126
- export const CellEditor = ({ value, extensions, box, gridId, onBlur, autoFocus, slots }: CellEditorProps) => {
125
+ export const CellEditor = ({ value, extensions, box, gridId, autoFocus, slots, onBlur }: CellEditorProps) => {
127
126
  const { themeMode } = useThemeContext();
128
127
  const { parentRef } = useTextEditor(() => {
129
128
  return {
@@ -132,7 +131,7 @@ export const CellEditor = ({ value, extensions, box, gridId, onBlur, autoFocus,
132
131
  selection: { anchor: value?.length ?? 0 },
133
132
  extensions: [
134
133
  extensions ?? [],
135
- preventNewline,
134
+ filterChars(/[\n\r]+/),
136
135
  EditorView.focusChangeEffect.of((state, focusing) => {
137
136
  if (!focusing) {
138
137
  onBlur?.(state.doc.toString());
@@ -145,18 +144,18 @@ export const CellEditor = ({ value, extensions, box, gridId, onBlur, autoFocus,
145
144
  slots: {
146
145
  editor: {
147
146
  className: mx(
148
- '!min-is-full !is-min !max-is-[--dx-grid-cell-editor-max-inline-size] !min-bs-full !max-bs-[--dx-grid-cell-editor-max-block-size]',
147
+ 'min-w-full! w-min! !max-w-(--dx-grid-cell-editor-max-w-size) min-h-full! !max-h-(--dx-grid-cell-editor-max-h-size)',
149
148
  slots?.editor?.className,
150
149
  ),
151
150
  },
152
- scroll: {
151
+ scroller: {
153
152
  className: mx(
154
- '!overflow-x-hidden !plb-[max(0,calc(var(--dx-grid-cell-editor-padding-block)-1px))] !pie-0 !pis-[--dx-grid-cell-editor-padding-inline]',
155
- slots?.scroll?.className,
153
+ 'overflow-x-hidden! !py-[max(0,calc(var(--dx-grid-cell-editor-padding-block)-1px))] pe-0! !pl-(--dx-grid-cell-editor-padding-inline)',
154
+ slots?.scroller?.className,
156
155
  ),
157
156
  },
158
157
  content: {
159
- className: mx('!break-normal', slots?.content?.className),
158
+ className: mx('break-normal!', slots?.content?.className),
160
159
  },
161
160
  },
162
161
  }),
@@ -174,7 +173,7 @@ export const CellEditor = ({ value, extensions, box, gridId, onBlur, autoFocus,
174
173
  insetBlockStart: box?.insetBlockStart ?? '0px',
175
174
  minInlineSize: box?.inlineSize ?? '180px',
176
175
  minBlockSize: box?.blockSize ?? '30px',
177
- ...{ '--dx-gridCellWidth': `${box?.inlineSize ?? 200}px` },
176
+ ...{ '--dx-grid-cell-width': `${box?.inlineSize ?? 200}px` },
178
177
  }}
179
178
  {...(gridId && { 'data-grid': gridId })}
180
179
  />
@@ -5,7 +5,6 @@
5
5
  import React, { useCallback } from 'react';
6
6
 
7
7
  import { type DxGridCellIndex, type GridScopedProps, useGridContext } from '../Grid';
8
-
9
8
  import { CellEditor, type CellEditorProps } from './CellEditor';
10
9
 
11
10
  export type GridCellEditorProps = GridScopedProps<
@@ -2,20 +2,21 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React, { type MouseEvent, type MutableRefObject, useCallback, useRef, useState } from 'react';
9
7
 
10
8
  import { defaultRowSize } from '@dxos/lit-grid';
11
- import { faker } from '@dxos/random';
9
+ import { type DxGridPlaneCells } from '@dxos/lit-grid';
10
+ import { random } from '@dxos/random';
12
11
  import { DropdownMenu } from '@dxos/react-ui';
13
- import { PopoverCombobox, type PopoverComboboxRootProps } from '@dxos/react-ui-searchlist';
14
- import { withTheme } from '@dxos/storybook-utils';
12
+ import { toPlaneCellIndex } from '@dxos/react-ui-grid';
13
+ import { Combobox, type ComboboxRootProps } from '@dxos/react-ui-list';
14
+ import { useSearchListResults } from '@dxos/react-ui-search';
15
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
15
16
 
16
17
  import { Grid, type GridContentProps, type GridEditing, type GridRootProps } from './Grid';
17
18
 
18
- const storybookItems = faker.helpers.uniqueArray(faker.commerce.productName, 16);
19
+ const storybookItems = random.helpers.uniqueArray(random.commerce.productName, 16);
19
20
 
20
21
  type GridStoryProps = GridContentProps & Pick<GridRootProps, 'onEditingChange'>;
21
22
 
@@ -33,7 +34,7 @@ const GridStory = ({ initialCells, ...props }: GridStoryProps) => {
33
34
  // Multiselect
34
35
  const [popoverOpen, setPopoverOpen] = useState(false);
35
36
  const [multiSelectValue, setInternalMultiselectValue] = useState('');
36
- const setMultiselectValue = useCallback<NonNullable<PopoverComboboxRootProps['onValueChange']>>((nextValue) => {
37
+ const setMultiselectValue = useCallback<NonNullable<ComboboxRootProps['onValueChange']>>((nextValue) => {
37
38
  setInternalMultiselectValue(nextValue);
38
39
  setCells((cells) => {
39
40
  // TODO(burdon): How can we get the cell address to update?
@@ -64,7 +65,7 @@ const GridStory = ({ initialCells, ...props }: GridStoryProps) => {
64
65
  }, []);
65
66
 
66
67
  return (
67
- <div role='none' className='fixed inset-0 grid'>
68
+ <div className='contents'>
68
69
  <Grid.Root id='story' editing={editing} onEditingChange={handleEditingChange}>
69
70
  {/* TODO(burdon): Why is this property not just "cells" or "values" */}
70
71
  <Grid.Content {...props} initialCells={cells} onClick={handleClick} />
@@ -80,38 +81,52 @@ const GridStory = ({ initialCells, ...props }: GridStoryProps) => {
80
81
  </DropdownMenu.Root>
81
82
 
82
83
  {/* Multiselect */}
83
- <PopoverCombobox.Root
84
+ <Combobox.Root
84
85
  open={popoverOpen}
85
86
  onOpenChange={setPopoverOpen}
86
87
  value={multiSelectValue}
87
88
  onValueChange={setMultiselectValue}
88
89
  >
89
- <PopoverCombobox.VirtualTrigger virtualRef={triggerRef} />
90
- <PopoverCombobox.Content filter={(value, search) => (value.includes(search) ? 1 : 0)}>
91
- <PopoverCombobox.Input placeholder='Search...' />
92
- <PopoverCombobox.List>
93
- {storybookItems.map((value) => (
94
- <PopoverCombobox.Item key={value}>{value}</PopoverCombobox.Item>
95
- ))}
96
- </PopoverCombobox.List>
97
- <PopoverCombobox.Arrow />
98
- </PopoverCombobox.Content>
99
- </PopoverCombobox.Root>
90
+ <Combobox.VirtualTrigger virtualRef={triggerRef} />
91
+ <ComboboxContentWithFiltering />
92
+ </Combobox.Root>
100
93
  </div>
101
94
  );
102
95
  };
103
96
 
97
+ const ComboboxContentWithFiltering = () => {
98
+ const { results, query, handleSearch } = useSearchListResults({
99
+ items: storybookItems,
100
+ });
101
+
102
+ return (
103
+ <Combobox.Content>
104
+ <Combobox.Input placeholder='Search...' value={query} onValueChange={handleSearch} />
105
+ <Combobox.List>
106
+ {results.map((value) => (
107
+ <Combobox.Item key={value} value={value} label={value} />
108
+ ))}
109
+ </Combobox.List>
110
+ <Combobox.Arrow />
111
+ </Combobox.Content>
112
+ );
113
+ };
114
+
104
115
  const meta = {
105
116
  title: 'ui/react-ui-grid/Grid',
106
117
  component: GridStory,
107
- decorators: [withTheme],
108
- parameters: { layout: 'fullscreen' },
118
+ decorators: [withTheme(), withLayout({ layout: 'column' })],
119
+ parameters: {
120
+ layout: 'fullscreen',
121
+ },
109
122
  } satisfies Meta<typeof GridStory>;
110
123
 
111
124
  export default meta;
112
125
 
113
126
  type Story = StoryObj<typeof meta>;
114
127
 
128
+ export const Default: Story = {};
129
+
115
130
  export const Basic: Story = {
116
131
  args: {
117
132
  id: 'story',
@@ -141,7 +156,7 @@ export const Basic: Story = {
141
156
  '1,1': {
142
157
  value: 'Demo decoration',
143
158
  accessoryHtml: `
144
- <button class="dx-button is-6 pli-0.5 min-bs-0 absolute inset-block-1 inline-end-1" data-story-action="menu">
159
+ <button class="dx-button w-6 px-0.5 min-h-0 absolute inset-y-1 right-1" data-story-action="menu">
145
160
  <svg><use href="/icons.svg#ph--arrow-right--regular"/></svg>
146
161
  </button>
147
162
  `,
@@ -158,26 +173,47 @@ export const Basic: Story = {
158
173
  },
159
174
  };
160
175
 
161
- // TODO(burdon): How to make single-column?
162
- export const SingleColumn: Story = {
176
+ const cellSize = 40;
177
+
178
+ // TODO(burdon): Calendar.
179
+ export const Calendar: Story = {
163
180
  args: {
164
181
  id: 'story',
165
- limitColumns: 1,
182
+ limitColumns: 7,
166
183
  columnDefault: {
167
184
  grid: {
168
- size: 180,
185
+ size: cellSize,
186
+ resizeable: false,
169
187
  },
170
188
  },
171
189
  rowDefault: {
172
190
  grid: {
173
- size: defaultRowSize,
191
+ size: cellSize,
174
192
  resizeable: false,
175
193
  },
176
194
  },
177
- columns: {
178
- grid: {
179
- 0: { size: 200 },
180
- },
195
+ getCells: (range, plane) => {
196
+ const cells: DxGridPlaneCells = {};
197
+ if (plane === 'grid') {
198
+ for (let col = range.start.col; col <= range.end.col; col++) {
199
+ for (let row = range.start.row; row <= range.end.row; row++) {
200
+ // TODO(burdon): Formatting changes when cell is selected.
201
+ cells[toPlaneCellIndex({ col, row })] = {
202
+ readonly: true,
203
+ accessoryHtml: '<div class="flex h-full w-full justify-center items-center overflow-hidden">0</div>',
204
+ className: '',
205
+ };
206
+ }
207
+ }
208
+ }
209
+ return cells;
181
210
  },
182
211
  },
212
+ render: (args) => (
213
+ <div className='h-full flex justify-center'>
214
+ <div className='h-full w-[288px] border-x border-separator'>
215
+ <GridStory {...args} />
216
+ </div>
217
+ </div>
218
+ ),
183
219
  };
package/src/Grid/Grid.tsx CHANGED
@@ -2,8 +2,6 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos/lit-grid/dx-grid.pcss';
6
-
7
5
  import { type EventName, createComponent } from '@lit/react';
8
6
  import { type Scope, createContextScope } from '@radix-ui/react-context';
9
7
  import { useControllableState } from '@radix-ui/react-use-controllable-state';
@@ -16,6 +14,7 @@ import React, {
16
14
  useState,
17
15
  } from 'react';
18
16
 
17
+ import '@dxos/lit-grid/dx-grid.pcss';
19
18
  import { type DxAxisResize, type DxEditRequest, type DxGridCellsSelect, DxGrid as NaturalDxGrid } from '@dxos/lit-grid';
20
19
 
21
20
  type DxGridElement = NaturalDxGrid;
@@ -63,20 +62,23 @@ const [createGridContext, createGridScope] = createContextScope(GRID_NAME, []);
63
62
  const [GridProvider, useGridContext] = createGridContext<GridContextValue>(GRID_NAME);
64
63
 
65
64
  type GridRootProps = PropsWithChildren<
66
- { id: string } & Partial<{
65
+ {
66
+ id: string;
67
+ } & Partial<{
67
68
  editing: GridEditing;
68
69
  defaultEditing: GridEditing;
69
70
  onEditingChange: (nextEditing: GridEditing) => void;
70
71
  }>
71
72
  >;
72
73
 
74
+ // TODO(burdon): Make headless.
73
75
  const GridRoot = ({
76
+ __gridScope,
77
+ children,
74
78
  id,
75
79
  editing: propsEditing,
76
80
  defaultEditing,
77
81
  onEditingChange,
78
- children,
79
- __gridScope,
80
82
  }: GridScopedProps<GridRootProps>) => {
81
83
  const [editing = null, setEditing] = useControllableState({
82
84
  prop: propsEditing,
@@ -102,13 +104,13 @@ const GridRoot = ({
102
104
 
103
105
  GridRoot.displayName = GRID_NAME;
104
106
 
107
+ const GRID_CONTENT_NAME = 'GridContent';
108
+
105
109
  type GridContentProps = Omit<ComponentProps<typeof DxGrid>, 'onEdit'> & {
106
110
  getCells?: NaturalDxGrid['getCells'];
107
111
  activeRefs?: string;
108
112
  };
109
113
 
110
- const GRID_CONTENT_NAME = 'GridContent';
111
-
112
114
  const GridContent = forwardRef<NaturalDxGrid, GridScopedProps<GridContentProps>>((props, forwardedRef) => {
113
115
  const { id, editing, setEditBox, setEditing } = useGridContext(GRID_CONTENT_NAME, props.__gridScope);
114
116
  const [dxGrid, setDxGridInternal] = useState<NaturalDxGrid | null>(null);
@@ -150,12 +152,12 @@ GridContent.displayName = GRID_CONTENT_NAME;
150
152
  // Fragments
151
153
  //
152
154
 
153
- // NOTE(Zan): These fragments add border to inline-end and block-end of the grid using pseudo-elements.
155
+ // NOTE(Zan): These fragments add border to w-end and h-end of the grid using pseudo-elements.
154
156
  // These are offset by 1px to avoid double borders in planks.
155
157
  const gridSeparatorInlineEnd =
156
- '[&>.dx-grid]:relative [&>.dx-grid]:after:absolute [&>.dx-grid]:after:inset-block-0 [&>.dx-grid]:after:-inline-end-px [&>.dx-grid]:after:is-px [&>.dx-grid]:after:bg-subduedSeparator';
158
+ '[&>.dx-grid]:relative [&>.dx-grid]:after:absolute [&>.dx-grid]:after:inset-y-0 [&>.dx-grid]:after:-right-px [&>.dx-grid]:after:w-px [&>.dx-grid]:after:bg-subdued-separator';
157
159
  const gridSeparatorBlockEnd =
158
- '[&>.dx-grid]:relative [&>.dx-grid]:before:absolute [&>.dx-grid]:before:inset-inline-0 [&>.dx-grid]:before:-block-end-px [&>.dx-grid]:before:bs-px [&>.dx-grid]:before:bg-subduedSeparator';
160
+ '[&>.dx-grid]:relative [&>.dx-grid]:before:absolute [&>.dx-grid]:before:inset-x-0 [&>.dx-grid]:before:-bottom-px [&>.dx-grid]:before:h-px [&>.dx-grid]:before:bg-subdued-separator';
159
161
 
160
162
  //
161
163
  // Exports
@@ -166,7 +168,7 @@ export const Grid = {
166
168
  Content: GridContent,
167
169
  };
168
170
 
169
- export { GridRoot, GridContent, useGridContext, createGridScope, gridSeparatorInlineEnd, gridSeparatorBlockEnd };
171
+ export { GridRoot, GridContent, createGridScope, gridSeparatorInlineEnd, gridSeparatorBlockEnd, useGridContext };
170
172
 
171
173
  export type { GridRootProps, GridContentProps, GridEditing, GridEditBox, GridScopedProps, DxGridElement };
172
174