@dxos/plugin-markdown 0.6.12-main.5cc132e → 0.6.12-main.89e9959
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/MarkdownContainer-OWVU5WMN.mjs +467 -0
- package/dist/lib/browser/MarkdownContainer-OWVU5WMN.mjs.map +7 -0
- package/dist/lib/{node-esm/chunk-4GGD6YJO.mjs → browser/chunk-2SJN46PA.mjs} +2 -5
- package/dist/lib/browser/chunk-2SJN46PA.mjs.map +7 -0
- package/dist/lib/browser/chunk-7WORDTCY.mjs +50 -0
- package/dist/lib/browser/chunk-7WORDTCY.mjs.map +7 -0
- package/dist/lib/browser/{chunk-7W37KPH3.mjs → chunk-OUZCML5B.mjs} +4 -2
- package/dist/lib/browser/chunk-OUZCML5B.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +68 -114
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/meta.mjs +1 -1
- package/dist/lib/browser/types/index.mjs +6 -4
- package/dist/lib/node/MarkdownContainer-IWMLWI6Z.cjs +482 -0
- package/dist/lib/node/MarkdownContainer-IWMLWI6Z.cjs.map +7 -0
- package/dist/lib/node/{chunk-EZTJSCMZ.cjs → chunk-HVDIBL5H.cjs} +11 -8
- package/dist/lib/node/chunk-HVDIBL5H.cjs.map +7 -0
- package/dist/lib/node/chunk-L2FIDO4L.cjs +72 -0
- package/dist/lib/node/chunk-L2FIDO4L.cjs.map +7 -0
- package/dist/lib/node/{MarkdownEditor-IUHHJ532.cjs → chunk-LPD4NOTH.cjs} +17 -9
- package/dist/lib/node/chunk-LPD4NOTH.cjs.map +7 -0
- package/dist/lib/node/index.cjs +103 -145
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.cjs +3 -3
- package/dist/lib/node/meta.cjs.map +1 -1
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/types/index.cjs +8 -6
- package/dist/lib/node/types/index.cjs.map +2 -2
- package/dist/lib/node-esm/MarkdownContainer-MLTDWWGB.mjs +468 -0
- package/dist/lib/node-esm/MarkdownContainer-MLTDWWGB.mjs.map +7 -0
- package/dist/lib/{browser/chunk-4GGD6YJO.mjs → node-esm/chunk-2BGTVWHN.mjs} +4 -5
- package/dist/lib/node-esm/chunk-2BGTVWHN.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-7W37KPH3.mjs → chunk-4AGP7IJE.mjs} +5 -2
- package/dist/lib/node-esm/chunk-4AGP7IJE.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-YOIARYUO.mjs +51 -0
- package/dist/lib/node-esm/chunk-YOIARYUO.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +69 -114
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/meta.mjs +2 -1
- package/dist/lib/node-esm/types/index.mjs +7 -4
- package/dist/types/src/MarkdownPlugin.d.ts.map +1 -1
- package/dist/types/src/components/MarkdownContainer.d.ts +15 -0
- package/dist/types/src/components/MarkdownContainer.d.ts.map +1 -0
- package/dist/types/src/components/MarkdownEditor.d.ts +8 -3
- package/dist/types/src/components/MarkdownEditor.d.ts.map +1 -1
- package/dist/types/src/components/MarkdownEditor.stories.d.ts +3 -3
- package/dist/types/src/components/MarkdownEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +2 -11
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/extensions.d.ts +11 -15
- package/dist/types/src/extensions.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +1 -4
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/types/document.d.ts +10 -1
- package/dist/types/src/types/document.d.ts.map +1 -1
- package/dist/types/src/types/types.d.ts +7 -8
- package/dist/types/src/types/types.d.ts.map +1 -1
- package/package.json +34 -33
- package/src/MarkdownPlugin.tsx +40 -94
- package/src/components/MarkdownContainer.tsx +108 -0
- package/src/components/MarkdownEditor.stories.tsx +11 -5
- package/src/components/MarkdownEditor.tsx +33 -39
- package/src/components/index.ts +2 -14
- package/src/extensions.tsx +124 -67
- package/src/meta.tsx +1 -5
- package/src/types/document.ts +12 -0
- package/src/types/types.ts +9 -6
- package/dist/lib/browser/DocumentCard-2P4EICBA.mjs +0 -11
- package/dist/lib/browser/DocumentCard-2P4EICBA.mjs.map +0 -7
- package/dist/lib/browser/DocumentEditor-PXYEHGQA.mjs +0 -11
- package/dist/lib/browser/DocumentEditor-PXYEHGQA.mjs.map +0 -7
- package/dist/lib/browser/MarkdownEditor-IE2ISQQF.mjs +0 -10
- package/dist/lib/browser/MarkdownEditor-IE2ISQQF.mjs.map +0 -7
- package/dist/lib/browser/chunk-354DCID5.mjs +0 -117
- package/dist/lib/browser/chunk-354DCID5.mjs.map +0 -7
- package/dist/lib/browser/chunk-4GGD6YJO.mjs.map +0 -7
- package/dist/lib/browser/chunk-5FP6MXCU.mjs +0 -164
- package/dist/lib/browser/chunk-5FP6MXCU.mjs.map +0 -7
- package/dist/lib/browser/chunk-7W37KPH3.mjs.map +0 -7
- package/dist/lib/browser/chunk-RL7QY322.mjs +0 -86
- package/dist/lib/browser/chunk-RL7QY322.mjs.map +0 -7
- package/dist/lib/browser/chunk-RQLDAFNM.mjs +0 -216
- package/dist/lib/browser/chunk-RQLDAFNM.mjs.map +0 -7
- package/dist/lib/node/DocumentCard-EHJDDSRY.cjs +0 -32
- package/dist/lib/node/DocumentCard-EHJDDSRY.cjs.map +0 -7
- package/dist/lib/node/DocumentEditor-DZOOGCBM.cjs +0 -29
- package/dist/lib/node/DocumentEditor-DZOOGCBM.cjs.map +0 -7
- package/dist/lib/node/MarkdownEditor-IUHHJ532.cjs.map +0 -7
- package/dist/lib/node/chunk-DFWSVQEA.cjs +0 -189
- package/dist/lib/node/chunk-DFWSVQEA.cjs.map +0 -7
- package/dist/lib/node/chunk-EZTJSCMZ.cjs.map +0 -7
- package/dist/lib/node/chunk-GRU6YP3J.cjs +0 -242
- package/dist/lib/node/chunk-GRU6YP3J.cjs.map +0 -7
- package/dist/lib/node/chunk-KTYIOXL5.cjs +0 -149
- package/dist/lib/node/chunk-KTYIOXL5.cjs.map +0 -7
- package/dist/lib/node/chunk-Q4ZSCBQE.cjs +0 -114
- package/dist/lib/node/chunk-Q4ZSCBQE.cjs.map +0 -7
- package/dist/lib/node/chunk-TGMR2CKU.cjs +0 -52
- package/dist/lib/node/chunk-TGMR2CKU.cjs.map +0 -7
- package/dist/lib/node-esm/DocumentCard-2P4EICBA.mjs +0 -11
- package/dist/lib/node-esm/DocumentCard-2P4EICBA.mjs.map +0 -7
- package/dist/lib/node-esm/DocumentEditor-PXYEHGQA.mjs +0 -11
- package/dist/lib/node-esm/DocumentEditor-PXYEHGQA.mjs.map +0 -7
- package/dist/lib/node-esm/MarkdownEditor-IE2ISQQF.mjs +0 -10
- package/dist/lib/node-esm/MarkdownEditor-IE2ISQQF.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-354DCID5.mjs +0 -117
- package/dist/lib/node-esm/chunk-354DCID5.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-4GGD6YJO.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-5FP6MXCU.mjs +0 -164
- package/dist/lib/node-esm/chunk-5FP6MXCU.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-7W37KPH3.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-RL7QY322.mjs +0 -86
- package/dist/lib/node-esm/chunk-RL7QY322.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-RQLDAFNM.mjs +0 -216
- package/dist/lib/node-esm/chunk-RQLDAFNM.mjs.map +0 -7
- package/dist/types/src/components/DocumentCard.d.ts +0 -16
- package/dist/types/src/components/DocumentCard.d.ts.map +0 -1
- package/dist/types/src/components/DocumentEditor.d.ts +0 -14
- package/dist/types/src/components/DocumentEditor.d.ts.map +0 -1
- package/dist/types/src/components/HeadingMenu.d.ts +0 -13
- package/dist/types/src/components/HeadingMenu.d.ts.map +0 -1
- package/dist/types/src/components/Layout.d.ts +0 -6
- package/dist/types/src/components/Layout.d.ts.map +0 -1
- package/src/components/DocumentCard.tsx +0 -107
- package/src/components/DocumentEditor.tsx +0 -137
- package/src/components/HeadingMenu.tsx +0 -46
- package/src/components/Layout.tsx +0 -27
package/src/MarkdownPlugin.tsx
CHANGED
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import React
|
|
5
|
+
import { TextAa } from '@phosphor-icons/react';
|
|
6
|
+
import React from 'react';
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
|
-
isObject,
|
|
10
9
|
parseIntentPlugin,
|
|
11
10
|
resolvePlugin,
|
|
12
11
|
LayoutAction,
|
|
@@ -16,7 +15,6 @@ import {
|
|
|
16
15
|
} from '@dxos/app-framework';
|
|
17
16
|
import { create } from '@dxos/echo-schema';
|
|
18
17
|
import { LocalStorageStore } from '@dxos/local-storage';
|
|
19
|
-
import { log } from '@dxos/log';
|
|
20
18
|
import { parseClientPlugin } from '@dxos/plugin-client';
|
|
21
19
|
import { type ActionGroup, createExtension, isActionGroup } from '@dxos/plugin-graph';
|
|
22
20
|
import { SpaceAction } from '@dxos/plugin-space';
|
|
@@ -34,12 +32,11 @@ import {
|
|
|
34
32
|
EditorViewModes,
|
|
35
33
|
translations as editorTranslations,
|
|
36
34
|
} from '@dxos/react-ui-editor';
|
|
37
|
-
import { isTileComponentProps } from '@dxos/react-ui-mosaic';
|
|
38
35
|
|
|
39
|
-
import {
|
|
36
|
+
import { MarkdownContainer, MarkdownSettings } from './components';
|
|
40
37
|
import meta, { MARKDOWN_PLUGIN } from './meta';
|
|
41
38
|
import translations from './translations';
|
|
42
|
-
import { DocumentType, TextType } from './types';
|
|
39
|
+
import { DocumentType, isEditorModel, TextType } from './types';
|
|
43
40
|
import {
|
|
44
41
|
type MarkdownPluginProvides,
|
|
45
42
|
type MarkdownSettingsProps,
|
|
@@ -48,19 +45,8 @@ import {
|
|
|
48
45
|
} from './types';
|
|
49
46
|
import { markdownExtensionPlugins, serializer } from './util';
|
|
50
47
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
*/
|
|
54
|
-
const isEditorModel = (data: any): data is { id: string; text: string } => {
|
|
55
|
-
return (
|
|
56
|
-
data &&
|
|
57
|
-
typeof data === 'object' &&
|
|
58
|
-
'id' in data &&
|
|
59
|
-
typeof data.id === 'string' &&
|
|
60
|
-
'text' in data &&
|
|
61
|
-
typeof data.text === 'string'
|
|
62
|
-
);
|
|
63
|
-
};
|
|
48
|
+
// TODO(burdon): Normalize active/object.
|
|
49
|
+
const getDoc = (object: any) => (object instanceof DocumentType ? object : undefined);
|
|
64
50
|
|
|
65
51
|
export const MarkdownPlugin = (): PluginDefinition<MarkdownPluginProvides> => {
|
|
66
52
|
const settings = new LocalStorageStore<MarkdownSettingsProps>(MARKDOWN_PLUGIN, {
|
|
@@ -72,13 +58,8 @@ export const MarkdownPlugin = (): PluginDefinition<MarkdownPluginProvides> => {
|
|
|
72
58
|
|
|
73
59
|
const state = new LocalStorageStore<MarkdownPluginState>(MARKDOWN_PLUGIN, { extensionProviders: [], viewMode: {} });
|
|
74
60
|
|
|
75
|
-
const getViewMode = (id
|
|
76
|
-
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
const setViewMode = (id: string, nextViewMode: EditorViewMode) => {
|
|
80
|
-
state.values.viewMode[id] = nextViewMode;
|
|
81
|
-
};
|
|
61
|
+
const getViewMode = (id: string) => (id && state.values.viewMode[id]) || settings.values.defaultViewMode;
|
|
62
|
+
const setViewMode = (id: string, viewMode: EditorViewMode) => (state.values.viewMode[id] = viewMode);
|
|
82
63
|
|
|
83
64
|
return {
|
|
84
65
|
meta,
|
|
@@ -109,7 +90,7 @@ export const MarkdownPlugin = (): PluginDefinition<MarkdownPluginProvides> => {
|
|
|
109
90
|
|
|
110
91
|
markdownExtensionPlugins(plugins).forEach((plugin) => {
|
|
111
92
|
const { extensions } = plugin.provides.markdown;
|
|
112
|
-
state.values.extensionProviders
|
|
93
|
+
state.values.extensionProviders?.push(extensions);
|
|
113
94
|
});
|
|
114
95
|
},
|
|
115
96
|
provides: {
|
|
@@ -119,8 +100,7 @@ export const MarkdownPlugin = (): PluginDefinition<MarkdownPluginProvides> => {
|
|
|
119
100
|
[DocumentType.typename]: {
|
|
120
101
|
label: (object: any) => (object instanceof DocumentType ? object.name ?? object.fallbackName : undefined),
|
|
121
102
|
placeholder: ['document title placeholder', { ns: MARKDOWN_PLUGIN }],
|
|
122
|
-
icon:
|
|
123
|
-
iconSymbol: 'ph--text-aa--regular',
|
|
103
|
+
icon: 'ph--text-aa--regular',
|
|
124
104
|
graphProps: {
|
|
125
105
|
managesAutofocus: true,
|
|
126
106
|
},
|
|
@@ -134,6 +114,12 @@ export const MarkdownPlugin = (): PluginDefinition<MarkdownPluginProvides> => {
|
|
|
134
114
|
echo: {
|
|
135
115
|
schema: [DocumentType, TextType],
|
|
136
116
|
},
|
|
117
|
+
space: {
|
|
118
|
+
onSpaceCreate: {
|
|
119
|
+
label: ['create document label', { ns: MARKDOWN_PLUGIN }],
|
|
120
|
+
action: MarkdownAction.CREATE,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
137
123
|
graph: {
|
|
138
124
|
builder: (plugins) => {
|
|
139
125
|
const client = resolvePlugin(plugins, parseClientPlugin)?.provides.client;
|
|
@@ -167,8 +153,7 @@ export const MarkdownPlugin = (): PluginDefinition<MarkdownPluginProvides> => {
|
|
|
167
153
|
},
|
|
168
154
|
properties: {
|
|
169
155
|
label: ['create document label', { ns: MARKDOWN_PLUGIN }],
|
|
170
|
-
icon:
|
|
171
|
-
iconSymbol: 'ph--text-aa--regular',
|
|
156
|
+
icon: 'ph--text-aa--regular',
|
|
172
157
|
testId: 'markdownPlugin.createObject',
|
|
173
158
|
},
|
|
174
159
|
},
|
|
@@ -244,9 +229,7 @@ export const MarkdownPlugin = (): PluginDefinition<MarkdownPluginProvides> => {
|
|
|
244
229
|
predicate: (obj) => obj instanceof DocumentType,
|
|
245
230
|
createSort: (doc: DocumentType) => {
|
|
246
231
|
const accessor = doc.content ? createDocAccessor(doc.content, ['content']) : undefined;
|
|
247
|
-
|
|
248
232
|
if (!accessor) {
|
|
249
|
-
log.warn('No accessor found for document content.');
|
|
250
233
|
return (_) => 0;
|
|
251
234
|
}
|
|
252
235
|
|
|
@@ -266,71 +249,34 @@ export const MarkdownPlugin = (): PluginDefinition<MarkdownPluginProvides> => {
|
|
|
266
249
|
},
|
|
267
250
|
},
|
|
268
251
|
surface: {
|
|
269
|
-
component: ({ data, role
|
|
270
|
-
const doc =
|
|
271
|
-
data.active instanceof DocumentType
|
|
272
|
-
? data.active
|
|
273
|
-
: data.object instanceof DocumentType
|
|
274
|
-
? data.object
|
|
275
|
-
: undefined;
|
|
276
|
-
|
|
252
|
+
component: ({ data, role }) => {
|
|
277
253
|
switch (role) {
|
|
278
254
|
case 'section':
|
|
279
255
|
case 'article': {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
settings={settings.values}
|
|
288
|
-
scrollPastEnd
|
|
289
|
-
viewMode={getViewMode(fullyQualifiedId(doc))}
|
|
290
|
-
onViewModeChange={setViewMode}
|
|
291
|
-
/>
|
|
292
|
-
);
|
|
293
|
-
} else if (isEditorModel(data.object)) {
|
|
294
|
-
return (
|
|
295
|
-
<MarkdownEditor
|
|
296
|
-
id={data.object.id}
|
|
297
|
-
role={role}
|
|
298
|
-
coordinate={data.coordinate as LayoutCoordinate}
|
|
299
|
-
initialValue={data.object.text}
|
|
300
|
-
extensionProviders={state.values.extensionProviders}
|
|
301
|
-
inputMode={settings.values.editorInputMode}
|
|
302
|
-
toolbar={settings.values.toolbar}
|
|
303
|
-
scrollPastEnd
|
|
304
|
-
viewMode={getViewMode(data.object.id)}
|
|
305
|
-
onViewModeChange={setViewMode}
|
|
306
|
-
/>
|
|
307
|
-
);
|
|
308
|
-
}
|
|
309
|
-
break;
|
|
310
|
-
}
|
|
256
|
+
// TODO(burdon): Normalize types (from FilesPlugin).
|
|
257
|
+
const doc = getDoc(data.active) ?? getDoc(data.object);
|
|
258
|
+
const { id, object } = isEditorModel(data.object)
|
|
259
|
+
? { id: data.object.id, object: data.object }
|
|
260
|
+
: doc
|
|
261
|
+
? { id: fullyQualifiedId(doc), object: doc }
|
|
262
|
+
: {};
|
|
311
263
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
isObject(data.content) &&
|
|
315
|
-
typeof data.content.id === 'string' &&
|
|
316
|
-
data.content.object instanceof DocumentType
|
|
317
|
-
) {
|
|
318
|
-
// isTileComponentProps is a type guard for these props.
|
|
319
|
-
// `props` will not pass this guard without transforming `data` into `item`.
|
|
320
|
-
const cardProps = {
|
|
321
|
-
...props,
|
|
322
|
-
item: {
|
|
323
|
-
id: data.content.id,
|
|
324
|
-
object: data.content.object,
|
|
325
|
-
color: typeof data.content.color === 'string' ? data.content.color : undefined,
|
|
326
|
-
} as DocumentItemProps,
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
return isTileComponentProps(cardProps) ? (
|
|
330
|
-
<DocumentCard {...cardProps} settings={settings.values} ref={forwardedRef as Ref<HTMLDivElement>} />
|
|
331
|
-
) : null;
|
|
264
|
+
if (!id || !object) {
|
|
265
|
+
return null;
|
|
332
266
|
}
|
|
333
|
-
|
|
267
|
+
|
|
268
|
+
return (
|
|
269
|
+
<MarkdownContainer
|
|
270
|
+
id={id}
|
|
271
|
+
object={object}
|
|
272
|
+
role={role}
|
|
273
|
+
coordinate={data.coordinate as LayoutCoordinate}
|
|
274
|
+
settings={settings.values}
|
|
275
|
+
extensionProviders={state.values.extensionProviders}
|
|
276
|
+
viewMode={getViewMode(id)}
|
|
277
|
+
onViewModeChange={setViewMode}
|
|
278
|
+
/>
|
|
279
|
+
);
|
|
334
280
|
}
|
|
335
281
|
|
|
336
282
|
case 'settings': {
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useEffect, useMemo } from 'react';
|
|
6
|
+
|
|
7
|
+
import { useResolvePlugin, parseFileManagerPlugin } from '@dxos/app-framework';
|
|
8
|
+
import { fullyQualifiedId, getSpace } from '@dxos/react-client/echo';
|
|
9
|
+
import { localStorageStateStoreAdapter, type EditorSelectionState } from '@dxos/react-ui-editor';
|
|
10
|
+
|
|
11
|
+
import { MarkdownEditor, type MarkdownEditorProps } from './MarkdownEditor';
|
|
12
|
+
import { useExtensions } from '../extensions';
|
|
13
|
+
import { DocumentType, type MarkdownSettingsProps } from '../types';
|
|
14
|
+
import { getFallbackName } from '../util';
|
|
15
|
+
|
|
16
|
+
export type MarkdownContainerProps = Pick<
|
|
17
|
+
MarkdownEditorProps,
|
|
18
|
+
'role' | 'coordinate' | 'extensionProviders' | 'viewMode' | 'onViewModeChange'
|
|
19
|
+
> & {
|
|
20
|
+
id: string;
|
|
21
|
+
object: DocumentType | any;
|
|
22
|
+
settings: MarkdownSettingsProps;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// TODO(burdon): Factor out difference for ECHO and non-ECHO objects; i.e., single component.
|
|
26
|
+
const MarkdownContainer = ({ role, id, object, settings, ...props }: MarkdownContainerProps) => {
|
|
27
|
+
const scrollPastEnd = role === 'article';
|
|
28
|
+
if (object instanceof DocumentType) {
|
|
29
|
+
return (
|
|
30
|
+
<DocumentEditor
|
|
31
|
+
id={fullyQualifiedId(object)}
|
|
32
|
+
document={object}
|
|
33
|
+
settings={settings}
|
|
34
|
+
scrollPastEnd={scrollPastEnd}
|
|
35
|
+
{...props}
|
|
36
|
+
/>
|
|
37
|
+
);
|
|
38
|
+
} else {
|
|
39
|
+
return (
|
|
40
|
+
<MarkdownEditor
|
|
41
|
+
id={id}
|
|
42
|
+
initialValue={object.text}
|
|
43
|
+
toolbar={settings.toolbar}
|
|
44
|
+
scrollPastEnd={scrollPastEnd}
|
|
45
|
+
{...props}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
type DocumentEditorProps = Omit<MarkdownContainerProps, 'object'> & { document: DocumentType } & Pick<
|
|
52
|
+
MarkdownEditorProps,
|
|
53
|
+
'id' | 'scrollPastEnd'
|
|
54
|
+
>;
|
|
55
|
+
|
|
56
|
+
export const DocumentEditor = ({
|
|
57
|
+
id,
|
|
58
|
+
document: doc,
|
|
59
|
+
extensionProviders,
|
|
60
|
+
settings,
|
|
61
|
+
viewMode,
|
|
62
|
+
...props
|
|
63
|
+
}: DocumentEditorProps) => {
|
|
64
|
+
const space = getSpace(doc);
|
|
65
|
+
const initialValue = useMemo(() => doc.content?.content, [doc.content]);
|
|
66
|
+
const extensions = useExtensions({ extensionProviders, document: doc, settings, viewMode });
|
|
67
|
+
|
|
68
|
+
// Migrate gradually to `fallbackName`.
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (!doc.fallbackName && doc.content?.content) {
|
|
71
|
+
doc.fallbackName = getFallbackName(doc.content.content);
|
|
72
|
+
}
|
|
73
|
+
}, [doc, doc.content]);
|
|
74
|
+
|
|
75
|
+
// Restore last selection and scroll point.
|
|
76
|
+
const { scrollTo, selection } = useMemo<EditorSelectionState>(
|
|
77
|
+
() => localStorageStateStoreAdapter.getState(id) ?? {},
|
|
78
|
+
[id, doc],
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// File dragging.
|
|
82
|
+
const fileManagerPlugin = useResolvePlugin(parseFileManagerPlugin);
|
|
83
|
+
const handleFileUpload = useMemo(() => {
|
|
84
|
+
if (space === undefined || fileManagerPlugin?.provides.file.upload === undefined) {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// TODO(burdon): Re-order props: space, file.
|
|
89
|
+
return async (file: File) => fileManagerPlugin?.provides?.file?.upload?.(file, space);
|
|
90
|
+
}, [space, fileManagerPlugin]);
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<MarkdownEditor
|
|
94
|
+
id={id}
|
|
95
|
+
initialValue={initialValue}
|
|
96
|
+
extensions={extensions}
|
|
97
|
+
scrollTo={scrollTo}
|
|
98
|
+
selection={selection}
|
|
99
|
+
toolbar={settings.toolbar}
|
|
100
|
+
inputMode={settings.editorInputMode}
|
|
101
|
+
viewMode={viewMode}
|
|
102
|
+
onFileUpload={handleFileUpload}
|
|
103
|
+
{...props}
|
|
104
|
+
/>
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export default MarkdownContainer;
|
|
@@ -3,13 +3,15 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
|
+
|
|
6
7
|
import React, { useMemo, type FC } from 'react';
|
|
7
8
|
|
|
8
9
|
import { createDocAccessor, createEchoObject } from '@dxos/react-client/echo';
|
|
9
|
-
import {
|
|
10
|
+
import { Main } from '@dxos/react-ui';
|
|
11
|
+
import { editorWithToolbarLayout, automerge } from '@dxos/react-ui-editor';
|
|
12
|
+
import { topbarBlockPaddingStart } from '@dxos/react-ui-theme';
|
|
10
13
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
11
14
|
|
|
12
|
-
import { MainLayout } from './Layout';
|
|
13
15
|
import { MarkdownEditor } from './MarkdownEditor';
|
|
14
16
|
|
|
15
17
|
const Story: FC<{
|
|
@@ -20,9 +22,13 @@ const Story: FC<{
|
|
|
20
22
|
const extensions = useMemo(() => [automerge(createDocAccessor(doc, ['content']))], [doc]);
|
|
21
23
|
|
|
22
24
|
return (
|
|
23
|
-
<
|
|
25
|
+
<Main.Content
|
|
26
|
+
bounce
|
|
27
|
+
data-toolbar={toolbar ? 'enabled' : 'disabled'}
|
|
28
|
+
classNames={[topbarBlockPaddingStart, editorWithToolbarLayout]}
|
|
29
|
+
>
|
|
24
30
|
<MarkdownEditor id='test' initialValue={doc.content} extensions={extensions} toolbar={toolbar} />
|
|
25
|
-
</
|
|
31
|
+
</Main.Content>
|
|
26
32
|
);
|
|
27
33
|
};
|
|
28
34
|
|
|
@@ -30,8 +36,8 @@ export default {
|
|
|
30
36
|
title: 'plugin-markdown/EditorMain',
|
|
31
37
|
component: MarkdownEditor,
|
|
32
38
|
decorators: [withTheme, withLayout({ tooltips: true })],
|
|
33
|
-
render: Story,
|
|
34
39
|
parameters: { layout: 'fullscreen' },
|
|
40
|
+
render: Story,
|
|
35
41
|
};
|
|
36
42
|
|
|
37
43
|
const content = Array.from({ length: 100 })
|
|
@@ -6,57 +6,44 @@ import { openSearchPanel } from '@codemirror/search';
|
|
|
6
6
|
import { type EditorView } from '@codemirror/view';
|
|
7
7
|
import React, { useMemo, useEffect, useCallback } from 'react';
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
type FileInfo,
|
|
11
|
-
LayoutAction,
|
|
12
|
-
type LayoutCoordinate,
|
|
13
|
-
useResolvePlugin,
|
|
14
|
-
parseLayoutPlugin,
|
|
15
|
-
useIntentDispatcher,
|
|
16
|
-
} from '@dxos/app-framework';
|
|
9
|
+
import { type FileInfo, LayoutAction, type LayoutCoordinate, useIntentDispatcher } from '@dxos/app-framework';
|
|
17
10
|
import { useThemeContext, useTranslation } from '@dxos/react-ui';
|
|
18
|
-
import {
|
|
11
|
+
import { useAttendableAttributes, useAttention } from '@dxos/react-ui-attention';
|
|
19
12
|
import {
|
|
20
13
|
type Action,
|
|
21
14
|
type DNDOptions,
|
|
22
15
|
type EditorViewMode,
|
|
23
16
|
type EditorInputMode,
|
|
24
|
-
type UseTextEditorProps,
|
|
25
17
|
Toolbar,
|
|
18
|
+
type UseTextEditorProps,
|
|
26
19
|
createBasicExtensions,
|
|
27
20
|
createMarkdownExtensions,
|
|
28
21
|
createThemeExtensions,
|
|
29
22
|
dropFile,
|
|
23
|
+
editorContent,
|
|
24
|
+
editorGutter,
|
|
30
25
|
processAction,
|
|
31
26
|
useActionHandler,
|
|
32
27
|
useCommentState,
|
|
33
28
|
useCommentClickListener,
|
|
34
29
|
useFormattingState,
|
|
35
30
|
useTextEditor,
|
|
36
|
-
editorContent,
|
|
37
|
-
editorGutter,
|
|
38
31
|
} from '@dxos/react-ui-editor';
|
|
39
32
|
import { sectionToolbarLayout } from '@dxos/react-ui-stack';
|
|
40
33
|
import { textBlockWidth, focusRing, mx } from '@dxos/react-ui-theme';
|
|
41
|
-
import { nonNullable } from '@dxos/util';
|
|
34
|
+
import { isNotFalsy, nonNullable } from '@dxos/util';
|
|
42
35
|
|
|
43
36
|
import { useSelectCurrentThread } from '../hooks';
|
|
44
37
|
import { MARKDOWN_PLUGIN } from '../meta';
|
|
45
|
-
import type
|
|
46
|
-
|
|
47
|
-
// TODO(Zan): Factor into a shared location.
|
|
48
|
-
const attentionFragment = mx(
|
|
49
|
-
'group-focus-within/editor:attention-surface group-[[aria-current]]/editor:attention-surface',
|
|
50
|
-
'group-focus-within/editor:border-separator',
|
|
51
|
-
);
|
|
38
|
+
import { type MarkdownPluginState } from '../types';
|
|
52
39
|
|
|
53
40
|
const DEFAULT_VIEW_MODE: EditorViewMode = 'preview';
|
|
54
41
|
|
|
55
42
|
export type MarkdownEditorProps = {
|
|
56
43
|
id: string;
|
|
44
|
+
role?: string;
|
|
57
45
|
coordinate?: LayoutCoordinate;
|
|
58
46
|
inputMode?: EditorInputMode;
|
|
59
|
-
role?: string;
|
|
60
47
|
scrollPastEnd?: boolean;
|
|
61
48
|
toolbar?: boolean;
|
|
62
49
|
viewMode?: EditorViewMode;
|
|
@@ -65,6 +52,12 @@ export type MarkdownEditorProps = {
|
|
|
65
52
|
} & Pick<UseTextEditorProps, 'initialValue' | 'scrollTo' | 'selection' | 'extensions'> &
|
|
66
53
|
Partial<Pick<MarkdownPluginState, 'extensionProviders'>>;
|
|
67
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Base markdown editor component.
|
|
57
|
+
*
|
|
58
|
+
* This component provides all the features of the markdown editor that do no depend on ECHO.
|
|
59
|
+
* This allows it to be used as a common editor for markdown content on arbitrary backends (e.g. files).
|
|
60
|
+
*/
|
|
68
61
|
export const MarkdownEditor = ({
|
|
69
62
|
id,
|
|
70
63
|
role = 'article',
|
|
@@ -82,12 +75,16 @@ export const MarkdownEditor = ({
|
|
|
82
75
|
const { t } = useTranslation(MARKDOWN_PLUGIN);
|
|
83
76
|
const { themeMode } = useThemeContext();
|
|
84
77
|
const dispatch = useIntentDispatcher();
|
|
85
|
-
const layoutPlugin = useResolvePlugin(parseLayoutPlugin);
|
|
86
78
|
const [formattingState, formattingObserver] = useFormattingState();
|
|
87
|
-
const
|
|
79
|
+
const attendableAttributes = useAttendableAttributes(id);
|
|
80
|
+
const { hasAttention } = useAttention(id);
|
|
88
81
|
|
|
89
82
|
// Extensions from other plugins.
|
|
90
|
-
|
|
83
|
+
// TODO(burdon): Reconcile with DocumentEditor.useExtensions.
|
|
84
|
+
const providerExtensions = useMemo(
|
|
85
|
+
() => extensionProviders?.flatMap((provider) => provider({})).filter(nonNullable),
|
|
86
|
+
[extensionProviders],
|
|
87
|
+
);
|
|
91
88
|
|
|
92
89
|
// TODO(Zan): Move these into thread plugin as well?
|
|
93
90
|
const [commentsState, commentObserver] = useCommentState();
|
|
@@ -128,16 +125,16 @@ export const MarkdownEditor = ({
|
|
|
128
125
|
slots: { content: { className: editorContent } },
|
|
129
126
|
}),
|
|
130
127
|
editorGutter,
|
|
131
|
-
role !== 'section' && onFileUpload
|
|
128
|
+
role !== 'section' && onFileUpload && dropFile({ onDrop: handleDrop }),
|
|
132
129
|
providerExtensions,
|
|
133
130
|
extensions,
|
|
134
|
-
].filter(
|
|
131
|
+
].filter(isNotFalsy),
|
|
135
132
|
...(role !== 'section' && {
|
|
136
133
|
id,
|
|
137
134
|
scrollTo,
|
|
138
135
|
selection,
|
|
139
136
|
// TODO(wittjosiah): Autofocus based on layout is racy.
|
|
140
|
-
autoFocus: layoutPlugin?.provides.layout ? layoutPlugin?.provides.layout.scrollIntoView === id : true,
|
|
137
|
+
// autoFocus: layoutPlugin?.provides.layout ? layoutPlugin?.provides.layout.scrollIntoView === id : true,
|
|
141
138
|
moveToEndOfLine: true,
|
|
142
139
|
}),
|
|
143
140
|
}),
|
|
@@ -173,25 +170,24 @@ export const MarkdownEditor = ({
|
|
|
173
170
|
{...(role === 'section'
|
|
174
171
|
? { className: 'flex flex-col' }
|
|
175
172
|
: {
|
|
176
|
-
className: 'contents
|
|
177
|
-
|
|
173
|
+
className: 'contents',
|
|
174
|
+
// TODO(wittjosiah): Factor out to `useAttendableAttributes`?
|
|
175
|
+
...(hasAttention && { 'aria-current': 'location' }),
|
|
176
|
+
...attendableAttributes,
|
|
178
177
|
})}
|
|
179
178
|
>
|
|
180
179
|
{toolbar && (
|
|
181
|
-
<div role='none' className=
|
|
180
|
+
<div role='none' className='flex shrink-0 justify-center overflow-x-auto attention-surface'>
|
|
182
181
|
<Toolbar.Root
|
|
183
182
|
classNames={
|
|
184
183
|
role === 'section'
|
|
185
184
|
? [
|
|
186
185
|
textBlockWidth,
|
|
187
186
|
'z-[2] group-focus-within/section:visible',
|
|
188
|
-
!
|
|
187
|
+
!hasAttention && 'invisible',
|
|
189
188
|
sectionToolbarLayout,
|
|
190
189
|
]
|
|
191
|
-
: [
|
|
192
|
-
textBlockWidth,
|
|
193
|
-
'group-focus-within/editor:border-separator group-[[aria-current]]/editor:border-separator',
|
|
194
|
-
]
|
|
190
|
+
: [textBlockWidth]
|
|
195
191
|
}
|
|
196
192
|
state={formattingState && { ...formattingState, ...commentsState }}
|
|
197
193
|
onAction={handleAction}
|
|
@@ -215,8 +211,8 @@ export const MarkdownEditor = ({
|
|
|
215
211
|
: mx(
|
|
216
212
|
'flex is-full bs-full overflow-hidden',
|
|
217
213
|
focusRing,
|
|
218
|
-
|
|
219
|
-
'
|
|
214
|
+
'focus-visible:ring-inset attention-surface',
|
|
215
|
+
'p-0.5', // TODO(burdon): Handle padding for focusRing consistently.
|
|
220
216
|
'data-[toolbar=disabled]:pbs-2 data-[toolbar=disabled]:row-span-2',
|
|
221
217
|
)
|
|
222
218
|
}
|
|
@@ -236,5 +232,3 @@ const useTest = (view?: EditorView) => {
|
|
|
236
232
|
}
|
|
237
233
|
}, [view]);
|
|
238
234
|
};
|
|
239
|
-
|
|
240
|
-
export default MarkdownEditor;
|
package/src/components/index.ts
CHANGED
|
@@ -2,20 +2,8 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import React
|
|
5
|
+
import React from 'react';
|
|
6
6
|
|
|
7
|
-
import { type DocumentEditor as DocumentEditorType } from './DocumentEditor';
|
|
8
|
-
|
|
9
|
-
export { type DocumentCardProps, type DocumentItemProps } from './DocumentCard';
|
|
10
|
-
|
|
11
|
-
export * from './DocumentCard';
|
|
12
|
-
export * from './DocumentEditor';
|
|
13
|
-
export * from './MarkdownEditor';
|
|
14
|
-
export * from './HeadingMenu';
|
|
15
|
-
export * from './Layout';
|
|
16
7
|
export * from './MarkdownSettings';
|
|
17
8
|
|
|
18
|
-
|
|
19
|
-
export const DocumentCard = React.lazy(() => import('./DocumentCard'));
|
|
20
|
-
export const DocumentEditor: LazyExoticComponent<DocumentEditorType> = React.lazy(() => import('./DocumentEditor'));
|
|
21
|
-
export const MarkdownEditor = React.lazy(() => import('./MarkdownEditor'));
|
|
9
|
+
export const MarkdownContainer = React.lazy(() => import('./MarkdownContainer'));
|