abstract-image 3.1.3 → 3.2.3

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 (188) hide show
  1. package/CHANGELOG.md +27 -21
  2. package/LICENSE +21 -21
  3. package/README.md +73 -71
  4. package/lib/__stories__/react-svg-export/example-1.stories.d.ts.map +1 -1
  5. package/lib/__stories__/react-svg-export/example-1.stories.js +1 -1
  6. package/lib/__stories__/react-svg-export/example-1.stories.js.map +1 -1
  7. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-ellipse.js +379 -379
  8. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-group.d.ts.map +1 -1
  9. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-group.js +124 -124
  10. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-group.js.map +1 -1
  11. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-line.js +55 -55
  12. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-polygon.js +89 -89
  13. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-polyline.js +79 -79
  14. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-rectangle.js +99 -99
  15. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-text-growth-directions.d.ts.map +1 -1
  16. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-text-growth-directions.js +139 -139
  17. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-text-growth-directions.js.map +1 -1
  18. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-text.d.ts.map +1 -1
  19. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-text.js +64 -64
  20. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-text.js.map +1 -1
  21. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-ellipse.js +24 -24
  22. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-empty-text.d.ts.map +1 -1
  23. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-empty-text.js +27 -27
  24. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-empty-text.js.map +1 -1
  25. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-group.d.ts.map +1 -1
  26. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-group.js +32 -32
  27. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-group.js.map +1 -1
  28. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-line.js +20 -20
  29. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-polygon.js +34 -34
  30. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-polyline.js +26 -26
  31. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-rectangle.js +20 -20
  32. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-text-growth-directions.d.ts.map +1 -1
  33. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-text-growth-directions.js +63 -63
  34. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-text-growth-directions.js.map +1 -1
  35. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-text.d.ts.map +1 -1
  36. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-text.js +27 -27
  37. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-text.js.map +1 -1
  38. package/lib/exporters/__tests__/exception/react-svg-direction-exception.test.js +2 -2
  39. package/lib/exporters/__tests__/exception/react-svg-direction-exception.test.js.map +1 -1
  40. package/lib/exporters/__tests__/exception/svg-direction-exception.test.js +2 -2
  41. package/lib/exporters/__tests__/exception/svg-direction-exception.test.js.map +1 -1
  42. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-binary.js +1 -1
  43. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-binary.js.map +1 -1
  44. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-callback.d.ts.map +1 -1
  45. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-callback.js +1 -1
  46. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-callback.js.map +1 -1
  47. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-empty-text.d.ts.map +1 -1
  48. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-empty-text.js +1 -1
  49. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-empty-text.js.map +1 -1
  50. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-group.d.ts.map +1 -1
  51. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-group.js +1 -1
  52. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-group.js.map +1 -1
  53. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-subimage.d.ts.map +1 -1
  54. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-subimage.js +1 -1
  55. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-subimage.js.map +1 -1
  56. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-bold.d.ts +3 -0
  57. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-bold.d.ts.map +1 -0
  58. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-bold.js +35 -0
  59. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-bold.js.map +1 -0
  60. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-growth-directions.d.ts.map +1 -1
  61. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-growth-directions.js +4 -4
  62. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-growth-directions.js.map +1 -1
  63. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-italic.d.ts +3 -0
  64. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-italic.d.ts.map +1 -0
  65. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-italic.js +36 -0
  66. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-italic.js.map +1 -0
  67. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-sub.d.ts.map +1 -1
  68. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-sub.js +1 -1
  69. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-sub.js.map +1 -1
  70. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text.d.ts.map +1 -1
  71. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text.js +2 -2
  72. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text.js.map +1 -1
  73. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-binary.js +2 -1
  74. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-binary.js.map +1 -1
  75. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-empty-text.d.ts.map +1 -1
  76. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-empty-text.js +1 -1
  77. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-empty-text.js.map +1 -1
  78. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-group.d.ts.map +1 -1
  79. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-group.js +1 -1
  80. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-group.js.map +1 -1
  81. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-text-bold.d.ts +3 -0
  82. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-text-bold.d.ts.map +1 -0
  83. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-text-bold.js +35 -0
  84. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-text-bold.js.map +1 -0
  85. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-text-growth-directions.d.ts.map +1 -1
  86. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-text-growth-directions.js +4 -4
  87. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-text-growth-directions.js.map +1 -1
  88. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-text-italic.d.ts +3 -0
  89. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-text-italic.d.ts.map +1 -0
  90. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-text-italic.js +36 -0
  91. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-text-italic.js.map +1 -0
  92. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-text.d.ts.map +1 -1
  93. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-text.js +2 -2
  94. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-text.js.map +1 -1
  95. package/lib/exporters/react-svg-export-image.js +4 -5
  96. package/lib/exporters/react-svg-export-image.js.map +1 -1
  97. package/lib/exporters/svg-export-image.d.ts.map +1 -1
  98. package/lib/exporters/svg-export-image.js +30 -35
  99. package/lib/exporters/svg-export-image.js.map +1 -1
  100. package/lib/model/component.d.ts +3 -2
  101. package/lib/model/component.d.ts.map +1 -1
  102. package/lib/model/component.js +12 -11
  103. package/lib/model/component.js.map +1 -1
  104. package/package.json +2 -2
  105. package/src/__stories__/react-svg-export/example-1.stories.tsx +54 -53
  106. package/src/__stories__/svg-export/example-1.stories.tsx +42 -42
  107. package/src/exporters/__tests__/dxf2d-export-image/export-test-def.ts +11 -11
  108. package/src/exporters/__tests__/dxf2d-export-image/export.test.tsx +13 -13
  109. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-ellipse.ts +405 -405
  110. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-group.ts +166 -165
  111. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-line.ts +80 -80
  112. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-polygon.ts +114 -114
  113. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-polyline.ts +103 -103
  114. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-rectangle.ts +125 -125
  115. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-text-growth-directions.ts +214 -210
  116. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-text.ts +97 -96
  117. package/src/exporters/__tests__/eps-export-image/export-test-def.ts +11 -11
  118. package/src/exporters/__tests__/eps-export-image/export.test.tsx +13 -13
  119. package/src/exporters/__tests__/eps-export-image/test-defs/eps-ellipse.ts +50 -50
  120. package/src/exporters/__tests__/eps-export-image/test-defs/eps-empty-text.ts +60 -59
  121. package/src/exporters/__tests__/eps-export-image/test-defs/eps-group.ts +74 -73
  122. package/src/exporters/__tests__/eps-export-image/test-defs/eps-line.ts +45 -45
  123. package/src/exporters/__tests__/eps-export-image/test-defs/eps-polygon.ts +65 -65
  124. package/src/exporters/__tests__/eps-export-image/test-defs/eps-polyline.ts +58 -58
  125. package/src/exporters/__tests__/eps-export-image/test-defs/eps-rectangle.ts +46 -46
  126. package/src/exporters/__tests__/eps-export-image/test-defs/eps-text-growth-directions.ts +138 -134
  127. package/src/exporters/__tests__/eps-export-image/test-defs/eps-text.ts +60 -59
  128. package/src/exporters/__tests__/exception/png-unsupported.test.tsx +25 -25
  129. package/src/exporters/__tests__/exception/react-svg-direction-exception.test.tsx +65 -63
  130. package/src/exporters/__tests__/exception/svg-direction-exception.test.tsx +65 -63
  131. package/src/exporters/__tests__/png-export-image/export-test-def.ts +11 -11
  132. package/src/exporters/__tests__/png-export-image/export.test.tsx +13 -13
  133. package/src/exporters/__tests__/png-export-image/test-defs/png-createPNG.tsx +26 -26
  134. package/src/exporters/__tests__/react-svg-export-image/export-test-def.tsx +13 -13
  135. package/src/exporters/__tests__/react-svg-export-image/export.test.tsx +13 -13
  136. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-binary-png.tsx +27 -27
  137. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-binary.tsx +26 -26
  138. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-callback.tsx +60 -59
  139. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-ellipse.tsx +28 -28
  140. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-empty-text.tsx +35 -34
  141. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-group.tsx +44 -43
  142. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-line.tsx +26 -26
  143. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-polygon.tsx +32 -32
  144. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-polyline.tsx +33 -33
  145. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-rectangle.tsx +27 -27
  146. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-subimage.tsx +36 -35
  147. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-bold.tsx +50 -0
  148. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-growth-directions.tsx +80 -76
  149. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-italic.tsx +65 -0
  150. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-sub.tsx +35 -34
  151. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text.tsx +35 -34
  152. package/src/exporters/__tests__/svg-export-image/export-test-def.ts +11 -11
  153. package/src/exporters/__tests__/svg-export-image/export.test.tsx +13 -13
  154. package/src/exporters/__tests__/svg-export-image/test-defs/svg-binary.tsx +26 -26
  155. package/src/exporters/__tests__/svg-export-image/test-defs/svg-ellipse.ts +27 -27
  156. package/src/exporters/__tests__/svg-export-image/test-defs/svg-empty-text.ts +34 -33
  157. package/src/exporters/__tests__/svg-export-image/test-defs/svg-group.ts +44 -43
  158. package/src/exporters/__tests__/svg-export-image/test-defs/svg-line.ts +26 -26
  159. package/src/exporters/__tests__/svg-export-image/test-defs/svg-polygon.ts +32 -32
  160. package/src/exporters/__tests__/svg-export-image/test-defs/svg-polyline.ts +33 -33
  161. package/src/exporters/__tests__/svg-export-image/test-defs/svg-rectangle.ts +27 -27
  162. package/src/exporters/__tests__/svg-export-image/test-defs/svg-text-bold.ts +50 -0
  163. package/src/exporters/__tests__/svg-export-image/test-defs/svg-text-growth-directions.ts +80 -76
  164. package/src/exporters/__tests__/svg-export-image/test-defs/svg-text-italic.ts +65 -0
  165. package/src/exporters/__tests__/svg-export-image/test-defs/svg-text.ts +35 -34
  166. package/src/exporters/dxf2d-export-image.ts +218 -218
  167. package/src/exporters/eps-export-image.ts +154 -154
  168. package/src/exporters/index.ts +3 -3
  169. package/src/exporters/png-export-image.ts +12 -12
  170. package/src/exporters/react-svg-export-image.tsx +301 -298
  171. package/src/exporters/svg-export-image.ts +296 -334
  172. package/src/index.ts +11 -11
  173. package/src/model/__tests__/color/export-test-def.ts +13 -13
  174. package/src/model/__tests__/color/export.test.tsx +14 -14
  175. package/src/model/__tests__/color/test-defs/color-from-string.ts +46 -46
  176. package/src/model/__tests__/color/test-defs/color-to-string.ts +35 -35
  177. package/src/model/__tests__/color/test-defs/color-undefined-2.ts +8 -8
  178. package/src/model/__tests__/color/test-defs/color-undefined.ts +8 -8
  179. package/src/model/abstract-image.ts +25 -25
  180. package/src/model/color.ts +52 -52
  181. package/src/model/component.ts +266 -279
  182. package/src/model/index.ts +5 -5
  183. package/src/model/point.ts +11 -11
  184. package/src/model/size.ts +11 -11
  185. package/lib/exporters/__tests__/svg-export-image.test.d.ts +0 -2
  186. package/lib/exporters/__tests__/svg-export-image.test.d.ts.map +0 -1
  187. package/lib/exporters/__tests__/svg-export-image.test.js +0 -35
  188. package/lib/exporters/__tests__/svg-export-image.test.js.map +0 -1
