@rcarls/rc-virtual-canvas 0.2.0 → 0.3.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.
package/README.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # `@rcarls/rc-virtual-canvas`
2
2
 
3
- A virtualized scrollable canvas component. Overlays a transparent scrollable div on top of a slotted `<canvas>` element, dispatching `rc-virtual-canvas-render` events with the current viewport and content rectangles. Consumers draw only what is visible. Built with [Lit 3](https://lit.dev).
3
+ Scrollable virtual canvas for rendering large coordinate-space content.
4
+
5
+ Docs: [https://richardcarls.github.io/rc-webcomponents/components/rc-virtual-canvas](https://richardcarls.github.io/rc-webcomponents/components/rc-virtual-canvas).
6
+
7
+ Overlays a transparent scrollable div on top of a slotted `<canvas>` element, dispatching
8
+ `rc-virtual-canvas-render` events with the current viewport and content rectangles.
9
+ Consumers draw only what is visible.
4
10
 
5
11
  ---
6
12
 
@@ -21,7 +27,9 @@ import { RCVirtualCanvas } from '@rcarls/rc-virtual-canvas'; // named class e
21
27
 
22
28
  ## Basic usage
23
29
 
24
- Place a `<canvas>` inside the component. Set `contentWidth` and `contentHeight` to the total size of your virtual content. On each `rc-virtual-canvas-render` event, use `detail.viewRect` to determine which portion of that content is visible and draw accordingly.
30
+ Place a `<canvas>` inside the component. Set `contentWidth` and `contentHeight` to the
31
+ total size of your virtual content. On each `rc-virtual-canvas-render` event, use
32
+ `detail.viewRect` to determine which portion of that content is visible and draw accordingly.
25
33
 
26
34
  ```html
27
35
  <rc-virtual-canvas
@@ -47,7 +55,9 @@ Place a `<canvas>` inside the component. Set `contentWidth` and `contentHeight`
47
55
  </script>
48
56
  ```
49
57
 
50
- The canvas element should match the component's rendered size (set via CSS). The virtual content is larger — scrolling is handled by an absolutely-positioned transparent `<div>` that triggers native scroll events.
58
+ The canvas element should match the component's rendered size (set via CSS). The virtual
59
+ content is larger — scrolling is handled by an absolutely-positioned transparent `<div>`
60
+ that triggers native scroll events.
51
61
 
52
62
  ---
53
63
 
@@ -63,6 +73,13 @@ The canvas element should match the component's rendered size (set via CSS). The
63
73
  | `renderMode` | `render-mode` | `'continuous' \| 'viewport-change' \| 'manual'` | `'continuous'` | Controls when render events are dispatched |
64
74
  | `imageRendering` | `image-rendering` | `'auto' \| 'crisp-edges' \| 'pixelated'` | `'auto'` | Convenience value applied to the slotted canvas via `image-rendering` |
65
75
 
76
+ ### Read-only properties
77
+
78
+ | Property | Type | Description |
79
+ |---|---|---|
80
+ | `canvasScaleX` | `number` | Ratio of canvas backing-store pixels to CSS pixels along the x-axis (≈ `devicePixelRatio` when `autoResizeCanvas` is `true`) |
81
+ | `canvasScaleY` | `number` | Ratio of canvas backing-store pixels to CSS pixels along the y-axis |
82
+
66
83
  ### Methods
67
84
 
68
85
  | Method | Description |
@@ -46,7 +46,7 @@
46
46
  "declarations": [
47
47
  {
48
48
  "kind": "class",
49
- "description": "An accessible virtual scrollable canvas component.",
49
+ "description": "A scrollable virtual canvas component.",
50
50
  "name": "RCVirtualCanvas",
51
51
  "slots": [
52
52
  {
@@ -161,7 +161,8 @@
161
161
  "type": {
162
162
  "text": "HTMLDivElement"
163
163
  },
164
- "privacy": "protected"
164
+ "privacy": "protected",
165
+ "description": "Shadow DOM scroll container that owns pointer interaction and native scrolling"
165
166
  },
166
167
  {
167
168
  "kind": "field",
@@ -169,7 +170,8 @@
169
170
  "type": {
170
171
  "text": "HTMLDivElement"
171
172
  },
172
- "privacy": "protected"
173
+ "privacy": "protected",
174
+ "description": "Absolutely-positioned div that establishes the virtual scroll range"
173
175
  },
174
176
  {
175
177
  "kind": "field",
@@ -178,7 +180,8 @@
178
180
  "text": "HTMLCanvasElement | null"
179
181
  },
180
182
  "privacy": "protected",
181
- "default": "null"
183
+ "default": "null",
184
+ "description": "Currently-tracked slotted canvas element; `null` when no canvas is slotted"
182
185
  },
183
186
  {
184
187
  "kind": "field",
@@ -187,7 +190,8 @@
187
190
  "text": "object"
188
191
  },
189
192
  "privacy": "protected",
190
- "default": "{ x: 0, y: 0, width: 0, height: 0, }"
193
+ "default": "{ x: 0, y: 0, width: 0, height: 0, }",
194
+ "description": "Mutable current-frame viewport geometry; call `getViewRect()` for a frozen snapshot"
191
195
  },
192
196
  {
193
197
  "kind": "field",
@@ -196,18 +200,21 @@
196
200
  "text": "object"
197
201
  },
198
202
  "privacy": "protected",
199
- "default": "{ x: 0, y: 0, width: 0, height: 0, }"
203
+ "default": "{ x: 0, y: 0, width: 0, height: 0, }",
204
+ "description": "Mutable content bounds; updated when `contentWidth` or `contentHeight` change"
200
205
  },
201
206
  {
202
207
  "kind": "field",
203
208
  "name": "_resizeObserver",
204
209
  "privacy": "protected",
205
- "default": "new ResizeObserver( (entries: ResizeObserverEntry[]) => { for (const entry of entries) { const devicePixelBoxSize = Array.isArray( entry.devicePixelContentBoxSize, ) ? entry.devicePixelContentBoxSize[0] : entry.devicePixelContentBoxSize; this._viewRect.width = this._getMeasuredCanvasWidth( devicePixelBoxSize?.inlineSize ?? Math.round(entry.contentRect.width * window.devicePixelRatio), ); this._viewRect.height = this._getMeasuredCanvasHeight( devicePixelBoxSize?.blockSize ?? Math.round(entry.contentRect.height * window.devicePixelRatio), ); this._syncCanvasBackingStore(); this._scheduleRender('viewport-change'); } }, )"
210
+ "default": "new ResizeObserver((entries: ResizeObserverEntry[]) => { for (const entry of entries) { const devicePixelBoxSize = Array.isArray(entry.devicePixelContentBoxSize) ? entry.devicePixelContentBoxSize[0] : entry.devicePixelContentBoxSize; this._viewRect.width = this._getMeasuredCanvasWidth( devicePixelBoxSize?.inlineSize ?? Math.round(entry.contentRect.width * window.devicePixelRatio), ); this._viewRect.height = this._getMeasuredCanvasHeight( devicePixelBoxSize?.blockSize ?? Math.round(entry.contentRect.height * window.devicePixelRatio), ); this._syncCanvasBackingStore(); this._scheduleRender('viewport-change'); } })",
211
+ "description": "Watches the slotted canvas for DPR-accurate dimension changes"
206
212
  },
207
213
  {
208
214
  "kind": "method",
209
215
  "name": "_onScroll",
210
- "privacy": "protected"
216
+ "privacy": "protected",
217
+ "description": "Reads scroll position from the root container and schedules a render"
211
218
  },
212
219
  {
213
220
  "kind": "method",
@@ -220,11 +227,13 @@
220
227
  "text": "Event"
221
228
  }
222
229
  }
223
- ]
230
+ ],
231
+ "description": "Replaces canvas tracking and re-registers the resize observer when slot content changes"
224
232
  },
225
233
  {
226
234
  "kind": "method",
227
- "name": "getViewRect"
235
+ "name": "getViewRect",
236
+ "description": "Returns a frozen snapshot of the current viewport rectangle.\n\nSafe to keep between frames — the returned object will not be mutated\nby future scroll or resize work."
228
237
  },
229
238
  {
230
239
  "kind": "method",
@@ -234,22 +243,26 @@
234
243
  "name": "x",
235
244
  "type": {
236
245
  "text": "number"
237
- }
246
+ },
247
+ "description": "Content x coordinate in CSS pixels"
238
248
  },
239
249
  {
240
250
  "name": "y",
241
251
  "type": {
242
252
  "text": "number"
243
- }
253
+ },
254
+ "description": "Content y coordinate in CSS pixels"
244
255
  },
245
256
  {
246
257
  "name": "options",
247
258
  "default": "{}",
248
259
  "type": {
249
260
  "text": "ScrollOptions"
250
- }
261
+ },
262
+ "description": "Native scroll options passed to scrollTo"
251
263
  }
252
- ]
264
+ ],
265
+ "description": "Scrolls so the content coordinate is at the viewport origin."
253
266
  },
254
267
  {
255
268
  "kind": "method",
@@ -259,22 +272,26 @@
259
272
  "name": "x",
260
273
  "type": {
261
274
  "text": "number"
262
- }
275
+ },
276
+ "description": "Content x coordinate in CSS pixels"
263
277
  },
264
278
  {
265
279
  "name": "y",
266
280
  "type": {
267
281
  "text": "number"
268
- }
282
+ },
283
+ "description": "Content y coordinate in CSS pixels"
269
284
  },
270
285
  {
271
286
  "name": "options",
272
287
  "default": "{}",
273
288
  "type": {
274
289
  "text": "ScrollOptions"
275
- }
290
+ },
291
+ "description": "Native scroll options passed to scrollTo"
276
292
  }
277
- ]
293
+ ],
294
+ "description": "Scrolls so the content coordinate is centered in the viewport."
278
295
  },
279
296
  {
280
297
  "kind": "method",
@@ -284,15 +301,18 @@
284
301
  "name": "clientX",
285
302
  "type": {
286
303
  "text": "number"
287
- }
304
+ },
305
+ "description": "Browser client x coordinate (e.g. from a pointer event)"
288
306
  },
289
307
  {
290
308
  "name": "clientY",
291
309
  "type": {
292
310
  "text": "number"
293
- }
311
+ },
312
+ "description": "Browser client y coordinate"
294
313
  }
295
- ]
314
+ ],
315
+ "description": "Converts browser client coordinates to content coordinates using the current\nbacking-store scale."
296
316
  },
297
317
  {
298
318
  "kind": "method",
@@ -302,15 +322,18 @@
302
322
  "name": "x",
303
323
  "type": {
304
324
  "text": "number"
305
- }
325
+ },
326
+ "description": "Content x coordinate in CSS pixels"
306
327
  },
307
328
  {
308
329
  "name": "y",
309
330
  "type": {
310
331
  "text": "number"
311
- }
332
+ },
333
+ "description": "Content y coordinate in CSS pixels"
312
334
  }
