@dxos/react-ui-editor 0.8.3-main.672df60 → 0.8.3-main.7f5a14c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/index.mjs +868 -260
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +911 -297
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +868 -260
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/EditorToolbar/util.d.ts +2 -2
- package/dist/types/src/components/Popover/CommandMenu.d.ts +34 -0
- package/dist/types/src/components/Popover/CommandMenu.d.ts.map +1 -0
- package/dist/types/src/components/Popover/RefPopover.d.ts +19 -6
- package/dist/types/src/components/Popover/RefPopover.d.ts.map +1 -1
- package/dist/types/src/components/Popover/index.d.ts +1 -0
- package/dist/types/src/components/Popover/index.d.ts.map +1 -1
- package/dist/types/src/defaults.d.ts.map +1 -1
- package/dist/types/src/extensions/command/menu.d.ts +40 -0
- package/dist/types/src/extensions/command/menu.d.ts.map +1 -1
- package/dist/types/src/extensions/factories.d.ts +1 -0
- package/dist/types/src/extensions/factories.d.ts.map +1 -1
- package/dist/types/src/extensions/index.d.ts +1 -0
- package/dist/types/src/extensions/index.d.ts.map +1 -1
- package/dist/types/src/extensions/placeholder.d.ts +4 -0
- package/dist/types/src/extensions/placeholder.d.ts.map +1 -0
- package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
- package/dist/types/src/hooks/useTextEditor.d.ts +8 -9
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
- package/dist/types/src/stories/CommandMenu.stories.d.ts +12 -0
- package/dist/types/src/stories/CommandMenu.stories.d.ts.map +1 -0
- package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
- package/package.json +31 -28
- package/src/components/Popover/CommandMenu.tsx +279 -0
- package/src/components/Popover/RefPopover.tsx +44 -22
- package/src/components/Popover/index.ts +1 -0
- package/src/defaults.ts +1 -0
- package/src/extensions/command/menu.ts +306 -4
- package/src/extensions/factories.ts +4 -1
- package/src/extensions/index.ts +1 -0
- package/src/extensions/outliner/outliner.ts +0 -3
- package/src/extensions/placeholder.ts +82 -0
- package/src/extensions/preview/preview.ts +3 -6
- package/src/hooks/useTextEditor.ts +11 -12
- package/src/stories/CommandMenu.stories.tsx +143 -0
- package/src/stories/Preview.stories.tsx +32 -30
@@ -0,0 +1,143 @@
|
|
1
|
+
//
|
2
|
+
// Copyright 2023 DXOS.org
|
3
|
+
//
|
4
|
+
|
5
|
+
import '@dxos-theme';
|
6
|
+
|
7
|
+
import { type EditorView } from '@codemirror/view';
|
8
|
+
import { type StoryObj } from '@storybook/react';
|
9
|
+
import React, { useCallback, useRef } from 'react';
|
10
|
+
|
11
|
+
import { Obj, Query } from '@dxos/echo';
|
12
|
+
import { faker } from '@dxos/random';
|
13
|
+
import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
|
14
|
+
import { createObjectFactory, Testing, type ValueGenerator } from '@dxos/schema/testing';
|
15
|
+
import { withLayout, withTheme, type Meta } from '@dxos/storybook-utils';
|
16
|
+
|
17
|
+
import { EditorStory, names } from './components';
|
18
|
+
import {
|
19
|
+
coreSlashCommands,
|
20
|
+
filterItems,
|
21
|
+
RefPopover,
|
22
|
+
type CommandMenuGroup,
|
23
|
+
CommandMenu,
|
24
|
+
type CommandMenuItem,
|
25
|
+
insertAtCursor,
|
26
|
+
insertAtLineStart,
|
27
|
+
linkSlashCommands,
|
28
|
+
} from '../components';
|
29
|
+
import { useCommandMenu, type UseCommandMenuOptions } from '../extensions';
|
30
|
+
import { str } from '../testing';
|
31
|
+
|
32
|
+
const generator: ValueGenerator = faker as any;
|
33
|
+
|
34
|
+
type Args = Omit<UseCommandMenuOptions, 'viewRef'> & { text: string };
|
35
|
+
|
36
|
+
const Story = ({ text, ...options }: Args) => {
|
37
|
+
const viewRef = useRef<EditorView>();
|
38
|
+
const { commandMenu, groupsRef, currentItem, onSelect, ...props } = useCommandMenu({ viewRef, ...options });
|
39
|
+
|
40
|
+
return (
|
41
|
+
<RefPopover modal={false} {...props}>
|
42
|
+
<EditorStory ref={viewRef} text={text} placeholder={''} extensions={commandMenu} />
|
43
|
+
<CommandMenu groups={groupsRef.current} currentItem={currentItem} onSelect={onSelect} />
|
44
|
+
</RefPopover>
|
45
|
+
);
|
46
|
+
};
|
47
|
+
|
48
|
+
const groups: CommandMenuGroup[] = [
|
49
|
+
coreSlashCommands,
|
50
|
+
linkSlashCommands,
|
51
|
+
{
|
52
|
+
id: 'custom',
|
53
|
+
label: 'Custom',
|
54
|
+
items: [
|
55
|
+
{
|
56
|
+
id: 'custom-1',
|
57
|
+
label: 'Log',
|
58
|
+
icon: 'ph--log--regular',
|
59
|
+
onSelect: console.log,
|
60
|
+
},
|
61
|
+
],
|
62
|
+
},
|
63
|
+
];
|
64
|
+
|
65
|
+
const meta: Meta<Args> = {
|
66
|
+
title: 'ui/react-ui-editor/CommandMenu',
|
67
|
+
decorators: [withTheme, withLayout({ fullscreen: true })],
|
68
|
+
render: (args) => <Story {...args} />,
|
69
|
+
parameters: { layout: 'fullscreen' },
|
70
|
+
};
|
71
|
+
|
72
|
+
export default meta;
|
73
|
+
|
74
|
+
export const Slash: StoryObj<Args> = {
|
75
|
+
args: {
|
76
|
+
trigger: '/',
|
77
|
+
getGroups: (query) =>
|
78
|
+
filterItems(groups, (item) =>
|
79
|
+
query ? (item.label as string).toLowerCase().includes(query.toLowerCase()) : true,
|
80
|
+
),
|
81
|
+
text: str('# Slash', '', names.join(' '), ''),
|
82
|
+
},
|
83
|
+
};
|
84
|
+
|
85
|
+
export const Link: StoryObj<Args> = {
|
86
|
+
render: (args) => {
|
87
|
+
const { space } = useClientProvider();
|
88
|
+
const getGroups = useCallback(
|
89
|
+
async (trigger: string, query?: string): Promise<CommandMenuGroup[]> => {
|
90
|
+
if (trigger === '/') {
|
91
|
+
return filterItems(groups, (item) =>
|
92
|
+
query ? (item.label as string).toLowerCase().includes(query.toLowerCase()) : true,
|
93
|
+
);
|
94
|
+
}
|
95
|
+
|
96
|
+
if (!space) {
|
97
|
+
return [];
|
98
|
+
}
|
99
|
+
|
100
|
+
const name = query?.startsWith('@') ? query.slice(1).toLowerCase() : query?.toLowerCase() ?? '';
|
101
|
+
const result = await space?.db.query(Query.type(Testing.Contact)).run();
|
102
|
+
const items = result.objects
|
103
|
+
.filter((object) => object.name.toLowerCase().includes(name))
|
104
|
+
.map(
|
105
|
+
(object): CommandMenuItem => ({
|
106
|
+
id: object.id,
|
107
|
+
label: object.name,
|
108
|
+
icon: 'ph--user--regular',
|
109
|
+
onSelect: (view, head) => {
|
110
|
+
const link = `[${object.name}][${Obj.getDXN(object)}]`;
|
111
|
+
if (query?.startsWith('@')) {
|
112
|
+
insertAtLineStart(view, head, `!${link}\n`);
|
113
|
+
} else {
|
114
|
+
insertAtCursor(view, head, `${link} `);
|
115
|
+
}
|
116
|
+
},
|
117
|
+
}),
|
118
|
+
);
|
119
|
+
return [{ id: 'echo', items }];
|
120
|
+
},
|
121
|
+
[space],
|
122
|
+
);
|
123
|
+
|
124
|
+
return <Story {...args} getGroups={getGroups} />;
|
125
|
+
},
|
126
|
+
args: {
|
127
|
+
trigger: ['/', '@'],
|
128
|
+
text: str('# Link', '', names.join(' '), ''),
|
129
|
+
},
|
130
|
+
decorators: [
|
131
|
+
withClientProvider({
|
132
|
+
createSpace: true,
|
133
|
+
onInitialized: async (client) => {
|
134
|
+
client.addTypes([Testing.Contact]);
|
135
|
+
},
|
136
|
+
onSpaceCreated: async ({ space }) => {
|
137
|
+
const createObjects = createObjectFactory(space.db, generator);
|
138
|
+
await createObjects([{ type: Testing.Contact, count: 10 }]);
|
139
|
+
await space.db.flush({ indexes: true });
|
140
|
+
},
|
141
|
+
}),
|
142
|
+
],
|
143
|
+
};
|
@@ -7,12 +7,13 @@ import '@dxos-theme';
|
|
7
7
|
import React, { useState, useEffect, type FC } from 'react';
|
8
8
|
|
9
9
|
import { faker } from '@dxos/random';
|
10
|
-
import {
|
11
|
-
import {
|
10
|
+
import { Popover } from '@dxos/react-ui';
|
11
|
+
import { Card } from '@dxos/react-ui-stack';
|
12
|
+
import { hoverableControlItem, hoverableControlItemTransition, hoverableControls } from '@dxos/react-ui-theme';
|
12
13
|
import { withLayout, withTheme, type Meta } from '@dxos/storybook-utils';
|
13
14
|
|
14
15
|
import { EditorStory } from './components';
|
15
|
-
import {
|
16
|
+
import { PreviewProvider, useRefPopover } from '../components';
|
16
17
|
import {
|
17
18
|
preview,
|
18
19
|
image,
|
@@ -49,10 +50,12 @@ const PreviewCard = () => {
|
|
49
50
|
const { target } = useRefPopover('PreviewCard');
|
50
51
|
return (
|
51
52
|
<Popover.Portal>
|
52
|
-
<Popover.Content
|
53
|
+
<Popover.Content onOpenAutoFocus={(event) => event.preventDefault()}>
|
53
54
|
<Popover.Viewport>
|
54
|
-
<
|
55
|
-
|
55
|
+
<Card.Container role='popover'>
|
56
|
+
<Card.Heading>{target?.label}</Card.Heading>
|
57
|
+
{target && <Card.Text classNames='line-clamp-3'>{target.text}</Card.Text>}
|
58
|
+
</Card.Container>
|
56
59
|
</Popover.Viewport>
|
57
60
|
<Popover.Arrow />
|
58
61
|
</Popover.Content>
|
@@ -64,45 +67,44 @@ const PreviewCard = () => {
|
|
64
67
|
const PreviewBlock: FC<PreviewRenderProps> = ({ readonly, link, onAction, onLookup }) => {
|
65
68
|
const target = useRefTarget(link, onLookup);
|
66
69
|
return (
|
67
|
-
<
|
68
|
-
<div className='flex items-
|
69
|
-
<div className='grow truncate'>
|
70
|
-
{/* <span className='text-xs text-subdued mie-2'>Prompt</span> */}
|
71
|
-
{link.label}
|
72
|
-
</div>
|
70
|
+
<Card.Content classNames={hoverableControls}>
|
71
|
+
<div className='flex items-start'>
|
73
72
|
{!readonly && (
|
74
|
-
<
|
73
|
+
<Card.Toolbar classNames='is-min p-[--dx-card-spacing-inline]'>
|
75
74
|
{(link.suggest && (
|
76
75
|
<>
|
76
|
+
<Card.ToolbarIconButton
|
77
|
+
label='Discard'
|
78
|
+
icon={'ph--x--regular'}
|
79
|
+
onClick={() => onAction({ type: 'delete', link })}
|
80
|
+
/>
|
77
81
|
{target && (
|
78
|
-
<
|
79
|
-
classNames='text-
|
82
|
+
<Card.ToolbarIconButton
|
83
|
+
classNames='bg-successSurface text-successSurfaceText'
|
80
84
|
label='Apply'
|
81
|
-
icon=
|
85
|
+
icon='ph--check--regular'
|
82
86
|
onClick={() => onAction({ type: 'insert', link, target })}
|
83
87
|
/>
|
84
88
|
)}
|
85
|
-
<IconButton
|
86
|
-
classNames='text-red-500'
|
87
|
-
label='Cancel'
|
88
|
-
icon={'ph--x--regular'}
|
89
|
-
onClick={() => onAction({ type: 'delete', link })}
|
90
|
-
/>
|
91
89
|
</>
|
92
90
|
)) || (
|
93
|
-
<
|
91
|
+
<Card.ToolbarIconButton
|
94
92
|
iconOnly
|
95
93
|
label='Delete'
|
96
|
-
icon=
|
97
|
-
classNames={
|
94
|
+
icon='ph--x--regular'
|
95
|
+
classNames={[hoverableControlItem, hoverableControlItemTransition]}
|
98
96
|
onClick={() => onAction({ type: 'delete', link })}
|
99
97
|
/>
|
100
98
|
)}
|
101
|
-
</
|
99
|
+
</Card.Toolbar>
|
102
100
|
)}
|
101
|
+
<Card.Heading classNames='grow order-first mie-0'>
|
102
|
+
{/* <span className='text-xs text-subdued mie-2'>Prompt</span> */}
|
103
|
+
{link.label}
|
104
|
+
</Card.Heading>
|
103
105
|
</div>
|
104
|
-
{target && <
|
105
|
-
</
|
106
|
+
{target && <Card.Text classNames='line-clamp-3 mbs-0'>{target.text}</Card.Text>}
|
107
|
+
</Card.Content>
|
106
108
|
);
|
107
109
|
};
|
108
110
|
|
@@ -117,7 +119,7 @@ export default meta;
|
|
117
119
|
|
118
120
|
export const Default = {
|
119
121
|
render: () => (
|
120
|
-
<
|
122
|
+
<PreviewProvider onLookup={handlePreviewLookup}>
|
121
123
|
<EditorStory
|
122
124
|
text={str(
|
123
125
|
'# Preview',
|
@@ -144,6 +146,6 @@ export const Default = {
|
|
144
146
|
]}
|
145
147
|
/>
|
146
148
|
<PreviewCard />
|
147
|
-
</
|
149
|
+
</PreviewProvider>
|
148
150
|
),
|
149
151
|
};
|