@ixfx/components 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (256) hide show
  1. package/bundle/index.d.ts +1086 -603
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +16527 -14258
  4. package/bundle/index.js.map +1 -1
  5. package/bundle/style.css +14 -4
  6. package/dist/ac-text.d.ts.map +1 -1
  7. package/dist/ac-text.js +11 -6
  8. package/dist/ac-text.js.map +1 -1
  9. package/dist/{button-DMt5XWUK.js → button-C3fLMKUv.js} +4 -4
  10. package/dist/{button-DMt5XWUK.js.map → button-C3fLMKUv.js.map} +1 -1
  11. package/dist/button.d.ts.map +1 -1
  12. package/dist/button.js +1 -1
  13. package/dist/checkbox.d.ts +2 -2
  14. package/dist/checkbox.d.ts.map +1 -1
  15. package/dist/checkbox.js +14 -8
  16. package/dist/checkbox.js.map +1 -1
  17. package/dist/{colour-C3MQIjFJ-BLXg8W3U.js → colour-C3MQIjFJ-vRCdr8W-.js} +146 -552
  18. package/dist/colour-C3MQIjFJ-vRCdr8W-.js.map +1 -0
  19. package/dist/{colour-picker-K6zu82lm.js → colour-picker-0sgAha1T.js} +14 -9
  20. package/dist/colour-picker-0sgAha1T.js.map +1 -0
  21. package/dist/colour-picker.d.ts +1 -1
  22. package/dist/colour-picker.js +1 -1
  23. package/dist/crumbs.d.ts +1 -1
  24. package/dist/crumbs.d.ts.map +1 -1
  25. package/dist/crumbs.js +11 -6
  26. package/dist/crumbs.js.map +1 -1
  27. package/dist/data-display.d.ts +9 -4
  28. package/dist/data-display.d.ts.map +1 -1
  29. package/dist/data-display.js +270 -43
  30. package/dist/data-display.js.map +1 -1
  31. package/dist/data-provider-DgI909FP.js +23 -0
  32. package/dist/data-provider-DgI909FP.js.map +1 -0
  33. package/dist/{decorate-D7rC1gLP.js → decorate-DdjvVHS-.js} +2 -2
  34. package/dist/{decorate-D7rC1gLP.js.map → decorate-DdjvVHS-.js.map} +1 -1
  35. package/dist/{defaults-C7elhuyJ.js → defaults-C3b9OWJD.js} +2 -2
  36. package/dist/{defaults-C7elhuyJ.js.map → defaults-C3b9OWJD.js.map} +1 -1
  37. package/dist/{dist-TAGYwaju.js → dist-CA13FvMn.js} +7 -66
  38. package/dist/dist-CA13FvMn.js.map +1 -0
  39. package/dist/editable-label.d.ts.map +1 -1
  40. package/dist/editable-label.js +3 -2
  41. package/dist/editable-label.js.map +1 -1
  42. package/dist/fallbacks-pNMOMx38.js +168 -0
  43. package/dist/fallbacks-pNMOMx38.js.map +1 -0
  44. package/dist/{hex-editor-DlfoA65y.d.ts → hex-editor-CMIxtNgT.d.ts} +1 -1
  45. package/dist/hex-editor-CMIxtNgT.d.ts.map +1 -0
  46. package/dist/{hex-editor-CsN_ySkH.js → hex-editor-WZrjRtvL.js} +3 -3
  47. package/dist/{hex-editor-CsN_ySkH.js.map → hex-editor-WZrjRtvL.js.map} +1 -1
  48. package/dist/hex.d.ts +1 -1
  49. package/dist/hex.js +1 -1
  50. package/dist/{highlight-TPbSHruI.js → highlight-B-eQrhhb.js} +1 -1
  51. package/dist/{highlight-TPbSHruI.js.map → highlight-B-eQrhhb.js.map} +1 -1
  52. package/dist/{icon-CeTDJhC7.d.ts → icon-Bd5BiU4b.d.ts} +2 -2
  53. package/dist/icon-Bd5BiU4b.d.ts.map +1 -0
  54. package/dist/{icon-9NWTXtE1.js → icon-Ndo40kNO.js} +6 -5
  55. package/dist/icon-Ndo40kNO.js.map +1 -0
  56. package/dist/icons.d.ts +2 -2
  57. package/dist/icons.js +3 -3
  58. package/dist/{incr-search-DSKKsk6w.js → incr-search-CeERfijK.js} +2 -2
  59. package/dist/{incr-search-DSKKsk6w.js.map → incr-search-CeERfijK.js.map} +1 -1
  60. package/dist/incr-search.d.ts +1 -1
  61. package/dist/incr-search.js +2 -2
  62. package/dist/{index-hXFn14tM.d.ts → index-BMjri2-S.d.ts} +2 -2
  63. package/dist/{index-hXFn14tM.d.ts.map → index-BMjri2-S.d.ts.map} +1 -1
  64. package/dist/{index-DSlPeWLK.d.ts → index-CWpNcQKE.d.ts} +5 -5
  65. package/dist/index-CWpNcQKE.d.ts.map +1 -0
  66. package/dist/{index--hrxPBkQ.d.ts → index-CowKi2mo.d.ts} +2 -1
  67. package/dist/index-CowKi2mo.d.ts.map +1 -0
  68. package/dist/{index-B1hNR7Z9.d.ts → index-D87kRcEs.d.ts} +1 -1
  69. package/dist/{index-B1hNR7Z9.d.ts.map → index-D87kRcEs.d.ts.map} +1 -1
  70. package/dist/{index-CnL9krsH.d.ts → index-DgXpCg_G.d.ts} +32 -26
  71. package/dist/index-DgXpCg_G.d.ts.map +1 -0
  72. package/dist/{index-DrQjA8Gs.d.ts → index-s_IhSnwc.d.ts} +2 -2
  73. package/dist/index-s_IhSnwc.d.ts.map +1 -0
  74. package/dist/index.d.ts +432 -121
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +2370 -1707
  77. package/dist/index.js.map +1 -1
  78. package/dist/interaction-D6efrBLo.js +1 -0
  79. package/dist/{keyboard-De_vOHLg.js → keyboard-CimiEskD.js} +1 -1
  80. package/dist/{keyboard-De_vOHLg.js.map → keyboard-CimiEskD.js.map} +1 -1
  81. package/dist/{labelled-input-base-CqPL5rKm.js → labelled-input-base-9K_w3oLj.js} +2 -2
  82. package/dist/{labelled-input-base-CqPL5rKm.js.map → labelled-input-base-9K_w3oLj.js.map} +1 -1
  83. package/dist/{labelled-input-base-D-Y7agky.d.ts → labelled-input-base-ORb1rGq6.d.ts} +1 -1
  84. package/dist/labelled-input-base-ORb1rGq6.d.ts.map +1 -0
  85. package/dist/labelled-radial-input.d.ts +2 -2
  86. package/dist/labelled-radial-input.d.ts.map +1 -1
  87. package/dist/labelled-radial-input.js +7 -6
  88. package/dist/labelled-radial-input.js.map +1 -1
  89. package/dist/labelled-range-input.d.ts +1 -1
  90. package/dist/labelled-range-input.js +2 -2
  91. package/dist/led.d.ts +1 -1
  92. package/dist/led.d.ts.map +1 -1
  93. package/dist/led.js +2 -2
  94. package/dist/led.js.map +1 -1
  95. package/dist/{menu-item-CKRfP0pN.js → menu-item-BrIuORYD.js} +20 -10
  96. package/dist/menu-item-BrIuORYD.js.map +1 -0
  97. package/dist/{menu-item-ClQF2DKN.d.ts → menu-item-COx-Azgj.d.ts} +1 -1
  98. package/dist/menu-item-COx-Azgj.d.ts.map +1 -0
  99. package/dist/{menu-DJJuCm-5.js → menu-vjoiHZ13.js} +30 -17
  100. package/dist/menu-vjoiHZ13.js.map +1 -0
  101. package/dist/menu.d.ts +2 -2
  102. package/dist/menu.js +2 -2
  103. package/dist/miller.d.ts +2 -2
  104. package/dist/miller.d.ts.map +1 -1
  105. package/dist/miller.js +18 -11
  106. package/dist/miller.js.map +1 -1
  107. package/dist/narrowed-text.d.ts.map +1 -1
  108. package/dist/narrowed-text.js +7 -5
  109. package/dist/narrowed-text.js.map +1 -1
  110. package/dist/panel.d.ts +1 -1
  111. package/dist/panel.d.ts.map +1 -1
  112. package/dist/panel.js +5 -4
  113. package/dist/panel.js.map +1 -1
  114. package/dist/plots.d.ts +2 -2
  115. package/dist/plots.js +2 -2
  116. package/dist/polar-pad.d.ts +1 -1
  117. package/dist/polar-pad.d.ts.map +1 -1
  118. package/dist/polar-pad.js +10 -4
  119. package/dist/polar-pad.js.map +1 -1
  120. package/dist/{popup-ImtS9KnS.js → popup-DiWMxsWj.js} +1 -1
  121. package/dist/{popup-ImtS9KnS.js.map → popup-DiWMxsWj.js.map} +1 -1
  122. package/dist/prominence-DlFShic3.js +104 -0
  123. package/dist/prominence-DlFShic3.js.map +1 -0
  124. package/dist/{radial-input-LUSBEycd.d.ts → radial-input-CpQPs1Mp.d.ts} +1 -1
  125. package/dist/radial-input-CpQPs1Mp.d.ts.map +1 -0
  126. package/dist/{radial-input-DZA6WaFu.js → radial-input-DZ7eQCcM.js} +11 -6
  127. package/dist/radial-input-DZ7eQCcM.js.map +1 -0
  128. package/dist/radial-input.d.ts +1 -1
  129. package/dist/radial-input.js +1 -1
  130. package/dist/range-input.d.ts +1 -1
  131. package/dist/range-input.d.ts.map +1 -1
  132. package/dist/range-input.js +9 -4
  133. package/dist/range-input.js.map +1 -1
  134. package/dist/range.d.ts +1 -1
  135. package/dist/range.d.ts.map +1 -1
  136. package/dist/range.js +5 -3
  137. package/dist/range.js.map +1 -1
  138. package/dist/{registry-Q2gYQCHM.js → registry-CkW7q09Q.js} +1 -1
  139. package/dist/{registry-Q2gYQCHM.js.map → registry-CkW7q09Q.js.map} +1 -1
  140. package/dist/selecthorizontal.d.ts +1 -1
  141. package/dist/selecthorizontal.d.ts.map +1 -1
  142. package/dist/selecthorizontal.js +4 -3
  143. package/dist/selecthorizontal.js.map +1 -1
  144. package/dist/snap-container.d.ts.map +1 -1
  145. package/dist/snap-container.js +1 -1
  146. package/dist/split-layout.d.ts +1 -1
  147. package/dist/split-layout.d.ts.map +1 -1
  148. package/dist/split-layout.js +6 -5
  149. package/dist/split-layout.js.map +1 -1
  150. package/dist/style.css +14 -4
  151. package/dist/swipe.d.ts.map +1 -1
  152. package/dist/swipe.js +9 -4
  153. package/dist/swipe.js.map +1 -1
  154. package/dist/{tab-list-BnqISNvO.d.ts → tab-list-DLzb-m-I.d.ts} +2 -2
  155. package/dist/tab-list-DLzb-m-I.d.ts.map +1 -0
  156. package/dist/tabs.d.ts +1 -1
  157. package/dist/tabs.js +10 -4
  158. package/dist/tabs.js.map +1 -1
  159. package/dist/{tickled-controller-Cq1Va0rR.d.ts → tickled-controller-BjYCzGrU.d.ts} +2 -2
  160. package/dist/tickled-controller-BjYCzGrU.d.ts.map +1 -0
  161. package/dist/{tickled-styles-Bg3QbcrD.js → tickled-styles-fNDdqf6l.js} +1 -17
  162. package/dist/{tickled-styles-Bg3QbcrD.js.map → tickled-styles-fNDdqf6l.js.map} +1 -1
  163. package/dist/{timeline-Cv75Th2U.js → timeline-2OFtN4Ta.js} +1437 -36
  164. package/dist/timeline-2OFtN4Ta.js.map +1 -0
  165. package/dist/timeline.d.ts +1 -1
  166. package/dist/timeline.js +1 -1
  167. package/dist/{tooltip-DMxmBNky.js → tooltip-CJt3WLmN.js} +1 -1
  168. package/dist/{tooltip-DMxmBNky.js.map → tooltip-CJt3WLmN.js.map} +1 -1
  169. package/dist/{tooltip-CwGfb4lp.d.ts → tooltip-RGVIwHrZ.d.ts} +1 -1
  170. package/dist/{tooltip-CwGfb4lp.d.ts.map → tooltip-RGVIwHrZ.d.ts.map} +1 -1
  171. package/dist/{tree-CvvKbO-f.js → tree-CJWpMUvD.js} +113 -103
  172. package/dist/tree-CJWpMUvD.js.map +1 -0
  173. package/dist/{tree-component-D8Bg48tt.d.ts → tree-component-BcO26Xvt.d.ts} +65 -3
  174. package/dist/tree-component-BcO26Xvt.d.ts.map +1 -0
  175. package/dist/tree.d.ts +15 -15
  176. package/dist/tree.d.ts.map +1 -1
  177. package/dist/tree.js +1 -1
  178. package/dist/{types-BKaqnpwx.d.ts → types-DSfbtHs3.d.ts} +1 -1
  179. package/dist/types-DSfbtHs3.d.ts.map +1 -0
  180. package/dist/{xy-axis-DL0tfJDr.js → xy-axis-CK64haaK.js} +1831 -359
  181. package/dist/xy-axis-CK64haaK.js.map +1 -0
  182. package/dist/{xy-axis-D9uwpGTT.d.ts → xy-axis-McdUpcYr.d.ts} +102 -3
  183. package/dist/xy-axis-McdUpcYr.d.ts.map +1 -0
  184. package/dist/xy-pad.d.ts +1 -1
  185. package/dist/xy-pad.d.ts.map +1 -1
  186. package/dist/xy-pad.js +11 -6
  187. package/dist/xy-pad.js.map +1 -1
  188. package/docs-user/README.md +45 -0
  189. package/docs-user/ac-text.md +67 -0
  190. package/docs-user/ac-token.md +187 -0
  191. package/docs-user/button.md +355 -0
  192. package/docs-user/checkbox.md +62 -0
  193. package/docs-user/colour-picker.md +237 -0
  194. package/docs-user/crumbs.md +66 -0
  195. package/docs-user/data-display.md +98 -0
  196. package/docs-user/editable-label.md +244 -0
  197. package/docs-user/grouped-item-lister.md +424 -0
  198. package/docs-user/icons.md +191 -0
  199. package/docs-user/incr-search.md +94 -0
  200. package/docs-user/labelled-radial-input.md +204 -0
  201. package/docs-user/labelled-range-input.md +194 -0
  202. package/docs-user/led.md +40 -0
  203. package/docs-user/menu.md +319 -0
  204. package/docs-user/miller.md +453 -0
  205. package/docs-user/narrowed-text.md +72 -0
  206. package/docs-user/panel.md +250 -0
  207. package/docs-user/plots.md +361 -0
  208. package/docs-user/polar-pad.md +228 -0
  209. package/docs-user/radial-input.md +559 -0
  210. package/docs-user/range-input.md +264 -0
  211. package/docs-user/range.md +12 -0
  212. package/docs-user/selecthorizontal.md +32 -0
  213. package/docs-user/snap-container.md +132 -0
  214. package/docs-user/split-layout.md +232 -0
  215. package/docs-user/swipe.md +44 -0
  216. package/docs-user/tabs.md +217 -0
  217. package/docs-user/timeline.md +116 -0
  218. package/docs-user/transitory-label.md +120 -0
  219. package/docs-user/tree.md +592 -0
  220. package/docs-user/user-catalog.md +165 -0
  221. package/docs-user/user-theming.md +379 -0
  222. package/docs-user/util.md +122 -0
  223. package/docs-user/vertical-list.md +351 -0
  224. package/docs-user/xy-pad.md +94 -0
  225. package/package.json +19 -18
  226. package/dist/colour-C3MQIjFJ-BLXg8W3U.js.map +0 -1
  227. package/dist/colour-picker-K6zu82lm.js.map +0 -1
  228. package/dist/dist-BX0OVIm7.js +0 -1290
  229. package/dist/dist-BX0OVIm7.js.map +0 -1
  230. package/dist/dist-TAGYwaju.js.map +0 -1
  231. package/dist/hex-editor-DlfoA65y.d.ts.map +0 -1
  232. package/dist/icon-9NWTXtE1.js.map +0 -1
  233. package/dist/icon-CeTDJhC7.d.ts.map +0 -1
  234. package/dist/index--hrxPBkQ.d.ts.map +0 -1
  235. package/dist/index-CnL9krsH.d.ts.map +0 -1
  236. package/dist/index-DSlPeWLK.d.ts.map +0 -1
  237. package/dist/index-DrQjA8Gs.d.ts.map +0 -1
  238. package/dist/interaction-DG7X1rhI.js +0 -1
  239. package/dist/labelled-input-base-D-Y7agky.d.ts.map +0 -1
  240. package/dist/menu-DJJuCm-5.js.map +0 -1
  241. package/dist/menu-item-CKRfP0pN.js.map +0 -1
  242. package/dist/menu-item-ClQF2DKN.d.ts.map +0 -1
  243. package/dist/prominence--ic9uWBl.js +0 -98
  244. package/dist/prominence--ic9uWBl.js.map +0 -1
  245. package/dist/radial-input-DZA6WaFu.js.map +0 -1
  246. package/dist/radial-input-LUSBEycd.d.ts.map +0 -1
  247. package/dist/tab-list-BnqISNvO.d.ts.map +0 -1
  248. package/dist/tickled-controller-Cq1Va0rR.d.ts.map +0 -1
  249. package/dist/timeline-Cv75Th2U.js.map +0 -1
  250. package/dist/tree-CvvKbO-f.js.map +0 -1
  251. package/dist/tree-component-D8Bg48tt.d.ts.map +0 -1
  252. package/dist/types-BKaqnpwx.d.ts.map +0 -1
  253. package/dist/xy-axis-D9uwpGTT.d.ts.map +0 -1
  254. package/dist/xy-axis-DL0tfJDr.js.map +0 -1
  255. /package/bundle/{chunk-pbuEa-1d.js → chunk-D7D4PA-g.js} +0 -0
  256. /package/dist/{chunk-pbuEa-1d.js → chunk-D7D4PA-g.js} +0 -0