313
- ]
335
+ ],
336
+ "description": "Converts content coordinates back to browser client coordinates."
314
337
  },
315
338
  {
316
339
  "kind": "method",
@@ -321,9 +344,29 @@
321
344
  "default": "'manual'",
322
345
  "type": {
323
346
  "text": "RCVirtualCanvasRenderReason"
324
- }
347
+ },
348
+ "description": "Render reason reported in the event detail"
325
349
  }
326
- ]
350
+ ],
351
+ "description": "Queues a render event on the next animation frame.\n\nRequired when `renderMode` is `'manual'`. In other modes the component\nschedules renders automatically; calling this with reason `'animation-frame'`\nis a no-op when `renderMode` is `'viewport-change'`."
352
+ },
353
+ {
354
+ "kind": "field",
355
+ "name": "canvasScaleX",
356
+ "type": {
357
+ "text": "number"
358
+ },
359
+ "description": "Ratio of canvas backing-store pixels to CSS pixels along the x-axis.\n\nEquals `devicePixelRatio` when `autoResizeCanvas` is `true`. Use as the x-scale\nfactor in `ctx.scale()` to draw at CSS pixel resolution.",
360
+ "readonly": true
361
+ },
362
+ {
363
+ "kind": "field",
364
+ "name": "canvasScaleY",
365
+ "type": {
366
+ "text": "number"
367
+ },
368
+ "description": "Ratio of canvas backing-store pixels to CSS pixels along the y-axis.\n\nEquals `devicePixelRatio` when `autoResizeCanvas` is `true`. Use as the y-scale\nfactor in `ctx.scale()` to draw at CSS pixel resolution.",
369
+ "readonly": true
327
370
  },
328
371
  {
329
372
  "kind": "method",
@@ -336,7 +379,8 @@
336
379
  "text": "PointerEvent | MouseEvent"
337
380
  }
338
381
  }
339
- ]
382
+ ],
383
+ "description": "Maps pointer and mouse events to content coordinates and dispatches `rc-virtual-canvas-pointer`"
340
384
  },
341
385
  {
342
386
  "kind": "method",
@@ -365,9 +409,11 @@
365
409
  "name": "time",
366
410
  "type": {
367
411
  "text": "DOMHighResTimeStamp"
368
- }
412
+ },
413
+ "description": "High-resolution timestamp from `requestAnimationFrame`"
369
414
  }
370
- ]
415
+ ],
416
+ "description": "RAF callback. Dispatches `rc-virtual-canvas-render` and re-schedules in continuous mode."
371
417
  },
372
418
  {
373
419
  "kind": "method",
@@ -1,5 +1,5 @@
1
1
  import { css as _, LitElement as p, html as g } from "lit";
2
- import { property as h, query as u, state as l } from "lit/decorators.js";
2
+ import { property as h, query as u, state as d } from "lit/decorators.js";
3
3
  import { styleMap as b } from "lit/directives/style-map.js";
4
4
  const w = _`
5
5
  *,
@@ -70,9 +70,9 @@ const w = _`
70
70
  image-rendering: var(--rc-virtual-canvas-image-rendering, auto);
71
71
  }
