@rogieking/figui3 3.4.3 → 3.6.0
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 +237 -32
- package/dist/fig.js +29 -29
- package/fig.js +399 -81
- package/package.json +1 -1
package/fig.js
CHANGED
|
@@ -9,7 +9,7 @@ function figIsWebKitOrIOSBrowser() {
|
|
|
9
9
|
const userAgent = navigator.userAgent || "";
|
|
10
10
|
const isIOSBrowser =
|
|
11
11
|
/\b(iPad|iPhone|iPod)\b/.test(userAgent) ||
|
|
12
|
-
/\bMacintosh\b/.test(userAgent) && /\bMobile\b/.test(userAgent);
|
|
12
|
+
(/\bMacintosh\b/.test(userAgent) && /\bMobile\b/.test(userAgent));
|
|
13
13
|
const isDesktopWebKit =
|
|
14
14
|
/\bAppleWebKit\b/.test(userAgent) &&
|
|
15
15
|
!/\b(Chrome|Chromium|Edg|OPR|SamsungBrowser)\b/.test(userAgent);
|
|
@@ -41,9 +41,10 @@ function figSupportsCustomizedBuiltIns() {
|
|
|
41
41
|
|
|
42
42
|
const figNeedsBuiltInPolyfill =
|
|
43
43
|
figIsWebKitOrIOSBrowser() && !figSupportsCustomizedBuiltIns();
|
|
44
|
-
const figBuiltInPolyfillReady = (
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
const figBuiltInPolyfillReady = (
|
|
45
|
+
figNeedsBuiltInPolyfill
|
|
46
|
+
? import("./polyfills/custom-elements-webkit.js")
|
|
47
|
+
: Promise.resolve()
|
|
47
48
|
)
|
|
48
49
|
.then(() => {})
|
|
49
50
|
.catch((error) => {
|
|
@@ -62,14 +63,12 @@ function figDefineCustomizedBuiltIn(name, constructor, options) {
|
|
|
62
63
|
return;
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
figBuiltInPolyfillReady
|
|
66
|
-
.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
);
|
|
72
|
-
});
|
|
66
|
+
figBuiltInPolyfillReady.then(define).catch((error) => {
|
|
67
|
+
console.error(
|
|
68
|
+
`[figui3] Failed to load customized built-in polyfill for "${name}".`,
|
|
69
|
+
error,
|
|
70
|
+
);
|
|
71
|
+
});
|
|
73
72
|
}
|
|
74
73
|
|
|
75
74
|
function figUniqueId() {
|
|
@@ -1208,18 +1207,25 @@ class FigPopup extends HTMLDialogElement {
|
|
|
1208
1207
|
}
|
|
1209
1208
|
|
|
1210
1209
|
ensureInitialized() {
|
|
1211
|
-
if (typeof this._anchorObserver === "undefined")
|
|
1212
|
-
|
|
1213
|
-
if (typeof this.
|
|
1214
|
-
|
|
1215
|
-
if (typeof this.
|
|
1210
|
+
if (typeof this._anchorObserver === "undefined")
|
|
1211
|
+
this._anchorObserver = null;
|
|
1212
|
+
if (typeof this._contentObserver === "undefined")
|
|
1213
|
+
this._contentObserver = null;
|
|
1214
|
+
if (typeof this._mutationObserver === "undefined")
|
|
1215
|
+
this._mutationObserver = null;
|
|
1216
|
+
if (typeof this._anchorTrackRAF === "undefined")
|
|
1217
|
+
this._anchorTrackRAF = null;
|
|
1218
|
+
if (typeof this._lastAnchorRect === "undefined")
|
|
1219
|
+
this._lastAnchorRect = null;
|
|
1216
1220
|
if (typeof this._isPopupActive === "undefined") this._isPopupActive = false;
|
|
1217
1221
|
if (typeof this._rafId === "undefined") this._rafId = null;
|
|
1218
1222
|
if (typeof this._anchorRef === "undefined") this._anchorRef = null;
|
|
1219
1223
|
if (typeof this._isDragging === "undefined") this._isDragging = false;
|
|
1220
1224
|
if (typeof this._dragPending === "undefined") this._dragPending = false;
|
|
1221
|
-
if (typeof this._dragStartPos === "undefined")
|
|
1222
|
-
|
|
1225
|
+
if (typeof this._dragStartPos === "undefined")
|
|
1226
|
+
this._dragStartPos = { x: 0, y: 0 };
|
|
1227
|
+
if (typeof this._dragOffset === "undefined")
|
|
1228
|
+
this._dragOffset = { x: 0, y: 0 };
|
|
1223
1229
|
if (typeof this._dragThreshold !== "number") this._dragThreshold = 3;
|
|
1224
1230
|
if (typeof this._wasDragged === "undefined") this._wasDragged = false;
|
|
1225
1231
|
|
|
@@ -1228,7 +1234,11 @@ class FigPopup extends HTMLDialogElement {
|
|
|
1228
1234
|
}
|
|
1229
1235
|
if (typeof this._boundScroll !== "function") {
|
|
1230
1236
|
this._boundScroll = (e) => {
|
|
1231
|
-
if (
|
|
1237
|
+
if (
|
|
1238
|
+
this.open &&
|
|
1239
|
+
!this.contains(e.target) &&
|
|
1240
|
+
this.shouldAutoReposition()
|
|
1241
|
+
) {
|
|
1232
1242
|
this.queueReposition();
|
|
1233
1243
|
}
|
|
1234
1244
|
};
|
|
@@ -1516,7 +1526,10 @@ class FigPopup extends HTMLDialogElement {
|
|
|
1516
1526
|
const anchor = this.resolveAnchor();
|
|
1517
1527
|
const nextRect = this.readRectSnapshot(anchor);
|
|
1518
1528
|
const canAutoReposition = this.shouldAutoReposition();
|
|
1519
|
-
if (
|
|
1529
|
+
if (
|
|
1530
|
+
canAutoReposition &&
|
|
1531
|
+
this.hasRectChanged(this._lastAnchorRect, nextRect)
|
|
1532
|
+
) {
|
|
1520
1533
|
this._lastAnchorRect = nextRect;
|
|
1521
1534
|
this.queueReposition();
|
|
1522
1535
|
} else if (!canAutoReposition) {
|
|
@@ -1989,7 +2002,9 @@ class FigPopup extends HTMLDialogElement {
|
|
|
1989
2002
|
const anchorCenterY = anchorRect.top + anchorRect.height / 2;
|
|
1990
2003
|
const measuredRect = this.getBoundingClientRect();
|
|
1991
2004
|
const rect =
|
|
1992
|
-
measuredRect.width > 0 && measuredRect.height > 0
|
|
2005
|
+
measuredRect.width > 0 && measuredRect.height > 0
|
|
2006
|
+
? measuredRect
|
|
2007
|
+
: popupRect;
|
|
1993
2008
|
// Always use the rendered popup rect so beak alignment matches real final placement.
|
|
1994
2009
|
const resolvedLeft = rect.left;
|
|
1995
2010
|
const resolvedTop = rect.top;
|
|
@@ -2082,14 +2097,7 @@ class FigPopup extends HTMLDialogElement {
|
|
|
2082
2097
|
let bestScore = Number.POSITIVE_INFINITY;
|
|
2083
2098
|
|
|
2084
2099
|
for (const { v, h, s } of candidates) {
|
|
2085
|
-
const coords = this.computeCoords(
|
|
2086
|
-
anchorRect,
|
|
2087
|
-
popupRect,
|
|
2088
|
-
v,
|
|
2089
|
-
h,
|
|
2090
|
-
offset,
|
|
2091
|
-
s,
|
|
2092
|
-
);
|
|
2100
|
+
const coords = this.computeCoords(anchorRect, popupRect, v, h, offset, s);
|
|
2093
2101
|
const placementSide = this.getPlacementSide(v, h, s);
|
|
2094
2102
|
|
|
2095
2103
|
if (s) {
|
|
@@ -3102,11 +3110,7 @@ class FigSlider extends HTMLElement {
|
|
|
3102
3110
|
break;
|
|
3103
3111
|
case "default":
|
|
3104
3112
|
this.default =
|
|
3105
|
-
newValue !== null
|
|
3106
|
-
? newValue
|
|
3107
|
-
: this.type === "delta"
|
|
3108
|
-
? 0
|
|
3109
|
-
: this.min;
|
|
3113
|
+
newValue !== null ? newValue : this.type === "delta" ? 0 : this.min;
|
|
3110
3114
|
this.#syncProperties();
|
|
3111
3115
|
break;
|
|
3112
3116
|
case "min":
|
|
@@ -3172,7 +3176,8 @@ class FigInputText extends HTMLElement {
|
|
|
3172
3176
|
this.placeholder = this.getAttribute("placeholder") || "";
|
|
3173
3177
|
this.name = this.getAttribute("name") || null;
|
|
3174
3178
|
this.readonly =
|
|
3175
|
-
this.hasAttribute("readonly") &&
|
|
3179
|
+
this.hasAttribute("readonly") &&
|
|
3180
|
+
this.getAttribute("readonly") !== "false";
|
|
3176
3181
|
|
|
3177
3182
|
if (this.type === "number") {
|
|
3178
3183
|
if (this.getAttribute("step")) {
|
|
@@ -4007,6 +4012,229 @@ class FigField extends HTMLElement {
|
|
|
4007
4012
|
}
|
|
4008
4013
|
customElements.define("fig-field", FigField);
|
|
4009
4014
|
|
|
4015
|
+
/* Field + Slider wrapper */
|
|
4016
|
+
class FigFieldSlider extends HTMLElement {
|
|
4017
|
+
#field = null;
|
|
4018
|
+
#label = null;
|
|
4019
|
+
#slider = null;
|
|
4020
|
+
#observer = null;
|
|
4021
|
+
#managedSliderAttrs = new Set();
|
|
4022
|
+
#boundHandleSliderInput = null;
|
|
4023
|
+
#boundHandleSliderChange = null;
|
|
4024
|
+
#ignoredSliderAttrs = new Set(["variant", "color", "text", "full"]);
|
|
4025
|
+
|
|
4026
|
+
static get observedAttributes() {
|
|
4027
|
+
return ["label", "direction"];
|
|
4028
|
+
}
|
|
4029
|
+
|
|
4030
|
+
connectedCallback() {
|
|
4031
|
+
if (!this.#field) {
|
|
4032
|
+
this.#initialize();
|
|
4033
|
+
}
|
|
4034
|
+
|
|
4035
|
+
this.#syncField();
|
|
4036
|
+
this.#syncSliderAttributes();
|
|
4037
|
+
this.#bindSliderEvents();
|
|
4038
|
+
|
|
4039
|
+
if (!this.#observer) {
|
|
4040
|
+
this.#observer = new MutationObserver((mutations) => {
|
|
4041
|
+
let syncField = false;
|
|
4042
|
+
let syncSlider = false;
|
|
4043
|
+
|
|
4044
|
+
for (const mutation of mutations) {
|
|
4045
|
+
if (mutation.type === "attributes") {
|
|
4046
|
+
if (
|
|
4047
|
+
mutation.attributeName &&
|
|
4048
|
+
this.#ignoredSliderAttrs.has(mutation.attributeName)
|
|
4049
|
+
) {
|
|
4050
|
+
continue;
|
|
4051
|
+
}
|
|
4052
|
+
if (
|
|
4053
|
+
mutation.attributeName === "label" ||
|
|
4054
|
+
mutation.attributeName === "direction"
|
|
4055
|
+
) {
|
|
4056
|
+
syncField = true;
|
|
4057
|
+
} else {
|
|
4058
|
+
syncSlider = true;
|
|
4059
|
+
}
|
|
4060
|
+
}
|
|
4061
|
+
}
|
|
4062
|
+
|
|
4063
|
+
if (syncField) this.#syncField();
|
|
4064
|
+
if (syncSlider) this.#syncSliderAttributes();
|
|
4065
|
+
});
|
|
4066
|
+
}
|
|
4067
|
+
|
|
4068
|
+
this.#observer.observe(this, { attributes: true });
|
|
4069
|
+
}
|
|
4070
|
+
|
|
4071
|
+
disconnectedCallback() {
|
|
4072
|
+
this.#observer?.disconnect();
|
|
4073
|
+
this.#unbindSliderEvents();
|
|
4074
|
+
}
|
|
4075
|
+
|
|
4076
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
4077
|
+
if (oldValue === newValue || !this.#field) return;
|
|
4078
|
+
if (name === "label" || name === "direction") {
|
|
4079
|
+
this.#syncField();
|
|
4080
|
+
}
|
|
4081
|
+
}
|
|
4082
|
+
|
|
4083
|
+
#initialize() {
|
|
4084
|
+
const initialChildren = Array.from(this.childNodes).filter((node) => {
|
|
4085
|
+
return (
|
|
4086
|
+
node.nodeType !== Node.TEXT_NODE || Boolean(node.textContent?.trim())
|
|
4087
|
+
);
|
|
4088
|
+
});
|
|
4089
|
+
|
|
4090
|
+
const field = document.createElement("fig-field");
|
|
4091
|
+
const label = document.createElement("label");
|
|
4092
|
+
const slider = document.createElement("fig-slider");
|
|
4093
|
+
slider.setAttribute("text", "true");
|
|
4094
|
+
for (const attrName of this.#getForwardedSliderAttrNames()) {
|
|
4095
|
+
const value = this.getAttribute(attrName);
|
|
4096
|
+
slider.setAttribute(attrName, value ?? "");
|
|
4097
|
+
}
|
|
4098
|
+
|
|
4099
|
+
field.append(label, slider);
|
|
4100
|
+
|
|
4101
|
+
this.#field = field;
|
|
4102
|
+
this.#label = label;
|
|
4103
|
+
this.#slider = slider;
|
|
4104
|
+
|
|
4105
|
+
this.replaceChildren(field);
|
|
4106
|
+
|
|
4107
|
+
for (const node of initialChildren) {
|
|
4108
|
+
this.#slider.appendChild(node);
|
|
4109
|
+
}
|
|
4110
|
+
}
|
|
4111
|
+
|
|
4112
|
+
#syncField() {
|
|
4113
|
+
if (!this.#field || !this.#label) return;
|
|
4114
|
+
const hasLabelAttr = this.hasAttribute("label");
|
|
4115
|
+
const rawLabel = this.getAttribute("label");
|
|
4116
|
+
const isBlankLabel = hasLabelAttr && (rawLabel ?? "").trim() === "";
|
|
4117
|
+
|
|
4118
|
+
if (isBlankLabel) {
|
|
4119
|
+
if (this.#label.parentElement === this.#field) {
|
|
4120
|
+
this.#label.remove();
|
|
4121
|
+
}
|
|
4122
|
+
} else {
|
|
4123
|
+
this.#label.textContent = hasLabelAttr ? rawLabel ?? "" : "Label";
|
|
4124
|
+
if (this.#label.parentElement !== this.#field) {
|
|
4125
|
+
this.#field.prepend(this.#label);
|
|
4126
|
+
}
|
|
4127
|
+
}
|
|
4128
|
+
|
|
4129
|
+
this.#field.setAttribute(
|
|
4130
|
+
"direction",
|
|
4131
|
+
this.getAttribute("direction") || "horizontal",
|
|
4132
|
+
);
|
|
4133
|
+
}
|
|
4134
|
+
|
|
4135
|
+
#syncSliderAttributes() {
|
|
4136
|
+
if (!this.#slider) return;
|
|
4137
|
+
const hostAttrs = this.#getForwardedSliderAttrNames();
|
|
4138
|
+
|
|
4139
|
+
const nextManaged = new Set(hostAttrs.filter((name) => name !== "text"));
|
|
4140
|
+
|
|
4141
|
+
for (const attrName of this.#managedSliderAttrs) {
|
|
4142
|
+
if (!nextManaged.has(attrName)) {
|
|
4143
|
+
this.#slider.removeAttribute(attrName);
|
|
4144
|
+
}
|
|
4145
|
+
}
|
|
4146
|
+
|
|
4147
|
+
for (const attrName of hostAttrs) {
|
|
4148
|
+
if (attrName === "text") continue;
|
|
4149
|
+
const value = this.getAttribute(attrName);
|
|
4150
|
+
this.#slider.setAttribute(attrName, value ?? "");
|
|
4151
|
+
}
|
|
4152
|
+
|
|
4153
|
+
this.#slider.removeAttribute("variant");
|
|
4154
|
+
this.#slider.removeAttribute("color");
|
|
4155
|
+
this.#slider.removeAttribute("transform");
|
|
4156
|
+
this.#slider.removeAttribute("full");
|
|
4157
|
+
this.#slider.setAttribute("text", "true");
|
|
4158
|
+
|
|
4159
|
+
const sliderType = (this.getAttribute("type") || "range").toLowerCase();
|
|
4160
|
+
if (sliderType === "delta" || sliderType === "stepper") {
|
|
4161
|
+
this.#slider.setAttribute(
|
|
4162
|
+
"default",
|
|
4163
|
+
this.getAttribute("default") ?? "50",
|
|
4164
|
+
);
|
|
4165
|
+
} else if (!this.hasAttribute("default")) {
|
|
4166
|
+
this.#slider.removeAttribute("default");
|
|
4167
|
+
}
|
|
4168
|
+
if (sliderType === "stepper") {
|
|
4169
|
+
this.#slider.setAttribute("step", this.getAttribute("step") ?? "10");
|
|
4170
|
+
} else if (!this.hasAttribute("step")) {
|
|
4171
|
+
this.#slider.removeAttribute("step");
|
|
4172
|
+
}
|
|
4173
|
+
if (sliderType === "opacity") {
|
|
4174
|
+
this.#slider.style.setProperty(
|
|
4175
|
+
"--color",
|
|
4176
|
+
"var(--figma-color-bg-tertiary)",
|
|
4177
|
+
);
|
|
4178
|
+
} else {
|
|
4179
|
+
this.#slider.style.removeProperty("--color");
|
|
4180
|
+
}
|
|
4181
|
+
|
|
4182
|
+
this.#managedSliderAttrs = nextManaged;
|
|
4183
|
+
}
|
|
4184
|
+
|
|
4185
|
+
#getForwardedSliderAttrNames() {
|
|
4186
|
+
const reserved = new Set(["label", "direction", "oninput", "onchange"]);
|
|
4187
|
+
return this.getAttributeNames().filter(
|
|
4188
|
+
(name) => !reserved.has(name) && !this.#ignoredSliderAttrs.has(name),
|
|
4189
|
+
);
|
|
4190
|
+
}
|
|
4191
|
+
|
|
4192
|
+
#bindSliderEvents() {
|
|
4193
|
+
if (!this.#slider) return;
|
|
4194
|
+
if (!this.#boundHandleSliderInput) {
|
|
4195
|
+
this.#boundHandleSliderInput = this.#forwardSliderEvent.bind(
|
|
4196
|
+
this,
|
|
4197
|
+
"input",
|
|
4198
|
+
);
|
|
4199
|
+
}
|
|
4200
|
+
if (!this.#boundHandleSliderChange) {
|
|
4201
|
+
this.#boundHandleSliderChange = this.#forwardSliderEvent.bind(
|
|
4202
|
+
this,
|
|
4203
|
+
"change",
|
|
4204
|
+
);
|
|
4205
|
+
}
|
|
4206
|
+
this.#slider.addEventListener("input", this.#boundHandleSliderInput);
|
|
4207
|
+
this.#slider.addEventListener("change", this.#boundHandleSliderChange);
|
|
4208
|
+
}
|
|
4209
|
+
|
|
4210
|
+
#unbindSliderEvents() {
|
|
4211
|
+
if (!this.#slider) return;
|
|
4212
|
+
if (this.#boundHandleSliderInput) {
|
|
4213
|
+
this.#slider.removeEventListener("input", this.#boundHandleSliderInput);
|
|
4214
|
+
}
|
|
4215
|
+
if (this.#boundHandleSliderChange) {
|
|
4216
|
+
this.#slider.removeEventListener("change", this.#boundHandleSliderChange);
|
|
4217
|
+
}
|
|
4218
|
+
}
|
|
4219
|
+
|
|
4220
|
+
#forwardSliderEvent(type, event) {
|
|
4221
|
+
event.stopPropagation();
|
|
4222
|
+
const detail =
|
|
4223
|
+
event instanceof CustomEvent && event.detail !== undefined
|
|
4224
|
+
? event.detail
|
|
4225
|
+
: this.#slider?.value;
|
|
4226
|
+
this.dispatchEvent(
|
|
4227
|
+
new CustomEvent(type, {
|
|
4228
|
+
detail,
|
|
4229
|
+
bubbles: true,
|
|
4230
|
+
cancelable: true,
|
|
4231
|
+
composed: true,
|
|
4232
|
+
}),
|
|
4233
|
+
);
|
|
4234
|
+
}
|
|
4235
|
+
}
|
|
4236
|
+
customElements.define("fig-field-slider", FigFieldSlider);
|
|
4237
|
+
|
|
4010
4238
|
/* Color swatch */
|
|
4011
4239
|
class FigInputColor extends HTMLElement {
|
|
4012
4240
|
rgba;
|
|
@@ -5684,8 +5912,22 @@ class FigChit extends HTMLElement {
|
|
|
5684
5912
|
}
|
|
5685
5913
|
}
|
|
5686
5914
|
|
|
5915
|
+
#resolveBackground(bg) {
|
|
5916
|
+
if (!bg || !bg.includes("var(")) return bg;
|
|
5917
|
+
const prev = this.style.background;
|
|
5918
|
+
this.style.background = bg;
|
|
5919
|
+
const cs = getComputedStyle(this);
|
|
5920
|
+
const bgImage = cs.backgroundImage;
|
|
5921
|
+
const bgColor = cs.backgroundColor;
|
|
5922
|
+
this.style.background = prev;
|
|
5923
|
+
if (bgImage && bgImage !== "none") return bgImage;
|
|
5924
|
+
return bgColor || bg;
|
|
5925
|
+
}
|
|
5926
|
+
|
|
5687
5927
|
#render() {
|
|
5688
|
-
const
|
|
5928
|
+
const rawBg = this.getAttribute("background") || "#D9D9D9";
|
|
5929
|
+
const isVar = rawBg.includes("var(");
|
|
5930
|
+
const bg = isVar ? this.#resolveBackground(rawBg) : rawBg;
|
|
5689
5931
|
const newType = this.#detectType(bg);
|
|
5690
5932
|
|
|
5691
5933
|
// Only rebuild DOM if type changes
|
|
@@ -5702,21 +5944,22 @@ class FigChit extends HTMLElement {
|
|
|
5702
5944
|
const hex = this.#toHex(bg);
|
|
5703
5945
|
this.innerHTML = `<input type="color" value="${hex}" />`;
|
|
5704
5946
|
this.input = this.querySelector("input");
|
|
5705
|
-
|
|
5947
|
+
if (!isVar) {
|
|
5948
|
+
this.input.addEventListener("input", this.#boundHandleInput);
|
|
5949
|
+
}
|
|
5706
5950
|
} else {
|
|
5707
5951
|
this.innerHTML = "";
|
|
5708
5952
|
this.input = null;
|
|
5709
5953
|
}
|
|
5710
5954
|
} else if (this.#type === "color" && this.input) {
|
|
5711
|
-
// Just update input value without rebuilding DOM
|
|
5712
5955
|
const hex = this.#toHex(bg);
|
|
5713
5956
|
if (this.input.value !== hex) {
|
|
5714
5957
|
this.input.value = hex;
|
|
5715
5958
|
}
|
|
5716
5959
|
}
|
|
5717
5960
|
|
|
5718
|
-
// Always update CSS variable
|
|
5719
|
-
this.style.setProperty("--chit-background",
|
|
5961
|
+
// Always update CSS variable with raw value so vars stay reactive
|
|
5962
|
+
this.style.setProperty("--chit-background", rawBg);
|
|
5720
5963
|
}
|
|
5721
5964
|
|
|
5722
5965
|
#handleInput(e) {
|
|
@@ -5783,9 +6026,15 @@ class FigImage extends HTMLElement {
|
|
|
5783
6026
|
super();
|
|
5784
6027
|
}
|
|
5785
6028
|
#getInnerHTML() {
|
|
5786
|
-
|
|
5787
|
-
this.
|
|
5788
|
-
|
|
6029
|
+
const cb =
|
|
6030
|
+
this.hasAttribute("checkerboard") &&
|
|
6031
|
+
this.getAttribute("checkerboard") !== "false";
|
|
6032
|
+
const bg = this.src
|
|
6033
|
+
? `url(${this.src})`
|
|
6034
|
+
: cb
|
|
6035
|
+
? "url()"
|
|
6036
|
+
: "var(--figma-color-bg-secondary)";
|
|
6037
|
+
return `<fig-chit size="large" data-type="image" background="${bg}" disabled${cb ? " checkerboard" : ""}></fig-chit><div>${
|
|
5789
6038
|
this.upload
|
|
5790
6039
|
? `<fig-button variant="overlay" type="upload">
|
|
5791
6040
|
${this.label}
|
|
@@ -5946,7 +6195,7 @@ class FigImage extends HTMLElement {
|
|
|
5946
6195
|
this.setAttribute("src", this.blob);
|
|
5947
6196
|
}
|
|
5948
6197
|
static get observedAttributes() {
|
|
5949
|
-
return ["src", "upload", "download", "aspect-ratio", "fit"];
|
|
6198
|
+
return ["src", "upload", "download", "aspect-ratio", "fit", "checkerboard"];
|
|
5950
6199
|
}
|
|
5951
6200
|
get src() {
|
|
5952
6201
|
return this.#src;
|
|
@@ -5960,10 +6209,17 @@ class FigImage extends HTMLElement {
|
|
|
5960
6209
|
if (name === "src") {
|
|
5961
6210
|
this.#src = newValue;
|
|
5962
6211
|
if (this.chit) {
|
|
5963
|
-
|
|
5964
|
-
"
|
|
5965
|
-
this
|
|
5966
|
-
)
|
|
6212
|
+
const hasCb =
|
|
6213
|
+
this.hasAttribute("checkerboard") &&
|
|
6214
|
+
this.getAttribute("checkerboard") !== "false";
|
|
6215
|
+
if (this.#src) {
|
|
6216
|
+
this.chit.setAttribute("background", `url(${this.#src})`);
|
|
6217
|
+
} else {
|
|
6218
|
+
this.chit.setAttribute(
|
|
6219
|
+
"background",
|
|
6220
|
+
hasCb ? "url()" : "var(--figma-color-bg-secondary)",
|
|
6221
|
+
);
|
|
6222
|
+
}
|
|
5967
6223
|
}
|
|
5968
6224
|
if (this.#src) {
|
|
5969
6225
|
this.#loadImage(this.#src);
|
|
@@ -5996,6 +6252,15 @@ class FigImage extends HTMLElement {
|
|
|
5996
6252
|
this.style.removeProperty("--fit");
|
|
5997
6253
|
}
|
|
5998
6254
|
}
|
|
6255
|
+
if (name === "checkerboard") {
|
|
6256
|
+
if (this.chit) {
|
|
6257
|
+
if (newValue !== null && newValue !== "false") {
|
|
6258
|
+
this.chit.setAttribute("checkerboard", "");
|
|
6259
|
+
} else {
|
|
6260
|
+
this.chit.removeAttribute("checkerboard");
|
|
6261
|
+
}
|
|
6262
|
+
}
|
|
6263
|
+
}
|
|
5999
6264
|
}
|
|
6000
6265
|
}
|
|
6001
6266
|
customElements.define("fig-image", FigImage);
|
|
@@ -6714,7 +6979,9 @@ class FigEasingCurve extends HTMLElement {
|
|
|
6714
6979
|
this.#startBezierDrag(e, 2),
|
|
6715
6980
|
);
|
|
6716
6981
|
|
|
6717
|
-
const bezierSurface = this.querySelector(
|
|
6982
|
+
const bezierSurface = this.querySelector(
|
|
6983
|
+
".fig-easing-curve-svg-container",
|
|
6984
|
+
);
|
|
6718
6985
|
if (bezierSurface) {
|
|
6719
6986
|
bezierSurface.addEventListener("pointerdown", (e) => {
|
|
6720
6987
|
// Handles keep their own direct drag behavior.
|
|
@@ -6732,7 +6999,9 @@ class FigEasingCurve extends HTMLElement {
|
|
|
6732
6999
|
this.#startSpringDrag(e, "duration");
|
|
6733
7000
|
});
|
|
6734
7001
|
|
|
6735
|
-
const springSurface = this.querySelector(
|
|
7002
|
+
const springSurface = this.querySelector(
|
|
7003
|
+
".fig-easing-curve-svg-container",
|
|
7004
|
+
);
|
|
6736
7005
|
if (springSurface) {
|
|
6737
7006
|
springSurface.addEventListener("pointerdown", (e) => {
|
|
6738
7007
|
// Bounce handle keeps its own drag mode/cursor.
|
|
@@ -6902,14 +7171,27 @@ class Fig3DRotate extends HTMLElement {
|
|
|
6902
7171
|
#fieldInputs = {};
|
|
6903
7172
|
|
|
6904
7173
|
static get observedAttributes() {
|
|
6905
|
-
return [
|
|
7174
|
+
return [
|
|
7175
|
+
"value",
|
|
7176
|
+
"precision",
|
|
7177
|
+
"aspect-ratio",
|
|
7178
|
+
"fields",
|
|
7179
|
+
"perspective",
|
|
7180
|
+
"perspective-origin",
|
|
7181
|
+
"transform-origin",
|
|
7182
|
+
"selected",
|
|
7183
|
+
"drag",
|
|
7184
|
+
];
|
|
6906
7185
|
}
|
|
6907
7186
|
|
|
6908
7187
|
connectedCallback() {
|
|
6909
7188
|
this.#precision = parseInt(this.getAttribute("precision") || "1");
|
|
6910
7189
|
this.#syncAspectRatioVar(this.getAttribute("aspect-ratio"));
|
|
6911
7190
|
this.#syncPerspectiveVar(this.getAttribute("perspective"));
|
|
6912
|
-
this.#syncCSSVar(
|
|
7191
|
+
this.#syncCSSVar(
|
|
7192
|
+
"--perspective-origin",
|
|
7193
|
+
this.getAttribute("perspective-origin"),
|
|
7194
|
+
);
|
|
6913
7195
|
this.#syncTransformOrigin(this.getAttribute("transform-origin"));
|
|
6914
7196
|
this.#parseFields(this.getAttribute("fields"));
|
|
6915
7197
|
const val = this.getAttribute("value");
|
|
@@ -6958,7 +7240,10 @@ class Fig3DRotate extends HTMLElement {
|
|
|
6958
7240
|
}
|
|
6959
7241
|
const parts = value.trim().split(/\s+/);
|
|
6960
7242
|
if (parts.length === 2) {
|
|
6961
|
-
this.style.setProperty(
|
|
7243
|
+
this.style.setProperty(
|
|
7244
|
+
"--transform-origin",
|
|
7245
|
+
`${parts[0]} ${parts[1]} -50cqi`,
|
|
7246
|
+
);
|
|
6962
7247
|
} else {
|
|
6963
7248
|
this.style.setProperty("--transform-origin", value.trim());
|
|
6964
7249
|
}
|
|
@@ -6974,7 +7259,10 @@ class Fig3DRotate extends HTMLElement {
|
|
|
6974
7259
|
const faces = this.#cube.querySelectorAll(".fig-3d-rotate-face");
|
|
6975
7260
|
const name = value ? value.trim().toLowerCase() : "";
|
|
6976
7261
|
for (const face of faces) {
|
|
6977
|
-
face.classList.toggle(
|
|
7262
|
+
face.classList.toggle(
|
|
7263
|
+
"selected",
|
|
7264
|
+
name !== "" && face.classList.contains(name),
|
|
7265
|
+
);
|
|
6978
7266
|
}
|
|
6979
7267
|
}
|
|
6980
7268
|
|
|
@@ -7062,7 +7350,11 @@ class Fig3DRotate extends HTMLElement {
|
|
|
7062
7350
|
|
|
7063
7351
|
#render() {
|
|
7064
7352
|
const axisLabels = { rotateX: "X", rotateY: "Y", rotateZ: "Z" };
|
|
7065
|
-
const axisValues = {
|
|
7353
|
+
const axisValues = {
|
|
7354
|
+
rotateX: this.#rx,
|
|
7355
|
+
rotateY: this.#ry,
|
|
7356
|
+
rotateZ: this.#rz,
|
|
7357
|
+
};
|
|
7066
7358
|
const fieldsHTML = this.#fields
|
|
7067
7359
|
.map(
|
|
7068
7360
|
(axis) =>
|
|
@@ -7115,17 +7407,21 @@ class Fig3DRotate extends HTMLElement {
|
|
|
7115
7407
|
}
|
|
7116
7408
|
|
|
7117
7409
|
#syncFieldInputs() {
|
|
7118
|
-
const axisValues = {
|
|
7410
|
+
const axisValues = {
|
|
7411
|
+
rotateX: this.#rx,
|
|
7412
|
+
rotateY: this.#ry,
|
|
7413
|
+
rotateZ: this.#rz,
|
|
7414
|
+
};
|
|
7119
7415
|
for (const axis of this.#fields) {
|
|
7120
7416
|
const input = this.#fieldInputs[axis];
|
|
7121
|
-
if (input)
|
|
7417
|
+
if (input)
|
|
7418
|
+
input.setAttribute("value", axisValues[axis].toFixed(this.#precision));
|
|
7122
7419
|
}
|
|
7123
7420
|
}
|
|
7124
7421
|
|
|
7125
7422
|
#updateCube() {
|
|
7126
7423
|
if (!this.#cube) return;
|
|
7127
|
-
this.#cube.style.transform =
|
|
7128
|
-
`rotateX(${this.#rx}deg) rotateY(${this.#ry}deg) rotateZ(${this.#rz}deg)`;
|
|
7424
|
+
this.#cube.style.transform = `rotateX(${this.#rx}deg) rotateY(${this.#ry}deg) rotateZ(${this.#rz}deg)`;
|
|
7129
7425
|
}
|
|
7130
7426
|
|
|
7131
7427
|
#emit(type) {
|
|
@@ -7339,11 +7635,7 @@ class FigOriginGrid extends HTMLElement {
|
|
|
7339
7635
|
}
|
|
7340
7636
|
|
|
7341
7637
|
#parseValue(value) {
|
|
7342
|
-
const parts = value
|
|
7343
|
-
.trim()
|
|
7344
|
-
.replace(/,/g, " ")
|
|
7345
|
-
.split(/\s+/)
|
|
7346
|
-
.filter(Boolean);
|
|
7638
|
+
const parts = value.trim().replace(/,/g, " ").split(/\s+/).filter(Boolean);
|
|
7347
7639
|
if (parts.length < 1) return;
|
|
7348
7640
|
|
|
7349
7641
|
if (parts.length === 1) {
|
|
@@ -7439,7 +7731,8 @@ class FigOriginGrid extends HTMLElement {
|
|
|
7439
7731
|
#syncHandlePosition() {
|
|
7440
7732
|
if (!this.#handle) return;
|
|
7441
7733
|
// Constrain draggable visual bounds to the 3x3 dot centers.
|
|
7442
|
-
const toVisual = (value) =>
|
|
7734
|
+
const toVisual = (value) =>
|
|
7735
|
+
16.6667 + (this.#clampPercentage(value) / 100) * 66.6667;
|
|
7443
7736
|
this.#handle.style.left = `${toVisual(this.#x)}%`;
|
|
7444
7737
|
this.#handle.style.top = `${toVisual(this.#y)}%`;
|
|
7445
7738
|
}
|
|
@@ -7553,10 +7846,18 @@ class FigOriginGrid extends HTMLElement {
|
|
|
7553
7846
|
}
|
|
7554
7847
|
|
|
7555
7848
|
#detachHandleDragListeners() {
|
|
7556
|
-
if (
|
|
7849
|
+
if (
|
|
7850
|
+
!this.#grid ||
|
|
7851
|
+
!this.#boundHandlePointerMove ||
|
|
7852
|
+
!this.#boundHandlePointerEnd
|
|
7853
|
+
)
|
|
7854
|
+
return;
|
|
7557
7855
|
this.#grid.removeEventListener("pointermove", this.#boundHandlePointerMove);
|
|
7558
7856
|
this.#grid.removeEventListener("pointerup", this.#boundHandlePointerEnd);
|
|
7559
|
-
this.#grid.removeEventListener(
|
|
7857
|
+
this.#grid.removeEventListener(
|
|
7858
|
+
"pointercancel",
|
|
7859
|
+
this.#boundHandlePointerEnd,
|
|
7860
|
+
);
|
|
7560
7861
|
this.#grid.removeEventListener(
|
|
7561
7862
|
"lostpointercapture",
|
|
7562
7863
|
this.#boundHandlePointerEnd,
|
|
@@ -7577,7 +7878,8 @@ class FigOriginGrid extends HTMLElement {
|
|
|
7577
7878
|
this.#grid.setPointerCapture(e.pointerId);
|
|
7578
7879
|
|
|
7579
7880
|
this.#boundHandlePointerMove = (moveEvent) => {
|
|
7580
|
-
if (!this.#isDragging || moveEvent.pointerId !== this.#activePointerId)
|
|
7881
|
+
if (!this.#isDragging || moveEvent.pointerId !== this.#activePointerId)
|
|
7882
|
+
return;
|
|
7581
7883
|
const dx = moveEvent.clientX - startClientX;
|
|
7582
7884
|
const dy = moveEvent.clientY - startClientY;
|
|
7583
7885
|
const distance = Math.hypot(dx, dy);
|
|
@@ -7595,7 +7897,8 @@ class FigOriginGrid extends HTMLElement {
|
|
|
7595
7897
|
};
|
|
7596
7898
|
|
|
7597
7899
|
this.#boundHandlePointerEnd = (endEvent) => {
|
|
7598
|
-
if (!this.#isDragging || endEvent.pointerId !== this.#activePointerId)
|
|
7900
|
+
if (!this.#isDragging || endEvent.pointerId !== this.#activePointerId)
|
|
7901
|
+
return;
|
|
7599
7902
|
this.#isDragging = false;
|
|
7600
7903
|
this.#activePointerId = null;
|
|
7601
7904
|
this.#grid.classList.remove("is-dragging");
|
|
@@ -7612,7 +7915,10 @@ class FigOriginGrid extends HTMLElement {
|
|
|
7612
7915
|
this.#grid.addEventListener("pointermove", this.#boundHandlePointerMove);
|
|
7613
7916
|
this.#grid.addEventListener("pointerup", this.#boundHandlePointerEnd);
|
|
7614
7917
|
this.#grid.addEventListener("pointercancel", this.#boundHandlePointerEnd);
|
|
7615
|
-
this.#grid.addEventListener(
|
|
7918
|
+
this.#grid.addEventListener(
|
|
7919
|
+
"lostpointercapture",
|
|
7920
|
+
this.#boundHandlePointerEnd,
|
|
7921
|
+
);
|
|
7616
7922
|
}
|
|
7617
7923
|
|
|
7618
7924
|
#setupEvents() {
|
|
@@ -8064,7 +8370,8 @@ class FigInputJoystick extends HTMLElement {
|
|
|
8064
8370
|
const isPercent = token.includes("%");
|
|
8065
8371
|
const numeric = Number.parseFloat(token.replace(/%/g, "").trim());
|
|
8066
8372
|
if (!Number.isFinite(numeric)) return 0.5;
|
|
8067
|
-
const decimal =
|
|
8373
|
+
const decimal =
|
|
8374
|
+
isPercent || Math.abs(numeric) > 1 ? numeric / 100 : numeric;
|
|
8068
8375
|
return Math.max(0, Math.min(1, decimal));
|
|
8069
8376
|
};
|
|
8070
8377
|
const x = parseAxis(parts[0]);
|
|
@@ -10480,7 +10787,9 @@ class FigChooser extends HTMLElement {
|
|
|
10480
10787
|
}
|
|
10481
10788
|
|
|
10482
10789
|
get #overflowMode() {
|
|
10483
|
-
return this.getAttribute("overflow") === "scrollbar"
|
|
10790
|
+
return this.getAttribute("overflow") === "scrollbar"
|
|
10791
|
+
? "scrollbar"
|
|
10792
|
+
: "buttons";
|
|
10484
10793
|
}
|
|
10485
10794
|
|
|
10486
10795
|
get #dragEnabled() {
|
|
@@ -10730,12 +11039,14 @@ class FigChooser extends HTMLElement {
|
|
|
10730
11039
|
|
|
10731
11040
|
if (isHorizontal) {
|
|
10732
11041
|
const atStart = this.scrollLeft <= threshold;
|
|
10733
|
-
const atEnd =
|
|
11042
|
+
const atEnd =
|
|
11043
|
+
this.scrollLeft + this.clientWidth >= this.scrollWidth - threshold;
|
|
10734
11044
|
this.classList.toggle("overflow-start", !atStart);
|
|
10735
11045
|
this.classList.toggle("overflow-end", !atEnd);
|
|
10736
11046
|
} else {
|
|
10737
11047
|
const atStart = this.scrollTop <= threshold;
|
|
10738
|
-
const atEnd =
|
|
11048
|
+
const atEnd =
|
|
11049
|
+
this.scrollTop + this.clientHeight >= this.scrollHeight - threshold;
|
|
10739
11050
|
this.classList.toggle("overflow-start", !atStart);
|
|
10740
11051
|
this.classList.toggle("overflow-end", !atEnd);
|
|
10741
11052
|
}
|
|
@@ -10800,7 +11111,9 @@ class FigChooser extends HTMLElement {
|
|
|
10800
11111
|
this.style.cursor = "";
|
|
10801
11112
|
this.style.userSelect = "";
|
|
10802
11113
|
if (e.pointerId !== undefined) {
|
|
10803
|
-
try {
|
|
11114
|
+
try {
|
|
11115
|
+
this.releasePointerCapture(e.pointerId);
|
|
11116
|
+
} catch {}
|
|
10804
11117
|
}
|
|
10805
11118
|
if (wasDrag) {
|
|
10806
11119
|
e.preventDefault();
|
|
@@ -10853,7 +11166,11 @@ class FigChooser extends HTMLElement {
|
|
|
10853
11166
|
this.removeEventListener("pointerdown", this.#dragState.onPointerDown);
|
|
10854
11167
|
window.removeEventListener("pointermove", this.#dragState.onPointerMove);
|
|
10855
11168
|
window.removeEventListener("pointerup", this.#dragState.onPointerUp);
|
|
10856
|
-
this.removeEventListener(
|
|
11169
|
+
this.removeEventListener(
|
|
11170
|
+
"pointerup",
|
|
11171
|
+
this.#dragState.onPointerUpCapture,
|
|
11172
|
+
true,
|
|
11173
|
+
);
|
|
10857
11174
|
this.removeEventListener("click", this.#dragState.onClick, true);
|
|
10858
11175
|
this.style.cursor = "";
|
|
10859
11176
|
this.style.userSelect = "";
|
|
@@ -10904,8 +11221,7 @@ class FigChooser extends HTMLElement {
|
|
|
10904
11221
|
}
|
|
10905
11222
|
|
|
10906
11223
|
#scrollByPage(direction) {
|
|
10907
|
-
const isHorizontal =
|
|
10908
|
-
this.getAttribute("layout") === "horizontal";
|
|
11224
|
+
const isHorizontal = this.getAttribute("layout") === "horizontal";
|
|
10909
11225
|
const pageSize = isHorizontal ? this.clientWidth : this.clientHeight;
|
|
10910
11226
|
const scrollAmount = pageSize * 0.8 * direction;
|
|
10911
11227
|
|
|
@@ -10925,12 +11241,14 @@ class FigChooser extends HTMLElement {
|
|
|
10925
11241
|
const options = { behavior: "smooth" };
|
|
10926
11242
|
|
|
10927
11243
|
if (overflowY) {
|
|
10928
|
-
const target =
|
|
11244
|
+
const target =
|
|
11245
|
+
el.offsetTop - this.clientHeight / 2 + el.offsetHeight / 2;
|
|
10929
11246
|
options.top = target;
|
|
10930
11247
|
}
|
|
10931
11248
|
|
|
10932
11249
|
if (overflowX) {
|
|
10933
|
-
const target =
|
|
11250
|
+
const target =
|
|
11251
|
+
el.offsetLeft - this.clientWidth / 2 + el.offsetWidth / 2;
|
|
10934
11252
|
options.left = target;
|
|
10935
11253
|
}
|
|
10936
11254
|
|