@rogieking/figui3 9.0.1 → 9.0.3

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-editor.css CHANGED
@@ -686,6 +686,10 @@ fig-select {
686
686
  color: var(--figma-color-text);
687
687
  box-shadow: inset 0 0 0 1px var(--figma-color-border);
688
688
 
689
+ &[size="large"] {
690
+ height: var(--spacer-5);
691
+ }
692
+
689
693
  &[full]:not([full="false"]) {
690
694
  display: flex;
691
695
  width: 100%;
package/fig-editor.js CHANGED
@@ -494,6 +494,7 @@ figEditorDefineElement("fig-select-options", FigSelectOptions);
494
494
  /**
495
495
  * A dropdown-styled select.
496
496
  * @attr {string} variant - Visual style. Use `ghost` for a borderless control.
497
+ * @attr {string} size - Control size. Use `large` for a 32px-tall control.
497
498
  */
498
499
  class FigSelect extends HTMLElement {
499
500
  #button = null;
@@ -537,6 +538,7 @@ class FigSelect extends HTMLElement {
537
538
  "closedby",
538
539
  "open",
539
540
  "variant",
541
+ "size",
540
542
  "aria-label",
541
543
  ];
542
544
  }
@@ -564,6 +566,7 @@ class FigSelect extends HTMLElement {
564
566
  this.#ensurePanelSlotAttrs();
565
567
  this.#syncOptionsFromAttribute();
566
568
  this.#syncDisabled();
569
+ this.#syncSize();
567
570
  this.#syncPopupAttrs();
568
571
  this.#syncValue();
569
572
  this.#setupListeners();
@@ -593,6 +596,10 @@ class FigSelect extends HTMLElement {
593
596
  this.#syncDisabled();
594
597
  return;
595
598
  }
599
+ if (name === "size") {
600
+ this.#syncSize();
601
+ return;
602
+ }
596
603
  if (name === "open") {
597
604
  if (newValue === null || newValue === "false") this.#closeList();
598
605
  else this.#openList();
@@ -611,6 +618,13 @@ class FigSelect extends HTMLElement {
611
618
  this.#button?.blur();
612
619
  }
613
620
 
621
+ #syncSize() {
622
+ if (!this.#button) return;
623
+ const size = this.getAttribute("size");
624
+ if (size) this.#button.setAttribute("size", size);
625
+ else this.#button.removeAttribute("size");
626
+ }
627
+
614
628
  #isMenuChild(node) {
615
629
  return (
616
630
  node?.nodeType === 1 &&
package/fig.js CHANGED
@@ -906,6 +906,7 @@ figDefineElement("fig-button", FigButton);
906
906
  * @attr {string} type - The dropdown type: "select" (default) or "dropdown"
907
907
  * @attr {string} value - The currently selected value
908
908
  * @attr {string} variant - Visual style. Use `ghost` for a borderless control.
909
+ * @attr {string} size - Control size. Use `large` for a 32px-tall control.
909
910
  */
910
911
  class FigDropdown extends HTMLElement {
911
912
  #label = "Menu";
@@ -1104,7 +1105,7 @@ class FigDropdown extends HTMLElement {
1104
1105
  this.setAttribute("value", value);
1105
1106
  }
1106
1107
  static get observedAttributes() {
1107
- return ["value", "type", "label", "disabled", "variant"];
1108
+ return ["value", "type", "label", "disabled", "variant", "size"];
1108
1109
  }
1109
1110
  #syncDisabled() {
1110
1111
  const disabled =
@@ -14054,15 +14055,18 @@ figDefineElement("fig-input-file", FigInputFile);
14054
14055
 
14055
14056
  /**
14056
14057
  * A bezier / spring easing curve editor with draggable control points.
14057
- * @attr {string} value - Bezier: "0.42, 0, 0.58, 1" or Spring: "spring(200, 15, 1)"
14058
+ * @attr {string} value - Bezier/spring shorthand or a serialized easing object
14058
14059
  * @attr {string} mode - Optional editor constraint: "bezier" or "spring".
14059
14060
  * @attr {number} precision - Decimal places for output values (default 2)
14060
14061
  * @attr {boolean} edit - Show the editor and custom preset options (default true; set "false" for presets only)
14062
+ * @attr {boolean} presets - Show the preset selector (default true)
14063
+ * @attr {boolean} text - Show the manual value input (default true)
14064
+ * @property {object|null} spring - Structured spring configuration when in spring mode
14061
14065
  */
14062
14066
  class FigEasingCurve extends HTMLElement {
14063
14067
  #cp1 = { x: 0.42, y: 0 };
14064
14068
  #cp2 = { x: 0.58, y: 1 };
14065
- #spring = { stiffness: 200, damping: 15, mass: 1 };
14069
+ #spring = { stiffness: 200, damping: 15, mass: 1, initialVelocity: 0 };
14066
14070
  #mode = "bezier";
14067
14071
  #precision = 2;
14068
14072
  #isDragging = null;
@@ -14155,7 +14159,15 @@ class FigEasingCurve extends HTMLElement {
14155
14159
  ];
14156
14160
 
14157
14161
  static get observedAttributes() {
14158
- return ["value", "mode", "precision", "aspect-ratio", "edit"];
14162
+ return [
14163
+ "value",
14164
+ "mode",
14165
+ "precision",
14166
+ "aspect-ratio",
14167
+ "edit",
14168
+ "presets",
14169
+ "text",
14170
+ ];
14159
14171
  }
14160
14172
 
14161
14173
  connectedCallback() {
@@ -14191,12 +14203,15 @@ class FigEasingCurve extends HTMLElement {
14191
14203
  return;
14192
14204
  }
14193
14205
 
14194
- if (name === "edit") {
14206
+ if (name === "edit" || name === "presets" || name === "text") {
14195
14207
  if (this.isConnected) this.#render();
14196
14208
  return;
14197
14209
  }
14198
14210
 
14199
14211
  if (name === "mode") {
14212
+ this.#applyModeConstraint();
14213
+ const value = this.getAttribute("value");
14214
+ if (value) this.#parseValue(value);
14200
14215
  this.#applyModeConstraint();
14201
14216
  this.#presetName = this.#matchPreset();
14202
14217
  if (this.isConnected) this.#render();
@@ -14222,8 +14237,9 @@ class FigEasingCurve extends HTMLElement {
14222
14237
 
14223
14238
  get value() {
14224
14239
  if (this.#mode === "spring") {
14225
- const { stiffness, damping, mass } = this.#spring;
14226
- return `spring(${stiffness}, ${damping}, ${mass})`;
14240
+ const { stiffness, damping, mass, initialVelocity = 0 } = this.#spring;
14241
+ const velocity = initialVelocity ? `, ${initialVelocity}` : "";
14242
+ return `spring(${stiffness}, ${damping}, ${mass}${velocity})`;
14227
14243
  }
14228
14244
  const p = this.#precision;
14229
14245
  return `${this.#cp1.x.toFixed(p)}, ${this.#cp1.y.toFixed(p)}, ${this.#cp2.x.toFixed(p)}, ${this.#cp2.y.toFixed(p)}`;
@@ -14251,9 +14267,39 @@ class FigEasingCurve extends HTMLElement {
14251
14267
  }
14252
14268
 
14253
14269
  set value(v) {
14270
+ if (v && typeof v === "object") {
14271
+ const normalized = this.#normalizeStructuredValue(v);
14272
+ if (!normalized) {
14273
+ throw new TypeError("Invalid easing value or mode/type mismatch");
14274
+ }
14275
+ const serialized =
14276
+ normalized.type === "spring"
14277
+ ? { type: "spring", ...normalized.spring }
14278
+ : {
14279
+ type: "bezier",
14280
+ x1: normalized.bezier[0],
14281
+ y1: normalized.bezier[1],
14282
+ x2: normalized.bezier[2],
14283
+ y2: normalized.bezier[3],
14284
+ };
14285
+ this.setAttribute("value", JSON.stringify(serialized));
14286
+ return;
14287
+ }
14254
14288
  this.setAttribute("value", v);
14255
14289
  }
14256
14290
 
14291
+ get spring() {
14292
+ if (this.#mode !== "spring") return null;
14293
+ return { ...this.#spring, initialVelocity: this.#spring.initialVelocity ?? 0 };
14294
+ }
14295
+
14296
+ set spring(value) {
14297
+ if (!value || typeof value !== "object") {
14298
+ throw new TypeError("spring must be an object");
14299
+ }
14300
+ this.value = { ...value, type: "spring" };
14301
+ }
14302
+
14257
14303
  #constrainedMode() {
14258
14304
  const mode = this.getAttribute("mode")?.trim().toLowerCase();
14259
14305
  return mode === "bezier" || mode === "spring" ? mode : null;
@@ -14269,15 +14315,114 @@ class FigEasingCurve extends HTMLElement {
14269
14315
  return !mode || preset.type === mode;
14270
14316
  }
14271
14317
 
14272
- #parseValue(str) {
14273
- const springMatch = str.match(
14274
- /^spring\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*\)$/,
14275
- );
14276
- if (springMatch && this.#constrainedMode() !== "bezier") {
14318
+ #normalizeStructuredValue(value) {
14319
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
14320
+ const constrainedMode = this.#constrainedMode();
14321
+ const declaredType =
14322
+ typeof value.type === "string" ? value.type.trim().toLowerCase() : null;
14323
+ if (declaredType && declaredType !== "bezier" && declaredType !== "spring") {
14324
+ return null;
14325
+ }
14326
+ if (declaredType && constrainedMode && declaredType !== constrainedMode) {
14327
+ return null;
14328
+ }
14329
+ const type = declaredType || constrainedMode;
14330
+ if (!type) return null;
14331
+
14332
+ if (type === "spring") {
14333
+ const spring = {
14334
+ mass: Number(value.mass),
14335
+ stiffness: Number(value.stiffness),
14336
+ damping: Number(value.damping),
14337
+ initialVelocity:
14338
+ value.initialVelocity === undefined
14339
+ ? 0
14340
+ : Number(value.initialVelocity),
14341
+ };
14342
+ if (
14343
+ !Number.isFinite(spring.mass) ||
14344
+ spring.mass <= 0 ||
14345
+ !Number.isFinite(spring.stiffness) ||
14346
+ spring.stiffness <= 0 ||
14347
+ !Number.isFinite(spring.damping) ||
14348
+ spring.damping < 0 ||
14349
+ !Number.isFinite(spring.initialVelocity)
14350
+ ) {
14351
+ return null;
14352
+ }
14353
+ return { type, spring };
14354
+ }
14355
+
14356
+ const bezier = [value.x1, value.y1, value.x2, value.y2].map(Number);
14357
+ if (
14358
+ bezier.some((part) => !Number.isFinite(part)) ||
14359
+ bezier[0] < 0 ||
14360
+ bezier[0] > 1 ||
14361
+ bezier[2] < 0 ||
14362
+ bezier[2] > 1
14363
+ ) {
14364
+ return null;
14365
+ }
14366
+ return { type, bezier };
14367
+ }
14368
+
14369
+ #applyStructuredValue(value) {
14370
+ if (value.type === "spring") {
14371
+ this.#mode = "spring";
14372
+ this.#spring = { ...value.spring };
14373
+ } else {
14374
+ this.#mode = "bezier";
14375
+ [this.#cp1.x, this.#cp1.y, this.#cp2.x, this.#cp2.y] = value.bezier;
14376
+ }
14377
+ }
14378
+
14379
+ #parseSpringString(str) {
14380
+ const match = str.match(/^spring\(\s*(.*?)\s*\)$/i);
14381
+ if (!match) return null;
14382
+ const parts = match[1].split(",").map((part) => Number(part.trim()));
14383
+ if (
14384
+ (parts.length !== 3 && parts.length !== 4) ||
14385
+ parts.some((part) => !Number.isFinite(part))
14386
+ ) {
14387
+ return null;
14388
+ }
14389
+ const spring = {
14390
+ stiffness: parts[0],
14391
+ damping: parts[1],
14392
+ mass: parts[2],
14393
+ initialVelocity: parts[3] ?? 0,
14394
+ };
14395
+ if (
14396
+ spring.stiffness <= 0 ||
14397
+ spring.damping < 0 ||
14398
+ spring.mass <= 0
14399
+ ) {
14400
+ return null;
14401
+ }
14402
+ return spring;
14403
+ }
14404
+
14405
+ #parseValue(input) {
14406
+ if (input && typeof input === "object") {
14407
+ const normalized = this.#normalizeStructuredValue(input);
14408
+ if (!normalized) return false;
14409
+ this.#applyStructuredValue(normalized);
14410
+ return true;
14411
+ }
14412
+ if (typeof input !== "string") return false;
14413
+ const str = input.trim();
14414
+ if (str.startsWith("{")) {
14415
+ try {
14416
+ return this.#parseValue(JSON.parse(str));
14417
+ } catch {
14418
+ return false;
14419
+ }
14420
+ }
14421
+
14422
+ const spring = this.#parseSpringString(str);
14423
+ if (spring && this.#constrainedMode() !== "bezier") {
14277
14424
  this.#mode = "spring";
14278
- this.#spring.stiffness = parseFloat(springMatch[1]);
14279
- this.#spring.damping = parseFloat(springMatch[2]);
14280
- this.#spring.mass = parseFloat(springMatch[3]);
14425
+ this.#spring = spring;
14281
14426
  return true;
14282
14427
  }
14283
14428
  const parts = str.split(",").map((s) => parseFloat(s.trim()));
@@ -14316,7 +14461,8 @@ class FigEasingCurve extends HTMLElement {
14316
14461
  if (
14317
14462
  Math.abs(this.#spring.stiffness - p.spring.stiffness) < ep &&
14318
14463
  Math.abs(this.#spring.damping - p.spring.damping) < ep &&
14319
- Math.abs(this.#spring.mass - p.spring.mass) < ep
14464
+ Math.abs(this.#spring.mass - p.spring.mass) < ep &&
14465
+ Math.abs(this.#spring.initialVelocity ?? 0) < ep
14320
14466
  )
14321
14467
  return p.name;
14322
14468
  }
@@ -14326,12 +14472,12 @@ class FigEasingCurve extends HTMLElement {
14326
14472
  // --- Spring simulation ---
14327
14473
 
14328
14474
  #simulateSpring() {
14329
- const { stiffness, damping, mass } = this.#spring;
14475
+ const { stiffness, damping, mass, initialVelocity = 0 } = this.#spring;
14330
14476
  const dt = 0.004;
14331
14477
  const maxTime = 5;
14332
14478
  const points = [];
14333
14479
  let pos = 0,
14334
- vel = 0;
14480
+ vel = initialVelocity;
14335
14481
  for (let t = 0; t <= maxTime; t += dt) {
14336
14482
  const force = -stiffness * (pos - 1) - damping * vel;
14337
14483
  vel += (force / mass) * dt;
@@ -14344,12 +14490,12 @@ class FigEasingCurve extends HTMLElement {
14344
14490
  }
14345
14491
 
14346
14492
  static #springIcon(spring, size = 24) {
14347
- const { stiffness, damping, mass } = spring;
14493
+ const { stiffness, damping, mass, initialVelocity = 0 } = spring;
14348
14494
  const dt = 0.004;
14349
14495
  const maxTime = 5;
14350
14496
  const pts = [];
14351
14497
  let pos = 0,
14352
- vel = 0;
14498
+ vel = initialVelocity;
14353
14499
  for (let t = 0; t <= maxTime; t += dt) {
14354
14500
  const force = -stiffness * (pos - 1) - damping * vel;
14355
14501
  vel += (force / mass) * dt;
@@ -14467,6 +14613,14 @@ class FigEasingCurve extends HTMLElement {
14467
14613
  return this.getAttribute("edit") !== "false";
14468
14614
  }
14469
14615
 
14616
+ #isPresetsEnabled() {
14617
+ return this.getAttribute("presets") !== "false";
14618
+ }
14619
+
14620
+ #isTextEnabled() {
14621
+ return this.getAttribute("text") !== "false";
14622
+ }
14623
+
14470
14624
  #render() {
14471
14625
  this.classList.toggle("spring-mode", this.#mode === "spring");
14472
14626
  this.classList.toggle("bezier-mode", this.#mode !== "spring");
@@ -14594,13 +14748,26 @@ class FigEasingCurve extends HTMLElement {
14594
14748
 
14595
14749
  #createContent() {
14596
14750
  const size = 200;
14597
- const select = this.#createSelect();
14598
- if (!this.#isEditEnabled()) return [select];
14599
- const valueInput = figCreateElement("fig-input-text", {
14600
- className: "fig-easing-curve-value-input",
14601
- value: this.value,
14602
- full: true,
14603
- });
14751
+ const select = this.#isPresetsEnabled() ? this.#createSelect() : null;
14752
+ if (!this.#isEditEnabled()) return select ? [select] : [];
14753
+ const valueInput = this.#isTextEnabled()
14754
+ ? this.#mode === "spring"
14755
+ ? figCreateElement("fig-input-number", {
14756
+ className: "fig-easing-curve-value-input",
14757
+ value: this.#springBounce().toFixed(2),
14758
+ min: "0",
14759
+ max: "1",
14760
+ step: "0.01",
14761
+ precision: "2",
14762
+ "aria-label": "Spring bounce",
14763
+ full: true,
14764
+ })
14765
+ : figCreateElement("fig-input-text", {
14766
+ className: "fig-easing-curve-value-input",
14767
+ value: this.value,
14768
+ full: true,
14769
+ })
14770
+ : null;
14604
14771
  const svgChildren = [
14605
14772
  figCreateSvgElement("rect", {
14606
14773
  className: "fig-easing-curve-bounds",
@@ -14686,13 +14853,13 @@ class FigEasingCurve extends HTMLElement {
14686
14853
  svgChildren,
14687
14854
  );
14688
14855
  return [
14689
- select,
14856
+ ...(select ? [select] : []),
14690
14857
  figCreateElement(
14691
14858
  "div",
14692
14859
  { className: "fig-easing-curve-svg-container" },
14693
14860
  svg,
14694
14861
  ),
14695
- valueInput,
14862
+ ...(valueInput ? [valueInput] : []),
14696
14863
  ];
14697
14864
  }
14698
14865
 
@@ -14981,7 +15148,43 @@ class FigEasingCurve extends HTMLElement {
14981
15148
 
14982
15149
  #syncValueInput() {
14983
15150
  if (!this.#valueInput) return;
14984
- this.#valueInput.setAttribute("value", this.value);
15151
+ const value =
15152
+ this.#mode === "spring"
15153
+ ? this.#springBounce().toFixed(2)
15154
+ : this.value;
15155
+ this.#valueInput.setAttribute("value", value);
15156
+ }
15157
+
15158
+ #springBounce() {
15159
+ const { stiffness, damping, mass } = this.#spring;
15160
+ const criticalDamping = 2 * Math.sqrt(stiffness * mass);
15161
+ if (!Number.isFinite(criticalDamping) || criticalDamping <= 0) return 0;
15162
+ return Math.max(0, Math.min(1, 1 - damping / criticalDamping));
15163
+ }
15164
+
15165
+ #setSpringBounce(value) {
15166
+ const bounce = Math.max(0, Math.min(1, Number(value)));
15167
+ const criticalDamping = 2 * Math.sqrt(
15168
+ this.#spring.stiffness * this.#spring.mass,
15169
+ );
15170
+ this.#spring.damping = Number(
15171
+ (criticalDamping * (1 - bounce)).toFixed(4),
15172
+ );
15173
+ }
15174
+
15175
+ #applySpringBounce(value, eventType) {
15176
+ const bounce = Number(value);
15177
+ if (!Number.isFinite(bounce)) {
15178
+ if (eventType === "change") this.#syncValueInput();
15179
+ return;
15180
+ }
15181
+
15182
+ this.#setSpringBounce(bounce);
15183
+ this.#presetName = this.#matchPreset();
15184
+ this.#updatePaths();
15185
+ this.#syncSelect();
15186
+ if (eventType === "change") this.#syncValueInput();
15187
+ this.#emit(eventType);
14985
15188
  }
14986
15189
 
14987
15190
  #parseManualBezierValue(value) {
@@ -14996,16 +15199,7 @@ class FigEasingCurve extends HTMLElement {
14996
15199
  }
14997
15200
 
14998
15201
  #parseManualSpringValue(value) {
14999
- const match = value.match(
15000
- /^spring\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*\)$/,
15001
- );
15002
- if (!match) return null;
15003
- const spring = {
15004
- stiffness: Number(match[1]),
15005
- damping: Number(match[2]),
15006
- mass: Number(match[3]),
15007
- };
15008
- return Object.values(spring).every((part) => part > 0) ? spring : null;
15202
+ return this.#parseSpringString(value);
15009
15203
  }
15010
15204
 
15011
15205
  #applyManualValue(value, eventType) {
@@ -15115,18 +15309,19 @@ class FigEasingCurve extends HTMLElement {
15115
15309
  const unit = event.shiftKey ? 10 : 1;
15116
15310
 
15117
15311
  if (handleType === "bounce") {
15312
+ const step = unit / 100;
15118
15313
  switch (event.key) {
15119
15314
  case "ArrowUp":
15120
- this.#spring.damping = Math.max(1, Math.round(this.#spring.damping - unit));
15315
+ this.#setSpringBounce(this.#springBounce() + step);
15121
15316
  break;
15122
15317
  case "ArrowDown":
15123
- this.#spring.damping = Math.max(1, Math.round(this.#spring.damping + unit));
15318
+ this.#setSpringBounce(this.#springBounce() - step);
15124
15319
  break;
15125
15320
  case "Home":
15126
- this.#spring.damping = 1;
15321
+ this.#setSpringBounce(0);
15127
15322
  break;
15128
15323
  case "End":
15129
- this.#spring.damping = 50;
15324
+ this.#setSpringBounce(1);
15130
15325
  break;
15131
15326
  default:
15132
15327
  return false;
@@ -15201,6 +15396,7 @@ class FigEasingCurve extends HTMLElement {
15201
15396
  // --- Events ---
15202
15397
 
15203
15398
  #emit(type) {
15399
+ const spring = this.spring;
15204
15400
  this.dispatchEvent(
15205
15401
  new CustomEvent(type, {
15206
15402
  bubbles: true,
@@ -15209,6 +15405,7 @@ class FigEasingCurve extends HTMLElement {
15209
15405
  value: this.value,
15210
15406
  cssValue: this.cssValue,
15211
15407
  preset: this.#presetName,
15408
+ ...(spring ? { spring } : {}),
15212
15409
  },
15213
15410
  }),
15214
15411
  );
@@ -15260,7 +15457,14 @@ class FigEasingCurve extends HTMLElement {
15260
15457
  }
15261
15458
 
15262
15459
  if (this.#select) {
15460
+ this.#select.addEventListener("input", (e) => {
15461
+ e.stopPropagation();
15462
+ });
15463
+ this.#select.addEventListener("optionhover", (e) => {
15464
+ e.stopPropagation();
15465
+ });
15263
15466
  this.#select.addEventListener("change", (e) => {
15467
+ e.stopPropagation();
15264
15468
  const name = e.detail;
15265
15469
  const preset = FigEasingCurve.PRESETS.find((p) => p.name === name);
15266
15470
  if (!preset) return;
@@ -15282,7 +15486,7 @@ class FigEasingCurve extends HTMLElement {
15282
15486
  }
15283
15487
  } else if (preset.type === "spring") {
15284
15488
  if (preset.spring) {
15285
- this.#spring = { ...preset.spring };
15489
+ this.#spring = { ...preset.spring, initialVelocity: 0 };
15286
15490
  }
15287
15491
  this.#presetName = name;
15288
15492
  if (this.#mode !== "spring") {
@@ -15302,14 +15506,20 @@ class FigEasingCurve extends HTMLElement {
15302
15506
  this.#valueInput.addEventListener("input", (e) => {
15303
15507
  e.stopPropagation();
15304
15508
  const value = e.detail ?? e.target?.value;
15305
- if (typeof value !== "string") return;
15306
- this.#applyManualValue(value, "input");
15509
+ if (this.#mode === "spring") {
15510
+ this.#applySpringBounce(value, "input");
15511
+ } else if (typeof value === "string") {
15512
+ this.#applyManualValue(value, "input");
15513
+ }
15307
15514
  });
15308
15515
  this.#valueInput.addEventListener("change", (e) => {
15309
15516
  e.stopPropagation();
15310
15517
  const value = e.detail ?? e.target?.value;
15311
- if (typeof value !== "string") return;
15312
- this.#applyManualValue(value, "change");
15518
+ if (this.#mode === "spring") {
15519
+ this.#applySpringBounce(value, "change");
15520
+ } else if (typeof value === "string") {
15521
+ this.#applyManualValue(value, "change");
15522
+ }
15313
15523
  });
15314
15524
  }
15315
15525
  }
@@ -15397,7 +15607,7 @@ class FigEasingCurve extends HTMLElement {
15397
15607
  this.#isDragging = handleType;
15398
15608
  this.classList.toggle("spring-bounce-dragging", handleType === "bounce");
15399
15609
 
15400
- const startDamping = this.#spring.damping;
15610
+ const startBounce = this.#springBounce();
15401
15611
  const startStiffness = this.#spring.stiffness;
15402
15612
  const startDuration = this.#springDuration;
15403
15613
  const startY = e.clientY;
@@ -15408,10 +15618,7 @@ class FigEasingCurve extends HTMLElement {
15408
15618
 
15409
15619
  if (handleType === "bounce") {
15410
15620
  const dy = e.clientY - startY;
15411
- this.#spring.damping = Math.max(
15412
- 1,
15413
- Math.round(startDamping + dy * 0.15),
15414
- );
15621
+ this.#setSpringBounce(startBounce - dy / 200);
15415
15622
  } else {
15416
15623
  const dx = e.clientX - startX;
15417
15624
  this.#springDuration = Math.max(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "9.0.1",
3
+ "version": "9.0.3",
4
4
  "description": "A lightweight web components library for building Figma plugin and widget UIs with native look and feel",
5
5
  "author": "Rogie King",
6
6
  "license": "SEE LICENSE IN LICENSE",