@rogieking/figui3 9.1.4 → 9.1.6

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/fig.js CHANGED
@@ -15799,48 +15799,83 @@ class FigAngle extends HTMLElement {
15799
15799
  centerY = 12,
15800
15800
  radius = 7,
15801
15801
  arrowSize = 3,
15802
+ startAngle = 0,
15802
15803
  ) {
15803
- const value = Number(angle);
15804
- const safeValue = Number.isFinite(value) ? value : 0;
15805
- const direction = safeValue < 0 ? "counterclockwise" : "clockwise";
15806
- const absoluteSweep = Math.abs(safeValue);
15807
- const fullSweep = absoluteSweep >= 360;
15808
- const sweep = absoluteSweep % 360;
15809
- if (sweep < 0.001 && !fullSweep) {
15804
+ const endValue = Number(angle);
15805
+ const startValue = Number(startAngle);
15806
+ const endDegrees = Number.isFinite(endValue) ? endValue : 0;
15807
+ const startDegrees = Number.isFinite(startValue) ? startValue : 0;
15808
+ const delta = endDegrees - startDegrees;
15809
+ const direction = delta < 0 ? "counterclockwise" : "clockwise";
15810
+ const absoluteSweep = Math.abs(delta);
15811
+ if (absoluteSweep < 0.001) {
15810
15812
  return { direction, arc: "", arrow: "" };
15811
15813
  }
15812
15814
 
15813
- const signedSweep = direction === "clockwise" ? sweep : -sweep;
15814
- const radians = (signedSweep * Math.PI) / 180;
15815
- const end = {
15816
- x: centerX + Math.cos(radians) * radius,
15817
- y: centerY + Math.sin(radians) * radius,
15818
- };
15819
- const tangent =
15820
- radians + (direction === "clockwise" ? Math.PI / 2 : -Math.PI / 2);
15821
- const back = tangent + Math.PI;
15822
- const spread = Math.PI / 4;
15823
- const arm = (offset) => ({
15824
- x: end.x + Math.cos(back + offset) * arrowSize,
15825
- y: end.y + Math.sin(back + offset) * arrowSize,
15826
- });
15827
- const firstArm = arm(-spread);
15828
- const secondArm = arm(spread);
15829
15815
  const point = (pointValue) =>
15830
15816
  `${Number(pointValue.x.toFixed(3))} ${Number(pointValue.y.toFixed(3))}`;
15817
+ const pointAt = (degrees) => {
15818
+ const radians = (degrees * Math.PI) / 180;
15819
+ return {
15820
+ x: centerX + Math.cos(radians) * radius,
15821
+ y: centerY + Math.sin(radians) * radius,
15822
+ };
15823
+ };
15824
+ const sweepSign = delta >= 0 ? 1 : -1;
15831
15825
  const sweepFlag = direction === "clockwise" ? 1 : 0;
15832
- const arc = fullSweep
15833
- ? `M ${centerX + radius} ${centerY} A ${radius} ${radius} 0 0 ${sweepFlag} ${centerX - radius} ${centerY} A ${radius} ${radius} 0 0 ${sweepFlag} ${centerX + radius} ${centerY}`
15834
- : `M ${centerX + radius} ${centerY} A ${radius} ${radius} 0 ${sweep > 180 ? 1 : 0} ${sweepFlag} ${point(end)}`;
15826
+ const arc = [`M ${point(pointAt(startDegrees))}`];
15827
+ let currentDegrees = startDegrees;
15828
+ const completeTurns = Math.floor(absoluteSweep / 360);
15829
+ for (let turn = 0; turn < completeTurns; turn++) {
15830
+ currentDegrees += sweepSign * 180;
15831
+ arc.push(
15832
+ `A ${radius} ${radius} 0 0 ${sweepFlag} ${point(pointAt(currentDegrees))}`,
15833
+ );
15834
+ currentDegrees += sweepSign * 180;
15835
+ arc.push(
15836
+ `A ${radius} ${radius} 0 0 ${sweepFlag} ${point(pointAt(currentDegrees))}`,
15837
+ );
15838
+ }
15839
+ const remainder = absoluteSweep - completeTurns * 360;
15840
+ if (remainder >= 0.001) {
15841
+ currentDegrees += sweepSign * remainder;
15842
+ arc.push(
15843
+ `A ${radius} ${radius} 0 ${remainder > 180 ? 1 : 0} ${sweepFlag} ${point(pointAt(currentDegrees))}`,
15844
+ );
15845
+ }
15846
+
15847
+ const end = pointAt(endDegrees);
15848
+ const radians = (endDegrees * Math.PI) / 180;
15849
+ const tangent =
15850
+ delta >= 0
15851
+ ? { x: -Math.sin(radians), y: Math.cos(radians) }
15852
+ : { x: Math.sin(radians), y: -Math.cos(radians) };
15853
+ const normal = { x: -tangent.y, y: tangent.x };
15854
+ const wing = arrowSize / Math.SQRT2;
15855
+ const firstArm = {
15856
+ x: end.x - tangent.x * wing + normal.x * wing,
15857
+ y: end.y - tangent.y * wing + normal.y * wing,
15858
+ };
15859
+ const secondArm = {
15860
+ x: end.x - tangent.x * wing - normal.x * wing,
15861
+ y: end.y - tangent.y * wing - normal.y * wing,
15862
+ };
15835
15863
  return {
15836
15864
  direction,
15837
- arc,
15865
+ arc: arc.join(" "),
15838
15866
  arrow: `M ${point(firstArm)} L ${point(end)} L ${point(secondArm)}`,
15839
15867
  };
15840
15868
  }
15841
15869
 
15842
- static #createRotationIcon(angle = 0) {
15843
- const geometry = FigAngle.#rotationGeometry(angle, 50, 50, 47, 4);
15870
+ static #createRotationIcon(angle = 0, startAngle = 0) {
15871
+ const geometry = FigAngle.#rotationGeometry(
15872
+ angle,
15873
+ 50,
15874
+ 50,
15875
+ 47,
15876
+ 4,
15877
+ startAngle,
15878
+ );
15844
15879
  return figCreateSvgElement(
15845
15880
  "svg",
15846
15881
  {
@@ -16109,6 +16144,29 @@ class FigAngle extends HTMLElement {
16109
16144
  return this.#units === "deg" ? "°" : this.#units;
16110
16145
  }
16111
16146
 
16147
+ #rotationParts() {
16148
+ const degrees = this.#toDegrees(this.#angle);
16149
+ const quotient = degrees / 360;
16150
+ const roundedQuotient = Math.round(quotient);
16151
+ const rotations =
16152
+ Math.abs(quotient - roundedQuotient) < 1e-10
16153
+ ? roundedQuotient
16154
+ : Math.trunc(quotient);
16155
+ const remainderDegrees = degrees - rotations * 360;
16156
+ return {
16157
+ rotations,
16158
+ remainder: this.#fromDegrees(
16159
+ Math.abs(remainderDegrees) < 1e-10 ? 0 : remainderDegrees,
16160
+ ),
16161
+ };
16162
+ }
16163
+
16164
+ #displayValue() {
16165
+ return this.#readBoolean("rotations")
16166
+ ? this.#rotationParts().remainder
16167
+ : this.#angle;
16168
+ }
16169
+
16112
16170
  #defaultStep() {