72
72
  `;
73
- var m = Object.defineProperty, f = Object.getOwnPropertyDescriptor, o = (r, t, e, i) => {
74
- for (var s = i > 1 ? void 0 : i ? f(t, e) : t, a = r.length - 1, d; a >= 0; a--)
75
- (d = r[a]) && (s = (i ? d(t, e, s) : d(s)) || s);
73
+ var m = Object.defineProperty, R = Object.getOwnPropertyDescriptor, o = (r, t, e, i) => {
74
+ for (var s = i > 1 ? void 0 : i ? R(t, e) : t, a = r.length - 1, l; a >= 0; a--)
75
+ (l = r[a]) && (s = (i ? l(t, e, s) : l(s)) || s);
76
76
  return i && s && m(t, e, s), s;
77
77
  };
78
78
  function c(r) {
@@ -95,20 +95,16 @@ const v = class v extends p {
95
95
  y: 0,
96
96
  width: 0,
97
97
  height: 0
98
- }, this._resizeObserver = new ResizeObserver(
99
- (t) => {
100
- for (const e of t) {
101
- const i = Array.isArray(
102
- e.devicePixelContentBoxSize
103
- ) ? e.devicePixelContentBoxSize[0] : e.devicePixelContentBoxSize;
104
- this._viewRect.width = this._getMeasuredCanvasWidth(
105
- i?.inlineSize ?? Math.round(e.contentRect.width * window.devicePixelRatio)
106
- ), this._viewRect.height = this._getMeasuredCanvasHeight(
107
- i?.blockSize ?? Math.round(e.contentRect.height * window.devicePixelRatio)
108
- ), this._syncCanvasBackingStore(), this._scheduleRender("viewport-change");
109
- }
98
+ }, this._resizeObserver = new ResizeObserver((t) => {
99
+ for (const e of t) {
100
+ const i = Array.isArray(e.devicePixelContentBoxSize) ? e.devicePixelContentBoxSize[0] : e.devicePixelContentBoxSize;
101
+ this._viewRect.width = this._getMeasuredCanvasWidth(
102
+ i?.inlineSize ?? Math.round(e.contentRect.width * window.devicePixelRatio)
103
+ ), this._viewRect.height = this._getMeasuredCanvasHeight(
104
+ i?.blockSize ?? Math.round(e.contentRect.height * window.devicePixelRatio)
105
+ ), this._syncCanvasBackingStore(), this._scheduleRender("viewport-change");
110
106
  }
111
- );
107
+ });
112
108
  }
113
109
  set contentWidth(t) {
114
110
  const e = this._contentWidth;
@@ -124,9 +120,11 @@ const v = class v extends p {
124
120
  get contentHeight() {
125
121
  return this._contentHeight;
126
122
  }
123
+ /** Reads scroll position from the root container and schedules a render */
127
124
  _onScroll() {
128
125
  this._viewRect.x = this._$root.scrollLeft, this._viewRect.y = this._$root.scrollTop, this._scheduleRender("viewport-change");
129
126
  }
127
+ /** Replaces canvas tracking and re-registers the resize observer when slot content changes */
130
128
  _onSlotChange(t) {
131
129
  this._resizeObserver.disconnect(), this._$canvas = t.currentTarget.assignedElements().filter((e) => e instanceof HTMLCanvasElement).at(0) ?? null, this._$canvas != null && (this._viewRect = {
132
130
  x: this._$root.scrollLeft ?? 0,
@@ -137,9 +135,22 @@ const v = class v extends p {
137
135
  box: "device-pixel-content-box"
138
136
  }), this._scheduleRender("viewport-change"));
139
137
  }
138
+ /**
139
+ * Returns a frozen snapshot of the current viewport rectangle.
140
+ *
141
+ * Safe to keep between frames — the returned object will not be mutated
142
+ * by future scroll or resize work.
143
+ */
140
144
  getViewRect() {
141
145
  return c(this._viewRect);
142
146
  }
147
+ /**
148
+ * Scrolls so the content coordinate is at the viewport origin.
149
+ *
150
+ * @param x - Content x coordinate in CSS pixels
151
+ * @param y - Content y coordinate in CSS pixels
152
+ * @param options - Native scroll options passed to scrollTo
153
+ */
143
154
  scrollToContent(t, e, i = {}) {
144
155
  this._$root.scrollTo({
145
156
  ...i,
@@ -147,6 +158,13 @@ const v = class v extends p {
147
158
  top: e
148
159
  }), this._viewRect.x = this._$root.scrollLeft, this._viewRect.y = this._$root.scrollTop, this._scheduleRender("viewport-change");
149
160
  }
161
+ /**
162
+ * Scrolls so the content coordinate is centered in the viewport.
163
+ *
164
+ * @param x - Content x coordinate in CSS pixels
165
+ * @param y - Content y coordinate in CSS pixels
166
+ * @param options - Native scroll options passed to scrollTo
167
+ */
150
168
  centerOnContent(t, e, i = {}) {
151
169
  this.scrollToContent(
152
170
  t - this._$root.clientWidth * 0.5,
@@ -154,6 +172,19 @@ const v = class v extends p {
154
172
  i
155
173
  );
156
174
  }
175
+ /**
176
+ * Converts browser client coordinates to content coordinates using the current
177
+ * backing-store scale.
178
+ *
179
+ * @param clientX - Browser client x coordinate (e.g. from a pointer event)
180
+ * @param clientY - Browser client y coordinate
181
+ *
182
+ * @example
183
+ * vc.addEventListener('pointerdown', (e) => {
184
+ * const { x, y } = vc.clientToContent(e.clientX, e.clientY);
185
+ * placeMarkerAt(x, y);
186
+ * });
187
+ */
157
188
  clientToContent(t, e) {
158
189
  const i = this._getCanvasClientRect(), s = this._getViewportScaleX(i), a = this._getViewportScaleY(i);
159
190
  return Object.freeze({
@@ -161,6 +192,17 @@ const v = class v extends p {
161
192
  y: this._viewRect.y + (e - i.top) * a
162
193
  });
163
194
  }
195
+ /**
196
+ * Converts content coordinates back to browser client coordinates.
197
+ *
198
+ * @param x - Content x coordinate in CSS pixels
199
+ * @param y - Content y coordinate in CSS pixels
200
+ *
201
+ * @example
202
+ * const { x, y } = vc.contentToClient(contentX, contentY);
203
+ * tooltip.style.left = `${x}px`;
204
+ * tooltip.style.top = `${y}px`;
205
+ */
164
206
  contentToClient(t, e) {
165
207
  const i = this._getCanvasClientRect(), s = this._getViewportScaleX(i), a = this._getViewportScaleY(i);
166
208
  return Object.freeze({
@@ -168,11 +210,50 @@ const v = class v extends p {
168
210
  y: i.top + (e - this._viewRect.y) / a
169
211
  });
170
212
  }
213
+ /**
214
+ * Queues a render event on the next animation frame.
215
+ *
216
+ * Required when `renderMode` is `'manual'`. In other modes the component
217
+ * schedules renders automatically; calling this with reason `'animation-frame'`
218
+ * is a no-op when `renderMode` is `'viewport-change'`.
219
+ *
220
+ * @param reason - Render reason reported in the event detail
221
+ */
171
222
  requestRender(t = "manual") {
172
223
  this._scheduleRender(t);
173
224
  }
225
+ /**
226
+ * Ratio of canvas backing-store pixels to CSS pixels along the x-axis.
227
+ *
228
+ * Equals `devicePixelRatio` when `autoResizeCanvas` is `true`. Use as the x-scale
229
+ * factor in `ctx.scale()` to draw at CSS pixel resolution.
230
+ *
231
+ * @example
232
+ * vc.addEventListener('rc-virtual-canvas-render', ({ detail: { viewRect } }) => {
233
+ * ctx.save();
234
+ * ctx.scale(vc.canvasScaleX, vc.canvasScaleY);
235
+ * drawScene(ctx, viewRect);
236
+ * ctx.restore();
237
+ * });
238
+ */
239
+ get canvasScaleX() {
240
+ const t = this._getCanvasClientRect();
241
+ return this._getViewportScaleX(t);
242
+ }
243
+ /**
244
+ * Ratio of canvas backing-store pixels to CSS pixels along the y-axis.
245
+ *
246
+ * Equals `devicePixelRatio` when `autoResizeCanvas` is `true`. Use as the y-scale
247
+ * factor in `ctx.scale()` to draw at CSS pixel resolution.
248
+ */
249
+ get canvasScaleY() {
250
+ const t = this._getCanvasClientRect();
251
+ return this._getViewportScaleY(t);
252
+ }
253
+ /** Maps pointer and mouse events to content coordinates and dispatches `rc-virtual-canvas-pointer` */
174
254
  _onPointerEvent(t) {
175
- if (this._handledPointerEvents.has(t) || this._isOverlayEvent(t)) return;
255
+ if (this._handledPointerEvents.has(t) || this._isOverlayEvent(t))
256
+ return;
176
257
  this._handledPointerEvents.add(t);
177
258
  const e = this.clientToContent(t.clientX, t.clientY);
178
259
  this.dispatchEvent(
@@ -199,10 +280,13 @@ const v = class v extends p {
199
280
  ) || t.preventDefault();
200
281
  }
201
282
  _isOverlayEvent(t) {
202
- return t.composedPath().some(
203
- (e) => e instanceof HTMLSlotElement && e.name === "overlay"
204
- );
283
+ return t.composedPath().some((e) => e instanceof HTMLSlotElement && e.name === "overlay");
205
284
  }
285
+ /**
286
+ * RAF callback. Dispatches `rc-virtual-canvas-render` and re-schedules in continuous mode.
287
+ *
288
+ * @param time - High-resolution timestamp from `requestAnimationFrame`
289
+ */
206
290
  _update(t) {
207
291
  if (this._rafHandle = 0, !this.isConnected || this._$canvas == null)
208
292
  return;
@@ -220,17 +304,17 @@ const v = class v extends p {
220
304
  })
221
305
  ), this.renderMode === "continuous" && this._scheduleRender("animation-frame");
222
306
  }
307
+ /** Registers pointer event listeners and starts rendering in continuous mode */
223
308
  connectedCallback() {
224
309
  super.connectedCallback(), this.addEventListener("pointerdown", this._boundPointerEvent), this.addEventListener("pointermove", this._boundPointerEvent), this.addEventListener("pointerup", this._boundPointerEvent), this.addEventListener("click", this._boundPointerEvent), this.addEventListener("dblclick", this._boundPointerEvent), this.addEventListener("contextmenu", this._boundPointerEvent), this._rafHandle && (cancelAnimationFrame(this._rafHandle), this._rafHandle = 0), this.renderMode === "continuous" && this._scheduleRender("animation-frame");
225
310
  }
311
+ /** Cancels any pending animation frame, disconnects the resize observer, and removes pointer listeners */
226
312
  disconnectedCallback() {
227
313
  super.disconnectedCallback(), cancelAnimationFrame(this._rafHandle), this._rafHandle = 0, this._resizeObserver.disconnect(), this.removeEventListener("pointerdown", this._boundPointerEvent), this.removeEventListener("pointermove", this._boundPointerEvent), this.removeEventListener("pointerup", this._boundPointerEvent), this.removeEventListener("click", this._boundPointerEvent), this.removeEventListener("dblclick", this._boundPointerEvent), this.removeEventListener("contextmenu", this._boundPointerEvent);
228
314
  }
315
+ /** Reacts to `renderMode`, `autoResizeCanvas`, and `imageRendering` property changes */
229
316
  updated(t) {
230
- t.has("renderMode") && (this.renderMode === "continuous" ? this._scheduleRender("animation-frame") : this._pendingRenderReason === "animation-frame" ? (this._rafHandle && (cancelAnimationFrame(this._rafHandle), this._rafHandle = 0), this._pendingRenderReason = void 0) : this.renderMode === "viewport-change" && this._scheduleRender("viewport-change")), t.has("autoResizeCanvas") && this._syncCanvasBackingStore(), t.has("imageRendering") && this.style.setProperty(
231
- "--rc-virtual-canvas-image-rendering",
232
- this.imageRendering
233
- );
317
+ t.has("renderMode") && (this.renderMode === "continuous" ? this._scheduleRender("animation-frame") : this._pendingRenderReason === "animation-frame" ? (this._rafHandle && (cancelAnimationFrame(this._rafHandle), this._rafHandle = 0), this._pendingRenderReason = void 0) : this.renderMode === "viewport-change" && this._scheduleRender("viewport-change")), t.has("autoResizeCanvas") && this._syncCanvasBackingStore(), t.has("imageRendering") && this.style.setProperty("--rc-virtual-canvas-image-rendering", this.imageRendering);
234
318
  }
235
319
  _scheduleRender(t) {
236
320
  this.renderMode === "manual" && t !== "manual" || this.renderMode === "viewport-change" && t === "animation-frame" || (this._pendingRenderReason = t, !(!this.isConnected || this._rafHandle) && (this._rafHandle = window.requestAnimationFrame(this._boundUpdate)));
@@ -272,6 +356,7 @@ const v = class v extends p {
272
356
  <div id="overlay" part="overlay">
273
357
  <slot name="overlay"></slot>
274
358
  </div>
359
+
275
360
  <div
276
361
  id="placeholder"
277
362
  style=${b({
@@ -309,15 +394,15 @@ o([
309
394
  u("#placeholder", !0)
310
395
  ], n.prototype, "_$placeholder", 2);
311
396
  o([
312
- l()
397
+ d()
313
398
  ], n.prototype, "_$canvas", 2);
314
399
  o([
315
- l()
400
+ d()
316
401
  ], n.prototype, "_viewRect", 2);
317
402
  o([
318
- l()
403
+ d()
319
404
  ], n.prototype, "_contentRect", 2);
320
405
  export {
321
406
  n as R
322
407
  };
323
- //# sourceMappingURL=rc-virtual-canvas-BJQbqNEu.js.map
408
+ //# sourceMappingURL=rc-virtual-canvas-CUVahMP7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rc-virtual-canvas-CUVahMP7.js","sources":["../src/rc-virtual-canvas.styles.ts","../src/rc-virtual-canvas.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const virtualCanvasStyles = css`\n *,\n *::before,\n *::after {\n box-sizing: border-box;\n }\n\n :host {\n position: relative;\n display: block;\n width: max-content;\n line-height: 0; /* remove extra space under slotted <canvas> element(s) */\n }\n\n :host([hidden]) {\n display: none;\n }\n\n #root {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n\n overflow: auto;\n scrollbar-width: var(--rc-virtual-canvas-scrollbar-width, thin);\n }\n\n #root::-webkit-scrollbar {\n width: var(--rc-virtual-canvas-scrollbar-size, auto);\n height: var(--rc-virtual-canvas-scrollbar-size, auto);\n }\n\n #root::-webkit-scrollbar-thumb {\n background: var(--rc-virtual-canvas-scrollbar-thumb-background, ButtonFace);\n }\n\n #root::-webkit-scrollbar-button {\n background: var(--rc-virtual-canvas-scrollbar-button-background, ButtonFace);\n }\n\n #root::-webkit-scrollbar-track {\n background: var(--rc-virtual-canvas-scrollbar-track-background, Canvas);\n }\n\n #placeholder {\n pointer-events: none;\n }\n\n #overlay {\n position: sticky;\n left: 0;\n top: 0;\n z-index: 1;\n display: block;\n width: 100%;\n height: 0;\n pointer-events: none;\n }\n\n ::slotted([slot='overlay']) {\n pointer-events: auto;\n }\n\n ::slotted(canvas) {\n image-rendering: var(--rc-virtual-canvas-image-rendering, auto);\n }\n`;\n\nexport default virtualCanvasStyles;\n","import { LitElement, html } from 'lit';\nimport { property, query, state } from 'lit/decorators.js';\nimport { styleMap } from 'lit/directives/style-map.js';\n\nimport virtualCanvasStyles from './rc-virtual-canvas.styles';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-virtual-canvas': RCVirtualCanvas;\n }\n\n interface HTMLElementEventMap {\n 'rc-virtual-canvas-render': CustomEvent<RCVirtualCanvasRenderInit>;\n 'rc-virtual-canvas-pointer': CustomEvent<RCVirtualCanvasPointerInit>;\n }\n}\n\n/**\n * Scrollable virtual canvas for rendering large coordinate-space content.\n *\n * `x` and `y` are scroll offsets in CSS pixels; `width` and `height` are the canvas\n * backing-store dimensions in device pixels (DPR-accurate when `autoResizeCanvas` is `true`).\n */\nexport type RCVirtualCanvasViewRect = Readonly<{\n /** Scroll offset x in content space (CSS px) */\n x: number;\n\n /** Scroll offset y in content space (CSS px) */\n y: number;\n\n /** Visible width in device pixels */\n width: number;\n\n /** Visible height in device pixels */\n height: number;\n}>;\n\n/** Controls when `rc-virtual-canvas-render` events are dispatched */\nexport type RCVirtualCanvasRenderMode = 'continuous' | 'viewport-change' | 'manual';\n\n/** Identifies what triggered a render event */\nexport type RCVirtualCanvasRenderReason = 'animation-frame' | 'viewport-change' | 'manual';\n\n/** Content-space coordinate pair returned by coordinate-conversion methods */\nexport type RCVirtualCanvasPoint = Readonly<{\n /** Content space x coordinate in CSS pixels */\n x: number;\n\n /** Content space y coordinate in CSS pixels */\n y: number;\n}>;\n\n/**\n * Detail shape for `rc-virtual-canvas-pointer` events.\n *\n * Carries the source event's coordinates in both browser-client and content space,\n * along with all modifier key state.\n */\nexport type RCVirtualCanvasPointerInit = {\n /** DOM event type (e.g. `'pointerdown'`, `'click'`) */\n type: string;\n\n /** Browser client x coordinate from the source event */\n clientX: number;\n\n /** Browser client y coordinate from the source event */\n clientY: number;\n\n /** Content x coordinate mapped through the current backing-store scale */\n contentX: number;\n\n /** Content y coordinate mapped through the current backing-store scale */\n contentY: number;\n\n /** Frozen viewport snapshot at the moment the pointer event fired */\n viewRect: RCVirtualCanvasViewRect;\n\n /** Mouse button index from the source event */\n button: number;\n\n /** Active button bitmask from the source event */\n buttons: number;\n\n /** Whether the Alt key was held */\n altKey: boolean;\n\n /** Whether the Ctrl key was held */\n ctrlKey: boolean;\n\n /** Whether the Shift key was held */\n shiftKey: boolean;\n\n /** Whether the Meta key was held */\n metaKey: boolean;\n\n /** The original DOM event that produced this detail */\n sourceEvent: PointerEvent | MouseEvent;\n};\n\n/** CSS `image-rendering` value applied to the slotted canvas */\nexport type RCVirtualCanvasImageRendering = 'auto' | 'crisp-edges' | 'pixelated';\n\n/** Detail shape for `rc-virtual-canvas-render` events */\nexport type RCVirtualCanvasRenderInit = {\n /** High-resolution timestamp from `requestAnimationFrame` */\n time: DOMHighResTimeStamp;\n\n /** What triggered this render */\n reason: RCVirtualCanvasRenderReason;\n\n /** Frozen snapshot of the viewport for this frame */\n viewRect: RCVirtualCanvasViewRect;\n\n /** Frozen snapshot of the full content bounds for this frame */\n contentRect: RCVirtualCanvasViewRect;\n};\n\nfunction createRectSnapshot(rect: RCVirtualCanvasViewRect) {\n return Object.freeze({\n x: rect.x,\n y: rect.y,\n width: rect.width,\n height: rect.height,\n });\n}\n\n/**\n * A scrollable virtual canvas component.\n *\n * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-virtual-canvas rc-virtual-canvas docs}\n *\n * @slot - The HTMLCanvasElement\n * @slot overlay - Optional viewport-positioned content rendered inside the\n * scroll container above the canvas.\n *\n * @fires rc-virtual-canvas-render - Fires with viewport data when the canvas should redraw.\n * @fires rc-virtual-canvas-pointer - Fires pointer/mouse input mapped to virtual content coordinates.\n */\nexport class RCVirtualCanvas extends LitElement {\n static styles = [virtualCanvasStyles];\n\n private _rafHandle: number = 0;\n private _pendingRenderReason?: RCVirtualCanvasRenderReason;\n private readonly _handledPointerEvents = new WeakSet<Event>();\n\n // Stored bound reference so the same closure is used for scheduling and\n // cancellation — `bind()` returns a new function every call.\n private readonly _boundUpdate = (time: DOMHighResTimeStamp) => this._update(time);\n\n private readonly _boundPointerEvent = (event: PointerEvent | MouseEvent) =>\n this._onPointerEvent(event);\n\n /** Pixel width of the virtual content */\n @property({ type: Number })\n set contentWidth(val: number) {\n const oldValue = this._contentWidth;\n\n this._contentWidth = val;\n\n this._contentRect.width = this._contentWidth;\n this.requestUpdate('contentWidth', oldValue);\n this._scheduleRender('viewport-change');\n }\n get contentWidth() {\n return this._contentWidth;\n }\n protected _contentWidth: number = 0;\n\n /** Pixel height of the virtual content */\n @property({ type: Number })\n set contentHeight(val: number) {\n const oldValue = this._contentHeight;\n\n this._contentHeight = val;\n\n this._contentRect.height = this._contentHeight;\n this.requestUpdate('contentHeight', oldValue);\n this._scheduleRender('viewport-change');\n }\n get contentHeight() {\n return this._contentHeight;\n }\n protected _contentHeight: number = 0;\n\n /** When true, keep the slotted canvas backing store aligned to the viewport. */\n @property({ attribute: 'auto-resize-canvas', type: Boolean })\n autoResizeCanvas: boolean = true;\n\n /** Controls when render events are dispatched. */\n @property({ attribute: 'render-mode' })\n renderMode: RCVirtualCanvasRenderMode = 'continuous';\n\n /** Convenience image-rendering value applied to the slotted canvas. */\n @property({ attribute: 'image-rendering' })\n imageRendering: RCVirtualCanvasImageRendering = 'auto';\n\n /** Shadow DOM scroll container that owns pointer interaction and native scrolling */\n @query('#root', true)\n protected _$root!: HTMLDivElement;\n\n /** Absolutely-positioned div that establishes the virtual scroll range */\n @query('#placeholder', true)\n protected _$placeholder!: HTMLDivElement;\n\n /** Currently-tracked slotted canvas element; `null` when no canvas is slotted */\n @state()\n protected _$canvas: HTMLCanvasElement | null = null;\n\n /** Mutable current-frame viewport geometry; call `getViewRect()` for a frozen snapshot */\n @state()\n protected _viewRect = {\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n };\n\n /** Mutable content bounds; updated when `contentWidth` or `contentHeight` change */\n @state()\n protected _contentRect = {\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n };\n\n /** Watches the slotted canvas for DPR-accurate dimension changes */\n protected _resizeObserver = new ResizeObserver((entries: ResizeObserverEntry[]) => {\n for (const entry of entries) {\n const devicePixelBoxSize = Array.isArray(entry.devicePixelContentBoxSize)\n ? entry.devicePixelContentBoxSize[0]\n : entry.devicePixelContentBoxSize;\n\n this._viewRect.width = this._getMeasuredCanvasWidth(\n devicePixelBoxSize?.inlineSize ??\n Math.round(entry.contentRect.width * window.devicePixelRatio),\n );\n\n this._viewRect.height = this._getMeasuredCanvasHeight(\n devicePixelBoxSize?.blockSize ??\n Math.round(entry.contentRect.height * window.devicePixelRatio),\n );\n\n this._syncCanvasBackingStore();\n this._scheduleRender('viewport-change');\n }\n });\n\n /** Reads scroll position from the root container and schedules a render */\n protected _onScroll() {\n this._viewRect.x = this._$root.scrollLeft;\n this._viewRect.y = this._$root.scrollTop;\n\n this._scheduleRender('viewport-change');\n }\n\n /** Replaces canvas tracking and re-registers the resize observer when slot content changes */\n protected _onSlotChange(e: Event) {\n this._resizeObserver.disconnect();\n\n this._$canvas =\n (e.currentTarget as HTMLSlotElement)\n .assignedElements()\n .filter((el) => el instanceof HTMLCanvasElement)\n .at(0) ?? null;\n\n if (this._$canvas != null) {\n this._viewRect = {\n x: this._$root.scrollLeft ?? 0,\n y: this._$root.scrollTop ?? 0,\n width: this._getMeasuredCanvasWidth(this._$canvas.clientWidth),\n height: this._getMeasuredCanvasHeight(this._$canvas.clientHeight),\n };\n\n this._syncCanvasBackingStore();\n this._resizeObserver.observe(this._$canvas, {\n box: 'device-pixel-content-box',\n });\n\n this._scheduleRender('viewport-change');\n }\n }\n\n /**\n * Returns a frozen snapshot of the current viewport rectangle.\n *\n * Safe to keep between frames — the returned object will not be mutated\n * by future scroll or resize work.\n */\n getViewRect() {\n return createRectSnapshot(this._viewRect);\n }\n\n /**\n * Scrolls so the content coordinate is at the viewport origin.\n *\n * @param x - Content x coordinate in CSS pixels\n * @param y - Content y coordinate in CSS pixels\n * @param options - Native scroll options passed to scrollTo\n */\n scrollToContent(x: number, y: number, options: ScrollOptions = {}) {\n this._$root.scrollTo({\n ...options,\n left: x,\n top: y,\n });\n\n this._viewRect.x = this._$root.scrollLeft;\n this._viewRect.y = this._$root.scrollTop;\n this._scheduleRender('viewport-change');\n }\n\n /**\n * Scrolls so the content coordinate is centered in the viewport.\n *\n * @param x - Content x coordinate in CSS pixels\n * @param y - Content y coordinate in CSS pixels\n * @param options - Native scroll options passed to scrollTo\n */\n centerOnContent(x: number, y: number, options: ScrollOptions = {}) {\n this.scrollToContent(\n x - this._$root.clientWidth * 0.5,\n y - this._$root.clientHeight * 0.5,\n options,\n );\n }\n\n /**\n * Converts browser client coordinates to content coordinates using the current\n * backing-store scale.\n *\n * @param clientX - Browser client x coordinate (e.g. from a pointer event)\n * @param clientY - Browser client y coordinate\n *\n * @example\n * vc.addEventListener('pointerdown', (e) => {\n * const { x, y } = vc.clientToContent(e.clientX, e.clientY);\n * placeMarkerAt(x, y);\n * });\n */\n clientToContent(clientX: number, clientY: number) {\n const canvasRect = this._getCanvasClientRect();\n const scaleX = this._getViewportScaleX(canvasRect);\n const scaleY = this._getViewportScaleY(canvasRect);\n\n return Object.freeze({\n x: this._viewRect.x + (clientX - canvasRect.left) * scaleX,\n y: this._viewRect.y + (clientY - canvasRect.top) * scaleY,\n });\n }\n\n /**\n * Converts content coordinates back to browser client coordinates.\n *\n * @param x - Content x coordinate in CSS pixels\n * @param y - Content y coordinate in CSS pixels\n *\n * @example\n * const { x, y } = vc.contentToClient(contentX, contentY);\n * tooltip.style.left = `${x}px`;\n * tooltip.style.top = `${y}px`;\n */\n contentToClient(x: number, y: number) {\n const canvasRect = this._getCanvasClientRect();\n const scaleX = this._getViewportScaleX(canvasRect);\n const scaleY = this._getViewportScaleY(canvasRect);\n\n return Object.freeze({\n x: canvasRect.left + (x - this._viewRect.x) / scaleX,\n y: canvasRect.top + (y - this._viewRect.y) / scaleY,\n });\n }\n\n /**\n * Queues a render event on the next animation frame.\n *\n * Required when `renderMode` is `'manual'`. In other modes the component\n * schedules renders automatically; calling this with reason `'animation-frame'`\n * is a no-op when `renderMode` is `'viewport-change'`.\n *\n * @param reason - Render reason reported in the event detail\n */\n requestRender(reason: RCVirtualCanvasRenderReason = 'manual') {\n this._scheduleRender(reason);\n }\n\n /**\n * Ratio of canvas backing-store pixels to CSS pixels along the x-axis.\n *\n * Equals `devicePixelRatio` when `autoResizeCanvas` is `true`. Use as the x-scale\n * factor in `ctx.scale()` to draw at CSS pixel resolution.\n *\n * @example\n * vc.addEventListener('rc-virtual-canvas-render', ({ detail: { viewRect } }) => {\n * ctx.save();\n * ctx.scale(vc.canvasScaleX, vc.canvasScaleY);\n * drawScene(ctx, viewRect);\n * ctx.restore();\n * });\n */\n get canvasScaleX(): number {\n const canvasRect = this._getCanvasClientRect();\n\n return this._getViewportScaleX(canvasRect);\n }\n\n /**\n * Ratio of canvas backing-store pixels to CSS pixels along the y-axis.\n *\n * Equals `devicePixelRatio` when `autoResizeCanvas` is `true`. Use as the y-scale\n * factor in `ctx.scale()` to draw at CSS pixel resolution.\n */\n get canvasScaleY(): number {\n const canvasRect = this._getCanvasClientRect();\n\n return this._getViewportScaleY(canvasRect);\n }\n\n /** Maps pointer and mouse events to content coordinates and dispatches `rc-virtual-canvas-pointer` */\n protected _onPointerEvent(event: PointerEvent | MouseEvent) {\n if (this._handledPointerEvents.has(event)) {\n return;\n }\n\n if (this._isOverlayEvent(event)) {\n return;\n }\n\n this._handledPointerEvents.add(event);\n\n const contentPoint = this.clientToContent(event.clientX, event.clientY);\n\n const shouldContinue = this.dispatchEvent(\n new CustomEvent<RCVirtualCanvasPointerInit>('rc-virtual-canvas-pointer', {\n bubbles: true,\n composed: true,\n cancelable: true,\n detail: {\n type: event.type,\n clientX: event.clientX,\n clientY: event.clientY,\n contentX: contentPoint.x,\n contentY: contentPoint.y,\n viewRect: createRectSnapshot(this._viewRect),\n button: event.button,\n buttons: event.buttons,\n altKey: event.altKey,\n ctrlKey: event.ctrlKey,\n shiftKey: event.shiftKey,\n metaKey: event.metaKey,\n sourceEvent: event,\n },\n }),\n );\n\n if (!shouldContinue) event.preventDefault();\n }\n\n private _isOverlayEvent(event: Event): boolean {\n return event\n .composedPath()\n .some((node) => node instanceof HTMLSlotElement && node.name === 'overlay');\n }\n\n /**\n * RAF callback. Dispatches `rc-virtual-canvas-render` and re-schedules in continuous mode.\n *\n * @param time - High-resolution timestamp from `requestAnimationFrame`\n */\n protected _update(time: DOMHighResTimeStamp) {\n this._rafHandle = 0;\n\n if (!this.isConnected || this._$canvas == null) {\n return;\n }\n\n const reason =\n this._pendingRenderReason ??\n (this.renderMode === 'continuous' ? 'animation-frame' : 'manual');\n\n this._pendingRenderReason = undefined;\n\n this.dispatchEvent(\n new CustomEvent<RCVirtualCanvasRenderInit>('rc-virtual-canvas-render', {\n bubbles: true,\n composed: true,\n detail: {\n time,\n reason,\n viewRect: createRectSnapshot(this._viewRect),\n contentRect: createRectSnapshot(this._contentRect),\n },\n }),\n );\n\n if (this.renderMode === 'continuous') {\n this._scheduleRender('animation-frame');\n }\n }\n\n /** Registers pointer event listeners and starts rendering in continuous mode */\n override connectedCallback() {\n super.connectedCallback();\n\n this.addEventListener('pointerdown', this._boundPointerEvent);\n this.addEventListener('pointermove', this._boundPointerEvent);\n this.addEventListener('pointerup', this._boundPointerEvent);\n this.addEventListener('click', this._boundPointerEvent);\n this.addEventListener('dblclick', this._boundPointerEvent);\n this.addEventListener('contextmenu', this._boundPointerEvent);\n\n if (this._rafHandle) {\n cancelAnimationFrame(this._rafHandle);\n this._rafHandle = 0;\n }\n\n if (this.renderMode === 'continuous') {\n this._scheduleRender('animation-frame');\n }\n }\n\n /** Cancels any pending animation frame, disconnects the resize observer, and removes pointer listeners */\n override disconnectedCallback() {\n super.disconnectedCallback();\n\n cancelAnimationFrame(this._rafHandle);\n this._rafHandle = 0;\n this._resizeObserver.disconnect();\n\n this.removeEventListener('pointerdown', this._boundPointerEvent);\n this.removeEventListener('pointermove', this._boundPointerEvent);\n this.removeEventListener('pointerup', this._boundPointerEvent);\n this.removeEventListener('click', this._boundPointerEvent);\n this.removeEventListener('dblclick', this._boundPointerEvent);\n this.removeEventListener('contextmenu', this._boundPointerEvent);\n }\n\n /** Reacts to `renderMode`, `autoResizeCanvas`, and `imageRendering` property changes */\n protected override updated(changed: Map<string, unknown>) {\n if (changed.has('renderMode')) {\n if (this.renderMode === 'continuous') {\n this._scheduleRender('animation-frame');\n } else if (this._pendingRenderReason === 'animation-frame') {\n if (this._rafHandle) {\n cancelAnimationFrame(this._rafHandle);\n\n this._rafHandle = 0;\n }\n\n this._pendingRenderReason = undefined;\n } else if (this.renderMode === 'viewport-change') {\n this._scheduleRender('viewport-change');\n }\n }\n\n if (changed.has('autoResizeCanvas')) {\n this._syncCanvasBackingStore();\n }\n\n if (changed.has('imageRendering')) {\n this.style.setProperty('--rc-virtual-canvas-image-rendering', this.imageRendering);\n }\n }\n\n private _scheduleRender(reason: RCVirtualCanvasRenderReason) {\n if (this.renderMode === 'manual' && reason !== 'manual') {\n return;\n }\n\n if (this.renderMode === 'viewport-change' && reason === 'animation-frame') {\n return;\n }\n\n this._pendingRenderReason = reason;\n\n if (!this.isConnected || this._rafHandle) {\n return;\n }\n\n this._rafHandle = window.requestAnimationFrame(this._boundUpdate);\n }\n\n private _syncCanvasBackingStore() {\n if (!this.autoResizeCanvas || this._$canvas == null) {\n return;\n }\n\n const width = Math.max(0, Math.round(this._viewRect.width));\n const height = Math.max(0, Math.round(this._viewRect.height));\n\n if (this._$canvas.width !== width) {\n this._$canvas.width = width;\n }\n\n if (this._$canvas.height !== height) {\n this._$canvas.height = height;\n }\n }\n\n private _getCanvasClientRect() {\n if (this._$canvas == null) {\n return this.getBoundingClientRect();\n }\n\n return this._$canvas.getBoundingClientRect();\n }\n\n private _getMeasuredCanvasWidth(fallbackWidth: number) {\n if (!this.autoResizeCanvas && this._$canvas && this._$canvas.width > 0) {\n return this._$canvas.width;\n }\n\n return fallbackWidth;\n }\n\n private _getMeasuredCanvasHeight(fallbackHeight: number) {\n if (!this.autoResizeCanvas && this._$canvas && this._$canvas.height > 0) {\n return this._$canvas.height;\n }\n\n return fallbackHeight;\n }\n\n private _getViewportScaleX(canvasRect: DOMRect) {\n return canvasRect.width > 0 ? this._viewRect.width / canvasRect.width : 1;\n }\n\n private _getViewportScaleY(canvasRect: DOMRect) {\n return canvasRect.height > 0 ? this._viewRect.height / canvasRect.height : 1;\n }\n\n render() {\n return html`\n <div\n id=\"root\"\n part=\"scroller\"\n @scroll=${this._onScroll}\n @pointerdown=${this._onPointerEvent}\n @pointermove=${this._onPointerEvent}\n @pointerup=${this._onPointerEvent}\n @click=${this._onPointerEvent}\n @dblclick=${this._onPointerEvent}\n @contextmenu=${this._onPointerEvent}\n >\n <div id=\"overlay\" part=\"overlay\">\n <slot name=\"overlay\"></slot>\n </div>\n\n <div\n id=\"placeholder\"\n style=${styleMap({\n width: `${this.contentWidth}px`,\n height: `${this.contentHeight}px`,\n })}\n ></div>\n </div>\n\n <slot @slotchange=${this._onSlotChange}></slot>\n `;\n }\n}\n\nexport default RCVirtualCanvas;\n"],"names":["virtualCanvasStyles","css","createRectSnapshot","rect","_RCVirtualCanvas","LitElement","time","event","entries","entry","devicePixelBoxSize","val","oldValue","e","el","x","y","options","clientX","clientY","canvasRect","scaleX","scaleY","reason","contentPoint","node","changed","width","height","fallbackWidth","fallbackHeight","html","styleMap","RCVirtualCanvas","__decorateClass","property","query","state"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { R as a } from "./rc-virtual-canvas-BJQbqNEu.js";
1
+ import { R as a } from "./rc-virtual-canvas-CUVahMP7.js";
2
2
  customElements.get("rc-virtual-canvas") || customElements.define("rc-virtual-canvas", a);
