@rogieking/figui3 2.11.0 → 2.12.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.
Files changed (3) hide show
  1. package/components.css +9 -9
  2. package/fig.js +198 -119
  3. package/package.json +1 -1
package/components.css CHANGED
@@ -308,20 +308,20 @@
308
308
  0px 10px 24px rgba(0, 0, 0, 0.18), 0px 2px 5px rgba(0, 0, 0, 0.15);
309
309
  --figma-elevation-100: 0px 0px 0.5px 0px rgba(0, 0, 0, 0.3),
310
310
  0px 1px 3px 0px rgba(0, 0, 0, 0.15);
311
- --figma-elevation-200: 0px 1px 3px 0px rgba(0, 0, 0, 0.102),
312
- 0px 3px 8px 0px rgba(0, 0, 0, 0.102), 0px 0px 0.5px 0px rgba(0, 0, 0, 0.18);
311
+ --figma-elevation-200: 0 0 0.5px 0 rgba(0, 0, 0, 0.18),
312
+ 0 3px 8px 0 rgba(0, 0, 0, 0.1), 0 1px 3px 0 rgba(0, 0, 0, 0.1);
313
313
  --figma-elevation-400-menu-panel: 0px 0px 0.5px 0px rgba(0, 0, 0, 0.12),
314
314
  0px 10px 16px 0px rgba(0, 0, 0, 0.12), 0px 2px 5px 0px rgba(0, 0, 0, 0.15);
315
315
  --figma-elevation-500-modal-window: 0px 0px 0.5px 0px rgba(0, 0, 0, 0.08),
316
316
  0px 10px 24px 0px rgba(0, 0, 0, 0.18), 0px 2px 5px 0px rgba(0, 0, 0, 0.15);
317
- --handle-shadow: 0px 0 0 0.5px rgba(0, 0, 0, 0.1), var(--figma-elevation-200);
317
+ --handle-shadow: 0px 0 0 0.5px rgba(0, 0, 0, 0.1), var(--figma-elevation-100);
318
318
  }
319
319
 
320
320
  /* Dark theme overrides for non-color values (shadows, elevations) */
321
321
  /* These cannot use light-dark() as they are complex values */
322
322
  @media (prefers-color-scheme: dark) {
323
323
  :root {
324
- --handle-shadow: 0px 0 0 0.75px 0px 0 0 0.75px rgba(0, 0, 0, 0.1),
324
+ --handle-shadow: 0px 0 0 0.75px rgba(0, 0, 0, 0.1),
325
325
  0px 0px 0.5px 0px rgba(255, 255, 255, 0.1);
326
326
  --figma-elevation-100: 0px 0px 0.5px 0px rgba(0, 0, 0, 0.5),
327
327
  0px 0.75px 0px 0px rgba(255, 255, 255, 0.1) inset,
@@ -343,7 +343,7 @@
343
343
 
344
344
  /* Class-based dark theme override (for manual theme switching) */
345
345
  :root.figma-dark {
346
- --handle-shadow: 0px 0 0 0.75px 0px 0 0 0.75px rgba(0, 0, 0, 0.1),
346
+ --handle-shadow: 0px 0 0 0.75px rgba(0, 0, 0, 0.1),
347
347
  0px 0px 0.5px 0px rgba(255, 255, 255, 0.1);
348
348
  --figma-elevation-100: 0px 0px 0.5px 0px rgba(0, 0, 0, 0.5),
349
349
  0px 0.75px 0px 0px rgba(255, 255, 255, 0.1) inset,
@@ -1575,7 +1575,7 @@ fig-slider {
1575
1575
  --slider-tick-size: calc(var(--slider-height) / 4);
1576
1576
  --slider-handle-shadow: inset 0 0 0 calc(4px + 0.5rem * var(--unchanged))
1577
1577
  var(--handle-color),
1578
- 0px 0 0 0.5px rgba(0, 0, 0, 0.1), var(--figma-elevation-200);
1578
+ 0px 0 0 0.5px rgba(0, 0, 0, 0.1), var(--figma-elevation-100);
1579
1579
  --slider-handle-shadow-focus: inset 0 0 0 4px var(--handle-color),
1580
1580
  inset 0 0 0 5px rgba(0, 0, 0, 0.1), var(--handle-shadow),
1581
1581
  0 0 0 1px var(--figma-color-border-selected);
@@ -1862,7 +1862,7 @@ fig-slider {
1862
1862
  --slider-handle-shadow: inset 0 0 0 calc(6px + 0.5rem * var(--unchanged))
1863
1863
  var(--handle-color),
1864
1864
  0 0 0 0.75px rgba(0, 0, 0, 0.075), inset 0 0 0 5px rgba(0, 0, 0, 0.1),
1865
- var(--figma-elevation-200);
1865
+ var(--figma-elevation-100);
1866
1866
 
1867
1867
  .fig-slider-input-container {
1868
1868
  height: var(--slider-height);
@@ -1891,7 +1891,7 @@ fig-slider {
1891
1891
  --slider-handle-shadow: inset 0 0 0 calc(6px + 0.5rem * var(--unchanged))
1892
1892
  var(--handle-color),
1893
1893
  0 0 0 0.75px rgba(0, 0, 0, 0.075), inset 0 0 0 5px rgba(0, 0, 0, 0.1),
1894
- var(--figma-elevation-200);
1894
+ var(--figma-elevation-100);
1895
1895
 
1896
1896
  background-color: var(--figma-color-bg-secondary);
1897
1897
  border-radius: var(--radius-medium);
@@ -2643,7 +2643,7 @@ fig-input-angle {
2643
2643
  grid-area: 1/1;
2644
2644
  width: calc(0.5rem + 2px);
2645
2645
  height: calc(0.5rem + 2px);
2646
- &:before {
2646
+ &::before {
2647
2647
  content: "";
2648
2648
  display: block;
2649
2649
  width: 0.5rem;
package/fig.js CHANGED
@@ -235,7 +235,7 @@ class FigDropdown extends HTMLElement {
235
235
  detail: selectedValue,
236
236
  bubbles: true,
237
237
  composed: true,
238
- }),
238
+ })
239
239
  );
240
240
  }
241
241
 
@@ -252,7 +252,7 @@ class FigDropdown extends HTMLElement {
252
252
  detail: selectedValue,
253
253
  bubbles: true,
254
254
  composed: true,
255
- }),
255
+ })
256
256
  );
257
257
  }
258
258
 
@@ -318,10 +318,11 @@ customElements.define("fig-dropdown", FigDropdown);
318
318
  */
