@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/README.md +57 -7
- package/dist/orbit.css +448 -600
- package/dist/orbit.js +535 -157
- package/dist/orbit.min.css +1 -1
- package/dist/orbit.min.js +39 -4
- package/dist/orbit.mjs +941 -0
- package/package.json +13 -4
package/dist/orbit.js
CHANGED
|
@@ -1,23 +1,340 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* orbit
|
|
3
|
-
* v.1.
|
|
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
|
-
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
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 /
|
|
20
|
-
const arcHeightPercentage = orbitRadius > 0 ? arcHeight
|
|
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
|
-
|
|
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,
|
|
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 =
|
|
77
|
-
const bigGap = (gap + strokeWidth * 1.25) /
|
|
78
|
-
const smallGap = (gap + strokeWidth * 1.25) /
|
|
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,
|
|
428
|
+
generatePathData(shape, params, arcHeight, orbitNumber2) {
|
|
110
429
|
let d = "";
|
|
111
430
|
switch (shape) {
|
|
112
431
|
case "rounded":
|
|
113
|
-
d = this.generateRoundedPath(params, arcHeight,
|
|
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,
|
|
440
|
+
d = this.generateCircleBPath(params, arcHeight, orbitNumber2);
|
|
122
441
|
break;
|
|
123
442
|
case "arrow":
|
|
124
|
-
d = this.generateArrowPath(params,
|
|
443
|
+
d = this.generateArrowPath(params, orbitNumber2);
|
|
125
444
|
break;
|
|
126
445
|
case "backslash":
|
|
127
446
|
case "slash":
|
|
128
|
-
d = this.generateSlashPath(params, shape,
|
|
447
|
+
d = this.generateSlashPath(params, shape, orbitNumber2);
|
|
129
448
|
break;
|
|
130
449
|
case "zigzag":
|
|
131
|
-
d = this.generateZigzagPath(params, arcHeight,
|
|
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,
|
|
457
|
+
generateRoundedPath(params, arcHeight, orbitNumber2) {
|
|
139
458
|
const { bigRadius, smallRadius } = params;
|
|
140
|
-
const
|
|
141
|
-
const
|
|
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 /
|
|
145
|
-
const newUpperEnd = this.arcPoint(bigRadius, params.upperAngleEnd, 0, -curve /
|
|
146
|
-
const newInnerStart = this.arcPoint(smallRadius, params.innerAngleStart, 0, curve /
|
|
147
|
-
const newInnerEnd = this.arcPoint(smallRadius, params.innerAngleEnd, 0, -curve /
|
|
148
|
-
const upperPointStart = this.arcPoint(bigRadius, params.upperAngleStart, -(curve / 2) /
|
|
149
|
-
const upperPointEnd = this.arcPoint(bigRadius, params.upperAngleEnd, -(curve / 2) /
|
|
150
|
-
const innerPointStart = this.arcPoint(smallRadius, params.innerAngleStart, curve / 2 /
|
|
151
|
-
const innerPointEnd = this.arcPoint(smallRadius, params.innerAngleEnd, curve / 2 /
|
|
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,
|
|
495
|
+
generateCircleBPath(params, arcHeight, orbitNumber2) {
|
|
176
496
|
const { upperAngleStart, upperAngleEnd, innerAngleStart, innerAngleEnd, bigRadius, smallRadius } = params;
|
|
177
|
-
const
|
|
178
|
-
const
|
|
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 /
|
|
182
|
-
const newUpperEnd = this.arcPoint(bigRadius, upperAngleEnd, 0, -segment /
|
|
183
|
-
const newInnerStart = this.arcPoint(smallRadius, innerAngleStart, 0, segment /
|
|
184
|
-
const newInnerEnd = this.arcPoint(smallRadius, innerAngleEnd, 0, -segment /
|
|
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,
|
|
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 /
|
|
195
|
-
const middleStart = this.arcPoint(radius, params.upperAngleStart, 0, 24 /
|
|
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,
|
|
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 /
|
|
207
|
-
const newUpperEnd = this.arcPoint(bigRadius, upperAngleEnd, 0, shape === "backslash" ? 0 : 24 /
|
|
208
|
-
const newInnerStart = this.arcPoint(smallRadius, innerAngleStart, 0, shape === "backslash" ? 24 /
|
|
209
|
-
const newInnerEnd = this.arcPoint(smallRadius, innerAngleEnd, 0, shape === "backslash" ? 24 /
|
|
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,
|
|
537
|
+
generateZigzagPath(params, arcHeight, orbitNumber2) {
|
|
217
538
|
const { upperArcStart, upperArcEnd, innerArcStart, innerArcEnd, bigRadius, smallRadius, largeArcFlagUpper, largeArcFlagInner, radius } = params;
|
|
218
|
-
const h2 = arcHeight /
|
|
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
|
|
313
|
-
const
|
|
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 =
|
|
320
|
-
const
|
|
321
|
-
const
|
|
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 ?
|
|
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
|
-
|
|
695
|
+
orbitNumber2,
|
|
341
696
|
shape,
|
|
342
697
|
strokeWidth,
|
|
343
698
|
arcHeight
|
|
344
699
|
);
|
|
345
|
-
return super.generatePathData(shape, params, arcHeight,
|
|
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
|
-
|
|
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")
|
|
453
|
-
const length =
|
|
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 =
|
|
456
|
-
const
|
|
457
|
-
const value =
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
this.
|
|
464
|
-
|
|
465
|
-
this.
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
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
|
-
|
|
863
|
+
orbitNumber2,
|
|
492
864
|
shape,
|
|
493
865
|
strokeWidth,
|
|
494
866
|
arcHeight,
|
|
495
867
|
gap
|
|
496
868
|
);
|
|
497
|
-
const dShape = super.generatePathData(shape, params, arcHeight,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
530
|
-
|
|
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
|
-
|
|
536
|
-
|
|
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
|
-
|
|
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
|
-
|
|
556
|
-
|
|
557
|
-
|
|
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
|
})();
|