@rogieking/figui3 3.18.0 → 3.19.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/fig.js CHANGED
@@ -13343,8 +13343,8 @@ class FigChooser extends HTMLElement {
13343
13343
  }
13344
13344
  customElements.define("fig-chooser", FigChooser);
13345
13345
 
13346
- /* Canvas Point */
13347
- class FigCanvasPoint extends HTMLElement {
13346
+ /* Canvas Control */
13347
+ class FigCanvasControl extends HTMLElement {
13348
13348
  static observedAttributes = [
13349
13349
  "type",
13350
13350
  "value",
@@ -13358,19 +13358,25 @@ class FigCanvasPoint extends HTMLElement {
13358
13358
 
13359
13359
  #x = 50;
13360
13360
  #y = 50;
13361
+ #x2 = 75;
13362
+ #y2 = 75;
13361
13363
  #radius = 0;
13362
13364
  #radiusIsPercent = false;
13363
13365
  #angle = 0;
13364
13366
  #pointHandle = null;
13367
+ #secondHandle = null;
13365
13368
  #angleHandle = null;
13366
13369
  #radiusSvg = null;
13367
13370
  #angleSvg = null;
13368
13371
  #pointTooltip = null;
13372
+ #secondTooltip = null;
13369
13373
  #radiusTooltip = null;
13370
13374
  #angleTooltip = null;
13371
13375
  #isDragging = false;
13376
+ #isSecondDragging = false;
13372
13377
  #isRadiusDragging = false;
13373
13378
  #isAngleDragging = false;
13379
+ #prevBodyCursor = "";
13374
13380
 
13375
13381
  get #type() {
13376
13382
  return this.getAttribute("type") || "point";
@@ -13384,6 +13390,14 @@ class FigCanvasPoint extends HTMLElement {
13384
13390
  return this.#type === "point-radius-angle";
13385
13391
  }
13386
13392
 
13393
+ get #hasSecondPoint() {
13394
+ return this.#type === "point-point";
13395
+ }
13396
+
13397
+ get #hasLine() {
13398
+ return this.#type === "point-radius-angle" || this.#type === "point-point";
13399
+ }
13400
+
13387
13401
  get #tooltipsEnabled() {
13388
13402
  const v = this.getAttribute("tooltips");
13389
13403
  return v === null || v !== "false";
@@ -13407,10 +13421,22 @@ class FigCanvasPoint extends HTMLElement {
13407
13421
 
13408
13422
  get #pointTipText() {
13409
13423
  const name = this.getAttribute("name");
13410
- if (name) return name;
13424
+ if (name) {
13425
+ const parts = name.split(",");
13426
+ return parts[0].trim();
13427
+ }
13411
13428
  return `${Math.round(this.#x)}%, ${Math.round(this.#y)}%`;
13412
13429
  }
13413
13430
 
13431
+ get #secondTipText() {
13432
+ const name = this.getAttribute("name");
13433
+ if (name) {
13434
+ const parts = name.split(",");
13435
+ if (parts.length > 1) return parts[1].trim();
13436
+ }
13437
+ return `${Math.round(this.#x2)}%, ${Math.round(this.#y2)}%`;
13438
+ }
13439
+
13414
13440
  get #dragSurface() {
13415
13441
  return this.getAttribute("drag-surface") || "parent";
13416
13442
  }