319
319
  class FigTooltip extends HTMLElement {
320
320
  #boundHideOnChromeOpen;
321
- #boundHideOnDragStart;
322
321
  #boundHidePopupOutsideClick;
323
322
  #touchTimeout;
324
323
  #isTouching = false;
324
+ #observer = null;
325
+ #repositionRAF = null;
325
326
  constructor() {
326
327
  super();
327
328
  this.action = this.getAttribute("action") || "hover";
@@ -330,7 +331,6 @@ class FigTooltip extends HTMLElement {
330
331
 
331
332
  // Bind methods that will be used as event listeners
332
333
  this.#boundHideOnChromeOpen = this.#hideOnChromeOpen.bind(this);
333
- this.#boundHideOnDragStart = this.hidePopup.bind(this);
334
334
  this.#boundHidePopupOutsideClick = this.hidePopupOutsideClick.bind(this);
335
335
  }
336
336
  connectedCallback() {
@@ -344,16 +344,16 @@ class FigTooltip extends HTMLElement {
344
344
  document.removeEventListener(
345
345
  "mousedown",
346
346
  this.#boundHideOnChromeOpen,
347
- true,
347
+ true
348
348
  );
349
- // Remove mousedown listener
350
- this.removeEventListener("mousedown", this.#boundHideOnDragStart);
349
+ // Disconnect mutation observer
350
+ this.#stopObserving();
351
351
 
352
352
  // Remove click outside listener for click action
353
353
  if (this.action === "click") {
354
354
  document.body.removeEventListener(
355
355
  "click",
356
- this.#boundHidePopupOutsideClick,
356
+ this.#boundHidePopupOutsideClick
357
357
  );
358
358
  }
359
359
 
@@ -411,6 +411,7 @@ class FigTooltip extends HTMLElement {
411
411
  }
412
412
 
413
413
  destroy() {
414
+ this.#stopObserving();
414
415
  if (this.popup) {
415
416
  this.popup.remove();
416
417
  }
@@ -418,7 +419,7 @@ class FigTooltip extends HTMLElement {
418
419
  if (this.action === "click") {
419
420
  document.body.removeEventListener(
420
421
  "click",
421
- this.#boundHidePopupOutsideClick,
422
+ this.#boundHidePopupOutsideClick
422
423
  );
423
424
  }
424
425
  }
@@ -436,12 +437,9 @@ class FigTooltip extends HTMLElement {
436
437
  this.addEventListener("pointerenter", this.showDelayedPopup.bind(this));
437
438
  this.addEventListener(
438
439
  "pointerleave",
439
- this.#handlePointerLeave.bind(this),
440
+ this.#handlePointerLeave.bind(this)
440
441
  );
441
442
  }
442
- // Add mousedown listener instead of dragstart
443
- this.addEventListener("mousedown", this.#boundHideOnDragStart);
444
-
445
443
  // Touch support for mobile hover simulation
446
444
  this.addEventListener("touchstart", this.#handleTouchStart.bind(this), {
447
445
  passive: true,
@@ -490,6 +488,20 @@ class FigTooltip extends HTMLElement {
490
488
  }
491
489
 
492
490
  showPopup() {
491
+ this.#repositionPopup();
492
+ this.popup.style.opacity = "1";
493
+ this.popup.style.visibility = "visible";
494
+ this.popup.style.display = "block";
495
+ this.popup.style.pointerEvents = "all";
496
+ this.popup.style.zIndex = figGetHighestZIndex() + 1;
497
+
498
+ this.isOpen = true;
499
+ this.#startObserving();
500
+ }
501
+
502
+ #repositionPopup() {
503
+ if (!this.popup || !this.firstElementChild) return;
504
+
493
505
  const rect = this.firstElementChild.getBoundingClientRect();
494
506
  const popupRect = this.popup.getBoundingClientRect();
495
507
  const offset = this.getOffset();
@@ -520,18 +532,12 @@ class FigTooltip extends HTMLElement {
520
532
 
521
533
  this.popup.style.top = `${top}px`;
522
534
  this.popup.style.left = `${left}px`;
523
- this.popup.style.opacity = "1";
524
- this.popup.style.visibility = "visible";
525
- this.popup.style.display = "block";
526
- this.popup.style.pointerEvents = "all";
527
- this.popup.style.zIndex = figGetHighestZIndex() + 1;
528
-
529
- this.isOpen = true;
530
535
  }
531
536
 
532
537
  hidePopup() {
533
538
  clearTimeout(this.timeout);
534
539
  clearTimeout(this.#touchTimeout);
540
+ this.#stopObserving();
535
541
  if (this.popup) {
536
542
  this.popup.style.opacity = "0";
537
543
  this.popup.style.display = "block";
@@ -542,6 +548,35 @@ class FigTooltip extends HTMLElement {
542
548
  this.isOpen = false;
543
549
  }
544
550
 
551
+ #startObserving() {
552
+ this.#stopObserving();
553
+ const target = this.firstElementChild;
554
+ if (!target) return;
555
+
556
+ this.#observer = new MutationObserver(() => {
557
+ if (this.#repositionRAF) cancelAnimationFrame(this.#repositionRAF);
558
+ this.#repositionRAF = requestAnimationFrame(() => {
559
+ this.#repositionPopup();
560
+ });
561
+ });
562
+
563
+ this.#observer.observe(target, {
564
+ attributes: true,
565
+ attributeFilter: ["style", "class", "transform"],
566
+ });
567
+ }
568
+
569
+ #stopObserving() {
570
+ if (this.#repositionRAF) {
571
+ cancelAnimationFrame(this.#repositionRAF);
572
+ this.#repositionRAF = null;
573
+ }
574
+ if (this.#observer) {
575
+ this.#observer.disconnect();
576
+ this.#observer = null;
577
+ }
578
+ }
579
+
545
580
  hidePopupOutsideClick(event) {
546
581
  if (this.isOpen && !this.popup.contains(event.target)) {
547
582
  this.hidePopup();
@@ -1278,7 +1313,7 @@ class FigSegmentedControl extends HTMLElement {
1278
1313
  requestAnimationFrame(() => {
1279
1314
  const segments = this.querySelectorAll("fig-segment");
1280
1315
  const hasSelected = Array.from(segments).some((s) =>
1281
- s.hasAttribute("selected"),
1316
+ s.hasAttribute("selected")
1282
1317
  );
1283
1318
  if (!hasSelected && segments.length > 0) {
1284
1319
  this.selectedSegment = segments[0];
@@ -1435,7 +1470,7 @@ class FigSlider extends HTMLElement {
1435
1470
  if (this.default) {
1436
1471
  this.style.setProperty(
1437
1472
  "--default",
1438
- this.#calculateNormal(this.default),
1473
+ this.#calculateNormal(this.default)
1439
1474
  );
1440
1475
  }
1441
1476
 
@@ -1445,7 +1480,7 @@ class FigSlider extends HTMLElement {
1445
1480
  this.inputContainer.append(this.datalist);
1446
1481
  this.datalist.setAttribute(
1447
1482
  "id",
1448
- this.datalist.getAttribute("id") || figUniqueId(),
1483
+ this.datalist.getAttribute("id") || figUniqueId()
1449
1484
  );
1450
1485
  this.input.setAttribute("list", this.datalist.getAttribute("id"));
1451
1486
  } else if (this.type === "stepper") {
@@ -1470,7 +1505,7 @@ class FigSlider extends HTMLElement {
1470
1505
  }
1471
1506
  if (this.datalist) {
1472
1507
  let defaultOption = this.datalist.querySelector(
1473
- `option[value='${this.default}']`,
1508
+ `option[value='${this.default}']`
1474
1509
  );
1475
1510
  if (defaultOption) {
1476
1511
  defaultOption.setAttribute("default", "true");
@@ -1479,19 +1514,19 @@ class FigSlider extends HTMLElement {
1479
1514
  if (this.figInputNumber) {
1480
1515
  this.figInputNumber.removeEventListener(
1481
1516
  "input",
1482
- this.#boundHandleTextInput,
1517
+ this.#boundHandleTextInput
1483
1518
  );
1484
1519
  this.figInputNumber.addEventListener(
1485
1520
  "input",
1486
- this.#boundHandleTextInput,
1521
+ this.#boundHandleTextInput
1487
1522
  );
1488
1523
  this.figInputNumber.removeEventListener(
1489
1524
  "change",
1490
- this.#boundHandleTextChange,
1525
+ this.#boundHandleTextChange
1491
1526
  );
1492
1527
  this.figInputNumber.addEventListener(
1493
1528
  "change",
1494
- this.#boundHandleTextChange,
1529
+ this.#boundHandleTextChange
1495
1530
  );
1496
1531
  }
1497
1532
 
@@ -1511,11 +1546,11 @@ class FigSlider extends HTMLElement {
1511
1546
  if (this.figInputNumber) {
1512
1547
  this.figInputNumber.removeEventListener(
1513
1548
  "input",
1514
- this.#boundHandleTextInput,
1549
+ this.#boundHandleTextInput
1515
1550
  );
1516
1551
  this.figInputNumber.removeEventListener(
1517
1552
  "change",
1518
- this.#boundHandleTextChange,
1553
+ this.#boundHandleTextChange
1519
1554
  );
1520
1555
  }
1521
1556
  }
@@ -1525,7 +1560,7 @@ class FigSlider extends HTMLElement {
1525
1560
  this.value = this.input.value = this.figInputNumber.value;
1526
1561
  this.#syncProperties();
1527
1562
  this.dispatchEvent(
1528
- new CustomEvent("input", { detail: this.value, bubbles: true }),
1563
+ new CustomEvent("input", { detail: this.value, bubbles: true })
1529
1564
  );
1530
1565
  }
1531
1566
  }
@@ -1555,14 +1590,14 @@ class FigSlider extends HTMLElement {
1555
1590
  #handleInput() {
1556
1591
  this.#syncValue();
1557
1592
  this.dispatchEvent(
1558
- new CustomEvent("input", { detail: this.value, bubbles: true }),
1593
+ new CustomEvent("input", { detail: this.value, bubbles: true })
1559
1594
  );
1560
1595
  }
1561
1596
 
1562
1597
  #handleChange() {
1563
1598
  this.#syncValue();
1564
1599
  this.dispatchEvent(
1565
- new CustomEvent("change", { detail: this.value, bubbles: true }),
1600
+ new CustomEvent("change", { detail: this.value, bubbles: true })
1566
1601
  );
1567
1602
  }
1568
1603
 
@@ -1571,7 +1606,7 @@ class FigSlider extends HTMLElement {
1571
1606
  this.value = this.input.value = this.figInputNumber.value;
1572
1607
  this.#syncProperties();
1573
1608
  this.dispatchEvent(
1574
- new CustomEvent("change", { detail: this.value, bubbles: true }),
1609
+ new CustomEvent("change", { detail: this.value, bubbles: true })
1575
1610
  );
1576
1611
  }
1577
1612
  }
@@ -1778,10 +1813,10 @@ class FigInputText extends HTMLElement {
1778
1813
  this.value = value;
1779
1814
  this.input.value = valueTransformed;
1780
1815
  this.dispatchEvent(
1781
- new CustomEvent("input", { detail: this.value, bubbles: true }),
1816
+ new CustomEvent("input", { detail: this.value, bubbles: true })
1782
1817
  );
1783
1818
  this.dispatchEvent(
1784
- new CustomEvent("change", { detail: this.value, bubbles: true }),
1819
+ new CustomEvent("change", { detail: this.value, bubbles: true })
1785
1820
  );
1786
1821
  }
1787
1822
  #handleMouseMove(e) {
@@ -1831,13 +1866,13 @@ class FigInputText extends HTMLElement {
1831
1866
  if (typeof this.min === "number") {
1832
1867
  sanitized = Math.max(
1833
1868
  transform ? this.#transformNumber(this.min) : this.min,
1834
- sanitized,
1869
+ sanitized
1835
1870
  );
1836
1871
  }
1837
1872
  if (typeof this.max === "number") {
1838
1873
  sanitized = Math.min(
1839
1874
  transform ? this.#transformNumber(this.max) : this.max,
1840
- sanitized,
1875
+ sanitized
1841
1876
  );
1842
1877
  }
1843
1878
 
@@ -2149,7 +2184,7 @@ class FigInputNumber extends HTMLElement {
2149
2184
  e.target.value = "";
2150
2185
  }
2151
2186
  this.dispatchEvent(
2152
- new CustomEvent("change", { detail: this.value, bubbles: true }),
2187
+ new CustomEvent("change", { detail: this.value, bubbles: true })
2153
2188
  );
2154
2189
  }
2155
2190
 
@@ -2175,10 +2210,10 @@ class FigInputNumber extends HTMLElement {
2175
2210
  this.input.value = this.#formatWithUnit(this.value);
2176
2211
 
2177
2212
  this.dispatchEvent(
2178
- new CustomEvent("input", { detail: this.value, bubbles: true }),
2213
+ new CustomEvent("input", { detail: this.value, bubbles: true })
2179
2214
  );
2180
2215
  this.dispatchEvent(
2181
- new CustomEvent("change", { detail: this.value, bubbles: true }),
2216
+ new CustomEvent("change", { detail: this.value, bubbles: true })
2182
2217
  );
2183
2218
  }
2184
2219
 
@@ -2190,7 +2225,7 @@ class FigInputNumber extends HTMLElement {
2190
2225
  this.value = "";
2191
2226
  }
2192
2227
  this.dispatchEvent(
2193
- new CustomEvent("input", { detail: this.value, bubbles: true }),
2228
+ new CustomEvent("input", { detail: this.value, bubbles: true })
2194
2229
  );
2195
2230
  }
2196
2231
 
@@ -2207,10 +2242,10 @@ class FigInputNumber extends HTMLElement {
2207
2242
  e.target.value = "";
2208
2243
  }
2209
2244
  this.dispatchEvent(
2210
- new CustomEvent("input", { detail: this.value, bubbles: true }),
2245
+ new CustomEvent("input", { detail: this.value, bubbles: true })
2211
2246
  );
2212
2247
  this.dispatchEvent(
2213
- new CustomEvent("change", { detail: this.value, bubbles: true }),
2248
+ new CustomEvent("change", { detail: this.value, bubbles: true })
2214
2249
  );
2215
2250
  }
2216
2251
 
@@ -2397,7 +2432,7 @@ class FigField extends HTMLElement {
2397
2432
  requestAnimationFrame(() => {
2398
2433
  this.label = this.querySelector(":scope>label");
2399
2434
  this.input = Array.from(this.childNodes).find((node) =>
2400
- node.nodeName.toLowerCase().startsWith("fig-"),
2435
+ node.nodeName.toLowerCase().startsWith("fig-")
2401
2436
  );
2402
2437
  if (this.input && this.label) {
2403
2438
  this.label.addEventListener("click", this.focus.bind(this));
@@ -2518,11 +2553,11 @@ class FigInputColor extends HTMLElement {
2518
2553
  }
2519
2554
  this.#fillPicker.addEventListener(
2520
2555
  "input",
2521
- this.#handleFillPickerInput.bind(this),
2556
+ this.#handleFillPickerInput.bind(this)
2522
2557
  );
2523
2558
  this.#fillPicker.addEventListener(
2524
2559
  "change",
2525
- this.#handleChange.bind(this),
2560
+ this.#handleChange.bind(this)
2526
2561
  );
2527
2562
  }
2528
2563
 
@@ -2535,22 +2570,22 @@ class FigInputColor extends HTMLElement {
2535
2570
  }
2536
2571
  this.#textInput.addEventListener(
2537
2572
  "input",
2538
- this.#handleTextInput.bind(this),
2573
+ this.#handleTextInput.bind(this)
2539
2574
  );
2540
2575
  this.#textInput.addEventListener(
2541
2576
  "change",
2542
- this.#handleChange.bind(this),
2577
+ this.#handleChange.bind(this)
2543
2578
  );
2544
2579
  }
2545
2580
 
2546
2581
  if (this.#alphaInput) {
2547
2582
  this.#alphaInput.addEventListener(
2548
2583
  "input",
2549
- this.#handleAlphaInput.bind(this),
2584
+ this.#handleAlphaInput.bind(this)
2550
2585
  );
2551
2586
  this.#alphaInput.addEventListener(
2552
2587
  "change",
2553
- this.#handleChange.bind(this),
2588
+ this.#handleChange.bind(this)
2554
2589
  );
2555
2590
  }
2556
2591
  });
@@ -2563,7 +2598,7 @@ class FigInputColor extends HTMLElement {
2563
2598
  g: isNaN(this.rgba.g) ? 0 : this.rgba.g,
2564
2599
  b: isNaN(this.rgba.b) ? 0 : this.rgba.b,
2565
2600
  },
2566
- this.rgba.a,
2601
+ this.rgba.a
2567
2602
  );
2568
2603
  this.hexWithAlpha = this.value.toUpperCase();
2569
2604
  this.hexOpaque = this.hexWithAlpha.slice(0, 7);
@@ -2606,7 +2641,7 @@ class FigInputColor extends HTMLElement {
2606
2641
  type: "solid",
2607
2642
  color: this.hexOpaque,
2608
2643
  opacity: this.alpha,
2609
- }),
2644
+ })
2610
2645
  );
