abstract-image 13.0.13 → 13.0.14

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.
@@ -4,6 +4,7 @@ import { AbstractImage } from "../model/abstract-image.js";
4
4
  import { createPoint, Point } from "../model/point.js";
5
5
  import { AbstractFontWeight, BinaryFormat, Component, GrowthDirection, ImageData } from "../model/component.js";
6
6
  import { Color } from "../model/color.js";
7
+ import { Optional } from "../model/shared.js";
7
8
 
8
9
  export interface ReactSvgCallbacks {
9
10
  readonly onClick?: MouseCallback;
@@ -14,13 +15,22 @@ export interface ReactSvgCallbacks {
14
15
 
15
16
  export type MouseCallback = (id: string | undefined, point: Point) => void;
16
17
 
18
+ export type ReactSvgOptions = {
19
+ readonly imageDataByUrl: Record<string, `data:image/${string},${string}`>;
20
+ };
21
+
17
22
  export function ReactSvg({
18
23
  image,
19
24
  callbacks,
25
+ options,
20
26
  }: {
21
27
  readonly image: AbstractImage;
22
28
  readonly callbacks?: ReactSvgCallbacks;
29
+ readonly options?: Optional<ReactSvgOptions>;
23
30
  }): React.JSX.Element {
31
+ const opts: ReactSvgOptions = {
32
+ imageDataByUrl: options?.imageDataByUrl ?? {},
33
+ };
24
34
  const cb = callbacks || {};
25
35
  const id = "ai_root";
26
36
  return (
@@ -35,7 +45,7 @@ export function ReactSvg({
35
45
  onContextMenu={_callback(cb.onContextMenu, id)}
36
46
  >
37
47
  {image.components.map((c, i) => (
38
- <JsxComponent key={i} component={c} />
48
+ <JsxComponent key={i} component={c} options={opts} />
39
49
  ))}
40
50
  </svg>
41
51
  );
@@ -74,18 +84,24 @@ function getIdAttr(target: Element | undefined, rootId: string): string | undefi
74
84
  return parts[1];
75
85
  }
76
86
 
77
- function JsxComponent({ component }: { readonly component: Component }): React.JSX.Element {
87
+ function JsxComponent({
88
+ component,
89
+ options,
90
+ }: {
91
+ readonly component: Component;
92
+ readonly options: ReactSvgOptions;
93
+ }): React.JSX.Element {
78
94
  switch (component.type) {
79
95
  case "group":
80
96
  return (
81
97
  <g name={component.name}>
82
98
  {component.children.flatMap((c, i) => (
83
- <JsxComponent key={i} component={c} />
99
+ <JsxComponent key={i} component={c} options={options} />
84
100
  ))}
85
101
  </g>
86
102
  );
87
103
  case "binaryimage":
88
- const url = getImageUrl(component.format, component.data);
104
+ const url = getImageUrl(component.format, component.data, options);
89
105
  return (
90
106
  <image
91
107
  x={component.topLeft.x}
@@ -270,9 +286,9 @@ function getTextFontWeight(fontWeight: AbstractFontWeight): React.CSSProperties[
270
286
  }
271
287
  }
272
288
 
273
- function getImageUrl(format: BinaryFormat, data: ImageData): string {
289
+ function getImageUrl(format: BinaryFormat, data: ImageData, options: ReactSvgOptions): string {
274
290
  if (data.type === "url") {
275
- return data.url;
291
+ return options.imageDataByUrl[data.url] ?? data.url;
276
292
  } else if (format === "png") {
277
293
  const base64 = fromByteArray(data.bytes);
278
294
  return `data:image/png;base64,${base64}`;
@@ -2,23 +2,37 @@ import { fromByteArray } from "base64-js";
2
2
  import { Component, GrowthDirection, BinaryFormat, ImageData } from "../model/component.js";
3
3
  import { AbstractImage } from "../model/abstract-image.js";
4
4
  import { Color } from "../model/color.js";
5
+ import { Optional } from "../model/shared.js";
5
6
 
6
- export function createSVG(image: AbstractImage, pixelWidth?: number, pixelHeight?: number): string {
7
- const imageElements = image.components.map((c: Component) => abstractComponentToSVG(c));
7
+ export const SVG_DATA_URL = "data:image/svg+xml,";
8
+
9
+ export type SvgOptions = {
10
+ readonly pixelWidth: number;
11
+ readonly pixelHeight: number;
12
+ readonly imageDataByUrl: Record<string, `data:image/${string},${string}`>;
13
+ };
14
+
15
+ export function createSVG(image: AbstractImage, options?: Optional<SvgOptions>): string {
16
+ const opts: SvgOptions = {
17
+ imageDataByUrl: options?.imageDataByUrl ?? {},
18
+ pixelWidth: options?.pixelWidth || image.size.width,
19
+ pixelHeight: options?.pixelHeight || image.size.height,
20
+ };
21
+ const imageElements = image.components.map((c: Component) => abstractComponentToSVG(c, opts));
8
22
 
9
23
  return createElement(
10
24
  "svg",
11
25
  {
12
26
  xmlns: "http://www.w3.org/2000/svg",
13
- width: `${pixelWidth || image.size.width}px`,
14
- height: `${pixelHeight || image.size.height}px`,
27
+ width: `${opts.pixelWidth}px`,
28
+ height: `${opts.pixelHeight}px`,
15
29
  viewBox: [0, 0, image.size.width, image.size.height].join(" "),
16
30
  },
17
31
  imageElements
18
32
  );
19
33
  }
20
34
 
21
- function abstractComponentToSVG(component: Component): string {
35
+ function abstractComponentToSVG(component: Component, options: SvgOptions): string {
22
36
  switch (component.type) {
23
37
  case "group":
24
38
  return createElement(
@@ -26,10 +40,10 @@ function abstractComponentToSVG(component: Component): string {
26
40
  {
27
41
  name: component.name,
28
42
  },
29
- component.children.map((c) => abstractComponentToSVG(c))
43
+ component.children.map((c) => abstractComponentToSVG(c, options))
30
44
  );
31
45
  case "binaryimage":
32
- const url = getImageUrl(component.format, component.data);
46
+ const url = getImageUrl(component.format, component.data, options);
33
47
  return url
34
48
  ? createElement(
35
49
  "image",
@@ -339,9 +353,9 @@ function colorToOpacity(color: Color): string {
339
353
  return (color.a / 255).toString();
340
354
  }
341
355
 
342
- function getImageUrl(format: BinaryFormat, data: ImageData): string | undefined {
356
+ function getImageUrl(format: BinaryFormat, data: ImageData, options: SvgOptions): string | undefined {
343
357
  if (data.type === "url") {
344
- return data.url;
358
+ return options.imageDataByUrl[data.url] ?? data.url;
345
359
  } else if (format === "png") {
346
360
  const base64 = fromByteArray(data.bytes);
347
361
  return `data:image/png;base64,${base64}`;