@zumer/orbit 1.4.11 → 1.5.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/dist/orbit.js CHANGED
@@ -1,23 +1,340 @@
1
1
  /*
2
2
  * orbit
3
- * v.1.4.11
3
+ * v.1.5.0
4
4
  * Author Juan Martin Muda - Zumerlab
5
5
  * License MIT
6
6
  */
7
7
  (() => {
8
+ // src/js/orbit-layout.js
9
+ var STATE = Symbol.for("zumer.orbit.layout.state.v1");
10
+ var REGISTRY = Symbol.for("zumer.orbit.layout.registry.v1");
11
+ var PROBE = Symbol.for("zumer.orbit.layout.probe.v1");
12
+ var VISUALS = "o-arc, o-progress";
13
+ var OBSERVE = { subtree: true, childList: true, characterData: true, attributes: true, attributeOldValue: true };
14
+ function orbitNumber(element) {
15
+ var _a;
16
+ for (const token of element.classList || []) {
17
+ if (/^orbit-\d+$/.test(token)) return Number(token.slice(6));
18
+ }
19
+ return ((_a = element.classList) == null ? void 0 : _a.contains("orbit")) ? null : void 0;
20
+ }
21
+ function setLayoutProperty(element, name, value) {
22
+ const text = String(value);
23
+ if (element.style.getPropertyValue(name) !== text) element.style.setProperty(name, text);
24
+ }
25
+ function cssNumber(element, value, fallback = 0, type = "number") {
26
+ const raw = String(value || "").trim();
27
+ if (!raw) return fallback;
28
+ if (type === "number" && /^[+-]?(?:\d+\.?\d*|\.\d+)$/.test(raw)) return Number(raw);
29
+ const angle = raw.match(/^([+-]?(?:\d+\.?\d*|\.\d+))(deg|rad|grad|turn)?$/);
30
+ if (type === "angle" && angle) {
31
+ return Number(angle[1]) * ({ deg: 1, rad: 180 / Math.PI, grad: 0.9, turn: 360 }[angle[2]] || 1);
32
+ }
33
+ const doc = element.ownerDocument;
34
+ if (!(doc == null ? void 0 : doc.documentElement)) return fallback;
35
+ let probe = doc[PROBE];
36
+ if (!probe) {
37
+ probe = doc.createElement("span");
38
+ probe.setAttribute("data-orbit-measure", "");
39
+ probe.setAttribute("aria-hidden", "true");
40
+ probe.style.cssText = "all:initial!important;position:fixed!important;visibility:hidden!important;pointer-events:none!important;width:0!important;height:0!important;overflow:hidden!important;";
41
+ doc.documentElement.appendChild(probe);
42
+ Object.defineProperty(doc, PROBE, { value: probe, configurable: true });
43
+ }
44
+ const property = type === "angle" ? "rotate" : "scale";
45
+ probe.style.removeProperty(property);
46
+ probe.style.setProperty(property, raw, "important");
47
+ if (!probe.style.getPropertyValue(property)) return fallback;
48
+ const computed = doc.defaultView.getComputedStyle(probe).getPropertyValue(property);
49
+ const resolved = parseFloat(computed);
50
+ return Number.isFinite(resolved) ? resolved : fallback;
51
+ }
52
+ function group(element) {
53
+ if (element.localName === "o-arc") return "arc";
54
+ for (const name of ["satellite", "vector", "side"]) if (element.classList.contains(name)) return name;
55
+ return null;
56
+ }
57
+ function layoutRing(ring) {
58
+ const counts = { arc: 0, satellite: 0, vector: 0, side: 0 };
59
+ for (const child of ring.children) {
60
+ const kind = group(child);
61
+ if (kind) setLayoutProperty(child, "--o-layout-index", counts[kind]++ - (kind === "side" ? 1 : 0));
62
+ else if (child.localName === "o-progress") setLayoutProperty(child, "--o-layout-index", 0);
63
+ }
64
+ const count = Math.max(counts.arc, counts.satellite, counts.vector, 1);
65
+ const divisor = counts.side ? String(counts.side) : `max(1, ${count} - var(--o-fit-range, 0))`;
66
+ setLayoutProperty(ring, "--o-layout-angle", `calc(var(--o-range, 360deg) / ${divisor})`);
67
+ }
68
+ function isScope(root) {
69
+ return (root == null ? void 0 : root.nodeType) === 9 || (root == null ? void 0 : root.nodeType) === 11 && !!root.host;
70
+ }
71
+ function scopeFor(element) {
72
+ var _a;
73
+ if (isScope(element)) return element;
74
+ const root = (_a = element == null ? void 0 : element.getRootNode) == null ? void 0 : _a.call(element);
75
+ return isScope(root) ? root : null;
76
+ }
77
+ function composedContains(ancestor, node) {
78
+ var _a, _b;
79
+ for (let current = node; current; current = (_a = current.getRootNode) == null ? void 0 : _a.call(current).host) {
80
+ if (ancestor === current || ((_b = ancestor.contains) == null ? void 0 : _b.call(ancestor, current))) return true;
81
+ }
82
+ return false;
83
+ }
84
+ function registryFor(doc) {
85
+ if (!doc[REGISTRY]) Object.defineProperty(doc, REGISTRY, {
86
+ value: { states: /* @__PURE__ */ new Set() },
87
+ configurable: true
88
+ });
89
+ return doc[REGISTRY];
90
+ }
91
+ function dispose(state) {
92
+ if (state.disposed) return;
93
+ state.disposed = true;
94
+ state.view.cancelAnimationFrame(state.frame);
95
+ state.observer.disconnect();
96
+ state.resize.disconnect();
97
+ for (const remove of state.listeners) remove();
98
+ state.rings.clear();
99
+ state.visuals.clear();
100
+ state.dirty.clear();
101
+ state.registry.states.delete(state);
102
+ if (state.scope[STATE] === state) delete state.scope[STATE];
103
+ }
104
+ function pruneDisconnected(registry) {
105
+ for (const state of registry.states) {
106
+ if (state.scope.host && !state.scope.host.isConnected) dispose(state);
107
+ }
108
+ }
109
+ function invalidateDescendants(state, target = state.scope) {
110
+ for (const other of state.registry.states) {
111
+ if (other !== state && other.scope.host && composedContains(target, other.scope.host)) {
112
+ other.all = true;
113
+ other.queue();
114
+ }
115
+ }
116
+ }
117
+ function hasLayoutStructure(node) {
118
+ if (node.nodeType !== 1 || node.hasAttribute("data-orbit-measure")) return false;
119
+ if (orbitNumber(node) !== void 0 || node.matches(".gravity-spot, o-arc, o-progress")) return true;
120
+ return !!node.querySelector(".gravity-spot, .orbit, [data-orbit-ring], o-arc, o-progress");
121
+ }
122
+ function hasStylesheet(node) {
123
+ return node.nodeType === 1 && (node.matches('style, link[rel~="stylesheet"]') || !!node.querySelector('style, link[rel~="stylesheet"]'));
124
+ }
125
+ function needsDiscovery(record, state) {
126
+ if (record.type === "childList") {
127
+ return [...record.addedNodes, ...record.removedNodes].some(hasLayoutStructure);
128
+ }
129
+ if (record.attributeName !== "class") return false;
130
+ const element = record.target;
131
+ return state.rings.has(element) || element.classList.contains("gravity-spot") || orbitNumber(element) !== void 0 || (record.oldValue || "").split(/\s+/).some((token) => token === "gravity-spot" || token === "orbit" || /^orbit-\d+$/.test(token));
132
+ }
133
+ function createState(scope) {
134
+ const doc = scope.nodeType === 9 ? scope : scope.ownerDocument;
135
+ const view = doc.defaultView;
136
+ const registry = registryFor(doc);
137
+ const state = {
138
+ scope,
139
+ doc,
140
+ view,
141
+ registry,
142
+ rings: /* @__PURE__ */ new Set(),
143
+ visuals: /* @__PURE__ */ new Set(),
144
+ dirty: /* @__PURE__ */ new Set(),
145
+ scan: true,
146
+ all: true,
147
+ frame: 0,
148
+ disposed: false,
149
+ listeners: []
150
+ };
151
+ const listen = (target, event, listener, options) => {
152
+ target == null ? void 0 : target.addEventListener(event, listener, options);
153
+ state.listeners.push(() => target == null ? void 0 : target.removeEventListener(event, listener, options));
154
+ };
155
+ const queue = () => {
156
+ if (!state.disposed && !state.frame) state.frame = view.requestAnimationFrame(() => {
157
+ state.frame = 0;
158
+ state.flush();
159
+ });
160
+ };
161
+ state.queue = queue;
162
+ state.flush = () => flush(state);
163
+ state.invalidate = (target = scope, includeDescendants = true) => {
164
+ var _a, _b;
165
+ if (includeDescendants && (target === scope || target === doc.documentElement || target === doc.head)) state.all = true;
166
+ else {
167
+ for (const ring of state.rings) if (ring === target || ring.contains(target) || includeDescendants && ((_a = target.contains) == null ? void 0 : _a.call(target, ring))) state.dirty.add(ring);
168
+ for (const visual of state.visuals) if (visual === target || visual.contains(target) || includeDescendants && ((_b = target.contains) == null ? void 0 : _b.call(target, visual))) state.dirty.add(visual);
169
+ }
170
+ if (state.all || state.dirty.size || state.scan) queue();
171
+ };
172
+ state.observer = new view.MutationObserver((records) => {
173
+ var _a, _b;
174
+ if (records.some((record) => record.type === "childList")) pruneDisconnected(registry);
175
+ if (state.disposed) return;
176
+ for (const record of records) {
177
+ const target = record.target.nodeType === 3 ? record.target.parentElement : record.target;
178
+ if (!target || ((_a = target.hasAttribute) == null ? void 0 : _a.call(target, "data-orbit-measure"))) continue;
179
+ const structural = needsDiscovery(record, state);
180
+ if (structural) state.scan = true;
181
+ const stylesheetChange = record.type === "childList" && [...record.addedNodes, ...record.removedNodes].some(hasStylesheet);
182
+ if (stylesheetChange || ((_b = target.closest) == null ? void 0 : _b.call(target, "head, style")) || target.localName === "link") {
183
+ state.all = true;
184
+ invalidateDescendants(state);
185
+ } else if (record.type === "attributes") invalidateDescendants(state, target);
186
+ state.invalidate(target, record.type === "attributes" || structural);
187
+ }
188
+ });
189
+ state.observer.observe(scope, OBSERVE);
190
+ state.resize = new view.ResizeObserver((entries) => {
191
+ for (const entry of entries) state.dirty.add(entry.target);
192
+ queue();
193
+ });
194
+ const invalidateAll = () => {
195
+ state.all = true;
196
+ invalidateDescendants(state);
197
+ queue();
198
+ };
199
+ if (scope === doc) {
200
+ listen(view, "resize", invalidateAll);
201
+ listen(doc.fonts, "loadingdone", invalidateAll);
202
+ }
203
+ listen(scope, "load", (event) => {
204
+ if (event.target.localName === "link") invalidateAll();
205
+ }, true);
206
+ for (const event of ["pointerover", "pointerout", "focusin", "focusout"]) {
207
+ listen(scope, event, (e) => {
208
+ state.invalidate(e.target);
209
+ invalidateDescendants(state, e.target);
210
+ }, true);
211
+ }
212
+ Object.defineProperty(scope, STATE, { value: state, configurable: true });
213
+ registry.states.add(state);
214
+ queue();
215
+ return state;
216
+ }
217
+ function ensureState(scope) {
218
+ const doc = scope.nodeType === 9 ? scope : scope.ownerDocument;
219
+ if (!(doc == null ? void 0 : doc.defaultView) || !doc.documentElement || scope.host && !scope.host.isConnected) return null;
220
+ if (scope[STATE] && scope[STATE].doc !== doc) dispose(scope[STATE]);
221
+ if (scope.host) {
222
+ const parent = scopeFor(scope.host);
223
+ if (parent) ensureState(parent);
224
+ }
225
+ return scope[STATE] || createState(scope);
226
+ }
227
+ function discover(state) {
228
+ const nextRings = /* @__PURE__ */ new Set();
229
+ const automaticCounts = /* @__PURE__ */ new Map();
230
+ for (const ring of state.scope.querySelectorAll(".gravity-spot > *")) {
231
+ const number = orbitNumber(ring);
232
+ if (number === void 0) continue;
233
+ let automatic = automaticCounts.get(ring.parentElement) || 0;
234
+ if (ring.classList.contains("orbit")) automaticCounts.set(ring.parentElement, ++automatic);
235
+ const resolved = number === null ? automatic : number;
236
+ ring.setAttribute("data-orbit-ring", "");
237
+ setLayoutProperty(ring, "--o-layout-number", resolved === 0 ? 1e-5 : resolved);
238
+ nextRings.add(ring);
239
+ state.dirty.add(ring);
240
+ if (!state.rings.has(ring)) state.resize.observe(ring);
241
+ }
242
+ for (const old of state.rings) if (!nextRings.has(old)) {
243
+ state.resize.unobserve(old);
244
+ old.removeAttribute("data-orbit-ring");
245
+ for (const name of ["--o-layout-number", "--o-layout-angle"]) old.style.removeProperty(name);
246
+ }
247
+ state.rings = nextRings;
248
+ const nextVisuals = new Set(state.scope.querySelectorAll(VISUALS));
249
+ for (const old of state.visuals) if (!nextVisuals.has(old)) state.resize.unobserve(old);
250
+ for (const visual of nextVisuals) if (!state.visuals.has(visual)) {
251
+ state.resize.observe(visual);
252
+ state.dirty.add(visual);
253
+ }
254
+ state.visuals = nextVisuals;
255
+ state.scan = false;
256
+ }
257
+ function flush(state) {
258
+ var _a;
259
+ if (state.disposed) return;
260
+ if (state.scope.host && !state.scope.host.isConnected) {
261
+ dispose(state);
262
+ return;
263
+ }
264
+ state.observer.disconnect();
265
+ try {
266
+ if (state.scan) discover(state);
267
+ const dirty = state.all ? /* @__PURE__ */ new Set([...state.rings, ...state.visuals]) : state.dirty;
268
+ const visuals = /* @__PURE__ */ new Set();
269
+ for (const target of dirty) {
270
+ if (!target.isConnected) continue;
271
+ if (state.rings.has(target)) {
272
+ layoutRing(target);
273
+ for (const visual of target.querySelectorAll(VISUALS)) visuals.add(visual);
274
+ } else if (state.visuals.has(target)) visuals.add(target);
275
+ }
276
+ for (const visual of state.visuals) if (visuals.has(visual)) (_a = visual.update) == null ? void 0 : _a.call(visual);
277
+ state.dirty = /* @__PURE__ */ new Set();
278
+ state.all = false;
279
+ } finally {
280
+ if (!state.disposed) state.observer.observe(state.scope, OBSERVE);
281
+ }
282
+ }
283
+ function requestLayout(element = globalThis.document) {
284
+ var _a;
285
+ const scope = scopeFor(element);
286
+ if (!scope) return;
287
+ const state = ensureState(scope);
288
+ if (!state) return;
289
+ if (((_a = element == null ? void 0 : element.matches) == null ? void 0 : _a.call(element, VISUALS)) && !state.visuals.has(element)) state.scan = true;
290
+ state.invalidate(element || scope);
291
+ }
292
+ function refreshLayout(root = globalThis.document) {
293
+ const scope = scopeFor(root);
294
+ if (!scope) return;
295
+ const state = ensureState(scope);
296
+ if (!state) return;
297
+ pruneDisconnected(state.registry);
298
+ for (const current of state.registry.states) {
299
+ if (current !== state && (!current.scope.host || !composedContains(root, current.scope.host))) continue;
300
+ current.scan = true;
301
+ current.all = true;
302
+ current.view.cancelAnimationFrame(current.frame);
303
+ current.frame = 0;
304
+ current.flush();
305
+ }
306
+ }
307
+
8
308
  // src/js/orbit-base.js
9
- var OrbitBase = class extends HTMLElement {
309
+ var OrbitBase = class extends (globalThis.HTMLElement || class {
310
+ }) {
10
311
  constructor() {
11
312
  super();
12
313
  }
314
+ connectedCallback() {
315
+ requestLayout(this);
316
+ }
317
+ disconnectedCallback() {
318
+ if (this._textFrame) this.ownerDocument.defaultView.cancelAnimationFrame(this._textFrame);
319
+ this._textFrame = 0;
320
+ }
321
+ readNumber(style, name, fallback = 0) {
322
+ return cssNumber(this, style.getPropertyValue(name), fallback);
323
+ }
324
+ readAngle(style, name, fallback = 0) {
325
+ return cssNumber(this, style.getPropertyValue(name), fallback, "angle");
326
+ }
13
327
  getCommonAttributes(element) {
14
- const orbitRadius = parseFloat(getComputedStyle(element).getPropertyValue("r") || 0);
15
- const orbitNumber = parseFloat(getComputedStyle(element).getPropertyValue("--o-orbit-number") || 1);
16
- const size = parseFloat(getComputedStyle(element).getPropertyValue("--o-size-ratio") || 1);
17
- const strokeWidth = parseFloat(getComputedStyle(element).getPropertyValue("--o-stroke-width") || 1);
328
+ var _a;
329
+ const style = element.ownerDocument.defaultView.getComputedStyle(element);
330
+ const measuredRadius = parseFloat(style.getPropertyValue("r"));
331
+ const orbitRadius = Math.max(0, Number.isFinite(measuredRadius) ? measuredRadius : (((_a = element.parentElement) == null ? void 0 : _a.clientWidth) || 0) / 2);
332
+ const orbitNumber2 = Math.max(1e-5, this.readNumber(style, "--o-orbit-number", 1));
333
+ const size = Math.max(0, this.readNumber(style, "--o-size-ratio", 1));
334
+ const strokeWidth = Math.max(0, this.readNumber(style, "--o-stroke-width", 1));
18
335
  const shape = element.getAttribute("shape") || "none";
19
- const arcHeight = orbitRadius / orbitNumber * size - strokeWidth + 0.3;
20
- const arcHeightPercentage = orbitRadius > 0 ? arcHeight / 2 * 100 / orbitRadius / 2 : 0;
336
+ const arcHeight = Math.max(0, orbitRadius / orbitNumber2 * size - strokeWidth + 0.3);
337
+ const arcHeightPercentage = orbitRadius > 0 ? Math.min(49.999, arcHeight * 25 / orbitRadius) : 0;
21
338
  let innerOuter = 0;
22
339
  if (element.classList.contains("outer-orbit")) {
23
340
  innerOuter = arcHeightPercentage;
@@ -37,13 +354,15 @@
37
354
  // Se sobrescribe en cada componente
38
355
  shape,
39
356
  arcHeightPercentage,
40
- orbitNumber,
357
+ orbitNumber: orbitNumber2,
41
358
  size,
42
- strokeWidth
359
+ strokeWidth,
360
+ style
43
361
  };
44
362
  }
45
363
  getProgressAngle(maxAngle, value, maxValue = 100) {
46
- return value / maxValue * maxAngle;
364
+ if (!Number.isFinite(value) || !Number.isFinite(maxValue) || maxValue <= 0) return 0;
365
+ return Math.min(1, Math.max(0, value / maxValue)) * Math.max(0, Math.min(360, maxAngle));
47
366
  }
48
367
  getControlPoint(x, y, x1, y1, direction = "clockwise") {
49
368
  const xm = (x + x1) / 2;
@@ -69,13 +388,13 @@
69
388
  y: 50 + adjustedRadius * Math.sin(adjustedAngle)
70
389
  };
71
390
  }
72
- calculateCommonArcParameters(arcAngle, radius, arcHeightPercentage, orbitNumber, shape, strokeWidth, arcHeight, gap = 0) {
391
+ calculateCommonArcParameters(arcAngle, radius, arcHeightPercentage, orbitNumber2, shape, strokeWidth, arcHeight, gap = 0) {
73
392
  const offset = Math.PI / 2;
74
- const fangle = arcAngle * Math.PI / 180;
393
+ const fangle = Math.max(0, Math.min(359.999999, arcAngle)) * Math.PI / 180;
75
394
  const bigRadius = radius + arcHeightPercentage;
76
- const smallRadius = radius - arcHeightPercentage !== 0 ? radius - arcHeightPercentage : radius;
77
- const bigGap = (gap + strokeWidth * 1.25) / orbitNumber / bigRadius;
78
- const smallGap = (gap + strokeWidth * 1.25) / orbitNumber / smallRadius;
395
+ const smallRadius = Math.max(1e-3, radius - arcHeightPercentage);
396
+ const bigGap = Math.min(fangle * 0.49, (gap + strokeWidth * 1.25) / orbitNumber2 / bigRadius);
397
+ const smallGap = Math.min(fangle * 0.49, (gap + strokeWidth * 1.25) / orbitNumber2 / smallRadius);
79
398
  const upperAngleStart = bigGap - offset;
80
399
  const upperAngleEnd = fangle - bigGap - offset;
81
400
  const innerAngleStart = smallGap - offset;
@@ -106,11 +425,11 @@
106
425
  innerAngleEnd
107
426
  };
108
427
  }
109
- generatePathData(shape, params, arcHeight, orbitNumber) {
428
+ generatePathData(shape, params, arcHeight, orbitNumber2) {
110
429
  let d = "";
111
430
  switch (shape) {
112
431
  case "rounded":
113
- d = this.generateRoundedPath(params, arcHeight, orbitNumber);
432
+ d = this.generateRoundedPath(params, arcHeight, orbitNumber2);
114
433
  break;
115
434
  case "circle":
116
435
  case "circle-a":
@@ -118,37 +437,38 @@
118
437
  d = this.generateCirclePath(params, shape);
119
438
  break;
120
439
  case "circle-b":
121
- d = this.generateCircleBPath(params, arcHeight, orbitNumber);
440
+ d = this.generateCircleBPath(params, arcHeight, orbitNumber2);
122
441
  break;
123
442
  case "arrow":
124
- d = this.generateArrowPath(params, orbitNumber);
443
+ d = this.generateArrowPath(params, orbitNumber2);
125
444
  break;
126
445
  case "backslash":
127
446
  case "slash":
128
- d = this.generateSlashPath(params, shape, orbitNumber);
447
+ d = this.generateSlashPath(params, shape, orbitNumber2);
129
448
  break;
130
449
  case "zigzag":
131
- d = this.generateZigzagPath(params, arcHeight, orbitNumber);
450
+ d = this.generateZigzagPath(params, arcHeight, orbitNumber2);
132
451
  break;
133
452
  default:
134
453
  d = this.generateDefaultPath(params);
135
454
  }
136
455
  return d;
137
456
  }
138
- generateRoundedPath(params, arcHeight, orbitNumber) {
457
+ generateRoundedPath(params, arcHeight, orbitNumber2) {
139
458
  const { bigRadius, smallRadius } = params;
140
- const curve = arcHeight < 10 ? 5 : arcHeight < 5 ? 2.5 : 10;
141
- const capRad = curve / orbitNumber * Math.PI / 180;
459
+ const available = Math.min(params.upperAngleEnd - params.upperAngleStart, params.innerAngleEnd - params.innerAngleStart);
460
+ const curve = Math.min(arcHeight < 5 ? 2.5 : arcHeight < 10 ? 5 : 10, available * 180 / Math.PI * orbitNumber2 * 0.49);
461
+ const capRad = curve / orbitNumber2 * Math.PI / 180;
142
462
  const flagU = params.upperAngleEnd - params.upperAngleStart - 2 * capRad > Math.PI ? 1 : 0;
143
463
  const flagI = params.innerAngleEnd - params.innerAngleStart - 2 * capRad > Math.PI ? 1 : 0;
144
- const newUpperStart = this.arcPoint(bigRadius, params.upperAngleStart, 0, curve / orbitNumber);
145
- const newUpperEnd = this.arcPoint(bigRadius, params.upperAngleEnd, 0, -curve / orbitNumber);
146
- const newInnerStart = this.arcPoint(smallRadius, params.innerAngleStart, 0, curve / orbitNumber);
147
- const newInnerEnd = this.arcPoint(smallRadius, params.innerAngleEnd, 0, -curve / orbitNumber);
148
- const upperPointStart = this.arcPoint(bigRadius, params.upperAngleStart, -(curve / 2) / orbitNumber, 0);
149
- const upperPointEnd = this.arcPoint(bigRadius, params.upperAngleEnd, -(curve / 2) / orbitNumber, 0);
150
- const innerPointStart = this.arcPoint(smallRadius, params.innerAngleStart, curve / 2 / orbitNumber, 0);
151
- const innerPointEnd = this.arcPoint(smallRadius, params.innerAngleEnd, curve / 2 / orbitNumber, 0);
464
+ const newUpperStart = this.arcPoint(bigRadius, params.upperAngleStart, 0, curve / orbitNumber2);
465
+ const newUpperEnd = this.arcPoint(bigRadius, params.upperAngleEnd, 0, -curve / orbitNumber2);
466
+ const newInnerStart = this.arcPoint(smallRadius, params.innerAngleStart, 0, curve / orbitNumber2);
467
+ const newInnerEnd = this.arcPoint(smallRadius, params.innerAngleEnd, 0, -curve / orbitNumber2);
468
+ const upperPointStart = this.arcPoint(bigRadius, params.upperAngleStart, -(curve / 2) / orbitNumber2, 0);
469
+ const upperPointEnd = this.arcPoint(bigRadius, params.upperAngleEnd, -(curve / 2) / orbitNumber2, 0);
470
+ const innerPointStart = this.arcPoint(smallRadius, params.innerAngleStart, curve / 2 / orbitNumber2, 0);
471
+ const innerPointEnd = this.arcPoint(smallRadius, params.innerAngleEnd, curve / 2 / orbitNumber2, 0);
152
472
  const Q = this.getControlPoint(newUpperEnd.x, newUpperEnd.y, upperPointEnd.x, upperPointEnd.y);
153
473
  const Q1 = this.getControlPoint(innerPointEnd.x, innerPointEnd.y, newInnerEnd.x, newInnerEnd.y);
154
474
  const Q2 = this.getControlPoint(newInnerStart.x, newInnerStart.y, innerPointStart.x, innerPointStart.y);
@@ -172,16 +492,17 @@
172
492
  d += ` Z`;
173
493
  return d;
174
494
  }
175
- generateCircleBPath(params, arcHeight, orbitNumber) {
495
+ generateCircleBPath(params, arcHeight, orbitNumber2) {
176
496
  const { upperAngleStart, upperAngleEnd, innerAngleStart, innerAngleEnd, bigRadius, smallRadius } = params;
177
- const segment = arcHeight * 1.36;
178
- const capRad = segment / orbitNumber * Math.PI / 180;
497
+ const available = Math.min(upperAngleEnd - upperAngleStart, innerAngleEnd - innerAngleStart);
498
+ const segment = Math.min(arcHeight * 1.36, available * 180 / Math.PI * orbitNumber2 * 0.49);
499
+ const capRad = segment / orbitNumber2 * Math.PI / 180;
179
500
  const flagU = upperAngleEnd - upperAngleStart - 2 * capRad > Math.PI ? 1 : 0;
180
501
  const flagI = innerAngleEnd - innerAngleStart - 2 * capRad > Math.PI ? 1 : 0;
181
- const newUpperStart = this.arcPoint(bigRadius, upperAngleStart, 0, segment / orbitNumber);
182
- const newUpperEnd = this.arcPoint(bigRadius, upperAngleEnd, 0, -segment / orbitNumber);
183
- const newInnerStart = this.arcPoint(smallRadius, innerAngleStart, 0, segment / orbitNumber);
184
- const newInnerEnd = this.arcPoint(smallRadius, innerAngleEnd, 0, -segment / orbitNumber);
502
+ const newUpperStart = this.arcPoint(bigRadius, upperAngleStart, 0, segment / orbitNumber2);
503
+ const newUpperEnd = this.arcPoint(bigRadius, upperAngleEnd, 0, -segment / orbitNumber2);
504
+ const newInnerStart = this.arcPoint(smallRadius, innerAngleStart, 0, segment / orbitNumber2);
505
+ const newInnerEnd = this.arcPoint(smallRadius, innerAngleEnd, 0, -segment / orbitNumber2);
185
506
  let d = `M ${newUpperStart.x},${newUpperStart.y} A ${bigRadius},${bigRadius} 0 ${flagU} 1 ${newUpperEnd.x},${newUpperEnd.y}`;
186
507
  d += ` A 1,1 0 0 1 ${newInnerEnd.x},${newInnerEnd.y} `;
187
508
  d += ` A ${smallRadius},${smallRadius} 0 ${flagI} 0 ${newInnerStart.x},${newInnerStart.y}`;
@@ -189,10 +510,10 @@
189
510
  d += ` Z`;
190
511
  return d;
191
512
  }
192
- generateArrowPath(params, orbitNumber) {
513
+ generateArrowPath(params, orbitNumber2) {
193
514
  const { upperArcStart, upperArcEnd, innerArcStart, innerArcEnd, bigRadius, smallRadius, largeArcFlagUpper, largeArcFlagInner, radius } = params;
194
- const middleEnd = this.arcPoint(radius, params.upperAngleEnd, 0, 24 / orbitNumber / 2);
195
- const middleStart = this.arcPoint(radius, params.upperAngleStart, 0, 24 / orbitNumber / 2);
515
+ const middleEnd = this.arcPoint(radius, params.upperAngleEnd, 0, 24 / orbitNumber2 / 2);
516
+ const middleStart = this.arcPoint(radius, params.upperAngleStart, 0, 24 / orbitNumber2 / 2);
196
517
  let d = `M ${upperArcStart.x},${upperArcStart.y} A ${bigRadius},${bigRadius} 0 ${largeArcFlagUpper} 1 ${upperArcEnd.x},${upperArcEnd.y}`;
197
518
  d += `L ${middleEnd.x} ${middleEnd.y}`;
198
519
  d += `L ${innerArcEnd.x} ${innerArcEnd.y}`;
@@ -201,21 +522,21 @@
201
522
  d += `Z`;
202
523
  return d;
203
524
  }
204
- generateSlashPath(params, shape, orbitNumber) {
525
+ generateSlashPath(params, shape, orbitNumber2) {
205
526
  const { upperAngleStart, upperAngleEnd, innerAngleStart, innerAngleEnd, bigRadius, smallRadius, largeArcFlagUpper, largeArcFlagInner } = params;
206
- const newUpperStart = this.arcPoint(bigRadius, upperAngleStart, 0, shape === "backslash" ? 0 : 24 / orbitNumber / 2);
207
- const newUpperEnd = this.arcPoint(bigRadius, upperAngleEnd, 0, shape === "backslash" ? 0 : 24 / orbitNumber / 2);
208
- const newInnerStart = this.arcPoint(smallRadius, innerAngleStart, 0, shape === "backslash" ? 24 / orbitNumber / 2 : 0);
209
- const newInnerEnd = this.arcPoint(smallRadius, innerAngleEnd, 0, shape === "backslash" ? 24 / orbitNumber / 2 : 0);
527
+ const newUpperStart = this.arcPoint(bigRadius, upperAngleStart, 0, shape === "backslash" ? 0 : 24 / orbitNumber2 / 2);
528
+ const newUpperEnd = this.arcPoint(bigRadius, upperAngleEnd, 0, shape === "backslash" ? 0 : 24 / orbitNumber2 / 2);
529
+ const newInnerStart = this.arcPoint(smallRadius, innerAngleStart, 0, shape === "backslash" ? 24 / orbitNumber2 / 2 : 0);
530
+ const newInnerEnd = this.arcPoint(smallRadius, innerAngleEnd, 0, shape === "backslash" ? 24 / orbitNumber2 / 2 : 0);
210
531
  let d = `M ${newUpperStart.x},${newUpperStart.y} A ${bigRadius},${bigRadius} 0 ${largeArcFlagUpper} 1 ${newUpperEnd.x},${newUpperEnd.y}`;
211
532
  d += `L ${newInnerEnd.x} ${newInnerEnd.y}`;
212
533
  d += `A ${smallRadius},${smallRadius} 0 ${largeArcFlagInner} 0 ${newInnerStart.x}, ${newInnerStart.y}`;
213
534
  d += `Z`;
214
535
  return d;
215
536
  }
216
- generateZigzagPath(params, arcHeight, orbitNumber) {
537
+ generateZigzagPath(params, arcHeight, orbitNumber2) {
217
538
  const { upperArcStart, upperArcEnd, innerArcStart, innerArcEnd, bigRadius, smallRadius, largeArcFlagUpper, largeArcFlagInner, radius } = params;
218
- const h2 = arcHeight / orbitNumber / 2;
539
+ const h2 = arcHeight / orbitNumber2 / 2;
219
540
  const s2 = this.arcPoint(radius, params.upperAngleStart, -h2, 3);
220
541
  const s3 = this.arcPoint(radius, params.upperAngleStart, 0, 0);
221
542
  const s4 = this.arcPoint(radius, params.upperAngleStart, h2, 3);
@@ -273,9 +594,17 @@
273
594
  overflow: visible;
274
595
  pointer-events: none;
275
596
  }
597
+ /* Display element by default: never steals clicks (see orbit-arc).
598
+ Interaction is opt-in via the "interactive" attribute. */
276
599
  svg * {
600
+ pointer-events: none;
601
+ }
602
+ :host([interactive]) svg * {
277
603
  pointer-events: visiblePainted;
278
604
  }
605
+ :host([interactive]) {
606
+ cursor: pointer;
607
+ }
279
608
  .progress-bar {
280
609
  fill: var(--o-fill);
281
610
  stroke: var(--o-stroke);
@@ -288,6 +617,23 @@
288
617
  stroke: var(--o-back-stroke);
289
618
  stroke-width: var(--o-back-stroke-width);
290
619
  }
620
+ /* variant="stroke": the thin, legible gauge (registro "medidor").
621
+ The default band is a FILLED donut wedge, so everyone building a
622
+ thin gauge tripped on --o-fill/--o-back-fill. With this variant the
623
+ paths are open arcs and the data color lives where you expect it:
624
+ --o-stroke for the bar, --o-back-stroke for the track. */
625
+ :host([variant="stroke"]) .progress-bar {
626
+ fill: none;
627
+ stroke: var(--o-stroke);
628
+ stroke-width: var(--o-stroke-width, 2);
629
+ stroke-linecap: round;
630
+ }
631
+ :host([variant="stroke"]) .progress-bg {
632
+ fill: none;
633
+ stroke: var(--o-back-stroke, var(--o-gray-light));
634
+ stroke-width: var(--o-back-stroke-width, 1);
635
+ stroke-linecap: round;
636
+ }
291
637
  </style>
292
638
  <svg viewBox="0 0 100 100">
293
639
  <path class="progress-bg" shape-rendering="geometricPrecision" vector-effect="non-scaling-stroke"></path>
@@ -295,30 +641,38 @@
295
641
  </svg>
296
642
  `;
297
643
  }
298
- connectedCallback() {
299
- this.update();
300
- this.setupObserver();
301
- }
302
- setupObserver() {
303
- this.observer = new MutationObserver((mutations) => {
304
- this.observer.disconnect();
305
- mutations.forEach(() => this.update());
306
- this.observer.observe(this, { attributes: true, childList: true });
307
- });
308
- this.observer.observe(this, { attributes: true, childList: true });
309
- }
310
644
  update() {
311
645
  const attrs = this.getAttributes();
312
- const dBg = this.calculateArcParameters(attrs, true);
313
- const dBar = this.calculateArcParameters(attrs, false);
646
+ const isStroke = this.getAttribute("variant") === "stroke";
647
+ const dBg = isStroke ? this.calculateStrokeArc(attrs, true) : this.calculateArcParameters(attrs, true);
648
+ const dBar = isStroke ? this.calculateStrokeArc(attrs, false) : this.calculateArcParameters(attrs, false);
314
649
  this.shadowRoot.querySelector(".progress-bg").setAttribute("d", dBg);
315
650
  this.shadowRoot.querySelector(".progress-bar").setAttribute("d", dBar);
316
651
  }
652
+ /**
653
+ * variant="stroke": open arc along the orbit radius (no closed band),
654
+ * stroked by CSS. Same progress math as the band variant.
655
+ */
656
+ calculateStrokeArc(attrs, full) {
657
+ const { realRadius } = attrs;
658
+ const arcAngle = Math.max(0, Math.min(this.getProgressAngle(attrs, full), 359.999999));
659
+ if (!(arcAngle > 0) || !(attrs.orbitRadius > 0)) return "";
660
+ const a0 = -90 * (Math.PI / 180);
661
+ const a1 = (-90 + arcAngle) * (Math.PI / 180);
662
+ const x0 = 50 + realRadius * Math.cos(a0);
663
+ const y0 = 50 + realRadius * Math.sin(a0);
664
+ const x1 = 50 + realRadius * Math.cos(a1);
665
+ const y1 = 50 + realRadius * Math.sin(a1);
666
+ const largeArcFlag = arcAngle > 180 ? 1 : 0;
667
+ return `M ${x0},${y0} A ${realRadius},${realRadius} 0 ${largeArcFlag} 1 ${x1},${y1}`;
668
+ }
317
669
  getAttributes() {
318
670
  const common = super.getCommonAttributes(this);
319
- const range = parseFloat(getComputedStyle(this).getPropertyValue("--o-range") || 360);
320
- const progress = parseFloat(getComputedStyle(this).getPropertyValue("--o-progress") || this.getAttribute("value") || 0);
321
- const maxValue = parseFloat(this.getAttribute("max")) || 100;
671
+ const range = Math.max(0, Math.min(360, this.readAngle(common.style, "--o-range", 360)));
672
+ const rawValue = common.style.getPropertyValue("--o-progress").trim();
673
+ const progress = rawValue ? this.readNumber(common.style, "--o-progress", 0) : Number(this.getAttribute("value") || 0);
674
+ const rawMax = this.getAttribute("max");
675
+ const maxValue = rawMax === null ? 100 : Number(rawMax);
322
676
  return {
323
677
  ...common,
324
678
  range,
@@ -328,27 +682,28 @@
328
682
  }
329
683
  getProgressAngle(attrs, full) {
330
684
  const { range, progress, maxValue } = attrs;
331
- return full ? (maxValue - 1e-5) / maxValue * range : progress / maxValue * range;
685
+ return full ? range : super.getProgressAngle(range, progress, maxValue);
332
686
  }
333
687
  calculateArcParameters(attrs, full) {
334
- const { shape, realRadius, arcHeightPercentage, orbitNumber, strokeWidth, arcHeight } = attrs;
688
+ const { shape, realRadius, arcHeightPercentage, orbitNumber: orbitNumber2, strokeWidth, arcHeight } = attrs;
335
689
  const arcAngle = this.getProgressAngle(attrs, full);
690
+ if (!(arcAngle > 0) || !(attrs.orbitRadius > 0)) return "";
336
691
  const params = super.calculateCommonArcParameters(
337
692
  arcAngle,
338
693
  realRadius,
339
694
  arcHeightPercentage,
340
- orbitNumber,
695
+ orbitNumber2,
341
696
  shape,
342
697
  strokeWidth,
343
698
  arcHeight
344
699
  );
345
- return super.generatePathData(shape, params, arcHeight, orbitNumber);
700
+ return super.generatePathData(shape, params, arcHeight, orbitNumber2);
346
701
  }
347
702
  };
348
703
 
349
704
  // src/js/orbit-arc.js
350
- var template = document.createElement("template");
351
- template.innerHTML = `
705
+ var template = typeof document === "undefined" ? null : document.createElement("template");
706
+ if (template) template.innerHTML = `
352
707
  <style>
353
708
  :host {
354
709
  --o-fill: var(--o-gray-light);
@@ -368,9 +723,19 @@
368
723
  overflow: visible;
369
724
  pointer-events: none;
370
725
  }
726
+ /* Arcs are display elements by default: they must NOT steal clicks from
727
+ satellites/controls nearby (a painted arc used to capture clicks even
728
+ at opacity 0, and pointer-events:none on the host could not pierce the
729
+ shadow). Interaction is opt-in via the "interactive" attribute. */
371
730
  svg * {
731
+ pointer-events: none;
732
+ }
733
+ :host([interactive]) svg * {
372
734
  pointer-events: visiblePainted;
373
735
  }
736
+ :host([interactive]) {
737
+ cursor: pointer;
738
+ }
374
739
  #orbitShape {
375
740
  fill: var(--o-fill);
376
741
  stroke: var(--o-stroke);
@@ -400,30 +765,16 @@
400
765
  this.attachShadow({ mode: "open" });
401
766
  this.shadowRoot.appendChild(template.content.cloneNode(true));
402
767
  }
403
- connectedCallback() {
404
- this.update();
405
- this.setupObservers();
406
- }
407
- disconnectedCallback() {
408
- if (this.observer) this.observer.disconnect();
409
- if (this.textObserver) this.textObserver.disconnect();
410
- }
411
- setupObservers() {
412
- this.observer = new MutationObserver((mutations) => {
413
- this.observer.disconnect();
414
- mutations.forEach(() => this.update());
415
- this.observer.observe(this, { attributes: true, childList: true });
416
- });
417
- this.observer.observe(this, { attributes: true, childList: true });
418
- this.textObserver = new MutationObserver(() => {
419
- const textPath = this.shadowRoot.querySelector("textPath");
420
- textPath.textContent = this.textContent;
421
- });
422
- this.textObserver.observe(this, { characterData: true, subtree: true });
423
- }
424
768
  update() {
425
769
  const attrs = this.getAttributes();
426
770
  const { length, fontSize, textAnchor, fitRange } = attrs;
771
+ if (this.hasAttribute("value")) {
772
+ setLayoutProperty(this, "--o-arc-start", `${attrs.stackOffset}deg`);
773
+ setLayoutProperty(this, "--o_stack", attrs.stackOffset + attrs.arcAngle);
774
+ } else {
775
+ this.style.removeProperty("--o-arc-start");
776
+ this.style.removeProperty("--o_stack");
777
+ }
427
778
  const orbitPath = this.shadowRoot.querySelector("#orbitPath");
428
779
  const orbitShape = this.shadowRoot.querySelector("#orbitShape");
429
780
  const textPath = this.shadowRoot.querySelector("textPath");
@@ -441,65 +792,87 @@
441
792
  }
442
793
  if (fitRange) {
443
794
  textPath.parentElement.setAttribute("textLength", orbitPath.getTotalLength());
795
+ } else {
796
+ textPath.parentElement.removeAttribute("textLength");
444
797
  }
445
798
  textPath.parentElement.style.fontSize = `calc(${fontSize} * (100 / (${length}) * (12 / var(--o-orbit-number)))`;
446
799
  textPath.textContent = this.textContent;
800
+ this.warnIfTextOverflows(orbitPath, textPath, fitRange);
801
+ }
802
+ /**
803
+ * Curved text has an explicit angular budget: glyphs past the end of the
804
+ * arc path are clipped SILENTLY by SVG. Measure and warn so nobody loses
805
+ * time to invisible truncation (~20 chars in 96° at default sizes).
806
+ * fit-range squeezes the text to the path via textLength, so it never clips.
807
+ */
808
+ warnIfTextOverflows(orbitPath, textPath, fitRange) {
809
+ const raw = (this.textContent || "").trim();
810
+ if (!raw || fitRange || !this.isConnected) return;
811
+ if (this._textFrame) this.ownerDocument.defaultView.cancelAnimationFrame(this._textFrame);
812
+ this._textFrame = this.ownerDocument.defaultView.requestAnimationFrame(() => {
813
+ this._textFrame = 0;
814
+ if (!this.isConnected) return;
815
+ try {
816
+ const pathLen = orbitPath.getTotalLength();
817
+ const textLen = textPath.parentElement.getComputedTextLength();
818
+ if (pathLen > 0 && textLen > pathLen && this._truncWarned !== raw) {
819
+ this._truncWarned = raw;
820
+ console.warn(
821
+ `[orbit] <o-arc> text "${raw.length > 34 ? raw.slice(0, 34) + "\u2026" : raw}" overflows its arc (${Math.round(textLen)} > ${Math.round(pathLen)} units) and will clip. Shorten the text, widen --o-range, or use fit-range to squeeze it.`
822
+ );
823
+ }
824
+ } catch (e) {
825
+ }
826
+ });
447
827
  }
448
828
  getAttributes() {
449
829
  const common = super.getCommonAttributes(this);
450
- let arcAngle;
830
+ const { style } = common;
831
+ const range = Math.max(0, Math.min(360, this.readAngle(style, "--o-range", 360)));
451
832
  const flip = this.hasAttribute("flip") || this.classList.contains("flip");
452
- const fitRange = this.hasAttribute("fit-range") || this.classList.contains("fit-range") || false;
453
- const length = parseFloat(getComputedStyle(this).getPropertyValue("--o-force")) || 100;
833
+ const fitRange = this.hasAttribute("fit-range") || this.classList.contains("fit-range");
834
+ const length = common.orbitRadius * 24 / common.orbitNumber || 100;
454
835
  const textAnchor = this.getAttribute("text-anchor") || "middle";
455
- const fontSize = getComputedStyle(this).getPropertyValue("font-size") || getComputedStyle(this).getPropertyValue("--font-size");
456
- const range = parseFloat(getComputedStyle(this).getPropertyValue("--o-range") || 360);
457
- const value = parseFloat(this.getAttribute("value"));
458
- const gap = parseFloat(getComputedStyle(this).getPropertyValue("--o-gap")) || 1;
459
- if (value) {
460
- arcAngle = super.getProgressAngle(range, value);
461
- const prevElement = this.previousElementSibling;
462
- const stackOffset = prevElement ? parseFloat(getComputedStyle(prevElement).getPropertyValue("--o_stack")) : 0;
463
- this.style.setProperty("--o_stack", stackOffset + arcAngle);
464
- if (stackOffset >= 0 && flip) {
465
- this.style.setProperty("--o-angle-composite", parseFloat(stackOffset) + "deg");
466
- }
467
- if (stackOffset > 0 && !flip) {
468
- this.style.setProperty("--o-angle-composite", parseFloat(stackOffset) + "deg");
836
+ const fontSize = style.fontSize || "16px";
837
+ const gap = Math.max(0, this.readNumber(style, "--o-gap", 1));
838
+ const value = Number(this.getAttribute("value"));
839
+ const rawMax = this.getAttribute("max");
840
+ const max = rawMax === null ? 100 : Number(rawMax);
841
+ const arcAngle = this.hasAttribute("value") ? super.getProgressAngle(range, value, max) : Math.max(0, Math.min(360, this.readAngle(style, "--o-angle", 0)));
842
+ let stackOffset = 0;
843
+ if (this.hasAttribute("value")) {
844
+ for (let prev = this.previousElementSibling; prev; prev = prev.previousElementSibling) {
845
+ if (prev.localName !== "o-arc") continue;
846
+ const previousStyle = this.ownerDocument.defaultView.getComputedStyle(prev);
847
+ if (prev.hasAttribute("value")) {
848
+ const prevMax = prev.hasAttribute("max") ? Number(prev.getAttribute("max")) : 100;
849
+ const prevRange = cssNumber(prev, previousStyle.getPropertyValue("--o-range"), range, "angle");
850
+ stackOffset += super.getProgressAngle(prevRange, Number(prev.getAttribute("value")), prevMax);
851
+ } else stackOffset += cssNumber(prev, previousStyle.getPropertyValue("--o-angle"), 0, "angle");
469
852
  }
470
- } else {
471
- const rawAngle = getComputedStyle(this).getPropertyValue("--o-angle");
472
- arcAngle = this.calcularExpresionCSS(rawAngle);
473
853
  }
474
- return {
475
- ...common,
476
- gap,
477
- arcAngle,
478
- flip,
479
- fitRange,
480
- length,
481
- fontSize,
482
- textAnchor
483
- };
854
+ return { ...common, gap, arcAngle, stackOffset, flip, fitRange, length, fontSize, textAnchor };
484
855
  }
485
856
  calculateArcParameters(attrs) {
486
- const { arcAngle, realRadius, arcHeightPercentage, orbitNumber, shape, strokeWidth, arcHeight, gap } = attrs;
857
+ const { arcAngle, realRadius, arcHeightPercentage, orbitNumber: orbitNumber2, shape, strokeWidth, arcHeight, gap } = attrs;
858
+ if (!(arcAngle > 0) || !(attrs.orbitRadius > 0)) return { dShape: "" };
487
859
  const params = super.calculateCommonArcParameters(
488
860
  arcAngle,
489
861
  realRadius,
490
862
  arcHeightPercentage,
491
- orbitNumber,
863
+ orbitNumber2,
492
864
  shape,
493
865
  strokeWidth,
494
866
  arcHeight,
495
867
  gap
496
868
  );
497
- const dShape = super.generatePathData(shape, params, arcHeight, orbitNumber);
869
+ const dShape = super.generatePathData(shape, params, arcHeight, orbitNumber2);
498
870
  return { dShape };
499
871
  }
500
872
  calculateTextArcParameters(attrs) {
501
873
  const { arcAngle, realRadius, gap, flip } = attrs;
502
- const adjustedGap = gap * 0.5;
874
+ if (!(arcAngle > 0)) return { dPath: "" };
875
+ const adjustedGap = Math.min(gap * 0.5, arcAngle * 0.49);
503
876
  const sweepFlag = flip ? 0 : 1;
504
877
  const largeArcFlag = arcAngle <= 180 ? 0 : 1;
505
878
  let coordX1 = 50 + realRadius * Math.cos((-90 + adjustedGap) * (Math.PI / 180));
@@ -511,48 +884,53 @@
511
884
  return { dPath };
512
885
  }
513
886
  calcularExpresionCSS(cssExpression) {
514
- const match = cssExpression.match(/calc\(\s*([\d.]+)deg\s*\/\s*\(\s*(\d+)\s*-\s*(\d+)\s*\)\s*\)/);
515
- if (match) {
516
- const value = parseFloat(match[1]);
517
- const divisor = parseInt(match[2]) - parseInt(match[3]);
518
- if (!isNaN(value) && !isNaN(divisor) && divisor !== 0) {
519
- return value / divisor;
520
- }
521
- }
522
- return 0;
887
+ return cssNumber(this, cssExpression, 0, "angle");
523
888
  }
524
889
  };
525
890
 
526
891
  // src/js/orbit-resize.js
892
+ var resizing = /* @__PURE__ */ new WeakMap();
527
893
  var Orbit = {
894
+ refresh: refreshLayout,
528
895
  resize(parentElementSelector) {
529
- const parent = document.querySelector(parentElementSelector);
530
- if (!parent) {
896
+ var _a, _b;
897
+ const parent = typeof parentElementSelector === "string" ? (_a = globalThis.document) == null ? void 0 : _a.querySelector(parentElementSelector) : parentElementSelector;
898
+ if (!(parent == null ? void 0 : parent.ownerDocument)) {
531
899
  console.error("Orbit.resize: element not found:", parentElementSelector);
532
- return;
900
+ return () => {
901
+ };
533
902
  }
903
+ (_b = resizing.get(parent)) == null ? void 0 : _b();
904
+ const view = parent.ownerDocument.defaultView;
534
905
  const applyRatio = (width) => {
535
- parent.querySelectorAll(".gravity-spot").forEach((el) => {
536
- el.style.setProperty("--o-force-ratio", String(width / 500));
537
- });
538
- };
539
- let w = parent.offsetWidth || parent.getBoundingClientRect().width;
540
- if (w > 0) applyRatio(w);
541
- else requestAnimationFrame(() => {
542
- w = parent.offsetWidth || parent.getBoundingClientRect().width;
543
- if (w > 0) applyRatio(w);
544
- });
545
- const ro = new ResizeObserver((entries) => {
546
- for (const e of entries) {
547
- if (e.contentRect.width > 0) applyRatio(e.contentRect.width);
906
+ if (!(width > 0)) return;
907
+ for (const element of parent.querySelectorAll(".gravity-spot")) {
908
+ const ratio = String(width / 500);
909
+ if (element.style.getPropertyValue("--o-force-ratio") !== ratio) element.style.setProperty("--o-force-ratio", ratio);
548
910
  }
911
+ refreshLayout(parent);
912
+ };
913
+ const observer = new view.ResizeObserver((entries) => {
914
+ for (const entry of entries) applyRatio(entry.contentRect.width);
549
915
  });
550
- ro.observe(parent);
916
+ observer.observe(parent);
917
+ applyRatio(parent.clientWidth || parent.getBoundingClientRect().width);
918
+ const stop = () => {
919
+ observer.disconnect();
920
+ if (resizing.get(parent) === stop) resizing.delete(parent);
921
+ };
922
+ resizing.set(parent, stop);
923
+ return stop;
551
924
  }
552
925
  };
553
926
 
554
927
  // src/orbit.js
555
- if (!customElements.get("o-progress")) customElements.define("o-progress", OrbitProgress);
556
- if (!customElements.get("o-arc")) customElements.define("o-arc", OrbitArc);
557
- window.Orbit = Orbit;
928
+ function registerOrbit() {
929
+ if (typeof customElements === "undefined" || typeof document === "undefined") return;
930
+ if (!customElements.get("o-progress")) customElements.define("o-progress", OrbitProgress);
931
+ if (!customElements.get("o-arc")) customElements.define("o-arc", OrbitArc);
932
+ requestLayout(document);
933
+ window.Orbit = Orbit;
934
+ }
935
+ registerOrbit();
558
936
  })();