@agentero/design-system 0.5.1 → 0.7.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.
@@ -576,13 +576,13 @@
576
576
  <header>
577
577
  <div class="wrap">
578
578
  <h1>Manifest Debugger</h1>
579
- <div class="summary"><a class="filter-pill all" data-k="all" href="#filter-all">All</a><span class="filter-pill ok" aria-disabled="true">6 components ok</span><span class="filter-pill ok" aria-disabled="true">79 stories ok</span></div>
579
+ <div class="summary"><a class="filter-pill all" data-k="all" href="#filter-all">All</a><span class="filter-pill ok" aria-disabled="true">8 components ok</span><span class="filter-pill ok" aria-disabled="true">97 stories ok</span></div>
580
580
  </div>
581
581
  </header>
582
582
  <main>
583
583
  <div class="wrap">
584
584
  <div class="note ok" style="margin-bottom: 16px;">
585
- Using <code>react-docgen-typescript</code>. Generation took <strong>2.0s</strong>.
585
+ Using <code>react-docgen-typescript</code>. Generation took <strong>2.1s</strong>.
586
586
  </div>
587
587
  <h2 class="section-title">Components</h2>
588
588
  <div class="grid" role="list">
@@ -2122,6 +2122,390 @@ label?: ReactNode</code></pre>
2122
2122
 
2123
2123
  </div>
2124
2124
  </article>
