@rogieking/figui3 8.9.36 → 8.9.37

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
@@ -1474,11 +1474,6 @@ propskit-slider {
1474
1474
  }
1475
1475
 
1476
1476
  fig-input-wheel {
1477
- --fig-input-wheel-elastic-distance: calc(var(--spacer-2) + 0.25rem);
1478
- --fig-input-wheel-elastic-scale: 1;
1479
- --fig-input-wheel-elastic-origin: left center;
1480
- --fig-input-wheel-elastic-transition: 0.28s
1481
- cubic-bezier(0.34, 1.56, 0.64, 1);
1482
1477
  --fig-input-wheel-handle-drag-max: 0.5rem;
1483
1478
  --fig-input-wheel-handle-drag-offset: 0px;
1484
1479
  --fig-input-wheel-handle-spring: 0.28s
@@ -1494,6 +1489,7 @@ fig-input-wheel {
1494
1489
  --fig-input-wheel-tick-fade: 1rem;
1495
1490
  --fig-input-wheel-tick-opacity: 0.5;
1496
1491
  --fig-input-wheel-tick-opacity-min: 0.1;
1492
+ --fig-input-wheel-fast-tick-blur: 2px;
1497
1493
  --fig-input-wheel-handle-width: var(--spacer-1);
1498
1494
  --fig-input-wheel-handle-height: calc(
1499
1495
  var(--control-height) - var(--spacer-2)
@@ -1512,8 +1508,6 @@ fig-input-wheel {
1512
1508
  background-color: var(--figma-color-bg-secondary);
1513
1509
  outline: none;
1514
1510
  cursor: ew-resize;
1515
- transform-origin: var(--fig-input-wheel-elastic-origin);
1516
- transition: transform var(--fig-input-wheel-elastic-transition);
1517
1511
 
1518
1512
  &:focus-visible {
1519
1513
  outline: var(--figma-focus-outline);
@@ -1527,8 +1521,9 @@ fig-input-wheel {
1527
1521
 
1528
1522
  .fig-input-wheel-svg {
1529
1523
  display: block;
1530
- width: 100%;
1524
+ width: calc(100% - var(--spacer-3));
1531
1525
  height: 100%;
1526
+ margin-inline: var(--spacer-2);
1532
1527
  overflow: visible;
1533
1528
  color: var(--figma-color-icon-tertiary);
1534
1529
  mask-image: linear-gradient(
@@ -1545,6 +1540,7 @@ fig-input-wheel {
1545
1540
  .fig-input-wheel-tick {
1546
1541
  fill: currentColor;
1547
1542
  stroke: none;
1543
+ transition: filter 0.12s ease-out;
1548
1544
  }
1549
1545
 
1550
1546
  .fig-input-wheel-handle {
@@ -1568,13 +1564,13 @@ fig-input-wheel {
1568
1564
  var(--handle-shadow);
1569
1565
  }
1570
1566
 
1571
- &[data-fig-input-wheel-elastic-dragging] {
1572
- transform: scaleX(var(--fig-input-wheel-elastic-scale));
1567
+ &[data-fig-input-wheel-active] .fig-input-wheel-handle {
1573
1568
  transition: none;
1569
+ }
1574
1570
 
1575
- .fig-input-wheel-handle {
1576
- transition: none;
1577
- }
1571
+ &[data-fig-input-wheel-fast] .fig-input-wheel-tick {
1572
+ filter: blur(var(--fig-input-wheel-fast-tick-blur));
1573
+ transition: none;
1578
1574
  }
1579
1575
 
1580
1576
  &[data-fig-input-wheel-keyboard-moving] .fig-input-wheel-handle {
@@ -1587,6 +1583,7 @@ fig-input-wheel {
1587
1583
  }
1588
1584
 
1589
1585
  propskit-wheel {
1586
+ --propskit-wheel-elastic-distance: calc(var(--spacer-2) + 0.25rem);
1590
1587
  --propskit-wheel-elastic-scale: 1;
1591
1588
  --propskit-wheel-elastic-origin: left center;
1592
1589
  --propskit-wheel-elastic-transition: 0.28s
@@ -1761,7 +1758,7 @@ body.fig-input-wheel-dragging * {
1761
1758
 
1762
1759
  @media (prefers-reduced-motion: reduce) {
1763
1760
  propskit-wheel,
1764
- fig-input-wheel,
1761
+ fig-input-wheel .fig-input-wheel-tick,
1765
1762
  fig-input-wheel .fig-input-wheel-handle {
1766
1763
  transition: none;
1767
1764
  }
package/fig-lab.js CHANGED
@@ -5553,7 +5553,6 @@ figLabDefineElement("propskit-slider", PropskitSlider);
5553
5553
  * @attr {number} max - Inclusive upper bound. Omitted = unbounded above.
5554
5554
  * @attr {number} step - Increment. Defaults to 1.
5555
5555
  * @attr {boolean|string} spin - Keeps ticks synchronized to value. Defaults to true.
5556
- * @attr {boolean|string} elastic - Enables resisted handle movement. Defaults to true.
5557
5556
  * @attr {boolean|string} disabled - Disables interaction and focus.
5558
5557
  * @fires input - Numeric composed event during interaction.
5559
5558
  * @fires change - Numeric composed event on commit.
@@ -5562,6 +5561,8 @@ class FigInputWheel extends HTMLElement {
5562
5561
  static #TICK_COUNT = 33;
5563
5562
  static #HALF_FOV_DEG = 60;
5564
5563
  static #PERSPECTIVE_K = 0.55;
5564
+ static #FAST_MOTION_THRESHOLD_PX_PER_MS = 1.5;
5565
+ static #FAST_MOTION_TIMEOUT_MS = 80;
5565
5566
  static #DRAGGING_BODY_CLASS = "fig-input-wheel-dragging";
5566
5567
 
5567
5568
  #surface = null;
@@ -5583,9 +5584,9 @@ class FigInputWheel extends HTMLElement {
5583
5584
  #dragStartValue = 0;
5584
5585
  #visualValue = null;
5585
5586
  #handleDragMaxPx = 0;
5586
- #elasticMaxPx = 0;
5587
- #elasticRangeRect = null;
5588
- #elasticHostWidth = 0;
5587
+ #motionLastX = null;
5588
+ #motionLastTime = 0;
5589
+ #motionBlurTimer = 0;
5589
5590
  #boundPointerDown = this.#handlePointerDown.bind(this);
5590
5591
  #boundPointerMove = this.#handlePointerMove.bind(this);
5591
5592
  #boundPointerUp = this.#handlePointerUp.bind(this);
@@ -5600,7 +5601,6 @@ class FigInputWheel extends HTMLElement {
5600
5601
  "min",
5601
5602
  "max",
5602
5603
  "spin",
5603
- "elastic",
5604
5604
  ];
5605
5605
  }
5606
5606
 
@@ -5960,7 +5960,8 @@ class FigInputWheel extends HTMLElement {
5960
5960
  this.#dragStartValue = Number.isFinite(requestedStart)
5961
5961
  ? this.#clamp(requestedStart)
5962
5962
  : this.#numericValue();
5963
- this.#startElasticPull();
5963
+ this.#startTickMotionTracking(clientX);
5964
+ this.#startHandlePull();
5964
5965
  this.setAttribute("data-fig-input-wheel-active", "");
5965
5966
  document.body.classList.add(FigInputWheel.#DRAGGING_BODY_CLASS);
5966
5967
  this.focus();
@@ -6006,13 +6007,19 @@ class FigInputWheel extends HTMLElement {
6006
6007
  const visibleUnits = this.#step() * visibleSteps;
6007
6008
  const delta =
6008
6009
  (clientX - this.#dragStartX) * (visibleUnits / width) * speed;
6010
+ const previousVisualValue = this.#visualValue ?? this.#dragStartValue;
6009
6011
  const rawValue = this.#clamp(this.#dragStartValue + delta);
6012
+ this.#updateTickMotionBlur(
6013
+ clientX,
6014
+ speed,
6015
+ rawValue !== previousVisualValue,
6016
+ );
6010
6017
  this.#visualValue = rawValue;
6011
6018
  this.#commitValue(rawValue, {
6012
6019
  snap: true,
6013
6020
  emit: "input",
6014
6021
  });
6015
- this.#updateElasticPull(clientX);
6022
+ this.#updateHandlePull(clientX);
6016
6023
  }
6017
6024
 
6018
6025
  #handlePointerUp(event) {
@@ -6029,30 +6036,19 @@ class FigInputWheel extends HTMLElement {
6029
6036
  this.#visualValue = null;
6030
6037
  this.removeAttribute("data-fig-input-wheel-active");
6031
6038
  document.body.classList.remove(FigInputWheel.#DRAGGING_BODY_CLASS);
6032
- this.#resetElasticPull();
6039
+ this.#stopTickMotionTracking();
6040
+ this.#resetHandlePull();
6033
6041
  window.removeEventListener("pointermove", this.#boundPointerMove);
6034
6042
  window.removeEventListener("pointerup", this.#boundPointerUp);
6035
6043
  window.removeEventListener("pointercancel", this.#boundPointerUp);
6036
6044
  this.#queueWheelLayout();
6037
6045
  }
6038
6046
 
6039
- #startElasticPull() {
6040
- this.#resetElasticPull();
6041
- if (this.getAttribute("elastic") === "false") return;
6047
+ #startHandlePull() {
6048
+ this.#resetHandlePull();
6042
6049
  this.#handleDragMaxPx = this.#readCssLength(
6043
6050
  "--fig-input-wheel-handle-drag-max",
6044
6051
  );
6045
- this.#elasticMaxPx = this.#readCssLength(
6046
- "--fig-input-wheel-elastic-distance",
6047
- );
6048
- const rect = this.#wheel?.getBoundingClientRect();
6049
- if (rect) {
6050
- this.#elasticRangeRect = {
6051
- left: rect.left,
6052
- right: rect.right,
6053
- };
6054
- }
6055
- this.#elasticHostWidth = this.getBoundingClientRect().width;
6056
6052
  }
6057
6053
 
6058
6054
  #readCssLength(propertyName) {
@@ -6075,14 +6071,14 @@ class FigInputWheel extends HTMLElement {
6075
6071
  return Number.isFinite(value) ? Math.max(0, value) : 0;
6076
6072
  }
6077
6073
 
6078
- #updateElasticPull(pointerX) {
6079
- if (!this.#handleDragMaxPx && !this.#elasticMaxPx) {
6080
- this.#clearElasticPull();
6074
+ #updateHandlePull(pointerX) {
6075
+ if (!this.#handleDragMaxPx) {
6076
+ this.#clearHandlePull();
6081
6077
  return;
6082
6078
  }
6083
6079
  const dragDelta = pointerX - this.#dragStartX;
6084
6080
  if (!dragDelta) {
6085
- this.#clearElasticPull();
6081
+ this.#clearHandlePull();
6086
6082
  return;
6087
6083
  }
6088
6084
  const offset =
@@ -6090,49 +6086,60 @@ class FigInputWheel extends HTMLElement {
6090
6086
  Math.tanh(
6091
6087
  dragDelta / Math.max(1, this.#handleDragMaxPx * 3),
6092
6088
  );
6093
- this.setAttribute("data-fig-input-wheel-elastic-dragging", "");
6094
6089
  this.style.setProperty(
6095
6090
  "--fig-input-wheel-handle-drag-offset",
6096
6091
  `${offset}px`,
6097
6092
  );
6093
+ }
6098
6094
 
6099
- const rect = this.#elasticRangeRect;
6100
- const overshoot = rect
6101
- ? pointerX < rect.left
6102
- ? pointerX - rect.left
6103
- : pointerX > rect.right
6104
- ? pointerX - rect.right
6105
- : 0
6106
- : 0;
6107
- if (!overshoot || !this.#elasticMaxPx) {
6108
- this.style.removeProperty("--fig-input-wheel-elastic-scale");
6109
- this.style.removeProperty("--fig-input-wheel-elastic-origin");
6095
+ #resetHandlePull() {
6096
+ this.#clearHandlePull();
6097
+ this.#handleDragMaxPx = 0;
6098
+ }
6099
+
6100
+ #clearHandlePull() {
6101
+ this.style.removeProperty("--fig-input-wheel-handle-drag-offset");
6102
+ }
6103
+
6104
+ #startTickMotionTracking(clientX) {
6105
+ this.#stopTickMotionTracking();
6106
+ this.#motionLastX = clientX;
6107
+ this.#motionLastTime = performance.now();
6108
+ }
6109
+
6110
+ #updateTickMotionBlur(clientX, speed, moved) {
6111
+ const now = performance.now();
6112
+ const elapsed = now - this.#motionLastTime;
6113
+ const distance =
6114
+ this.#motionLastX === null ? 0 : Math.abs(clientX - this.#motionLastX);
6115
+ this.#motionLastX = clientX;
6116
+ this.#motionLastTime = now;
6117
+ const velocity =
6118
+ elapsed > 0 ? (distance / elapsed) * Math.max(1, Math.abs(speed)) : 0;
6119
+ if (
6120
+ !moved ||
6121
+ velocity < FigInputWheel.#FAST_MOTION_THRESHOLD_PX_PER_MS
6122
+ ) {
6123
+ this.#clearTickMotionBlur();
6110
6124
  return;
6111
6125
  }
6112
- const stretch = Math.min(this.#elasticMaxPx, Math.abs(overshoot) * 0.5);
6113
- const scale = this.#elasticHostWidth
6114
- ? (this.#elasticHostWidth + stretch) / this.#elasticHostWidth
6115
- : 1;
6116
- this.style.setProperty("--fig-input-wheel-elastic-scale", `${scale}`);
6117
- this.style.setProperty(
6118
- "--fig-input-wheel-elastic-origin",
6119
- overshoot < 0 ? "right center" : "left center",
6120
- );
6126
+ this.toggleAttribute("data-fig-input-wheel-fast", true);
6127
+ clearTimeout(this.#motionBlurTimer);
6128
+ this.#motionBlurTimer = window.setTimeout(() => {
6129
+ this.#clearTickMotionBlur();
6130
+ }, FigInputWheel.#FAST_MOTION_TIMEOUT_MS);
6121
6131
  }
6122
6132
 
6123
- #resetElasticPull() {
6124
- this.#clearElasticPull();
6125
- this.#handleDragMaxPx = 0;
6126
- this.#elasticMaxPx = 0;
6127
- this.#elasticRangeRect = null;
6128
- this.#elasticHostWidth = 0;
6133
+ #stopTickMotionTracking() {
6134
+ this.#motionLastX = null;
6135
+ this.#motionLastTime = 0;
6136
+ this.#clearTickMotionBlur();
6129
6137
  }
6130
6138
 
6131
- #clearElasticPull() {
6132
- this.removeAttribute("data-fig-input-wheel-elastic-dragging");
6133
- this.style.removeProperty("--fig-input-wheel-handle-drag-offset");
6134
- this.style.removeProperty("--fig-input-wheel-elastic-scale");
6135
- this.style.removeProperty("--fig-input-wheel-elastic-origin");
6139
+ #clearTickMotionBlur() {
6140
+ clearTimeout(this.#motionBlurTimer);
6141
+ this.#motionBlurTimer = 0;
6142
+ this.removeAttribute("data-fig-input-wheel-fast");
6136
6143
  }
6137
6144
 
6138
6145
  #handleWheel(event) {
@@ -6221,8 +6228,7 @@ class FigInputWheel extends HTMLElement {
6221
6228
  ) || 0;
6222
6229
  const duration = multiplier > 1 ? 120 : 90;
6223
6230
  const startedAt = performance.now();
6224
- const shouldAnimateHandle =
6225
- animateHandle && this.getAttribute("elastic") !== "false";
6231
+ const shouldAnimateHandle = animateHandle;
6226
6232
  this.setAttribute("data-fig-input-wheel-keyboard-moving", "");
6227
6233
 
6228
6234
  const frame = (now) => {
@@ -6336,6 +6342,7 @@ figLabDefineElement("fig-input-wheel", FigInputWheel);
6336
6342
  * @attr {string} label - Field label. Defaults to "Value".
6337
6343
  * @attr {string} default - Reset target.
6338
6344
  * @attr {number} precision - Number field display decimals.
6345
+ * @attr {boolean|string} elastic - Enables resisted row stretching. Defaults to true.
6339
6346
  * @attr {boolean|string} spin - Keeps wheel ticks synchronized to value. Defaults to true.
6340
6347
  * @attr {boolean|string} text - Shows the number field. Defaults to true.
6341
6348
  * @attr {string} size - Set to "small" for compact sizing.
@@ -6382,9 +6389,12 @@ class PropskitWheel extends HTMLElement {
6382
6389
  #input = null;
6383
6390
  #hasCustomLabel = false;
6384
6391
  #observer = null;
6385
- #elasticObserver = null;
6386
6392
  #managedInputAttrs = new Set();
6387
6393
  #initialValue = null;
6394
+ #elasticPointerId = null;
6395
+ #elasticMaxPx = 0;
6396
+ #elasticRangeRect = null;
6397
+ #elasticHostWidth = 0;
6388
6398
  #numberPointerId = null;
6389
6399
  #numberPointerStartX = 0;
6390
6400
  #numberPointerStartY = 0;
@@ -6399,6 +6409,9 @@ class PropskitWheel extends HTMLElement {
6399
6409
  #boundNumberPointerDown = this.#handleNumberPointerDown.bind(this);
6400
6410
  #boundNumberPointerMove = this.#handleNumberPointerMove.bind(this);
6401
6411
  #boundNumberPointerEnd = this.#handleNumberPointerEnd.bind(this);
6412
+ #boundElasticPointerDown = this.#handleElasticPointerDown.bind(this);
6413
+ #boundElasticPointerMove = this.#handleElasticPointerMove.bind(this);
6414
+ #boundElasticPointerEnd = this.#handleElasticPointerEnd.bind(this);
6402
6415
  #boundClick = this.#handleClick.bind(this);
6403
6416
 
6404
6417
  connectedCallback() {
@@ -6425,24 +6438,11 @@ class PropskitWheel extends HTMLElement {
6425
6438
  }
6426
6439
  });
6427
6440
  this.#observer.observe(this, { attributes: true });
6428
- this.#elasticObserver?.disconnect();
6429
- this.#elasticObserver = new MutationObserver(() => {
6430
- this.#syncElasticComposition();
6431
- });
6432
- if (this.#wheel) {
6433
- this.#elasticObserver.observe(this.#wheel, {
6434
- attributes: true,
6435
- attributeFilter: ["style", "data-fig-input-wheel-elastic-dragging"],
6436
- });
6437
- }
6438
- this.#syncElasticComposition();
6439
6441
  }
6440
6442
 
6441
6443
  disconnectedCallback() {
6442
6444
  this.#observer?.disconnect();
6443
- this.#elasticObserver?.disconnect();
6444
- this.#elasticObserver = null;
6445
- this.#clearElasticComposition();
6445
+ this.#stopElasticTracking();
6446
6446
  this.#unbindEvents();
6447
6447
  this.#stopNumberTracking();
6448
6448
  clearTimeout(this.#numberClickResetTimer);
@@ -6455,6 +6455,9 @@ class PropskitWheel extends HTMLElement {
6455
6455
  if (oldValue === newValue || !this.#surface) return;
6456
6456
  if (name === "label") this.#syncLabel();
6457
6457
  if (name === "text") this.#syncText();
6458
+ if (name === "elastic" && newValue === "false") {
6459
+ this.#stopElasticTracking();
6460
+ }
6458
6461
  if (
6459
6462
  [
6460
6463
  "units",
@@ -6542,7 +6545,7 @@ class PropskitWheel extends HTMLElement {
6542
6545
  #syncPrimitive() {
6543
6546
  if (!this.#wheel) return;
6544
6547
  this.#wheel.removeAttribute("units");
6545
- for (const name of ["min", "max", "elastic", "spin"]) {
6548
+ for (const name of ["min", "max", "spin"]) {
6546
6549
  this.#mirrorAttribute(name);
6547
6550
  }
6548
6551
  if (this.hasAttribute("step")) this.#mirrorAttribute("step");
@@ -6562,34 +6565,6 @@ class PropskitWheel extends HTMLElement {
6562
6565
  this.#syncPrimitiveAria();
6563
6566
  }
6564
6567
 
6565
- #syncElasticComposition() {
6566
- if (!this.#wheel) return;
6567
- const active = this.#wheel.hasAttribute(
6568
- "data-fig-input-wheel-elastic-dragging",
6569
- );
6570
- if (!active) {
6571
- this.#clearElasticComposition();
6572
- return;
6573
- }
6574
- this.toggleAttribute("data-propskit-wheel-elastic-dragging", true);
6575
- this.style.setProperty(
6576
- "--propskit-wheel-elastic-scale",
6577
- this.#wheel.style.getPropertyValue("--fig-input-wheel-elastic-scale") ||
6578
- "1",
6579
- );
6580
- this.style.setProperty(
6581
- "--propskit-wheel-elastic-origin",
6582
- this.#wheel.style.getPropertyValue("--fig-input-wheel-elastic-origin") ||
6583
- "left center",
6584
- );
6585
- }
6586
-
6587
- #clearElasticComposition() {
6588
- this.removeAttribute("data-propskit-wheel-elastic-dragging");
6589
- this.style.removeProperty("--propskit-wheel-elastic-scale");
6590
- this.style.removeProperty("--propskit-wheel-elastic-origin");
6591
- }
6592
-
6593
6568
  #getForwardedInputAttrNames() {
6594
6569
  return this.getAttributeNames().filter(
6595
6570
  (name) =>
@@ -6685,6 +6660,9 @@ class PropskitWheel extends HTMLElement {
6685
6660
  this.#wheel?.addEventListener("change", this.#boundPrimitiveChange);
6686
6661
  this.#input?.addEventListener("input", this.#boundNumberInput);
6687
6662
  this.#input?.addEventListener("change", this.#boundNumberChange);
6663
+ this.addEventListener("pointerdown", this.#boundElasticPointerDown, {
6664
+ capture: true,
6665
+ });
6688
6666
  this.addEventListener("pointerdown", this.#boundNumberPointerDown, {
6689
6667
  capture: true,
6690
6668
  });
@@ -6696,6 +6674,9 @@ class PropskitWheel extends HTMLElement {
6696
6674
  this.#wheel?.removeEventListener("change", this.#boundPrimitiveChange);
6697
6675
  this.#input?.removeEventListener("input", this.#boundNumberInput);
6698
6676
  this.#input?.removeEventListener("change", this.#boundNumberChange);
6677
+ this.removeEventListener("pointerdown", this.#boundElasticPointerDown, {
6678
+ capture: true,
6679
+ });
6699
6680
  this.removeEventListener("pointerdown", this.#boundNumberPointerDown, {
6700
6681
  capture: true,
6701
6682
  });
@@ -6751,6 +6732,127 @@ class PropskitWheel extends HTMLElement {
6751
6732
  this.#emit(type, value);
6752
6733
  }
6753
6734
 
6735
+ #handleElasticPointerDown(event) {
6736
+ if (
6737
+ event.button !== 0 ||
6738
+ figLabBooleanAttribute(this, "disabled") ||
6739
+ this.getAttribute("elastic") === "false" ||
6740
+ !(event.target instanceof Element) ||
6741
+ event.target.closest("fig-input-number") ||
6742
+ !event.target.closest("fig-input-wheel")
6743
+ ) {
6744
+ return;
6745
+ }
6746
+ this.#startElasticTracking(event.pointerId);
6747
+ }
6748
+
6749
+ #startElasticTracking(pointerId) {
6750
+ this.#stopElasticTracking();
6751
+ if (this.getAttribute("elastic") === "false") return;
6752
+ this.#elasticPointerId = pointerId;
6753
+ this.#elasticMaxPx = this.#readCssLength(
6754
+ "--propskit-wheel-elastic-distance",
6755
+ );
6756
+ const rangeRect = this.#wheel?.getBoundingClientRect();
6757
+ if (rangeRect) {
6758
+ this.#elasticRangeRect = {
6759
+ left: rangeRect.left,
6760
+ right: rangeRect.right,
6761
+ };
6762
+ }
6763
+ this.#elasticHostWidth = this.getBoundingClientRect().width;
6764
+ window.addEventListener("pointermove", this.#boundElasticPointerMove, {
6765
+ passive: true,
6766
+ });
6767
+ window.addEventListener("pointerup", this.#boundElasticPointerEnd);
6768
+ window.addEventListener("pointercancel", this.#boundElasticPointerEnd);
6769
+ window.addEventListener("blur", this.#boundElasticPointerEnd);
6770
+ }
6771
+
6772
+ #handleElasticPointerMove(event) {
6773
+ if (event.pointerId !== this.#elasticPointerId) return;
6774
+ if (event.buttons === 0) {
6775
+ this.#handleElasticPointerEnd(event);
6776
+ return;
6777
+ }
6778
+ this.#updateElasticStretch(event.clientX);
6779
+ }
6780
+
6781
+ #handleElasticPointerEnd(event) {
6782
+ if (
6783
+ event?.pointerId !== undefined &&
6784
+ this.#elasticPointerId !== null &&
6785
+ event.pointerId !== this.#elasticPointerId
6786
+ ) {
6787
+ return;
6788
+ }
6789
+ this.#stopElasticTracking();
6790
+ }
6791
+
6792
+ #updateElasticStretch(pointerX) {
6793
+ const rect = this.#elasticRangeRect;
6794
+ if (!rect || !this.#elasticMaxPx) {
6795
+ this.#clearElasticStretch();
6796
+ return;
6797
+ }
6798
+ const overshoot =
6799
+ pointerX < rect.left
6800
+ ? pointerX - rect.left
6801
+ : pointerX > rect.right
6802
+ ? pointerX - rect.right
6803
+ : 0;
6804
+ if (!overshoot) {
6805
+ this.#clearElasticStretch();
6806
+ return;
6807
+ }
6808
+ const stretch = Math.min(this.#elasticMaxPx, Math.abs(overshoot) * 0.5);
6809
+ const scale = this.#elasticHostWidth
6810
+ ? (this.#elasticHostWidth + stretch) / this.#elasticHostWidth
6811
+ : 1;
6812
+ this.toggleAttribute("data-propskit-wheel-elastic-dragging", true);
6813
+ this.style.setProperty("--propskit-wheel-elastic-scale", String(scale));
6814
+ this.style.setProperty(
6815
+ "--propskit-wheel-elastic-origin",
6816
+ overshoot < 0 ? "right center" : "left center",
6817
+ );
6818
+ }
6819
+
6820
+ #readCssLength(propertyName) {
6821
+ let raw = getComputedStyle(this).getPropertyValue(propertyName).trim();
6822
+ if (raw.includes("var(") || !raw.endsWith("px")) {
6823
+ const probe = document.createElement("div");
6824
+ Object.assign(probe.style, {
6825
+ position: "absolute",
6826
+ visibility: "hidden",
6827
+ pointerEvents: "none",
6828
+ width: `var(${propertyName})`,
6829
+ });
6830
+ this.appendChild(probe);
6831
+ raw = getComputedStyle(probe).width;
6832
+ probe.remove();
6833
+ }
6834
+ const value = Number.parseFloat(raw);
6835
+ return Number.isFinite(value) ? Math.max(0, value) : 0;
6836
+ }
6837
+
6838
+ #stopElasticTracking() {
6839
+ window.removeEventListener("pointermove", this.#boundElasticPointerMove);
6840
+ window.removeEventListener("pointerup", this.#boundElasticPointerEnd);
6841
+ window.removeEventListener("pointercancel", this.#boundElasticPointerEnd);
6842
+ window.removeEventListener("blur", this.#boundElasticPointerEnd);
6843
+ this.#elasticPointerId = null;
6844
+ this.#elasticMaxPx = 0;
6845
+ this.#elasticRangeRect = null;
6846
+ this.#elasticHostWidth = 0;
6847
+ this.#clearElasticStretch();
6848
+ }
6849
+
6850
+ #clearElasticStretch() {
6851
+ this.removeAttribute("data-propskit-wheel-elastic-dragging");
6852
+ this.style.removeProperty("--propskit-wheel-elastic-scale");
6853
+ this.style.removeProperty("--propskit-wheel-elastic-origin");
6854
+ }
6855
+
6754
6856
  #handleNumberPointerDown(event) {
6755
6857
  if (
6756
6858
  event.button !== 0 ||
@@ -6795,6 +6897,7 @@ class PropskitWheel extends HTMLElement {
6795
6897
  if (distance < 4) return;
6796
6898
  this.#isNumberScrubbing = true;
6797
6899
  this.setAttribute("data-number-scrubbing", "");
6900
+ this.#startElasticTracking(event.pointerId);
6798
6901
  this.#wheel?.beginScrub({
6799
6902
  clientX: this.#numberPointerStartX,
6800
6903
  pointerId: event.pointerId,
@@ -6802,6 +6905,7 @@ class PropskitWheel extends HTMLElement {
6802
6905
  });
6803
6906
  }
6804
6907
  this.#wheel?.updateScrub(event);
6908
+ this.#updateElasticStretch(event.clientX);
6805
6909
  }
6806
6910
 
6807
6911
  #handleNumberPointerEnd(event) {
@@ -6813,6 +6917,7 @@ class PropskitWheel extends HTMLElement {
6813
6917
  return;
6814
6918
  }
6815
6919
  const wasScrubbing = this.#isNumberScrubbing;
6920
+ this.#stopElasticTracking();
6816
6921
  this.#stopNumberTracking();
6817
6922
  if (wasScrubbing) {
6818
6923
  this.#wheel?.endScrub();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "8.9.36",
3
+ "version": "8.9.37",
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",