@rogieking/figui3 2.11.1 → 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 +195 -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() {
@@ -4948,6 +5012,7 @@ class FigInputAngle extends HTMLElement {
4948
5012
  if (this.angleInput) {
4949
5013
  this.angleInput.setAttribute("value", this.angle.toFixed(this.precision));
4950
5014
  }
5015
+ this.#updateRotationDisplay();
4951
5016
  }
4952
5017
 
4953
5018
  attributeChangedCallback(name, oldValue, newValue) {
@@ -4995,6 +5060,14 @@ class FigInputAngle extends HTMLElement {
4995
5060
  this.#syncHandlePosition();
4996
5061
  }
4997
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;
4998
5071
  }
4999
5072
  }
5000
5073
  }
@@ -5095,7 +5168,7 @@ class FigLayer extends HTMLElement {
5095
5168
  new CustomEvent("openchange", {
5096
5169
  detail: { open: value },
5097
5170
  bubbles: true,
5098
- }),
5171
+ })
5099
5172
  );
5100
5173
  }
5101
5174
  }
@@ -5117,7 +5190,7 @@ class FigLayer extends HTMLElement {
5117
5190
  new CustomEvent("visibilitychange", {
5118
5191
  detail: { visible: value },
5119
5192
  bubbles: true,
5120
- }),
5193
+ })
5121
5194
  );
5122
5195
  }
5123
5196
  }
@@ -5131,7 +5204,7 @@ class FigLayer extends HTMLElement {
5131
5204
  new CustomEvent("openchange", {
5132
5205
  detail: { open: isOpen },
5133
5206
  bubbles: true,
5134
- }),
5207
+ })
5135
5208
  );
5136
5209
  }
5137
5210
 
@@ -5141,7 +5214,7 @@ class FigLayer extends HTMLElement {
5141
5214
  new CustomEvent("visibilitychange", {
5142
5215
  detail: { visible: isVisible },
5143
5216
  bubbles: true,
5144
- }),
5217
+ })
5145
5218
  );
5146
5219
  }
5147
5220
  }
@@ -5305,7 +5378,7 @@ class FigFillPicker extends HTMLElement {
5305
5378
  bg = `url(${this.#image.url})`;
5306
5379
  const sizing = this.#getBackgroundSizing(
5307
5380
  this.#image.scaleMode,
5308
- this.#image.scale,
5381
+ this.#image.scale
5309
5382
  );
5310
5383
  bgSize = sizing.size;
5311
5384
  bgPosition = sizing.position;
@@ -5318,7 +5391,7 @@ class FigFillPicker extends HTMLElement {
5318
5391
  bg = `url(${this.#video.url})`;
5319
5392
  const sizing = this.#getBackgroundSizing(
5320
5393
  this.#video.scaleMode,
5321
- this.#video.scale,
5394
+ this.#video.scale
5322
5395
  );
5323
5396
  bgSize = sizing.size;
5324
5397
  bgPosition = sizing.position;
