@rogieking/figui3 6.9.8 → 6.10.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-lab.css CHANGED
@@ -9,23 +9,37 @@
9
9
  */
10
10
 
11
11
  fig-field-slider {
12
- --fig-field-slider-elastic-distance: var(--spacer-2);
12
+ --fig-field-slider-elastic-distance: calc(var(--spacer-2) + 0.25rem);
13
13
  --fig-field-slider-elastic-size: 0px;
14
- --fig-field-slider-elastic-position-offset: 0px;
15
- --fig-field-slider-elastic-transition: 0.1s ease-out;
14
+ --fig-field-slider-elastic-scale: 1;
15
+ --fig-field-slider-elastic-origin: left center;
16
+ --fig-field-slider-elastic-transition: 0.18s cubic-bezier(0.2, 1.45, 0.35, 1);
16
17
  --fig-field-slider-height: 2rem;
18
+ --fig-field-slider-vertical-padding: var(--spacer-1);
19
+ --fig-field-slider-horizontal-padding: var(--spacer-3);
17
20
  --figma-focus-outline-offset: 1px;
18
21
  --fig-field-slider-progress-shadow: inset 0px 0.75px 0px 0px light-dark(transparent, rgba(255, 255, 255, 0.1));
19
22
  --field-text-shadow: 0 1px 1.5px
20
23
  light-dark(rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2));
24
+ --fig-field-slider-fig-slider-height: calc(var(--fig-field-slider-height) - var(--fig-field-slider-vertical-padding) * 2);
25
+ --fig-field-slider-fig-slider-thumb-height: calc(var(--fig-field-slider-fig-slider-height) - var(--spacer-2-5));
26
+
21
27
  display: block;
22
28
  position: relative;
23
29
  width: 100%;
24
30
  height: var(--fig-field-slider-height);
25
31
  border-radius: var(--radius-medium);
26
- transition:
27
- width var(--fig-field-slider-elastic-transition),
28
- transform var(--fig-field-slider-elastic-transition);
32
+ transform-origin: var(--fig-field-slider-elastic-origin);
33
+ transition: transform var(--fig-field-slider-elastic-transition);
34
+ padding-top: var(--fig-field-slider-vertical-padding);
35
+ padding-bottom: var(--fig-field-slider-vertical-padding);
36
+ padding-left: var(--fig-field-slider-horizontal-padding);
37
+ padding-right: var(--fig-field-slider-horizontal-padding);
38
+
39
+ &[size="large"]{
40
+ --fig-field-slider-vertical-padding: 0px;
41
+ --fig-field-slider-fig-slider-thumb-height: calc(var(--fig-field-slider-fig-slider-height) - var(--spacer-3));
42
+ }
29
43
 
