@firecms/core 3.1.0 → 3.2.0-canary.4c3b8f2
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/components/EntityCollectionView/CollectionDataErrorBanner.d.ts +4 -0
- package/dist/components/ErrorBoundary.d.ts +3 -1
- package/dist/components/HomePage/DefaultHomePage.d.ts +0 -1
- package/dist/components/LanguageToggle.d.ts +1 -0
- package/dist/components/UnsavedChangesDialog.d.ts +1 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/core/DrawerNavigationGroup.d.ts +2 -2
- package/dist/editor/components/SlashCommandMenu.d.ts +6 -0
- package/dist/editor/components/editor-bubble-item.d.ts +8 -0
- package/dist/editor/components/editor-bubble.d.ts +8 -0
- package/dist/editor/components/image-bubble.d.ts +5 -0
- package/dist/editor/components/index.d.ts +16 -0
- package/dist/editor/components/table-bubble.d.ts +5 -0
- package/dist/editor/editor.d.ts +30 -0
- package/dist/editor/extensions/HighlightDecorationExtension.d.ts +24 -0
- package/dist/editor/extensions/Image/index.d.ts +6 -0
- package/dist/editor/extensions/Image.d.ts +6 -0
- package/dist/editor/extensions/TextLoadingDecorationExtension.d.ts +16 -0
- package/dist/editor/extensions/clipboard.d.ts +7 -0
- package/dist/editor/extensions/custom-keymap.d.ts +1 -0
- package/dist/editor/extensions/drag-and-drop.d.ts +9 -0
- package/dist/editor/hooks/useProseMirror.d.ts +13 -0
- package/dist/editor/hooks/useProseMirrorContext.d.ts +9 -0
- package/dist/editor/index.d.ts +2 -0
- package/dist/editor/markdown.d.ts +5 -0
- package/dist/editor/nodeViews/ImageComponent.d.ts +3 -0
- package/dist/editor/nodeViews/ReactNodeView.d.ts +29 -0
- package/dist/editor/nodeViews/TaskItemComponent.d.ts +3 -0
- package/dist/editor/nodeViews/index.d.ts +6 -0
- package/dist/editor/plugins/index.d.ts +2 -0
- package/dist/editor/plugins/inputrules.d.ts +6 -0
- package/dist/editor/plugins/placeholderPlugin.d.ts +3 -0
- package/dist/editor/plugins/slashCommandPlugin.d.ts +12 -0
- package/dist/editor/schema.d.ts +2 -0
- package/dist/editor/selectors/ai-selector.d.ts +0 -0
- package/dist/editor/selectors/color-selector.d.ts +10 -0
- package/dist/editor/selectors/link-selector.d.ts +8 -0
- package/dist/editor/selectors/node-selector.d.ts +15 -0
- package/dist/editor/selectors/text-buttons.d.ts +1 -0
- package/dist/editor/types.d.ts +5 -0
- package/dist/editor/useProseMirror.d.ts +16 -0
- package/dist/editor/utils/prosemirror-utils.d.ts +6 -0
- package/dist/editor/utils/remove_classes.d.ts +1 -0
- package/dist/editor/utils/useDebouncedCallback.d.ts +1 -0
- package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/useBuildNavigationController.d.ts +0 -1
- package/dist/hooks/useCollapsedGroups.d.ts +3 -3
- package/dist/hooks/useTranslation.d.ts +17 -0
- package/dist/i18n/FireCMSi18nProvider.d.ts +33 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.es.js +12898 -2265
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +12877 -2264
- package/dist/index.umd.js.map +1 -1
- package/dist/locales/de.d.ts +2 -0
- package/dist/locales/en.d.ts +10 -0
- package/dist/locales/es.d.ts +10 -0
- package/dist/locales/fr.d.ts +2 -0
- package/dist/locales/hi.d.ts +2 -0
- package/dist/locales/it.d.ts +2 -0
- package/dist/locales/pt.d.ts +7 -0
- package/dist/types/customization_controller.d.ts +2 -1
- package/dist/types/firecms.d.ts +2 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/navigation.d.ts +2 -2
- package/dist/types/plugins.d.ts +7 -0
- package/dist/types/storage.d.ts +1 -0
- package/dist/types/translations.d.ts +646 -0
- package/dist/util/useStorageUploadController.d.ts +10 -1
- package/package.json +45 -9
- package/src/app/Scaffold.tsx +7 -5
- package/src/components/AIIcon.tsx +3 -1
- package/src/components/ArrayContainer.tsx +6 -4
- package/src/components/ClearFilterSortButton.tsx +6 -3
- package/src/components/ConfirmationDialog.tsx +4 -2
- package/src/components/DeleteEntityDialog.tsx +10 -7
- package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +6 -3
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -1
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +3 -2
- package/src/components/EntityCollectionView/BoardSortableList.tsx +3 -1
- package/src/components/EntityCollectionView/CollectionDataErrorBanner.tsx +43 -0
- package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +16 -43
- package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +17 -25
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +26 -18
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +4 -3
- package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +4 -2
- package/src/components/EntityCollectionView/FiltersDialog.tsx +8 -5
- package/src/components/EntityCollectionView/ViewModeToggle.tsx +11 -8
- package/src/components/EntityView.tsx +3 -2
- package/src/components/ErrorBoundary.tsx +27 -15
- package/src/components/HomePage/DefaultHomePage.tsx +19 -13
- package/src/components/HomePage/HomePageDnD.tsx +3 -1
- package/src/components/HomePage/NavigationGroup.tsx +3 -1
- package/src/components/HomePage/RenameGroupDialog.tsx +15 -13
- package/src/components/LanguageToggle.tsx +66 -0
- package/src/components/NotFoundPage.tsx +5 -3
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +9 -7
- package/src/components/ReferenceWidget.tsx +3 -2
- package/src/components/SearchIconsView.tsx +3 -1
- package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +11 -0
- package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +15 -2
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +11 -0
- package/src/components/UnsavedChangesDialog.tsx +6 -4
- package/src/components/VirtualTable/VirtualTable.performance.test.tsx +1 -0
- package/src/components/VirtualTable/VirtualTableHeader.tsx +12 -10
- package/src/components/common/default_entity_actions.tsx +4 -0
- package/src/components/common/useDataSourceTableController.tsx +12 -4
- package/src/components/index.tsx +1 -0
- package/src/core/DefaultAppBar.tsx +14 -10
- package/src/core/DefaultDrawer.tsx +8 -2
- package/src/core/DrawerNavigationGroup.tsx +5 -3
- package/src/core/EntityEditView.tsx +4 -3
- package/src/core/EntityEditViewFormActions.tsx +24 -17
- package/src/core/EntitySidePanel.tsx +6 -5
- package/src/core/FireCMS.tsx +33 -6
- package/src/editor/components/SlashCommandMenu.tsx +516 -0
- package/src/editor/components/editor-bubble-item.tsx +32 -0
- package/src/editor/components/editor-bubble.tsx +118 -0
- package/src/editor/components/image-bubble.tsx +156 -0
- package/src/editor/components/index.ts +14 -0
- package/src/editor/components/table-bubble.tsx +165 -0
- package/src/editor/editor.tsx +455 -0
- package/src/editor/extensions/HighlightDecorationExtension.ts +114 -0
- package/src/editor/extensions/Image/index.ts +133 -0
- package/src/editor/extensions/Image.ts +159 -0
- package/src/editor/extensions/TextLoadingDecorationExtension.tsx +107 -0
- package/src/editor/extensions/clipboard.ts +72 -0
- package/src/editor/extensions/custom-keymap.ts +24 -0
- package/src/editor/extensions/drag-and-drop.tsx +480 -0
- package/src/editor/hooks/useProseMirror.ts +124 -0
- package/src/editor/hooks/useProseMirrorContext.ts +15 -0
- package/src/editor/index.ts +2 -0
- package/src/editor/markdown.ts +172 -0
- package/src/editor/nodeViews/ImageComponent.tsx +20 -0
- package/src/editor/nodeViews/ReactNodeView.tsx +89 -0
- package/src/editor/nodeViews/TaskItemComponent.tsx +29 -0
- package/src/editor/nodeViews/index.ts +35 -0
- package/src/editor/plugins/index.ts +58 -0
- package/src/editor/plugins/inputrules.ts +82 -0
- package/src/editor/plugins/placeholderPlugin.ts +55 -0
- package/src/editor/plugins/slashCommandPlugin.ts +61 -0
- package/src/editor/schema.ts +240 -0
- package/src/editor/selectors/ai-selector.tsx +111 -0
- package/src/editor/selectors/color-selector.tsx +200 -0
- package/src/editor/selectors/link-selector.tsx +118 -0
- package/src/editor/selectors/node-selector.tsx +157 -0
- package/src/editor/selectors/text-buttons.tsx +86 -0
- package/src/editor/types.ts +6 -0
- package/src/editor/useProseMirror.ts +126 -0
- package/src/editor/utils/prosemirror-utils.ts +108 -0
- package/src/editor/utils/remove_classes.ts +17 -0
- package/src/editor/utils/useDebouncedCallback.ts +25 -0
- package/src/form/EntityForm.tsx +16 -3
- package/src/form/EntityFormActions.tsx +19 -12
- package/src/form/PropertyFieldBinding.tsx +3 -2
- package/src/form/components/LocalChangesMenu.tsx +13 -13
- package/src/form/components/StorageItemPreview.tsx +3 -2
- package/src/form/components/StorageUploadProgress.tsx +18 -3
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +4 -4
- package/src/form/field_bindings/BlockFieldBinding.tsx +5 -2
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +23 -18
- package/src/form/field_bindings/MapFieldBinding.tsx +4 -3
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +33 -19
- package/src/form/field_bindings/RepeatFieldBinding.tsx +3 -1
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +4 -3
- package/src/hooks/index.tsx +1 -0
- package/src/hooks/useBuildNavigationController.tsx +45 -18
- package/src/hooks/useCollapsedGroups.ts +7 -6
- package/src/hooks/useTranslation.ts +31 -0
- package/src/i18n/FireCMSi18nProvider.tsx +160 -0
- package/src/index.ts +4 -0
- package/src/internal/useBuildSideEntityController.tsx +22 -20
- package/src/locales/de.ts +691 -0
- package/src/locales/en.ts +703 -0
- package/src/locales/es.ts +703 -0
- package/src/locales/fr.ts +691 -0
- package/src/locales/hi.ts +691 -0
- package/src/locales/it.ts +691 -0
- package/src/locales/pt.ts +700 -0
- package/src/preview/components/UrlComponentPreview.tsx +4 -2
- package/src/preview/components/UserPreview.tsx +3 -1
- package/src/types/customization_controller.tsx +2 -1
- package/src/types/firecms.tsx +2 -1
- package/src/types/index.ts +1 -0
- package/src/types/navigation.ts +2 -2
- package/src/types/plugins.tsx +8 -0
- package/src/types/properties.ts +1 -0
- package/src/types/storage.ts +2 -1
- package/src/types/translations.ts +725 -0
- package/src/util/useStorageUploadController.tsx +23 -29
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { cls, defaultBorderMixin } from "@firecms/ui";
|
|
2
|
+
import { Decoration, DecorationSet, EditorView } from "prosemirror-view";
|
|
3
|
+
import { Plugin, PluginKey } from "prosemirror-state";
|
|
4
|
+
|
|
5
|
+
export type UploadFn = (image: File) => Promise<string>;
|
|
6
|
+
|
|
7
|
+
export async function onFileRead(view: EditorView, readerEvent: ProgressEvent<FileReader>, pos: number, upload: UploadFn, image: File) {
|
|
8
|
+
|
|
9
|
+
const { schema } = view.state;
|
|
10
|
+
|
|
11
|
+
// @ts-ignore
|
|
12
|
+
const plugin = view.state.plugins.find((p: Plugin) => p.key === ImagePluginKey.key);
|
|
13
|
+
if (!plugin) {
|
|
14
|
+
console.error("Image plugin not found");
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const decoId = Math.random().toString(36).substring(7);
|
|
19
|
+
const placeholder = document.createElement("div");
|
|
20
|
+
const imageElement = document.createElement("img");
|
|
21
|
+
imageElement.setAttribute("class", "opacity-40 rounded-lg border " + defaultBorderMixin);
|
|
22
|
+
imageElement.src = readerEvent.target?.result as string;
|
|
23
|
+
placeholder.appendChild(imageElement);
|
|
24
|
+
|
|
25
|
+
const deco = Decoration.widget(pos, placeholder, { id: decoId });
|
|
26
|
+
let decorationSet = plugin.getState(view.state);
|
|
27
|
+
decorationSet = decorationSet?.add(view.state.doc, [deco]);
|
|
28
|
+
view.dispatch(view.state.tr.setMeta(plugin, { decorationSet }));
|
|
29
|
+
|
|
30
|
+
// Image Upload Logic
|
|
31
|
+
const src = await upload(image);
|
|
32
|
+
console.debug("Uploaded image", src);
|
|
33
|
+
|
|
34
|
+
const replacePlaceholder = () => {
|
|
35
|
+
// Retrieve the LATEST state after the async upload
|
|
36
|
+
let currentDecos = plugin.getState(view.state) as DecorationSet;
|
|
37
|
+
const foundDecos = currentDecos.find(undefined, undefined, spec => spec.id === decoId);
|
|
38
|
+
|
|
39
|
+
if (foundDecos.length === 0) {
|
|
40
|
+
console.warn("Image placeholder removed before upload completed.");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Get the mapped position of the decoration
|
|
45
|
+
const currentPos = foundDecos[0].from;
|
|
46
|
+
|
|
47
|
+
// Replace placeholder with actual image at the correct mapped position
|
|
48
|
+
const imageNode = view.state.schema.nodes.image.create({ src });
|
|
49
|
+
const tr = view.state.tr.replaceWith(currentPos, currentPos, imageNode);
|
|
50
|
+
|
|
51
|
+
// Remove placeholder decoration using the LATEST decorationSet
|
|
52
|
+
currentDecos = currentDecos.remove(foundDecos);
|
|
53
|
+
tr.setMeta(plugin, { decorationSet: currentDecos });
|
|
54
|
+
view.dispatch(tr);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Preload the image so it doesn't cause a layout shift when replacing the placeholder
|
|
58
|
+
const preloader = new Image();
|
|
59
|
+
preloader.src = src;
|
|
60
|
+
preloader.onload = replacePlaceholder;
|
|
61
|
+
preloader.onerror = replacePlaceholder;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const ImagePluginKey = new PluginKey("imagePlugin");
|
|
65
|
+
|
|
66
|
+
export const createDropImagePlugin = (upload: UploadFn): Plugin => {
|
|
67
|
+
const plugin: Plugin = new Plugin({
|
|
68
|
+
key: ImagePluginKey,
|
|
69
|
+
state: {
|
|
70
|
+
// Initialize the plugin state with an empty DecorationSet
|
|
71
|
+
init: () => DecorationSet.empty,
|
|
72
|
+
// Apply transactions to update the state
|
|
73
|
+
apply: (tr, old) => {
|
|
74
|
+
// Handle custom transaction steps that update decorations
|
|
75
|
+
const meta = tr.getMeta(plugin);
|
|
76
|
+
if (meta && meta.decorationSet) {
|
|
77
|
+
return meta.decorationSet;
|
|
78
|
+
}
|
|
79
|
+
// Map decorations to the new document structure
|
|
80
|
+
return old.map(tr.mapping, tr.doc);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
props: {
|
|
84
|
+
handleDOMEvents: {
|
|
85
|
+
dragover: (view: EditorView, event: DragEvent) => {
|
|
86
|
+
if (event.dataTransfer?.types?.includes("Files")) {
|
|
87
|
+
event.preventDefault();
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
91
|
+
},
|
|
92
|
+
drop: (view: EditorView, event: DragEvent) => {
|
|
93
|
+
if (!event.dataTransfer?.files || event.dataTransfer?.files.length === 0) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
event.preventDefault();
|
|
97
|
+
|
|
98
|
+
const files = Array.from(event.dataTransfer.files);
|
|
99
|
+
const images = files.filter(file => /image/i.test(file.type));
|
|
100
|
+
|
|
101
|
+
if (images.length === 0) {
|
|
102
|
+
console.log("No images found in dropped files");
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
images.forEach(image => {
|
|
107
|
+
const position = view.posAtCoords({
|
|
108
|
+
left: event.clientX,
|
|
109
|
+
top: event.clientY
|
|
110
|
+
});
|
|
111
|
+
if (!position) return;
|
|
112
|
+
|
|
113
|
+
const reader = new FileReader();
|
|
114
|
+
reader.onload = async (readerEvent) => {
|
|
115
|
+
await onFileRead(view as any, readerEvent, position.pos, upload, image);
|
|
116
|
+
};
|
|
117
|
+
reader.readAsDataURL(image);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
handlePaste(view: EditorView, event: ClipboardEvent, slice: any) {
|
|
124
|
+
const html = event.clipboardData?.getData("text/html");
|
|
125
|
+
if (html && html.includes("<img")) {
|
|
126
|
+
// Let ProseMirror handle the HTML paste natively (e.g. copy-pasted from the editor itself or a webpage)
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const items = Array.from(event.clipboardData?.items || []);
|
|
131
|
+
const pos = view.state.selection.from;
|
|
132
|
+
let anyImageFound = false;
|
|
133
|
+
|
|
134
|
+
items
|
|
135
|
+
.filter((item) => item.type.startsWith("image/"))
|
|
136
|
+
.forEach((item) => {
|
|
137
|
+
const image = item.getAsFile();
|
|
138
|
+
if (image) {
|
|
139
|
+
anyImageFound = true;
|
|
140
|
+
const reader = new FileReader();
|
|
141
|
+
reader.onload = async (readerEvent) => {
|
|
142
|
+
await onFileRead(view as any, readerEvent, pos, upload, image);
|
|
143
|
+
};
|
|
144
|
+
reader.readAsDataURL(image);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
return anyImageFound;
|
|
149
|
+
},
|
|
150
|
+
decorations(state) {
|
|
151
|
+
return plugin.getState(state);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
return plugin;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Plugin, PluginKey, Transaction, EditorState } from "prosemirror-state";
|
|
2
|
+
import { Decoration, DecorationSet } from "prosemirror-view";
|
|
3
|
+
|
|
4
|
+
// Define and export the plugin key
|
|
5
|
+
export const loadingDecorationKey = new PluginKey<LoadingDecorationState>("loadingDecoration");
|
|
6
|
+
|
|
7
|
+
interface LoadingDecorationState {
|
|
8
|
+
decorationSet: DecorationSet;
|
|
9
|
+
hasDecoration: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const textLoadingCommands = {
|
|
13
|
+
toggleLoadingDecoration: (state: EditorState, dispatch: ((tr: Transaction) => void) | undefined, loadingHtml?: string): boolean => {
|
|
14
|
+
const { selection } = state;
|
|
15
|
+
const pos = selection.from;
|
|
16
|
+
|
|
17
|
+
if (!dispatch) return false;
|
|
18
|
+
|
|
19
|
+
const tr = state.tr.setMeta(loadingDecorationKey, {
|
|
20
|
+
pos,
|
|
21
|
+
type: "loadingDecoration",
|
|
22
|
+
remove: false,
|
|
23
|
+
loadingHtml
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
dispatch(tr);
|
|
27
|
+
return true;
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
removeLoadingDecoration: (state: EditorState, dispatch: ((tr: Transaction) => void) | undefined): boolean => {
|
|
31
|
+
if (!dispatch) return false;
|
|
32
|
+
|
|
33
|
+
const tr = state.tr.setMeta(loadingDecorationKey, {
|
|
34
|
+
pos: 0, // We can pass any position as it will remove the entire decoration set
|
|
35
|
+
type: "loadingDecoration",
|
|
36
|
+
remove: true
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
dispatch(tr);
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* This plugin is used to display streaming content from an LLM.
|
|
46
|
+
*/
|
|
47
|
+
export const textLoadingDecorationPlugin = () => {
|
|
48
|
+
return new Plugin<LoadingDecorationState>({
|
|
49
|
+
key: loadingDecorationKey,
|
|
50
|
+
|
|
51
|
+
state: {
|
|
52
|
+
init() {
|
|
53
|
+
return {
|
|
54
|
+
decorationSet: DecorationSet.empty,
|
|
55
|
+
hasDecoration: false
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
apply(tr, oldState) {
|
|
60
|
+
const action = tr.getMeta(loadingDecorationKey);
|
|
61
|
+
|
|
62
|
+
if (action?.type === "loadingDecoration") {
|
|
63
|
+
const { pos, remove, loadingHtml } = action;
|
|
64
|
+
|
|
65
|
+
if (remove) {
|
|
66
|
+
return {
|
|
67
|
+
decorationSet: DecorationSet.empty,
|
|
68
|
+
hasDecoration: false
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const decoration = Decoration.widget(pos, () => {
|
|
73
|
+
const container = document.createElement("span");
|
|
74
|
+
container.className = "loading-decoration";
|
|
75
|
+
|
|
76
|
+
// Sanitize and append HTML
|
|
77
|
+
if (loadingHtml) {
|
|
78
|
+
container.innerHTML = loadingHtml;
|
|
79
|
+
} else {
|
|
80
|
+
const span = document.createElement("span");
|
|
81
|
+
span.innerText = "loading...";
|
|
82
|
+
container.appendChild(span);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return container;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
decorationSet: DecorationSet.empty.add(tr.doc, [decoration]),
|
|
90
|
+
hasDecoration: true
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
decorationSet: oldState.decorationSet.map(tr.mapping, tr.doc),
|
|
96
|
+
hasDecoration: oldState.hasDecoration
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
props: {
|
|
102
|
+
decorations(state) {
|
|
103
|
+
return this.getState(state)?.decorationSet || DecorationSet.empty;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { DOMSerializer, Slice } from "prosemirror-model"
|
|
2
|
+
import { EditorView } from "prosemirror-view";
|
|
3
|
+
|
|
4
|
+
export function serializeForClipboard(view: EditorView, slice: Slice) {
|
|
5
|
+
view.someProp("transformCopied", f => {
|
|
6
|
+
slice = f(slice!, view)
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
const context = [];
|
|
10
|
+
let {
|
|
11
|
+
content,
|
|
12
|
+
openStart,
|
|
13
|
+
openEnd
|
|
14
|
+
} = slice
|
|
15
|
+
while (openStart > 1 && openEnd > 1 && content.childCount == 1 && content.firstChild!.childCount == 1) {
|
|
16
|
+
openStart--
|
|
17
|
+
openEnd--
|
|
18
|
+
const node = content.firstChild!
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
context.push(node.type.name, node.attrs != node.type.defaultAttrs ? node.attrs : null)
|
|
21
|
+
content = node.content
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const serializer = view.someProp("clipboardSerializer") || DOMSerializer.fromSchema(view.state.schema)
|
|
25
|
+
const doc = detachedDoc(), wrap = doc.createElement("div")
|
|
26
|
+
wrap.appendChild(serializer.serializeFragment(content, { document: doc }))
|
|
27
|
+
|
|
28
|
+
let firstChild = wrap.firstChild, needsWrap, wrappers = 0
|
|
29
|
+
while (firstChild && firstChild.nodeType == 1 && (needsWrap = wrapMap[firstChild.nodeName.toLowerCase()])) {
|
|
30
|
+
for (let i = needsWrap.length - 1; i >= 0; i--) {
|
|
31
|
+
const wrapper = doc.createElement(needsWrap[i])
|
|
32
|
+
while (wrap.firstChild) wrapper.appendChild(wrap.firstChild)
|
|
33
|
+
wrap.appendChild(wrapper)
|
|
34
|
+
wrappers++
|
|
35
|
+
}
|
|
36
|
+
firstChild = wrap.firstChild
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (firstChild && firstChild.nodeType == 1)
|
|
40
|
+
(firstChild as HTMLElement).setAttribute(
|
|
41
|
+
"data-pm-slice", `${openStart} ${openEnd}${wrappers ? ` -${wrappers}` : ""} ${JSON.stringify(context)}`)
|
|
42
|
+
|
|
43
|
+
const text = view.someProp("clipboardTextSerializer", f => f(slice, view)) ||
|
|
44
|
+
slice.content.textBetween(0, slice.content.size, "\n\n")
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
dom: wrap,
|
|
48
|
+
text,
|
|
49
|
+
slice
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Trick from jQuery -- some elements must be wrapped in other
|
|
54
|
+
// elements for innerHTML to work. I.e. if you do `div.innerHTML =
|
|
55
|
+
// "<td>..</td>"` the table cells are ignored.
|
|
56
|
+
const wrapMap: { [node: string]: string[] } = {
|
|
57
|
+
thead: ["table"],
|
|
58
|
+
tbody: ["table"],
|
|
59
|
+
tfoot: ["table"],
|
|
60
|
+
caption: ["table"],
|
|
61
|
+
colgroup: ["table"],
|
|
62
|
+
col: ["table", "colgroup"],
|
|
63
|
+
tr: ["table", "tbody"],
|
|
64
|
+
td: ["table", "tbody", "tr"],
|
|
65
|
+
th: ["table", "tbody", "tr"]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let _detachedDoc: Document | null = null
|
|
69
|
+
|
|
70
|
+
function detachedDoc() {
|
|
71
|
+
return _detachedDoc || (_detachedDoc = document.implementation.createHTMLDocument("title"))
|
|
72
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { keymap } from "prosemirror-keymap";
|
|
2
|
+
|
|
3
|
+
export const customKeymapPlugin = () => {
|
|
4
|
+
return keymap({
|
|
5
|
+
"Mod-a": (state, dispatch) => {
|
|
6
|
+
const { tr } = state;
|
|
7
|
+
const startSelectionPos = tr.selection.from;
|
|
8
|
+
const endSelectionPos = tr.selection.to;
|
|
9
|
+
const startNodePos = tr.selection.$from.start();
|
|
10
|
+
const endNodePos = tr.selection.$to.end();
|
|
11
|
+
|
|
12
|
+
const isCurrentTextSelectionNotExtendedToNodeBoundaries =
|
|
13
|
+
startSelectionPos > startNodePos || endSelectionPos < endNodePos;
|
|
14
|
+
|
|
15
|
+
if (isCurrentTextSelectionNotExtendedToNodeBoundaries) {
|
|
16
|
+
if (dispatch) {
|
|
17
|
+
dispatch(tr.setSelection((state.selection.constructor as any).create(state.doc, startNodePos, endNodePos)));
|
|
18
|
+
}
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
};
|