2611
2646
  }
2612
2647
  this.#emitInputEvent();
@@ -2629,7 +2664,7 @@ class FigInputColor extends HTMLElement {
2629
2664
  // Display without # prefix
2630
2665
  this.#textInput.setAttribute(
2631
2666
  "value",
2632
- this.hexOpaque.slice(1).toUpperCase(),
2667
+ this.hexOpaque.slice(1).toUpperCase()
2633
2668
  );
2634
2669
  }
2635
2670
  this.#emitInputEvent();
@@ -2651,7 +2686,7 @@ class FigInputColor extends HTMLElement {
2651
2686
  if (this.#textInput) {
2652
2687
  this.#textInput.setAttribute(
2653
2688
  "value",
2654
- this.hexOpaque.slice(1).toUpperCase(),
2689
+ this.hexOpaque.slice(1).toUpperCase()
2655
2690
  );
2656
2691
  }
2657
2692
  if (this.#alphaInput && detail.alpha !== undefined) {
@@ -2709,7 +2744,7 @@ class FigInputColor extends HTMLElement {
2709
2744
  type: "solid",
2710
2745
  color: this.hexOpaque,
2711
2746
  opacity: this.alpha,
2712
- }),
2747
+ })
2713
2748
  );
2714
2749
  }
2715
2750
  if (this.#alphaInput) {
@@ -2776,7 +2811,7 @@ class FigInputColor extends HTMLElement {
2776
2811
  // Handle rgba colors
2777
2812
  else if (color.startsWith("rgba") || color.startsWith("rgb")) {
2778
2813
  let matches = color.match(
2779
- /rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)/,
2814
+ /rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)/
2780
2815
  );
2781
2816
  if (matches) {
2782
2817
  r = parseInt(matches[1]);
@@ -2788,7 +2823,7 @@ class FigInputColor extends HTMLElement {
2788
2823
  // Handle hsla colors
2789
2824
  else if (color.startsWith("hsla") || color.startsWith("hsl")) {
2790
2825
  let matches = color.match(
2791
- /hsla?\((\d+),\s*(\d+)%,\s*(\d+)%(?:,\s*(\d+(?:\.\d+)?))?\)/,
2826
+ /hsla?\((\d+),\s*(\d+)%,\s*(\d+)%(?:,\s*(\d+(?:\.\d+)?))?\)/
2792
2827
  );
2793
2828
  if (matches) {
2794
2829
  let h = parseInt(matches[1]) / 360;
@@ -3015,7 +3050,9 @@ class FigInputFill extends HTMLElement {
3015
3050
 
3016
3051
  this.innerHTML = `
3017
3052
  <div class="input-combo">
3018
- <fig-fill-picker value='${fillPickerValue}' ${disabled ? "disabled" : ""}></fig-fill-picker>
3053
+ <fig-fill-picker value='${fillPickerValue}' ${
3054
+ disabled ? "disabled" : ""
3055
+ }></fig-fill-picker>
3019
3056
  ${controlsHtml}
3020
3057
  </div>`;
3021
3058
 
@@ -3144,13 +3181,13 @@ class FigInputFill extends HTMLElement {
3144
3181
  if (this.#hexInput) {
3145
3182
  this.#hexInput.setAttribute(
3146
3183
  "value",
3147
- this.#solid.color.slice(1).toUpperCase(),
3184
+ this.#solid.color.slice(1).toUpperCase()
3148
3185
  );
3149
3186
  }
3150
3187
  if (this.#opacityInput) {
3151
3188
  this.#opacityInput.setAttribute(
3152
3189
  "value",
3153
- Math.round(this.#solid.alpha * 100),
3190
+ Math.round(this.#solid.alpha * 100)
3154
3191
  );
3155
3192
  }
3156
3193
  break;
@@ -3158,7 +3195,7 @@ class FigInputFill extends HTMLElement {
3158
3195
  if (this.#opacityInput) {
3159
3196
  this.#opacityInput.setAttribute(
3160
3197
  "value",
3161
- this.#gradient.stops[0]?.opacity || 100,
3198
+ this.#gradient.stops[0]?.opacity || 100
3162
3199
  );
3163
3200
  }
3164
3201
  break;
@@ -3166,7 +3203,7 @@ class FigInputFill extends HTMLElement {
3166
3203
  if (this.#opacityInput) {
3167
3204
  this.#opacityInput.setAttribute(
3168
3205
  "value",
3169
- Math.round((this.#image.opacity ?? 1) * 100),
3206
+ Math.round((this.#image.opacity ?? 1) * 100)
3170
3207
  );
3171
3208
  }
3172
3209
  break;
@@ -3174,7 +3211,7 @@ class FigInputFill extends HTMLElement {
3174
3211
  if (this.#opacityInput) {
3175
3212
  this.#opacityInput.setAttribute(
3176
3213
  "value",
3177
- Math.round((this.#video.opacity ?? 1) * 100),
3214
+ Math.round((this.#video.opacity ?? 1) * 100)
3178
3215
  );
3179
3216
  }
3180
3217
  break;
@@ -3182,7 +3219,7 @@ class FigInputFill extends HTMLElement {
3182
3219
  if (this.#opacityInput) {
3183
3220
  this.#opacityInput.setAttribute(
3184
3221
  "value",
3185
- Math.round((this.#webcam.opacity ?? 1) * 100),
3222
+ Math.round((this.#webcam.opacity ?? 1) * 100)
3186
3223
  );
3187
3224
  }
3188
3225
  break;
@@ -3386,7 +3423,7 @@ class FigInputFill extends HTMLElement {
3386
3423
  new CustomEvent("input", {
3387
3424
  bubbles: true,
3388
3425
  detail: this.value,
3389
- }),
3426
+ })
3390
3427
  );
3391
3428
  }
3392
3429
 
@@ -3395,7 +3432,7 @@ class FigInputFill extends HTMLElement {
3395
3432
  new CustomEvent("change", {
3396
3433
  bubbles: true,
3397
3434
  detail: this.value,
3398
- }),
3435
+ })
3399
3436
  );
3400
3437
  }
3401
3438
 
@@ -3602,14 +3639,14 @@ class FigCheckbox extends HTMLElement {
3602
3639
  bubbles: true,
3603
3640
  composed: true,
3604
3641
  detail: { checked: this.input.checked, value: this.input.value },
3605
- }),
3642
+ })
3606
3643
  );
3607
3644
  this.dispatchEvent(
3608
3645
  new CustomEvent("change", {
3609
3646
  bubbles: true,
3610
3647
  composed: true,
3611
3648
  detail: { checked: this.input.checked, value: this.input.value },
3612
- }),
3649
+ })
3613
3650
  );
3614
3651
  }
3615
3652
  }
@@ -3822,7 +3859,7 @@ class FigComboInput extends HTMLElement {
3822
3859
 
3823
3860
  this.dropdown.addEventListener(
3824
3861
  "input",
3825
- this.handleSelectInput.bind(this),
3862
+ this.handleSelectInput.bind(this)
3826
3863
  );
3827
3864
 
3828
3865
  // Apply initial disabled state
@@ -4076,7 +4113,7 @@ class FigImage extends HTMLElement {
4076
4113
  disconnectedCallback() {
4077
4114
  this.fileInput.removeEventListener(
4078
4115
  "change",
4079
- this.#handleFileInput.bind(this),
4116
+ this.#handleFileInput.bind(this)
4080
4117
  );
4081
4118
  }
4082
4119
 
@@ -4088,22 +4125,22 @@ class FigImage extends HTMLElement {
4088
4125
  this.fileInput = this.uploadButton?.querySelector("input");
4089
4126
  this.fileInput.removeEventListener(
4090
4127
  "change",
4091
- this.#handleFileInput.bind(this),
4128
+ this.#handleFileInput.bind(this)
4092
4129
  );
4093
4130
  this.fileInput.addEventListener(
4094
4131
  "change",
4095
- this.#handleFileInput.bind(this),
4132
+ this.#handleFileInput.bind(this)
4096
4133
  );
4097
4134
  }
4098
4135
  if (this.download) {
4099
4136
  this.downloadButton = this.querySelector("fig-button[type='download']");
4100
4137
  this.downloadButton.removeEventListener(
4101
4138
  "click",
4102
- this.#handleDownload.bind(this),
4139
+ this.#handleDownload.bind(this)
4103
4140
  );
4104
4141
  this.downloadButton.addEventListener(
4105
4142
  "click",
4106
- this.#handleDownload.bind(this),
4143
+ this.#handleDownload.bind(this)
4107
4144
  );
4108
4145
  }
4109
4146
  });
@@ -4124,7 +4161,7 @@ class FigImage extends HTMLElement {
4124
4161
  this.aspectRatio = this.image.width / this.image.height;
4125
4162
  this.style.setProperty(
4126
4163
  "--aspect-ratio",
4127
- `${this.image.width}/${this.image.height}`,
4164
+ `${this.image.width}/${this.image.height}`
4128
4165
  );
4129
4166
  this.dispatchEvent(
4130
4167
  new CustomEvent("loaded", {
@@ -4134,7 +4171,7 @@ class FigImage extends HTMLElement {
4134
4171
  blob: this.blob,
4135
4172
  base64: this.base64,
4136
4173
  },
4137
- }),
4174
+ })
4138
4175
  );
4139
4176
  resolve();
4140
4177
 
@@ -4185,14 +4222,14 @@ class FigImage extends HTMLElement {
4185
4222
  blob: this.blob,
4186
4223
  base64: this.base64,
4187
4224
  },
4188
- }),
4225
+ })
4189
4226
  );
