@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.
Files changed (191) hide show
  1. package/dist/components/EntityCollectionView/CollectionDataErrorBanner.d.ts +4 -0
  2. package/dist/components/ErrorBoundary.d.ts +3 -1
  3. package/dist/components/HomePage/DefaultHomePage.d.ts +0 -1
  4. package/dist/components/LanguageToggle.d.ts +1 -0
  5. package/dist/components/UnsavedChangesDialog.d.ts +1 -0
  6. package/dist/components/index.d.ts +1 -0
  7. package/dist/core/DrawerNavigationGroup.d.ts +2 -2
  8. package/dist/editor/components/SlashCommandMenu.d.ts +6 -0
  9. package/dist/editor/components/editor-bubble-item.d.ts +8 -0
  10. package/dist/editor/components/editor-bubble.d.ts +8 -0
  11. package/dist/editor/components/image-bubble.d.ts +5 -0
  12. package/dist/editor/components/index.d.ts +16 -0
  13. package/dist/editor/components/table-bubble.d.ts +5 -0
  14. package/dist/editor/editor.d.ts +30 -0
  15. package/dist/editor/extensions/HighlightDecorationExtension.d.ts +24 -0
  16. package/dist/editor/extensions/Image/index.d.ts +6 -0
  17. package/dist/editor/extensions/Image.d.ts +6 -0
  18. package/dist/editor/extensions/TextLoadingDecorationExtension.d.ts +16 -0
  19. package/dist/editor/extensions/clipboard.d.ts +7 -0
  20. package/dist/editor/extensions/custom-keymap.d.ts +1 -0
  21. package/dist/editor/extensions/drag-and-drop.d.ts +9 -0
  22. package/dist/editor/hooks/useProseMirror.d.ts +13 -0
  23. package/dist/editor/hooks/useProseMirrorContext.d.ts +9 -0
  24. package/dist/editor/index.d.ts +2 -0
  25. package/dist/editor/markdown.d.ts +5 -0
  26. package/dist/editor/nodeViews/ImageComponent.d.ts +3 -0
  27. package/dist/editor/nodeViews/ReactNodeView.d.ts +29 -0
  28. package/dist/editor/nodeViews/TaskItemComponent.d.ts +3 -0
  29. package/dist/editor/nodeViews/index.d.ts +6 -0
  30. package/dist/editor/plugins/index.d.ts +2 -0
  31. package/dist/editor/plugins/inputrules.d.ts +6 -0
  32. package/dist/editor/plugins/placeholderPlugin.d.ts +3 -0
  33. package/dist/editor/plugins/slashCommandPlugin.d.ts +12 -0
  34. package/dist/editor/schema.d.ts +2 -0
  35. package/dist/editor/selectors/ai-selector.d.ts +0 -0
  36. package/dist/editor/selectors/color-selector.d.ts +10 -0
  37. package/dist/editor/selectors/link-selector.d.ts +8 -0
  38. package/dist/editor/selectors/node-selector.d.ts +15 -0
  39. package/dist/editor/selectors/text-buttons.d.ts +1 -0
  40. package/dist/editor/types.d.ts +5 -0
  41. package/dist/editor/useProseMirror.d.ts +16 -0
  42. package/dist/editor/utils/prosemirror-utils.d.ts +6 -0
  43. package/dist/editor/utils/remove_classes.d.ts +1 -0
  44. package/dist/editor/utils/useDebouncedCallback.d.ts +1 -0
  45. package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +1 -1
  46. package/dist/hooks/index.d.ts +1 -0
  47. package/dist/hooks/useBuildNavigationController.d.ts +0 -1
  48. package/dist/hooks/useCollapsedGroups.d.ts +3 -3
  49. package/dist/hooks/useTranslation.d.ts +17 -0
  50. package/dist/i18n/FireCMSi18nProvider.d.ts +33 -0
  51. package/dist/index.d.ts +4 -0
  52. package/dist/index.es.js +12898 -2265
  53. package/dist/index.es.js.map +1 -1
  54. package/dist/index.umd.js +12877 -2264
  55. package/dist/index.umd.js.map +1 -1
  56. package/dist/locales/de.d.ts +2 -0
  57. package/dist/locales/en.d.ts +10 -0
  58. package/dist/locales/es.d.ts +10 -0
  59. package/dist/locales/fr.d.ts +2 -0
  60. package/dist/locales/hi.d.ts +2 -0
  61. package/dist/locales/it.d.ts +2 -0
  62. package/dist/locales/pt.d.ts +7 -0
  63. package/dist/types/customization_controller.d.ts +2 -1
  64. package/dist/types/firecms.d.ts +2 -1
  65. package/dist/types/index.d.ts +1 -0
  66. package/dist/types/navigation.d.ts +2 -2
  67. package/dist/types/plugins.d.ts +7 -0
  68. package/dist/types/storage.d.ts +1 -0
  69. package/dist/types/translations.d.ts +646 -0
  70. package/dist/util/useStorageUploadController.d.ts +10 -1
  71. package/package.json +45 -9
  72. package/src/app/Scaffold.tsx +7 -5
  73. package/src/components/AIIcon.tsx +3 -1
  74. package/src/components/ArrayContainer.tsx +6 -4
  75. package/src/components/ClearFilterSortButton.tsx +6 -3
  76. package/src/components/ConfirmationDialog.tsx +4 -2
  77. package/src/components/DeleteEntityDialog.tsx +10 -7
  78. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +6 -3
  79. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -1
  80. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +3 -2
  81. package/src/components/EntityCollectionView/BoardSortableList.tsx +3 -1
  82. package/src/components/EntityCollectionView/CollectionDataErrorBanner.tsx +43 -0
  83. package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +16 -43
  84. package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +17 -25
  85. package/src/components/EntityCollectionView/EntityCollectionView.tsx +26 -18
  86. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +4 -3
  87. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +4 -2
  88. package/src/components/EntityCollectionView/FiltersDialog.tsx +8 -5
  89. package/src/components/EntityCollectionView/ViewModeToggle.tsx +11 -8
  90. package/src/components/EntityView.tsx +3 -2
  91. package/src/components/ErrorBoundary.tsx +27 -15
  92. package/src/components/HomePage/DefaultHomePage.tsx +19 -13
  93. package/src/components/HomePage/HomePageDnD.tsx +3 -1
  94. package/src/components/HomePage/NavigationGroup.tsx +3 -1
  95. package/src/components/HomePage/RenameGroupDialog.tsx +15 -13
  96. package/src/components/LanguageToggle.tsx +66 -0
  97. package/src/components/NotFoundPage.tsx +5 -3
  98. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +9 -7
  99. package/src/components/ReferenceWidget.tsx +3 -2
  100. package/src/components/SearchIconsView.tsx +3 -1
  101. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +11 -0
  102. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +15 -2
  103. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +11 -0
  104. package/src/components/UnsavedChangesDialog.tsx +6 -4
  105. package/src/components/VirtualTable/VirtualTable.performance.test.tsx +1 -0
  106. package/src/components/VirtualTable/VirtualTableHeader.tsx +12 -10
  107. package/src/components/common/default_entity_actions.tsx +4 -0
  108. package/src/components/common/useDataSourceTableController.tsx +12 -4
  109. package/src/components/index.tsx +1 -0
  110. package/src/core/DefaultAppBar.tsx +14 -10
  111. package/src/core/DefaultDrawer.tsx +8 -2
  112. package/src/core/DrawerNavigationGroup.tsx +5 -3
  113. package/src/core/EntityEditView.tsx +4 -3
  114. package/src/core/EntityEditViewFormActions.tsx +24 -17
  115. package/src/core/EntitySidePanel.tsx +6 -5
  116. package/src/core/FireCMS.tsx +33 -6
  117. package/src/editor/components/SlashCommandMenu.tsx +516 -0
  118. package/src/editor/components/editor-bubble-item.tsx +32 -0
  119. package/src/editor/components/editor-bubble.tsx +118 -0
  120. package/src/editor/components/image-bubble.tsx +156 -0
  121. package/src/editor/components/index.ts +14 -0
  122. package/src/editor/components/table-bubble.tsx +165 -0
  123. package/src/editor/editor.tsx +455 -0
  124. package/src/editor/extensions/HighlightDecorationExtension.ts +114 -0
  125. package/src/editor/extensions/Image/index.ts +133 -0
  126. package/src/editor/extensions/Image.ts +159 -0
  127. package/src/editor/extensions/TextLoadingDecorationExtension.tsx +107 -0
  128. package/src/editor/extensions/clipboard.ts +72 -0
  129. package/src/editor/extensions/custom-keymap.ts +24 -0
  130. package/src/editor/extensions/drag-and-drop.tsx +480 -0
  131. package/src/editor/hooks/useProseMirror.ts +124 -0
  132. package/src/editor/hooks/useProseMirrorContext.ts +15 -0
  133. package/src/editor/index.ts +2 -0
  134. package/src/editor/markdown.ts +172 -0
  135. package/src/editor/nodeViews/ImageComponent.tsx +20 -0
  136. package/src/editor/nodeViews/ReactNodeView.tsx +89 -0
  137. package/src/editor/nodeViews/TaskItemComponent.tsx +29 -0
  138. package/src/editor/nodeViews/index.ts +35 -0
  139. package/src/editor/plugins/index.ts +58 -0
  140. package/src/editor/plugins/inputrules.ts +82 -0
  141. package/src/editor/plugins/placeholderPlugin.ts +55 -0
  142. package/src/editor/plugins/slashCommandPlugin.ts +61 -0
  143. package/src/editor/schema.ts +240 -0
  144. package/src/editor/selectors/ai-selector.tsx +111 -0
  145. package/src/editor/selectors/color-selector.tsx +200 -0
  146. package/src/editor/selectors/link-selector.tsx +118 -0
  147. package/src/editor/selectors/node-selector.tsx +157 -0
  148. package/src/editor/selectors/text-buttons.tsx +86 -0
  149. package/src/editor/types.ts +6 -0
  150. package/src/editor/useProseMirror.ts +126 -0
  151. package/src/editor/utils/prosemirror-utils.ts +108 -0
  152. package/src/editor/utils/remove_classes.ts +17 -0
  153. package/src/editor/utils/useDebouncedCallback.ts +25 -0
  154. package/src/form/EntityForm.tsx +16 -3
  155. package/src/form/EntityFormActions.tsx +19 -12
  156. package/src/form/PropertyFieldBinding.tsx +3 -2
  157. package/src/form/components/LocalChangesMenu.tsx +13 -13
  158. package/src/form/components/StorageItemPreview.tsx +3 -2
  159. package/src/form/components/StorageUploadProgress.tsx +18 -3
  160. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +4 -4
  161. package/src/form/field_bindings/BlockFieldBinding.tsx +5 -2
  162. package/src/form/field_bindings/KeyValueFieldBinding.tsx +23 -18
  163. package/src/form/field_bindings/MapFieldBinding.tsx +4 -3
  164. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +33 -19
  165. package/src/form/field_bindings/RepeatFieldBinding.tsx +3 -1
  166. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +4 -3
  167. package/src/hooks/index.tsx +1 -0
  168. package/src/hooks/useBuildNavigationController.tsx +45 -18
  169. package/src/hooks/useCollapsedGroups.ts +7 -6
  170. package/src/hooks/useTranslation.ts +31 -0
  171. package/src/i18n/FireCMSi18nProvider.tsx +160 -0
  172. package/src/index.ts +4 -0
  173. package/src/internal/useBuildSideEntityController.tsx +22 -20
  174. package/src/locales/de.ts +691 -0
  175. package/src/locales/en.ts +703 -0
  176. package/src/locales/es.ts +703 -0
  177. package/src/locales/fr.ts +691 -0
  178. package/src/locales/hi.ts +691 -0
  179. package/src/locales/it.ts +691 -0
  180. package/src/locales/pt.ts +700 -0
  181. package/src/preview/components/UrlComponentPreview.tsx +4 -2
  182. package/src/preview/components/UserPreview.tsx +3 -1
  183. package/src/types/customization_controller.tsx +2 -1
  184. package/src/types/firecms.tsx +2 -1
  185. package/src/types/index.ts +1 -0
  186. package/src/types/navigation.ts +2 -2
  187. package/src/types/plugins.tsx +8 -0
  188. package/src/types/properties.ts +1 -0
  189. package/src/types/storage.ts +2 -1
  190. package/src/types/translations.ts +725 -0
  191. 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
+ };