@zumer/orbit 1.4.12 → 1.6.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.12
3
+ * v.1.6.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);
@@ -320,18 +641,6 @@
320
641
  </svg>
321
642
  `;
322
643
  }
323
- connectedCallback() {
324
- this.update();
325
- this.setupObserver();
326
- }
327
- setupObserver() {
328
- this.observer = new MutationObserver((mutations) => {
329
- this.observer.disconnect();
330
- mutations.forEach(() => this.update());
331
- this.observer.observe(this, { attributes: true, childList: true });
332
- });
333
- this.observer.observe(this, { attributes: true, childList: true });
334
- }
335
644
  update() {
336
645
  const attrs = this.getAttributes();
337
646
  const isStroke = this.getAttribute("variant") === "stroke";
@@ -346,7 +655,13 @@
346
655
  */
347
656
  calculateStrokeArc(attrs, full) {
348
657
  const { realRadius } = attrs;
349
- const arcAngle = Math.max(0, Math.min(this.getProgressAngle(attrs, full), 359.99));
658
+ const arcAngle = Math.max(0, Math.min(this.getProgressAngle(attrs, full), 360));
659
+ if (!(arcAngle > 0) || !(attrs.orbitRadius > 0)) return "";
660
+ if (arcAngle >= 359.9) {
661
+ const top = 50 - realRadius;
662
+ const bottom = 50 + realRadius;
663
+ return `M 50,${top} A ${realRadius},${realRadius} 0 1 1 50,${bottom} A ${realRadius},${realRadius} 0 1 1 50,${top}`;
664
+ }
350
665
  const a0 = -90 * (Math.PI / 180);
351
666
  const a1 = (-90 + arcAngle) * (Math.PI / 180);
352
667
  const x0 = 50 + realRadius * Math.cos(a0);
@@ -358,9 +673,11 @@
358
673
  }
359
674
  getAttributes() {
360
675
  const common = super.getCommonAttributes(this);
361
- const range = parseFloat(getComputedStyle(this).getPropertyValue("--o-range") || 360);
362
- const progress = parseFloat(getComputedStyle(this).getPropertyValue("--o-progress") || this.getAttribute("value") || 0);
363
- const maxValue = parseFloat(this.getAttribute("max")) || 100;
676
+ const range = Math.max(0, Math.min(360, this.readAngle(common.style, "--o-range", 360)));
677
+ const rawValue = common.style.getPropertyValue("--o-progress").trim();
678
+ const progress = rawValue ? this.readNumber(common.style, "--o-progress", 0) : Number(this.getAttribute("value") || 0);
679
+ const rawMax = this.getAttribute("max");
680
+ const maxValue = rawMax === null ? 100 : Number(rawMax);
364
681
  return {
365
682
  ...common,
366
683
  range,
@@ -370,27 +687,28 @@
370
687
  }
371
688
  getProgressAngle(attrs, full) {
372
689
  const { range, progress, maxValue } = attrs;
373
- return full ? (maxValue - 1e-5) / maxValue * range : progress / maxValue * range;
690
+ return full ? range : super.getProgressAngle(range, progress, maxValue);
374
691
  }
375
692
  calculateArcParameters(attrs, full) {
376
- const { shape, realRadius, arcHeightPercentage, orbitNumber, strokeWidth, arcHeight } = attrs;
693
+ const { shape, realRadius, arcHeightPercentage, orbitNumber: orbitNumber2, strokeWidth, arcHeight } = attrs;
377
694
  const arcAngle = this.getProgressAngle(attrs, full);
695
+ if (!(arcAngle > 0) || !(attrs.orbitRadius > 0)) return "";
378
696
  const params = super.calculateCommonArcParameters(
379
697
  arcAngle,
380
698
  realRadius,
381
699
  arcHeightPercentage,
382
- orbitNumber,
700
+ orbitNumber2,
383
701
  shape,
384
702
  strokeWidth,
385
703
  arcHeight
386
704
  );
387
- return super.generatePathData(shape, params, arcHeight, orbitNumber);
705
+ return super.generatePathData(shape, params, arcHeight, orbitNumber2);
388
706
  }
389
707
  };
390
708
 
391
709
  // src/js/orbit-arc.js
392
- var template = document.createElement("template");
393
- template.innerHTML = `
710
+ var template = typeof document === "undefined" ? null : document.createElement("template");
711
+ if (template) template.innerHTML = `
394
712
  <style>
395
713
  :host {
396
714
  --o-fill: var(--o-gray-light);
@@ -452,30 +770,16 @@
452
770
  this.attachShadow({ mode: "open" });
453
771
  this.shadowRoot.appendChild(template.content.cloneNode(true));
454
772
  }
455
- connectedCallback() {
456
- this.update();
457
- this.setupObservers();
458
- }
459
- disconnectedCallback() {
460
- if (this.observer) this.observer.disconnect();
461
- if (this.textObserver) this.textObserver.disconnect();
462
- }
463
- setupObservers() {
464
- this.observer = new MutationObserver((mutations) => {
465
- this.observer.disconnect();
466
- mutations.forEach(() => this.update());
467
- this.observer.observe(this, { attributes: true, childList: true });
468
- });
469
- this.observer.observe(this, { attributes: true, childList: true });
470
- this.textObserver = new MutationObserver(() => {
471
- const textPath = this.shadowRoot.querySelector("textPath");
472
- textPath.textContent = this.textContent;
473
- });
474
- this.textObserver.observe(this, { characterData: true, subtree: true });
475
- }
476
773
  update() {
477
774
  const attrs = this.getAttributes();
478
775
  const { length, fontSize, textAnchor, fitRange } = attrs;
776
+ if (this.hasAttribute("value")) {
777
+ setLayoutProperty(this, "--o-arc-start", `${attrs.stackOffset}deg`);
778
+ setLayoutProperty(this, "--o_stack", attrs.stackOffset + attrs.arcAngle);
779
+ } else {
780
+ this.style.removeProperty("--o-arc-start");
781
+ this.style.removeProperty("--o_stack");
782
+ }
479
783
  const orbitPath = this.shadowRoot.querySelector("#orbitPath");
480
784
  const orbitShape = this.shadowRoot.querySelector("#orbitShape");
481
785
  const textPath = this.shadowRoot.querySelector("textPath");
@@ -493,6 +797,8 @@
493
797
  }
494
798
  if (fitRange) {
495
799
  textPath.parentElement.setAttribute("textLength", orbitPath.getTotalLength());
800
+ } else {
801
+ textPath.parentElement.removeAttribute("textLength");
496
802
  }
497
803
  textPath.parentElement.style.fontSize = `calc(${fontSize} * (100 / (${length}) * (12 / var(--o-orbit-number)))`;
498
804
  textPath.textContent = this.textContent;
@@ -507,7 +813,10 @@
507
813
  warnIfTextOverflows(orbitPath, textPath, fitRange) {
508
814
  const raw = (this.textContent || "").trim();
509
815
  if (!raw || fitRange || !this.isConnected) return;
510
- requestAnimationFrame(() => {
816
+ if (this._textFrame) this.ownerDocument.defaultView.cancelAnimationFrame(this._textFrame);
817
+ this._textFrame = this.ownerDocument.defaultView.requestAnimationFrame(() => {
818
+ this._textFrame = 0;
819
+ if (!this.isConnected) return;
511
820
  try {
512
821
  const pathLen = orbitPath.getTotalLength();
513
822
  const textLen = textPath.parentElement.getComputedTextLength();
@@ -523,59 +832,52 @@
523
832
  }
524
833
  getAttributes() {
525
834
  const common = super.getCommonAttributes(this);
526
- let arcAngle;
835
+ const { style } = common;
836
+ const range = Math.max(0, Math.min(360, this.readAngle(style, "--o-range", 360)));
527
837
  const flip = this.hasAttribute("flip") || this.classList.contains("flip");
528
- const fitRange = this.hasAttribute("fit-range") || this.classList.contains("fit-range") || false;
529
- const length = parseFloat(getComputedStyle(this).getPropertyValue("--o-force")) || 100;
838
+ const fitRange = this.hasAttribute("fit-range") || this.classList.contains("fit-range");
839
+ const length = common.orbitRadius * 24 / common.orbitNumber || 100;
530
840
  const textAnchor = this.getAttribute("text-anchor") || "middle";
531
- const fontSize = getComputedStyle(this).getPropertyValue("font-size") || getComputedStyle(this).getPropertyValue("--font-size");
532
- const range = parseFloat(getComputedStyle(this).getPropertyValue("--o-range") || 360);
533
- const value = parseFloat(this.getAttribute("value"));
534
- const gap = parseFloat(getComputedStyle(this).getPropertyValue("--o-gap")) || 1;
535
- if (value) {
536
- arcAngle = super.getProgressAngle(range, value);
537
- const prevElement = this.previousElementSibling;
538
- const stackOffset = prevElement ? parseFloat(getComputedStyle(prevElement).getPropertyValue("--o_stack")) : 0;
539
- this.style.setProperty("--o_stack", stackOffset + arcAngle);
540
- if (stackOffset >= 0 && flip) {
541
- this.style.setProperty("--o-angle-composite", parseFloat(stackOffset) + "deg");
542
- }
543
- if (stackOffset > 0 && !flip) {
544
- this.style.setProperty("--o-angle-composite", parseFloat(stackOffset) + "deg");
841
+ const fontSize = style.fontSize || "16px";
842
+ const gap = Math.max(0, this.readNumber(style, "--o-gap", 1));
843
+ const value = Number(this.getAttribute("value"));
844
+ const rawMax = this.getAttribute("max");
845
+ const max = rawMax === null ? 100 : Number(rawMax);
846
+ const arcAngle = this.hasAttribute("value") ? super.getProgressAngle(range, value, max) : Math.max(0, Math.min(360, this.readAngle(style, "--o-angle", 0)));
847
+ let stackOffset = 0;
848
+ if (this.hasAttribute("value")) {
849
+ for (let prev = this.previousElementSibling; prev; prev = prev.previousElementSibling) {
850
+ if (prev.localName !== "o-arc") continue;
851
+ const previousStyle = this.ownerDocument.defaultView.getComputedStyle(prev);
852
+ if (prev.hasAttribute("value")) {
853
+ const prevMax = prev.hasAttribute("max") ? Number(prev.getAttribute("max")) : 100;
854
+ const prevRange = cssNumber(prev, previousStyle.getPropertyValue("--o-range"), range, "angle");
855
+ stackOffset += super.getProgressAngle(prevRange, Number(prev.getAttribute("value")), prevMax);
856
+ } else stackOffset += cssNumber(prev, previousStyle.getPropertyValue("--o-angle"), 0, "angle");
545
857
  }
546
- } else {
547
- const rawAngle = getComputedStyle(this).getPropertyValue("--o-angle");
548
- arcAngle = this.calcularExpresionCSS(rawAngle);
549
858
  }
550
- return {
551
- ...common,
552
- gap,
553
- arcAngle,
554
- flip,
555
- fitRange,
556
- length,
557
- fontSize,
558
- textAnchor
559
- };
859
+ return { ...common, gap, arcAngle, stackOffset, flip, fitRange, length, fontSize, textAnchor };
560
860
  }
561
861
  calculateArcParameters(attrs) {
562
- const { arcAngle, realRadius, arcHeightPercentage, orbitNumber, shape, strokeWidth, arcHeight, gap } = attrs;
862
+ const { arcAngle, realRadius, arcHeightPercentage, orbitNumber: orbitNumber2, shape, strokeWidth, arcHeight, gap } = attrs;
863
+ if (!(arcAngle > 0) || !(attrs.orbitRadius > 0)) return { dShape: "" };
563
864
  const params = super.calculateCommonArcParameters(
564
865
  arcAngle,
565
866
  realRadius,
566
867
  arcHeightPercentage,
567
- orbitNumber,
868
+ orbitNumber2,
568
869
  shape,
569
870
  strokeWidth,
570
871
  arcHeight,
571
872
  gap
572
873
  );
573
- const dShape = super.generatePathData(shape, params, arcHeight, orbitNumber);
874
+ const dShape = super.generatePathData(shape, params, arcHeight, orbitNumber2);
574
875
  return { dShape };
575
876
  }
576
877
  calculateTextArcParameters(attrs) {
577
878
  const { arcAngle, realRadius, gap, flip } = attrs;
578
- const adjustedGap = gap * 0.5;
879
+ if (!(arcAngle > 0)) return { dPath: "" };
880
+ const adjustedGap = Math.min(gap * 0.5, arcAngle * 0.49);
579
881
  const sweepFlag = flip ? 0 : 1;
580
882
  const largeArcFlag = arcAngle <= 180 ? 0 : 1;
581
883
  let coordX1 = 50 + realRadius * Math.cos((-90 + adjustedGap) * (Math.PI / 180));
@@ -587,48 +889,53 @@
587
889
  return { dPath };
588
890
  }
589
891
  calcularExpresionCSS(cssExpression) {
590
- const match = cssExpression.match(/calc\(\s*([\d.]+)deg\s*\/\s*\(\s*(\d+)\s*-\s*(\d+)\s*\)\s*\)/);
591
- if (match) {
592
- const value = parseFloat(match[1]);
593
- const divisor = parseInt(match[2]) - parseInt(match[3]);
594
- if (!isNaN(value) && !isNaN(divisor) && divisor !== 0) {
595
- return value / divisor;
596
- }
597
- }
598
- return 0;
892
+ return cssNumber(this, cssExpression, 0, "angle");
599
893
  }
600
894
  };
601
895
 
602
896
  // src/js/orbit-resize.js
897
+ var resizing = /* @__PURE__ */ new WeakMap();
603
898
  var Orbit = {
899
+ refresh: refreshLayout,
604
900
  resize(parentElementSelector) {
605
- const parent = document.querySelector(parentElementSelector);
606
- if (!parent) {
901
+ var _a, _b;
902
+ const parent = typeof parentElementSelector === "string" ? (_a = globalThis.document) == null ? void 0 : _a.querySelector(parentElementSelector) : parentElementSelector;
903
+ if (!(parent == null ? void 0 : parent.ownerDocument)) {
607
904
  console.error("Orbit.resize: element not found:", parentElementSelector);
608
- return;
905
+ return () => {
906
+ };
609
907
  }
908
+ (_b = resizing.get(parent)) == null ? void 0 : _b();
909
+ const view = parent.ownerDocument.defaultView;
610
910
  const applyRatio = (width) => {
611
- parent.querySelectorAll(".gravity-spot").forEach((el) => {
612
- el.style.setProperty("--o-force-ratio", String(width / 500));
613
- });
614
- };
615
- let w = parent.offsetWidth || parent.getBoundingClientRect().width;
616
- if (w > 0) applyRatio(w);
617
- else requestAnimationFrame(() => {
618
- w = parent.offsetWidth || parent.getBoundingClientRect().width;
619
- if (w > 0) applyRatio(w);
620
- });
621
- const ro = new ResizeObserver((entries) => {
622
- for (const e of entries) {
623
- if (e.contentRect.width > 0) applyRatio(e.contentRect.width);
911
+ if (!(width > 0)) return;
912
+ for (const element of parent.querySelectorAll(".gravity-spot")) {
913
+ const ratio = String(width / 500);
914
+ if (element.style.getPropertyValue("--o-force-ratio") !== ratio) element.style.setProperty("--o-force-ratio", ratio);
624
915
  }
916
+ refreshLayout(parent);
917
+ };
918
+ const observer = new view.ResizeObserver((entries) => {
919
+ for (const entry of entries) applyRatio(entry.contentRect.width);
625
920
  });
626
- ro.observe(parent);
921
+ observer.observe(parent);
922
+ applyRatio(parent.clientWidth || parent.getBoundingClientRect().width);
923
+ const stop = () => {
924
+ observer.disconnect();
925
+ if (resizing.get(parent) === stop) resizing.delete(parent);
926
+ };
927
+ resizing.set(parent, stop);
928
+ return stop;
627
929
  }
628
930
  };
629
931
 
630
932
  // src/orbit.js
631
- if (!customElements.get("o-progress")) customElements.define("o-progress", OrbitProgress);
632
- if (!customElements.get("o-arc")) customElements.define("o-arc", OrbitArc);
633
- window.Orbit = Orbit;
933
+ function registerOrbit() {
934
+ if (typeof customElements === "undefined" || typeof document === "undefined") return;
935
+ if (!customElements.get("o-progress")) customElements.define("o-progress", OrbitProgress);
936
+ if (!customElements.get("o-arc")) customElements.define("o-arc", OrbitArc);
937
+ requestLayout(document);
938
+ window.Orbit = Orbit;
939
+ }
940
+ registerOrbit();
634
941
  })();