4190
4227
  //emit for change too
4191
4228
  this.dispatchEvent(
4192
4229
  new CustomEvent("change", {
4193
4230
  bubbles: true,
4194
4231
  cancelable: true,
4195
- }),
4232
+ })
4196
4233
  );
4197
4234
  this.setAttribute("src", this.blob);
4198
4235
  }
@@ -4213,7 +4250,7 @@ class FigImage extends HTMLElement {
4213
4250
  if (this.chit) {
4214
4251
  this.chit.setAttribute(
4215
4252
  "background",
4216
- this.#src ? `url(${this.#src})` : "",
4253
+ this.#src ? `url(${this.#src})` : ""
4217
4254
  );
4218
4255
  }
4219
4256
  if (this.#src) {
@@ -4326,7 +4363,7 @@ class FigInputJoystick extends HTMLElement {
4326
4363
  this.plane.addEventListener("mousedown", this.#handleMouseDown.bind(this));
4327
4364
  this.plane.addEventListener(
4328
4365
  "touchstart",
4329
- this.#handleTouchStart.bind(this),
4366
+ this.#handleTouchStart.bind(this)
4330
4367
  );
4331
4368
  window.addEventListener("keydown", this.#handleKeyDown.bind(this));
4332
4369
  window.addEventListener("keyup", this.#handleKeyUp.bind(this));
@@ -4386,7 +4423,7 @@ class FigInputJoystick extends HTMLElement {
4386
4423
  let x = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
4387
4424
  let screenY = Math.max(
4388
4425
  0,
4389
- Math.min(1, (e.clientY - rect.top) / rect.height),
4426
+ Math.min(1, (e.clientY - rect.top) / rect.height)
4390
4427
  );
4391
4428
 
4392
4429
  // Convert screen Y to internal Y (flip for math coordinates)
@@ -4414,7 +4451,7 @@ class FigInputJoystick extends HTMLElement {
4414
4451
  new CustomEvent("input", {
4415
4452
  bubbles: true,
4416
4453
  cancelable: true,
4417
- }),
4454
+ })
4418
4455
  );
4419
4456
  }
4420
4457
 
@@ -4423,7 +4460,7 @@ class FigInputJoystick extends HTMLElement {
4423
4460
  new CustomEvent("change", {
4424
4461
  bubbles: true,
4425
4462
  cancelable: true,
4426
- }),
4463
+ })
4427
4464
  );
4428
4465
  }
4429
4466
 
@@ -4552,6 +4589,7 @@ customElements.define("fig-input-joystick", FigInputJoystick);
4552
4589
  * @attr {boolean} text - Whether to display a text input for the angle value.
4553
4590
  * @attr {number} adjacent - The adjacent value of the angle.
4554
4591
  * @attr {number} opposite - The opposite value of the angle.
4592
+ * @attr {boolean} show-rotations - Whether to display a rotation count (×N) when rotations > 1. Defaults to false.
4555
4593
  */
4556
4594
  class FigInputAngle extends HTMLElement {
4557
4595
  // Private fields
@@ -4574,6 +4612,8 @@ class FigInputAngle extends HTMLElement {
4574
4612
  this.units = "°";
4575
4613
  this.min = null;
4576
4614
  this.max = null;
4615
+ this.showRotations = false;
4616
+ this.rotationSpan = null;
4577
4617
 
4578
4618
  this.#boundHandleRawChange = this.#handleRawChange.bind(this);
4579
4619
  }
@@ -4594,6 +4634,8 @@ class FigInputAngle extends HTMLElement {
4594
4634
  this.max = this.hasAttribute("max")
4595
4635
  ? Number(this.getAttribute("max"))
4596
4636
  : null;
4637
+ this.showRotations =
4638
+ this.getAttribute("show-rotations") === "true";
4597
4639
 
4598
4640
  this.#render();
4599
4641
  this.#setupListeners();
@@ -4602,7 +4644,7 @@ class FigInputAngle extends HTMLElement {
4602
4644
  if (this.text && this.angleInput) {
4603
4645
  this.angleInput.setAttribute(
4604
4646
  "value",
4605
- this.angle.toFixed(this.precision),
4647
+ this.angle.toFixed(this.precision)
4606
4648
  );
4607
4649
  }
4608
4650
  });
@@ -4633,12 +4675,30 @@ class FigInputAngle extends HTMLElement {
4633
4675
  ${minAttr}
4634
4676
  ${maxAttr}
4635
4677
  units="${this.units}">
4678
+ ${this.showRotations ? `<span slot="append" class="fig-input-angle-rotations"></span>` : ""}
4636
4679
  </fig-input-number>`
4637
4680
  : ""
4638
4681
  }
4639
4682
  `;
4640
4683
  }
4641
4684
 
4685
+ #getRotationCount() {
4686
+ const degrees = Math.abs(this.#toDegrees(this.angle));
4687
+ return Math.floor(degrees / 360);
4688
+ }
4689
+
4690
+ #updateRotationDisplay() {
4691
+ if (!this.rotationSpan) return;
4692
+ const rotations = this.#getRotationCount();
4693
+ if (rotations > 1) {
4694
+ this.rotationSpan.textContent = `\u00d7${rotations}`;
4695
+ this.rotationSpan.style.display = "";
4696
+ } else {
4697
+ this.rotationSpan.textContent = "";
4698
+ this.rotationSpan.style.display = "none";
4699
+ }
4700
+ }
4701
+
4642
4702
  #getStepForUnit() {
4643
4703
  switch (this.units) {
4644
4704
  case "rad":
@@ -4704,17 +4764,19 @@ class FigInputAngle extends HTMLElement {
4704
4764
  this.handle = this.querySelector(".fig-input-angle-handle");
4705
4765
  this.plane = this.querySelector(".fig-input-angle-plane");
4706
4766
  this.angleInput = this.querySelector("fig-input-number[name='angle']");
4767
+ this.rotationSpan = this.querySelector(".fig-input-angle-rotations");
4768
+ this.#updateRotationDisplay();
4707
4769
  this.plane.addEventListener("mousedown", this.#handleMouseDown.bind(this));
4708
4770
  this.plane.addEventListener(
4709
4771
  "touchstart",
4710
- this.#handleTouchStart.bind(this),
4772
+ this.#handleTouchStart.bind(this)
4711
4773
  );
4712
4774
  window.addEventListener("keydown", this.#handleKeyDown.bind(this));
4713
4775
  window.addEventListener("keyup", this.#handleKeyUp.bind(this));
4714
4776
  if (this.text && this.angleInput) {
4715
4777
  this.angleInput.addEventListener(
4716
4778
  "input",
4717
- this.#handleAngleInput.bind(this),
4779
+ this.#handleAngleInput.bind(this)
4718
4780
  );
4719
4781
  }
4720
4782
  // Capture-phase listener for unit suffix parsing
@@ -4753,6 +4815,7 @@ class FigInputAngle extends HTMLElement {
4753
4815
  this.angle = Number(e.target.value);
4754
4816
  this.#calculateAdjacentAndOpposite();
4755
4817
  this.#syncHandlePosition();
4818
+ this.#updateRotationDisplay();
4756
4819
  this.#emitInputEvent();
4757
4820
  this.#emitChangeEvent();
4758
4821
  }
@@ -4819,6 +4882,7 @@ class FigInputAngle extends HTMLElement {
4819
4882
  if (this.text && this.angleInput) {
4820
4883
  this.angleInput.setAttribute("value", this.angle.toFixed(this.precision));
4821
4884
  }
4885
+ this.#updateRotationDisplay();
4822
4886
 
4823
4887
  this.#emitInputEvent();
4824
4888
  }
@@ -4830,7 +4894,7 @@ class FigInputAngle extends HTMLElement {
4830
4894
  new CustomEvent("input", {
4831
4895
  bubbles: true,
4832
4896
  cancelable: true,
4833
- }),
4897
+ })
4834
4898
  );
4835
4899
  }
4836
4900
 
@@ -4839,7 +4903,7 @@ class FigInputAngle extends HTMLElement {
4839
4903
  new CustomEvent("change", {
4840
4904
  bubbles: true,
4841
4905
  cancelable: true,
4842
- }),
4906
+ })
4843
4907
  );
4844
4908
  }
