@limetech/lime-elements 38.12.4 → 38.13.0

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 (59) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/lime-elements.cjs.js +1 -1
  3. package/dist/cjs/limel-ai-avatar.cjs.entry.js +1 -1
  4. package/dist/cjs/limel-ai-avatar.cjs.entry.js.map +1 -1
  5. package/dist/cjs/limel-markdown.cjs.entry.js +49 -1
  6. package/dist/cjs/limel-markdown.cjs.entry.js.map +1 -1
  7. package/dist/cjs/limel-prosemirror-adapter.cjs.entry.js +41 -20
  8. package/dist/cjs/limel-prosemirror-adapter.cjs.entry.js.map +1 -1
  9. package/dist/cjs/loader.cjs.js +1 -1
  10. package/dist/cjs/{markdown-parser-5b5ed6c4.js → markdown-parser-564adb69.js} +32 -1
  11. package/dist/cjs/{markdown-parser-5b5ed6c4.js.map → markdown-parser-564adb69.js.map} +1 -1
  12. package/dist/collection/components/ai-avatar/ai-avatar.css +7 -4
  13. package/dist/collection/components/markdown/allowed-css-properties.js +3 -0
  14. package/dist/collection/components/markdown/allowed-css-properties.js.map +1 -1
  15. package/dist/collection/components/markdown/image-intersection-observer.js +29 -0
  16. package/dist/collection/components/markdown/image-intersection-observer.js.map +1 -0
  17. package/dist/collection/components/markdown/image-markdown-plugin.js +28 -0
  18. package/dist/collection/components/markdown/image-markdown-plugin.js.map +1 -0
  19. package/dist/collection/components/markdown/markdown-parser.js +2 -0
  20. package/dist/collection/components/markdown/markdown-parser.js.map +1 -1
  21. package/dist/collection/components/markdown/markdown.js +38 -0
  22. package/dist/collection/components/markdown/markdown.js.map +1 -1
  23. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/image/node.js +31 -14
  24. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/image/node.js.map +1 -1
  25. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/image/view.js +10 -5
  26. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/image/view.js.map +1 -1
  27. package/dist/esm/lime-elements.js +1 -1
  28. package/dist/esm/limel-ai-avatar.entry.js +1 -1
  29. package/dist/esm/limel-ai-avatar.entry.js.map +1 -1
  30. package/dist/esm/limel-markdown.entry.js +49 -1
  31. package/dist/esm/limel-markdown.entry.js.map +1 -1
  32. package/dist/esm/limel-prosemirror-adapter.entry.js +41 -20
  33. package/dist/esm/limel-prosemirror-adapter.entry.js.map +1 -1
  34. package/dist/esm/loader.js +1 -1
  35. package/dist/esm/{markdown-parser-ecdce41c.js → markdown-parser-1c1fdedc.js} +32 -1
  36. package/dist/esm/{markdown-parser-ecdce41c.js.map → markdown-parser-1c1fdedc.js.map} +1 -1
  37. package/dist/lime-elements/lime-elements.esm.js +1 -1
  38. package/dist/lime-elements/lime-elements.esm.js.map +1 -1
  39. package/dist/lime-elements/{p-8f4c55fa.entry.js → p-587f6b6d.entry.js} +2 -2
  40. package/dist/lime-elements/p-587f6b6d.entry.js.map +1 -0
  41. package/dist/lime-elements/p-98478c6d.entry.js +2 -0
  42. package/dist/lime-elements/p-98478c6d.entry.js.map +1 -0
  43. package/dist/lime-elements/p-ce152b39.entry.js +2 -0
  44. package/dist/lime-elements/p-ce152b39.entry.js.map +1 -0
  45. package/dist/lime-elements/{p-e5c8cf08.js → p-cf87519f.js} +3 -3
  46. package/dist/lime-elements/p-cf87519f.js.map +1 -0
  47. package/dist/types/components/markdown/image-intersection-observer.d.ts +10 -0
  48. package/dist/types/components/markdown/image-markdown-plugin.d.ts +9 -0
  49. package/dist/types/components/markdown/markdown-parser.d.ts +1 -0
  50. package/dist/types/components/markdown/markdown.d.ts +9 -1
  51. package/dist/types/components/text-editor/prosemirror-adapter/plugins/image/node.d.ts +4 -0
  52. package/dist/types/components.d.ts +8 -0
  53. package/package.json +1 -1
  54. package/dist/lime-elements/p-8f4c55fa.entry.js.map +0 -1
  55. package/dist/lime-elements/p-e5c8cf08.js.map +0 -1
  56. package/dist/lime-elements/p-eadff599.entry.js +0 -2
  57. package/dist/lime-elements/p-eadff599.entry.js.map +0 -1
  58. package/dist/lime-elements/p-f20b7faa.entry.js +0 -2
  59. package/dist/lime-elements/p-f20b7faa.entry.js.map +0 -1
