@rogieking/figui3 6.24.5 → 6.26.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/fig-lab.js CHANGED
@@ -18,6 +18,20 @@ function figLabBooleanAttribute(element, name) {
18
18
  return element.hasAttribute(name) && element.getAttribute(name) !== "false";
19
19
  }
20
20
 
21
+ function figLabColorEventAliases(color, alpha, opacity) {
22
+ const numericAlpha = Number(alpha);
23
+ const numericOpacity = Number(opacity);
24
+ const normalizedAlpha = Number.isFinite(numericAlpha)
25
+ ? Math.max(0, Math.min(1, numericAlpha))
26
+ : Number.isFinite(numericOpacity)
27
+ ? Math.max(0, Math.min(100, numericOpacity)) / 100
28
+ : 1;
29
+ const normalizedOpacity = Number.isFinite(numericOpacity)
30
+ ? Math.max(0, Math.min(100, numericOpacity))
31
+ : Math.round(normalizedAlpha * 100);
32
+ return { color, alpha: normalizedAlpha, opacity: normalizedOpacity };
33
+ }
34
+
21
35
  /* Unique IDs for lab components such as propskit-group. */
22
36
  let figLabUniqueIdCounter = 0;
23
37
  function figLabUniqueId(prefix = "fig-lab") {
@@ -1226,7 +1240,6 @@ class PropskitGroup extends HTMLElement {
1226
1240
  ].join(",");
1227
1241
 
1228
1242
  #header = null;
1229
- #disclosure = null;
1230
1243
  #chevron = null;
1231
1244
  #resetTooltip = null;
1232
1245
  #defaults = new WeakMap();
@@ -1249,7 +1262,8 @@ class PropskitGroup extends HTMLElement {
1249
1262
  cancelAnimationFrame(this.#dirtyFrame);
1250
1263
  this.#dirtyFrame = 0;
1251
1264
  }
1252
- this.#disclosure?.removeEventListener("click", this.#handleToggle);
1265
+ this.#header?.removeEventListener("click", this.#handleToggle);
1266
+ this.#header?.removeEventListener("keydown", this.#handleHeaderKeyDown);
1253
1267
  const resetBtn = this.#resetTooltip?.querySelector("fig-button");
1254
1268
  resetBtn?.removeEventListener("click", this.#handleReset);
1255
1269
  }
@@ -1257,7 +1271,7 @@ class PropskitGroup extends HTMLElement {
1257
1271
  attributeChangedCallback(name, oldValue, newValue) {
1258
1272
  if (oldValue === newValue) return;
1259
1273
  if (name === "open") {
1260
- this.#disclosure?.setAttribute("aria-expanded", String(this.open));
1274
+ this.#header?.setAttribute("aria-expanded", String(this.open));
1261
1275
  return;
1262
1276
  }
1263
1277
  if (name === "show-reset") {
@@ -1280,7 +1294,7 @@ class PropskitGroup extends HTMLElement {
1280
1294
  } else {
1281
1295
  this.setAttribute("open", "false");
1282
1296
  }
1283
- this.#disclosure?.setAttribute("aria-expanded", String(!!value));
1297
+ this.#header?.setAttribute("aria-expanded", String(!!value));
1284
1298
  if (was !== !!value) {
1285
1299
  this.dispatchEvent(
1286
1300
  new CustomEvent("openchange", {
@@ -1360,6 +1374,14 @@ class PropskitGroup extends HTMLElement {
1360
1374
  this.open = !this.open;
1361
1375
  };
1362
1376
 
1377
+ #handleHeaderKeyDown = (e) => {
1378
+ if (this.#isResetTarget(e.target)) return;
1379
+ if (e.key !== "Enter" && e.key !== " ") return;
1380
+ e.preventDefault();
1381
+ e.stopPropagation();
1382
+ this.open = !this.open;
1383
+ };
1384
+
1363
1385
  #handleReset = (e) => {
1364
1386
  e.preventDefault();
1365
1387
  e.stopPropagation();
@@ -1552,24 +1574,30 @@ class PropskitGroup extends HTMLElement {
1552
1574
  this.prepend(this.#header);
1553
1575
  }
1554
1576
 
1555
- let disclosure = this.#header.querySelector(
1556
- ":scope > .propskit-group-disclosure",
1577
+ let h3 = this.#header.querySelector(":scope > h3");
1578
+ const legacyDisclosure = this.#header.querySelector(
1579
+ ":scope > fig-button.propskit-group-disclosure",
1557
1580
  );
1558
- if (!disclosure) {
1559
- disclosure = document.createElement("fig-button");
1560
- disclosure.className = "propskit-group-disclosure";
1561
- disclosure.setAttribute("variant", "ghost");
1562
- const existingHeading = this.#header.querySelector(":scope > h3");
1563
- if (existingHeading) disclosure.appendChild(existingHeading);
1564
- this.#header.prepend(disclosure);
1581
+ if (!h3 && legacyDisclosure) {
1582
+ h3 = legacyDisclosure.querySelector(":scope > h3");
1583
+ if (h3) this.#header.insertBefore(h3, legacyDisclosure);
1584
+ legacyDisclosure.remove();
1565
1585
  }
1566
- this.#disclosure = disclosure;
1567
-
1568
- let h3 = disclosure.querySelector("h3");
1569
1586
  if (!h3) {
1570
1587
  h3 = document.createElement("h3");
1571
- disclosure.appendChild(h3);
1588
+ this.#header.prepend(h3);
1589
+ }
1590
+
1591
+ const disclosure = h3.querySelector(
1592
+ ":scope > button.propskit-group-disclosure",
1593
+ );
1594
+ if (disclosure) {
1595
+ while (disclosure.firstChild) {
1596
+ h3.insertBefore(disclosure.firstChild, disclosure);
1597
+ }
1598
+ disclosure.remove();
1572
1599
  }
1600
+
1573
1601
  if (!h3.id) h3.id = figLabUniqueId("propskit-group");
1574
1602
  if (this.#header.dataset.generated) {
1575
1603
  // Preserve chevron while updating the title text node.
@@ -1594,17 +1622,17 @@ class PropskitGroup extends HTMLElement {
1594
1622
  }
1595
1623
  this.#chevron = h3.querySelector(".propskit-group-chevron");
1596
1624
  this.#syncResetButton();
1597
- this.#header.removeAttribute("role");
1598
- this.#header.removeAttribute("tabindex");
1599
- this.#header.removeAttribute("aria-expanded");
1600
- this.#disclosure.setAttribute("aria-labelledby", h3.id);
1601
- this.#disclosure.setAttribute("aria-expanded", String(this.open));
1602
- this.#disclosure.removeEventListener("click", this.#handleToggle);
1603
- this.#disclosure.addEventListener("click", this.#handleToggle);
1625
+ this.#header.setAttribute("role", "button");
1626
+ this.#header.setAttribute("tabindex", "0");
1627
+ this.#header.setAttribute("aria-expanded", String(this.open));
1628
+ this.#header.removeEventListener("click", this.#handleToggle);
1629
+ this.#header.addEventListener("click", this.#handleToggle);
1630
+ this.#header.removeEventListener("keydown", this.#handleHeaderKeyDown);
1631
+ this.#header.addEventListener("keydown", this.#handleHeaderKeyDown);
1604
1632
 
1605
1633
  if (!this.hasAttribute("open")) {
1606
1634
  this.setAttribute("open", "false");
1607
- this.#disclosure.setAttribute("aria-expanded", "false");
1635
+ this.#header.setAttribute("aria-expanded", "false");
1608
1636
  }
1609
1637
  }
1610
1638
  }
@@ -3229,18 +3257,29 @@ class FigCanvasControl extends HTMLElement {
3229
3257
  this.#syncPointPointCursors();
3230
3258
  }
3231
3259
 
3232
- #emitInput() {
3260
+ #emitInput(detail = this.value) {
3233
3261
  this.dispatchEvent(
3234
- new CustomEvent("input", { bubbles: true, detail: this.value }),
3262
+ new CustomEvent("input", { bubbles: true, detail }),
3235
3263
  );
3236
3264
  }
3237
3265
 
3238
- #emitChange() {
3266
+ #emitChange(detail = this.value) {
3239
3267
  this.dispatchEvent(
3240
- new CustomEvent("change", { bubbles: true, detail: this.value }),
3268
+ new CustomEvent("change", { bubbles: true, detail }),
3241
3269
  );
3242
3270
  }
3243
3271
 
3272
+ #colorEventDetail(detail) {
3273
+ return {
3274
+ ...this.value,
3275
+ ...figLabColorEventAliases(
3276
+ detail.color,
3277
+ detail.alpha,
3278
+ detail.opacity,
3279
+ ),
3280
+ };
3281
+ }
3282
+
3244
3283
  #syncValueAttribute() {
3245
3284
  this.setAttribute("value", JSON.stringify(this.value));
3246
3285
  }
@@ -3251,8 +3290,11 @@ class FigCanvasControl extends HTMLElement {
3251
3290
  this.#pointHandle.addEventListener("input", (e) => {
3252
3291
  e.stopPropagation();
3253
3292
  if (e.detail?.color) {
3254
- this.setAttribute("color", e.detail.color);
3255
- this.#emitInput();
3293
+ this.setAttribute(
3294
+ "color",
3295
+ this.#pointHandle.getAttribute("color") || e.detail.color,
3296
+ );
3297
+ this.#emitInput(this.#colorEventDetail(e.detail));
3256
3298
  return;
3257
3299
  }
3258
3300
  this.#isDragging = true;
@@ -3271,8 +3313,11 @@ class FigCanvasControl extends HTMLElement {
3271
3313
  this.#pointHandle.addEventListener("change", (e) => {
3272
3314
  e.stopPropagation();
3273
3315
  if (e.detail?.color) {
3274
- this.setAttribute("color", e.detail.color);
3275
- this.#emitChange();
3316
+ this.setAttribute(
3317
+ "color",
3318
+ this.#pointHandle.getAttribute("color") || e.detail.color,
3319
+ );
3320
+ this.#emitChange(this.#colorEventDetail(e.detail));
3276
3321
  return;
3277
3322
  }
3278
3323
  const px = e.detail?.px ?? this.#x / 100;
@@ -5272,32 +5317,22 @@ class FigReorder extends HTMLElement {
5272
5317
 
5273
5318
  static #DRAG_THRESHOLD = 6;
5274
5319
 
5275
- static #INTERACTIVE_SELECTORS = [
5276
- "input",
5277
- "button",
5278
- "select",
5279
- "textarea",
5280
- "a[href]",
5281
- "label",
5282
- "summary",
5283
- "fig-button",
5284
- "fig-dropdown",
5320
+ static #NESTED_DRAG_SELECTORS = [
5321
+ '[draggable]:not([draggable="false"])',
5322
+ 'img:not([draggable="false"])',
5323
+ 'a[href]:not([draggable="false"])',
5324
+ 'input[type="range"]',
5325
+ "fig-handle",
5285
5326
  "fig-slider",
5286
5327
  "fig-input-number",
5287
- "fig-input-text",
5288
- "propskit-color",
5328
+ "fig-input-gradient",
5329
+ "fig-easing-curve",
5330
+ "fig-input-angle",
5331
+ "fig-input-joystick",
5332
+ "fig-canvas-control",
5289
5333
  "propskit-number",
5290
- "propskit-select",
5291
5334
  "propskit-slider",
5292
- "propskit-switch",
5293
- "propskit-text",
5294
- "fig-checkbox",
5295
- "fig-switch",
5296
- "fig-combo-input",
5297
- "fig-segmented-control",
5298
- "fig-input-color",
5299
- "fig-input-fill",
5300
- '[contenteditable="true"]',
5335
+ "propskit-oscillator",
5301
5336
  ];
5302
5337
 
5303
5338
  #childObserver = null;
@@ -5458,7 +5493,7 @@ class FigReorder extends HTMLElement {
5458
5493
  if (event.button !== 0) return;
5459
5494
  if (
5460
5495
  !this.#handleSelector &&
5461
- this.#isInteractiveTarget(event.target, child)
5496
+ this.#isNestedDragTarget(event.target, child)
5462
5497
  ) {
5463
5498
  return;
5464
5499
  }
@@ -5567,11 +5602,11 @@ class FigReorder extends HTMLElement {
5567
5602
  });
5568
5603
  }
5569
5604
 
5570
- #isInteractiveTarget(target, child) {
5605
+ #isNestedDragTarget(target, child) {
5571
5606
  let node = target;
5572
5607
  while (node && node !== child) {
5573
5608
  if (node instanceof Element) {
5574
- for (const selector of FigReorder.#INTERACTIVE_SELECTORS) {
5609
+ for (const selector of FigReorder.#NESTED_DRAG_SELECTORS) {
5575
5610
  if (node.matches(selector)) return true;
5576
5611
  }
5577
5612
  }
@@ -5588,7 +5623,6 @@ class FigReorder extends HTMLElement {
5588
5623
 
5589
5624
  #startPendingDrag(event, item, target) {
5590
5625
  this.#cancelDrag();
5591
- FigReorder.#setDocumentDragging(true);
5592
5626
 
5593
5627
  const state = {
5594
5628
  item,
@@ -5618,6 +5652,7 @@ class FigReorder extends HTMLElement {
5618
5652
  event.preventDefault();
5619
5653
  event.stopPropagation();
5620
5654
  item.classList.add("dragging");
5655
+ FigReorder.#setDocumentDragging(true);
5621
5656
  try {
5622
5657
  target.setPointerCapture(state.pointerId);
5623
5658
  } catch {}
package/fig.js CHANGED
@@ -39,6 +39,20 @@ function figBooleanAttribute(element, name) {
39
39
  return element.hasAttribute(name) && element.getAttribute(name) !== "false";
40
40
  }
41
41
 
42
+ function figColorEventAliases(color, alpha, opacity) {
43
+ const numericAlpha = Number(alpha);
44
+ const numericOpacity = Number(opacity);
45
+ const normalizedAlpha = Number.isFinite(numericAlpha)
46
+ ? Math.max(0, Math.min(1, numericAlpha))
47
+ : Number.isFinite(numericOpacity)
48
+ ? Math.max(0, Math.min(100, numericOpacity)) / 100
49
+ : 1;
50
+ const normalizedOpacity = Number.isFinite(numericOpacity)
51
+ ? Math.max(0, Math.min(100, numericOpacity))
52
+ : Math.round(normalizedAlpha * 100);
53
+ return { color, alpha: normalizedAlpha, opacity: normalizedOpacity };
54
+ }
55
+
42
56
  function figNormalizeTextOnlyInputSlots(host) {
43
57
  host
44
58
  .querySelectorAll(':scope > [slot="prepend"], :scope > [slot="append"]')
@@ -9115,7 +9129,7 @@ class FigInputColor extends HTMLElement {
9115
9129
  new CustomEvent("input", {
9116
9130
  bubbles: true,
9117
9131
  cancelable: true,
9118
- detail: { value: this.value, hex: this.hex, rgba: this.rgba },
9132
+ detail: this.#eventDetail(),
9119
9133
  }),
9120
9134
  );
9121
9135
  }
@@ -9124,11 +9138,24 @@ class FigInputColor extends HTMLElement {
9124
9138
  new CustomEvent("change", {
9125
9139
  bubbles: true,
9126
9140
  cancelable: true,
9127
- detail: { value: this.value, hex: this.hex, rgba: this.rgba },
9141
+ detail: this.#eventDetail(),
9128
9142
  }),
9129
9143
  );
9130
9144
  }
9131
9145
 
9146
+ #eventDetail() {
9147
+ return {
9148
+ value: this.value,
9149
+ hex: this.hex,
9150
+ rgba: this.rgba,
9151
+ ...figColorEventAliases(
9152
+ this.hexOpaque,
9153
+ this.rgba?.a,
9154
+ Math.round((this.rgba?.a ?? 1) * 100),
9155
+ ),
9156
+ };
9157
+ }
9158
+
9132
9159
  static get observedAttributes() {
9133
9160
  return [
9134
9161
  "value",
@@ -11605,6 +11632,10 @@ class FigInputGradient extends HTMLElement {
11605
11632
  this.#gradient.stops[idx].color = e.detail.color;
11606
11633
  if (e.detail.opacity !== undefined) {
11607
11634
  this.#gradient.stops[idx].opacity = e.detail.opacity;
11635
+ } else if (e.detail.alpha !== undefined) {
11636
+ this.#gradient.stops[idx].opacity = Math.round(
11637
+ e.detail.alpha * 100,
11638
+ );
11608
11639
  }
11609
11640
  handle.setAttribute(
11610
11641
  "color",
@@ -13367,7 +13398,8 @@ figDefineElement("fig-video", FigVideo);
13367
13398
  /**
13368
13399
  * <fig-card> — Media card with optional link, selection chrome, and truncated label.
13369
13400
  *
13370
- * Composes a generated `fig-image` (or authored fig-image/fig-media/fig-preview).
13401
+ * Composes a generated `fig-image` when `src` is set. Without `src`, authored
13402
+ * children remain direct children and generated label content is appended.
13371
13403
  * Selection is attribute-only (`selected`); apps own toggle/group logic.
13372
13404
  * When `href` is set (and not disabled), content wraps in a real `<a>`.
13373
13405
  *
@@ -13405,18 +13437,39 @@ class FigCard extends HTMLElement {
13405
13437
  }
13406
13438
 
13407
13439
  #linkEl = null;
13408
- #mediaWrap = null;
13409
13440
  #textWrap = null;
13410
13441
  #imageEl = null;
13411
13442
  #labelEl = null;
13412
13443
  #sublabelEl = null;
13413
13444
  #usesAuthoredMedia = false;
13445
+ #mediaObserver = new MutationObserver((mutations) => {
13446
+ const authoredMediaChanged = mutations.some((mutation) =>
13447
+ [...mutation.addedNodes, ...mutation.removedNodes].some(
13448
+ (node) =>
13449
+ node instanceof Element &&
13450
+ (node.matches(
13451
+ "fig-image:not([data-generated]), fig-media:not([data-generated]), fig-preview:not([data-generated])",
13452
+ ) ||
13453
+ node.querySelector(
13454
+ "fig-image:not([data-generated]), fig-media:not([data-generated]), fig-preview:not([data-generated])",
13455
+ )),
13456
+ ),
13457
+ );
13458
+ if (!authoredMediaChanged || !this.isConnected) return;
13459
+ this.#ensureStructure();
13460
+ this.#sync();
13461
+ });
13414
13462
 
13415
13463
  connectedCallback() {
13464
+ this.#mediaObserver.observe(this, { childList: true, subtree: true });
13416
13465
  this.#ensureStructure();
13417
13466
  this.#sync();
13418
13467
  }
13419
13468
 
13469
+ disconnectedCallback() {
13470
+ this.#mediaObserver.disconnect();
13471
+ }
13472
+
13420
13473
  attributeChangedCallback() {
13421
13474
  if (!this.isConnected) return;
13422
13475
  this.#ensureStructure();
@@ -13441,13 +13494,27 @@ class FigCard extends HTMLElement {
13441
13494
  return raw === "2" ? "2" : "1";
13442
13495
  }
13443
13496
 
13497
+ #usesGeneratedMedia() {
13498
+ return Boolean(this.getAttribute("src"));
13499
+ }
13500
+
13444
13501
  #findAuthoredMedia() {
13445
13502
  return this.querySelector(
13446
- ":scope > fig-image:not([data-generated]), :scope > fig-media:not([data-generated]), :scope > fig-preview:not([data-generated]), :scope > .fig-card-link > .fig-card-media > fig-image:not([data-generated]), :scope > .fig-card-link > .fig-card-media > fig-media:not([data-generated]), :scope > .fig-card-link > .fig-card-media > fig-preview:not([data-generated])",
13503
+ ":scope > fig-image:not([data-generated]), :scope > fig-media:not([data-generated]), :scope > fig-preview:not([data-generated]), :scope > .fig-card-link > fig-image:not([data-generated]), :scope > .fig-card-link > fig-media:not([data-generated]), :scope > .fig-card-link > fig-preview:not([data-generated]), :scope > .fig-card-link > .fig-card-media > fig-image:not([data-generated]), :scope > .fig-card-link > .fig-card-media > fig-media:not([data-generated]), :scope > .fig-card-link > .fig-card-media > fig-preview:not([data-generated])",
13447
13504
  );
13448
13505
  }
13449
13506
 
13450
13507
  #ensureStructure() {
13508
+ if (!this.#usesGeneratedMedia()) {
13509
+ this.#ensureManualStructure();
13510
+ return;
13511
+ }
13512
+
13513
+ this.querySelectorAll(":scope > .fig-card-text[data-generated]").forEach(
13514
+ (element) => element.remove(),
13515
+ );
13516
+ if (this.#textWrap?.parentElement === this) this.#textWrap = null;
13517
+
13451
13518
  const authored = this.#findAuthoredMedia();
13452
13519
  this.#usesAuthoredMedia = Boolean(authored);
13453
13520
 
@@ -13468,29 +13535,30 @@ class FigCard extends HTMLElement {
13468
13535
  this.#linkEl = next;
13469
13536
  }
13470
13537
 
13471
- if (!this.#mediaWrap || !this.#mediaWrap.isConnected) {
13472
- this.#mediaWrap = this.#linkEl.querySelector(":scope > .fig-card-media");
13473
- if (!this.#mediaWrap) {
13474
- this.#mediaWrap = document.createElement("div");
13475
- this.#mediaWrap.className = "fig-card-media";
13476
- this.#linkEl.prepend(this.#mediaWrap);
13538
+ const legacyMediaWrap = this.#linkEl.querySelector(
13539
+ ":scope > .fig-card-media",
13540
+ );
13541
+ if (legacyMediaWrap) {
13542
+ while (legacyMediaWrap.firstChild) {
13543
+ this.#linkEl.insertBefore(legacyMediaWrap.firstChild, legacyMediaWrap);
13477
13544
  }
13545
+ legacyMediaWrap.remove();
13478
13546
  }
13479
13547
 
13480
- if (authored && authored.parentElement !== this.#mediaWrap) {
13481
- this.#mediaWrap
13482
- .querySelectorAll("[data-generated]")
13548
+ if (authored && authored.parentElement !== this.#linkEl) {
13549
+ this.#linkEl
13550
+ .querySelectorAll(":scope > [data-generated]:not(.fig-card-text)")
13483
13551
  .forEach((el) => el.remove());
13484
- this.#mediaWrap.appendChild(authored);
13552
+ this.#linkEl.prepend(authored);
13485
13553
  this.#imageEl = null;
13486
13554
  }
13487
13555
 
13488
13556
  if (!this.#usesAuthoredMedia) {
13489
13557
  this.#imageEl =
13490
- this.#mediaWrap.querySelector(":scope > fig-image[data-generated]") ||
13558
+ this.#linkEl.querySelector(":scope > fig-image[data-generated]") ||
13491
13559
  null;
13492
13560
  if (!this.#imageEl) {
13493
- this.#mediaWrap
13561
+ this.#linkEl
13494
13562
  .querySelectorAll(
13495
13563
  ":scope > fig-image, :scope > fig-media, :scope > fig-preview",
13496
13564
  )
@@ -13501,12 +13569,26 @@ class FigCard extends HTMLElement {
13501
13569
  this.#imageEl.setAttribute("data-generated", "");
13502
13570
  this.#imageEl.setAttribute("full", "");
13503
13571
  this.#imageEl.setAttribute("size", "auto");
13504
- this.#mediaWrap.appendChild(this.#imageEl);
13572
+ this.#linkEl.prepend(this.#imageEl);
13505
13573
  }
13506
13574
  }
13507
13575
 
13508
13576
  if (!this.#textWrap || !this.#textWrap.isConnected) {
13509
13577
  this.#textWrap = this.#linkEl.querySelector(":scope > .fig-card-text");
13578
+ if (this.#textWrap?.tagName.toLowerCase() !== "div") {
13579
+ const text = document.createElement("div");
13580
+ text.className = "fig-card-text";
13581
+ text.setAttribute("data-generated", "");
13582
+ if (this.#textWrap) {
13583
+ while (this.#textWrap.firstChild) {
13584
+ text.appendChild(this.#textWrap.firstChild);
13585
+ }
13586
+ this.#textWrap.replaceWith(text);
13587
+ } else {
13588
+ this.#linkEl.appendChild(text);
13589
+ }
13590
+ this.#textWrap = text;
13591
+ }
13510
13592
  if (!this.#textWrap) {
13511
13593
  this.#textWrap = document.createElement("div");
13512
13594
  this.#textWrap.className = "fig-card-text";
@@ -13542,7 +13624,7 @@ class FigCard extends HTMLElement {
13542
13624
  ":scope > .fig-card-sublabel[data-generated]",
13543
13625
  ) || null;
13544
13626
  if (!this.#sublabelEl) {
13545
- this.#sublabelEl = document.createElement("span");
13627
+ this.#sublabelEl = document.createElement("label");
13546
13628
  this.#sublabelEl.className = "fig-card-sublabel";
13547
13629
  this.#sublabelEl.setAttribute("data-generated", "");
13548
13630
  }
@@ -13551,7 +13633,49 @@ class FigCard extends HTMLElement {
13551
13633
  }
13552
13634
  }
13553
13635
 
13636
+ #ensureManualStructure() {
13637
+ const staleLink =
13638
+ this.#linkEl || this.querySelector(":scope > .fig-card-link");
13639
+ if (staleLink) {
13640
+ staleLink
13641
+ .querySelectorAll(
13642
+ "fig-image:not([data-generated]), fig-media:not([data-generated]), fig-preview:not([data-generated])",
13643
+ )
13644
+ .forEach((media) => this.insertBefore(media, staleLink));
13645
+ staleLink.remove();
13646
+ }
13647
+ this.querySelectorAll(":scope > [data-generated]").forEach((element) =>
13648
+ element.remove(),
13649
+ );
13650
+
13651
+ this.#linkEl = null;
13652
+ this.#imageEl = null;
13653
+ this.#usesAuthoredMedia = true;
13654
+
13655
+ this.#textWrap = document.createElement("div");
13656
+ this.#textWrap.className = "fig-card-text";
13657
+ this.#textWrap.setAttribute("data-generated", "");
13658
+ this.#labelEl = document.createElement("label");
13659
+ this.#labelEl.className = "fig-card-label";
13660
+ this.#labelEl.setAttribute("data-generated", "");
13661
+ this.#textWrap.appendChild(this.#labelEl);
13662
+
13663
+ const sublabel = this.#sublabelText();
13664
+ this.#sublabelEl = null;
13665
+ if (sublabel) {
13666
+ this.#sublabelEl = document.createElement("label");
13667
+ this.#sublabelEl.className = "fig-card-sublabel";
13668
+ this.#sublabelEl.setAttribute("data-generated", "");
13669
+ this.#textWrap.appendChild(this.#sublabelEl);
13670
+ }
13671
+ this.appendChild(this.#textWrap);
13672
+ }
13673
+
13554
13674
  #sync() {
13675
+ if (!this.#usesGeneratedMedia()) {
13676
+ this.#syncManual();
13677
+ return;
13678
+ }
13555
13679
  if (!this.#linkEl) return;
13556
13680
 
13557
13681
  const disabled = figBooleanAttribute(this, "disabled");
@@ -13634,6 +13758,29 @@ class FigCard extends HTMLElement {
13634
13758
  this.#textWrap.hidden = !label && !sublabel;
13635
13759
  }
13636
13760
  }
13761
+
13762
+ #syncManual() {
13763
+ const disabled = figBooleanAttribute(this, "disabled");
13764
+ const label = this.#labelText();
13765
+ const sublabel = this.#sublabelText();
13766
+
13767
+ this.style.setProperty(
13768
+ "--fig-card-label-line-clamp",
13769
+ this.#lineClamp(),
13770
+ );
13771
+ this.setAttribute("role", "group");
13772
+ const ariaLabel = [label, sublabel].filter(Boolean).join(", ");
13773
+ if (ariaLabel) this.setAttribute("aria-label", ariaLabel);
13774
+ else this.removeAttribute("aria-label");
13775
+ if (disabled) this.setAttribute("aria-disabled", "true");
13776
+ else this.removeAttribute("aria-disabled");
13777
+
13778
+ if (this.#labelEl) {
13779
+ this.#labelEl.textContent = label;
13780
+ this.#labelEl.hidden = !label;
13781
+ }
13782
+ if (this.#sublabelEl) this.#sublabelEl.textContent = sublabel;
13783
+ }
13637
13784
  }
13638
13785
  figDefineElement("fig-card", FigCard);
13639
13786
 
@@ -17637,6 +17784,14 @@ class FigColorTip extends HTMLElement {
17637
17784
  eventDetail.opacity = Math.round(detail.alpha * 100);
17638
17785
  }
17639
17786
  }
17787
+ Object.assign(
17788
+ eventDetail,
17789
+ figColorEventAliases(
17790
+ eventDetail.color,
17791
+ this.#alphaEnabled ? detail?.alpha : 1,
17792
+ eventDetail.opacity,
17793
+ ),
17794
+ );
17640
17795
 
17641
17796
  this.dispatchEvent(
17642
17797
  new CustomEvent(type, {
@@ -19183,7 +19338,11 @@ class FigHandle extends HTMLElement {
19183
19338
  : detail.alpha !== undefined
19184
19339
  ? Math.round(detail.alpha * 100)
19185
19340
  : undefined;
19186
- return { color: detail.color, opacity };
19341
+ return {
19342
+ color: detail.color,
19343
+ opacity,
19344
+ ...figColorEventAliases(detail.color, detail.alpha, opacity),
19345
+ };
19187
19346
  }
19188
19347
 
19189
19348
  #handleDirectColorPickerInput = (e) => {
@@ -19214,7 +19373,11 @@ class FigHandle extends HTMLElement {
19214
19373
 
19215
19374
  #detailFromNativeColor(value) {
19216
19375
  const { opacity } = this.#normalizeColorForPicker();
19217
- return opacity < 100 ? { color: value, opacity } : { color: value };
19376
+ const detail = opacity < 100 ? { color: value, opacity } : { color: value };
19377
+ return {
19378
+ ...detail,
19379
+ ...figColorEventAliases(value, undefined, opacity),
19380
+ };
19218
19381
  }
19219
19382
 
19220
19383
  #handleNativeColorInput = (e) => {
@@ -19249,14 +19412,23 @@ class FigHandle extends HTMLElement {
19249
19412
  #handleColorTipInput = (e) => {
19250
19413
  e.stopPropagation();
19251
19414
  if (e.detail?.color) {
19415
+ const detail = {
19416
+ color: e.detail.color,
19417
+ opacity: e.detail.opacity,
19418
+ ...figColorEventAliases(
19419
+ e.detail.color,
19420
+ e.detail.alpha,
19421
+ e.detail.opacity,
19422
+ ),
19423
+ };
19252
19424
  this.setAttribute(
19253
19425
  "color",
19254
- this.#colorWithOpacity(e.detail.color, e.detail.opacity),
19426
+ this.#colorWithOpacity(detail.color, detail.opacity),
19255
19427
  );
19256
19428
  this.dispatchEvent(
19257
19429
  new CustomEvent("input", {
19258
19430
  bubbles: true,
19259
- detail: { color: e.detail.color, opacity: e.detail.opacity },
19431
+ detail,
19260
19432
  }),
19261
19433
  );
19262
19434
  }
@@ -19265,14 +19437,23 @@ class FigHandle extends HTMLElement {
19265
19437
  #handleColorTipChange = (e) => {
19266
19438
  e.stopPropagation();
19267
19439
  if (e.detail?.color) {
19440
+ const detail = {
19441
+ color: e.detail.color,
19442
+ opacity: e.detail.opacity,
19443
+ ...figColorEventAliases(
19444
+ e.detail.color,
19445
+ e.detail.alpha,
19446
+ e.detail.opacity,
19447
+ ),
19448
+ };
19268
19449
  this.setAttribute(
19269
19450
  "color",
19270
- this.#colorWithOpacity(e.detail.color, e.detail.opacity),
19451
+ this.#colorWithOpacity(detail.color, detail.opacity),
19271
19452
  );
19272
19453
  this.dispatchEvent(
19273
19454
  new CustomEvent("change", {
19274
19455
  bubbles: true,
19275
- detail: { color: e.detail.color, opacity: e.detail.opacity },
19456
+ detail,
19276
19457
  }),
19277
19458
  );
19278
19459
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "6.24.5",
3
+ "version": "6.26.0",
4
4
  "description": "A lightweight web components library for building Figma plugin and widget UIs with native look and feel",
5
5
  "author": "Rogie King",
6
6
  "license": "MIT",