@zumer/orbit 1.4.12 → 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.12
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);
@@ -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,8 @@
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), 359.999999));
659
+ if (!(arcAngle > 0) || !(attrs.orbitRadius > 0)) return "";
350
660
  const a0 = -90 * (Math.PI / 180);
351
661
  const a1 = (-90 + arcAngle) * (Math.PI / 180);
352
662
  const x0 = 50 + realRadius * Math.cos(a0);
@@ -358,9 +668,11 @@
358
668
  }
359
669
  getAttributes() {
360
670
  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;
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);
364
676
  return {
365
677
  ...common,
366
678
  range,
@@ -370,27 +682,28 @@
370
682
  }
371
683
  getProgressAngle(attrs, full) {
372
684
  const { range, progress, maxValue } = attrs;
373
- return full ? (maxValue - 1e-5) / maxValue * range : progress / maxValue * range;
685
+ return full ? range : super.getProgressAngle(range, progress, maxValue);
374
686
  }
375
687
  calculateArcParameters(attrs, full) {
376
- const { shape, realRadius, arcHeightPercentage, orbitNumber, strokeWidth, arcHeight } = attrs;
688
+ const { shape, realRadius, arcHeightPercentage, orbitNumber: orbitNumber2, strokeWidth, arcHeight } = attrs;
377
689
  const arcAngle = this.getProgressAngle(attrs, full);
690
+ if (!(arcAngle > 0) || !(attrs.orbitRadius > 0)) return "";
378
691
  const params = super.calculateCommonArcParameters(
379
692
  arcAngle,
380
693
  realRadius,
381
694
  arcHeightPercentage,
382
- orbitNumber,
695
+ orbitNumber2,
383
696
  shape,
384
697
  strokeWidth,
385
698
  arcHeight
386
699
  );
387
- return super.generatePathData(shape, params, arcHeight, orbitNumber);
700
+ return super.generatePathData(shape, params, arcHeight, orbitNumber2);
388
701
  }
389
702
  };
390
703
 
391
704
  // src/js/orbit-arc.js
392
- var template = document.createElement("template");
393
- template.innerHTML = `
705
+ var template = typeof document === "undefined" ? null : document.createElement("template");
706
+ if (template) template.innerHTML = `
394
707
  <style>
395
708
  :host {
396
709
  --o-fill: var(--o-gray-light);
@@ -452,30 +765,16 @@
452
765
  this.attachShadow({ mode: "open" });
453
766
  this.shadowRoot.appendChild(template.content.cloneNode(true));
454
767
  }
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
768
  update() {
477
769
  const attrs = this.getAttributes();
478
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
+ }
479
778
  const orbitPath = this.shadowRoot.querySelector("#orbitPath");
480
779
  const orbitShape = this.shadowRoot.querySelector("#orbitShape");
481
780
  const textPath = this.shadowRoot.querySelector("textPath");
@@ -493,6 +792,8 @@
493
792
  }
494
793
  if (fitRange) {
495
794
  textPath.parentElement.setAttribute("textLength", orbitPath.getTotalLength());
795
+ } else {
796
+ textPath.parentElement.removeAttribute("textLength");
496
797
  }
497
798
  textPath.parentElement.style.fontSize = `calc(${fontSize} * (100 / (${length}) * (12 / var(--o-orbit-number)))`;
498
799
  textPath.textContent = this.textContent;
@@ -507,7 +808,10 @@
507
808
  warnIfTextOverflows(orbitPath, textPath, fitRange) {
508
809
  const raw = (this.textContent || "").trim();
509
810
  if (!raw || fitRange || !this.isConnected) return;
510
- requestAnimationFrame(() => {
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;
511
815
  try {
512
816
  const pathLen = orbitPath.getTotalLength();
513
817
  const textLen = textPath.parentElement.getComputedTextLength();
@@ -523,59 +827,52 @@
523
827
  }
524
828
  getAttributes() {
525
829
  const common = super.getCommonAttributes(this);
526
- let arcAngle;
830
+ const { style } = common;
831
+ const range = Math.max(0, Math.min(360, this.readAngle(style, "--o-range", 360)));
527
832
  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;
833
+ const fitRange = this.hasAttribute("fit-range") || this.classList.contains("fit-range");
834
+ const length = common.orbitRadius * 24 / common.orbitNumber || 100;
530
835
  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");
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");
545
852
  }
546
- } else {
547
- const rawAngle = getComputedStyle(this).getPropertyValue("--o-angle");
548
- arcAngle = this.calcularExpresionCSS(rawAngle);
549
853
  }
550
- return {
551
- ...common,
552
- gap,
553
- arcAngle,
554
- flip,
555
- fitRange,
556
- length,
557
- fontSize,
558
- textAnchor
559
- };
854
+ return { ...common, gap, arcAngle, stackOffset, flip, fitRange, length, fontSize, textAnchor };
560
855
  }
561
856
  calculateArcParameters(attrs) {
562
- 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: "" };
563
859
  const params = super.calculateCommonArcParameters(
564
860
  arcAngle,
565
861
  realRadius,
566
862
  arcHeightPercentage,
567
- orbitNumber,
863
+ orbitNumber2,
568
864
  shape,
569
865
  strokeWidth,
570
866
  arcHeight,
571
867
  gap
572
868
  );
573
- const dShape = super.generatePathData(shape, params, arcHeight, orbitNumber);
869
+ const dShape = super.generatePathData(shape, params, arcHeight, orbitNumber2);
574
870
  return { dShape };
575
871
  }
576
872
  calculateTextArcParameters(attrs) {
577
873
  const { arcAngle, realRadius, gap, flip } = attrs;
578
- const adjustedGap = gap * 0.5;
874
+ if (!(arcAngle > 0)) return { dPath: "" };
875
+ const adjustedGap = Math.min(gap * 0.5, arcAngle * 0.49);
579
876
  const sweepFlag = flip ? 0 : 1;
580
877
  const largeArcFlag = arcAngle <= 180 ? 0 : 1;
581
878
  let coordX1 = 50 + realRadius * Math.cos((-90 + adjustedGap) * (Math.PI / 180));
@@ -587,48 +884,53 @@
587
884
  return { dPath };
588
885
  }
589
886
  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;
887
+ return cssNumber(this, cssExpression, 0, "angle");
599
888
  }
600
889
  };
601
890
 
602
891
  // src/js/orbit-resize.js
892
+ var resizing = /* @__PURE__ */ new WeakMap();
603
893
  var Orbit = {
894
+ refresh: refreshLayout,
604
895
  resize(parentElementSelector) {
605
- const parent = document.querySelector(parentElementSelector);
606
- 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)) {
607
899
  console.error("Orbit.resize: element not found:", parentElementSelector);
608
- return;
900
+ return () => {
901
+ };
609
902
  }
903
+ (_b = resizing.get(parent)) == null ? void 0 : _b();
904
+ const view = parent.ownerDocument.defaultView;
610
905
  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);
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);
624
910
  }
911
+ refreshLayout(parent);
912
+ };
913
+ const observer = new view.ResizeObserver((entries) => {
914
+ for (const entry of entries) applyRatio(entry.contentRect.width);
625
915
  });
626
- 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;
627
924
  }
628
925
  };
629
926
 
630
927
  // 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;
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();
634
936
  })();