@rogieking/figui3 8.5.0 → 8.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/fig-editor.css CHANGED
@@ -53,22 +53,32 @@ fig-interpolation-swatch {
53
53
 
54
54
  /* Toast */
55
55
  dialog[is="fig-toast"] {
56
- contain: layout paint;
56
+ --fig-toast-bg-color: var(--figma-color-bg);
57
57
  --z-index: 999999;
58
+ contain: none;
58
59
  z-index: var(--z-index);
59
60
  box-shadow: var(--figma-elevation-100);
60
61
  border-radius: var(--radius-large);
61
62
  padding: var(--spacer-2) var(--spacer-3);
62
63
  align-items: center;
63
- gap: var(--spacer-2);
64
+ gap: var(--spacer-2-5);
64
65
  height: 2.5rem;
66
+ flex-wrap: nowrap;
65
67
  justify-content: center;
66
68
  min-width: 0;
67
69
  color: var(--figma-color-text);
68
- background-color: var(--figma-color-bg);
70
+ background-color: var(--fig-toast-bg-color);
71
+
72
+ &[icon]{
73
+ &>fig-icon:first-child {
74
+ margin-inline: calc(var(--spacer-2) * -1);
75
+ }
76
+ }
69
77
 
78
+ /* Generic dialog[open] styling stacks content in a column; toasts stay inline. */
70
79
  &[open]:not([open="false"]) {
71
80
  display: flex;
81
+ flex-direction: row;
72
82
  }
73
83
 
74
84
  & p {
@@ -82,12 +92,29 @@ dialog[is="fig-toast"] {
82
92
  margin: 0;
83
93
  }
84
94
 
95
+ & > fig-separator[data-fig-toast-dismiss-separator] {
96
+ flex: 0 0 auto;
97
+ align-self: stretch;
98
+ margin: 0;
99
+ --fig-separator-color: var(--figma-color-bordertranslucent);
100
+ }
85
101
  & fig-button:last-child {
86
- margin-right: calc(var(--spacer-2) * -1);
102
+ margin-inline: calc(var(--spacer-2) * -1);
103
+ margin-left: calc(var(--spacer-1) * -1);
104
+ }
105
+
106
+ & > fig-icon[data-fig-toast-icon] {
107
+ flex: 0 0 auto;
108
+ --size: var(--spacer-4);
109
+ }
110
+
111
+ & > fig-button[data-fig-toast-dismiss] {
112
+ flex: 0 0 auto;
87
113
  }
88
114
 
89
115
  &[theme="dark"] {
90
116
  color-scheme: dark;
117
+ --fig-toast-bg-color: var(--figma-color-bg-menu);
91
118
  }
92
119
 
93
120
  &[theme="light"] {
package/fig.js CHANGED
@@ -2295,6 +2295,17 @@ class FigDialog extends HTMLDialogElement {
2295
2295
  figDefineCustomizedBuiltIn("fig-dialog", FigDialog, { extends: "dialog" });
2296
2296
 
2297
2297
  /* Toast */
2298
+ /* Toast */
2299
+ /**
2300
+ * A temporary toast notification based on <dialog>.
2301
+ * @attr {number|string} duration - Auto-dismiss ms (default 5000; 0 = no dismiss).
2302
+ * @attr {number|string} offset - Distance from bottom in px (default 16).
2303
+ * @attr {string} theme - Visual theme: "dark", "light", "danger", "brand", "success", or "auto".
2304
+ * @attr {string} live - Set to "assertive" for urgent announcements.
2305
+ * @attr {string} icon - Optional fig-icon name prepended when set; omit for no icon.
2306
+ * @attr {boolean|string} dismiss - When true, appends a ghost close button that hides the toast.
2307
+ * @attr {boolean|string} open - Open when present and not "false".
2308
+ */
2298
2309
  class FigToast extends HTMLDialogElement {
2299
2310
  constructor() {
2300
2311
  super();
@@ -2310,6 +2321,9 @@ class FigToast extends HTMLDialogElement {
2310
2321
  this._syncingOpen = false;
2311
2322
  this._managedRole = null;
2312
2323
  this._managedLive = null;
2324
+ this._managedIcon = null;
2325
+ this._managedDismiss = null;
2326
+ this._managedDismissSeparator = null;
2313
2327
  this._boundHandleClose = this.handleClose.bind(this);
2314
2328
  }
2315
2329
 
@@ -2317,6 +2331,8 @@ class FigToast extends HTMLDialogElement {
2317
2331
  this._figInit();
2318
2332
  if (!this.hasAttribute("theme")) this.setAttribute("theme", "dark");
2319
2333
  this.syncLiveRegion();
2334
+ this.syncIcon();
2335
+ this.syncDismiss();
2320
2336
  this.addCloseListeners();
2321
2337
  this.applyPosition();
2322
2338
  if (this.hasAttribute("open") && this.getAttribute("open") !== "false") {
@@ -2384,6 +2400,101 @@ class FigToast extends HTMLDialogElement {
2384
2400
  }
2385
2401
  }
2386
2402
 
2403
+ syncIcon() {
2404
+ const name = this.getAttribute("icon");
2405
+ let icon = this._managedIcon;
2406
+ if (!icon || icon.parentNode !== this) {
2407
+ icon = this.querySelector(":scope > fig-icon[data-fig-toast-icon]");
2408
+ }
2409
+
2410
+ if (!name) {
2411
+ if (icon) icon.remove();
2412
+ this._managedIcon = null;
2413
+ return;
2414
+ }
2415
+
2416
+ if (!icon) {
2417
+ icon = document.createElement("fig-icon");
2418
+ icon.setAttribute("data-fig-toast-icon", "");
2419
+ icon.setAttribute("aria-hidden", "true");
2420
+ this.prepend(icon);
2421
+ } else if (this.firstElementChild !== icon) {
2422
+ this.prepend(icon);
2423
+ }
2424
+
2425
+ if (icon.getAttribute("size") !== "medium") {
2426
+ icon.setAttribute("size", "medium");
2427
+ }
2428
+ if (icon.getAttribute("name") !== name) {
2429
+ icon.setAttribute("name", name);
2430
+ }
2431
+ this._managedIcon = icon;
2432
+ }
2433
+
2434
+ syncDismiss() {
2435
+ const enabled = figBooleanAttribute(this, "dismiss");
2436
+ let button = this._managedDismiss;
2437
+ if (!button || button.parentNode !== this) {
2438
+ button = this.querySelector(":scope > fig-button[data-fig-toast-dismiss]");
2439
+ }
2440
+ let separator = this._managedDismissSeparator;
2441
+ if (!separator || separator.parentNode !== this) {
2442
+ separator = this.querySelector(
2443
+ ":scope > fig-separator[data-fig-toast-dismiss-separator]",
2444
+ );
2445
+ }
2446
+
2447
+ if (!enabled) {
2448
+ if (button) {
2449
+ button.removeEventListener("click", this._boundHandleClose);
2450
+ button.remove();
2451
+ }
2452
+ if (separator) separator.remove();
2453
+ this._managedDismiss = null;
2454
+ this._managedDismissSeparator = null;
2455
+ return;
2456
+ }
2457
+
2458
+ if (!separator) {
2459
+ separator = document.createElement("fig-separator");
2460
+ separator.setAttribute("data-fig-toast-dismiss-separator", "");
2461
+ separator.setAttribute("direction", "vertical");
2462
+ separator.setAttribute("aria-orientation", "vertical");
2463
+ separator.setAttribute("aria-hidden", "true");
2464
+ }
2465
+
2466
+ if (!button) {
2467
+ button = document.createElement("fig-button");
2468
+ button.setAttribute("data-fig-toast-dismiss", "");
2469
+ button.setAttribute("variant", "ghost");
2470
+ button.setAttribute("icon", "");
2471
+ button.setAttribute("close-toast", "");
2472
+ button.setAttribute("aria-label", "Close notification");
2473
+ const icon = document.createElement("fig-icon");
2474
+ icon.setAttribute("name", "close");
2475
+ icon.setAttribute("aria-hidden", "true");
2476
+ button.append(icon);
2477
+ }
2478
+
2479
+ if (separator.parentNode !== this || button.parentNode !== this) {
2480
+ this.append(separator, button);
2481
+ } else if (
2482
+ this.lastElementChild !== button ||
2483
+ button.previousElementSibling !== separator
2484
+ ) {
2485
+ this.append(separator, button);
2486
+ }
2487
+
2488
+ if (separator.getAttribute("direction") !== "vertical") {
2489
+ separator.setAttribute("direction", "vertical");
2490
+ }
2491
+
2492
+ button.removeEventListener("click", this._boundHandleClose);
2493
+ button.addEventListener("click", this._boundHandleClose);
2494
+ this._managedDismiss = button;
2495
+ this._managedDismissSeparator = separator;
2496
+ }
2497
+
2387
2498
  startAutoClose() {
2388
2499
  this.clearAutoClose();
2389
2500
  const duration = parseInt(this.getAttribute("duration") ?? "5000");
@@ -2409,6 +2520,8 @@ class FigToast extends HTMLDialogElement {
2409
2520
  showToast() {
2410
2521
  const wasVisible = this._toastVisible;
2411
2522
  this.syncLiveRegion();
2523
+ this.syncIcon();
2524
+ this.syncDismiss();
2412
2525
  this._resolveAutoTheme();
2413
2526
  this._syncingOpen = true;
2414
2527
  try {
@@ -2441,7 +2554,7 @@ class FigToast extends HTMLDialogElement {
2441
2554
  }
2442
2555
 
2443
2556
  static get observedAttributes() {
2444
- return ["duration", "offset", "open", "theme", "live"];
2557
+ return ["duration", "offset", "open", "theme", "live", "icon", "dismiss"];
2445
2558
  }
2446
2559
 
2447
2560
  attributeChangedCallback(name, oldValue, newValue) {
@@ -2460,6 +2573,8 @@ class FigToast extends HTMLDialogElement {
2460
2573
  else this.style.removeProperty("color-scheme");
2461
2574
  }
2462
2575
  if (name === "theme" || name === "live") this.syncLiveRegion();
2576
+ if (name === "icon") this.syncIcon();
2577
+ if (name === "dismiss") this.syncDismiss();
2463
2578
  }
2464
2579
  }
2465
2580
  figDefineCustomizedBuiltIn("fig-toast", FigToast, { extends: "dialog" });
@@ -16419,6 +16534,7 @@ const FIG_ICON_TOKENS = {
16419
16534
  visible: { medium: "--icon-24-visible", small: "--icon-16-visible" },
16420
16535
  hidden: { medium: "--icon-24-hidden", small: "--icon-16-hidden" },
16421
16536
  globe: { medium: "--icon-24-globe", small: "--icon-16-globe" },
16537
+ warning: { medium: "--icon-24-warning", small: "--icon-16-warning" },
16422
16538
  };
16423
16539
 
16424
16540
  function figIconCssVar(name, size = "medium") {
@@ -18572,11 +18688,12 @@ figDefineElement("fig-menu-item", FigMenuItem);
18572
18688
  /**
18573
18689
  * Visual divider between content groups.
18574
18690
  * @attr {string} label - Optional group label shown in secondary text under the line.
18691
+ * @attr {string} direction - Orientation: "vertical" for a tall rule; default is horizontal.
18575
18692
  * @attr {boolean} borderless - Hides the separator line when present or "true".
18576
18693
  */
18577
18694
  class FigSeparator extends HTMLElement {
18578
18695
  static get observedAttributes() {
18579
- return ["label"];
18696
+ return ["label", "direction"];
18580
18697
  }
18581
18698
 
18582
18699
  get label() {
@@ -18601,6 +18718,12 @@ class FigSeparator extends HTMLElement {
18601
18718
  if (!this.hasAttribute("role")) {
18602
18719
  this.setAttribute("role", "separator");
18603
18720
  }
18721
+ const direction = this.getAttribute("direction");
18722
+ if (direction === "vertical") {
18723
+ this.setAttribute("aria-orientation", "vertical");
18724
+ } else if (!this.hasAttribute("aria-orientation")) {
18725
+ this.setAttribute("aria-orientation", "horizontal");
18726
+ }
18604
18727
  const label = this.label.trim();
18605
18728
  if (label) this.setAttribute("aria-label", label);
18606
18729
  else this.removeAttribute("aria-label");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "8.5.0",
3
+ "version": "8.6.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",