@xplor-education/core-stencil-components 3.1.2 → 3.1.4

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 (261) hide show
  1. package/components/index.js +28 -3
  2. package/components/index.js.map +1 -1
  3. package/components/{p-B49oUO_9.js → p-AzuDJugb.js} +3 -3
  4. package/components/{p-B49oUO_9.js.map → p-AzuDJugb.js.map} +1 -1
  5. package/components/{p-D4jVa8dE.js → p-BQ6aLuGP.js} +4 -4
  6. package/components/{p-D4jVa8dE.js.map → p-BQ6aLuGP.js.map} +1 -1
  7. package/components/{p-VIJbFoA4.js → p-BZQ63ULK.js} +3 -3
  8. package/components/{p-VIJbFoA4.js.map → p-BZQ63ULK.js.map} +1 -1
  9. package/components/{p-BRWe4TXp.js → p-C-gHaxf1.js} +3 -3
  10. package/components/p-C-gHaxf1.js.map +1 -0
  11. package/components/{p-Bs_ocvfe.js → p-CyGsSa4H.js} +7 -4
  12. package/components/p-CyGsSa4H.js.map +1 -0
  13. package/components/{p-B3zR7peH.js → p-oanCZPh6.js} +4 -4
  14. package/components/{p-B3zR7peH.js.map → p-oanCZPh6.js.map} +1 -1
  15. package/components/xplor-alert-message.js +2 -2
  16. package/components/xplor-avatar-and-name.js +2 -2
  17. package/components/xplor-avatar.js +1 -1
  18. package/components/xplor-badge-active.js +1 -1
  19. package/components/xplor-badge-archived.js +1 -1
  20. package/components/xplor-badge-deleted.js +1 -1
  21. package/components/xplor-badge-expired.js +1 -1
  22. package/components/xplor-badge-inactive.js +1 -1
  23. package/components/xplor-badge-recalled.js +1 -1
  24. package/components/xplor-badge-upcoming.js +1 -1
  25. package/components/xplor-badge-waitlist.js +1 -1
  26. package/components/xplor-badge.js +1 -1
  27. package/components/xplor-btn-back-to-parent.js +1 -1
  28. package/components/xplor-btn-back.js +1 -1
  29. package/components/xplor-btn-icon.js +1 -1
  30. package/components/xplor-btn-toggle-group.js +1 -1
  31. package/components/xplor-btn-toggle-secondary.js +1 -1
  32. package/components/xplor-btn-toggle.js +1 -1
  33. package/components/xplor-btn-tooltip.js +3 -3
  34. package/components/xplor-button.js +1 -1
  35. package/components/xplor-canvas-editor.d.ts +11 -0
  36. package/components/xplor-canvas-editor.js +369 -0
  37. package/components/xplor-canvas-editor.js.map +1 -0
  38. package/components/xplor-chat-widget.js +3 -3
  39. package/components/xplor-checkbox.js +1 -1
  40. package/components/xplor-combobox.js +5 -5
  41. package/components/xplor-datatable.js +2 -2
  42. package/components/xplor-date-picker.js +5 -5
  43. package/components/xplor-drag-and-drop-input.js +26 -5
  44. package/components/xplor-drag-and-drop-input.js.map +1 -1
  45. package/components/xplor-dropdown.js +1 -1
  46. package/components/xplor-expansion-panel.js +4 -4
  47. package/components/xplor-expansion-panels.js +1 -1
  48. package/components/xplor-file-upload.js +2 -2
  49. package/components/xplor-inline-checkbox.js +2 -2
  50. package/components/xplor-inline-date-picker.js +1 -1
  51. package/components/xplor-inline-switch.js +1 -1
  52. package/components/xplor-input-file.js +1 -1
  53. package/components/xplor-input-search.js +2 -2
  54. package/components/xplor-input-select.js +2 -2
  55. package/components/xplor-input-send.js +3 -3
  56. package/components/xplor-input-text-area.js +2 -2
  57. package/components/xplor-input-text-secondary.js +2 -2
  58. package/components/xplor-input-text.js +2 -2
  59. package/components/xplor-input-title.js +1 -1
  60. package/components/xplor-links.js +1 -1
  61. package/components/xplor-modal-persistent.js +2 -2
  62. package/components/xplor-modal.js +1 -1
  63. package/components/xplor-nav-tabs.js +2 -2
  64. package/components/xplor-radio-btn.js +2 -2
  65. package/components/xplor-rich-text-editor.d.ts +11 -0
  66. package/components/xplor-rich-text-editor.js +23754 -0
  67. package/components/xplor-rich-text-editor.js.map +1 -0
  68. package/components/xplor-section-card.js +2 -2
  69. package/components/xplor-section-heading.js +2 -2
  70. package/components/xplor-table.js +5 -5
  71. package/components/xplor-text-bubble.js +2 -2
  72. package/components/xplor-text-field.js +2 -2
  73. package/components/xplor-time-picker.js +6 -6
  74. package/components/xplor-tooltip.js +1 -1
  75. package/dist/cjs/{index-BjAapk2n.js → index--_m3aUsf.js} +32 -7
  76. package/dist/cjs/index--_m3aUsf.js.map +1 -0
  77. package/dist/cjs/loader.cjs.js +2 -2
  78. package/dist/cjs/xplor-alert-dialog_60.cjs.entry.js +29457 -0
  79. package/dist/cjs/xplor-alert-dialog_60.cjs.entry.js.map +1 -0
  80. package/dist/cjs/xplor-component-library.cjs.js +2 -2
  81. package/dist/collection/collection-manifest.json +2 -0
  82. package/dist/collection/components/xplor-alert-message/xplor-alert-message.js +2 -2
  83. package/dist/collection/components/xplor-avatar/xplor-avatar.js +2 -2
  84. package/dist/collection/components/xplor-avatar-and-name/xplor-avatar-and-name.js +1 -1
  85. package/dist/collection/components/xplor-badge/xplor-badge.js +1 -1
  86. package/dist/collection/components/xplor-badge-active/xplor-badge-active.js +1 -1
  87. package/dist/collection/components/xplor-badge-archived/xplor-badge-archived.js +1 -1
  88. package/dist/collection/components/xplor-badge-deleted/xplor-badge-deleted.js +1 -1
  89. package/dist/collection/components/xplor-badge-expired/xplor-badge-expired.js +1 -1
  90. package/dist/collection/components/xplor-badge-inactive/xplor-badge-inactive.js +1 -1
  91. package/dist/collection/components/xplor-badge-recalled/xplor-badge-recalled.js +1 -1
  92. package/dist/collection/components/xplor-badge-upcoming/xplor-badge-upcoming.js +1 -1
  93. package/dist/collection/components/xplor-badge-waitlist/xplor-badge-waitlist.js +1 -1
  94. package/dist/collection/components/xplor-btn-back/xplor-btn-back.js +1 -1
  95. package/dist/collection/components/xplor-btn-back-to-parent/xplor-btn-back-to-parent.js +1 -1
  96. package/dist/collection/components/xplor-btn-icon/xplor-btn-icon.js +1 -1
  97. package/dist/collection/components/xplor-btn-toggle/xplor-btn-toggle.js +1 -1
  98. package/dist/collection/components/xplor-btn-toggle-group/xplor-btn-toggle-group.js +1 -1
  99. package/dist/collection/components/xplor-btn-toggle-secondary/xplor-btn-toggle-secondary.js +1 -1
  100. package/dist/collection/components/xplor-btn-tooltip/xplor-btn-tooltip.js +1 -1
  101. package/dist/collection/components/xplor-button/xplor-button.css +8 -3
  102. package/dist/collection/components/xplor-button/xplor-button.js +23 -1
  103. package/dist/collection/components/xplor-button/xplor-button.js.map +1 -1
  104. package/dist/collection/components/xplor-canvas-editor/xplor-canvas-editor.css +1224 -0
  105. package/dist/collection/components/xplor-canvas-editor/xplor-canvas-editor.js +520 -0
  106. package/dist/collection/components/xplor-canvas-editor/xplor-canvas-editor.js.map +1 -0
  107. package/dist/collection/components/xplor-canvas-editor/xplor-canvas-editor.stories.js +280 -0
  108. package/dist/collection/components/xplor-canvas-editor/xplor-canvas-editor.stories.js.map +1 -0
  109. package/dist/collection/components/xplor-chat-widget/xplor-chat-widget.js +1 -1
  110. package/dist/collection/components/xplor-checkbox/xplor-checkbox.js +1 -1
  111. package/dist/collection/components/xplor-combobox/xplor-combobox.js +5 -5
  112. package/dist/collection/components/xplor-datatable/xplor-datatable.js +2 -2
  113. package/dist/collection/components/xplor-date-picker/xplor-date-picker.js +4 -4
  114. package/dist/collection/components/xplor-drag-and-drop-input/xplor-drag-and-drop-input.css +62 -29
  115. package/dist/collection/components/xplor-drag-and-drop-input/xplor-drag-and-drop-input.js +25 -4
  116. package/dist/collection/components/xplor-drag-and-drop-input/xplor-drag-and-drop-input.js.map +1 -1
  117. package/dist/collection/components/xplor-dropdown/xplor-dropdown.js +1 -1
  118. package/dist/collection/components/xplor-expansion-panel/xplor-expansion-panel.js +4 -4
  119. package/dist/collection/components/xplor-expansion-panels/xplor-expansion-panels.js +1 -1
  120. package/dist/collection/components/xplor-file-upload/xplor-file-upload.js +2 -2
  121. package/dist/collection/components/xplor-inline-checkbox/xplor-inline-checkbox.js +2 -2
  122. package/dist/collection/components/xplor-inline-date-picker/xplor-inline-date-picker.js +1 -1
  123. package/dist/collection/components/xplor-inline-switch/xplor-inline-switch.js +1 -1
  124. package/dist/collection/components/xplor-input-file/xplor-input-file.js +1 -1
  125. package/dist/collection/components/xplor-input-search/xplor-input-search.js +2 -2
  126. package/dist/collection/components/xplor-input-select/xplor-input-select.js +2 -2
  127. package/dist/collection/components/xplor-input-send/xplor-input-send.js +2 -2
  128. package/dist/collection/components/xplor-input-text/xplor-input-text.js +2 -2
  129. package/dist/collection/components/xplor-input-text-area/xplor-input-text-area.js +2 -2
  130. package/dist/collection/components/xplor-input-text-secondary/xplor-input-text-secondary.js +2 -2
  131. package/dist/collection/components/xplor-input-title/xplor-input-title.js +1 -1
  132. package/dist/collection/components/xplor-links/xplor-links.js +1 -1
  133. package/dist/collection/components/xplor-modal/xplor-modal.css +10 -10
  134. package/dist/collection/components/xplor-modal-persistent/xplor-modal-persistent.js +1 -1
  135. package/dist/collection/components/xplor-nav-tabs/xplor-nav-tabs.js +2 -2
  136. package/dist/collection/components/xplor-radio-btn/xplor-radio-btn.js +2 -2
  137. package/dist/collection/components/xplor-rich-text-editor/xplor-rich-text-editor.css +1445 -0
  138. package/dist/collection/components/xplor-rich-text-editor/xplor-rich-text-editor.js +800 -0
  139. package/dist/collection/components/xplor-rich-text-editor/xplor-rich-text-editor.js.map +1 -0
  140. package/dist/collection/components/xplor-rich-text-editor/xplor-rich-text-editor.stories.js +260 -0
  141. package/dist/collection/components/xplor-rich-text-editor/xplor-rich-text-editor.stories.js.map +1 -0
  142. package/dist/collection/components/xplor-section-card/xplor-section-card.js +2 -2
  143. package/dist/collection/components/xplor-section-heading/xplor-section-heading.js +2 -2
  144. package/dist/collection/components/xplor-table/xplor-table.js +5 -5
  145. package/dist/collection/components/xplor-text-bubble/xplor-text-bubble.js +2 -2
  146. package/dist/collection/components/xplor-text-field/xplor-text-field.js +2 -2
  147. package/dist/collection/components/xplor-time-picker/xplor-time-picker.js +6 -6
  148. package/dist/collection/components/xplor-tooltip/xplor-tooltip.js +2 -2
  149. package/dist/collection/utils/slate-legacy-converter.js +223 -0
  150. package/dist/collection/utils/slate-legacy-converter.js.map +1 -0
  151. package/dist/components/index.js +28 -3
  152. package/dist/components/index.js.map +1 -1
  153. package/dist/components/{p-04oMLTZR.js → p-B7FEf2_x.js} +3 -3
  154. package/dist/components/p-B7FEf2_x.js.map +1 -0
  155. package/dist/components/{p--zhT6rvJ.js → p-DGaHlr-k.js} +4 -4
  156. package/dist/components/{p--zhT6rvJ.js.map → p-DGaHlr-k.js.map} +1 -1
  157. package/dist/components/{p-B5rS_jjI.js → p-DWaTb2EJ.js} +7 -4
  158. package/dist/components/p-DWaTb2EJ.js.map +1 -0
  159. package/dist/components/{p-J41B2nOt.js → p-DroQBoSo.js} +3 -3
  160. package/dist/components/{p-J41B2nOt.js.map → p-DroQBoSo.js.map} +1 -1
  161. package/dist/components/{p-DQ8MCJFX.js → p-TfYzqGwr.js} +3 -3
  162. package/dist/components/{p-DQ8MCJFX.js.map → p-TfYzqGwr.js.map} +1 -1
  163. package/dist/components/{p-DbQ6ZNvh.js → p-vl-J3tKJ.js} +4 -4
  164. package/dist/components/{p-DbQ6ZNvh.js.map → p-vl-J3tKJ.js.map} +1 -1
  165. package/dist/components/xplor-alert-message.js +2 -2
  166. package/dist/components/xplor-avatar-and-name.js +2 -2
  167. package/dist/components/xplor-avatar.js +1 -1
  168. package/dist/components/xplor-badge-active.js +1 -1
  169. package/dist/components/xplor-badge-archived.js +1 -1
  170. package/dist/components/xplor-badge-deleted.js +1 -1
  171. package/dist/components/xplor-badge-expired.js +1 -1
  172. package/dist/components/xplor-badge-inactive.js +1 -1
  173. package/dist/components/xplor-badge-recalled.js +1 -1
  174. package/dist/components/xplor-badge-upcoming.js +1 -1
  175. package/dist/components/xplor-badge-waitlist.js +1 -1
  176. package/dist/components/xplor-badge.js +1 -1
  177. package/dist/components/xplor-btn-back-to-parent.js +1 -1
  178. package/dist/components/xplor-btn-back.js +1 -1
  179. package/dist/components/xplor-btn-icon.js +1 -1
  180. package/dist/components/xplor-btn-toggle-group.js +1 -1
  181. package/dist/components/xplor-btn-toggle-secondary.js +1 -1
  182. package/dist/components/xplor-btn-toggle.js +1 -1
  183. package/dist/components/xplor-btn-tooltip.js +3 -3
  184. package/dist/components/xplor-button.js +1 -1
  185. package/dist/components/xplor-canvas-editor.d.ts +11 -0
  186. package/dist/components/xplor-canvas-editor.js +370 -0
  187. package/dist/components/xplor-canvas-editor.js.map +1 -0
  188. package/dist/components/xplor-chat-widget.js +3 -3
  189. package/dist/components/xplor-checkbox.js +1 -1
  190. package/dist/components/xplor-combobox.js +5 -5
  191. package/dist/components/xplor-datatable.js +2 -2
  192. package/dist/components/xplor-date-picker.js +5 -5
  193. package/dist/components/xplor-drag-and-drop-input.js +26 -5
  194. package/dist/components/xplor-drag-and-drop-input.js.map +1 -1
  195. package/dist/components/xplor-dropdown.js +1 -1
  196. package/dist/components/xplor-expansion-panel.js +4 -4
  197. package/dist/components/xplor-expansion-panels.js +1 -1
  198. package/dist/components/xplor-file-upload.js +2 -2
  199. package/dist/components/xplor-inline-checkbox.js +2 -2
  200. package/dist/components/xplor-inline-date-picker.js +1 -1
  201. package/dist/components/xplor-inline-switch.js +1 -1
  202. package/dist/components/xplor-input-file.js +1 -1
  203. package/dist/components/xplor-input-search.js +2 -2
  204. package/dist/components/xplor-input-select.js +2 -2
  205. package/dist/components/xplor-input-send.js +3 -3
  206. package/dist/components/xplor-input-text-area.js +2 -2
  207. package/dist/components/xplor-input-text-secondary.js +2 -2
  208. package/dist/components/xplor-input-text.js +2 -2
  209. package/dist/components/xplor-input-title.js +1 -1
  210. package/dist/components/xplor-links.js +1 -1
  211. package/dist/components/xplor-modal-persistent.js +2 -2
  212. package/dist/components/xplor-modal.js +1 -1
  213. package/dist/components/xplor-nav-tabs.js +2 -2
  214. package/dist/components/xplor-radio-btn.js +2 -2
  215. package/dist/components/xplor-rich-text-editor.d.ts +11 -0
  216. package/dist/components/xplor-rich-text-editor.js +23755 -0
  217. package/dist/components/xplor-rich-text-editor.js.map +1 -0
  218. package/dist/components/xplor-section-card.js +2 -2
  219. package/dist/components/xplor-section-heading.js +2 -2
  220. package/dist/components/xplor-table.js +5 -5
  221. package/dist/components/xplor-text-bubble.js +2 -2
  222. package/dist/components/xplor-text-field.js +2 -2
  223. package/dist/components/xplor-time-picker.js +6 -6
  224. package/dist/components/xplor-tooltip.js +1 -1
  225. package/dist/esm/{index-KRfMjDC2.js → index-QwDfQUbw.js} +32 -7
  226. package/dist/esm/index-QwDfQUbw.js.map +1 -0
  227. package/dist/esm/loader.js +3 -3
  228. package/dist/esm/xplor-alert-dialog_60.entry.js +29396 -0
  229. package/dist/esm/xplor-alert-dialog_60.entry.js.map +1 -0
  230. package/dist/esm/xplor-component-library.js +3 -3
  231. package/dist/hydrate/index.js +24231 -96
  232. package/dist/hydrate/index.mjs +24231 -96
  233. package/dist/types/components/xplor-button/xplor-button.d.ts +2 -0
  234. package/dist/types/components/xplor-canvas-editor/xplor-canvas-editor.d.ts +63 -0
  235. package/dist/types/components/xplor-canvas-editor/xplor-canvas-editor.stories.d.ts +84 -0
  236. package/dist/types/components/xplor-drag-and-drop-input/xplor-drag-and-drop-input.d.ts +6 -0
  237. package/dist/types/components/xplor-rich-text-editor/xplor-rich-text-editor.d.ts +129 -0
  238. package/dist/types/components/xplor-rich-text-editor/xplor-rich-text-editor.stories.d.ts +105 -0
  239. package/dist/types/components.d.ts +286 -0
  240. package/dist/types/utils/slate-legacy-converter.d.ts +83 -0
  241. package/dist/xplor-component-library/p-0d477ec2.entry.js +2 -0
  242. package/dist/xplor-component-library/p-0d477ec2.entry.js.map +1 -0
  243. package/dist/xplor-component-library/p-QwDfQUbw.js +3 -0
  244. package/dist/xplor-component-library/p-QwDfQUbw.js.map +1 -0
  245. package/dist/xplor-component-library/xplor-component-library.css +1 -1
  246. package/dist/xplor-component-library/xplor-component-library.esm.js +1 -1
  247. package/package.json +7 -1
  248. package/components/p-BRWe4TXp.js.map +0 -1
  249. package/components/p-Bs_ocvfe.js.map +0 -1
  250. package/dist/cjs/index-BjAapk2n.js.map +0 -1
  251. package/dist/cjs/xplor-alert-dialog_58.cjs.entry.js +0 -5395
  252. package/dist/cjs/xplor-alert-dialog_58.cjs.entry.js.map +0 -1
  253. package/dist/components/p-04oMLTZR.js.map +0 -1
  254. package/dist/components/p-B5rS_jjI.js.map +0 -1
  255. package/dist/esm/index-KRfMjDC2.js.map +0 -1
  256. package/dist/esm/xplor-alert-dialog_58.entry.js +0 -5336
  257. package/dist/esm/xplor-alert-dialog_58.entry.js.map +0 -1
  258. package/dist/xplor-component-library/p-7ecb45dd.entry.js +0 -2
  259. package/dist/xplor-component-library/p-7ecb45dd.entry.js.map +0 -1
  260. package/dist/xplor-component-library/p-KRfMjDC2.js +0 -3
  261. package/dist/xplor-component-library/p-KRfMjDC2.js.map +0 -1