@@ -26,7 +26,6 @@
26
26
 
27
27
  .core,
28
28
  .orbitals {
29
- mix-blend-mode: overlay;
30
29
  position: absolute;
31
30
  z-index: 10;
32
31
  inset: 0;
@@ -35,18 +34,22 @@
35
34
  align-items: center;
36
35
  justify-content: center;
37
36
  aspect-ratio: 1;
38
- width: clamp(0.375rem, 20%, 3.5rem);
39
37
  border-radius: 50%;
40
38
  }
41
39
 
42
40
  .core {
41
+ opacity: 0.8;
42
+ width: 70%;
43
43
  animation: breathe 3s ease infinite;
44
44
  animation-play-state: var(--ai-avatar-animation-play-state, paused);
45
- background-color: rgb(var(--color-glaucous-lighter));
45
+ background-color: inherit;
46
46
  box-shadow: var(--shadow-depth-8);
47
+ backdrop-filter: blur(1rem);
47
48
  }
48
49
 
49
50
  .orbitals {
51
+ mix-blend-mode: overlay;
52
+ width: clamp(0.375rem, 20%, 3.5rem);
50
53
  animation: rotate 5s linear infinite;
51
54
  animation-play-state: var(--ai-avatar-animation-play-state, paused);
52
55
  transition: opacity 0.2s ease;
@@ -91,7 +94,7 @@
91
94
  transform: scale(1);
92
95
  }
93
96
  50% {
94
- transform: scale(0.7);
97
+ transform: scale(0.9);
95
98
  }
96
99
  }
97
100
  @keyframes rotate {
@@ -10,5 +10,8 @@ export const allowedCssProperties = [
10
10
  'text-decoration-thickness',
11
11
  'text-decoration',
12
12
  'width',
13
+ 'height',
14
+ 'min-width',
15
+ 'min-height',
13
16
  ];
14
17
  //# sourceMappingURL=allowed-css-properties.js.map
@@ -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;CACV,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];\n"]}
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":"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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 { 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(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}\n"]}
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;AAGnD;;;;;;;;;;;;;;;;;;GAkBG;AAMH,MAAM,OAAO,QAAQ;;;qBAmBb,YAAY,CAAC,iBAAiB;;EAG3B,KAAK,CAAC,WAAW;;IACpB,IAAI;MACA,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,KAAK,EAAE;QAC1C,mBAAmB,EAAE,IAAI;QACzB,SAAS,EAAE,MAAA,IAAI,CAAC,SAAS,mCAAI,EAAE;OAClC,CAAC,CAAC;MACH,IAAI,CAAC,WAAW,CAAC,SAAS,GAAG,IAAI,CAAC;KACrC;IAAC,OAAO,KAAK,EAAE;MACZ,sCAAsC;MACtC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;KACxB;EACL,CAAC;EAEM,KAAK,CAAC,gBAAgB;IACzB,IAAI,CAAC,WAAW,EAAE,CAAC;EACvB,CAAC;EAIM,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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';\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 @Watch('value')\n public async textChanged() {\n try {\n const html = await markdownToHTML(this.value, {\n forceHardLineBreaks: true,\n whitelist: this.whitelist ?? [],\n });\n this.rootElement.innerHTML = html;\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(error);\n }\n }\n\n public async componentDidLoad() {\n this.textChanged();\n }\n\n private rootElement: HTMLDivElement;\n\n public render() {\n return [\n <div\n id=\"markdown\"\n ref={(el) => (this.rootElement = el as HTMLDivElement)}\n />,\n ];\n }\n}\n"]}
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"]}
@@ -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 { state, alt, src, width, maxWidth } = node.attrs;
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(src, alt, width, maxWidth);
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(src, alt, width, maxWidth) {
54
+ function getImageHTML(attrs) {
48
55
  const style = [];
49
- if (width) {
50
- style.push(`width: ${width};`);
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 (maxWidth) {
53
- style.push(`max-width: ${maxWidth};`);
65
+ if (attrs.minWidth) {
66
+ style.push(`min-width: ${attrs.minWidth};`);
54
67
  }
55
- const styleAttribute = style.length > 0 ? ` style="${style.join(' ')}"` : '';
56
- return `<img src="${src}" alt="${alt}"${styleAttribute} />`;
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.style.maxWidth = node.attrs.maxWidth;
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.style.maxWidth = node.attrs.maxWidth;
133
- img.style.width = node.attrs.width;
150
+ applyImageStyles(img, node);
134
151
  return img;
135
152
  }
136
153
  //# sourceMappingURL=node.js.map
@@ -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.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 })));
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.style.maxWidth = node.attrs.maxWidth;
119
- this.img.style.width = node.attrs.width;
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.style.maxWidth = node.attrs.maxWidth;
130
- this.img.style.width = node.attrs.width;
135
+ applyImageStyles(this.img, node);
131
136
  }
