@limetech/lime-elements 38.12.5 → 38.13.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.
- package/CHANGELOG.md +16 -0
- package/dist/cjs/lime-elements.cjs.js +1 -1
- package/dist/cjs/limel-markdown.cjs.entry.js +49 -1
- package/dist/cjs/limel-markdown.cjs.entry.js.map +1 -1
- package/dist/cjs/limel-prosemirror-adapter.cjs.entry.js +100 -29
- package/dist/cjs/limel-prosemirror-adapter.cjs.entry.js.map +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/{markdown-parser-5b5ed6c4.js → markdown-parser-564adb69.js} +32 -1
- package/dist/cjs/{markdown-parser-5b5ed6c4.js.map → markdown-parser-564adb69.js.map} +1 -1
- package/dist/collection/components/markdown/allowed-css-properties.js +3 -0
- package/dist/collection/components/markdown/allowed-css-properties.js.map +1 -1
- package/dist/collection/components/markdown/image-intersection-observer.js +29 -0
- package/dist/collection/components/markdown/image-intersection-observer.js.map +1 -0
- package/dist/collection/components/markdown/image-markdown-plugin.js +28 -0
- package/dist/collection/components/markdown/image-markdown-plugin.js.map +1 -0
- package/dist/collection/components/markdown/markdown-parser.js +2 -0
- package/dist/collection/components/markdown/markdown-parser.js.map +1 -1
- package/dist/collection/components/markdown/markdown.js +38 -0
- package/dist/collection/components/markdown/markdown.js.map +1 -1
- package/dist/collection/components/text-editor/prosemirror-adapter/plugins/image/inserter.js +59 -9
- package/dist/collection/components/text-editor/prosemirror-adapter/plugins/image/inserter.js.map +1 -1
- package/dist/collection/components/text-editor/prosemirror-adapter/plugins/image/node.js +31 -14
- package/dist/collection/components/text-editor/prosemirror-adapter/plugins/image/node.js.map +1 -1
- package/dist/collection/components/text-editor/prosemirror-adapter/plugins/image/view.js +10 -5
- package/dist/collection/components/text-editor/prosemirror-adapter/plugins/image/view.js.map +1 -1
- package/dist/esm/lime-elements.js +1 -1
- package/dist/esm/limel-markdown.entry.js +49 -1
- package/dist/esm/limel-markdown.entry.js.map +1 -1
- package/dist/esm/limel-prosemirror-adapter.entry.js +100 -29
- package/dist/esm/limel-prosemirror-adapter.entry.js.map +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/esm/{markdown-parser-ecdce41c.js → markdown-parser-1c1fdedc.js} +32 -1
- package/dist/esm/{markdown-parser-ecdce41c.js.map → markdown-parser-1c1fdedc.js.map} +1 -1
- package/dist/lime-elements/lime-elements.esm.js +1 -1
- package/dist/lime-elements/lime-elements.esm.js.map +1 -1
- package/dist/lime-elements/p-ce152b39.entry.js +2 -0
- package/dist/lime-elements/p-ce152b39.entry.js.map +1 -0
- package/dist/lime-elements/{p-e5c8cf08.js → p-cf87519f.js} +3 -3
- package/dist/lime-elements/p-cf87519f.js.map +1 -0
- package/dist/lime-elements/{p-8f4c55fa.entry.js → p-d5ac8f59.entry.js} +2 -2
- package/dist/lime-elements/p-d5ac8f59.entry.js.map +1 -0
- package/dist/types/components/markdown/image-intersection-observer.d.ts +10 -0
- package/dist/types/components/markdown/image-markdown-plugin.d.ts +9 -0
- package/dist/types/components/markdown/markdown-parser.d.ts +1 -0
- package/dist/types/components/markdown/markdown.d.ts +9 -1
- package/dist/types/components/text-editor/prosemirror-adapter/plugins/image/node.d.ts +4 -0
- package/dist/types/components.d.ts +8 -0
- package/package.json +1 -1
- package/dist/lime-elements/p-8f4c55fa.entry.js.map +0 -1
- package/dist/lime-elements/p-e5c8cf08.js.map +0 -1
- package/dist/lime-elements/p-eadff599.entry.js +0 -2
- package/dist/lime-elements/p-eadff599.entry.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"allowed-css-properties.js","sourceRoot":"","sources":["../../../src/components/markdown/allowed-css-properties.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG;EAChC,kBAAkB;EAClB,OAAO;EACP,YAAY;EACZ,aAAa;EACb,uBAAuB;EACvB,sBAAsB;EACtB,0BAA0B;EAC1B,uBAAuB;EACvB,2BAA2B;EAC3B,iBAAiB;EACjB,OAAO;
|
|
1
|
+
{"version":3,"file":"allowed-css-properties.js","sourceRoot":"","sources":["../../../src/components/markdown/allowed-css-properties.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG;EAChC,kBAAkB;EAClB,OAAO;EACP,YAAY;EACZ,aAAa;EACb,uBAAuB;EACvB,sBAAsB;EACtB,0BAA0B;EAC1B,uBAAuB;EACvB,2BAA2B;EAC3B,iBAAiB;EACjB,OAAO;EACP,QAAQ;EACR,WAAW;EACX,YAAY;CACf,CAAC","sourcesContent":["export const allowedCssProperties = [\n 'background-color',\n 'color',\n 'font-style',\n 'font-weight',\n 'text-decoration-color',\n 'text-decoration-line',\n 'text-decoration-skip-ink',\n 'text-decoration-style',\n 'text-decoration-thickness',\n 'text-decoration',\n 'width',\n 'height',\n 'min-width',\n 'min-height',\n];\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export class ImageIntersectionObserver {
|
|
2
|
+
/**
|
|
3
|
+
* @param containerElement - The element containing images to observe.
|
|
4
|
+
*/
|
|
5
|
+
constructor(containerElement) {
|
|
6
|
+
this.handleIntersection = (entries) => {
|
|
7
|
+
entries.forEach((entry) => {
|
|
8
|
+
if (entry.isIntersecting) {
|
|
9
|
+
const img = entry.target;
|
|
10
|
+
const dataSrc = img.getAttribute('data-src');
|
|
11
|
+
if (dataSrc) {
|
|
12
|
+
img.setAttribute('src', dataSrc);
|
|
13
|
+
img.removeAttribute('data-src');
|
|
14
|
+
}
|
|
15
|
+
this.observer.unobserve(img);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
this.observer = new IntersectionObserver(this.handleIntersection);
|
|
20
|
+
const images = containerElement.querySelectorAll('img');
|
|
21
|
+
images.forEach((img) => {
|
|
22
|
+
this.observer.observe(img);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
disconnect() {
|
|
26
|
+
this.observer.disconnect();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=image-intersection-observer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-intersection-observer.js","sourceRoot":"","sources":["../../../src/components/markdown/image-intersection-observer.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,yBAAyB;EAGlC;;KAEG;EACH,YAAmB,gBAA6B;IAa/B,uBAAkB,GAAG,CAClC,OAAoC,EACtC,EAAE;MACA,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;QACtB,IAAI,KAAK,CAAC,cAAc,EAAE;UACtB,MAAM,GAAG,GAAG,KAAK,CAAC,MAA0B,CAAC;UAC7C,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;UAE7C,IAAI,OAAO,EAAE;YACT,GAAG,CAAC,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YACjC,GAAG,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;WACnC;UAED,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;SAChC;MACL,CAAC,CAAC,CAAC;IACP,CAAC,CAAC;IA5BE,IAAI,CAAC,QAAQ,GAAG,IAAI,oBAAoB,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAElE,MAAM,MAAM,GAAG,gBAAgB,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACxD,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;MACnB,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;EACP,CAAC;EAEM,UAAU;IACb,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;EAC/B,CAAC;CAmBJ","sourcesContent":["export class ImageIntersectionObserver {\n private observer: IntersectionObserver;\n\n /**\n * @param containerElement - The element containing images to observe.\n */\n public constructor(containerElement: HTMLElement) {\n this.observer = new IntersectionObserver(this.handleIntersection);\n\n const images = containerElement.querySelectorAll('img');\n images.forEach((img) => {\n this.observer.observe(img);\n });\n }\n\n public disconnect() {\n this.observer.disconnect();\n }\n\n private readonly handleIntersection = (\n entries: IntersectionObserverEntry[],\n ) => {\n entries.forEach((entry) => {\n if (entry.isIntersecting) {\n const img = entry.target as HTMLImageElement;\n const dataSrc = img.getAttribute('data-src');\n\n if (dataSrc) {\n img.setAttribute('src', dataSrc);\n img.removeAttribute('data-src');\n }\n\n this.observer.unobserve(img);\n }\n });\n };\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { visit } from 'unist-util-visit';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a unified.js plugin that transforms image elements for lazy loading
|
|
4
|
+
*
|
|
5
|
+
* @param lazyLoadImages - Whether to enable lazy loading for images
|
|
6
|
+
* @returns A unified.js plugin function
|
|
7
|
+
*/
|
|
8
|
+
export function createLazyLoadImagesPlugin(lazyLoadImages = false) {
|
|
9
|
+
return () => {
|
|
10
|
+
if (!lazyLoadImages) {
|
|
11
|
+
return (tree) => tree;
|
|
12
|
+
}
|
|
13
|
+
return (tree) => {
|
|
14
|
+
visit(tree, 'element', (node) => {
|
|
15
|
+
if (node.tagName === 'img') {
|
|
16
|
+
node.properties = node.properties || {};
|
|
17
|
+
node.properties.loading = 'lazy';
|
|
18
|
+
if (node.properties.src) {
|
|
19
|
+
node.properties['data-src'] = node.properties.src;
|
|
20
|
+
node.properties.src = undefined;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
return tree;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=image-markdown-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-markdown-plugin.js","sourceRoot":"","sources":["../../../src/components/markdown/image-markdown-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAIzC;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CAAC,cAAc,GAAG,KAAK;EAC7D,OAAO,GAAgB,EAAE;IACrB,IAAI,CAAC,cAAc,EAAE;MACjB,OAAO,CAAC,IAAU,EAAE,EAAE,CAAC,IAAI,CAAC;KAC/B;IAED,OAAO,CAAC,IAAU,EAAE,EAAE;MAClB,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,IAAS,EAAE,EAAE;QACjC,IAAI,IAAI,CAAC,OAAO,KAAK,KAAK,EAAE;UACxB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC;UACxC,IAAI,CAAC,UAAU,CAAC,OAAO,GAAG,MAAM,CAAC;UAEjC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE;YACrB,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAClD,IAAI,CAAC,UAAU,CAAC,GAAG,GAAG,SAAS,CAAC;WACnC;SACJ;MACL,CAAC,CAAC,CAAC;MAEH,OAAO,IAAI,CAAC;IAChB,CAAC,CAAC;EACN,CAAC,CAAC;AACN,CAAC","sourcesContent":["import { visit } from 'unist-util-visit';\nimport { Node } from 'unist';\nimport { Plugin, Transformer } from 'unified';\n\n/**\n * Creates a unified.js plugin that transforms image elements for lazy loading\n *\n * @param lazyLoadImages - Whether to enable lazy loading for images\n * @returns A unified.js plugin function\n */\nexport function createLazyLoadImagesPlugin(lazyLoadImages = false): Plugin {\n return (): Transformer => {\n if (!lazyLoadImages) {\n return (tree: Node) => tree;\n }\n\n return (tree: Node) => {\n visit(tree, 'element', (node: any) => {\n if (node.tagName === 'img') {\n node.properties = node.properties || {};\n node.properties.loading = 'lazy';\n\n if (node.properties.src) {\n node.properties['data-src'] = node.properties.src;\n node.properties.src = undefined;\n }\n }\n });\n\n return tree;\n };\n };\n}\n"]}
|
|
@@ -9,6 +9,7 @@ import rehypeStringify from 'rehype-stringify';
|
|
|
9
9
|
import rehypeRaw from 'rehype-raw';
|
|
10
10
|
import { visit } from 'unist-util-visit';
|
|
11
11
|
import { sanitizeStyle } from './sanitize-style';
|
|
12
|
+
import { createLazyLoadImagesPlugin } from './image-markdown-plugin';
|
|
12
13
|
/**
|
|
13
14
|
* Takes a string as input and returns a new string
|
|
14
15
|
* where the text has been converted to HTML.
|
|
@@ -42,6 +43,7 @@ export async function markdownToHTML(text, options) {
|
|
|
42
43
|
visit(tree, 'element', sanitizeStyle);
|
|
43
44
|
};
|
|
44
45
|
})
|
|
46
|
+
.use(createLazyLoadImagesPlugin(options === null || options === void 0 ? void 0 : options.lazyLoadImages))
|
|
45
47
|
.use(rehypeStringify)
|
|
46
48
|
.process(text);
|
|
47
49
|
return file.toString();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown-parser.js","sourceRoot":"","sources":["../../../src/components/markdown/markdown-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,WAAW,MAAM,cAAc,CAAC;AACvC,OAAO,YAAY,MAAM,eAAe,CAAC;AACzC,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,WAAW,MAAM,cAAc,CAAC;AACvC,OAAO,mBAAmB,MAAM,uBAAuB,CAAC;AACxD,OAAO,cAAc,EAAE,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,eAAe,MAAM,kBAAkB,CAAC;AAC/C,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"markdown-parser.js","sourceRoot":"","sources":["../../../src/components/markdown/markdown-parser.ts"],"names":[],"mappings":"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","sourcesContent":["import { unified } from 'unified';\nimport remarkParse from 'remark-parse';\nimport remarkRehype from 'remark-rehype';\nimport remarkGfm from 'remark-gfm';\nimport rehypeParse from 'rehype-parse';\nimport rehypeExternalLinks from 'rehype-external-links';\nimport rehypeSanitize, { defaultSchema } from 'rehype-sanitize';\nimport rehypeStringify from 'rehype-stringify';\nimport rehypeRaw from 'rehype-raw';\nimport { visit } from 'unist-util-visit';\nimport { sanitizeStyle } from './sanitize-style';\nimport { Node } from 'unist';\nimport { Schema } from 'rehype-sanitize/lib';\nimport { createLazyLoadImagesPlugin } from './image-markdown-plugin';\nimport { CustomElementDefinition } from '../../global/shared-types/custom-element.types';\n\n/**\n * Takes a string as input and returns a new string\n * where the text has been converted to HTML.\n *\n * If the text is formatted with .md markdown, it will\n * be transformed to HTML.\n *\n * If the text already is in HTML it will be sanitized and\n * \"dangerous\" tags such as <script> will be removed.\n *\n * @param text - The string to convert.\n * @param options - Options for the conversions.\n * @returns The resulting HTML.\n */\nexport async function markdownToHTML(\n text: string,\n options?: MarkdownToHTMLOptions,\n): Promise<string> {\n if (options?.forceHardLineBreaks) {\n text = text.replace(/(?<!\\\\)([\\n\\r])/g, ' $1');\n }\n\n const file = await unified()\n .use(remarkParse)\n .use(remarkGfm)\n .use(remarkRehype, { allowDangerousHtml: true })\n .use(rehypeExternalLinks, { target: '_blank' })\n .use(rehypeRaw)\n .use(rehypeSanitize, {\n ...getWhiteList(options?.whitelist ?? []),\n })\n .use(() => {\n return (tree: Node) => {\n // Run the sanitizeStyle function on all elements, to sanitize\n // the value of the `style` attribute, if there is one.\n visit(tree, 'element', sanitizeStyle);\n };\n })\n .use(createLazyLoadImagesPlugin(options?.lazyLoadImages))\n .use(rehypeStringify)\n .process(text);\n\n return file.toString();\n}\n\n/**\n * Sanitizes a given HTML string by removing dangerous tags and attributes.\n *\n * @param html - The string containing HTML to sanitize.\n * @param whitelist - Optional whitelist of custom components.\n * @returns The sanitized HTML string.\n */\nexport async function sanitizeHTML(\n html: string,\n whitelist?: CustomElementDefinition[],\n): Promise<string> {\n const file = await unified()\n .use(rehypeParse)\n .use(rehypeSanitize, {\n ...getWhiteList(whitelist ?? []),\n })\n .use(() => {\n return (tree: Node) => {\n // Run the sanitizeStyle function on all elements, to sanitize\n // the value of the `style` attribute, if there is one.\n visit(tree, 'element', sanitizeStyle);\n };\n })\n .use(rehypeStringify)\n .process(html);\n\n return file.toString();\n}\n\nfunction getWhiteList(allowedComponents: CustomElementDefinition[]): Schema {\n const defaultSchemaClone = [...(defaultSchema.attributes['*'] ?? [])];\n const asteriskAttributeWhitelist = defaultSchemaClone.filter((attr) => {\n return attr !== 'height';\n });\n asteriskAttributeWhitelist.push('style');\n\n const whitelist: Schema = {\n ...defaultSchema,\n tagNames: [\n ...(defaultSchema.tagNames || []),\n ...allowedComponents.map((component) => component.tagName),\n ],\n attributes: {\n ...defaultSchema.attributes,\n p: [\n ...(defaultSchema.attributes.p ?? []),\n ['className', 'MsoNormal'],\n ], // Allow the class 'MsoNormal' on <p> elements\n '*': asteriskAttributeWhitelist,\n },\n };\n\n for (const component of allowedComponents) {\n whitelist.attributes[component.tagName] = component.attributes;\n }\n\n return whitelist;\n}\n\n/**\n * Options for markdownToHTML.\n */\nexport interface MarkdownToHTMLOptions {\n /**\n * Set to `true` to convert all soft line breaks to hard line breaks.\n */\n forceHardLineBreaks?: boolean;\n whitelist?: CustomElementDefinition[];\n lazyLoadImages?: boolean;\n}\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { h } from '@stencil/core';
|
|
2
2
|
import { markdownToHTML } from './markdown-parser';
|
|
3
3
|
import { globalConfig } from '../../global/config';
|
|
4
|
+
import { ImageIntersectionObserver } from './image-intersection-observer';
|
|
4
5
|
/**
|
|
5
6
|
* The Markdown component receives markdown syntax
|
|
6
7
|
* and renders it as HTML.
|
|
@@ -22,17 +23,22 @@ import { globalConfig } from '../../global/config';
|
|
|
22
23
|
*/
|
|
23
24
|
export class Markdown {
|
|
24
25
|
constructor() {
|
|
26
|
+
this.imageIntersectionObserver = null;
|
|
25
27
|
this.value = undefined;
|
|
26
28
|
this.whitelist = globalConfig.markdownWhitelist;
|
|
29
|
+
this.lazyLoadImages = false;
|
|
27
30
|
}
|
|
28
31
|
async textChanged() {
|
|
29
32
|
var _a;
|
|
30
33
|
try {
|
|
34
|
+
this.cleanupImageIntersectionObserver();
|
|
31
35
|
const html = await markdownToHTML(this.value, {
|
|
32
36
|
forceHardLineBreaks: true,
|
|
33
37
|
whitelist: (_a = this.whitelist) !== null && _a !== void 0 ? _a : [],
|
|
38
|
+
lazyLoadImages: this.lazyLoadImages,
|
|
34
39
|
});
|
|
35
40
|
this.rootElement.innerHTML = html;
|
|
41
|
+
this.setupImageIntersectionObserver();
|
|
36
42
|
}
|
|
37
43
|
catch (error) {
|
|
38
44
|
// eslint-disable-next-line no-console
|
|
@@ -42,11 +48,25 @@ export class Markdown {
|
|
|
42
48
|
async componentDidLoad() {
|
|
43
49
|
this.textChanged();
|
|
44
50
|
}
|
|
51
|
+
disconnectedCallback() {
|
|
52
|
+
this.cleanupImageIntersectionObserver();
|
|
53
|
+
}
|
|
45
54
|
render() {
|
|
46
55
|
return [
|
|
47
56
|
h("div", { id: "markdown", ref: (el) => (this.rootElement = el) }),
|
|
48
57
|
];
|
|
49
58
|
}
|
|
59
|
+
setupImageIntersectionObserver() {
|
|
60
|
+
if (this.lazyLoadImages) {
|
|
61
|
+
this.imageIntersectionObserver = new ImageIntersectionObserver(this.rootElement);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
cleanupImageIntersectionObserver() {
|
|
65
|
+
if (this.imageIntersectionObserver) {
|
|
66
|
+
this.imageIntersectionObserver.disconnect();
|
|
67
|
+
this.imageIntersectionObserver = null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
50
70
|
static get is() { return "limel-markdown"; }
|
|
51
71
|
static get encapsulation() { return "shadow"; }
|
|
52
72
|
static get originalStyleUrls() {
|
|
@@ -101,6 +121,24 @@ export class Markdown {
|
|
|
101
121
|
"text": "Whitelisted html elements.\n\nAny custom element added here will not be sanitized and thus rendered.\nCan also be set via `limel-config`. Setting this property will override\nthe global config."
|
|
102
122
|
},
|
|
103
123
|
"defaultValue": "globalConfig.markdownWhitelist"
|
|
124
|
+
},
|
|
125
|
+
"lazyLoadImages": {
|
|
126
|
+
"type": "boolean",
|
|
127
|
+
"mutable": false,
|
|
128
|
+
"complexType": {
|
|
129
|
+
"original": "boolean",
|
|
130
|
+
"resolved": "boolean",
|
|
131
|
+
"references": {}
|
|
132
|
+
},
|
|
133
|
+
"required": false,
|
|
134
|
+
"optional": false,
|
|
135
|
+
"docs": {
|
|
136
|
+
"tags": [],
|
|
137
|
+
"text": "Enable lazy loading for images"
|
|
138
|
+
},
|
|
139
|
+
"attribute": "lazy-load-images",
|
|
140
|
+
"reflect": false,
|
|
141
|
+
"defaultValue": "false"
|
|
104
142
|
}
|
|
105
143
|
};
|
|
106
144
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../../src/components/markdown/markdown.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../../src/components/markdown/markdown.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAE1E;;;;;;;;;;;;;;;;;;GAkBG;AAMH,MAAM,OAAO,QAAQ;;IAgDT,8BAAyB,GAAqC,IAAI,CAAC;;qBA7BvE,YAAY,CAAC,iBAAiB;0BAMV,KAAK;;EAGtB,KAAK,CAAC,WAAW;;IACpB,IAAI;MACA,IAAI,CAAC,gCAAgC,EAAE,CAAC;MAExC,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,KAAK,EAAE;QAC1C,mBAAmB,EAAE,IAAI;QACzB,SAAS,EAAE,MAAA,IAAI,CAAC,SAAS,mCAAI,EAAE;QAC/B,cAAc,EAAE,IAAI,CAAC,cAAc;OACtC,CAAC,CAAC;MAEH,IAAI,CAAC,WAAW,CAAC,SAAS,GAAG,IAAI,CAAC;MAElC,IAAI,CAAC,8BAA8B,EAAE,CAAC;KACzC;IAAC,OAAO,KAAK,EAAE;MACZ,sCAAsC;MACtC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;KACxB;EACL,CAAC;EAKM,KAAK,CAAC,gBAAgB;IACzB,IAAI,CAAC,WAAW,EAAE,CAAC;EACvB,CAAC;EAEM,oBAAoB;IACvB,IAAI,CAAC,gCAAgC,EAAE,CAAC;EAC5C,CAAC;EAEM,MAAM;IACT,OAAO;MACH,WACI,EAAE,EAAC,UAAU,EACb,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,GAAG,EAAoB,CAAC,GACxD;KACL,CAAC;EACN,CAAC;EAEO,8BAA8B;IAClC,IAAI,IAAI,CAAC,cAAc,EAAE;MACrB,IAAI,CAAC,yBAAyB,GAAG,IAAI,yBAAyB,CAC1D,IAAI,CAAC,WAAW,CACnB,CAAC;KACL;EACL,CAAC;EAEO,gCAAgC;IACpC,IAAI,IAAI,CAAC,yBAAyB,EAAE;MAChC,IAAI,CAAC,yBAAyB,CAAC,UAAU,EAAE,CAAC;MAC5C,IAAI,CAAC,yBAAyB,GAAG,IAAI,CAAC;KACzC;EACL,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CACJ","sourcesContent":["import { Component, h, Prop, Watch } from '@stencil/core';\nimport { markdownToHTML } from './markdown-parser';\nimport { globalConfig } from '../../global/config';\nimport { CustomElementDefinition } from '../../global/shared-types/custom-element.types';\nimport { ImageIntersectionObserver } from './image-intersection-observer';\n\n/**\n * The Markdown component receives markdown syntax\n * and renders it as HTML.\n *\n * @exampleComponent limel-example-markdown-headings\n * @exampleComponent limel-example-markdown-emphasis\n * @exampleComponent limel-example-markdown-lists\n * @exampleComponent limel-example-markdown-links\n * @exampleComponent limel-example-markdown-images\n * @exampleComponent limel-example-markdown-code\n * @exampleComponent limel-example-markdown-footnotes\n * @exampleComponent limel-example-markdown-tables\n * @exampleComponent limel-example-markdown-html\n * @exampleComponent limel-example-markdown-keys\n * @exampleComponent limel-example-markdown-blockquotes\n * @exampleComponent limel-example-markdown-horizontal-rule\n * @exampleComponent limel-example-markdown-composite\n * @exampleComponent limel-example-markdown-custom-component\n */\n@Component({\n tag: 'limel-markdown',\n styleUrl: 'markdown.scss',\n shadow: true,\n})\nexport class Markdown {\n /**\n * The input text. Treated as GitHub Flavored Markdown, with the addition\n * that any included HTML will be parsed and rendered as HTML, rather than\n * as text.\n */\n @Prop()\n public value: string;\n\n /**\n * Whitelisted html elements.\n *\n * Any custom element added here will not be sanitized and thus rendered.\n * Can also be set via `limel-config`. Setting this property will override\n * the global config.\n * @alpha\n */\n @Prop()\n public whitelist?: CustomElementDefinition[] =\n globalConfig.markdownWhitelist;\n\n /**\n * Enable lazy loading for images\n */\n @Prop()\n public lazyLoadImages = false;\n\n @Watch('value')\n public async textChanged() {\n try {\n this.cleanupImageIntersectionObserver();\n\n const html = await markdownToHTML(this.value, {\n forceHardLineBreaks: true,\n whitelist: this.whitelist ?? [],\n lazyLoadImages: this.lazyLoadImages,\n });\n\n this.rootElement.innerHTML = html;\n\n this.setupImageIntersectionObserver();\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(error);\n }\n }\n\n private rootElement: HTMLDivElement;\n private imageIntersectionObserver: ImageIntersectionObserver | null = null;\n\n public async componentDidLoad() {\n this.textChanged();\n }\n\n public disconnectedCallback() {\n this.cleanupImageIntersectionObserver();\n }\n\n public render() {\n return [\n <div\n id=\"markdown\"\n ref={(el) => (this.rootElement = el as HTMLDivElement)}\n />,\n ];\n }\n\n private setupImageIntersectionObserver() {\n if (this.lazyLoadImages) {\n this.imageIntersectionObserver = new ImageIntersectionObserver(\n this.rootElement,\n );\n }\n }\n\n private cleanupImageIntersectionObserver() {\n if (this.imageIntersectionObserver) {\n this.imageIntersectionObserver.disconnect();\n this.imageIntersectionObserver = null;\n }\n }\n}\n"]}
|
package/dist/collection/components/text-editor/prosemirror-adapter/plugins/image/inserter.js
CHANGED
|
@@ -132,9 +132,29 @@ const processPasteEvent = (view, event, slice) => {
|
|
|
132
132
|
if (!clipboardData) {
|
|
133
133
|
return false;
|
|
134
134
|
}
|
|
135
|
+
const isImageFilePasted = handlePastedImages(view, clipboardData);
|
|
136
|
+
const filteredSlice = new Slice(filterImageNodes(slice.content), slice.openStart, slice.openEnd);
|
|
137
|
+
if (filteredSlice.content.childCount < slice.content.childCount) {
|
|
138
|
+
const { state, dispatch } = view;
|
|
139
|
+
const tr = state.tr.replaceSelection(filteredSlice);
|
|
140
|
+
dispatch(tr);
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
return isImageFilePasted;
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Processes any image files found in the clipboard data and dispatches an imagePasted event.
|
|
147
|
+
*
|
|
148
|
+
* @param view - The ProseMirror editor view
|
|
149
|
+
* @param clipboardData - The clipboard data transfer object containing potential image files
|
|
150
|
+
* @returns True if at least one valid image file was found and processed, false otherwise
|
|
151
|
+
*/
|
|
152
|
+
function handlePastedImages(view, clipboardData) {
|
|
153
|
+
let isImageFilePasted = false;
|
|
135
154
|
const files = Array.from(clipboardData.files || []);
|
|
136
155
|
for (const file of files) {
|
|
137
|
-
if (file
|
|
156
|
+
if (isImageFile(file, clipboardData)) {
|
|
157
|
+
isImageFilePasted = true;
|
|
138
158
|
const reader = new FileReader();
|
|
139
159
|
reader.onloadend = () => {
|
|
140
160
|
view.dom.dispatchEvent(new CustomEvent('imagePasted', {
|
|
@@ -144,13 +164,43 @@ const processPasteEvent = (view, event, slice) => {
|
|
|
144
164
|
reader.readAsDataURL(file);
|
|
145
165
|
}
|
|
146
166
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
167
|
+
return isImageFilePasted;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Determines if a file is an image that should be processed by the image handler.
|
|
171
|
+
*
|
|
172
|
+
* This function checks both the file's MIME type and the clipboard HTML content.
|
|
173
|
+
* It filters out HTML content from Excel and HTML tables, as they are not relevant for image processing.
|
|
174
|
+
*
|
|
175
|
+
* @param file - The file object to check
|
|
176
|
+
* @param clipboardData - The full clipboard data transfer object to examine for context
|
|
177
|
+
* @returns True if the file is an image that should be processed, false otherwise
|
|
178
|
+
*/
|
|
179
|
+
function isImageFile(file, clipboardData) {
|
|
180
|
+
var _a, _b;
|
|
181
|
+
if (!isContentTypeImage(file)) {
|
|
182
|
+
return false;
|
|
153
183
|
}
|
|
154
|
-
|
|
155
|
-
|
|
184
|
+
const html = (_b = (_a = clipboardData === null || clipboardData === void 0 ? void 0 : clipboardData.getData('text/html')) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : '';
|
|
185
|
+
return !isHtmlFromExcel(html) && !isHtmlTable(html);
|
|
186
|
+
}
|
|
187
|
+
function isContentTypeImage(file) {
|
|
188
|
+
if (!(file === null || file === void 0 ? void 0 : file.type)) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
return file.type.startsWith('image/');
|
|
192
|
+
}
|
|
193
|
+
function isHtmlFromExcel(html) {
|
|
194
|
+
if (!html) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
return (html.includes('name=generator content="microsoft excel"') ||
|
|
198
|
+
html.includes('xmlns:x="urn:schemas-microsoft-com:office:excel"'));
|
|
199
|
+
}
|
|
200
|
+
function isHtmlTable(html) {
|
|
201
|
+
if (!html) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
return html.includes('<table');
|
|
205
|
+
}
|
|
156
206
|
//# sourceMappingURL=inserter.js.map
|
package/dist/collection/components/text-editor/prosemirror-adapter/plugins/image/inserter.js.map
CHANGED
|
@@ -1 +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 } from 'prosemirror-state';\nimport { EditorView } from 'prosemirror-view';\nimport { createFileInfo } from '../../../../../util/files';\nimport { FileInfo } from '../../../../../global/shared-types/file.types';\nimport { ImageInserter, EditorImageState } from '../../../text-editor.types';\nimport { Node, Slice, Fragment } from 'prosemirror-model';\nimport { ImageNodeAttrs } from './node';\n\nexport const pluginKey = new PluginKey('imageInserterPlugin');\n\ntype ImagePastedCallback = (data: ImageInserter) => CustomEvent<ImageInserter>;\n\nexport const createImageInserterPlugin = (\n imagePastedCallback: ImagePastedCallback,\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 });\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 imageNodeAttrs = createImageNodeAttrs(\n base64Data,\n fileInfo,\n 'loading',\n );\n const placeholderNode = schema.nodes.image.create(imageNodeAttrs);\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 imageNodeAttrs = createImageNodeAttrs(\n src ? src : node.attrs.src,\n fileInfo,\n 'success',\n );\n const imageNode = schema.nodes.image.create(imageNodeAttrs);\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 imageNodeAttrs = createImageNodeAttrs(\n node.attrs.src,\n fileInfo,\n 'failed',\n );\n const errorPlaceholderNode =\n schema.nodes.image.create(imageNodeAttrs);\n\n tr.replaceWith(pos, pos + node.nodeSize, errorPlaceholderNode);\n\n return false;\n }\n });\n\n dispatch(tr);\n };\n\nfunction createImageNodeAttrs(\n src: string,\n fileInfo: FileInfo,\n state: EditorImageState,\n): ImageNodeAttrs {\n return {\n src: src,\n alt: fileInfo.filename,\n fileInfoId: fileInfo.id,\n state: state,\n };\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"]}
|
|
1
|
+
{"version":3,"file":"inserter.js","sourceRoot":"","sources":["../../../../../../src/components/text-editor/prosemirror-adapter/plugins/image/inserter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEtD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAG3D,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAG1D,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,qBAAqB,CAAC,CAAC;AAI9D,MAAM,CAAC,MAAM,yBAAyB,GAAG,CACrC,mBAAwC,EAC1C,EAAE;EACA,OAAO,IAAI,MAAM,CAAC;IACd,GAAG,EAAE,SAAS;IACd,KAAK,EAAE;MACH,WAAW,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QAChC,OAAO,iBAAiB,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;MACjD,CAAC;MACD,eAAe,EAAE;QACb,WAAW,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE;UACtB,mBAAmB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACtC,CAAC;OACJ;KACJ;GACJ,CAAC,CAAC;AACP,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAChC,IAAgB,EAChB,UAAkB,EAClB,QAAkB,EACL,EAAE;EACf,OAAO;IACH,QAAQ,EAAE,QAAQ;IAClB,eAAe,EAAE,uBAAuB,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC;IACpE,WAAW,EAAE,mBAAmB,CAAC,IAAI,EAAE,QAAQ,CAAC;IAChD,qBAAqB,EAAE,6BAA6B,CAAC,IAAI,EAAE,QAAQ,CAAC;GACvE,CAAC;AACN,CAAC,CAAC;AAEF,MAAM,uBAAuB,GACzB,CAAC,IAAgB,EAAE,UAAkB,EAAE,QAAkB,EAAE,EAAE,CAAC,GAAG,EAAE;EAC/D,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;EACjC,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;EAEzB,MAAM,cAAc,GAAG,oBAAoB,CACvC,UAAU,EACV,QAAQ,EACR,SAAS,CACZ,CAAC;EACF,MAAM,eAAe,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;EAElE,MAAM,WAAW,GAAG,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAAC,eAAe,CAAC,CAAC;EAEnE,QAAQ,CAAC,WAAW,CAAC,CAAC;AAC1B,CAAC,CAAC;AAEN,MAAM,mBAAmB,GACrB,CAAC,IAAgB,EAAE,QAAkB,EAAE,EAAE,CAAC,CAAC,GAAY,EAAE,EAAE;EACvD,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;EACjC,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;EAEzB,MAAM,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC;EACpB,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IAChC,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,KAAK,QAAQ,CAAC,EAAE,EAAE;MACvC,MAAM,cAAc,GAAG,oBAAoB,CACvC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAC1B,QAAQ,EACR,SAAS,CACZ,CAAC;MACF,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;MAE5D,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;MAEpD,OAAO,KAAK,CAAC;KAChB;EACL,CAAC,CAAC,CAAC;EAEH,QAAQ,CAAC,EAAE,CAAC,CAAC;AACjB,CAAC,CAAC;AAEN,MAAM,6BAA6B,GAC/B,CAAC,IAAgB,EAAE,QAAkB,EAAE,EAAE,CAAC,GAAG,EAAE;EAC3C,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;EACjC,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;EAEzB,MAAM,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC;EACpB,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IAChC,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,KAAK,QAAQ,CAAC,EAAE,EAAE;MACvC,MAAM,cAAc,GAAG,oBAAoB,CACvC,IAAI,CAAC,KAAK,CAAC,GAAG,EACd,QAAQ,EACR,QAAQ,CACX,CAAC;MACF,MAAM,oBAAoB,GACtB,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;MAE9C,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAC;MAE/D,OAAO,KAAK,CAAC;KAChB;EACL,CAAC,CAAC,CAAC;EAEH,QAAQ,CAAC,EAAE,CAAC,CAAC;AACjB,CAAC,CAAC;AAEN,SAAS,oBAAoB,CACzB,GAAW,EACX,QAAkB,EAClB,KAAuB;EAEvB,OAAO;IACH,GAAG,EAAE,GAAG;IACR,GAAG,EAAE,QAAQ,CAAC,QAAQ;IACtB,UAAU,EAAE,QAAQ,CAAC,EAAE;IACvB,KAAK,EAAE,KAAK;GACf,CAAC;AACN,CAAC;AAED;;;;GAIG;AACH,MAAM,WAAW,GAAG,CAAC,IAAqB,EAAW,EAAE;EACnD,IAAI,IAAI,YAAY,IAAI,EAAE;IACtB,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE;MAC5B,OAAO,IAAI,CAAC;KACf;IAED,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;MAC3B,IAAI,WAAW,CAAC,KAAK,CAAC,EAAE;QACpB,KAAK,GAAG,IAAI,CAAC;OAChB;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,KAAK,CAAC;GAChB;OAAM,IAAI,IAAI,YAAY,QAAQ,EAAE;IACjC,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;MACnB,IAAI,WAAW,CAAC,KAAK,CAAC,EAAE;QACpB,KAAK,GAAG,IAAI,CAAC;OAChB;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,KAAK,CAAC;GAChB;EAED,OAAO,KAAK,CAAC;AACjB,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,gBAAgB,GAAG,CAAC,QAAkB,EAAY,EAAE;EACtD,MAAM,gBAAgB,GAAW,EAAE,CAAC;EAEpC,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;IACvB,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE;MACrB,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,EAAE;QACxB,MAAM,eAAe,GAAG,gBAAgB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACxD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC5C,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;OAClC;WAAM;QACH,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;OAChC;KACJ;EACL,CAAC,CAAC,CAAC;EAEH,OAAO,QAAQ,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;AAChD,CAAC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,iBAAiB,GAAG,CACtB,IAAgB,EAChB,KAAqB,EACrB,KAAY,EACL,EAAE;EACT,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,CAAC;EAC1C,IAAI,CAAC,aAAa,EAAE;IAChB,OAAO,KAAK,CAAC;GAChB;EAED,MAAM,iBAAiB,GAAG,kBAAkB,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;EAElE,MAAM,aAAa,GAAG,IAAI,KAAK,CAC3B,gBAAgB,CAAC,KAAK,CAAC,OAAO,CAAC,EAC/B,KAAK,CAAC,SAAS,EACf,KAAK,CAAC,OAAO,CAChB,CAAC;EAEF,IAAI,aAAa,CAAC,OAAO,CAAC,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE;IAC7D,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IACjC,MAAM,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC;IACpD,QAAQ,CAAC,EAAE,CAAC,CAAC;IAEb,OAAO,IAAI,CAAC;GACf;EAED,OAAO,iBAAiB,CAAC;AAC7B,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,SAAS,kBAAkB,CACvB,IAAgB,EAChB,aAA2B;EAE3B,IAAI,iBAAiB,GAAG,KAAK,CAAC;EAC9B,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;EAEpD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;IACtB,IAAI,WAAW,CAAC,IAAI,EAAE,aAAa,CAAC,EAAE;MAClC,iBAAiB,GAAG,IAAI,CAAC;MAEzB,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;MAChC,MAAM,CAAC,SAAS,GAAG,GAAG,EAAE;QACpB,IAAI,CAAC,GAAG,CAAC,aAAa,CAClB,IAAI,WAAW,CAAC,aAAa,EAAE;UAC3B,MAAM,EAAE,oBAAoB,CACxB,IAAI,EACJ,MAAM,CAAC,MAAgB,EACvB,cAAc,CAAC,IAAI,CAAC,CACvB;SACJ,CAAC,CACL,CAAC;MACN,CAAC,CAAC;MAEF,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;KAC9B;GACJ;EAED,OAAO,iBAAiB,CAAC;AAC7B,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,WAAW,CAAC,IAAU,EAAE,aAA2B;;EACxD,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE;IAC3B,OAAO,KAAK,CAAC;GAChB;EAED,MAAM,IAAI,GAAG,MAAA,MAAA,aAAa,aAAb,aAAa,uBAAb,aAAa,CAAE,OAAO,CAAC,WAAW,CAAC,0CAAE,WAAW,EAAE,mCAAI,EAAE,CAAC;EAEtE,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAU;EAClC,IAAI,CAAC,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,IAAI,CAAA,EAAE;IACb,OAAO,KAAK,CAAC;GAChB;EAED,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,eAAe,CAAC,IAAY;EACjC,IAAI,CAAC,IAAI,EAAE;IACP,OAAO,KAAK,CAAC;GAChB;EAED,OAAO,CACH,IAAI,CAAC,QAAQ,CAAC,0CAA0C,CAAC;IACzD,IAAI,CAAC,QAAQ,CAAC,kDAAkD,CAAC,CACpE,CAAC;AACN,CAAC;AAED,SAAS,WAAW,CAAC,IAAY;EAC7B,IAAI,CAAC,IAAI,EAAE;IACP,OAAO,KAAK,CAAC;GAChB;EAED,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACnC,CAAC","sourcesContent":["import { Plugin, PluginKey } from 'prosemirror-state';\nimport { EditorView } from 'prosemirror-view';\nimport { createFileInfo } from '../../../../../util/files';\nimport { FileInfo } from '../../../../../global/shared-types/file.types';\nimport { ImageInserter, EditorImageState } from '../../../text-editor.types';\nimport { Node, Slice, Fragment } from 'prosemirror-model';\nimport { ImageNodeAttrs } from './node';\n\nexport const pluginKey = new PluginKey('imageInserterPlugin');\n\ntype ImagePastedCallback = (data: ImageInserter) => CustomEvent<ImageInserter>;\n\nexport const createImageInserterPlugin = (\n imagePastedCallback: ImagePastedCallback,\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 });\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 imageNodeAttrs = createImageNodeAttrs(\n base64Data,\n fileInfo,\n 'loading',\n );\n const placeholderNode = schema.nodes.image.create(imageNodeAttrs);\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 imageNodeAttrs = createImageNodeAttrs(\n src ? src : node.attrs.src,\n fileInfo,\n 'success',\n );\n const imageNode = schema.nodes.image.create(imageNodeAttrs);\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 imageNodeAttrs = createImageNodeAttrs(\n node.attrs.src,\n fileInfo,\n 'failed',\n );\n const errorPlaceholderNode =\n schema.nodes.image.create(imageNodeAttrs);\n\n tr.replaceWith(pos, pos + node.nodeSize, errorPlaceholderNode);\n\n return false;\n }\n });\n\n dispatch(tr);\n };\n\nfunction createImageNodeAttrs(\n src: string,\n fileInfo: FileInfo,\n state: EditorImageState,\n): ImageNodeAttrs {\n return {\n src: src,\n alt: fileInfo.filename,\n fileInfoId: fileInfo.id,\n state: state,\n };\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 isImageFilePasted = handlePastedImages(view, clipboardData);\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 isImageFilePasted;\n};\n\n/**\n * Processes any image files found in the clipboard data and dispatches an imagePasted event.\n *\n * @param view - The ProseMirror editor view\n * @param clipboardData - The clipboard data transfer object containing potential image files\n * @returns True if at least one valid image file was found and processed, false otherwise\n */\nfunction handlePastedImages(\n view: EditorView,\n clipboardData: DataTransfer,\n): boolean {\n let isImageFilePasted = false;\n const files = Array.from(clipboardData.files || []);\n\n for (const file of files) {\n if (isImageFile(file, clipboardData)) {\n isImageFilePasted = true;\n\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 return isImageFilePasted;\n}\n\n/**\n * Determines if a file is an image that should be processed by the image handler.\n *\n * This function checks both the file's MIME type and the clipboard HTML content.\n * It filters out HTML content from Excel and HTML tables, as they are not relevant for image processing.\n *\n * @param file - The file object to check\n * @param clipboardData - The full clipboard data transfer object to examine for context\n * @returns True if the file is an image that should be processed, false otherwise\n */\nfunction isImageFile(file: File, clipboardData: DataTransfer): boolean {\n if (!isContentTypeImage(file)) {\n return false;\n }\n\n const html = clipboardData?.getData('text/html')?.toLowerCase() ?? '';\n\n return !isHtmlFromExcel(html) && !isHtmlTable(html);\n}\n\nfunction isContentTypeImage(file: File): boolean {\n if (!file?.type) {\n return false;\n }\n\n return file.type.startsWith('image/');\n}\n\nfunction isHtmlFromExcel(html: string): boolean {\n if (!html) {\n return false;\n }\n\n return (\n html.includes('name=generator content=\"microsoft excel\"') ||\n html.includes('xmlns:x=\"urn:schemas-microsoft-com:office:excel\"')\n );\n}\n\nfunction isHtmlTable(html: string): boolean {\n if (!html) {\n return false;\n }\n\n return html.includes('<table');\n}\n"]}
|
|
@@ -6,6 +6,13 @@ export function getImageNode(language) {
|
|
|
6
6
|
export function getImageNodeMarkdownSerializer(language) {
|
|
7
7
|
return { image: createImageNodeMarkdownSerializer(language) };
|
|
8
8
|
}
|
|
9
|
+
export function applyImageStyles(img, node) {
|
|
10
|
+
img.style.height = node.attrs.height;
|
|
11
|
+
img.style.width = node.attrs.width;
|
|
12
|
+
img.style.minHeight = node.attrs.minHeight;
|
|
13
|
+
img.style.minWidth = node.attrs.minWidth;
|
|
14
|
+
img.style.maxWidth = node.attrs.maxWidth;
|
|
15
|
+
}
|
|
9
16
|
/**
|
|
10
17
|
* Recursively checks if a ProseMirror node or
|
|
11
18
|
* any of its child nodes is an image node.
|
|
@@ -24,16 +31,16 @@ export function hasImageNode(node) {
|
|
|
24
31
|
}
|
|
25
32
|
function createImageNodeMarkdownSerializer(language) {
|
|
26
33
|
return (markdownSerializerState, node) => {
|
|
27
|
-
const
|
|
34
|
+
const state = node.attrs.state;
|
|
28
35
|
if (!isEditorImageState(state)) {
|
|
29
36
|
return;
|
|
30
37
|
}
|
|
31
38
|
if (state === 'success') {
|
|
32
|
-
const imageHTML = getImageHTML(
|
|
39
|
+
const imageHTML = getImageHTML(node.attrs);
|
|
33
40
|
markdownSerializerState.write(imageHTML);
|
|
34
41
|
return;
|
|
35
42
|
}
|
|
36
|
-
const statusHTML = getStatusHTML(state, alt, language);
|
|
43
|
+
const statusHTML = getStatusHTML(state, node.attrs.alt, language);
|
|
37
44
|
markdownSerializerState.write(statusHTML);
|
|
38
45
|
};
|
|
39
46
|
}
|
|
@@ -44,16 +51,25 @@ function getStatusHTML(state, alt, language) {
|
|
|
44
51
|
});
|
|
45
52
|
return `<span>${text}</span>`;
|
|
46
53
|
}
|
|
47
|
-
function getImageHTML(
|
|
54
|
+
function getImageHTML(attrs) {
|
|
48
55
|
const style = [];
|
|
49
|
-
if (
|
|
50
|
-
style.push(`
|
|
56
|
+
if (attrs.height) {
|
|
57
|
+
style.push(`height: ${attrs.height};`);
|
|
58
|
+
}
|
|
59
|
+
if (attrs.width) {
|
|
60
|
+
style.push(`width: ${attrs.width};`);
|
|
61
|
+
}
|
|
62
|
+
if (attrs.minHeight) {
|
|
63
|
+
style.push(`min-height: ${attrs.minHeight};`);
|
|
51
64
|
}
|
|
52
|
-
if (
|
|
53
|
-
style.push(`
|
|
65
|
+
if (attrs.minWidth) {
|
|
66
|
+
style.push(`min-width: ${attrs.minWidth};`);
|
|
54
67
|
}
|
|
55
|
-
|
|
56
|
-
|
|
68
|
+
if (attrs.maxWidth) {
|
|
69
|
+
style.push(`max-width: ${attrs.maxWidth};`);
|
|
70
|
+
}
|
|
71
|
+
const styleAttribute = style.length > 0 ? ` style="${style.join('')}"` : '';
|
|
72
|
+
return `<img src="${attrs.src}" alt="${attrs.alt}"${styleAttribute} />`;
|
|
57
73
|
}
|
|
58
74
|
function createImageNodeSpec(language) {
|
|
59
75
|
return {
|
|
@@ -63,7 +79,10 @@ function createImageNodeSpec(language) {
|
|
|
63
79
|
src: { default: '' },
|
|
64
80
|
alt: { default: '' },
|
|
65
81
|
fileInfoId: { default: '' },
|
|
82
|
+
height: { default: '' },
|
|
66
83
|
width: { default: '' },
|
|
84
|
+
minHeight: { default: '' },
|
|
85
|
+
minWidth: { default: '' },
|
|
67
86
|
maxWidth: { default: '100%' },
|
|
68
87
|
state: { default: 'success' },
|
|
69
88
|
},
|
|
@@ -121,16 +140,14 @@ function createStatusSpan(key, node, language) {
|
|
|
121
140
|
}
|
|
122
141
|
function updateImageElement(img, node) {
|
|
123
142
|
img.alt = node.attrs.alt;
|
|
124
|
-
img
|
|
125
|
-
img.style.width = node.attrs.width;
|
|
143
|
+
applyImageStyles(img, node);
|
|
126
144
|
return img;
|
|
127
145
|
}
|
|
128
146
|
function createImageElement(node) {
|
|
129
147
|
const img = document.createElement('img');
|
|
130
148
|
img.src = node.attrs.src;
|
|
131
149
|
img.alt = node.attrs.alt;
|
|
132
|
-
img
|
|
133
|
-
img.style.width = node.attrs.width;
|
|
150
|
+
applyImageStyles(img, node);
|
|
134
151
|
return img;
|
|
135
152
|
}
|
|
136
153
|
//# sourceMappingURL=node.js.map
|
package/dist/collection/components/text-editor/prosemirror-adapter/plugins/image/node.js.map
CHANGED
|
@@ -1 +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, DOMOutputSpec } from 'prosemirror-model';\nimport { EditorImageState } from '../../../text-editor.types';\nimport { MarkdownSerializerState } from 'prosemirror-markdown';\nimport { Languages } from '../../../../date-picker/date.types';\nimport translate from '../../../../../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 (markdownSerializerState: MarkdownSerializerState, node: Node) => {\n const { state, alt, src, width, maxWidth } = node.attrs;\n\n if (!isEditorImageState(state)) {\n return;\n }\n\n if (state === 'success') {\n const imageHTML = getImageHTML(src, alt, width, maxWidth);\n markdownSerializerState.write(imageHTML);\n\n return;\n }\n\n const statusHTML = getStatusHTML(state, alt, language);\n markdownSerializerState.write(statusHTML);\n };\n}\n\nfunction getStatusHTML(\n state: EditorImageState,\n alt: string,\n language: Languages,\n): string {\n const key = state === 'failed' ? 'failed' : 'loading';\n const text = translate.get(`editor-image-view.${key}`, language, {\n filename: alt || 'file',\n });\n\n return `<span>${text}</span>`;\n}\n\nfunction getImageHTML(\n src: string,\n alt: string,\n width: string,\n maxWidth: string,\n): string {\n const style = [];\n\n if (width) {\n style.push(`width: ${width};`);\n }\n\n if (maxWidth) {\n style.push(`max-width: ${maxWidth};`);\n }\n\n const styleAttribute =\n style.length > 0 ? ` style=\"${style.join(' ')}\"` : '';\n\n return `<img src=\"${src}\" alt=\"${alt}\"${styleAttribute} />`;\n}\n\nexport interface ImageNodeAttrs {\n src: string;\n alt: string;\n state: EditorImageState;\n fileInfoId: string | number;\n width?: string;\n maxWidth?: string;\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: 'success' },\n },\n toDOM: (node): DOMOutputSpec => {\n if (!isEditorImageState(node.attrs.state)) {\n return;\n }\n\n if (node.attrs.state === 'success') {\n return getOrCreateImageElement(node.attrs.fileInfoId, node);\n }\n\n return createStatusSpanForState(node.attrs.state, node, language);\n },\n parseDOM: [\n {\n tag: 'img',\n getAttrs: (dom: HTMLElement): ImageNodeAttrs => {\n return {\n src: dom.getAttribute('src') || '',\n alt: dom.getAttribute('alt') || 'file',\n width: dom.style.width || '',\n maxWidth: '100%',\n state: 'success',\n fileInfoId: crypto.randomUUID(),\n };\n },\n },\n ],\n };\n}\n\nfunction isEditorImageState(state: unknown): state is EditorImageState {\n return state === 'loading' || state === 'failed' || state === 'success';\n}\n\nfunction getOrCreateImageElement(\n fileInfoId: string,\n node: Node,\n): HTMLImageElement {\n let img = imageCache.get(fileInfoId);\n\n if (img) {\n updateImageElement(img, node);\n } else {\n img = createImageElement(node);\n imageCache.set(fileInfoId, img);\n }\n\n return img;\n}\n\nfunction createStatusSpanForState(\n state: EditorImageState,\n node: Node,\n language: Languages,\n): HTMLSpanElement {\n const statusKey = state === 'failed' ? 'failed' : 'loading';\n\n return createStatusSpan(statusKey, node, language);\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"]}
|
|
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, DOMOutputSpec } from 'prosemirror-model';\nimport { EditorImageState } from '../../../text-editor.types';\nimport { MarkdownSerializerState } from 'prosemirror-markdown';\nimport { Languages } from '../../../../date-picker/date.types';\nimport translate from '../../../../../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\nexport function applyImageStyles(img: HTMLImageElement, node: Node) {\n img.style.height = node.attrs.height;\n img.style.width = node.attrs.width;\n img.style.minHeight = node.attrs.minHeight;\n img.style.minWidth = node.attrs.minWidth;\n img.style.maxWidth = node.attrs.maxWidth;\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 (markdownSerializerState: MarkdownSerializerState, node: Node) => {\n const state = node.attrs.state;\n if (!isEditorImageState(state)) {\n return;\n }\n\n if (state === 'success') {\n const imageHTML = getImageHTML(node.attrs as ImageNodeAttrs);\n markdownSerializerState.write(imageHTML);\n\n return;\n }\n\n const statusHTML = getStatusHTML(state, node.attrs.alt, language);\n markdownSerializerState.write(statusHTML);\n };\n}\n\nfunction getStatusHTML(\n state: EditorImageState,\n alt: string,\n language: Languages,\n): string {\n const key = state === 'failed' ? 'failed' : 'loading';\n const text = translate.get(`editor-image-view.${key}`, language, {\n filename: alt || 'file',\n });\n\n return `<span>${text}</span>`;\n}\n\nfunction getImageHTML(attrs: ImageNodeAttrs): string {\n const style = [];\n\n if (attrs.height) {\n style.push(`height: ${attrs.height};`);\n }\n\n if (attrs.width) {\n style.push(`width: ${attrs.width};`);\n }\n\n if (attrs.minHeight) {\n style.push(`min-height: ${attrs.minHeight};`);\n }\n\n if (attrs.minWidth) {\n style.push(`min-width: ${attrs.minWidth};`);\n }\n\n if (attrs.maxWidth) {\n style.push(`max-width: ${attrs.maxWidth};`);\n }\n\n const styleAttribute = style.length > 0 ? ` style=\"${style.join('')}\"` : '';\n\n return `<img src=\"${attrs.src}\" alt=\"${attrs.alt}\"${styleAttribute} />`;\n}\n\nexport interface ImageNodeAttrs {\n src: string;\n alt: string;\n state: EditorImageState;\n fileInfoId: string | number;\n height?: string;\n width?: string;\n minHeight?: string;\n minWidth?: string;\n maxWidth?: string;\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 height: { default: '' },\n width: { default: '' },\n minHeight: { default: '' },\n minWidth: { default: '' },\n maxWidth: { default: '100%' },\n state: { default: 'success' },\n },\n toDOM: (node): DOMOutputSpec => {\n if (!isEditorImageState(node.attrs.state)) {\n return;\n }\n\n if (node.attrs.state === 'success') {\n return getOrCreateImageElement(node.attrs.fileInfoId, node);\n }\n\n return createStatusSpanForState(node.attrs.state, node, language);\n },\n parseDOM: [\n {\n tag: 'img',\n getAttrs: (dom: HTMLElement): ImageNodeAttrs => {\n return {\n src: dom.getAttribute('src') || '',\n alt: dom.getAttribute('alt') || 'file',\n width: dom.style.width || '',\n maxWidth: '100%',\n state: 'success',\n fileInfoId: crypto.randomUUID(),\n };\n },\n },\n ],\n };\n}\n\nfunction isEditorImageState(state: unknown): state is EditorImageState {\n return state === 'loading' || state === 'failed' || state === 'success';\n}\n\nfunction getOrCreateImageElement(\n fileInfoId: string,\n node: Node,\n): HTMLImageElement {\n let img = imageCache.get(fileInfoId);\n\n if (img) {\n updateImageElement(img, node);\n } else {\n img = createImageElement(node);\n imageCache.set(fileInfoId, img);\n }\n\n return img;\n}\n\nfunction createStatusSpanForState(\n state: EditorImageState,\n node: Node,\n language: Languages,\n): HTMLSpanElement {\n const statusKey = state === 'failed' ? 'failed' : 'loading';\n\n return createStatusSpan(statusKey, node, language);\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 applyImageStyles(img, node);\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 applyImageStyles(img, node);\n\n return img;\n}\n"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Plugin } from 'prosemirror-state';
|
|
2
2
|
import translate from '../../../../../global/translations';
|
|
3
|
+
import { applyImageStyles } from './node';
|
|
3
4
|
const MIN_WIDTH = 10;
|
|
4
5
|
export const createImageViewPlugin = (language) => {
|
|
5
6
|
return new Plugin({
|
|
@@ -50,7 +51,7 @@ class ImageView {
|
|
|
50
51
|
window.removeEventListener('pointermove', onPointerMove);
|
|
51
52
|
window.removeEventListener('pointerup', onPointerUp);
|
|
52
53
|
handle.setAttribute('aria-grabbed', 'false');
|
|
53
|
-
this.
|
|
54
|
+
this.persistDimensions();
|
|
54
55
|
};
|
|
55
56
|
window.addEventListener('pointermove', onPointerMove);
|
|
56
57
|
window.addEventListener('pointerup', onPointerUp);
|
|
@@ -115,19 +116,23 @@ class ImageView {
|
|
|
115
116
|
this.img = document.createElement('img');
|
|
116
117
|
this.img.src = node.attrs.src;
|
|
117
118
|
this.img.alt = node.attrs.alt;
|
|
118
|
-
this.img
|
|
119
|
-
this.img.
|
|
119
|
+
applyImageStyles(this.img, node);
|
|
120
|
+
this.img.onload = () => {
|
|
121
|
+
this.persistDimensions();
|
|
122
|
+
};
|
|
120
123
|
this.dom.appendChild(this.img);
|
|
121
124
|
this.transitionBetweenStates();
|
|
122
125
|
}
|
|
126
|
+
persistDimensions() {
|
|
127
|
+
this.view.dispatch(this.view.state.tr.setNodeMarkup(this.getPos(), undefined, Object.assign(Object.assign({}, this.node.attrs), { height: `${this.img.offsetHeight}px`, width: `${this.img.offsetWidth}px`, minHeight: `${this.img.offsetHeight}px`, minWidth: `${this.img.offsetWidth}px` })));
|
|
128
|
+
}
|
|
123
129
|
// Ensure that the existing NodeView is reused rather than recreated.
|
|
124
130
|
// Recreating the NodeView will cause flickering between states.
|
|
125
131
|
update(node) {
|
|
126
132
|
if (!this.transitioningBetweenSuccessStates(node)) {
|
|
127
133
|
this.img.src = node.attrs.src;
|
|
128
134
|
this.img.alt = node.attrs.alt;
|
|
129
|
-
this.img
|
|
130
|
-
this.img.style.width = node.attrs.width;
|
|
135
|
+
applyImageStyles(this.img, node);
|
|
131
136
|
}
|
|
132
137
|
this.node = node;
|
|
133
138
|
this.transitionBetweenStates();
|