@zumer/orbit 1.4.11 → 1.4.12

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 CHANGED
@@ -18,6 +18,8 @@
18
18
 
19
19
  Radial UIs usually need JavaScript to compute angles, radii, and positions. **Orbit** handles all of that with CSS classes and a couple of Web Components. Drop in two files and start building.
20
20
 
21
+ > **Want ready-made components?** [**orbit-kit**](https://zumerlab.com/orbit-kit) wraps the common Orbit patterns into one-line custom elements — gauges, charts, activity rings, knobs, pie menus, cockpit instruments, analog clocks, compasses and radars.
22
+
21
23
  ## Installation
22
24
 
23
25
  **CDN (fastest):**
@@ -172,6 +174,7 @@ asking the model to build a radial UI.
172
174
 
173
175
  ## Resources
174
176
 
177
+ - [orbit-kit](https://zumerlab.com/orbit-kit) — ready-made radial components built on Orbit
175
178
  - [Full documentation](https://zumerlab.github.io/orbit-docs) — elements, tools, advanced examples
176
179
  - [Contributing](CONTRIBUTING.md)
177
180
  - [GitHub Discussions](https://github.com/zumerlab/orbit/discussions)
package/dist/orbit.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * orbit
3
- * v.1.4.11
3
+ * v.1.4.12
4
4
  * Author Juan Martin Muda - Zumerlab
5
5
  * License MIT
6
6
  */
@@ -273,9 +273,17 @@
273
273
  overflow: visible;
274
274
  pointer-events: none;
275
275
  }
276
+ /* Display element by default: never steals clicks (see orbit-arc).
277
+ Interaction is opt-in via the "interactive" attribute. */
276
278
  svg * {
279
+ pointer-events: none;
280
+ }
281
+ :host([interactive]) svg * {
277
282
  pointer-events: visiblePainted;
278
283
  }
284
+ :host([interactive]) {
285
+ cursor: pointer;
286
+ }
279
287
  .progress-bar {
280
288
  fill: var(--o-fill);
281
289
  stroke: var(--o-stroke);
@@ -288,6 +296,23 @@
288
296
  stroke: var(--o-back-stroke);
289
297
  stroke-width: var(--o-back-stroke-width);
290
298
  }
299
+ /* variant="stroke": the thin, legible gauge (registro "medidor").
300
+ The default band is a FILLED donut wedge, so everyone building a
301
+ thin gauge tripped on --o-fill/--o-back-fill. With this variant the
302
+ paths are open arcs and the data color lives where you expect it:
303
+ --o-stroke for the bar, --o-back-stroke for the track. */
304
+ :host([variant="stroke"]) .progress-bar {
305
+ fill: none;
306
+ stroke: var(--o-stroke);
307
+ stroke-width: var(--o-stroke-width, 2);
308
+ stroke-linecap: round;
309
+ }
310
+ :host([variant="stroke"]) .progress-bg {
311
+ fill: none;
312
+ stroke: var(--o-back-stroke, var(--o-gray-light));
313
+ stroke-width: var(--o-back-stroke-width, 1);
314
+ stroke-linecap: round;
315
+ }
291
316
  </style>
292
317
  <svg viewBox="0 0 100 100">
293
318
  <path class="progress-bg" shape-rendering="geometricPrecision" vector-effect="non-scaling-stroke"></path>
@@ -309,11 +334,28 @@
309
334
  }
310
335
  update() {
311
336
  const attrs = this.getAttributes();
312
- const dBg = this.calculateArcParameters(attrs, true);
313
- const dBar = this.calculateArcParameters(attrs, false);
337
+ const isStroke = this.getAttribute("variant") === "stroke";
338
+ const dBg = isStroke ? this.calculateStrokeArc(attrs, true) : this.calculateArcParameters(attrs, true);
339
+ const dBar = isStroke ? this.calculateStrokeArc(attrs, false) : this.calculateArcParameters(attrs, false);
314
340
  this.shadowRoot.querySelector(".progress-bg").setAttribute("d", dBg);
315
341
  this.shadowRoot.querySelector(".progress-bar").setAttribute("d", dBar);
316
342
  }
343
+ /**
344
+ * variant="stroke": open arc along the orbit radius (no closed band),
345
+ * stroked by CSS. Same progress math as the band variant.
346
+ */
347
+ calculateStrokeArc(attrs, full) {
348
+ const { realRadius } = attrs;
349
+ const arcAngle = Math.max(0, Math.min(this.getProgressAngle(attrs, full), 359.99));
350
+ const a0 = -90 * (Math.PI / 180);
351
+ const a1 = (-90 + arcAngle) * (Math.PI / 180);
352
+ const x0 = 50 + realRadius * Math.cos(a0);
353
+ const y0 = 50 + realRadius * Math.sin(a0);
354
+ const x1 = 50 + realRadius * Math.cos(a1);
355
+ const y1 = 50 + realRadius * Math.sin(a1);
356
+ const largeArcFlag = arcAngle > 180 ? 1 : 0;
357
+ return `M ${x0},${y0} A ${realRadius},${realRadius} 0 ${largeArcFlag} 1 ${x1},${y1}`;
358
+ }
317
359
  getAttributes() {
318
360
  const common = super.getCommonAttributes(this);
319
361
  const range = parseFloat(getComputedStyle(this).getPropertyValue("--o-range") || 360);
@@ -368,9 +410,19 @@
368
410
  overflow: visible;
369
411
  pointer-events: none;
370
412
  }
413
+ /* Arcs are display elements by default: they must NOT steal clicks from
414
+ satellites/controls nearby (a painted arc used to capture clicks even
415
+ at opacity 0, and pointer-events:none on the host could not pierce the
416
+ shadow). Interaction is opt-in via the "interactive" attribute. */
371
417
  svg * {
418
+ pointer-events: none;
419
+ }
420
+ :host([interactive]) svg * {
372
421
  pointer-events: visiblePainted;
373
422
  }
423
+ :host([interactive]) {
424
+ cursor: pointer;
425
+ }
374
426
  #orbitShape {
375
427
  fill: var(--o-fill);
376
428
  stroke: var(--o-stroke);
@@ -444,6 +496,30 @@
444
496
  }
445
497
  textPath.parentElement.style.fontSize = `calc(${fontSize} * (100 / (${length}) * (12 / var(--o-orbit-number)))`;
446
498
  textPath.textContent = this.textContent;
499
+ this.warnIfTextOverflows(orbitPath, textPath, fitRange);
500
+ }
501
+ /**
502
+ * Curved text has an explicit angular budget: glyphs past the end of the
503
+ * arc path are clipped SILENTLY by SVG. Measure and warn so nobody loses
504
+ * time to invisible truncation (~20 chars in 96° at default sizes).
505
+ * fit-range squeezes the text to the path via textLength, so it never clips.
506
+ */
507
+ warnIfTextOverflows(orbitPath, textPath, fitRange) {
508
+ const raw = (this.textContent || "").trim();
509
+ if (!raw || fitRange || !this.isConnected) return;
510
+ requestAnimationFrame(() => {
511
+ try {
512
+ const pathLen = orbitPath.getTotalLength();
513
+ const textLen = textPath.parentElement.getComputedTextLength();
514
+ if (pathLen > 0 && textLen > pathLen && this._truncWarned !== raw) {
515
+ this._truncWarned = raw;
516
+ console.warn(
517
+ `[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.`
518
+ );
519
+ }
520
+ } catch (e) {
521
+ }
522
+ });
447
523
  }