4845
4909
 
@@ -4922,7 +4986,7 @@ class FigInputAngle extends HTMLElement {
4922
4986
  // --- Attributes ---
4923
4987
 
4924
4988
  static get observedAttributes() {
4925
- return ["value", "precision", "text", "min", "max", "units"];
4989
+ return ["value", "precision", "text", "min", "max", "units", "show-rotations"];
4926
4990
  }
4927
4991
 
4928
4992
  get value() {
@@ -4945,6 +5009,10 @@ class FigInputAngle extends HTMLElement {
4945
5009
  this.angle = value;
4946
5010
  this.#calculateAdjacentAndOpposite();
4947
5011
  this.#syncHandlePosition();
5012
+ if (this.angleInput) {
5013
+ this.angleInput.setAttribute("value", this.angle.toFixed(this.precision));
5014
+ }
5015
+ this.#updateRotationDisplay();
4948
5016
  }
4949
5017
 
4950
5018
  attributeChangedCallback(name, oldValue, newValue) {
@@ -4992,6 +5060,14 @@ class FigInputAngle extends HTMLElement {
4992
5060
  this.#syncHandlePosition();
4993
5061
  }
4994
5062
  break;
5063
+ case "show-rotations":
5064
+ this.showRotations = newValue === "true";
5065
+ if (this.plane) {
5066
+ this.#render();
5067
+ this.#setupListeners();
5068
+ this.#syncHandlePosition();
5069
+ }
5070
+ break;
4995
5071
  }
4996
5072
  }
4997
5073
  }