2125
+ <article
2126
+ class="card
2127
+ no-error
2128
+ no-info
2129
+ no-story-error
2130
+ no-doc-error"
2131
+ role="listitem"
2132
+ aria-label="DropdownMenu.Root">
2133
+ <div class="head">
2134
+ <div class="title">
2135
+ <h2><span class="status-dot dot-ok"></span> DropdownMenu.Root</h2>
2136
+ <div class="badges">
2137
+
2138
+
2139
+ <label for="c-4-components-dropdownmenu-stories" class="badge ok as-toggle">7 stories</label>
2140
+
2141
+ </div>
2142
+ </div>
2143
+ <div class="meta" title="./src/dropdown-menu/dropdown-menu.stories.tsx">components-dropdownmenu · ./src/dropdown-menu/dropdown-menu.stories.tsx</div>
2144
+
2145
+ <div class="hint">DropdownMenu is a compound overlay for secondary actions, bulk operations, or navigation shortcuts triggered off a button or icon. Compose it from `Root` / `Trigger` / `Portal` / `Content` and fill the menu with `Item`, `Separator`, `Label`, and — for nested flows — `Sub` / `SubTrigger` / `SubContent`. Built on Radix UI&#39;s DropdownMenu primitive, so keyboard navigation, focus trapping, typeahead, and collision detection come for free.</div>
2146
+
2147
+ </div>
2148
+
2149
+ <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
2150
+
2151
+
2152
+ <input id="c-4-components-dropdownmenu-stories" class="tg tg-stories" type="checkbox" hidden />
2153
+
2154
+
2155
+
2156
+ <div class="panels">
2157
+
2158
+
2159
+
2160
+
2161
+ <div class="panel panel-stories">
2162
+
2163
+
2164
+
2165
+ <div class="note ok">
2166
+ <div class="row">
2167
+ <span class="ex-name">Imports</span>
2168
+ </div>
2169
+ <pre><code>import { Button, DropdownMenu } from "@agentero/design-system";</code></pre>
2170
+ </div>
2171
+
2172
+
2173
+ <div class="note ok">
2174
+ <div class="row">
2175
+ <span class="ex-name">Default</span>
2176
+ <span class="badge ok">story ok</span>
2177
+ </div>
2178
+ <div>Basic menu with item rows and a separated destructive action</div>
2179
+ <div class="hint">A minimal DropdownMenu with four `Item`s — edit/duplicate/archive plus a separator before a destructive delete. Use this as the starting point for most overflow menus.</div>
2180
+ <pre><code>const Default = () =&gt; (
2181
+ &lt;DropdownMenu.Root&gt;
2182
+ &lt;DropdownMenu.Trigger asChild&gt;
2183
+ &lt;Button size=&quot;md&quot; variant=&quot;secondary&quot;&gt;
2184
+ Click me
2185
+ &lt;IconKeyboardArrowDown /&gt;
2186
+ &lt;/Button&gt;
2187
+ &lt;/DropdownMenu.Trigger&gt;
2188
+ &lt;DropdownMenu.Portal&gt;
2189
+ &lt;DropdownMenu.Content&gt;
2190
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Edit clicked&#39;)}&gt;Edit&lt;/DropdownMenu.Item&gt;
2191
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Duplicate clicked&#39;)}&gt;
2192
+ Duplicate
2193
+ &lt;/DropdownMenu.Item&gt;
2194
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Archive clicked&#39;)}&gt;
2195
+ Archive
2196
+ &lt;/DropdownMenu.Item&gt;
2197
+ &lt;DropdownMenu.Separator /&gt;
2198
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Delete clicked&#39;)}&gt;
2199
+ &lt;span className=&quot;text-red-500&quot;&gt;Delete&lt;/span&gt;
2200
+ &lt;/DropdownMenu.Item&gt;
2201
+ &lt;/DropdownMenu.Content&gt;
2202
+ &lt;/DropdownMenu.Portal&gt;
2203
+ &lt;/DropdownMenu.Root&gt;
2204
+ );</code></pre>
2205
+ </div>
2206
+ <div class="note ok">
2207
+ <div class="row">
2208
+ <span class="ex-name">With Groups And Dividers</span>
2209
+ <span class="badge ok">story ok</span>
2210
+ </div>
2211
+ <div>Menu with multiple `Separator`-split groups and a disabled item</div>
2212
+ <div class="hint">Multiple clusters of `Item`s split by `Separator` rules — mirrors an &quot;edit history / clipboard / export&quot; overflow menu. Demonstrates the `disabled` state on a menu row.</div>
2213
+ <pre><code>const WithGroupsAndDividers = () =&gt; (
2214
+ &lt;DropdownMenu.Root&gt;
2215
+ &lt;DropdownMenu.Trigger asChild&gt;
2216
+ &lt;Button size=&quot;md&quot; variant=&quot;secondary&quot;&gt;
2217
+ More Options
2218
+ &lt;IconKeyboardArrowDown /&gt;
2219
+ &lt;/Button&gt;
2220
+ &lt;/DropdownMenu.Trigger&gt;
2221
+ &lt;DropdownMenu.Portal&gt;
2222
+ &lt;DropdownMenu.Content&gt;
2223
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Undo&#39;)}&gt;Undo&lt;/DropdownMenu.Item&gt;
2224
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Redo&#39;)}&gt;Redo&lt;/DropdownMenu.Item&gt;
2225
+ &lt;DropdownMenu.Separator /&gt;
2226
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Copy&#39;)}&gt;Copy&lt;/DropdownMenu.Item&gt;
2227
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Cut&#39;)}&gt;Cut&lt;/DropdownMenu.Item&gt;
2228
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Paste&#39;)}&gt;Paste&lt;/DropdownMenu.Item&gt;
2229
+ &lt;DropdownMenu.Separator /&gt;
2230
+ &lt;DropdownMenu.Item disabled&gt;Print (disabled)&lt;/DropdownMenu.Item&gt;
2231
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Export&#39;)}&gt;Export&lt;/DropdownMenu.Item&gt;
2232
+ &lt;/DropdownMenu.Content&gt;
2233
+ &lt;/DropdownMenu.Portal&gt;
2234
+ &lt;/DropdownMenu.Root&gt;
2235
+ );</code></pre>
2236
+ </div>
2237
+ <div class="note ok">
2238
+ <div class="row">
2239
+ <span class="ex-name">With Icons</span>
2240
+ <span class="badge ok">story ok</span>
2241
+ </div>
2242
+ <div>Icon-leading items with a destructive delete row</div>
2243
+ <div class="hint">Menu items with leading icons and a destructive row using the `data-[highlighted]:bg-red-100` + `text-red-500` escape hatch. Use this pattern when a row should read as destructive.</div>
2244
+ <pre><code>const WithIcons = () =&gt; (
2245
+ &lt;DropdownMenu.Root&gt;
2246
+ &lt;DropdownMenu.Trigger asChild&gt;
2247
+ &lt;Button size=&quot;md&quot; variant=&quot;secondary&quot;&gt;
2248
+ Actions
2249
+ &lt;IconKeyboardArrowDown /&gt;
2250
+ &lt;/Button&gt;
2251
+ &lt;/DropdownMenu.Trigger&gt;
2252
+ &lt;DropdownMenu.Portal&gt;
2253
+ &lt;DropdownMenu.Content&gt;
2254
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Copy&#39;)}&gt;
2255
+ &lt;IconContentCopy /&gt;
2256
+ Copy
2257
+ &lt;/DropdownMenu.Item&gt;
2258
+ &lt;DropdownMenu.Separator /&gt;
2259
+ &lt;DropdownMenu.Item
2260
+ onSelect={() =&gt; console.log(&#39;Delete&#39;)}
2261
+ className=&quot;data-[highlighted]:bg-red-100&quot;&gt;
2262
+ &lt;IconDelete className=&quot;[&amp;&gt;path]:fill-red-500&quot; /&gt;
2263
+ &lt;span className=&quot;text-red-500&quot;&gt;Delete&lt;/span&gt;
2264
+ &lt;/DropdownMenu.Item&gt;
2265
+ &lt;/DropdownMenu.Content&gt;
2266
+ &lt;/DropdownMenu.Portal&gt;
2267
+ &lt;/DropdownMenu.Root&gt;
2268
+ );</code></pre>
2269
+ </div>
2270
+ <div class="note ok">
2271
+ <div class="row">
2272
+ <span class="ex-name">With Group Titles</span>
2273
+ <span class="badge ok">story ok</span>
2274
+ </div>
2275
+ <div>Menu with `Label`-titled sections and a destructive logout row</div>
2276
+ <div class="hint">`Label`-segmented Profile / Settings / Account sections with a destructive Logout row. Use `Label` to name groups that would otherwise be ambiguous from the item text alone.</div>
2277
+ <pre><code>const WithGroupTitles = () =&gt; (
2278
+ &lt;DropdownMenu.Root&gt;
2279
+ &lt;DropdownMenu.Trigger asChild&gt;
2280
+ &lt;Button size=&quot;md&quot; variant=&quot;secondary&quot;&gt;
2281
+ Account Menu
2282
+ &lt;IconKeyboardArrowDown /&gt;
2283
+ &lt;/Button&gt;
2284
+ &lt;/DropdownMenu.Trigger&gt;
2285
+ &lt;DropdownMenu.Portal&gt;
2286
+ &lt;DropdownMenu.Content&gt;
2287
+ &lt;DropdownMenu.Label&gt;Profile&lt;/DropdownMenu.Label&gt;
2288
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;View Profile&#39;)}&gt;
2289
+ View Profile
2290
+ &lt;/DropdownMenu.Item&gt;
2291
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Edit Profile&#39;)}&gt;
2292
+ Edit Profile
2293
+ &lt;/DropdownMenu.Item&gt;
2294
+ &lt;DropdownMenu.Separator /&gt;
2295
+ &lt;DropdownMenu.Label&gt;Settings&lt;/DropdownMenu.Label&gt;
2296
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Preferences&#39;)}&gt;
2297
+ Preferences
2298
+ &lt;/DropdownMenu.Item&gt;
2299
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Privacy&#39;)}&gt;Privacy&lt;/DropdownMenu.Item&gt;
2300
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Notifications&#39;)}&gt;
2301
+ Notifications
2302
+ &lt;/DropdownMenu.Item&gt;
2303
+ &lt;DropdownMenu.Separator /&gt;
2304
+ &lt;DropdownMenu.Label&gt;Account&lt;/DropdownMenu.Label&gt;
2305
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Billing&#39;)}&gt;Billing&lt;/DropdownMenu.Item&gt;
2306
+ &lt;DropdownMenu.Item
2307
+ onSelect={() =&gt; console.log(&#39;Logout&#39;)}
2308
+ className=&quot;data-[highlighted]:bg-red-100&quot;&gt;
2309
+ &lt;span className=&quot;text-red-500&quot;&gt;Logout&lt;/span&gt;
2310
+ &lt;/DropdownMenu.Item&gt;
2311
+ &lt;/DropdownMenu.Content&gt;
2312
+ &lt;/DropdownMenu.Portal&gt;
2313
+ &lt;/DropdownMenu.Root&gt;
2314
+ );</code></pre>
2315
+ </div>
2316
+ <div class="note ok">
2317
+ <div class="row">
2318
+ <span class="ex-name">With Submenu</span>
2319
+ <span class="badge ok">story ok</span>
2320
+ </div>
2321
+ <div>Single-level submenus for Share and Move-to flows</div>
2322
+ <div class="hint">One level of nesting: Share and Move-to submenus wired via `Sub` / `SubTrigger` / `SubContent`. Each submenu is portaled independently so it escapes the parent&#39;s stacking context.</div>
2323
+ <pre><code>const WithSubmenu = () =&gt; (
2324
+ &lt;DropdownMenu.Root&gt;
2325
+ &lt;DropdownMenu.Trigger asChild&gt;
2326
+ &lt;Button size=&quot;md&quot; variant=&quot;secondary&quot;&gt;
2327
+ File Options
2328
+ &lt;IconKeyboardArrowDown /&gt;
2329
+ &lt;/Button&gt;
2330
+ &lt;/DropdownMenu.Trigger&gt;
2331
+ &lt;DropdownMenu.Portal&gt;
2332
+ &lt;DropdownMenu.Content&gt;
2333
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;New File&#39;)}&gt;New File&lt;/DropdownMenu.Item&gt;
2334
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Open&#39;)}&gt;Open&lt;/DropdownMenu.Item&gt;
2335
+ &lt;DropdownMenu.Separator /&gt;
2336
+ &lt;DropdownMenu.Sub&gt;
2337
+ &lt;DropdownMenu.SubTrigger&gt;
2338
+ &lt;IconIosShare /&gt;
2339
+ Share
2340
+ &lt;/DropdownMenu.SubTrigger&gt;
2341
+ &lt;DropdownMenu.Portal&gt;
2342
+ &lt;DropdownMenu.SubContent&gt;
2343
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Email&#39;)}&gt;Email&lt;/DropdownMenu.Item&gt;
2344
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Slack&#39;)}&gt;Slack&lt;/DropdownMenu.Item&gt;
2345
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Copy Link&#39;)}&gt;
2346
+ Copy Link
2347
+ &lt;/DropdownMenu.Item&gt;
2348
+ &lt;/DropdownMenu.SubContent&gt;
2349
+ &lt;/DropdownMenu.Portal&gt;
2350
+ &lt;/DropdownMenu.Sub&gt;
2351
+ &lt;DropdownMenu.Sub&gt;
2352
+ &lt;DropdownMenu.SubTrigger&gt;
2353
+ &lt;IconDriveFileMove /&gt;
2354
+ Move to
2355
+ &lt;/DropdownMenu.SubTrigger&gt;
2356
+ &lt;DropdownMenu.Portal&gt;
2357
+ &lt;DropdownMenu.SubContent&gt;
2358
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Archive&#39;)}&gt;
2359
+ Archive
2360
+ &lt;/DropdownMenu.Item&gt;
2361
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Projects&#39;)}&gt;
2362
+ Projects
2363
+ &lt;/DropdownMenu.Item&gt;
2364
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Drafts&#39;)}&gt;Drafts&lt;/DropdownMenu.Item&gt;
2365
+ &lt;/DropdownMenu.SubContent&gt;
2366
+ &lt;/DropdownMenu.Portal&gt;
2367
+ &lt;/DropdownMenu.Sub&gt;
2368
+ &lt;DropdownMenu.Separator /&gt;
2369
+ &lt;DropdownMenu.Item
2370
+ onSelect={() =&gt; console.log(&#39;Delete&#39;)}
2371
+ className=&quot;data-[highlighted]:bg-red-100&quot;&gt;
2372
+ &lt;IconDelete className=&quot;[&amp;&gt;path]:fill-red-500&quot; /&gt;
2373
+ &lt;span className=&quot;text-red-500&quot;&gt;Delete&lt;/span&gt;
2374
+ &lt;/DropdownMenu.Item&gt;
2375
+ &lt;/DropdownMenu.Content&gt;
2376
+ &lt;/DropdownMenu.Portal&gt;
2377
+ &lt;/DropdownMenu.Root&gt;
2378
+ );</code></pre>
2379
+ </div>
2380
+ <div class="note ok">
2381
+ <div class="row">
2382
+ <span class="ex-name">Nested Submenus</span>
2383
+ <span class="badge ok">story ok</span>
2384
+ </div>
2385
+ <div>Two-level nested submenus inside a parent menu</div>
2386
+ <div class="hint">Two levels of nesting: Organization → Teams → (Engineering, Design). Deeper nesting is rarely a good UX — prefer splitting into separate screens once the tree grows past two levels.</div>
2387
+ <pre><code>const NestedSubmenus = () =&gt; (
2388
+ &lt;DropdownMenu.Root&gt;
2389
+ &lt;DropdownMenu.Trigger asChild&gt;
2390
+ &lt;Button size=&quot;md&quot; variant=&quot;secondary&quot;&gt;
2391
+ Organization Menu
2392
+ &lt;IconKeyboardArrowDown /&gt;
2393
+ &lt;/Button&gt;
2394
+ &lt;/DropdownMenu.Trigger&gt;
2395
+ &lt;DropdownMenu.Portal&gt;
2396
+ &lt;DropdownMenu.Content&gt;
2397
+ &lt;DropdownMenu.Label&gt;Organization&lt;/DropdownMenu.Label&gt;
2398
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Dashboard&#39;)}&gt;Dashboard&lt;/DropdownMenu.Item&gt;
2399
+ &lt;DropdownMenu.Sub&gt;
2400
+ &lt;DropdownMenu.SubTrigger&gt;
2401
+ &lt;IconPeople /&gt;
2402
+ Teams
2403
+ &lt;/DropdownMenu.SubTrigger&gt;
2404
+ &lt;DropdownMenu.Portal&gt;
2405
+ &lt;DropdownMenu.SubContent&gt;
2406
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;View All Teams&#39;)}&gt;
2407
+ View All Teams
2408
+ &lt;/DropdownMenu.Item&gt;
2409
+ &lt;DropdownMenu.Separator /&gt;
2410
+ &lt;DropdownMenu.Sub&gt;
2411
+ &lt;DropdownMenu.SubTrigger&gt;Engineering&lt;/DropdownMenu.SubTrigger&gt;
2412
+ &lt;DropdownMenu.Portal&gt;
2413
+ &lt;DropdownMenu.SubContent&gt;
2414
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Frontend&#39;)}&gt;
2415
+ Frontend
2416
+ &lt;/DropdownMenu.Item&gt;
2417
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Backend&#39;)}&gt;
2418
+ Backend
2419
+ &lt;/DropdownMenu.Item&gt;
2420
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;DevOps&#39;)}&gt;
2421
+ DevOps
2422
+ &lt;/DropdownMenu.Item&gt;
2423
+ &lt;/DropdownMenu.SubContent&gt;
2424
+ &lt;/DropdownMenu.Portal&gt;
2425
+ &lt;/DropdownMenu.Sub&gt;
2426
+ &lt;DropdownMenu.Sub&gt;
2427
+ &lt;DropdownMenu.SubTrigger&gt;Design&lt;/DropdownMenu.SubTrigger&gt;
2428
+ &lt;DropdownMenu.Portal&gt;
2429
+ &lt;DropdownMenu.SubContent&gt;
2430
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Product Design&#39;)}&gt;
2431
+ Product Design
2432
+ &lt;/DropdownMenu.Item&gt;
2433
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Brand&#39;)}&gt;
2434
+ Brand
2435
+ &lt;/DropdownMenu.Item&gt;
2436
+ &lt;/DropdownMenu.SubContent&gt;
2437
+ &lt;/DropdownMenu.Portal&gt;
2438
+ &lt;/DropdownMenu.Sub&gt;
2439
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Marketing&#39;)}&gt;
2440
+ Marketing
2441
+ &lt;/DropdownMenu.Item&gt;
2442
+ &lt;/DropdownMenu.SubContent&gt;
2443
+ &lt;/DropdownMenu.Portal&gt;
2444
+ &lt;/DropdownMenu.Sub&gt;
2445
+ &lt;DropdownMenu.Separator /&gt;
2446
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Settings&#39;)}&gt;Settings&lt;/DropdownMenu.Item&gt;
2447
+ &lt;/DropdownMenu.Content&gt;
2448
+ &lt;/DropdownMenu.Portal&gt;
2449
+ &lt;/DropdownMenu.Root&gt;
2450
+ );</code></pre>
2451
+ </div>
2452
+ <div class="note ok">
2453
+ <div class="row">
2454
+ <span class="ex-name">Submenu With Disabled Items</span>
2455
+ <span class="badge ok">story ok</span>
2456
+ </div>
2457
+ <div>Submenu with `disabled` items and a disabled `SubTrigger`</div>
2458
+ <div class="hint">Submenu that contains `disabled` `Item`s alongside a fully-disabled `SubTrigger`. Use `disabled` sparingly — prefer hiding unavailable actions outright unless the user needs to see them and understand why.</div>
2459
+ <pre><code>const SubmenuWithDisabledItems = () =&gt; (
2460
+ &lt;DropdownMenu.Root&gt;
2461
+ &lt;DropdownMenu.Trigger asChild&gt;
2462
+ &lt;Button size=&quot;md&quot; variant=&quot;secondary&quot;&gt;
2463
+ Document Actions
2464
+ &lt;IconKeyboardArrowDown /&gt;
2465
+ &lt;/Button&gt;
2466
+ &lt;/DropdownMenu.Trigger&gt;
2467
+ &lt;DropdownMenu.Portal&gt;
2468
+ &lt;DropdownMenu.Content&gt;
2469
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;View&#39;)}&gt;View&lt;/DropdownMenu.Item&gt;
2470
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Edit&#39;)}&gt;Edit&lt;/DropdownMenu.Item&gt;
2471
+ &lt;DropdownMenu.Separator /&gt;
2472
+ &lt;DropdownMenu.Sub&gt;
2473
+ &lt;DropdownMenu.SubTrigger&gt;
2474
+ &lt;IconContentCopy /&gt;
2475
+ Export as
2476
+ &lt;/DropdownMenu.SubTrigger&gt;
2477
+ &lt;DropdownMenu.Portal&gt;
2478
+ &lt;DropdownMenu.SubContent&gt;
2479
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;PDF&#39;)}&gt;PDF&lt;/DropdownMenu.Item&gt;
2480
+ &lt;DropdownMenu.Item onSelect={() =&gt; console.log(&#39;Word&#39;)}&gt;
2481
+ Word Document
2482
+ &lt;/DropdownMenu.Item&gt;
2483
+ &lt;DropdownMenu.Item disabled&gt;Excel (Premium only)&lt;/DropdownMenu.Item&gt;
2484
+ &lt;DropdownMenu.Item disabled&gt;PowerPoint (Premium only)&lt;/DropdownMenu.Item&gt;
2485
+ &lt;/DropdownMenu.SubContent&gt;
2486
+ &lt;/DropdownMenu.Portal&gt;
2487
+ &lt;/DropdownMenu.Sub&gt;
2488
+ &lt;DropdownMenu.Sub&gt;
2489
+ &lt;DropdownMenu.SubTrigger disabled&gt;
2490
+ &lt;IconIosShare /&gt;
2491
+ Share (Sign in required)
2492
+ &lt;/DropdownMenu.SubTrigger&gt;
2493
+ &lt;DropdownMenu.Portal&gt;
2494
+ &lt;DropdownMenu.SubContent&gt;
2495
+ &lt;DropdownMenu.Item&gt;Email&lt;/DropdownMenu.Item&gt;
2496
+ &lt;DropdownMenu.Item&gt;Link&lt;/DropdownMenu.Item&gt;
2497
+ &lt;/DropdownMenu.SubContent&gt;
2498
+ &lt;/DropdownMenu.Portal&gt;
2499
+ &lt;/DropdownMenu.Sub&gt;
2500
+ &lt;/DropdownMenu.Content&gt;
2501
+ &lt;/DropdownMenu.Portal&gt;
2502
+ &lt;/DropdownMenu.Root&gt;
2503
+ );</code></pre>
2504
+ </div>
2505
+ </div>
2506
+
2507
+ </div>
2508
+ </article>
2125
2509
  <article
2126
2510
  class="card
2127
2511
  no-error
@@ -2134,9 +2518,9 @@ label?: ReactNode</code></pre>
2134
2518
  <div class="title">
2135
2519
  <h2><span class="status-dot dot-ok"></span> Loading</h2>
2136
2520
  <div class="badges">
2137
- <label for="c-4-components-loading-props" class="badge ok as-toggle">1 prop type</label>
2521
+ <label for="c-5-components-loading-props" class="badge ok as-toggle">1 prop type</label>
2138
2522
 
2139
- <label for="c-4-components-loading-stories" class="badge ok as-toggle">7 stories</label>
2523
+ <label for="c-5-components-loading-stories" class="badge ok as-toggle">7 stories</label>
2140
2524
 
2141
2525
  </div>
2142
2526
  </div>
@@ -2149,9 +2533,9 @@ label?: ReactNode</code></pre>
2149
2533
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
2150
2534
 
2151
2535
 
2152
- <input id="c-4-components-loading-stories" class="tg tg-stories" type="checkbox" hidden />
2536
+ <input id="c-5-components-loading-stories" class="tg tg-stories" type="checkbox" hidden />
2153
2537
 
2154
- <input id="c-4-components-loading-props" class="tg tg-props" type="checkbox" hidden />
2538
+ <input id="c-5-components-loading-props" class="tg tg-props" type="checkbox" hidden />
2155
2539
 
2156
2540
  <div class="panels">
2157
2541
 
@@ -2293,7 +2677,7 @@ size?: &quot;sm&quot; | &quot;md&quot; | &quot;lg&quot;</code></pre>
2293
2677
  <div class="badges">
2294
2678
 
2295
2679
 
2296
- <label for="c-5-design-system-feedback-statusball-stories" class="badge ok as-toggle">12 stories</label>
2680
+ <label for="c-6-design-system-feedback-statusball-stories" class="badge ok as-toggle">12 stories</label>
2297
2681
 
2298
2682
  </div>
2299
2683
  </div>
@@ -2306,7 +2690,7 @@ size?: &quot;sm&quot; | &quot;md&quot; | &quot;lg&quot;</code></pre>
2306
2690
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
2307
2691
 
2308
2692
 
2309
- <input id="c-5-design-system-feedback-statusball-stories" class="tg tg-stories" type="checkbox" hidden />
2693
+ <input id="c-6-design-system-feedback-statusball-stories" class="tg tg-stories" type="checkbox" hidden />
2310
2694
 
2311
2695
 
2312
2696
 
@@ -2497,6 +2881,432 @@ size?: &quot;sm&quot; | &quot;md&quot; | &quot;lg&quot;</code></pre>
2497
2881
  </div>
2498
2882
  </div>
2499
2883
 
2884
+ </div>
2885
+ </article>
2886
+ <article
2887
+ class="card
2888
+ no-error
2889
+ no-info
2890
+ no-story-error
2891
+ no-doc-error"
2892
+ role="listitem"
2893
+ aria-label="Toast">
2894
+ <div class="head">
2895
+ <div class="title">
2896
+ <h2><span class="status-dot dot-ok"></span> Toast</h2>
2897
+ <div class="badges">
2898
+ <label for="c-7-components-toast-props" class="badge ok as-toggle">11 prop types</label>
2899
+
2900
+ <label for="c-7-components-toast-stories" class="badge ok as-toggle">11 stories</label>
2901
+
2902
+ </div>
2903
+ </div>
2904
+ <div class="meta" title="./src/toast/toast.stories.tsx">components-toast · ./src/toast/toast.stories.tsx</div>
2905
+
2906
+ <div class="hint">Toast renders transient feedback about an outcome — saved, failed, warning, info — without interrupting the user&#39;s flow. Mount `&lt;Toast /&gt;` once near the app root to register the provider, then call the imperative `toast()` API from anywhere in the tree. Five semantic types (`neutral`, `success`, `error`, `warning`, `info`) × two layouts (`inline`, `expanded`) cover the common feedback patterns.</div>
2907
+
2908
+ </div>
2909
+
2910
+ <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
2911
+
2912
+
2913
+ <input id="c-7-components-toast-stories" class="tg tg-stories" type="checkbox" hidden />
2914
+
2915
+ <input id="c-7-components-toast-props" class="tg tg-props" type="checkbox" hidden />
2916
+
2917
+ <div class="panels">
2918
+
2919
+
2920
+
2921
+ <div class="panel panel-props">
2922
+ <div class="note ok">
2923
+ <div class="row">
2924
+ <span class="ex-name">Prop types <small>(react-docgen-typescript)</small></span>
2925
+ <span class="badge ok">11 prop types</span>
2926
+ </div>
2927
+ <pre><code>Component: src/toast/toast.tsx::Toast</code></pre>
2928
+ <pre><code>Props:</code></pre>
2929
+ <pre><code>/**
2930
+ * Additional className merged onto the sonner `&lt;Toaster&gt;` root.
2931
+ */
2932
+ className?: string
2933
+
2934
+ /**
2935
+ * Text direction. Sonner forwards this to the rendered toaster root.
2936
+ */
2937
+ dir?: &quot;auto&quot; | &quot;ltr&quot; | &quot;rtl&quot;
2938
+
2939
+ /**
2940
+ * Default auto-dismiss duration in ms for toasts fired against this provider. Defaults to `5000`.
2941
+ */
2942
+ duration?: number = 5000
2943
+
2944
+ /**
2945
+ * When `true`, stack expands on hover instead of auto-collapsing.
2946
+ */
2947
+ expand?: boolean
2948
+
2949
+ /**
2950
+ * Gap between stacked toasts, in pixels.
2951
+ */
2952
+ gap?: number
2953
+
2954
+ /**
2955
+ * Keyboard shortcut used to focus the toaster.
2956
+ */
2957
+ hotkey?: string[]
2958
+
2959
+ /**
2960
+ * When `true`, flips light/dark theme on the toaster root.
2961
+ */
2962
+ invert?: boolean
2963
+
2964
+ /**
2965
+ * Offset from the screen edge, in pixels or as a CSS length.
2966
+ */
2967
+ offset?: string | number
2968
+
2969
+ /**
2970
+ * Placement of the toaster on the screen. Defaults to `&#39;bottom-right&#39;`.
2971
+ */
2972
+ position?: ToastPosition = bottom-right
2973
+
2974
+ /**
2975
+ * When `true`, sonner paints colored backgrounds per type (DS uses its own icon tokens by default).
2976
+ */
2977
+ richColors?: boolean
2978
+
2979
+ /**
2980
+ * Maximum number of toasts rendered at once before earlier ones dismiss.
2981
+ */
2982
+ visibleToasts?: number</code></pre>
2983
+ </div>
2984
+ </div>
2985
+
2986
+ <div class="panel panel-stories">
2987
+
2988
+
2989
+
2990
+ <div class="note ok">
2991
+ <div class="row">
2992
+ <span class="ex-name">Imports</span>
2993
+ </div>
2994
+ <pre><code>import { Button, Toast } from "@agentero/design-system";</code></pre>
2995
+ </div>
2996
+
2997
+
2998
+ <div class="note ok">
2999
+ <div class="row">
3000
+ <span class="ex-name">Default</span>
3001
+ <span class="badge ok">story ok</span>
3002
+ </div>
3003
+ <div>Default playground for the Toast provider</div>
3004
+ <div class="hint">Args-controlled playground. Click the button to fire a neutral toast; tweak `position` and `duration` from the Controls panel to preview the provider defaults.</div>
3005
+ <pre><code>const Default = () =&gt; (
3006
+ &lt;div style={{ minHeight: &#39;240px&#39; }}&gt;
3007
+ &lt;Button variant=&quot;secondary&quot; onClick={() =&gt; toast(&#39;Hello from Toast&#39;)}&gt;
3008
+ Show toast
3009
+ &lt;/Button&gt;
3010
+ &lt;/div&gt;
3011
+ );</code></pre>
3012
+ </div>
3013
+ <div class="note ok">
3014
+ <div class="row">
3015
+ <span class="ex-name">Inline</span>
3016
+ <span class="badge ok">story ok</span>
3017
+ </div>
3018
+ <div>Inline layout across all five semantic types</div>
3019
+ <div class="hint">Inline layout (default). Compact treatment — title with an optional description stacked directly below it. The `type` drives the icon color. Use for short acknowledgments that don&#39;t need a colored rail.</div>
3020
+ <pre><code>const Inline = () =&gt; (
3021
+ &lt;div style={{ display: &#39;flex&#39;, flexWrap: &#39;wrap&#39;, gap: &#39;0.5rem&#39; }}&gt;
3022
+ &lt;Button
3023
+ variant=&quot;secondary&quot;
3024
+ onClick={() =&gt; toast(&#39;Something happened&#39;, { description: &#39;Additional context.&#39; })}&gt;
3025
+ Neutral
3026
+ &lt;/Button&gt;
3027
+ &lt;Button
3028
+ variant=&quot;secondary&quot;
3029
+ onClick={() =&gt; toast.success(&#39;Policy saved&#39;, { description: &#39;Your edits are live.&#39; })}&gt;
3030
+ Success
3031
+ &lt;/Button&gt;
3032
+ &lt;Button
3033
+ variant=&quot;secondary&quot;
3034
+ onClick={() =&gt;
3035
+ toast.error(&#39;Failed to save&#39;, { description: &#39;The file could not be processed.&#39; })
3036
+ }&gt;
3037
+ Error
3038
+ &lt;/Button&gt;
3039
+ &lt;Button
3040
+ variant=&quot;secondary&quot;
3041
+ onClick={() =&gt;
3042
+ toast.warning(&#39;License expiring soon&#39;, { description: &#39;Renew before end of month.&#39; })
3043
+ }&gt;
3044
+ Warning
3045
+ &lt;/Button&gt;
3046
+ &lt;Button
3047
+ variant=&quot;secondary&quot;
3048
+ onClick={() =&gt;
3049
+ toast.info(&#39;Sync in progress&#39;, { description: &#39;Please wait while we sync your data.&#39; })
3050
+ }&gt;
3051
+ Info
3052
+ &lt;/Button&gt;
3053
+ &lt;/div&gt;
3054
+ );</code></pre>
3055
+ </div>
3056
+ <div class="note ok">
3057
+ <div class="row">
3058
+ <span class="ex-name">Expanded</span>
3059
+ <span class="badge ok">story ok</span>
3060
+ </div>
3061
+ <div>Expanded layout across all five semantic types</div>
3062
+ <div class="hint">Expanded layout. Multi-line treatment with a 0.375rem colored left rail that tracks the toast&#39;s `type`. Use for feedback that benefits from a description on its own line or a trailing action button.</div>
3063
+ <pre><code>const Expanded = () =&gt; (
3064
+ &lt;div style={{ display: &#39;flex&#39;, flexWrap: &#39;wrap&#39;, gap: &#39;0.5rem&#39; }}&gt;
3065
+ &lt;Button
3066
+ variant=&quot;secondary&quot;
3067
+ onClick={() =&gt;
3068
+ toast(&#39;Something happened&#39;, {
3069
+ variant: &#39;expanded&#39;,
3070
+ description: &#39;Additional context for the user.&#39;
3071
+ })
3072
+ }&gt;
3073
+ Neutral
3074
+ &lt;/Button&gt;
3075
+ &lt;Button
3076
+ variant=&quot;secondary&quot;
3077
+ onClick={() =&gt;
3078
+ toast.success(&#39;Policy saved&#39;, {
3079
+ variant: &#39;expanded&#39;,
3080
+ description: &#39;Your changes have been saved successfully.&#39;
3081
+ })
3082
+ }&gt;
3083
+ Success
3084
+ &lt;/Button&gt;
3085
+ &lt;Button
3086
+ variant=&quot;secondary&quot;
3087
+ onClick={() =&gt;
3088
+ toast.error(&#39;Failed to save&#39;, {
3089
+ variant: &#39;expanded&#39;,
3090
+ description: &#39;The file could not be processed.&#39;
3091
+ })
3092
+ }&gt;
3093
+ Error
3094
+ &lt;/Button&gt;
3095
+ &lt;Button
3096
+ variant=&quot;secondary&quot;
3097
+ onClick={() =&gt;
3098
+ toast.warning(&#39;License expiring soon&#39;, {
3099
+ variant: &#39;expanded&#39;,
3100
+ description: &#39;Renew before the end of the month to avoid interruption.&#39;
3101
+ })
3102
+ }&gt;
3103
+ Warning
3104
+ &lt;/Button&gt;
3105
+ &lt;Button
3106
+ variant=&quot;secondary&quot;
3107
+ onClick={() =&gt;
3108
+ toast.info(&#39;Sync in progress&#39;, {
3109
+ variant: &#39;expanded&#39;,
3110
+ description: &#39;Please wait while we sync your data.&#39;
3111
+ })
3112
+ }&gt;
3113
+ Info
3114
+ &lt;/Button&gt;
3115
+ &lt;/div&gt;
3116
+ );</code></pre>
3117
+ </div>
3118
+ <div class="note ok">
3119
+ <div class="row">
3120
+ <span class="ex-name">With Action Object</span>
3121
+ <span class="badge ok">story ok</span>
3122
+ </div>
3123
+ <div>Toast with a structured action button</div>
3124
+ <div class="hint">Toast with a structured `action` object. Passing `{ label, onClick }` renders the built-in secondary Button; the handler runs and the toast auto-dismisses afterward.</div>
3125
+ <pre><code>const WithActionObject = () =&gt; (
3126
+ &lt;Button
3127
+ variant=&quot;secondary&quot;
3128
+ onClick={() =&gt;
3129
+ toast.error(&#39;Upload failed&#39;, {
3130
+ variant: &#39;expanded&#39;,
3131
+ description: &#39;The file could not be processed.&#39;,
3132
+ action: { label: &#39;Retry&#39;, onClick: () =&gt; {} }
3133
+ })
3134
+ }&gt;
3135
+ Show toast with action
3136
+ &lt;/Button&gt;
3137
+ );</code></pre>
3138
+ </div>
3139
+ <div class="note ok">
3140
+ <div class="row">
3141
+ <span class="ex-name">With Action Node</span>
3142
+ <span class="badge ok">story ok</span>
3143
+ </div>
3144
+ <div>Toast with a custom ReactNode action</div>
3145
+ <div class="hint">Toast with a raw-ReactNode `action`. Pass any JSX to render a custom trailing control — a link, a pair of buttons, etc. — when the default secondary Button doesn&#39;t fit.</div>
3146
+ <pre><code>const WithActionNode = () =&gt; (
3147
+ &lt;Button
3148
+ variant=&quot;secondary&quot;
3149
+ onClick={() =&gt;
3150
+ toast(&#39;File uploaded&#39;, {
3151
+ description: &#39;document.pdf is ready.&#39;,
3152
+ action: (
3153
+ &lt;Button variant=&quot;link&quot; onClick={() =&gt; {}}&gt;
3154
+ View file
3155
+ &lt;/Button&gt;
3156
+ )
3157
+ })
3158
+ }&gt;
3159
+ Show toast with link action
3160
+ &lt;/Button&gt;
3161
+ );</code></pre>
3162
+ </div>
3163
+ <div class="note ok">
3164
+ <div class="row">
3165
+ <span class="ex-name">With Action And Cancel</span>
3166
+ <span class="badge ok">story ok</span>
3167
+ </div>
3168
+ <div>Toast with an action and a cancel control</div>
3169
+ <div class="hint">Toast with both `action` and `cancel`. Cancel renders as a ghost Button next to the action; its handler runs and the toast auto-dismisses.</div>
3170
+ <pre><code>const WithActionAndCancel = () =&gt; (
3171
+ &lt;Button
3172
+ variant=&quot;secondary&quot;
3173
+ onClick={() =&gt;
3174
+ toast.success(&#39;Record archived&#39;, {
3175
+ variant: &#39;expanded&#39;,
3176
+ description: &#39;The record is hidden from the default view.&#39;,
3177
+ action: { label: &#39;Undo&#39;, onClick: () =&gt; {} },
3178
+ cancel: { label: &#39;Dismiss&#39;, onClick: () =&gt; {} }
3179
+ })
3180
+ }&gt;
3181
+ Show toast with action + cancel
3182
+ &lt;/Button&gt;
3183
+ );</code></pre>
3184
+ </div>
3185
+ <div class="note ok">
3186
+ <div class="row">
3187
+ <span class="ex-name">Non Dismissible</span>
3188
+ <span class="badge ok">story ok</span>
3189
+ </div>
3190
+ <div>Non-dismissible toast (no close button)</div>
3191
+ <div class="hint">Non-dismissible toast. Setting `dismissible: false` hides the close button; the toast still auto-dismisses when its `duration` elapses, or programmatically via `toast.dismiss(id)`.</div>
3192
+ <pre><code>const NonDismissible = () =&gt; (
3193
+ &lt;Button
3194
+ variant=&quot;secondary&quot;
3195
+ onClick={() =&gt;
3196
+ toast.info(&#39;Sync in progress&#39;, {
3197
+ dismissible: false,
3198
+ description: &#39;Please wait while we sync your data.&#39;
3199
+ })
3200
+ }&gt;
3201
+ Show non-dismissible toast
3202
+ &lt;/Button&gt;
3203
+ );</code></pre>
3204
+ </div>
3205
+ <div class="note ok">
3206
+ <div class="row">
3207
+ <span class="ex-name">Long Duration</span>
3208
+ <span class="badge ok">story ok</span>
3209
+ </div>
3210
+ <div>Toast with a custom `duration`</div>
3211
+ <div class="hint">Long-duration toast. `duration` (in ms) overrides the provider default so long-running operations stay visible until they&#39;re relevant.</div>
3212
+ <pre><code>const LongDuration = () =&gt; (
3213
+ &lt;Button
3214
+ variant=&quot;secondary&quot;
3215
+ onClick={() =&gt;
3216
+ toast.info(&#39;Uploading documents&#39;, {
3217
+ description: &#39;This may take a moment.&#39;,
3218
+ duration: 10_000
3219
+ })
3220
+ }&gt;
3221
+ Show 10s toast
3222
+ &lt;/Button&gt;
3223
+ );</code></pre>
3224
+ </div>
3225
+ <div class="note ok">
3226
+ <div class="row">
3227
+ <span class="ex-name">Programmatic Dismiss</span>
3228
+ <span class="badge ok">story ok</span>
3229
+ </div>
3230
+ <div>Dismiss a toast programmatically via its id</div>
3231
+ <div class="hint">Programmatic dismissal. `toast()` returns the toast id, which you can pass to `toast.dismiss(id)` to clear it before its duration elapses.</div>
3232
+ <pre><code>const ProgrammaticDismiss = () =&gt; {
3233
+ let toastId: string | number | undefined;
3234
+ return (
3235
+ &lt;div style={{ display: &#39;flex&#39;, gap: &#39;0.5rem&#39; }}&gt;
3236
+ &lt;Button
3237
+ variant=&quot;secondary&quot;
3238
+ onClick={() =&gt; {
3239
+ toastId = toast.info(&#39;Uploading...&#39;, { duration: 60_000 });
3240
+ }}&gt;
3241
+ Start
3242
+ &lt;/Button&gt;
3243
+ &lt;Button
3244
+ variant=&quot;ghost&quot;
3245
+ onClick={() =&gt; {
3246
+ if (toastId !== undefined) toast.dismiss(toastId);
3247
+ }}&gt;
3248
+ Dismiss
3249
+ &lt;/Button&gt;
3250
+ &lt;/div&gt;
3251
+ );
3252
+ };</code></pre>
3253
+ </div>
3254
+ <div class="note ok">
3255
+ <div class="row">
3256
+ <span class="ex-name">With Promise</span>
3257
+ <span class="badge ok">story ok</span>
3258
+ </div>
3259
+ <div>Wire a toast to an async operation via `toast.promise`</div>
3260
+ <div class="hint">`toast.promise` wires a toast to an async operation — loading while the promise is pending, success or error when it settles. Useful for save/submit flows where the outcome feeds back into the same toast.</div>
3261
+ <pre><code>const WithPromise = () =&gt; {
3262
+ const fakeFetch = () =&gt;
3263
+ new Promise&lt;string&gt;((resolve, reject) =&gt; {
3264
+ window.setTimeout(
3265
+ () =&gt; (Math.random() &gt; 0.5 ? resolve(&#39;ok&#39;) : reject(new globalThis.Error(&#39;nope&#39;))),
3266
+ 1500
3267
+ );
3268
+ });
3269
+ return (
3270
+ &lt;Button
3271
+ variant=&quot;secondary&quot;
3272
+ onClick={() =&gt;
3273
+ toast.promise(fakeFetch(), {
3274
+ loading: &#39;Saving document…&#39;,
3275
+ success: &#39;Document saved&#39;,
3276
+ error: &#39;Save failed&#39;
3277
+ })
3278
+ }&gt;
3279
+ Run promise
3280
+ &lt;/Button&gt;
3281
+ );
3282
+ };</code></pre>
3283
+ </div>
3284
+ <div class="note ok">
3285
+ <div class="row">
3286
+ <span class="ex-name">Custom Icon</span>
3287
+ <span class="badge ok">story ok</span>
3288
+ </div>
3289
+ <div>Override the default type-based icon</div>
3290
+ <div class="hint">Custom `icon` override. Pass any ReactNode as `icon` to replace the type-based default — helpful when the toast reflects a domain-specific action (e.g., a sync icon for a sync-in-progress toast).</div>
3291
+ <pre><code>const CustomIcon = () =&gt; (
3292
+ &lt;Button
3293
+ variant=&quot;secondary&quot;
3294
+ onClick={() =&gt;
3295
+ toast.success(&#39;Sync started&#39;, {
3296
+ description: &#39;Data sync is in progress.&#39;,
3297
+ icon: (
3298
+ &lt;svg viewBox=&quot;0 0 24 24&quot; width=&quot;24&quot; height=&quot;24&quot; fill=&quot;currentColor&quot; aria-hidden&gt;
3299
+ &lt;path d=&quot;M12 4V1L8 5l4 4V6c3.3 0 6 2.7 6 6 0 1-.3 2-.7 2.8l1.5 1.5C19.5 15 20 13.6 20 12c0-4.4-3.6-8-8-8zm0 14c-3.3 0-6-2.7-6-6 0-1 .3-2 .7-2.8L5.2 7.7C4.5 9 4 10.4 4 12c0 4.4 3.6 8 8 8v3l4-4-4-4v3z&quot; /&gt;
3300
+ &lt;/svg&gt;
3301
+ )
3302
+ })
3303
+ }&gt;
3304
+ Show toast with custom icon
3305
+ &lt;/Button&gt;
3306
+ );</code></pre>
3307
+ </div>
3308
+ </div>
3309
+
2500
3310
  </div>
2501
3311
  </article>
2502
3312
  </div>