@rogieking/figui3 8.9.41 → 8.9.43
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/components.css +5 -3
- package/dist/components.css +1 -1
- package/dist/fig-lab.css +1 -1
- package/dist/fig-lab.js +2 -2
- package/dist/fig.css +1 -1
- package/fig-lab.css +27 -13
- package/fig-lab.js +219 -0
- package/package.json +1 -1
package/fig-lab.css
CHANGED
|
@@ -1060,6 +1060,12 @@ propskit-slider {
|
|
|
1060
1060
|
var(--propskit-slider-fig-slider-height) - var(--spacer-3)
|
|
1061
1061
|
);
|
|
1062
1062
|
--propskit-slider-number-right: calc(2px + var(--spacer-1));
|
|
1063
|
+
--propskit-slider-handle-fade-start-threshold: var(--spacer-3);
|
|
1064
|
+
--propskit-slider-handle-fade-end-threshold: var(--spacer-2);
|
|
1065
|
+
--propskit-slider-handle-proximity: 1;
|
|
1066
|
+
--propskit-slider-handle-reveal: 0;
|
|
1067
|
+
--propskit-slider-handle-scale: var(--propskit-slider-handle-proximity);
|
|
1068
|
+
--propskit-slider-handle-color: light-dark(#222, var(--figma-color-text));
|
|
1063
1069
|
|
|
1064
1070
|
display: block;
|
|
1065
1071
|
position: relative;
|
|
@@ -1075,6 +1081,11 @@ propskit-slider {
|
|
|
1075
1081
|
padding-left: var(--propskit-slider-horizontal-padding);
|
|
1076
1082
|
padding-right: var(--propskit-slider-horizontal-padding);
|
|
1077
1083
|
|
|
1084
|
+
&:hover,
|
|
1085
|
+
&:focus-within {
|
|
1086
|
+
--propskit-slider-handle-reveal: 1;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1078
1089
|
&[size="small"] {
|
|
1079
1090
|
--propskit-slider-height: 1.5rem;
|
|
1080
1091
|
--propskit-slider-fig-slider-thumb-height: calc(
|
|
@@ -1149,10 +1160,7 @@ propskit-slider {
|
|
|
1149
1160
|
}
|
|
1150
1161
|
--slider-field-height: var(--propskit-slider-height);
|
|
1151
1162
|
--slider-thumb-width: var(--spacer-1);
|
|
1152
|
-
--slider-thumb-color:
|
|
1153
|
-
var(--figma-color-text-secondary),
|
|
1154
|
-
var(--figma-color-text)
|
|
1155
|
-
);
|
|
1163
|
+
--slider-thumb-color: var(--propskit-slider-handle-color);
|
|
1156
1164
|
/*--slider-thumb-height: calc(
|
|
1157
1165
|
var(--spacer-2) + (var(--spacer-3) - var(--spacer-2)) *
|
|
1158
1166
|
min(var(--slider-complete), calc(1 - var(--slider-complete))) * 2
|
|
@@ -1176,14 +1184,11 @@ propskit-slider {
|
|
|
1176
1184
|
var(--propskit-slider-thumb-inset)
|
|
1177
1185
|
);
|
|
1178
1186
|
--slider-handle-shadow:
|
|
1179
|
-
inset 0 0 0 var(--slider-thumb-height) var(--slider-thumb-color)
|
|
1180
|
-
var(--handle-shadow) !important;
|
|
1181
|
-
/* Fade handle toward track ends (0 at edges, 1 at center). Squared = sharper falloff. */
|
|
1182
|
-
--slider-thumb-edge-fade: calc(
|
|
1183
|
-
min(var(--slider-complete), calc(1 - var(--slider-complete))) * 2
|
|
1184
|
-
);
|
|
1187
|
+
inset 0 0 0 var(--slider-thumb-height) var(--slider-thumb-color) !important;
|
|
1185
1188
|
--slider-thumb-opacity: calc(
|
|
1186
|
-
var(--slider-
|
|
1189
|
+
var(--propskit-slider-handle-proximity) *
|
|
1190
|
+
var(--propskit-slider-handle-proximity) *
|
|
1191
|
+
var(--propskit-slider-handle-reveal)
|
|
1187
1192
|
);
|
|
1188
1193
|
--slider-tick-opacity: 0.5;
|
|
1189
1194
|
|
|
@@ -1481,6 +1486,16 @@ propskit-slider input[type="range"]:active::-moz-range-thumb {
|
|
|
1481
1486
|
cursor: ew-resize;
|
|
1482
1487
|
}
|
|
1483
1488
|
|
|
1489
|
+
propskit-slider input[type="range"]::-webkit-slider-thumb {
|
|
1490
|
+
transform: scale(var(--propskit-slider-handle-scale));
|
|
1491
|
+
transform-origin: center;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
propskit-slider input[type="range"]::-moz-range-thumb {
|
|
1495
|
+
transform: scale(var(--propskit-slider-handle-scale));
|
|
1496
|
+
transform-origin: center;
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1484
1499
|
fig-input-wheel {
|
|
1485
1500
|
--fig-input-wheel-handle-drag-max: 0.5rem;
|
|
1486
1501
|
--fig-input-wheel-handle-drag-offset: 0px;
|
|
@@ -1568,8 +1583,7 @@ fig-input-wheel {
|
|
|
1568
1583
|
transition: transform var(--fig-input-wheel-handle-spring);
|
|
1569
1584
|
box-shadow:
|
|
1570
1585
|
inset 0 0 0 var(--fig-input-wheel-handle-height)
|
|
1571
|
-
var(--fig-input-wheel-handle-color)
|
|
1572
|
-
var(--handle-shadow);
|
|
1586
|
+
var(--fig-input-wheel-handle-color);
|
|
1573
1587
|
}
|
|
1574
1588
|
|
|
1575
1589
|
&[data-fig-input-wheel-active] .fig-input-wheel-handle {
|
package/fig-lab.js
CHANGED
|
@@ -4631,6 +4631,7 @@ figLabDefineElement("propskit-group", PropskitGroup);
|
|
|
4631
4631
|
class PropskitSlider extends HTMLElement {
|
|
4632
4632
|
static #DRAG_THRESHOLD_PX = 4;
|
|
4633
4633
|
static #DRAGGING_BODY_CLASS = "propskit-slider-dragging";
|
|
4634
|
+
static #TEXT_MEASURE_CANVAS = null;
|
|
4634
4635
|
|
|
4635
4636
|
#field = null;
|
|
4636
4637
|
#label = null;
|
|
@@ -4640,6 +4641,12 @@ class PropskitSlider extends HTMLElement {
|
|
|
4640
4641
|
#managedSliderAttrs = new Set();
|
|
4641
4642
|
#steppersSyncFrame = 0;
|
|
4642
4643
|
#focusSyncFrame = 0;
|
|
4644
|
+
#handleProximityFrame = 0;
|
|
4645
|
+
#handleProximityObserver = null;
|
|
4646
|
+
#handleProximityMetricsDirty = true;
|
|
4647
|
+
#handleRadius = 0;
|
|
4648
|
+
#handleFadeStartThreshold = 1;
|
|
4649
|
+
#handleFadeEndThreshold = 0;
|
|
4643
4650
|
#rangeInput = null;
|
|
4644
4651
|
#contextMenu = null;
|
|
4645
4652
|
#pendingClickTimer = 0;
|
|
@@ -4698,6 +4705,8 @@ class PropskitSlider extends HTMLElement {
|
|
|
4698
4705
|
this.#syncSliderAttributes();
|
|
4699
4706
|
this.#bindSliderEvents();
|
|
4700
4707
|
this.#queueFocusDelegationSync();
|
|
4708
|
+
this.#connectHandleProximityObserver();
|
|
4709
|
+
this.#queueHandleProximitySync();
|
|
4701
4710
|
this.removeEventListener("pointerdown", this.#boundHandleElasticPointerDown, {
|
|
4702
4711
|
capture: true,
|
|
4703
4712
|
});
|
|
@@ -4777,6 +4786,11 @@ class PropskitSlider extends HTMLElement {
|
|
|
4777
4786
|
cancelAnimationFrame(this.#focusSyncFrame);
|
|
4778
4787
|
this.#focusSyncFrame = 0;
|
|
4779
4788
|
}
|
|
4789
|
+
if (this.#handleProximityFrame) {
|
|
4790
|
+
cancelAnimationFrame(this.#handleProximityFrame);
|
|
4791
|
+
this.#handleProximityFrame = 0;
|
|
4792
|
+
}
|
|
4793
|
+
this.#handleProximityObserver?.disconnect();
|
|
4780
4794
|
this.#clearPendingClick();
|
|
4781
4795
|
this.#stopNumberTracking();
|
|
4782
4796
|
clearTimeout(this.#numberClickResetTimer);
|
|
@@ -4881,6 +4895,7 @@ class PropskitSlider extends HTMLElement {
|
|
|
4881
4895
|
"direction",
|
|
4882
4896
|
this.getAttribute("direction") || "horizontal",
|
|
4883
4897
|
);
|
|
4898
|
+
this.#queueHandleProximitySync();
|
|
4884
4899
|
}
|
|
4885
4900
|
|
|
4886
4901
|
#syncSliderAttributes() {
|
|
@@ -4941,6 +4956,9 @@ class PropskitSlider extends HTMLElement {
|
|
|
4941
4956
|
this.#managedSliderAttrs = nextManaged;
|
|
4942
4957
|
this.#pushExternalValueToSlider();
|
|
4943
4958
|
this.#queueSteppersSync();
|
|
4959
|
+
this.#handleProximityMetricsDirty = true;
|
|
4960
|
+
this.#connectHandleProximityObserver();
|
|
4961
|
+
this.#queueHandleProximitySync();
|
|
4944
4962
|
}
|
|
4945
4963
|
|
|
4946
4964
|
#syncNumberTheme(sliderType) {
|
|
@@ -5006,6 +5024,207 @@ class PropskitSlider extends HTMLElement {
|
|
|
5006
5024
|
: 0;
|
|
5007
5025
|
this.style.setProperty("--propskit-slider-complete", String(complete));
|
|
5008
5026
|
this.#syncNumberTheme((this.getAttribute("type") || "range").toLowerCase());
|
|
5027
|
+
this.#queueHandleProximitySync();
|
|
5028
|
+
}
|
|
5029
|
+
|
|
5030
|
+
#connectHandleProximityObserver() {
|
|
5031
|
+
if (!globalThis.ResizeObserver) return;
|
|
5032
|
+
if (!this.#handleProximityObserver) {
|
|
5033
|
+
this.#handleProximityObserver = new ResizeObserver(() => {
|
|
5034
|
+
this.#handleProximityMetricsDirty = true;
|
|
5035
|
+
this.#queueHandleProximitySync();
|
|
5036
|
+
});
|
|
5037
|
+
}
|
|
5038
|
+
this.#handleProximityObserver.disconnect();
|
|
5039
|
+
for (const element of [
|
|
5040
|
+
this,
|
|
5041
|
+
this.#label,
|
|
5042
|
+
this.#slider?.querySelector("fig-input-number"),
|
|
5043
|
+
this.#slider?.querySelector("fig-input-number input"),
|
|
5044
|
+
this.#slider?.querySelector('input[type="range"]'),
|
|
5045
|
+
]) {
|
|
5046
|
+
if (element) this.#handleProximityObserver.observe(element);
|
|
5047
|
+
}
|
|
5048
|
+
}
|
|
5049
|
+
|
|
5050
|
+
#queueHandleProximitySync() {
|
|
5051
|
+
if (this.#handleProximityFrame) {
|
|
5052
|
+
cancelAnimationFrame(this.#handleProximityFrame);
|
|
5053
|
+
}
|
|
5054
|
+
this.#handleProximityFrame = requestAnimationFrame(() => {
|
|
5055
|
+
this.#handleProximityFrame = 0;
|
|
5056
|
+
this.#syncHandleProximity();
|
|
5057
|
+
});
|
|
5058
|
+
}
|
|
5059
|
+
|
|
5060
|
+
#syncHandleProximity() {
|
|
5061
|
+
const rangeInput = this.#slider?.querySelector('input[type="range"]');
|
|
5062
|
+
if (!rangeInput || !this.#slider) return;
|
|
5063
|
+
const rangeRect = rangeInput.getBoundingClientRect();
|
|
5064
|
+
if (!rangeRect.width) return;
|
|
5065
|
+
|
|
5066
|
+
const min = Number(rangeInput.min || 0);
|
|
5067
|
+
const max = Number(rangeInput.max || 100);
|
|
5068
|
+
const value = Number(rangeInput.value);
|
|
5069
|
+
const complete =
|
|
5070
|
+
Number.isFinite(min) &&
|
|
5071
|
+
Number.isFinite(max) &&
|
|
5072
|
+
max > min &&
|
|
5073
|
+
Number.isFinite(value)
|
|
5074
|
+
? Math.max(0, Math.min(1, (value - min) / (max - min)))
|
|
5075
|
+
: 0;
|
|
5076
|
+
const handleCenter = rangeRect.left + complete * rangeRect.width;
|
|
5077
|
+
if (this.#handleProximityMetricsDirty) {
|
|
5078
|
+
this.#handleRadius =
|
|
5079
|
+
this.#readCssLength("--slider-thumb-width", this.#slider) / 2;
|
|
5080
|
+
this.#handleFadeStartThreshold = Math.max(
|
|
5081
|
+
1,
|
|
5082
|
+
this.#readCssLength(
|
|
5083
|
+
"--propskit-slider-handle-fade-start-threshold",
|
|
5084
|
+
),
|
|
5085
|
+
);
|
|
5086
|
+
this.#handleFadeEndThreshold = Math.min(
|
|
5087
|
+
this.#handleFadeStartThreshold,
|
|
5088
|
+
this.#readCssLength(
|
|
5089
|
+
"--propskit-slider-handle-fade-end-threshold",
|
|
5090
|
+
),
|
|
5091
|
+
);
|
|
5092
|
+
this.#handleProximityMetricsDirty = false;
|
|
5093
|
+
}
|
|
5094
|
+
const handleRadius = this.#handleRadius;
|
|
5095
|
+
const distances = [];
|
|
5096
|
+
const labelRect =
|
|
5097
|
+
this.#label?.parentElement === this.#field
|
|
5098
|
+
? this.#contentRect(this.#label)
|
|
5099
|
+
: null;
|
|
5100
|
+
const numberInput = this.#slider.querySelector("fig-input-number input");
|
|
5101
|
+
const numberRect = numberInput ? this.#inputTextRect(numberInput) : null;
|
|
5102
|
+
|
|
5103
|
+
if (
|
|
5104
|
+
labelRect?.width &&
|
|
5105
|
+
labelRect.bottom > rangeRect.top &&
|
|
5106
|
+
labelRect.top < rangeRect.bottom
|
|
5107
|
+
) {
|
|
5108
|
+
distances.push(handleCenter - handleRadius - labelRect.right);
|
|
5109
|
+
}
|
|
5110
|
+
if (
|
|
5111
|
+
numberRect?.width &&
|
|
5112
|
+
numberRect.bottom > rangeRect.top &&
|
|
5113
|
+
numberRect.top < rangeRect.bottom
|
|
5114
|
+
) {
|
|
5115
|
+
distances.push(numberRect.left - handleCenter - handleRadius);
|
|
5116
|
+
}
|
|
5117
|
+
|
|
5118
|
+
const fadeStartThreshold = this.#handleFadeStartThreshold;
|
|
5119
|
+
const fadeEndThreshold = this.#handleFadeEndThreshold;
|
|
5120
|
+
const nearestDistance = distances.length
|
|
5121
|
+
? Math.min(...distances)
|
|
5122
|
+
: fadeStartThreshold;
|
|
5123
|
+
const fadeRange = Math.max(
|
|
5124
|
+
0.001,
|
|
5125
|
+
fadeStartThreshold - fadeEndThreshold,
|
|
5126
|
+
);
|
|
5127
|
+
const proximity = Math.max(
|
|
5128
|
+
0,
|
|
5129
|
+
Math.min(1, (nearestDistance - fadeEndThreshold) / fadeRange),
|
|
5130
|
+
);
|
|
5131
|
+
this.style.setProperty(
|
|
5132
|
+
"--propskit-slider-handle-proximity",
|
|
5133
|
+
String(proximity),
|
|
5134
|
+
);
|
|
5135
|
+
}
|
|
5136
|
+
|
|
5137
|
+
#contentRect(element) {
|
|
5138
|
+
const range = document.createRange();
|
|
5139
|
+
range.selectNodeContents(element);
|
|
5140
|
+
const rects = [...range.getClientRects()].filter(
|
|
5141
|
+
(rect) => rect.width > 0 && rect.height > 0,
|
|
5142
|
+
);
|
|
5143
|
+
range.detach?.();
|
|
5144
|
+
if (!rects.length) return null;
|
|
5145
|
+
const left = Math.min(...rects.map((rect) => rect.left));
|
|
5146
|
+
const right = Math.max(...rects.map((rect) => rect.right));
|
|
5147
|
+
const top = Math.min(...rects.map((rect) => rect.top));
|
|
5148
|
+
const bottom = Math.max(...rects.map((rect) => rect.bottom));
|
|
5149
|
+
return {
|
|
5150
|
+
left,
|
|
5151
|
+
right,
|
|
5152
|
+
top,
|
|
5153
|
+
bottom,
|
|
5154
|
+
width: right - left,
|
|
5155
|
+
height: bottom - top,
|
|
5156
|
+
};
|
|
5157
|
+
}
|
|
5158
|
+
|
|
5159
|
+
#inputTextRect(input) {
|
|
5160
|
+
const text = input.value || input.placeholder || "";
|
|
5161
|
+
if (!text) return null;
|
|
5162
|
+
const inputRect = input.getBoundingClientRect();
|
|
5163
|
+
const style = getComputedStyle(input);
|
|
5164
|
+
const textWidth = this.#measureTextWidth(text, style);
|
|
5165
|
+
const contentLeft =
|
|
5166
|
+
inputRect.left +
|
|
5167
|
+
(Number.parseFloat(style.borderLeftWidth) || 0) +
|
|
5168
|
+
(Number.parseFloat(style.paddingLeft) || 0);
|
|
5169
|
+
const contentRight =
|
|
5170
|
+
inputRect.right -
|
|
5171
|
+
(Number.parseFloat(style.borderRightWidth) || 0) -
|
|
5172
|
+
(Number.parseFloat(style.paddingRight) || 0);
|
|
5173
|
+
const contentWidth = Math.max(0, contentRight - contentLeft);
|
|
5174
|
+
const width = Math.min(contentWidth, textWidth);
|
|
5175
|
+
const alignment = style.textAlign;
|
|
5176
|
+
let left = contentLeft;
|
|
5177
|
+
if (alignment === "right" || alignment === "end") {
|
|
5178
|
+
left = contentRight - width;
|
|
5179
|
+
} else if (alignment === "center") {
|
|
5180
|
+
left = contentLeft + (contentWidth - width) / 2;
|
|
5181
|
+
}
|
|
5182
|
+
return {
|
|
5183
|
+
left,
|
|
5184
|
+
right: left + width,
|
|
5185
|
+
top: inputRect.top,
|
|
5186
|
+
bottom: inputRect.bottom,
|
|
5187
|
+
width,
|
|
5188
|
+
height: inputRect.height,
|
|
5189
|
+
};
|
|
5190
|
+
}
|
|
5191
|
+
|
|
5192
|
+
#measureTextWidth(text, style) {
|
|
5193
|
+
if (!PropskitSlider.#TEXT_MEASURE_CANVAS) {
|
|
5194
|
+
PropskitSlider.#TEXT_MEASURE_CANVAS = document.createElement("canvas");
|
|
5195
|
+
}
|
|
5196
|
+
const context = PropskitSlider.#TEXT_MEASURE_CANVAS.getContext("2d");
|
|
5197
|
+
if (!context) return 0;
|
|
5198
|
+
context.font =
|
|
5199
|
+
style.font ||
|
|
5200
|
+
`${style.fontStyle} ${style.fontWeight} ${style.fontSize} ${style.fontFamily}`;
|
|
5201
|
+
if ("fontKerning" in context) context.fontKerning = style.fontKerning;
|
|
5202
|
+
if ("fontStretch" in context) context.fontStretch = style.fontStretch;
|
|
5203
|
+
if ("fontVariantCaps" in context) {
|
|
5204
|
+
context.fontVariantCaps = style.fontVariantCaps;
|
|
5205
|
+
}
|
|
5206
|
+
if ("letterSpacing" in context) {
|
|
5207
|
+
context.letterSpacing = style.letterSpacing;
|
|
5208
|
+
}
|
|
5209
|
+
return context.measureText(text).width;
|
|
5210
|
+
}
|
|
5211
|
+
|
|
5212
|
+
#readCssLength(property, context = this) {
|
|
5213
|
+
const probe = document.createElement("div");
|
|
5214
|
+
probe.style.setProperty(
|
|
5215
|
+
property,
|
|
5216
|
+
getComputedStyle(context).getPropertyValue(property),
|
|
5217
|
+
);
|
|
5218
|
+
Object.assign(probe.style, {
|
|
5219
|
+
position: "absolute",
|
|
5220
|
+
visibility: "hidden",
|
|
5221
|
+
pointerEvents: "none",
|
|
5222
|
+
width: `var(${property})`,
|
|
5223
|
+
});
|
|
5224
|
+
this.appendChild(probe);
|
|
5225
|
+
const value = Number.parseFloat(getComputedStyle(probe).width);
|
|
5226
|
+
probe.remove();
|
|
5227
|
+
return Number.isFinite(value) ? Math.max(0, value) : 0;
|
|
5009
5228
|
}
|
|
5010
5229
|
|
|
5011
5230
|
#pushExternalValueToSlider() {
|
package/package.json
CHANGED