@@ -5092,7 +5168,7 @@ class FigLayer extends HTMLElement {
5092
5168
  new CustomEvent("openchange", {
5093
5169
  detail: { open: value },
5094
5170
  bubbles: true,
5095
- }),
5171
+ })
5096
5172
  );
5097
5173
  }
5098
5174
  }
@@ -5114,7 +5190,7 @@ class FigLayer extends HTMLElement {
5114
5190
  new CustomEvent("visibilitychange", {
5115
5191
  detail: { visible: value },
5116
5192
  bubbles: true,
5117
- }),
5193
+ })
5118
5194
  );
5119
5195
  }
5120
5196
  }
@@ -5128,7 +5204,7 @@ class FigLayer extends HTMLElement {
5128
5204
  new CustomEvent("openchange", {
5129
5205
  detail: { open: isOpen },
5130
5206
  bubbles: true,
5131
- }),
5207
+ })
5132
5208
  );
5133
5209
  }
5134
5210
 
@@ -5138,7 +5214,7 @@ class FigLayer extends HTMLElement {
5138
5214
  new CustomEvent("visibilitychange", {
5139
5215
  detail: { visible: isVisible },
5140
5216
  bubbles: true,
5141
- }),
5217
+ })
5142
5218
  );
5143
5219
  }