132
137
  this.node = node;
133
138
  this.transitionBetweenStates();
@@ -1 +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;AAE3C,OAAO,SAAS,MAAM,oCAAoC,CAAC;AAG3D,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,MAAM,aAAa,GAAyC;QACxD,OAAO,EAAE,IAAI,CAAC,kBAAkB;QAChC,OAAO,EAAE,IAAI,CAAC,kBAAkB;QAChC,MAAM,EAAE,IAAI,CAAC,iBAAiB;OACjC,CAAC;MAEF,MAAM,KAAK,GAAqB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;MACtD,MAAA,aAAa,CAAC,KAAK,CAAC,6DAAI,CAAC;IAC7B,CAAC,CAAC;IAEM,sCAAiC,GAAG,CAAC,OAAa,EAAW,EAAE;MACnE,OAAO,CACH,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS;QACnC,OAAO,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,CACpC,CAAC;IACN,CAAC,CAAC;IA5JE,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;EA6ID,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 { EditorImageState } from '../../../text-editor.types';\nimport translate from '../../../../../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 const stateHandlers: Record<EditorImageState, () => void> = {\n loading: this.createLoadingState,\n success: this.createSuccessState,\n failed: this.createFailedState,\n };\n\n const state: EditorImageState = this.node.attrs.state;\n stateHandlers[state]?.();\n };\n\n private transitioningBetweenSuccessStates = (newNode: Node): boolean => {\n return (\n this.node.attrs.state === 'success' &&\n newNode.attrs.state === '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"]}
1
+ {"version":3,"file":"view.js","sourceRoot":"","sources":["../../../../../../src/components/text-editor/prosemirror-adapter/plugins/image/view.ts"],"names":[],"mappings":"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","sourcesContent":["import { EditorView, NodeView } from 'prosemirror-view';\nimport { Node } from 'prosemirror-model';\nimport { Plugin } from 'prosemirror-state';\nimport { EditorImageState } from '../../../text-editor.types';\nimport translate from '../../../../../global/translations';\nimport { Languages } from '../../../../date-picker/date.types';\nimport { applyImageStyles } from './node';\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 applyImageStyles(this.img, node);\n\n this.img.onload = () => {\n this.persistDimensions();\n };\n\n this.dom.appendChild(this.img);\n\n this.transitionBetweenStates();\n }\n\n private persistDimensions() {\n this.view.dispatch(\n this.view.state.tr.setNodeMarkup(this.getPos(), undefined, {\n ...this.node.attrs,\n height: `${this.img.offsetHeight}px`,\n width: `${this.img.offsetWidth}px`,\n minHeight: `${this.img.offsetHeight}px`,\n minWidth: `${this.img.offsetWidth}px`,\n }),\n );\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.persistDimensions();\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 const stateHandlers: Record<EditorImageState, () => void> = {\n loading: this.createLoadingState,\n success: this.createSuccessState,\n failed: this.createFailedState,\n };\n\n const state: EditorImageState = this.node.attrs.state;\n stateHandlers[state]?.();\n };\n\n private transitioningBetweenSuccessStates = (newNode: Node): boolean => {\n return (\n this.node.attrs.state === 'success' &&\n newNode.attrs.state === '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 applyImageStyles(this.img, node);\n }\n\n this.node = node;\n this.transitionBetweenStates();\n\n return true;\n }\n}\n"]}