@rogieking/figui3 8.9.32 → 8.9.34
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/.cursor/skills/fig-lab/SKILL.md +10 -7
- package/.cursor/skills/fig-lab/reference.md +18 -4
- package/.cursor/skills/figui3/SKILL.md +3 -3
- package/README.md +38 -5
- package/dist/fig-lab.css +1 -1
- package/dist/fig-lab.js +2 -2
- package/fig-lab.css +147 -105
- package/fig-lab.js +761 -431
- package/package.json +1 -1
package/fig-lab.js
CHANGED
|
@@ -5546,43 +5546,26 @@ class PropskitSlider extends HTMLElement {
|
|
|
5546
5546
|
figLabDefineElement("propskit-slider", PropskitSlider);
|
|
5547
5547
|
|
|
5548
5548
|
/**
|
|
5549
|
-
*
|
|
5549
|
+
* Standalone interactive numeric wheel.
|
|
5550
5550
|
*
|
|
5551
|
-
* @attr {string} label - Field label. Omitted defaults to "Value"; authored values (including blank) are used as-is.
|
|
5552
|
-
* @attr {string} units - Optional unit forwarded to fig-input-number. Time aliases are normalized.
|
|
5553
5551
|
* @attr {number} value - Numeric value. Unbounded unless min/max are set.
|
|
5554
5552
|
* @attr {number} min - Inclusive lower bound. Omitted = unbounded below.
|
|
5555
5553
|
* @attr {number} max - Inclusive upper bound. Omitted = unbounded above.
|
|
5556
|
-
* @attr {
|
|
5557
|
-
* @attr {
|
|
5558
|
-
* @attr {
|
|
5559
|
-
* @attr {boolean|string}
|
|
5560
|
-
* @
|
|
5561
|
-
* @
|
|
5562
|
-
* @attr {string} variant - Set to "minimal" for compact chrome.
|
|
5563
|
-
* @fires input - Composed event while dragging or typing.
|
|
5564
|
-
* @fires change - Composed event on commit.
|
|
5554
|
+
* @attr {number} step - Increment. Defaults to 1.
|
|
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
|
+
* @attr {boolean|string} disabled - Disables interaction and focus.
|
|
5558
|
+
* @fires input - Numeric composed event during interaction.
|
|
5559
|
+
* @fires change - Numeric composed event on commit.
|
|
5565
5560
|
*/
|
|
5566
|
-
class
|
|
5561
|
+
class FigInputWheel extends HTMLElement {
|
|
5567
5562
|
static #TICK_COUNT = 33;
|
|
5568
5563
|
static #HALF_FOV_DEG = 60;
|
|
5569
5564
|
static #PERSPECTIVE_K = 0.55;
|
|
5570
|
-
static #DRAGGING_BODY_CLASS = "fig-
|
|
5571
|
-
static #RESERVED_ATTRS = new Set([
|
|
5572
|
-
"label",
|
|
5573
|
-
"size",
|
|
5574
|
-
"disabled",
|
|
5575
|
-
"variant",
|
|
5576
|
-
"elastic",
|
|
5577
|
-
"default",
|
|
5578
|
-
"class",
|
|
5579
|
-
"style",
|
|
5580
|
-
"id",
|
|
5581
|
-
"oninput",
|
|
5582
|
-
"onchange",
|
|
5583
|
-
]);
|
|
5565
|
+
static #DRAGGING_BODY_CLASS = "fig-input-wheel-dragging";
|
|
5584
5566
|
|
|
5585
5567
|
#surface = null;
|
|
5568
|
+
#track = null;
|
|
5586
5569
|
#wheel = null;
|
|
5587
5570
|
#svg = null;
|
|
5588
5571
|
#tickPath = null;
|
|
@@ -5593,13 +5576,7 @@ class PropskitWheel extends HTMLElement {
|
|
|
5593
5576
|
#layoutFrame = 0;
|
|
5594
5577
|
#keyboardAnimationFrame = 0;
|
|
5595
5578
|
#keyboardSettleTimer = 0;
|
|
5596
|
-
#label = null;
|
|
5597
|
-
#input = null;
|
|
5598
|
-
#hasCustomLabel = false;
|
|
5599
|
-
#observer = null;
|
|
5600
5579
|
#resizeObserver = null;
|
|
5601
|
-
#managedInputAttrs = new Set();
|
|
5602
|
-
#initialValue = null;
|
|
5603
5580
|
#isDragging = false;
|
|
5604
5581
|
#dragPointerId = null;
|
|
5605
5582
|
#dragStartX = 0;
|
|
@@ -5609,168 +5586,115 @@ class PropskitWheel extends HTMLElement {
|
|
|
5609
5586
|
#elasticMaxPx = 0;
|
|
5610
5587
|
#elasticRangeRect = null;
|
|
5611
5588
|
#elasticHostWidth = 0;
|
|
5612
|
-
#numberPointerId = null;
|
|
5613
|
-
#numberPointerStartX = 0;
|
|
5614
|
-
#numberPointerStartY = 0;
|
|
5615
|
-
#numberPointerStartValue = 0;
|
|
5616
|
-
#isNumberScrubbing = false;
|
|
5617
|
-
#suppressNumberClick = false;
|
|
5618
|
-
#numberClickResetTimer = 0;
|
|
5619
|
-
#boundHandleInput = null;
|
|
5620
|
-
#boundHandleChange = null;
|
|
5621
5589
|
#boundPointerDown = this.#handlePointerDown.bind(this);
|
|
5622
5590
|
#boundPointerMove = this.#handlePointerMove.bind(this);
|
|
5623
5591
|
#boundPointerUp = this.#handlePointerUp.bind(this);
|
|
5624
|
-
#boundNumberPointerDown = this.#handleNumberPointerDown.bind(this);
|
|
5625
|
-
#boundNumberPointerMove = this.#handleNumberPointerMove.bind(this);
|
|
5626
|
-
#boundNumberPointerEnd = this.#handleNumberPointerEnd.bind(this);
|
|
5627
5592
|
#boundWheel = this.#handleWheel.bind(this);
|
|
5628
5593
|
#boundKeyDown = this.#handleKeyDown.bind(this);
|
|
5629
|
-
#boundClick = this.#handleClick.bind(this);
|
|
5630
5594
|
|
|
5631
5595
|
static get observedAttributes() {
|
|
5632
|
-
return [
|
|
5596
|
+
return [
|
|
5597
|
+
"value",
|
|
5598
|
+
"disabled",
|
|
5599
|
+
"step",
|
|
5600
|
+
"min",
|
|
5601
|
+
"max",
|
|
5602
|
+
"spin",
|
|
5603
|
+
"elastic",
|
|
5604
|
+
];
|
|
5633
5605
|
}
|
|
5634
5606
|
|
|
5635
5607
|
connectedCallback() {
|
|
5636
5608
|
if (!this.#surface) this.#initialize();
|
|
5637
|
-
this.#syncLabel();
|
|
5638
5609
|
this.#syncDisabled();
|
|
5639
|
-
this.#syncInputAttributes();
|
|
5640
5610
|
this.#syncValueFromHost();
|
|
5641
5611
|
this.#bindEvents();
|
|
5642
|
-
figLabConnectPropskitResetMenu(this);
|
|
5643
|
-
this.#observer?.disconnect();
|
|
5644
|
-
this.#observer = new MutationObserver((mutations) => {
|
|
5645
|
-
let syncInput = false;
|
|
5646
|
-
for (const mutation of mutations) {
|
|
5647
|
-
if (mutation.type !== "attributes") continue;
|
|
5648
|
-
const name = mutation.attributeName;
|
|
5649
|
-
if (
|
|
5650
|
-
!PropskitWheel.#RESERVED_ATTRS.has(name) &&
|
|
5651
|
-
!PropskitWheel.observedAttributes.includes(name) &&
|
|
5652
|
-
!name?.startsWith("data-")
|
|
5653
|
-
) {
|
|
5654
|
-
syncInput = true;
|
|
5655
|
-
}
|
|
5656
|
-
}
|
|
5657
|
-
if (syncInput) this.#syncInputAttributes();
|
|
5658
|
-
});
|
|
5659
|
-
this.#observer.observe(this, { attributes: true });
|
|
5660
5612
|
this.#resizeObserver?.disconnect();
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5613
|
+
if (globalThis.ResizeObserver) {
|
|
5614
|
+
this.#resizeObserver = new ResizeObserver((entries) => {
|
|
5615
|
+
const rect = entries.at(-1)?.contentRect;
|
|
5616
|
+
if (rect) this.#setWheelSize(rect.width, rect.height);
|
|
5617
|
+
});
|
|
5618
|
+
this.#resizeObserver.observe(this);
|
|
5619
|
+
}
|
|
5666
5620
|
this.#queueWheelLayout();
|
|
5667
5621
|
}
|
|
5668
5622
|
|
|
5669
5623
|
disconnectedCallback() {
|
|
5670
|
-
this.#observer?.disconnect();
|
|
5671
5624
|
this.#resizeObserver?.disconnect();
|
|
5672
5625
|
if (this.#layoutFrame) cancelAnimationFrame(this.#layoutFrame);
|
|
5673
5626
|
this.#layoutFrame = 0;
|
|
5674
5627
|
this.#stopKeyboardAnimation();
|
|
5675
5628
|
this.#unbindEvents();
|
|
5676
5629
|
this.#stopDrag();
|
|
5677
|
-
figLabDisconnectPropskitResetMenu(this);
|
|
5678
5630
|
}
|
|
5679
5631
|
|
|
5680
5632
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
5681
5633
|
if (oldValue === newValue || !this.#surface) return;
|
|
5682
|
-
if (name === "label") this.#syncLabel();
|
|
5683
5634
|
if (name === "disabled") this.#syncDisabled();
|
|
5684
5635
|
if (name === "value" && !this.#isDragging) {
|
|
5685
5636
|
this.#syncValueFromHost();
|
|
5686
5637
|
}
|
|
5687
|
-
if (name === "
|
|
5688
|
-
this.#
|
|
5638
|
+
if (name === "step") {
|
|
5639
|
+
this.#syncWheelAria();
|
|
5689
5640
|
this.#queueWheelLayout();
|
|
5690
5641
|
}
|
|
5642
|
+
if (name === "spin") this.#queueWheelLayout();
|
|
5691
5643
|
if (name === "min" || name === "max") {
|
|
5692
|
-
this.#syncInputAttributes();
|
|
5693
5644
|
this.#commitValue(this.#numericValue());
|
|
5694
5645
|
}
|
|
5695
5646
|
}
|
|
5696
5647
|
|
|
5697
5648
|
#initialize() {
|
|
5698
|
-
this.#
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
)
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
});
|
|
5710
|
-
const wheel = figLabCreateElement("div", {
|
|
5711
|
-
className: "propskit-wheel-wheel",
|
|
5712
|
-
tabindex: "0",
|
|
5713
|
-
role: "spinbutton",
|
|
5714
|
-
});
|
|
5649
|
+
this.#surface = this;
|
|
5650
|
+
this.#track = this;
|
|
5651
|
+
this.#wheel = this;
|
|
5652
|
+
this.setAttribute("role", "spinbutton");
|
|
5653
|
+
if (
|
|
5654
|
+
!this.hasAttribute("aria-label") &&
|
|
5655
|
+
!this.hasAttribute("aria-labelledby")
|
|
5656
|
+
) {
|
|
5657
|
+
this.setAttribute("aria-label", "Value");
|
|
5658
|
+
}
|
|
5659
|
+
this.setAttribute("tabindex", "0");
|
|
5715
5660
|
const svg = figLabCreateSvgElement("svg", {
|
|
5716
|
-
className: "
|
|
5661
|
+
className: "fig-input-wheel-svg",
|
|
5717
5662
|
"aria-hidden": "true",
|
|
5718
5663
|
});
|
|
5719
|
-
const label = customLabel || document.createElement("label");
|
|
5720
|
-
const input = document.createElement("fig-input-number");
|
|
5721
|
-
const labelId = figLabUniqueId("propskit-wheel-label");
|
|
5722
|
-
label.id = labelId;
|
|
5723
|
-
wheel.setAttribute("aria-labelledby", labelId);
|
|
5724
|
-
|
|
5725
5664
|
const handle = figLabCreateElement("div", {
|
|
5726
|
-
className: "
|
|
5665
|
+
className: "fig-input-wheel-handle",
|
|
5727
5666
|
});
|
|
5728
|
-
wheel.append(svg);
|
|
5729
|
-
surface.append(wheel, handle, label, input);
|
|
5730
|
-
this.#surface = surface;
|
|
5731
|
-
this.#wheel = wheel;
|
|
5732
5667
|
this.#svg = svg;
|
|
5733
|
-
this
|
|
5734
|
-
this.#input = input;
|
|
5735
|
-
this.#hasCustomLabel = Boolean(customLabel);
|
|
5736
|
-
this.replaceChildren(surface);
|
|
5668
|
+
this.replaceChildren(svg, handle);
|
|
5737
5669
|
|
|
5738
5670
|
const metricProbe = document.createElement("div");
|
|
5739
5671
|
metricProbe.setAttribute("aria-hidden", "true");
|
|
5740
5672
|
metricProbe.style.cssText =
|
|
5741
5673
|
"position:absolute;visibility:hidden;pointer-events:none;left:0;top:0";
|
|
5742
5674
|
const maxEl = document.createElement("div");
|
|
5743
|
-
maxEl.style.height = "var(--
|
|
5675
|
+
maxEl.style.height = "var(--fig-input-wheel-tick-height)";
|
|
5744
5676
|
const minEl = document.createElement("div");
|
|
5745
|
-
minEl.style.height = "var(--
|
|
5677
|
+
minEl.style.height = "var(--fig-input-wheel-tick-height-min)";
|
|
5746
5678
|
const maxWidthEl = document.createElement("div");
|
|
5747
|
-
maxWidthEl.style.width = "var(--
|
|
5679
|
+
maxWidthEl.style.width = "var(--fig-input-wheel-tick-width)";
|
|
5748
5680
|
const minWidthEl = document.createElement("div");
|
|
5749
|
-
minWidthEl.style.width = "var(--
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
metricProbe.append(maxEl, minEl, maxWidthEl, minWidthEl, insetEl);
|
|
5753
|
-
surface.append(metricProbe);
|
|
5681
|
+
minWidthEl.style.width = "var(--fig-input-wheel-tick-width-min)";
|
|
5682
|
+
metricProbe.append(maxEl, minEl, maxWidthEl, minWidthEl);
|
|
5683
|
+
this.append(metricProbe);
|
|
5754
5684
|
this.#tickMetricProbe = {
|
|
5755
5685
|
maxEl,
|
|
5756
5686
|
minEl,
|
|
5757
5687
|
maxWidthEl,
|
|
5758
5688
|
minWidthEl,
|
|
5759
|
-
insetEl,
|
|
5760
5689
|
};
|
|
5761
5690
|
|
|
5762
|
-
for (const node of initialChildren) {
|
|
5763
|
-
if (node === customLabel) continue;
|
|
5764
|
-
input.appendChild(node);
|
|
5765
|
-
}
|
|
5766
|
-
|
|
5767
5691
|
this.#ensureTickPath();
|
|
5768
5692
|
}
|
|
5769
5693
|
|
|
5770
5694
|
#ensureTickPath() {
|
|
5771
5695
|
if (!this.#svg || this.#tickPath) return;
|
|
5772
5696
|
this.#tickPath = figLabCreateSvgElement("path", {
|
|
5773
|
-
className: "
|
|
5697
|
+
className: "fig-input-wheel-tick",
|
|
5774
5698
|
});
|
|
5775
5699
|
this.#svg.append(this.#tickPath);
|
|
5776
5700
|
}
|
|
@@ -5784,60 +5708,20 @@ class PropskitWheel extends HTMLElement {
|
|
|
5784
5708
|
this.#tickMetricProbe?.maxWidthEl.getBoundingClientRect().width || 2,
|
|
5785
5709
|
minW:
|
|
5786
5710
|
this.#tickMetricProbe?.minWidthEl.getBoundingClientRect().width || 1,
|
|
5787
|
-
inset: this.#tickMetricProbe?.insetEl.getBoundingClientRect().width || 0,
|
|
5788
5711
|
};
|
|
5789
5712
|
return this.#tickMetrics;
|
|
5790
5713
|
}
|
|
5791
5714
|
|
|
5792
|
-
#units() {
|
|
5793
|
-
const raw = (this.getAttribute("units") || "").trim();
|
|
5794
|
-
const normalized = raw.toLowerCase();
|
|
5795
|
-
if (
|
|
5796
|
-
normalized === "ms" ||
|
|
5797
|
-
normalized === "millisecond" ||
|
|
5798
|
-
normalized === "milliseconds"
|
|
5799
|
-
) {
|
|
5800
|
-
return "ms";
|
|
5801
|
-
}
|
|
5802
|
-
if (
|
|
5803
|
-
normalized === "s" ||
|
|
5804
|
-
normalized === "second" ||
|
|
5805
|
-
normalized === "seconds"
|
|
5806
|
-
) {
|
|
5807
|
-
return "s";
|
|
5808
|
-
}
|
|
5809
|
-
return raw;
|
|
5810
|
-
}
|
|
5811
|
-
|
|
5812
|
-
#defaultStep() {
|
|
5813
|
-
const units = this.#units();
|
|
5814
|
-
if (units === "s") return 0.1;
|
|
5815
|
-
if (units === "ms") return 100;
|
|
5816
|
-
return 1;
|
|
5817
|
-
}
|
|
5818
|
-
|
|
5819
|
-
#defaultPrecision() {
|
|
5820
|
-
return this.#units() === "s" ? 2 : 0;
|
|
5821
|
-
}
|
|
5822
|
-
|
|
5823
5715
|
#step() {
|
|
5824
5716
|
if (this.hasAttribute("step")) {
|
|
5825
5717
|
const parsed = Number(this.getAttribute("step"));
|
|
5826
5718
|
if (parsed > 0) return parsed;
|
|
5827
5719
|
}
|
|
5828
|
-
return
|
|
5829
|
-
}
|
|
5830
|
-
|
|
5831
|
-
#precision() {
|
|
5832
|
-
if (this.hasAttribute("precision")) {
|
|
5833
|
-
const parsed = Number(this.getAttribute("precision"));
|
|
5834
|
-
if (Number.isInteger(parsed) && parsed >= 0) return parsed;
|
|
5835
|
-
}
|
|
5836
|
-
return this.#defaultPrecision();
|
|
5720
|
+
return 1;
|
|
5837
5721
|
}
|
|
5838
5722
|
|
|
5839
5723
|
#numericValue() {
|
|
5840
|
-
const parsed = Number(this.getAttribute("value") ??
|
|
5724
|
+
const parsed = Number(this.getAttribute("value") ?? 0);
|
|
5841
5725
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
5842
5726
|
}
|
|
5843
5727
|
|
|
@@ -5894,9 +5778,6 @@ class PropskitWheel extends HTMLElement {
|
|
|
5894
5778
|
if (this.getAttribute("value") !== asString) {
|
|
5895
5779
|
this.setAttribute("value", asString);
|
|
5896
5780
|
}
|
|
5897
|
-
if (this.#input?.getAttribute("value") !== asString) {
|
|
5898
|
-
this.#input?.setAttribute("value", asString);
|
|
5899
|
-
}
|
|
5900
5781
|
this.#syncWheelAria(next);
|
|
5901
5782
|
this.#queueWheelLayout();
|
|
5902
5783
|
if (emit) {
|
|
@@ -5914,53 +5795,35 @@ class PropskitWheel extends HTMLElement {
|
|
|
5914
5795
|
#syncValueFromHost() {
|
|
5915
5796
|
const value = this.#numericValue();
|
|
5916
5797
|
const clamped = this.#clamp(value);
|
|
5917
|
-
if (
|
|
5798
|
+
if (
|
|
5799
|
+
clamped !== value ||
|
|
5800
|
+
this.getAttribute("value") !== String(clamped)
|
|
5801
|
+
) {
|
|
5918
5802
|
this.#commitValue(clamped);
|
|
5919
5803
|
return;
|
|
5920
5804
|
}
|
|
5921
|
-
const asString = String(value);
|
|
5922
|
-
if (this.#input?.getAttribute("value") !== asString) {
|
|
5923
|
-
this.#input?.setAttribute("value", asString);
|
|
5924
|
-
}
|
|
5925
5805
|
this.#syncWheelAria(value);
|
|
5926
5806
|
this.#queueWheelLayout();
|
|
5927
5807
|
}
|
|
5928
5808
|
|
|
5929
5809
|
#syncWheelAria(value = this.#numericValue()) {
|
|
5930
5810
|
if (!this.#wheel) return;
|
|
5931
|
-
const units = this.#units();
|
|
5932
5811
|
const min = this.#boundMin();
|
|
5933
5812
|
const max = this.#boundMax();
|
|
5934
5813
|
this.#wheel.setAttribute("aria-valuenow", String(value));
|
|
5935
|
-
this.#wheel.setAttribute(
|
|
5936
|
-
"aria-valuetext",
|
|
5937
|
-
units
|
|
5938
|
-
? `${value} ${
|
|
5939
|
-
units === "s" ? "seconds" : units === "ms" ? "milliseconds" : units
|
|
5940
|
-
}`
|
|
5941
|
-
: String(value),
|
|
5942
|
-
);
|
|
5814
|
+
this.#wheel.setAttribute("aria-valuetext", String(value));
|
|
5943
5815
|
if (min === null) this.#wheel.removeAttribute("aria-valuemin");
|
|
5944
5816
|
else this.#wheel.setAttribute("aria-valuemin", String(min));
|
|
5945
5817
|
if (max === null) this.#wheel.removeAttribute("aria-valuemax");
|
|
5946
5818
|
else this.#wheel.setAttribute("aria-valuemax", String(max));
|
|
5947
5819
|
}
|
|
5948
5820
|
|
|
5949
|
-
#syncLabel() {
|
|
5950
|
-
if (!this.#label) return;
|
|
5951
|
-
if (this.#hasCustomLabel) return;
|
|
5952
|
-
if (!this.hasAttribute("label")) {
|
|
5953
|
-
this.#label.textContent = "Value";
|
|
5954
|
-
return;
|
|
5955
|
-
}
|
|
5956
|
-
this.#label.textContent = this.getAttribute("label") ?? "";
|
|
5957
|
-
}
|
|
5958
|
-
|
|
5959
5821
|
#syncDisabled() {
|
|
5960
5822
|
const disabled = figLabBooleanAttribute(this, "disabled");
|
|
5961
|
-
this.#input?.toggleAttribute("disabled", disabled);
|
|
5962
5823
|
if (!this.#wheel) return;
|
|
5963
5824
|
if (disabled) {
|
|
5825
|
+
if (this.#isDragging) this.#stopDrag();
|
|
5826
|
+
this.#stopKeyboardAnimation();
|
|
5964
5827
|
this.setAttribute("aria-disabled", "true");
|
|
5965
5828
|
this.#wheel.setAttribute("tabindex", "-1");
|
|
5966
5829
|
this.#wheel.setAttribute("aria-disabled", "true");
|
|
@@ -5971,39 +5834,6 @@ class PropskitWheel extends HTMLElement {
|
|
|
5971
5834
|
}
|
|
5972
5835
|
}
|
|
5973
5836
|
|
|
5974
|
-
#getForwardedInputAttrNames() {
|
|
5975
|
-
return this.getAttributeNames().filter(
|
|
5976
|
-
(name) =>
|
|
5977
|
-
!PropskitWheel.#RESERVED_ATTRS.has(name) && !name.startsWith("data-"),
|
|
5978
|
-
);
|
|
5979
|
-
}
|
|
5980
|
-
|
|
5981
|
-
#syncInputAttributes() {
|
|
5982
|
-
if (!this.#input) return;
|
|
5983
|
-
const inputAttrs = this.#getForwardedInputAttrNames();
|
|
5984
|
-
const nextManaged = new Set(inputAttrs);
|
|
5985
|
-
for (const attrName of this.#managedInputAttrs) {
|
|
5986
|
-
if (!nextManaged.has(attrName)) this.#input.removeAttribute(attrName);
|
|
5987
|
-
}
|
|
5988
|
-
for (const attrName of inputAttrs) {
|
|
5989
|
-
this.#input.setAttribute(attrName, this.getAttribute(attrName) ?? "");
|
|
5990
|
-
}
|
|
5991
|
-
const units = this.#units();
|
|
5992
|
-
if (units) this.#input.setAttribute("units", units);
|
|
5993
|
-
else this.#input.removeAttribute("units");
|
|
5994
|
-
nextManaged.add("units");
|
|
5995
|
-
if (!this.hasAttribute("step")) {
|
|
5996
|
-
this.#input.setAttribute("step", String(this.#defaultStep()));
|
|
5997
|
-
nextManaged.add("step");
|
|
5998
|
-
}
|
|
5999
|
-
if (!this.hasAttribute("precision")) {
|
|
6000
|
-
this.#input.setAttribute("precision", String(this.#defaultPrecision()));
|
|
6001
|
-
nextManaged.add("precision");
|
|
6002
|
-
}
|
|
6003
|
-
this.#managedInputAttrs = nextManaged;
|
|
6004
|
-
this.#syncWheelAria();
|
|
6005
|
-
}
|
|
6006
|
-
|
|
6007
5837
|
#setWheelSize(width, height) {
|
|
6008
5838
|
if (!this.#svg || width < 1 || height < 1) return;
|
|
6009
5839
|
if (width === this.#wheelWidth && height === this.#wheelHeight) return;
|
|
@@ -6038,23 +5868,25 @@ class PropskitWheel extends HTMLElement {
|
|
|
6038
5868
|
const height = this.#wheelHeight;
|
|
6039
5869
|
if (width < 1 || height < 1) return;
|
|
6040
5870
|
|
|
6041
|
-
const value =
|
|
6042
|
-
|
|
5871
|
+
const value =
|
|
5872
|
+
this.getAttribute("spin") === "false"
|
|
5873
|
+
? (this.#boundMin() ?? 0)
|
|
5874
|
+
: (this.#visualValue ?? this.#numericValue());
|
|
5875
|
+
const tickStep = 360 / FigInputWheel.#TICK_COUNT;
|
|
6043
5876
|
const step = this.#step();
|
|
6044
5877
|
const base = this.#boundMin() ?? 0;
|
|
6045
5878
|
const offsetDeg = ((value - base) / step) * tickStep;
|
|
6046
|
-
const k =
|
|
5879
|
+
const k = FigInputWheel.#PERSPECTIVE_K;
|
|
6047
5880
|
const cx = width / 2;
|
|
6048
5881
|
const cy = height / 2;
|
|
6049
5882
|
|
|
6050
|
-
const { maxH, minH, maxW, minW
|
|
6051
|
-
const
|
|
6052
|
-
const
|
|
6053
|
-
const
|
|
6054
|
-
const visibleHalf = Math.max(drawableWidth / 2, 1);
|
|
5883
|
+
const { maxH, minH, maxW, minW } = this.#readTickMetrics();
|
|
5884
|
+
const fov = FigInputWheel.#HALF_FOV_DEG;
|
|
5885
|
+
const visibleHalf = Math.max(width / 2, 1);
|
|
5886
|
+
const radius = visibleHalf * 0.84;
|
|
6055
5887
|
const commands = [];
|
|
6056
5888
|
|
|
6057
|
-
for (let index = 0; index <
|
|
5889
|
+
for (let index = 0; index < FigInputWheel.#TICK_COUNT; index += 1) {
|
|
6058
5890
|
let theta = offsetDeg + index * tickStep;
|
|
6059
5891
|
theta = ((((theta + 180) % 360) + 360) % 360) - 180;
|
|
6060
5892
|
if (theta < -fov || theta >= fov) continue;
|
|
@@ -6084,106 +5916,93 @@ class PropskitWheel extends HTMLElement {
|
|
|
6084
5916
|
}
|
|
6085
5917
|
|
|
6086
5918
|
#bindEvents() {
|
|
6087
|
-
this.#boundHandleInput ??= this.#forwardInputEvent.bind(this, "input");
|
|
6088
|
-
this.#boundHandleChange ??= this.#forwardInputEvent.bind(this, "change");
|
|
6089
|
-
this.#input?.addEventListener("input", this.#boundHandleInput);
|
|
6090
|
-
this.#input?.addEventListener("change", this.#boundHandleChange);
|
|
6091
5919
|
this.#surface?.addEventListener("pointerdown", this.#boundPointerDown);
|
|
6092
|
-
this.addEventListener("pointerdown", this.#boundNumberPointerDown, {
|
|
6093
|
-
capture: true,
|
|
6094
|
-
});
|
|
6095
5920
|
this.#wheel?.addEventListener("wheel", this.#boundWheel, { passive: false });
|
|
6096
5921
|
this.#wheel?.addEventListener("keydown", this.#boundKeyDown);
|
|
6097
|
-
this.addEventListener("click", this.#boundClick, true);
|
|
6098
5922
|
}
|
|
6099
5923
|
|
|
6100
5924
|
#unbindEvents() {
|
|
6101
|
-
this.#input?.removeEventListener("input", this.#boundHandleInput);
|
|
6102
|
-
this.#input?.removeEventListener("change", this.#boundHandleChange);
|
|
6103
5925
|
this.#surface?.removeEventListener("pointerdown", this.#boundPointerDown);
|
|
6104
|
-
this.removeEventListener("pointerdown", this.#boundNumberPointerDown, {
|
|
6105
|
-
capture: true,
|
|
6106
|
-
});
|
|
6107
5926
|
this.#wheel?.removeEventListener("wheel", this.#boundWheel);
|
|
6108
5927
|
this.#wheel?.removeEventListener("keydown", this.#boundKeyDown);
|
|
6109
|
-
this.removeEventListener("click", this.#boundClick, true);
|
|
6110
|
-
this.#stopNumberTracking();
|
|
6111
|
-
clearTimeout(this.#numberClickResetTimer);
|
|
6112
|
-
this.#numberClickResetTimer = 0;
|
|
6113
5928
|
window.removeEventListener("pointermove", this.#boundPointerMove);
|
|
6114
5929
|
window.removeEventListener("pointerup", this.#boundPointerUp);
|
|
6115
5930
|
window.removeEventListener("pointercancel", this.#boundPointerUp);
|
|
6116
5931
|
}
|
|
6117
5932
|
|
|
6118
|
-
#forwardInputEvent(type, event) {
|
|
6119
|
-
event.stopImmediatePropagation();
|
|
6120
|
-
if (figLabBooleanAttribute(this, "disabled")) return;
|
|
6121
|
-
const raw =
|
|
6122
|
-
event instanceof CustomEvent && event.detail !== undefined
|
|
6123
|
-
? event.detail
|
|
6124
|
-
: this.#input?.value;
|
|
6125
|
-
const parsed = Number(raw);
|
|
6126
|
-
const next = Number.isFinite(parsed) ? parsed : this.#numericValue();
|
|
6127
|
-
this.#commitValue(next, { emit: type });
|
|
6128
|
-
}
|
|
6129
|
-
|
|
6130
|
-
#handleClick(event) {
|
|
6131
|
-
if (figLabBooleanAttribute(this, "disabled")) return;
|
|
6132
|
-
if (
|
|
6133
|
-
event.target instanceof Element &&
|
|
6134
|
-
event.target.closest("fig-input-number, fig-menu")
|
|
6135
|
-
) {
|
|
6136
|
-
if (
|
|
6137
|
-
this.#suppressNumberClick &&
|
|
6138
|
-
event.target.closest("fig-input-number")
|
|
6139
|
-
) {
|
|
6140
|
-
event.preventDefault();
|
|
6141
|
-
event.stopImmediatePropagation();
|
|
6142
|
-
this.#suppressNumberClick = false;
|
|
6143
|
-
}
|
|
6144
|
-
return;
|
|
6145
|
-
}
|
|
6146
|
-
this.#wheel?.focus();
|
|
6147
|
-
}
|
|
6148
|
-
|
|
6149
5933
|
#handlePointerDown(event) {
|
|
6150
5934
|
if (figLabBooleanAttribute(this, "disabled")) return;
|
|
6151
5935
|
if (event.button !== 0) return;
|
|
6152
|
-
if (
|
|
6153
|
-
event.target instanceof Element &&
|
|
6154
|
-
event.target.closest("fig-input-number, fig-menu")
|
|
6155
|
-
) {
|
|
6156
|
-
return;
|
|
6157
|
-
}
|
|
6158
5936
|
event.preventDefault();
|
|
6159
|
-
this
|
|
6160
|
-
this.#isDragging = true;
|
|
6161
|
-
this.#dragPointerId = event.pointerId;
|
|
6162
|
-
this.#dragStartX = event.clientX;
|
|
6163
|
-
this.#dragStartValue = this.#numericValue();
|
|
6164
|
-
this.#startElasticPull();
|
|
6165
|
-
this.setAttribute("data-active", "");
|
|
6166
|
-
document.body.classList.add(PropskitWheel.#DRAGGING_BODY_CLASS);
|
|
6167
|
-
this.#wheel?.focus();
|
|
5937
|
+
if (!this.beginScrub(event)) return;
|
|
6168
5938
|
this.#surface?.setPointerCapture?.(event.pointerId);
|
|
6169
5939
|
window.addEventListener("pointermove", this.#boundPointerMove);
|
|
6170
5940
|
window.addEventListener("pointerup", this.#boundPointerUp);
|
|
6171
5941
|
window.addEventListener("pointercancel", this.#boundPointerUp);
|
|
6172
5942
|
}
|
|
6173
5943
|
|
|
5944
|
+
beginScrub(start = {}) {
|
|
5945
|
+
if (figLabBooleanAttribute(this, "disabled")) return false;
|
|
5946
|
+
const clientX =
|
|
5947
|
+
typeof start === "number" ? start : Number(start?.clientX ?? start?.x);
|
|
5948
|
+
if (!Number.isFinite(clientX)) return false;
|
|
5949
|
+
this.#stopKeyboardAnimation();
|
|
5950
|
+
this.#isDragging = true;
|
|
5951
|
+
this.#dragPointerId =
|
|
5952
|
+
start && typeof start === "object" && start.pointerId !== undefined
|
|
5953
|
+
? start.pointerId
|
|
5954
|
+
: null;
|
|
5955
|
+
this.#dragStartX = clientX;
|
|
5956
|
+
const requestedStart =
|
|
5957
|
+
start && typeof start === "object"
|
|
5958
|
+
? Number(start.startValue)
|
|
5959
|
+
: Number.NaN;
|
|
5960
|
+
this.#dragStartValue = Number.isFinite(requestedStart)
|
|
5961
|
+
? this.#clamp(requestedStart)
|
|
5962
|
+
: this.#numericValue();
|
|
5963
|
+
this.#startElasticPull();
|
|
5964
|
+
this.setAttribute("data-fig-input-wheel-active", "");
|
|
5965
|
+
document.body.classList.add(FigInputWheel.#DRAGGING_BODY_CLASS);
|
|
5966
|
+
this.focus();
|
|
5967
|
+
return true;
|
|
5968
|
+
}
|
|
5969
|
+
|
|
5970
|
+
updateScrub(position, speed) {
|
|
5971
|
+
if (!this.#isDragging) return this.#numericValue();
|
|
5972
|
+
const clientX =
|
|
5973
|
+
typeof position === "number"
|
|
5974
|
+
? position
|
|
5975
|
+
: Number(position?.clientX ?? position?.x);
|
|
5976
|
+
if (!Number.isFinite(clientX)) return this.#numericValue();
|
|
5977
|
+
const multiplier =
|
|
5978
|
+
speed ??
|
|
5979
|
+
(typeof position === "object" && position?.shiftKey ? 10 : 1);
|
|
5980
|
+
this.#updateDrag(clientX, multiplier);
|
|
5981
|
+
return this.#numericValue();
|
|
5982
|
+
}
|
|
5983
|
+
|
|
5984
|
+
endScrub(commit = true) {
|
|
5985
|
+
if (!this.#isDragging) return this.#numericValue();
|
|
5986
|
+
if (commit && this.#numericValue() !== this.#dragStartValue) {
|
|
5987
|
+
this.#commitValue(this.#numericValue(), { snap: true, emit: "change" });
|
|
5988
|
+
}
|
|
5989
|
+
this.#stopDrag();
|
|
5990
|
+
return this.#numericValue();
|
|
5991
|
+
}
|
|
5992
|
+
|
|
6174
5993
|
#handlePointerMove(event) {
|
|
6175
5994
|
if (!this.#isDragging) return;
|
|
6176
5995
|
if (this.#dragPointerId !== null && event.pointerId !== this.#dragPointerId) {
|
|
6177
5996
|
return;
|
|
6178
5997
|
}
|
|
6179
|
-
this
|
|
5998
|
+
this.updateScrub(event);
|
|
6180
5999
|
}
|
|
6181
6000
|
|
|
6182
6001
|
#updateDrag(clientX, speed = 1) {
|
|
6183
6002
|
const width = this.#wheel?.clientWidth || 1;
|
|
6184
6003
|
const visibleSteps =
|
|
6185
|
-
|
|
6186
|
-
((
|
|
6004
|
+
FigInputWheel.#TICK_COUNT *
|
|
6005
|
+
((FigInputWheel.#HALF_FOV_DEG * 2) / 360);
|
|
6187
6006
|
const visibleUnits = this.#step() * visibleSteps;
|
|
6188
6007
|
const delta =
|
|
6189
6008
|
(clientX - this.#dragStartX) * (visibleUnits / width) * speed;
|
|
@@ -6201,18 +6020,15 @@ class PropskitWheel extends HTMLElement {
|
|
|
6201
6020
|
if (this.#dragPointerId !== null && event.pointerId !== this.#dragPointerId) {
|
|
6202
6021
|
return;
|
|
6203
6022
|
}
|
|
6204
|
-
|
|
6205
|
-
this.#commitValue(this.#numericValue(), { snap: true, emit: "change" });
|
|
6206
|
-
}
|
|
6207
|
-
this.#stopDrag();
|
|
6023
|
+
this.endScrub();
|
|
6208
6024
|
}
|
|
6209
6025
|
|
|
6210
6026
|
#stopDrag() {
|
|
6211
6027
|
this.#isDragging = false;
|
|
6212
6028
|
this.#dragPointerId = null;
|
|
6213
6029
|
this.#visualValue = null;
|
|
6214
|
-
this.removeAttribute("data-active");
|
|
6215
|
-
document.body.classList.remove(
|
|
6030
|
+
this.removeAttribute("data-fig-input-wheel-active");
|
|
6031
|
+
document.body.classList.remove(FigInputWheel.#DRAGGING_BODY_CLASS);
|
|
6216
6032
|
this.#resetElasticPull();
|
|
6217
6033
|
window.removeEventListener("pointermove", this.#boundPointerMove);
|
|
6218
6034
|
window.removeEventListener("pointerup", this.#boundPointerUp);
|
|
@@ -6220,107 +6036,14 @@ class PropskitWheel extends HTMLElement {
|
|
|
6220
6036
|
this.#queueWheelLayout();
|
|
6221
6037
|
}
|
|
6222
6038
|
|
|
6223
|
-
#handleNumberPointerDown(event) {
|
|
6224
|
-
if (
|
|
6225
|
-
event.button !== 0 ||
|
|
6226
|
-
event.altKey ||
|
|
6227
|
-
figLabBooleanAttribute(this, "disabled") ||
|
|
6228
|
-
!(event.target instanceof Element)
|
|
6229
|
-
) {
|
|
6230
|
-
return;
|
|
6231
|
-
}
|
|
6232
|
-
const input = event.target.closest("fig-input-number input");
|
|
6233
|
-
if (!input || input === document.activeElement) return;
|
|
6234
|
-
|
|
6235
|
-
event.preventDefault();
|
|
6236
|
-
event.stopImmediatePropagation();
|
|
6237
|
-
this.#stopNumberTracking();
|
|
6238
|
-
this.#numberPointerId = event.pointerId;
|
|
6239
|
-
this.#numberPointerStartX = event.clientX;
|
|
6240
|
-
this.#numberPointerStartY = event.clientY;
|
|
6241
|
-
this.#numberPointerStartValue = this.#numericValue();
|
|
6242
|
-
window.addEventListener("pointermove", this.#boundNumberPointerMove);
|
|
6243
|
-
window.addEventListener("pointerup", this.#boundNumberPointerEnd, {
|
|
6244
|
-
once: true,
|
|
6245
|
-
});
|
|
6246
|
-
window.addEventListener("pointercancel", this.#boundNumberPointerEnd, {
|
|
6247
|
-
once: true,
|
|
6248
|
-
});
|
|
6249
|
-
window.addEventListener("blur", this.#boundNumberPointerEnd, {
|
|
6250
|
-
once: true,
|
|
6251
|
-
});
|
|
6252
|
-
}
|
|
6253
|
-
|
|
6254
|
-
#handleNumberPointerMove(event) {
|
|
6255
|
-
if (event.pointerId !== this.#numberPointerId) return;
|
|
6256
|
-
if (event.buttons === 0) {
|
|
6257
|
-
this.#handleNumberPointerEnd(event);
|
|
6258
|
-
return;
|
|
6259
|
-
}
|
|
6260
|
-
if (!this.#isNumberScrubbing) {
|
|
6261
|
-
const distance = Math.hypot(
|
|
6262
|
-
event.clientX - this.#numberPointerStartX,
|
|
6263
|
-
event.clientY - this.#numberPointerStartY,
|
|
6264
|
-
);
|
|
6265
|
-
if (distance < 4) return;
|
|
6266
|
-
this.#stopKeyboardAnimation();
|
|
6267
|
-
this.#isNumberScrubbing = true;
|
|
6268
|
-
this.setAttribute("data-number-scrubbing", "");
|
|
6269
|
-
this.#isDragging = true;
|
|
6270
|
-
this.#dragPointerId = event.pointerId;
|
|
6271
|
-
this.#dragStartX = this.#numberPointerStartX;
|
|
6272
|
-
this.#dragStartValue = this.#numberPointerStartValue;
|
|
6273
|
-
this.#startElasticPull();
|
|
6274
|
-
this.setAttribute("data-active", "");
|
|
6275
|
-
document.body.classList.add(PropskitWheel.#DRAGGING_BODY_CLASS);
|
|
6276
|
-
this.#wheel?.focus();
|
|
6277
|
-
}
|
|
6278
|
-
this.#updateDrag(event.clientX, event.shiftKey ? 10 : 1);
|
|
6279
|
-
}
|
|
6280
|
-
|
|
6281
|
-
#handleNumberPointerEnd(event) {
|
|
6282
|
-
if (
|
|
6283
|
-
event?.pointerId !== undefined &&
|
|
6284
|
-
this.#numberPointerId !== null &&
|
|
6285
|
-
event.pointerId !== this.#numberPointerId
|
|
6286
|
-
) {
|
|
6287
|
-
return;
|
|
6288
|
-
}
|
|
6289
|
-
const wasScrubbing = this.#isNumberScrubbing;
|
|
6290
|
-
this.#stopNumberTracking();
|
|
6291
|
-
if (wasScrubbing) {
|
|
6292
|
-
this.#commitValue(this.#numericValue(), { snap: true, emit: "change" });
|
|
6293
|
-
this.#stopDrag();
|
|
6294
|
-
this.#suppressNumberClick = true;
|
|
6295
|
-
clearTimeout(this.#numberClickResetTimer);
|
|
6296
|
-
this.#numberClickResetTimer = window.setTimeout(() => {
|
|
6297
|
-
this.#suppressNumberClick = false;
|
|
6298
|
-
this.#numberClickResetTimer = 0;
|
|
6299
|
-
}, 0);
|
|
6300
|
-
requestAnimationFrame(() => this.#wheel?.focus());
|
|
6301
|
-
return;
|
|
6302
|
-
}
|
|
6303
|
-
this.#input?.querySelector("input")?.focus();
|
|
6304
|
-
}
|
|
6305
|
-
|
|
6306
|
-
#stopNumberTracking() {
|
|
6307
|
-
window.removeEventListener("pointermove", this.#boundNumberPointerMove);
|
|
6308
|
-
window.removeEventListener("pointerup", this.#boundNumberPointerEnd);
|
|
6309
|
-
window.removeEventListener("pointercancel", this.#boundNumberPointerEnd);
|
|
6310
|
-
window.removeEventListener("blur", this.#boundNumberPointerEnd);
|
|
6311
|
-
this.#numberPointerId = null;
|
|
6312
|
-
this.#isNumberScrubbing = false;
|
|
6313
|
-
this.removeAttribute("data-number-scrubbing");
|
|
6314
|
-
}
|
|
6315
|
-
|
|
6316
6039
|
#startElasticPull() {
|
|
6317
6040
|
this.#resetElasticPull();
|
|
6318
6041
|
if (this.getAttribute("elastic") === "false") return;
|
|
6319
6042
|
this.#handleDragMaxPx = this.#readCssLength(
|
|
6320
|
-
"--
|
|
6043
|
+
"--fig-input-wheel-handle-drag-max",
|
|
6321
6044
|
);
|
|
6322
6045
|
this.#elasticMaxPx = this.#readCssLength(
|
|
6323
|
-
"--
|
|
6046
|
+
"--fig-input-wheel-elastic-distance",
|
|
6324
6047
|
);
|
|
6325
6048
|
const rect = this.#wheel?.getBoundingClientRect();
|
|
6326
6049
|
if (rect) {
|
|
@@ -6367,9 +6090,9 @@ class PropskitWheel extends HTMLElement {
|
|
|
6367
6090
|
Math.tanh(
|
|
6368
6091
|
dragDelta / Math.max(1, this.#handleDragMaxPx * 3),
|
|
6369
6092
|
);
|
|
6370
|
-
this.
|
|
6093
|
+
this.setAttribute("data-fig-input-wheel-elastic-dragging", "");
|
|
6371
6094
|
this.style.setProperty(
|
|
6372
|
-
"--
|
|
6095
|
+
"--fig-input-wheel-handle-drag-offset",
|
|
6373
6096
|
`${offset}px`,
|
|
6374
6097
|
);
|
|
6375
6098
|
|
|
@@ -6382,17 +6105,17 @@ class PropskitWheel extends HTMLElement {
|
|
|
6382
6105
|
: 0
|
|
6383
6106
|
: 0;
|
|
6384
6107
|
if (!overshoot || !this.#elasticMaxPx) {
|
|
6385
|
-
this.style.removeProperty("--
|
|
6386
|
-
this.style.removeProperty("--
|
|
6108
|
+
this.style.removeProperty("--fig-input-wheel-elastic-scale");
|
|
6109
|
+
this.style.removeProperty("--fig-input-wheel-elastic-origin");
|
|
6387
6110
|
return;
|
|
6388
6111
|
}
|
|
6389
6112
|
const stretch = Math.min(this.#elasticMaxPx, Math.abs(overshoot) * 0.5);
|
|
6390
6113
|
const scale = this.#elasticHostWidth
|
|
6391
6114
|
? (this.#elasticHostWidth + stretch) / this.#elasticHostWidth
|
|
6392
6115
|
: 1;
|
|
6393
|
-
this.style.setProperty("--
|
|
6116
|
+
this.style.setProperty("--fig-input-wheel-elastic-scale", `${scale}`);
|
|
6394
6117
|
this.style.setProperty(
|
|
6395
|
-
"--
|
|
6118
|
+
"--fig-input-wheel-elastic-origin",
|
|
6396
6119
|
overshoot < 0 ? "right center" : "left center",
|
|
6397
6120
|
);
|
|
6398
6121
|
}
|
|
@@ -6406,10 +6129,10 @@ class PropskitWheel extends HTMLElement {
|
|
|
6406
6129
|
}
|
|
6407
6130
|
|
|
6408
6131
|
#clearElasticPull() {
|
|
6409
|
-
this.removeAttribute("data-elastic-dragging");
|
|
6410
|
-
this.style.removeProperty("--
|
|
6411
|
-
this.style.removeProperty("--
|
|
6412
|
-
this.style.removeProperty("--
|
|
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");
|
|
6413
6136
|
}
|
|
6414
6137
|
|
|
6415
6138
|
#handleWheel(event) {
|
|
@@ -6469,7 +6192,13 @@ class PropskitWheel extends HTMLElement {
|
|
|
6469
6192
|
this.#commitValue(this.#numericValue(), { emit: "change" });
|
|
6470
6193
|
}
|
|
6471
6194
|
|
|
6472
|
-
#animateKeyboardMovement(
|
|
6195
|
+
#animateKeyboardMovement(
|
|
6196
|
+
from,
|
|
6197
|
+
to,
|
|
6198
|
+
direction,
|
|
6199
|
+
multiplier,
|
|
6200
|
+
animateHandle = true,
|
|
6201
|
+
) {
|
|
6473
6202
|
if (
|
|
6474
6203
|
from === to ||
|
|
6475
6204
|
globalThis.matchMedia?.("(prefers-reduced-motion: reduce)").matches
|
|
@@ -6484,25 +6213,26 @@ class PropskitWheel extends HTMLElement {
|
|
|
6484
6213
|
}
|
|
6485
6214
|
clearTimeout(this.#keyboardSettleTimer);
|
|
6486
6215
|
this.#keyboardSettleTimer = 0;
|
|
6487
|
-
this.removeAttribute("data-keyboard-settling");
|
|
6216
|
+
this.removeAttribute("data-fig-input-wheel-keyboard-settling");
|
|
6488
6217
|
const visualFrom = this.#visualValue ?? from;
|
|
6489
6218
|
const handleFrom =
|
|
6490
6219
|
Number.parseFloat(
|
|
6491
|
-
this.style.getPropertyValue("--
|
|
6220
|
+
this.style.getPropertyValue("--fig-input-wheel-handle-drag-offset"),
|
|
6492
6221
|
) || 0;
|
|
6493
6222
|
const duration = multiplier > 1 ? 120 : 90;
|
|
6494
6223
|
const startedAt = performance.now();
|
|
6495
|
-
const
|
|
6496
|
-
|
|
6224
|
+
const shouldAnimateHandle =
|
|
6225
|
+
animateHandle && this.getAttribute("elastic") !== "false";
|
|
6226
|
+
this.setAttribute("data-fig-input-wheel-keyboard-moving", "");
|
|
6497
6227
|
|
|
6498
6228
|
const frame = (now) => {
|
|
6499
6229
|
const progress = Math.min(1, (now - startedAt) / duration);
|
|
6500
6230
|
this.#visualValue = visualFrom + (to - visualFrom) * progress;
|
|
6501
|
-
if (
|
|
6231
|
+
if (shouldAnimateHandle) {
|
|
6502
6232
|
const eased = Math.sin((progress * Math.PI) / 2);
|
|
6503
6233
|
const offset = handleFrom + (direction * 4 - handleFrom) * eased;
|
|
6504
6234
|
this.style.setProperty(
|
|
6505
|
-
"--
|
|
6235
|
+
"--fig-input-wheel-handle-drag-offset",
|
|
6506
6236
|
`${offset}px`,
|
|
6507
6237
|
);
|
|
6508
6238
|
}
|
|
@@ -6515,11 +6245,11 @@ class PropskitWheel extends HTMLElement {
|
|
|
6515
6245
|
|
|
6516
6246
|
this.#keyboardAnimationFrame = 0;
|
|
6517
6247
|
this.#visualValue = null;
|
|
6518
|
-
this.removeAttribute("data-keyboard-moving");
|
|
6519
|
-
this.setAttribute("data-keyboard-settling", "");
|
|
6520
|
-
this.style.removeProperty("--
|
|
6248
|
+
this.removeAttribute("data-fig-input-wheel-keyboard-moving");
|
|
6249
|
+
this.setAttribute("data-fig-input-wheel-keyboard-settling", "");
|
|
6250
|
+
this.style.removeProperty("--fig-input-wheel-handle-drag-offset");
|
|
6521
6251
|
this.#keyboardSettleTimer = window.setTimeout(() => {
|
|
6522
|
-
this.removeAttribute("data-keyboard-settling");
|
|
6252
|
+
this.removeAttribute("data-fig-input-wheel-keyboard-settling");
|
|
6523
6253
|
this.#keyboardSettleTimer = 0;
|
|
6524
6254
|
}, 140);
|
|
6525
6255
|
this.#queueWheelLayout();
|
|
@@ -6536,9 +6266,9 @@ class PropskitWheel extends HTMLElement {
|
|
|
6536
6266
|
clearTimeout(this.#keyboardSettleTimer);
|
|
6537
6267
|
this.#keyboardSettleTimer = 0;
|
|
6538
6268
|
this.#visualValue = null;
|
|
6539
|
-
this.removeAttribute("data-keyboard-moving");
|
|
6540
|
-
this.removeAttribute("data-keyboard-settling");
|
|
6541
|
-
this.style.removeProperty("--
|
|
6269
|
+
this.removeAttribute("data-fig-input-wheel-keyboard-moving");
|
|
6270
|
+
this.removeAttribute("data-fig-input-wheel-keyboard-settling");
|
|
6271
|
+
this.style.removeProperty("--fig-input-wheel-handle-drag-offset");
|
|
6542
6272
|
if (this.isConnected) this.#queueWheelLayout();
|
|
6543
6273
|
}
|
|
6544
6274
|
|
|
@@ -6555,6 +6285,25 @@ class PropskitWheel extends HTMLElement {
|
|
|
6555
6285
|
this.#commitValue(Number.isFinite(parsed) ? parsed : 0);
|
|
6556
6286
|
}
|
|
6557
6287
|
|
|
6288
|
+
spinTo(nextValue) {
|
|
6289
|
+
const previousValue = this.#numericValue();
|
|
6290
|
+
this.value = nextValue;
|
|
6291
|
+
const value = this.#numericValue();
|
|
6292
|
+
if (this.getAttribute("spin") === "false") {
|
|
6293
|
+
this.#stopKeyboardAnimation();
|
|
6294
|
+
return value;
|
|
6295
|
+
}
|
|
6296
|
+
const direction = Math.sign(value - previousValue);
|
|
6297
|
+
this.#animateKeyboardMovement(
|
|
6298
|
+
previousValue,
|
|
6299
|
+
value,
|
|
6300
|
+
direction,
|
|
6301
|
+
Math.abs(value - previousValue) > this.#step() ? 10 : 1,
|
|
6302
|
+
false,
|
|
6303
|
+
);
|
|
6304
|
+
return value;
|
|
6305
|
+
}
|
|
6306
|
+
|
|
6558
6307
|
get min() {
|
|
6559
6308
|
return this.#boundMin();
|
|
6560
6309
|
}
|
|
@@ -6571,6 +6320,585 @@ class PropskitWheel extends HTMLElement {
|
|
|
6571
6320
|
this.#writeBound("max", nextValue);
|
|
6572
6321
|
}
|
|
6573
6322
|
|
|
6323
|
+
get step() {
|
|
6324
|
+
return this.#step();
|
|
6325
|
+
}
|
|
6326
|
+
|
|
6327
|
+
focus(options) {
|
|
6328
|
+
HTMLElement.prototype.focus.call(this, options);
|
|
6329
|
+
}
|
|
6330
|
+
}
|
|
6331
|
+
figLabDefineElement("fig-input-wheel", FigInputWheel);
|
|
6332
|
+
|
|
6333
|
+
/**
|
|
6334
|
+
* Labeled numeric wheel with an optional editable number field.
|
|
6335
|
+
*
|
|
6336
|
+
* @attr {string} label - Field label. Defaults to "Value".
|
|
6337
|
+
* @attr {string} default - Reset target.
|
|
6338
|
+
* @attr {number} precision - Number field display decimals.
|
|
6339
|
+
* @attr {boolean|string} spin - Keeps wheel ticks synchronized to value. Defaults to true.
|
|
6340
|
+
* @attr {boolean|string} text - Shows the number field. Defaults to true.
|
|
6341
|
+
* @attr {string} size - Set to "small" for compact sizing.
|
|
6342
|
+
* @attr {string} variant - Set to "minimal" for compact chrome.
|
|
6343
|
+
* @fires input - Retargeted numeric composed event.
|
|
6344
|
+
* @fires change - Retargeted numeric composed event.
|
|
6345
|
+
*/
|
|
6346
|
+
class PropskitWheel extends HTMLElement {
|
|
6347
|
+
static #RESERVED_ATTRS = new Set([
|
|
6348
|
+
"label",
|
|
6349
|
+
"size",
|
|
6350
|
+
"disabled",
|
|
6351
|
+
"variant",
|
|
6352
|
+
"elastic",
|
|
6353
|
+
"spin",
|
|
6354
|
+
"text",
|
|
6355
|
+
"default",
|
|
6356
|
+
"class",
|
|
6357
|
+
"style",
|
|
6358
|
+
"id",
|
|
6359
|
+
"oninput",
|
|
6360
|
+
"onchange",
|
|
6361
|
+
]);
|
|
6362
|
+
|
|
6363
|
+
static get observedAttributes() {
|
|
6364
|
+
return [
|
|
6365
|
+
"label",
|
|
6366
|
+
"units",
|
|
6367
|
+
"value",
|
|
6368
|
+
"disabled",
|
|
6369
|
+
"step",
|
|
6370
|
+
"precision",
|
|
6371
|
+
"min",
|
|
6372
|
+
"max",
|
|
6373
|
+
"elastic",
|
|
6374
|
+
"spin",
|
|
6375
|
+
"text",
|
|
6376
|
+
];
|
|
6377
|
+
}
|
|
6378
|
+
|
|
6379
|
+
#surface = null;
|
|
6380
|
+
#wheel = null;
|
|
6381
|
+
#label = null;
|
|
6382
|
+
#input = null;
|
|
6383
|
+
#hasCustomLabel = false;
|
|
6384
|
+
#observer = null;
|
|
6385
|
+
#elasticObserver = null;
|
|
6386
|
+
#managedInputAttrs = new Set();
|
|
6387
|
+
#initialValue = null;
|
|
6388
|
+
#numberPointerId = null;
|
|
6389
|
+
#numberPointerStartX = 0;
|
|
6390
|
+
#numberPointerStartY = 0;
|
|
6391
|
+
#numberPointerStartValue = 0;
|
|
6392
|
+
#isNumberScrubbing = false;
|
|
6393
|
+
#suppressNumberClick = false;
|
|
6394
|
+
#numberClickResetTimer = 0;
|
|
6395
|
+
#boundPrimitiveInput = this.#handlePrimitiveEvent.bind(this, "input");
|
|
6396
|
+
#boundPrimitiveChange = this.#handlePrimitiveEvent.bind(this, "change");
|
|
6397
|
+
#boundNumberInput = this.#handleNumberEvent.bind(this, "input");
|
|
6398
|
+
#boundNumberChange = this.#handleNumberEvent.bind(this, "change");
|
|
6399
|
+
#boundNumberPointerDown = this.#handleNumberPointerDown.bind(this);
|
|
6400
|
+
#boundNumberPointerMove = this.#handleNumberPointerMove.bind(this);
|
|
6401
|
+
#boundNumberPointerEnd = this.#handleNumberPointerEnd.bind(this);
|
|
6402
|
+
#boundClick = this.#handleClick.bind(this);
|
|
6403
|
+
|
|
6404
|
+
connectedCallback() {
|
|
6405
|
+
if (!this.#surface) this.#initialize();
|
|
6406
|
+
this.#syncLabel();
|
|
6407
|
+
this.#syncText();
|
|
6408
|
+
this.#syncPrimitive();
|
|
6409
|
+
this.#syncInputAttributes();
|
|
6410
|
+
this.#bindEvents();
|
|
6411
|
+
figLabConnectPropskitResetMenu(this);
|
|
6412
|
+
this.#observer?.disconnect();
|
|
6413
|
+
this.#observer = new MutationObserver((mutations) => {
|
|
6414
|
+
if (
|
|
6415
|
+
mutations.some(
|
|
6416
|
+
({ type, attributeName }) =>
|
|
6417
|
+
type === "attributes" &&
|
|
6418
|
+
attributeName &&
|
|
6419
|
+
!PropskitWheel.#RESERVED_ATTRS.has(attributeName) &&
|
|
6420
|
+
!PropskitWheel.observedAttributes.includes(attributeName) &&
|
|
6421
|
+
!attributeName.startsWith("data-"),
|
|
6422
|
+
)
|
|
6423
|
+
) {
|
|
6424
|
+
this.#syncInputAttributes();
|
|
6425
|
+
}
|
|
6426
|
+
});
|
|
6427
|
+
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
|
+
}
|
|
6440
|
+
|
|
6441
|
+
disconnectedCallback() {
|
|
6442
|
+
this.#observer?.disconnect();
|
|
6443
|
+
this.#elasticObserver?.disconnect();
|
|
6444
|
+
this.#elasticObserver = null;
|
|
6445
|
+
this.#clearElasticComposition();
|
|
6446
|
+
this.#unbindEvents();
|
|
6447
|
+
this.#stopNumberTracking();
|
|
6448
|
+
clearTimeout(this.#numberClickResetTimer);
|
|
6449
|
+
this.#numberClickResetTimer = 0;
|
|
6450
|
+
this.#wheel?.endScrub(false);
|
|
6451
|
+
figLabDisconnectPropskitResetMenu(this);
|
|
6452
|
+
}
|
|
6453
|
+
|
|
6454
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
6455
|
+
if (oldValue === newValue || !this.#surface) return;
|
|
6456
|
+
if (name === "label") this.#syncLabel();
|
|
6457
|
+
if (name === "text") this.#syncText();
|
|
6458
|
+
if (
|
|
6459
|
+
[
|
|
6460
|
+
"units",
|
|
6461
|
+
"value",
|
|
6462
|
+
"disabled",
|
|
6463
|
+
"step",
|
|
6464
|
+
"min",
|
|
6465
|
+
"max",
|
|
6466
|
+
"elastic",
|
|
6467
|
+
"spin",
|
|
6468
|
+
].includes(name)
|
|
6469
|
+
) {
|
|
6470
|
+
this.#syncPrimitive();
|
|
6471
|
+
}
|
|
6472
|
+
if (
|
|
6473
|
+
["units", "value", "disabled", "step", "precision", "min", "max"].includes(
|
|
6474
|
+
name,
|
|
6475
|
+
)
|
|
6476
|
+
) {
|
|
6477
|
+
this.#syncInputAttributes();
|
|
6478
|
+
}
|
|
6479
|
+
}
|
|
6480
|
+
|
|
6481
|
+
#initialize() {
|
|
6482
|
+
this.#initialValue = this.getAttribute("value") ?? "0";
|
|
6483
|
+
const initialChildren = Array.from(this.childNodes).filter(
|
|
6484
|
+
(node) =>
|
|
6485
|
+
node.nodeType !== Node.TEXT_NODE || Boolean(node.textContent?.trim()),
|
|
6486
|
+
);
|
|
6487
|
+
const customLabel = initialChildren.find(
|
|
6488
|
+
(node) => node.nodeType === Node.ELEMENT_NODE && node.matches("label"),
|
|
6489
|
+
);
|
|
6490
|
+
const surface = figLabCreateElement("div", {
|
|
6491
|
+
className: "propskit-wheel-surface",
|
|
6492
|
+
});
|
|
6493
|
+
const label = customLabel || document.createElement("label");
|
|
6494
|
+
const wheel = document.createElement("fig-input-wheel");
|
|
6495
|
+
const input = document.createElement("fig-input-number");
|
|
6496
|
+
const labelId = figLabUniqueId("propskit-wheel-label");
|
|
6497
|
+
label.id = labelId;
|
|
6498
|
+
wheel.setAttribute("aria-labelledby", labelId);
|
|
6499
|
+
input.setAttribute("aria-labelledby", labelId);
|
|
6500
|
+
surface.append(label, wheel, input);
|
|
6501
|
+
this.#surface = surface;
|
|
6502
|
+
this.#wheel = wheel;
|
|
6503
|
+
this.#label = label;
|
|
6504
|
+
this.#input = input;
|
|
6505
|
+
this.#hasCustomLabel = Boolean(customLabel);
|
|
6506
|
+
this.replaceChildren(surface);
|
|
6507
|
+
for (const node of initialChildren) {
|
|
6508
|
+
if (node !== customLabel) input.appendChild(node);
|
|
6509
|
+
}
|
|
6510
|
+
}
|
|
6511
|
+
|
|
6512
|
+
#syncLabel() {
|
|
6513
|
+
if (!this.#label) return;
|
|
6514
|
+
if (!this.#hasCustomLabel) {
|
|
6515
|
+
this.#label.textContent = this.hasAttribute("label")
|
|
6516
|
+
? (this.getAttribute("label") ?? "")
|
|
6517
|
+
: "Value";
|
|
6518
|
+
}
|
|
6519
|
+
this.toggleAttribute(
|
|
6520
|
+
"data-label-empty",
|
|
6521
|
+
!(this.#label.textContent ?? "").trim(),
|
|
6522
|
+
);
|
|
6523
|
+
}
|
|
6524
|
+
|
|
6525
|
+
#syncText() {
|
|
6526
|
+
if (!this.#surface || !this.#input) return;
|
|
6527
|
+
const enabled = this.getAttribute("text") !== "false";
|
|
6528
|
+
const isInserted = this.#input.parentElement === this.#surface;
|
|
6529
|
+
if (enabled && !isInserted) this.#surface.append(this.#input);
|
|
6530
|
+
else if (!enabled && isInserted) this.#input.remove();
|
|
6531
|
+
}
|
|
6532
|
+
|
|
6533
|
+
#mirrorAttribute(name) {
|
|
6534
|
+
if (!this.#wheel) return;
|
|
6535
|
+
if (this.hasAttribute(name)) {
|
|
6536
|
+
this.#wheel.setAttribute(name, this.getAttribute(name) ?? "");
|
|
6537
|
+
} else {
|
|
6538
|
+
this.#wheel.removeAttribute(name);
|
|
6539
|
+
}
|
|
6540
|
+
}
|
|
6541
|
+
|
|
6542
|
+
#syncPrimitive() {
|
|
6543
|
+
if (!this.#wheel) return;
|
|
6544
|
+
this.#wheel.removeAttribute("units");
|
|
6545
|
+
for (const name of ["min", "max", "elastic", "spin"]) {
|
|
6546
|
+
this.#mirrorAttribute(name);
|
|
6547
|
+
}
|
|
6548
|
+
if (this.hasAttribute("step")) this.#mirrorAttribute("step");
|
|
6549
|
+
else this.#wheel.setAttribute("step", String(this.#defaultStep()));
|
|
6550
|
+
this.#wheel.toggleAttribute(
|
|
6551
|
+
"disabled",
|
|
6552
|
+
figLabBooleanAttribute(this, "disabled"),
|
|
6553
|
+
);
|
|
6554
|
+
this.#wheel.value = this.getAttribute("value") ?? "0";
|
|
6555
|
+
const normalized = this.#wheel.value;
|
|
6556
|
+
if (this.getAttribute("value") !== normalized) {
|
|
6557
|
+
this.setAttribute("value", normalized);
|
|
6558
|
+
}
|
|
6559
|
+
if (this.#input?.getAttribute("value") !== normalized) {
|
|
6560
|
+
this.#input?.setAttribute("value", normalized);
|
|
6561
|
+
}
|
|
6562
|
+
this.#syncPrimitiveAria();
|
|
6563
|
+
}
|
|
6564
|
+
|
|
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
|
+
#getForwardedInputAttrNames() {
|
|
6594
|
+
return this.getAttributeNames().filter(
|
|
6595
|
+
(name) =>
|
|
6596
|
+
!PropskitWheel.#RESERVED_ATTRS.has(name) &&
|
|
6597
|
+
!PropskitWheel.observedAttributes.includes(name) &&
|
|
6598
|
+
!name.startsWith("data-"),
|
|
6599
|
+
);
|
|
6600
|
+
}
|
|
6601
|
+
|
|
6602
|
+
#units() {
|
|
6603
|
+
const raw = (this.getAttribute("units") || "").trim();
|
|
6604
|
+
const normalized = raw.toLowerCase();
|
|
6605
|
+
if (
|
|
6606
|
+
normalized === "ms" ||
|
|
6607
|
+
normalized === "millisecond" ||
|
|
6608
|
+
normalized === "milliseconds"
|
|
6609
|
+
) {
|
|
6610
|
+
return "ms";
|
|
6611
|
+
}
|
|
6612
|
+
if (
|
|
6613
|
+
normalized === "s" ||
|
|
6614
|
+
normalized === "second" ||
|
|
6615
|
+
normalized === "seconds"
|
|
6616
|
+
) {
|
|
6617
|
+
return "s";
|
|
6618
|
+
}
|
|
6619
|
+
return raw;
|
|
6620
|
+
}
|
|
6621
|
+
|
|
6622
|
+
#defaultStep() {
|
|
6623
|
+
const units = this.#units();
|
|
6624
|
+
if (units === "s") return 0.1;
|
|
6625
|
+
if (units === "ms") return 100;
|
|
6626
|
+
return 1;
|
|
6627
|
+
}
|
|
6628
|
+
|
|
6629
|
+
#defaultPrecision() {
|
|
6630
|
+
return this.#units() === "s" ? 2 : 0;
|
|
6631
|
+
}
|
|
6632
|
+
|
|
6633
|
+
#syncPrimitiveAria() {
|
|
6634
|
+
if (!this.#wheel) return;
|
|
6635
|
+
const value = Number(this.#wheel.value);
|
|
6636
|
+
const units = this.#units();
|
|
6637
|
+
this.#wheel.setAttribute(
|
|
6638
|
+
"aria-valuetext",
|
|
6639
|
+
units
|
|
6640
|
+
? `${value} ${
|
|
6641
|
+
units === "s" ? "seconds" : units === "ms" ? "milliseconds" : units
|
|
6642
|
+
}`
|
|
6643
|
+
: String(value),
|
|
6644
|
+
);
|
|
6645
|
+
}
|
|
6646
|
+
|
|
6647
|
+
#syncInputAttributes() {
|
|
6648
|
+
if (!this.#input || !this.#wheel) return;
|
|
6649
|
+
const forwarded = this.#getForwardedInputAttrNames();
|
|
6650
|
+
const nextManaged = new Set(forwarded);
|
|
6651
|
+
for (const name of this.#managedInputAttrs) {
|
|
6652
|
+
if (!nextManaged.has(name)) this.#input.removeAttribute(name);
|
|
6653
|
+
}
|
|
6654
|
+
for (const name of forwarded) {
|
|
6655
|
+
this.#input.setAttribute(name, this.getAttribute(name) ?? "");
|
|
6656
|
+
}
|
|
6657
|
+
const units = this.#units();
|
|
6658
|
+
if (units) this.#input.setAttribute("units", units);
|
|
6659
|
+
else this.#input.removeAttribute("units");
|
|
6660
|
+
this.#input.setAttribute("step", String(this.#wheel.step));
|
|
6661
|
+
if (this.hasAttribute("precision")) {
|
|
6662
|
+
this.#input.setAttribute(
|
|
6663
|
+
"precision",
|
|
6664
|
+
this.getAttribute("precision") ?? "",
|
|
6665
|
+
);
|
|
6666
|
+
} else {
|
|
6667
|
+
this.#input.setAttribute("precision", String(this.#defaultPrecision()));
|
|
6668
|
+
}
|
|
6669
|
+
for (const name of ["min", "max"]) {
|
|
6670
|
+
const value = this.#wheel[name];
|
|
6671
|
+
if (value === null) this.#input.removeAttribute(name);
|
|
6672
|
+
else this.#input.setAttribute(name, String(value));
|
|
6673
|
+
}
|
|
6674
|
+
this.#input.toggleAttribute(
|
|
6675
|
+
"disabled",
|
|
6676
|
+
figLabBooleanAttribute(this, "disabled"),
|
|
6677
|
+
);
|
|
6678
|
+
this.#input.setAttribute("value", this.#wheel.value);
|
|
6679
|
+
this.#managedInputAttrs = nextManaged;
|
|
6680
|
+
}
|
|
6681
|
+
|
|
6682
|
+
#bindEvents() {
|
|
6683
|
+
this.#unbindEvents();
|
|
6684
|
+
this.#wheel?.addEventListener("input", this.#boundPrimitiveInput);
|
|
6685
|
+
this.#wheel?.addEventListener("change", this.#boundPrimitiveChange);
|
|
6686
|
+
this.#input?.addEventListener("input", this.#boundNumberInput);
|
|
6687
|
+
this.#input?.addEventListener("change", this.#boundNumberChange);
|
|
6688
|
+
this.addEventListener("pointerdown", this.#boundNumberPointerDown, {
|
|
6689
|
+
capture: true,
|
|
6690
|
+
});
|
|
6691
|
+
this.addEventListener("click", this.#boundClick, true);
|
|
6692
|
+
}
|
|
6693
|
+
|
|
6694
|
+
#unbindEvents() {
|
|
6695
|
+
this.#wheel?.removeEventListener("input", this.#boundPrimitiveInput);
|
|
6696
|
+
this.#wheel?.removeEventListener("change", this.#boundPrimitiveChange);
|
|
6697
|
+
this.#input?.removeEventListener("input", this.#boundNumberInput);
|
|
6698
|
+
this.#input?.removeEventListener("change", this.#boundNumberChange);
|
|
6699
|
+
this.removeEventListener("pointerdown", this.#boundNumberPointerDown, {
|
|
6700
|
+
capture: true,
|
|
6701
|
+
});
|
|
6702
|
+
this.removeEventListener("click", this.#boundClick, true);
|
|
6703
|
+
}
|
|
6704
|
+
|
|
6705
|
+
#setSynchronizedValue(value, { spin = false } = {}) {
|
|
6706
|
+
const parsed = Number(value);
|
|
6707
|
+
if (!this.#wheel) {
|
|
6708
|
+
const normalized = Number.isFinite(parsed) ? parsed : 0;
|
|
6709
|
+
this.setAttribute("value", String(normalized));
|
|
6710
|
+
return normalized;
|
|
6711
|
+
}
|
|
6712
|
+
const nextValue = Number.isFinite(parsed) ? parsed : 0;
|
|
6713
|
+
if (spin) this.#wheel.spinTo(nextValue);
|
|
6714
|
+
else this.#wheel.value = nextValue;
|
|
6715
|
+
const normalized = this.#wheel.value;
|
|
6716
|
+
if (this.getAttribute("value") !== normalized) {
|
|
6717
|
+
this.setAttribute("value", normalized);
|
|
6718
|
+
}
|
|
6719
|
+
if (this.#input?.getAttribute("value") !== normalized) {
|
|
6720
|
+
this.#input?.setAttribute("value", normalized);
|
|
6721
|
+
}
|
|
6722
|
+
this.#syncPrimitiveAria();
|
|
6723
|
+
return Number(normalized);
|
|
6724
|
+
}
|
|
6725
|
+
|
|
6726
|
+
#emit(type, value) {
|
|
6727
|
+
this.dispatchEvent(
|
|
6728
|
+
new CustomEvent(type, {
|
|
6729
|
+
detail: value,
|
|
6730
|
+
bubbles: true,
|
|
6731
|
+
cancelable: true,
|
|
6732
|
+
composed: true,
|
|
6733
|
+
}),
|
|
6734
|
+
);
|
|
6735
|
+
}
|
|
6736
|
+
|
|
6737
|
+
#handlePrimitiveEvent(type, event) {
|
|
6738
|
+
event.stopImmediatePropagation();
|
|
6739
|
+
const value = this.#setSynchronizedValue(event.detail);
|
|
6740
|
+
this.#emit(type, value);
|
|
6741
|
+
}
|
|
6742
|
+
|
|
6743
|
+
#handleNumberEvent(type, event) {
|
|
6744
|
+
event.stopImmediatePropagation();
|
|
6745
|
+
if (figLabBooleanAttribute(this, "disabled")) return;
|
|
6746
|
+
const raw =
|
|
6747
|
+
event instanceof CustomEvent && event.detail !== undefined
|
|
6748
|
+
? event.detail
|
|
6749
|
+
: this.#input?.value;
|
|
6750
|
+
const value = this.#setSynchronizedValue(raw, { spin: type === "input" });
|
|
6751
|
+
this.#emit(type, value);
|
|
6752
|
+
}
|
|
6753
|
+
|
|
6754
|
+
#handleNumberPointerDown(event) {
|
|
6755
|
+
if (
|
|
6756
|
+
event.button !== 0 ||
|
|
6757
|
+
event.altKey ||
|
|
6758
|
+
figLabBooleanAttribute(this, "disabled") ||
|
|
6759
|
+
!(event.target instanceof Element)
|
|
6760
|
+
) {
|
|
6761
|
+
return;
|
|
6762
|
+
}
|
|
6763
|
+
const input = event.target.closest("fig-input-number input");
|
|
6764
|
+
if (!input || input === document.activeElement) return;
|
|
6765
|
+
event.preventDefault();
|
|
6766
|
+
event.stopImmediatePropagation();
|
|
6767
|
+
this.#stopNumberTracking();
|
|
6768
|
+
this.#numberPointerId = event.pointerId;
|
|
6769
|
+
this.#numberPointerStartX = event.clientX;
|
|
6770
|
+
this.#numberPointerStartY = event.clientY;
|
|
6771
|
+
this.#numberPointerStartValue = Number(this.#wheel?.value ?? 0);
|
|
6772
|
+
window.addEventListener("pointermove", this.#boundNumberPointerMove);
|
|
6773
|
+
window.addEventListener("pointerup", this.#boundNumberPointerEnd, {
|
|
6774
|
+
once: true,
|
|
6775
|
+
});
|
|
6776
|
+
window.addEventListener("pointercancel", this.#boundNumberPointerEnd, {
|
|
6777
|
+
once: true,
|
|
6778
|
+
});
|
|
6779
|
+
window.addEventListener("blur", this.#boundNumberPointerEnd, {
|
|
6780
|
+
once: true,
|
|
6781
|
+
});
|
|
6782
|
+
}
|
|
6783
|
+
|
|
6784
|
+
#handleNumberPointerMove(event) {
|
|
6785
|
+
if (event.pointerId !== this.#numberPointerId) return;
|
|
6786
|
+
if (event.buttons === 0) {
|
|
6787
|
+
this.#handleNumberPointerEnd(event);
|
|
6788
|
+
return;
|
|
6789
|
+
}
|
|
6790
|
+
if (!this.#isNumberScrubbing) {
|
|
6791
|
+
const distance = Math.hypot(
|
|
6792
|
+
event.clientX - this.#numberPointerStartX,
|
|
6793
|
+
event.clientY - this.#numberPointerStartY,
|
|
6794
|
+
);
|
|
6795
|
+
if (distance < 4) return;
|
|
6796
|
+
this.#isNumberScrubbing = true;
|
|
6797
|
+
this.setAttribute("data-number-scrubbing", "");
|
|
6798
|
+
this.#wheel?.beginScrub({
|
|
6799
|
+
clientX: this.#numberPointerStartX,
|
|
6800
|
+
pointerId: event.pointerId,
|
|
6801
|
+
startValue: this.#numberPointerStartValue,
|
|
6802
|
+
});
|
|
6803
|
+
}
|
|
6804
|
+
this.#wheel?.updateScrub(event);
|
|
6805
|
+
}
|
|
6806
|
+
|
|
6807
|
+
#handleNumberPointerEnd(event) {
|
|
6808
|
+
if (
|
|
6809
|
+
event?.pointerId !== undefined &&
|
|
6810
|
+
this.#numberPointerId !== null &&
|
|
6811
|
+
event.pointerId !== this.#numberPointerId
|
|
6812
|
+
) {
|
|
6813
|
+
return;
|
|
6814
|
+
}
|
|
6815
|
+
const wasScrubbing = this.#isNumberScrubbing;
|
|
6816
|
+
this.#stopNumberTracking();
|
|
6817
|
+
if (wasScrubbing) {
|
|
6818
|
+
this.#wheel?.endScrub();
|
|
6819
|
+
this.#suppressNumberClick = true;
|
|
6820
|
+
clearTimeout(this.#numberClickResetTimer);
|
|
6821
|
+
this.#numberClickResetTimer = window.setTimeout(() => {
|
|
6822
|
+
this.#suppressNumberClick = false;
|
|
6823
|
+
this.#numberClickResetTimer = 0;
|
|
6824
|
+
}, 0);
|
|
6825
|
+
requestAnimationFrame(() => this.#wheel?.focus());
|
|
6826
|
+
return;
|
|
6827
|
+
}
|
|
6828
|
+
this.#input?.querySelector("input")?.focus();
|
|
6829
|
+
}
|
|
6830
|
+
|
|
6831
|
+
#stopNumberTracking() {
|
|
6832
|
+
window.removeEventListener("pointermove", this.#boundNumberPointerMove);
|
|
6833
|
+
window.removeEventListener("pointerup", this.#boundNumberPointerEnd);
|
|
6834
|
+
window.removeEventListener("pointercancel", this.#boundNumberPointerEnd);
|
|
6835
|
+
window.removeEventListener("blur", this.#boundNumberPointerEnd);
|
|
6836
|
+
this.#numberPointerId = null;
|
|
6837
|
+
this.#isNumberScrubbing = false;
|
|
6838
|
+
this.removeAttribute("data-number-scrubbing");
|
|
6839
|
+
}
|
|
6840
|
+
|
|
6841
|
+
#handleClick(event) {
|
|
6842
|
+
if (figLabBooleanAttribute(this, "disabled")) return;
|
|
6843
|
+
if (
|
|
6844
|
+
event.target instanceof Element &&
|
|
6845
|
+
event.target.closest("fig-input-number, fig-menu")
|
|
6846
|
+
) {
|
|
6847
|
+
if (
|
|
6848
|
+
this.#suppressNumberClick &&
|
|
6849
|
+
event.target.closest("fig-input-number")
|
|
6850
|
+
) {
|
|
6851
|
+
event.preventDefault();
|
|
6852
|
+
event.stopImmediatePropagation();
|
|
6853
|
+
this.#suppressNumberClick = false;
|
|
6854
|
+
}
|
|
6855
|
+
return;
|
|
6856
|
+
}
|
|
6857
|
+
if (
|
|
6858
|
+
!(event.target instanceof Element) ||
|
|
6859
|
+
!event.target.closest("fig-input-wheel")
|
|
6860
|
+
) {
|
|
6861
|
+
this.#wheel?.focus();
|
|
6862
|
+
}
|
|
6863
|
+
}
|
|
6864
|
+
|
|
6865
|
+
get value() {
|
|
6866
|
+
return this.getAttribute("value") ?? this.#wheel?.value ?? "0";
|
|
6867
|
+
}
|
|
6868
|
+
|
|
6869
|
+
set value(nextValue) {
|
|
6870
|
+
const parsed = Number(nextValue);
|
|
6871
|
+
this.#setSynchronizedValue(Number.isFinite(parsed) ? parsed : 0);
|
|
6872
|
+
}
|
|
6873
|
+
|
|
6874
|
+
get min() {
|
|
6875
|
+
return this.#wheel?.min ?? null;
|
|
6876
|
+
}
|
|
6877
|
+
|
|
6878
|
+
set min(nextValue) {
|
|
6879
|
+
if (nextValue === null || nextValue === undefined || nextValue === "") {
|
|
6880
|
+
this.removeAttribute("min");
|
|
6881
|
+
} else {
|
|
6882
|
+
const parsed = Number(nextValue);
|
|
6883
|
+
if (Number.isFinite(parsed)) this.setAttribute("min", String(parsed));
|
|
6884
|
+
else this.removeAttribute("min");
|
|
6885
|
+
}
|
|
6886
|
+
}
|
|
6887
|
+
|
|
6888
|
+
get max() {
|
|
6889
|
+
return this.#wheel?.max ?? null;
|
|
6890
|
+
}
|
|
6891
|
+
|
|
6892
|
+
set max(nextValue) {
|
|
6893
|
+
if (nextValue === null || nextValue === undefined || nextValue === "") {
|
|
6894
|
+
this.removeAttribute("max");
|
|
6895
|
+
} else {
|
|
6896
|
+
const parsed = Number(nextValue);
|
|
6897
|
+
if (Number.isFinite(parsed)) this.setAttribute("max", String(parsed));
|
|
6898
|
+
else this.removeAttribute("max");
|
|
6899
|
+
}
|
|
6900
|
+
}
|
|
6901
|
+
|
|
6574
6902
|
get defaultValue() {
|
|
6575
6903
|
return this.getAttribute("default") ?? this.#initialValue ?? "0";
|
|
6576
6904
|
}
|
|
@@ -6581,8 +6909,9 @@ class PropskitWheel extends HTMLElement {
|
|
|
6581
6909
|
|
|
6582
6910
|
resetToDefault() {
|
|
6583
6911
|
const parsed = Number(this.defaultValue);
|
|
6584
|
-
const value =
|
|
6585
|
-
|
|
6912
|
+
const value = this.#setSynchronizedValue(
|
|
6913
|
+
Number.isFinite(parsed) ? parsed : 0,
|
|
6914
|
+
);
|
|
6586
6915
|
figLabEmitPropskitReset(this, value);
|
|
6587
6916
|
}
|
|
6588
6917
|
|
|
@@ -9756,6 +10085,7 @@ class FigReorder extends HTMLElement {
|
|
|
9756
10085
|
"fig-input-gradient",
|
|
9757
10086
|
"fig-easing-curve",
|
|
9758
10087
|
"fig-input-angle",
|
|
10088
|
+
"fig-input-wheel",
|
|
9759
10089
|
"fig-input-joystick",
|
|
9760
10090
|
"fig-canvas-control",
|
|
9761
10091
|
"propskit-color-point",
|