@zumer/orbit 1.6.0 → 2.0.1
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 +218 -89
- package/dist/custom-elements.json +250 -0
- package/dist/orbit.css +2776 -1261
- package/dist/orbit.d.ts +107 -0
- package/dist/orbit.js +373 -209
- package/dist/orbit.min.css +1 -1
- package/dist/orbit.min.js +48 -26
- package/dist/orbit.mjs +373 -209
- package/dist/react.d.ts +25 -0
- package/dist/style.d.ts +3 -0
- package/dist/vscode.html-custom-data.json +166 -0
- package/package.json +21 -3
package/dist/orbit.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* orbit
|
|
3
|
-
* v.
|
|
3
|
+
* v.2.0.1
|
|
4
4
|
* Author Juan Martin Muda - Zumerlab
|
|
5
5
|
* License MIT
|
|
6
6
|
*/
|
|
@@ -18,6 +18,56 @@ function orbitNumber(element) {
|
|
|
18
18
|
}
|
|
19
19
|
return ((_a = element.classList) == null ? void 0 : _a.contains("orbit")) ? null : void 0;
|
|
20
20
|
}
|
|
21
|
+
var WARNED = Symbol.for("zumer.orbit.warned.v1");
|
|
22
|
+
function warnOnce(element, key, message) {
|
|
23
|
+
const seen = element[WARNED] || (element[WARNED] = /* @__PURE__ */ new Set());
|
|
24
|
+
if (seen.has(key)) return;
|
|
25
|
+
seen.add(key);
|
|
26
|
+
console.warn(`[orbit] ${message}`);
|
|
27
|
+
}
|
|
28
|
+
function composedClosest(element, selector) {
|
|
29
|
+
var _a, _b;
|
|
30
|
+
for (let current = element; current; current = current.parentElement || ((_a = current.getRootNode) == null ? void 0 : _a.call(current).host)) {
|
|
31
|
+
if ((_b = current.matches) == null ? void 0 : _b.call(current, selector)) return current;
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
var KINDS = ".satellite, .vector, .side, o-arc, o-progress";
|
|
36
|
+
function describe(element) {
|
|
37
|
+
const classes = [...element.classList].slice(0, 3).map((name) => "." + name).join("");
|
|
38
|
+
return `<${element.localName}${classes ? " " + classes : ""}>`;
|
|
39
|
+
}
|
|
40
|
+
function diagnose(ring) {
|
|
41
|
+
const spot = ring.parentElement;
|
|
42
|
+
const universe = composedClosest(spot.parentElement, ".bigbang, .satellite");
|
|
43
|
+
if (!universe) {
|
|
44
|
+
warnOnce(spot, "universe", 'A .gravity-spot is not inside a .bigbang, so it measures the viewport. Wrap it: <div class="bigbang"><div class="gravity-spot">\u2026</div></div>.');
|
|
45
|
+
} else if (universe.getClientRects().length && (!universe.clientWidth || !universe.clientHeight)) {
|
|
46
|
+
warnOnce(universe, "empty", `${describe(universe)} has no size (${universe.clientWidth}\xD7${universe.clientHeight}px), so its system is invisible. Give it a width, or place it in a parent with a width.`);
|
|
47
|
+
}
|
|
48
|
+
const visual = ring.querySelector(":scope > o-arc, :scope > o-progress");
|
|
49
|
+
if (visual) {
|
|
50
|
+
const style = ring.ownerDocument.defaultView.getComputedStyle(ring);
|
|
51
|
+
const x = style.getPropertyValue("--o-ellipse-x").trim(), y = style.getPropertyValue("--o-ellipse-y").trim();
|
|
52
|
+
if (x && Number(x) !== 1 || y && Number(y) !== 1) {
|
|
53
|
+
warnOnce(ring, "ellipse-visual", `${describe(visual)} is on ${describe(ring)}, which is stretched into an ellipse (--o-ellipse-x: ${x || 1}, --o-ellipse-y: ${y || 1}). Arcs and progress are drawn on circles only, so it is hidden. Put it on a round ring.`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (!composedClosest(ring, ".dev-orbit")) return;
|
|
57
|
+
for (const child of ring.children) {
|
|
58
|
+
if (orbitNumber(child) !== void 0) {
|
|
59
|
+
warnOnce(child, "nested-ring", `${describe(child)} is inside another ring. Rings must be direct children of .gravity-spot; to nest a system, put a .gravity-spot directly inside a .satellite.`);
|
|
60
|
+
} else if (!child.matches(KINDS)) {
|
|
61
|
+
warnOnce(child, "ring-child", `${describe(child)} is not placed by its ring. A ring holds only .satellite, .vector, .side, <o-arc> and <o-progress>; wrap content as <div class="satellite"><div class="capsule">\u2026</div></div>.`);
|
|
62
|
+
} else if (child.classList.contains("satellite")) {
|
|
63
|
+
for (const inner of child.children) {
|
|
64
|
+
if (!inner.matches(".capsule, .gravity-spot, .bigbang")) {
|
|
65
|
+
warnOnce(inner, "satellite-child", `${describe(inner)} sits directly in a .satellite. Put content in a .capsule so it stays upright and centered.`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
21
71
|
function setLayoutProperty(element, name, value) {
|
|
22
72
|
const text = String(value);
|
|
23
73
|
if (element.style.getPropertyValue(name) !== text) element.style.setProperty(name, text);
|
|
@@ -62,7 +112,8 @@ function layoutRing(ring) {
|
|
|
62
112
|
else if (child.localName === "o-progress") setLayoutProperty(child, "--o-layout-index", 0);
|
|
63
113
|
}
|
|
64
114
|
const count = Math.max(counts.arc, counts.satellite, counts.vector, 1);
|
|
65
|
-
const
|
|
115
|
+
const points = counts.satellite || counts.vector;
|
|
116
|
+
const divisor = counts.side ? String(counts.side) : points ? `max(1, ${count} - var(--o-fit-range, 0))` : String(count);
|
|
66
117
|
setLayoutProperty(ring, "--o-layout-angle", `calc(var(--o-range, 360deg) / ${divisor})`);
|
|
67
118
|
}
|
|
68
119
|
function isScope(root) {
|
|
@@ -88,10 +139,19 @@ function registryFor(doc) {
|
|
|
88
139
|
});
|
|
89
140
|
return doc[REGISTRY];
|
|
90
141
|
}
|
|
142
|
+
function cancel(state) {
|
|
143
|
+
if (state.frame < 0) state.view.clearTimeout(-state.frame);
|
|
144
|
+
else if (state.frame) state.view.cancelAnimationFrame(state.frame);
|
|
145
|
+
state.frame = 0;
|
|
146
|
+
}
|
|
147
|
+
function settle(state) {
|
|
148
|
+
for (const resolve of state.waiters.splice(0)) resolve();
|
|
149
|
+
}
|
|
91
150
|
function dispose(state) {
|
|
92
151
|
if (state.disposed) return;
|
|
93
152
|
state.disposed = true;
|
|
94
|
-
|
|
153
|
+
cancel(state);
|
|
154
|
+
settle(state);
|
|
95
155
|
state.observer.disconnect();
|
|
96
156
|
state.resize.disconnect();
|
|
97
157
|
for (const remove of state.listeners) remove();
|
|
@@ -146,17 +206,20 @@ function createState(scope) {
|
|
|
146
206
|
all: true,
|
|
147
207
|
frame: 0,
|
|
148
208
|
disposed: false,
|
|
149
|
-
listeners: []
|
|
209
|
+
listeners: [],
|
|
210
|
+
waiters: []
|
|
150
211
|
};
|
|
151
212
|
const listen = (target, event, listener, options) => {
|
|
152
213
|
target == null ? void 0 : target.addEventListener(event, listener, options);
|
|
153
214
|
state.listeners.push(() => target == null ? void 0 : target.removeEventListener(event, listener, options));
|
|
154
215
|
};
|
|
155
216
|
const queue = () => {
|
|
156
|
-
if (
|
|
217
|
+
if (state.disposed || state.frame) return;
|
|
218
|
+
const run = () => {
|
|
157
219
|
state.frame = 0;
|
|
158
220
|
state.flush();
|
|
159
|
-
}
|
|
221
|
+
};
|
|
222
|
+
state.frame = doc.visibilityState === "hidden" ? -view.setTimeout(run, 0) : view.requestAnimationFrame(run);
|
|
160
223
|
};
|
|
161
224
|
state.queue = queue;
|
|
162
225
|
state.flush = () => flush(state);
|
|
@@ -196,6 +259,12 @@ function createState(scope) {
|
|
|
196
259
|
invalidateDescendants(state);
|
|
197
260
|
queue();
|
|
198
261
|
};
|
|
262
|
+
listen(doc, "visibilitychange", () => {
|
|
263
|
+
if (doc.visibilityState === "hidden" && state.frame > 0) {
|
|
264
|
+
cancel(state);
|
|
265
|
+
queue();
|
|
266
|
+
}
|
|
267
|
+
});
|
|
199
268
|
if (scope === doc) {
|
|
200
269
|
listen(view, "resize", invalidateAll);
|
|
201
270
|
listen(doc.fonts, "loadingdone", invalidateAll);
|
|
@@ -255,13 +324,13 @@ function discover(state) {
|
|
|
255
324
|
state.scan = false;
|
|
256
325
|
}
|
|
257
326
|
function flush(state) {
|
|
258
|
-
var _a;
|
|
259
327
|
if (state.disposed) return;
|
|
260
328
|
if (state.scope.host && !state.scope.host.isConnected) {
|
|
261
329
|
dispose(state);
|
|
262
330
|
return;
|
|
263
331
|
}
|
|
264
332
|
state.observer.disconnect();
|
|
333
|
+
const drawn = [];
|
|
265
334
|
try {
|
|
266
335
|
if (state.scan) discover(state);
|
|
267
336
|
const dirty = state.all ? /* @__PURE__ */ new Set([...state.rings, ...state.visuals]) : state.dirty;
|
|
@@ -270,15 +339,21 @@ function flush(state) {
|
|
|
270
339
|
if (!target.isConnected) continue;
|
|
271
340
|
if (state.rings.has(target)) {
|
|
272
341
|
layoutRing(target);
|
|
342
|
+
diagnose(target);
|
|
273
343
|
for (const visual of target.querySelectorAll(VISUALS)) visuals.add(visual);
|
|
274
344
|
} else if (state.visuals.has(target)) visuals.add(target);
|
|
275
345
|
}
|
|
276
|
-
for (const visual of state.visuals) if (visuals.has(visual)
|
|
346
|
+
for (const visual of state.visuals) if (visuals.has(visual) && visual.update) {
|
|
347
|
+
visual.update();
|
|
348
|
+
drawn.push(visual);
|
|
349
|
+
}
|
|
277
350
|
state.dirty = /* @__PURE__ */ new Set();
|
|
278
351
|
state.all = false;
|
|
279
352
|
} finally {
|
|
280
353
|
if (!state.disposed) state.observer.observe(state.scope, OBSERVE);
|
|
281
354
|
}
|
|
355
|
+
for (const visual of drawn) visual.dispatchEvent(new state.view.Event("o-rendered", { bubbles: true, composed: true }));
|
|
356
|
+
if (!state.frame) settle(state);
|
|
282
357
|
}
|
|
283
358
|
function requestLayout(element = globalThis.document) {
|
|
284
359
|
var _a;
|
|
@@ -299,11 +374,22 @@ function refreshLayout(root = globalThis.document) {
|
|
|
299
374
|
if (current !== state && (!current.scope.host || !composedContains(root, current.scope.host))) continue;
|
|
300
375
|
current.scan = true;
|
|
301
376
|
current.all = true;
|
|
302
|
-
|
|
303
|
-
current.frame = 0;
|
|
377
|
+
cancel(current);
|
|
304
378
|
current.flush();
|
|
305
379
|
}
|
|
306
380
|
}
|
|
381
|
+
function layoutReady(root = globalThis.document) {
|
|
382
|
+
const scope = scopeFor(root);
|
|
383
|
+
const state = scope && ensureState(scope);
|
|
384
|
+
if (!state) return Promise.resolve();
|
|
385
|
+
pruneDisconnected(state.registry);
|
|
386
|
+
const pending = [...state.registry.states].filter((current) => current === state || current.scope.host && composedContains(root, current.scope.host));
|
|
387
|
+
return Promise.all(pending.map((current) => new Promise((resolve) => {
|
|
388
|
+
current.waiters.push(resolve);
|
|
389
|
+
current.queue();
|
|
390
|
+
}))).then(() => {
|
|
391
|
+
});
|
|
392
|
+
}
|
|
307
393
|
|
|
308
394
|
// src/js/orbit-base.js
|
|
309
395
|
var OrbitBase = class extends (globalThis.HTMLElement || class {
|
|
@@ -312,8 +398,32 @@ var OrbitBase = class extends (globalThis.HTMLElement || class {
|
|
|
312
398
|
super();
|
|
313
399
|
}
|
|
314
400
|
connectedCallback() {
|
|
401
|
+
for (const name of ["value", "max"]) {
|
|
402
|
+
if (Object.prototype.hasOwnProperty.call(this, name)) {
|
|
403
|
+
const pending = this[name];
|
|
404
|
+
delete this[name];
|
|
405
|
+
this[name] = pending;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
315
408
|
requestLayout(this);
|
|
316
409
|
}
|
|
410
|
+
/** value and max mirror their attributes, so property bindings redraw too. */
|
|
411
|
+
get value() {
|
|
412
|
+
const raw = this.getAttribute("value");
|
|
413
|
+
return raw === null || raw.trim() === "" ? null : Number(raw);
|
|
414
|
+
}
|
|
415
|
+
set value(next) {
|
|
416
|
+
if (next === null || next === void 0 || next === "") this.removeAttribute("value");
|
|
417
|
+
else this.setAttribute("value", String(next));
|
|
418
|
+
}
|
|
419
|
+
get max() {
|
|
420
|
+
const raw = this.getAttribute("max");
|
|
421
|
+
return raw === null ? 100 : Number(raw);
|
|
422
|
+
}
|
|
423
|
+
set max(next) {
|
|
424
|
+
if (next === null || next === void 0 || next === "") this.removeAttribute("max");
|
|
425
|
+
else this.setAttribute("max", String(next));
|
|
426
|
+
}
|
|
317
427
|
disconnectedCallback() {
|
|
318
428
|
if (this._textFrame) this.ownerDocument.defaultView.cancelAnimationFrame(this._textFrame);
|
|
319
429
|
this._textFrame = 0;
|
|
@@ -324,6 +434,28 @@ var OrbitBase = class extends (globalThis.HTMLElement || class {
|
|
|
324
434
|
readAngle(style, name, fallback = 0) {
|
|
325
435
|
return cssNumber(this, style.getPropertyValue(name), fallback, "angle");
|
|
326
436
|
}
|
|
437
|
+
/** The value attribute as a number. Invalid values draw nothing, and say why. */
|
|
438
|
+
readValue() {
|
|
439
|
+
const raw = this.getAttribute("value");
|
|
440
|
+
if (raw === null || raw.trim() === "") return 0;
|
|
441
|
+
const value = Number(raw);
|
|
442
|
+
if (!Number.isFinite(value)) {
|
|
443
|
+
warnOnce(this, "value:" + raw, `<${this.localName} value="${raw}"> is not a number, so nothing is drawn.`);
|
|
444
|
+
return 0;
|
|
445
|
+
}
|
|
446
|
+
return value;
|
|
447
|
+
}
|
|
448
|
+
/** The max attribute (default 100). It must be a positive number. */
|
|
449
|
+
readMax() {
|
|
450
|
+
const raw = this.getAttribute("max");
|
|
451
|
+
if (raw === null) return 100;
|
|
452
|
+
const max = Number(raw);
|
|
453
|
+
if (!(max > 0)) {
|
|
454
|
+
warnOnce(this, "max:" + raw, `<${this.localName} max="${raw}"> must be a positive number, so nothing is drawn.`);
|
|
455
|
+
return 0;
|
|
456
|
+
}
|
|
457
|
+
return max;
|
|
458
|
+
}
|
|
327
459
|
getCommonAttributes(element) {
|
|
328
460
|
var _a;
|
|
329
461
|
const style = element.ownerDocument.defaultView.getComputedStyle(element);
|
|
@@ -351,7 +483,7 @@ var OrbitBase = class extends (globalThis.HTMLElement || class {
|
|
|
351
483
|
arcHeight,
|
|
352
484
|
realRadius,
|
|
353
485
|
arcAngle: 0,
|
|
354
|
-
//
|
|
486
|
+
// set by each component
|
|
355
487
|
shape,
|
|
356
488
|
arcHeightPercentage,
|
|
357
489
|
orbitNumber: orbitNumber2,
|
|
@@ -364,22 +496,6 @@ var OrbitBase = class extends (globalThis.HTMLElement || class {
|
|
|
364
496
|
if (!Number.isFinite(value) || !Number.isFinite(maxValue) || maxValue <= 0) return 0;
|
|
365
497
|
return Math.min(1, Math.max(0, value / maxValue)) * Math.max(0, Math.min(360, maxAngle));
|
|
366
498
|
}
|
|
367
|
-
getControlPoint(x, y, x1, y1, direction = "clockwise") {
|
|
368
|
-
const xm = (x + x1) / 2;
|
|
369
|
-
const ym = (y + y1) / 2;
|
|
370
|
-
const dx = x1 - x;
|
|
371
|
-
const dy = y1 - y;
|
|
372
|
-
if (direction === "clockwise") {
|
|
373
|
-
return {
|
|
374
|
-
xc: xm + dy * 0.4,
|
|
375
|
-
yc: ym - dx * 0.4
|
|
376
|
-
};
|
|
377
|
-
}
|
|
378
|
-
return {
|
|
379
|
-
xc: xm - dy * 0.4,
|
|
380
|
-
yc: ym + dx * 0.4
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
499
|
arcPoint(radius, angle, radiusAdjustment = 0, angleOffsetDegrees = 0) {
|
|
384
500
|
const adjustedRadius = radius + radiusAdjustment;
|
|
385
501
|
const adjustedAngle = angle + angleOffsetDegrees * Math.PI / 180;
|
|
@@ -390,7 +506,7 @@ var OrbitBase = class extends (globalThis.HTMLElement || class {
|
|
|
390
506
|
}
|
|
391
507
|
calculateCommonArcParameters(arcAngle, radius, arcHeightPercentage, orbitNumber2, shape, strokeWidth, arcHeight, gap = 0) {
|
|
392
508
|
const offset = Math.PI / 2;
|
|
393
|
-
const fangle = Math.max(0, Math.min(
|
|
509
|
+
const fangle = Math.max(0, Math.min(360, arcAngle)) * Math.PI / 180;
|
|
394
510
|
const bigRadius = radius + arcHeightPercentage;
|
|
395
511
|
const smallRadius = Math.max(1e-3, radius - arcHeightPercentage);
|
|
396
512
|
const bigGap = Math.min(fangle * 0.49, (gap + strokeWidth * 1.25) / orbitNumber2 / bigRadius);
|
|
@@ -403,19 +519,11 @@ var OrbitBase = class extends (globalThis.HTMLElement || class {
|
|
|
403
519
|
const upperArcEnd = this.arcPoint(bigRadius, upperAngleEnd);
|
|
404
520
|
const innerArcStart = this.arcPoint(smallRadius, innerAngleStart);
|
|
405
521
|
const innerArcEnd = this.arcPoint(smallRadius, innerAngleEnd);
|
|
406
|
-
const upperSweep = upperAngleEnd - upperAngleStart;
|
|
407
|
-
const innerSweep = innerAngleEnd - innerAngleStart;
|
|
408
|
-
const largeArcFlagUpper = upperSweep > Math.PI ? 1 : 0;
|
|
409
|
-
const largeArcFlagInner = innerSweep > Math.PI ? 1 : 0;
|
|
410
522
|
return {
|
|
411
523
|
upperArcStart,
|
|
412
524
|
upperArcEnd,
|
|
413
525
|
innerArcStart,
|
|
414
526
|
innerArcEnd,
|
|
415
|
-
largeArcFlag: largeArcFlagUpper,
|
|
416
|
-
// back-compat alias; prefer the two below
|
|
417
|
-
largeArcFlagUpper,
|
|
418
|
-
largeArcFlagInner,
|
|
419
527
|
bigRadius,
|
|
420
528
|
smallRadius,
|
|
421
529
|
radius,
|
|
@@ -425,143 +533,153 @@ var OrbitBase = class extends (globalThis.HTMLElement || class {
|
|
|
425
533
|
innerAngleEnd
|
|
426
534
|
};
|
|
427
535
|
}
|
|
428
|
-
generatePathData(shape, params
|
|
429
|
-
let d = "";
|
|
536
|
+
generatePathData(shape, params) {
|
|
430
537
|
switch (shape) {
|
|
431
538
|
case "rounded":
|
|
432
|
-
|
|
433
|
-
break;
|
|
539
|
+
return this.generateRoundedPath(params);
|
|
434
540
|
case "circle":
|
|
435
541
|
case "circle-a":
|
|
436
542
|
case "bullet":
|
|
437
|
-
|
|
438
|
-
break;
|
|
543
|
+
return this.generateCirclePath(params, shape);
|
|
439
544
|
case "circle-b":
|
|
440
|
-
|
|
441
|
-
break;
|
|
545
|
+
return this.generateCircleBPath(params);
|
|
442
546
|
case "arrow":
|
|
443
|
-
|
|
444
|
-
break;
|
|
547
|
+
return this.generateArrowPath(params);
|
|
445
548
|
case "backslash":
|
|
446
549
|
case "slash":
|
|
447
|
-
|
|
448
|
-
break;
|
|
550
|
+
return this.generateSlashPath(params, shape);
|
|
449
551
|
case "zigzag":
|
|
450
|
-
|
|
451
|
-
break;
|
|
552
|
+
return this.generateZigzagPath(params);
|
|
452
553
|
default:
|
|
453
|
-
|
|
554
|
+
return this.generateDefaultPath(params);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
/** "x,y" of a point at radius and angle (radians), in viewBox units. */
|
|
558
|
+
pt(radius, angle) {
|
|
559
|
+
const point = this.arcPoint(radius, angle);
|
|
560
|
+
return `${point.x},${point.y}`;
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* SVG arc commands from the current point (at `from`) to `to`, along one
|
|
564
|
+
* circle. Every arc in Orbit goes through here. Splitting into pieces of at
|
|
565
|
+
* most 90° means no large-arc flag to get wrong and no arc whose endpoints
|
|
566
|
+
* coincide, so a full 360° circle is just another arc.
|
|
567
|
+
*/
|
|
568
|
+
arcTo(radius, from, to) {
|
|
569
|
+
const steps = Math.max(1, Math.ceil(Math.abs(to - from) / (Math.PI / 2) - 1e-9));
|
|
570
|
+
const sweep = to >= from ? 1 : 0;
|
|
571
|
+
let d = "";
|
|
572
|
+
for (let i = 1; i <= steps; i++) {
|
|
573
|
+
d += ` A ${radius},${radius} 0 0 ${sweep} ${this.pt(radius, from + (to - from) * i / steps)}`;
|
|
454
574
|
}
|
|
455
575
|
return d;
|
|
456
576
|
}
|
|
457
|
-
|
|
577
|
+
/** An open arc along one radius: strokes, curved text and touch areas. */
|
|
578
|
+
linePath(radius, from, to) {
|
|
579
|
+
return `M ${this.pt(radius, from)}${this.arcTo(radius, from, to)}`;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Shape endings are sized from the band itself (h = half its thickness, in
|
|
583
|
+
* viewBox units), so they look the same on every ring and at every size.
|
|
584
|
+
* Angles are in radians, as in calculateCommonArcParameters().
|
|
585
|
+
*/
|
|
586
|
+
ending(params, factor) {
|
|
458
587
|
const { bigRadius, smallRadius } = params;
|
|
459
|
-
const
|
|
460
|
-
const
|
|
461
|
-
const
|
|
462
|
-
const
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
const
|
|
471
|
-
const
|
|
472
|
-
const
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
d += `Q ${Q.xc},${Q.yc} ${upperPointEnd.x},${upperPointEnd.y} L ${innerPointEnd.x},${innerPointEnd.y}`;
|
|
478
|
-
d += `Q ${Q1.xc},${Q1.yc} ${newInnerEnd.x},${newInnerEnd.y}`;
|
|
479
|
-
d += `A ${smallRadius},${smallRadius} 0 ${flagI} 0 ${newInnerStart.x},${newInnerStart.y}`;
|
|
480
|
-
d += `Q ${Q2.xc},${Q2.yc} ${innerPointStart.x},${innerPointStart.y} L ${upperPointStart.x},${upperPointStart.y}`;
|
|
481
|
-
d += ` Q ${Q3.xc},${Q3.yc} ${newUpperStart.x},${newUpperStart.y}`;
|
|
482
|
-
d += ` Z`;
|
|
588
|
+
const h = (bigRadius - smallRadius) / 2;
|
|
589
|
+
const middle = (bigRadius + smallRadius) / 2;
|
|
590
|
+
const span = Math.min(params.upperAngleEnd - params.upperAngleStart, params.innerAngleEnd - params.innerAngleStart);
|
|
591
|
+
const length = Math.max(0, Math.min(h * factor, span * smallRadius * 0.49));
|
|
592
|
+
return { h, middle, length, angle: length / middle };
|
|
593
|
+
}
|
|
594
|
+
generateDefaultPath(params) {
|
|
595
|
+
const { bigRadius: R, smallRadius: r, upperAngleStart: uS, upperAngleEnd: uE, innerAngleStart: iS, innerAngleEnd: iE } = params;
|
|
596
|
+
return `M ${this.pt(R, uS)}${this.arcTo(R, uS, uE)} L ${this.pt(r, iE)}${this.arcTo(r, iE, iS)} Z`;
|
|
597
|
+
}
|
|
598
|
+
generateRoundedPath(params) {
|
|
599
|
+
const { bigRadius: R, smallRadius: r, upperAngleStart: uS, upperAngleEnd: uE, innerAngleStart: iS, innerAngleEnd: iE } = params;
|
|
600
|
+
const { length: c } = this.ending(params, 0.5);
|
|
601
|
+
const corner = (radius, angle) => ` A ${c},${c} 0 0 1 ${this.pt(radius, angle)}`;
|
|
602
|
+
let d = `M ${this.pt(R, uS + c / R)}${this.arcTo(R, uS + c / R, uE - c / R)}`;
|
|
603
|
+
d += `${corner(R - c, uE)} L ${this.pt(r + c, iE)}${corner(r, iE - c / r)}`;
|
|
604
|
+
d += this.arcTo(r, iE - c / r, iS + c / r);
|
|
605
|
+
d += `${corner(r + c, iS)} L ${this.pt(R - c, uS)}${corner(R, uS + c / R)} Z`;
|
|
483
606
|
return d;
|
|
484
607
|
}
|
|
485
|
-
// Dentro de la clase OrbitCommon en orbit-common.js
|
|
486
608
|
generateCirclePath(params, shape) {
|
|
487
|
-
const {
|
|
488
|
-
|
|
489
|
-
d
|
|
490
|
-
d +=
|
|
491
|
-
d += ` A 1,1 0 0 ${shape === "circle" || shape === "circle-a" ? 1 : 0} ${upperArcStart.x},${upperArcStart.y} `;
|
|
492
|
-
d += ` Z`;
|
|
609
|
+
const { bigRadius: R, smallRadius: r, upperAngleStart: uS, upperAngleEnd: uE, innerAngleStart: iS, innerAngleEnd: iE } = params;
|
|
610
|
+
const startSweep = shape === "bullet" ? 0 : 1;
|
|
611
|
+
let d = `M ${this.pt(R, uS)}${this.arcTo(R, uS, uE)} A 1,1 0 0 1 ${this.pt(r, iE)}`;
|
|
612
|
+
d += `${this.arcTo(r, iE, iS)} A 1,1 0 0 ${startSweep} ${this.pt(R, uS)} Z`;
|
|
493
613
|
return d;
|
|
494
614
|
}
|
|
495
|
-
generateCircleBPath(params
|
|
496
|
-
const {
|
|
497
|
-
const
|
|
498
|
-
const
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
|
|
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);
|
|
506
|
-
let d = `M ${newUpperStart.x},${newUpperStart.y} A ${bigRadius},${bigRadius} 0 ${flagU} 1 ${newUpperEnd.x},${newUpperEnd.y}`;
|
|
507
|
-
d += ` A 1,1 0 0 1 ${newInnerEnd.x},${newInnerEnd.y} `;
|
|
508
|
-
d += ` A ${smallRadius},${smallRadius} 0 ${flagI} 0 ${newInnerStart.x},${newInnerStart.y}`;
|
|
509
|
-
d += ` A 1,1 0 0 1 ${newUpperStart.x},${newUpperStart.y} `;
|
|
510
|
-
d += ` Z`;
|
|
615
|
+
generateCircleBPath(params) {
|
|
616
|
+
const { bigRadius: R, smallRadius: r } = params;
|
|
617
|
+
const { h, angle } = this.ending(params, 1);
|
|
618
|
+
const a0 = params.upperAngleStart + angle;
|
|
619
|
+
const a1 = params.upperAngleEnd - angle;
|
|
620
|
+
let d = `M ${this.pt(R, a0)}${this.arcTo(R, a0, a1)} A ${h},${h} 0 0 1 ${this.pt(r, a1)}`;
|
|
621
|
+
d += `${this.arcTo(r, a1, a0)} A ${h},${h} 0 0 1 ${this.pt(R, a0)} Z`;
|
|
511
622
|
return d;
|
|
512
623
|
}
|
|
513
|
-
generateArrowPath(params
|
|
514
|
-
const {
|
|
515
|
-
const
|
|
516
|
-
const
|
|
517
|
-
|
|
518
|
-
d
|
|
519
|
-
d += `L ${
|
|
520
|
-
d +=
|
|
521
|
-
d += `L ${middleStart.x} ${middleStart.y}`;
|
|
522
|
-
d += `Z`;
|
|
624
|
+
generateArrowPath(params) {
|
|
625
|
+
const { bigRadius: R, smallRadius: r } = params;
|
|
626
|
+
const { middle, angle } = this.ending(params, 1.6);
|
|
627
|
+
const a0 = params.upperAngleStart;
|
|
628
|
+
const a1 = params.upperAngleEnd;
|
|
629
|
+
let d = `M ${this.pt(R, a0)}${this.arcTo(R, a0, a1 - angle)}`;
|
|
630
|
+
d += ` L ${this.pt(middle, a1)} L ${this.pt(r, a1 - angle)}`;
|
|
631
|
+
d += `${this.arcTo(r, a1 - angle, a0)} L ${this.pt(middle, a0 + angle)} Z`;
|
|
523
632
|
return d;
|
|
524
633
|
}
|
|
525
|
-
generateSlashPath(params, shape
|
|
526
|
-
const {
|
|
527
|
-
const
|
|
528
|
-
const
|
|
529
|
-
const
|
|
530
|
-
const
|
|
531
|
-
|
|
532
|
-
d += `L ${newInnerEnd.x} ${newInnerEnd.y}`;
|
|
533
|
-
d += `A ${smallRadius},${smallRadius} 0 ${largeArcFlagInner} 0 ${newInnerStart.x}, ${newInnerStart.y}`;
|
|
534
|
-
d += `Z`;
|
|
535
|
-
return d;
|
|
634
|
+
generateSlashPath(params, shape) {
|
|
635
|
+
const { bigRadius: R, smallRadius: r } = params;
|
|
636
|
+
const { angle } = this.ending(params, 1);
|
|
637
|
+
const a0 = params.upperAngleStart;
|
|
638
|
+
const a1 = params.upperAngleEnd;
|
|
639
|
+
const [outer0, outer1, inner0, inner1] = shape === "backslash" ? [a0, a1 - angle, a0 + angle, a1] : [a0 + angle, a1, a0, a1 - angle];
|
|
640
|
+
return `M ${this.pt(R, outer0)}${this.arcTo(R, outer0, outer1)} L ${this.pt(r, inner1)}${this.arcTo(r, inner1, inner0)} Z`;
|
|
536
641
|
}
|
|
537
|
-
generateZigzagPath(params
|
|
538
|
-
const {
|
|
539
|
-
const
|
|
540
|
-
const
|
|
541
|
-
const
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
const e4 = this.arcPoint(radius, params.innerAngleEnd, -h2, 3);
|
|
546
|
-
let d = `M ${upperArcStart.x},${upperArcStart.y} A ${bigRadius},${bigRadius} 0 ${largeArcFlagUpper} 1 ${upperArcEnd.x},${upperArcEnd.y}`;
|
|
547
|
-
d += `L ${e2.x} ${e2.y}`;
|
|
548
|
-
d += `L ${e3.x} ${e3.y}`;
|
|
549
|
-
d += `L ${e4.x} ${e4.y}`;
|
|
550
|
-
d += `L ${innerArcEnd.x} ${innerArcEnd.y}`;
|
|
551
|
-
d += `A ${smallRadius},${smallRadius} 0 ${largeArcFlagInner} 0 ${innerArcStart.x}, ${innerArcStart.y}`;
|
|
552
|
-
d += `L ${s2.x} ${s2.y}`;
|
|
553
|
-
d += `L ${s3.x} ${s3.y}`;
|
|
554
|
-
d += `L ${s4.x} ${s4.y}`;
|
|
555
|
-
d += `Z`;
|
|
642
|
+
generateZigzagPath(params) {
|
|
643
|
+
const { bigRadius: R, smallRadius: r } = params;
|
|
644
|
+
const { middle, angle } = this.ending(params, 1.6);
|
|
645
|
+
const a0 = params.upperAngleStart;
|
|
646
|
+
const a1 = params.upperAngleEnd;
|
|
647
|
+
let d = `M ${this.pt(R, a0 + angle)}${this.arcTo(R, a0 + angle, a1)}`;
|
|
648
|
+
d += ` L ${this.pt(middle, a1 - angle)} L ${this.pt(r, a1)}`;
|
|
649
|
+
d += `${this.arcTo(r, a1, a0 + angle)} L ${this.pt(middle, a0)} Z`;
|
|
556
650
|
return d;
|
|
557
651
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
652
|
+
/**
|
|
653
|
+
* The touch area of an interactive arc or progress: an invisible stroke
|
|
654
|
+
* along the middle of the band, from `from` to `to` degrees (0 = start of
|
|
655
|
+
* the element). It is as wide as the band plus `hit-width` px on each side,
|
|
656
|
+
* and catches the pointer whatever the fill, so transparent bands work.
|
|
657
|
+
*/
|
|
658
|
+
updateHitArea(hit, attrs, from, to) {
|
|
659
|
+
const extra = Math.max(0, Number(this.getAttribute("hit-width")) || 0);
|
|
660
|
+
this.fitCanvas(attrs, this.hasAttribute("interactive") ? extra : 0);
|
|
661
|
+
if (!this.hasAttribute("interactive") || !(to > from) || !(attrs.orbitRadius > 0)) {
|
|
662
|
+
hit.removeAttribute("d");
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
const radians = (degrees) => (degrees - 90) * Math.PI / 180;
|
|
666
|
+
hit.setAttribute("d", this.linePath(attrs.realRadius, radians(from), radians(Math.min(to, from + 360))));
|
|
667
|
+
hit.style.strokeWidth = `${attrs.arcHeight + attrs.strokeWidth + 2 * extra}px`;
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Grow the SVG canvas past the ring's box so it covers the whole band, its
|
|
671
|
+
* stroke and touch area. Some engines (WebKit) neither paint reliably nor
|
|
672
|
+
* hit-test SVG content that overflows its box.
|
|
673
|
+
*/
|
|
674
|
+
fitCanvas(attrs, extra) {
|
|
675
|
+
const svg = this.shadowRoot.querySelector("svg");
|
|
676
|
+
const scale = attrs.orbitRadius / 50;
|
|
677
|
+
if (!svg || !(scale > 0)) return;
|
|
678
|
+
const outer = (attrs.realRadius + attrs.arcHeightPercentage) * scale + attrs.strokeWidth + extra;
|
|
679
|
+
const pad = Math.max(0, Math.ceil(outer - attrs.orbitRadius));
|
|
680
|
+
const units = pad / scale;
|
|
681
|
+
svg.setAttribute("viewBox", `${-units} ${-units} ${100 + 2 * units} ${100 + 2 * units}`);
|
|
682
|
+
svg.style.inset = `${-pad}px`;
|
|
565
683
|
}
|
|
566
684
|
};
|
|
567
685
|
|
|
@@ -580,17 +698,11 @@ var OrbitProgress = class extends OrbitBase {
|
|
|
580
698
|
--o-back-stroke: none;
|
|
581
699
|
--o-back-stroke-width: 1;
|
|
582
700
|
}
|
|
583
|
-
:host(:hover) {
|
|
584
|
-
--o-fill: var(--o-gray-light);
|
|
585
|
-
--o-stroke: var(--o-fill);
|
|
586
|
-
--o-stroke-width: 1;
|
|
587
|
-
--o-back-fill: transparent;
|
|
588
|
-
--o-back-stroke: none;
|
|
589
|
-
--o-back-stroke-width: 1;
|
|
590
|
-
}
|
|
591
701
|
svg {
|
|
592
|
-
|
|
593
|
-
|
|
702
|
+
position: absolute;
|
|
703
|
+
inset: 0;
|
|
704
|
+
width: auto;
|
|
705
|
+
height: auto;
|
|
594
706
|
overflow: visible;
|
|
595
707
|
pointer-events: none;
|
|
596
708
|
}
|
|
@@ -602,6 +714,14 @@ var OrbitProgress = class extends OrbitBase {
|
|
|
602
714
|
:host([interactive]) svg * {
|
|
603
715
|
pointer-events: visiblePainted;
|
|
604
716
|
}
|
|
717
|
+
/* The touch area covers the whole track, whatever its colors. */
|
|
718
|
+
.hit {
|
|
719
|
+
fill: none;
|
|
720
|
+
stroke: transparent;
|
|
721
|
+
}
|
|
722
|
+
:host([interactive]) .hit {
|
|
723
|
+
pointer-events: stroke;
|
|
724
|
+
}
|
|
605
725
|
:host([interactive]) {
|
|
606
726
|
cursor: pointer;
|
|
607
727
|
}
|
|
@@ -617,11 +737,17 @@ var OrbitProgress = class extends OrbitBase {
|
|
|
617
737
|
stroke: var(--o-back-stroke);
|
|
618
738
|
stroke-width: var(--o-back-stroke-width);
|
|
619
739
|
}
|
|
620
|
-
/* variant="stroke": the thin, legible gauge
|
|
740
|
+
/* variant="stroke": the thin, legible gauge.
|
|
621
741
|
The default band is a FILLED donut wedge, so everyone building a
|
|
622
742
|
thin gauge tripped on --o-fill/--o-back-fill. With this variant the
|
|
623
743
|
paths are open arcs and the data color lives where you expect it:
|
|
624
744
|
--o-stroke for the bar, --o-back-stroke for the track. */
|
|
745
|
+
/* The stroke variant draws a 2px bar over a gray 1px track unless you
|
|
746
|
+
set your own values on the element. */
|
|
747
|
+
:host([variant="stroke"]) {
|
|
748
|
+
--o-stroke-width: 2;
|
|
749
|
+
--o-back-stroke: var(--o-gray-light);
|
|
750
|
+
}
|
|
625
751
|
:host([variant="stroke"]) .progress-bar {
|
|
626
752
|
fill: none;
|
|
627
753
|
stroke: var(--o-stroke);
|
|
@@ -636,6 +762,7 @@ var OrbitProgress = class extends OrbitBase {
|
|
|
636
762
|
}
|
|
637
763
|
</style>
|
|
638
764
|
<svg viewBox="0 0 100 100">
|
|
765
|
+
<path class="hit" vector-effect="non-scaling-stroke"></path>
|
|
639
766
|
<path class="progress-bg" shape-rendering="geometricPrecision" vector-effect="non-scaling-stroke"></path>
|
|
640
767
|
<path class="progress-bar" shape-rendering="geometricPrecision" vector-effect="non-scaling-stroke"></path>
|
|
641
768
|
</svg>
|
|
@@ -648,6 +775,7 @@ var OrbitProgress = class extends OrbitBase {
|
|
|
648
775
|
const dBar = isStroke ? this.calculateStrokeArc(attrs, false) : this.calculateArcParameters(attrs, false);
|
|
649
776
|
this.shadowRoot.querySelector(".progress-bg").setAttribute("d", dBg);
|
|
650
777
|
this.shadowRoot.querySelector(".progress-bar").setAttribute("d", dBar);
|
|
778
|
+
this.updateHitArea(this.shadowRoot.querySelector(".hit"), attrs, 0, attrs.range);
|
|
651
779
|
}
|
|
652
780
|
/**
|
|
653
781
|
* variant="stroke": open arc along the orbit radius (no closed band),
|
|
@@ -657,27 +785,15 @@ var OrbitProgress = class extends OrbitBase {
|
|
|
657
785
|
const { realRadius } = attrs;
|
|
658
786
|
const arcAngle = Math.max(0, Math.min(this.getProgressAngle(attrs, full), 360));
|
|
659
787
|
if (!(arcAngle > 0) || !(attrs.orbitRadius > 0)) return "";
|
|
660
|
-
|
|
661
|
-
|
|
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
|
-
}
|
|
665
|
-
const a0 = -90 * (Math.PI / 180);
|
|
666
|
-
const a1 = (-90 + arcAngle) * (Math.PI / 180);
|
|
667
|
-
const x0 = 50 + realRadius * Math.cos(a0);
|
|
668
|
-
const y0 = 50 + realRadius * Math.sin(a0);
|
|
669
|
-
const x1 = 50 + realRadius * Math.cos(a1);
|
|
670
|
-
const y1 = 50 + realRadius * Math.sin(a1);
|
|
671
|
-
const largeArcFlag = arcAngle > 180 ? 1 : 0;
|
|
672
|
-
return `M ${x0},${y0} A ${realRadius},${realRadius} 0 ${largeArcFlag} 1 ${x1},${y1}`;
|
|
788
|
+
const start = -Math.PI / 2;
|
|
789
|
+
return this.linePath(realRadius, start, start + arcAngle * Math.PI / 180);
|
|
673
790
|
}
|
|
674
791
|
getAttributes() {
|
|
675
792
|
const common = super.getCommonAttributes(this);
|
|
676
793
|
const range = Math.max(0, Math.min(360, this.readAngle(common.style, "--o-range", 360)));
|
|
677
794
|
const rawValue = common.style.getPropertyValue("--o-progress").trim();
|
|
678
|
-
const progress = rawValue ? this.readNumber(common.style, "--o-progress", 0) :
|
|
679
|
-
const
|
|
680
|
-
const maxValue = rawMax === null ? 100 : Number(rawMax);
|
|
795
|
+
const progress = rawValue ? this.readNumber(common.style, "--o-progress", 0) : this.readValue();
|
|
796
|
+
const maxValue = this.readMax();
|
|
681
797
|
return {
|
|
682
798
|
...common,
|
|
683
799
|
range,
|
|
@@ -702,7 +818,7 @@ var OrbitProgress = class extends OrbitBase {
|
|
|
702
818
|
strokeWidth,
|
|
703
819
|
arcHeight
|
|
704
820
|
);
|
|
705
|
-
return super.generatePathData(shape, params
|
|
821
|
+
return super.generatePathData(shape, params);
|
|
706
822
|
}
|
|
707
823
|
};
|
|
708
824
|
|
|
@@ -716,15 +832,11 @@ if (template) template.innerHTML = `
|
|
|
716
832
|
--o-stroke-width: 1;
|
|
717
833
|
--o-color: currentcolor;
|
|
718
834
|
}
|
|
719
|
-
:host(:hover) {
|
|
720
|
-
--o-fill: var(--o-gray-light);
|
|
721
|
-
--o-stroke: var(--o-fill);
|
|
722
|
-
--o-stroke-width: 1;
|
|
723
|
-
--o-color: currentcolor;
|
|
724
|
-
}
|
|
725
835
|
svg {
|
|
726
|
-
|
|
727
|
-
|
|
836
|
+
position: absolute;
|
|
837
|
+
inset: 0;
|
|
838
|
+
width: auto;
|
|
839
|
+
height: auto;
|
|
728
840
|
overflow: visible;
|
|
729
841
|
pointer-events: none;
|
|
730
842
|
}
|
|
@@ -738,6 +850,20 @@ if (template) template.innerHTML = `
|
|
|
738
850
|
:host([interactive]) svg * {
|
|
739
851
|
pointer-events: visiblePainted;
|
|
740
852
|
}
|
|
853
|
+
/* Text lets the pointer through to the band under it: WebKit fires no
|
|
854
|
+
click on SVG text inside a shadow root. */
|
|
855
|
+
:host([interactive]) svg text,
|
|
856
|
+
:host([interactive]) svg textPath {
|
|
857
|
+
pointer-events: none;
|
|
858
|
+
}
|
|
859
|
+
/* The touch area catches the pointer even where the band is transparent. */
|
|
860
|
+
#orbitHit {
|
|
861
|
+
fill: none;
|
|
862
|
+
stroke: transparent;
|
|
863
|
+
}
|
|
864
|
+
:host([interactive]) #orbitHit {
|
|
865
|
+
pointer-events: stroke;
|
|
866
|
+
}
|
|
741
867
|
:host([interactive]) {
|
|
742
868
|
cursor: pointer;
|
|
743
869
|
}
|
|
@@ -750,18 +876,21 @@ if (template) template.innerHTML = `
|
|
|
750
876
|
text {
|
|
751
877
|
fill: var(--o-color);
|
|
752
878
|
}
|
|
753
|
-
|
|
754
|
-
|
|
879
|
+
/* Guides for text: never painted and never under the pointer. */
|
|
880
|
+
#orbitPath, #measure {
|
|
881
|
+
fill: none;
|
|
755
882
|
stroke: none;
|
|
756
|
-
|
|
883
|
+
pointer-events: none;
|
|
757
884
|
}
|
|
758
885
|
</style>
|
|
759
886
|
<svg viewBox="0 0 100 100">
|
|
887
|
+
<path id="orbitHit" vector-effect="non-scaling-stroke"></path>
|
|
760
888
|
<path id="orbitShape" shape-rendering="geometricPrecision" vector-effect="non-scaling-stroke"></path>
|
|
761
889
|
<path id="orbitPath" shape-rendering="geometricPrecision" vector-effect="non-scaling-stroke"></path>
|
|
762
890
|
<text>
|
|
763
891
|
<textPath href="#orbitPath" alignment-baseline="middle"></textPath>
|
|
764
892
|
</text>
|
|
893
|
+
<text id="measure" visibility="hidden" aria-hidden="true"></text>
|
|
765
894
|
</svg>
|
|
766
895
|
`;
|
|
767
896
|
var OrbitArc = class extends OrbitBase {
|
|
@@ -772,9 +901,10 @@ var OrbitArc = class extends OrbitBase {
|
|
|
772
901
|
}
|
|
773
902
|
update() {
|
|
774
903
|
const attrs = this.getAttributes();
|
|
775
|
-
const {
|
|
776
|
-
if (
|
|
777
|
-
|
|
904
|
+
const { fitRange, stacked } = attrs;
|
|
905
|
+
if (stacked) {
|
|
906
|
+
const anchor = attrs.auto && this.hasAttribute("text-anchor") ? { middle: -0.5, end: -1 }[attrs.textAnchor] || 0 : 0;
|
|
907
|
+
setLayoutProperty(this, "--o-arc-start", `${attrs.stackOffset + anchor * attrs.arcAngle}deg`);
|
|
778
908
|
setLayoutProperty(this, "--o_stack", attrs.stackOffset + attrs.arcAngle);
|
|
779
909
|
} else {
|
|
780
910
|
this.style.removeProperty("--o-arc-start");
|
|
@@ -785,6 +915,8 @@ var OrbitArc = class extends OrbitBase {
|
|
|
785
915
|
const textPath = this.shadowRoot.querySelector("textPath");
|
|
786
916
|
orbitShape.setAttribute("d", this.calculateArcParameters(attrs).dShape);
|
|
787
917
|
orbitPath.setAttribute("d", this.calculateTextArcParameters(attrs).dPath);
|
|
918
|
+
this.updateHitArea(this.shadowRoot.querySelector("#orbitHit"), attrs, 0, attrs.arcAngle);
|
|
919
|
+
const textAnchor = attrs.textAnchor;
|
|
788
920
|
if (textAnchor === "start") {
|
|
789
921
|
textPath.setAttribute("startOffset", "0%");
|
|
790
922
|
textPath.setAttribute("text-anchor", "start");
|
|
@@ -800,9 +932,9 @@ var OrbitArc = class extends OrbitBase {
|
|
|
800
932
|
} else {
|
|
801
933
|
textPath.parentElement.removeAttribute("textLength");
|
|
802
934
|
}
|
|
803
|
-
textPath.parentElement.style.fontSize =
|
|
935
|
+
textPath.parentElement.style.fontSize = attrs.svgFontSize;
|
|
804
936
|
textPath.textContent = this.textContent;
|
|
805
|
-
this.warnIfTextOverflows(orbitPath, textPath, fitRange);
|
|
937
|
+
if (!attrs.auto) this.warnIfTextOverflows(orbitPath, textPath, fitRange);
|
|
806
938
|
}
|
|
807
939
|
/**
|
|
808
940
|
* Curved text has an explicit angular budget: glyphs past the end of the
|
|
@@ -823,7 +955,7 @@ var OrbitArc = class extends OrbitBase {
|
|
|
823
955
|
if (pathLen > 0 && textLen > pathLen && this._truncWarned !== raw) {
|
|
824
956
|
this._truncWarned = raw;
|
|
825
957
|
console.warn(
|
|
826
|
-
`[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,
|
|
958
|
+
`[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, add range="auto" to fit the arc to the text, or fit-range to squeeze it.`
|
|
827
959
|
);
|
|
828
960
|
}
|
|
829
961
|
} catch (e) {
|
|
@@ -840,23 +972,43 @@ var OrbitArc = class extends OrbitBase {
|
|
|
840
972
|
const textAnchor = this.getAttribute("text-anchor") || "middle";
|
|
841
973
|
const fontSize = style.fontSize || "16px";
|
|
842
974
|
const gap = Math.max(0, this.readNumber(style, "--o-gap", 1));
|
|
843
|
-
const
|
|
844
|
-
const
|
|
845
|
-
const
|
|
846
|
-
|
|
975
|
+
const svgFontSize = `calc(${fontSize} * (100 / (${length}) * (12 / var(--o-orbit-number)))`;
|
|
976
|
+
const auto = this.getAttribute("range") === "auto";
|
|
977
|
+
const stacked = auto || this.hasAttribute("value");
|
|
978
|
+
let arcAngle;
|
|
979
|
+
if (auto) arcAngle = this.measureTextAngle(svgFontSize, common.realRadius, gap);
|
|
980
|
+
else if (this.hasAttribute("value")) arcAngle = super.getProgressAngle(range, this.readValue(), this.readMax());
|
|
981
|
+
else arcAngle = Math.max(0, Math.min(360, this.readAngle(style, "--o-angle", 0)));
|
|
982
|
+
this._autoAngle = auto ? arcAngle : 0;
|
|
847
983
|
let stackOffset = 0;
|
|
848
|
-
if (
|
|
984
|
+
if (stacked) {
|
|
849
985
|
for (let prev = this.previousElementSibling; prev; prev = prev.previousElementSibling) {
|
|
850
986
|
if (prev.localName !== "o-arc") continue;
|
|
851
987
|
const previousStyle = this.ownerDocument.defaultView.getComputedStyle(prev);
|
|
852
|
-
if (prev.
|
|
988
|
+
if (prev.getAttribute("range") === "auto") stackOffset += prev._autoAngle || 0;
|
|
989
|
+
else if (prev.hasAttribute("value")) {
|
|
853
990
|
const prevMax = prev.hasAttribute("max") ? Number(prev.getAttribute("max")) : 100;
|
|
854
991
|
const prevRange = cssNumber(prev, previousStyle.getPropertyValue("--o-range"), range, "angle");
|
|
855
992
|
stackOffset += super.getProgressAngle(prevRange, Number(prev.getAttribute("value")), prevMax);
|
|
856
993
|
} else stackOffset += cssNumber(prev, previousStyle.getPropertyValue("--o-angle"), 0, "angle");
|
|
857
994
|
}
|
|
858
995
|
}
|
|
859
|
-
return { ...common, gap, arcAngle, stackOffset, flip, fitRange, length, fontSize, textAnchor };
|
|
996
|
+
return { ...common, gap, arcAngle, stackOffset, flip, fitRange, length, fontSize, svgFontSize, textAnchor, auto, stacked };
|
|
997
|
+
}
|
|
998
|
+
/** range="auto": the angle that fits this arc's text, with half an em of room at each end. */
|
|
999
|
+
measureTextAngle(svgFontSize, radius, gap) {
|
|
1000
|
+
const text = (this.textContent || "").trim();
|
|
1001
|
+
if (!text || !(radius > 0)) return 0;
|
|
1002
|
+
const measure = this.shadowRoot.querySelector("#measure");
|
|
1003
|
+
measure.style.fontSize = svgFontSize;
|
|
1004
|
+
measure.textContent = text;
|
|
1005
|
+
let length = 0;
|
|
1006
|
+
try {
|
|
1007
|
+
length = measure.getComputedTextLength() + parseFloat(this.ownerDocument.defaultView.getComputedStyle(measure).fontSize);
|
|
1008
|
+
} catch (e) {
|
|
1009
|
+
}
|
|
1010
|
+
if (!(length > 0)) return 0;
|
|
1011
|
+
return Math.min(360, length / radius * 180 / Math.PI + gap);
|
|
860
1012
|
}
|
|
861
1013
|
calculateArcParameters(attrs) {
|
|
862
1014
|
const { arcAngle, realRadius, arcHeightPercentage, orbitNumber: orbitNumber2, shape, strokeWidth, arcHeight, gap } = attrs;
|
|
@@ -871,32 +1023,38 @@ var OrbitArc = class extends OrbitBase {
|
|
|
871
1023
|
arcHeight,
|
|
872
1024
|
gap
|
|
873
1025
|
);
|
|
874
|
-
const dShape = super.generatePathData(shape, params
|
|
1026
|
+
const dShape = super.generatePathData(shape, params);
|
|
875
1027
|
return { dShape };
|
|
876
1028
|
}
|
|
877
1029
|
calculateTextArcParameters(attrs) {
|
|
878
1030
|
const { arcAngle, realRadius, gap, flip } = attrs;
|
|
879
1031
|
if (!(arcAngle > 0)) return { dPath: "" };
|
|
880
1032
|
const adjustedGap = Math.min(gap * 0.5, arcAngle * 0.49);
|
|
881
|
-
const
|
|
882
|
-
const
|
|
883
|
-
|
|
884
|
-
let coordY1 = 50 + realRadius * Math.sin((-90 + adjustedGap) * (Math.PI / 180));
|
|
885
|
-
let coordX2 = 50 + realRadius * Math.cos((arcAngle - 90 - adjustedGap) * Math.PI / 180);
|
|
886
|
-
let coordY2 = 50 + realRadius * Math.sin((arcAngle - 90 - adjustedGap) * Math.PI / 180);
|
|
887
|
-
const [startX, startY, endX, endY] = flip ? [coordX2, coordY2, coordX1, coordY1] : [coordX1, coordY1, coordX2, coordY2];
|
|
888
|
-
const dPath = `M ${startX},${startY} A ${realRadius},${realRadius} 0 ${largeArcFlag} ${sweepFlag} ${endX},${endY}`;
|
|
889
|
-
return { dPath };
|
|
1033
|
+
const start = (adjustedGap - 90) * Math.PI / 180;
|
|
1034
|
+
const end = (arcAngle - adjustedGap - 90) * Math.PI / 180;
|
|
1035
|
+
return { dPath: flip ? this.linePath(realRadius, end, start) : this.linePath(realRadius, start, end) };
|
|
890
1036
|
}
|
|
891
|
-
|
|
1037
|
+
resolveAngle(cssExpression) {
|
|
892
1038
|
return cssNumber(this, cssExpression, 0, "angle");
|
|
893
1039
|
}
|
|
1040
|
+
/** @deprecated Use resolveAngle(). */
|
|
1041
|
+
calcularExpresionCSS(cssExpression) {
|
|
1042
|
+
return this.resolveAngle(cssExpression);
|
|
1043
|
+
}
|
|
894
1044
|
};
|
|
895
1045
|
|
|
896
1046
|
// src/js/orbit-resize.js
|
|
897
1047
|
var resizing = /* @__PURE__ */ new WeakMap();
|
|
1048
|
+
var FIXED = /^[+-]?(?:\d+\.?\d*|\.\d+)px$/;
|
|
898
1049
|
var Orbit = {
|
|
899
1050
|
refresh: refreshLayout,
|
|
1051
|
+
/** A promise that resolves when pending layouts are drawn. */
|
|
1052
|
+
ready: layoutReady,
|
|
1053
|
+
/**
|
|
1054
|
+
* Scale systems that use a fixed px --o-force with their container's width
|
|
1055
|
+
* (500px wide = ratio 1). Systems on the default --o-force already follow
|
|
1056
|
+
* their .bigbang, so they are left alone.
|
|
1057
|
+
*/
|
|
900
1058
|
resize(parentElementSelector) {
|
|
901
1059
|
var _a, _b;
|
|
902
1060
|
const parent = typeof parentElementSelector === "string" ? (_a = globalThis.document) == null ? void 0 : _a.querySelector(parentElementSelector) : parentElementSelector;
|
|
@@ -909,10 +1067,16 @@ var Orbit = {
|
|
|
909
1067
|
const view = parent.ownerDocument.defaultView;
|
|
910
1068
|
const applyRatio = (width) => {
|
|
911
1069
|
if (!(width > 0)) return;
|
|
1070
|
+
let fixed = 0;
|
|
912
1071
|
for (const element of parent.querySelectorAll(".gravity-spot")) {
|
|
1072
|
+
if (!FIXED.test(view.getComputedStyle(element).getPropertyValue("--o-force").trim())) continue;
|
|
1073
|
+
fixed++;
|
|
913
1074
|
const ratio = String(width / 500);
|
|
914
1075
|
if (element.style.getPropertyValue("--o-force-ratio") !== ratio) element.style.setProperty("--o-force-ratio", ratio);
|
|
915
1076
|
}
|
|
1077
|
+
if (!fixed) {
|
|
1078
|
+
warnOnce(parent, "resize", "Orbit.resize() has nothing to do here: every .bigbang already scales with its container. It only scales systems that set a fixed px --o-force.");
|
|
1079
|
+
}
|
|
916
1080
|
refreshLayout(parent);
|
|
917
1081
|
};
|
|
918
1082
|
const observer = new view.ResizeObserver((entries) => {
|