5144
5220
  }
@@ -5302,7 +5378,7 @@ class FigFillPicker extends HTMLElement {
5302
5378
  bg = `url(${this.#image.url})`;
5303
5379
  const sizing = this.#getBackgroundSizing(
5304
5380
  this.#image.scaleMode,
5305
- this.#image.scale,
5381
+ this.#image.scale
5306
5382
  );
5307
5383
  bgSize = sizing.size;
5308
5384
  bgPosition = sizing.position;
@@ -5315,7 +5391,7 @@ class FigFillPicker extends HTMLElement {
5315
5391
  bg = `url(${this.#video.url})`;
5316
5392
  const sizing = this.#getBackgroundSizing(
5317
5393
  this.#video.scaleMode,
5318
- this.#video.scale,
5394
+ this.#video.scale
5319
5395
  );
5320
5396
  bgSize = sizing.size;
5321
5397
  bgPosition = sizing.position;
@@ -5464,7 +5540,9 @@ class FigFillPicker extends HTMLElement {
5464
5540
  ? `<span class="fig-fill-picker-type-label">${
5465
5541
  lockedMode.charAt(0).toUpperCase() + lockedMode.slice(1)
5466
5542
  }</span>`
5467
- : `<fig-dropdown class="fig-fill-picker-type" variant="neue" value="${this.#fillType}">
5543
+ : `<fig-dropdown class="fig-fill-picker-type" variant="neue" value="${
5544
+ this.#fillType
5545
+ }">
5468
5546
  <option value="solid">Solid</option>
5469
5547
  <option value="gradient">Gradient</option>
5470
5548
  <option value="image">Image</option>
@@ -5584,7 +5662,7 @@ class FigFillPicker extends HTMLElement {
5584
5662
  <div class="fig-fill-picker-inputs">
5585
5663
  <fig-button icon variant="ghost" class="fig-fill-picker-eyedropper" title="Pick color from screen"><span class="fig-mask-icon" style="--icon: var(--icon-eyedropper)"></span></fig-button>
5586
5664
  <fig-input-color class="fig-fill-picker-color-input" text="true" picker="false" value="${this.#hsvToHex(
5587
- this.#color,
5665
+ this.#color
5588
5666
  )}"></fig-input-color>
5589
5667
  </div>
