@dxos/react-ui-grid 0.8.4-main.84f28bd → 0.8.4-main.8baae0fced
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/LICENSE +102 -5
- package/dist/lib/browser/index.mjs +152 -162
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/CellEditor/CellEditor.d.ts +4 -3
- package/dist/types/src/CellEditor/CellEditor.d.ts.map +1 -1
- package/dist/types/src/CellEditor/CellEditor.stories.d.ts +12 -4
- package/dist/types/src/CellEditor/CellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/CellEditor/GridCellEditor.d.ts +3 -3
- package/dist/types/src/CellEditor/GridCellEditor.d.ts.map +1 -1
- package/dist/types/src/Grid/Grid.d.ts +10 -10
- package/dist/types/src/Grid/Grid.d.ts.map +1 -1
- package/dist/types/src/Grid/Grid.stories.d.ts +16 -4
- package/dist/types/src/Grid/Grid.stories.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +33 -29
- package/src/CellEditor/CellEditor.stories.tsx +10 -12
- package/src/CellEditor/CellEditor.tsx +32 -20
- package/src/CellEditor/GridCellEditor.tsx +4 -4
- package/src/Grid/Grid.stories.tsx +94 -35
- package/src/Grid/Grid.tsx +16 -13
- package/dist/lib/node-esm/index.mjs +0 -341
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
package/package.json
CHANGED
|
@@ -1,57 +1,61 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-grid",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.8baae0fced",
|
|
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
|
-
"
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/dxos/dxos"
|
|
10
|
+
},
|
|
11
|
+
"license": "FSL-1.1-Apache-2.0",
|
|
8
12
|
"author": "DXOS.org",
|
|
9
|
-
"sideEffects":
|
|
13
|
+
"sideEffects": false,
|
|
10
14
|
"type": "module",
|
|
11
15
|
"exports": {
|
|
12
16
|
".": {
|
|
17
|
+
"source": "./src/index.ts",
|
|
13
18
|
"types": "./dist/types/src/index.d.ts",
|
|
14
|
-
"browser": "./dist/lib/browser/index.mjs"
|
|
19
|
+
"browser": "./dist/lib/browser/index.mjs",
|
|
20
|
+
"node": "./dist/lib/browser/index.mjs"
|
|
15
21
|
}
|
|
16
22
|
},
|
|
17
23
|
"types": "dist/types/src/index.d.ts",
|
|
18
|
-
"typesVersions": {
|
|
19
|
-
"*": {}
|
|
20
|
-
},
|
|
21
24
|
"files": [
|
|
22
25
|
"dist",
|
|
23
26
|
"src"
|
|
24
27
|
],
|
|
25
28
|
"dependencies": {
|
|
26
|
-
"@codemirror/autocomplete": "^6.
|
|
27
|
-
"@codemirror/state": "^6.
|
|
28
|
-
"@codemirror/view": "^6.
|
|
29
|
-
"@lit/react": "^1.0.
|
|
30
|
-
"@preact-signals/safe-react": "^0.9.0",
|
|
29
|
+
"@codemirror/autocomplete": "^6.19.0",
|
|
30
|
+
"@codemirror/state": "^6.5.2",
|
|
31
|
+
"@codemirror/view": "^6.38.5",
|
|
32
|
+
"@lit/react": "^1.0.8",
|
|
31
33
|
"@radix-ui/react-context": "1.1.1",
|
|
32
34
|
"@radix-ui/react-popper": "1.2.2",
|
|
33
35
|
"@radix-ui/react-use-controllable-state": "1.1.0",
|
|
34
|
-
"@dxos/lit-grid": "0.8.4-main.
|
|
35
|
-
"@dxos/react-ui-editor": "0.8.4-main.
|
|
36
|
-
"@dxos/
|
|
36
|
+
"@dxos/lit-grid": "0.8.4-main.8baae0fced",
|
|
37
|
+
"@dxos/react-ui-editor": "0.8.4-main.8baae0fced",
|
|
38
|
+
"@dxos/ui-editor": "0.8.4-main.8baae0fced",
|
|
39
|
+
"@dxos/util": "0.8.4-main.8baae0fced"
|
|
37
40
|
},
|
|
38
41
|
"devDependencies": {
|
|
39
|
-
"@types/react": "~
|
|
40
|
-
"@types/react-dom": "~
|
|
41
|
-
"react": "~
|
|
42
|
-
"react-dom": "~
|
|
43
|
-
"vite": "
|
|
44
|
-
"@dxos/random": "0.8.4-main.
|
|
45
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
46
|
-
"@dxos/react-ui-
|
|
47
|
-
"@dxos/
|
|
48
|
-
"@dxos/
|
|
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.13",
|
|
47
|
+
"@dxos/random": "0.8.4-main.8baae0fced",
|
|
48
|
+
"@dxos/react-ui": "0.8.4-main.8baae0fced",
|
|
49
|
+
"@dxos/react-ui-list": "0.8.4-main.8baae0fced",
|
|
50
|
+
"@dxos/storybook-utils": "0.8.4-main.8baae0fced",
|
|
51
|
+
"@dxos/react-ui-search": "0.8.4-main.8baae0fced",
|
|
52
|
+
"@dxos/ui-theme": "0.8.4-main.8baae0fced"
|
|
49
53
|
},
|
|
50
54
|
"peerDependencies": {
|
|
51
|
-
"react": "~
|
|
52
|
-
"react-dom": "~
|
|
53
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
54
|
-
"@dxos/
|
|
55
|
+
"react": "~19.2.3",
|
|
56
|
+
"react-dom": "~19.2.3",
|
|
57
|
+
"@dxos/react-ui": "0.8.4-main.8baae0fced",
|
|
58
|
+
"@dxos/ui-theme": "0.8.4-main.8baae0fced"
|
|
55
59
|
},
|
|
56
60
|
"publishConfig": {
|
|
57
61
|
"access": "public"
|
|
@@ -2,17 +2,15 @@
|
|
|
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/
|
|
8
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
11
9
|
|
|
12
10
|
import { CellEditor, type CellEditorProps, type EditorKeyEvent, editorKeys } from './CellEditor';
|
|
13
11
|
|
|
14
|
-
const DefaultStory = (
|
|
15
|
-
const [value, setValue] = useState(
|
|
12
|
+
const DefaultStory = (props: CellEditorProps) => {
|
|
13
|
+
const [value, setValue] = useState(props.value || 'Edit me');
|
|
16
14
|
const [lastAction, setLastAction] = useState<string>('');
|
|
17
15
|
|
|
18
16
|
const handleBlur = (newValue?: string) => {
|
|
@@ -32,7 +30,7 @@ const DefaultStory = (args: CellEditorProps) => {
|
|
|
32
30
|
};
|
|
33
31
|
|
|
34
32
|
// Create an extension with editor keys
|
|
35
|
-
const
|
|
33
|
+
const extensions = props.extensions || [
|
|
36
34
|
editorKeys({
|
|
37
35
|
onClose: handleKeyEvent,
|
|
38
36
|
onNav: (value, event) => {
|
|
@@ -52,8 +50,8 @@ const DefaultStory = (args: CellEditorProps) => {
|
|
|
52
50
|
<div className='relative border border-separator h-[100px] w-[300px]'>
|
|
53
51
|
<CellEditor
|
|
54
52
|
value={value}
|
|
55
|
-
|
|
56
|
-
autoFocus={
|
|
53
|
+
extensions={extensions}
|
|
54
|
+
autoFocus={props.autoFocus}
|
|
57
55
|
onBlur={handleBlur}
|
|
58
56
|
box={{
|
|
59
57
|
insetInlineStart: 10,
|
|
@@ -68,19 +66,19 @@ const DefaultStory = (args: CellEditorProps) => {
|
|
|
68
66
|
);
|
|
69
67
|
};
|
|
70
68
|
|
|
71
|
-
const meta
|
|
69
|
+
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
|
},
|
|
79
|
-
}
|
|
77
|
+
} satisfies Meta<typeof CellEditor>;
|
|
80
78
|
|
|
81
79
|
export default meta;
|
|
82
80
|
|
|
83
|
-
type Story = StoryObj<
|
|
81
|
+
type Story = StoryObj<typeof meta>;
|
|
84
82
|
|
|
85
83
|
export const Default: Story = {
|
|
86
84
|
args: {
|
|
@@ -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
|
-
type
|
|
13
|
+
type ThemeExtensionsOptions,
|
|
13
14
|
createBasicExtensions,
|
|
14
15
|
createThemeExtensions,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
|
|
@@ -116,14 +115,14 @@ export const editorKeys = ({ onNav, onClose }: EditorKeysProps): Extension => {
|
|
|
116
115
|
|
|
117
116
|
export type CellEditorProps = {
|
|
118
117
|
value?: string;
|
|
119
|
-
|
|
118
|
+
extensions?: Extension;
|
|
120
119
|
box?: GridEditBox;
|
|
121
120
|
gridId?: string;
|
|
122
121
|
onBlur?: EditorBlurHandler;
|
|
123
122
|
} & Pick<UseTextEditorProps, 'autoFocus'> &
|
|
124
123
|
Pick<ThemeExtensionsOptions, 'slots'>;
|
|
125
124
|
|
|
126
|
-
export const CellEditor = ({ value,
|
|
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 {
|
|
@@ -131,13 +130,24 @@ export const CellEditor = ({ value, extension, autoFocus, onBlur, box, gridId, s
|
|
|
131
130
|
initialValue: value,
|
|
132
131
|
selection: { anchor: value?.length ?? 0 },
|
|
133
132
|
extensions: [
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
EditorView.focusChangeEffect
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
133
|
+
extensions ?? [],
|
|
134
|
+
filterChars(/[\n\r]+/),
|
|
135
|
+
// Observe the underlying blur DOM event rather than `EditorView.focusChangeEffect`. The
|
|
136
|
+
// focus-change facet fires asynchronously (CodeMirror schedules it on a 10ms setTimeout),
|
|
137
|
+
// which means in React strict mode the destroy → blur of the first EditorView runs the
|
|
138
|
+
// callback after the second view has mounted — committing stale data and closing the
|
|
139
|
+
// editor on the user's first keystroke. Deferring via `queueMicrotask` runs the check
|
|
140
|
+
// *after* `view.destroy()` finishes its synchronous body (which calls `dom.remove()`),
|
|
141
|
+
// so `view.dom.isConnected === false` reliably distinguishes a programmatic teardown
|
|
142
|
+
// from a real user blur. Pass `undefined` on teardown so downstream handlers can consume
|
|
143
|
+
// any pending suppress-next-blur flag without committing stale data.
|
|
144
|
+
EditorView.domEventObservers({
|
|
145
|
+
blur: (_event, view) => {
|
|
146
|
+
const doc = view.state.doc.toString();
|
|
147
|
+
queueMicrotask(() => {
|
|
148
|
+
onBlur?.(view.dom.isConnected ? doc : undefined);
|
|
149
|
+
});
|
|
150
|
+
},
|
|
141
151
|
}),
|
|
142
152
|
createBasicExtensions({ lineWrapping: true }),
|
|
143
153
|
createThemeExtensions({
|
|
@@ -145,22 +155,24 @@ export const CellEditor = ({ value, extension, autoFocus, onBlur, box, gridId, s
|
|
|
145
155
|
slots: {
|
|
146
156
|
editor: {
|
|
147
157
|
className: mx(
|
|
148
|
-
'
|
|
158
|
+
'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
159
|
slots?.editor?.className,
|
|
150
160
|
),
|
|
151
161
|
},
|
|
152
162
|
scroller: {
|
|
153
163
|
className: mx(
|
|
154
|
-
'
|
|
164
|
+
'overflow-x-hidden! !py-[max(0,calc(var(--dx-grid-cell-editor-padding-block)-1px))] pe-0! !pl-(--dx-grid-cell-editor-padding-inline)',
|
|
155
165
|
slots?.scroller?.className,
|
|
156
166
|
),
|
|
157
167
|
},
|
|
158
|
-
content: {
|
|
168
|
+
content: {
|
|
169
|
+
className: mx('break-normal!', slots?.content?.className),
|
|
170
|
+
},
|
|
159
171
|
},
|
|
160
172
|
}),
|
|
161
173
|
],
|
|
162
174
|
};
|
|
163
|
-
}, [
|
|
175
|
+
}, [extensions, autoFocus, value, onBlur, themeMode, slots]);
|
|
164
176
|
|
|
165
177
|
return (
|
|
166
178
|
<div
|
|
@@ -172,7 +184,7 @@ export const CellEditor = ({ value, extension, autoFocus, onBlur, box, gridId, s
|
|
|
172
184
|
insetBlockStart: box?.insetBlockStart ?? '0px',
|
|
173
185
|
minInlineSize: box?.inlineSize ?? '180px',
|
|
174
186
|
minBlockSize: box?.blockSize ?? '30px',
|
|
175
|
-
...{ '--dx-
|
|
187
|
+
...{ '--dx-grid-cell-width': `${box?.inlineSize ?? 200}px` },
|
|
176
188
|
}}
|
|
177
189
|
{...(gridId && { 'data-grid': gridId })}
|
|
178
190
|
/>
|
|
@@ -4,16 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { useCallback } from 'react';
|
|
6
6
|
|
|
7
|
+
import { type DxGridCellIndex, type GridScopedProps, useGridContext } from '../Grid';
|
|
7
8
|
import { CellEditor, type CellEditorProps } from './CellEditor';
|
|
8
|
-
import { type GridScopedProps, useGridContext, type DxGridCellIndex } from '../Grid';
|
|
9
9
|
|
|
10
10
|
export type GridCellEditorProps = GridScopedProps<
|
|
11
|
-
Pick<CellEditorProps, '
|
|
11
|
+
Pick<CellEditorProps, 'extensions' | 'onBlur' | 'slots'> & {
|
|
12
12
|
getCellContent: (index: DxGridCellIndex) => string | undefined;
|
|
13
13
|
}
|
|
14
14
|
>;
|
|
15
15
|
|
|
16
|
-
export const GridCellEditor = ({
|
|
16
|
+
export const GridCellEditor = ({ extensions, getCellContent, onBlur, slots, __gridScope }: GridCellEditorProps) => {
|
|
17
17
|
const { id, editing, setEditing, editBox } = useGridContext('GridCellEditor', __gridScope);
|
|
18
18
|
|
|
19
19
|
const handleBlur = useCallback(
|
|
@@ -30,7 +30,7 @@ export const GridCellEditor = ({ extension, getCellContent, onBlur, slots, __gri
|
|
|
30
30
|
autoFocus
|
|
31
31
|
box={editBox}
|
|
32
32
|
onBlur={handleBlur}
|
|
33
|
-
|
|
33
|
+
extensions={extensions}
|
|
34
34
|
gridId={id}
|
|
35
35
|
slots={slots}
|
|
36
36
|
/>
|
|
@@ -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 {
|
|
9
|
+
import { type DxGridPlaneCells } from '@dxos/lit-grid';
|
|
10
|
+
import { random } from '@dxos/random';
|
|
12
11
|
import { DropdownMenu } from '@dxos/react-ui';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
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
|
-
import { Grid, type
|
|
17
|
+
import { Grid, type GridContentProps, type GridEditing, type GridRootProps } from './Grid';
|
|
17
18
|
|
|
18
|
-
const storybookItems =
|
|
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<
|
|
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
|
|
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,75 @@ const GridStory = ({ initialCells, ...props }: GridStoryProps) => {
|
|
|
80
81
|
</DropdownMenu.Root>
|
|
81
82
|
|
|
82
83
|
{/* Multiselect */}
|
|
83
|
-
<
|
|
84
|
+
<Combobox.Root
|
|
84
85
|
open={popoverOpen}
|
|
85
86
|
onOpenChange={setPopoverOpen}
|
|
86
87
|
value={multiSelectValue}
|
|
87
88
|
onValueChange={setMultiselectValue}
|
|
88
89
|
>
|
|
89
|
-
<
|
|
90
|
-
<
|
|
91
|
-
|
|
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
|
|
|
104
|
-
const
|
|
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
|
+
|
|
115
|
+
const meta = {
|
|
105
116
|
title: 'ui/react-ui-grid/Grid',
|
|
106
117
|
component: GridStory,
|
|
107
|
-
decorators: [withTheme],
|
|
108
|
-
parameters: {
|
|
109
|
-
|
|
118
|
+
decorators: [withTheme(), withLayout({ layout: 'column' })],
|
|
119
|
+
parameters: {
|
|
120
|
+
layout: 'fullscreen',
|
|
121
|
+
},
|
|
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
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Single focusable cell — for verifying focus-ring alignment with grid lines.
|
|
132
|
+
*/
|
|
133
|
+
export const SingleCell: Story = {
|
|
134
|
+
args: {
|
|
135
|
+
id: 'story',
|
|
136
|
+
limitColumns: 1,
|
|
137
|
+
limitRows: 1,
|
|
138
|
+
columnDefault: { grid: { size: 200, resizeable: false } },
|
|
139
|
+
rowDefault: { grid: { size: 32, resizeable: false } },
|
|
140
|
+
initialCells: {
|
|
141
|
+
grid: {
|
|
142
|
+
'0,0': { value: 'Focus me' },
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
render: (args) => (
|
|
147
|
+
<div className='h-full grid place-items-center'>
|
|
148
|
+
<GridStory {...args} />
|
|
149
|
+
</div>
|
|
150
|
+
),
|
|
151
|
+
};
|
|
152
|
+
|
|
115
153
|
export const Basic: Story = {
|
|
116
154
|
args: {
|
|
117
155
|
id: 'story',
|
|
@@ -141,7 +179,7 @@ export const Basic: Story = {
|
|
|
141
179
|
'1,1': {
|
|
142
180
|
value: 'Demo decoration',
|
|
143
181
|
accessoryHtml: `
|
|
144
|
-
<button class="dx-button
|
|
182
|
+
<button class="dx-button w-6 px-0.5 min-h-0 absolute inset-y-1 right-1" data-story-action="menu">
|
|
145
183
|
<svg><use href="/icons.svg#ph--arrow-right--regular"/></svg>
|
|
146
184
|
</button>
|
|
147
185
|
`,
|
|
@@ -158,26 +196,47 @@ export const Basic: Story = {
|
|
|
158
196
|
},
|
|
159
197
|
};
|
|
160
198
|
|
|
161
|
-
|
|
162
|
-
|
|
199
|
+
const cellSize = 40;
|
|
200
|
+
|
|
201
|
+
// TODO(burdon): Calendar.
|
|
202
|
+
export const Calendar: Story = {
|
|
163
203
|
args: {
|
|
164
204
|
id: 'story',
|
|
165
|
-
limitColumns:
|
|
205
|
+
limitColumns: 7,
|
|
166
206
|
columnDefault: {
|
|
167
207
|
grid: {
|
|
168
|
-
size:
|
|
208
|
+
size: cellSize,
|
|
209
|
+
resizeable: false,
|
|
169
210
|
},
|
|
170
211
|
},
|
|
171
212
|
rowDefault: {
|
|
172
213
|
grid: {
|
|
173
|
-
size:
|
|
214
|
+
size: cellSize,
|
|
174
215
|
resizeable: false,
|
|
175
216
|
},
|
|
176
217
|
},
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
218
|
+
getCells: (range, plane) => {
|
|
219
|
+
const cells: DxGridPlaneCells = {};
|
|
220
|
+
if (plane === 'grid') {
|
|
221
|
+
for (let col = range.start.col; col <= range.end.col; col++) {
|
|
222
|
+
for (let row = range.start.row; row <= range.end.row; row++) {
|
|
223
|
+
// TODO(burdon): Formatting changes when cell is selected.
|
|
224
|
+
cells[toPlaneCellIndex({ col, row })] = {
|
|
225
|
+
readonly: true,
|
|
226
|
+
accessoryHtml: '<div class="flex h-full w-full justify-center items-center overflow-hidden">0</div>',
|
|
227
|
+
className: '',
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return cells;
|
|
181
233
|
},
|
|
182
234
|
},
|
|
235
|
+
render: (args) => (
|
|
236
|
+
<div className='h-full flex justify-center'>
|
|
237
|
+
<div className='h-full w-[288px] border-x border-separator'>
|
|
238
|
+
<GridStory {...args} />
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
),
|
|
183
242
|
};
|
package/src/Grid/Grid.tsx
CHANGED
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@
|
|
6
|
-
|
|
7
|
-
import { createComponent, type EventName } from '@lit/react';
|
|
8
|
-
import { createContextScope, type Scope } from '@radix-ui/react-context';
|
|
5
|
+
import { type EventName, createComponent } from '@lit/react';
|
|
6
|
+
import { type Scope, createContextScope } from '@radix-ui/react-context';
|
|
9
7
|
import { useControllableState } from '@radix-ui/react-use-controllable-state';
|
|
10
8
|
import React, {
|
|
11
9
|
type ComponentProps,
|
|
@@ -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
|
-
{
|
|
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
|
|
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-
|
|
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-
|
|
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,
|
|
171
|
+
export { GridRoot, GridContent, createGridScope, gridSeparatorInlineEnd, gridSeparatorBlockEnd, useGridContext };
|
|
170
172
|
|
|
171
173
|
export type { GridRootProps, GridContentProps, GridEditing, GridEditBox, GridScopedProps, DxGridElement };
|
|
172
174
|
|
|
@@ -178,6 +180,7 @@ export {
|
|
|
178
180
|
toPlaneCellIndex,
|
|
179
181
|
parseCellIndex,
|
|
180
182
|
cellQuery,
|
|
183
|
+
DxEditRequest,
|
|
181
184
|
} from '@dxos/lit-grid';
|
|
182
185
|
|
|
183
186
|
export type {
|