@framv/canvas 0.1.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.
@@ -0,0 +1,160 @@
1
+ // src/element.ts
2
+ var STYLES = `
3
+ :host {
4
+ display: block;
5
+ position: relative;
6
+ overflow: hidden;
7
+ background: #1a1a2e;
8
+ cursor: grab;
9
+ user-select: none;
10
+ font-family: system-ui, sans-serif;
11
+ }
12
+ :host(.framv-grabbing) { cursor: grabbing; }
13
+ .framv-canvas-stage {
14
+ position: absolute;
15
+ top: 0; left: 0;
16
+ transform-origin: 0 0;
17
+ will-change: transform;
18
+ }
19
+ .framv-canvas-stage > * {
20
+ position: absolute;
21
+ }
22
+ .framv-canvas-badge {
23
+ position: absolute; top: 8px; left: 8px;
24
+ background: rgba(0,0,0,0.5); color: rgba(255,255,255,0.7);
25
+ padding: 3px 8px; border-radius: 4px;
26
+ font: 10px system-ui; z-index: 10;
27
+ pointer-events: none;
28
+ }
29
+ `;
30
+ var FramvCanvasElement = class extends HTMLElement {
31
+ static observedAttributes = ["scale"];
32
+ _stage;
33
+ _shadow;
34
+ _scale = 1;
35
+ _tx = 0;
36
+ _ty = 0;
37
+ _panning = false;
38
+ _lastX = 0;
39
+ _lastY = 0;
40
+ _minScale = 0.1;
41
+ _maxScale = 5;
42
+ constructor() {
43
+ super();
44
+ this._shadow = this.attachShadow({ mode: "open" });
45
+ }
46
+ get width() {
47
+ return parseInt(this.getAttribute("width") ?? "3000");
48
+ }
49
+ get height() {
50
+ return parseInt(this.getAttribute("height") ?? "2000");
51
+ }
52
+ connectedCallback() {
53
+ const s = parseFloat(this.getAttribute("scale") ?? "1");
54
+ this._scale = Math.min(Math.max(s, this._minScale), this._maxScale);
55
+ const containerW = this.clientWidth || 800;
56
+ const containerH = this.clientHeight || 600;
57
+ this._tx = (containerW - this.width * this._scale) / 2;
58
+ this._ty = (containerH - this.height * this._scale) / 2;
59
+ this._shadow.innerHTML = `
60
+ <style>${STYLES}</style>
61
+ <div class="framv-canvas-badge">${Math.round(this._scale * 100)}%</div>
62
+ <div class="framv-canvas-stage" style="width:${this.width}px;height:${this.height}px">
63
+ <slot></slot>
64
+ </div>
65
+ `;
66
+ this._stage = this._shadow.querySelector(".framv-canvas-stage");
67
+ this._updateTransform();
68
+ this.addEventListener(
69
+ "wheel",
70
+ (e) => {
71
+ e.preventDefault();
72
+ const rect = this.getBoundingClientRect();
73
+ const px = e.clientX - rect.left;
74
+ const py = e.clientY - rect.top;
75
+ const delta = -e.deltaY * 1e-3;
76
+ const newScale = Math.min(Math.max(this._scale * (1 + delta), this._minScale), this._maxScale);
77
+ const worldX = (px - this._tx) / this._scale;
78
+ const worldY = (py - this._ty) / this._scale;
79
+ this._tx = px - worldX * newScale;
80
+ this._ty = py - worldY * newScale;
81
+ this._scale = newScale;
82
+ this._updateTransform();
83
+ this._updateBadge();
84
+ },
85
+ { passive: false }
86
+ );
87
+ this.addEventListener("pointerdown", (e) => {
88
+ if (e.target instanceof HTMLElement && e.target.closest("button, a, input, select, textarea, [contenteditable]")) return;
89
+ this._panning = true;
90
+ this._lastX = e.clientX;
91
+ this._lastY = e.clientY;
92
+ this.classList.add("framv-grabbing");
93
+ this.setPointerCapture(e.pointerId);
94
+ });
95
+ this.addEventListener("pointermove", (e) => {
96
+ if (!this._panning) return;
97
+ const dx = e.clientX - this._lastX;
98
+ const dy = e.clientY - this._lastY;
99
+ this._tx += dx;
100
+ this._ty += dy;
101
+ this._lastX = e.clientX;
102
+ this._lastY = e.clientY;
103
+ this._updateTransform();
104
+ });
105
+ this.addEventListener("pointerup", () => {
106
+ this._panning = false;
107
+ this.classList.remove("framv-grabbing");
108
+ });
109
+ this.addEventListener("pointerleave", () => {
110
+ if (this._panning) {
111
+ this._panning = false;
112
+ this.classList.remove("framv-grabbing");
113
+ }
114
+ });
115
+ }
116
+ attributeChangedCallback(name, _old, value) {
117
+ if (name === "scale" && this._stage) {
118
+ const s = parseFloat(value);
119
+ if (!isNaN(s)) {
120
+ this._scale = Math.min(Math.max(s, this._minScale), this._maxScale);
121
+ this._updateTransform();
122
+ this._updateBadge();
123
+ }
124
+ }
125
+ }
126
+ /** Pan to center a specific position. */
127
+ centerAt(x, y) {
128
+ const containerW = this.clientWidth || 800;
129
+ const containerH = this.clientHeight || 600;
130
+ this._tx = containerW / 2 - x * this._scale;
131
+ this._ty = containerH / 2 - y * this._scale;
132
+ this._updateTransform();
133
+ }
134
+ /** Reset to initial centered view. */
135
+ reset() {
136
+ this._scale = 1;
137
+ const containerW = this.clientWidth || 800;
138
+ const containerH = this.clientHeight || 600;
139
+ this._tx = (containerW - this.width) / 2;
140
+ this._ty = (containerH - this.height) / 2;
141
+ this._updateTransform();
142
+ this._updateBadge();
143
+ }
144
+ get scale() {
145
+ return this._scale;
146
+ }
147
+ _updateTransform() {
148
+ this._stage.style.transform = `translate(${this._tx}px, ${this._ty}px) scale(${this._scale})`;
149
+ }
150
+ _updateBadge() {
151
+ const badge = this._shadow.querySelector(".framv-canvas-badge");
152
+ if (badge) badge.textContent = `${Math.round(this._scale * 100)}%`;
153
+ }
154
+ };
155
+ if (!customElements.get("framv-canvas")) {
156
+ customElements.define("framv-canvas", FramvCanvasElement);
157
+ }
158
+ export {
159
+ FramvCanvasElement
160
+ };
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ var FramvCanvas = (() => {
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/cdn.ts
22
+ var cdn_exports = {};
23
+ __export(cdn_exports, {
24
+ FramvCanvasElement: () => FramvCanvasElement
25
+ });
26
+
27
+ // src/element.ts
28
+ var STYLES = `
29
+ :host {
30
+ display: block;
31
+ position: relative;
32
+ overflow: hidden;
33
+ background: #1a1a2e;
34
+ cursor: grab;
35
+ user-select: none;
36
+ font-family: system-ui, sans-serif;
37
+ }
38
+ :host(.framv-grabbing) { cursor: grabbing; }
39
+ .framv-canvas-stage {
40
+ position: absolute;
41
+ top: 0; left: 0;
42
+ transform-origin: 0 0;
43
+ will-change: transform;
44
+ }
45
+ .framv-canvas-stage > * {
46
+ position: absolute;
47
+ }
48
+ .framv-canvas-badge {
49
+ position: absolute; top: 8px; left: 8px;
50
+ background: rgba(0,0,0,0.5); color: rgba(255,255,255,0.7);
51
+ padding: 3px 8px; border-radius: 4px;
52
+ font: 10px system-ui; z-index: 10;
53
+ pointer-events: none;
54
+ }
55
+ `;
56
+ var FramvCanvasElement = class extends HTMLElement {
57
+ static observedAttributes = ["scale"];
58
+ _stage;
59
+ _shadow;
60
+ _scale = 1;
61
+ _tx = 0;
62
+ _ty = 0;
63
+ _panning = false;
64
+ _lastX = 0;
65
+ _lastY = 0;
66
+ _minScale = 0.1;
67
+ _maxScale = 5;
68
+ constructor() {
69
+ super();
70
+ this._shadow = this.attachShadow({ mode: "open" });
71
+ }
72
+ get width() {
73
+ return parseInt(this.getAttribute("width") ?? "3000");
74
+ }
75
+ get height() {
76
+ return parseInt(this.getAttribute("height") ?? "2000");
77
+ }
78
+ connectedCallback() {
79
+ const s = parseFloat(this.getAttribute("scale") ?? "1");
80
+ this._scale = Math.min(Math.max(s, this._minScale), this._maxScale);
81
+ const containerW = this.clientWidth || 800;
82
+ const containerH = this.clientHeight || 600;
83
+ this._tx = (containerW - this.width * this._scale) / 2;
84
+ this._ty = (containerH - this.height * this._scale) / 2;
85
+ this._shadow.innerHTML = `
86
+ <style>${STYLES}</style>
87
+ <div class="framv-canvas-badge">${Math.round(this._scale * 100)}%</div>
88
+ <div class="framv-canvas-stage" style="width:${this.width}px;height:${this.height}px">
89
+ <slot></slot>
90
+ </div>
91
+ `;
92
+ this._stage = this._shadow.querySelector(".framv-canvas-stage");
93
+ this._updateTransform();
94
+ this.addEventListener(
95
+ "wheel",
96
+ (e) => {
97
+ e.preventDefault();
98
+ const rect = this.getBoundingClientRect();
99
+ const px = e.clientX - rect.left;
100
+ const py = e.clientY - rect.top;
101
+ const delta = -e.deltaY * 1e-3;
102
+ const newScale = Math.min(Math.max(this._scale * (1 + delta), this._minScale), this._maxScale);
103
+ const worldX = (px - this._tx) / this._scale;
104
+ const worldY = (py - this._ty) / this._scale;
105
+ this._tx = px - worldX * newScale;
106
+ this._ty = py - worldY * newScale;
107
+ this._scale = newScale;
108
+ this._updateTransform();
109
+ this._updateBadge();
110
+ },
111
+ { passive: false }
112
+ );
113
+ this.addEventListener("pointerdown", (e) => {
114
+ if (e.target instanceof HTMLElement && e.target.closest("button, a, input, select, textarea, [contenteditable]")) return;
115
+ this._panning = true;
116
+ this._lastX = e.clientX;
117
+ this._lastY = e.clientY;
118
+ this.classList.add("framv-grabbing");
119
+ this.setPointerCapture(e.pointerId);
120
+ });
121
+ this.addEventListener("pointermove", (e) => {
122
+ if (!this._panning) return;
123
+ const dx = e.clientX - this._lastX;
124
+ const dy = e.clientY - this._lastY;
125
+ this._tx += dx;
126
+ this._ty += dy;
127
+ this._lastX = e.clientX;
128
+ this._lastY = e.clientY;
129
+ this._updateTransform();
130
+ });
131
+ this.addEventListener("pointerup", () => {
132
+ this._panning = false;
133
+ this.classList.remove("framv-grabbing");
134
+ });
135
+ this.addEventListener("pointerleave", () => {
136
+ if (this._panning) {
137
+ this._panning = false;
138
+ this.classList.remove("framv-grabbing");
139
+ }
140
+ });
141
+ }
142
+ attributeChangedCallback(name, _old, value) {
143
+ if (name === "scale" && this._stage) {
144
+ const s = parseFloat(value);
145
+ if (!isNaN(s)) {
146
+ this._scale = Math.min(Math.max(s, this._minScale), this._maxScale);
147
+ this._updateTransform();
148
+ this._updateBadge();
149
+ }
150
+ }
151
+ }
152
+ /** Pan to center a specific position. */
153
+ centerAt(x, y) {
154
+ const containerW = this.clientWidth || 800;
155
+ const containerH = this.clientHeight || 600;
156
+ this._tx = containerW / 2 - x * this._scale;
157
+ this._ty = containerH / 2 - y * this._scale;
158
+ this._updateTransform();
159
+ }
160
+ /** Reset to initial centered view. */
161
+ reset() {
162
+ this._scale = 1;
163
+ const containerW = this.clientWidth || 800;
164
+ const containerH = this.clientHeight || 600;
165
+ this._tx = (containerW - this.width) / 2;
166
+ this._ty = (containerH - this.height) / 2;
167
+ this._updateTransform();
168
+ this._updateBadge();
169
+ }
170
+ get scale() {
171
+ return this._scale;
172
+ }
173
+ _updateTransform() {
174
+ this._stage.style.transform = `translate(${this._tx}px, ${this._ty}px) scale(${this._scale})`;
175
+ }
176
+ _updateBadge() {
177
+ const badge = this._shadow.querySelector(".framv-canvas-badge");
178
+ if (badge) badge.textContent = `${Math.round(this._scale * 100)}%`;
179
+ }
180
+ };
181
+ if (!customElements.get("framv-canvas")) {
182
+ customElements.define("framv-canvas", FramvCanvasElement);
183
+ }
184
+ return __toCommonJS(cdn_exports);
185
+ })();
package/dist/cdn.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import "./element.js";
2
+ export { FramvCanvasElement } from "./element.js";
3
+ //# sourceMappingURL=cdn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdn.d.ts","sourceRoot":"","sources":["../src/cdn.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,CAAC;AACtB,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC"}
package/dist/cdn.js ADDED
@@ -0,0 +1,3 @@
1
+ import "./element.js";
2
+ export { FramvCanvasElement } from "./element.js";
3
+ //# sourceMappingURL=cdn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdn.js","sourceRoot":"","sources":["../src/cdn.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,CAAC;AACtB,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * `<framv-canvas>` — infinite canvas for positioning elements.
3
+ *
4
+ * Children are absolutely positioned via `left`, `top` CSS properties.
5
+ * Use `data-x` and `data-y` attributes to set position.
6
+ *
7
+ * Interactions:
8
+ * - Drag empty space to pan
9
+ * - Ctrl+wheel or pinch to zoom
10
+ * - Click children normally (events pass through)
11
+ *
12
+ * Attributes:
13
+ * width — canvas logical width (default: 3000)
14
+ * height — canvas logical height (default: 2000)
15
+ * scale — initial zoom level (default: 1)
16
+ *
17
+ * @example
18
+ * ```html
19
+ * <framv-canvas style="width:100%;height:500px">
20
+ * <div style="left:100px;top:80px;width:200px;background:#ff79c6;padding:16px;border-radius:8px;color:white">
21
+ * Box at (100, 80)
22
+ * </div>
23
+ * <div style="left:500px;top:200px;background:#50fa7b;padding:12px;border-radius:4px">
24
+ * Another box
25
+ * </div>
26
+ * </framv-canvas>
27
+ * ```
28
+ */
29
+ export declare class FramvCanvasElement extends HTMLElement {
30
+ static observedAttributes: string[];
31
+ private _stage;
32
+ private _shadow;
33
+ private _scale;
34
+ private _tx;
35
+ private _ty;
36
+ private _panning;
37
+ private _lastX;
38
+ private _lastY;
39
+ private _minScale;
40
+ private _maxScale;
41
+ constructor();
42
+ get width(): number;
43
+ get height(): number;
44
+ connectedCallback(): void;
45
+ attributeChangedCallback(name: string, _old: string, value: string): void;
46
+ /** Pan to center a specific position. */
47
+ centerAt(x: number, y: number): void;
48
+ /** Reset to initial centered view. */
49
+ reset(): void;
50
+ get scale(): number;
51
+ private _updateTransform;
52
+ private _updateBadge;
53
+ }
54
+ //# sourceMappingURL=element.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element.d.ts","sourceRoot":"","sources":["../src/element.ts"],"names":[],"mappings":"AA6BA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,kBAAmB,SAAQ,WAAW;IACjD,MAAM,CAAC,kBAAkB,WAAa;IAEtC,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,SAAS,CAAO;IACxB,OAAO,CAAC,SAAS,CAAK;;IAOtB,IAAI,KAAK,IAAI,MAAM,CAElB;IACD,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,iBAAiB,IAAI,IAAI;IAgFzB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAWzE,yCAAyC;IACzC,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAQpC,sCAAsC;IACtC,KAAK,IAAI,IAAI;IAUb,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,YAAY;CAIrB"}
@@ -0,0 +1,188 @@
1
+ const STYLES = `
2
+ :host {
3
+ display: block;
4
+ position: relative;
5
+ overflow: hidden;
6
+ background: #1a1a2e;
7
+ cursor: grab;
8
+ user-select: none;
9
+ font-family: system-ui, sans-serif;
10
+ }
11
+ :host(.framv-grabbing) { cursor: grabbing; }
12
+ .framv-canvas-stage {
13
+ position: absolute;
14
+ top: 0; left: 0;
15
+ transform-origin: 0 0;
16
+ will-change: transform;
17
+ }
18
+ .framv-canvas-stage > * {
19
+ position: absolute;
20
+ }
21
+ .framv-canvas-badge {
22
+ position: absolute; top: 8px; left: 8px;
23
+ background: rgba(0,0,0,0.5); color: rgba(255,255,255,0.7);
24
+ padding: 3px 8px; border-radius: 4px;
25
+ font: 10px system-ui; z-index: 10;
26
+ pointer-events: none;
27
+ }
28
+ `;
29
+ /**
30
+ * `<framv-canvas>` — infinite canvas for positioning elements.
31
+ *
32
+ * Children are absolutely positioned via `left`, `top` CSS properties.
33
+ * Use `data-x` and `data-y` attributes to set position.
34
+ *
35
+ * Interactions:
36
+ * - Drag empty space to pan
37
+ * - Ctrl+wheel or pinch to zoom
38
+ * - Click children normally (events pass through)
39
+ *
40
+ * Attributes:
41
+ * width — canvas logical width (default: 3000)
42
+ * height — canvas logical height (default: 2000)
43
+ * scale — initial zoom level (default: 1)
44
+ *
45
+ * @example
46
+ * ```html
47
+ * <framv-canvas style="width:100%;height:500px">
48
+ * <div style="left:100px;top:80px;width:200px;background:#ff79c6;padding:16px;border-radius:8px;color:white">
49
+ * Box at (100, 80)
50
+ * </div>
51
+ * <div style="left:500px;top:200px;background:#50fa7b;padding:12px;border-radius:4px">
52
+ * Another box
53
+ * </div>
54
+ * </framv-canvas>
55
+ * ```
56
+ */
57
+ export class FramvCanvasElement extends HTMLElement {
58
+ static observedAttributes = ["scale"];
59
+ _stage;
60
+ _shadow;
61
+ _scale = 1;
62
+ _tx = 0;
63
+ _ty = 0;
64
+ _panning = false;
65
+ _lastX = 0;
66
+ _lastY = 0;
67
+ _minScale = 0.1;
68
+ _maxScale = 5;
69
+ constructor() {
70
+ super();
71
+ this._shadow = this.attachShadow({ mode: "open" });
72
+ }
73
+ get width() {
74
+ return parseInt(this.getAttribute("width") ?? "3000");
75
+ }
76
+ get height() {
77
+ return parseInt(this.getAttribute("height") ?? "2000");
78
+ }
79
+ connectedCallback() {
80
+ const s = parseFloat(this.getAttribute("scale") ?? "1");
81
+ this._scale = Math.min(Math.max(s, this._minScale), this._maxScale);
82
+ // Center the view initially
83
+ const containerW = this.clientWidth || 800;
84
+ const containerH = this.clientHeight || 600;
85
+ this._tx = (containerW - this.width * this._scale) / 2;
86
+ this._ty = (containerH - this.height * this._scale) / 2;
87
+ this._shadow.innerHTML = `
88
+ <style>${STYLES}</style>
89
+ <div class="framv-canvas-badge">${Math.round(this._scale * 100)}%</div>
90
+ <div class="framv-canvas-stage" style="width:${this.width}px;height:${this.height}px">
91
+ <slot></slot>
92
+ </div>
93
+ `;
94
+ this._stage = this._shadow.querySelector(".framv-canvas-stage");
95
+ this._updateTransform();
96
+ // Mouse wheel zoom (Ctrl + wheel or just wheel)
97
+ this.addEventListener("wheel", (e) => {
98
+ e.preventDefault();
99
+ const rect = this.getBoundingClientRect();
100
+ const px = e.clientX - rect.left;
101
+ const py = e.clientY - rect.top;
102
+ const delta = -e.deltaY * 0.001;
103
+ const newScale = Math.min(Math.max(this._scale * (1 + delta), this._minScale), this._maxScale);
104
+ // Pointer-aware zoom
105
+ const worldX = (px - this._tx) / this._scale;
106
+ const worldY = (py - this._ty) / this._scale;
107
+ this._tx = px - worldX * newScale;
108
+ this._ty = py - worldY * newScale;
109
+ this._scale = newScale;
110
+ this._updateTransform();
111
+ this._updateBadge();
112
+ }, { passive: false });
113
+ // Pointer drag to pan
114
+ this.addEventListener("pointerdown", (e) => {
115
+ if (e.target instanceof HTMLElement && e.target.closest("button, a, input, select, textarea, [contenteditable]"))
116
+ return;
117
+ this._panning = true;
118
+ this._lastX = e.clientX;
119
+ this._lastY = e.clientY;
120
+ this.classList.add("framv-grabbing");
121
+ this.setPointerCapture(e.pointerId);
122
+ });
123
+ this.addEventListener("pointermove", (e) => {
124
+ if (!this._panning)
125
+ return;
126
+ const dx = e.clientX - this._lastX;
127
+ const dy = e.clientY - this._lastY;
128
+ this._tx += dx;
129
+ this._ty += dy;
130
+ this._lastX = e.clientX;
131
+ this._lastY = e.clientY;
132
+ this._updateTransform();
133
+ });
134
+ this.addEventListener("pointerup", () => {
135
+ this._panning = false;
136
+ this.classList.remove("framv-grabbing");
137
+ });
138
+ this.addEventListener("pointerleave", () => {
139
+ if (this._panning) {
140
+ this._panning = false;
141
+ this.classList.remove("framv-grabbing");
142
+ }
143
+ });
144
+ }
145
+ attributeChangedCallback(name, _old, value) {
146
+ if (name === "scale" && this._stage) {
147
+ const s = parseFloat(value);
148
+ if (!isNaN(s)) {
149
+ this._scale = Math.min(Math.max(s, this._minScale), this._maxScale);
150
+ this._updateTransform();
151
+ this._updateBadge();
152
+ }
153
+ }
154
+ }
155
+ /** Pan to center a specific position. */
156
+ centerAt(x, y) {
157
+ const containerW = this.clientWidth || 800;
158
+ const containerH = this.clientHeight || 600;
159
+ this._tx = containerW / 2 - x * this._scale;
160
+ this._ty = containerH / 2 - y * this._scale;
161
+ this._updateTransform();
162
+ }
163
+ /** Reset to initial centered view. */
164
+ reset() {
165
+ this._scale = 1;
166
+ const containerW = this.clientWidth || 800;
167
+ const containerH = this.clientHeight || 600;
168
+ this._tx = (containerW - this.width) / 2;
169
+ this._ty = (containerH - this.height) / 2;
170
+ this._updateTransform();
171
+ this._updateBadge();
172
+ }
173
+ get scale() {
174
+ return this._scale;
175
+ }
176
+ _updateTransform() {
177
+ this._stage.style.transform = `translate(${this._tx}px, ${this._ty}px) scale(${this._scale})`;
178
+ }
179
+ _updateBadge() {
180
+ const badge = this._shadow.querySelector(".framv-canvas-badge");
181
+ if (badge)
182
+ badge.textContent = `${Math.round(this._scale * 100)}%`;
183
+ }
184
+ }
185
+ if (!customElements.get("framv-canvas")) {
186
+ customElements.define("framv-canvas", FramvCanvasElement);
187
+ }
188
+ //# sourceMappingURL=element.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element.js","sourceRoot":"","sources":["../src/element.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { FramvCanvasElement } from "./element.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { FramvCanvasElement } from "./element.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC"}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@framv/canvas",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "description": "Infinite canvas component for framv — position elements with zoom and pan",
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "default": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsc -b && npm run bundle",
21
+ "prepublishOnly": "npm run build",
22
+ "bundle": "esbuild src/index.ts --bundle --format=esm --outfile=dist/bundle.esm.js && esbuild src/cdn.ts --bundle --format=iife --global-name=FramvCanvas --outfile=dist/bundle.iife.js"
23
+ },
24
+ "devDependencies": {
25
+ "esbuild": "^0.27.2",
26
+ "typescript": "^5.9.3"
27
+ },
28
+ "author": "Mens Reversa",
29
+ "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/mensreversa/framv.git",
33
+ "directory": "packages/canvas"
34
+ },
35
+ "bugs": {
36
+ "url": "https://github.com/mensreversa/framv/issues"
37
+ }
38
+ }