5590
5668
  `;
@@ -5611,7 +5689,7 @@ class FigFillPicker extends HTMLElement {
5611
5689
  // Setup opacity slider
5612
5690
  if (showAlpha) {
5613
5691
  this.#opacitySlider = container.querySelector(
5614
- 'fig-slider[type="opacity"]',
5692
+ 'fig-slider[type="opacity"]'
5615
5693
  );
5616
5694
  this.#opacitySlider.addEventListener("input", (e) => {
5617
5695
  this.#color.a = parseFloat(e.target.value) / 100;
@@ -5711,7 +5789,7 @@ class FigFillPicker extends HTMLElement {
5711
5789
  this.#colorAreaHandle.style.top = `${y}px`;
5712
5790
  this.#colorAreaHandle.style.setProperty(
5713
5791
  "--picker-color",
5714
- this.#hsvToHex({ ...this.#color, a: 1 }),
5792
+ this.#hsvToHex({ ...this.#color, a: 1 })
5715
5793
  );
5716
5794
  }
5717
5795
 
@@ -5774,7 +5852,7 @@ class FigFillPicker extends HTMLElement {
5774
5852
  const hex = this.#hsvToHex(this.#color);
5775
5853
 
5776
5854
  const colorInput = this.#dialog.querySelector(
5777
- ".fig-fill-picker-color-input",
5855
+ ".fig-fill-picker-color-input"
5778
5856
  );
5779
5857
  if (colorInput) {
5780
5858
  colorInput.setAttribute("value", hex);
@@ -5841,7 +5919,7 @@ class FigFillPicker extends HTMLElement {
5841
5919
  #setupGradientEvents(container) {
5842
5920
  // Type dropdown
5843
5921
  const typeDropdown = container.querySelector(
5844
- ".fig-fill-picker-gradient-type",
5922
+ ".fig-fill-picker-gradient-type"
5845
5923
  );
5846
5924
  typeDropdown.addEventListener("change", (e) => {
5847
5925
  this.#gradient.type = e.target.value;
@@ -5852,7 +5930,7 @@ class FigFillPicker extends HTMLElement {
5852
5930
  // Angle input
5853
5931
  // Convert from fig-input-angle coordinates (0° = right) to CSS coordinates (0° = up)
5854
5932
  const angleInput = container.querySelector(
5855
- ".fig-fill-picker-gradient-angle",
5933
+ ".fig-fill-picker-gradient-angle"
5856
5934
  );
5857
5935
  angleInput.addEventListener("input", (e) => {
5858
5936
  const pickerAngle = parseFloat(e.target.value) || 0;
@@ -5911,10 +5989,10 @@ class FigFillPicker extends HTMLElement {
5911
5989
 
5912
5990
  // Show/hide angle vs center inputs
5913
5991
  const angleInput = container.querySelector(
5914
- ".fig-fill-picker-gradient-angle",
5992
+ ".fig-fill-picker-gradient-angle"
5915
5993
  );
5916
5994
  const centerInputs = container.querySelector(
5917
- ".fig-fill-picker-gradient-center",
5995
+ ".fig-fill-picker-gradient-center"
5918
5996
  );
5919
5997
 
5920
5998
  if (this.#gradient.type === "radial") {
@@ -5947,7 +6025,7 @@ class FigFillPicker extends HTMLElement {
5947
6025
  if (!this.#dialog) return;
5948
6026
 
5949
6027
  const list = this.#dialog.querySelector(
5950
- ".fig-fill-picker-gradient-stops-list",
6028
+ ".fig-fill-picker-gradient-stops-list"
5951
6029
  );
5952
6030
  if (!list) return;
5953
6031
 
@@ -5967,7 +6045,7 @@ class FigFillPicker extends HTMLElement {
5967
6045
  <span class="fig-mask-icon" style="--icon: var(--icon-minus)"></span>
5968
6046
  </fig-button>
5969
6047
  </div>
5970
- `,
6048
+ `
5971
6049
  )
5972
6050
  .join("");
5973
6051
 
@@ -6065,7 +6143,7 @@ class FigFillPicker extends HTMLElement {
6065
6143
 
6066
6144
  #setupImageEvents(container) {
6067
6145
  const scaleModeDropdown = container.querySelector(
6068
- ".fig-fill-picker-scale-mode",
6146
+ ".fig-fill-picker-scale-mode"
6069
6147
  );
6070
6148
  const scaleInput = container.querySelector(".fig-fill-picker-scale");
6071
6149
  const uploadBtn = container.querySelector(".fig-fill-picker-upload");
@@ -6107,7 +6185,7 @@ class FigFillPicker extends HTMLElement {
6107
6185
 
6108
6186
  // Drag and drop
6109
6187
  const previewArea = container.querySelector(
6110
- ".fig-fill-picker-media-preview",
6188
+ ".fig-fill-picker-media-preview"
6111
6189
  );
6112
6190
  previewArea.addEventListener("dragover", (e) => {
6113
6191
  e.preventDefault();
@@ -6213,7 +6291,7 @@ class FigFillPicker extends HTMLElement {
6213
6291
 
6214
6292
  #setupVideoEvents(container) {
6215
6293
  const scaleModeDropdown = container.querySelector(
6216
- ".fig-fill-picker-scale-mode",
6294
+ ".fig-fill-picker-scale-mode"
6217
6295
  );
6218
6296
  const uploadBtn = container.querySelector(".fig-fill-picker-upload");
6219
6297
  const fileInput = container.querySelector('input[type="file"]');
@@ -6232,7 +6310,7 @@ class FigFillPicker extends HTMLElement {
6232
6310
 
6233
6311
  // Drag and drop
6234
6312
  const previewArea = container.querySelector(
6235
- ".fig-fill-picker-media-preview",
6313
+ ".fig-fill-picker-media-preview"
6236
6314
  );
6237
6315
 
6238
6316
  fileInput.addEventListener("change", (e) => {
@@ -6301,10 +6379,10 @@ class FigFillPicker extends HTMLElement {
6301
6379
  const video = container.querySelector(".fig-fill-picker-webcam-video");
6302
6380
  const status = container.querySelector(".fig-fill-picker-webcam-status");
6303
6381
  const captureBtn = container.querySelector(
6304
- ".fig-fill-picker-webcam-capture",
6382
+ ".fig-fill-picker-webcam-capture"
6305
6383
  );
6306
6384
  const cameraSelect = container.querySelector(
6307
- ".fig-fill-picker-camera-select",
6385
+ ".fig-fill-picker-camera-select"
6308
6386
  );
6309
6387
 
6310
6388
  const startWebcam = async (deviceId = null) => {
@@ -6317,8 +6395,9 @@ class FigFillPicker extends HTMLElement {
6317
6395
  this.#webcam.stream.getTracks().forEach((track) => track.stop());
6318
6396
  }
6319
6397
 
6320
- this.#webcam.stream =
6321
- await navigator.mediaDevices.getUserMedia(constraints);
6398
+ this.#webcam.stream = await navigator.mediaDevices.getUserMedia(
6399
+ constraints
6400
+ );
6322
6401
  video.srcObject = this.#webcam.stream;
6323
6402
  video.style.display = "block";
6324
6403
  status.style.display = "none";
@@ -6334,7 +6413,7 @@ class FigFillPicker extends HTMLElement {
6334
6413
  (cam, i) =>
6335
6414
  `<option value="${cam.deviceId}">${
6336
6415
  cam.label || `Camera ${i + 1}`
6337
- }</option>`,
6416
+ }</option>`
6338
6417
  )
6339
6418
  .join("");
6340
6419
  }
@@ -6626,7 +6705,7 @@ class FigFillPicker extends HTMLElement {
6626
6705
  new CustomEvent("input", {
6627
6706
  bubbles: true,
6628
6707
  detail: this.value,
6629
- }),
6708
+ })
6630
6709
  );
6631
6710
  }
6632
6711
 
@@ -6635,7 +6714,7 @@ class FigFillPicker extends HTMLElement {
6635
6714
  new CustomEvent("change", {
6636
6715
  bubbles: true,
6637
6716
  detail: this.value,
6638
- }),
6717
+ })
6639
6718
  );
6640
6719
  }
6641
6720
 
@@ -6707,7 +6786,7 @@ class FigFillPicker extends HTMLElement {
6707
6786
  this.#opacitySlider.setAttribute("value", this.#color.a * 100);
6708
6787
  this.#opacitySlider.setAttribute(
6709
6788
  "color",
6710
- this.#hsvToHex(this.#color),
6789
+ this.#hsvToHex(this.#color)
6711
6790
  );
6712
6791
  }
6713
6792
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "2.11.0",
3
+ "version": "2.12.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",