@@ -5467,7 +5540,9 @@ class FigFillPicker extends HTMLElement {
5467
5540
  ? `<span class="fig-fill-picker-type-label">${
5468
5541
  lockedMode.charAt(0).toUpperCase() + lockedMode.slice(1)
5469
5542
  }</span>`
5470
- : `<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
+ }">
5471
5546
  <option value="solid">Solid</option>
5472
5547
  <option value="gradient">Gradient</option>
5473
5548
  <option value="image">Image</option>
@@ -5587,7 +5662,7 @@ class FigFillPicker extends HTMLElement {
5587
5662
  <div class="fig-fill-picker-inputs">
5588
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>
5589
5664
  <fig-input-color class="fig-fill-picker-color-input" text="true" picker="false" value="${this.#hsvToHex(
5590
- this.#color,
5665
+ this.#color
5591
5666
  )}"></fig-input-color>
5592
5667
  </div>
5593
5668
  `;
@@ -5614,7 +5689,7 @@ class FigFillPicker extends HTMLElement {
5614
5689
  // Setup opacity slider
5615
5690
  if (showAlpha) {
5616
5691
  this.#opacitySlider = container.querySelector(
5617
- 'fig-slider[type="opacity"]',
5692
+ 'fig-slider[type="opacity"]'
5618
5693
  );
5619
5694
  this.#opacitySlider.addEventListener("input", (e) => {
5620
5695
  this.#color.a = parseFloat(e.target.value) / 100;
@@ -5714,7 +5789,7 @@ class FigFillPicker extends HTMLElement {
5714
5789
  this.#colorAreaHandle.style.top = `${y}px`;
5715
5790
  this.#colorAreaHandle.style.setProperty(
5716
5791
  "--picker-color",
5717
- this.#hsvToHex({ ...this.#color, a: 1 }),
5792
+ this.#hsvToHex({ ...this.#color, a: 1 })
5718
5793
  );
5719
5794
  }
5720
5795
 
@@ -5777,7 +5852,7 @@ class FigFillPicker extends HTMLElement {
5777
5852
  const hex = this.#hsvToHex(this.#color);
5778
5853
 
5779
5854
  const colorInput = this.#dialog.querySelector(
5780
- ".fig-fill-picker-color-input",
5855
+ ".fig-fill-picker-color-input"
5781
5856
  );
5782
5857
  if (colorInput) {
5783
5858
  colorInput.setAttribute("value", hex);
@@ -5844,7 +5919,7 @@ class FigFillPicker extends HTMLElement {
5844
5919
  #setupGradientEvents(container) {
5845
5920
  // Type dropdown
5846
5921
  const typeDropdown = container.querySelector(
5847
- ".fig-fill-picker-gradient-type",
5922
+ ".fig-fill-picker-gradient-type"
5848
5923
  );
5849
5924
  typeDropdown.addEventListener("change", (e) => {
5850
5925
  this.#gradient.type = e.target.value;
@@ -5855,7 +5930,7 @@ class FigFillPicker extends HTMLElement {
5855
5930
  // Angle input
5856
5931
  // Convert from fig-input-angle coordinates (0° = right) to CSS coordinates (0° = up)
5857
5932
  const angleInput = container.querySelector(
5858
- ".fig-fill-picker-gradient-angle",
5933
+ ".fig-fill-picker-gradient-angle"
5859
5934
  );
5860
5935
  angleInput.addEventListener("input", (e) => {
5861
5936
  const pickerAngle = parseFloat(e.target.value) || 0;
@@ -5914,10 +5989,10 @@ class FigFillPicker extends HTMLElement {
5914
5989
 
5915
5990
  // Show/hide angle vs center inputs
5916
5991
  const angleInput = container.querySelector(
5917
- ".fig-fill-picker-gradient-angle",
5992
+ ".fig-fill-picker-gradient-angle"
5918
5993
  );
5919
5994
  const centerInputs = container.querySelector(
5920
- ".fig-fill-picker-gradient-center",
5995
+ ".fig-fill-picker-gradient-center"
5921
5996
  );
5922
5997
 
5923
5998
  if (this.#gradient.type === "radial") {
@@ -5950,7 +6025,7 @@ class FigFillPicker extends HTMLElement {
5950
6025
  if (!this.#dialog) return;
5951
6026
 
5952
6027
  const list = this.#dialog.querySelector(
5953
- ".fig-fill-picker-gradient-stops-list",
6028
+ ".fig-fill-picker-gradient-stops-list"
5954
6029
  );
5955
6030
  if (!list) return;
5956
6031
 
@@ -5970,7 +6045,7 @@ class FigFillPicker extends HTMLElement {
5970
6045
  <span class="fig-mask-icon" style="--icon: var(--icon-minus)"></span>
5971
6046
  </fig-button>
5972
6047
  </div>
5973
- `,
6048
+ `
5974
6049
  )
5975
6050
  .join("");
5976
6051
 
@@ -6068,7 +6143,7 @@ class FigFillPicker extends HTMLElement {
6068
6143
 
6069
6144
  #setupImageEvents(container) {
6070
6145
  const scaleModeDropdown = container.querySelector(
6071
- ".fig-fill-picker-scale-mode",
6146
+ ".fig-fill-picker-scale-mode"
6072
6147
  );
6073
6148
  const scaleInput = container.querySelector(".fig-fill-picker-scale");
6074
6149
  const uploadBtn = container.querySelector(".fig-fill-picker-upload");
@@ -6110,7 +6185,7 @@ class FigFillPicker extends HTMLElement {
6110
6185
 
6111
6186
  // Drag and drop
6112
6187
  const previewArea = container.querySelector(
6113
- ".fig-fill-picker-media-preview",
6188
+ ".fig-fill-picker-media-preview"
6114
6189
  );
6115
6190
  previewArea.addEventListener("dragover", (e) => {
6116
6191
  e.preventDefault();
@@ -6216,7 +6291,7 @@ class FigFillPicker extends HTMLElement {
6216
6291
 
6217
6292
  #setupVideoEvents(container) {
6218
6293
  const scaleModeDropdown = container.querySelector(
6219
- ".fig-fill-picker-scale-mode",
6294
+ ".fig-fill-picker-scale-mode"
6220
6295
  );
6221
6296
  const uploadBtn = container.querySelector(".fig-fill-picker-upload");
6222
6297
  const fileInput = container.querySelector('input[type="file"]');
@@ -6235,7 +6310,7 @@ class FigFillPicker extends HTMLElement {
6235
6310
 
6236
6311
  // Drag and drop
6237
6312
  const previewArea = container.querySelector(
6238
- ".fig-fill-picker-media-preview",
6313
+ ".fig-fill-picker-media-preview"
6239
6314
  );
6240
6315
 
6241
6316
  fileInput.addEventListener("change", (e) => {
@@ -6304,10 +6379,10 @@ class FigFillPicker extends HTMLElement {
6304
6379
  const video = container.querySelector(".fig-fill-picker-webcam-video");
6305
6380
  const status = container.querySelector(".fig-fill-picker-webcam-status");
6306
6381
  const captureBtn = container.querySelector(
6307
- ".fig-fill-picker-webcam-capture",
6382
+ ".fig-fill-picker-webcam-capture"
6308
6383
  );
6309
6384
  const cameraSelect = container.querySelector(
6310
- ".fig-fill-picker-camera-select",
6385
+ ".fig-fill-picker-camera-select"
6311
6386
  );
6312
6387
 
6313
6388
  const startWebcam = async (deviceId = null) => {
@@ -6320,8 +6395,9 @@ class FigFillPicker extends HTMLElement {
6320
6395
  this.#webcam.stream.getTracks().forEach((track) => track.stop());
6321
6396
  }
6322
6397
 
6323
- this.#webcam.stream =
6324
- await navigator.mediaDevices.getUserMedia(constraints);
6398
+ this.#webcam.stream = await navigator.mediaDevices.getUserMedia(
6399
+ constraints
6400
+ );
6325
6401
  video.srcObject = this.#webcam.stream;
6326
6402
  video.style.display = "block";
6327
6403
  status.style.display = "none";
@@ -6337,7 +6413,7 @@ class FigFillPicker extends HTMLElement {
6337
6413
  (cam, i) =>
6338
6414
  `<option value="${cam.deviceId}">${
6339
6415
  cam.label || `Camera ${i + 1}`
6340
- }</option>`,
6416
+ }</option>`
6341
6417
  )
6342
6418
  .join("");
6343
6419
  }
@@ -6629,7 +6705,7 @@ class FigFillPicker extends HTMLElement {
6629
6705
  new CustomEvent("input", {
6630
6706
  bubbles: true,
6631
6707
  detail: this.value,
6632
- }),
6708
+ })
6633
6709
  );
6634
6710
  }
6635
6711
 
@@ -6638,7 +6714,7 @@ class FigFillPicker extends HTMLElement {
6638
6714
  new CustomEvent("change", {
6639
6715
  bubbles: true,
6640
6716
  detail: this.value,
6641
- }),
6717
+ })
6642
6718
  );
6643
6719
  }
6644
6720
 
@@ -6710,7 +6786,7 @@ class FigFillPicker extends HTMLElement {
6710
6786
  this.#opacitySlider.setAttribute("value", this.#color.a * 100);
6711
6787
  this.#opacitySlider.setAttribute(
6712
6788
  "color",
6713
- this.#hsvToHex(this.#color),
6789
+ this.#hsvToHex(this.#color)
6714
6790
  );
6715
6791
  }
6716
6792
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "2.11.1",
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",