@@ -13426,8 +13452,8 @@ class FigCanvasPoint extends HTMLElement {
13426
13452
  if (surface === "parent") {
13427
13453
  const container = this.parentElement;
13428
13454
  if (container) {
13429
- container.setAttribute("data-fig-canvas-point-surface", "");
13430
- return "[data-fig-canvas-point-surface]";
13455
+ container.setAttribute("data-fig-canvas-control-surface", "");
13456
+ return "[data-fig-canvas-control-surface]";
13431
13457
  }
13432
13458
  }
13433
13459
  return surface;
@@ -13454,7 +13480,7 @@ class FigCanvasPoint extends HTMLElement {
13454
13480
 
13455
13481
  attributeChangedCallback(name, oldVal, newVal) {
13456
13482
  if (oldVal === newVal) return;
13457
- if (name === "value" && !this.#isDragging && !this.#isRadiusDragging && !this.#isAngleDragging) {
13483
+ if (name === "value" && !this.#isDragging && !this.#isSecondDragging && !this.#isRadiusDragging && !this.#isAngleDragging) {
13458
13484
  this.#parseValue();
13459
13485
  if (this.#pointHandle) this.#syncPositions();
13460
13486
  else this.#render();
@@ -13475,9 +13501,11 @@ class FigCanvasPoint extends HTMLElement {
13475
13501
  }
13476
13502
  if (name === "snapping" && this.#pointHandle) {
13477
13503
  this.#pointHandle.setAttribute("drag-snapping", newVal || "false");
13504
+ if (this.#secondHandle) this.#secondHandle.setAttribute("drag-snapping", newVal || "false");
13478
13505
  }
13479
- if (name === "name" && this.#pointTooltip) {
13480
- this.#pointTooltip.setAttribute("text", this.#pointTipText);
13506
+ if (name === "name") {
13507
+ if (this.#pointTooltip) this.#pointTooltip.setAttribute("text", this.#pointTipText);
13508
+ if (this.#secondTooltip) this.#secondTooltip.setAttribute("text", this.#secondTipText);
13481
13509
  }
13482
13510
  }
13483
13511
 
@@ -13500,6 +13528,8 @@ class FigCanvasPoint extends HTMLElement {
13500
13528
  if (!Number.isFinite(this.#radius)) this.#radius = 0;
13501
13529
  }
13502
13530
  if (typeof v.angle === "number") this.#angle = v.angle;
13531
+ if (typeof v.x2 === "number") this.#x2 = v.x2;
13532
+ if (typeof v.y2 === "number") this.#y2 = v.y2;
13503
13533
  } catch { /* ignore */ }
13504
13534
  }
13505
13535
 
@@ -13509,6 +13539,10 @@ class FigCanvasPoint extends HTMLElement {
13509
13539
  v.radius = this.#radiusIsPercent ? `${this.#radius}%` : this.#radius;
13510
13540
  }
13511
13541
  if (this.#hasAngle) v.angle = this.#angle;
13542
+ if (this.#hasSecondPoint) {
13543
+ v.x2 = this.#x2;
13544
+ v.y2 = this.#y2;
13545
+ }
13512
13546
  return v;
13513
13547
  }
13514
13548
 
@@ -13523,10 +13557,12 @@ class FigCanvasPoint extends HTMLElement {
13523
13557
  #render() {
13524
13558
  this.innerHTML = "";
13525
13559
  this.#pointHandle = null;
13560
+ this.#secondHandle = null;
13526
13561
  this.#angleHandle = null;
13527
13562
  this.#radiusSvg = null;
13528
13563
  this.#angleSvg = null;
13529
13564
  this.#pointTooltip = null;
13565
+ this.#secondTooltip = null;
13530
13566
  this.#radiusTooltip = null;
13531
13567
  this.#angleTooltip = null;
13532
13568
 
@@ -13548,13 +13584,17 @@ class FigCanvasPoint extends HTMLElement {
13548
13584
  const color = this.getAttribute("color");
13549
13585
  if (color) handle.setAttribute("color", color);
13550
13586
  }
13587
+ if (this.#hasSecondPoint) {
13588
+ handle.setAttribute("hit-area", "12 circle");
13589
+ handle.setAttribute("hit-area-mode", "delegate");
13590
+ }
13551
13591
  this.#pointHandle = handle;
13552
13592
 
13553
13593
  if (this.#hasRadius) {
13554
13594
  this.#createRadiusSvg();
13555
13595
  }
13556
13596
 
13557
- if (this.#hasAngle) {
13597
+ if (this.#hasLine) {
13558
13598
  this.#createAngleSvg();
13559
13599
  }
13560
13600
 
@@ -13573,6 +13613,10 @@ class FigCanvasPoint extends HTMLElement {
13573
13613
  this.#createAngleHandle(disabled, tooltips, handleSurface);
13574
13614
  }
13575
13615
 
13616
+ if (this.#hasSecondPoint) {
13617
+ this.#createSecondHandle(disabled, tooltips, handleSurface);
13618
+ }
13619
+
13576
13620
  this.#setupEventListeners();
13577
13621
  requestAnimationFrame(() => this.#syncPositions());
13578
13622
  }
@@ -13580,10 +13624,10 @@ class FigCanvasPoint extends HTMLElement {
13580
13624
  #createRadiusSvg() {
13581
13625
  const ns = "http://www.w3.org/2000/svg";
13582
13626
  const svg = document.createElementNS(ns, "svg");
13583
- svg.classList.add("fig-canvas-point-radius");
13627
+ svg.classList.add("fig-canvas-control-radius");
13584
13628
  svg.setAttribute("overflow", "visible");
13585
13629
  const hitCircle = document.createElementNS(ns, "circle");
13586
- hitCircle.classList.add("fig-canvas-point-radius-hit");
13630
+ hitCircle.classList.add("fig-canvas-control-radius-hit");
13587
13631
  svg.appendChild(hitCircle);
13588
13632
  const circle = document.createElementNS(ns, "circle");
13589
13633
  svg.appendChild(circle);
@@ -13606,12 +13650,12 @@ class FigCanvasPoint extends HTMLElement {
13606
13650
  #createAngleSvg() {
13607
13651
  const ns = "http://www.w3.org/2000/svg";
13608
13652
  const svg = document.createElementNS(ns, "svg");
13609
- svg.classList.add("fig-canvas-point-angle-svg");
13653
+ svg.classList.add("fig-canvas-control-angle-svg");
13610
13654
  svg.setAttribute("overflow", "visible");
13611
13655
  svg.style.position = "absolute";
13612
13656
  svg.style.pointerEvents = "none";
13613
13657
  const line = document.createElementNS(ns, "line");
13614
- line.classList.add("fig-canvas-point-angle-line");
13658
+ line.classList.add("fig-canvas-control-angle-line");
13615
13659
  svg.appendChild(line);
13616
13660
  this.#angleSvg = svg;
13617
13661
  this.appendChild(svg);
@@ -13640,6 +13684,30 @@ class FigCanvasPoint extends HTMLElement {
13640
13684
  }
13641
13685
  }
13642
13686
 
13687
+ #createSecondHandle(disabled, tooltips, handleSurface) {
13688
+ const handle = document.createElement("fig-handle");
13689
+ handle.setAttribute("drag", "true");
13690
+ handle.setAttribute("drag-surface", handleSurface);
13691
+ handle.setAttribute("drag-axes", "x,y");
13692
+ handle.setAttribute("drag-snapping", this.#snappingMode);
13693
+ handle.setAttribute("hit-area", "12 circle");
13694
+ handle.setAttribute("hit-area-mode", "delegate");
13695
+ handle.setAttribute("value", `${this.#x2}% ${this.#y2}%`);
13696
+ if (disabled) handle.setAttribute("disabled", "");
13697
+ this.#secondHandle = handle;
13698
+
13699
+ if (tooltips) {
13700
+ const tip = document.createElement("fig-tooltip");
13701
+ tip.setAttribute("action", "manual");
13702
+ tip.setAttribute("text", this.#secondTipText);
13703
+ tip.appendChild(handle);
13704
+ this.appendChild(tip);
13705
+ this.#secondTooltip = tip;
13706
+ } else {
13707
+ this.appendChild(handle);
13708
+ }
13709
+ }
13710
+
13643
13711
  #resizeCursorSvg(deg) {
13644
13712
  const r = Math.round(deg);
13645
13713
  return `url("data:image/svg+xml,%3Csvg width='32' height='32' viewBox='0 0 32 32' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform='rotate(${r} 16 16)'%3E%3Cg filter='url(%23f)'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M11.1212 16.9998L11.5607 17.4394C12.1465 18.0252 12.1464 18.975 11.5606 19.5607C10.9748 20.1465 10.0251 20.1465 9.4393 19.5606L6.4393 16.5604C5.85354 15.9746 5.85357 15.0249 6.43938 14.4391L9.43938 11.4393C10.0252 10.8535 10.9749 10.8536 11.5607 11.4394C12.1465 12.0252 12.1464 12.9749 11.5606 13.5607L11.1215 13.9998L20.8786 13.9999L20.4394 13.5607C19.8536 12.9749 19.8535 12.0252 20.4393 11.4394C21.0251 10.8536 21.9749 10.8536 22.5606 11.4394L25.5606 14.4393C25.842 14.7206 26 15.1021 26 15.4999C26 15.8978 25.842 16.2793 25.5607 16.5606L22.5607 19.5607C21.9749 20.1465 21.0251 20.1465 20.4393 19.5607C19.8536 18.9749 19.8535 18.0252 20.4393 17.4394L20.8788 16.9999L11.1212 16.9998Z' fill='white'/%3E%3C/g%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M10.8536 12.1465C11.0488 12.3417 11.0488 12.6583 10.8535 12.8536L8.70715 14.9998L23.2929 14.9999L21.1465 12.8536C20.9512 12.6583 20.9512 12.3417 21.1464 12.1465C21.3417 11.9512 21.6583 11.9512 21.8535 12.1465L24.8535 15.1464C24.9473 15.2402 25 15.3673 25 15.4999C25 15.6326 24.9473 15.7597 24.8536 15.8535L21.8536 18.8536C21.6583 19.0488 21.3417 19.0488 21.1465 18.8536C20.9512 18.6583 20.9512 18.3417 21.1464 18.1465L23.2929 15.9999L8.70705 15.9998L10.8536 18.1465C11.0488 18.3417 11.0488 18.6583 10.8535 18.8536C10.6583 19.0488 10.3417 19.0488 10.1464 18.8535L7.14643 15.8533C6.95118 15.658 6.95119 15.3415 7.14646 15.1462L10.1465 12.1464C10.3417 11.9512 10.6583 11.9512 10.8536 12.1465Z' fill='black'/%3E%3C/g%3E%3Cdefs%3E%3Cfilter id='f' x='3' y='9' width='26' height='15' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeFlood flood-opacity='0' result='a'/%3E%3CfeColorMatrix in='SourceAlpha' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0' result='b'/%3E%3CfeOffset dy='1'/%3E%3CfeGaussianBlur stdDeviation='1.5'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.35 0'/%3E%3CfeBlend in2='a' result='c'/%3E%3CfeBlend in='SourceGraphic' in2='c'/%3E%3C/filter%3E%3C/defs%3E%3C/svg%3E") 16 16, nwse-resize`;
@@ -13657,6 +13725,22 @@ class FigCanvasPoint extends HTMLElement {
13657
13725
  hitArea.style.cursor = this.#rotateCursorSvg(this.#angle);
13658
13726
  }
13659
13727
 
13728
+ #pointPointLineDeg() {
13729
+ return (Math.atan2(this.#y2 - this.#y, this.#x2 - this.#x) * 180) / Math.PI;
13730
+ }
13731
+
13732
+ #syncPointPointCursors() {
13733
+ if (!this.#hasSecondPoint) return;
13734
+ const deg = this.#pointPointLineDeg();
13735
+ const setHitCursor = (handle, rotateDeg) => {
13736
+ if (!handle) return;
13737
+ const hitArea = handle.querySelector(".fig-handle-hit-area");
13738
+ if (hitArea) hitArea.style.cursor = this.#rotateCursorSvg(rotateDeg);
13739
+ };
13740
+ setHitCursor(this.#pointHandle, deg + 180);
13741
+ setHitCursor(this.#secondHandle, deg);
13742
+ }
13743
+
13660
13744
  #syncPositions() {
13661
13745
  const container = this.#container;
13662
13746
  if (!container || !this.#pointHandle) return;
@@ -13687,13 +13771,19 @@ class FigCanvasPoint extends HTMLElement {
13687
13771
  }
13688
13772
  }
13689
13773
 
13690
- if (this.#angleSvg && this.#hasAngle) {
13774
+ if (this.#angleSvg && this.#hasLine) {
13691
13775
  const cx = (this.#x / 100) * rect.width;
13692
13776
  const cy = (this.#y / 100) * rect.height;
13693
- const r = this.#resolveRadius(rect.width);
13694
- const angleRad = (this.#angle * Math.PI) / 180;
13695
- const ax = cx + r * Math.cos(angleRad);
13696
- const ay = cy + r * Math.sin(angleRad);
13777
+ let lx2, ly2;
13778
+ if (this.#hasSecondPoint) {
13779
+ lx2 = (this.#x2 / 100) * rect.width;
13780
+ ly2 = (this.#y2 / 100) * rect.height;
13781
+ } else {
13782
+ const r = this.#resolveRadius(rect.width);
13783
+ const angleRad = (this.#angle * Math.PI) / 180;
13784
+ lx2 = cx + r * Math.cos(angleRad);
13785
+ ly2 = cy + r * Math.sin(angleRad);
13786
+ }
13697
13787
 
13698
13788
  const svg = this.#angleSvg;
13699
13789
  svg.style.width = `${rect.width}px`;
@@ -13705,8 +13795,8 @@ class FigCanvasPoint extends HTMLElement {
13705
13795
  if (line) {
13706
13796
  line.setAttribute("x1", String(cx));
13707
13797
  line.setAttribute("y1", String(cy));
13708
- line.setAttribute("x2", String(ax));
13709
- line.setAttribute("y2", String(ay));
13798
+ line.setAttribute("x2", String(lx2));
13799
+ line.setAttribute("y2", String(ly2));
13710
13800
  }
13711
13801
  }
13712
13802
 
@@ -13726,7 +13816,15 @@ class FigCanvasPoint extends HTMLElement {
13726
13816
  }
13727
13817
  }
13728
13818
 
13819
+ if (this.#secondHandle && this.#hasSecondPoint) {
13820
+ this.#secondHandle.setAttribute("value", `${this.#x2}% ${this.#y2}%`);
13821
+ if (this.#secondTooltip) {
13822
+ this.#secondTooltip.setAttribute("text", this.#secondTipText);
13823
+ }
13824
+ }
13825
+
13729
13826
  this.#syncAngleCursor();
13827
+ this.#syncPointPointCursors();
13730
13828
  }
13731
13829
 
13732
13830
  #emitInput() {
@@ -13746,6 +13844,9 @@ class FigCanvasPoint extends HTMLElement {
13746
13844
 
13747
13845
  this.#pointHandle.addEventListener("input", (e) => {
13748
13846
  e.stopPropagation();
13847
+ if (!this.#isDragging && this.#hasSecondPoint) {
13848
+ this.#prevBodyCursor = document.body.style.cursor;
13849
+ }
13749
13850
  this.#isDragging = true;
13750
13851
  const px = e.detail?.px ?? this.#x / 100;
13751
13852
  const py = e.detail?.py ?? this.#y / 100;
@@ -13757,6 +13858,9 @@ class FigCanvasPoint extends HTMLElement {
13757
13858
  this.#pointTooltip.showPopup?.();
13758
13859
  }
13759
13860
  this.#syncPositions();
13861
+ if (this.#hasSecondPoint) {
13862
+ document.body.style.cursor = this.#resizeCursorSvg(this.#pointPointLineDeg());
13863
+ }
13760
13864
  this.#emitInput();
13761
13865
  });
13762
13866
 
@@ -13767,6 +13871,9 @@ class FigCanvasPoint extends HTMLElement {
13767
13871
  this.#x = Math.round(Math.max(0, Math.min(100, px * 100)));
13768
13872
  this.#y = Math.round(Math.max(0, Math.min(100, py * 100)));
13769
13873
  if (this.#pointTooltip) this.#pointTooltip.removeAttribute("show");
13874
+ if (this.#hasSecondPoint) {
13875
+ document.body.style.cursor = this.#prevBodyCursor ?? "";
13876
+ }
13770
13877
  this.#syncPositions();
13771
13878
  this.#syncValueAttribute();
13772
13879
  this.#emitChange();
@@ -13777,7 +13884,7 @@ class FigCanvasPoint extends HTMLElement {
13777
13884
  this.#angleHandle.addEventListener("input", (e) => {
13778
13885
  e.stopPropagation();
13779
13886
  this.#isAngleDragging = true;
13780
- this.classList.add("fig-canvas-point-ring-active");
13887
+ this.classList.add("fig-canvas-control-ring-active");
13781
13888
  const container = this.#container;
13782
13889
  if (!container) return;
13783
13890
  const rect = container.getBoundingClientRect();
@@ -13826,7 +13933,7 @@ class FigCanvasPoint extends HTMLElement {
13826
13933
 
13827
13934
  this.#angleHandle.addEventListener("change", (e) => {
13828
13935
  e.stopPropagation();
13829
- this.classList.remove("fig-canvas-point-ring-active");
13936
+ this.classList.remove("fig-canvas-control-ring-active");
13830
13937
  if (this.#angleTooltip) this.#angleTooltip.removeAttribute("show");
13831
13938
  this.#syncPositions();
13832
13939
  this.#syncValueAttribute();
@@ -13840,7 +13947,7 @@ class FigCanvasPoint extends HTMLElement {
13840
13947
  if (!origEvent) return;
13841
13948
  origEvent.preventDefault();
13842
13949
  this.#isAngleDragging = true;
13843
- this.classList.add("fig-canvas-point-ring-active");
13950
+ this.classList.add("fig-canvas-control-ring-active");
13844
13951
  const container = this.#container;
13845
13952
  if (!container) return;
13846
13953
 
@@ -13873,7 +13980,7 @@ class FigCanvasPoint extends HTMLElement {
13873
13980
 
13874
13981
  const onUp = () => {
13875
13982
  this.#isAngleDragging = false;
13876
- this.classList.remove("fig-canvas-point-ring-active");
13983
+ this.classList.remove("fig-canvas-control-ring-active");
13877
13984
  document.body.style.cursor = prevBodyCursor;
13878
13985
  if (this.#angleTooltip) this.#angleTooltip.removeAttribute("show");
13879
13986
  this.#syncValueAttribute();
@@ -13886,6 +13993,116 @@ class FigCanvasPoint extends HTMLElement {
13886
13993
  window.addEventListener("pointerup", onUp);
13887
13994
  });
13888
13995
  }
13996
+
13997
+ if (this.#secondHandle) {
13998
+ this.#secondHandle.addEventListener("input", (e) => {
13999
+ e.stopPropagation();
14000
+ if (!this.#isSecondDragging) {
14001
+ this.#prevBodyCursor = document.body.style.cursor;
14002
+ }
14003
+ this.#isSecondDragging = true;
14004
+ const px = e.detail?.px ?? this.#x2 / 100;
14005
+ const py = e.detail?.py ?? this.#y2 / 100;
14006
+ this.#x2 = Math.round(Math.max(0, Math.min(100, px * 100)));
14007
+ this.#y2 = Math.round(Math.max(0, Math.min(100, py * 100)));
14008
+ if (this.#secondTooltip) {
14009
+ this.#secondTooltip.setAttribute("text", this.#secondTipText);
14010
+ this.#secondTooltip.setAttribute("show", "true");
14011
+ this.#secondTooltip.showPopup?.();
14012
+ }
14013
+ this.#syncPositions();
14014
+ document.body.style.cursor = this.#resizeCursorSvg(this.#pointPointLineDeg());
14015
+ this.#emitInput();
14016
+ });
14017
+
14018
+ this.#secondHandle.addEventListener("change", (e) => {
14019
+ e.stopPropagation();
14020
+ document.body.style.cursor = this.#prevBodyCursor ?? "";
14021
+ if (this.#secondTooltip) this.#secondTooltip.removeAttribute("show");
14022
+ this.#syncPositions();
14023
+ this.#syncValueAttribute();
14024
+ this.#emitChange();
14025
+ requestAnimationFrame(() => { this.#isSecondDragging = false; });
14026
+ });
14027
+
14028
+ this.#setupPointPointHitArea(this.#pointHandle, true);
14029
+ this.#setupPointPointHitArea(this.#secondHandle, false);
14030
+ }
14031
+ }
14032
+
14033
+ #setupPointPointHitArea(handle, isFirst) {
14034
+ if (!handle) return;
14035
+ handle.addEventListener("hitareadown", (e) => {
14036
+ e.stopPropagation();
14037
+ const origEvent = e.detail?.originalEvent;
14038
+ if (!origEvent) return;
14039
+ origEvent.preventDefault();
14040
+ this.#isDragging = true;
14041
+ const container = this.#container;
14042
+ if (!container) return;
14043
+ const rect = container.getBoundingClientRect();
14044
+
14045
+ const pivotX = isFirst ? this.#x2 : this.#x;
14046
+ const pivotY = isFirst ? this.#y2 : this.#y;
14047
+ const movingX = isFirst ? this.#x : this.#x2;
14048
+ const movingY = isFirst ? this.#y : this.#y2;
14049
+ const pcx = (pivotX / 100) * rect.width;
14050
+ const pcy = (pivotY / 100) * rect.height;
14051
+ const mcx = (movingX / 100) * rect.width;
14052
+ const mcy = (movingY / 100) * rect.height;
14053
+ const fixedLen = Math.sqrt((mcx - pcx) ** 2 + (mcy - pcy) ** 2);
14054
+
14055
+ const tooltip = isFirst ? this.#pointTooltip : this.#secondTooltip;
14056
+ if (tooltip) {
14057
+ tooltip.setAttribute("show", "true");
14058
+ tooltip.showPopup?.();
14059
+ }
14060
+
14061
+ const prevBodyCursor = document.body.style.cursor;
14062
+ const initDeg = this.#pointPointLineDeg();
14063
+ document.body.style.cursor = this.#rotateCursorSvg(isFirst ? initDeg + 180 : initDeg);
14064
+
14065
+ const onMove = (ev) => {
14066
+ const r = container.getBoundingClientRect();
14067
+ const px = (pivotX / 100) * r.width;
14068
+ const py = (pivotY / 100) * r.height;
14069
+ const dx = ev.clientX - r.left - px;
14070
+ const dy = ev.clientY - r.top - py;
14071
+ let angle = Math.atan2(dy, dx);
14072
+ if (this.#shouldSnap(ev.shiftKey)) {
14073
+ const snapDeg = Math.round((angle * 180) / Math.PI / 15) * 15;
14074
+ angle = (snapDeg * Math.PI) / 180;
14075
+ }
14076
+ const nx = px + fixedLen * Math.cos(angle);
14077
+ const ny = py + fixedLen * Math.sin(angle);
14078
+ const newPctX = Math.round(Math.max(0, Math.min(100, (nx / r.width) * 100)));
14079
+ const newPctY = Math.round(Math.max(0, Math.min(100, (ny / r.height) * 100)));
14080
+ if (isFirst) {
14081
+ this.#x = newPctX;
14082
+ this.#y = newPctY;
14083
+ } else {
14084
+ this.#x2 = newPctX;
14085
+ this.#y2 = newPctY;
14086
+ }
14087
+ this.#syncPositions();
14088
+ const curDeg = this.#pointPointLineDeg();
14089
+ document.body.style.cursor = this.#rotateCursorSvg(isFirst ? curDeg + 180 : curDeg);
14090
+ this.#emitInput();
14091
+ };
14092
+
14093
+ const onUp = () => {
14094
+ this.#isDragging = false;
14095
+ document.body.style.cursor = prevBodyCursor;
14096
+ if (tooltip) tooltip.removeAttribute("show");
14097
+ this.#syncValueAttribute();
14098
+ this.#emitChange();
14099
+ window.removeEventListener("pointermove", onMove);
14100
+ window.removeEventListener("pointerup", onUp);
14101
+ };
14102
+
14103
+ window.addEventListener("pointermove", onMove);
14104
+ window.addEventListener("pointerup", onUp);
14105
+ });
13889
14106
  }
13890
14107
 
13891
14108
  #setupRadiusDrag(circle) {
@@ -13905,7 +14122,7 @@ class FigCanvasPoint extends HTMLElement {
13905
14122
  e.preventDefault();
13906
14123
  e.stopPropagation();
13907
14124
  this.#isRadiusDragging = true;
13908
- this.classList.add("fig-canvas-point-ring-active");
14125
+ this.classList.add("fig-canvas-control-ring-active");
13909
14126
  const container = this.#container;
13910
14127
  if (!container) return;
13911
14128
 
@@ -13951,7 +14168,7 @@ class FigCanvasPoint extends HTMLElement {
13951
14168
 
13952
14169
  const onUp = () => {
13953
14170
  this.#isRadiusDragging = false;
13954
- this.classList.remove("fig-canvas-point-ring-active");
14171
+ this.classList.remove("fig-canvas-control-ring-active");
13955
14172
  circle.style.pointerEvents = "";
13956
14173
  document.body.style.cursor = prevBodyCursor;
13957
14174
  if (this.#radiusTooltip) this.#radiusTooltip.removeAttribute("show");
@@ -13975,7 +14192,7 @@ class FigCanvasPoint extends HTMLElement {
13975
14192
  }
13976
14193
  }
13977
14194
  }
13978
- customElements.define("fig-canvas-point", FigCanvasPoint);
14195
+ customElements.define("fig-canvas-control", FigCanvasControl);
13979
14196
 
13980
14197
  /* Handle */
13981
14198
  class FigHandle extends HTMLElement {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "3.18.0",
3
+ "version": "3.19.0",
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": "MIT",