@rogieking/figui3 8.9.39 → 8.9.41

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/dist/fig.js CHANGED
@@ -138,9 +138,9 @@ fig-icon[size="small"] {
138
138
  display: flex;
139
139
  border: 0;
140
140
  flex: 1;
141
- text-align: center;
141
+ text-align: var(--fig-button-text-alignment, center);
142
142
  align-items: stretch;
143
- justify-content: center;
143
+ justify-content: var(--fig-button-content-alignment, center);
144
144
  font: inherit;
145
145
  color: inherit;
146
146
  outline: 0;
package/fig-lab.css CHANGED
@@ -1413,7 +1413,7 @@ propskit-slider {
1413
1413
  cursor: ew-resize;
1414
1414
  touch-action: none;
1415
1415
 
1416
- &:is(:focus, :active) {
1416
+ &:focus {
1417
1417
  cursor: text;
1418
1418
  touch-action: auto;
1419
1419
  }
@@ -1473,6 +1473,14 @@ propskit-slider {
1473
1473
  }
1474
1474
  }
1475
1475
 
1476
+ propskit-slider input[type="range"]:active::-webkit-slider-thumb {
1477
+ cursor: ew-resize;
1478
+ }
1479
+
1480
+ propskit-slider input[type="range"]:active::-moz-range-thumb {
1481
+ cursor: ew-resize;
1482
+ }
1483
+
1476
1484
  fig-input-wheel {
1477
1485
  --fig-input-wheel-handle-drag-max: 0.5rem;
1478
1486
  --fig-input-wheel-handle-drag-offset: 0px;
@@ -1650,6 +1658,9 @@ propskit-wheel {
1650
1658
  border-radius: var(--radius-medium);
1651
1659
  background-color: var(--figma-color-bg-secondary);
1652
1660
  overflow: hidden;
1661
+ cursor: ew-resize;
1662
+ touch-action: none;
1663
+ user-select: none;
1653
1664
 
1654
1665
  &:focus-within {
1655
1666
  outline: var(--figma-focus-outline);
@@ -1735,8 +1746,9 @@ propskit-wheel {
1735
1746
  text-align: right;
1736
1747
  cursor: ew-resize;
1737
1748
  touch-action: none;
1749
+ user-select: text;
1738
1750
 
1739
- &:is(:focus, :active) {
1751
+ &:focus {
1740
1752
  cursor: text;
1741
1753
  touch-action: auto;
1742
1754
  }
@@ -1750,7 +1762,9 @@ propskit-wheel {
1750
1762
  }
1751
1763
 
1752
1764
  body.fig-input-wheel-dragging,
1753
- body.fig-input-wheel-dragging * {
1765
+ body.fig-input-wheel-dragging *,
1766
+ body.propskit-slider-dragging,
1767
+ body.propskit-slider-dragging * {
1754
1768
  cursor: grabbing !important;
1755
1769
  user-select: none !important;
1756
1770
  -webkit-user-select: none !important;
@@ -1960,13 +1974,18 @@ propskit-position {
1960
1974
 
1961
1975
  :is(
1962
1976
  propskit-number,
1963
- propskit-slider,
1964
- propskit-wheel,
1965
1977
  propskit-position
1966
1978
  ) fig-input-number:is(:focus-within, :active, :has(input:active)) {
1967
1979
  background-color: var(--figma-color-bg);
1968
1980
  }
1969
1981
 
1982
+ :is(
1983
+ propskit-slider,
1984
+ propskit-wheel
1985
+ ) fig-input-number:has(input:focus) {
1986
+ background-color: var(--figma-color-bg);
1987
+ }
1988
+
1970
1989
  :is(
1971
1990
  propskit-switch,
1972
1991
  propskit-color,
package/fig-lab.js CHANGED
@@ -4629,6 +4629,9 @@ figLabDefineElement("propskit-group", PropskitGroup);
4629
4629
 
4630
4630
  /* Field + Slider wrapper */
4631
4631
  class PropskitSlider extends HTMLElement {
4632
+ static #DRAG_THRESHOLD_PX = 4;
4633
+ static #DRAGGING_BODY_CLASS = "propskit-slider-dragging";
4634
+
4632
4635
  #field = null;
4633
4636
  #label = null;
4634
4637
  #slider = null;
@@ -4647,6 +4650,7 @@ class PropskitSlider extends HTMLElement {
4647
4650
  #elasticRangeRect = null;
4648
4651
  #elasticHostWidth = 0;
4649
4652
  #elasticPointerId = null;
4653
+ #surfacePointerStartX = 0;
4650
4654
  #numberPointerId = null;
4651
4655
  #numberPointerStartX = 0;
4652
4656
  #numberPointerStartY = 0;
@@ -5172,6 +5176,7 @@ class PropskitSlider extends HTMLElement {
5172
5176
  if (distance < 4) return;
5173
5177
  this.#isNumberScrubbing = true;
5174
5178
  this.setAttribute("data-number-scrubbing", "");
5179
+ this.#setDraggingCursor(true);
5175
5180
  }
5176
5181
  this.#setSliderValue(this.#valueFromPointer(event), "input");
5177
5182
  }
@@ -5208,11 +5213,11 @@ class PropskitSlider extends HTMLElement {
5208
5213
  this.#numberPointerId = null;
5209
5214
  this.#isNumberScrubbing = false;
5210
5215
  this.removeAttribute("data-number-scrubbing");
5216
+ this.#setDraggingCursor(false);
5211
5217
  }
5212
5218
 
5213
5219
  #handleElasticPointerDown(event) {
5214
5220
  if (event.button !== 0 || figLabBooleanAttribute(this, "disabled")) return;
5215
- if (this.getAttribute("elastic") === "false") return;
5216
5221
  if (event.target?.closest?.("fig-input-number")) return;
5217
5222
  const rangeInput =
5218
5223
  this.#slider?.querySelector('input[type="range"]') ?? this.#rangeInput;
@@ -5222,7 +5227,11 @@ class PropskitSlider extends HTMLElement {
5222
5227
  this.#rangeInput = rangeInput;
5223
5228
  this.#isElasticTracking = true;
5224
5229
  this.#elasticPointerId = event.pointerId;
5225
- this.#elasticMaxPx = this.#readElasticDistance();
5230
+ this.#surfacePointerStartX = event.clientX;
5231
+ this.#elasticMaxPx =
5232
+ this.getAttribute("elastic") === "false"
5233
+ ? 0
5234
+ : this.#readElasticDistance();
5226
5235
  const rect = rangeInput.getBoundingClientRect();
5227
5236
  const hostRect = this.getBoundingClientRect();
5228
5237
  this.#elasticRangeRect = {
@@ -5252,7 +5261,13 @@ class PropskitSlider extends HTMLElement {
5252
5261
  this.#handleElasticPointerEnd(event);
5253
5262
  return;
5254
5263
  }
5255
- this.#updateElasticPull(event.clientX);
5264
+ if (
5265
+ Math.abs(event.clientX - this.#surfacePointerStartX) >=
5266
+ PropskitSlider.#DRAG_THRESHOLD_PX
5267
+ ) {
5268
+ this.#setDraggingCursor(true);
5269
+ }
5270
+ if (this.#elasticMaxPx) this.#updateElasticPull(event.clientX);
5256
5271
  }
5257
5272
 
5258
5273
  #handleElasticPointerEnd(event) {
@@ -5278,6 +5293,13 @@ class PropskitSlider extends HTMLElement {
5278
5293
  window.removeEventListener("blur", this.#boundHandleElasticPointerEnd);
5279
5294
  this.#isElasticTracking = false;
5280
5295
  this.#elasticPointerId = null;
5296
+ this.#surfacePointerStartX = 0;
5297
+ this.#setDraggingCursor(false);
5298
+ }
5299
+
5300
+ #setDraggingCursor(active) {
5301
+ this.toggleAttribute("data-propskit-slider-dragging", active);
5302
+ document.body.classList.toggle(PropskitSlider.#DRAGGING_BODY_CLASS, active);
5281
5303
  }
5282
5304
 
5283
5305
  #handleContextMenu(event) {
@@ -5563,6 +5585,7 @@ class FigInputWheel extends HTMLElement {
5563
5585
  static #PERSPECTIVE_K = 0.55;
5564
5586
  static #FAST_MOTION_THRESHOLD_PX_PER_MS = 1.5;
5565
5587
  static #FAST_MOTION_TIMEOUT_MS = 80;
5588
+ static #DRAG_THRESHOLD_PX = 4;
5566
5589
  static #DRAGGING_BODY_CLASS = "fig-input-wheel-dragging";
5567
5590
 
5568
5591
  #surface = null;
@@ -5582,6 +5605,7 @@ class FigInputWheel extends HTMLElement {
5582
5605
  #dragPointerId = null;
5583
5606
  #dragStartX = 0;
5584
5607
  #dragStartValue = 0;
5608
+ #hasCrossedDragThreshold = false;
5585
5609
  #visualValue = null;
5586
5610
  #handleDragMaxPx = 0;
5587
5611
  #motionLastX = null;
@@ -5960,10 +5984,11 @@ class FigInputWheel extends HTMLElement {
5960
5984
  this.#dragStartValue = Number.isFinite(requestedStart)
5961
5985
  ? this.#clamp(requestedStart)
5962
5986
  : this.#numericValue();
5987
+ this.#hasCrossedDragThreshold = false;
5963
5988
  this.#startTickMotionTracking(clientX);
5964
5989
  this.#startHandlePull();
5965
5990
  this.setAttribute("data-fig-input-wheel-active", "");
5966
- document.body.classList.add(FigInputWheel.#DRAGGING_BODY_CLASS);
5991
+ document.body.classList.remove(FigInputWheel.#DRAGGING_BODY_CLASS);
5967
5992
  this.focus();
5968
5993
  return true;
5969
5994
  }
@@ -6000,6 +6025,14 @@ class FigInputWheel extends HTMLElement {
6000
6025
  }
6001
6026
 
6002
6027
  #updateDrag(clientX, speed = 1) {
6028
+ if (
6029
+ !this.#hasCrossedDragThreshold &&
6030
+ Math.abs(clientX - this.#dragStartX) >=
6031
+ FigInputWheel.#DRAG_THRESHOLD_PX
6032
+ ) {
6033
+ this.#hasCrossedDragThreshold = true;
6034
+ document.body.classList.add(FigInputWheel.#DRAGGING_BODY_CLASS);
6035
+ }
6003
6036
  const width = this.#wheel?.clientWidth || 1;
6004
6037
  const visibleSteps =
6005
6038
  FigInputWheel.#TICK_COUNT *
@@ -6033,6 +6066,7 @@ class FigInputWheel extends HTMLElement {
6033
6066
  #stopDrag() {
6034
6067
  this.#isDragging = false;
6035
6068
  this.#dragPointerId = null;
6069
+ this.#hasCrossedDragThreshold = false;
6036
6070
  this.#visualValue = null;
6037
6071
  this.removeAttribute("data-fig-input-wheel-active");
6038
6072
  document.body.classList.remove(FigInputWheel.#DRAGGING_BODY_CLASS);
@@ -6291,7 +6325,7 @@ class FigInputWheel extends HTMLElement {
6291
6325
  this.#commitValue(Number.isFinite(parsed) ? parsed : 0);
6292
6326
  }
6293
6327
 
6294
- spinTo(nextValue) {
6328
+ spinTo(nextValue, { animateHandle = false } = {}) {
6295
6329
  const previousValue = this.#numericValue();
6296
6330
  this.value = nextValue;
6297
6331
  const value = this.#numericValue();
@@ -6305,7 +6339,7 @@ class FigInputWheel extends HTMLElement {
6305
6339
  value,
6306
6340
  direction,
6307
6341
  Math.abs(value - previousValue) > this.#step() ? 10 : 1,
6308
- false,
6342
+ animateHandle,
6309
6343
  );
6310
6344
  return value;
6311
6345
  }
@@ -6395,6 +6429,7 @@ class PropskitWheel extends HTMLElement {
6395
6429
  #elasticMaxPx = 0;
6396
6430
  #elasticRangeRect = null;
6397
6431
  #elasticHostWidth = 0;
6432
+ #surfacePointerId = null;
6398
6433
  #numberPointerId = null;
6399
6434
  #numberPointerStartX = 0;
6400
6435
  #numberPointerStartY = 0;
@@ -6402,14 +6437,18 @@ class PropskitWheel extends HTMLElement {
6402
6437
  #isNumberScrubbing = false;
6403
6438
  #suppressNumberClick = false;
6404
6439
  #numberClickResetTimer = 0;
6440
+ #animateNumberInputHandle = false;
6405
6441
  #boundPrimitiveInput = this.#handlePrimitiveEvent.bind(this, "input");
6406
6442
  #boundPrimitiveChange = this.#handlePrimitiveEvent.bind(this, "change");
6407
6443
  #boundNumberInput = this.#handleNumberEvent.bind(this, "input");
6408
6444
  #boundNumberChange = this.#handleNumberEvent.bind(this, "change");
6445
+ #boundNumberKeyDown = this.#handleNumberKeyDown.bind(this);
6409
6446
  #boundNumberPointerDown = this.#handleNumberPointerDown.bind(this);
6410
6447
  #boundNumberPointerMove = this.#handleNumberPointerMove.bind(this);
6411
6448
  #boundNumberPointerEnd = this.#handleNumberPointerEnd.bind(this);
6412
- #boundElasticPointerDown = this.#handleElasticPointerDown.bind(this);
6449
+ #boundSurfacePointerDown = this.#handleSurfacePointerDown.bind(this);
6450
+ #boundSurfacePointerMove = this.#handleSurfacePointerMove.bind(this);
6451
+ #boundSurfacePointerEnd = this.#handleSurfacePointerEnd.bind(this);
6413
6452
  #boundElasticPointerMove = this.#handleElasticPointerMove.bind(this);
6414
6453
  #boundElasticPointerEnd = this.#handleElasticPointerEnd.bind(this);
6415
6454
  #boundClick = this.#handleClick.bind(this);
@@ -6442,6 +6481,7 @@ class PropskitWheel extends HTMLElement {
6442
6481
 
6443
6482
  disconnectedCallback() {
6444
6483
  this.#observer?.disconnect();
6484
+ this.#stopSurfaceTracking();
6445
6485
  this.#stopElasticTracking();
6446
6486
  this.#unbindEvents();
6447
6487
  this.#stopNumberTracking();
@@ -6660,7 +6700,10 @@ class PropskitWheel extends HTMLElement {
6660
6700
  this.#wheel?.addEventListener("change", this.#boundPrimitiveChange);
6661
6701
  this.#input?.addEventListener("input", this.#boundNumberInput);
6662
6702
  this.#input?.addEventListener("change", this.#boundNumberChange);
6663
- this.addEventListener("pointerdown", this.#boundElasticPointerDown, {
6703
+ this.#input?.addEventListener("keydown", this.#boundNumberKeyDown, {
6704
+ capture: true,
6705
+ });
6706
+ this.addEventListener("pointerdown", this.#boundSurfacePointerDown, {
6664
6707
  capture: true,
6665
6708
  });
6666
6709
  this.addEventListener("pointerdown", this.#boundNumberPointerDown, {
@@ -6674,7 +6717,10 @@ class PropskitWheel extends HTMLElement {
6674
6717
  this.#wheel?.removeEventListener("change", this.#boundPrimitiveChange);
6675
6718
  this.#input?.removeEventListener("input", this.#boundNumberInput);
6676
6719
  this.#input?.removeEventListener("change", this.#boundNumberChange);
6677
- this.removeEventListener("pointerdown", this.#boundElasticPointerDown, {
6720
+ this.#input?.removeEventListener("keydown", this.#boundNumberKeyDown, {
6721
+ capture: true,
6722
+ });
6723
+ this.removeEventListener("pointerdown", this.#boundSurfacePointerDown, {
6678
6724
  capture: true,
6679
6725
  });
6680
6726
  this.removeEventListener("pointerdown", this.#boundNumberPointerDown, {
@@ -6683,7 +6729,10 @@ class PropskitWheel extends HTMLElement {
6683
6729
  this.removeEventListener("click", this.#boundClick, true);
6684
6730
  }
6685
6731
 
6686
- #setSynchronizedValue(value, { spin = false } = {}) {
6732
+ #setSynchronizedValue(
6733
+ value,
6734
+ { spin = false, animateHandle = false } = {},
6735
+ ) {
6687
6736
  const parsed = Number(value);
6688
6737
  if (!this.#wheel) {
6689
6738
  const normalized = Number.isFinite(parsed) ? parsed : 0;
@@ -6691,7 +6740,7 @@ class PropskitWheel extends HTMLElement {
6691
6740
  return normalized;
6692
6741
  }
6693
6742
  const nextValue = Number.isFinite(parsed) ? parsed : 0;
6694
- if (spin) this.#wheel.spinTo(nextValue);
6743
+ if (spin) this.#wheel.spinTo(nextValue, { animateHandle });
6695
6744
  else this.#wheel.value = nextValue;
6696
6745
  const normalized = this.#wheel.value;
6697
6746
  if (this.getAttribute("value") !== normalized) {
@@ -6728,22 +6777,78 @@ class PropskitWheel extends HTMLElement {
6728
6777
  event instanceof CustomEvent && event.detail !== undefined
6729
6778
  ? event.detail
6730
6779
  : this.#input?.value;
6731
- const value = this.#setSynchronizedValue(raw, { spin: type === "input" });
6780
+ const value = this.#setSynchronizedValue(raw, {
6781
+ spin: type === "input",
6782
+ animateHandle: type === "input" && this.#animateNumberInputHandle,
6783
+ });
6784
+ if (type === "input") this.#animateNumberInputHandle = false;
6732
6785
  this.#emit(type, value);
6733
6786
  }
6734
6787
 
6735
- #handleElasticPointerDown(event) {
6788
+ #handleNumberKeyDown(event) {
6789
+ this.#animateNumberInputHandle =
6790
+ !figLabBooleanAttribute(this, "disabled") &&
6791
+ (event.key === "ArrowUp" || event.key === "ArrowDown");
6792
+ window.setTimeout(() => {
6793
+ this.#animateNumberInputHandle = false;
6794
+ }, 0);
6795
+ }
6796
+
6797
+ #handleSurfacePointerDown(event) {
6736
6798
  if (
6737
6799
  event.button !== 0 ||
6738
6800
  figLabBooleanAttribute(this, "disabled") ||
6739
- this.getAttribute("elastic") === "false" ||
6740
6801
  !(event.target instanceof Element) ||
6741
6802
  event.target.closest("fig-input-number") ||
6742
- !event.target.closest("fig-input-wheel")
6803
+ event.target.closest(".propskit-wheel-surface") !== this.#surface
6743
6804
  ) {
6744
6805
  return;
6745
6806
  }
6807
+ event.preventDefault();
6808
+ event.stopImmediatePropagation();
6809
+ this.#stopSurfaceTracking();
6810
+ const started = this.#wheel?.beginScrub({
6811
+ clientX: event.clientX,
6812
+ pointerId: event.pointerId,
6813
+ startValue: Number(this.#wheel.value),
6814
+ });
6815
+ if (!started) return;
6816
+ this.#surfacePointerId = event.pointerId;
6746
6817
  this.#startElasticTracking(event.pointerId);
6818
+ window.addEventListener("pointermove", this.#boundSurfacePointerMove);
6819
+ window.addEventListener("pointerup", this.#boundSurfacePointerEnd);
6820
+ window.addEventListener("pointercancel", this.#boundSurfacePointerEnd);
6821
+ window.addEventListener("blur", this.#boundSurfacePointerEnd);
6822
+ }
6823
+
6824
+ #handleSurfacePointerMove(event) {
6825
+ if (event.pointerId !== this.#surfacePointerId) return;
6826
+ if (event.buttons === 0) {
6827
+ this.#handleSurfacePointerEnd(event);
6828
+ return;
6829
+ }
6830
+ this.#wheel?.updateScrub(event);
6831
+ }
6832
+
6833
+ #handleSurfacePointerEnd(event) {
6834
+ if (
6835
+ event?.pointerId !== undefined &&
6836
+ this.#surfacePointerId !== null &&
6837
+ event.pointerId !== this.#surfacePointerId
6838
+ ) {
6839
+ return;
6840
+ }
6841
+ this.#wheel?.endScrub();
6842
+ this.#stopSurfaceTracking();
6843
+ this.#stopElasticTracking();
6844
+ }
6845
+
6846
+ #stopSurfaceTracking() {
6847
+ window.removeEventListener("pointermove", this.#boundSurfacePointerMove);
6848
+ window.removeEventListener("pointerup", this.#boundSurfacePointerEnd);
6849
+ window.removeEventListener("pointercancel", this.#boundSurfacePointerEnd);
6850
+ window.removeEventListener("blur", this.#boundSurfacePointerEnd);
6851
+ this.#surfacePointerId = null;
6747
6852
  }
6748
6853
 
6749
6854
  #startElasticTracking(pointerId) {
package/fig.js CHANGED
@@ -595,6 +595,7 @@ function figSupportsPopover() {
595
595
  * @attr {string} type - The button type: "button" (default), "toggle", "submit", or "link"
596
596
  * @attr {boolean} selected - Whether the button is in a selected state
597
597
  * @attr {boolean} disabled - Whether the button is disabled
598
+ * @attr {string} align - Content alignment: "start", "center" (default), or "end"
598
599
  * @attr {string} href - URL for link type buttons
599
600
  * @attr {string} target - Target window for link type buttons (e.g., "_blank")
600
601
  */
@@ -637,9 +638,9 @@ class FigButton extends HTMLElement {
637
638
  display: flex;
638
639
  border: 0;
639
640
  flex: 1;
640
- text-align: center;
641
+ text-align: var(--fig-button-text-alignment, center);
641
642
  align-items: stretch;
642
- justify-content: center;
643
+ justify-content: var(--fig-button-content-alignment, center);
643
644
  font: inherit;
644
645
  color: inherit;
645
646
  outline: 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "8.9.39",
3
+ "version": "8.9.41",
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",