448
524
  getAttributes() {
449
525
  const common = super.getCommonAttributes(this);
package/dist/orbit.min.js CHANGED
@@ -1,10 +1,10 @@
1
1
  /*
2
2
  * orbit
3
- * v.1.4.11
3
+ * v.1.4.12
4
4
  * Author Juan Martin Muda - Zumerlab
5
5
  * License MIT
6
6
  */
7
- (()=>{var b=class extends HTMLElement{constructor(){super()}getCommonAttributes(t){let r=parseFloat(getComputedStyle(t).getPropertyValue("r")||0),e=parseFloat(getComputedStyle(t).getPropertyValue("--o-orbit-number")||1),s=parseFloat(getComputedStyle(t).getPropertyValue("--o-size-ratio")||1),n=parseFloat(getComputedStyle(t).getPropertyValue("--o-stroke-width")||1),o=t.getAttribute("shape")||"none",l=r/e*s-n+.3,a=r>0?l/2*100/r/2:0,i=0;t.classList.contains("outer-orbit")?i=a:t.classList.contains("quarter-outer-orbit")?i=a*-.5:t.classList.contains("inner-orbit")?i=a*-1:t.classList.contains("quarter-inner-orbit")&&(i=a*.5);let c=50+i;return{orbitRadius:r,arcHeight:l,realRadius:c,arcAngle:0,shape:o,arcHeightPercentage:a,orbitNumber:e,size:s,strokeWidth:n}}getProgressAngle(t,r,e=100){return r/e*t}getControlPoint(t,r,e,s,n="clockwise"){let o=(t+e)/2,l=(r+s)/2,a=e-t,i=s-r;return n==="clockwise"?{xc:o+i*.4,yc:l-a*.4}:{xc:o-i*.4,yc:l+a*.4}}arcPoint(t,r,e=0,s=0){let n=t+e,o=r+s*Math.PI/180;return{x:50+n*Math.cos(o),y:50+n*Math.sin(o)}}calculateCommonArcParameters(t,r,e,s,n,o,l,a=0){let i=Math.PI/2,c=t*Math.PI/180,h=r+e,g=r-e!==0?r-e:r,u=(a+o*1.25)/s/h,d=(a+o*1.25)/s/g,$=u-i,p=c-u-i,A=d-i,P=c-d-i,x=this.arcPoint(h,$),y=this.arcPoint(h,p),S=this.arcPoint(g,A),f=this.arcPoint(g,P),C=p-$,M=P-A,m=C>Math.PI?1:0,F=M>Math.PI?1:0;return{upperArcStart:x,upperArcEnd:y,innerArcStart:S,innerArcEnd:f,largeArcFlag:m,largeArcFlagUpper:m,largeArcFlagInner:F,bigRadius:h,smallRadius:g,radius:r,upperAngleStart:$,upperAngleEnd:p,innerAngleStart:A,innerAngleEnd:P}}generatePathData(t,r,e,s){let n="";switch(t){case"rounded":n=this.generateRoundedPath(r,e,s);break;case"circle":case"circle-a":case"bullet":n=this.generateCirclePath(r,t);break;case"circle-b":n=this.generateCircleBPath(r,e,s);break;case"arrow":n=this.generateArrowPath(r,s);break;case"backslash":case"slash":n=this.generateSlashPath(r,t,s);break;case"zigzag":n=this.generateZigzagPath(r,e,s);break;default:n=this.generateDefaultPath(r)}return n}generateRoundedPath(t,r,e){let{bigRadius:s,smallRadius:n}=t,o=r<10?5:r<5?2.5:10,l=o/e*Math.PI/180,a=t.upperAngleEnd-t.upperAngleStart-2*l>Math.PI?1:0,i=t.innerAngleEnd-t.innerAngleStart-2*l>Math.PI?1:0,c=this.arcPoint(s,t.upperAngleStart,0,o/e),h=this.arcPoint(s,t.upperAngleEnd,0,-o/e),g=this.arcPoint(n,t.innerAngleStart,0,o/e),u=this.arcPoint(n,t.innerAngleEnd,0,-o/e),d=this.arcPoint(s,t.upperAngleStart,-(o/2)/e,0),$=this.arcPoint(s,t.upperAngleEnd,-(o/2)/e,0),p=this.arcPoint(n,t.innerAngleStart,o/2/e,0),A=this.arcPoint(n,t.innerAngleEnd,o/2/e,0),P=this.getControlPoint(h.x,h.y,$.x,$.y),x=this.getControlPoint(A.x,A.y,u.x,u.y),y=this.getControlPoint(g.x,g.y,p.x,p.y),S=this.getControlPoint(d.x,d.y,c.x,c.y),f=`M ${c.x},${c.y} A ${s},${s} 0 ${a} 1 ${h.x},${h.y}`;return f+=`Q ${P.xc},${P.yc} ${$.x},${$.y} L ${A.x},${A.y}`,f+=`Q ${x.xc},${x.yc} ${u.x},${u.y}`,f+=`A ${n},${n} 0 ${i} 0 ${g.x},${g.y}`,f+=`Q ${y.xc},${y.yc} ${p.x},${p.y} L ${d.x},${d.y}`,f+=` Q ${S.xc},${S.yc} ${c.x},${c.y}`,f+=" Z",f}generateCirclePath(t,r){let{upperArcStart:e,upperArcEnd:s,innerArcStart:n,innerArcEnd:o,bigRadius:l,smallRadius:a,largeArcFlagUpper:i,largeArcFlagInner:c}=t,h=`M ${e.x},${e.y} A ${l},${l} 0 ${i} 1 ${s.x},${s.y}`;return h+=` A 1,1 0 0 1 ${o.x},${o.y} `,h+=` A ${a},${a} 0 ${c} 0 ${n.x},${n.y}`,h+=` A 1,1 0 0 ${r==="circle"||r==="circle-a"?1:0} ${e.x},${e.y} `,h+=" Z",h}generateCircleBPath(t,r,e){let{upperAngleStart:s,upperAngleEnd:n,innerAngleStart:o,innerAngleEnd:l,bigRadius:a,smallRadius:i}=t,c=r*1.36,h=c/e*Math.PI/180,g=n-s-2*h>Math.PI?1:0,u=l-o-2*h>Math.PI?1:0,d=this.arcPoint(a,s,0,c/e),$=this.arcPoint(a,n,0,-c/e),p=this.arcPoint(i,o,0,c/e),A=this.arcPoint(i,l,0,-c/e),P=`M ${d.x},${d.y} A ${a},${a} 0 ${g} 1 ${$.x},${$.y}`;return P+=` A 1,1 0 0 1 ${A.x},${A.y} `,P+=` A ${i},${i} 0 ${u} 0 ${p.x},${p.y}`,P+=` A 1,1 0 0 1 ${d.x},${d.y} `,P+=" Z",P}generateArrowPath(t,r){let{upperArcStart:e,upperArcEnd:s,innerArcStart:n,innerArcEnd:o,bigRadius:l,smallRadius:a,largeArcFlagUpper:i,largeArcFlagInner:c,radius:h}=t,g=this.arcPoint(h,t.upperAngleEnd,0,24/r/2),u=this.arcPoint(h,t.upperAngleStart,0,24/r/2),d=`M ${e.x},${e.y} A ${l},${l} 0 ${i} 1 ${s.x},${s.y}`;return d+=`L ${g.x} ${g.y}`,d+=`L ${o.x} ${o.y}`,d+=`A ${a},${a} 0 ${c} 0 ${n.x}, ${n.y}`,d+=`L ${u.x} ${u.y}`,d+="Z",d}generateSlashPath(t,r,e){let{upperAngleStart:s,upperAngleEnd:n,innerAngleStart:o,innerAngleEnd:l,bigRadius:a,smallRadius:i,largeArcFlagUpper:c,largeArcFlagInner:h}=t,g=this.arcPoint(a,s,0,r==="backslash"?0:24/e/2),u=this.arcPoint(a,n,0,r==="backslash"?0:24/e/2),d=this.arcPoint(i,o,0,r==="backslash"?24/e/2:0),$=this.arcPoint(i,l,0,r==="backslash"?24/e/2:0),p=`M ${g.x},${g.y} A ${a},${a} 0 ${c} 1 ${u.x},${u.y}`;return p+=`L ${$.x} ${$.y}`,p+=`A ${i},${i} 0 ${h} 0 ${d.x}, ${d.y}`,p+="Z",p}generateZigzagPath(t,r,e){let{upperArcStart:s,upperArcEnd:n,innerArcStart:o,innerArcEnd:l,bigRadius:a,smallRadius:i,largeArcFlagUpper:c,largeArcFlagInner:h,radius:g}=t,u=r/e/2,d=this.arcPoint(g,t.upperAngleStart,-u,3),$=this.arcPoint(g,t.upperAngleStart,0,0),p=this.arcPoint(g,t.upperAngleStart,u,3),A=this.arcPoint(g,t.innerAngleEnd,u,3),P=this.arcPoint(g,t.innerAngleEnd,0,0),x=this.arcPoint(g,t.innerAngleEnd,-u,3),y=`M ${s.x},${s.y} A ${a},${a} 0 ${c} 1 ${n.x},${n.y}`;return y+=`L ${A.x} ${A.y}`,y+=`L ${P.x} ${P.y}`,y+=`L ${x.x} ${x.y}`,y+=`L ${l.x} ${l.y}`,y+=`A ${i},${i} 0 ${h} 0 ${o.x}, ${o.y}`,y+=`L ${d.x} ${d.y}`,y+=`L ${$.x} ${$.y}`,y+=`L ${p.x} ${p.y}`,y+="Z",y}generateDefaultPath(t){let{upperArcStart:r,upperArcEnd:e,innerArcStart:s,innerArcEnd:n,bigRadius:o,smallRadius:l,largeArcFlagUpper:a,largeArcFlagInner:i}=t,c=`M ${r.x},${r.y} A ${o},${o} 0 ${a} 1 ${e.x},${e.y}`;return c+=`L ${n.x} ${n.y}`,c+=`A ${l},${l} 0 ${i} 0 ${s.x}, ${s.y}`,c+="Z",c}};var k=class extends b{constructor(){super(),this.attachShadow({mode:"open"}),this.shadowRoot.innerHTML=`
7
+ (()=>{var b=class extends HTMLElement{constructor(){super()}getCommonAttributes(t){let r=parseFloat(getComputedStyle(t).getPropertyValue("r")||0),e=parseFloat(getComputedStyle(t).getPropertyValue("--o-orbit-number")||1),s=parseFloat(getComputedStyle(t).getPropertyValue("--o-size-ratio")||1),n=parseFloat(getComputedStyle(t).getPropertyValue("--o-stroke-width")||1),o=t.getAttribute("shape")||"none",l=r/e*s-n+.3,a=r>0?l/2*100/r/2:0,i=0;t.classList.contains("outer-orbit")?i=a:t.classList.contains("quarter-outer-orbit")?i=a*-.5:t.classList.contains("inner-orbit")?i=a*-1:t.classList.contains("quarter-inner-orbit")&&(i=a*.5);let c=50+i;return{orbitRadius:r,arcHeight:l,realRadius:c,arcAngle:0,shape:o,arcHeightPercentage:a,orbitNumber:e,size:s,strokeWidth:n}}getProgressAngle(t,r,e=100){return r/e*t}getControlPoint(t,r,e,s,n="clockwise"){let o=(t+e)/2,l=(r+s)/2,a=e-t,i=s-r;return n==="clockwise"?{xc:o+i*.4,yc:l-a*.4}:{xc:o-i*.4,yc:l+a*.4}}arcPoint(t,r,e=0,s=0){let n=t+e,o=r+s*Math.PI/180;return{x:50+n*Math.cos(o),y:50+n*Math.sin(o)}}calculateCommonArcParameters(t,r,e,s,n,o,l,a=0){let i=Math.PI/2,c=t*Math.PI/180,h=r+e,g=r-e!==0?r-e:r,u=(a+o*1.25)/s/h,d=(a+o*1.25)/s/g,$=u-i,p=c-u-i,A=d-i,P=c-d-i,v=this.arcPoint(h,$),f=this.arcPoint(h,p),k=this.arcPoint(g,A),y=this.arcPoint(g,P),C=p-$,R=P-A,m=C>Math.PI?1:0,I=R>Math.PI?1:0;return{upperArcStart:v,upperArcEnd:f,innerArcStart:k,innerArcEnd:y,largeArcFlag:m,largeArcFlagUpper:m,largeArcFlagInner:I,bigRadius:h,smallRadius:g,radius:r,upperAngleStart:$,upperAngleEnd:p,innerAngleStart:A,innerAngleEnd:P}}generatePathData(t,r,e,s){let n="";switch(t){case"rounded":n=this.generateRoundedPath(r,e,s);break;case"circle":case"circle-a":case"bullet":n=this.generateCirclePath(r,t);break;case"circle-b":n=this.generateCircleBPath(r,e,s);break;case"arrow":n=this.generateArrowPath(r,s);break;case"backslash":case"slash":n=this.generateSlashPath(r,t,s);break;case"zigzag":n=this.generateZigzagPath(r,e,s);break;default:n=this.generateDefaultPath(r)}return n}generateRoundedPath(t,r,e){let{bigRadius:s,smallRadius:n}=t,o=r<10?5:r<5?2.5:10,l=o/e*Math.PI/180,a=t.upperAngleEnd-t.upperAngleStart-2*l>Math.PI?1:0,i=t.innerAngleEnd-t.innerAngleStart-2*l>Math.PI?1:0,c=this.arcPoint(s,t.upperAngleStart,0,o/e),h=this.arcPoint(s,t.upperAngleEnd,0,-o/e),g=this.arcPoint(n,t.innerAngleStart,0,o/e),u=this.arcPoint(n,t.innerAngleEnd,0,-o/e),d=this.arcPoint(s,t.upperAngleStart,-(o/2)/e,0),$=this.arcPoint(s,t.upperAngleEnd,-(o/2)/e,0),p=this.arcPoint(n,t.innerAngleStart,o/2/e,0),A=this.arcPoint(n,t.innerAngleEnd,o/2/e,0),P=this.getControlPoint(h.x,h.y,$.x,$.y),v=this.getControlPoint(A.x,A.y,u.x,u.y),f=this.getControlPoint(g.x,g.y,p.x,p.y),k=this.getControlPoint(d.x,d.y,c.x,c.y),y=`M ${c.x},${c.y} A ${s},${s} 0 ${a} 1 ${h.x},${h.y}`;return y+=`Q ${P.xc},${P.yc} ${$.x},${$.y} L ${A.x},${A.y}`,y+=`Q ${v.xc},${v.yc} ${u.x},${u.y}`,y+=`A ${n},${n} 0 ${i} 0 ${g.x},${g.y}`,y+=`Q ${f.xc},${f.yc} ${p.x},${p.y} L ${d.x},${d.y}`,y+=` Q ${k.xc},${k.yc} ${c.x},${c.y}`,y+=" Z",y}generateCirclePath(t,r){let{upperArcStart:e,upperArcEnd:s,innerArcStart:n,innerArcEnd:o,bigRadius:l,smallRadius:a,largeArcFlagUpper:i,largeArcFlagInner:c}=t,h=`M ${e.x},${e.y} A ${l},${l} 0 ${i} 1 ${s.x},${s.y}`;return h+=` A 1,1 0 0 1 ${o.x},${o.y} `,h+=` A ${a},${a} 0 ${c} 0 ${n.x},${n.y}`,h+=` A 1,1 0 0 ${r==="circle"||r==="circle-a"?1:0} ${e.x},${e.y} `,h+=" Z",h}generateCircleBPath(t,r,e){let{upperAngleStart:s,upperAngleEnd:n,innerAngleStart:o,innerAngleEnd:l,bigRadius:a,smallRadius:i}=t,c=r*1.36,h=c/e*Math.PI/180,g=n-s-2*h>Math.PI?1:0,u=l-o-2*h>Math.PI?1:0,d=this.arcPoint(a,s,0,c/e),$=this.arcPoint(a,n,0,-c/e),p=this.arcPoint(i,o,0,c/e),A=this.arcPoint(i,l,0,-c/e),P=`M ${d.x},${d.y} A ${a},${a} 0 ${g} 1 ${$.x},${$.y}`;return P+=` A 1,1 0 0 1 ${A.x},${A.y} `,P+=` A ${i},${i} 0 ${u} 0 ${p.x},${p.y}`,P+=` A 1,1 0 0 1 ${d.x},${d.y} `,P+=" Z",P}generateArrowPath(t,r){let{upperArcStart:e,upperArcEnd:s,innerArcStart:n,innerArcEnd:o,bigRadius:l,smallRadius:a,largeArcFlagUpper:i,largeArcFlagInner:c,radius:h}=t,g=this.arcPoint(h,t.upperAngleEnd,0,24/r/2),u=this.arcPoint(h,t.upperAngleStart,0,24/r/2),d=`M ${e.x},${e.y} A ${l},${l} 0 ${i} 1 ${s.x},${s.y}`;return d+=`L ${g.x} ${g.y}`,d+=`L ${o.x} ${o.y}`,d+=`A ${a},${a} 0 ${c} 0 ${n.x}, ${n.y}`,d+=`L ${u.x} ${u.y}`,d+="Z",d}generateSlashPath(t,r,e){let{upperAngleStart:s,upperAngleEnd:n,innerAngleStart:o,innerAngleEnd:l,bigRadius:a,smallRadius:i,largeArcFlagUpper:c,largeArcFlagInner:h}=t,g=this.arcPoint(a,s,0,r==="backslash"?0:24/e/2),u=this.arcPoint(a,n,0,r==="backslash"?0:24/e/2),d=this.arcPoint(i,o,0,r==="backslash"?24/e/2:0),$=this.arcPoint(i,l,0,r==="backslash"?24/e/2:0),p=`M ${g.x},${g.y} A ${a},${a} 0 ${c} 1 ${u.x},${u.y}`;return p+=`L ${$.x} ${$.y}`,p+=`A ${i},${i} 0 ${h} 0 ${d.x}, ${d.y}`,p+="Z",p}generateZigzagPath(t,r,e){let{upperArcStart:s,upperArcEnd:n,innerArcStart:o,innerArcEnd:l,bigRadius:a,smallRadius:i,largeArcFlagUpper:c,largeArcFlagInner:h,radius:g}=t,u=r/e/2,d=this.arcPoint(g,t.upperAngleStart,-u,3),$=this.arcPoint(g,t.upperAngleStart,0,0),p=this.arcPoint(g,t.upperAngleStart,u,3),A=this.arcPoint(g,t.innerAngleEnd,u,3),P=this.arcPoint(g,t.innerAngleEnd,0,0),v=this.arcPoint(g,t.innerAngleEnd,-u,3),f=`M ${s.x},${s.y} A ${a},${a} 0 ${c} 1 ${n.x},${n.y}`;return f+=`L ${A.x} ${A.y}`,f+=`L ${P.x} ${P.y}`,f+=`L ${v.x} ${v.y}`,f+=`L ${l.x} ${l.y}`,f+=`A ${i},${i} 0 ${h} 0 ${o.x}, ${o.y}`,f+=`L ${d.x} ${d.y}`,f+=`L ${$.x} ${$.y}`,f+=`L ${p.x} ${p.y}`,f+="Z",f}generateDefaultPath(t){let{upperArcStart:r,upperArcEnd:e,innerArcStart:s,innerArcEnd:n,bigRadius:o,smallRadius:l,largeArcFlagUpper:a,largeArcFlagInner:i}=t,c=`M ${r.x},${r.y} A ${o},${o} 0 ${a} 1 ${e.x},${e.y}`;return c+=`L ${n.x} ${n.y}`,c+=`A ${l},${l} 0 ${i} 0 ${s.x}, ${s.y}`,c+="Z",c}};var S=class extends b{constructor(){super(),this.attachShadow({mode:"open"}),this.shadowRoot.innerHTML=`
8
8
  <style>
9
9
  :host {
10
10
  --o-fill: var(--o-gray-light);
@@ -28,9 +28,17 @@
28
28
  overflow: visible;
29
29
  pointer-events: none;
30
30
  }
31
+ /* Display element by default: never steals clicks (see orbit-arc).
32
+ Interaction is opt-in via the "interactive" attribute. */
31
33
  svg * {
34
+ pointer-events: none;
35
+ }
36
+ :host([interactive]) svg * {
32
37
  pointer-events: visiblePainted;
33
38
  }
39
+ :host([interactive]) {
40
+ cursor: pointer;
41
+ }
34
42
  .progress-bar {
35
43
  fill: var(--o-fill);
36
44
  stroke: var(--o-stroke);
@@ -43,12 +51,29 @@
43
51
  stroke: var(--o-back-stroke);
44
52
  stroke-width: var(--o-back-stroke-width);
45
53
  }
54
+ /* variant="stroke": the thin, legible gauge (registro "medidor").
55
+ The default band is a FILLED donut wedge, so everyone building a
56
+ thin gauge tripped on --o-fill/--o-back-fill. With this variant the
57
+ paths are open arcs and the data color lives where you expect it:
58
+ --o-stroke for the bar, --o-back-stroke for the track. */
59
+ :host([variant="stroke"]) .progress-bar {
60
+ fill: none;
61
+ stroke: var(--o-stroke);
62
+ stroke-width: var(--o-stroke-width, 2);
63
+ stroke-linecap: round;
64
+ }
65
+ :host([variant="stroke"]) .progress-bg {
66
+ fill: none;
67
+ stroke: var(--o-back-stroke, var(--o-gray-light));
68
+ stroke-width: var(--o-back-stroke-width, 1);
69
+ stroke-linecap: round;
70
+ }
46
71
  </style>
47
72
  <svg viewBox="0 0 100 100">
48
73
  <path class="progress-bg" shape-rendering="geometricPrecision" vector-effect="non-scaling-stroke"></path>
49
74
  <path class="progress-bar" shape-rendering="geometricPrecision" vector-effect="non-scaling-stroke"></path>
50
75
  </svg>
51
- `}connectedCallback(){this.update(),this.setupObserver()}setupObserver(){this.observer=new MutationObserver(t=>{this.observer.disconnect(),t.forEach(()=>this.update()),this.observer.observe(this,{attributes:!0,childList:!0})}),this.observer.observe(this,{attributes:!0,childList:!0})}update(){let t=this.getAttributes(),r=this.calculateArcParameters(t,!0),e=this.calculateArcParameters(t,!1);this.shadowRoot.querySelector(".progress-bg").setAttribute("d",r),this.shadowRoot.querySelector(".progress-bar").setAttribute("d",e)}getAttributes(){let t=super.getCommonAttributes(this),r=parseFloat(getComputedStyle(this).getPropertyValue("--o-range")||360),e=parseFloat(getComputedStyle(this).getPropertyValue("--o-progress")||this.getAttribute("value")||0),s=parseFloat(this.getAttribute("max"))||100;return{...t,range:r,progress:e,maxValue:s}}getProgressAngle(t,r){let{range:e,progress:s,maxValue:n}=t;return r?(n-1e-5)/n*e:s/n*e}calculateArcParameters(t,r){let{shape:e,realRadius:s,arcHeightPercentage:n,orbitNumber:o,strokeWidth:l,arcHeight:a}=t,i=this.getProgressAngle(t,r),c=super.calculateCommonArcParameters(i,s,n,o,e,l,a);return super.generatePathData(e,c,a,o)}};var E=document.createElement("template");E.innerHTML=`
76
+ `}connectedCallback(){this.update(),this.setupObserver()}setupObserver(){this.observer=new MutationObserver(t=>{this.observer.disconnect(),t.forEach(()=>this.update()),this.observer.observe(this,{attributes:!0,childList:!0})}),this.observer.observe(this,{attributes:!0,childList:!0})}update(){let t=this.getAttributes(),r=this.getAttribute("variant")==="stroke",e=r?this.calculateStrokeArc(t,!0):this.calculateArcParameters(t,!0),s=r?this.calculateStrokeArc(t,!1):this.calculateArcParameters(t,!1);this.shadowRoot.querySelector(".progress-bg").setAttribute("d",e),this.shadowRoot.querySelector(".progress-bar").setAttribute("d",s)}calculateStrokeArc(t,r){let{realRadius:e}=t,s=Math.max(0,Math.min(this.getProgressAngle(t,r),359.99)),n=-90*(Math.PI/180),o=(-90+s)*(Math.PI/180),l=50+e*Math.cos(n),a=50+e*Math.sin(n),i=50+e*Math.cos(o),c=50+e*Math.sin(o),h=s>180?1:0;return`M ${l},${a} A ${e},${e} 0 ${h} 1 ${i},${c}`}getAttributes(){let t=super.getCommonAttributes(this),r=parseFloat(getComputedStyle(this).getPropertyValue("--o-range")||360),e=parseFloat(getComputedStyle(this).getPropertyValue("--o-progress")||this.getAttribute("value")||0),s=parseFloat(this.getAttribute("max"))||100;return{...t,range:r,progress:e,maxValue:s}}getProgressAngle(t,r){let{range:e,progress:s,maxValue:n}=t;return r?(n-1e-5)/n*e:s/n*e}calculateArcParameters(t,r){let{shape:e,realRadius:s,arcHeightPercentage:n,orbitNumber:o,strokeWidth:l,arcHeight:a}=t,i=this.getProgressAngle(t,r),c=super.calculateCommonArcParameters(i,s,n,o,e,l,a);return super.generatePathData(e,c,a,o)}};var E=document.createElement("template");E.innerHTML=`
52
77
  <style>
53
78
  :host {
54
79
  --o-fill: var(--o-gray-light);
@@ -68,9 +93,19 @@
68
93
  overflow: visible;
69
94
  pointer-events: none;
70
95
  }
96
+ /* Arcs are display elements by default: they must NOT steal clicks from
97
+ satellites/controls nearby (a painted arc used to capture clicks even
98
+ at opacity 0, and pointer-events:none on the host could not pierce the
99
+ shadow). Interaction is opt-in via the "interactive" attribute. */
71
100
  svg * {
101
+ pointer-events: none;
102
+ }
103
+ :host([interactive]) svg * {
72
104
  pointer-events: visiblePainted;
73
105
  }
106
+ :host([interactive]) {
107
+ cursor: pointer;
108
+ }
74
109
  #orbitShape {
75
110
  fill: var(--o-fill);
76
111
  stroke: var(--o-stroke);
@@ -93,4 +128,4 @@
93
128
  <textPath href="#orbitPath" alignment-baseline="middle"></textPath>
94
129
  </text>
95
130
  </svg>
96
- `;var w=class extends b{constructor(){super(),this.attachShadow({mode:"open"}),this.shadowRoot.appendChild(E.content.cloneNode(!0))}connectedCallback(){this.update(),this.setupObservers()}disconnectedCallback(){this.observer&&this.observer.disconnect(),this.textObserver&&this.textObserver.disconnect()}setupObservers(){this.observer=new MutationObserver(t=>{this.observer.disconnect(),t.forEach(()=>this.update()),this.observer.observe(this,{attributes:!0,childList:!0})}),this.observer.observe(this,{attributes:!0,childList:!0}),this.textObserver=new MutationObserver(()=>{let t=this.shadowRoot.querySelector("textPath");t.textContent=this.textContent}),this.textObserver.observe(this,{characterData:!0,subtree:!0})}update(){let t=this.getAttributes(),{length:r,fontSize:e,textAnchor:s,fitRange:n}=t,o=this.shadowRoot.querySelector("#orbitPath"),l=this.shadowRoot.querySelector("#orbitShape"),a=this.shadowRoot.querySelector("textPath");l.setAttribute("d",this.calculateArcParameters(t).dShape),o.setAttribute("d",this.calculateTextArcParameters(t).dPath),s==="start"?(a.setAttribute("startOffset","0%"),a.setAttribute("text-anchor","start")):s==="middle"?(a.setAttribute("startOffset","50%"),a.setAttribute("text-anchor","middle")):s==="end"&&(a.setAttribute("startOffset","100%"),a.setAttribute("text-anchor","end")),n&&a.parentElement.setAttribute("textLength",o.getTotalLength()),a.parentElement.style.fontSize=`calc(${e} * (100 / (${r}) * (12 / var(--o-orbit-number)))`,a.textContent=this.textContent}getAttributes(){let t=super.getCommonAttributes(this),r,e=this.hasAttribute("flip")||this.classList.contains("flip"),s=this.hasAttribute("fit-range")||this.classList.contains("fit-range")||!1,n=parseFloat(getComputedStyle(this).getPropertyValue("--o-force"))||100,o=this.getAttribute("text-anchor")||"middle",l=getComputedStyle(this).getPropertyValue("font-size")||getComputedStyle(this).getPropertyValue("--font-size"),a=parseFloat(getComputedStyle(this).getPropertyValue("--o-range")||360),i=parseFloat(this.getAttribute("value")),c=parseFloat(getComputedStyle(this).getPropertyValue("--o-gap"))||1;if(i){r=super.getProgressAngle(a,i);let h=this.previousElementSibling,g=h?parseFloat(getComputedStyle(h).getPropertyValue("--o_stack")):0;this.style.setProperty("--o_stack",g+r),g>=0&&e&&this.style.setProperty("--o-angle-composite",parseFloat(g)+"deg"),g>0&&!e&&this.style.setProperty("--o-angle-composite",parseFloat(g)+"deg")}else{let h=getComputedStyle(this).getPropertyValue("--o-angle");r=this.calcularExpresionCSS(h)}return{...t,gap:c,arcAngle:r,flip:e,fitRange:s,length:n,fontSize:l,textAnchor:o}}calculateArcParameters(t){let{arcAngle:r,realRadius:e,arcHeightPercentage:s,orbitNumber:n,shape:o,strokeWidth:l,arcHeight:a,gap:i}=t,c=super.calculateCommonArcParameters(r,e,s,n,o,l,a,i);return{dShape:super.generatePathData(o,c,a,n)}}calculateTextArcParameters(t){let{arcAngle:r,realRadius:e,gap:s,flip:n}=t,o=s*.5,l=n?0:1,a=r<=180?0:1,i=50+e*Math.cos((-90+o)*(Math.PI/180)),c=50+e*Math.sin((-90+o)*(Math.PI/180)),h=50+e*Math.cos((r-90-o)*Math.PI/180),g=50+e*Math.sin((r-90-o)*Math.PI/180),[u,d,$,p]=n?[h,g,i,c]:[i,c,h,g];return{dPath:`M ${u},${d} A ${e},${e} 0 ${a} ${l} ${$},${p}`}}calcularExpresionCSS(t){let r=t.match(/calc\(\s*([\d.]+)deg\s*\/\s*\(\s*(\d+)\s*-\s*(\d+)\s*\)\s*\)/);if(r){let e=parseFloat(r[1]),s=parseInt(r[2])-parseInt(r[3]);if(!isNaN(e)&&!isNaN(s)&&s!==0)return e/s}return 0}};var R={resize(v){let t=document.querySelector(v);if(!t){console.error("Orbit.resize: element not found:",v);return}let r=n=>{t.querySelectorAll(".gravity-spot").forEach(o=>{o.style.setProperty("--o-force-ratio",String(n/500))})},e=t.offsetWidth||t.getBoundingClientRect().width;e>0?r(e):requestAnimationFrame(()=>{e=t.offsetWidth||t.getBoundingClientRect().width,e>0&&r(e)}),new ResizeObserver(n=>{for(let o of n)o.contentRect.width>0&&r(o.contentRect.width)}).observe(t)}};customElements.get("o-progress")||customElements.define("o-progress",k);customElements.get("o-arc")||customElements.define("o-arc",w);window.Orbit=R;})();
131
+ `;var w=class extends b{constructor(){super(),this.attachShadow({mode:"open"}),this.shadowRoot.appendChild(E.content.cloneNode(!0))}connectedCallback(){this.update(),this.setupObservers()}disconnectedCallback(){this.observer&&this.observer.disconnect(),this.textObserver&&this.textObserver.disconnect()}setupObservers(){this.observer=new MutationObserver(t=>{this.observer.disconnect(),t.forEach(()=>this.update()),this.observer.observe(this,{attributes:!0,childList:!0})}),this.observer.observe(this,{attributes:!0,childList:!0}),this.textObserver=new MutationObserver(()=>{let t=this.shadowRoot.querySelector("textPath");t.textContent=this.textContent}),this.textObserver.observe(this,{characterData:!0,subtree:!0})}update(){let t=this.getAttributes(),{length:r,fontSize:e,textAnchor:s,fitRange:n}=t,o=this.shadowRoot.querySelector("#orbitPath"),l=this.shadowRoot.querySelector("#orbitShape"),a=this.shadowRoot.querySelector("textPath");l.setAttribute("d",this.calculateArcParameters(t).dShape),o.setAttribute("d",this.calculateTextArcParameters(t).dPath),s==="start"?(a.setAttribute("startOffset","0%"),a.setAttribute("text-anchor","start")):s==="middle"?(a.setAttribute("startOffset","50%"),a.setAttribute("text-anchor","middle")):s==="end"&&(a.setAttribute("startOffset","100%"),a.setAttribute("text-anchor","end")),n&&a.parentElement.setAttribute("textLength",o.getTotalLength()),a.parentElement.style.fontSize=`calc(${e} * (100 / (${r}) * (12 / var(--o-orbit-number)))`,a.textContent=this.textContent,this.warnIfTextOverflows(o,a,n)}warnIfTextOverflows(t,r,e){let s=(this.textContent||"").trim();!s||e||!this.isConnected||requestAnimationFrame(()=>{try{let n=t.getTotalLength(),o=r.parentElement.getComputedTextLength();n>0&&o>n&&this._truncWarned!==s&&(this._truncWarned=s,console.warn(`[orbit] <o-arc> text "${s.length>34?s.slice(0,34)+"\u2026":s}" overflows its arc (${Math.round(o)} > ${Math.round(n)} units) and will clip. Shorten the text, widen --o-range, or use fit-range to squeeze it.`))}catch(n){}})}getAttributes(){let t=super.getCommonAttributes(this),r,e=this.hasAttribute("flip")||this.classList.contains("flip"),s=this.hasAttribute("fit-range")||this.classList.contains("fit-range")||!1,n=parseFloat(getComputedStyle(this).getPropertyValue("--o-force"))||100,o=this.getAttribute("text-anchor")||"middle",l=getComputedStyle(this).getPropertyValue("font-size")||getComputedStyle(this).getPropertyValue("--font-size"),a=parseFloat(getComputedStyle(this).getPropertyValue("--o-range")||360),i=parseFloat(this.getAttribute("value")),c=parseFloat(getComputedStyle(this).getPropertyValue("--o-gap"))||1;if(i){r=super.getProgressAngle(a,i);let h=this.previousElementSibling,g=h?parseFloat(getComputedStyle(h).getPropertyValue("--o_stack")):0;this.style.setProperty("--o_stack",g+r),g>=0&&e&&this.style.setProperty("--o-angle-composite",parseFloat(g)+"deg"),g>0&&!e&&this.style.setProperty("--o-angle-composite",parseFloat(g)+"deg")}else{let h=getComputedStyle(this).getPropertyValue("--o-angle");r=this.calcularExpresionCSS(h)}return{...t,gap:c,arcAngle:r,flip:e,fitRange:s,length:n,fontSize:l,textAnchor:o}}calculateArcParameters(t){let{arcAngle:r,realRadius:e,arcHeightPercentage:s,orbitNumber:n,shape:o,strokeWidth:l,arcHeight:a,gap:i}=t,c=super.calculateCommonArcParameters(r,e,s,n,o,l,a,i);return{dShape:super.generatePathData(o,c,a,n)}}calculateTextArcParameters(t){let{arcAngle:r,realRadius:e,gap:s,flip:n}=t,o=s*.5,l=n?0:1,a=r<=180?0:1,i=50+e*Math.cos((-90+o)*(Math.PI/180)),c=50+e*Math.sin((-90+o)*(Math.PI/180)),h=50+e*Math.cos((r-90-o)*Math.PI/180),g=50+e*Math.sin((r-90-o)*Math.PI/180),[u,d,$,p]=n?[h,g,i,c]:[i,c,h,g];return{dPath:`M ${u},${d} A ${e},${e} 0 ${a} ${l} ${$},${p}`}}calcularExpresionCSS(t){let r=t.match(/calc\(\s*([\d.]+)deg\s*\/\s*\(\s*(\d+)\s*-\s*(\d+)\s*\)\s*\)/);if(r){let e=parseFloat(r[1]),s=parseInt(r[2])-parseInt(r[3]);if(!isNaN(e)&&!isNaN(s)&&s!==0)return e/s}return 0}};var M={resize(x){let t=document.querySelector(x);if(!t){console.error("Orbit.resize: element not found:",x);return}let r=n=>{t.querySelectorAll(".gravity-spot").forEach(o=>{o.style.setProperty("--o-force-ratio",String(n/500))})},e=t.offsetWidth||t.getBoundingClientRect().width;e>0?r(e):requestAnimationFrame(()=>{e=t.offsetWidth||t.getBoundingClientRect().width,e>0&&r(e)}),new ResizeObserver(n=>{for(let o of n)o.contentRect.width>0&&r(o.contentRect.width)}).observe(t)}};customElements.get("o-progress")||customElements.define("o-progress",S);customElements.get("o-arc")||customElements.define("o-arc",w);window.Orbit=M;})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zumer/orbit",
3
- "version": "1.4.11",
3
+ "version": "1.4.12",
4
4
  "description": "Orbit is a CSS framework for radial interfaces",
5
5
  "main": "./dist/orbit.js",
6
6
  "exports": {