@@ -1,7 +1,7 @@
1
- import { n as safeCustomElement, t as __decorate } from "./decorate-D7rC1gLP.js";
1
+ import { n as safeCustomElement, t as __decorate$1 } from "./decorate-DdjvVHS-.js";
2
2
  import { LitElement, css, html, nothing } from "lit";
3
- import { property, state } from "lit/decorators.js";
4
- import "@clinth/vcanvas";
3
+ import { customElement, property, state } from "lit/decorators.js";
4
+ import { classMap } from "lit/directives/class-map.js";
5
5
  //#region src/timeline/timeline-controller.ts
6
6
  /**
7
7
  * External adapter class that can configure a timeline element
@@ -992,24 +992,1425 @@ let TimelineTrackTabElement = class TimelineTrackTabElement extends LitElement {
992
992
  this.style.cursor = ``;
993
993
  }
994
994
  };
995
- __decorate([property({ type: String })], TimelineTrackTabElement.prototype, "label", void 0);
996
- __decorate([property({
995
+ __decorate$1([property({ type: String })], TimelineTrackTabElement.prototype, "label", void 0);
996
+ __decorate$1([property({
997
997
  type: String,
998
998
  attribute: `track-key`
999
999
  })], TimelineTrackTabElement.prototype, "trackKey", void 0);
1000
- __decorate([property({
1000
+ __decorate$1([property({
1001
1001
  type: Boolean,
1002
1002
  reflect: true
1003
1003
  })], TimelineTrackTabElement.prototype, "selected", void 0);
1004
- __decorate([property({
1004
+ __decorate$1([property({
1005
1005
  type: Boolean,
1006
1006
  reflect: true
1007
1007
  })], TimelineTrackTabElement.prototype, "reorderable", void 0);
1008
- __decorate([property({
1008
+ __decorate$1([property({
1009
1009
  type: Boolean,
1010
1010
  reflect: true
1011
1011
  })], TimelineTrackTabElement.prototype, "resizable", void 0);
1012
- TimelineTrackTabElement = __decorate([safeCustomElement(`ixfx-timeline-track-tab`)], TimelineTrackTabElement);
1012
+ TimelineTrackTabElement = __decorate$1([safeCustomElement(`ixfx-timeline-track-tab`)], TimelineTrackTabElement);
1013
+ //#endregion
1014
+ //#region node_modules/.pnpm/@clinth+vcanvas@0.0.3/node_modules/@clinth/vcanvas/dist/convert.js
1015
+ /**
1016
+ * Converts viewport-relative coordinates to canvas-relative (logical) coordinates.
1017
+ *
1018
+ * This function accounts for the current scroll position and content offset to translate
1019
+ * coordinates from the visible viewport space to the logical canvas space.
1020
+ *
1021
+ * @param x - The x-coordinate in viewport space (pixels from left edge of viewport)
1022
+ * @param y - The y-coordinate in viewport space (pixels from top)
1023
+ * @param scrollLeft - The current horizontal scroll position in pixels
1024
+ * @param contentOffset - The horizontal offset applied to center content in single-tile mode
1025
+ * @param scrollTop - The current vertical scroll position in pixels
1026
+ * @param contentOffsetY - The vertical offset applied to center content in single-tile-row mode
1027
+ * @returns The point in canvas (logical) coordinate space
1028
+ *
1029
+ * @example
1030
+ * ```typescript
1031
+ * // Viewport coordinate (100, 50) with scroll at 500 and no offset
1032
+ * const canvasPoint = viewportToCanvas(100, 50, 500, 0, 0, 0);
1033
+ * // Returns: { x: 600, y: 50 }
1034
+ * ```
1035
+ */
1036
+ function viewportToCanvas(x, y, scrollLeft, contentOffset, scrollTop = 0, contentOffsetY = 0) {
1037
+ return {
1038
+ x: x + scrollLeft - contentOffset,
1039
+ y: y + scrollTop - contentOffsetY
1040
+ };
1041
+ }
1042
+ /**
1043
+ * Converts canvas-relative (logical) coordinates to viewport-relative coordinates.
1044
+ *
1045
+ * This function translates coordinates from the logical canvas space back to the
1046
+ * visible viewport space, accounting for scroll position and content offset.
1047
+ *
1048
+ * @param x - The x-coordinate in canvas (logical) space
1049
+ * @param y - The y-coordinate in canvas (logical) space
1050
+ * @param scrollLeft - The current horizontal scroll position in pixels
1051
+ * @param contentOffset - The horizontal offset applied to center content in single-tile mode
1052
+ * @param scrollTop - The current vertical scroll position in pixels
1053
+ * @param contentOffsetY - The vertical offset applied to center content in single-tile-row mode
1054
+ * @returns The point in viewport coordinate space
1055
+ *
1056
+ * @example
1057
+ * ```typescript
1058
+ * // Canvas coordinate (600, 50) with scroll at 500 and no offset
1059
+ * const viewportPoint = canvasToViewport(600, 50, 500, 0, 0, 0);
1060
+ * // Returns: { x: 100, y: 50 }
1061
+ * ```
1062
+ */
1063
+ function canvasToViewport(x, y, scrollLeft, contentOffset, scrollTop = 0, contentOffsetY = 0) {
1064
+ return {
1065
+ x: x - scrollLeft + contentOffset,
1066
+ y: y - scrollTop + contentOffsetY
1067
+ };
1068
+ }
1069
+ /**
1070
+ * Clamps a numeric value between a minimum and maximum bound.
1071
+ *
1072
+ * This is a utility function to constrain values within a valid range.
1073
+ *
1074
+ * @param value - The value to clamp
1075
+ * @param min - The minimum allowed value
1076
+ * @param max - The maximum allowed value
1077
+ * @returns The clamped value, guaranteed to be between min and max (inclusive)
1078
+ *
1079
+ * @example
1080
+ * ```typescript
1081
+ * clamp(15, 0, 10); // Returns: 10
1082
+ * clamp(-5, 0, 10); // Returns: 0
1083
+ * clamp(5, 0, 10); // Returns: 5
1084
+ * ```
1085
+ */
1086
+ function clamp(value, min, max) {
1087
+ return Math.max(min, Math.min(value, max));
1088
+ }
1089
+ //#endregion
1090
+ //#region node_modules/.pnpm/@clinth+vcanvas@0.0.3/node_modules/@clinth/vcanvas/dist/styles.js
1091
+ /**
1092
+ * CSS styles for the VCanvas component.
1093
+ *
1094
+ * These styles define the layout and positioning of:
1095
+ * - The host element (`:host`)
1096
+ * - The scroll container (`.scroll-container`)
1097
+ * - The content spacer (`.spacer`)
1098
+ * - The tile pool container (`.tile-pool`)
1099
+ * - Canvas elements
1100
+ * - The children container for slotted elements (`.children-container`)
1101
+ *
1102
+ * The styles use absolute positioning to create a virtualized scrolling experience
1103
+ * where tiles are rendered only when visible in the viewport.
1104
+ *
1105
+ * @example
1106
+ * ```typescript
1107
+ * import { vcanvasStyles } from '@clinth/vcanvas';
1108
+ *
1109
+ * // Styles are automatically applied when using the VCanvas component
1110
+ * // You typically don't need to use these directly unless extending the component
1111
+ * ```
1112
+ */
1113
+ const vcanvasStyles = css`
1114
+ :host {
1115
+ display: block;
1116
+ position: relative;
1117
+ overflow: hidden;
1118
+ }
1119
+
1120
+ .scroll-container {
1121
+ overflow-x: auto;
1122
+ overflow-y: clip;
1123
+ width: 100%;
1124
+ height: 100%;
1125
+ position: relative;
1126
+ touch-action: pan-x;
1127
+ scrollbar-width: var(--scrollbar-width, auto);
1128
+ scrollbar-color: var(--scrollbar-color, auto);
1129
+ }
1130
+
1131
+ .scroll-container.vertical {
1132
+ overflow-y: auto;
1133
+ touch-action: pan-x pan-y;
1134
+ }
1135
+
1136
+ .spacer {
1137
+ position: absolute;
1138
+ left: 0;
1139
+ top: 0;
1140
+ pointer-events: none;
1141
+ z-index: -1;
1142
+ }
1143
+
1144
+ .tile-pool {
1145
+ position: absolute;
1146
+ top: 0;
1147
+ left: 0;
1148
+ height: 100%;
1149
+ width: 100%;
1150
+ pointer-events: none;
1151
+ }
1152
+
1153
+ canvas {
1154
+ position: absolute;
1155
+ top: 0;
1156
+ pointer-events: auto;
1157
+ }
1158
+
1159
+ .children-container {
1160
+ position: absolute;
1161
+ top: 0;
1162
+ height: 100%;
1163
+ left: 0;
1164
+ }
1165
+
1166
+ @property --left-fade {
1167
+ syntax: "<length>";
1168
+ inherits: false;
1169
+ initial-value: 0;
1170
+ }
1171
+
1172
+ @property --right-fade {
1173
+ syntax: "<length>";
1174
+ inherits: false;
1175
+ initial-value: 0;
1176
+ }
1177
+
1178
+ @keyframes scrollfade {
1179
+ 0% {
1180
+ --left-fade: 0;
1181
+ }
1182
+ 10%,
1183
+ 100% {
1184
+ --left-fade: 3rem;
1185
+ }
1186
+ 0%,
1187
+ 90% {
1188
+ --right-fade: 3rem;
1189
+ }
1190
+ 100% {
1191
+ --right-fade: 0;
1192
+ }
1193
+ }
1194
+
1195
+ .scrollable {
1196
+ mask: linear-gradient(to right, #0000, #ffff var(--left-fade) calc(100% - var(--right-fade)), #0000);
1197
+ animation: scrollfade;
1198
+ animation-timeline: --scrollfade;
1199
+ scroll-timeline: --scrollfade x;
1200
+ }
1201
+
1202
+ `;
1203
+ //#endregion
1204
+ //#region node_modules/.pnpm/@clinth+vcanvas@0.0.3/node_modules/@clinth/vcanvas/dist/tile-calculator.js
1205
+ /**
1206
+ * Calculates the width of a tile based on viewport width and multiplier.
1207
+ *
1208
+ * @param viewportWidth - The width of the viewport in pixels
1209
+ * @param multiplier - The multiplier to apply (typically 0.25 to 1.0)
1210
+ * @returns The calculated tile width in pixels
1211
+ *
1212
+ * @example
1213
+ * ```typescript
1214
+ * getTileWidth(1000, 0.5); // Returns: 500
1215
+ * ```
1216
+ */
1217
+ function getTileWidth(viewportWidth, multiplier) {
1218
+ return viewportWidth * multiplier;
1219
+ }
1220
+ /**
1221
+ * Determines if the content fits within a single tile (single-tile mode).
1222
+ *
1223
+ * In single-tile mode, the content is centered in the viewport rather than
1224
+ * being scrollable. This also returns true when viewport width is 0 as a
1225
+ * defensive edge case.
1226
+ *
1227
+ * @param logicalWidth - The total logical width of the content
1228
+ * @param viewportWidth - The width of the viewport in pixels
1229
+ * @returns `true` if content fits in single tile, `false` otherwise
1230
+ *
1231
+ * @example
1232
+ * ```typescript
1233
+ * isSingleTileMode(500, 1000); // Returns: true
1234
+ * isSingleTileMode(1500, 1000); // Returns: false
1235
+ * ```
1236
+ */
1237
+ function isSingleTileMode(logicalWidth, viewportWidth) {
1238
+ if (viewportWidth === 0) return true;
1239
+ return logicalWidth <= viewportWidth;
1240
+ }
1241
+ /**
1242
+ * Calculates the horizontal offset to center content in single-tile mode.
1243
+ *
1244
+ * In multi-tile mode (when content exceeds viewport), this returns 0.
1245
+ * In single-tile mode, this calculates the offset needed to center the content.
1246
+ *
1247
+ * @param logicalWidth - The total logical width of the content
1248
+ * @param viewportWidth - The width of the viewport in pixels
1249
+ * @returns The horizontal offset in pixels, or 0 in multi-tile mode
1250
+ *
1251
+ * @example
1252
+ * ```typescript
1253
+ * // Content width 500 in viewport 1000: center it with 250px offset
1254
+ * getContentOffset(500, 1000); // Returns: 250
1255
+ *
1256
+ * // Content width 2000 in viewport 1000: no offset needed
1257
+ * getContentOffset(2000, 1000); // Returns: 0
1258
+ * ```
1259
+ */
1260
+ function getContentOffset(logicalWidth, viewportWidth) {
1261
+ if (logicalWidth < 0 || viewportWidth === 0) return 0;
1262
+ return isSingleTileMode(logicalWidth, viewportWidth) ? (viewportWidth - logicalWidth) / 2 : 0;
1263
+ }
1264
+ /**
1265
+ * Calculates the clipped width of a tile at a specific logical position.
1266
+ *
1267
+ * This accounts for:
1268
+ * - Single-tile mode (returns logicalWidth)
1269
+ * - Content boundaries (clips tiles extending beyond content)
1270
+ * - Overscroll area (if enabled)
1271
+ *
1272
+ * @param logicalX - The x-coordinate of the tile's left edge in logical space
1273
+ * @param tileWidth - The full width of the tile
1274
+ * @param logicalWidth - The total logical width of the content
1275
+ * @param viewportWidth - The width of the viewport in pixels
1276
+ * @param overscrollMultiplier - Multiplier for overscroll padding
1277
+ * @returns The clipped width of the tile (may be 0 if tile is outside valid area)
1278
+ *
1279
+ * @example
1280
+ * ```typescript
1281
+ * // Standard tile within bounds
1282
+ * getTileClippedWidth(0, 500, 2000, 1000, 1); // Returns: 500
1283
+ *
1284
+ * // Tile at end of content that needs clipping
1285
+ * getTileClippedWidth(2950, 500, 2000, 1000, 1); // Returns: 50
1286
+ * ```
1287
+ */
1288
+ function getTileClippedWidth(logicalX, tileWidth, logicalWidth, viewportWidth, overscrollMultiplier) {
1289
+ if (tileWidth === 0) return 0;
1290
+ if (logicalX < 0) return 0;
1291
+ const maxLogicalX = logicalWidth + viewportWidth * overscrollMultiplier;
1292
+ if (logicalX >= maxLogicalX) return 0;
1293
+ if (isSingleTileMode(logicalWidth, viewportWidth)) return logicalWidth;
1294
+ if (logicalX + tileWidth > maxLogicalX) return Math.max(0, maxLogicalX - logicalX);
1295
+ return tileWidth;
1296
+ }
1297
+ /**
1298
+ * Calculates the range of tiles needed to render the current viewport.
1299
+ *
1300
+ * This determines which tiles are visible based on scroll position and
1301
+ * includes buffer tiles for smooth scrolling. Respects maxPoolSize limits.
1302
+ *
1303
+ * @param scrollLeft - The current horizontal scroll position in pixels
1304
+ * @param viewportWidth - The width of the viewport in pixels
1305
+ * @param logicalWidth - The total logical width of the content
1306
+ * @param options - Configuration options for tile calculation
1307
+ * @returns Information about the tile range including indices and positions
1308
+ *
1309
+ * @example
1310
+ * ```typescript
1311
+ * const options = {
1312
+ * tileWidthMultiplier: 0.5,
1313
+ * tilesBuffer: 2,
1314
+ * maxPoolSize: 5,
1315
+ * // ... other options
1316
+ * };
1317
+ * const result = calculateTileRange(600, 1000, 10000, options);
1318
+ * // Returns: { firstTile: 1, lastTile: 3, tiles: [...] }
1319
+ * ```
1320
+ */
1321
+ function calculateTileRange(scrollLeft, viewportWidth, logicalWidth, options) {
1322
+ if (logicalWidth === 0) return {
1323
+ firstTile: 0,
1324
+ lastTile: 0,
1325
+ tiles: []
1326
+ };
1327
+ const tileWidth = getTileWidth(viewportWidth, options.tileWidthMultiplier);
1328
+ if (isSingleTileMode(logicalWidth, viewportWidth)) return {
1329
+ firstTile: 0,
1330
+ lastTile: 0,
1331
+ tiles: [{
1332
+ index: 0,
1333
+ logicalX: 0
1334
+ }]
1335
+ };
1336
+ const firstTile = Math.floor(scrollLeft / tileWidth);
1337
+ const lastTile = Math.floor((scrollLeft + viewportWidth - 1) / tileWidth);
1338
+ const maxTileIndex = Math.max(0, Math.ceil(logicalWidth / tileWidth) - 1);
1339
+ const tiles = [];
1340
+ const startI = firstTile;
1341
+ const endI = Math.min(lastTile + options.tilesBuffer, maxTileIndex);
1342
+ for (let i = startI; i <= endI; i++) {
1343
+ const logicalX = i * tileWidth;
1344
+ if (logicalX >= logicalWidth) continue;
1345
+ tiles.push({
1346
+ index: i,
1347
+ logicalX
1348
+ });
1349
+ if (tiles.length >= options.maxPoolSize) break;
1350
+ }
1351
+ return {
1352
+ firstTile: Math.max(0, firstTile),
1353
+ lastTile: tiles.length > 0 ? tiles[tiles.length - 1].index : 0,
1354
+ tiles
1355
+ };
1356
+ }
1357
+ /**
1358
+ * Calculates the height of a tile based on viewport height and multiplier.
1359
+ */
1360
+ function getTileHeight(viewportHeight, multiplier) {
1361
+ return viewportHeight * multiplier;
1362
+ }
1363
+ /**
1364
+ * Determines if content fits within a single tile row (no vertical virtualization needed).
1365
+ * Returns true when effectiveLogicalHeight fits within the viewport.
1366
+ */
1367
+ function isSingleTileModeY(effectiveLogicalHeight, viewportHeight) {
1368
+ if (viewportHeight === 0) return true;
1369
+ return effectiveLogicalHeight <= viewportHeight;
1370
+ }
1371
+ /**
1372
+ * Calculates the vertical offset to center content in single-tile-row mode.
1373
+ */
1374
+ function getContentOffsetY(effectiveLogicalHeight, viewportHeight) {
1375
+ if (effectiveLogicalHeight < 0 || viewportHeight === 0) return 0;
1376
+ return isSingleTileModeY(effectiveLogicalHeight, viewportHeight) ? (viewportHeight - effectiveLogicalHeight) / 2 : 0;
1377
+ }
1378
+ /**
1379
+ * Calculates the clipped height of a tile at a specific logical Y position.
1380
+ */
1381
+ function getTileClippedHeight(logicalY, tileHeight, effectiveLogicalHeight, viewportHeight, overscrollMultiplier) {
1382
+ if (tileHeight === 0) return 0;
1383
+ if (logicalY < 0) return 0;
1384
+ const maxLogicalY = effectiveLogicalHeight + viewportHeight * overscrollMultiplier;
1385
+ if (logicalY >= maxLogicalY) return 0;
1386
+ if (isSingleTileModeY(effectiveLogicalHeight, viewportHeight)) return effectiveLogicalHeight;
1387
+ if (logicalY + tileHeight > maxLogicalY) return Math.max(0, maxLogicalY - logicalY);
1388
+ return tileHeight;
1389
+ }
1390
+ /**
1391
+ * Calculates the range of tile rows needed to render the current viewport vertically.
1392
+ *
1393
+ * @param scrollTop - The current vertical scroll position in pixels
1394
+ * @param viewportHeight - The height of the viewport in pixels
1395
+ * @param effectiveLogicalHeight - The effective total logical height (logicalHeight || viewportHeight)
1396
+ * @param options - Configuration options for tile calculation
1397
+ */
1398
+ function calculateTileRowRange(scrollTop, viewportHeight, effectiveLogicalHeight, options) {
1399
+ if (effectiveLogicalHeight === 0) return {
1400
+ firstRow: 0,
1401
+ lastRow: 0,
1402
+ rows: []
1403
+ };
1404
+ const tileHeight = getTileHeight(viewportHeight, options.tileHeightMultiplier);
1405
+ if (isSingleTileModeY(effectiveLogicalHeight, viewportHeight)) return {
1406
+ firstRow: 0,
1407
+ lastRow: 0,
1408
+ rows: [{
1409
+ row: 0,
1410
+ logicalY: 0
1411
+ }]
1412
+ };
1413
+ const firstRow = Math.floor(scrollTop / tileHeight);
1414
+ const lastRow = Math.floor((scrollTop + viewportHeight - 1) / tileHeight);
1415
+ const maxRowIndex = Math.max(0, Math.ceil(effectiveLogicalHeight / tileHeight) - 1);
1416
+ const rows = [];
1417
+ const endI = Math.min(lastRow + options.tilesBuffer, maxRowIndex);
1418
+ for (let i = firstRow; i <= endI; i++) {
1419
+ const logicalY = i * tileHeight;
1420
+ if (logicalY >= effectiveLogicalHeight) continue;
1421
+ rows.push({
1422
+ row: i,
1423
+ logicalY
1424
+ });
1425
+ if (rows.length >= options.maxPoolSize) break;
1426
+ }
1427
+ return {
1428
+ firstRow: Math.max(0, firstRow),
1429
+ lastRow: rows.length > 0 ? rows[rows.length - 1].row : 0,
1430
+ rows
1431
+ };
1432
+ }
1433
+ //#endregion
1434
+ //#region node_modules/.pnpm/@clinth+vcanvas@0.0.3/node_modules/@clinth/vcanvas/dist/util/debouncer.js
1435
+ /**
1436
+ * Creates a debounced version of a function that delays execution until after
1437
+ * a specified timeout has elapsed since the last call.
1438
+ *
1439
+ * Debouncing is useful for handling rapidly firing events (like scroll or resize)
1440
+ * by ensuring the function only executes once after the event stream settles.
1441
+ *
1442
+ * @template T - The type of the function to debounce
1443
+ * @param fn - The function to debounce
1444
+ * @param timeoutMs - The delay in milliseconds before executing the function
1445
+ * @returns A debounced version of the function with a `cancel` method
1446
+ *
1447
+ * @example
1448
+ * ```typescript
1449
+ * const debouncedLog = debouncer((msg: string) => {
1450
+ * console.log(msg);
1451
+ * }, 100);
1452
+ *
1453
+ * debouncedLog('Hello'); // Will log after 100ms
1454
+ * debouncedLog('World'); // Resets timer, will log 'World' after 100ms
1455
+ *
1456
+ * // Cancel pending execution
1457
+ * debouncedLog.cancel();
1458
+ * ```
1459
+ */
1460
+ function debouncer(fn, timeoutMs) {
1461
+ let timer = null;
1462
+ const debounced = (...args) => {
1463
+ if (timer) clearTimeout(timer);
1464
+ timer = setTimeout(() => {
1465
+ fn(...args);
1466
+ }, timeoutMs);
1467
+ };
1468
+ debounced.cancel = () => {
1469
+ if (timer) {
1470
+ clearTimeout(timer);
1471
+ timer = null;
1472
+ }
1473
+ };
1474
+ return debounced;
1475
+ }
1476
+ //#endregion
1477
+ //#region node_modules/.pnpm/@clinth+vcanvas@0.0.3/node_modules/@clinth/vcanvas/dist/util/draw.js
1478
+ /** Color palette for debug tile visualization */
1479
+ const TILE_COLORS = [
1480
+ `#FF6B6B`,
1481
+ `#4ECDC4`,
1482
+ `#45B7D1`,
1483
+ `#96CEB4`,
1484
+ `#FFEAA7`,
1485
+ `#DDA0DD`,
1486
+ `#98D8C8`,
1487
+ `#F7DC6F`,
1488
+ `#BB8FCE`,
1489
+ `#85C1E9`
1490
+ ];
1491
+ /**
1492
+ * Gets a color for a tile based on its index.
1493
+ * Colors cycle through the TILE_COLORS palette.
1494
+ *
1495
+ * @param index - The tile index
1496
+ * @returns A hex color string
1497
+ */
1498
+ function getTileColor(index) {
1499
+ return TILE_COLORS[index % TILE_COLORS.length];
1500
+ }
1501
+ /**
1502
+ * Draws debug visualization graphics on a tile canvas.
1503
+ *
1504
+ * This function renders diagnostic information useful for debugging tile
1505
+ * boundaries, positions, and sizing. It draws:
1506
+ * - A colored background
1507
+ * - Diagonal cross lines
1508
+ * - Center crosshair
1509
+ * - Border rectangle
1510
+ * - Text information (tile index, position, size, total width)
1511
+ *
1512
+ * @param tileIndex - The index of the tile being drawn
1513
+ * @param tilesTotal - The total number of tiles in the pool
1514
+ * @param widthTotal - The total logical width of the virtual canvas
1515
+ * @param ctx - The canvas 2D rendering context
1516
+ * @param region - The rectangular region of the tile in logical coordinates
1517
+ *
1518
+ * @example
1519
+ * ```typescript
1520
+ * // Enable debug drawing
1521
+ * vcanvas.debugDraw = true;
1522
+ *
1523
+ * // The debug graphics will be drawn on each tile automatically
1524
+ * ```
1525
+ */
1526
+ function drawDebugGraphic(tileIndex, tilesTotal, widthTotal, ctx, region) {
1527
+ ctx.fillStyle = `whitesmoke`;
1528
+ ctx.fillRect(region.x, region.y, region.width, region.height);
1529
+ ctx.fillStyle = getTileColor(tileIndex);
1530
+ ctx.strokeStyle = getTileColor(tileIndex);
1531
+ ctx.lineWidth = 2;
1532
+ ctx.beginPath();
1533
+ ctx.moveTo(region.x, region.y);
1534
+ ctx.lineTo(region.x + region.width, region.y + region.height);
1535
+ ctx.moveTo(region.x + region.width, region.y);
1536
+ ctx.lineTo(region.x, region.y + region.height);
1537
+ ctx.stroke();
1538
+ ctx.font = `24pt system-ui`;
1539
+ ctx.textBaseline = `top`;
1540
+ ctx.fillText(`#${tileIndex}/${tilesTotal}`, region.x + 20, region.y + 20);
1541
+ ctx.font = `18pt system-ui`;
1542
+ ctx.fillText(`Pos: ${Math.round(region.x)},${Math.round(region.y)}`, region.x + 20, region.y + 60);
1543
+ ctx.fillText(`Size: ${Math.round(region.width)}x${Math.round(region.height)}`, region.x + 20, region.y + 90);
1544
+ ctx.fillText(`VCanvas width: ${Math.round(widthTotal)}`, region.x + 20, region.y + 120);
1545
+ const centerX = region.x + region.width / 2;
1546
+ const centerY = region.y + region.height / 2;
1547
+ ctx.beginPath();
1548
+ ctx.moveTo(region.x, centerY);
1549
+ ctx.lineTo(region.x + region.width, centerY);
1550
+ ctx.moveTo(centerX, region.y);
1551
+ ctx.lineTo(centerX, region.y + region.height);
1552
+ ctx.stroke();
1553
+ ctx.strokeRect(region.x, region.y, region.width, region.height);
1554
+ }
1555
+ //#endregion
1556
+ //#region node_modules/.pnpm/@clinth+vcanvas@0.0.3/node_modules/@clinth/vcanvas/dist/vcanvas.js
1557
+ var __decorate = function(decorators, target, key, desc) {
1558
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1559
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1560
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1561
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1562
+ };
1563
+ const tileKey = (col, row) => `${col},${row}`;
1564
+ const DEFAULT_TILE_WIDTH_MULTIPLIER = .5;
1565
+ const DEFAULT_TILE_HEIGHT_MULTIPLIER = .5;
1566
+ const DEFAULT_TILES_BUFFER = 2;
1567
+ const DEFAULT_OVERLAP_PERCENT = .01;
1568
+ const DEFAULT_OVERSCROLL_PAD_MULTIPLIER = 1;
1569
+ const DEFAULT_MAX_POOL_SIZE = 3;
1570
+ const DEFAULT_SCROLL_DEBOUNCE_MS = 100;
1571
+ let VCanvas = class VCanvas extends LitElement {
1572
+ _emitScrollEndEvent(userGesture) {
1573
+ const event = new CustomEvent(`scrollend-virtual`, {
1574
+ detail: {
1575
+ logicalScrollLeft: this._scrollLeft,
1576
+ logicalScrollTop: this._scrollTop,
1577
+ userGesture,
1578
+ containerScrollLeft: this._scrollContainer?.scrollLeft ?? 0,
1579
+ containerScrollTop: this._scrollContainer?.scrollTop ?? 0
1580
+ },
1581
+ bubbles: true,
1582
+ composed: true
1583
+ });
1584
+ this.dispatchEvent(event);
1585
+ }
1586
+ constructor() {
1587
+ super();
1588
+ /**
1589
+ * Enables detailed console logging of tile operations for debugging.
1590
+ *
1591
+ * When `true`, the component logs information about tile creation,
1592
+ * positioning, and rendering to the console. Useful for debugging
1593
+ * tile-related issues during development.
1594
+ *
1595
+ * @default false
1596
+ */
1597
+ this.debugTrace = false;
1598
+ this.debugDraw = false;
1599
+ /**
1600
+ * Enables visual hinting for scroll position at container edges.
1601
+ *
1602
+ * When `true`, applies the 'scrollable' class to the scroll container which
1603
+ * adds CSS scroll-driven fade animations at the left and right edges,
1604
+ * providing visual feedback about scroll availability.
1605
+ *
1606
+ * @default false
1607
+ */
1608
+ this.hinting = false;
1609
+ this._viewportWidth = 0;
1610
+ this._viewportHeight = 0;
1611
+ this._scrollLeft = 0;
1612
+ this._scrollTop = 0;
1613
+ this._logicalHeight = 0;
1614
+ this._tilePool = /* @__PURE__ */ new Map();
1615
+ this._visibleTiles = /* @__PURE__ */ new Set();
1616
+ this._pendingDraws = /* @__PURE__ */ new Map();
1617
+ this._scrollContainer = null;
1618
+ this._spacer = null;
1619
+ this._tilePoolEl = null;
1620
+ this._childrenContainerEl = null;
1621
+ this._resizeObserver = null;
1622
+ this._positionedChildren = /* @__PURE__ */ new Map();
1623
+ this._boundOnScroll = this._onScroll.bind(this);
1624
+ this._renderingOptions = {
1625
+ tileWidthMultiplier: DEFAULT_TILE_WIDTH_MULTIPLIER,
1626
+ tileHeightMultiplier: DEFAULT_TILE_HEIGHT_MULTIPLIER,
1627
+ tilesBuffer: DEFAULT_TILES_BUFFER,
1628
+ overlapPercent: DEFAULT_OVERLAP_PERCENT,
1629
+ overscrollPadMultiplier: DEFAULT_OVERSCROLL_PAD_MULTIPLIER,
1630
+ maxPoolSize: DEFAULT_MAX_POOL_SIZE,
1631
+ scrollDebounceMs: DEFAULT_SCROLL_DEBOUNCE_MS
1632
+ };
1633
+ this._dpr = window.devicePixelRatio || 1;
1634
+ this._logicalWidth = 0;
1635
+ this.logicalWidth = 0;
1636
+ this.logicalHeight = 0;
1637
+ this.drawCallback = null;
1638
+ this._scrollDebounce = debouncer(() => {
1639
+ this._requestRedrawAll(`scrollDebounce`);
1640
+ }, this._renderingOptions.scrollDebounceMs);
1641
+ this._scrollEndDebounce = debouncer(() => {
1642
+ this._emitScrollEndEvent(true);
1643
+ }, this._renderingOptions.scrollDebounceMs);
1644
+ this._resizeDebounce = debouncer(() => {
1645
+ this._onResize();
1646
+ }, this._renderingOptions.scrollDebounceMs);
1647
+ }
1648
+ static {
1649
+ this.styles = vcanvasStyles;
1650
+ }
1651
+ connectedCallback() {
1652
+ super.connectedCallback();
1653
+ this._setupResizeObserver();
1654
+ }
1655
+ disconnectedCallback() {
1656
+ super.disconnectedCallback();
1657
+ this._resizeObserver?.disconnect();
1658
+ this._resizeObserver = null;
1659
+ this._scrollEndDebounce.cancel();
1660
+ this._pendingDraws.forEach((ctrl) => ctrl.abort());
1661
+ this._tilePool.clear();
1662
+ this._scrollContainer?.removeEventListener(`scroll`, this._boundOnScroll);
1663
+ }
1664
+ firstUpdated() {
1665
+ const scrollContainer = this.shadowRoot?.querySelector(`.scroll-container`);
1666
+ this._scrollContainer = scrollContainer;
1667
+ this._spacer = this.shadowRoot?.querySelector(`.spacer`);
1668
+ this._tilePoolEl = this.shadowRoot?.querySelector(`.tile-pool`);
1669
+ this._childrenContainerEl = this.shadowRoot?.querySelector(`.children-container`);
1670
+ if (this._scrollContainer) this._scrollContainer.addEventListener(`scroll`, this._boundOnScroll);
1671
+ requestAnimationFrame(() => {
1672
+ this._updateViewportDimensions();
1673
+ this._updateSpacer();
1674
+ this._updateScrollContainerClass();
1675
+ this._recalculateTiles(`firstUpdated cb`);
1676
+ this._positionChildren(`firstUpdated cb`);
1677
+ });
1678
+ }
1679
+ /**
1680
+ * The total logical width of the virtual canvas content.
1681
+ *
1682
+ * This represents the full width of the scrollable content in logical units.
1683
+ * Setting this property triggers recalculation of tiles and updates the spacer
1684
+ * element that enables native scroll behavior.
1685
+ *
1686
+ * @example
1687
+ * ```typescript
1688
+ * // Create a virtual canvas 10,000 logical units wide
1689
+ * vcanvas.logicalWidth = 10000;
1690
+ * ```
1691
+ */
1692
+ set logicalWidth(value) {
1693
+ this._logicalWidth = value;
1694
+ this._updateSpacer();
1695
+ this._updateScrollContainerClass();
1696
+ if (this.hasUpdated) {
1697
+ this._updateTileWidths(`set logicalWidth`);
1698
+ this._recalculateTiles(`set logicalWidth`);
1699
+ }
1700
+ }
1701
+ get logicalWidth() {
1702
+ return this._logicalWidth;
1703
+ }
1704
+ /**
1705
+ * The total logical height of the virtual canvas content.
1706
+ *
1707
+ * When set to 0 (default), the component uses the actual viewport height,
1708
+ * meaning no vertical virtualization is applied. Set to a value greater than
1709
+ * the viewport height to enable vertical scrolling and tile-based virtualization.
1710
+ *
1711
+ * @example
1712
+ * ```typescript
1713
+ * // Create a canvas virtualized in both axes
1714
+ * vcanvas.logicalWidth = 10000;
1715
+ * vcanvas.logicalHeight = 5000;
1716
+ * ```
1717
+ */
1718
+ set logicalHeight(value) {
1719
+ this._logicalHeight = value;
1720
+ this._updateSpacer();
1721
+ this._updateScrollContainerClass();
1722
+ if (this.hasUpdated) this._recalculateTiles(`set logicalHeight`);
1723
+ }
1724
+ get logicalHeight() {
1725
+ return this._logicalHeight;
1726
+ }
1727
+ /** Effective logical width: uses viewport width when logicalWidth is 0 (no horizontal virtualization). */
1728
+ get effectiveLogicalWidth() {
1729
+ return this._logicalWidth || this._viewportWidth;
1730
+ }
1731
+ /** Effective logical height: uses viewport height when logicalHeight is 0 (no vertical virtualization). */
1732
+ get effectiveLogicalHeight() {
1733
+ return this._logicalHeight || this._viewportHeight;
1734
+ }
1735
+ _setupResizeObserver() {
1736
+ this._resizeObserver = new ResizeObserver(() => {
1737
+ this._resizeDebounce();
1738
+ });
1739
+ this._resizeObserver.observe(this);
1740
+ }
1741
+ _onResize() {
1742
+ const oldWidth = this._viewportWidth;
1743
+ const oldHeight = this._viewportHeight;
1744
+ this._updateViewportDimensions();
1745
+ const widthChanged = oldWidth !== this._viewportWidth;
1746
+ const heightChanged = oldHeight !== this._viewportHeight;
1747
+ if (widthChanged || heightChanged) {
1748
+ this._updateSpacer();
1749
+ this._updateScrollContainerClass();
1750
+ this._recalculateTiles(`onResize`);
1751
+ }
1752
+ }
1753
+ _updateTileWidths(reason) {
1754
+ const tileWidth = this._getTileWidth();
1755
+ for (const tile of this._tilePool.values()) {
1756
+ const clippedWidth = this._getTileClippedWidth(tile.logicalX, tileWidth);
1757
+ tile.canvas.width = clippedWidth * this._dpr;
1758
+ tile.canvas.style.width = `${clippedWidth}px`;
1759
+ tile.ctx.scale(this._dpr, this._dpr);
1760
+ }
1761
+ this._requestRedrawAll(`updateTileWidths (${reason})`);
1762
+ }
1763
+ _updateViewportDimensions() {
1764
+ const rect = this.getBoundingClientRect();
1765
+ this._viewportWidth = rect.width;
1766
+ this._viewportHeight = rect.height;
1767
+ }
1768
+ _onScroll() {
1769
+ if (!this._scrollContainer) return;
1770
+ const newScrollLeft = this._scrollContainer.scrollLeft;
1771
+ const newScrollTop = this._scrollContainer.scrollTop;
1772
+ if (newScrollLeft === this._scrollLeft && newScrollTop === this._scrollTop) return;
1773
+ this._scrollLeft = newScrollLeft;
1774
+ this._scrollTop = newScrollTop;
1775
+ this._recalculateTiles(`onScroll`);
1776
+ this._emitScrollEvent(true);
1777
+ this._scrollDebounce();
1778
+ this._scrollEndDebounce();
1779
+ }
1780
+ _updateSpacer() {
1781
+ if (!this._spacer) return;
1782
+ const noHorizontalScroll = isSingleTileMode(this.effectiveLogicalWidth, this._viewportWidth);
1783
+ const noVerticalScroll = isSingleTileModeY(this.effectiveLogicalHeight, this._viewportHeight);
1784
+ if (noHorizontalScroll && noVerticalScroll) this._spacer.style.display = `none`;
1785
+ else {
1786
+ this._spacer.style.display = `block`;
1787
+ this._spacer.style.width = `${this.effectiveLogicalWidth}px`;
1788
+ this._spacer.style.height = `${this.effectiveLogicalHeight}px`;
1789
+ }
1790
+ if (this._childrenContainerEl) {
1791
+ this._childrenContainerEl.style.width = `${this.effectiveLogicalWidth}px`;
1792
+ this._childrenContainerEl.style.height = `${this.effectiveLogicalHeight}px`;
1793
+ }
1794
+ }
1795
+ _updateScrollContainerClass() {
1796
+ if (!this._scrollContainer) return;
1797
+ const verticalScroll = this.effectiveLogicalHeight > this._viewportHeight;
1798
+ this._scrollContainer.classList.toggle(`vertical`, verticalScroll);
1799
+ }
1800
+ _getTileWidth() {
1801
+ return getTileWidth(this._viewportWidth, this._renderingOptions.tileWidthMultiplier);
1802
+ }
1803
+ _getTileHeight() {
1804
+ return getTileHeight(this._viewportHeight, this._renderingOptions.tileHeightMultiplier);
1805
+ }
1806
+ _getTileClippedWidth(logicalX, tileWidth) {
1807
+ return getTileClippedWidth(logicalX, tileWidth, this.effectiveLogicalWidth, this._viewportWidth, this._renderingOptions.overscrollPadMultiplier);
1808
+ }
1809
+ _getTileClippedHeight(logicalY, tileHeight) {
1810
+ return getTileClippedHeight(logicalY, tileHeight, this.effectiveLogicalHeight, this._viewportHeight, this._renderingOptions.overscrollPadMultiplier);
1811
+ }
1812
+ _calculateTileRange() {
1813
+ return calculateTileRange(this._scrollLeft, this._viewportWidth, this.effectiveLogicalWidth, this._renderingOptions);
1814
+ }
1815
+ _calculateTileRowRange() {
1816
+ return calculateTileRowRange(this._scrollTop, this._viewportHeight, this.effectiveLogicalHeight, this._renderingOptions);
1817
+ }
1818
+ _recalculateTiles(reason) {
1819
+ this._updateViewportDimensions();
1820
+ const { tiles } = this._calculateTileRange();
1821
+ const { rows } = this._calculateTileRowRange();
1822
+ const neededKeys = /* @__PURE__ */ new Set();
1823
+ for (const { index: col, logicalX } of tiles) for (const { row, logicalY } of rows) {
1824
+ neededKeys.add(tileKey(col, row));
1825
+ if (!this._tilePool.has(tileKey(col, row))) this._tilePool.set(tileKey(col, row), this._createTile(col, row, logicalX, logicalY));
1826
+ }
1827
+ const maxPoolSize = this._renderingOptions.maxPoolSize * Math.max(1, rows.length);
1828
+ const reuseCandidates = [...this._tilePool.keys()].filter((k) => !neededKeys.has(k));
1829
+ const missingKeys = [...neededKeys].filter((k) => !this._tilePool.has(k));
1830
+ const tilesToReuse = Math.min(reuseCandidates.length, missingKeys.length);
1831
+ for (let i = 0; i < tilesToReuse; i++) {
1832
+ const oldKey = reuseCandidates[i];
1833
+ const newKey = missingKeys[i];
1834
+ const tile = this._tilePool.get(oldKey);
1835
+ if (tile && newKey) {
1836
+ const [colStr, rowStr] = newKey.split(`,`);
1837
+ const col = Number(colStr);
1838
+ const row = Number(rowStr);
1839
+ this._tilePool.delete(oldKey);
1840
+ tile.col = col;
1841
+ tile.row = row;
1842
+ tile.logicalX = col * this._getTileWidth();
1843
+ tile.logicalY = row * this._getTileHeight();
1844
+ tile.visible = false;
1845
+ tile.canvas.dataset.tileKey = newKey;
1846
+ this._tilePool.set(newKey, tile);
1847
+ }
1848
+ }
1849
+ const keysAfterReuse = [...this._tilePool.keys()];
1850
+ if (keysAfterReuse.length > maxPoolSize) {
1851
+ const excessCount = keysAfterReuse.length - maxPoolSize;
1852
+ keysAfterReuse.filter((k) => !neededKeys.has(k)).slice(0, excessCount).forEach((k) => {
1853
+ const tile = this._tilePool.get(k);
1854
+ if (tile) {
1855
+ this._tilePool.delete(k);
1856
+ tile.canvas.remove();
1857
+ }
1858
+ });
1859
+ }
1860
+ this._updateTiles(`recalculateTiles (${reason})`);
1861
+ }
1862
+ _createTile(col, row, logicalX, logicalY) {
1863
+ const canvas = document.createElement(`canvas`);
1864
+ const key = tileKey(col, row);
1865
+ canvas.dataset.tileKey = key;
1866
+ const tileWidth = this._getTileWidth();
1867
+ const tileHeight = this._getTileHeight();
1868
+ const clippedWidth = this._getTileClippedWidth(logicalX, tileWidth);
1869
+ const clippedHeight = this._getTileClippedHeight(logicalY, tileHeight);
1870
+ const ctx = canvas.getContext(`2d`);
1871
+ if (!ctx) throw new Error(`Could not get canvas context`);
1872
+ if (this._viewportHeight === 0) this.#debug(`Warning: Creating tile ${key} with viewport height 0. The VCanvas component may not be properly sized or visible.`);
1873
+ canvas.width = clippedWidth * this._dpr;
1874
+ canvas.height = clippedHeight * this._dpr;
1875
+ canvas.style.width = `${clippedWidth}px`;
1876
+ canvas.style.height = `${clippedHeight}px`;
1877
+ ctx.scale(this._dpr, this._dpr);
1878
+ this.#debug(`Created tile ${key} at logicalX ${logicalX} logicalY ${logicalY}`);
1879
+ return {
1880
+ canvas,
1881
+ ctx,
1882
+ logicalX,
1883
+ logicalY,
1884
+ col,
1885
+ row,
1886
+ visible: false,
1887
+ isInDOM: false
1888
+ };
1889
+ }
1890
+ _updateTiles(reason) {
1891
+ const { tiles, firstTile, lastTile } = this._calculateTileRange();
1892
+ const { rows, firstRow, lastRow } = this._calculateTileRowRange();
1893
+ const tileWidth = this._getTileWidth();
1894
+ const tileHeight = this._getTileHeight();
1895
+ const isSingleTileX = isSingleTileMode(this.effectiveLogicalWidth, this._viewportWidth);
1896
+ const isSingleTileY = isSingleTileModeY(this.effectiveLogicalHeight, this._viewportHeight);
1897
+ const contentOffsetX = getContentOffset(this.effectiveLogicalWidth, this._viewportWidth);
1898
+ const contentOffsetY = getContentOffsetY(this.effectiveLogicalHeight, this._viewportHeight);
1899
+ const tilesBuffer = this._renderingOptions.tilesBuffer;
1900
+ const tilesRedrawn = [];
1901
+ for (const tile of this._tilePool.values()) tile.visible = false;
1902
+ this._visibleTiles.clear();
1903
+ for (const [key, tile] of this._tilePool) {
1904
+ const colVisible = isSingleTileX ? tile.col === 0 : tile.col >= firstTile - tilesBuffer && tile.col <= lastTile + tilesBuffer;
1905
+ const rowVisible = isSingleTileY ? tile.row === 0 : tile.row >= firstRow - tilesBuffer && tile.row <= lastRow + tilesBuffer;
1906
+ if (colVisible && rowVisible) {
1907
+ this._visibleTiles.add(key);
1908
+ const newLogicalX = tile.col * tileWidth;
1909
+ const newLogicalY = tile.row * tileHeight;
1910
+ const visualX = isSingleTileX ? contentOffsetX : newLogicalX;
1911
+ const visualY = isSingleTileY ? contentOffsetY : newLogicalY;
1912
+ const clippedWidth = this._getTileClippedWidth(newLogicalX, tileWidth);
1913
+ const clippedHeight = this._getTileClippedHeight(newLogicalY, tileHeight);
1914
+ if (!tile.visible || tile.logicalX !== newLogicalX || tile.logicalY !== newLogicalY || tile.canvas.style.left !== `${visualX}px` || tile.canvas.style.top !== `${visualY}px` || tile.canvas.width !== clippedWidth * this._dpr || tile.canvas.height !== clippedHeight * this._dpr) {
1915
+ tile.logicalX = newLogicalX;
1916
+ tile.logicalY = newLogicalY;
1917
+ tile.canvas.width = clippedWidth * this._dpr;
1918
+ tile.canvas.style.width = `${clippedWidth}px`;
1919
+ tile.canvas.height = clippedHeight * this._dpr;
1920
+ tile.canvas.style.height = `${clippedHeight}px`;
1921
+ tile.canvas.style.left = `${visualX}px`;
1922
+ tile.canvas.style.top = `${visualY}px`;
1923
+ tile.ctx.scale(this._dpr, this._dpr);
1924
+ if (!tile.isInDOM && this._tilePoolEl) {
1925
+ this._tilePoolEl.appendChild(tile.canvas);
1926
+ tile.isInDOM = true;
1927
+ }
1928
+ if (!tile.visible) {
1929
+ tilesRedrawn.push(key);
1930
+ this._requestTileDraw(`updateTiles(${reason})`, key);
1931
+ }
1932
+ }
1933
+ tile.visible = true;
1934
+ } else if (tile.visible) {
1935
+ tile.visible = false;
1936
+ tile.isInDOM = false;
1937
+ tile.canvas.remove();
1938
+ }
1939
+ }
1940
+ for (const { index: col, logicalX } of tiles) for (const { row, logicalY } of rows) {
1941
+ const key = tileKey(col, row);
1942
+ if (!this._tilePool.has(key) && this._tilePoolEl) {
1943
+ const newTile = this._createTile(col, row, logicalX, logicalY);
1944
+ const visualX = isSingleTileX ? contentOffsetX : logicalX;
1945
+ const visualY = isSingleTileY ? contentOffsetY : logicalY;
1946
+ newTile.canvas.style.left = `${visualX}px`;
1947
+ newTile.canvas.style.top = `${visualY}px`;
1948
+ newTile.visible = true;
1949
+ newTile.isInDOM = true;
1950
+ this._tilePoolEl.appendChild(newTile.canvas);
1951
+ this._tilePool.set(key, newTile);
1952
+ this._visibleTiles.add(key);
1953
+ tilesRedrawn.push(key);
1954
+ this._requestTileDraw(`updateTiles(${reason})`, key);
1955
+ }
1956
+ }
1957
+ return tilesRedrawn;
1958
+ }
1959
+ _requestRedrawAll(reason) {
1960
+ this.#debug(`Requesting _requestRedrawAll: ${reason} visible tiles: ${this._visibleTiles.size} pending draws: ${this._pendingDraws.size}`);
1961
+ this._pendingDraws.forEach((ctrl) => ctrl.abort());
1962
+ this._pendingDraws.clear();
1963
+ for (const tileIndex of this._visibleTiles) this._requestTileDraw(`requestRedrawAll(${reason})`, tileIndex);
1964
+ }
1965
+ #debug(msg) {
1966
+ if (!this.debugTrace) return;
1967
+ console.log(`[VCanvas]`, msg);
1968
+ }
1969
+ _requestTileDraw(_reason, tileKey, region) {
1970
+ const tile = this._tilePool.get(tileKey);
1971
+ if (!tile || !this.drawCallback) return;
1972
+ const abortCtrl = new AbortController();
1973
+ this._pendingDraws.set(tileKey, abortCtrl);
1974
+ const tileWidth = this._getTileWidth();
1975
+ const tileHeight = this._getTileHeight();
1976
+ const isSingleTileX = isSingleTileMode(this.effectiveLogicalWidth, this._viewportWidth);
1977
+ const isSingleTileY = isSingleTileModeY(this.effectiveLogicalHeight, this._viewportHeight);
1978
+ let drawRegion;
1979
+ if (region) drawRegion = region;
1980
+ else {
1981
+ const overlapX = tileWidth * this._renderingOptions.overlapPercent;
1982
+ const overlapY = tileHeight * this._renderingOptions.overlapPercent;
1983
+ drawRegion = {
1984
+ x: isSingleTileX ? 0 : tile.logicalX - overlapX,
1985
+ y: isSingleTileY ? 0 : tile.logicalY - overlapY,
1986
+ width: isSingleTileX ? this.effectiveLogicalWidth : tileWidth + overlapX * 2,
1987
+ height: isSingleTileY ? this.effectiveLogicalHeight : tileHeight + overlapY * 2
1988
+ };
1989
+ }
1990
+ tile.ctx.save();
1991
+ tile.ctx.translate(-tile.logicalX, -tile.logicalY);
1992
+ const localX = drawRegion.x - tile.logicalX;
1993
+ const localY = drawRegion.y - tile.logicalY;
1994
+ if (region) {
1995
+ tile.ctx.beginPath();
1996
+ tile.ctx.rect(localX, localY, drawRegion.width, drawRegion.height);
1997
+ tile.ctx.clip();
1998
+ tile.ctx.clearRect(localX, localY, drawRegion.width, drawRegion.height);
1999
+ } else tile.ctx.clearRect(0, 0, tile.canvas.width, tile.canvas.height);
2000
+ if (this.debugDraw) drawDebugGraphic(tile.col, this._tilePool.size, this.effectiveLogicalWidth, tile.ctx, drawRegion);
2001
+ try {
2002
+ this.drawCallback(tile.ctx, drawRegion, abortCtrl.signal);
2003
+ } finally {
2004
+ if (tile) tile.ctx.restore();
2005
+ this._pendingDraws.delete(tileKey);
2006
+ }
2007
+ }
2008
+ _emitScrollEvent(userGesture) {
2009
+ const event = new CustomEvent(`scroll-virtual`, {
2010
+ detail: {
2011
+ logicalScrollLeft: this._scrollLeft,
2012
+ logicalScrollTop: this._scrollTop,
2013
+ userGesture,
2014
+ containerScrollLeft: this._scrollContainer?.scrollLeft ?? 0,
2015
+ containerScrollTop: this._scrollContainer?.scrollTop ?? 0,
2016
+ viewportStartLogical: this._scrollLeft,
2017
+ viewportEndLogical: this._scrollLeft + this._viewportWidth,
2018
+ viewportStartLogicalY: this._scrollTop,
2019
+ viewportEndLogicalY: this._scrollTop + this._viewportHeight
2020
+ },
2021
+ bubbles: true,
2022
+ composed: true
2023
+ });
2024
+ this.dispatchEvent(event);
2025
+ }
2026
+ /**
2027
+ * Checks if any part of a rectangular region is currently visible in the viewport.
2028
+ *
2029
+ * This method is useful for determining whether content needs to be rendered
2030
+ * or updated based on its visibility status. The region is specified using
2031
+ * logical coordinates relative to the virtual canvas content.
2032
+ *
2033
+ * @param rect - The rectangular region to check, in logical coordinates
2034
+ * @returns true if any part of the region overlaps with the visible viewport
2035
+ *
2036
+ * @example
2037
+ * ```typescript
2038
+ * const region = { x: 1000, y: 0, width: 500, height: 1000 };
2039
+ * if (vcanvas.isRegionVisible(region)) {
2040
+ * // Region is at least partially visible, render it
2041
+ * }
2042
+ * ```
2043
+ */
2044
+ isRegionVisible(rect) {
2045
+ const viewportLeft = this._scrollLeft;
2046
+ const viewportRight = this._scrollLeft + this._viewportWidth;
2047
+ const xVisible = rect.x + rect.width > viewportLeft && rect.x < viewportRight;
2048
+ const viewportTop = this._scrollTop;
2049
+ const viewportBottom = this._scrollTop + this._viewportHeight;
2050
+ const yVisible = rect.y + rect.height > viewportTop && rect.y < viewportBottom;
2051
+ return xVisible && yVisible;
2052
+ }
2053
+ _updateScrollPosition(userGesture) {
2054
+ if (this._scrollContainer) {
2055
+ this._scrollContainer.scrollLeft = this._scrollLeft;
2056
+ this._scrollContainer.scrollTop = this._scrollTop;
2057
+ }
2058
+ this._recalculateTiles(`updateScrollPosition`);
2059
+ this._emitScrollEvent(userGesture);
2060
+ this._scrollDebounce();
2061
+ }
2062
+ get logicalScrollLeft() {
2063
+ return this._scrollLeft;
2064
+ }
2065
+ /**
2066
+ * The current logical scroll position.
2067
+ *
2068
+ * Setting this property smoothly scrolls the virtual canvas to the specified
2069
+ * position. The value is automatically clamped to valid bounds (0 to
2070
+ * logicalWidth - viewportWidth).
2071
+ *
2072
+ * @example
2073
+ * ```typescript
2074
+ * // Scroll to position 5000
2075
+ * vcanvas.logicalScrollLeft = 5000;
2076
+ *
2077
+ * // Get current scroll position
2078
+ * console.log(vcanvas.logicalScrollLeft);
2079
+ * ```
2080
+ */
2081
+ set logicalScrollLeft(value) {
2082
+ const oldValue = this._scrollLeft;
2083
+ const clampedValue = clamp(value, 0, Math.max(0, this.effectiveLogicalWidth - this._viewportWidth));
2084
+ if (clampedValue !== this._scrollLeft) {
2085
+ this._scrollLeft = clampedValue;
2086
+ this.requestUpdate(`logicalScrollLeft`, oldValue);
2087
+ this._updateScrollPosition(false);
2088
+ }
2089
+ }
2090
+ get logicalScrollTop() {
2091
+ return this._scrollTop;
2092
+ }
2093
+ set logicalScrollTop(value) {
2094
+ const oldValue = this._scrollTop;
2095
+ const clampedValue = clamp(value, 0, Math.max(0, this.effectiveLogicalHeight - this._viewportHeight));
2096
+ if (clampedValue !== this._scrollTop) {
2097
+ this._scrollTop = clampedValue;
2098
+ this.requestUpdate(`logicalScrollTop`, oldValue);
2099
+ this._updateScrollPosition(false);
2100
+ }
2101
+ }
2102
+ get containerScrollLeft() {
2103
+ return this._scrollContainer?.scrollLeft ?? 0;
2104
+ }
2105
+ get containerScrollTop() {
2106
+ return this._scrollContainer?.scrollTop ?? 0;
2107
+ }
2108
+ /**
2109
+ * The current viewport width in pixels.
2110
+ *
2111
+ * This value is automatically updated when the component is resized.
2112
+ * Read-only - use CSS to control the component's size.
2113
+ *
2114
+ * @example
2115
+ * ```typescript
2116
+ * const vw = vcanvas.viewportWidth;
2117
+ * ```
2118
+ */
2119
+ get viewportWidth() {
2120
+ return this._viewportWidth;
2121
+ }
2122
+ /**
2123
+ * The current viewport height in pixels.
2124
+ *
2125
+ * This value is automatically updated when the component is resized.
2126
+ * Read-only - use CSS to control the component's size.
2127
+ *
2128
+ * @example
2129
+ * ```typescript
2130
+ * const vh = vcanvas.viewportHeight;
2131
+ * ```
2132
+ */
2133
+ get viewportHeight() {
2134
+ return this._viewportHeight;
2135
+ }
2136
+ /**
2137
+ * Updates rendering options for the virtual canvas.
2138
+ *
2139
+ * Pass a partial options object to update specific settings. Any options
2140
+ * not provided retain their current values. After updating options,
2141
+ * tiles are recalculated to reflect the new configuration.
2142
+ *
2143
+ * @param options - Partial options object specifying which settings to change
2144
+ *
2145
+ * @example
2146
+ * ```typescript
2147
+ * // Reduce tile size and increase pool for smoother scrolling
2148
+ * vcanvas.setRenderingOptions({
2149
+ * tileWidthMultiplier: 0.25,
2150
+ * maxPoolSize: 8,
2151
+ * });
2152
+ *
2153
+ * // Restore defaults
2154
+ * vcanvas.setRenderingOptions({
2155
+ * tileWidthMultiplier: 0.5,
2156
+ * tilesBuffer: 2,
2157
+ * maxPoolSize: 5,
2158
+ * });
2159
+ * ```
2160
+ */
2161
+ setRenderingOptions(options) {
2162
+ this._renderingOptions = {
2163
+ ...this._renderingOptions,
2164
+ ...options
2165
+ };
2166
+ this._recalculateTiles(`setRenderingOptions`);
2167
+ }
2168
+ /**
2169
+ * Gets the current rendering options.
2170
+ *
2171
+ * Returns a copy of the current configuration options controlling tile
2172
+ * rendering, pool management, and scroll behavior.
2173
+ *
2174
+ * @returns The current VCanvasRenderingOptions configuration
2175
+ *
2176
+ * @example
2177
+ * ```typescript
2178
+ * const options = vcanvas.getRenderingOptions();
2179
+ * console.log('Current tile width multiplier:', options.tileWidthMultiplier);
2180
+ * ```
2181
+ */
2182
+ getRenderingOptions() {
2183
+ return this._renderingOptions;
2184
+ }
2185
+ /**
2186
+ * Forces a complete redraw of all visible tiles.
2187
+ *
2188
+ * Call this when the visual appearance of the content needs to change
2189
+ * but the logical dimensions remain the same. This aborts any pending
2190
+ * draw operations and triggers new ones for all visible tiles.
2191
+ *
2192
+ * @example
2193
+ * ```typescript
2194
+ * // Redraw after theme change
2195
+ * theme.addEventListener('change', () => {
2196
+ * vcanvas.invalidateAll();
2197
+ * });
2198
+ * ```
2199
+ */
2200
+ invalidateAll() {
2201
+ this._requestRedrawAll(`invalidateAll`);
2202
+ }
2203
+ /**
2204
+ * Forces redraw of tiles that intersect with the specified region.
2205
+ *
2206
+ * Use this for partial invalidation when only a portion of the content
2207
+ * has changed. More efficient than invalidateAll() for localized updates.
2208
+ *
2209
+ * @param x - The x-coordinate of the region's left edge (logical coordinates)
2210
+ * @param y - The y-coordinate of the region's top edge (logical coordinates)
2211
+ * @param width - The width of the region to invalidate
2212
+ * @param height - The height of the region to invalidate
2213
+ *
2214
+ * @example
2215
+ * ```typescript
2216
+ * // Redraw a specific area after updating content
2217
+ * vcanvas.invalidateRegion(1000, 0, 500, 1000);
2218
+ * ```
2219
+ */
2220
+ invalidateRegion(x, y, width, height) {
2221
+ this.#debug(`invalidateRegion: ${x},${y},${width},${height}`);
2222
+ const tileWidth = this._getTileWidth();
2223
+ const tileHeight = this._getTileHeight();
2224
+ const { firstTile, lastTile } = this._calculateTileRange();
2225
+ const { firstRow, lastRow } = this._calculateTileRowRange();
2226
+ const overlap = this._renderingOptions.overlapPercent;
2227
+ const startCol = Math.floor(x / tileWidth);
2228
+ const endCol = Math.ceil((x + width) / tileWidth);
2229
+ const startRow = Math.floor(y / tileHeight);
2230
+ const endRow = Math.ceil((y + height) / tileHeight);
2231
+ for (let col = Math.max(firstTile, startCol); col <= Math.min(lastTile, endCol); col++) for (let row = Math.max(firstRow, startRow); row <= Math.min(lastRow, endRow); row++) {
2232
+ const key = tileKey(col, row);
2233
+ if (this._visibleTiles.has(key)) {
2234
+ const tile = this._tilePool.get(key);
2235
+ if (tile) {
2236
+ const overlapX = tileWidth * overlap;
2237
+ const overlapY = tileHeight * overlap;
2238
+ const tileStartX = tile.logicalX - overlapX;
2239
+ const tileEndX = tile.logicalX + tileWidth + overlapX;
2240
+ const tileStartY = tile.logicalY - overlapY;
2241
+ const tileEndY = tile.logicalY + tileHeight + overlapY;
2242
+ const intersectX = Math.max(x, tileStartX);
2243
+ const intersectWidth = Math.min(x + width, tileEndX) - intersectX;
2244
+ const intersectY = Math.max(y, tileStartY);
2245
+ const intersectHeight = Math.min(y + height, tileEndY) - intersectY;
2246
+ if (intersectWidth > 0 && intersectHeight > 0) {
2247
+ const region = {
2248
+ x: intersectX,
2249
+ y: intersectY,
2250
+ width: intersectWidth,
2251
+ height: intersectHeight
2252
+ };
2253
+ this._requestTileDraw(`invalidateRegion`, key, region);
2254
+ }
2255
+ }
2256
+ }
2257
+ }
2258
+ }
2259
+ /**
2260
+ * Converts viewport-relative coordinates to canvas-relative (logical) coordinates.
2261
+ *
2262
+ * Viewport coordinates are relative to the visible area, while canvas coordinates
2263
+ * are in the logical coordinate system of the virtual canvas content.
2264
+ *
2265
+ * @param x - The x-coordinate in viewport space
2266
+ * @param y - The y-coordinate in viewport space
2267
+ * @returns The coordinates in canvas (logical) space
2268
+ *
2269
+ * @example
2270
+ * ```typescript
2271
+ * // Convert mouse position to logical coordinates for hit testing
2272
+ * vcanvas.addEventListener('click', (e) => {
2273
+ * const logical = vcanvas.convertFromViewportToCanvas(e.offsetX, e.offsetY);
2274
+ * console.log('Clicked at logical position:', logical.x);
2275
+ * });
2276
+ * ```
2277
+ */
2278
+ convertFromViewportToCanvas(x, y) {
2279
+ const contentOffsetX = getContentOffset(this.effectiveLogicalWidth, this._viewportWidth);
2280
+ const contentOffsetY = getContentOffsetY(this.effectiveLogicalHeight, this._viewportHeight);
2281
+ return viewportToCanvas(x, y, this._scrollLeft, contentOffsetX, this._scrollTop, contentOffsetY);
2282
+ }
2283
+ /**
2284
+ * Converts canvas-relative (logical) coordinates to viewport-relative coordinates.
2285
+ *
2286
+ * This is the inverse of `convertFromViewportToCanvas`. Use this to convert
2287
+ * logical positions back to viewport coordinates for positioning UI elements.
2288
+ *
2289
+ * @param x - The x-coordinate in logical space
2290
+ * @param y - The y-coordinate in logical space
2291
+ * @returns The coordinates in viewport space
2292
+ *
2293
+ * @example
2294
+ * ```typescript
2295
+ * // Convert logical marker position to viewport for tooltip positioning
2296
+ * const viewportPos = vcanvas.convertFromCanvasToViewport(markerX, 0);
2297
+ * tooltip.style.left = `${viewportPos.x}px`;
2298
+ * ```
2299
+ */
2300
+ convertFromCanvasToViewport(x, y) {
2301
+ const contentOffsetX = getContentOffset(this.effectiveLogicalWidth, this._viewportWidth);
2302
+ const contentOffsetY = getContentOffsetY(this.effectiveLogicalHeight, this._viewportHeight);
2303
+ return canvasToViewport(x, y, this._scrollLeft, contentOffsetX, this._scrollTop, contentOffsetY);
2304
+ }
2305
+ /**
2306
+ * Positions light DOM child elements matching the query within a logical coordinate range.
2307
+ *
2308
+ * This method allows you to position HTML elements (like tooltips, markers, or overlays)
2309
+ * at specific logical coordinates within the virtual canvas. The elements will scroll
2310
+ * with the canvas content, maintaining their logical position.
2311
+ *
2312
+ * @param query - A CSS selector string to match child elements
2313
+ * @param startX - The starting x-coordinate in logical space
2314
+ * @param endX - The ending x-coordinate in logical space (determines width)
2315
+ * @param startY - The starting y-coordinate in logical space (optional)
2316
+ * @param endY - The ending y-coordinate in logical space (optional)
2317
+ *
2318
+ * @example
2319
+ * ```typescript
2320
+ * // Position a marker element at logical position x=200 spanning 200px
2321
+ * vcanvas.positionChildRange('#marker', 200, 400);
2322
+ *
2323
+ * // Position multiple elements with a class
2324
+ * vcanvas.positionChildRange('.timeline-item', 0, 1000);
2325
+ * ```
2326
+ */
2327
+ positionChildRange(query, startX, endX, startY, endY) {
2328
+ if (Number.isNaN(startX) || Number.isNaN(endX)) {
2329
+ console.warn(`positionChildRange: Invalid startX or endX values: ${startX}, ${endX}`);
2330
+ return;
2331
+ }
2332
+ const children = Array.from(this.children).filter((child) => child.matches?.(query));
2333
+ const resolvedStartY = startY ?? 0;
2334
+ const resolvedEndY = endY ?? this.effectiveLogicalHeight;
2335
+ for (const child of children) this._positionedChildren.set(child, {
2336
+ startX,
2337
+ endX,
2338
+ startY: resolvedStartY,
2339
+ endY: resolvedEndY
2340
+ });
2341
+ this._positionChildren(`positionChildRange`);
2342
+ }
2343
+ /**
2344
+ * Manually triggers an update of child element positioning.
2345
+ *
2346
+ * This is useful when the viewport dimensions have changed outside the normal
2347
+ * update cycle and you need to reposition slotted children immediately.
2348
+ * Normally, child positioning is automatically updated during scroll and resize.
2349
+ *
2350
+ * @example
2351
+ * ```typescript
2352
+ * // Force immediate update after custom layout changes
2353
+ * vcanvas.manualChildPositioningUpdate();
2354
+ * ```
2355
+ */
2356
+ manualChildPositioningUpdate() {
2357
+ this._positionChildren(`manualChildPositioningUpdate`);
2358
+ }
2359
+ /**
2360
+ * Stops positioning child elements matching the query and resets their styles.
2361
+ *
2362
+ * This removes children from the positioning system and clears any inline
2363
+ * styles that were applied by `positionChildRange()`. The children remain
2364
+ * in the DOM but are no longer positioned by the virtual canvas.
2365
+ *
2366
+ * @param query - A CSS selector string to match child elements
2367
+ *
2368
+ * @example
2369
+ * ```typescript
2370
+ * // Stop positioning a specific child element
2371
+ * vcanvas.positionChildClear('#child-0');
2372
+ *
2373
+ * // Stop positioning all children with a specific class
2374
+ * vcanvas.positionChildClear('.my-element');
2375
+ * ```
2376
+ */
2377
+ positionChildClear(query) {
2378
+ for (const [child] of this._positionedChildren) if (child.matches?.(query)) {
2379
+ this._positionedChildren.delete(child);
2380
+ child.removeAttribute(`style`);
2381
+ }
2382
+ }
2383
+ _positionChildren(source) {
2384
+ this.#debug(`_positionChildren: ${source} positionedChildren: ${this._positionedChildren.size}`);
2385
+ if (!this.hasUpdated) return;
2386
+ if (!this._childrenContainerEl) return;
2387
+ for (const [child, config] of this._positionedChildren) {
2388
+ const width = config.endX - config.startX;
2389
+ const height = config.endY - config.startY;
2390
+ child.setAttribute(`style`, `position: absolute; left: ${config.startX}px; top: ${config.startY}px; width: ${width}px; height: ${height}px;`);
2391
+ }
2392
+ }
2393
+ render() {
2394
+ return html`
2395
+ <div class=${classMap({
2396
+ "scroll-container": true,
2397
+ "scrollable": this.hinting
2398
+ })}>
2399
+ <div class="spacer"></div>
2400
+ <div class="tile-pool"></div>
2401
+ <div class="children-container">
2402
+ <slot></slot>
2403
+ </div>
2404
+ </div>
2405
+ `;
2406
+ }
2407
+ };
2408
+ __decorate([property({ type: Boolean })], VCanvas.prototype, "hinting", void 0);
2409
+ __decorate([property()], VCanvas.prototype, "logicalWidth", null);
2410
+ __decorate([property()], VCanvas.prototype, "logicalHeight", null);
2411
+ __decorate([property({ type: Number })], VCanvas.prototype, "logicalScrollLeft", null);
2412
+ __decorate([property({ type: Number })], VCanvas.prototype, "logicalScrollTop", null);
2413
+ VCanvas = __decorate([customElement(`x-vcanvas`)], VCanvas);
1013
2414
  //#endregion