16113
16171
  switch (this.#units) {
16114
16172
  case "rad":
@@ -16138,10 +16196,16 @@ class FigAngle extends HTMLElement {
16138
16196
  this.#teardownEvents();
16139
16197
  const children = [];
16140
16198
  const rotations = this.#readBoolean("rotations")
16141
- ? figCreateElement("span", {
16142
- slot: "append",
16143
- className: "fig-angle-rotations",
16144
- })
16199
+ ? figCreateElement(
16200
+ "span",
16201
+ {
16202
+ slot: "append",
16203
+ className: "fig-angle-rotations",
16204
+ },
16205
+ figCreateElement("span", {
16206
+ className: "fig-angle-rotation-value",
16207
+ }),
16208
+ )
16145
16209
  : null;
16146
16210
  const showDial = this.#readBoolean("dial", true);
16147
16211
  const angleInput = figCreateElement(
@@ -16151,7 +16215,7 @@ class FigAngle extends HTMLElement {
16151
16215
  variant: showDial ? "ghost" : undefined,
16152
16216
  tabular: true,
16153
16217
  name: "angle",
16154
- value: this.#angle,
16218
+ value: this.#displayValue(),
16155
16219
  min: this.#minimum(),
16156
16220
  max: this.#maximum(),
16157
16221
  step: this.#step(),
@@ -16227,7 +16291,10 @@ class FigAngle extends HTMLElement {
16227
16291
  r: "47",
16228
16292
  }),
16229
16293
  ...ticks,
16230
- FigAngle.#createRotationIcon(this.#toDegrees(this.#angle)),
16294
+ FigAngle.#createRotationIcon(
16295
+ this.#toDegrees(this.#angle),
16296
+ this.#toDegrees(this.#resolveDefaultValue()),
16297
+ ),
16231
16298
  figCreateSvgElement("line", {
16232
16299
  class: "fig-angle-indicator-halo",
16233
16300
  x1: "75",
@@ -16315,12 +16382,14 @@ class FigAngle extends HTMLElement {
16315
16382
  const transform = `rotate(${normalized} 50 50)`;
16316
16383
  this.#indicator?.setAttribute("transform", transform);
16317
16384
  this.#indicatorHalo?.setAttribute("transform", transform);
16385
+ const defaultDegrees = this.#toDegrees(this.#resolveDefaultValue());
16318
16386
  const referenceGeometry = FigAngle.#rotationGeometry(
16319
16387
  degrees,
16320
16388
  50,
16321
16389
  50,
16322
16390
  47,
16323
16391
  4,
16392
+ defaultDegrees,
16324
16393
  );
16325
16394
  this.#reference?.setAttribute(
16326
16395
  "data-direction",
@@ -16354,15 +16423,20 @@ class FigAngle extends HTMLElement {
16354
16423
  );
16355
16424
  }
16356
16425
  if (this.#angleInput) {
16357
- const value = this.#angle.toFixed(this.#precision);
16426
+ const value = this.#displayValue().toFixed(this.#precision);
16358
16427
  if (this.#angleInput.getAttribute("value") !== value) {
16359
16428
  this.#angleInput.setAttribute("value", value);
16360
16429
  }
16361
16430
  }
16362
16431
  if (this.#rotationSpan) {
16363
- const rotations = Math.floor(Math.abs(degrees) / 360);
16364
- this.#rotationSpan.textContent = rotations > 1 ? `×${rotations}` : "";
16365
- this.#rotationSpan.hidden = rotations <= 1;
16432
+ const { rotations } = this.#rotationParts();
16433
+ const rotationValue = this.#rotationSpan.querySelector(
16434
+ ".fig-angle-rotation-value",
16435
+ );
16436
+ if (rotationValue) {
16437
+ rotationValue.textContent = rotations ? `×${rotations}` : "";
16438
+ }
16439
+ this.#rotationSpan.hidden = rotations === 0;
16366
16440
  }
16367
16441
  if (this.#resetButton) {
16368
16442
  this.#resetButton.hidden =
@@ -16483,7 +16557,14 @@ class FigAngle extends HTMLElement {
16483
16557
  if (this.#isDisabled()) return;
16484
16558
  const value = Number(event.currentTarget.value);
16485
16559
  if (!Number.isFinite(value)) return;
16486
- this.#setValue(value);
16560
+ if (this.#readBoolean("rotations")) {
16561
+ const { rotations } = this.#rotationParts();
16562
+ this.#setValue(
16563
+ this.#fromDegrees(rotations * 360 + this.#toDegrees(value)),
16564
+ );
16565
+ } else {
16566
+ this.#setValue(value);
16567
+ }
16487
16568
  this.#emit(event.type);
16488
16569
  }
16489
16570
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "9.1.4",
3
+ "version": "9.1.6",
4
4
  "description": "A lightweight web components library for building Figma plugin and widget UIs with native look and feel",
5
5
  "author": "Rogie King",
6
6
  "license": "SEE LICENSE IN LICENSE",