@rogieking/figui3 6.6.0 → 6.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fig.js +26 -26
- package/fig.js +588 -213
- package/package.json +1 -1
package/fig.js
CHANGED
|
@@ -27,6 +27,14 @@ function createFigIcon(name, options = {}) {
|
|
|
27
27
|
return icon;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/** Run callback on the next frame; skip if the host disconnected first. */
|
|
31
|
+
function figNextFrame(host, callback) {
|
|
32
|
+
requestAnimationFrame(() => {
|
|
33
|
+
if (host && !host.isConnected) return;
|
|
34
|
+
callback();
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
30
38
|
function createFigOverflowButtons({
|
|
31
39
|
owner,
|
|
32
40
|
onStart,
|
|
@@ -202,16 +210,26 @@ class FigButton extends HTMLElement {
|
|
|
202
210
|
#selected;
|
|
203
211
|
#a11yAttributes = ["aria-label", "aria-labelledby", "aria-describedby", "title"];
|
|
204
212
|
#boundHandleControlKeydown = this.#handleControlKeydown.bind(this);
|
|
213
|
+
#boundHandleClick = this.#handleClick.bind(this);
|
|
214
|
+
#boundHandleFocus = () => {
|
|
215
|
+
if (this.button?.matches(":focus-visible")) {
|
|
216
|
+
this.setAttribute("data-focus-visible", "");
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
#boundHandleBlur = () => {
|
|
220
|
+
this.removeAttribute("data-focus-visible");
|
|
221
|
+
};
|
|
205
222
|
constructor() {
|
|
206
223
|
super();
|
|
207
224
|
this.attachShadow({ mode: "open", delegatesFocus: true });
|
|
208
225
|
}
|
|
209
226
|
connectedCallback() {
|
|
210
227
|
this.type = this.getAttribute("type") || "button";
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
228
|
+
if (!this.button) {
|
|
229
|
+
const isControlWrapper = this.type === "select" || this.type === "upload";
|
|
230
|
+
const controlTag = isControlWrapper ? "span" : "button";
|
|
231
|
+
const typeAttr = isControlWrapper ? "" : ` type="${this.type}"`;
|
|
232
|
+
this.shadowRoot.innerHTML = `
|
|
215
233
|
<style>
|
|
216
234
|
button, button:hover, button:active, .fig-button-control {
|
|
217
235
|
padding: 0 var(--spacer-2);
|
|
@@ -249,24 +267,18 @@ class FigButton extends HTMLElement {
|
|
|
249
267
|
</${controlTag}>
|
|
250
268
|
`;
|
|
251
269
|
|
|
270
|
+
this.button = this.shadowRoot.querySelector("button, .fig-button-control");
|
|
271
|
+
this.button.addEventListener("click", this.#boundHandleClick);
|
|
272
|
+
this.button.addEventListener("focus", this.#boundHandleFocus);
|
|
273
|
+
this.button.addEventListener("blur", this.#boundHandleBlur);
|
|
274
|
+
this.addEventListener("keydown", this.#boundHandleControlKeydown);
|
|
275
|
+
}
|
|
276
|
+
|
|
252
277
|
this.#selected =
|
|
253
278
|
this.hasAttribute("selected") &&
|
|
254
279
|
this.getAttribute("selected") !== "false";
|
|
255
280
|
|
|
256
|
-
this.button = this.shadowRoot.querySelector("button, .fig-button-control");
|
|
257
281
|
this.#syncButtonAttributes();
|
|
258
|
-
this.button.addEventListener("click", this.#handleClick.bind(this));
|
|
259
|
-
|
|
260
|
-
// Forward focus-visible state to host element
|
|
261
|
-
this.button.addEventListener("focus", () => {
|
|
262
|
-
if (this.button.matches(":focus-visible")) {
|
|
263
|
-
this.setAttribute("data-focus-visible", "");
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
this.button.addEventListener("blur", () => {
|
|
267
|
-
this.removeAttribute("data-focus-visible");
|
|
268
|
-
});
|
|
269
|
-
this.addEventListener("keydown", this.#boundHandleControlKeydown);
|
|
270
282
|
}
|
|
271
283
|
|
|
272
284
|
get type() {
|
|
@@ -390,8 +402,17 @@ class FigButton extends HTMLElement {
|
|
|
390
402
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
391
403
|
if (oldValue === newValue) return;
|
|
392
404
|
switch (name) {
|
|
405
|
+
case "type": {
|
|
406
|
+
const isWrapper = (type) => type === "select" || type === "upload";
|
|
407
|
+
if (isWrapper(oldValue || "button") !== isWrapper(newValue || "button")) {
|
|
408
|
+
this.button = null;
|
|
409
|
+
this.connectedCallback();
|
|
410
|
+
break;
|
|
411
|
+
}
|
|
412
|
+
this.#syncButtonAttributes();
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
393
415
|
case "disabled":
|
|
394
|
-
case "type":
|
|
395
416
|
this.#syncButtonAttributes();
|
|
396
417
|
break;
|
|
397
418
|
case "selected":
|
|
@@ -520,11 +541,19 @@ class FigDropdown extends HTMLElement {
|
|
|
520
541
|
this.select.setAttribute("aria-label", this.#label);
|
|
521
542
|
this.#syncDisabled();
|
|
522
543
|
|
|
523
|
-
|
|
524
|
-
|
|
544
|
+
if (!this.select.isConnected) {
|
|
545
|
+
this.appendChild(this.select);
|
|
546
|
+
}
|
|
547
|
+
if (!this.optionsSlot.isConnected) {
|
|
548
|
+
this.shadowRoot.appendChild(this.optionsSlot);
|
|
549
|
+
}
|
|
525
550
|
|
|
551
|
+
this.optionsSlot.removeEventListener("slotchange", this.#boundSlotChange);
|
|
526
552
|
this.optionsSlot.addEventListener("slotchange", this.#boundSlotChange);
|
|
527
553
|
|
|
554
|
+
this.select.removeEventListener("input", this.#boundHandleSelectInput);
|
|
555
|
+
this.select.removeEventListener("change", this.#boundHandleSelectChange);
|
|
556
|
+
this.select.removeEventListener("keydown", this.#boundHandleSelectKeydown);
|
|
528
557
|
this.#addEventListeners();
|
|
529
558
|
}
|
|
530
559
|
|
|
@@ -1152,7 +1181,7 @@ class FigTruncate extends HTMLElement {
|
|
|
1152
1181
|
|
|
1153
1182
|
connectedCallback() {
|
|
1154
1183
|
this.#originalText = this.textContent;
|
|
1155
|
-
|
|
1184
|
+
figNextFrame(this, () => {
|
|
1156
1185
|
this.#render();
|
|
1157
1186
|
this.#setupTooltip();
|
|
1158
1187
|
});
|
|
@@ -1284,7 +1313,7 @@ class FigDialog extends HTMLDialogElement {
|
|
|
1284
1313
|
|
|
1285
1314
|
this._ensureHeader();
|
|
1286
1315
|
|
|
1287
|
-
|
|
1316
|
+
figNextFrame(this, () => {
|
|
1288
1317
|
this._addCloseListeners();
|
|
1289
1318
|
this._setupDragListeners();
|
|
1290
1319
|
this._applyPosition();
|
|
@@ -3111,7 +3140,7 @@ class FigTab extends HTMLElement {
|
|
|
3111
3140
|
if (!this.hasAttribute("tabindex")) this.setAttribute("tabindex", "-1");
|
|
3112
3141
|
this.addEventListener("click", this.#boundHandleClick);
|
|
3113
3142
|
|
|
3114
|
-
|
|
3143
|
+
figNextFrame(this, () => {
|
|
3115
3144
|
if (typeof this.getAttribute("content") === "string") {
|
|
3116
3145
|
this.content = document.querySelector(this.getAttribute("content"));
|
|
3117
3146
|
if (this.content) {
|
|
@@ -3211,7 +3240,7 @@ class FigTabs extends HTMLElement {
|
|
|
3211
3240
|
this.#createNavButtons();
|
|
3212
3241
|
this.#startObserver();
|
|
3213
3242
|
this.#startResizeObserver();
|
|
3214
|
-
|
|
3243
|
+
figNextFrame(this, () => {
|
|
3215
3244
|
const value = this.getAttribute("value");
|
|
3216
3245
|
if (value) {
|
|
3217
3246
|
this.#selectByValue(value);
|
|
@@ -3453,26 +3482,45 @@ class FigTabs extends HTMLElement {
|
|
|
3453
3482
|
this.querySelectorAll("fig-tab").forEach((tab) => tab.removeAttribute("selected"));
|
|
3454
3483
|
this.selectedTab = tabs[newIndex];
|
|
3455
3484
|
tabs[newIndex].setAttribute("selected", "true");
|
|
3456
|
-
const val = tabs[newIndex]
|
|
3485
|
+
const val = this.#resolveTabValue(tabs[newIndex]);
|
|
3457
3486
|
if (val) this.setAttribute("value", val);
|
|
3487
|
+
else this.removeAttribute("value");
|
|
3458
3488
|
tabs[newIndex].focus();
|
|
3459
3489
|
this.#syncTabIndexes();
|
|
3460
3490
|
this.#scrollSelectedTabIntoView(tabs[newIndex]);
|
|
3491
|
+
this.#emitSelectionEvents();
|
|
3461
3492
|
}
|
|
3462
3493
|
}
|
|
3463
3494
|
|
|
3464
3495
|
get value() {
|
|
3465
|
-
return this.selectedTab
|
|
3496
|
+
return this.#resolveTabValue(this.selectedTab);
|
|
3466
3497
|
}
|
|
3467
3498
|
|
|
3468
3499
|
set value(val) {
|
|
3469
3500
|
this.setAttribute("value", val);
|
|
3470
3501
|
}
|
|
3471
3502
|
|
|
3503
|
+
#emitSelectionEvents() {
|
|
3504
|
+
const val = this.value;
|
|
3505
|
+
this.dispatchEvent(
|
|
3506
|
+
new CustomEvent("input", { detail: val, bubbles: true }),
|
|
3507
|
+
);
|
|
3508
|
+
this.dispatchEvent(
|
|
3509
|
+
new CustomEvent("change", { detail: val, bubbles: true }),
|
|
3510
|
+
);
|
|
3511
|
+
}
|
|
3512
|
+
|
|
3513
|
+
#resolveTabValue(tab) {
|
|
3514
|
+
if (!tab) return "";
|
|
3515
|
+
const attrValue = tab.getAttribute("value");
|
|
3516
|
+
if (attrValue !== null) return attrValue;
|
|
3517
|
+
return tab.textContent?.trim() || "";
|
|
3518
|
+
}
|
|
3519
|
+
|
|
3472
3520
|
#selectByValue(value) {
|
|
3473
3521
|
const tabs = this.querySelectorAll("fig-tab");
|
|
3474
3522
|
for (const tab of tabs) {
|
|
3475
|
-
if (tab
|
|
3523
|
+
if (this.#resolveTabValue(tab) === value) {
|
|
3476
3524
|
this.selectedTab = tab;
|
|
3477
3525
|
tab.setAttribute("selected", "true");
|
|
3478
3526
|
} else {
|
|
@@ -3500,6 +3548,8 @@ class FigTabs extends HTMLElement {
|
|
|
3500
3548
|
if (this.hasAttribute("disabled")) return;
|
|
3501
3549
|
const target = event.target.closest("fig-tab");
|
|
3502
3550
|
if (!target || !this.contains(target)) return;
|
|
3551
|
+
const previousTab = this.selectedTab;
|
|
3552
|
+
const previousValue = this.value;
|
|
3503
3553
|
const tabs = this.querySelectorAll("fig-tab");
|
|
3504
3554
|
for (const tab of tabs) {
|
|
3505
3555
|
if (tab === target) {
|
|
@@ -3509,10 +3559,14 @@ class FigTabs extends HTMLElement {
|
|
|
3509
3559
|
tab.removeAttribute("selected");
|
|
3510
3560
|
}
|
|
3511
3561
|
}
|
|
3512
|
-
const val = target
|
|
3562
|
+
const val = this.#resolveTabValue(target);
|
|
3513
3563
|
if (val) this.setAttribute("value", val);
|
|
3564
|
+
else this.removeAttribute("value");
|
|
3514
3565
|
this.#syncTabIndexes();
|
|
3515
3566
|
this.#scrollSelectedTabIntoView(target);
|
|
3567
|
+
if (previousTab !== target || previousValue !== this.value) {
|
|
3568
|
+
this.#emitSelectionEvents();
|
|
3569
|
+
}
|
|
3516
3570
|
}
|
|
3517
3571
|
}
|
|
3518
3572
|
customElements.define("fig-tabs", FigTabs);
|
|
@@ -3637,7 +3691,7 @@ class FigSegmentedControl extends HTMLElement {
|
|
|
3637
3691
|
this.#startResizeObserver();
|
|
3638
3692
|
|
|
3639
3693
|
// Defer initial selection so child segments are available.
|
|
3640
|
-
|
|
3694
|
+
figNextFrame(this, () => {
|
|
3641
3695
|
this.#syncSelectionFromAttributes({ enforceFallback: true });
|
|
3642
3696
|
this.#refreshResizeObserverTargets();
|
|
3643
3697
|
this.#queueIndicatorSync({ forceInstant: true });
|
|
@@ -4093,13 +4147,52 @@ class FigOptions extends HTMLElement {
|
|
|
4093
4147
|
return String(fallbackValue);
|
|
4094
4148
|
}
|
|
4095
4149
|
|
|
4150
|
+
#canReuseControl() {
|
|
4151
|
+
if (this.#parsedOptions.length === 0) return false;
|
|
4152
|
+
const segments = this.querySelector(":scope > fig-segmented-control");
|
|
4153
|
+
if (segments) {
|
|
4154
|
+
const opts = segments.querySelectorAll("fig-segment");
|
|
4155
|
+
if (opts.length !== this.#parsedOptions.length) return false;
|
|
4156
|
+
return Array.from(opts).every(
|
|
4157
|
+
(seg, i) => seg.getAttribute("value") === this.#parsedOptions[i],
|
|
4158
|
+
);
|
|
4159
|
+
}
|
|
4160
|
+
const dropdown = this.querySelector(":scope > fig-dropdown");
|
|
4161
|
+
if (dropdown) {
|
|
4162
|
+
const opts = dropdown.querySelectorAll("option");
|
|
4163
|
+
return (
|
|
4164
|
+
opts.length === this.#parsedOptions.length &&
|
|
4165
|
+
Array.from(opts).every(
|
|
4166
|
+
(opt, i) => opt.textContent?.trim() === this.#parsedOptions[i],
|
|
4167
|
+
)
|
|
4168
|
+
);
|
|
4169
|
+
}
|
|
4170
|
+
return false;
|
|
4171
|
+
}
|
|
4172
|
+
|
|
4173
|
+
#reuseControl() {
|
|
4174
|
+
const segments = this.querySelector(":scope > fig-segmented-control");
|
|
4175
|
+
if (segments) {
|
|
4176
|
+
this.#childControl = segments;
|
|
4177
|
+
this.#currentMode = "segments";
|
|
4178
|
+
} else {
|
|
4179
|
+
this.#childControl = this.querySelector(":scope > fig-dropdown");
|
|
4180
|
+
this.#currentMode = "dropdown";
|
|
4181
|
+
}
|
|
4182
|
+
this.#syncValueToChild();
|
|
4183
|
+
}
|
|
4184
|
+
|
|
4096
4185
|
connectedCallback() {
|
|
4097
4186
|
this.#parseOptions();
|
|
4187
|
+
if (this.#canReuseControl()) {
|
|
4188
|
+
this.#reuseControl();
|
|
4189
|
+
this.#startResizeObserver();
|
|
4190
|
+
figNextFrame(this, () => figNextFrame(this, () => this.#checkOverflow()));
|
|
4191
|
+
return;
|
|
4192
|
+
}
|
|
4098
4193
|
this.#renderSegments();
|
|
4099
4194
|
this.#startResizeObserver();
|
|
4100
|
-
|
|
4101
|
-
requestAnimationFrame(() => this.#checkOverflow());
|
|
4102
|
-
});
|
|
4195
|
+
figNextFrame(this, () => figNextFrame(this, () => this.#checkOverflow()));
|
|
4103
4196
|
}
|
|
4104
4197
|
|
|
4105
4198
|
disconnectedCallback() {
|
|
@@ -4451,7 +4544,7 @@ class FigSlider extends HTMLElement {
|
|
|
4451
4544
|
};
|
|
4452
4545
|
}
|
|
4453
4546
|
|
|
4454
|
-
#
|
|
4547
|
+
#readAttributesFromMarkup() {
|
|
4455
4548
|
const rawValue = this.getAttribute("value");
|
|
4456
4549
|
this.type = this.getAttribute("type") || "range";
|
|
4457
4550
|
this.variant = this.getAttribute("variant") || "default";
|
|
@@ -4482,6 +4575,103 @@ class FigSlider extends HTMLElement {
|
|
|
4482
4575
|
(rawValue === null ||
|
|
4483
4576
|
(typeof rawValue === "string" && rawValue.trim() === ""));
|
|
4484
4577
|
this.value = this.#normalizeSliderValue(rawValue);
|
|
4578
|
+
}
|
|
4579
|
+
|
|
4580
|
+
#canReuseRenderedMarkup() {
|
|
4581
|
+
const range = this.querySelector("[type=range]");
|
|
4582
|
+
if (!range) return false;
|
|
4583
|
+
const wantsText = this.getAttribute("text") !== "false";
|
|
4584
|
+
return wantsText === !!this.querySelector("fig-input-number");
|
|
4585
|
+
}
|
|
4586
|
+
|
|
4587
|
+
#updateRenderedMarkup() {
|
|
4588
|
+
this.#readAttributesFromMarkup();
|
|
4589
|
+
if (this.color) {
|
|
4590
|
+
this.style.setProperty("--color", this.color);
|
|
4591
|
+
} else {
|
|
4592
|
+
this.style.removeProperty("--color");
|
|
4593
|
+
}
|
|
4594
|
+
|
|
4595
|
+
this.input = this.querySelector("[type=range]");
|
|
4596
|
+
this.inputContainer = this.querySelector(".fig-slider-input-container");
|
|
4597
|
+
this.input.className = this.type;
|
|
4598
|
+
this.input.min = String(this.min);
|
|
4599
|
+
this.input.max = String(this.max);
|
|
4600
|
+
this.input.step = String(this.step);
|
|
4601
|
+
this.input.value = String(this.value);
|
|
4602
|
+
this.input.disabled = this.disabled;
|
|
4603
|
+
if (this.text) this.input.setAttribute("tabindex", "-1");
|
|
4604
|
+
else this.input.removeAttribute("tabindex");
|
|
4605
|
+
this.input.setAttribute("aria-valuemin", String(this.min));
|
|
4606
|
+
this.input.setAttribute("aria-valuemax", String(this.max));
|
|
4607
|
+
this.input.setAttribute("aria-valuenow", String(this.value));
|
|
4608
|
+
|
|
4609
|
+
this.figInputNumber = this.querySelector("fig-input-number");
|
|
4610
|
+
if (this.figInputNumber) {
|
|
4611
|
+
this.figInputNumber.setAttribute("placeholder", this.placeholder);
|
|
4612
|
+
this.figInputNumber.setAttribute("min", String(this.min));
|
|
4613
|
+
this.figInputNumber.setAttribute("max", String(this.max));
|
|
4614
|
+
this.figInputNumber.setAttribute("transform", String(this.transform));
|
|
4615
|
+
this.figInputNumber.setAttribute("step", String(this.step));
|
|
4616
|
+
this.figInputNumber.setAttribute(
|
|
4617
|
+
"value",
|
|
4618
|
+
this.#showEmptyTextValue ? "" : String(this.value),
|
|
4619
|
+
);
|
|
4620
|
+
if (this.units) this.figInputNumber.setAttribute("units", this.units);
|
|
4621
|
+
else this.figInputNumber.removeAttribute("units");
|
|
4622
|
+
if (this.precision !== null) {
|
|
4623
|
+
this.figInputNumber.setAttribute("precision", String(this.precision));
|
|
4624
|
+
} else {
|
|
4625
|
+
this.figInputNumber.removeAttribute("precision");
|
|
4626
|
+
}
|
|
4627
|
+
this.figInputNumber.disabled = this.disabled;
|
|
4628
|
+
this.figInputNumber.toggleAttribute("disabled", this.disabled);
|
|
4629
|
+
}
|
|
4630
|
+
}
|
|
4631
|
+
|
|
4632
|
+
#bindControlListeners() {
|
|
4633
|
+
this.#syncInputA11yAttributes();
|
|
4634
|
+
this.input.removeEventListener("input", this.#boundHandleInput);
|
|
4635
|
+
this.input.addEventListener("input", this.#boundHandleInput);
|
|
4636
|
+
this.input.removeEventListener("change", this.#boundHandleChange);
|
|
4637
|
+
this.input.addEventListener("change", this.#boundHandleChange);
|
|
4638
|
+
this.input.removeEventListener("keydown", this.#boundHandleKeyDown);
|
|
4639
|
+
this.input.addEventListener("keydown", this.#boundHandleKeyDown);
|
|
4640
|
+
this.input.removeEventListener("pointerdown", this.#boundRangePointerDown);
|
|
4641
|
+
this.input.addEventListener("pointerdown", this.#boundRangePointerDown);
|
|
4642
|
+
this.input.removeEventListener("pointerup", this.#boundRangePointerUp);
|
|
4643
|
+
this.input.addEventListener("pointerup", this.#boundRangePointerUp);
|
|
4644
|
+
|
|
4645
|
+
if (this.default) {
|
|
4646
|
+
this.style.setProperty(
|
|
4647
|
+
"--default",
|
|
4648
|
+
this.#calculateNormal(this.default),
|
|
4649
|
+
);
|
|
4650
|
+
}
|
|
4651
|
+
|
|
4652
|
+
if (this.figInputNumber) {
|
|
4653
|
+
this.#syncTextInputA11yAttributes();
|
|
4654
|
+
this.figInputNumber.removeEventListener(
|
|
4655
|
+
"input",
|
|
4656
|
+
this.#boundHandleTextInput,
|
|
4657
|
+
);
|
|
4658
|
+
this.figInputNumber.addEventListener(
|
|
4659
|
+
"input",
|
|
4660
|
+
this.#boundHandleTextInput,
|
|
4661
|
+
);
|
|
4662
|
+
this.figInputNumber.removeEventListener(
|
|
4663
|
+
"change",
|
|
4664
|
+
this.#boundHandleTextChange,
|
|
4665
|
+
);
|
|
4666
|
+
this.figInputNumber.addEventListener(
|
|
4667
|
+
"change",
|
|
4668
|
+
this.#boundHandleTextChange,
|
|
4669
|
+
);
|
|
4670
|
+
}
|
|
4671
|
+
}
|
|
4672
|
+
|
|
4673
|
+
#regenerateInnerHTML() {
|
|
4674
|
+
this.#readAttributesFromMarkup();
|
|
4485
4675
|
|
|
4486
4676
|
if (this.color) {
|
|
4487
4677
|
this.style.setProperty("--color", this.color);
|
|
@@ -4522,24 +4712,7 @@ class FigSlider extends HTMLElement {
|
|
|
4522
4712
|
|
|
4523
4713
|
this.input = this.querySelector("[type=range]");
|
|
4524
4714
|
this.inputContainer = this.querySelector(".fig-slider-input-container");
|
|
4525
|
-
this.#
|
|
4526
|
-
this.input.removeEventListener("input", this.#boundHandleInput);
|
|
4527
|
-
this.input.addEventListener("input", this.#boundHandleInput);
|
|
4528
|
-
this.input.removeEventListener("change", this.#boundHandleChange);
|
|
4529
|
-
this.input.addEventListener("change", this.#boundHandleChange);
|
|
4530
|
-
this.input.removeEventListener("keydown", this.#boundHandleKeyDown);
|
|
4531
|
-
this.input.addEventListener("keydown", this.#boundHandleKeyDown);
|
|
4532
|
-
this.input.removeEventListener("pointerdown", this.#boundRangePointerDown);
|
|
4533
|
-
this.input.addEventListener("pointerdown", this.#boundRangePointerDown);
|
|
4534
|
-
this.input.removeEventListener("pointerup", this.#boundRangePointerUp);
|
|
4535
|
-
this.input.addEventListener("pointerup", this.#boundRangePointerUp);
|
|
4536
|
-
|
|
4537
|
-
if (this.default) {
|
|
4538
|
-
this.style.setProperty(
|
|
4539
|
-
"--default",
|
|
4540
|
-
this.#calculateNormal(this.default),
|
|
4541
|
-
);
|
|
4542
|
-
}
|
|
4715
|
+
this.#bindControlListeners();
|
|
4543
4716
|
|
|
4544
4717
|
this.datalist = this.querySelector("datalist");
|
|
4545
4718
|
this.figInputNumber = this.querySelector("fig-input-number");
|
|
@@ -4578,30 +4751,16 @@ class FigSlider extends HTMLElement {
|
|
|
4578
4751
|
defaultOption.setAttribute("default", "true");
|
|
4579
4752
|
}
|
|
4580
4753
|
}
|
|
4581
|
-
if (this.figInputNumber) {
|
|
4582
|
-
this.#syncTextInputA11yAttributes();
|
|
4583
|
-
this.figInputNumber.removeEventListener(
|
|
4584
|
-
"input",
|
|
4585
|
-
this.#boundHandleTextInput,
|
|
4586
|
-
);
|
|
4587
|
-
this.figInputNumber.addEventListener(
|
|
4588
|
-
"input",
|
|
4589
|
-
this.#boundHandleTextInput,
|
|
4590
|
-
);
|
|
4591
|
-
this.figInputNumber.removeEventListener(
|
|
4592
|
-
"change",
|
|
4593
|
-
this.#boundHandleTextChange,
|
|
4594
|
-
);
|
|
4595
|
-
this.figInputNumber.addEventListener(
|
|
4596
|
-
"change",
|
|
4597
|
-
this.#boundHandleTextChange,
|
|
4598
|
-
);
|
|
4599
|
-
}
|
|
4600
|
-
|
|
4601
4754
|
this.#syncValue();
|
|
4602
4755
|
}
|
|
4603
4756
|
|
|
4604
4757
|
connectedCallback() {
|
|
4758
|
+
if (this.#canReuseRenderedMarkup()) {
|
|
4759
|
+
this.#updateRenderedMarkup();
|
|
4760
|
+
this.#bindControlListeners();
|
|
4761
|
+
this.#syncValue();
|
|
4762
|
+
return;
|
|
4763
|
+
}
|
|
4605
4764
|
this.#regenerateInnerHTML();
|
|
4606
4765
|
}
|
|
4607
4766
|
|
|
@@ -5039,35 +5198,7 @@ class FigInputText extends HTMLElement {
|
|
|
5039
5198
|
}
|
|
5040
5199
|
}
|
|
5041
5200
|
|
|
5042
|
-
|
|
5043
|
-
type="${this.type}"
|
|
5044
|
-
${this.name ? `name="${this.name}"` : ""}
|
|
5045
|
-
placeholder="${this.placeholder}"
|
|
5046
|
-
value="${
|
|
5047
|
-
this.type === "number" ? this.#transformNumber(this.value) : this.value
|
|
5048
|
-
}" />`;
|
|
5049
|
-
if (this.multiline) {
|
|
5050
|
-
html = `<textarea
|
|
5051
|
-
placeholder="${this.placeholder}">${this.value}</textarea>`;
|
|
5052
|
-
}
|
|
5053
|
-
|
|
5054
|
-
let append = this.querySelector("[slot=append]");
|
|
5055
|
-
let prepend = this.querySelector("[slot=prepend]");
|
|
5056
|
-
|
|
5057
|
-
this.innerHTML = html;
|
|
5058
|
-
|
|
5059
|
-
if (prepend) {
|
|
5060
|
-
prepend.removeEventListener("click", this.#boundFocusControl);
|
|
5061
|
-
prepend.addEventListener("click", this.#boundFocusControl);
|
|
5062
|
-
this.prepend(prepend);
|
|
5063
|
-
}
|
|
5064
|
-
if (append) {
|
|
5065
|
-
append.removeEventListener("click", this.#boundFocusControl);
|
|
5066
|
-
append.addEventListener("click", this.#boundFocusControl);
|
|
5067
|
-
this.append(append);
|
|
5068
|
-
}
|
|
5069
|
-
|
|
5070
|
-
this.input = this.querySelector("input,textarea");
|
|
5201
|
+
this.input = this.#ensureInputControl();
|
|
5071
5202
|
this.input.readOnly = this.readonly;
|
|
5072
5203
|
this.#syncInputA11yAttributes();
|
|
5073
5204
|
this.#syncSearchPrefix();
|
|
@@ -5107,6 +5238,46 @@ class FigInputText extends HTMLElement {
|
|
|
5107
5238
|
focus() {
|
|
5108
5239
|
this.input.focus();
|
|
5109
5240
|
}
|
|
5241
|
+
#ensureInputControl() {
|
|
5242
|
+
const wantsTextarea = this.multiline;
|
|
5243
|
+
const existing = this.querySelector("input,textarea");
|
|
5244
|
+
if (existing) {
|
|
5245
|
+
const matches = wantsTextarea
|
|
5246
|
+
? existing.tagName === "TEXTAREA"
|
|
5247
|
+
: existing.tagName === "INPUT";
|
|
5248
|
+
if (matches) return existing;
|
|
5249
|
+
}
|
|
5250
|
+
|
|
5251
|
+
let html = `<input
|
|
5252
|
+
type="${this.type}"
|
|
5253
|
+
${this.name ? `name="${this.name}"` : ""}
|
|
5254
|
+
placeholder="${this.placeholder}"
|
|
5255
|
+
value="${
|
|
5256
|
+
this.type === "number" ? this.#transformNumber(this.value) : this.value
|
|
5257
|
+
}" />`;
|
|
5258
|
+
if (wantsTextarea) {
|
|
5259
|
+
html = `<textarea
|
|
5260
|
+
placeholder="${this.placeholder}">${this.value}</textarea>`;
|
|
5261
|
+
}
|
|
5262
|
+
|
|
5263
|
+
const append = this.querySelector("[slot=append]");
|
|
5264
|
+
const prepend = this.querySelector("[slot=prepend]");
|
|
5265
|
+
|
|
5266
|
+
this.innerHTML = html;
|
|
5267
|
+
|
|
5268
|
+
if (prepend) {
|
|
5269
|
+
prepend.removeEventListener("click", this.#boundFocusControl);
|
|
5270
|
+
prepend.addEventListener("click", this.#boundFocusControl);
|
|
5271
|
+
this.prepend(prepend);
|
|
5272
|
+
}
|
|
5273
|
+
if (append) {
|
|
5274
|
+
append.removeEventListener("click", this.#boundFocusControl);
|
|
5275
|
+
append.addEventListener("click", this.#boundFocusControl);
|
|
5276
|
+
this.append(append);
|
|
5277
|
+
}
|
|
5278
|
+
|
|
5279
|
+
return this.querySelector("input,textarea");
|
|
5280
|
+
}
|
|
5110
5281
|
#syncInputA11yAttributes() {
|
|
5111
5282
|
if (!this.input) return;
|
|
5112
5283
|
this.#a11yAttributes.forEach((name) => {
|
|
@@ -5128,13 +5299,19 @@ class FigInputText extends HTMLElement {
|
|
|
5128
5299
|
}
|
|
5129
5300
|
const prepend = this.querySelector('[slot="prepend"]');
|
|
5130
5301
|
if (prepend && prepend !== generated) return;
|
|
5131
|
-
if (generated)
|
|
5302
|
+
if (generated) {
|
|
5303
|
+
const icon = generated.querySelector("fig-icon");
|
|
5304
|
+
if (icon && icon.getAttribute("name") !== "search") {
|
|
5305
|
+
icon.setAttribute("name", "search");
|
|
5306
|
+
}
|
|
5307
|
+
return;
|
|
5308
|
+
}
|
|
5132
5309
|
|
|
5133
5310
|
const icon = createFigIcon("search");
|
|
5134
5311
|
icon.setAttribute("slot", "prepend");
|
|
5135
5312
|
icon.setAttribute("data-generated", "search-prefix");
|
|
5136
5313
|
icon.setAttribute("color", "var(--figma-color-icon)");
|
|
5137
|
-
icon.addEventListener("click", this
|
|
5314
|
+
icon.addEventListener("click", this.#boundFocusControl);
|
|
5138
5315
|
this.prepend(icon);
|
|
5139
5316
|
}
|
|
5140
5317
|
#syncSearchClear() {
|
|
@@ -5147,7 +5324,13 @@ class FigInputText extends HTMLElement {
|
|
|
5147
5324
|
}
|
|
5148
5325
|
const append = this.querySelector('[slot="append"]');
|
|
5149
5326
|
if (append && append !== generated) return;
|
|
5150
|
-
if (generated)
|
|
5327
|
+
if (generated) {
|
|
5328
|
+
const icon = generated.querySelector("fig-icon");
|
|
5329
|
+
if (icon && icon.getAttribute("name") !== "close") {
|
|
5330
|
+
icon.setAttribute("name", "close");
|
|
5331
|
+
}
|
|
5332
|
+
return;
|
|
5333
|
+
}
|
|
5151
5334
|
|
|
5152
5335
|
const wrapper = document.createElement("span");
|
|
5153
5336
|
wrapper.setAttribute("slot", "append");
|
|
@@ -5161,13 +5344,12 @@ class FigInputText extends HTMLElement {
|
|
|
5161
5344
|
button.setAttribute("icon", "");
|
|
5162
5345
|
button.setAttribute("aria-label", "Clear search");
|
|
5163
5346
|
|
|
5164
|
-
const icon = createFigIcon("", { size: "small" });
|
|
5347
|
+
const icon = createFigIcon("close", { size: "small" });
|
|
5165
5348
|
icon.setAttribute("color", "var(--figma-color-icon-secondary)");
|
|
5166
5349
|
button.append(icon);
|
|
5167
5350
|
tooltip.append(button);
|
|
5168
5351
|
wrapper.append(tooltip);
|
|
5169
5352
|
this.append(wrapper);
|
|
5170
|
-
icon.style.setProperty("--icon", "var(--icon-16-close)");
|
|
5171
5353
|
|
|
5172
5354
|
button.addEventListener("click", (e) => {
|
|
5173
5355
|
e.preventDefault();
|
|
@@ -5633,30 +5815,7 @@ class FigInputNumber extends HTMLElement {
|
|
|
5633
5815
|
this.hasAttribute("steppers") &&
|
|
5634
5816
|
this.getAttribute("steppers") !== "false";
|
|
5635
5817
|
|
|
5636
|
-
|
|
5637
|
-
type="text"
|
|
5638
|
-
inputmode="decimal"
|
|
5639
|
-
${this.name ? `name="${this.name}"` : ""}
|
|
5640
|
-
placeholder="${this.placeholder}"
|
|
5641
|
-
value="${this.#formatWithUnit(this.value)}" />`;
|
|
5642
|
-
|
|
5643
|
-
let append = this.querySelector("[slot=append]");
|
|
5644
|
-
let prepend = this.querySelector("[slot=prepend]");
|
|
5645
|
-
|
|
5646
|
-
this.innerHTML = html;
|
|
5647
|
-
|
|
5648
|
-
if (prepend) {
|
|
5649
|
-
prepend.removeEventListener("click", this.#boundFocusControl);
|
|
5650
|
-
prepend.addEventListener("click", this.#boundFocusControl);
|
|
5651
|
-
this.prepend(prepend);
|
|
5652
|
-
}
|
|
5653
|
-
if (append) {
|
|
5654
|
-
append.removeEventListener("click", this.#boundFocusControl);
|
|
5655
|
-
append.addEventListener("click", this.#boundFocusControl);
|
|
5656
|
-
this.append(append);
|
|
5657
|
-
}
|
|
5658
|
-
|
|
5659
|
-
this.input = this.querySelector("input");
|
|
5818
|
+
this.input = this.#ensureInputControl();
|
|
5660
5819
|
this.#syncInputA11yAttributes();
|
|
5661
5820
|
|
|
5662
5821
|
if (this.getAttribute("min")) {
|
|
@@ -5710,6 +5869,36 @@ class FigInputNumber extends HTMLElement {
|
|
|
5710
5869
|
this.input.focus();
|
|
5711
5870
|
}
|
|
5712
5871
|
|
|
5872
|
+
#ensureInputControl() {
|
|
5873
|
+
const existing = this.querySelector("input");
|
|
5874
|
+
if (existing) return existing;
|
|
5875
|
+
|
|
5876
|
+
const html = `<input
|
|
5877
|
+
type="text"
|
|
5878
|
+
inputmode="decimal"
|
|
5879
|
+
${this.name ? `name="${this.name}"` : ""}
|
|
5880
|
+
placeholder="${this.placeholder}"
|
|
5881
|
+
value="${this.#formatWithUnit(this.value)}" />`;
|
|
5882
|
+
|
|
5883
|
+
const append = this.querySelector("[slot=append]");
|
|
5884
|
+
const prepend = this.querySelector("[slot=prepend]");
|
|
5885
|
+
|
|
5886
|
+
this.innerHTML = html;
|
|
5887
|
+
|
|
5888
|
+
if (prepend) {
|
|
5889
|
+
prepend.removeEventListener("click", this.#boundFocusControl);
|
|
5890
|
+
prepend.addEventListener("click", this.#boundFocusControl);
|
|
5891
|
+
this.prepend(prepend);
|
|
5892
|
+
}
|
|
5893
|
+
if (append) {
|
|
5894
|
+
append.removeEventListener("click", this.#boundFocusControl);
|
|
5895
|
+
append.addEventListener("click", this.#boundFocusControl);
|
|
5896
|
+
this.append(append);
|
|
5897
|
+
}
|
|
5898
|
+
|
|
5899
|
+
return this.querySelector("input");
|
|
5900
|
+
}
|
|
5901
|
+
|
|
5713
5902
|
#syncInputA11yAttributes() {
|
|
5714
5903
|
if (!this.input) return;
|
|
5715
5904
|
this.#a11yAttributes.forEach((name) => {
|
|
@@ -6293,6 +6482,14 @@ class FigInputColor extends HTMLElement {
|
|
|
6293
6482
|
#suppressNativeColorClick = false;
|
|
6294
6483
|
#pendingFillPickerPointerOpen = false;
|
|
6295
6484
|
#nativeColorClickTimer = null;
|
|
6485
|
+
#boundSwatchPointerDown = this.#handleSwatchPointerDown.bind(this);
|
|
6486
|
+
#boundSwatchClick = this.#handleSwatchClick.bind(this);
|
|
6487
|
+
#boundSwatchKeyDown = this.#handleSwatchKeyDown.bind(this);
|
|
6488
|
+
#boundHandleInput = this.#handleInput.bind(this);
|
|
6489
|
+
#boundTextInput = this.#handleTextInput.bind(this);
|
|
6490
|
+
#boundChange = this.#handleChange.bind(this);
|
|
6491
|
+
#boundAlphaInput = this.#handleAlphaInput.bind(this);
|
|
6492
|
+
#boundFillPickerInput = this.#handleFillPickerInput.bind(this);
|
|
6296
6493
|
constructor() {
|
|
6297
6494
|
super();
|
|
6298
6495
|
}
|
|
@@ -6329,9 +6526,105 @@ class FigInputColor extends HTMLElement {
|
|
|
6329
6526
|
}
|
|
6330
6527
|
|
|
6331
6528
|
connectedCallback() {
|
|
6529
|
+
if (this.#canReuseUI()) {
|
|
6530
|
+
this.#refreshUI();
|
|
6531
|
+
return;
|
|
6532
|
+
}
|
|
6332
6533
|
this.#buildUI();
|
|
6333
6534
|
}
|
|
6334
6535
|
|
|
6536
|
+
disconnectedCallback() {
|
|
6537
|
+
this.#teardownControlListeners();
|
|
6538
|
+
}
|
|
6539
|
+
|
|
6540
|
+
#canReuseUI() {
|
|
6541
|
+
const showText = this.getAttribute("text") !== "false";
|
|
6542
|
+
return showText
|
|
6543
|
+
? !!this.querySelector(":scope > .input-combo")
|
|
6544
|
+
: !!this.querySelector(":scope > fig-chit");
|
|
6545
|
+
}
|
|
6546
|
+
|
|
6547
|
+
#refreshUI() {
|
|
6548
|
+
this.#setValues(this.getAttribute("value"));
|
|
6549
|
+
this.#swatch = this.querySelector("fig-chit");
|
|
6550
|
+
this.#fillPicker = this.querySelector("fig-fill-picker");
|
|
6551
|
+
this.#textInput = this.querySelector("fig-input-text:not([type=number])");
|
|
6552
|
+
this.#alphaInput = this.querySelector("fig-input-number");
|
|
6553
|
+
if (this.#textInput) {
|
|
6554
|
+
this.#textInput.setAttribute(
|
|
6555
|
+
"value",
|
|
6556
|
+
this.hexOpaque.slice(1).toUpperCase(),
|
|
6557
|
+
);
|
|
6558
|
+
}
|
|
6559
|
+
if (this.#alphaInput) {
|
|
6560
|
+
this.#alphaInput.setAttribute("value", String(this.#alphaPercent));
|
|
6561
|
+
}
|
|
6562
|
+
if (this.#swatch) {
|
|
6563
|
+
this.#swatch.setAttribute("background", this.hexOpaque);
|
|
6564
|
+
this.#swatch.setAttribute("alpha", String(this.rgba.a));
|
|
6565
|
+
}
|
|
6566
|
+
this.#syncA11yAttributes();
|
|
6567
|
+
this.#bindControlListeners();
|
|
6568
|
+
}
|
|
6569
|
+
|
|
6570
|
+
#teardownControlListeners() {
|
|
6571
|
+
if (this.#swatch) {
|
|
6572
|
+
this.#swatch.removeEventListener(
|
|
6573
|
+
"pointerdown",
|
|
6574
|
+
this.#boundSwatchPointerDown,
|
|
6575
|
+
{ capture: true },
|
|
6576
|
+
);
|
|
6577
|
+
this.#swatch.removeEventListener("click", this.#boundSwatchClick, {
|
|
6578
|
+
capture: true,
|
|
6579
|
+
});
|
|
6580
|
+
const swatchInput = this.#swatch.querySelector('input[type="color"]');
|
|
6581
|
+
swatchInput?.removeEventListener("keydown", this.#boundSwatchKeyDown);
|
|
6582
|
+
this.#swatch.removeEventListener("input", this.#boundHandleInput);
|
|
6583
|
+
}
|
|
6584
|
+
this.#textInput?.removeEventListener("input", this.#boundTextInput);
|
|
6585
|
+
this.#textInput?.removeEventListener("change", this.#boundChange);
|
|
6586
|
+
this.#alphaInput?.removeEventListener("input", this.#boundAlphaInput);
|
|
6587
|
+
this.#alphaInput?.removeEventListener("change", this.#boundChange);
|
|
6588
|
+
this.#fillPicker?.removeEventListener("input", this.#boundFillPickerInput);
|
|
6589
|
+
this.#fillPicker?.removeEventListener("change", this.#boundChange);
|
|
6590
|
+
}
|
|
6591
|
+
|
|
6592
|
+
#bindControlListeners() {
|
|
6593
|
+
if (this.#swatch) {
|
|
6594
|
+
this.#swatch.disabled = this.hasAttribute("disabled");
|
|
6595
|
+
const swatchInput = this.#swatch.querySelector('input[type="color"]');
|
|
6596
|
+
if (this.#textInput || this.hasAttribute("swatch-disabled")) {
|
|
6597
|
+
swatchInput?.setAttribute("tabindex", "-1");
|
|
6598
|
+
}
|
|
6599
|
+
if (this.hasAttribute("swatch-disabled")) {
|
|
6600
|
+
swatchInput?.setAttribute("disabled", "");
|
|
6601
|
+
if (swatchInput) swatchInput.style.pointerEvents = "none";
|
|
6602
|
+
}
|
|
6603
|
+
this.#swatch.addEventListener(
|
|
6604
|
+
"pointerdown",
|
|
6605
|
+
this.#boundSwatchPointerDown,
|
|
6606
|
+
{ capture: true },
|
|
6607
|
+
);
|
|
6608
|
+
this.#swatch.addEventListener("click", this.#boundSwatchClick, {
|
|
6609
|
+
capture: true,
|
|
6610
|
+
});
|
|
6611
|
+
swatchInput?.addEventListener("keydown", this.#boundSwatchKeyDown);
|
|
6612
|
+
this.#swatch.addEventListener("input", this.#boundHandleInput);
|
|
6613
|
+
}
|
|
6614
|
+
if (this.#textInput) {
|
|
6615
|
+
this.#textInput.addEventListener("input", this.#boundTextInput);
|
|
6616
|
+
this.#textInput.addEventListener("change", this.#boundChange);
|
|
6617
|
+
}
|
|
6618
|
+
if (this.#alphaInput) {
|
|
6619
|
+
this.#alphaInput.addEventListener("input", this.#boundAlphaInput);
|
|
6620
|
+
this.#alphaInput.addEventListener("change", this.#boundChange);
|
|
6621
|
+
}
|
|
6622
|
+
if (this.#fillPicker) {
|
|
6623
|
+
this.#fillPicker.addEventListener("input", this.#boundFillPickerInput);
|
|
6624
|
+
this.#fillPicker.addEventListener("change", this.#boundChange);
|
|
6625
|
+
}
|
|
6626
|
+
}
|
|
6627
|
+
|
|
6335
6628
|
#buildUI() {
|
|
6336
6629
|
this.#setValues(this.getAttribute("value"));
|
|
6337
6630
|
|
|
@@ -6377,54 +6670,15 @@ class FigInputColor extends HTMLElement {
|
|
|
6377
6670
|
this.#alphaInput = this.querySelector("fig-input-number");
|
|
6378
6671
|
this.#syncA11yAttributes();
|
|
6379
6672
|
|
|
6380
|
-
// Setup swatch (native picker)
|
|
6381
|
-
if (this.#swatch) {
|
|
6382
|
-
this.#swatch.disabled = this.hasAttribute("disabled");
|
|
6383
|
-
const swatchInput = this.#swatch.querySelector('input[type="color"]');
|
|
6384
|
-
if (this.#textInput || this.hasAttribute("swatch-disabled")) {
|
|
6385
|
-
swatchInput?.setAttribute("tabindex", "-1");
|
|
6386
|
-
}
|
|
6387
|
-
if (this.hasAttribute("swatch-disabled")) {
|
|
6388
|
-
swatchInput?.setAttribute("disabled", "");
|
|
6389
|
-
if (swatchInput) swatchInput.style.pointerEvents = "none";
|
|
6390
|
-
}
|
|
6391
|
-
this.#swatch.addEventListener("pointerdown", this.#handleSwatchPointerDown.bind(this), {
|
|
6392
|
-
capture: true,
|
|
6393
|
-
});
|
|
6394
|
-
this.#swatch.addEventListener("click", this.#handleSwatchClick.bind(this), {
|
|
6395
|
-
capture: true,
|
|
6396
|
-
});
|
|
6397
|
-
swatchInput?.addEventListener("keydown", this.#handleSwatchKeyDown.bind(this));
|
|
6398
|
-
this.#swatch.addEventListener("input", this.#handleInput.bind(this));
|
|
6399
|
-
}
|
|
6400
|
-
|
|
6401
6673
|
if (this.#textInput) {
|
|
6402
6674
|
const hex = this.rgbAlphaToHex(this.rgba, 1);
|
|
6403
|
-
// Display without # prefix
|
|
6404
6675
|
this.#textInput.value = hex.slice(1).toUpperCase();
|
|
6405
6676
|
if (this.#swatch) {
|
|
6406
6677
|
this.#swatch.background = hex;
|
|
6407
6678
|
}
|
|
6408
|
-
this.#textInput.addEventListener(
|
|
6409
|
-
"input",
|
|
6410
|
-
this.#handleTextInput.bind(this),
|
|
6411
|
-
);
|
|
6412
|
-
this.#textInput.addEventListener(
|
|
6413
|
-
"change",
|
|
6414
|
-
this.#handleChange.bind(this),
|
|
6415
|
-
);
|
|
6416
6679
|
}
|
|
6417
6680
|
|
|
6418
|
-
|
|
6419
|
-
this.#alphaInput.addEventListener(
|
|
6420
|
-
"input",
|
|
6421
|
-
this.#handleAlphaInput.bind(this),
|
|
6422
|
-
);
|
|
6423
|
-
this.#alphaInput.addEventListener(
|
|
6424
|
-
"change",
|
|
6425
|
-
this.#handleChange.bind(this),
|
|
6426
|
-
);
|
|
6427
|
-
}
|
|
6681
|
+
this.#bindControlListeners();
|
|
6428
6682
|
}
|
|
6429
6683
|
|
|
6430
6684
|
#syncFillPicker() {
|
|
@@ -7976,6 +8230,7 @@ class FigInputPalette extends HTMLElement {
|
|
|
7976
8230
|
if (this.#renderRAF) cancelAnimationFrame(this.#renderRAF);
|
|
7977
8231
|
this.#renderRAF = requestAnimationFrame(() => {
|
|
7978
8232
|
this.#renderRAF = null;
|
|
8233
|
+
if (!this.isConnected) return;
|
|
7979
8234
|
this.#parseValue();
|
|
7980
8235
|
this.#render();
|
|
7981
8236
|
});
|
|
@@ -9511,6 +9766,24 @@ class FigComboInput extends HTMLElement {
|
|
|
9511
9766
|
this.setAttribute("value", val ?? "");
|
|
9512
9767
|
}
|
|
9513
9768
|
|
|
9769
|
+
#canReuseMarkup() {
|
|
9770
|
+
return !!this.querySelector(":scope > .input-combo");
|
|
9771
|
+
}
|
|
9772
|
+
|
|
9773
|
+
#refreshMarkup() {
|
|
9774
|
+
this.#input = this.querySelector("fig-input-text");
|
|
9775
|
+
this.#button = this.querySelector("fig-button");
|
|
9776
|
+
this.#dropdown = this.querySelector("fig-dropdown");
|
|
9777
|
+
if (this.#input) {
|
|
9778
|
+
this.#input.setAttribute("value", this.value);
|
|
9779
|
+
this.#input.setAttribute(
|
|
9780
|
+
"placeholder",
|
|
9781
|
+
this.getAttribute("placeholder") || "",
|
|
9782
|
+
);
|
|
9783
|
+
}
|
|
9784
|
+
this.#syncA11yAttributes();
|
|
9785
|
+
}
|
|
9786
|
+
|
|
9514
9787
|
connectedCallback() {
|
|
9515
9788
|
this.#customDropdown =
|
|
9516
9789
|
Array.from(this.children).find(
|
|
@@ -9521,8 +9794,13 @@ class FigComboInput extends HTMLElement {
|
|
|
9521
9794
|
this.#customDropdown.remove();
|
|
9522
9795
|
}
|
|
9523
9796
|
|
|
9524
|
-
this.#
|
|
9525
|
-
|
|
9797
|
+
if (this.#canReuseMarkup()) {
|
|
9798
|
+
this.#refreshMarkup();
|
|
9799
|
+
this.#setupListeners();
|
|
9800
|
+
} else {
|
|
9801
|
+
this.#render();
|
|
9802
|
+
this.#setupListeners();
|
|
9803
|
+
}
|
|
9526
9804
|
|
|
9527
9805
|
if (this.hasAttribute("disabled")) {
|
|
9528
9806
|
this.#applyDisabled(true);
|
|
@@ -9576,6 +9854,7 @@ class FigComboInput extends HTMLElement {
|
|
|
9576
9854
|
}
|
|
9577
9855
|
|
|
9578
9856
|
#setupListeners() {
|
|
9857
|
+
this.#teardownListeners();
|
|
9579
9858
|
this.#dropdown?.addEventListener("input", this.#boundHandleDropdownInput);
|
|
9580
9859
|
this.#input?.addEventListener("input", this.#boundHandleTextInput);
|
|
9581
9860
|
this.#input?.addEventListener("change", this.#boundHandleTextChange);
|
|
@@ -12226,8 +12505,11 @@ class Fig3DRotate extends HTMLElement {
|
|
|
12226
12505
|
#container = null;
|
|
12227
12506
|
#boundKeyDown = null;
|
|
12228
12507
|
#boundKeyUp = null;
|
|
12508
|
+
#boundContainerPointerDown = (e) => this.#startDrag(e);
|
|
12509
|
+
#eventAbort = null;
|
|
12229
12510
|
#fields = [];
|
|
12230
12511
|
#fieldInputs = {};
|
|
12512
|
+
#fieldInputHandlers = {};
|
|
12231
12513
|
|
|
12232
12514
|
static get observedAttributes() {
|
|
12233
12515
|
return [
|
|
@@ -12256,16 +12538,36 @@ class Fig3DRotate extends HTMLElement {
|
|
|
12256
12538
|
this.#parseFields(this.getAttribute("fields"));
|
|
12257
12539
|
const val = this.getAttribute("value");
|
|
12258
12540
|
if (val) this.#parseValue(val);
|
|
12259
|
-
this
|
|
12541
|
+
if (this.querySelector(".fig-3d-rotate-container")) {
|
|
12542
|
+
this.#reuseRenderedMarkup();
|
|
12543
|
+
} else {
|
|
12544
|
+
this.#render();
|
|
12545
|
+
}
|
|
12260
12546
|
this.#syncSelected(this.getAttribute("selected"));
|
|
12261
12547
|
this.#syncDragState();
|
|
12262
12548
|
}
|
|
12263
12549
|
|
|
12264
12550
|
disconnectedCallback() {
|
|
12265
12551
|
this.#isDragging = false;
|
|
12552
|
+
this.#teardownEvents();
|
|
12553
|
+
}
|
|
12554
|
+
|
|
12555
|
+
#reuseRenderedMarkup() {
|
|
12556
|
+
this.#container = this.querySelector(".fig-3d-rotate-container");
|
|
12557
|
+
this.#cube = this.querySelector(".fig-3d-rotate-cube");
|
|
12558
|
+
this.#wireFieldInputs();
|
|
12559
|
+
this.#updateCube();
|
|
12560
|
+
this.#setupEvents();
|
|
12561
|
+
}
|
|
12562
|
+
|
|
12563
|
+
#teardownEvents() {
|
|
12564
|
+
this.#eventAbort?.abort();
|
|
12565
|
+
this.#eventAbort = null;
|
|
12266
12566
|
if (this.#boundKeyDown) {
|
|
12267
12567
|
window.removeEventListener("keydown", this.#boundKeyDown);
|
|
12268
12568
|
window.removeEventListener("keyup", this.#boundKeyUp);
|
|
12569
|
+
this.#boundKeyDown = null;
|
|
12570
|
+
this.#boundKeyUp = null;
|
|
12269
12571
|
}
|
|
12270
12572
|
}
|
|
12271
12573
|
|
|
@@ -12419,12 +12721,19 @@ class Fig3DRotate extends HTMLElement {
|
|
|
12419
12721
|
</div>${fieldsHTML}`;
|
|
12420
12722
|
this.#container = this.querySelector(".fig-3d-rotate-container");
|
|
12421
12723
|
this.#cube = this.querySelector(".fig-3d-rotate-cube");
|
|
12724
|
+
this.#wireFieldInputs();
|
|
12725
|
+
this.#updateCube();
|
|
12726
|
+
this.#setupEvents();
|
|
12727
|
+
}
|
|
12728
|
+
|
|
12729
|
+
#wireFieldInputs() {
|
|
12422
12730
|
this.#fieldInputs = {};
|
|
12423
12731
|
for (const axis of this.#fields) {
|
|
12424
12732
|
const input = this.querySelector(`fig-input-number[name="${axis}"]`);
|
|
12425
|
-
if (input)
|
|
12426
|
-
|
|
12427
|
-
|
|
12733
|
+
if (!input) continue;
|
|
12734
|
+
this.#fieldInputs[axis] = input;
|
|
12735
|
+
if (!this.#fieldInputHandlers[axis]) {
|
|
12736
|
+
this.#fieldInputHandlers[axis] = (e) => {
|
|
12428
12737
|
e.stopPropagation();
|
|
12429
12738
|
const val = parseFloat(e.target.value);
|
|
12430
12739
|
if (isNaN(val)) return;
|
|
@@ -12434,12 +12743,13 @@ class Fig3DRotate extends HTMLElement {
|
|
|
12434
12743
|
this.#updateCube();
|
|
12435
12744
|
this.#emit(e.type);
|
|
12436
12745
|
};
|
|
12437
|
-
input.addEventListener("input", handleFieldValue);
|
|
12438
|
-
input.addEventListener("change", handleFieldValue);
|
|
12439
12746
|
}
|
|
12747
|
+
const handler = this.#fieldInputHandlers[axis];
|
|
12748
|
+
input.removeEventListener("input", handler);
|
|
12749
|
+
input.removeEventListener("change", handler);
|
|
12750
|
+
input.addEventListener("input", handler);
|
|
12751
|
+
input.addEventListener("change", handler);
|
|
12440
12752
|
}
|
|
12441
|
-
this.#updateCube();
|
|
12442
|
-
this.#setupEvents();
|
|
12443
12753
|
}
|
|
12444
12754
|
|
|
12445
12755
|
#syncFieldInputs() {
|
|
@@ -12480,7 +12790,12 @@ class Fig3DRotate extends HTMLElement {
|
|
|
12480
12790
|
}
|
|
12481
12791
|
|
|
12482
12792
|
#setupEvents() {
|
|
12483
|
-
this.#
|
|
12793
|
+
this.#teardownEvents();
|
|
12794
|
+
if (!this.#container) return;
|
|
12795
|
+
this.#eventAbort = new AbortController();
|
|
12796
|
+
this.#container.addEventListener("pointerdown", this.#boundContainerPointerDown, {
|
|
12797
|
+
signal: this.#eventAbort.signal,
|
|
12798
|
+
});
|
|
12484
12799
|
this.#boundKeyDown = (e) => {
|
|
12485
12800
|
if (e.key === "Shift") this.#isShiftHeld = true;
|
|
12486
12801
|
};
|
|
@@ -12572,12 +12887,28 @@ class FigOriginGrid extends HTMLElement {
|
|
|
12572
12887
|
return ["value", "precision", "aspect-ratio", "drag", "fields"];
|
|
12573
12888
|
}
|
|
12574
12889
|
|
|
12890
|
+
#reuseRenderedMarkup() {
|
|
12891
|
+
this.#grid = this.querySelector(".origin-grid");
|
|
12892
|
+
this.#cells = Array.from(this.querySelectorAll(".origin-grid-cell"));
|
|
12893
|
+
this.#handle = this.querySelector("fig-handle");
|
|
12894
|
+
this.#xInput = this.querySelector('fig-input-number[name="x"]');
|
|
12895
|
+
this.#yInput = this.querySelector('fig-input-number[name="y"]');
|
|
12896
|
+
this.#syncHandlePosition();
|
|
12897
|
+
this.#syncOverflowState();
|
|
12898
|
+
this.#syncValueInputs();
|
|
12899
|
+
this.#setupEvents();
|
|
12900
|
+
}
|
|
12901
|
+
|
|
12575
12902
|
connectedCallback() {
|
|
12576
12903
|
this.#precision = parseInt(this.getAttribute("precision") || "0");
|
|
12577
12904
|
figSyncCssVar(this, "--aspect-ratio", this.getAttribute("aspect-ratio"));
|
|
12578
12905
|
this.#applyIncomingValue(this.getAttribute("value"));
|
|
12579
12906
|
|
|
12580
|
-
this
|
|
12907
|
+
if (this.querySelector(".fig-origin-grid-surface")) {
|
|
12908
|
+
this.#reuseRenderedMarkup();
|
|
12909
|
+
} else {
|
|
12910
|
+
this.#render();
|
|
12911
|
+
}
|
|
12581
12912
|
this.#syncDragState();
|
|
12582
12913
|
this.#syncValueAttribute();
|
|
12583
12914
|
}
|
|
@@ -12585,6 +12916,7 @@ class FigOriginGrid extends HTMLElement {
|
|
|
12585
12916
|
disconnectedCallback() {
|
|
12586
12917
|
this.#isDragging = false;
|
|
12587
12918
|
this.#detachHandleDragListeners();
|
|
12919
|
+
this.#teardownEvents();
|
|
12588
12920
|
}
|
|
12589
12921
|
|
|
12590
12922
|
get value() {
|
|
@@ -12989,38 +13321,65 @@ class FigOriginGrid extends HTMLElement {
|
|
|
12989
13321
|
);
|
|
12990
13322
|
}
|
|
12991
13323
|
|
|
13324
|
+
#eventAbort = null;
|
|
13325
|
+
|
|
13326
|
+
#teardownEvents() {
|
|
13327
|
+
this.#eventAbort?.abort();
|
|
13328
|
+
this.#eventAbort = null;
|
|
13329
|
+
}
|
|
13330
|
+
|
|
12992
13331
|
#setupEvents() {
|
|
13332
|
+
this.#teardownEvents();
|
|
12993
13333
|
if (!this.#grid || !this.#handle) return;
|
|
12994
13334
|
|
|
12995
|
-
this.#
|
|
12996
|
-
|
|
12997
|
-
this.#setHoveredCell(hovered);
|
|
13335
|
+
this.#eventAbort = new AbortController();
|
|
13336
|
+
const { signal } = this.#eventAbort;
|
|
12998
13337
|
|
|
12999
|
-
|
|
13000
|
-
|
|
13001
|
-
|
|
13002
|
-
|
|
13338
|
+
this.#grid.addEventListener(
|
|
13339
|
+
"pointerdown",
|
|
13340
|
+
(e) => {
|
|
13341
|
+
const hovered = this.#gridCellFromClient(e.clientX, e.clientY);
|
|
13342
|
+
this.#setHoveredCell(hovered);
|
|
13003
13343
|
|
|
13004
|
-
|
|
13005
|
-
|
|
13006
|
-
|
|
13007
|
-
|
|
13344
|
+
if (this.#dragEnabled) {
|
|
13345
|
+
this.#startGridDrag(e);
|
|
13346
|
+
return;
|
|
13347
|
+
}
|
|
13008
13348
|
|
|
13009
|
-
|
|
13010
|
-
|
|
13011
|
-
|
|
13012
|
-
|
|
13013
|
-
|
|
13349
|
+
const center = this.#cellCenterFromClient(e.clientX, e.clientY);
|
|
13350
|
+
this.#setFromPercent(center.x, center.y, "input");
|
|
13351
|
+
this.#emit("change");
|
|
13352
|
+
},
|
|
13353
|
+
{ signal },
|
|
13354
|
+
);
|
|
13014
13355
|
|
|
13015
|
-
this.#grid.addEventListener(
|
|
13016
|
-
|
|
13017
|
-
|
|
13356
|
+
this.#grid.addEventListener(
|
|
13357
|
+
"pointermove",
|
|
13358
|
+
(e) => {
|
|
13359
|
+
if (this.#isDragging) return;
|
|
13360
|
+
const hovered = this.#gridCellFromClient(e.clientX, e.clientY);
|
|
13361
|
+
this.#setHoveredCell(hovered);
|
|
13362
|
+
},
|
|
13363
|
+
{ signal },
|
|
13364
|
+
);
|
|
13018
13365
|
|
|
13019
|
-
this.#
|
|
13020
|
-
|
|
13021
|
-
|
|
13022
|
-
|
|
13023
|
-
|
|
13366
|
+
this.#grid.addEventListener(
|
|
13367
|
+
"pointerleave",
|
|
13368
|
+
() => {
|
|
13369
|
+
this.#clearHoveredCells();
|
|
13370
|
+
},
|
|
13371
|
+
{ signal },
|
|
13372
|
+
);
|
|
13373
|
+
|
|
13374
|
+
this.#handle.addEventListener(
|
|
13375
|
+
"keydown",
|
|
13376
|
+
(e) => {
|
|
13377
|
+
if (!this.#moveHandleByKeyboard(e)) return;
|
|
13378
|
+
e.preventDefault();
|
|
13379
|
+
e.stopPropagation();
|
|
13380
|
+
},
|
|
13381
|
+
{ signal },
|
|
13382
|
+
);
|
|
13024
13383
|
|
|
13025
13384
|
const bindValueInput = (inputEl, axis) => {
|
|
13026
13385
|
if (!inputEl) return;
|
|
@@ -13033,11 +13392,15 @@ class FigOriginGrid extends HTMLElement {
|
|
|
13033
13392
|
this.#setFromPercent(this.#x, next, "input");
|
|
13034
13393
|
}
|
|
13035
13394
|
};
|
|
13036
|
-
inputEl.addEventListener("input", handle);
|
|
13037
|
-
inputEl.addEventListener("change", handle);
|
|
13038
|
-
inputEl.addEventListener(
|
|
13039
|
-
|
|
13040
|
-
|
|
13395
|
+
inputEl.addEventListener("input", handle, { signal });
|
|
13396
|
+
inputEl.addEventListener("change", handle, { signal });
|
|
13397
|
+
inputEl.addEventListener(
|
|
13398
|
+
"focusout",
|
|
13399
|
+
() => {
|
|
13400
|
+
this.#emit("change");
|
|
13401
|
+
},
|
|
13402
|
+
{ signal },
|
|
13403
|
+
);
|
|
13041
13404
|
};
|
|
13042
13405
|
|
|
13043
13406
|
bindValueInput(this.#xInput, "x");
|
|
@@ -13091,9 +13454,16 @@ class FigInputJoystick extends HTMLElement {
|
|
|
13091
13454
|
this.#boundYFocusOut = () => this.#handleFieldFocusOut();
|
|
13092
13455
|
}
|
|
13093
13456
|
|
|
13457
|
+
#reuseRenderedMarkup() {
|
|
13458
|
+
this.#setupListeners();
|
|
13459
|
+
this.#syncHandlePosition();
|
|
13460
|
+
this.#syncValueAttribute();
|
|
13461
|
+
this.#syncResetButton();
|
|
13462
|
+
this.#initialized = true;
|
|
13463
|
+
}
|
|
13464
|
+
|
|
13094
13465
|
connectedCallback() {
|
|
13095
|
-
|
|
13096
|
-
requestAnimationFrame(() => {
|
|
13466
|
+
figNextFrame(this, () => {
|
|
13097
13467
|
this.precision = this.getAttribute("precision") || 3;
|
|
13098
13468
|
this.precision = parseInt(this.precision);
|
|
13099
13469
|
this.transform = this.getAttribute("transform") || 1;
|
|
@@ -13104,6 +13474,11 @@ class FigInputJoystick extends HTMLElement {
|
|
|
13104
13474
|
this.setAttribute("value", "50% 50%");
|
|
13105
13475
|
}
|
|
13106
13476
|
|
|
13477
|
+
if (this.querySelector(".fig-input-joystick-plane")) {
|
|
13478
|
+
this.#reuseRenderedMarkup();
|
|
13479
|
+
return;
|
|
13480
|
+
}
|
|
13481
|
+
|
|
13107
13482
|
this.#render();
|
|
13108
13483
|
this.#setupListeners();
|
|
13109
13484
|
this.#syncHandlePosition();
|
|
@@ -14392,7 +14767,7 @@ class FigChooser extends HTMLElement {
|
|
|
14392
14767
|
this.#startObserver();
|
|
14393
14768
|
this.#startResizeObserver();
|
|
14394
14769
|
|
|
14395
|
-
|
|
14770
|
+
figNextFrame(this, () => {
|
|
14396
14771
|
this.#syncSelection();
|
|
14397
14772
|
this.#syncOverflow();
|
|
14398
14773
|
this.#scheduleInitialScrollSettle();
|