1014
2415
  //#region src/timeline/timeline-element.ts
1015
2416
  /** Reasonable maximum ppu to prevent infinite zoom-in */
@@ -1699,54 +3100,54 @@ let IxfxTimelineElement = class IxfxTimelineElement extends LitElement {
1699
3100
  }));
1700
3101
  }
1701
3102
  };
1702
- __decorate([property({ type: Array })], IxfxTimelineElement.prototype, "tracks", void 0);
1703
- __decorate([property({ type: Array })], IxfxTimelineElement.prototype, "events", void 0);
1704
- __decorate([property({ type: Number })], IxfxTimelineElement.prototype, "min", void 0);
1705
- __decorate([property({ type: Number })], IxfxTimelineElement.prototype, "max", void 0);
1706
- __decorate([property({
3103
+ __decorate$1([property({ type: Array })], IxfxTimelineElement.prototype, "tracks", void 0);
3104
+ __decorate$1([property({ type: Array })], IxfxTimelineElement.prototype, "events", void 0);
3105
+ __decorate$1([property({ type: Number })], IxfxTimelineElement.prototype, "min", void 0);
3106
+ __decorate$1([property({ type: Number })], IxfxTimelineElement.prototype, "max", void 0);
3107
+ __decorate$1([property({
1707
3108
  type: String,
1708
3109
  attribute: `track-height`
1709
3110
  })], IxfxTimelineElement.prototype, "trackHeight", void 0);
1710
- __decorate([property({
3111
+ __decorate$1([property({
1711
3112
  type: String,
1712
3113
  attribute: `user-scale`
1713
3114
  })], IxfxTimelineElement.prototype, "userScale", void 0);
1714
- __decorate([property({ type: Boolean })], IxfxTimelineElement.prototype, "reorderable", void 0);
1715
- __decorate([property({
3115
+ __decorate$1([property({ type: Boolean })], IxfxTimelineElement.prototype, "reorderable", void 0);
3116
+ __decorate$1([property({
1716
3117
  type: Boolean,
1717
3118
  attribute: `meta`
1718
3119
  })], IxfxTimelineElement.prototype, "metaEnabled", void 0);
1719
- __decorate([property({
3120
+ __decorate$1([property({
1720
3121
  type: Boolean,
1721
3122
  attribute: `alt-major-bg`
1722
3123
  })], IxfxTimelineElement.prototype, "altMajorBg", void 0);
1723
- __decorate([property({
3124
+ __decorate$1([property({
1724
3125
  type: Number,
1725
3126
  attribute: `default-track-height`
1726
3127
  })], IxfxTimelineElement.prototype, "defaultTrackHeight", void 0);
1727
- __decorate([property({
3128
+ __decorate$1([property({
1728
3129
  type: String,
1729
3130
  attribute: `grid-mode`
1730
3131
  })], IxfxTimelineElement.prototype, "gridMode", void 0);
1731
- __decorate([property({
3132
+ __decorate$1([property({
1732
3133
  type: Number,
1733
3134
  attribute: `minor-step`
1734
3135
  })], IxfxTimelineElement.prototype, "minorStep", void 0);
1735
- __decorate([property({ type: Object })], IxfxTimelineElement.prototype, "onDragging", void 0);
1736
- __decorate([state()], IxfxTimelineElement.prototype, "_ppu", void 0);
1737
- __decorate([state()], IxfxTimelineElement.prototype, "_viewRange", void 0);
1738
- __decorate([state()], IxfxTimelineElement.prototype, "_vViewRange", void 0);
1739
- __decorate([state()], IxfxTimelineElement.prototype, "_vScale", void 0);
1740
- __decorate([state()], IxfxTimelineElement.prototype, "_selectedIds", void 0);
1741
- __decorate([state()], IxfxTimelineElement.prototype, "_tickledId", void 0);
1742
- __decorate([state()], IxfxTimelineElement.prototype, "_drag", void 0);
1743
- __decorate([state()], IxfxTimelineElement.prototype, "_metaHeight", void 0);
1744
- __decorate([state()], IxfxTimelineElement.prototype, "_trackHeightOverrides", void 0);
1745
- __decorate([state()], IxfxTimelineElement.prototype, "_trackLayout", void 0);
1746
- __decorate([state()], IxfxTimelineElement.prototype, "_internalEvents", void 0);
1747
- __decorate([state()], IxfxTimelineElement.prototype, "_internalTracks", void 0);
1748
- IxfxTimelineElement = __decorate([safeCustomElement(`ixfx-timeline`)], IxfxTimelineElement);
3136
+ __decorate$1([property({ type: Object })], IxfxTimelineElement.prototype, "onDragging", void 0);
3137
+ __decorate$1([state()], IxfxTimelineElement.prototype, "_ppu", void 0);
3138
+ __decorate$1([state()], IxfxTimelineElement.prototype, "_viewRange", void 0);
3139
+ __decorate$1([state()], IxfxTimelineElement.prototype, "_vViewRange", void 0);
3140
+ __decorate$1([state()], IxfxTimelineElement.prototype, "_vScale", void 0);
3141
+ __decorate$1([state()], IxfxTimelineElement.prototype, "_selectedIds", void 0);
3142
+ __decorate$1([state()], IxfxTimelineElement.prototype, "_tickledId", void 0);
3143
+ __decorate$1([state()], IxfxTimelineElement.prototype, "_drag", void 0);
3144
+ __decorate$1([state()], IxfxTimelineElement.prototype, "_metaHeight", void 0);
3145
+ __decorate$1([state()], IxfxTimelineElement.prototype, "_trackHeightOverrides", void 0);
3146
+ __decorate$1([state()], IxfxTimelineElement.prototype, "_trackLayout", void 0);
3147
+ __decorate$1([state()], IxfxTimelineElement.prototype, "_internalEvents", void 0);
3148
+ __decorate$1([state()], IxfxTimelineElement.prototype, "_internalTracks", void 0);
3149
+ IxfxTimelineElement = __decorate$1([safeCustomElement(`ixfx-timeline`)], IxfxTimelineElement);
1749
3150
  //#endregion
1750
3151
  export { resolveCssVars as a, attachTimelineInteractions as i, TimelineTrackTabElement as n, resolveGrid as o, timelineStyles as r, TimelineController as s, IxfxTimelineElement as t };
1751
3152
 
1752
- //# sourceMappingURL=timeline-Cv75Th2U.js.map
3153
+ //# sourceMappingURL=timeline-2OFtN4Ta.js.map