3
3
  export {
4
4
  a as RCVirtualCanvas
@@ -1,4 +1,4 @@
1
- import { R as o } from "./rc-virtual-canvas-BJQbqNEu.js";
1
+ import { R as o } from "./rc-virtual-canvas-CUVahMP7.js";
2
2
  export {
3
3
  o as RCVirtualCanvas
4
4
  };
@@ -1,53 +1,96 @@
1
1
  import { LitElement } from 'lit';
2
+ declare global {
3
+ interface HTMLElementTagNameMap {
4
+ 'rc-virtual-canvas': RCVirtualCanvas;
5
+ }
6
+ interface HTMLElementEventMap {
7
+ 'rc-virtual-canvas-render': CustomEvent<RCVirtualCanvasRenderInit>;
8
+ 'rc-virtual-canvas-pointer': CustomEvent<RCVirtualCanvasPointerInit>;
9
+ }
10
+ }
11
+ /**
12
+ * Scrollable virtual canvas for rendering large coordinate-space content.
13
+ *
14
+ * `x` and `y` are scroll offsets in CSS pixels; `width` and `height` are the canvas
15
+ * backing-store dimensions in device pixels (DPR-accurate when `autoResizeCanvas` is `true`).
16
+ */
2
17
  export type RCVirtualCanvasViewRect = Readonly<{
18
+ /** Scroll offset x in content space (CSS px) */
3
19
  x: number;
20
+ /** Scroll offset y in content space (CSS px) */
4
21
  y: number;
22
+ /** Visible width in device pixels */
5
23
  width: number;
24
+ /** Visible height in device pixels */
6
25
  height: number;
7
26
  }>;
27
+ /** Controls when `rc-virtual-canvas-render` events are dispatched */
8
28
  export type RCVirtualCanvasRenderMode = 'continuous' | 'viewport-change' | 'manual';
29
+ /** Identifies what triggered a render event */
9
30
  export type RCVirtualCanvasRenderReason = 'animation-frame' | 'viewport-change' | 'manual';
31
+ /** Content-space coordinate pair returned by coordinate-conversion methods */
10
32
  export type RCVirtualCanvasPoint = Readonly<{
33
+ /** Content space x coordinate in CSS pixels */
11
34
  x: number;
35
+ /** Content space y coordinate in CSS pixels */
12
36
  y: number;
13
37
  }>;
38
+ /**
39
+ * Detail shape for `rc-virtual-canvas-pointer` events.
40
+ *
41
+ * Carries the source event's coordinates in both browser-client and content space,
42
+ * along with all modifier key state.
43
+ */
14
44
  export type RCVirtualCanvasPointerInit = {
45
+ /** DOM event type (e.g. `'pointerdown'`, `'click'`) */
15
46
  type: string;
47
+ /** Browser client x coordinate from the source event */
16
48
  clientX: number;
49
+ /** Browser client y coordinate from the source event */
17
50
  clientY: number;
51
+ /** Content x coordinate mapped through the current backing-store scale */
18
52
  contentX: number;
53
+ /** Content y coordinate mapped through the current backing-store scale */
19
54
  contentY: number;
55
+ /** Frozen viewport snapshot at the moment the pointer event fired */
20
56
  viewRect: RCVirtualCanvasViewRect;
57
+ /** Mouse button index from the source event */
21
58
  button: number;
59
+ /** Active button bitmask from the source event */
22
60
  buttons: number;
61
+ /** Whether the Alt key was held */
23
62
  altKey: boolean;
63
+ /** Whether the Ctrl key was held */
24
64
  ctrlKey: boolean;
65
+ /** Whether the Shift key was held */
25
66
  shiftKey: boolean;
67
+ /** Whether the Meta key was held */
26
68
  metaKey: boolean;
69
+ /** The original DOM event that produced this detail */
27
70
  sourceEvent: PointerEvent | MouseEvent;
28
71
  };
72
+ /** CSS `image-rendering` value applied to the slotted canvas */
29
73
  export type RCVirtualCanvasImageRendering = 'auto' | 'crisp-edges' | 'pixelated';
74
+ /** Detail shape for `rc-virtual-canvas-render` events */
30
75
  export type RCVirtualCanvasRenderInit = {
76
+ /** High-resolution timestamp from `requestAnimationFrame` */
31
77
  time: DOMHighResTimeStamp;
78
+ /** What triggered this render */
32
79
  reason: RCVirtualCanvasRenderReason;
80
+ /** Frozen snapshot of the viewport for this frame */
33
81
  viewRect: RCVirtualCanvasViewRect;
82
+ /** Frozen snapshot of the full content bounds for this frame */
34
83
  contentRect: RCVirtualCanvasViewRect;
35
84
  };
36
- declare global {
37
- interface HTMLElementTagNameMap {
38
- 'rc-virtual-canvas': RCVirtualCanvas;
39
- }
40
- interface HTMLElementEventMap {
41
- 'rc-virtual-canvas-render': CustomEvent<RCVirtualCanvasRenderInit>;
42
- 'rc-virtual-canvas-pointer': CustomEvent<RCVirtualCanvasPointerInit>;
43
- }
44
- }
45
85
  /**
46
- * An accessible virtual scrollable canvas component.
86
+ * A scrollable virtual canvas component.
87
+ *
88
+ * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-virtual-canvas rc-virtual-canvas docs}
47
89
  *
48
90
  * @slot - The HTMLCanvasElement
49
91
  * @slot overlay - Optional viewport-positioned content rendered inside the
50
92
  * scroll container above the canvas.
93
+ *
51
94
  * @fires rc-virtual-canvas-render - Fires with viewport data when the canvas should redraw.
52
95
  * @fires rc-virtual-canvas-pointer - Fires pointer/mouse input mapped to virtual content coordinates.
53
96
  */
@@ -72,46 +115,138 @@ export declare class RCVirtualCanvas extends LitElement {
72
115
  renderMode: RCVirtualCanvasRenderMode;
73
116
  /** Convenience image-rendering value applied to the slotted canvas. */
74
117
  imageRendering: RCVirtualCanvasImageRendering;
118
+ /** Shadow DOM scroll container that owns pointer interaction and native scrolling */
75
119
  protected _$root: HTMLDivElement;
120
+ /** Absolutely-positioned div that establishes the virtual scroll range */
76
121
  protected _$placeholder: HTMLDivElement;
122
+ /** Currently-tracked slotted canvas element; `null` when no canvas is slotted */
77
123
  protected _$canvas: HTMLCanvasElement | null;
124
+ /** Mutable current-frame viewport geometry; call `getViewRect()` for a frozen snapshot */
78
125
  protected _viewRect: {
79
126
  x: number;
80
127
  y: number;
81
128
  width: number;
82
129
  height: number;
83
130
  };
131
+ /** Mutable content bounds; updated when `contentWidth` or `contentHeight` change */
84
132
  protected _contentRect: {
85
133
  x: number;
86
134
  y: number;
87
135
  width: number;
88
136
  height: number;
89
137
  };
138
+ /** Watches the slotted canvas for DPR-accurate dimension changes */
90
139
  protected _resizeObserver: ResizeObserver;
140
+ /** Reads scroll position from the root container and schedules a render */
91
141
  protected _onScroll(): void;
142
+ /** Replaces canvas tracking and re-registers the resize observer when slot content changes */
92
143
  protected _onSlotChange(e: Event): void;
144
+ /**
145
+ * Returns a frozen snapshot of the current viewport rectangle.
146
+ *
147
+ * Safe to keep between frames — the returned object will not be mutated
148
+ * by future scroll or resize work.
149
+ */
93
150
  getViewRect(): Readonly<{
94
151
  x: number;
95
152
  y: number;
96
153
  width: number;
97
154
  height: number;
98
155
  }>;
156
+ /**
157
+ * Scrolls so the content coordinate is at the viewport origin.
158
+ *
159
+ * @param x - Content x coordinate in CSS pixels
160
+ * @param y - Content y coordinate in CSS pixels
161
+ * @param options - Native scroll options passed to scrollTo
162
+ */
99
163
  scrollToContent(x: number, y: number, options?: ScrollOptions): void;
164
+ /**
165
+ * Scrolls so the content coordinate is centered in the viewport.
166
+ *
167
+ * @param x - Content x coordinate in CSS pixels
168
+ * @param y - Content y coordinate in CSS pixels
169
+ * @param options - Native scroll options passed to scrollTo
170
+ */
100
171
  centerOnContent(x: number, y: number, options?: ScrollOptions): void;
172
+ /**
173
+ * Converts browser client coordinates to content coordinates using the current
174
+ * backing-store scale.
175
+ *
176
+ * @param clientX - Browser client x coordinate (e.g. from a pointer event)
177
+ * @param clientY - Browser client y coordinate
178
+ *
179
+ * @example
180
+ * vc.addEventListener('pointerdown', (e) => {
181
+ * const { x, y } = vc.clientToContent(e.clientX, e.clientY);
182
+ * placeMarkerAt(x, y);
183
+ * });
184
+ */
101
185
  clientToContent(clientX: number, clientY: number): Readonly<{
102
186
  x: number;
103
187
  y: number;
104
188
  }>;
189
+ /**
190
+ * Converts content coordinates back to browser client coordinates.
191
+ *
192
+ * @param x - Content x coordinate in CSS pixels
193
+ * @param y - Content y coordinate in CSS pixels
194
+ *
195
+ * @example
196
+ * const { x, y } = vc.contentToClient(contentX, contentY);
197
+ * tooltip.style.left = `${x}px`;
198
+ * tooltip.style.top = `${y}px`;
199
+ */
105
200
  contentToClient(x: number, y: number): Readonly<{
106
201
  x: number;
107
202
  y: number;
108
203
  }>;
204
+ /**
205
+ * Queues a render event on the next animation frame.
206
+ *
207
+ * Required when `renderMode` is `'manual'`. In other modes the component
208
+ * schedules renders automatically; calling this with reason `'animation-frame'`
209
+ * is a no-op when `renderMode` is `'viewport-change'`.
210
+ *
211
+ * @param reason - Render reason reported in the event detail
212
+ */
109
213
  requestRender(reason?: RCVirtualCanvasRenderReason): void;
214
+ /**
215
+ * Ratio of canvas backing-store pixels to CSS pixels along the x-axis.
216
+ *
217
+ * Equals `devicePixelRatio` when `autoResizeCanvas` is `true`. Use as the x-scale
218
+ * factor in `ctx.scale()` to draw at CSS pixel resolution.
219
+ *
220
+ * @example
221
+ * vc.addEventListener('rc-virtual-canvas-render', ({ detail: { viewRect } }) => {
222
+ * ctx.save();
223
+ * ctx.scale(vc.canvasScaleX, vc.canvasScaleY);
224
+ * drawScene(ctx, viewRect);
225
+ * ctx.restore();
226
+ * });
227
+ */
228
+ get canvasScaleX(): number;
229
+ /**
230
+ * Ratio of canvas backing-store pixels to CSS pixels along the y-axis.
231
+ *
232
+ * Equals `devicePixelRatio` when `autoResizeCanvas` is `true`. Use as the y-scale
233
+ * factor in `ctx.scale()` to draw at CSS pixel resolution.
234
+ */
235
+ get canvasScaleY(): number;
236
+ /** Maps pointer and mouse events to content coordinates and dispatches `rc-virtual-canvas-pointer` */
110
237
  protected _onPointerEvent(event: PointerEvent | MouseEvent): void;
111
238
  private _isOverlayEvent;
239
+ /**
240
+ * RAF callback. Dispatches `rc-virtual-canvas-render` and re-schedules in continuous mode.
241
+ *
242
+ * @param time - High-resolution timestamp from `requestAnimationFrame`
243
+ */
112
244
  protected _update(time: DOMHighResTimeStamp): void;
245
+ /** Registers pointer event listeners and starts rendering in continuous mode */
113
246
  connectedCallback(): void;
247
+ /** Cancels any pending animation frame, disconnects the resize observer, and removes pointer listeners */
114
248
  disconnectedCallback(): void;
249
+ /** Reacts to `renderMode`, `autoResizeCanvas`, and `imageRendering` property changes */
115
250
  protected updated(changed: Map<string, unknown>): void;
116
251
  private _scheduleRender;
117
252
  private _syncCanvasBackingStore;
package/package.json CHANGED
@@ -3,14 +3,14 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.2.0",
7
- "description": "Headless virtual canvas web component built with Lit",
6
+ "version": "0.3.0",
7
+ "description": "Scrollable virtual canvas for rendering large coordinate-space content.",
8
8
  "repository": {
9
9
  "type": "git",
10
10
  "url": "git+https://github.com/richardcarls/rc-webcomponents.git",
11
11
  "directory": "packages/rc-virtual-canvas"
12
12
  },
13
- "homepage": "https://github.com/richardcarls/rc-webcomponents#readme",
13
+ "homepage": "https://richardcarls.github.io/rc-webcomponents/components/rc-virtual-canvas",
14
14
  "license": "MIT",
15
15
  "type": "module",
16
16
  "files": [
@@ -37,17 +37,15 @@
37
37
  ],
38
38
  "customElements": "dist/custom-elements.json",
39
39
  "scripts": {
40
- "dev": "vite",
41
40
  "build": "tsc && vite build && cem analyze",
42
41
  "cem:analyze": "cem analyze",
43
42
  "preview": "vite preview",
44
- "test:browser": "vitest",
45
- "test:browser:chrome": "vitest --project=chromium",
46
- "test:browser:firefox": "vitest --project=firefox"
43
+ "test:browser": "vitest --run",
44
+ "test:browser:chrome": "vitest --run --project=chromium",
45
+ "test:browser:firefox": "vitest --run --project=firefox"
47
46
  },
48
47
  "devDependencies": {
49
48
  "@custom-elements-manifest/analyzer": "0.11.0",
50
- "@guanghechen/rollup-plugin-copy": "^6.0.9",
51
49
  "@types/node": "^25.6.0",
52
50
  "@vitest/browser-playwright": "4.1.5",
53
51
  "lit": "^3.0.0",
Binary file
package/dist/demo.css DELETED
@@ -1,85 +0,0 @@
1
- *, *::before, *::after {
2
- box-sizing: border-box;
3
- }
4
-
5
- :root {
6
- color-scheme: light dark;
7
- --rc-accent: Highlight; /* Chrome doesn't support AccentColor on the open web */
8
- }
9
-
10
- @supports (color: AccentColor) {
11
- :root {
12
- --rc-accent: AccentColor;
13
- }
14
- }
15
-
16
- [data-theme="light"] { color-scheme: light; }
17
- [data-theme="dark"] { color-scheme: dark; }
18
-
19
- [data-theme="solarized-light"] {
20
- color-scheme: light;
21
- --rc-surface: #fdf6e3;
22
- --rc-text: #657b83;
23
- --rc-border: 1px solid #93a1a1;
24
- --rc-shadow: 0 2px 8px rgba(0, 0, 0, .12);
25
- }
26
-
27
- [data-theme="solarized-dark"] {
28
- color-scheme: dark;
29
- --rc-surface: #002b36;
30
- --rc-text: #839496;
31
- --rc-border: 1px solid #586e75;
32
- --rc-shadow: 0 2px 8px rgba(0, 0, 0, .3);
33
- }
34
-
35
- body {
36
- font-family: system-ui, sans-serif;
37
- margin: 0;
38
- }
39
-
40
- .demo-page {
41
- max-width: 60rem;
42
- margin: 0 auto;
43
- padding: 2rem 1.5rem;
44
- }
45
-
46
- .demo-controls {
47
- display: flex;
48
- align-items: center;
49
- gap: 1rem;
50
- margin-bottom: 2rem;
51
- padding-bottom: 1rem;
52
- border-bottom: 1px solid ButtonBorder;
53
- }
54
-
55
- .demo-controls .demo-title {
56
- flex: 1;
57
- }
58
-
59
- .demo-controls h1 {
60
- margin: 0 0 0.15rem;
61
- font-size: 1.5rem;
62
- }
63
-
64
- .demo-controls a {
65
- font-size: 0.8rem;
66
- }
67
-
68
- .demo-section {
69
- margin-bottom: 2rem;
70
- }
71
-
72
- .demo-section h2 {
73
- margin: 0 0 0.5rem;
74
- }
75
-
76
- .theme-picker {
77
- padding: 0.35em 0.75em;
78
- font-family: inherit;
79
- font-size: 0.8rem;
80
- cursor: pointer;
81
- border: 1px solid ButtonBorder;
82
- border-radius: 4px;
83
- background: Canvas;
84
- color: CanvasText;
85
- }
package/dist/demo.js DELETED
@@ -1,40 +0,0 @@
1
- const THEMES = ['', 'light', 'dark', 'solarized-light', 'solarized-dark'];
2
- const LABELS = ['Auto', 'Light', 'Dark', 'Solarized ☀', 'Solarized ☾'];
3
-
4
- const stored = localStorage.getItem('rc-demo-theme') ?? '';
5
- applyTheme(stored);
6
-
7
- function applyTheme(theme) {
8
- if (theme) {
9
- document.documentElement.dataset.theme = theme;
10
- } else {
11
- delete document.documentElement.dataset.theme;
12
- }
13
- }
14
-
15
- function currentIndex() {
16
- const current = document.documentElement.dataset.theme ?? '';
17
- const idx = THEMES.indexOf(current);
18
- return idx === -1 ? 0 : idx;
19
- }
20
-
21
- function updateButtons() {
22
- const label = LABELS[currentIndex()];
23
- document.querySelectorAll('.theme-picker').forEach((btn) => {
24
- btn.textContent = label;
25
- });
26
- }
27
-
28
- window.cycleTheme = function () {
29
- const next = THEMES[(currentIndex() + 1) % THEMES.length];
30
- applyTheme(next);
31
- localStorage.setItem('rc-demo-theme', next);
32
- updateButtons();
33
- };
34
-
35
- document.addEventListener('DOMContentLoaded', () => {
36
- updateButtons();
37
- document.querySelectorAll('.theme-picker').forEach((btn) => {
38
- btn.addEventListener('click', window.cycleTheme);
39
- });
40
- });
@@ -1 +0,0 @@
1
- {"version":3,"file":"rc-virtual-canvas-BJQbqNEu.js","sources":["../src/rc-virtual-canvas.styles.ts","../src/rc-virtual-canvas.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const virtualCanvasStyles = css`\n *,\n *::before,\n *::after {\n box-sizing: border-box;\n }\n\n :host {\n position: relative;\n display: block;\n width: max-content;\n line-height: 0; /* remove extra space under slotted <canvas> element(s) */\n }\n\n :host([hidden]) {\n display: none;\n }\n\n #root {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n\n overflow: auto;\n scrollbar-width: var(--rc-virtual-canvas-scrollbar-width, thin);\n }\n\n #root::-webkit-scrollbar {\n width: var(--rc-virtual-canvas-scrollbar-size, auto);\n height: var(--rc-virtual-canvas-scrollbar-size, auto);\n }\n\n #root::-webkit-scrollbar-thumb {\n background: var(--rc-virtual-canvas-scrollbar-thumb-background, ButtonFace);\n }\n\n #root::-webkit-scrollbar-button {\n background: var(--rc-virtual-canvas-scrollbar-button-background, ButtonFace);\n }\n\n #root::-webkit-scrollbar-track {\n background: var(--rc-virtual-canvas-scrollbar-track-background, Canvas);\n }\n\n #placeholder {\n pointer-events: none;\n }\n\n #overlay {\n position: sticky;\n left: 0;\n top: 0;\n z-index: 1;\n display: block;\n width: 100%;\n height: 0;\n pointer-events: none;\n }\n\n ::slotted([slot='overlay']) {\n pointer-events: auto;\n }\n\n ::slotted(canvas) {\n image-rendering: var(--rc-virtual-canvas-image-rendering, auto);\n }\n`;\n\nexport default virtualCanvasStyles;\n","import { LitElement, html } from 'lit';\nimport { property, query, state } from 'lit/decorators.js';\nimport { styleMap } from 'lit/directives/style-map.js';\n\nimport virtualCanvasStyles from './rc-virtual-canvas.styles';\n\nexport type RCVirtualCanvasViewRect = Readonly<{\n x: number;\n y: number;\n width: number;\n height: number;\n}>;\n\nexport type RCVirtualCanvasRenderMode =\n | 'continuous'\n | 'viewport-change'\n | 'manual';\n\nexport type RCVirtualCanvasRenderReason =\n | 'animation-frame'\n | 'viewport-change'\n | 'manual';\n\nexport type RCVirtualCanvasPoint = Readonly<{\n x: number;\n y: number;\n}>;\n\nexport type RCVirtualCanvasPointerInit = {\n type: string;\n clientX: number;\n clientY: number;\n contentX: number;\n contentY: number;\n viewRect: RCVirtualCanvasViewRect;\n button: number;\n buttons: number;\n altKey: boolean;\n ctrlKey: boolean;\n shiftKey: boolean;\n metaKey: boolean;\n sourceEvent: PointerEvent | MouseEvent;\n};\n\nexport type RCVirtualCanvasImageRendering =\n | 'auto'\n | 'crisp-edges'\n | 'pixelated';\n\nexport type RCVirtualCanvasRenderInit = {\n time: DOMHighResTimeStamp;\n reason: RCVirtualCanvasRenderReason;\n viewRect: RCVirtualCanvasViewRect;\n contentRect: RCVirtualCanvasViewRect;\n};\n\nfunction createRectSnapshot(rect: RCVirtualCanvasViewRect) {\n return Object.freeze({\n x: rect.x,\n y: rect.y,\n width: rect.width,\n height: rect.height,\n });\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-virtual-canvas': RCVirtualCanvas;\n }\n\n interface HTMLElementEventMap {\n 'rc-virtual-canvas-render': CustomEvent<RCVirtualCanvasRenderInit>;\n 'rc-virtual-canvas-pointer': CustomEvent<RCVirtualCanvasPointerInit>;\n }\n}\n\n/**\n * An accessible virtual scrollable canvas component.\n *\n * @slot - The HTMLCanvasElement\n * @slot overlay - Optional viewport-positioned content rendered inside the\n * scroll container above the canvas.\n * @fires rc-virtual-canvas-render - Fires with viewport data when the canvas should redraw.\n * @fires rc-virtual-canvas-pointer - Fires pointer/mouse input mapped to virtual content coordinates.\n */\nexport class RCVirtualCanvas extends LitElement {\n static styles = [virtualCanvasStyles];\n\n private _rafHandle: number = 0;\n private _pendingRenderReason?: RCVirtualCanvasRenderReason;\n private readonly _handledPointerEvents = new WeakSet<Event>();\n\n // Stored bound reference so the same closure is used for scheduling and\n // cancellation — `bind()` returns a new function every call.\n private readonly _boundUpdate = (time: DOMHighResTimeStamp) =>\n this._update(time);\n\n private readonly _boundPointerEvent = (event: PointerEvent | MouseEvent) =>\n this._onPointerEvent(event);\n\n /** Pixel width of the virtual content */\n @property({ type: Number })\n set contentWidth(val: number) {\n const oldValue = this._contentWidth;\n\n this._contentWidth = val;\n\n this._contentRect.width = this._contentWidth;\n this.requestUpdate('contentWidth', oldValue);\n this._scheduleRender('viewport-change');\n }\n get contentWidth() {\n return this._contentWidth;\n }\n protected _contentWidth: number = 0;\n\n /** Pixel height of the virtual content */\n @property({ type: Number })\n set contentHeight(val: number) {\n const oldValue = this._contentHeight;\n\n this._contentHeight = val;\n\n this._contentRect.height = this._contentHeight;\n this.requestUpdate('contentHeight', oldValue);\n this._scheduleRender('viewport-change');\n }\n get contentHeight() {\n return this._contentHeight;\n }\n protected _contentHeight: number = 0;\n\n /** When true, keep the slotted canvas backing store aligned to the viewport. */\n @property({ attribute: 'auto-resize-canvas', type: Boolean })\n autoResizeCanvas: boolean = true;\n\n /** Controls when render events are dispatched. */\n @property({ attribute: 'render-mode' })\n renderMode: RCVirtualCanvasRenderMode = 'continuous';\n\n /** Convenience image-rendering value applied to the slotted canvas. */\n @property({ attribute: 'image-rendering' })\n imageRendering: RCVirtualCanvasImageRendering = 'auto';\n\n @query('#root', true)\n protected _$root!: HTMLDivElement;\n\n @query('#placeholder', true)\n protected _$placeholder!: HTMLDivElement;\n\n @state()\n protected _$canvas: HTMLCanvasElement | null = null;\n\n @state()\n protected _viewRect = {\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n };\n\n @state()\n protected _contentRect = {\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n };\n\n protected _resizeObserver = new ResizeObserver(\n (entries: ResizeObserverEntry[]) => {\n for (const entry of entries) {\n const devicePixelBoxSize = Array.isArray(\n entry.devicePixelContentBoxSize,\n )\n ? entry.devicePixelContentBoxSize[0]\n : entry.devicePixelContentBoxSize;\n\n this._viewRect.width = this._getMeasuredCanvasWidth(\n devicePixelBoxSize?.inlineSize\n ?? Math.round(entry.contentRect.width * window.devicePixelRatio),\n );\n this._viewRect.height = this._getMeasuredCanvasHeight(\n devicePixelBoxSize?.blockSize\n ?? Math.round(entry.contentRect.height * window.devicePixelRatio),\n );\n\n this._syncCanvasBackingStore();\n this._scheduleRender('viewport-change');\n }\n },\n );\n\n protected _onScroll() {\n this._viewRect.x = this._$root.scrollLeft;\n this._viewRect.y = this._$root.scrollTop;\n\n this._scheduleRender('viewport-change');\n }\n\n protected _onSlotChange(e: Event) {\n this._resizeObserver.disconnect();\n\n this._$canvas =\n (e.currentTarget as HTMLSlotElement)\n .assignedElements()\n .filter((el) => el instanceof HTMLCanvasElement)\n .at(0) ?? null;\n\n if (this._$canvas != null) {\n this._viewRect = {\n x: this._$root.scrollLeft ?? 0,\n y: this._$root.scrollTop ?? 0,\n width: this._getMeasuredCanvasWidth(this._$canvas.clientWidth),\n height: this._getMeasuredCanvasHeight(this._$canvas.clientHeight),\n };\n\n this._syncCanvasBackingStore();\n this._resizeObserver.observe(this._$canvas, {\n box: 'device-pixel-content-box',\n });\n this._scheduleRender('viewport-change');\n }\n }\n\n getViewRect() {\n return createRectSnapshot(this._viewRect);\n }\n\n scrollToContent(x: number, y: number, options: ScrollOptions = {}) {\n this._$root.scrollTo({\n ...options,\n left: x,\n top: y,\n });\n\n this._viewRect.x = this._$root.scrollLeft;\n this._viewRect.y = this._$root.scrollTop;\n this._scheduleRender('viewport-change');\n }\n\n centerOnContent(x: number, y: number, options: ScrollOptions = {}) {\n this.scrollToContent(\n x - (this._$root.clientWidth * 0.5),\n y - (this._$root.clientHeight * 0.5),\n options,\n );\n }\n\n clientToContent(clientX: number, clientY: number) {\n const canvasRect = this._getCanvasClientRect();\n const scaleX = this._getViewportScaleX(canvasRect);\n const scaleY = this._getViewportScaleY(canvasRect);\n\n return Object.freeze({\n x: this._viewRect.x + ((clientX - canvasRect.left) * scaleX),\n y: this._viewRect.y + ((clientY - canvasRect.top) * scaleY),\n });\n }\n\n contentToClient(x: number, y: number) {\n const canvasRect = this._getCanvasClientRect();\n const scaleX = this._getViewportScaleX(canvasRect);\n const scaleY = this._getViewportScaleY(canvasRect);\n\n return Object.freeze({\n x: canvasRect.left + ((x - this._viewRect.x) / scaleX),\n y: canvasRect.top + ((y - this._viewRect.y) / scaleY),\n });\n }\n\n requestRender(reason: RCVirtualCanvasRenderReason = 'manual') {\n this._scheduleRender(reason);\n }\n\n protected _onPointerEvent(event: PointerEvent | MouseEvent) {\n if (this._handledPointerEvents.has(event)) return;\n if (this._isOverlayEvent(event)) return;\n\n this._handledPointerEvents.add(event);\n\n const contentPoint = this.clientToContent(event.clientX, event.clientY);\n\n const shouldContinue = this.dispatchEvent(\n new CustomEvent<RCVirtualCanvasPointerInit>('rc-virtual-canvas-pointer', {\n bubbles: true,\n composed: true,\n cancelable: true,\n detail: {\n type: event.type,\n clientX: event.clientX,\n clientY: event.clientY,\n contentX: contentPoint.x,\n contentY: contentPoint.y,\n viewRect: createRectSnapshot(this._viewRect),\n button: event.button,\n buttons: event.buttons,\n altKey: event.altKey,\n ctrlKey: event.ctrlKey,\n shiftKey: event.shiftKey,\n metaKey: event.metaKey,\n sourceEvent: event,\n },\n }),\n );\n\n if (!shouldContinue) event.preventDefault();\n }\n\n private _isOverlayEvent(event: Event): boolean {\n return event.composedPath().some((node) =>\n node instanceof HTMLSlotElement\n && node.name === 'overlay',\n );\n }\n\n protected _update(time: DOMHighResTimeStamp) {\n this._rafHandle = 0;\n\n if (!this.isConnected || this._$canvas == null) {\n return;\n }\n\n const reason = this._pendingRenderReason\n ?? (this.renderMode === 'continuous' ? 'animation-frame' : 'manual');\n\n this._pendingRenderReason = undefined;\n\n this.dispatchEvent(\n new CustomEvent<RCVirtualCanvasRenderInit>('rc-virtual-canvas-render', {\n bubbles: true,\n composed: true,\n detail: {\n time,\n reason,\n viewRect: createRectSnapshot(this._viewRect),\n contentRect: createRectSnapshot(this._contentRect),\n },\n }),\n );\n\n if (this.renderMode === 'continuous') {\n this._scheduleRender('animation-frame');\n }\n }\n\n override connectedCallback() {\n super.connectedCallback();\n\n this.addEventListener('pointerdown', this._boundPointerEvent);\n this.addEventListener('pointermove', this._boundPointerEvent);\n this.addEventListener('pointerup', this._boundPointerEvent);\n this.addEventListener('click', this._boundPointerEvent);\n this.addEventListener('dblclick', this._boundPointerEvent);\n this.addEventListener('contextmenu', this._boundPointerEvent);\n\n if (this._rafHandle) {\n cancelAnimationFrame(this._rafHandle);\n this._rafHandle = 0;\n }\n\n if (this.renderMode === 'continuous') {\n this._scheduleRender('animation-frame');\n }\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n\n cancelAnimationFrame(this._rafHandle);\n this._rafHandle = 0;\n this._resizeObserver.disconnect();\n\n this.removeEventListener('pointerdown', this._boundPointerEvent);\n this.removeEventListener('pointermove', this._boundPointerEvent);\n this.removeEventListener('pointerup', this._boundPointerEvent);\n this.removeEventListener('click', this._boundPointerEvent);\n this.removeEventListener('dblclick', this._boundPointerEvent);\n this.removeEventListener('contextmenu', this._boundPointerEvent);\n }\n\n protected override updated(changed: Map<string, unknown>) {\n if (changed.has('renderMode')) {\n if (this.renderMode === 'continuous') {\n this._scheduleRender('animation-frame');\n } else if (this._pendingRenderReason === 'animation-frame') {\n if (this._rafHandle) {\n cancelAnimationFrame(this._rafHandle);\n this._rafHandle = 0;\n }\n\n this._pendingRenderReason = undefined;\n } else if (this.renderMode === 'viewport-change') {\n this._scheduleRender('viewport-change');\n }\n }\n\n if (changed.has('autoResizeCanvas')) {\n this._syncCanvasBackingStore();\n }\n\n if (changed.has('imageRendering')) {\n this.style.setProperty(\n '--rc-virtual-canvas-image-rendering',\n this.imageRendering,\n );\n }\n }\n\n private _scheduleRender(reason: RCVirtualCanvasRenderReason) {\n if (this.renderMode === 'manual' && reason !== 'manual') {\n return;\n }\n\n if (\n this.renderMode === 'viewport-change'\n && reason === 'animation-frame'\n ) {\n return;\n }\n\n this._pendingRenderReason = reason;\n\n if (!this.isConnected || this._rafHandle) {\n return;\n }\n\n this._rafHandle = window.requestAnimationFrame(this._boundUpdate);\n }\n\n private _syncCanvasBackingStore() {\n if (!this.autoResizeCanvas || this._$canvas == null) {\n return;\n }\n\n const width = Math.max(0, Math.round(this._viewRect.width));\n const height = Math.max(0, Math.round(this._viewRect.height));\n\n if (this._$canvas.width !== width) {\n this._$canvas.width = width;\n }\n\n if (this._$canvas.height !== height) {\n this._$canvas.height = height;\n }\n }\n\n private _getCanvasClientRect() {\n if (this._$canvas == null) {\n return this.getBoundingClientRect();\n }\n\n return this._$canvas.getBoundingClientRect();\n }\n\n private _getMeasuredCanvasWidth(fallbackWidth: number) {\n if (!this.autoResizeCanvas && this._$canvas && this._$canvas.width > 0) {\n return this._$canvas.width;\n }\n\n return fallbackWidth;\n }\n\n private _getMeasuredCanvasHeight(fallbackHeight: number) {\n if (!this.autoResizeCanvas && this._$canvas && this._$canvas.height > 0) {\n return this._$canvas.height;\n }\n\n return fallbackHeight;\n }\n\n private _getViewportScaleX(canvasRect: DOMRect) {\n return canvasRect.width > 0\n ? this._viewRect.width / canvasRect.width\n : 1;\n }\n\n private _getViewportScaleY(canvasRect: DOMRect) {\n return canvasRect.height > 0\n ? this._viewRect.height / canvasRect.height\n : 1;\n }\n\n render() {\n return html`\n <div\n id=\"root\"\n part=\"scroller\"\n @scroll=${this._onScroll}\n @pointerdown=${this._onPointerEvent}\n @pointermove=${this._onPointerEvent}\n @pointerup=${this._onPointerEvent}\n @click=${this._onPointerEvent}\n @dblclick=${this._onPointerEvent}\n @contextmenu=${this._onPointerEvent}\n >\n <div id=\"overlay\" part=\"overlay\">\n <slot name=\"overlay\"></slot>\n </div>\n <div\n id=\"placeholder\"\n style=${styleMap({\n width: `${this.contentWidth}px`,\n height: `${this.contentHeight}px`,\n })}\n ></div>\n </div>\n\n <slot @slotchange=${this._onSlotChange}></slot>\n `;\n }\n}\n\nexport default RCVirtualCanvas;\n"],"names":["virtualCanvasStyles","css","createRectSnapshot","rect","_RCVirtualCanvas","LitElement","time","event","entries","entry","devicePixelBoxSize","val","oldValue","e","el","x","y","options","clientX","clientY","canvasRect","scaleX","scaleY","reason","contentPoint","node","changed","width","height","fallbackWidth","fallbackHeight","html","styleMap","RCVirtualCanvas","__decorateClass","property","query","state"],"mappings":"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"}