@@ -0,0 +1,520 @@
1
+ import { Host, h, } from "@stencil/core";
2
+ let canvasIdCounter = 0;
3
+ // ---------------------------------------------------------------------------
4
+ // Component
5
+ // ---------------------------------------------------------------------------
6
+ export class XplorCanvasEditor {
7
+ constructor() {
8
+ this.componentId = `xplor-canvas-${++canvasIdCounter}`;
9
+ // Drawing state (non-reactive, no need to trigger re-render)
10
+ this.isDrawing = false;
11
+ this.drawStart = null;
12
+ // Resize state
13
+ this.isResizing = false;
14
+ this.resizeHandle = null;
15
+ this.resizeStart = null;
16
+ // -------------------------------------------------------------------------
17
+ // Props
18
+ // -------------------------------------------------------------------------
19
+ /** Width of the SVG canvas in pixels */
20
+ this.width = 800;
21
+ /** Height of the SVG canvas in pixels */
22
+ this.height = 500;
23
+ /**
24
+ * Initially active tool. The consumer can switch tool programmatically by
25
+ * updating this prop; the internal `activeTool` state takes over once the
26
+ * component mounts.
27
+ */
28
+ this.tool = 'select';
29
+ // -------------------------------------------------------------------------
30
+ // State
31
+ // -------------------------------------------------------------------------
32
+ this.shapes = [];
33
+ this.selectedId = null;
34
+ this.activeTool = 'select';
35
+ this.draftShape = null;
36
+ // -------------------------------------------------------------------------
37
+ // Private: Drawing handlers (SVG events)
38
+ // -------------------------------------------------------------------------
39
+ this.onSvgMouseDown = (event) => {
40
+ // Only draw if target is the SVG itself or the background rect
41
+ const target = event.target;
42
+ const isBackground = target === this.svgEl ||
43
+ target.classList.contains('xplor-canvas__bg');
44
+ if (this.activeTool === 'select') {
45
+ // Clicking background clears selection
46
+ if (isBackground) {
47
+ this.selectedId = null;
48
+ }
49
+ return;
50
+ }
51
+ if (!isBackground)
52
+ return;
53
+ const point = this.getSvgPoint(event);
54
+ this.isDrawing = true;
55
+ this.drawStart = point;
56
+ this.draftShape = {
57
+ id: '__draft__',
58
+ type: this.activeTool,
59
+ x: point.x,
60
+ y: point.y,
61
+ width: 0,
62
+ height: 0,
63
+ fill: 'rgba(33, 150, 243, 0.15)',
64
+ stroke: '#2196f3',
65
+ };
66
+ event.preventDefault();
67
+ };
68
+ this.onSvgMouseMove = (event) => {
69
+ if (!this.isDrawing || !this.drawStart || !this.draftShape)
70
+ return;
71
+ const point = this.getSvgPoint(event);
72
+ const x = Math.min(point.x, this.drawStart.x);
73
+ const y = Math.min(point.y, this.drawStart.y);
74
+ const width = Math.abs(point.x - this.drawStart.x);
75
+ const height = Math.abs(point.y - this.drawStart.y);
76
+ // Spread to a new object to trigger re-render
77
+ this.draftShape = Object.assign(Object.assign({}, this.draftShape), { x, y, width, height });
78
+ };
79
+ this.onSvgMouseUp = (_event) => {
80
+ if (!this.isDrawing || !this.draftShape) {
81
+ this.isDrawing = false;
82
+ return;
83
+ }
84
+ this.isDrawing = false;
85
+ // Discard draws smaller than 5 px in either dimension
86
+ if (this.draftShape.width < 5 || this.draftShape.height < 5) {
87
+ this.draftShape = null;
88
+ this.drawStart = null;
89
+ return;
90
+ }
91
+ const newShape = Object.assign(Object.assign({}, this.draftShape), { id: this.generateId(), fill: 'rgba(33, 150, 243, 0.25)', stroke: '#1976d2' });
92
+ this.shapes = [...this.shapes, newShape];
93
+ this.draftShape = null;
94
+ this.drawStart = null;
95
+ this.xplorShapeAdd.emit(newShape);
96
+ };
97
+ this.onShapeClick = (event, shape) => {
98
+ if (this.activeTool !== 'select')
99
+ return;
100
+ event.stopPropagation();
101
+ this.selectedId = shape.id;
102
+ };
103
+ // -------------------------------------------------------------------------
104
+ // Private: Resize handlers
105
+ // -------------------------------------------------------------------------
106
+ this.onResizeHandleMouseDown = (event, handleName) => {
107
+ event.stopPropagation();
108
+ event.preventDefault();
109
+ const selectedShape = this.shapes.find(s => s.id === this.selectedId);
110
+ if (!selectedShape)
111
+ return;
112
+ const point = this.getSvgPoint(event);
113
+ this.isResizing = true;
114
+ this.resizeHandle = handleName;
115
+ this.resizeStart = { x: point.x, y: point.y, shape: Object.assign({}, selectedShape) };
116
+ const onMouseMove = (e) => this.onResizeMouseMove(e);
117
+ const onMouseUp = (e) => {
118
+ this.onResizeMouseUp(e);
119
+ window.removeEventListener('mousemove', onMouseMove);
120
+ window.removeEventListener('mouseup', onMouseUp);
121
+ };
122
+ window.addEventListener('mousemove', onMouseMove);
123
+ window.addEventListener('mouseup', onMouseUp);
124
+ };
125
+ this.onResizeMouseMove = (event) => {
126
+ if (!this.isResizing || !this.resizeStart || !this.resizeHandle)
127
+ return;
128
+ const point = this.getSvgPoint(event);
129
+ const dx = point.x - this.resizeStart.x;
130
+ const dy = point.y - this.resizeStart.y;
131
+ const orig = this.resizeStart.shape;
132
+ const MIN = 10;
133
+ let { x, y, width, height } = orig;
134
+ if (this.resizeHandle.includes('e')) {
135
+ width = Math.max(MIN, orig.width + dx);
136
+ }
137
+ if (this.resizeHandle.includes('w')) {
138
+ const newWidth = Math.max(MIN, orig.width - dx);
139
+ x = orig.x + (orig.width - newWidth);
140
+ width = newWidth;
141
+ }
142
+ if (this.resizeHandle.includes('s')) {
143
+ height = Math.max(MIN, orig.height + dy);
144
+ }
145
+ if (this.resizeHandle.includes('n')) {
146
+ const newHeight = Math.max(MIN, orig.height - dy);
147
+ y = orig.y + (orig.height - newHeight);
148
+ height = newHeight;
149
+ }
150
+ this.shapes = this.shapes.map(s => s.id === this.selectedId ? Object.assign(Object.assign({}, s), { x, y, width, height }) : s);
151
+ };
152
+ this.onResizeMouseUp = (_event) => {
153
+ if (!this.isResizing)
154
+ return;
155
+ this.isResizing = false;
156
+ this.resizeHandle = null;
157
+ this.resizeStart = null;
158
+ const updatedShape = this.shapes.find(s => s.id === this.selectedId);
159
+ if (updatedShape) {
160
+ this.xplorShapeChange.emit(Object.assign({}, updatedShape));
161
+ }
162
+ };
163
+ // -------------------------------------------------------------------------
164
+ // Private: Keyboard handler (on the wrapper div)
165
+ // -------------------------------------------------------------------------
166
+ this.onKeyDown = (event) => {
167
+ if (event.key === 'Delete' || event.key === 'Backspace') {
168
+ event.preventDefault();
169
+ this.deleteSelected();
170
+ }
171
+ };
172
+ }
173
+ // -------------------------------------------------------------------------
174
+ // Lifecycle
175
+ // -------------------------------------------------------------------------
176
+ componentWillLoad() {
177
+ this.activeTool = this.tool;
178
+ }
179
+ // -------------------------------------------------------------------------
180
+ // Public methods
181
+ // -------------------------------------------------------------------------
182
+ /** Removes all shapes and clears the selection */
183
+ async clearAll() {
184
+ this.shapes = [];
185
+ this.selectedId = null;
186
+ }
187
+ /** Returns a shallow copy of all current shapes */
188
+ async getShapes() {
189
+ return [...this.shapes];
190
+ }
191
+ // -------------------------------------------------------------------------
192
+ // Private: Utilities
193
+ // -------------------------------------------------------------------------
194
+ generateId() {
195
+ return `shape-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
196
+ }
197
+ getSvgPoint(event) {
198
+ const rect = this.svgEl.getBoundingClientRect();
199
+ return {
200
+ x: Math.max(0, Math.min(this.width, event.clientX - rect.left)),
201
+ y: Math.max(0, Math.min(this.height, event.clientY - rect.top)),
202
+ };
203
+ }
204
+ setActiveTool(tool) {
205
+ this.activeTool = tool;
206
+ this.draftShape = null;
207
+ this.isDrawing = false;
208
+ }
209
+ deleteSelected() {
210
+ if (!this.selectedId)
211
+ return;
212
+ const id = this.selectedId;
213
+ this.shapes = this.shapes.filter(s => s.id !== id);
214
+ this.selectedId = null;
215
+ this.xplorShapeRemove.emit(id);
216
+ }
217
+ // -------------------------------------------------------------------------
218
+ // Private: SVG render helpers
219
+ // -------------------------------------------------------------------------
220
+ renderShape(shape, isDraft = false) {
221
+ const isSelected = !isDraft && shape.id === this.selectedId;
222
+ const commonProps = {
223
+ class: {
224
+ 'xplor-canvas__shape': true,
225
+ 'xplor-canvas__shape--selected': isSelected,
226
+ 'xplor-canvas__shape--draft': isDraft,
227
+ },
228
+ fill: shape.fill,
229
+ stroke: shape.stroke,
230
+ 'stroke-width': '2',
231
+ style: {
232
+ cursor: this.activeTool === 'select' && !isDraft ? 'pointer' : 'default',
233
+ },
234
+ onClick: !isDraft
235
+ ? (e) => this.onShapeClick(e, shape)
236
+ : undefined,
237
+ };
238
+ if (shape.type === 'rect') {
239
+ return (h("rect", Object.assign({}, commonProps, { x: shape.x, y: shape.y, width: shape.width, height: shape.height, rx: "2" })));
240
+ }
241
+ if (shape.type === 'circle') {
242
+ return (h("ellipse", Object.assign({}, commonProps, { cx: shape.x + shape.width / 2, cy: shape.y + shape.height / 2, rx: shape.width / 2, ry: shape.height / 2 })));
243
+ }
244
+ if (shape.type === 'line') {
245
+ return (h("line", Object.assign({}, commonProps, { x1: shape.x, y1: shape.y, x2: shape.x + shape.width, y2: shape.y + shape.height, fill: "none" })));
246
+ }
247
+ return null;
248
+ }
249
+ renderSelectionOverlay(shape) {
250
+ const overlayProps = {
251
+ class: 'xplor-canvas__selection-overlay',
252
+ stroke: 'var(--selection-color, #2196f3)',
253
+ 'stroke-dasharray': '5,3',
254
+ 'stroke-width': '2',
255
+ fill: 'none',
256
+ style: { pointerEvents: 'none' },
257
+ };
258
+ if (shape.type === 'line') {
259
+ return (h("line", Object.assign({}, overlayProps, { x1: shape.x, y1: shape.y, x2: shape.x + shape.width, y2: shape.y + shape.height })));
260
+ }
261
+ if (shape.type === 'circle') {
262
+ return (h("ellipse", Object.assign({}, overlayProps, { cx: shape.x + shape.width / 2, cy: shape.y + shape.height / 2, rx: shape.width / 2, ry: shape.height / 2 })));
263
+ }
264
+ // rect (default)
265
+ return (h("rect", Object.assign({}, overlayProps, { x: shape.x, y: shape.y, width: shape.width, height: shape.height, rx: "2" })));
266
+ }
267
+ renderResizeHandles(shape) {
268
+ const { x, y, width, height } = shape;
269
+ const mx = x + width / 2;
270
+ const my = y + height / 2;
271
+ const handles = [
272
+ { name: 'nw', cx: x, cy: y, cursor: 'nwse-resize' },
273
+ { name: 'n', cx: mx, cy: y, cursor: 'ns-resize' },
274
+ { name: 'ne', cx: x + width, cy: y, cursor: 'nesw-resize' },
275
+ { name: 'e', cx: x + width, cy: my, cursor: 'ew-resize' },
276
+ { name: 'se', cx: x + width, cy: y + height, cursor: 'nwse-resize' },
277
+ { name: 's', cx: mx, cy: y + height, cursor: 'ns-resize' },
278
+ { name: 'sw', cx: x, cy: y + height, cursor: 'nesw-resize' },
279
+ { name: 'w', cx: x, cy: my, cursor: 'ew-resize' },
280
+ ];
281
+ return handles.map(h => (h("circle", { key: h.name, class: "xplor-canvas__handle", cx: h.cx, cy: h.cy, r: "var(--handle-size, 5)", style: { cursor: h.cursor }, onMouseDown: (e) => this.onResizeHandleMouseDown(e, h.name), "aria-label": `Resize ${h.name.toUpperCase()} handle`, role: "button" })));
282
+ }
283
+ // -------------------------------------------------------------------------
284
+ // Private: Toolbar render
285
+ // -------------------------------------------------------------------------
286
+ renderToolbar() {
287
+ const tools = [
288
+ {
289
+ id: 'select',
290
+ label: 'Select tool',
291
+ icon: (h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true" }, h("path", { d: "M4 0 L20 12 L12.5 14.5 L10 24 Z" }))),
292
+ },
293
+ {
294
+ id: 'rect',
295
+ label: 'Rectangle tool',
296
+ icon: (h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "aria-hidden": "true" }, h("rect", { x: "3", y: "3", width: "18", height: "18", rx: "1" }))),
297
+ },
298
+ {
299
+ id: 'circle',
300
+ label: 'Circle / Ellipse tool',
301
+ icon: (h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "aria-hidden": "true" }, h("ellipse", { cx: "12", cy: "12", rx: "9", ry: "9" }))),
302
+ },
303
+ {
304
+ id: 'line',
305
+ label: 'Line tool',
306
+ icon: (h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "aria-hidden": "true" }, h("line", { x1: "3", y1: "21", x2: "21", y2: "3" }))),
307
+ },
308
+ ];
309
+ return (h("div", { class: "xplor-canvas__toolbar", role: "toolbar", "aria-label": "Canvas drawing tools", id: `${this.componentId}-toolbar` }, h("div", { class: "xplor-canvas__toolbar-group", role: "group", "aria-label": "Drawing tools" }, tools.map(t => (h("button", { type: "button", key: t.id, class: {
310
+ 'xplor-canvas__toolbar-btn': true,
311
+ 'xplor-canvas__toolbar-btn--active': this.activeTool === t.id,
312
+ }, "aria-label": t.label, "aria-pressed": String(this.activeTool === t.id), title: t.label, onClick: () => this.setActiveTool(t.id) }, t.icon)))), h("div", { class: "xplor-canvas__toolbar-divider", "aria-hidden": "true" }), h("div", { class: "xplor-canvas__toolbar-group", role: "group", "aria-label": "Canvas actions" }, h("button", { type: "button", class: {
313
+ 'xplor-canvas__toolbar-btn': true,
314
+ 'xplor-canvas__toolbar-btn--danger': !!this.selectedId,
315
+ }, "aria-label": "Delete selected shape", title: "Delete selected (Del)", disabled: !this.selectedId, onClick: () => this.deleteSelected() }, h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true" }, h("path", { d: "M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z" }))), h("button", { type: "button", class: "xplor-canvas__toolbar-btn", "aria-label": "Clear all shapes", title: "Clear all", onClick: () => this.clearAll() }, h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true" }, h("path", { d: "M5 13h14v-2H5v2zm-2 4h14v-2H3v2zM7 7v2h14V7H7z" }), h("path", { d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }))))));
316
+ }
317
+ // -------------------------------------------------------------------------
318
+ // Render
319
+ // -------------------------------------------------------------------------
320
+ render() {
321
+ const selectedShape = this.selectedId
322
+ ? this.shapes.find(s => s.id === this.selectedId)
323
+ : null;
324
+ const svgCursor = this.activeTool !== 'select' ? 'crosshair' : 'default';
325
+ return (h(Host, { key: '14f6878d3a4a6de64d1862ab998f2acdf00004d5' }, h("div", { key: '62a4fff983faeea8884d30df8a3bc3e39e6931a6', class: "xplor-canvas", role: "application", "aria-label": "Canvas editor", "aria-describedby": `${this.componentId}-desc`, tabIndex: 0, onKeyDown: this.onKeyDown }, h("span", { key: 'cb962f76307036784990f491e9b55f38baa0b660', id: `${this.componentId}-desc`, class: "xplor-canvas__sr-only" }, "Interactive canvas editor. Use toolbar to select a drawing tool, then click and drag on the canvas to draw shapes. Press Delete to remove a selected shape."), this.renderToolbar(), h("div", { key: 'e46429cfb82d2804ad97f474469e307d4cedc61a', class: "xplor-canvas__svg-wrapper" }, h("svg", { key: '01f59f5ad95af1854ce8883b79fbcb382b8c910f', ref: el => (this.svgEl = el), class: "xplor-canvas__svg", width: this.width, height: this.height, style: { cursor: svgCursor }, "aria-label": `Drawing canvas, ${this.shapes.length} shape${this.shapes.length !== 1 ? 's' : ''}`, role: "img", onMouseDown: this.onSvgMouseDown, onMouseMove: this.onSvgMouseMove, onMouseUp: this.onSvgMouseUp }, h("rect", { key: '831a519b540b5dd36c15edec85ad67bcb6d1dc03', class: "xplor-canvas__bg", x: "0", y: "0", width: this.width, height: this.height, fill: "transparent" }), this.shapes.map(shape => this.renderShape(shape)), selectedShape && this.renderSelectionOverlay(selectedShape), selectedShape &&
326
+ this.activeTool === 'select' &&
327
+ this.renderResizeHandles(selectedShape), this.draftShape && this.renderShape(this.draftShape, true))), h("div", { key: '5758ccf14f88614a1732085e287bad7f29ea0030', class: "xplor-canvas__sr-only", "aria-live": "polite", "aria-atomic": "true" }, this.shapes.length > 0
328
+ ? `Canvas contains ${this.shapes.length} shape${this.shapes.length !== 1 ? 's' : ''}.`
329
+ : 'Canvas is empty.'))));
330
+ }
331
+ static get is() { return "xplor-canvas-editor"; }
332
+ static get encapsulation() { return "scoped"; }
333
+ static get originalStyleUrls() {
334
+ return {
335
+ "$": ["xplor-canvas-editor.scss"]
336
+ };
337
+ }
338
+ static get styleUrls() {
339
+ return {
340
+ "$": ["xplor-canvas-editor.css"]
341
+ };
342
+ }
343
+ static get properties() {
344
+ return {
345
+ "width": {
346
+ "type": "number",
347
+ "attribute": "width",
348
+ "mutable": false,
349
+ "complexType": {
350
+ "original": "number",
351
+ "resolved": "number",
352
+ "references": {}
353
+ },
354
+ "required": false,
355
+ "optional": false,
356
+ "docs": {
357
+ "tags": [],
358
+ "text": "Width of the SVG canvas in pixels"
359
+ },
360
+ "getter": false,
361
+ "setter": false,
362
+ "reflect": false,
363
+ "defaultValue": "800"
364
+ },
365
+ "height": {
366
+ "type": "number",
367
+ "attribute": "height",
368
+ "mutable": false,
369
+ "complexType": {
370
+ "original": "number",
371
+ "resolved": "number",
372
+ "references": {}
373
+ },
374
+ "required": false,
375
+ "optional": false,
376
+ "docs": {
377
+ "tags": [],
378
+ "text": "Height of the SVG canvas in pixels"
379
+ },
380
+ "getter": false,
381
+ "setter": false,
382
+ "reflect": false,
383
+ "defaultValue": "500"
384
+ },
385
+ "tool": {
386
+ "type": "string",
387
+ "attribute": "tool",
388
+ "mutable": false,
389
+ "complexType": {
390
+ "original": "'select' | 'rect' | 'circle' | 'line'",
391
+ "resolved": "\"circle\" | \"line\" | \"rect\" | \"select\"",
392
+ "references": {}
393
+ },
394
+ "required": false,
395
+ "optional": false,
396
+ "docs": {
397
+ "tags": [],
398
+ "text": "Initially active tool. The consumer can switch tool programmatically by\nupdating this prop; the internal `activeTool` state takes over once the\ncomponent mounts."
399
+ },
400
+ "getter": false,
401
+ "setter": false,
402
+ "reflect": false,
403
+ "defaultValue": "'select'"
404
+ }
405
+ };
406
+ }
407
+ static get states() {
408
+ return {
409
+ "shapes": {},
410
+ "selectedId": {},
411
+ "activeTool": {},
412
+ "draftShape": {}
413
+ };
414
+ }
415
+ static get events() {
416
+ return [{
417
+ "method": "xplorShapeAdd",
418
+ "name": "xplorShapeAdd",
419
+ "bubbles": true,
420
+ "cancelable": true,
421
+ "composed": true,
422
+ "docs": {
423
+ "tags": [],
424
+ "text": "Emitted when a new shape is committed to the canvas"
425
+ },
426
+ "complexType": {
427
+ "original": "Shape",
428
+ "resolved": "Shape",
429
+ "references": {
430
+ "Shape": {
431
+ "location": "local",
432
+ "path": "/Users/darryn.lee-warden/Documents/DEV/xplor_design_system/apps/core-stencil-components/src/components/xplor-canvas-editor/xplor-canvas-editor.tsx",
433
+ "id": "src/components/xplor-canvas-editor/xplor-canvas-editor.tsx::Shape"
434
+ }
435
+ }
436
+ }
437
+ }, {
438
+ "method": "xplorShapeRemove",
439
+ "name": "xplorShapeRemove",
440
+ "bubbles": true,
441
+ "cancelable": true,
442
+ "composed": true,
443
+ "docs": {
444
+ "tags": [],
445
+ "text": "Emitted when a shape is removed; payload is the shape id"
446
+ },
447
+ "complexType": {
448
+ "original": "string",
449
+ "resolved": "string",
450
+ "references": {}
451
+ }
452
+ }, {
453
+ "method": "xplorShapeChange",
454
+ "name": "xplorShapeChange",
455
+ "bubbles": true,
456
+ "cancelable": true,
457
+ "composed": true,
458
+ "docs": {
459
+ "tags": [],
460
+ "text": "Emitted when a shape's bounds are updated via resize"
461
+ },
462
+ "complexType": {
463
+ "original": "Shape",
464
+ "resolved": "Shape",
465
+ "references": {
466
+ "Shape": {
467
+ "location": "local",
468
+ "path": "/Users/darryn.lee-warden/Documents/DEV/xplor_design_system/apps/core-stencil-components/src/components/xplor-canvas-editor/xplor-canvas-editor.tsx",
469
+ "id": "src/components/xplor-canvas-editor/xplor-canvas-editor.tsx::Shape"
470
+ }
471
+ }
472
+ }
473
+ }];
474
+ }
475
+ static get methods() {
476
+ return {
477
+ "clearAll": {
478
+ "complexType": {
479
+ "signature": "() => Promise<void>",
480
+ "parameters": [],
481
+ "references": {
482
+ "Promise": {
483
+ "location": "global",
484
+ "id": "global::Promise"
485
+ }
486
+ },
487
+ "return": "Promise<void>"
488
+ },
489
+ "docs": {
490
+ "text": "Removes all shapes and clears the selection",
491
+ "tags": []
492
+ }
493
+ },
494
+ "getShapes": {
495
+ "complexType": {
496
+ "signature": "() => Promise<Shape[]>",
497
+ "parameters": [],
498
+ "references": {
499
+ "Promise": {
500
+ "location": "global",
501
+ "id": "global::Promise"
502
+ },
503
+ "Shape": {
504
+ "location": "local",
505
+ "path": "/Users/darryn.lee-warden/Documents/DEV/xplor_design_system/apps/core-stencil-components/src/components/xplor-canvas-editor/xplor-canvas-editor.tsx",
506
+ "id": "src/components/xplor-canvas-editor/xplor-canvas-editor.tsx::Shape"
507
+ }
508
+ },
509
+ "return": "Promise<Shape[]>"
510
+ },
511
+ "docs": {
512
+ "text": "Returns a shallow copy of all current shapes",
513
+ "tags": []
514
+ }
515
+ }
516
+ };
517
+ }
518
+ static get elementRef() { return "el"; }
519
+ }
520
+ //# sourceMappingURL=xplor-canvas-editor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xplor-canvas-editor.js","sourceRoot":"","sources":["../../../src/components/xplor-canvas-editor/xplor-canvas-editor.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n Host,\n h,\n Prop,\n State,\n Event,\n EventEmitter,\n Method,\n Element,\n} from '@stencil/core';\n\n// ---------------------------------------------------------------------------\n// Shape interface\n// ---------------------------------------------------------------------------\n\nexport interface Shape {\n id: string;\n type: 'rect' | 'circle' | 'line';\n x: number;\n y: number;\n width: number;\n height: number;\n fill: string;\n stroke: string;\n}\n\nlet canvasIdCounter = 0;\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\n@Component({\n tag: 'xplor-canvas-editor',\n styleUrl: 'xplor-canvas-editor.scss',\n scoped: true,\n})\nexport class XplorCanvasEditor {\n // -------------------------------------------------------------------------\n // Internal refs & IDs\n // -------------------------------------------------------------------------\n\n @Element() el: HTMLElement;\n\n private readonly componentId = `xplor-canvas-${++canvasIdCounter}`;\n private svgEl!: SVGSVGElement;\n\n // Drawing state (non-reactive, no need to trigger re-render)\n private isDrawing = false;\n private drawStart: { x: number; y: number } | null = null;\n\n // Resize state\n private isResizing = false;\n private resizeHandle: string | null = null;\n private resizeStart: { x: number; y: number; shape: Shape } | null = null;\n\n // -------------------------------------------------------------------------\n // Props\n // -------------------------------------------------------------------------\n\n /** Width of the SVG canvas in pixels */\n @Prop() width: number = 800;\n\n /** Height of the SVG canvas in pixels */\n @Prop() height: number = 500;\n\n /**\n * Initially active tool. The consumer can switch tool programmatically by\n * updating this prop; the internal `activeTool` state takes over once the\n * component mounts.\n */\n @Prop() tool: 'select' | 'rect' | 'circle' | 'line' = 'select';\n\n // -------------------------------------------------------------------------\n // State\n // -------------------------------------------------------------------------\n\n @State() private shapes: Shape[] = [];\n @State() private selectedId: string | null = null;\n @State() private activeTool: 'select' | 'rect' | 'circle' | 'line' = 'select';\n @State() private draftShape: Shape | null = null;\n\n // -------------------------------------------------------------------------\n // Events\n // -------------------------------------------------------------------------\n\n /** Emitted when a new shape is committed to the canvas */\n @Event() xplorShapeAdd: EventEmitter<Shape>;\n\n /** Emitted when a shape is removed; payload is the shape id */\n @Event() xplorShapeRemove: EventEmitter<string>;\n\n /** Emitted when a shape's bounds are updated via resize */\n @Event() xplorShapeChange: EventEmitter<Shape>;\n\n // -------------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------------\n\n componentWillLoad() {\n this.activeTool = this.tool;\n }\n\n // -------------------------------------------------------------------------\n // Public methods\n // -------------------------------------------------------------------------\n\n /** Removes all shapes and clears the selection */\n @Method()\n async clearAll(): Promise<void> {\n this.shapes = [];\n this.selectedId = null;\n }\n\n /** Returns a shallow copy of all current shapes */\n @Method()\n async getShapes(): Promise<Shape[]> {\n return [...this.shapes];\n }\n\n // -------------------------------------------------------------------------\n // Private: Utilities\n // -------------------------------------------------------------------------\n\n private generateId(): string {\n return `shape-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;\n }\n\n private getSvgPoint(event: MouseEvent): { x: number; y: number } {\n const rect = this.svgEl.getBoundingClientRect();\n return {\n x: Math.max(0, Math.min(this.width, event.clientX - rect.left)),\n y: Math.max(0, Math.min(this.height, event.clientY - rect.top)),\n };\n }\n\n private setActiveTool(tool: 'select' | 'rect' | 'circle' | 'line') {\n this.activeTool = tool;\n this.draftShape = null;\n this.isDrawing = false;\n }\n\n private deleteSelected() {\n if (!this.selectedId) return;\n const id = this.selectedId;\n this.shapes = this.shapes.filter(s => s.id !== id);\n this.selectedId = null;\n this.xplorShapeRemove.emit(id);\n }\n\n // -------------------------------------------------------------------------\n // Private: Drawing handlers (SVG events)\n // -------------------------------------------------------------------------\n\n private onSvgMouseDown = (event: MouseEvent) => {\n // Only draw if target is the SVG itself or the background rect\n const target = event.target as SVGElement;\n const isBackground =\n target === this.svgEl ||\n target.classList.contains('xplor-canvas__bg');\n\n if (this.activeTool === 'select') {\n // Clicking background clears selection\n if (isBackground) {\n this.selectedId = null;\n }\n return;\n }\n\n if (!isBackground) return;\n\n const point = this.getSvgPoint(event);\n this.isDrawing = true;\n this.drawStart = point;\n\n this.draftShape = {\n id: '__draft__',\n type: this.activeTool as 'rect' | 'circle' | 'line',\n x: point.x,\n y: point.y,\n width: 0,\n height: 0,\n fill: 'rgba(33, 150, 243, 0.15)',\n stroke: '#2196f3',\n };\n\n event.preventDefault();\n };\n\n private onSvgMouseMove = (event: MouseEvent) => {\n if (!this.isDrawing || !this.drawStart || !this.draftShape) return;\n\n const point = this.getSvgPoint(event);\n const x = Math.min(point.x, this.drawStart.x);\n const y = Math.min(point.y, this.drawStart.y);\n const width = Math.abs(point.x - this.drawStart.x);\n const height = Math.abs(point.y - this.drawStart.y);\n\n // Spread to a new object to trigger re-render\n this.draftShape = { ...this.draftShape, x, y, width, height };\n };\n\n private onSvgMouseUp = (_event: MouseEvent) => {\n if (!this.isDrawing || !this.draftShape) {\n this.isDrawing = false;\n return;\n }\n\n this.isDrawing = false;\n\n // Discard draws smaller than 5 px in either dimension\n if (this.draftShape.width < 5 || this.draftShape.height < 5) {\n this.draftShape = null;\n this.drawStart = null;\n return;\n }\n\n const newShape: Shape = {\n ...this.draftShape,\n id: this.generateId(),\n fill: 'rgba(33, 150, 243, 0.25)',\n stroke: '#1976d2',\n };\n\n this.shapes = [...this.shapes, newShape];\n this.draftShape = null;\n this.drawStart = null;\n this.xplorShapeAdd.emit(newShape);\n };\n\n private onShapeClick = (event: MouseEvent, shape: Shape) => {\n if (this.activeTool !== 'select') return;\n event.stopPropagation();\n this.selectedId = shape.id;\n };\n\n // -------------------------------------------------------------------------\n // Private: Resize handlers\n // -------------------------------------------------------------------------\n\n private onResizeHandleMouseDown = (event: MouseEvent, handleName: string) => {\n event.stopPropagation();\n event.preventDefault();\n\n const selectedShape = this.shapes.find(s => s.id === this.selectedId);\n if (!selectedShape) return;\n\n const point = this.getSvgPoint(event);\n this.isResizing = true;\n this.resizeHandle = handleName;\n this.resizeStart = { x: point.x, y: point.y, shape: { ...selectedShape } };\n\n const onMouseMove = (e: MouseEvent) => this.onResizeMouseMove(e);\n const onMouseUp = (e: MouseEvent) => {\n this.onResizeMouseUp(e);\n window.removeEventListener('mousemove', onMouseMove);\n window.removeEventListener('mouseup', onMouseUp);\n };\n\n window.addEventListener('mousemove', onMouseMove);\n window.addEventListener('mouseup', onMouseUp);\n };\n\n private onResizeMouseMove = (event: MouseEvent) => {\n if (!this.isResizing || !this.resizeStart || !this.resizeHandle) return;\n\n const point = this.getSvgPoint(event);\n const dx = point.x - this.resizeStart.x;\n const dy = point.y - this.resizeStart.y;\n const orig = this.resizeStart.shape;\n const MIN = 10;\n\n let { x, y, width, height } = orig;\n\n if (this.resizeHandle.includes('e')) {\n width = Math.max(MIN, orig.width + dx);\n }\n if (this.resizeHandle.includes('w')) {\n const newWidth = Math.max(MIN, orig.width - dx);\n x = orig.x + (orig.width - newWidth);\n width = newWidth;\n }\n if (this.resizeHandle.includes('s')) {\n height = Math.max(MIN, orig.height + dy);\n }\n if (this.resizeHandle.includes('n')) {\n const newHeight = Math.max(MIN, orig.height - dy);\n y = orig.y + (orig.height - newHeight);\n height = newHeight;\n }\n\n this.shapes = this.shapes.map(s =>\n s.id === this.selectedId ? { ...s, x, y, width, height } : s,\n );\n };\n\n private onResizeMouseUp = (_event: MouseEvent) => {\n if (!this.isResizing) return;\n this.isResizing = false;\n this.resizeHandle = null;\n this.resizeStart = null;\n\n const updatedShape = this.shapes.find(s => s.id === this.selectedId);\n if (updatedShape) {\n this.xplorShapeChange.emit({ ...updatedShape });\n }\n };\n\n // -------------------------------------------------------------------------\n // Private: Keyboard handler (on the wrapper div)\n // -------------------------------------------------------------------------\n\n private onKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Delete' || event.key === 'Backspace') {\n event.preventDefault();\n this.deleteSelected();\n }\n };\n\n // -------------------------------------------------------------------------\n // Private: SVG render helpers\n // -------------------------------------------------------------------------\n\n private renderShape(shape: Shape, isDraft = false) {\n const isSelected = !isDraft && shape.id === this.selectedId;\n\n const commonProps = {\n class: {\n 'xplor-canvas__shape': true,\n 'xplor-canvas__shape--selected': isSelected,\n 'xplor-canvas__shape--draft': isDraft,\n },\n fill: shape.fill,\n stroke: shape.stroke,\n 'stroke-width': '2',\n style: {\n cursor:\n this.activeTool === 'select' && !isDraft ? 'pointer' : 'default',\n },\n onClick: !isDraft\n ? (e: MouseEvent) => this.onShapeClick(e, shape)\n : undefined,\n };\n\n if (shape.type === 'rect') {\n return (\n <rect\n {...commonProps}\n x={shape.x}\n y={shape.y}\n width={shape.width}\n height={shape.height}\n rx=\"2\"\n />\n );\n }\n\n if (shape.type === 'circle') {\n return (\n <ellipse\n {...commonProps}\n cx={shape.x + shape.width / 2}\n cy={shape.y + shape.height / 2}\n rx={shape.width / 2}\n ry={shape.height / 2}\n />\n );\n }\n\n if (shape.type === 'line') {\n return (\n <line\n {...commonProps}\n x1={shape.x}\n y1={shape.y}\n x2={shape.x + shape.width}\n y2={shape.y + shape.height}\n fill=\"none\"\n />\n );\n }\n\n return null;\n }\n\n private renderSelectionOverlay(shape: Shape) {\n const overlayProps = {\n class: 'xplor-canvas__selection-overlay',\n stroke: 'var(--selection-color, #2196f3)',\n 'stroke-dasharray': '5,3',\n 'stroke-width': '2',\n fill: 'none',\n style: { pointerEvents: 'none' as any },\n };\n\n if (shape.type === 'line') {\n return (\n <line\n {...overlayProps}\n x1={shape.x}\n y1={shape.y}\n x2={shape.x + shape.width}\n y2={shape.y + shape.height}\n />\n );\n }\n\n if (shape.type === 'circle') {\n return (\n <ellipse\n {...overlayProps}\n cx={shape.x + shape.width / 2}\n cy={shape.y + shape.height / 2}\n rx={shape.width / 2}\n ry={shape.height / 2}\n />\n );\n }\n\n // rect (default)\n return (\n <rect\n {...overlayProps}\n x={shape.x}\n y={shape.y}\n width={shape.width}\n height={shape.height}\n rx=\"2\"\n />\n );\n }\n\n private renderResizeHandles(shape: Shape) {\n const { x, y, width, height } = shape;\n const mx = x + width / 2;\n const my = y + height / 2;\n\n const handles: Array<{ name: string; cx: number; cy: number; cursor: string }> = [\n { name: 'nw', cx: x, cy: y, cursor: 'nwse-resize' },\n { name: 'n', cx: mx, cy: y, cursor: 'ns-resize' },\n { name: 'ne', cx: x + width, cy: y, cursor: 'nesw-resize' },\n { name: 'e', cx: x + width, cy: my, cursor: 'ew-resize' },\n { name: 'se', cx: x + width, cy: y + height, cursor: 'nwse-resize' },\n { name: 's', cx: mx, cy: y + height, cursor: 'ns-resize' },\n { name: 'sw', cx: x, cy: y + height, cursor: 'nesw-resize' },\n { name: 'w', cx: x, cy: my, cursor: 'ew-resize' },\n ];\n\n return handles.map(h => (\n <circle\n key={h.name}\n class=\"xplor-canvas__handle\"\n cx={h.cx}\n cy={h.cy}\n r=\"var(--handle-size, 5)\"\n style={{ cursor: h.cursor }}\n onMouseDown={(e: MouseEvent) => this.onResizeHandleMouseDown(e, h.name)}\n aria-label={`Resize ${h.name.toUpperCase()} handle`}\n role=\"button\"\n />\n ));\n }\n\n // -------------------------------------------------------------------------\n // Private: Toolbar render\n // -------------------------------------------------------------------------\n\n private renderToolbar() {\n const tools: Array<{\n id: 'select' | 'rect' | 'circle' | 'line';\n label: string;\n icon: any;\n }> = [\n {\n id: 'select',\n label: 'Select tool',\n icon: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M4 0 L20 12 L12.5 14.5 L10 24 Z\" />\n </svg>\n ),\n },\n {\n id: 'rect',\n label: 'Rectangle tool',\n icon: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"1\" />\n </svg>\n ),\n },\n {\n id: 'circle',\n label: 'Circle / Ellipse tool',\n icon: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <ellipse cx=\"12\" cy=\"12\" rx=\"9\" ry=\"9\" />\n </svg>\n ),\n },\n {\n id: 'line',\n label: 'Line tool',\n icon: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <line x1=\"3\" y1=\"21\" x2=\"21\" y2=\"3\" />\n </svg>\n ),\n },\n ];\n\n return (\n <div\n class=\"xplor-canvas__toolbar\"\n role=\"toolbar\"\n aria-label=\"Canvas drawing tools\"\n id={`${this.componentId}-toolbar`}\n >\n {/* Drawing tool buttons */}\n <div class=\"xplor-canvas__toolbar-group\" role=\"group\" aria-label=\"Drawing tools\">\n {tools.map(t => (\n <button\n type=\"button\"\n key={t.id}\n class={{\n 'xplor-canvas__toolbar-btn': true,\n 'xplor-canvas__toolbar-btn--active': this.activeTool === t.id,\n }}\n aria-label={t.label}\n aria-pressed={String(this.activeTool === t.id)}\n title={t.label}\n onClick={() => this.setActiveTool(t.id)}\n >\n {t.icon}\n </button>\n ))}\n </div>\n\n <div class=\"xplor-canvas__toolbar-divider\" aria-hidden=\"true\" />\n\n {/* Action buttons */}\n <div class=\"xplor-canvas__toolbar-group\" role=\"group\" aria-label=\"Canvas actions\">\n <button\n type=\"button\"\n class={{\n 'xplor-canvas__toolbar-btn': true,\n 'xplor-canvas__toolbar-btn--danger': !!this.selectedId,\n }}\n aria-label=\"Delete selected shape\"\n title=\"Delete selected (Del)\"\n disabled={!this.selectedId}\n onClick={() => this.deleteSelected()}\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z\" />\n </svg>\n </button>\n\n <button\n type=\"button\"\n class=\"xplor-canvas__toolbar-btn\"\n aria-label=\"Clear all shapes\"\n title=\"Clear all\"\n onClick={() => this.clearAll()}\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M5 13h14v-2H5v2zm-2 4h14v-2H3v2zM7 7v2h14V7H7z\" />\n <path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\" />\n </svg>\n </button>\n </div>\n </div>\n );\n }\n\n // -------------------------------------------------------------------------\n // Render\n // -------------------------------------------------------------------------\n\n render() {\n const selectedShape = this.selectedId\n ? this.shapes.find(s => s.id === this.selectedId)\n : null;\n\n const svgCursor =\n this.activeTool !== 'select' ? 'crosshair' : 'default';\n\n return (\n <Host>\n <div\n class=\"xplor-canvas\"\n role=\"application\"\n aria-label=\"Canvas editor\"\n aria-describedby={`${this.componentId}-desc`}\n tabIndex={0}\n onKeyDown={this.onKeyDown}\n >\n {/* Visually hidden description for screen readers */}\n <span id={`${this.componentId}-desc`} class=\"xplor-canvas__sr-only\">\n Interactive canvas editor. Use toolbar to select a drawing tool, then\n click and drag on the canvas to draw shapes. Press Delete to remove a\n selected shape.\n </span>\n\n {this.renderToolbar()}\n\n <div class=\"xplor-canvas__svg-wrapper\">\n <svg\n ref={el => (this.svgEl = el as SVGSVGElement)}\n class=\"xplor-canvas__svg\"\n width={this.width}\n height={this.height}\n style={{ cursor: svgCursor }}\n aria-label={`Drawing canvas, ${this.shapes.length} shape${this.shapes.length !== 1 ? 's' : ''}`}\n role=\"img\"\n onMouseDown={this.onSvgMouseDown}\n onMouseMove={this.onSvgMouseMove}\n onMouseUp={this.onSvgMouseUp}\n >\n {/* Transparent background rect — captures click/mousedown on empty area */}\n <rect\n class=\"xplor-canvas__bg\"\n x=\"0\"\n y=\"0\"\n width={this.width}\n height={this.height}\n fill=\"transparent\"\n />\n\n {/* Committed shapes */}\n {this.shapes.map(shape => this.renderShape(shape))}\n\n {/* Selection overlay (dashed outline) */}\n {selectedShape && this.renderSelectionOverlay(selectedShape)}\n\n {/* Resize handles */}\n {selectedShape &&\n this.activeTool === 'select' &&\n this.renderResizeHandles(selectedShape)}\n\n {/* In-progress draft shape */}\n {this.draftShape && this.renderShape(this.draftShape, true)}\n </svg>\n </div>\n\n {/* Live region for screen reader announcements */}\n <div\n class=\"xplor-canvas__sr-only\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n >\n {this.shapes.length > 0\n ? `Canvas contains ${this.shapes.length} shape${this.shapes.length !== 1 ? 's' : ''}.`\n : 'Canvas is empty.'}\n </div>\n </div>\n </Host>\n );\n }\n}\n"]}