@rogieking/figui3 6.15.0 → 6.16.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/README.md +17 -0
- package/dist/fig-editor.css +1 -1
- package/dist/fig-lab.css +1 -1
- package/dist/fig-lab.js +5 -5
- package/fig-lab.css +118 -0
- package/fig-lab.js +264 -0
- package/package.json +1 -1
package/fig-lab.js
CHANGED
|
@@ -22,6 +22,7 @@ class PropskitSwitch extends HTMLElement {
|
|
|
22
22
|
#managedSwitchAttrs = new Set();
|
|
23
23
|
#boundHandleInput = null;
|
|
24
24
|
#boundHandleChange = null;
|
|
25
|
+
#boundHandleClick = this.#handleClick.bind(this);
|
|
25
26
|
|
|
26
27
|
static get observedAttributes() {
|
|
27
28
|
return ["label", "direction"];
|
|
@@ -32,6 +33,8 @@ class PropskitSwitch extends HTMLElement {
|
|
|
32
33
|
this.#syncField();
|
|
33
34
|
this.#syncSwitchAttributes();
|
|
34
35
|
this.#bindSwitchEvents();
|
|
36
|
+
this.removeEventListener("click", this.#boundHandleClick);
|
|
37
|
+
this.addEventListener("click", this.#boundHandleClick);
|
|
35
38
|
|
|
36
39
|
if (!this.#observer) {
|
|
37
40
|
this.#observer = new MutationObserver((mutations) => {
|
|
@@ -61,6 +64,7 @@ class PropskitSwitch extends HTMLElement {
|
|
|
61
64
|
disconnectedCallback() {
|
|
62
65
|
this.#observer?.disconnect();
|
|
63
66
|
this.#unbindSwitchEvents();
|
|
67
|
+
this.removeEventListener("click", this.#boundHandleClick);
|
|
64
68
|
}
|
|
65
69
|
|
|
66
70
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -193,6 +197,14 @@ class PropskitSwitch extends HTMLElement {
|
|
|
193
197
|
);
|
|
194
198
|
}
|
|
195
199
|
|
|
200
|
+
#handleClick(event) {
|
|
201
|
+
if (event.target instanceof Element && event.target.closest("fig-segmented-control")) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const value = this.#switch?.value === "on" ? "off" : "on";
|
|
205
|
+
this.#switch?.querySelector(`fig-segment[value="${value}"]`)?.click();
|
|
206
|
+
}
|
|
207
|
+
|
|
196
208
|
get checked() {
|
|
197
209
|
return this.#switch
|
|
198
210
|
? this.#switch.value === "on"
|
|
@@ -220,6 +232,216 @@ class PropskitSwitch extends HTMLElement {
|
|
|
220
232
|
}
|
|
221
233
|
customElements.define("propskit-switch", PropskitSwitch);
|
|
222
234
|
|
|
235
|
+
/* Field + Color wrapper */
|
|
236
|
+
class PropskitColor extends HTMLElement {
|
|
237
|
+
#field = null;
|
|
238
|
+
#label = null;
|
|
239
|
+
#input = null;
|
|
240
|
+
#hasCustomLabel = false;
|
|
241
|
+
#observer = null;
|
|
242
|
+
#managedInputAttrs = new Set();
|
|
243
|
+
#boundHandleInput = null;
|
|
244
|
+
#boundHandleChange = null;
|
|
245
|
+
#boundHandleClick = this.#handleClick.bind(this);
|
|
246
|
+
|
|
247
|
+
static get observedAttributes() {
|
|
248
|
+
return ["label", "direction", "aria-label"];
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
connectedCallback() {
|
|
252
|
+
if (!this.#field) this.#initialize();
|
|
253
|
+
this.#syncField();
|
|
254
|
+
this.#syncInputAttributes();
|
|
255
|
+
this.#bindInputEvents();
|
|
256
|
+
this.removeEventListener("click", this.#boundHandleClick);
|
|
257
|
+
this.addEventListener("click", this.#boundHandleClick);
|
|
258
|
+
|
|
259
|
+
if (!this.#observer) {
|
|
260
|
+
this.#observer = new MutationObserver((mutations) => {
|
|
261
|
+
let syncField = false;
|
|
262
|
+
let syncInput = false;
|
|
263
|
+
|
|
264
|
+
for (const mutation of mutations) {
|
|
265
|
+
if (mutation.type !== "attributes") continue;
|
|
266
|
+
if (
|
|
267
|
+
mutation.attributeName === "label" ||
|
|
268
|
+
mutation.attributeName === "direction" ||
|
|
269
|
+
mutation.attributeName === "aria-label"
|
|
270
|
+
) {
|
|
271
|
+
syncField = true;
|
|
272
|
+
} else {
|
|
273
|
+
syncInput = true;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (syncField) this.#syncField();
|
|
278
|
+
if (syncInput) this.#syncInputAttributes();
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
this.#observer.observe(this, { attributes: true });
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
disconnectedCallback() {
|
|
286
|
+
this.#observer?.disconnect();
|
|
287
|
+
this.#unbindInputEvents();
|
|
288
|
+
this.removeEventListener("click", this.#boundHandleClick);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
292
|
+
if (oldValue === newValue || !this.#field) return;
|
|
293
|
+
if (name === "label" || name === "direction" || name === "aria-label") {
|
|
294
|
+
this.#syncField();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
#initialize() {
|
|
299
|
+
const initialChildren = Array.from(this.childNodes).filter(
|
|
300
|
+
(node) =>
|
|
301
|
+
node.nodeType !== Node.TEXT_NODE || Boolean(node.textContent?.trim()),
|
|
302
|
+
);
|
|
303
|
+
const customLabel = initialChildren.find(
|
|
304
|
+
(node) => node.nodeType === Node.ELEMENT_NODE && node.matches("label"),
|
|
305
|
+
);
|
|
306
|
+
const field = document.createElement("fig-field");
|
|
307
|
+
const label = customLabel || document.createElement("label");
|
|
308
|
+
const input = document.createElement("fig-input-color");
|
|
309
|
+
|
|
310
|
+
for (const node of initialChildren) {
|
|
311
|
+
if (node !== customLabel) input.appendChild(node);
|
|
312
|
+
}
|
|
313
|
+
field.append(label, input);
|
|
314
|
+
this.#field = field;
|
|
315
|
+
this.#label = label;
|
|
316
|
+
this.#input = input;
|
|
317
|
+
this.#hasCustomLabel = Boolean(customLabel);
|
|
318
|
+
this.replaceChildren(field);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
#syncField() {
|
|
322
|
+
if (!this.#field || !this.#label || !this.#input) return;
|
|
323
|
+
const hasLabelAttr = this.hasAttribute("label");
|
|
324
|
+
const rawLabel = this.getAttribute("label");
|
|
325
|
+
const isBlankLabel = hasLabelAttr && (rawLabel ?? "").trim() === "";
|
|
326
|
+
|
|
327
|
+
if (isBlankLabel) {
|
|
328
|
+
this.#label.remove();
|
|
329
|
+
} else {
|
|
330
|
+
if (!this.#hasCustomLabel) {
|
|
331
|
+
this.#label.textContent = hasLabelAttr ? (rawLabel ?? "") : "Label";
|
|
332
|
+
}
|
|
333
|
+
if (this.#label.parentElement !== this.#field) {
|
|
334
|
+
this.#field.prepend(this.#label);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
this.#field.setAttribute(
|
|
339
|
+
"direction",
|
|
340
|
+
this.getAttribute("direction") || "horizontal",
|
|
341
|
+
);
|
|
342
|
+
this.#input.setAttribute(
|
|
343
|
+
"aria-label",
|
|
344
|
+
this.getAttribute("aria-label") ||
|
|
345
|
+
this.#label.textContent?.trim() ||
|
|
346
|
+
"Color",
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
#getForwardedInputAttrNames() {
|
|
351
|
+
const reserved = new Set([
|
|
352
|
+
"label",
|
|
353
|
+
"direction",
|
|
354
|
+
"oninput",
|
|
355
|
+
"onchange",
|
|
356
|
+
"class",
|
|
357
|
+
"style",
|
|
358
|
+
"id",
|
|
359
|
+
"size",
|
|
360
|
+
"aria-label",
|
|
361
|
+
"text",
|
|
362
|
+
]);
|
|
363
|
+
return this.getAttributeNames().filter(
|
|
364
|
+
(name) => !reserved.has(name) && !name.startsWith("data-"),
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
#syncInputAttributes() {
|
|
369
|
+
if (!this.#input) return;
|
|
370
|
+
const inputAttrs = this.#getForwardedInputAttrNames();
|
|
371
|
+
const nextManaged = new Set(inputAttrs);
|
|
372
|
+
|
|
373
|
+
for (const attrName of this.#managedInputAttrs) {
|
|
374
|
+
if (!nextManaged.has(attrName)) this.#input.removeAttribute(attrName);
|
|
375
|
+
}
|
|
376
|
+
for (const attrName of inputAttrs) {
|
|
377
|
+
this.#input.setAttribute(attrName, this.getAttribute(attrName) ?? "");
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
this.#input.setAttribute("text", "true");
|
|
381
|
+
this.#managedInputAttrs = nextManaged;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
#bindInputEvents() {
|
|
385
|
+
if (!this.#input) return;
|
|
386
|
+
this.#boundHandleInput ??= this.#forwardInputEvent.bind(this, "input");
|
|
387
|
+
this.#boundHandleChange ??= this.#forwardInputEvent.bind(this, "change");
|
|
388
|
+
this.#input.addEventListener("input", this.#boundHandleInput);
|
|
389
|
+
this.#input.addEventListener("change", this.#boundHandleChange);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
#unbindInputEvents() {
|
|
393
|
+
if (!this.#input) return;
|
|
394
|
+
if (this.#boundHandleInput) {
|
|
395
|
+
this.#input.removeEventListener("input", this.#boundHandleInput);
|
|
396
|
+
}
|
|
397
|
+
if (this.#boundHandleChange) {
|
|
398
|
+
this.#input.removeEventListener("change", this.#boundHandleChange);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
#forwardInputEvent(type, event) {
|
|
403
|
+
event.stopImmediatePropagation();
|
|
404
|
+
const value = this.#input?.getAttribute("value") ?? "";
|
|
405
|
+
this.setAttribute("value", value);
|
|
406
|
+
const detail =
|
|
407
|
+
event instanceof CustomEvent && event.detail !== undefined
|
|
408
|
+
? event.detail
|
|
409
|
+
: value;
|
|
410
|
+
this.dispatchEvent(
|
|
411
|
+
new CustomEvent(type, {
|
|
412
|
+
detail,
|
|
413
|
+
bubbles: true,
|
|
414
|
+
cancelable: true,
|
|
415
|
+
composed: true,
|
|
416
|
+
}),
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
#handleClick(event) {
|
|
421
|
+
if (event.target instanceof Element && event.target.closest("fig-input-color")) {
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
this.focus();
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
get value() {
|
|
428
|
+
return this.#input?.getAttribute("value") ?? this.getAttribute("value") ?? "";
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
set value(nextValue) {
|
|
432
|
+
if (nextValue === null || nextValue === undefined || nextValue === "") {
|
|
433
|
+
this.removeAttribute("value");
|
|
434
|
+
} else {
|
|
435
|
+
this.setAttribute("value", String(nextValue));
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
focus(options) {
|
|
440
|
+
this.#input?.querySelector("input:not([tabindex='-1'])")?.focus(options);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
customElements.define("propskit-color", PropskitColor);
|
|
444
|
+
|
|
223
445
|
/* Field + Select wrapper */
|
|
224
446
|
class PropskitSelect extends HTMLElement {
|
|
225
447
|
#field = null;
|
|
@@ -230,6 +452,7 @@ class PropskitSelect extends HTMLElement {
|
|
|
230
452
|
#managedSelectAttrs = new Set();
|
|
231
453
|
#boundHandleInput = null;
|
|
232
454
|
#boundHandleChange = null;
|
|
455
|
+
#boundHandleClick = this.#handleClick.bind(this);
|
|
233
456
|
|
|
234
457
|
static get observedAttributes() {
|
|
235
458
|
return ["label", "direction", "aria-label"];
|
|
@@ -240,6 +463,8 @@ class PropskitSelect extends HTMLElement {
|
|
|
240
463
|
this.#syncField();
|
|
241
464
|
this.#syncSelectAttributes();
|
|
242
465
|
this.#bindSelectEvents();
|
|
466
|
+
this.removeEventListener("click", this.#boundHandleClick);
|
|
467
|
+
this.addEventListener("click", this.#boundHandleClick);
|
|
243
468
|
|
|
244
469
|
if (!this.#observer) {
|
|
245
470
|
this.#observer = new MutationObserver((mutations) => {
|
|
@@ -270,6 +495,7 @@ class PropskitSelect extends HTMLElement {
|
|
|
270
495
|
disconnectedCallback() {
|
|
271
496
|
this.#observer?.disconnect();
|
|
272
497
|
this.#unbindSelectEvents();
|
|
498
|
+
this.removeEventListener("click", this.#boundHandleClick);
|
|
273
499
|
}
|
|
274
500
|
|
|
275
501
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -399,6 +625,21 @@ class PropskitSelect extends HTMLElement {
|
|
|
399
625
|
);
|
|
400
626
|
}
|
|
401
627
|
|
|
628
|
+
#handleClick(event) {
|
|
629
|
+
if (event.target instanceof Element && event.target.closest("fig-dropdown")) {
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
const select = this.#select?.querySelector("select");
|
|
633
|
+
select?.focus();
|
|
634
|
+
if (typeof select?.showPicker === "function") {
|
|
635
|
+
try {
|
|
636
|
+
select.showPicker();
|
|
637
|
+
} catch {
|
|
638
|
+
// Browser may reject showPicker when no user activation is available.
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
402
643
|
get value() {
|
|
403
644
|
return this.#select?.value ?? this.getAttribute("value") ?? "";
|
|
404
645
|
}
|
|
@@ -427,6 +668,7 @@ class PropskitText extends HTMLElement {
|
|
|
427
668
|
#managedInputAttrs = new Set();
|
|
428
669
|
#boundHandleInput = null;
|
|
429
670
|
#boundHandleChange = null;
|
|
671
|
+
#boundHandleClick = this.#handleClick.bind(this);
|
|
430
672
|
|
|
431
673
|
static get observedAttributes() {
|
|
432
674
|
return ["label", "direction", "aria-label"];
|
|
@@ -437,6 +679,8 @@ class PropskitText extends HTMLElement {
|
|
|
437
679
|
this.#syncField();
|
|
438
680
|
this.#syncInputAttributes();
|
|
439
681
|
this.#bindInputEvents();
|
|
682
|
+
this.removeEventListener("click", this.#boundHandleClick);
|
|
683
|
+
this.addEventListener("click", this.#boundHandleClick);
|
|
440
684
|
|
|
441
685
|
if (!this.#observer) {
|
|
442
686
|
this.#observer = new MutationObserver((mutations) => {
|
|
@@ -467,6 +711,7 @@ class PropskitText extends HTMLElement {
|
|
|
467
711
|
disconnectedCallback() {
|
|
468
712
|
this.#observer?.disconnect();
|
|
469
713
|
this.#unbindInputEvents();
|
|
714
|
+
this.removeEventListener("click", this.#boundHandleClick);
|
|
470
715
|
}
|
|
471
716
|
|
|
472
717
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -598,6 +843,13 @@ class PropskitText extends HTMLElement {
|
|
|
598
843
|
);
|
|
599
844
|
}
|
|
600
845
|
|
|
846
|
+
#handleClick(event) {
|
|
847
|
+
if (event.target instanceof Element && event.target.closest("fig-input-text")) {
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
this.focus();
|
|
851
|
+
}
|
|
852
|
+
|
|
601
853
|
get value() {
|
|
602
854
|
return this.#input?.value ?? this.getAttribute("value") ?? "";
|
|
603
855
|
}
|
|
@@ -626,6 +878,7 @@ class PropskitNumber extends HTMLElement {
|
|
|
626
878
|
#managedInputAttrs = new Set();
|
|
627
879
|
#boundHandleInput = null;
|
|
628
880
|
#boundHandleChange = null;
|
|
881
|
+
#boundHandleClick = this.#handleClick.bind(this);
|
|
629
882
|
|
|
630
883
|
static get observedAttributes() {
|
|
631
884
|
return ["label", "direction"];
|
|
@@ -636,6 +889,8 @@ class PropskitNumber extends HTMLElement {
|
|
|
636
889
|
this.#syncField();
|
|
637
890
|
this.#syncInputAttributes();
|
|
638
891
|
this.#bindInputEvents();
|
|
892
|
+
this.removeEventListener("click", this.#boundHandleClick);
|
|
893
|
+
this.addEventListener("click", this.#boundHandleClick);
|
|
639
894
|
|
|
640
895
|
if (!this.#observer) {
|
|
641
896
|
this.#observer = new MutationObserver((mutations) => {
|
|
@@ -665,6 +920,7 @@ class PropskitNumber extends HTMLElement {
|
|
|
665
920
|
disconnectedCallback() {
|
|
666
921
|
this.#observer?.disconnect();
|
|
667
922
|
this.#unbindInputEvents();
|
|
923
|
+
this.removeEventListener("click", this.#boundHandleClick);
|
|
668
924
|
}
|
|
669
925
|
|
|
670
926
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -786,6 +1042,13 @@ class PropskitNumber extends HTMLElement {
|
|
|
786
1042
|
);
|
|
787
1043
|
}
|
|
788
1044
|
|
|
1045
|
+
#handleClick(event) {
|
|
1046
|
+
if (event.target instanceof Element && event.target.closest("fig-input-number")) {
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
this.focus();
|
|
1050
|
+
}
|
|
1051
|
+
|
|
789
1052
|
get value() {
|
|
790
1053
|
return this.#input?.value ?? this.getAttribute("value") ?? "";
|
|
791
1054
|
}
|
|
@@ -4051,6 +4314,7 @@ class FigReorder extends HTMLElement {
|
|
|
4051
4314
|
"fig-slider",
|
|
4052
4315
|
"fig-input-number",
|
|
4053
4316
|
"fig-input-text",
|
|
4317
|
+
"propskit-color",
|
|
4054
4318
|
"propskit-number",
|
|
4055
4319
|
"propskit-select",
|
|
4056
4320
|
"propskit-slider",
|
package/package.json
CHANGED