@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.
Files changed (45) hide show
  1. package/dist/lib/browser/index.mjs +868 -260
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node/index.cjs +911 -297
  5. package/dist/lib/node/index.cjs.map +4 -4
  6. package/dist/lib/node/meta.json +1 -1
  7. package/dist/lib/node-esm/index.mjs +868 -260
  8. package/dist/lib/node-esm/index.mjs.map +4 -4
  9. package/dist/lib/node-esm/meta.json +1 -1
  10. package/dist/types/src/components/EditorToolbar/util.d.ts +2 -2
  11. package/dist/types/src/components/Popover/CommandMenu.d.ts +34 -0
  12. package/dist/types/src/components/Popover/CommandMenu.d.ts.map +1 -0
  13. package/dist/types/src/components/Popover/RefPopover.d.ts +19 -6
  14. package/dist/types/src/components/Popover/RefPopover.d.ts.map +1 -1
  15. package/dist/types/src/components/Popover/index.d.ts +1 -0
  16. package/dist/types/src/components/Popover/index.d.ts.map +1 -1
  17. package/dist/types/src/defaults.d.ts.map +1 -1
  18. package/dist/types/src/extensions/command/menu.d.ts +40 -0
  19. package/dist/types/src/extensions/command/menu.d.ts.map +1 -1
  20. package/dist/types/src/extensions/factories.d.ts +1 -0
  21. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  22. package/dist/types/src/extensions/index.d.ts +1 -0
  23. package/dist/types/src/extensions/index.d.ts.map +1 -1
  24. package/dist/types/src/extensions/placeholder.d.ts +4 -0
  25. package/dist/types/src/extensions/placeholder.d.ts.map +1 -0
  26. package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
  27. package/dist/types/src/hooks/useTextEditor.d.ts +8 -9
  28. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  29. package/dist/types/src/stories/CommandMenu.stories.d.ts +12 -0
  30. package/dist/types/src/stories/CommandMenu.stories.d.ts.map +1 -0
  31. package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
  32. package/package.json +31 -28
  33. package/src/components/Popover/CommandMenu.tsx +279 -0
  34. package/src/components/Popover/RefPopover.tsx +44 -22
  35. package/src/components/Popover/index.ts +1 -0
  36. package/src/defaults.ts +1 -0
  37. package/src/extensions/command/menu.ts +306 -4
  38. package/src/extensions/factories.ts +4 -1
  39. package/src/extensions/index.ts +1 -0
  40. package/src/extensions/outliner/outliner.ts +0 -3
  41. package/src/extensions/placeholder.ts +82 -0
  42. package/src/extensions/preview/preview.ts +3 -6
  43. package/src/hooks/useTextEditor.ts +11 -12
  44. package/src/stories/CommandMenu.stories.tsx +143 -0
  45. 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 { IconButton, Popover } from '@dxos/react-ui';
11
- import { hoverableHidden } from '@dxos/react-ui-theme';
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 { RefPopover, useRefPopover } from '../components';
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 classNames='popover-card-width p-2' onOpenAutoFocus={(event) => event.preventDefault()}>
53
+ <Popover.Content onOpenAutoFocus={(event) => event.preventDefault()}>
53
54
  <Popover.Viewport>
54
- <h2 className='grow truncate'>{target?.label}</h2>
55
- {target && <div className='line-clamp-3'>{target.text}</div>}
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
- <div className='group flex flex-col gap-2'>
68
- <div className='flex items-center gap-4'>
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
- <div className='flex gap-1'>
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
- <IconButton
79
- classNames='text-green-500'
82
+ <Card.ToolbarIconButton
83
+ classNames='bg-successSurface text-successSurfaceText'
80
84
  label='Apply'
81
- icon={'ph--check--regular'}
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
- <IconButton
91
+ <Card.ToolbarIconButton
94
92
  iconOnly
95
93
  label='Delete'
96
- icon={'ph--x--regular'}
97
- classNames={hoverableHidden}
94
+ icon='ph--x--regular'
95
+ classNames={[hoverableControlItem, hoverableControlItemTransition]}
98
96
  onClick={() => onAction({ type: 'delete', link })}
99
97
  />
100
98
  )}
101
- </div>
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 && <div className='line-clamp-3'>{target.text}</div>}
105
- </div>
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
- <RefPopover.Provider onLookup={handlePreviewLookup}>
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
- </RefPopover.Provider>
149
+ </PreviewProvider>
148
150
  ),
149
151
  };