30
44
  label {
31
45
  pointer-events: none;
@@ -57,7 +71,7 @@ fig-field-slider {
57
71
 
58
72
  fig-field:not([direction="vertical"]) {
59
73
  position: relative;
60
- padding-left: var(--spacer-2-5);
74
+ padding-left: var(--spacer-2);
61
75
  display: flex;
62
76
  height: 100%;
63
77
 
@@ -83,8 +97,8 @@ fig-field-slider {
83
97
  inset: 0;
84
98
  padding: 0;
85
99
  background-color: transparent;
86
- --slider-height: 2rem;
87
- --slider-field-height: 2rem;
100
+ --slider-height: var(--fig-field-slider-fig-slider-height);
101
+ --slider-field-height: calc(var(--fig-field-slider-height) - var(--fig-field-slider-vertical-padding) * 2);
88
102
  --slider-thumb-width: var(--spacer-1);
89
103
  --slider-thumb-color: light-dark(
90
104
  var(--figma-color-text-secondary),
@@ -94,7 +108,7 @@ fig-field-slider {
94
108
  var(--spacer-2) + (var(--spacer-3) - var(--spacer-2)) *
95
109
  min(var(--slider-complete), calc(1 - var(--slider-complete))) * 2
96
110
  );*/
97
- --slider-thumb-height: calc(var(--slider-height) - var(--spacer-3));
111
+ --slider-thumb-height: var(--fig-field-slider-fig-slider-thumb-height);
98
112
  --slider-border-radius: var(--radius-medium);
99
113
  --slider-tick-width: 0.125rem;
100
114
  --slider-tick-height: var(--spacer-2);
@@ -317,8 +331,7 @@ fig-field-slider {
317
331
  }
318
332
 
319
333
  &[data-elastic-dragging="true"] {
320
- width: calc(100% + var(--fig-field-slider-elastic-size));
321
- transform: translateX(var(--fig-field-slider-elastic-position-offset));
334
+ transform: scaleX(var(--fig-field-slider-elastic-scale));
322
335
  transition: none;
323
336
  }
324
337
  }
@@ -540,10 +553,18 @@ fig-input-oscillator {
540
553
 
541
554
  .fig-input-oscillator-svg-container {
542
555
  border-radius: var(--radius-medium);
543
- background: var(--figma-color-bg-secondary);
544
- padding: var(--spacer-3) var(--spacer-2);
556
+ padding: var(--spacer-3) var(--spacer-5);
545
557
  margin-bottom: var(--spacer-2);
546
558
  overflow: hidden;
559
+ position: relative;
560
+
561
+ &::before{
562
+ position: absolute;
563
+ inset: 0 var(--spacer-3);
564
+ content: "";
565
+ background: var(--figma-color-bg-secondary);
566
+ border-radius: var(--radius-medium);
567
+ }
547
568
 
548
569
  &:is(:hover, :active, :focus, :focus-visible, :focus-within) {
549
570
  overflow: visible;
@@ -558,6 +579,8 @@ fig-input-oscillator {
558
579
  width: 100%;
559
580
  aspect-ratio: var(--aspect-ratio);
560
581
  overflow: visible;
582
+ position: relative;
583
+ z-index: 1;
561
584
 
562
585
  .fig-input-oscillator-handle{
563
586
  opacity: 0;
@@ -616,56 +639,31 @@ fig-input-oscillator {
616
639
  .fig-input-oscillator-waves {
617
640
  display: flex;
618
641
  flex-direction: column;
619
- gap: var(--spacer-2);
642
+ gap: 0;
620
643
  }
621
644
 
622
645
  .fig-input-oscillator-wave {
623
- padding: 0;
624
-
625
- > .fig-input-oscillator-wave-summary {
626
- min-height: var(--control-height);
627
- padding: 0;
628
-
629
- > label {
630
- display: inline-flex;
631
- align-items: center;
632
- gap: var(--spacer-1);
633
- min-width: 0;
646
+ margin-bottom: 0;
634
647
 
635
- > svg {
636
- flex: 0 0 auto;
637
- }
638
- }
639
-
640
- > .fig-input-oscillator-remove-button {
641
- margin-left: auto;
648
+ > fig-header {
649
+ .fig-input-oscillator-remove-button,
650
+ .fig-input-oscillator-add-type-button {
651
+ flex-shrink: 0;
642
652
  }
643
653
  }
644
654
  }
645
655
 
646
656
  .fig-input-oscillator-field {
647
- --fig-field-left-padding: 0;
648
- --fig-field-right-padding: 0;
649
-
650
- > fig-slider {
651
- min-width: 0;
652
- }
653
- }
654
-
655
- .fig-input-oscillator-add-wave {
656
- display: grid;
657
- grid-template-columns: auto;
658
- justify-content: end;
659
657
  width: 100%;
658
+ }
660
659
 
661
- > fig-button[type="select"] {
662
- > fig-dropdown,
663
- > fig-dropdown > select {
664
- width: 100%;
665
- height: 100%;
666
- min-width: 100%;
667
- padding: 0;
668
- }
660
+ .fig-input-oscillator-add-type-button {
661
+ > fig-dropdown,
662
+ > fig-dropdown > select {
663
+ width: 100%;
664
+ height: 100%;
665
+ min-width: 100%;
666
+ padding: 0;
669
667
  }
670
668
  }
671
669
 
package/fig-lab.js CHANGED
@@ -25,6 +25,8 @@ class FigFieldSlider extends HTMLElement {
25
25
  #isElasticTracking = false;
26
26
  #elasticMaxPx = 0;
27
27
  #elasticRangeRect = null;
28
+ #elasticHostWidth = 0;
29
+ #elasticPointerId = null;
28
30
  #boundHandleSliderInput = null;
29
31
  #boundHandleSliderChange = null;
30
32
  #boundHandleElasticPointerDown = this.#handleElasticPointerDown.bind(this);
@@ -38,6 +40,12 @@ class FigFieldSlider extends HTMLElement {
38
40
  "color",
39
41
  "text",
40
42
  "full",
43
+ "elastic",
44
+ "size",
45
+ "name",
46
+ "class",
47
+ "data-wave-index",
48
+ "data-active",
41
49
  "data-elastic-dragging",
42
50
  "style",
43
51
  ]);
@@ -111,6 +119,7 @@ class FigFieldSlider extends HTMLElement {
111
119
  this.#focusSyncFrame = 0;
112
120
  }
113
121
  this.#clearPendingClick();
122
+ this.#stopElasticTracking();
114
123
  this.#resetElasticPull();
115
124
  this.#unbindRangeInput();
116
125
  this.#unbindSliderEvents();
@@ -323,19 +332,25 @@ class FigFieldSlider extends HTMLElement {
323
332
 
324
333
  #handleElasticPointerDown(event) {
325
334
  if (event.button !== 0 || this.hasAttribute("disabled")) return;
335
+ if (this.getAttribute("elastic") === "false") return;
326
336
  if (event.target?.closest?.("fig-input-number")) return;
327
337
  const rangeInput =
328
338
  this.#slider?.querySelector('input[type="range"]') ?? this.#rangeInput;
329
339
  if (!rangeInput) return;
340
+ this.#stopElasticTracking();
341
+ this.#resetElasticPull();
330
342
  this.#rangeInput = rangeInput;
331
343
  this.#isElasticTracking = true;
344
+ this.#elasticPointerId = event.pointerId;
332
345
  this.#elasticMaxPx = this.#readElasticDistance();
333
346
  const rect = rangeInput.getBoundingClientRect();
347
+ const hostRect = this.getBoundingClientRect();
334
348
  this.#elasticRangeRect = {
335
349
  left: rect.left,
336
350
  right: rect.right,
337
351
  width: rect.width,
338
352
  };
353
+ this.#elasticHostWidth = hostRect.width;
339
354
  window.addEventListener("pointermove", this.#boundHandleElasticPointerMove, {
340
355
  passive: true,
341
356
  });
@@ -345,19 +360,40 @@ class FigFieldSlider extends HTMLElement {
345
360
  window.addEventListener("pointercancel", this.#boundHandleElasticPointerEnd, {
346
361
  once: true,
347
362
  });
363
+ window.addEventListener("blur", this.#boundHandleElasticPointerEnd, {
364
+ once: true,
365
+ });
348
366
  }
349
367
 
350
368
  #handleElasticPointerMove(event) {
351
369
  if (!this.#isElasticTracking) return;
370
+ if (event.pointerId !== this.#elasticPointerId) return;
371
+ if (event.buttons === 0) {
372
+ this.#handleElasticPointerEnd(event);
373
+ return;
374
+ }
352
375
  this.#updateElasticPull(event.clientX);
353
376
  }
354
377
 
355
- #handleElasticPointerEnd() {
378
+ #handleElasticPointerEnd(event) {
379
+ if (
380
+ event?.pointerId !== undefined &&
381
+ this.#elasticPointerId !== null &&
382
+ event.pointerId !== this.#elasticPointerId
383
+ ) {
384
+ return;
385
+ }
386
+ this.#stopElasticTracking();
387
+ this.#resetElasticPull();
388
+ }
389
+
390
+ #stopElasticTracking() {
356
391
  window.removeEventListener("pointermove", this.#boundHandleElasticPointerMove);
357
392
  window.removeEventListener("pointerup", this.#boundHandleElasticPointerEnd);
358
393
  window.removeEventListener("pointercancel", this.#boundHandleElasticPointerEnd);
394
+ window.removeEventListener("blur", this.#boundHandleElasticPointerEnd);
359
395
  this.#isElasticTracking = false;
360
- this.#resetElasticPull();
396
+ this.#elasticPointerId = null;
361
397
  }
362
398
 
363
399
  #handleContextMenu(event) {
@@ -445,11 +481,15 @@ class FigFieldSlider extends HTMLElement {
445
481
  Math.min(this.#elasticMaxPx, overshoot * 0.5),
446
482
  );
447
483
  const stretch = Math.abs(offset);
484
+ const scale = this.#elasticHostWidth
485
+ ? (this.#elasticHostWidth + stretch) / this.#elasticHostWidth
486
+ : 1;
448
487
  this.dataset.elasticDragging = "true";
449
488
  this.style.setProperty("--fig-field-slider-elastic-size", `${stretch}px`);
489
+ this.style.setProperty("--fig-field-slider-elastic-scale", `${scale}`);
450
490
  this.style.setProperty(
451
- "--fig-field-slider-elastic-position-offset",
452
- offset < 0 ? `${-stretch / 2}px` : `${stretch / 2}px`,
491
+ "--fig-field-slider-elastic-origin",
492
+ offset < 0 ? "right center" : "left center",
453
493
  );
454
494
  }
455
495
 
@@ -457,12 +497,14 @@ class FigFieldSlider extends HTMLElement {
457
497
  this.#clearElasticPull();
458
498
  this.#elasticMaxPx = 0;
459
499
  this.#elasticRangeRect = null;
500
+ this.#elasticHostWidth = 0;
501
+ this.#elasticPointerId = null;
460
502
  }
461
503
 
462
504
  #clearElasticPull() {
463
505
  this.removeAttribute("data-elastic-dragging");
464
506
  this.style.removeProperty("--fig-field-slider-elastic-size");
465
- this.style.removeProperty("--fig-field-slider-elastic-position-offset");
507
+ this.style.removeProperty("--fig-field-slider-elastic-scale");
466
508
  }
467
509
 
468
510
  #valueFromPointer(event) {
@@ -558,7 +600,7 @@ class FigFieldSlider extends HTMLElement {
558
600
  }
559
601
 
560
602
  #forwardSliderEvent(type, event) {
561
- event.stopPropagation();
603
+ event.stopImmediatePropagation();
562
604
  if (type === "change") {
563
605
  this.#resetElasticPull();
564
606
  }
@@ -1797,11 +1839,13 @@ class FigInputOscillator extends HTMLElement {
1797
1839
  #bounds = null;
1798
1840
  #handleAmplitude = null;
1799
1841
  #handleFrequency = null;
1800
- #addTypeControl = null;
1801
1842
  #fields = [];
1802
1843
  #typeControls = [];
1803
1844
  #waveRows = [];
1845
+ #waveGroups = [];
1846
+ #expandedWaveIndices = new Set();
1804
1847
  #resizeObserver = null;
1848
+ #activeFieldInput = null;
1805
1849
 
1806
1850
  static TYPES = [
1807
1851
  { name: "Wave", value: "sine" },
@@ -2064,12 +2108,6 @@ class FigInputOscillator extends HTMLElement {
2064
2108
  #getWaveControlsHTML(disabled) {
2065
2109
  return `<div class="fig-input-oscillator-waves">
2066
2110
  ${this.#waves.map((wave, index) => this.#getWaveRowHTML(wave, index, disabled)).join("")}
2067
- <hstack class="fig-input-oscillator-add-wave">
2068
- <fig-button class="fig-input-oscillator-add-type-button" type="select" variant="ghost" icon aria-label="Choose waveform type"${disabled}>
2069
- <fig-icon name="settings"></fig-icon>
2070
- ${this.#getWaveTypeDropdownHTML("fig-input-oscillator-add-type", "sine", disabled)}
2071
- </fig-button>
2072
- </hstack>
2073
2111
  </div>`;
2074
2112
  }
2075
2113
 
@@ -2077,18 +2115,27 @@ class FigInputOscillator extends HTMLElement {
2077
2115
  const removeDisabled = disabled || this.#waves.length <= 1 ? " disabled" : "";
2078
2116
  const active = index === this.#activeWaveIndex ? " data-active" : "";
2079
2117
  const label = FigInputOscillator.#labelForType(wave.type);
2080
- return `<details class="fig-input-oscillator-wave" data-wave-index="${index}" open>
2081
- <summary class="fig-input-oscillator-wave-summary">
2082
- <label>${FigInputOscillator.waveIcon(wave.type, 24)}<span>${label}</span></label>
2083
- <fig-button class="fig-input-oscillator-remove-button" variant="ghost" icon data-wave-index="${index}" aria-label="Remove waveform"${removeDisabled}><fig-icon name="minus"></fig-icon></fig-button>
2084
- </summary>
2118
+ const open = this.#expandedWaveIndices.has(index) ? ' open="true"' : ' open="false"';
2119
+ return `<fig-group class="fig-input-oscillator-wave" collapsible borderless compact="true"${open} data-wave-index="${index}">
2120
+ <fig-header borderless>
2121
+ <h3>${label}</h3>
2122
+ <fig-tooltip text="Remove form">
2123
+ <fig-button class="fig-input-oscillator-remove-button" variant="ghost" icon data-wave-index="${index}" aria-label="Remove form"${removeDisabled}><fig-icon name="minus"></fig-icon></fig-button>
2124
+ </fig-tooltip>
2125
+ <fig-tooltip text="Add form">
2126
+ <fig-button class="fig-input-oscillator-add-type-button" type="select" variant="ghost" icon data-wave-index="${index}" aria-label="Add form"${disabled}>
2127
+ <fig-icon name="add"></fig-icon>
2128
+ ${this.#getWaveTypeDropdownHTML("fig-input-oscillator-add-type", "sine", disabled, index)}
2129
+ </fig-button>
2130
+ </fig-tooltip>
2131
+ </fig-header>
2085
2132
  <div class="fig-input-oscillator-fields" data-wave-index="${index}"${active}>
2086
2133
  ${this.#getNumberFieldHTML(index, "frequency", "Frequency", 0.1, 16, 0.1, "")}
2087
2134
  ${this.#getNumberFieldHTML(index, "amplitude", "Amplitude", -4, 4, 0.1, "")}
2088
2135
  ${this.#getNumberFieldHTML(index, "phase", "Phase", -360, 360, 1, "°")}
2089
2136
  ${this.#getNumberFieldHTML(index, "offset", "Offset", -4, 4, 0.1, "")}
2090
2137
  </div>
2091
- </details>`;
2138
+ </fig-group>`;
2092
2139
  }
2093
2140
 
2094
2141
  #getWaveTypeDropdownHTML(className, value, disabled, index = null) {
@@ -2099,9 +2146,9 @@ class FigInputOscillator extends HTMLElement {
2099
2146
  <label>${type.name}</label>
2100
2147
  </option>`;
2101
2148
  }).join("");
2102
- const indexAttr = index === null ? "" : ` data-wave-index="${index}"`;
2103
- const dropdownAttr = index === null ? ' type="dropdown" label="Choose waveform type"' : "";
2104
- return `<fig-dropdown class="${className}" value="${value}" experimental="modern"${dropdownAttr}${indexAttr}${disabled}>${options}</fig-dropdown>`;
2149
+ const indexAttr =
2150
+ index === null ? "" : ` data-wave-index="${FigInputOscillator.#escapeAttribute(String(index))}"`;
2151
+ return `<fig-dropdown class="${className}" value="${value}" experimental="modern" type="dropdown" label="Add form"${indexAttr}${disabled}>${options}</fig-dropdown>`;
2105
2152
  }
2106
2153
 
2107
2154
  #getNumberFieldHTML(index, name, label, min, max, step, units) {
@@ -2110,11 +2157,7 @@ class FigInputOscillator extends HTMLElement {
2110
2157
  ? ` units="${FigInputOscillator.#escapeAttribute(units)}"`
2111
2158
  : "";
2112
2159
  const wave = this.#waves[index] || FigInputOscillator.#defaultWave();
2113
- return `<fig-field class="fig-input-oscillator-field" direction="horizontal" data-wave-index="${index}">
2114
- <label>${label}</label>
2115
- <fig-slider name="${name}" data-wave-index="${index}" value="${this.#round(wave[name])}" min="${min}" max="${max}" step="${step}" precision="${this.#precision}" text="true"${unitsAttr}${disabled}></fig-slider>
2116
- </fig-field>
2117
- `;
2160
+ return `<fig-field-slider class="fig-input-oscillator-field" label="${label}" direction="horizontal" name="${name}" data-wave-index="${index}" value="${this.#round(wave[name])}" min="${min}" max="${max}" step="${step}" precision="${this.#precision}" elastic="false"${unitsAttr}${disabled}></fig-field-slider>`;
2118
2161
  }
2119
2162
 
2120
2163
  #cacheRefs() {
@@ -2125,14 +2168,34 @@ class FigInputOscillator extends HTMLElement {
2125
2168
  this.#bounds = this.querySelector(".fig-input-oscillator-bounds");
2126
2169
  this.#handleAmplitude = this.querySelector('[data-handle="amplitude"]');
2127
2170
  this.#handleFrequency = this.querySelector('[data-handle="frequency"]');
2128
- this.#addTypeControl = this.querySelector(".fig-input-oscillator-add-type");
2129
2171
  this.#typeControls = Array.from(
2130
2172
  this.querySelectorAll(".fig-input-oscillator-wave-type"),
2131
2173
  );
2132
- this.#fields = Array.from(this.querySelectorAll("fig-slider[name]"));
2133
- this.#waveRows = Array.from(this.querySelectorAll("[data-wave-index]")).filter(
2134
- (row) => row.classList.contains("fig-input-oscillator-field"),
2174
+ this.#fields = Array.from(this.querySelectorAll("fig-field-slider[name]"));
2175
+ this.#waveGroups = Array.from(
2176
+ this.querySelectorAll("fig-group.fig-input-oscillator-wave"),
2135
2177
  );
2178
+ this.#waveRows = Array.from(
2179
+ this.querySelectorAll(".fig-input-oscillator-fields"),
2180
+ );
2181
+ }
2182
+
2183
+ #reindexExpandedWaves(removedIndex) {
2184
+ const nextExpanded = new Set();
2185
+ for (const index of this.#expandedWaveIndices) {
2186
+ if (index < removedIndex) nextExpanded.add(index);
2187
+ else if (index > removedIndex) nextExpanded.add(index - 1);
2188
+ }
2189
+ this.#expandedWaveIndices = nextExpanded;
2190
+ }
2191
+
2192
+ #reindexExpandedWavesAfterInsert(insertIndex) {
2193
+ const nextExpanded = new Set();
2194
+ for (const index of this.#expandedWaveIndices) {
2195
+ nextExpanded.add(index >= insertIndex ? index + 1 : index);
2196
+ }
2197
+ nextExpanded.add(insertIndex);
2198
+ this.#expandedWaveIndices = nextExpanded;
2136
2199
  }
2137
2200
 
2138
2201
  #setupResizeObserver() {
@@ -2308,12 +2371,17 @@ class FigInputOscillator extends HTMLElement {
2308
2371
  for (const field of this.#fields) {
2309
2372
  field.addEventListener("input", (event) => {
2310
2373
  event.stopPropagation();
2311
- this.#applyFieldValue(
2312
- this.#indexFromElement(field),
2313
- field.getAttribute("name"),
2314
- event.detail ?? event.currentTarget?.value ?? event.target?.value,
2315
- "input",
2316
- );
2374
+ this.#activeFieldInput = field;
2375
+ try {
2376
+ this.#applyFieldValue(
2377
+ this.#indexFromElement(field),
2378
+ field.getAttribute("name"),
2379
+ event.detail ?? event.currentTarget?.value ?? event.target?.value,
2380
+ "input",
2381
+ );
2382
+ } finally {
2383
+ this.#activeFieldInput = null;
2384
+ }
2317
2385
  });
2318
2386
  field.addEventListener("change", (event) => {
2319
2387
  event.stopPropagation();
@@ -2326,31 +2394,64 @@ class FigInputOscillator extends HTMLElement {
2326
2394
  });
2327
2395
  }
2328
2396
 
2329
- for (const row of this.#waveRows) {
2330
- row.addEventListener("pointerdown", () => {
2331
- this.#setActiveWave(this.#indexFromElement(row));
2397
+ for (const group of this.#waveGroups) {
2398
+ group.addEventListener("pointerdown", () => {
2399
+ this.#setActiveWave(this.#indexFromElement(group));
2400
+ });
2401
+ group.addEventListener("focusin", () => {
2402
+ this.#setActiveWave(this.#indexFromElement(group));
2332
2403
  });
2333
- row.addEventListener("focusin", () => {
2334
- this.#setActiveWave(this.#indexFromElement(row));
2404
+ group.addEventListener("openchange", (event) => {
2405
+ const index = this.#indexFromElement(group);
2406
+ if (event.detail?.open) {
2407
+ this.#expandedWaveIndices.add(index);
2408
+ } else {
2409
+ this.#expandedWaveIndices.delete(index);
2410
+ }
2335
2411
  });
2336
2412
  }
2337
2413
 
2338
- this.#addTypeControl?.addEventListener("change", (event) => {
2339
- if (this.#isDisabled()) return;
2340
- if (event.target !== this.#addTypeControl) return;
2341
- const type = this.#normalizeType(event.detail ?? this.#addTypeControl?.value);
2342
- this.#waves.push(FigInputOscillator.#defaultWave(type));
2343
- this.#activeWaveIndex = this.#waves.length - 1;
2344
- this.#render();
2345
- this.#emit("input");
2346
- this.#emit("change");
2347
- });
2414
+ for (const control of this.querySelectorAll(".fig-input-oscillator-add-type")) {
2415
+ const stopHeaderToggle = (event) => {
2416
+ event.stopPropagation();
2417
+ };
2418
+ control.addEventListener("pointerdown", stopHeaderToggle);
2419
+ control.addEventListener("click", stopHeaderToggle);
2420
+ control.addEventListener("change", (event) => {
2421
+ if (event.target !== control) return;
2422
+ if (this.#isDisabled()) return;
2423
+ const insertAfter = this.#indexFromElement(control);
2424
+ const insertIndex = insertAfter + 1;
2425
+ const type = this.#normalizeType(event.detail ?? control.value);
2426
+ this.#waves.splice(insertIndex, 0, FigInputOscillator.#defaultWave(type));
2427
+ this.#reindexExpandedWavesAfterInsert(insertIndex);
2428
+ this.#activeWaveIndex = insertIndex;
2429
+ this.#render();
2430
+ this.#emit("input");
2431
+ this.#emit("change");
2432
+ });
2433
+ }
2434
+
2435
+ for (const button of this.querySelectorAll(".fig-input-oscillator-add-type-button")) {
2436
+ const stopHeaderToggle = (event) => {
2437
+ event.stopPropagation();
2438
+ };
2439
+ button.addEventListener("pointerdown", stopHeaderToggle);
2440
+ button.addEventListener("click", stopHeaderToggle);
2441
+ button.closest("fig-tooltip")?.addEventListener("pointerdown", stopHeaderToggle);
2442
+ button.closest("fig-tooltip")?.addEventListener("click", stopHeaderToggle);
2443
+ }
2348
2444
 
2349
2445
  for (const button of this.querySelectorAll(".fig-input-oscillator-remove-button")) {
2350
- button.addEventListener("click", () => {
2446
+ button.addEventListener("pointerdown", (event) => {
2447
+ event.stopPropagation();
2448
+ });
2449
+ button.addEventListener("click", (event) => {
2450
+ event.stopPropagation();
2351
2451
  if (this.#isDisabled() || this.#waves.length <= 1) return;
2352
2452
  const index = this.#indexFromElement(button);
2353
2453
  this.#waves.splice(index, 1);
2454
+ this.#reindexExpandedWaves(index);
2354
2455
  this.#activeWaveIndex = Math.min(this.#activeWaveIndex, this.#waves.length - 1);
2355
2456
  this.#render();
2356
2457
  this.#emit("input");
@@ -2436,7 +2537,7 @@ class FigInputOscillator extends HTMLElement {
2436
2537
  ...this.#waves[index],
2437
2538
  [name]: next,
2438
2539
  });
2439
- this.#syncUI();
2540
+ this.#syncUI({ skipFieldSync: eventType == "input" });
2440
2541
  this.#emit(eventType);
2441
2542
  }
2442
2543
 
@@ -2548,9 +2649,9 @@ class FigInputOscillator extends HTMLElement {
2548
2649
  );
2549
2650
  }
2550
2651
 
2551
- #syncUI() {
2652
+ #syncUI({ skipFieldSync = false } = {}) {
2552
2653
  this.#syncTypeControls();
2553
- this.#syncFields();
2654
+ if (!skipFieldSync) this.#syncFields();
2554
2655
  this.#syncActiveWave();
2555
2656
  this.#updateWaveform();
2556
2657
  }
@@ -2564,9 +2665,16 @@ class FigInputOscillator extends HTMLElement {
2564
2665
 
2565
2666
  #syncFields() {
2566
2667
  for (const field of this.#fields) {
2668
+ if (field === this.#activeFieldInput) continue;
2567
2669
  const index = this.#indexFromElement(field);
2568
2670
  const name = field.getAttribute("name");
2569
- field.setAttribute("value", this.#round(this.#waves[index]?.[name] ?? 0));
2671
+ const next = this.#round(this.#waves[index]?.[name] ?? 0);
2672
+ const slider = field.querySelector("fig-slider");
2673
+ if (slider) {
2674
+ slider.value = next;
2675
+ } else {
2676
+ field.setAttribute("value", String(next));
2677
+ }
2570
2678
  }
2571
2679
  }
2572
2680
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "6.9.8",
3
+ "version": "6.10.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",