@@ -1,298 +1,301 @@
1
- import * as R from "ramda";
2
- import * as React from "react";
3
- import * as AbstractImage from "../model/index";
4
-
5
- export interface ReactSvgCallbacks {
6
- readonly onClick?: MouseCallback;
7
- readonly onDoubleClick?: MouseCallback;
8
- readonly onMouseMove?: MouseCallback;
9
- readonly onContextMenu?: MouseCallback;
10
- }
11
-
12
- export type MouseCallback = (id: string | undefined, point: AbstractImage.Point) => void;
13
-
14
- export function createReactSvg(
15
- image: AbstractImage.AbstractImage,
16
- callbacks?: ReactSvgCallbacks
17
- ): React.ReactElement<{}> {
18
- const cb = callbacks || {};
19
- const id = "ai_root";
20
- return (
21
- <svg
22
- id={id}
23
- width={`${image.size.width}px`}
24
- height={`${image.size.height}px`}
25
- viewBox={[0, 0, image.size.width, image.size.height].join(" ")}
26
- onClick={_callback(cb.onClick, id)}
27
- onDoubleClick={_callback(cb.onDoubleClick, id)}
28
- onMouseMove={_callback(cb.onMouseMove, id)}
29
- onContextMenu={_callback(cb.onContextMenu, id)}
30
- >
31
- {R.unnest(
32
- R.addIndex(R.map)(
33
- // tslint:disable-next-line:no-any
34
- (c, i) => _visit(i.toString(), c as any),
35
- image.components
36
- )
37
- )}
38
- </svg>
39
- );
40
- }
41
-
42
- function _callback(callback: MouseCallback | undefined, rootId: string): React.MouseEventHandler<Element> | undefined {
43
- if (!callback) {
44
- return undefined;
45
- }
46
- return (e: React.MouseEvent<Element>) => {
47
- const rect = e.currentTarget.getBoundingClientRect();
48
- const offsetX = e.clientX - rect.left;
49
- const offsetY = e.clientY - rect.top;
50
- const mousePoint = AbstractImage.createPoint(offsetX, offsetY);
51
- const id = getIdAttr(e.target as Element, rootId);
52
- callback(id && id !== "" ? id : undefined, mousePoint);
53
- };
54
- }
55
-
56
- function makeIdAttr(id: string | undefined): string | undefined {
57
- if (!id) {
58
- return undefined;
59
- }
60
- return `ai%${id}`;
61
- }
62
-
63
- function getIdAttr(target: Element | undefined, rootId: string): string | undefined {
64
- if (!target || target.id === rootId) {
65
- return undefined;
66
- }
67
- const id = target.id;
68
- const parts = id.split("%");
69
- if (parts.length !== 2 || parts[0] !== "ai") {
70
- return getIdAttr((target.parentNode as Element) || undefined, rootId);
71
- }
72
- return parts[1];
73
- }
74
-
75
- function _visit(key: string, component: AbstractImage.Component): Array<React.ReactElement<{}>> {
76
- switch (component.type) {
77
- case "group":
78
- return [
79
- <g key={key} name={component.name}>
80
- {R.unnest(
81
- R.addIndex(R.map)(
82
- // tslint:disable-next-line:no-any
83
- (c, i) => _visit(i.toString(), c as any),
84
- component.children
85
- )
86
- )}
87
- </g>,
88
- ];
89
- case "binaryimage":
90
- switch (component.format) {
91
- case "svg":
92
- return [
93
- <g
94
- key={key}
95
- id={makeIdAttr(component.id)}
96
- dangerouslySetInnerHTML={{
97
- __html: component.data.reduce((a, b) => a + String.fromCharCode(b), ""),
98
- }}
99
- />,
100
- ];
101
- default:
102
- return [];
103
- }
104
- case "line":
105
- return [
106
- <line
107
- id={makeIdAttr(component.id)}
108
- key={key}
109
- x1={component.start.x}
110
- y1={component.start.y}
111
- x2={component.end.x}
112
- y2={component.end.y}
113
- stroke={colorToRgb(component.strokeColor)}
114
- strokeWidth={component.strokeThickness}
115
- strokeOpacity={colorToOpacity(component.strokeColor)}
116
- />,
117
- ];
118
- case "text":
119
- if (!component.text) {
120
- return [];
121
- }
122
- const lineHeight = component.fontSize;
123
-
124
- const shadowStyle = {
125
- textAnchor: getTextAnchor(component.horizontalGrowthDirection),
126
- fontSize: component.fontSize.toString() + "px",
127
- fontWeight: component.fontWeight,
128
- fontFamily: component.fontFamily,
129
- stroke: colorToRgb(component.strokeColor),
130
- strokeWidth: component.strokeThickness,
131
- };
132
- const style = {
133
- textAnchor: getTextAnchor(component.horizontalGrowthDirection),
134
- fontSize: component.fontSize.toString() + "px",
135
- fontWeight: component.fontWeight,
136
- fontFamily: component.fontFamily,
137
- fill: colorToRgb(component.textColor),
138
- };
139
- const dy = getBaselineAdjustment(component.verticalGrowthDirection);
140
-
141
- const transform =
142
- "rotate(" +
143
- component.clockwiseRotationDegrees.toString() +
144
- " " +
145
- component.position.x.toString() +
146
- " " +
147
- component.position.y.toString() +
148
- ")";
149
-
150
- const lines: Array<string> = component.text !== null ? component.text.split("\n") : [];
151
- const tSpans = lines.map((t) =>
152
- renderLine(
153
- t,
154
- component.position.x,
155
- component.position.y + (lines.indexOf(t) + dy) * lineHeight,
156
- component.fontSize,
157
- lineHeight
158
- )
159
- );
160
- let cs: Array<React.ReactElement<{}>> = [];
161
- if (component.strokeThickness > 0 && component.strokeColor) {
162
- cs.push(
163
- <text key={key + "shadow"} style={shadowStyle} transform={transform}>
164
- {tSpans}
165
- </text>
166
- );
167
- }
168
- cs.push(
169
- <text key={key} style={style} transform={transform}>
170
- {tSpans}
171
- </text>
172
- );
173
- return cs;
174
- case "ellipse":
175
- const rx = Math.abs(component.bottomRight.x - component.topLeft.x) * 0.5;
176
- const ry = Math.abs(component.bottomRight.y - component.topLeft.y) * 0.5;
177
- const cx = (component.bottomRight.x + component.topLeft.x) * 0.5;
178
- const cy = (component.bottomRight.y + component.topLeft.y) * 0.5;
179
- return [
180
- <ellipse
181
- id={makeIdAttr(component.id)}
182
- key={key}
183
- cx={cx}
184
- cy={cy}
185
- rx={rx}
186
- ry={ry}
187
- stroke={colorToRgb(component.strokeColor)}
188
- strokeWidth={component.strokeThickness}
189
- strokeOpacity={colorToOpacity(component.strokeColor)}
190
- fillOpacity={colorToOpacity(component.fillColor)}
191
- fill={colorToRgb(component.fillColor)}
192
- />,
193
- ];
194
- case "polyline":
195
- let linePoints = component.points.map((p) => p.x.toString() + "," + p.y.toString()).join(" ");
196
- return [
197
- <polyline
198
- id={makeIdAttr(component.id)}
199
- key={key}
200
- points={linePoints}
201
- stroke={colorToRgb(component.strokeColor)}
202
- strokeWidth={component.strokeThickness}
203
- strokeOpacity={colorToOpacity(component.strokeColor)}
204
- fill="none"
205
- />,
206
- ];
207
- case "polygon":
208
- let points = component.points.map((p) => p.x.toString() + "," + p.y.toString()).join(" ");
209
- return [
210
- <polygon
211
- id={makeIdAttr(component.id)}
212
- key={key}
213
- points={points}
214
- stroke={colorToRgb(component.strokeColor)}
215
- strokeWidth={component.strokeThickness}
216
- strokeOpacity={colorToOpacity(component.strokeColor)}
217
- fillOpacity={colorToOpacity(component.fillColor)}
218
- fill={colorToRgb(component.fillColor)}
219
- />,
220
- ];
221
- case "rectangle":
222
- return [
223
- <rect
224
- id={makeIdAttr(component.id)}
225
- key={key}
226
- x={component.topLeft.x}
227
- y={component.topLeft.y}
228
- width={Math.abs(component.bottomRight.x - component.topLeft.x)}
229
- height={Math.abs(component.bottomRight.y - component.topLeft.y)}
230
- stroke={colorToRgb(component.strokeColor)}
231
- strokeWidth={component.strokeThickness}
232
- strokeOpacity={colorToOpacity(component.strokeColor)}
233
- fillOpacity={colorToOpacity(component.fillColor)}
234
- fill={colorToRgb(component.fillColor)}
235
- />,
236
- ];
237
- default:
238
- return [];
239
- }
240
- }
241
-
242
- function renderLine(text: string, x: number, y: number, fontSize: number, lineHeight: number): JSX.Element {
243
- const split = R.unnest<string>(text.split("<sub>").map((t) => t.split("</sub>")));
244
- let inside = false;
245
- const tags: Array<JSX.Element> = [];
246
- for (let i = 0; i < split.length; ++i) {
247
- const splitText = split[i];
248
- if (inside) {
249
- tags.push(
250
- <tspan key={i} baselineShift="sub" style={{ fontSize: (fontSize * 0.8).toString() + "px" }}>
251
- {splitText}
252
- </tspan>
253
- );
254
- } else {
255
- tags.push(<tspan key={i}>{splitText}</tspan>);
256
- }
257
- inside = !inside;
258
- }
259
- return (
260
- <tspan key={text} x={x} y={y} height={lineHeight.toString() + "px"}>
261
- {tags}
262
- </tspan>
263
- );
264
- }
265
-
266
- function getBaselineAdjustment(d: AbstractImage.GrowthDirection): number {
267
- if (d === "up") {
268
- return 0.0;
269
- }
270
- if (d === "uniform") {
271
- return 0.5;
272
- }
273
- if (d === "down") {
274
- return 1.0;
275
- }
276
- throw "Unknown text alignment " + d;
277
- }
278
-
279
- function getTextAnchor(d: AbstractImage.GrowthDirection): "end" | "middle" | "start" {
280
- if (d === "left") {
281
- return "end";
282
- }
283
- if (d === "uniform") {
284
- return "middle";
285
- }
286
- if (d === "right") {
287
- return "start";
288
- }
289
- throw "Unknown text alignment " + d;
290
- }
291
-
292
- function colorToRgb(color: AbstractImage.Color): string {
293
- return "rgb(" + color.r.toString() + "," + color.g.toString() + "," + color.b.toString() + ")";
294
- }
295
-
296
- function colorToOpacity(color: AbstractImage.Color): string {
297
- return (color.a / 255).toString();
298
- }
1
+ import * as R from "ramda";
2
+ import * as React from "react";
3
+ import * as AbstractImage from "../model/index";
4
+
5
+ export interface ReactSvgCallbacks {
6
+ readonly onClick?: MouseCallback;
7
+ readonly onDoubleClick?: MouseCallback;
8
+ readonly onMouseMove?: MouseCallback;
9
+ readonly onContextMenu?: MouseCallback;
10
+ }
11
+
12
+ export type MouseCallback = (id: string | undefined, point: AbstractImage.Point) => void;
13
+
14
+ export function createReactSvg(
15
+ image: AbstractImage.AbstractImage,
16
+ callbacks?: ReactSvgCallbacks
17
+ ): React.ReactElement<{}> {
18
+ const cb = callbacks || {};
19
+ const id = "ai_root";
20
+ return (
21
+ <svg
22
+ id={id}
23
+ width={`${image.size.width}px`}
24
+ height={`${image.size.height}px`}
25
+ viewBox={[0, 0, image.size.width, image.size.height].join(" ")}
26
+ onClick={_callback(cb.onClick, id)}
27
+ onDoubleClick={_callback(cb.onDoubleClick, id)}
28
+ onMouseMove={_callback(cb.onMouseMove, id)}
29
+ onContextMenu={_callback(cb.onContextMenu, id)}
30
+ >
31
+ {R.unnest(
32
+ R.addIndex(R.map)(
33
+ // tslint:disable-next-line:no-any
34
+ (c, i) => _visit(i.toString(), c as any),
35
+ image.components
36
+ )
37
+ )}
38
+ </svg>
39
+ );
40
+ }
41
+
42
+ function _callback(callback: MouseCallback | undefined, rootId: string): React.MouseEventHandler<Element> | undefined {
43
+ if (!callback) {
44
+ return undefined;
45
+ }
46
+ return (e: React.MouseEvent<Element>) => {
47
+ const rect = e.currentTarget.getBoundingClientRect();
48
+ const offsetX = e.clientX - rect.left;
49
+ const offsetY = e.clientY - rect.top;
50
+ const mousePoint = AbstractImage.createPoint(offsetX, offsetY);
51
+ const id = getIdAttr(e.target as Element, rootId);
52
+ callback(id && id !== "" ? id : undefined, mousePoint);
53
+ };
54
+ }
55
+
56
+ function makeIdAttr(id: string | undefined): string | undefined {
57
+ if (!id) {
58
+ return undefined;
59
+ }
60
+ return `ai%${id}`;
61
+ }
62
+
63
+ function getIdAttr(target: Element | undefined, rootId: string): string | undefined {
64
+ if (!target || target.id === rootId) {
65
+ return undefined;
66
+ }
67
+ const id = target.id;
68
+ const parts = id.split("%");
69
+ if (parts.length !== 2 || parts[0] !== "ai") {
70
+ return getIdAttr((target.parentNode as Element) || undefined, rootId);
71
+ }
72
+ return parts[1];
73
+ }
74
+
75
+ function _visit(key: string, component: AbstractImage.Component): Array<React.ReactElement<{}>> {
76
+ switch (component.type) {
77
+ case "group":
78
+ return [
79
+ <g key={key} name={component.name}>
80
+ {R.unnest(
81
+ R.addIndex(R.map)(
82
+ // tslint:disable-next-line:no-any
83
+ (c, i) => _visit(i.toString(), c as any),
84
+ component.children
85
+ )
86
+ )}
87
+ </g>,
88
+ ];
89
+ case "binaryimage":
90
+ switch (component.format) {
91
+ case "svg":
92
+ const svg = component.data.reduce((a, b) => a + String.fromCharCode(b), "");
93
+ return [
94
+ <image
95
+ key={key}
96
+ x={component.topLeft.x}
97
+ y={component.topLeft.y}
98
+ width={component.bottomRight.x - component.topLeft.x}
99
+ height={component.bottomRight.y - component.topLeft.y}
100
+ id={makeIdAttr(component.id)}
101
+ href={`data:image/svg+xml;utf8,${svg}`}
102
+ />,
103
+ ];
104
+ default:
105
+ return [];
106
+ }
107
+ case "line":
108
+ return [
109
+ <line
110
+ id={makeIdAttr(component.id)}
111
+ key={key}
112
+ x1={component.start.x}
113
+ y1={component.start.y}
114
+ x2={component.end.x}
115
+ y2={component.end.y}
116
+ stroke={colorToRgb(component.strokeColor)}
117
+ strokeWidth={component.strokeThickness}
118
+ strokeOpacity={colorToOpacity(component.strokeColor)}
119
+ />,
120
+ ];
121
+ case "text":
122
+ if (!component.text) {
123
+ return [];
124
+ }
125
+ const lineHeight = component.fontSize;
126
+
127
+ const shadowStyle = {
128
+ textAnchor: getTextAnchor(component.horizontalGrowthDirection),
129
+ fontSize: component.fontSize.toString() + "px",
130
+ fontWeight: component.fontWeight === "mediumBold" ? "bold" : component.fontWeight,
131
+ fontFamily: component.fontFamily,
132
+ stroke: colorToRgb(component.strokeColor),
133
+ strokeWidth: component.strokeThickness,
134
+ };
135
+ const style = {
136
+ textAnchor: getTextAnchor(component.horizontalGrowthDirection),
137
+ fontSize: component.fontSize.toString() + "px",
138
+ fontWeight: component.fontWeight === "mediumBold" ? "bold" : component.fontWeight,
139
+ fontFamily: component.fontFamily,
140
+ fill: colorToRgb(component.textColor),
141
+ };
142
+ const dy = getBaselineAdjustment(component.verticalGrowthDirection);
143
+
144
+ const transform =
145
+ "rotate(" +
146
+ component.clockwiseRotationDegrees.toString() +
147
+ " " +
148
+ component.position.x.toString() +
149
+ " " +
150
+ component.position.y.toString() +
151
+ ")";
152
+
153
+ const lines: Array<string> = component.text !== null ? component.text.split("\n") : [];
154
+ const tSpans = lines.map((t) =>
155
+ renderLine(
156
+ t,
157
+ component.position.x,
158
+ component.position.y + (lines.indexOf(t) + dy) * lineHeight,
159
+ component.fontSize,
160
+ lineHeight
161
+ )
162
+ );
163
+ let cs: Array<React.ReactElement<{}>> = [];
164
+ if (component.strokeThickness > 0 && component.strokeColor) {
165
+ cs.push(
166
+ <text key={key + "shadow"} style={shadowStyle} transform={transform}>
167
+ {tSpans}
168
+ </text>
169
+ );
170
+ }
171
+ cs.push(
172
+ <text key={key} style={style} transform={transform}>
173
+ {tSpans}
174
+ </text>
175
+ );
176
+ return cs;
177
+ case "ellipse":
178
+ const rx = Math.abs(component.bottomRight.x - component.topLeft.x) * 0.5;
179
+ const ry = Math.abs(component.bottomRight.y - component.topLeft.y) * 0.5;
180
+ const cx = (component.bottomRight.x + component.topLeft.x) * 0.5;
181
+ const cy = (component.bottomRight.y + component.topLeft.y) * 0.5;
182
+ return [
183
+ <ellipse
184
+ id={makeIdAttr(component.id)}
185
+ key={key}
186
+ cx={cx}
187
+ cy={cy}
188
+ rx={rx}
189
+ ry={ry}
190
+ stroke={colorToRgb(component.strokeColor)}
191
+ strokeWidth={component.strokeThickness}
192
+ strokeOpacity={colorToOpacity(component.strokeColor)}
193
+ fillOpacity={colorToOpacity(component.fillColor)}
194
+ fill={colorToRgb(component.fillColor)}
195
+ />,
196
+ ];
197
+ case "polyline":
198
+ let linePoints = component.points.map((p) => p.x.toString() + "," + p.y.toString()).join(" ");
199
+ return [
200
+ <polyline
201
+ id={makeIdAttr(component.id)}
202
+ key={key}
203
+ points={linePoints}
204
+ stroke={colorToRgb(component.strokeColor)}
205
+ strokeWidth={component.strokeThickness}
206
+ strokeOpacity={colorToOpacity(component.strokeColor)}
207
+ fill="none"
208
+ />,
209
+ ];
210
+ case "polygon":
211
+ let points = component.points.map((p) => p.x.toString() + "," + p.y.toString()).join(" ");
212
+ return [
213
+ <polygon
214
+ id={makeIdAttr(component.id)}
215
+ key={key}
216
+ points={points}
217
+ stroke={colorToRgb(component.strokeColor)}
218
+ strokeWidth={component.strokeThickness}
219
+ strokeOpacity={colorToOpacity(component.strokeColor)}
220
+ fillOpacity={colorToOpacity(component.fillColor)}
221
+ fill={colorToRgb(component.fillColor)}
222
+ />,
223
+ ];
224
+ case "rectangle":
225
+ return [
226
+ <rect
227
+ id={makeIdAttr(component.id)}
228
+ key={key}
229
+ x={component.topLeft.x}
230
+ y={component.topLeft.y}
231
+ width={Math.abs(component.bottomRight.x - component.topLeft.x)}
232
+ height={Math.abs(component.bottomRight.y - component.topLeft.y)}
233
+ stroke={colorToRgb(component.strokeColor)}
234
+ strokeWidth={component.strokeThickness}
235
+ strokeOpacity={colorToOpacity(component.strokeColor)}
236
+ fillOpacity={colorToOpacity(component.fillColor)}
237
+ fill={colorToRgb(component.fillColor)}
238
+ />,
239
+ ];
240
+ default:
241
+ return [];
242
+ }
243
+ }
244
+
245
+ function renderLine(text: string, x: number, y: number, fontSize: number, lineHeight: number): JSX.Element {
246
+ const split = R.unnest<string>(text.split("<sub>").map((t) => t.split("</sub>")));
247
+ let inside = false;
248
+ const tags: Array<JSX.Element> = [];
249
+ for (let i = 0; i < split.length; ++i) {
250
+ const splitText = split[i];
251
+ if (inside) {
252
+ tags.push(
253
+ <tspan key={i} baselineShift="sub" style={{ fontSize: (fontSize * 0.8).toString() + "px" }}>
254
+ {splitText}
255
+ </tspan>
256
+ );
257
+ } else {
258
+ tags.push(<tspan key={i}>{splitText}</tspan>);
259
+ }
260
+ inside = !inside;
261
+ }
262
+ return (
263
+ <tspan key={text} x={x} y={y} height={lineHeight.toString() + "px"}>
264
+ {tags}
265
+ </tspan>
266
+ );
267
+ }
268
+
269
+ function getBaselineAdjustment(d: AbstractImage.GrowthDirection): number {
270
+ if (d === "up") {
271
+ return 0.0;
272
+ }
273
+ if (d === "uniform") {
274
+ return 0.5;
275
+ }
276
+ if (d === "down") {
277
+ return 1.0;
278
+ }
279
+ throw "Unknown text alignment " + d;
280
+ }
281
+
282
+ function getTextAnchor(d: AbstractImage.GrowthDirection): "end" | "middle" | "start" {
283
+ if (d === "left") {
284
+ return "end";
285
+ }
286
+ if (d === "uniform") {
287
+ return "middle";
288
+ }
289
+ if (d === "right") {
290
+ return "start";
291
+ }
292
+ throw "Unknown text alignment " + d;
293
+ }
294
+
295
+ function colorToRgb(color: AbstractImage.Color): string {
296
+ return "rgb(" + color.r.toString() + "," + color.g.toString() + "," + color.b.toString() + ")";
297
+ }
298
+
299
+ function colorToOpacity(color: AbstractImage.Color): string {
300
+ return (color.a / 255).toString();
301
+ }