@limetech/lime-elements 38.2.3 → 38.3.1

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 (145) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/cjs/files-c08d24d4.js +44 -0
  3. package/dist/cjs/files-c08d24d4.js.map +1 -0
  4. package/dist/cjs/index.cjs.js +9 -0
  5. package/dist/cjs/index.cjs.js.map +1 -1
  6. package/dist/cjs/limel-action-bar_2.cjs.entry.js +1 -1
  7. package/dist/cjs/limel-callout.cjs.entry.js +1 -1
  8. package/dist/cjs/limel-chart.cjs.entry.js +1 -1
  9. package/dist/cjs/limel-chip_2.cjs.entry.js +1 -1
  10. package/dist/cjs/limel-file-dropzone_2.cjs.entry.js +8 -43
  11. package/dist/cjs/limel-file-dropzone_2.cjs.entry.js.map +1 -1
  12. package/dist/cjs/limel-file-viewer.cjs.entry.js +1 -1
  13. package/dist/cjs/limel-file.cjs.entry.js +1 -1
  14. package/dist/cjs/limel-flatpickr-adapter.cjs.entry.js +1 -1
  15. package/dist/cjs/limel-linear-progress.cjs.entry.js +1 -1
  16. package/dist/cjs/limel-prosemirror-adapter.cjs.entry.js +404 -19
  17. package/dist/cjs/limel-prosemirror-adapter.cjs.entry.js.map +1 -1
  18. package/dist/cjs/limel-snackbar.cjs.entry.js +1 -1
  19. package/dist/cjs/limel-text-editor.cjs.entry.js +11 -1
  20. package/dist/cjs/limel-text-editor.cjs.entry.js.map +1 -1
  21. package/dist/cjs/text-editor.types-5e5567e2.js +13 -0
  22. package/dist/cjs/text-editor.types-5e5567e2.js.map +1 -0
  23. package/dist/cjs/{translations-0d53f9bc.js → translations-b5da5dcd.js} +55 -4
  24. package/dist/cjs/translations-b5da5dcd.js.map +1 -0
  25. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/image/inserter.js +195 -0
  26. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/image/inserter.js.map +1 -0
  27. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/image/node.js +128 -0
  28. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/image/node.js.map +1 -0
  29. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/image/view.js +139 -0
  30. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/image/view.js.map +1 -0
  31. package/dist/collection/components/text-editor/prosemirror-adapter/prosemirror-adapter.css +187 -0
  32. package/dist/collection/components/text-editor/prosemirror-adapter/prosemirror-adapter.js +60 -3
  33. package/dist/collection/components/text-editor/prosemirror-adapter/prosemirror-adapter.js.map +1 -1
  34. package/dist/collection/components/text-editor/text-editor.js +63 -1
  35. package/dist/collection/components/text-editor/text-editor.js.map +1 -1
  36. package/dist/collection/components/text-editor/text-editor.types.js +9 -1
  37. package/dist/collection/components/text-editor/text-editor.types.js.map +1 -1
  38. package/dist/collection/components/text-editor/utils/html-converter.js +7 -4
  39. package/dist/collection/components/text-editor/utils/html-converter.js.map +1 -1
  40. package/dist/collection/components/text-editor/utils/markdown-converter.js +8 -8
  41. package/dist/collection/components/text-editor/utils/markdown-converter.js.map +1 -1
  42. package/dist/collection/global/translations.js +9 -2
  43. package/dist/collection/global/translations.js.map +1 -1
  44. package/dist/collection/translations/da.js +6 -0
  45. package/dist/collection/translations/da.js.map +1 -1
  46. package/dist/collection/translations/de.js +5 -0
  47. package/dist/collection/translations/de.js.map +1 -1
  48. package/dist/collection/translations/en.js +5 -0
  49. package/dist/collection/translations/en.js.map +1 -1
  50. package/dist/collection/translations/fi.js +7 -1
  51. package/dist/collection/translations/fi.js.map +1 -1
  52. package/dist/collection/translations/fr.js +5 -0
  53. package/dist/collection/translations/fr.js.map +1 -1
  54. package/dist/collection/translations/nl.js +6 -0
  55. package/dist/collection/translations/nl.js.map +1 -1
  56. package/dist/collection/translations/no.js +6 -0
  57. package/dist/collection/translations/no.js.map +1 -1
  58. package/dist/collection/translations/sv.js +5 -0
  59. package/dist/collection/translations/sv.js.map +1 -1
  60. package/dist/esm/files-2be62a61.js +41 -0
  61. package/dist/esm/files-2be62a61.js.map +1 -0
  62. package/dist/esm/index.js +2 -0
  63. package/dist/esm/index.js.map +1 -1
  64. package/dist/esm/limel-action-bar_2.entry.js +1 -1
  65. package/dist/esm/limel-callout.entry.js +1 -1
  66. package/dist/esm/limel-chart.entry.js +1 -1
  67. package/dist/esm/limel-chip_2.entry.js +1 -1
  68. package/dist/esm/limel-file-dropzone_2.entry.js +2 -37
  69. package/dist/esm/limel-file-dropzone_2.entry.js.map +1 -1
  70. package/dist/esm/limel-file-viewer.entry.js +1 -1
  71. package/dist/esm/limel-file.entry.js +1 -1
  72. package/dist/esm/limel-flatpickr-adapter.entry.js +1 -1
  73. package/dist/esm/limel-linear-progress.entry.js +1 -1
  74. package/dist/esm/limel-prosemirror-adapter.entry.js +404 -19
  75. package/dist/esm/limel-prosemirror-adapter.entry.js.map +1 -1
  76. package/dist/esm/limel-snackbar.entry.js +1 -1
  77. package/dist/esm/limel-text-editor.entry.js +11 -1
  78. package/dist/esm/limel-text-editor.entry.js.map +1 -1
  79. package/dist/esm/text-editor.types-e82469d1.js +13 -0
  80. package/dist/esm/text-editor.types-e82469d1.js.map +1 -0
  81. package/dist/esm/{translations-7ad188e0.js → translations-8b7272f2.js} +55 -4
  82. package/dist/esm/translations-8b7272f2.js.map +1 -0
  83. package/dist/lime-elements/index.esm.js +1 -1
  84. package/dist/lime-elements/index.esm.js.map +1 -1
  85. package/dist/lime-elements/lime-elements.esm.js +1 -1
  86. package/dist/lime-elements/{p-fc7f9e93.entry.js → p-24a46d85.entry.js} +2 -2
  87. package/dist/lime-elements/{p-54b4a06b.entry.js → p-2ca8f253.entry.js} +2 -2
  88. package/dist/lime-elements/{p-0468e34c.entry.js → p-3479aa66.entry.js} +2 -2
  89. package/dist/lime-elements/{p-b78a9a5c.entry.js → p-54710fb8.entry.js} +2 -2
  90. package/dist/lime-elements/{p-180675a5.entry.js → p-5ac99c32.entry.js} +2 -2
  91. package/dist/lime-elements/{p-7fdab6b0.entry.js → p-62a21e92.entry.js} +2 -2
  92. package/dist/lime-elements/p-62a21e92.entry.js.map +1 -0
  93. package/dist/lime-elements/{p-28c6b698.entry.js → p-8c15a058.entry.js} +2 -2
  94. package/dist/lime-elements/p-9ca516ed.js +2 -0
  95. package/dist/lime-elements/p-9ca516ed.js.map +1 -0
  96. package/dist/lime-elements/{p-fefef194.entry.js → p-ae417884.entry.js} +2 -2
  97. package/dist/lime-elements/{p-ffe954d4.entry.js → p-bda61285.entry.js} +2 -2
  98. package/dist/lime-elements/{p-8ada443f.entry.js → p-c348740c.entry.js} +2 -2
  99. package/dist/lime-elements/p-dbcde7db.entry.js +2 -0
  100. package/dist/lime-elements/p-dbcde7db.entry.js.map +1 -0
  101. package/dist/lime-elements/p-dc3d2ee1.js +2 -0
  102. package/dist/lime-elements/p-dc3d2ee1.js.map +1 -0
  103. package/dist/lime-elements/p-e3d7aba7.js +2 -0
  104. package/dist/lime-elements/p-e3d7aba7.js.map +1 -0
  105. package/dist/lime-elements/p-f641bcb0.entry.js +2 -0
  106. package/dist/lime-elements/p-f641bcb0.entry.js.map +1 -0
  107. package/dist/types/components/text-editor/prosemirror-adapter/plugins/image/inserter.d.ts +15 -0
  108. package/dist/types/components/text-editor/prosemirror-adapter/plugins/image/node.d.ts +14 -0
  109. package/dist/types/components/text-editor/prosemirror-adapter/plugins/image/view.d.ts +4 -0
  110. package/dist/types/components/text-editor/prosemirror-adapter/prosemirror-adapter.d.ts +14 -0
  111. package/dist/types/components/text-editor/text-editor.d.ts +18 -0
  112. package/dist/types/components/text-editor/text-editor.types.d.ts +51 -0
  113. package/dist/types/components/text-editor/utils/markdown-converter.d.ts +2 -1
  114. package/dist/types/components.d.ts +34 -2
  115. package/dist/types/global/translations.d.ts +1 -1
  116. package/dist/types/translations/da.d.ts +6 -0
  117. package/dist/types/translations/de.d.ts +5 -0
  118. package/dist/types/translations/en.d.ts +5 -0
  119. package/dist/types/translations/fi.d.ts +7 -1
  120. package/dist/types/translations/fr.d.ts +5 -0
  121. package/dist/types/translations/nl.d.ts +6 -0
  122. package/dist/types/translations/no.d.ts +6 -0
  123. package/dist/types/translations/sv.d.ts +5 -0
  124. package/package.json +1 -1
  125. package/dist/cjs/translations-0d53f9bc.js.map +0 -1
  126. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/image-remover-plugin.js +0 -78
  127. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/image-remover-plugin.js.map +0 -1
  128. package/dist/esm/translations-7ad188e0.js.map +0 -1
  129. package/dist/lime-elements/p-7fdab6b0.entry.js.map +0 -1
  130. package/dist/lime-elements/p-ad9801f8.entry.js +0 -2
  131. package/dist/lime-elements/p-ad9801f8.entry.js.map +0 -1
  132. package/dist/lime-elements/p-e5545944.entry.js +0 -2
  133. package/dist/lime-elements/p-e5545944.entry.js.map +0 -1
  134. package/dist/lime-elements/p-efd753ba.js +0 -2
  135. package/dist/lime-elements/p-efd753ba.js.map +0 -1
  136. package/dist/types/components/text-editor/prosemirror-adapter/plugins/image-remover-plugin.d.ts +0 -4
  137. /package/dist/lime-elements/{p-fc7f9e93.entry.js.map → p-24a46d85.entry.js.map} +0 -0
  138. /package/dist/lime-elements/{p-54b4a06b.entry.js.map → p-2ca8f253.entry.js.map} +0 -0
  139. /package/dist/lime-elements/{p-0468e34c.entry.js.map → p-3479aa66.entry.js.map} +0 -0
  140. /package/dist/lime-elements/{p-b78a9a5c.entry.js.map → p-54710fb8.entry.js.map} +0 -0
  141. /package/dist/lime-elements/{p-180675a5.entry.js.map → p-5ac99c32.entry.js.map} +0 -0
  142. /package/dist/lime-elements/{p-28c6b698.entry.js.map → p-8c15a058.entry.js.map} +0 -0
  143. /package/dist/lime-elements/{p-fefef194.entry.js.map → p-ae417884.entry.js.map} +0 -0
  144. /package/dist/lime-elements/{p-ffe954d4.entry.js.map → p-bda61285.entry.js.map} +0 -0
  145. /package/dist/lime-elements/{p-8ada443f.entry.js.map → p-c348740c.entry.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inserter.js","sourceRoot":"","sources":["../../../../../../src/components/text-editor/prosemirror-adapter/plugins/image/inserter.ts"],"names":[],"mappings":"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","sourcesContent":["import { Plugin, PluginKey, Transaction, StateField } from 'prosemirror-state';\nimport { EditorView } from 'prosemirror-view';\nimport { createFileInfo } from '../../../../../util/files';\nimport { FileInfo } from '../../../../../global/shared-types/file.types';\nimport {\n ImageInserter,\n ImageInfo,\n ImageState,\n} from '../../../text-editor.types';\nimport { Node, Slice, Fragment } from 'prosemirror-model';\nimport { imageCache } from './node';\n\nexport const pluginKey = new PluginKey('imageInserterPlugin');\n\ntype ImagePastedCallback = (data: ImageInserter) => CustomEvent<ImageInserter>;\n\ntype ImageRemovedCallback = (data: ImageInfo) => CustomEvent<ImageInfo>;\n\ntype PluginState = {\n insertedImages: Record<string, Node>;\n};\n\nexport const createImageInserterPlugin = (\n imagePastedCallback: ImagePastedCallback,\n imageRemovedCallback: ImageRemovedCallback,\n) => {\n return new Plugin({\n key: pluginKey,\n props: {\n handlePaste: (view, event, slice) => {\n return processPasteEvent(view, event, slice);\n },\n handleDOMEvents: {\n imagePasted: (_, event) => {\n imagePastedCallback(event.detail);\n },\n },\n },\n state: {\n init: (): PluginState => {\n return { insertedImages: {} };\n },\n apply: (tr, pluginState): PluginState => {\n const newState = { ...pluginState };\n\n newState.insertedImages = getImagesFromTransaction(tr);\n findAndHandleRemovedImages(\n imageRemovedCallback,\n pluginState.insertedImages,\n newState.insertedImages,\n );\n\n return newState;\n },\n } as StateField<PluginState>,\n });\n};\n\nconst getImagesFromTransaction = (tr: Transaction): Record<string, Node> => {\n const images: Record<string, Node> = {};\n tr.doc.descendants((node) => {\n if (node.type.name === 'image') {\n images[node.attrs.fileInfoId] = node;\n }\n });\n\n return images;\n};\n\nconst findAndHandleRemovedImages = (\n imageRemovedCallback: ImageRemovedCallback,\n previousImages: Record<string, Node>,\n newImages: Record<string, Node>,\n) => {\n const removedKeys = Object.keys(previousImages).filter(\n (key) => !(key in newImages),\n );\n\n for (const removedKey of removedKeys) {\n const removedImage = previousImages[removedKey];\n const imageInfo: ImageInfo = {\n fileInfoId: removedImage.attrs.fileInfoId,\n src: removedImage.attrs.src,\n state: removedImage.attrs.state,\n };\n imageRemovedCallback(imageInfo);\n\n imageCache.delete(removedImage.attrs.fileInfoId);\n }\n};\n\nexport const imageInserterFactory = (\n view: EditorView,\n base64Data: string,\n fileInfo: FileInfo,\n): ImageInserter => {\n return {\n fileInfo: fileInfo,\n insertThumbnail: createThumbnailInserter(view, base64Data, fileInfo),\n insertImage: createImageInserter(view, fileInfo),\n insertFailedThumbnail: createFailedThumbnailInserter(view, fileInfo),\n };\n};\n\nconst createThumbnailInserter =\n (view: EditorView, base64Data: string, fileInfo: FileInfo) => () => {\n const { state, dispatch } = view;\n const { schema } = state;\n\n const placeholderNode = schema.nodes.image.create({\n src: base64Data,\n alt: fileInfo.filename,\n fileInfoId: fileInfo.id,\n state: ImageState.LOADING,\n });\n\n const transaction = state.tr.replaceSelectionWith(placeholderNode);\n\n dispatch(transaction);\n };\n\nconst createImageInserter =\n (view: EditorView, fileInfo: FileInfo) => (src?: string) => {\n const { state, dispatch } = view;\n const { schema } = state;\n\n const tr = state.tr;\n state.doc.descendants((node, pos) => {\n if (node.attrs.fileInfoId === fileInfo.id) {\n const imageNode = schema.nodes.image.create({\n src: src ? src : node.attrs.src,\n alt: fileInfo.filename,\n fileInfoId: fileInfo.id,\n state: ImageState.SUCCESS,\n });\n\n tr.replaceWith(pos, pos + node.nodeSize, imageNode);\n\n return false;\n }\n });\n\n dispatch(tr);\n };\n\nconst createFailedThumbnailInserter =\n (view: EditorView, fileInfo: FileInfo) => () => {\n const { state, dispatch } = view;\n const { schema } = state;\n\n const tr = state.tr;\n state.doc.descendants((node, pos) => {\n if (node.attrs.fileInfoId === fileInfo.id) {\n const errorPlaceholderNode = schema.nodes.image.create({\n src: node.attrs.src,\n alt: fileInfo.filename,\n fileInfoId: fileInfo.id,\n state: ImageState.FAILED,\n });\n\n tr.replaceWith(pos, pos + node.nodeSize, errorPlaceholderNode);\n\n return false;\n }\n });\n\n dispatch(tr);\n };\n\n/**\n * Check if a given ProseMirror node or fragment contains any image nodes.\n * @param node - The ProseMirror node or fragment to check.\n * @returns A boolean indicating whether the node contains any image nodes.\n */\nconst isImageNode = (node: Node | Fragment): boolean => {\n if (node instanceof Node) {\n if (node.type.name === 'image') {\n return true;\n }\n\n let found = false;\n node.content.forEach((child) => {\n if (isImageNode(child)) {\n found = true;\n }\n });\n\n return found;\n } else if (node instanceof Fragment) {\n let found = false;\n node.forEach((child) => {\n if (isImageNode(child)) {\n found = true;\n }\n });\n\n return found;\n }\n\n return false;\n};\n\n/**\n * Filter out image nodes from a ProseMirror fragment.\n * @param fragment - The ProseMirror fragment to filter.\n * @returns A new fragment with image nodes removed.\n */\nconst filterImageNodes = (fragment: Fragment): Fragment => {\n const filteredChildren: Node[] = [];\n\n fragment.forEach((child) => {\n if (!isImageNode(child)) {\n if (child.content.size > 0) {\n const filteredContent = filterImageNodes(child.content);\n const newNode = child.copy(filteredContent);\n filteredChildren.push(newNode);\n } else {\n filteredChildren.push(child);\n }\n }\n });\n\n return Fragment.fromArray(filteredChildren);\n};\n\n/**\n * Process a paste event and trigger an imagePasted event if an image file is pasted.\n * If an HTML image element is pasted, this image is filtered out from the slice content.\n *\n * @param view - The ProseMirror editor view.\n * @param event - The paste event.\n * @returns A boolean; True if an image file was pasted to prevent default paste behavior, otherwise false.\n */\nconst processPasteEvent = (\n view: EditorView,\n event: ClipboardEvent,\n slice: Slice,\n): boolean => {\n const clipboardData = event.clipboardData;\n if (!clipboardData) {\n return false;\n }\n\n const files = Array.from(clipboardData.files || []);\n for (const file of files) {\n if (file.type.startsWith('image/')) {\n const reader = new FileReader();\n reader.onloadend = () => {\n view.dom.dispatchEvent(\n new CustomEvent('imagePasted', {\n detail: imageInserterFactory(\n view,\n reader.result as string,\n createFileInfo(file),\n ),\n }),\n );\n };\n\n reader.readAsDataURL(file);\n }\n }\n\n const filteredSlice = new Slice(\n filterImageNodes(slice.content),\n slice.openStart,\n slice.openEnd,\n );\n\n if (filteredSlice.content.childCount < slice.content.childCount) {\n const { state, dispatch } = view;\n const tr = state.tr.replaceSelection(filteredSlice);\n dispatch(tr);\n\n return true;\n }\n\n return files.length > 0;\n};\n"]}
@@ -0,0 +1,128 @@
1
+ import { ImageState } from '../../../text-editor.types';
2
+ import translate from 'src/global/translations';
3
+ export const imageCache = new Map();
4
+ export function getImageNode(language) {
5
+ return { image: createImageNodeSpec(language) };
6
+ }
7
+ export function getImageNodeMarkdownSerializer(language) {
8
+ return { image: createImageNodeMarkdownSerializer(language) };
9
+ }
10
+ /**
11
+ * Recursively checks if a ProseMirror node or
12
+ * any of its child nodes is an image node.
13
+ */
14
+ export function hasImageNode(node) {
15
+ if (node.type.name === 'image') {
16
+ return true;
17
+ }
18
+ for (let i = 0; i < node.childCount; i++) {
19
+ const childNode = node.child(i);
20
+ if (hasImageNode(childNode)) {
21
+ return true;
22
+ }
23
+ }
24
+ return false;
25
+ }
26
+ function createImageNodeMarkdownSerializer(language) {
27
+ return (state, node) => {
28
+ if (node.attrs.state === ImageState.FAILED) {
29
+ const text = translate.get('editor-image-view.failed', language, {
30
+ filename: node.attrs.alt || 'file',
31
+ });
32
+ state.write(`<span>${text}</span>`);
33
+ return;
34
+ }
35
+ else if (node.attrs.state === ImageState.LOADING) {
36
+ const text = translate.get('editor-image-view.loading', language, {
37
+ filename: node.attrs.alt || 'file',
38
+ });
39
+ state.write(`<span>${text}</span>`);
40
+ return;
41
+ }
42
+ let imageHTML = `<img src="${node.attrs.src}"`;
43
+ if (node.attrs.alt) {
44
+ imageHTML += ` alt="${node.attrs.alt}"`;
45
+ }
46
+ const style = [];
47
+ if (node.attrs.width) {
48
+ style.push(`width: ${node.attrs.width};`);
49
+ }
50
+ if (node.attrs.maxWidth) {
51
+ style.push(`max-width: ${node.attrs.maxWidth};`);
52
+ }
53
+ if (style.length > 0) {
54
+ imageHTML += ` style="${style.join(' ')}"`;
55
+ }
56
+ imageHTML += ' />';
57
+ state.write(imageHTML);
58
+ };
59
+ }
60
+ function createImageNodeSpec(language) {
61
+ return {
62
+ group: 'inline',
63
+ inline: true,
64
+ attrs: {
65
+ src: { default: '' },
66
+ alt: { default: '' },
67
+ fileInfoId: { default: '' },
68
+ width: { default: '' },
69
+ maxWidth: { default: '100%' },
70
+ state: { default: '' },
71
+ },
72
+ toDOM: (node) => {
73
+ if (node.attrs.state === ImageState.FAILED) {
74
+ return createStatusSpan('failed', node, language);
75
+ }
76
+ else if (node.attrs.state === ImageState.LOADING) {
77
+ return createStatusSpan('loading', node, language);
78
+ }
79
+ let img = imageCache.get(node.attrs.fileInfoId);
80
+ if (img) {
81
+ updateImageElement(img, node);
82
+ }
83
+ else {
84
+ img = createImageElement(node);
85
+ imageCache.set(node.attrs.fileInfoId, img);
86
+ }
87
+ return img;
88
+ },
89
+ parseDOM: [
90
+ {
91
+ tag: 'img',
92
+ getAttrs: (dom) => {
93
+ return {
94
+ src: dom.getAttribute('src') || '',
95
+ alt: dom.getAttribute('alt') || 'file',
96
+ width: dom.style.width || '',
97
+ maxWidth: '100%',
98
+ state: ImageState.SUCCESS,
99
+ fileInfoId: crypto.randomUUID(),
100
+ };
101
+ },
102
+ },
103
+ ],
104
+ };
105
+ }
106
+ function createStatusSpan(key, node, language) {
107
+ const text = translate.get(`editor-image-view.${key}`, language, {
108
+ filename: node.attrs.alt || 'file',
109
+ });
110
+ const span = document.createElement('span');
111
+ span.textContent = text;
112
+ return span;
113
+ }
114
+ function updateImageElement(img, node) {
115
+ img.alt = node.attrs.alt;
116
+ img.style.maxWidth = node.attrs.maxWidth;
117
+ img.style.width = node.attrs.width;
118
+ return img;
119
+ }
120
+ function createImageElement(node) {
121
+ const img = document.createElement('img');
122
+ img.src = node.attrs.src;
123
+ img.alt = node.attrs.alt;
124
+ img.style.maxWidth = node.attrs.maxWidth;
125
+ img.style.width = node.attrs.width;
126
+ return img;
127
+ }
128
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.js","sourceRoot":"","sources":["../../../../../../src/components/text-editor/prosemirror-adapter/plugins/image/node.ts"],"names":[],"mappings":"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","sourcesContent":["import { NodeSpec, Node, Attrs, DOMOutputSpec } from 'prosemirror-model';\nimport { ImageState } from '../../../text-editor.types';\nimport { MarkdownSerializerState } from 'prosemirror-markdown';\nimport { Languages } from '../../../../date-picker/date.types';\nimport translate from 'src/global/translations';\n\nexport const imageCache = new Map<string, HTMLImageElement>();\n\ntype MarkdownSerializerFunction = (\n state: MarkdownSerializerState,\n node: Node,\n) => void;\n\nexport function getImageNode(language: Languages): Record<string, NodeSpec> {\n return { image: createImageNodeSpec(language) };\n}\n\nexport function getImageNodeMarkdownSerializer(\n language: Languages,\n): Record<string, MarkdownSerializerFunction> {\n return { image: createImageNodeMarkdownSerializer(language) };\n}\n\n/**\n * Recursively checks if a ProseMirror node or\n * any of its child nodes is an image node.\n */\nexport function hasImageNode(node: Node): boolean {\n if (node.type.name === 'image') {\n return true;\n }\n\n for (let i = 0; i < node.childCount; i++) {\n const childNode = node.child(i);\n if (hasImageNode(childNode)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction createImageNodeMarkdownSerializer(\n language: Languages,\n): MarkdownSerializerFunction {\n return (state: MarkdownSerializerState, node: Node) => {\n if (node.attrs.state === ImageState.FAILED) {\n const text = translate.get('editor-image-view.failed', language, {\n filename: node.attrs.alt || 'file',\n });\n state.write(`<span>${text}</span>`);\n\n return;\n } else if (node.attrs.state === ImageState.LOADING) {\n const text = translate.get('editor-image-view.loading', language, {\n filename: node.attrs.alt || 'file',\n });\n state.write(`<span>${text}</span>`);\n\n return;\n }\n\n let imageHTML = `<img src=\"${node.attrs.src}\"`;\n\n if (node.attrs.alt) {\n imageHTML += ` alt=\"${node.attrs.alt}\"`;\n }\n\n const style = [];\n\n if (node.attrs.width) {\n style.push(`width: ${node.attrs.width};`);\n }\n\n if (node.attrs.maxWidth) {\n style.push(`max-width: ${node.attrs.maxWidth};`);\n }\n\n if (style.length > 0) {\n imageHTML += ` style=\"${style.join(' ')}\"`;\n }\n\n imageHTML += ' />';\n\n state.write(imageHTML);\n };\n}\n\nfunction createImageNodeSpec(language: Languages): NodeSpec {\n return {\n group: 'inline',\n inline: true,\n attrs: {\n src: { default: '' },\n alt: { default: '' },\n fileInfoId: { default: '' },\n width: { default: '' },\n maxWidth: { default: '100%' },\n state: { default: '' },\n },\n toDOM: (node): DOMOutputSpec => {\n if (node.attrs.state === ImageState.FAILED) {\n return createStatusSpan('failed', node, language);\n } else if (node.attrs.state === ImageState.LOADING) {\n return createStatusSpan('loading', node, language);\n }\n\n let img = imageCache.get(node.attrs.fileInfoId);\n if (img) {\n updateImageElement(img, node);\n } else {\n img = createImageElement(node);\n imageCache.set(node.attrs.fileInfoId, img);\n }\n\n return img;\n },\n parseDOM: [\n {\n tag: 'img',\n getAttrs: (dom: HTMLElement): Attrs => {\n return {\n src: dom.getAttribute('src') || '',\n alt: dom.getAttribute('alt') || 'file',\n width: dom.style.width || '',\n maxWidth: '100%',\n state: ImageState.SUCCESS,\n fileInfoId: crypto.randomUUID(),\n };\n },\n },\n ],\n };\n}\n\nfunction createStatusSpan(\n key: string,\n node: Node,\n language: Languages,\n): HTMLSpanElement {\n const text = translate.get(`editor-image-view.${key}`, language, {\n filename: node.attrs.alt || 'file',\n });\n const span = document.createElement('span');\n span.textContent = text;\n\n return span;\n}\n\nfunction updateImageElement(\n img: HTMLImageElement,\n node: Node,\n): HTMLImageElement {\n img.alt = node.attrs.alt;\n img.style.maxWidth = node.attrs.maxWidth;\n img.style.width = node.attrs.width;\n\n return img;\n}\n\nfunction createImageElement(node: Node): HTMLImageElement {\n const img = document.createElement('img');\n img.src = node.attrs.src;\n img.alt = node.attrs.alt;\n img.style.maxWidth = node.attrs.maxWidth;\n img.style.width = node.attrs.width;\n\n return img;\n}\n"]}
@@ -0,0 +1,139 @@
1
+ import { Plugin } from 'prosemirror-state';
2
+ import { ImageState } from '../../../text-editor.types';
3
+ import translate from 'src/global/translations';
4
+ const MIN_WIDTH = 10;
5
+ export const createImageViewPlugin = (language) => {
6
+ return new Plugin({
7
+ props: {
8
+ nodeViews: {
9
+ image: (node, view, getPos) => {
10
+ return new ImageView(node, view, getPos, language);
11
+ },
12
+ },
13
+ },
14
+ });
15
+ };
16
+ class ImageView {
17
+ constructor(node, view, getPos, language) {
18
+ this.createResizeHandle = (position) => {
19
+ const handle = document.createElement('div');
20
+ handle.className = `resize-handle ${position}`;
21
+ handle.setAttribute('role', 'slider');
22
+ handle.setAttribute('aria-label', translate.get('editor-image-view.resize-handle', this.language));
23
+ handle.setAttribute('tabindex', '0');
24
+ handle.setAttribute('aria-valuemin', MIN_WIDTH.toString());
25
+ handle.setAttribute('aria-valuenow', this.img.offsetWidth.toString());
26
+ handle.setAttribute('aria-valuetext', `${this.img.offsetWidth} pixels`);
27
+ handle.setAttribute('aria-grabbed', 'false');
28
+ handle.addEventListener('pointerdown', (e) => {
29
+ handle.setAttribute('aria-grabbed', 'true');
30
+ this.onResizeStart(e, position);
31
+ });
32
+ return handle;
33
+ };
34
+ this.onResizeStart = (event, position) => {
35
+ event.preventDefault();
36
+ const handle = event.target;
37
+ const startX = event.clientX;
38
+ const startWidth = this.img.offsetWidth;
39
+ const onPointerMove = (e) => {
40
+ const delta = e.clientX - startX;
41
+ const widthDelta = position === 'top-left' ? -delta : delta;
42
+ const newWidth = Math.max(MIN_WIDTH, startWidth + widthDelta);
43
+ this.img.style.width = `${newWidth}px`;
44
+ const handles = this.dom.querySelectorAll('.resize-handle');
45
+ handles.forEach((resizeHandle) => {
46
+ resizeHandle.setAttribute('aria-valuenow', newWidth.toString());
47
+ resizeHandle.setAttribute('aria-valuetext', `${newWidth} pixels`);
48
+ });
49
+ };
50
+ const onPointerUp = () => {
51
+ window.removeEventListener('pointermove', onPointerMove);
52
+ window.removeEventListener('pointerup', onPointerUp);
53
+ handle.setAttribute('aria-grabbed', 'false');
54
+ this.view.dispatch(this.view.state.tr.setNodeMarkup(this.getPos(), undefined, Object.assign(Object.assign({}, this.node.attrs), { width: this.img.style.width, height: this.node.attrs.height })));
55
+ };
56
+ window.addEventListener('pointermove', onPointerMove);
57
+ window.addEventListener('pointerup', onPointerUp);
58
+ };
59
+ this.createLoadingState = () => {
60
+ this.dom.setAttribute('aria-live', 'polite');
61
+ this.dom.setAttribute('aria-busy', 'true');
62
+ this.dom.setAttribute('aria-label', translate.get('editor-image-view.loading', this.language, {
63
+ filename: this.node.attrs.alt || 'file',
64
+ }));
65
+ const spinnerElement = document.createElement('limel-linear-progress');
66
+ spinnerElement.setAttribute('indeterminate', 'true');
67
+ this.dom.appendChild(spinnerElement);
68
+ };
69
+ this.createSuccessState = () => {
70
+ this.dom.setAttribute('aria-live', 'polite');
71
+ this.dom.setAttribute('aria-busy', 'false');
72
+ this.dom.setAttribute('aria-label', translate.get('editor-image-view.success', this.language, {
73
+ filename: this.node.attrs.alt || 'file',
74
+ }));
75
+ const bottomRightHandle = this.createResizeHandle('bottom-right');
76
+ const topLeftHandle = this.createResizeHandle('top-left');
77
+ this.dom.appendChild(bottomRightHandle);
78
+ this.dom.appendChild(topLeftHandle);
79
+ };
80
+ this.createFailedState = () => {
81
+ this.dom.setAttribute('aria-live', 'assertive');
82
+ this.dom.setAttribute('aria-busy', 'false');
83
+ this.dom.setAttribute('aria-label', translate.get('editor-image-view.failed', this.language, {
84
+ filename: this.node.attrs.alt || 'file',
85
+ }));
86
+ };
87
+ this.cleanUpPreviousState = () => {
88
+ Array.from(this.dom.childNodes).forEach((child) => {
89
+ if (!(child instanceof HTMLImageElement)) {
90
+ child.remove();
91
+ }
92
+ });
93
+ };
94
+ this.transitionBetweenStates = () => {
95
+ this.cleanUpPreviousState();
96
+ this.dom.className = `image-wrapper state-${this.node.attrs.state}`;
97
+ if (this.node.attrs.state === ImageState.LOADING) {
98
+ this.createLoadingState();
99
+ }
100
+ else if (this.node.attrs.state === ImageState.SUCCESS) {
101
+ this.createSuccessState();
102
+ }
103
+ else if (this.node.attrs.state === ImageState.FAILED) {
104
+ this.createFailedState();
105
+ }
106
+ };
107
+ this.transitioningBetweenSuccessStates = (newNode) => {
108
+ return (this.node.attrs.state === ImageState.SUCCESS &&
109
+ newNode.attrs.state === ImageState.SUCCESS);
110
+ };
111
+ this.node = node;
112
+ this.view = view;
113
+ this.getPos = getPos;
114
+ this.language = language;
115
+ this.dom = document.createElement('div');
116
+ this.dom.className = `image-wrapper state-${node.attrs.state}`;
117
+ this.img = document.createElement('img');
118
+ this.img.src = node.attrs.src;
119
+ this.img.alt = node.attrs.alt;
120
+ this.img.style.maxWidth = node.attrs.maxWidth;
121
+ this.img.style.width = node.attrs.width;
122
+ this.dom.appendChild(this.img);
123
+ this.transitionBetweenStates();
124
+ }
125
+ // Ensure that the existing NodeView is reused rather than recreated.
126
+ // Recreating the NodeView will cause flickering between states.
127
+ update(node) {
128
+ if (!this.transitioningBetweenSuccessStates(node)) {
129
+ this.img.src = node.attrs.src;
130
+ this.img.alt = node.attrs.alt;
131
+ this.img.style.maxWidth = node.attrs.maxWidth;
132
+ this.img.style.width = node.attrs.width;
133
+ }
134
+ this.node = node;
135
+ this.transitionBetweenStates();
136
+ return true;
137
+ }
138
+ }
139
+ //# sourceMappingURL=view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view.js","sourceRoot":"","sources":["../../../../../../src/components/text-editor/prosemirror-adapter/plugins/image/view.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,SAAS,MAAM,yBAAyB,CAAC;AAGhD,MAAM,SAAS,GAAG,EAAE,CAAC;AAErB,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,QAAmB,EAAE,EAAE;EACzD,OAAO,IAAI,MAAM,CAAC;IACd,KAAK,EAAE;MACH,SAAS,EAAE;QACP,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;UAC1B,OAAO,IAAI,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QACvD,CAAC;OACJ;KACJ;GACJ,CAAC,CAAC;AACP,CAAC,CAAC;AAEF,MAAM,SAAS;EAQX,YACI,IAAU,EACV,IAAgB,EAChB,MAAoB,EACpB,QAAmB;IAqBf,uBAAkB,GAAG,CAAC,QAAqC,EAAE,EAAE;MACnE,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;MAC7C,MAAM,CAAC,SAAS,GAAG,iBAAiB,QAAQ,EAAE,CAAC;MAC/C,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;MACtC,MAAM,CAAC,YAAY,CACf,YAAY,EACZ,SAAS,CAAC,GAAG,CAAC,iCAAiC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAClE,CAAC;MACF,MAAM,CAAC,YAAY,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;MACrC,MAAM,CAAC,YAAY,CAAC,eAAe,EAAE,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC;MAC3D,MAAM,CAAC,YAAY,CAAC,eAAe,EAAE,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC;MACtE,MAAM,CAAC,YAAY,CAAC,gBAAgB,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,SAAS,CAAC,CAAC;MACxE,MAAM,CAAC,YAAY,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;MAE7C,MAAM,CAAC,gBAAgB,CAAC,aAAa,EAAE,CAAC,CAAe,EAAE,EAAE;QACvD,MAAM,CAAC,YAAY,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;QAC5C,IAAI,CAAC,aAAa,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;MACpC,CAAC,CAAC,CAAC;MAEH,OAAO,MAAM,CAAC;IAClB,CAAC,CAAC;IAEM,kBAAa,GAAG,CACpB,KAAmB,EACnB,QAAqC,EACvC,EAAE;MACA,KAAK,CAAC,cAAc,EAAE,CAAC;MACvB,MAAM,MAAM,GAAG,KAAK,CAAC,MAAqB,CAAC;MAE3C,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC;MAC7B,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC;MAExC,MAAM,aAAa,GAAG,CAAC,CAAe,EAAE,EAAE;QACtC,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,GAAG,MAAM,CAAC;QACjC,MAAM,UAAU,GAAG,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;QAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,UAAU,GAAG,UAAU,CAAC,CAAC;QAE9D,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,QAAQ,IAAI,CAAC;QAEvC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;QAC5D,OAAO,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,EAAE;UAC7B,YAAY,CAAC,YAAY,CAAC,eAAe,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;UAChE,YAAY,CAAC,YAAY,CACrB,gBAAgB,EAChB,GAAG,QAAQ,SAAS,CACvB,CAAC;QACN,CAAC,CAAC,CAAC;MACP,CAAC,CAAC;MAEF,MAAM,WAAW,GAAG,GAAG,EAAE;QACrB,MAAM,CAAC,mBAAmB,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;QACzD,MAAM,CAAC,mBAAmB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;QACrD,MAAM,CAAC,YAAY,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;QAE7C,IAAI,CAAC,IAAI,CAAC,QAAQ,CACd,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,SAAS,kCAClD,IAAI,CAAC,IAAI,CAAC,KAAK,KAClB,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAC3B,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,IAChC,CACL,CAAC;MACN,CAAC,CAAC;MAEF,MAAM,CAAC,gBAAgB,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;MACtD,MAAM,CAAC,gBAAgB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IACtD,CAAC,CAAC;IAEM,uBAAkB,GAAG,GAAG,EAAE;MAC9B,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;MAC7C,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;MAC3C,IAAI,CAAC,GAAG,CAAC,YAAY,CACjB,YAAY,EACZ,SAAS,CAAC,GAAG,CAAC,2BAA2B,EAAE,IAAI,CAAC,QAAQ,EAAE;QACtD,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,MAAM;OAC1C,CAAC,CACL,CAAC;MAEF,MAAM,cAAc,GAAG,QAAQ,CAAC,aAAa,CAAC,uBAAuB,CAAC,CAAC;MACvE,cAAc,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;MACrD,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC;IACzC,CAAC,CAAC;IAEM,uBAAkB,GAAG,GAAG,EAAE;MAC9B,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;MAC7C,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;MAC5C,IAAI,CAAC,GAAG,CAAC,YAAY,CACjB,YAAY,EACZ,SAAS,CAAC,GAAG,CAAC,2BAA2B,EAAE,IAAI,CAAC,QAAQ,EAAE;QACtD,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,MAAM;OAC1C,CAAC,CACL,CAAC;MAEF,MAAM,iBAAiB,GAAG,IAAI,CAAC,kBAAkB,CAAC,cAAc,CAAC,CAAC;MAClE,MAAM,aAAa,GAAG,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC;MAE1D,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,iBAAiB,CAAC,CAAC;MACxC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;IACxC,CAAC,CAAC;IAEM,sBAAiB,GAAG,GAAG,EAAE;MAC7B,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;MAChD,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;MAC5C,IAAI,CAAC,GAAG,CAAC,YAAY,CACjB,YAAY,EACZ,SAAS,CAAC,GAAG,CAAC,0BAA0B,EAAE,IAAI,CAAC,QAAQ,EAAE;QACrD,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,MAAM;OAC1C,CAAC,CACL,CAAC;IACN,CAAC,CAAC;IAEM,yBAAoB,GAAG,GAAG,EAAE;MAChC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;QAC9C,IAAI,CAAC,CAAC,KAAK,YAAY,gBAAgB,CAAC,EAAE;UACtC,KAAK,CAAC,MAAM,EAAE,CAAC;SAClB;MACL,CAAC,CAAC,CAAC;IACP,CAAC,CAAC;IAEM,4BAAuB,GAAG,GAAG,EAAE;MACnC,IAAI,CAAC,oBAAoB,EAAE,CAAC;MAC5B,IAAI,CAAC,GAAG,CAAC,SAAS,GAAG,uBAAuB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;MAEpE,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,UAAU,CAAC,OAAO,EAAE;QAC9C,IAAI,CAAC,kBAAkB,EAAE,CAAC;OAC7B;WAAM,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,UAAU,CAAC,OAAO,EAAE;QACrD,IAAI,CAAC,kBAAkB,EAAE,CAAC;OAC7B;WAAM,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,UAAU,CAAC,MAAM,EAAE;QACpD,IAAI,CAAC,iBAAiB,EAAE,CAAC;OAC5B;IACL,CAAC,CAAC;IAEM,sCAAiC,GAAG,CAAC,OAAa,EAAW,EAAE;MACnE,OAAO,CACH,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,UAAU,CAAC,OAAO;QAC5C,OAAO,CAAC,KAAK,CAAC,KAAK,KAAK,UAAU,CAAC,OAAO,CAC7C,CAAC;IACN,CAAC,CAAC;IA3JE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACrB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAEzB,IAAI,CAAC,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IACzC,IAAI,CAAC,GAAG,CAAC,SAAS,GAAG,uBAAuB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IAE/D,IAAI,CAAC,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IACzC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;IAC9B,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;IAC9B,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC9C,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;IAExC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAE/B,IAAI,CAAC,uBAAuB,EAAE,CAAC;EACnC,CAAC;EA4ID,qEAAqE;EACrE,gEAAgE;EACzD,MAAM,CAAC,IAAU;IACpB,IAAI,CAAC,IAAI,CAAC,iCAAiC,CAAC,IAAI,CAAC,EAAE;MAC/C,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;MAC9B,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;MAC9B,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;MAC9C,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;KAC3C;IAED,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACjB,IAAI,CAAC,uBAAuB,EAAE,CAAC;IAE/B,OAAO,IAAI,CAAC;EAChB,CAAC;CACJ","sourcesContent":["import { EditorView, NodeView } from 'prosemirror-view';\nimport { Node } from 'prosemirror-model';\nimport { Plugin } from 'prosemirror-state';\nimport { ImageState } from '../../../text-editor.types';\nimport translate from 'src/global/translations';\nimport { Languages } from '../../../../date-picker/date.types';\n\nconst MIN_WIDTH = 10;\n\nexport const createImageViewPlugin = (language: Languages) => {\n return new Plugin({\n props: {\n nodeViews: {\n image: (node, view, getPos) => {\n return new ImageView(node, view, getPos, language);\n },\n },\n },\n });\n};\n\nclass ImageView implements NodeView {\n node: Node;\n view: EditorView;\n getPos: () => number;\n dom: HTMLDivElement;\n img: HTMLImageElement;\n language: Languages;\n\n public constructor(\n node: Node,\n view: EditorView,\n getPos: () => number,\n language: Languages,\n ) {\n this.node = node;\n this.view = view;\n this.getPos = getPos;\n this.language = language;\n\n this.dom = document.createElement('div');\n this.dom.className = `image-wrapper state-${node.attrs.state}`;\n\n this.img = document.createElement('img');\n this.img.src = node.attrs.src;\n this.img.alt = node.attrs.alt;\n this.img.style.maxWidth = node.attrs.maxWidth;\n this.img.style.width = node.attrs.width;\n\n this.dom.appendChild(this.img);\n\n this.transitionBetweenStates();\n }\n\n private createResizeHandle = (position: 'bottom-right' | 'top-left') => {\n const handle = document.createElement('div');\n handle.className = `resize-handle ${position}`;\n handle.setAttribute('role', 'slider');\n handle.setAttribute(\n 'aria-label',\n translate.get('editor-image-view.resize-handle', this.language),\n );\n handle.setAttribute('tabindex', '0');\n handle.setAttribute('aria-valuemin', MIN_WIDTH.toString());\n handle.setAttribute('aria-valuenow', this.img.offsetWidth.toString());\n handle.setAttribute('aria-valuetext', `${this.img.offsetWidth} pixels`);\n handle.setAttribute('aria-grabbed', 'false');\n\n handle.addEventListener('pointerdown', (e: PointerEvent) => {\n handle.setAttribute('aria-grabbed', 'true');\n this.onResizeStart(e, position);\n });\n\n return handle;\n };\n\n private onResizeStart = (\n event: PointerEvent,\n position: 'bottom-right' | 'top-left',\n ) => {\n event.preventDefault();\n const handle = event.target as HTMLElement;\n\n const startX = event.clientX;\n const startWidth = this.img.offsetWidth;\n\n const onPointerMove = (e: PointerEvent) => {\n const delta = e.clientX - startX;\n const widthDelta = position === 'top-left' ? -delta : delta;\n const newWidth = Math.max(MIN_WIDTH, startWidth + widthDelta);\n\n this.img.style.width = `${newWidth}px`;\n\n const handles = this.dom.querySelectorAll('.resize-handle');\n handles.forEach((resizeHandle) => {\n resizeHandle.setAttribute('aria-valuenow', newWidth.toString());\n resizeHandle.setAttribute(\n 'aria-valuetext',\n `${newWidth} pixels`,\n );\n });\n };\n\n const onPointerUp = () => {\n window.removeEventListener('pointermove', onPointerMove);\n window.removeEventListener('pointerup', onPointerUp);\n handle.setAttribute('aria-grabbed', 'false');\n\n this.view.dispatch(\n this.view.state.tr.setNodeMarkup(this.getPos(), undefined, {\n ...this.node.attrs,\n width: this.img.style.width,\n height: this.node.attrs.height,\n }),\n );\n };\n\n window.addEventListener('pointermove', onPointerMove);\n window.addEventListener('pointerup', onPointerUp);\n };\n\n private createLoadingState = () => {\n this.dom.setAttribute('aria-live', 'polite');\n this.dom.setAttribute('aria-busy', 'true');\n this.dom.setAttribute(\n 'aria-label',\n translate.get('editor-image-view.loading', this.language, {\n filename: this.node.attrs.alt || 'file',\n }),\n );\n\n const spinnerElement = document.createElement('limel-linear-progress');\n spinnerElement.setAttribute('indeterminate', 'true');\n this.dom.appendChild(spinnerElement);\n };\n\n private createSuccessState = () => {\n this.dom.setAttribute('aria-live', 'polite');\n this.dom.setAttribute('aria-busy', 'false');\n this.dom.setAttribute(\n 'aria-label',\n translate.get('editor-image-view.success', this.language, {\n filename: this.node.attrs.alt || 'file',\n }),\n );\n\n const bottomRightHandle = this.createResizeHandle('bottom-right');\n const topLeftHandle = this.createResizeHandle('top-left');\n\n this.dom.appendChild(bottomRightHandle);\n this.dom.appendChild(topLeftHandle);\n };\n\n private createFailedState = () => {\n this.dom.setAttribute('aria-live', 'assertive');\n this.dom.setAttribute('aria-busy', 'false');\n this.dom.setAttribute(\n 'aria-label',\n translate.get('editor-image-view.failed', this.language, {\n filename: this.node.attrs.alt || 'file',\n }),\n );\n };\n\n private cleanUpPreviousState = () => {\n Array.from(this.dom.childNodes).forEach((child) => {\n if (!(child instanceof HTMLImageElement)) {\n child.remove();\n }\n });\n };\n\n private transitionBetweenStates = () => {\n this.cleanUpPreviousState();\n this.dom.className = `image-wrapper state-${this.node.attrs.state}`;\n\n if (this.node.attrs.state === ImageState.LOADING) {\n this.createLoadingState();\n } else if (this.node.attrs.state === ImageState.SUCCESS) {\n this.createSuccessState();\n } else if (this.node.attrs.state === ImageState.FAILED) {\n this.createFailedState();\n }\n };\n\n private transitioningBetweenSuccessStates = (newNode: Node): boolean => {\n return (\n this.node.attrs.state === ImageState.SUCCESS &&\n newNode.attrs.state === ImageState.SUCCESS\n );\n };\n\n // Ensure that the existing NodeView is reused rather than recreated.\n // Recreating the NodeView will cause flickering between states.\n public update(node: Node): boolean {\n if (!this.transitioningBetweenSuccessStates(node)) {\n this.img.src = node.attrs.src;\n this.img.alt = node.attrs.alt;\n this.img.style.maxWidth = node.attrs.maxWidth;\n this.img.style.width = node.attrs.width;\n }\n\n this.node = node;\n this.transitionBetweenStates();\n\n return true;\n }\n}\n"]}
@@ -621,4 +621,191 @@ kbd {
621
621
  img {
622
622
  max-width: 100%;
623
623
  border-radius: 0.25rem;
624
+ }
625
+
626
+ /**
627
+ * Note! This file is exported to `dist/scss/` in the published
628
+ * node module, for consumer projects to import.
629
+ * That means this file cannot import from any file that isn't
630
+ * also exported, keeping the same relative path.
631
+ *
632
+ * Or, just don't import anything, that works too.
633
+ */
634
+ /**
635
+ * This can be used on a trigger element that opens a dropdown menu or a popover.
636
+ */
637
+ /**
638
+ * This mixin will mask out the content that is close to
639
+ * the edges of a scrollable area.
640
+ * - If the scrollable content has `overflow-y`, use `vertically`
641
+ * as an argument for `$direction`.
642
+ - If the scrollable content has `overflow-x`, use `horizontally`
643
+ * as an argument for `$direction`.
644
+ *
645
+ * For the visual effect to work smoothly, we need to make sure that
646
+ * the size of the fade-out edge effect is the same as the
647
+ * internal paddings of the scrollable area. Otherwise, content of a
648
+ * scrollable area that does not have a padding will fade out before
649
+ * any scrolling has been done.
650
+ * This is why this mixin already adds paddings, which automatically
651
+ * default to the size of the fade-out effect.
652
+ * This size defaults to `1rem`, but to override the size use
653
+ * `--limel-top-edge-fade-height` & `--limel-bottom-edge-fade-height`
654
+ * when `vertically` argument is set, and use
655
+ * `--limel-left-edge-fade-width` & `--limel-right-edge-fade-width`
656
+ * when `horizontally` argument is set.
657
+ * Of course you can also programmatically increase and decrease the
658
+ * size of these variables for each edge, based on the amount of
659
+ * scrolling that has been done by the user. In this case, make sure
660
+ * to add a custom padding where the mixin is used, to override
661
+ * the paddings that are automatically added by the mixin in the
662
+ * compiled CSS code.
663
+ */
664
+ /**
665
+ * This mixin will add an animated underline to the bottom of an `a` elements.
666
+ * Note that you may need to add `all: unset;` –depending on your use case–
667
+ * before using this mixin.
668
+ */
669
+ /**
670
+ * This mixin creates a cross-browser font stack.
671
+ * - `sans-serif` can be used for the UI of the components.
672
+ * - `monospace` can be used for code.
673
+ *
674
+ * ⚠️ If we change the font stacks, we need to update
675
+ * 1. the consumer documentation in `README.md`, and
676
+ * 2. the CSS variables of `--kompendium-example-font-family`
677
+ * in the `<style>` tag of `index.html`.
678
+ */
679
+ /**
680
+ * This mixin is a hack, using old CSS syntax
681
+ * to enable you to truncate a piece of text,
682
+ * after a certain number of lines.
683
+ */
684
+ .image-wrapper {
685
+ display: inline-flex;
686
+ position: relative;
687
+ }
688
+ .image-wrapper limel-linear-progress {
689
+ position: absolute;
690
+ inset: 0.25rem auto auto 0.25rem;
691
+ width: calc(100% - 0.5rem);
692
+ }
693
+ .image-wrapper img {
694
+ transition: opacity 0.2s ease, scale 0.6s ease;
695
+ }
696
+ .image-wrapper.state-failed, .image-wrapper.state-loading {
697
+ background: url("data:image/svg+xml;charset=utf-8, <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8' style='fill-rule:evenodd;'><path fill='rgba(186,186,192,0.16)' d='M0 0h4v4H0zM4 4h4v4H4z'/></svg>");
698
+ background-size: 0.5rem;
699
+ }
700
+ .image-wrapper.state-failed img, .image-wrapper.state-loading img {
701
+ opacity: 0.3;
702
+ scale: 0.98;
703
+ }
704
+ .image-wrapper::before {
705
+ pointer-events: none;
706
+ content: "";
707
+ display: block;
708
+ position: absolute;
709
+ inset: 0;
710
+ border-radius: 0.25rem;
711
+ transition: border-color 0.4s ease, opacity 0.2s ease, box-shadow 0.6s ease;
712
+ border: 1px dashed transparent;
713
+ opacity: 0.2;
714
+ }
715
+ .image-wrapper.state-failed img {
716
+ filter: grayscale(0.8);
717
+ }
718
+ .image-wrapper.state-failed:before {
719
+ opacity: 0.8;
720
+ box-shadow: var(--shadow-error-state);
721
+ }
722
+ .image-wrapper:hover::before {
723
+ opacity: 0.8;
724
+ box-shadow: var(--shadow-depth-8);
725
+ }
726
+ .image-wrapper:has(.resize-handle[aria-grabbed=true])::before {
727
+ border-color: var(--mdc-theme-primary);
728
+ opacity: 0.8;
729
+ box-shadow: var(--shadow-depth-16), var(--shadow-depth-8);
730
+ }
731
+
732
+ .resize-handle {
733
+ transition: color var(--limel-clickable-transition-speed, 0.4s) ease, background-color var(--limel-clickable-transition-speed, 0.4s) ease, box-shadow var(--limel-clickable-transform-speed, 0.4s) ease, transform var(--limel-clickable-transform-speed, 0.4s) var(--limel-clickable-transform-timing-function, ease);
734
+ cursor: pointer;
735
+ color: var(--mdc-theme-on-surface);
736
+ background-color: rgb(var(--contrast-900));
737
+ box-shadow: var(--button-shadow-normal);
738
+ cursor: nwse-resize;
739
+ position: absolute;
740
+ display: flex;
741
+ align-items: center;
742
+ justify-content: center;
743
+ width: 0.5rem;
744
+ height: 0.5rem;
745
+ border-radius: 50%;
746
+ opacity: 0.6;
747
+ }
748
+ .resize-handle:hover, .resize-handle:focus, .resize-handle:focus-visible {
749
+ will-change: color, background-color, box-shadow, transform;
750
+ }
751
+ .resize-handle:hover {
752
+ transform: translate3d(0, -0.04rem, 0);
753
+ color: var(--mdc-theme-on-surface);
754
+ background-color: var(--lime-elevated-surface-background-color);
755
+ box-shadow: var(--button-shadow-hovered);
756
+ }
757
+ .resize-handle:active {
758
+ --limel-clickable-transform-timing-function: cubic-bezier(
759
+ 0.83,
760
+ -0.15,
761
+ 0.49,
762
+ 1.16
763
+ );
764
+ transform: translate3d(0, 0.05rem, 0);
765
+ box-shadow: var(--button-shadow-pressed);
766
+ }
767
+ .resize-handle:hover, .resize-handle:active {
768
+ --limel-clickable-transition-speed: 0.2s;
769
+ --limel-clickable-transform-speed: 0.16s;
770
+ }
771
+ .resize-handle:focus {
772
+ outline: none;
773
+ }
774
+ .resize-handle:focus-visible {
775
+ outline: none;
776
+ box-shadow: var(--shadow-depth-8-focused);
777
+ }
778
+ .resize-handle.top-left {
779
+ left: -0.25rem;
780
+ top: -0.25rem;
781
+ translate: -50%, -50%;
782
+ }
783
+ .resize-handle.bottom-right {
784
+ right: -0.25rem;
785
+ bottom: -0.25rem;
786
+ translate: 50%, 50%;
787
+ }
788
+ .resize-wrapper:has(.resize-handle[aria-grabbed=true]) .resize-handle, .resize-wrapper:hover .resize-handle, .resize-handle:hover, .resize-handle:focus-visible {
789
+ opacity: 1;
790
+ }
791
+ .resize-handle[aria-grabbed=true], .resize-handle:hover, .resize-handle:focus-visible {
792
+ opacity: 1;
793
+ background-color: var(--mdc-theme-primary);
794
+ }
795
+ .resize-handle:hover, .resize-handle[aria-grabbed=true] {
796
+ scale: 1.4;
797
+ }
798
+ .resize-handle:hover:before, .resize-handle[aria-grabbed=true]:before {
799
+ background-color: rgb(var(--color-white));
800
+ }
801
+ .resize-handle:before {
802
+ content: "";
803
+ display: block;
804
+ position: absolute;
805
+ inset: 0;
806
+ margin: auto;
807
+ border-radius: 50%;
808
+ width: 0.25rem;
809
+ height: 0.25rem;
810
+ background-color: rgb(var(--contrast-100));
624
811
  }
@@ -17,12 +17,14 @@ import { isItem } from 'src/components/action-bar/isItem';
17
17
  import { cloneDeep, debounce } from 'lodash-es';
18
18
  import { strikethrough } from './menu/menu-schema-extender';
19
19
  import { createLinkPlugin, } from './plugins/link-plugin';
20
- import { createImageRemoverPlugin } from './plugins/image-remover-plugin';
20
+ import { createImageInserterPlugin } from './plugins/image/inserter';
21
+ import { createImageViewPlugin } from './plugins/image/view';
21
22
  import { createMenuStateTrackingPlugin } from './plugins/menu-state-tracking-plugin';
22
23
  import { createActionBarInteractionPlugin } from './plugins/menu-action-interaction-plugin';
23
24
  import { createNodeSpec } from '../utils/plugin-factory';
24
25
  import { createTriggerPlugin } from './plugins/trigger/factory';
25
26
  import { getTableNodes, getTableEditingPlugins } from './plugins/table-plugin';
27
+ import { getImageNode, imageCache } from './plugins/image/node';
26
28
  const DEBOUNCE_TIMEOUT = 300;
27
29
  /**
28
30
  * The ProseMirror adapter offers a rich text editing experience with markdown support.
@@ -180,6 +182,7 @@ export class ProsemirrorAdapter {
180
182
  }
181
183
  disconnectedCallback() {
182
184
  var _a, _b, _c;
185
+ imageCache.clear();
183
186
  this.host.removeEventListener('open-editor-link-menu', this.handleOpenLinkMenu);
184
187
  (_b = (_a = this.view) === null || _a === void 0 ? void 0 : _a.dom) === null || _b === void 0 ? void 0 : _b.removeEventListener('blur', this.handleBlur);
185
188
  (_c = this.view) === null || _c === void 0 ? void 0 : _c.destroy();
@@ -195,7 +198,7 @@ export class ProsemirrorAdapter {
195
198
  }
196
199
  setupContentConverter() {
197
200
  if (this.contentType === 'markdown') {
198
- this.contentConverter = new MarkdownConverter(this.customElements);
201
+ this.contentConverter = new MarkdownConverter(this.customElements, this.language);
199
202
  }
200
203
  else if (this.contentType === 'html') {
201
204
  this.contentConverter = new HTMLConverter(this.customElements);
@@ -228,6 +231,7 @@ export class ProsemirrorAdapter {
228
231
  if (this.contentType === 'html') {
229
232
  nodes = nodes.append(getTableNodes());
230
233
  }
234
+ nodes = nodes.append(getImageNode(this.language));
231
235
  return new Schema({
232
236
  nodes: nodes,
233
237
  marks: schema.spec.marks.append({
@@ -254,7 +258,8 @@ export class ProsemirrorAdapter {
254
258
  keymap(this.menuCommandFactory.buildKeymap()),
255
259
  createTriggerPlugin(this.triggerCharacters, this.contentConverter),
256
260
  createLinkPlugin(this.handleNewLinkSelection),
257
- createImageRemoverPlugin(),
261
+ createImageInserterPlugin(this.imagePasted.emit, this.imageRemoved.emit),
262
+ createImageViewPlugin(this.language),
258
263
  createMenuStateTrackingPlugin(editorMenuTypesArray, this.menuCommandFactory, this.updateActiveActionBarItems),
259
264
  createActionBarInteractionPlugin(this.menuCommandFactory),
260
265
  ...getTableEditingPlugins(this.contentType === 'html'),
@@ -443,6 +448,58 @@ export class ProsemirrorAdapter {
443
448
  "resolved": "string",
444
449
  "references": {}
445
450
  }
451
+ }, {
452
+ "method": "imagePasted",
453
+ "name": "imagePasted",
454
+ "bubbles": true,
455
+ "cancelable": true,
456
+ "composed": true,
457
+ "docs": {
458
+ "tags": [{
459
+ "name": "private",
460
+ "text": undefined
461
+ }, {
462
+ "name": "alpha",
463
+ "text": undefined
464
+ }],
465
+ "text": "Dispatched when a image is pasted into the editor"
466
+ },
467
+ "complexType": {
468
+ "original": "ImageInserter",
469
+ "resolved": "ImageInserter",
470
+ "references": {
471
+ "ImageInserter": {
472
+ "location": "import",
473
+ "path": "../text-editor.types"
474
+ }
475
+ }
476
+ }
477
+ }, {
478
+ "method": "imageRemoved",
479
+ "name": "imageRemoved",
480
+ "bubbles": true,
481
+ "cancelable": true,
482
+ "composed": true,
483
+ "docs": {
484
+ "tags": [{
485
+ "name": "private",
486
+ "text": undefined
487
+ }, {
488
+ "name": "alpha",
489
+ "text": undefined
490
+ }],
491
+ "text": "Dispatched when a image is removed from the editor"
492
+ },
493
+ "complexType": {
494
+ "original": "ImageInfo",
495
+ "resolved": "ImageInfo",
496
+ "references": {
497
+ "ImageInfo": {
498
+ "location": "import",
499
+ "path": "../text-editor.types"
500
+ }
501
+ }
502
+ }
446
503
  }];
447
504
  }
448
505
  static get elementRef() { return "host"; }