@adelalawdi/fnon2 2.0.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/index.html ADDED
@@ -0,0 +1,3888 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Fnon.js v2 &mdash; Playground &amp; Demos</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
+ <link
10
+ href="https://fonts.googleapis.com/css2?family=Quicksand:wght@400;500;600;700&display=swap"
11
+ rel="stylesheet"
12
+ />
13
+ <link rel="stylesheet" href="fnon.min.css" />
14
+ <style>
15
+ * {
16
+ margin: 0;
17
+ padding: 0;
18
+ box-sizing: border-box;
19
+ }
20
+ body {
21
+ font-family: "Quicksand", sans-serif;
22
+ background: #f5f7fa;
23
+ color: #222f3e;
24
+ }
25
+ .container {
26
+ max-width: 1100px;
27
+ margin: 0 auto;
28
+ padding: 2rem 1rem;
29
+ }
30
+ .header {
31
+ position: sticky;
32
+ top: 0px;
33
+ z-index: 9;
34
+ padding-bottom: 1rem;
35
+ padding-top: 1rem;
36
+ background: #f5f7fa;
37
+ }
38
+ [data-theme="dark"] .header {
39
+ background: #0f172a;
40
+ }
41
+ h1 {
42
+ font-size: 1.8rem;
43
+ display: flex;
44
+ align-items: center;
45
+ gap: 10px;
46
+ flex-wrap: wrap;
47
+ }
48
+ h1 small {
49
+ font-weight: 400;
50
+ font-size: 0.8rem;
51
+ color: #718096;
52
+ }
53
+ .sub {
54
+ color: #718096;
55
+ font-size: 0.9rem;
56
+ margin-bottom: 1.5rem;
57
+ }
58
+
59
+ section {
60
+ margin-bottom: 2rem;
61
+ }
62
+ section > h2 {
63
+ font-size: 1.2rem;
64
+ font-weight: 600;
65
+ margin-bottom: 0.2rem;
66
+ }
67
+ section > p.section-desc {
68
+ font-size: 0.82rem;
69
+ color: #718096;
70
+ margin-bottom: 0.75rem;
71
+ }
72
+
73
+ .demo-grid {
74
+ display: grid;
75
+ grid-template-columns: repeat(auto-fill, minmax(500px, 1fr));
76
+ gap: 1rem;
77
+ }
78
+ .demo-card {
79
+ background: #fff;
80
+ border-radius: 8px;
81
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
82
+ overflow: hidden;
83
+ }
84
+ .demo-card .card-body {
85
+ padding: 1rem;
86
+ }
87
+ .demo-card .card-actions {
88
+ display: flex;
89
+ flex-wrap: wrap;
90
+ gap: 0.4rem;
91
+ align-items: center;
92
+ }
93
+ .demo-card h3 {
94
+ font-size: 0.85rem;
95
+ font-weight: 600;
96
+ margin-bottom: 0.15rem;
97
+ }
98
+ .demo-card p.desc {
99
+ font-size: 0.73rem;
100
+ color: #718096;
101
+ margin-bottom: 0.5rem;
102
+ }
103
+
104
+ .demo-card button {
105
+ font-family: "Quicksand", sans-serif;
106
+ font-size: 0.75rem;
107
+ padding: 0.35rem 0.75rem;
108
+ border: 1px solid #e2e8f0;
109
+ border-radius: 6px;
110
+ background: #fff;
111
+ color: #2d3748;
112
+ cursor: pointer;
113
+ transition: all 0.15s;
114
+ }
115
+ .demo-card button:hover {
116
+ border-color: #029eff;
117
+ color: #029eff;
118
+ background: #ebf8ff;
119
+ }
120
+ .demo-card button:focus-visible {
121
+ outline: 2px solid #029eff;
122
+ outline-offset: 2px;
123
+ }
124
+ .demo-card button.pri {
125
+ background: #029eff;
126
+ color: #fff;
127
+ border-color: #029eff;
128
+ }
129
+ .demo-card button.pri:hover {
130
+ background: #0288e0;
131
+ }
132
+ .demo-card button.suc {
133
+ background: #39da8a;
134
+ color: #fff;
135
+ border-color: #39da8a;
136
+ }
137
+ .demo-card button.war {
138
+ background: #fdd347;
139
+ color: #222f3e;
140
+ border-color: #fdd347;
141
+ }
142
+ .demo-card button.dan {
143
+ background: #ff6b6b;
144
+ color: #fff;
145
+ border-color: #ff6b6b;
146
+ }
147
+ .demo-card button.inf {
148
+ background: #48dbfb;
149
+ color: #222f3e;
150
+ border-color: #48dbfb;
151
+ }
152
+ .demo-card button.drk {
153
+ background: #222f3e;
154
+ color: #fff;
155
+ border-color: #222f3e;
156
+ }
157
+ .demo-card button:disabled {
158
+ opacity: 0.5;
159
+ cursor: not-allowed;
160
+ }
161
+ .demo-card button.small {
162
+ font-size: 0.65rem;
163
+ padding: 0.25rem 0.5rem;
164
+ }
165
+
166
+ .code {
167
+ font-size: 0.65rem;
168
+ background: #1a202c;
169
+ color: #a0aec0;
170
+ padding: 0.5rem 0.6rem;
171
+ border-radius: 0 0 6px 6px;
172
+ overflow-x: auto;
173
+ line-height: 1.5;
174
+ font-family: "Consolas", "Courier New", monospace;
175
+ user-select: all;
176
+ border-top: 1px solid #2d3748;
177
+ white-space: pre-wrap;
178
+ }
179
+ .code .kw {
180
+ color: #63b3ed;
181
+ }
182
+ .code .str {
183
+ color: #9ae6b4;
184
+ }
185
+ .code .fn {
186
+ color: #f6ad55;
187
+ }
188
+ .code .cm {
189
+ color: #718096;
190
+ font-style: italic;
191
+ }
192
+ .code .num {
193
+ color: #fbbf24;
194
+ }
195
+ .code .pr {
196
+ color: #fc8181;
197
+ }
198
+
199
+ .ref-section {
200
+ background: #1a202c;
201
+ border-radius: 8px;
202
+ padding: 1rem;
203
+ margin-top: 0.5rem;
204
+ overflow-x: auto;
205
+ }
206
+ .ref-section h3 {
207
+ color: #e2e8f0;
208
+ font-size: 0.85rem;
209
+ margin-bottom: 0.5rem;
210
+ }
211
+ .ref-section pre {
212
+ font-family: "Consolas", "Courier New", monospace;
213
+ font-size: 0.72rem;
214
+ color: #a0aec0;
215
+ line-height: 1.6;
216
+ white-space: pre-wrap;
217
+ }
218
+ .ref-table {
219
+ width: 100%;
220
+ border-collapse: collapse;
221
+ font-size: 0.72rem;
222
+ font-family: "Consolas", "Courier New", monospace;
223
+ }
224
+ .ref-table th {
225
+ color: #e2e8f0;
226
+ text-align: left;
227
+ padding: 6px 8px;
228
+ border-bottom: 1px solid #2d3748;
229
+ font-size: 0.7rem;
230
+ text-transform: uppercase;
231
+ letter-spacing: 0.05em;
232
+ }
233
+ .ref-table td {
234
+ color: #a0aec0;
235
+ padding: 5px 8px;
236
+ border-bottom: 1px solid #1f2937;
237
+ }
238
+ .ref-table td:first-child {
239
+ color: #63b3ed;
240
+ }
241
+ .ref-table td:nth-child(2) {
242
+ color: #9ae6b4;
243
+ }
244
+ .ref-table td:nth-child(3) {
245
+ color: #fbbf24;
246
+ }
247
+ .ref-table td:nth-child(4) {
248
+ color: #6b7280;
249
+ font-style: italic;
250
+ }
251
+ .ref-table tr:hover td {
252
+ background: #1f2937;
253
+ }
254
+
255
+ .toc {
256
+ display: flex;
257
+ flex-wrap: wrap;
258
+ gap: 6px;
259
+ margin-bottom: 1.5rem;
260
+ }
261
+ .toc a {
262
+ font-size: 0.75rem;
263
+ padding: 0.3rem 0.6rem;
264
+ background: #e2e8f0;
265
+ border-radius: 6px;
266
+ color: #4a5568;
267
+ text-decoration: none;
268
+ transition: all 0.15s;
269
+ }
270
+ .toc a:hover {
271
+ background: #029eff;
272
+ color: #fff;
273
+ }
274
+
275
+ .badge {
276
+ font-size: 0.6rem;
277
+ background: #ebf8ff;
278
+ color: #029eff;
279
+ padding: 0.15rem 0.5rem;
280
+ border-radius: 99px;
281
+ font-weight: 600;
282
+ margin-left: 6px;
283
+ vertical-align: middle;
284
+ white-space: nowrap;
285
+ }
286
+ hr {
287
+ border: none;
288
+ border-top: 1px solid #e2e8f0;
289
+ margin: 2rem 0;
290
+ }
291
+ footer {
292
+ text-align: center;
293
+ font-size: 0.78rem;
294
+ color: #a0aec0;
295
+ padding-bottom: 2rem;
296
+ }
297
+ #box-target {
298
+ position: relative;
299
+ min-height: 80px;
300
+ background: #fff;
301
+ border-radius: 8px;
302
+ border: 2px dashed #e2e8f0;
303
+ display: flex;
304
+ align-items: center;
305
+ justify-content: center;
306
+ color: #a0aec0;
307
+ font-size: 0.82rem;
308
+ margin-top: 0.5rem;
309
+ transition: min-height 0.2s;
310
+ }
311
+
312
+ /* ── Sandbox form styles ── */
313
+ .sandbox {
314
+ background: #fff;
315
+ border-radius: 8px;
316
+ border: 2px solid #e2e8f0;
317
+ padding: 1rem;
318
+ margin-top: 0.75rem;
319
+ }
320
+ .sandbox h3 {
321
+ font-size: 0.9rem;
322
+ font-weight: 600;
323
+ margin-bottom: 0.2rem;
324
+ color: #2d3748;
325
+ }
326
+ .sandbox > p {
327
+ font-size: 0.73rem;
328
+ color: #718096;
329
+ margin-bottom: 0.75rem;
330
+ }
331
+ .sandbox-grid {
332
+ display: grid;
333
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
334
+ gap: 0.5rem 0.75rem;
335
+ }
336
+ .sandbox-row {
337
+ display: flex;
338
+ flex-direction: column;
339
+ gap: 2px;
340
+ }
341
+ .sandbox-row label {
342
+ font-size: 0.7rem;
343
+ font-weight: 600;
344
+ color: #4a5568;
345
+ text-transform: uppercase;
346
+ letter-spacing: 0.03em;
347
+ }
348
+ .sandbox-row input,
349
+ .sandbox-row select {
350
+ font-family: "Quicksand", sans-serif;
351
+ font-size: 0.78rem;
352
+ padding: 0.35rem 0.5rem;
353
+ border: 1px solid #e2e8f0;
354
+ border-radius: 5px;
355
+ background: #f7fafc;
356
+ color: #2d3748;
357
+ transition: border-color 0.15s;
358
+ }
359
+ .sandbox-row input:focus,
360
+ .sandbox-row select:focus {
361
+ outline: none;
362
+ border-color: #029eff;
363
+ background: #fff;
364
+ }
365
+ .sandbox-row input[type="checkbox"] {
366
+ width: 18px;
367
+ height: 18px;
368
+ accent-color: #029eff;
369
+ cursor: pointer;
370
+ }
371
+ .sandbox-row input[type="color"] {
372
+ height: 34px;
373
+ cursor: pointer;
374
+ padding: 2px;
375
+ }
376
+ .sandbox-row .chk-row {
377
+ display: flex;
378
+ align-items: center;
379
+ gap: 6px;
380
+ padding-top: 4px;
381
+ }
382
+ .sandbox-row .chk-row label {
383
+ text-transform: none;
384
+ font-weight: 400;
385
+ font-size: 0.78rem;
386
+ cursor: pointer;
387
+ }
388
+ .sandbox-actions {
389
+ display: flex;
390
+ gap: 0.5rem;
391
+ align-items: center;
392
+ margin-top: 0.75rem;
393
+ padding-top: 0.75rem;
394
+ border-top: 1px solid #e2e8f0;
395
+ }
396
+ .sandbox-actions button {
397
+ font-family: "Quicksand", sans-serif;
398
+ font-size: 0.8rem;
399
+ padding: 0.4rem 1rem;
400
+ border: 1px solid #e2e8f0;
401
+ border-radius: 6px;
402
+ background: #fff;
403
+ color: #2d3748;
404
+ cursor: pointer;
405
+ transition: all 0.15s;
406
+ }
407
+ .sandbox-actions button:hover {
408
+ border-color: #029eff;
409
+ color: #029eff;
410
+ background: #ebf8ff;
411
+ }
412
+ .sandbox-actions button:focus-visible {
413
+ outline: 2px solid #029eff;
414
+ outline-offset: 2px;
415
+ }
416
+ .sandbox-actions button.pri {
417
+ background: #029eff;
418
+ color: #fff;
419
+ border-color: #029eff;
420
+ }
421
+ .sandbox-actions button.pri:hover {
422
+ background: #0288e0;
423
+ }
424
+ .sandbox-actions button.dan {
425
+ background: #ff6b6b;
426
+ color: #fff;
427
+ border-color: #ff6b6b;
428
+ }
429
+ .sandbox-actions button.dan:hover {
430
+ background: #e05555;
431
+ }
432
+ .sandbox-section-label {
433
+ grid-column: 1 / -1;
434
+ font-size: 0.68rem;
435
+ font-weight: 600;
436
+ color: #a0aec0;
437
+ text-transform: uppercase;
438
+ letter-spacing: 0.05em;
439
+ padding-top: 0.4rem;
440
+ border-top: 1px solid #edf2f7;
441
+ margin-top: 0.2rem;
442
+ }
443
+
444
+ /* ── Theme toggle ── */
445
+ .theme-toggle, .rtl-toggle {
446
+ margin-left: auto;
447
+ background: none;
448
+ border: 2px solid #e2e8f0;
449
+ border-radius: 50%;
450
+ width: 36px;
451
+ height: 36px;
452
+ font-size: 1.1rem;
453
+ cursor: pointer;
454
+ display: inline-flex;
455
+ align-items: center;
456
+ justify-content: center;
457
+ transition: all 0.2s;
458
+ flex-shrink: 0;
459
+ line-height: 1;
460
+ padding: 0;
461
+ }
462
+ .theme-toggle:hover, .rtl-toggle:hover {
463
+ border-color: #029eff;
464
+ }
465
+
466
+ /* ── Dark theme ── */
467
+ [data-theme="dark"] body {
468
+ background: #0f172a;
469
+ color: #e2e8f0;
470
+ }
471
+ [data-theme="dark"] .demo-card {
472
+ background: #1e293b;
473
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
474
+ }
475
+ [data-theme="dark"] .demo-card h3 {
476
+ color: #e2e8f0;
477
+ }
478
+ [data-theme="dark"] .demo-card p.desc {
479
+ color: #94a3b8;
480
+ }
481
+ [data-theme="dark"] .demo-card button {
482
+ background: #334155;
483
+ border-color: #475569;
484
+ color: #e2e8f0;
485
+ }
486
+ [data-theme="dark"] .demo-card button:hover {
487
+ border-color: #38bdf8;
488
+ color: #38bdf8;
489
+ background: #1e293b;
490
+ }
491
+ [data-theme="dark"] .demo-card button.pri {
492
+ background: #0284c7;
493
+ border-color: #0284c7;
494
+ color: #fff;
495
+ }
496
+ [data-theme="dark"] .demo-card button.pri:hover {
497
+ background: #0369a1;
498
+ }
499
+ [data-theme="dark"] .demo-card button.suc {
500
+ background: #16a34a;
501
+ border-color: #16a34a;
502
+ }
503
+ [data-theme="dark"] .demo-card button.suc:hover {
504
+ background: #15803d;
505
+ }
506
+ [data-theme="dark"] .demo-card button.war {
507
+ background: #ca8a04;
508
+ border-color: #ca8a04;
509
+ color: #fff;
510
+ }
511
+ [data-theme="dark"] .demo-card button.war:hover {
512
+ background: #a16207;
513
+ }
514
+ [data-theme="dark"] .demo-card button.dan {
515
+ background: #dc2626;
516
+ border-color: #dc2626;
517
+ }
518
+ [data-theme="dark"] .demo-card button.dan:hover {
519
+ background: #b91c1c;
520
+ }
521
+ [data-theme="dark"] .demo-card button.inf {
522
+ background: #0284c7;
523
+ border-color: #0284c7;
524
+ }
525
+ [data-theme="dark"] .demo-card button.inf:hover {
526
+ background: #0369a1;
527
+ }
528
+ [data-theme="dark"] .demo-card button.drk {
529
+ background: #64748b;
530
+ border-color: #64748b;
531
+ }
532
+ [data-theme="dark"] .demo-card button.drk:hover {
533
+ background: #475569;
534
+ }
535
+ [data-theme="dark"] h1 small {
536
+ color: #64748b;
537
+ }
538
+ [data-theme="dark"] .sub {
539
+ color: #94a3b8;
540
+ }
541
+ [data-theme="dark"] section > p.section-desc {
542
+ color: #94a3b8;
543
+ }
544
+ [data-theme="dark"] .toc a {
545
+ background: #334155;
546
+ color: #cbd5e1;
547
+ }
548
+ [data-theme="dark"] .toc a:hover {
549
+ background: #0284c7;
550
+ color: #fff;
551
+ }
552
+ [data-theme="dark"] .badge {
553
+ background: #0f172a;
554
+ color: #38bdf8;
555
+ }
556
+ [data-theme="dark"] hr {
557
+ border-color: #334155;
558
+ }
559
+ [data-theme="dark"] footer {
560
+ color: #64748b;
561
+ }
562
+ [data-theme="dark"] #box-target {
563
+ background: #1e293b;
564
+ border-color: #334155;
565
+ color: #64748b;
566
+ }
567
+ [data-theme="dark"] .sandbox {
568
+ background: #1e293b;
569
+ border-color: #334155;
570
+ }
571
+ [data-theme="dark"] .sandbox h3 {
572
+ color: #e2e8f0;
573
+ }
574
+ [data-theme="dark"] .sandbox > p {
575
+ color: #94a3b8;
576
+ }
577
+ [data-theme="dark"] .sandbox-row label {
578
+ color: #94a3b8;
579
+ }
580
+ [data-theme="dark"] .sandbox-row input,
581
+ [data-theme="dark"] .sandbox-row select {
582
+ background: #0f172a;
583
+ border-color: #475569;
584
+ color: #e2e8f0;
585
+ }
586
+ [data-theme="dark"] .sandbox-row input:focus,
587
+ [data-theme="dark"] .sandbox-row select:focus {
588
+ border-color: #38bdf8;
589
+ background: #0f172a;
590
+ }
591
+ [data-theme="dark"] .sandbox-actions {
592
+ border-color: #334155;
593
+ }
594
+ [data-theme="dark"] .sandbox-actions button {
595
+ background: #334155;
596
+ border-color: #475569;
597
+ color: #e2e8f0;
598
+ }
599
+ [data-theme="dark"] .sandbox-actions button:hover {
600
+ border-color: #38bdf8;
601
+ color: #38bdf8;
602
+ background: #1e293b;
603
+ }
604
+ [data-theme="dark"] .sandbox-actions button.pri {
605
+ background: #0284c7;
606
+ border-color: #0284c7;
607
+ color: #fff;
608
+ }
609
+ [data-theme="dark"] .sandbox-actions button.pri:hover {
610
+ background: #0369a1;
611
+ }
612
+ [data-theme="dark"] .sandbox-actions button.dan {
613
+ background: #dc2626;
614
+ border-color: #dc2626;
615
+ color: #fff;
616
+ }
617
+ [data-theme="dark"] .sandbox-actions button.dan:hover {
618
+ background: #b91c1c;
619
+ }
620
+ [data-theme="dark"] .theme-toggle, [data-theme="dark"] .rtl-toggle {
621
+ border-color: #475569;
622
+ }
623
+ [data-theme="dark"] .theme-toggle:hover, [data-theme="dark"] .rtl-toggle:hover {
624
+ border-color: #38bdf8;
625
+ }
626
+ </style>
627
+ </head>
628
+ <body>
629
+ <div class="container">
630
+ <div class="header">
631
+ <h1>
632
+ Fnon.js v2.0.0
633
+ <small
634
+ >Interactive Playground &mdash; all APIs, live sandbox, Init
635
+ reference</small
636
+ >
637
+ <button
638
+ id="theme-toggle"
639
+ class="theme-toggle"
640
+ onclick="toggleTheme()"
641
+ title="Toggle dark/light mode"
642
+ >
643
+ 🌙
644
+ </button>
645
+ <button
646
+ id="rtl-toggle"
647
+ class="rtl-toggle"
648
+ onclick="toggleRtl()"
649
+ title="Toggle RTL/LTR direction"
650
+ >
651
+
652
+ </button>
653
+ </h1>
654
+ <p class="sub">
655
+ Seven feature sections. Each has code examples (try instantly), an
656
+ Init() config reference table, an Async explanation, and a live
657
+ sandbox form. Open browser console for lifecycle logs.
658
+ </p>
659
+ <div class="toc">
660
+ <a href="#hint">1 Hint</a>
661
+ <a href="#wait">2 Wait</a>
662
+ <a href="#box">3 Box</a>
663
+ <a href="#alert">4 Alert</a>
664
+ <a href="#ask">5 Ask</a>
665
+ <a href="#dialogue">6 Dialogue</a>
666
+ <a href="#breaking">7 Breaking</a>
667
+ <a href="#datepicker">8 DatePicker</a>
668
+ <a href="#contextmenu">9 ContextMenu</a>
669
+ </div>
670
+ </div>
671
+
672
+
673
+
674
+ <!-- ══════════════════════════ HINT ══════════════════════════ -->
675
+ <section id="hint">
676
+ <h2>
677
+ 1. Hint <span class="badge">Fnon.Hint.Theme(msg, titleOrOpts?)</span>
678
+ </h2>
679
+ <p class="section-desc">
680
+ Toast-style notifications. Auto-dismisses after 4 seconds. 7 themes, 9
681
+ positions, customizable width/font/progress bar.
682
+ </p>
683
+
684
+ <div class="demo-grid">
685
+ <div class="demo-card">
686
+ <div class="card-body">
687
+ <h3>String + title</h3>
688
+ <p class="desc">
689
+ Fnon.Hint.Theme(message, title). Auto-dismisses after 4s.
690
+ </p>
691
+ <div class="card-actions">
692
+ <button
693
+ class="pri"
694
+ onclick="Fnon.Hint.Primary('Hello!', 'Title')"
695
+ >
696
+ Primary
697
+ </button>
698
+ <button
699
+ class="suc"
700
+ onclick="Fnon.Hint.Success('Saved!', 'Title')"
701
+ >
702
+ Success
703
+ </button>
704
+ <button
705
+ class="dan"
706
+ onclick="Fnon.Hint.Danger('Error!', 'Title')"
707
+ >
708
+ Danger
709
+ </button>
710
+ <button
711
+ class="war"
712
+ onclick="Fnon.Hint.Warning('Warning!', 'Title')"
713
+ >
714
+ Warning
715
+ </button>
716
+ <button
717
+ class="inf"
718
+ onclick="Fnon.Hint.Info('Info!', 'Title')"
719
+ >
720
+ Info
721
+ </button>
722
+ <button
723
+ class="dar"
724
+ onclick="Fnon.Hint.Dark('Dark!', 'Title')"
725
+ >
726
+ Dark
727
+ </button>
728
+ <button
729
+ onclick="Fnon.Hint.Light('Light!', 'Title')"
730
+ >
731
+ Light
732
+ </button>
733
+ </div>
734
+ </div>
735
+ <div class="code">
736
+ <span class="kw">Fnon</span>.Hint.Success(<span class="str"
737
+ >'Message'</span
738
+ >, <span class="str">'Title'</span>)
739
+ <span class="kw">Fnon</span>.Hint.Warning(<span class="str"
740
+ >'Message'</span
741
+ >, <span class="str">'Title'</span>)
742
+ <span class="kw">Fnon</span>.Hint.Danger(<span class="str"
743
+ >'Message'</span
744
+ >, <span class="str">'Title'</span>)
745
+ </div>
746
+ </div>
747
+
748
+ <div class="demo-card">
749
+ <div class="card-body">
750
+ <h3>Position options</h3>
751
+ <p class="desc">
752
+ Pass object as 2nd arg: <code>{ position: 'left-top' }</code>. 9
753
+ positions available.
754
+ </p>
755
+ <div class="card-actions">
756
+ <button
757
+ onclick="
758
+ Fnon.Hint.Primary('right-top', { position: 'right-top' })
759
+ "
760
+ >
761
+ R-Top
762
+ </button>
763
+ <button
764
+ onclick="
765
+ Fnon.Hint.Primary('left-top', { position: 'left-top' })
766
+ "
767
+ >
768
+ L-Top
769
+ </button>
770
+ <button
771
+ onclick="
772
+ Fnon.Hint.Primary('center-top', { position: 'center-top' })
773
+ "
774
+ >
775
+ C-Top
776
+ </button>
777
+ <button
778
+ onclick="
779
+ Fnon.Hint.Primary('right-bottom', {
780
+ position: 'right-bottom',
781
+ })
782
+ "
783
+ >
784
+ R-Bottom
785
+ </button>
786
+ <button
787
+ onclick="
788
+ Fnon.Hint.Primary('center-center', {
789
+ position: 'center-center',
790
+ })
791
+ "
792
+ >
793
+ Center
794
+ </button>
795
+ </div>
796
+ </div>
797
+ <div class="code">
798
+ <span class="kw">Fnon</span>.Hint.Primary(<span class="str"
799
+ >'msg'</span
800
+ >, { <span class="str">position</span>:
801
+ <span class="str">'left-top'</span> })
802
+ </div>
803
+ </div>
804
+
805
+ <div class="demo-card">
806
+ <div class="card-body">
807
+ <h3>closable + timeOut</h3>
808
+ <p class="desc">
809
+ Disable the X button or control auto-dismiss timeout.
810
+ </p>
811
+ <div class="card-actions">
812
+ <button
813
+ onclick="
814
+ Fnon.Hint.Primary('No close btn, 3s', {
815
+ closable: false,
816
+ timeOut: 3000,
817
+ })
818
+ "
819
+ >
820
+ No Close / 3s
821
+ </button>
822
+ <button
823
+ onclick="Fnon.Hint.Success('10 seconds', { timeOut: 10000 })"
824
+ >
825
+ 10 seconds
826
+ </button>
827
+ </div>
828
+ </div>
829
+ <div class="code">
830
+ <span class="kw">Fnon</span>.Hint.Primary(<span class="str"
831
+ >'msg'</span
832
+ >, { <span class="str">closable</span>:
833
+ <span class="pr">false</span>, <span class="str">timeOut</span>:
834
+ <span class="num">3000</span> })
835
+ </div>
836
+ </div>
837
+
838
+ <div class="demo-card">
839
+ <div class="card-body">
840
+ <h3>width + fontSize + spacing</h3>
841
+ <p class="desc">
842
+ Control hint dimensions and gap between stacked hints.
843
+ </p>
844
+ <div class="card-actions">
845
+ <button
846
+ onclick="
847
+ Fnon.Hint.Primary('Wide hint', 'Wide', {
848
+ width: '500px',
849
+ timeOut: 4000,
850
+ })
851
+ "
852
+ >
853
+ Wide (500px)
854
+ </button>
855
+ <button
856
+ onclick="
857
+ Fnon.Hint.Success('Large text', 'Big', {
858
+ fontSize: '1.1rem',
859
+ timeOut: 3000,
860
+ })
861
+ "
862
+ >
863
+ Large Font
864
+ </button>
865
+ <button
866
+ onclick="
867
+ Fnon.Hint.Warning('Extra spacing', 'Space', {
868
+ spacing: '24px',
869
+ timeOut: 3000,
870
+ })
871
+ "
872
+ >
873
+ Spacing (24px)
874
+ </button>
875
+ </div>
876
+ </div>
877
+ <div class="code">
878
+ <span class="kw">Fnon</span>.Hint.Primary(<span class="str"
879
+ >'msg'</span
880
+ >, { <span class="str">width</span>:
881
+ <span class="str">'500px'</span> })
882
+ </div>
883
+ </div>
884
+
885
+ <div class="demo-card">
886
+ <div class="card-body">
887
+ <h3>Progress bar + Hint.Init</h3>
888
+ <p class="desc">
889
+ Progress bar shows remaining time. Use Init() to set global
890
+ defaults.
891
+ </p>
892
+ <div class="card-actions">
893
+ <button
894
+ class="pri"
895
+ onclick="
896
+ Fnon.Hint.Primary('6 second progress', 'Progress', {
897
+ timeOut: 6000,
898
+ })
899
+ "
900
+ >
901
+ 6s Progress
902
+ </button>
903
+ <button
904
+ onclick="
905
+ Fnon.Hint.Init({ timeOut: 6000, position: 'left-top' });
906
+ Fnon.Hint.Success('Defaults changed!', '.Init');
907
+ "
908
+ >
909
+ Apply .Init
910
+ </button>
911
+ <button
912
+ onclick="
913
+ Fnon.Hint.Init({ timeOut: 4000, position: 'right-top' });
914
+ Fnon.Hint.Info('Reset');
915
+ "
916
+ >
917
+ Reset
918
+ </button>
919
+ </div>
920
+ </div>
921
+ <div class="code">
922
+ <span class="kw">Fnon</span>.Hint.<span class="fn">Init</span>({
923
+ <span class="str">timeOut</span>: <span class="num">6000</span> })
924
+ </div>
925
+ </div>
926
+ </div>
927
+
928
+ <div class="ref-section">
929
+ <h3>HintOptions &mdash; Init() config reference</h3>
930
+ <table class="ref-table">
931
+ <tr>
932
+ <th>Option</th>
933
+ <th>Default</th>
934
+ <th>Type</th>
935
+ <th>Description</th>
936
+ </tr>
937
+ <tr>
938
+ <td>type</td>
939
+ <td>'Primary'</td>
940
+ <td>ThemeName</td>
941
+ <td>Theme key (set automatically by shortcut)</td>
942
+ </tr>
943
+ <tr>
944
+ <td>title</td>
945
+ <td>—</td>
946
+ <td>string | HTMLElement</td>
947
+ <td>Dialog title</td>
948
+ </tr>
949
+ <tr>
950
+ <td>position</td>
951
+ <td>'right-top'</td>
952
+ <td>string</td>
953
+ <td>
954
+ right-top, right-center, right-bottom, left-top, left-center,
955
+ left-bottom, center-top, center-center, center-bottom
956
+ </td>
957
+ </tr>
958
+ <tr>
959
+ <td>timeOut</td>
960
+ <td>4000</td>
961
+ <td>number (ms)</td>
962
+ <td>Auto-dismiss after N ms (0 = no dismiss)</td>
963
+ </tr>
964
+ <tr>
965
+ <td>closable</td>
966
+ <td>true</td>
967
+ <td>boolean</td>
968
+ <td>Show/hide the X button</td>
969
+ </tr>
970
+ <tr>
971
+ <td>width</td>
972
+ <td>'360px'</td>
973
+ <td>string</td>
974
+ <td>Hint container max-width</td>
975
+ </tr>
976
+ <tr>
977
+ <td>fontSize</td>
978
+ <td>'0.88rem'</td>
979
+ <td>string</td>
980
+ <td>Hint text size</td>
981
+ </tr>
982
+ <tr>
983
+ <td>fontFamily</td>
984
+ <td>'Quicksand', sans-serif</td>
985
+ <td>string</td>
986
+ <td>Font family</td>
987
+ </tr>
988
+ <tr>
989
+ <td>spacing</td>
990
+ <td>'8px'</td>
991
+ <td>string</td>
992
+ <td>Gap between stacked hints</td>
993
+ </tr>
994
+ <tr>
995
+ <td>animation</td>
996
+ <td>'slide-left'</td>
997
+ <td>string</td>
998
+ <td>CSS animation class</td>
999
+ </tr>
1000
+ <tr>
1001
+ <td>animationDuration</td>
1002
+ <td>'0.3s'</td>
1003
+ <td>string</td>
1004
+ <td>Enter/exit transition duration</td>
1005
+ </tr>
1006
+ <tr>
1007
+ <td>progressColor</td>
1008
+ <td>—</td>
1009
+ <td>color</td>
1010
+ <td>Progress bar color (defaults to theme borderColor)</td>
1011
+ </tr>
1012
+ <tr>
1013
+ <td>svg</td>
1014
+ <td>—</td>
1015
+ <td>string</td>
1016
+ <td>Custom icon SVG HTML</td>
1017
+ </tr>
1018
+ <tr>
1019
+ <td>svgSize</td>
1020
+ <td>{w:20,h:20}</td>
1021
+ <td>object</td>
1022
+ <td>Icon dimensions in px</td>
1023
+ </tr>
1024
+ <tr>
1025
+ <td>appendTo</td>
1026
+ <td>body</td>
1027
+ <td>selector</td>
1028
+ <td>Mount container element</td>
1029
+ </tr>
1030
+ <tr>
1031
+ <td>onClose</td>
1032
+ <td>—</td>
1033
+ <td>() => void</td>
1034
+ <td>Called when hint is dismissed</td>
1035
+ </tr>
1036
+ <tr>
1037
+ <td>onclosed</td>
1038
+ <td>—</td>
1039
+ <td>() => void</td>
1040
+ <td>Called after hint is fully removed</td>
1041
+ </tr>
1042
+ </table>
1043
+ </div>
1044
+
1045
+ <p style="font-size: 0.82rem; color: #718096; margin: 0.5rem 0">
1046
+ <strong>Async:</strong>
1047
+ <code>Fnon.Hint.Theme.Async(message, title?)</code> returns a
1048
+ <code>Promise&lt;void&gt;</code> that resolves when the hint closes
1049
+ (auto or manual). Useful for sequencing notifications.
1050
+ </p>
1051
+
1052
+ <div class="sandbox">
1053
+ <h3>Sandbox &mdash; Hint</h3>
1054
+ <p>Fill in the fields and click Run to see the hint live.</p>
1055
+ <div class="sandbox-grid">
1056
+ <div class="sandbox-row">
1057
+ <label>Message</label>
1058
+ <input
1059
+ type="text"
1060
+ id="hint-msg"
1061
+ value="Hello from the sandbox!"
1062
+ />
1063
+ </div>
1064
+ <div class="sandbox-row">
1065
+ <label>Title</label>
1066
+ <input type="text" id="hint-title" value="Sandbox" />
1067
+ </div>
1068
+ <div class="sandbox-row">
1069
+ <label>Theme</label>
1070
+ <select id="hint-theme">
1071
+ <option>Primary</option>
1072
+ <option>Success</option>
1073
+ <option>Warning</option>
1074
+ <option>Danger</option>
1075
+ <option>Info</option>
1076
+ <option>Light</option>
1077
+ <option>Dark</option>
1078
+ </select>
1079
+ </div>
1080
+ <div class="sandbox-row">
1081
+ <label>Position</label>
1082
+ <select id="hint-position">
1083
+ <option>right-top</option>
1084
+ <option>right-center</option>
1085
+ <option>right-bottom</option>
1086
+ <option>left-top</option>
1087
+ <option>left-center</option>
1088
+ <option>left-bottom</option>
1089
+ <option>center-top</option>
1090
+ <option>center-center</option>
1091
+ <option>center-bottom</option>
1092
+ </select>
1093
+ </div>
1094
+ <div class="sandbox-row">
1095
+ <label>timeOut (ms)</label>
1096
+ <input type="number" id="hint-timeout" value="4000" />
1097
+ </div>
1098
+ <div class="sandbox-row">
1099
+ <label>Width</label>
1100
+ <input type="text" id="hint-width" value="360px" />
1101
+ </div>
1102
+ <div class="sandbox-row">
1103
+ <label>Font size</label>
1104
+ <input type="text" id="hint-fontsize" value="0.88rem" />
1105
+ </div>
1106
+ <div class="sandbox-row">
1107
+ <label>Progress color</label>
1108
+ <input type="color" id="hint-progresscolor" value="#029eff" />
1109
+ </div>
1110
+ <div class="sandbox-row">
1111
+ <div class="chk-row">
1112
+ <input type="checkbox" id="hint-closable" checked />
1113
+ <label for="hint-closable">Closable (show X button)</label>
1114
+ </div>
1115
+ </div>
1116
+ </div>
1117
+ <div class="sandbox-actions">
1118
+ <button class="pri" onclick="runHintSandbox()">Run Hint</button>
1119
+ <button
1120
+ onclick="
1121
+ (async () => {
1122
+ await Fnon.Hint.Primary.Async('Async resolved!', 'Async');
1123
+ Fnon.Hint.Success('Promise resolved!');
1124
+ })()
1125
+ "
1126
+ >
1127
+ Try .Async
1128
+ </button>
1129
+ </div>
1130
+ </div>
1131
+ </section>
1132
+
1133
+ <!-- ══════════════════════════ WAIT ══════════════════════════ -->
1134
+ <section id="wait">
1135
+ <h2>2. Wait <span class="badge">Fnon.Wait.Type(text?, opts?)</span></h2>
1136
+ <p class="section-desc">
1137
+ Full-page spinner overlay. 5 spinner types (Orbit, Spin / Infinity, Ripple, Typing).
1138
+ Supports clickToClose, text label, custom color, and Remove/Change
1139
+ controls.
1140
+ </p>
1141
+
1142
+ <div class="demo-grid">
1143
+ <div class="demo-card">
1144
+ <div class="card-body">
1145
+ <h3>CSS spinners — Orbit, Spin</h3>
1146
+ <p class="desc">
1147
+ Lightweight CSS-only spinners with
1148
+ <code>clickToClose: true</code> for dismiss.
1149
+ </p>
1150
+ <div class="card-actions">
1151
+ <button
1152
+ onclick="Fnon.Wait.Orbit('Orbit', { clickToClose: true })"
1153
+ >
1154
+ Orbit
1155
+ </button>
1156
+ <button
1157
+ onclick="Fnon.Wait.Spin('Spin', { clickToClose: true })"
1158
+ >
1159
+ Spin
1160
+ </button>
1161
+ </div>
1162
+ </div>
1163
+ <div class="code">
1164
+ <span class="kw">Fnon</span>.Wait.Orbit(<span class="str"
1165
+ >'Loading'</span
1166
+ >, { <span class="str">clickToClose</span>:
1167
+ <span class="pr">true</span> })
1168
+ </div>
1169
+ </div>
1170
+
1171
+ <div class="demo-card">
1172
+ <div class="card-body">
1173
+ <h3>SVG spanners (Infinity, Ripple, Typing)</h3>
1174
+ <p class="desc">
1175
+ Lightweight SVG spinners: Infinity, Ripple, Typing.
1176
+ </p>
1177
+ <div class="card-actions" style="gap: 0.25rem">
1178
+ <button
1179
+ class="small"
1180
+ onclick="
1181
+ Fnon.Wait.Infinity('Infinity', { clickToClose: true })
1182
+ "
1183
+ >
1184
+ &infin;
1185
+ </button>
1186
+ <button
1187
+ class="small"
1188
+ onclick="Fnon.Wait.Ripple('Ripple', { clickToClose: true })"
1189
+ >
1190
+ Ripple
1191
+ </button>
1192
+ <button
1193
+ class="small"
1194
+ onclick="Fnon.Wait.Typing('Typing', { clickToClose: true })"
1195
+ >
1196
+ Typing
1197
+ </button>
1198
+ </div>
1199
+ </div>
1200
+ <div class="code">
1201
+ <span class="kw">Fnon</span>.Wait.Infinity(<span class="str"
1202
+ >'Loading'</span
1203
+ >, { <span class="str">clickToClose</span>:
1204
+ <span class="pr">true</span> })
1205
+ </div>
1206
+ </div>
1207
+
1208
+ <div class="demo-card">
1209
+ <div class="card-body">
1210
+ <h3>Custom color + containerSize</h3>
1211
+ <p class="desc">
1212
+ Override <code>svgColor</code> and <code>containerSize</code> to
1213
+ match your brand.
1214
+ </p>
1215
+ <div class="card-actions">
1216
+ <button
1217
+ onclick="
1218
+ Fnon.Wait.Orbit('Red', {
1219
+ svgColor: '#ff6b6b',
1220
+ clickToClose: true,
1221
+ })
1222
+ "
1223
+ >
1224
+ Red Orbit
1225
+ </button>
1226
+ <button
1227
+ onclick="
1228
+ Fnon.Wait.Orbit('Wide', {
1229
+ containerSize: '400px',
1230
+ clickToClose: true,
1231
+ })
1232
+ "
1233
+ >
1234
+ Wide Orbit
1235
+ </button>
1236
+ </div>
1237
+ </div>
1238
+ <div class="code">
1239
+ <span class="kw">Fnon</span>.Wait.Orbit(<span class="str"
1240
+ >'Loading'</span
1241
+ >, { <span class="str">svgColor</span>:
1242
+ <span class="str">'#ff6b6b'</span> })
1243
+ </div>
1244
+ </div>
1245
+
1246
+ <div class="demo-card">
1247
+ <div class="card-body">
1248
+ <h3>Wait controls + Wait.Init</h3>
1249
+ <p class="desc">
1250
+ Remove(), Change(text), and Init() for global defaults.
1251
+ </p>
1252
+ <div class="card-actions">
1253
+ <button
1254
+ class="pri"
1255
+ onclick="
1256
+ Fnon.Wait.Orbit('Working...');
1257
+ setTimeout(() => Fnon.Wait.Change('Almost done...'), 1500);
1258
+ setTimeout(() => Fnon.Wait.Remove(), 3000);
1259
+ "
1260
+ >
1261
+ 3s demo
1262
+ </button>
1263
+ <button
1264
+ onclick="
1265
+ Fnon.Wait.Init({ svgColor: '#39DA8A' });
1266
+ Fnon.Wait.Spin('Green', { clickToClose: true });
1267
+ "
1268
+ >
1269
+ Green Init
1270
+ </button>
1271
+ <button
1272
+ onclick="
1273
+ Fnon.Wait.Init({ svgColor: '#029eff' });
1274
+ Fnon.Hint.Info('Reset');
1275
+ "
1276
+ >
1277
+ Reset
1278
+ </button>
1279
+ </div>
1280
+ </div>
1281
+ <div class="code">
1282
+ <span class="kw">Fnon</span>.Wait.Pulse(<span class="str"
1283
+ >'Working'</span
1284
+ >)<br /><span class="kw">Fnon</span>.Wait.<span class="fn"
1285
+ >Change</span
1286
+ >(<span class="str">'New text'</span>)<br /><span class="kw"
1287
+ >Fnon</span
1288
+ >.Wait.<span class="fn">Remove</span>()
1289
+ </div>
1290
+ </div>
1291
+
1292
+ <div class="demo-card">
1293
+ <div class="card-body">
1294
+ <h3>Wait.Async</h3>
1295
+ <p class="desc">
1296
+ Returns a Promise that resolves when Remove() is called or the
1297
+ spinner is dismissed.
1298
+ </p>
1299
+ <div class="card-actions">
1300
+ <button
1301
+ class="pri"
1302
+ onclick="
1303
+ (async () => {
1304
+ Fnon.Wait.Orbit.Async('Working...').then(() =>
1305
+ Fnon.Hint.Success('Done!'),
1306
+ );
1307
+ setTimeout(() => Fnon.Wait.Remove(), 2000);
1308
+ })()
1309
+ "
1310
+ >
1311
+ Wait.Async (2s)
1312
+ </button>
1313
+ </div>
1314
+ </div>
1315
+ <div class="code">
1316
+ <span class="kw">Fnon</span>.Wait.Orbit.<span class="fn"
1317
+ >Async</span
1318
+ >(<span class="str">'Working'</span>)<br />&nbsp;&nbsp;.then(() =>
1319
+ <span class="kw">Fnon</span>.Hint.Success(<span class="str"
1320
+ >'Done!'</span
1321
+ >))
1322
+ </div>
1323
+ </div>
1324
+ </div>
1325
+
1326
+ <div class="ref-section">
1327
+ <h3>SpinnerOptions &mdash; Wait</h3>
1328
+ <table class="ref-table">
1329
+ <tr>
1330
+ <th>Option</th>
1331
+ <th>Default</th>
1332
+ <th>Type</th>
1333
+ <th>Description</th>
1334
+ </tr>
1335
+ <tr>
1336
+ <td>text</td>
1337
+ <td>''</td>
1338
+ <td>string</td>
1339
+ <td>Label text below the spinner</td>
1340
+ </tr>
1341
+ <tr>
1342
+ <td>clickToClose</td>
1343
+ <td>false</td>
1344
+ <td>boolean</td>
1345
+ <td>Click the overlay to dismiss</td>
1346
+ </tr>
1347
+ <tr>
1348
+ <td>svgColor</td>
1349
+ <td>'#029eff'</td>
1350
+ <td>color</td>
1351
+ <td>Spinner / accent color</td>
1352
+ </tr>
1353
+ <tr>
1354
+ <td>fontFamily</td>
1355
+ <td>'Quicksand', sans-serif</td>
1356
+ <td>string</td>
1357
+ <td>Font family for text</td>
1358
+ </tr>
1359
+ <tr>
1360
+ <td>containerSize</td>
1361
+ <td>—</td>
1362
+ <td>string</td>
1363
+ <td>Spinner box width (e.g. '400px')</td>
1364
+ </tr>
1365
+ <tr>
1366
+ <td>appendTarget</td>
1367
+ <td>body</td>
1368
+ <td>selector</td>
1369
+ <td>Mount container</td>
1370
+ </tr>
1371
+ <tr>
1372
+ <td colspan="4" style="color: #63b3ed; text-align: center">
1373
+ Spinner types: Orbit, Spin (CSS) &bull; Infinity,
1374
+ Ripple, Typing (SVG)
1375
+ </td>
1376
+ </tr>
1377
+ </table>
1378
+ </div>
1379
+
1380
+ <p style="font-size: 0.82rem; color: #718096; margin: 0.5rem 0">
1381
+ <strong>Async:</strong>
1382
+ <code>Fnon.Wait.Type.Async(text?, opts?)</code> returns
1383
+ <code>Promise&lt;boolean&gt;</code> (resolves <code>true</code> when
1384
+ <code>Remove()</code> is called or the spinner is dismissed).
1385
+ </p>
1386
+
1387
+ <div class="sandbox">
1388
+ <h3>Sandbox &mdash; Wait</h3>
1389
+ <p>Select a spinner type, configure options, and launch.</p>
1390
+ <div class="sandbox-grid">
1391
+ <div class="sandbox-row">
1392
+ <label>Text</label>
1393
+ <input type="text" id="wait-text" value="Loading..." />
1394
+ </div>
1395
+ <div class="sandbox-row">
1396
+ <label>Spinner type</label>
1397
+ <select id="wait-type">
1398
+ <option>Orbit</option>
1399
+ <option>Spin</option>
1400
+ <option>Infinity</option>
1401
+ <option>Ripple</option>
1402
+ <option>Typing</option>
1403
+ </select>
1404
+ </div>
1405
+ <div class="sandbox-row">
1406
+ <label>svgColor</label>
1407
+ <input type="color" id="wait-svgcolor" value="#029eff" />
1408
+ </div>
1409
+ <div class="sandbox-row">
1410
+ <label>Container size</label>
1411
+ <input
1412
+ type="text"
1413
+ id="wait-containersize"
1414
+ placeholder="e.g. 400px"
1415
+ />
1416
+ </div>
1417
+ <div class="sandbox-row">
1418
+ <label>Font family</label>
1419
+ <input
1420
+ type="text"
1421
+ id="wait-fontfamily"
1422
+ value="'Quicksand', sans-serif"
1423
+ />
1424
+ </div>
1425
+ <div class="sandbox-row">
1426
+ <div class="chk-row">
1427
+ <input type="checkbox" id="wait-clicktoclose" />
1428
+ <label for="wait-clicktoclose">Click to close</label>
1429
+ </div>
1430
+ </div>
1431
+ </div>
1432
+ <div class="sandbox-actions">
1433
+ <button class="pri" onclick="runWaitSandbox()">Run Wait</button>
1434
+ <button class="dan" onclick="Fnon.Wait.Remove()">
1435
+ Remove Wait
1436
+ </button>
1437
+ </div>
1438
+ </div>
1439
+ </section>
1440
+
1441
+ <!-- ══════════════════════════ BOX ══════════════════════════ -->
1442
+ <section id="box">
1443
+ <h2>
1444
+ 3. Box
1445
+ <span class="badge">Fnon.Box.Type(selector, text?, opts?)</span>
1446
+ </h2>
1447
+ <p class="section-desc">
1448
+ Container-bound spinner overlay. Same 5 spinner types as Wait, but
1449
+ rendered inside a specific DOM element instead of full-screen.
1450
+ </p>
1451
+
1452
+ <div class="demo-grid">
1453
+ <div class="demo-card">
1454
+ <div class="card-body">
1455
+ <h3>Spinner in container</h3>
1456
+ <p class="desc">
1457
+ First arg is a CSS selector. Spinner appears inside that
1458
+ element.
1459
+ </p>
1460
+ <div class="card-actions">
1461
+ <button onclick="Fnon.Box.Orbit('#box-target', 'Loading')">
1462
+ Orbit
1463
+ </button>
1464
+ <button onclick="Fnon.Box.Spin('#box-target', 'Working')">
1465
+ Spin
1466
+ </button>
1467
+ <button onclick="Fnon.Box.Infinity('#box-target', '&infin;')">
1468
+ Infinity
1469
+ </button>
1470
+ <button class="dan" onclick="Fnon.Box.Remove('#box-target')">
1471
+ Remove
1472
+ </button>
1473
+ </div>
1474
+ </div>
1475
+ <div class="code">
1476
+ <span class="kw">Fnon</span>.Box.Orbit(<span class="str"
1477
+ >'#box-target'</span
1478
+ >, <span class="str">'Loading'</span>)
1479
+ </div>
1480
+ </div>
1481
+
1482
+ <div class="demo-card">
1483
+ <div class="card-body">
1484
+ <h3>Custom color + svgSize</h3>
1485
+ <p class="desc">
1486
+ Override <code>svgColor</code> and <code>svgSize</code> for the
1487
+ box spinner.
1488
+ </p>
1489
+ <div class="card-actions">
1490
+ <button
1491
+ onclick="
1492
+ Fnon.Box.Spin('#box-target', 'Red', {
1493
+ svgColor: '#ff6b6b',
1494
+ });
1495
+ setTimeout(() => Fnon.Box.Remove('#box-target'), 2000);
1496
+ "
1497
+ >
1498
+ Red Spin
1499
+ </button>
1500
+ <button
1501
+ onclick="
1502
+ Fnon.Box.Orbit('#box-target', 'Big', {
1503
+ svgSize: { w: 80, h: 80 },
1504
+ });
1505
+ setTimeout(() => Fnon.Box.Remove('#box-target'), 2000);
1506
+ "
1507
+ >
1508
+ Big Orbit
1509
+ </button>
1510
+ </div>
1511
+ </div>
1512
+ <div class="code">
1513
+ <span class="kw">Fnon</span>.Box.Orbit(<span class="str"
1514
+ >'#box-target'</span
1515
+ >, <span class="str">'Loading'</span>, {
1516
+ <span class="str">svgSize</span>: { <span class="str">w</span>:
1517
+ <span class="num">80</span>, <span class="str">h</span>:
1518
+ <span class="num">80</span> } })
1519
+ </div>
1520
+ </div>
1521
+
1522
+ <div class="demo-card">
1523
+ <div class="card-body">
1524
+ <h3>Box.Init + Box.Async</h3>
1525
+ <p class="desc">
1526
+ <code>Box.Init</code> sets global defaults.
1527
+ <code>Box.Async</code> returns a Promise.
1528
+ </p>
1529
+ <div class="card-actions">
1530
+ <button
1531
+ onclick="
1532
+ Fnon.Box.Init({ svgColor: '#39DA8A' });
1533
+ Fnon.Box.Spin('#box-target', 'Green');
1534
+ setTimeout(() => Fnon.Box.Remove('#box-target'), 2000);
1535
+ "
1536
+ >
1537
+ Green Init
1538
+ </button>
1539
+ <button
1540
+ onclick="
1541
+ Fnon.Box.Init({ svgColor: '#029eff' });
1542
+ Fnon.Hint.Info('Reset');
1543
+ "
1544
+ >
1545
+ Reset
1546
+ </button>
1547
+ <button
1548
+ class="pri"
1549
+ onclick="
1550
+ (async () => {
1551
+ Fnon.Box.Pulse.Async('#box-target', 'Working...').then(
1552
+ () => Fnon.Hint.Success('Box done!'),
1553
+ );
1554
+ setTimeout(() => Fnon.Box.Remove('#box-target'), 1500);
1555
+ })()
1556
+ "
1557
+ >
1558
+ Box.Async (1.5s)
1559
+ </button>
1560
+ </div>
1561
+ </div>
1562
+ <div class="code">
1563
+ <span class="kw">Fnon</span>.Box.<span class="fn">Init</span>({
1564
+ <span class="str">svgColor</span>:
1565
+ <span class="str">'#39DA8A'</span> })
1566
+ </div>
1567
+ </div>
1568
+ </div>
1569
+
1570
+ <div id="box-target">#box-target &mdash; spinners appear here</div>
1571
+
1572
+ <div class="ref-section">
1573
+ <h3>BoxOptions &mdash; Box</h3>
1574
+ <table class="ref-table">
1575
+ <tr>
1576
+ <th>Option</th>
1577
+ <th>Default</th>
1578
+ <th>Type</th>
1579
+ <th>Description</th>
1580
+ </tr>
1581
+ <tr>
1582
+ <td>selector</td>
1583
+ <td>—</td>
1584
+ <td>string</td>
1585
+ <td>CSS selector for the target element</td>
1586
+ </tr>
1587
+ <tr>
1588
+ <td>text</td>
1589
+ <td>''</td>
1590
+ <td>string</td>
1591
+ <td>Label below the spinner</td>
1592
+ </tr>
1593
+ <tr>
1594
+ <td>svgColor</td>
1595
+ <td>'#029eff'</td>
1596
+ <td>color</td>
1597
+ <td>Spinner accent color</td>
1598
+ </tr>
1599
+ <tr>
1600
+ <td>svgSize</td>
1601
+ <td>—</td>
1602
+ <td>{w,h}</td>
1603
+ <td>Spinner SVG dimensions in px</td>
1604
+ </tr>
1605
+ <tr>
1606
+ <td>fontFamily</td>
1607
+ <td>'Quicksand', sans-serif</td>
1608
+ <td>string</td>
1609
+ <td>Font family for text</td>
1610
+ </tr>
1611
+ <tr>
1612
+ <td>containerSize</td>
1613
+ <td>—</td>
1614
+ <td>string</td>
1615
+ <td>Spinner box width</td>
1616
+ </tr>
1617
+ <tr>
1618
+ <td colspan="4" style="color: #63b3ed; text-align: center">
1619
+ Same 5 spinner types as Wait: Orbit, Spin, Infinity,
1620
+ Ripple, Typing
1621
+ </td>
1622
+ </tr>
1623
+ </table>
1624
+ </div>
1625
+
1626
+ <p style="font-size: 0.82rem; color: #718096; margin: 0.5rem 0">
1627
+ <strong>Async:</strong>
1628
+ <code>Fnon.Box.Type.Async(selector, text?, opts?)</code> returns
1629
+ <code>Promise&lt;boolean&gt;</code> (resolves <code>true</code> when
1630
+ <code>Box.Remove(selector)</code> is called).
1631
+ </p>
1632
+
1633
+ <div class="sandbox">
1634
+ <h3>Sandbox &mdash; Box</h3>
1635
+ <p>Enter a CSS selector and configure the spinner.</p>
1636
+ <div class="sandbox-grid">
1637
+ <div class="sandbox-row">
1638
+ <label>CSS selector</label>
1639
+ <input type="text" id="box-selector" value="#box-target" />
1640
+ </div>
1641
+ <div class="sandbox-row">
1642
+ <label>Text</label>
1643
+ <input type="text" id="box-text" value="Working..." />
1644
+ </div>
1645
+ <div class="sandbox-row">
1646
+ <label>Spinner type</label>
1647
+ <select id="box-type">
1648
+ <option>Orbit</option>
1649
+ <option>Spin</option>
1650
+ <option>Infinity</option>
1651
+ <option>Ripple</option>
1652
+ <option>Typing</option>
1653
+ </select>
1654
+ </div>
1655
+ <div class="sandbox-row">
1656
+ <label>svgColor</label>
1657
+ <input type="color" id="box-svgcolor" value="#029eff" />
1658
+ </div>
1659
+ <div class="sandbox-row">
1660
+ <label>svgSize w</label>
1661
+ <input
1662
+ type="number"
1663
+ id="box-svgw"
1664
+ value="40"
1665
+ placeholder="width"
1666
+ />
1667
+ </div>
1668
+ <div class="sandbox-row">
1669
+ <label>svgSize h</label>
1670
+ <input
1671
+ type="number"
1672
+ id="box-svgh"
1673
+ value="40"
1674
+ placeholder="height"
1675
+ />
1676
+ </div>
1677
+ </div>
1678
+ <div class="sandbox-actions">
1679
+ <button class="pri" onclick="runBoxSandbox()">Run Box</button>
1680
+ <button
1681
+ class="dan"
1682
+ onclick="
1683
+ Fnon.Box.Remove(
1684
+ document.getElementById('box-selector').value ||
1685
+ '#box-target',
1686
+ )
1687
+ "
1688
+ >
1689
+ Remove Box
1690
+ </button>
1691
+ </div>
1692
+ </div>
1693
+ </section>
1694
+
1695
+ <!-- ══════════════════════════ ALERT ══════════════════════════ -->
1696
+ <section id="alert">
1697
+ <h2>
1698
+ 4. Alert
1699
+ <span class="badge"
1700
+ >Fnon.Alert.Theme(msg, title?, btnOkText?, callback?)</span
1701
+ >
1702
+ </h2>
1703
+ <p class="section-desc">
1704
+ A one-button modal for simple messages. Supports both positional args
1705
+ and a single options object.
1706
+ </p>
1707
+
1708
+ <div class="demo-grid">
1709
+ <div class="demo-card">
1710
+ <div class="card-body">
1711
+ <h3>String message</h3>
1712
+ <p class="desc">
1713
+ Three positional args: message, title, button text.
1714
+ </p>
1715
+ <div class="card-actions">
1716
+ <button
1717
+ class="pri"
1718
+ onclick="
1719
+ Fnon.Alert.Primary('Hello from Fnon!', 'Greeting', 'OK')
1720
+ "
1721
+ >
1722
+ Primary
1723
+ </button>
1724
+ <button
1725
+ class="suc"
1726
+ onclick="Fnon.Alert.Success('Saved!', 'Success', 'OK')"
1727
+ >
1728
+ Success
1729
+ </button>
1730
+ <button
1731
+ class="dan"
1732
+ onclick="Fnon.Alert.Danger('Error!', 'Error', 'OK')"
1733
+ >
1734
+ Danger
1735
+ </button>
1736
+ </div>
1737
+ </div>
1738
+ <div class="code">
1739
+ <span class="kw">Fnon</span>.Alert.Primary(<span class="str"
1740
+ >'Hello!'</span
1741
+ >, <span class="str">'Title'</span>,
1742
+ <span class="str">'OK'</span>)
1743
+ </div>
1744
+ </div>
1745
+
1746
+ <div class="demo-card">
1747
+ <div class="card-body">
1748
+ <h3>Object args</h3>
1749
+ <p class="desc">
1750
+ Access every option by passing a single object.
1751
+ </p>
1752
+ <div class="card-actions">
1753
+ <button
1754
+ class="pri"
1755
+ onclick="
1756
+ Fnon.Alert.Primary({
1757
+ message: 'Object mode!',
1758
+ title: 'Object Args',
1759
+ btnOkText: 'Got it',
1760
+ width: 'sm',
1761
+ })
1762
+ "
1763
+ >
1764
+ sm object
1765
+ </button>
1766
+ <button
1767
+ class="suc"
1768
+ onclick="
1769
+ Fnon.Alert.Success({
1770
+ message: 'Wide modal',
1771
+ title: 'Wide',
1772
+ width: 'xl',
1773
+ closeButton: false,
1774
+ })
1775
+ "
1776
+ >
1777
+ xl, no close
1778
+ </button>
1779
+ </div>
1780
+ </div>
1781
+ <div class="code">
1782
+ <span class="kw">Fnon</span>.Alert.Primary({
1783
+ <span class="str">message</span>:
1784
+ <span class="str">'Object mode!'</span>,
1785
+ <span class="str">width</span>: <span class="str">'sm'</span> })
1786
+ </div>
1787
+ </div>
1788
+
1789
+ <div class="demo-card">
1790
+ <div class="card-body">
1791
+ <h3>delButtons + autoFocus</h3>
1792
+ <p class="desc">
1793
+ <code>delButtons: true</code> removes footer.
1794
+ <code>autoFocus: true</code> highlights the button.
1795
+ </p>
1796
+ <div class="card-actions">
1797
+ <button
1798
+ onclick="
1799
+ Fnon.Alert.Primary({
1800
+ message: 'No footer buttons',
1801
+ title: 'No Buttons',
1802
+ delButtons: true,
1803
+ })
1804
+ "
1805
+ >
1806
+ delButtons
1807
+ </button>
1808
+ <button
1809
+ class="pri"
1810
+ onclick="
1811
+ Fnon.Alert.Primary({
1812
+ message: 'Button auto-focused',
1813
+ autoFocus: true,
1814
+ })
1815
+ "
1816
+ >
1817
+ autoFocus
1818
+ </button>
1819
+ </div>
1820
+ </div>
1821
+ <div class="code">
1822
+ <span class="kw">Fnon</span>.Alert.Primary({
1823
+ <span class="str">delButtons</span>:
1824
+ <span class="pr">true</span> })
1825
+ </div>
1826
+ </div>
1827
+
1828
+ <div class="demo-card">
1829
+ <div class="card-body">
1830
+ <h3>Button styling overrides</h3>
1831
+ <p class="desc">
1832
+ Custom colors, background, shadow, border for the OK button.
1833
+ </p>
1834
+ <div class="card-actions">
1835
+ <button
1836
+ onclick="
1837
+ Fnon.Alert.Primary({
1838
+ message: 'Styled!',
1839
+ btnOkColor: '#fff',
1840
+ btnOkBackground: '#6c5ce7',
1841
+ btnOkShadow: '0 4px 12px rgba(108,92,231,.4)',
1842
+ })
1843
+ "
1844
+ >
1845
+ Styled
1846
+ </button>
1847
+ </div>
1848
+ </div>
1849
+ <div class="code">
1850
+ <span class="kw">Fnon</span>.Alert.Primary({
1851
+ <span class="str">btnOkColor</span>:
1852
+ <span class="str">'#fff'</span>,
1853
+ <span class="str">btnOkBackground</span>:
1854
+ <span class="str">'#6c5ce7'</span> })
1855
+ </div>
1856
+ </div>
1857
+
1858
+ <div class="demo-card">
1859
+ <div class="card-body">
1860
+ <h3>.Async + .Init</h3>
1861
+ <p class="desc">
1862
+ <code>.Async</code> returns a Promise. <code>.Init</code> sets
1863
+ global defaults.
1864
+ </p>
1865
+ <div class="card-actions">
1866
+ <button
1867
+ class="pri"
1868
+ onclick="
1869
+ (async () => {
1870
+ await Fnon.Alert.Primary.Async(
1871
+ 'Promise resolved!',
1872
+ 'Async',
1873
+ );
1874
+ Fnon.Hint.Success('Resolved!');
1875
+ })()
1876
+ "
1877
+ >
1878
+ Alert.Async
1879
+ </button>
1880
+ <button
1881
+ onclick="
1882
+ Fnon.Alert.Init({ btnOkText: 'Got it!' });
1883
+ Fnon.Alert.Primary('Button changed!', '.Init');
1884
+ "
1885
+ >
1886
+ Apply .Init
1887
+ </button>
1888
+ <button
1889
+ onclick="
1890
+ Fnon.Alert.Init({ btnOkText: void 0 });
1891
+ Fnon.Hint.Info('Reset');
1892
+ "
1893
+ >
1894
+ Reset
1895
+ </button>
1896
+ </div>
1897
+ </div>
1898
+ <div class="code">
1899
+ <span class="kw">await</span>
1900
+ <span class="kw">Fnon</span>.Alert.Primary.<span class="fn"
1901
+ >Async</span
1902
+ >(<span class="str">'msg'</span>,
1903
+ <span class="str">'title'</span>)
1904
+ </div>
1905
+ </div>
1906
+ </div>
1907
+
1908
+ <div class="ref-section">
1909
+ <h3>Alert — common ModalOptions</h3>
1910
+ <table class="ref-table">
1911
+ <tr>
1912
+ <th>Option</th>
1913
+ <th>Default</th>
1914
+ <th>Type</th>
1915
+ <th>Description</th>
1916
+ </tr>
1917
+ <tr>
1918
+ <td>message</td>
1919
+ <td>—</td>
1920
+ <td>string | HTMLElement | VNode</td>
1921
+ <td>Body content</td>
1922
+ </tr>
1923
+ <tr>
1924
+ <td>title</td>
1925
+ <td>—</td>
1926
+ <td>string</td>
1927
+ <td>Dialog title</td>
1928
+ </tr>
1929
+ <tr>
1930
+ <td>btnOkText</td>
1931
+ <td>'OK'</td>
1932
+ <td>string</td>
1933
+ <td>OK button label</td>
1934
+ </tr>
1935
+ <tr>
1936
+ <td>width</td>
1937
+ <td>—</td>
1938
+ <td>'sm' | 'lg' | 'xl' | 'fl'</td>
1939
+ <td>Width mode (360/640/800/full px)</td>
1940
+ </tr>
1941
+ <tr>
1942
+ <td>closeButton</td>
1943
+ <td>true</td>
1944
+ <td>boolean</td>
1945
+ <td>Show/hide X button</td>
1946
+ </tr>
1947
+ <tr>
1948
+ <td>showClose</td>
1949
+ <td>—</td>
1950
+ <td>boolean</td>
1951
+ <td>Alias for closeButton</td>
1952
+ </tr>
1953
+ <tr>
1954
+ <td>delButtons</td>
1955
+ <td>false</td>
1956
+ <td>boolean</td>
1957
+ <td>Remove all footer buttons</td>
1958
+ </tr>
1959
+ <tr>
1960
+ <td>autoFocus</td>
1961
+ <td>false</td>
1962
+ <td>boolean</td>
1963
+ <td>Auto-focus first matching element</td>
1964
+ </tr>
1965
+ <tr>
1966
+ <td>autoFocusTag</td>
1967
+ <td>'input:not([disabled])'</td>
1968
+ <td>selector</td>
1969
+ <td>Focus target selector</td>
1970
+ </tr>
1971
+ <tr>
1972
+ <td>fontFamily</td>
1973
+ <td>'Quicksand', sans-serif</td>
1974
+ <td>string</td>
1975
+ <td>CSS font-family</td>
1976
+ </tr>
1977
+ <tr>
1978
+ <td>animation</td>
1979
+ <td>'slide-top'</td>
1980
+ <td>string</td>
1981
+ <td>CSS animation class</td>
1982
+ </tr>
1983
+ <tr>
1984
+ <td>color</td>
1985
+ <td>'#2b2b2b'</td>
1986
+ <td>color</td>
1987
+ <td>Body text color</td>
1988
+ </tr>
1989
+ <tr>
1990
+ <td>background</td>
1991
+ <td>'rgba(0,0,0,0.4)'</td>
1992
+ <td>color</td>
1993
+ <td>Overlay background</td>
1994
+ </tr>
1995
+ <tr>
1996
+ <td>titleColor</td>
1997
+ <td>—</td>
1998
+ <td>color</td>
1999
+ <td>Title text color</td>
2000
+ </tr>
2001
+ <tr>
2002
+ <td>titleBackground</td>
2003
+ <td>—</td>
2004
+ <td>color</td>
2005
+ <td>Title bar background</td>
2006
+ </tr>
2007
+ <tr>
2008
+ <td>zIndex</td>
2009
+ <td>4000</td>
2010
+ <td>number</td>
2011
+ <td>Overlay z-index</td>
2012
+ </tr>
2013
+ <tr>
2014
+ <td>showIcon</td>
2015
+ <td>false</td>
2016
+ <td>boolean</td>
2017
+ <td>Show an icon in the body</td>
2018
+ </tr>
2019
+ <tr>
2020
+ <td>data</td>
2021
+ <td>—</td>
2022
+ <td>object</td>
2023
+ <td>Shared mutable data for lifecycle callbacks</td>
2024
+ </tr>
2025
+ <tr>
2026
+ <td>focusTrap</td>
2027
+ <td>true</td>
2028
+ <td>boolean</td>
2029
+ <td>Lock Tab focus inside the modal</td>
2030
+ </tr>
2031
+ <tr>
2032
+ <td>isDark</td>
2033
+ <td>—</td>
2034
+ <td>boolean | ()=>boolean</td>
2035
+ <td>Enable dark mode for body &amp; footer</td>
2036
+ </tr>
2037
+ <tr>
2038
+ <td>darkBgColor</td>
2039
+ <td>'#1a202c'</td>
2040
+ <td>color</td>
2041
+ <td>Dark mode background color</td>
2042
+ </tr>
2043
+ <tr>
2044
+ <td>onOpen</td>
2045
+ <td>—</td>
2046
+ <td>(el,data)=>void</td>
2047
+ <td>After modal opens</td>
2048
+ </tr>
2049
+ <tr>
2050
+ <td>onClose</td>
2051
+ <td>—</td>
2052
+ <td>(data)=>void</td>
2053
+ <td>When modal closes</td>
2054
+ </tr>
2055
+ <tr>
2056
+ <td>onInit</td>
2057
+ <td>—</td>
2058
+ <td>(el,data)=>void</td>
2059
+ <td>After element built, before DOM append</td>
2060
+ </tr>
2061
+ <tr>
2062
+ <td>onClosed</td>
2063
+ <td>—</td>
2064
+ <td>(data)=>void</td>
2065
+ <td>After modal fully removed</td>
2066
+ </tr>
2067
+ </table>
2068
+ </div>
2069
+
2070
+ <p style="font-size: 0.82rem; color: #718096; margin: 0.5rem 0">
2071
+ <strong>Async:</strong>
2072
+ <code>Fnon.Alert.Theme.Async(message, title?, btnOkText?)</code>
2073
+ returns <code>Promise&lt;void&gt;</code> that resolves when OK is
2074
+ clicked or the modal is closed.
2075
+ </p>
2076
+
2077
+ <div class="sandbox">
2078
+ <h3>Sandbox &mdash; Alert</h3>
2079
+ <p>Configure and launch an Alert modal.</p>
2080
+ <div class="sandbox-grid">
2081
+ <div class="sandbox-row">
2082
+ <label>Message</label>
2083
+ <input
2084
+ type="text"
2085
+ id="alert-msg"
2086
+ value="Hello from the sandbox!"
2087
+ />
2088
+ </div>
2089
+ <div class="sandbox-row">
2090
+ <label>Title</label>
2091
+ <input type="text" id="alert-title" value="Alert Sandbox" />
2092
+ </div>
2093
+ <div class="sandbox-row">
2094
+ <label>btnOkText</label>
2095
+ <input type="text" id="alert-btnok" value="OK" />
2096
+ </div>
2097
+ <div class="sandbox-row">
2098
+ <label>Theme</label>
2099
+ <select id="alert-theme">
2100
+ <option>Primary</option>
2101
+ <option>Success</option>
2102
+ <option>Warning</option>
2103
+ <option>Danger</option>
2104
+ <option>Info</option>
2105
+ <option>Light</option>
2106
+ <option>Dark</option>
2107
+ </select>
2108
+ </div>
2109
+ <div class="sandbox-row">
2110
+ <label>Width</label>
2111
+ <select id="alert-width">
2112
+ <option value="">Normal</option>
2113
+ <option>sm</option>
2114
+ <option>lg</option>
2115
+ <option>xl</option>
2116
+ <option>fl</option>
2117
+ </select>
2118
+ </div>
2119
+ <div class="sandbox-row">
2120
+ <label>Font family</label>
2121
+ <input
2122
+ type="text"
2123
+ id="alert-fontfamily"
2124
+ value="'Quicksand', sans-serif"
2125
+ />
2126
+ </div>
2127
+ <div class="sandbox-row">
2128
+ <div class="chk-row">
2129
+ <input type="checkbox" id="alert-showclose" checked /><label
2130
+ for="alert-showclose"
2131
+ >Show close button</label
2132
+ >
2133
+ </div>
2134
+ </div>
2135
+ <div class="sandbox-row">
2136
+ <div class="chk-row">
2137
+ <input type="checkbox" id="alert-autofocus" /><label
2138
+ for="alert-autofocus"
2139
+ >Auto-focus</label
2140
+ >
2141
+ </div>
2142
+ </div>
2143
+ </div>
2144
+ <div class="sandbox-actions">
2145
+ <button class="pri" onclick="runAlertSandbox()">Run Alert</button>
2146
+ </div>
2147
+ </div>
2148
+ </section>
2149
+
2150
+ <!-- ══════════════════════════ ASK ══════════════════════════ -->
2151
+ <section id="ask">
2152
+ <h2>
2153
+ 5. Ask
2154
+ <span class="badge"
2155
+ >Fnon.Ask.Theme(msg, title?, okText?, cancelText?, callback?)</span
2156
+ >
2157
+ </h2>
2158
+ <p class="section-desc">
2159
+ A two-button confirm modal (OK / Cancel). The callback receives
2160
+ <code>true</code> or <code>false</code>. Supports object args and
2161
+ async.
2162
+ </p>
2163
+
2164
+ <div class="demo-grid">
2165
+ <div class="demo-card">
2166
+ <div class="card-body">
2167
+ <h3>Positional args</h3>
2168
+ <p class="desc">
2169
+ message, title, OK text, Cancel text, callback(result).
2170
+ </p>
2171
+ <div class="card-actions">
2172
+ <button
2173
+ class="pri"
2174
+ onclick="
2175
+ Fnon.Ask.Primary('Proceed?', 'Confirm', 'Yes', 'No', (r) =>
2176
+ Fnon.Hint.Primary(r ? 'Yes' : 'No'),
2177
+ )
2178
+ "
2179
+ >
2180
+ Primary
2181
+ </button>
2182
+ <button
2183
+ class="dan"
2184
+ onclick="
2185
+ Fnon.Ask.Danger(
2186
+ 'Delete file?',
2187
+ 'Delete',
2188
+ 'Delete',
2189
+ 'Keep',
2190
+ (r) => Fnon.Hint.Danger(r ? 'Deleted' : 'Kept'),
2191
+ )
2192
+ "
2193
+ >
2194
+ Danger
2195
+ </button>
2196
+ </div>
2197
+ </div>
2198
+ <div class="code">
2199
+ <span class="kw">Fnon</span>.Ask.Primary(<span class="str"
2200
+ >'Proceed?'</span
2201
+ >, <span class="str">'Confirm'</span>,
2202
+ <span class="str">'Yes'</span>, <span class="str">'No'</span>, r
2203
+ => {})
2204
+ </div>
2205
+ </div>
2206
+
2207
+ <div class="demo-card">
2208
+ <div class="card-body">
2209
+ <h3>Object args</h3>
2210
+ <p class="desc">Pass a single options object for full control.</p>
2211
+ <div class="card-actions">
2212
+ <button
2213
+ class="pri"
2214
+ onclick="
2215
+ Fnon.Ask.Primary({
2216
+ message: 'Object mode!',
2217
+ btnOkText: 'Sure',
2218
+ btnCancelText: 'Nah',
2219
+ })
2220
+ "
2221
+ >
2222
+ Object
2223
+ </button>
2224
+ </div>
2225
+ </div>
2226
+ <div class="code">
2227
+ <span class="kw">Fnon</span>.Ask.Primary({
2228
+ <span class="str">message</span>:
2229
+ <span class="str">'Object mode!'</span>,
2230
+ <span class="str">btnOkText</span>:
2231
+ <span class="str">'Sure'</span> })
2232
+ </div>
2233
+ </div>
2234
+
2235
+ <div class="demo-card">
2236
+ <div class="card-body">
2237
+ <h3>Button styling overrides</h3>
2238
+ <p class="desc">Custom colors for both OK and Cancel buttons.</p>
2239
+ <div class="card-actions">
2240
+ <button
2241
+ onclick="
2242
+ Fnon.Ask.Primary({
2243
+ message: 'Styled!',
2244
+ btnOkColor: '#fff',
2245
+ btnOkBackground: '#6c5ce7',
2246
+ btnOkShadow: '0 4px 12px rgba(108,92,231,.4)',
2247
+ btnCancelColor: '#fff',
2248
+ btnCancelBackground: '#e17055',
2249
+ })
2250
+ "
2251
+ >
2252
+ Styled
2253
+ </button>
2254
+ </div>
2255
+ </div>
2256
+ <div class="code">
2257
+ <span class="kw">Fnon</span>.Ask.Primary({
2258
+ <span class="str">btnOkColor</span>:
2259
+ <span class="str">'#fff'</span>,
2260
+ <span class="str">btnOkBackground</span>:
2261
+ <span class="str">'#6c5ce7'</span> })
2262
+ </div>
2263
+ </div>
2264
+
2265
+ <div class="demo-card">
2266
+ <div class="card-body">
2267
+ <h3>.Async + .Init</h3>
2268
+ <p class="desc">
2269
+ <code>.Async</code> resolves <code>true</code> or
2270
+ <code>false</code>. <code>.Init</code> sets global defaults.
2271
+ </p>
2272
+ <div class="card-actions">
2273
+ <button
2274
+ class="pri"
2275
+ onclick="
2276
+ (async () => {
2277
+ let r = await Fnon.Ask.Primary.Async(
2278
+ 'Continue?',
2279
+ 'Async',
2280
+ 'Yes',
2281
+ 'No',
2282
+ );
2283
+ Fnon.Hint.Primary(r ? 'Yes' : 'No');
2284
+ })()
2285
+ "
2286
+ >
2287
+ Ask.Async
2288
+ </button>
2289
+ <button
2290
+ onclick="
2291
+ Fnon.Ask.Init({ btnOkText: 'Sure' });
2292
+ Fnon.Ask.Primary('Button changed!');
2293
+ "
2294
+ >
2295
+ Apply .Init
2296
+ </button>
2297
+ <button
2298
+ onclick="
2299
+ Fnon.Ask.Init({ btnOkText: void 0 });
2300
+ Fnon.Hint.Info('Reset');
2301
+ "
2302
+ >
2303
+ Reset
2304
+ </button>
2305
+ </div>
2306
+ </div>
2307
+ <div class="code">
2308
+ <span class="kw">let</span> r = <span class="kw">await</span>
2309
+ <span class="kw">Fnon</span>.Ask.Primary.<span class="fn"
2310
+ >Async</span
2311
+ >(<span class="str">'Continue?'</span>)
2312
+ </div>
2313
+ </div>
2314
+ </div>
2315
+
2316
+ <div class="ref-section">
2317
+ <h3>Ask — common ModalOptions</h3>
2318
+ <table class="ref-table">
2319
+ <tr>
2320
+ <th>Option</th>
2321
+ <th>Default</th>
2322
+ <th>Type</th>
2323
+ <th>Description</th>
2324
+ </tr>
2325
+ <tr>
2326
+ <td>message</td>
2327
+ <td>—</td>
2328
+ <td>string | HTMLElement | VNode</td>
2329
+ <td>Body content</td>
2330
+ </tr>
2331
+ <tr>
2332
+ <td>title</td>
2333
+ <td>—</td>
2334
+ <td>string</td>
2335
+ <td>Dialog title</td>
2336
+ </tr>
2337
+ <tr>
2338
+ <td>btnOkText</td>
2339
+ <td>'OK'</td>
2340
+ <td>string</td>
2341
+ <td>OK button label</td>
2342
+ </tr>
2343
+ <tr>
2344
+ <td>btnCancelText</td>
2345
+ <td>'Cancel'</td>
2346
+ <td>string</td>
2347
+ <td>Cancel button label</td>
2348
+ </tr>
2349
+ <tr>
2350
+ <td>width</td>
2351
+ <td>—</td>
2352
+ <td>'sm' | 'lg' | 'xl' | 'fl'</td>
2353
+ <td>Width mode</td>
2354
+ </tr>
2355
+ <tr>
2356
+ <td>closeButton</td>
2357
+ <td>true</td>
2358
+ <td>boolean</td>
2359
+ <td>Show/hide X button</td>
2360
+ </tr>
2361
+ <tr>
2362
+ <td>showClose</td>
2363
+ <td>—</td>
2364
+ <td>boolean</td>
2365
+ <td>Alias for closeButton</td>
2366
+ </tr>
2367
+ <tr>
2368
+ <td>autoFocus</td>
2369
+ <td>false</td>
2370
+ <td>boolean</td>
2371
+ <td>Auto-focus first matching element</td>
2372
+ </tr>
2373
+ <tr>
2374
+ <td>fontFamily</td>
2375
+ <td>'Quicksand', sans-serif</td>
2376
+ <td>string</td>
2377
+ <td>CSS font-family</td>
2378
+ </tr>
2379
+ <tr>
2380
+ <td>color</td>
2381
+ <td>'#2b2b2b'</td>
2382
+ <td>color</td>
2383
+ <td>Body text color</td>
2384
+ </tr>
2385
+ <tr>
2386
+ <td>btnOkColor</td>
2387
+ <td>—</td>
2388
+ <td>color</td>
2389
+ <td>OK button text color</td>
2390
+ </tr>
2391
+ <tr>
2392
+ <td>btnOkBackground</td>
2393
+ <td>—</td>
2394
+ <td>color</td>
2395
+ <td>OK button background</td>
2396
+ </tr>
2397
+ <tr>
2398
+ <td>btnOkShadow</td>
2399
+ <td>—</td>
2400
+ <td>string</td>
2401
+ <td>OK button box-shadow</td>
2402
+ </tr>
2403
+ <tr>
2404
+ <td>btnCancelColor</td>
2405
+ <td>—</td>
2406
+ <td>color</td>
2407
+ <td>Cancel button text color</td>
2408
+ </tr>
2409
+ <tr>
2410
+ <td>btnCancelBackground</td>
2411
+ <td>—</td>
2412
+ <td>color</td>
2413
+ <td>Cancel button background</td>
2414
+ </tr>
2415
+ <tr>
2416
+ <td>focusTrap</td>
2417
+ <td>true</td>
2418
+ <td>boolean</td>
2419
+ <td>Lock Tab focus inside the modal</td>
2420
+ </tr>
2421
+ <tr>
2422
+ <td>isDark</td>
2423
+ <td>—</td>
2424
+ <td>boolean | ()=>boolean</td>
2425
+ <td>Enable dark mode for body &amp; footer</td>
2426
+ </tr>
2427
+ <tr>
2428
+ <td>darkBgColor</td>
2429
+ <td>'#1a202c'</td>
2430
+ <td>color</td>
2431
+ <td>Dark mode background color</td>
2432
+ </tr>
2433
+ </table>
2434
+ </div>
2435
+
2436
+ <p style="font-size: 0.82rem; color: #718096; margin: 0.5rem 0">
2437
+ <strong>Async:</strong>
2438
+ <code
2439
+ >Fnon.Ask.Theme.Async(message, title?, okText?, cancelText?)</code
2440
+ >
2441
+ returns <code>Promise&lt;boolean&gt;</code> — <code>true</code> on OK,
2442
+ <code>false</code> on Cancel or close.
2443
+ </p>
2444
+
2445
+ <div class="sandbox">
2446
+ <h3>Sandbox &mdash; Ask</h3>
2447
+ <p>Configure and launch a confirmation modal.</p>
2448
+ <div class="sandbox-grid">
2449
+ <div class="sandbox-row">
2450
+ <label>Message</label>
2451
+ <input
2452
+ type="text"
2453
+ id="ask-msg"
2454
+ value="Proceed with this action?"
2455
+ />
2456
+ </div>
2457
+ <div class="sandbox-row">
2458
+ <label>Title</label>
2459
+ <input type="text" id="ask-title" value="Confirm" />
2460
+ </div>
2461
+ <div class="sandbox-row">
2462
+ <label>btnOkText</label>
2463
+ <input type="text" id="ask-btnok" value="Yes" />
2464
+ </div>
2465
+ <div class="sandbox-row">
2466
+ <label>btnCancelText</label>
2467
+ <input type="text" id="ask-btncancel" value="No" />
2468
+ </div>
2469
+ <div class="sandbox-row">
2470
+ <label>Theme</label>
2471
+ <select id="ask-theme">
2472
+ <option>Primary</option>
2473
+ <option>Success</option>
2474
+ <option>Warning</option>
2475
+ <option>Danger</option>
2476
+ <option>Info</option>
2477
+ <option>Light</option>
2478
+ <option>Dark</option>
2479
+ </select>
2480
+ </div>
2481
+ <div class="sandbox-row">
2482
+ <label>Width</label>
2483
+ <select id="ask-width">
2484
+ <option value="">Normal</option>
2485
+ <option>sm</option>
2486
+ <option>lg</option>
2487
+ <option>xl</option>
2488
+ <option>fl</option>
2489
+ </select>
2490
+ </div>
2491
+ <div class="sandbox-row">
2492
+ <div class="chk-row">
2493
+ <input type="checkbox" id="ask-showclose" checked /><label
2494
+ for="ask-showclose"
2495
+ >Show close button</label
2496
+ >
2497
+ </div>
2498
+ </div>
2499
+ </div>
2500
+ <div class="sandbox-actions">
2501
+ <button class="pri" onclick="runAskSandbox()">Run Ask</button>
2502
+ </div>
2503
+ </div>
2504
+ </section>
2505
+
2506
+ <!-- ══════════════════════════ DIALOGUE ══════════════════════════ -->
2507
+ <section id="dialogue">
2508
+ <h2>
2509
+ 6. Dialogue
2510
+ <span class="badge"
2511
+ >Fnon.Dialogue(opts) · Fnon.Dialogue.Theme(...)</span
2512
+ >
2513
+ </h2>
2514
+ <p class="section-desc">
2515
+ The most flexible modal. Custom buttons, full lifecycle hooks,
2516
+ minimize/maximize, drag, and raw options control.
2517
+ </p>
2518
+
2519
+ <div class="demo-grid">
2520
+ <div class="demo-card">
2521
+ <div class="card-body">
2522
+ <h3>Custom buttons</h3>
2523
+ <p class="desc">
2524
+ Define buttons with text, style, and callback. Callback receives
2525
+ <code>close()</code>.
2526
+ </p>
2527
+ <div class="card-actions">
2528
+ <button
2529
+ class="pri"
2530
+ onclick="
2531
+ Fnon.Dialogue({
2532
+ message: 'Two custom buttons',
2533
+ title: 'Dialogue',
2534
+ buttons: [
2535
+ {
2536
+ text: 'Save',
2537
+ style: 'Success',
2538
+ callback: (c) => {
2539
+ Fnon.Hint.Success('Saved!');
2540
+ c();
2541
+ },
2542
+ },
2543
+ {
2544
+ text: 'Cancel',
2545
+ style: 'Light',
2546
+ callback: (c) => {
2547
+ Fnon.Hint.Light('Cancelled');
2548
+ c();
2549
+ },
2550
+ },
2551
+ ],
2552
+ })
2553
+ "
2554
+ >
2555
+ Custom Btns
2556
+ </button>
2557
+ </div>
2558
+ </div>
2559
+ <div class="code">
2560
+ <span class="kw">Fnon</span>.Dialogue({
2561
+ <span class="str">buttons</span>: [{
2562
+ <span class="str">text</span>: <span class="str">'Save'</span>,
2563
+ <span class="fn">callback</span>: c => c() }] })
2564
+ </div>
2565
+ </div>
2566
+
2567
+ <div class="demo-card">
2568
+ <div class="card-body">
2569
+ <h3>Themed shortcut + prevent close</h3>
2570
+ <p class="desc">
2571
+ Positional shortcut: msg, title, OK, Cancel, callback(close,
2572
+ html). Return <code>false</code> to stay open.
2573
+ </p>
2574
+ <div class="card-actions">
2575
+ <button
2576
+ class="pri"
2577
+ onclick="
2578
+ Fnon.Dialogue.Primary(
2579
+ 'Shortcut!',
2580
+ 'Dialogue',
2581
+ 'OK',
2582
+ 'Cancel',
2583
+ (c) => {
2584
+ Fnon.Hint.Success('OK!');
2585
+ c();
2586
+ },
2587
+ )
2588
+ "
2589
+ >
2590
+ Positional
2591
+ </button>
2592
+ <button
2593
+ class="dan"
2594
+ onclick="
2595
+ Fnon.Dialogue({
2596
+ message: 'Return false to stay open',
2597
+ title: 'Prevent',
2598
+ buttons: [
2599
+ {
2600
+ text: 'Stay open',
2601
+ style: 'Danger',
2602
+ callback: () => {
2603
+ Fnon.Hint.Warning('Prevented!');
2604
+ return false;
2605
+ },
2606
+ },
2607
+ {
2608
+ text: 'Close',
2609
+ style: 'Primary',
2610
+ callback: (c) => c(),
2611
+ },
2612
+ ],
2613
+ })
2614
+ "
2615
+ >
2616
+ Prevent Close
2617
+ </button>
2618
+ </div>
2619
+ </div>
2620
+ <div class="code">
2621
+ <span class="cm">// Return false to prevent close</span
2622
+ ><br />callback: (c, html) => { <span class="kw">return</span>
2623
+ <span class="pr">false</span> }
2624
+ </div>
2625
+ </div>
2626
+
2627
+ <div class="demo-card">
2628
+ <div class="card-body">
2629
+ <h3>Minimize / Maximize</h3>
2630
+ <p class="desc">
2631
+ <code>showMin</code> and <code>showMaxMin</code> add window
2632
+ controls to the title bar. Minimized modals go to a task bar.
2633
+ </p>
2634
+ <div class="card-actions">
2635
+ <button
2636
+ onclick="
2637
+ Fnon.Dialogue({
2638
+ message: 'Click _ to minimize, ⛶ to maximize',
2639
+ title: 'Min/Max',
2640
+ showMin: true,
2641
+ showMaxMin: true,
2642
+ buttons: [
2643
+ { text: 'OK', style: 'Primary', callback: (c) => c() },
2644
+ ],
2645
+ })
2646
+ "
2647
+ >
2648
+ Open
2649
+ </button>
2650
+ </div>
2651
+ </div>
2652
+ <div class="code">
2653
+ <span class="kw">Fnon</span>.Dialogue({
2654
+ <span class="str">showMin</span>: <span class="pr">true</span>,
2655
+ <span class="str">showMaxMin</span>:
2656
+ <span class="pr">true</span> })
2657
+ </div>
2658
+ </div>
2659
+
2660
+ <div class="demo-card">
2661
+ <div class="card-body">
2662
+ <h3>Lifecycle hooks + data</h3>
2663
+ <p class="desc">
2664
+ onInit → beforeShow → afterShow → onOpen → onClosing → onClose →
2665
+ onClosed. All receive <code>(el, data)</code>.
2666
+ </p>
2667
+ <div class="card-actions">
2668
+ <button
2669
+ class="pri"
2670
+ onclick="
2671
+ Fnon.Dialogue({
2672
+ message: 'Watch console',
2673
+ title: 'Lifecycle',
2674
+ onInit: (el, d) => console.log('1 onInit', d),
2675
+ beforeShow: (el, d) => console.log('2 beforeShow', d),
2676
+ afterShow: (el, d) => console.log('3 afterShow', d),
2677
+ onOpen: (el, d) => console.log('4 onOpen', d),
2678
+ onClosing: (el, d) => console.log('5 onClosing', d),
2679
+ onClose: (d) => console.log('6 onClose', d),
2680
+ onClosed: (d) => console.log('7 onClosed', d),
2681
+ })
2682
+ "
2683
+ >
2684
+ Lifecycle Demo
2685
+ </button>
2686
+ </div>
2687
+ </div>
2688
+ <div class="code">
2689
+ <span class="kw">Fnon</span>.Dialogue({
2690
+ <span class="str">onInit</span>: (el, data) => {},
2691
+ <span class="str">onOpen</span>: (el, data) => {} })
2692
+ </div>
2693
+ </div>
2694
+
2695
+ <div class="demo-card">
2696
+ <div class="card-body">
2697
+ <h3>Custom styling + drag</h3>
2698
+ <p class="desc">
2699
+ Override colors, font, width. All modals are draggable by the
2700
+ title bar.
2701
+ </p>
2702
+ <div class="card-actions">
2703
+ <button
2704
+ onclick="
2705
+ Fnon.Dialogue({
2706
+ message: 'Drag me!',
2707
+ title: 'Styled',
2708
+ width: 'sm',
2709
+ titleColor: '#fff',
2710
+ titleBackground: '#6c5ce7',
2711
+ color: '#2d3748',
2712
+ fontFamily: 'monospace',
2713
+ })
2714
+ "
2715
+ >
2716
+ Styled
2717
+ </button>
2718
+ <button
2719
+ onclick="
2720
+ Fnon.Dialogue({
2721
+ message: 'Full width',
2722
+ title: 'Full',
2723
+ width: 'fl',
2724
+ showClose: false,
2725
+ delButtons: true,
2726
+ })
2727
+ "
2728
+ >
2729
+ Full width
2730
+ </button>
2731
+ </div>
2732
+ </div>
2733
+ <div class="code">
2734
+ <span class="kw">Fnon</span>.Dialogue({
2735
+ <span class="str">titleColor</span>:
2736
+ <span class="str">'#fff'</span>, <span class="str">width</span>:
2737
+ <span class="str">'sm'</span> })
2738
+ </div>
2739
+ </div>
2740
+ </div>
2741
+
2742
+ <div class="ref-section">
2743
+ <h3>Dialogue — ModalOptions (full reference)</h3>
2744
+ <table class="ref-table">
2745
+ <tr>
2746
+ <th>Option</th>
2747
+ <th>Default</th>
2748
+ <th>Type</th>
2749
+ <th>Description</th>
2750
+ </tr>
2751
+ <tr>
2752
+ <td>message</td>
2753
+ <td>—</td>
2754
+ <td>string | HTMLElement | VNode</td>
2755
+ <td>Body content</td>
2756
+ </tr>
2757
+ <tr>
2758
+ <td>title</td>
2759
+ <td>—</td>
2760
+ <td>string</td>
2761
+ <td>Dialog title</td>
2762
+ </tr>
2763
+ <tr>
2764
+ <td>buttons</td>
2765
+ <td>—</td>
2766
+ <td>ButtonDef[]</td>
2767
+ <td>Custom button definitions</td>
2768
+ </tr>
2769
+ <tr>
2770
+ <td>width</td>
2771
+ <td>—</td>
2772
+ <td>'sm' | 'lg' | 'xl' | 'fl'</td>
2773
+ <td>Width mode</td>
2774
+ </tr>
2775
+ <tr>
2776
+ <td>layout</td>
2777
+ <td>'hbf'</td>
2778
+ <td>string</td>
2779
+ <td>Order of header/body/footer</td>
2780
+ </tr>
2781
+ <tr>
2782
+ <td>closeButton</td>
2783
+ <td>true</td>
2784
+ <td>boolean</td>
2785
+ <td>Show/hide X button</td>
2786
+ </tr>
2787
+ <tr>
2788
+ <td>showClose</td>
2789
+ <td>—</td>
2790
+ <td>boolean</td>
2791
+ <td>Alias for closeButton</td>
2792
+ </tr>
2793
+ <tr>
2794
+ <td>showMin</td>
2795
+ <td>false</td>
2796
+ <td>boolean</td>
2797
+ <td>Show minimize button</td>
2798
+ </tr>
2799
+ <tr>
2800
+ <td>showMaxMin</td>
2801
+ <td>false</td>
2802
+ <td>boolean</td>
2803
+ <td>Show maximize button</td>
2804
+ </tr>
2805
+ <tr>
2806
+ <td>delButtons</td>
2807
+ <td>false</td>
2808
+ <td>boolean</td>
2809
+ <td>Remove all footer buttons</td>
2810
+ </tr>
2811
+ <tr>
2812
+ <td>autoFocus</td>
2813
+ <td>false</td>
2814
+ <td>boolean</td>
2815
+ <td>Auto-focus first input</td>
2816
+ </tr>
2817
+ <tr>
2818
+ <td>fontFamily</td>
2819
+ <td>'Quicksand', sans-serif</td>
2820
+ <td>string</td>
2821
+ <td>CSS font-family</td>
2822
+ </tr>
2823
+ <tr>
2824
+ <td>animation</td>
2825
+ <td>'slide-top'</td>
2826
+ <td>string</td>
2827
+ <td>CSS animation class</td>
2828
+ </tr>
2829
+ <tr>
2830
+ <td>data</td>
2831
+ <td>—</td>
2832
+ <td>object</td>
2833
+ <td>Shared mutable data for all lifecycle hooks</td>
2834
+ </tr>
2835
+ <tr>
2836
+ <td>color</td>
2837
+ <td>'#2b2b2b'</td>
2838
+ <td>color</td>
2839
+ <td>Body text color</td>
2840
+ </tr>
2841
+ <tr>
2842
+ <td>background</td>
2843
+ <td>'rgba(0,0,0,0.4)'</td>
2844
+ <td>color</td>
2845
+ <td>Overlay background</td>
2846
+ </tr>
2847
+ <tr>
2848
+ <td>titleColor</td>
2849
+ <td>—</td>
2850
+ <td>color</td>
2851
+ <td>Title text color</td>
2852
+ </tr>
2853
+ <tr>
2854
+ <td>titleBackground</td>
2855
+ <td>—</td>
2856
+ <td>color</td>
2857
+ <td>Title bar background</td>
2858
+ </tr>
2859
+ <tr>
2860
+ <td>zIndex</td>
2861
+ <td>4000</td>
2862
+ <td>number</td>
2863
+ <td>Overlay z-index</td>
2864
+ </tr>
2865
+ <tr>
2866
+ <td>appendTarget</td>
2867
+ <td>'body'</td>
2868
+ <td>string | HTMLElement</td>
2869
+ <td>Mount container</td>
2870
+ </tr>
2871
+ <tr>
2872
+ <td>focusTrap</td>
2873
+ <td>true</td>
2874
+ <td>boolean</td>
2875
+ <td>Lock Tab focus inside the modal</td>
2876
+ </tr>
2877
+ <tr>
2878
+ <td>isDark</td>
2879
+ <td>—</td>
2880
+ <td>boolean | ()=>boolean</td>
2881
+ <td>Enable dark mode for body &amp; footer</td>
2882
+ </tr>
2883
+ <tr>
2884
+ <td>darkBgColor</td>
2885
+ <td>'#1a202c'</td>
2886
+ <td>color</td>
2887
+ <td>Dark mode background color</td>
2888
+ </tr>
2889
+ <tr>
2890
+ <td>onInit</td>
2891
+ <td>—</td>
2892
+ <td>(el,data)=>void</td>
2893
+ <td>After element built</td>
2894
+ </tr>
2895
+ <tr>
2896
+ <td>beforeShow</td>
2897
+ <td>—</td>
2898
+ <td>(el,data)=>void</td>
2899
+ <td>Before DOM append</td>
2900
+ </tr>
2901
+ <tr>
2902
+ <td>afterShow</td>
2903
+ <td>—</td>
2904
+ <td>(el,data)=>void</td>
2905
+ <td>After DOM append</td>
2906
+ </tr>
2907
+ <tr>
2908
+ <td>onOpen</td>
2909
+ <td>—</td>
2910
+ <td>(el,data)=>void</td>
2911
+ <td>After modal opens</td>
2912
+ </tr>
2913
+ <tr>
2914
+ <td>onClosing</td>
2915
+ <td>—</td>
2916
+ <td>(el,data)=>void</td>
2917
+ <td>When modal starts closing</td>
2918
+ </tr>
2919
+ <tr>
2920
+ <td>onClose</td>
2921
+ <td>—</td>
2922
+ <td>(data)=>void</td>
2923
+ <td>During close animation</td>
2924
+ </tr>
2925
+ <tr>
2926
+ <td>onClosed</td>
2927
+ <td>—</td>
2928
+ <td>(data)=>void</td>
2929
+ <td>After modal fully removed</td>
2930
+ </tr>
2931
+ </table>
2932
+ </div>
2933
+
2934
+ <p style="font-size: 0.82rem; color: #718096; margin: 0.5rem 0">
2935
+ <strong>Async:</strong>
2936
+ <code
2937
+ >Fnon.Dialogue.Theme.Async(message, title?, okText?,
2938
+ cancelText?)</code
2939
+ >
2940
+ returns <code>Promise&lt;boolean | null&gt;</code> —
2941
+ <code>true</code> on OK, <code>false</code> on Cancel,
2942
+ <code>null</code> on close/X.
2943
+ </p>
2944
+
2945
+ <div class="sandbox">
2946
+ <h3>Sandbox &mdash; Dialogue</h3>
2947
+ <p>Configure a modal with the most common options.</p>
2948
+ <div class="sandbox-grid">
2949
+ <div class="sandbox-row">
2950
+ <label>Message</label>
2951
+ <input type="text" id="dlg-msg" value="Custom dialogue message" />
2952
+ </div>
2953
+ <div class="sandbox-row">
2954
+ <label>Title</label>
2955
+ <input type="text" id="dlg-title" value="Dialogue Sandbox" />
2956
+ </div>
2957
+ <div class="sandbox-row">
2958
+ <label>Theme</label>
2959
+ <select id="dlg-theme">
2960
+ <option>Primary</option>
2961
+ <option>Success</option>
2962
+ <option>Warning</option>
2963
+ <option>Danger</option>
2964
+ <option>Info</option>
2965
+ <option>Light</option>
2966
+ <option>Dark</option>
2967
+ </select>
2968
+ </div>
2969
+ <div class="sandbox-row">
2970
+ <label>Width</label>
2971
+ <select id="dlg-width">
2972
+ <option value="">Normal</option>
2973
+ <option>sm</option>
2974
+ <option>lg</option>
2975
+ <option>xl</option>
2976
+ <option>fl</option>
2977
+ </select>
2978
+ </div>
2979
+ <div class="sandbox-row">
2980
+ <label>Layout</label>
2981
+ <input type="text" id="dlg-layout" value="hbf" />
2982
+ </div>
2983
+ <div class="sandbox-row">
2984
+ <label>Font family</label>
2985
+ <input
2986
+ type="text"
2987
+ id="dlg-fontfamily"
2988
+ value="'Quicksand', sans-serif"
2989
+ />
2990
+ </div>
2991
+ <div class="sandbox-row">
2992
+ <div class="chk-row">
2993
+ <input type="checkbox" id="dlg-showclose" checked /><label
2994
+ for="dlg-showclose"
2995
+ >Show close button</label
2996
+ >
2997
+ </div>
2998
+ </div>
2999
+ <div class="sandbox-row">
3000
+ <div class="chk-row">
3001
+ <input type="checkbox" id="dlg-showmin" /><label
3002
+ for="dlg-showmin"
3003
+ >Show minimize button</label
3004
+ >
3005
+ </div>
3006
+ </div>
3007
+ <div class="sandbox-row">
3008
+ <div class="chk-row">
3009
+ <input type="checkbox" id="dlg-showmaxmin" /><label
3010
+ for="dlg-showmaxmin"
3011
+ >Show maximize button</label
3012
+ >
3013
+ </div>
3014
+ </div>
3015
+ <div class="sandbox-row">
3016
+ <div class="chk-row">
3017
+ <input type="checkbox" id="dlg-autofocus" /><label
3018
+ for="dlg-autofocus"
3019
+ >Auto-focus</label
3020
+ >
3021
+ </div>
3022
+ </div>
3023
+ </div>
3024
+ <div class="sandbox-actions">
3025
+ <button class="pri" onclick="runDialogueSandbox()">
3026
+ Run Dialogue
3027
+ </button>
3028
+ </div>
3029
+ </div>
3030
+ </section>
3031
+
3032
+ <!-- ══════════════════════════ BREAKING ══════════════════════════ -->
3033
+ <section id="breaking">
3034
+ <h2>
3035
+ 7. Breaking
3036
+ <span class="badge"
3037
+ >Fnon.Breaking(opts) · Fnon.Breaking.Theme(headline?)</span
3038
+ >
3039
+ </h2>
3040
+ <p class="section-desc">
3041
+ Full-width notification bar anchored to top or bottom. Auto-dismisses
3042
+ after 5 seconds. Supports custom buttons and headlines.
3043
+ </p>
3044
+
3045
+ <div class="demo-grid">
3046
+ <div class="demo-card">
3047
+ <div class="card-body">
3048
+ <h3>Themed shortcuts</h3>
3049
+ <p class="desc">
3050
+ One arg: <code>Fnon.Breaking.Theme(headline)</code>.
3051
+ Auto-dismisses after 5s.
3052
+ </p>
3053
+ <div class="card-actions">
3054
+ <button
3055
+ class="dan"
3056
+ onclick="Fnon.Breaking.Danger('Breaking news')"
3057
+ >
3058
+ Danger
3059
+ </button>
3060
+ <button
3061
+ class="suc"
3062
+ onclick="Fnon.Breaking.Success('Good news!')"
3063
+ >
3064
+ Success
3065
+ </button>
3066
+ <button class="war" onclick="Fnon.Breaking.Warning('Heads up')">
3067
+ Warning
3068
+ </button>
3069
+ <button class="pri" onclick="Fnon.Breaking.Primary('Notice')">
3070
+ Primary
3071
+ </button>
3072
+ </div>
3073
+ </div>
3074
+ <div class="code">
3075
+ <span class="kw">Fnon</span>.Breaking.Danger(<span class="str"
3076
+ >'Breaking news'</span
3077
+ >)
3078
+ </div>
3079
+ </div>
3080
+
3081
+ <div class="demo-card">
3082
+ <div class="card-body">
3083
+ <h3>Full options + bottom anchor</h3>
3084
+ <p class="desc">
3085
+ headline, message, theme, anchor, buttons, timeout, closable,
3086
+ onClose.
3087
+ </p>
3088
+ <div class="card-actions">
3089
+ <button
3090
+ onclick="
3091
+ Fnon.Breaking({
3092
+ headline: 'BOTTOM',
3093
+ message: 'Bottom anchored!',
3094
+ anchor: 'bottom',
3095
+ theme: 'Success',
3096
+ timeout: 4000,
3097
+ })
3098
+ "
3099
+ >
3100
+ Bottom (4s)
3101
+ </button>
3102
+ <button
3103
+ onclick="
3104
+ Fnon.Breaking({
3105
+ headline: 'ACTIONS',
3106
+ message: 'With buttons',
3107
+ buttons: [
3108
+ {
3109
+ text: 'Click',
3110
+ style: 'Danger',
3111
+ callback: (c) => {
3112
+ Fnon.Hint.Info('Clicked!');
3113
+ },
3114
+ },
3115
+ ],
3116
+ timeout: 6000,
3117
+ })
3118
+ "
3119
+ >
3120
+ + Buttons (6s)
3121
+ </button>
3122
+ </div>
3123
+ </div>
3124
+ <div class="code">
3125
+ <span class="kw">Fnon</span>.Breaking({
3126
+ <span class="str">headline</span>:
3127
+ <span class="str">'BOTTOM'</span>,
3128
+ <span class="str">anchor</span>:
3129
+ <span class="str">'bottom'</span> })
3130
+ </div>
3131
+ </div>
3132
+
3133
+ <div class="demo-card">
3134
+ <div class="card-body">
3135
+ <h3>.Async + .Init</h3>
3136
+ <p class="desc">
3137
+ <code>.Async</code> resolves when bar closes.
3138
+ <code>.Init</code> changes global defaults.
3139
+ </p>
3140
+ <div class="card-actions">
3141
+ <button
3142
+ class="pri"
3143
+ onclick="
3144
+ (async () => {
3145
+ await Fnon.Breaking.Danger.Async('Resolved!');
3146
+ Fnon.Hint.Success('Closed!');
3147
+ })()
3148
+ "
3149
+ >
3150
+ Breaking.Async
3151
+ </button>
3152
+ <button
3153
+ onclick="
3154
+ Fnon.Breaking.Init({
3155
+ headline: 'ALERT',
3156
+ theme: 'Warning',
3157
+ timeout: 3000,
3158
+ });
3159
+ Fnon.Breaking('Defaults changed!');
3160
+ "
3161
+ >
3162
+ Apply .Init
3163
+ </button>
3164
+ <button
3165
+ onclick="
3166
+ Fnon.Breaking.Init({
3167
+ headline: 'BREAKING',
3168
+ theme: 'Danger',
3169
+ timeout: 5000,
3170
+ });
3171
+ Fnon.Hint.Info('Reset');
3172
+ "
3173
+ >
3174
+ Reset
3175
+ </button>
3176
+ </div>
3177
+ </div>
3178
+ <div class="code">
3179
+ <span class="kw">Fnon</span>.Breaking.<span class="fn">Init</span
3180
+ >({ <span class="str">headline</span>:
3181
+ <span class="str">'ALERT'</span> })
3182
+ </div>
3183
+ </div>
3184
+ </div>
3185
+
3186
+ <div class="ref-section">
3187
+ <h3>BreakingOptions</h3>
3188
+ <table class="ref-table">
3189
+ <tr>
3190
+ <th>Option</th>
3191
+ <th>Default</th>
3192
+ <th>Type</th>
3193
+ <th>Description</th>
3194
+ </tr>
3195
+ <tr>
3196
+ <td>headline</td>
3197
+ <td>'BREAKING'</td>
3198
+ <td>string</td>
3199
+ <td>Bold label text</td>
3200
+ </tr>
3201
+ <tr>
3202
+ <td>message</td>
3203
+ <td>—</td>
3204
+ <td>string | HTMLElement</td>
3205
+ <td>Body content</td>
3206
+ </tr>
3207
+ <tr>
3208
+ <td>theme</td>
3209
+ <td>'Danger'</td>
3210
+ <td>ThemeName</td>
3211
+ <td>Color scheme</td>
3212
+ </tr>
3213
+ <tr>
3214
+ <td>anchor</td>
3215
+ <td>'top'</td>
3216
+ <td>'top' | 'bottom'</td>
3217
+ <td>Where the bar attaches</td>
3218
+ </tr>
3219
+ <tr>
3220
+ <td>timeout</td>
3221
+ <td>5000</td>
3222
+ <td>number (ms)</td>
3223
+ <td>Auto-dismiss after N ms</td>
3224
+ </tr>
3225
+ <tr>
3226
+ <td>closable</td>
3227
+ <td>true</td>
3228
+ <td>boolean</td>
3229
+ <td>Show/hide the X button</td>
3230
+ </tr>
3231
+ <tr>
3232
+ <td>buttons</td>
3233
+ <td>—</td>
3234
+ <td>ButtonDef[]</td>
3235
+ <td>Action buttons in the bar</td>
3236
+ </tr>
3237
+ <tr>
3238
+ <td>fontFamily</td>
3239
+ <td>'Quicksand', sans-serif</td>
3240
+ <td>string</td>
3241
+ <td>Font family</td>
3242
+ </tr>
3243
+ <tr>
3244
+ <td>appendTarget</td>
3245
+ <td>'body'</td>
3246
+ <td>string | HTMLElement</td>
3247
+ <td>Mount container</td>
3248
+ </tr>
3249
+ <tr>
3250
+ <td>onClose</td>
3251
+ <td>—</td>
3252
+ <td>() => void</td>
3253
+ <td>Called when bar closes</td>
3254
+ </tr>
3255
+ </table>
3256
+ </div>
3257
+
3258
+ <p style="font-size: 0.82rem; color: #718096; margin: 0.5rem 0">
3259
+ <strong>Async:</strong>
3260
+ <code>Fnon.Breaking.Theme.Async(message?, headline?)</code> returns
3261
+ <code>Promise&lt;void&gt;</code> that resolves when the bar is closed
3262
+ (auto-dismiss or manual).
3263
+ </p>
3264
+
3265
+ <div class="sandbox">
3266
+ <h3>Sandbox &mdash; Breaking</h3>
3267
+ <p>Configure a breaking notification bar.</p>
3268
+ <div class="sandbox-grid">
3269
+ <div class="sandbox-row">
3270
+ <label>Headline</label>
3271
+ <input type="text" id="breaking-headline" value="BREAKING" />
3272
+ </div>
3273
+ <div class="sandbox-row">
3274
+ <label>Message</label>
3275
+ <input
3276
+ type="text"
3277
+ id="breaking-msg"
3278
+ value="This is a breaking notification!"
3279
+ />
3280
+ </div>
3281
+ <div class="sandbox-row">
3282
+ <label>Theme</label>
3283
+ <select id="breaking-theme">
3284
+ <option>Danger</option>
3285
+ <option>Primary</option>
3286
+ <option>Success</option>
3287
+ <option>Warning</option>
3288
+ <option>Info</option>
3289
+ <option>Light</option>
3290
+ <option>Dark</option>
3291
+ </select>
3292
+ </div>
3293
+ <div class="sandbox-row">
3294
+ <label>Anchor</label>
3295
+ <select id="breaking-anchor">
3296
+ <option>top</option>
3297
+ <option>bottom</option>
3298
+ </select>
3299
+ </div>
3300
+ <div class="sandbox-row">
3301
+ <label>Timeout (ms)</label>
3302
+ <input type="number" id="breaking-timeout" value="5000" />
3303
+ </div>
3304
+ <div class="sandbox-row">
3305
+ <div class="chk-row">
3306
+ <input type="checkbox" id="breaking-closable" checked /><label
3307
+ for="breaking-closable"
3308
+ >Closable (show X button)</label
3309
+ >
3310
+ </div>
3311
+ </div>
3312
+ </div>
3313
+ <div class="sandbox-actions">
3314
+ <button class="pri" onclick="runBreakingSandbox()">
3315
+ Run Breaking
3316
+ </button>
3317
+ </div>
3318
+ </div>
3319
+ </section>
3320
+
3321
+ <!-- ══════════════════════════ DATEPICKER ══════════════════════════ -->
3322
+ <section id="datepicker">
3323
+ <h2>
3324
+ 8. DatePicker
3325
+ <span class="badge">Fnon.DatePicker(opts) · Fnon.DP(opts)</span>
3326
+ </h2>
3327
+ <p class="section-desc">
3328
+ A modal date picker with month/year selector, range mode, optional
3329
+ time picker, and min/max constraints. Returns a Promise.
3330
+ </p>
3331
+
3332
+ <div class="demo-grid">
3333
+ <div class="demo-card">
3334
+ <div class="card-body">
3335
+ <h3>Single Date</h3>
3336
+ <p class="desc">
3337
+ Click a date &rarr; resolves immediately. Toggle dark mode first
3338
+ to test dark theme support.
3339
+ </p>
3340
+ <div class="card-actions">
3341
+ <button
3342
+ class="pri"
3343
+ onclick="
3344
+ Fnon.DatePicker({ title: 'Pick a date' })
3345
+ .then((d) => Fnon.Hint.Success(d.toLocaleDateString()))
3346
+ .catch(() => Fnon.Hint.Info('Cancelled'))
3347
+ "
3348
+ >
3349
+ Pick Date
3350
+ </button>
3351
+ <button
3352
+ onclick="
3353
+ Fnon.DatePicker({
3354
+ withTime: true,
3355
+ title: 'Select Date & Time',
3356
+ })
3357
+ .then((d) => Fnon.Hint.Success(d.toLocaleString()))
3358
+ .catch(() => Fnon.Hint.Info('Cancelled'))
3359
+ "
3360
+ >
3361
+ Date + Time
3362
+ </button>
3363
+ </div>
3364
+ </div>
3365
+ <div class="code">
3366
+ <span class="kw">Fnon</span>.<span class="fn">DatePicker</span>({
3367
+ <span class="str">title</span>:
3368
+ <span class="str">'Pick a date'</span> }) .<span class="fn"
3369
+ >then</span
3370
+ >(<span class="pr">d</span> <span class="kw">=&gt;</span>
3371
+ <span class="fn">console</span>.<span class="fn">log</span>(<span
3372
+ class="pr"
3373
+ >d</span
3374
+ >)) .<span class="fn">catch</span>(()
3375
+ <span class="kw">=&gt;</span>
3376
+ <span class="fn">console</span>.<span class="fn">log</span>(<span
3377
+ class="str"
3378
+ >'cancelled'</span
3379
+ >))
3380
+ </div>
3381
+ </div>
3382
+
3383
+ <div class="demo-card">
3384
+ <div class="card-body">
3385
+ <h3>Date Range</h3>
3386
+ <p class="desc">
3387
+ Click start date, then end date. Promise resolves with
3388
+ <code>{ start, end }</code>.
3389
+ </p>
3390
+ <div class="card-actions">
3391
+ <button
3392
+ class="pri"
3393
+ onclick="
3394
+ Fnon.DatePicker({ dateRange: true, title: 'Select Range' })
3395
+ .then((r) =>
3396
+ Fnon.Hint.Success(
3397
+ r.start.toLocaleDateString() +
3398
+ ' — ' +
3399
+ r.end.toLocaleDateString(),
3400
+ ),
3401
+ )
3402
+ .catch(() => Fnon.Hint.Info('Cancelled'))
3403
+ "
3404
+ >
3405
+ Pick Range
3406
+ </button>
3407
+ </div>
3408
+ </div>
3409
+ <div class="code">
3410
+ <span class="kw">Fnon</span>.<span class="fn">DatePicker</span>({
3411
+ <span class="str">dateRange</span>:
3412
+ <span class="num">true</span> }) .<span class="fn">then</span
3413
+ >(<span class="pr">r</span> <span class="kw">=&gt;</span>
3414
+ <span class="fn">console</span>.<span class="fn">log</span>(<span
3415
+ class="pr"
3416
+ >r</span
3417
+ >.<span class="pr">start</span>, <span class="pr">r</span>.<span
3418
+ class="pr"
3419
+ >end</span
3420
+ >))
3421
+ </div>
3422
+ </div>
3423
+
3424
+ <div class="demo-card">
3425
+ <div class="card-body">
3426
+ <h3>Constrained</h3>
3427
+ <p class="desc">
3428
+ Only dates within a range are selectable (<code>minDate</code> /
3429
+ <code>maxDate</code>).
3430
+ </p>
3431
+ <div class="card-actions">
3432
+ <button
3433
+ onclick="
3434
+ var n = new Date();
3435
+ Fnon.DatePicker({
3436
+ initDate: n,
3437
+ minDate: new Date(n.getFullYear(), n.getMonth(), 1),
3438
+ maxDate: new Date(n.getFullYear(), n.getMonth() + 1, 0),
3439
+ title: 'This month only',
3440
+ })
3441
+ .then((d) => Fnon.Hint.Success(d.toLocaleDateString()))
3442
+ .catch(() => Fnon.Hint.Info('Cancelled'));
3443
+ "
3444
+ >
3445
+ This Month Only
3446
+ </button>
3447
+ </div>
3448
+ </div>
3449
+ <div class="code">
3450
+ <span class="kw">Fnon</span>.<span class="fn">DatePicker</span>({
3451
+ <span class="str">minDate</span>: <span class="kw">new</span>
3452
+ <span class="fn">Date</span>(<span class="str">'2026-01-01'</span
3453
+ >), <span class="str">maxDate</span>: <span class="kw">new</span>
3454
+ <span class="fn">Date</span>(<span class="str">'2026-12-31'</span
3455
+ >) })
3456
+ </div>
3457
+ </div>
3458
+ </div>
3459
+
3460
+ <div class="ref-section">
3461
+ <h3>DatePickerOptions</h3>
3462
+ <pre>
3463
+ {
3464
+ <span style="color:#63b3ed">initDate</span>: <span style="color:#9ae6b4">Date</span> <span style="color:#6b7280">// Initial month to show (default: today)</span>
3465
+ <span style="color:#63b3ed">dateRange</span>: <span style="color:#9ae6b4">boolean</span> <span style="color:#6b7280">// Enable range mode (default: false)</span>
3466
+ <span style="color:#63b3ed">withTime</span>: <span style="color:#9ae6b4">boolean</span> <span style="color:#6b7280">// Show time picker after date selection (default: false)</span>
3467
+ <span style="color:#63b3ed">minDate</span>: <span style="color:#9ae6b4">Date</span> <span style="color:#6b7280">// Earliest selectable date</span>
3468
+ <span style="color:#63b3ed">maxDate</span>: <span style="color:#9ae6b4">Date</span> <span style="color:#6b7280">// Latest selectable date</span>
3469
+ <span style="color:#63b3ed">title</span>: <span style="color:#9ae6b4">string</span> <span style="color:#6b7280">// Modal title</span>
3470
+ <span style="color:#63b3ed">isDark</span>: <span style="color:#9ae6b4">boolean|()=&gt;boolean</span> <span style="color:#6b7280">// Enable dark mode for this instance</span>
3471
+ }
3472
+ </pre>
3473
+ </div>
3474
+
3475
+ <p style="font-size: 0.82rem; color: #718096; margin: 0.5rem 0">
3476
+ <strong>Async:</strong> Always async &mdash; returns
3477
+ <code>Promise&lt;Date | { start: Date; end: Date }&gt;</code>. Rejects
3478
+ when the modal is closed without selecting. Also available as
3479
+ <code>Fnon.DP(opts)</code>.
3480
+ </p>
3481
+
3482
+ <div class="sandbox">
3483
+ <h3>Sandbox &mdash; DatePicker</h3>
3484
+ <p>Configure and run a DatePicker instance.</p>
3485
+ <div class="sandbox-grid">
3486
+ <div class="sandbox-row">
3487
+ <label for="dp-title">Title</label>
3488
+ <input type="text" id="dp-title" value="Select a Date" />
3489
+ </div>
3490
+ <div class="sandbox-row">
3491
+ <div class="chk-row">
3492
+ <input type="checkbox" id="dp-range" /><label for="dp-range"
3493
+ >Date range mode</label
3494
+ >
3495
+ </div>
3496
+ </div>
3497
+ <div class="sandbox-row">
3498
+ <div class="chk-row">
3499
+ <input type="checkbox" id="dp-time" /><label for="dp-time"
3500
+ >With time picker</label
3501
+ >
3502
+ </div>
3503
+ </div>
3504
+ <div class="sandbox-row">
3505
+ <label for="dp-min">Min date (YYYY-MM-DD, optional)</label>
3506
+ <input type="date" id="dp-min" />
3507
+ </div>
3508
+ <div class="sandbox-row">
3509
+ <label for="dp-max">Max date (YYYY-MM-DD, optional)</label>
3510
+ <input type="date" id="dp-max" />
3511
+ </div>
3512
+ </div>
3513
+ <div class="sandbox-actions">
3514
+ <button class="pri" onclick="runDpSandbox()">
3515
+ Open DatePicker
3516
+ </button>
3517
+ </div>
3518
+ </div>
3519
+ </section>
3520
+
3521
+ <!-- ══════════════════════════ CONTEXT MENU ══════════════════════════ -->
3522
+ <section id="contextmenu">
3523
+ <h2>
3524
+ 9. ContextMenu
3525
+ <span class="badge">Fnon.ContextMenu(event, items[], globalOnClick?)</span>
3526
+ </h2>
3527
+ <p class="section-desc">
3528
+ Right-click or programmatic context menu with nested submenus,
3529
+ keyboard navigation, viewport-aware positioning, and dark mode support.
3530
+ </p>
3531
+
3532
+ <div class="demo-grid">
3533
+ <div class="demo-card">
3534
+ <div class="card-body" style="text-align: center;">
3535
+ <h3>Right-click anywhere</h3>
3536
+ <p class="desc">
3537
+ Right-click (or Ctrl+click) on this card to open the context menu.
3538
+ Try submenus and keyboard navigation (Arrow keys, Enter, Escape).
3539
+ </p>
3540
+ <div class="card-actions" style="justify-content: center;">
3541
+ <button class="pri" onclick="openContextMenu(event)">
3542
+ Open Context Menu
3543
+ </button>
3544
+ <button onclick="openContextMenuDark(event)">
3545
+ Dark menu
3546
+ </button>
3547
+ </div>
3548
+ </div>
3549
+ <div class="code">
3550
+ <span class="kw">Fnon</span>.<span class="fn">ContextMenu</span>(event, [
3551
+ <br />&nbsp;&nbsp;{ <span class="str">text</span>: <span class="str">'Edit'</span>, <span class="fn">onClick</span>: () => {} },
3552
+ <br />&nbsp;&nbsp;{ <span class="str">text</span>: <span class="str">'Share'</span>,
3553
+ <br />&nbsp;&nbsp;&nbsp;&nbsp;<span class="str">items</span>: [
3554
+ <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <span class="str">text</span>: <span class="str">'Twitter'</span> },
3555
+ <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <span class="str">text</span>: <span class="str">'Facebook'</span> },
3556
+ <br />&nbsp;&nbsp;&nbsp;&nbsp;] },
3557
+ <br />&nbsp;&nbsp;{ <span class="str">text</span>: <span class="str">'Delete'</span>, <span class="str">disabled</span>: <span class="pr">true</span> },
3558
+ <br />])
3559
+ </div>
3560
+ </div>
3561
+ </div>
3562
+
3563
+ <div class="ref-section">
3564
+ <h3>ContextMenuItem</h3>
3565
+ <table class="ref-table">
3566
+ <tr>
3567
+ <th>Prop</th>
3568
+ <th>Type</th>
3569
+ <th>Default</th>
3570
+ <th>Description</th>
3571
+ </tr>
3572
+ <tr>
3573
+ <td>label</td>
3574
+ <td>string</td>
3575
+ <td>—</td>
3576
+ <td>Menu item text</td>
3577
+ </tr>
3578
+ <tr>
3579
+ <td>onClick</td>
3580
+ <td>() => void</td>
3581
+ <td>—</td>
3582
+ <td>Called when item is chosen. Ignored if <code>items</code> is set.</td>
3583
+ </tr>
3584
+ <tr>
3585
+ <td>items</td>
3586
+ <td>ContextMenuItem[]</td>
3587
+ <td>—</td>
3588
+ <td>Nested submenu (takes priority over onClick)</td>
3589
+ </tr>
3590
+ <tr>
3591
+ <td>disabled</td>
3592
+ <td>boolean</td>
3593
+ <td>false</td>
3594
+ <td>Gray out and prevent interaction</td>
3595
+ </tr>
3596
+ <tr>
3597
+ <td>divider</td>
3598
+ <td>boolean</td>
3599
+ <td>false</td>
3600
+ <td>Render as a visual separator</td>
3601
+ </tr>
3602
+ <tr>
3603
+ <td>icon</td>
3604
+ <td>string</td>
3605
+ <td>—</td>
3606
+ <td>Optional icon HTML (e.g. SVG) prepended to label</td>
3607
+ </tr>
3608
+ </table>
3609
+ </div>
3610
+
3611
+ <p style="font-size: 0.82rem; color: #718096; margin: 0.5rem 0">
3612
+ <strong>Keyboard:</strong> Arrow keys to navigate, Enter/Space to select,
3613
+ Escape to close, ArrowRight to open submenu, ArrowLeft to close submenu.
3614
+ <strong>Alias:</strong> <code>Fnon.CM(event, items, globalOnClick?)</code>
3615
+ </p>
3616
+ </section>
3617
+
3618
+ <hr />
3619
+ <footer>
3620
+ Fnon.js v2.0.0 &mdash; Interactive Playground &middot; MIT License
3621
+ &middot;
3622
+ <a
3623
+ href="https://github.com/anomalyco/fnon"
3624
+ target="_blank"
3625
+ style="color: #029eff"
3626
+ >GitHub</a
3627
+ >
3628
+ </footer>
3629
+ </div>
3630
+
3631
+ <script src="fnon.es.js"></script>
3632
+ <script src="fnon.min.js"></script>
3633
+ <script>
3634
+ // ── Helper: get value or undefined ──
3635
+ function val(id) {
3636
+ var e = document.getElementById(id);
3637
+ return e ? e.value : undefined;
3638
+ }
3639
+ function num(id) {
3640
+ var v = parseFloat(val(id));
3641
+ return isNaN(v) ? undefined : v;
3642
+ }
3643
+ function chk(id) {
3644
+ var e = document.getElementById(id);
3645
+ return e ? e.checked : false;
3646
+ }
3647
+ function themeFn(theme) {
3648
+ return Fnon.Hint[theme] || Fnon.Hint.Primary;
3649
+ }
3650
+
3651
+ // ── Hint sandbox ──
3652
+ function runHintSandbox() {
3653
+ var theme = val("hint-theme") || "Primary";
3654
+ var msg = val("hint-msg") || "Hello!";
3655
+ var opts = {};
3656
+ var title = val("hint-title");
3657
+ if (title) opts.title = title;
3658
+ var pos = val("hint-position");
3659
+ if (pos) opts.position = pos;
3660
+ var to = num("hint-timeout");
3661
+ if (to !== undefined) opts.timeOut = to;
3662
+ opts.closable = chk("hint-closable");
3663
+ var w = val("hint-width");
3664
+ if (w) opts.width = w;
3665
+ var fs = val("hint-fontsize");
3666
+ if (fs) opts.fontSize = fs;
3667
+ var pc = val("hint-progresscolor");
3668
+ if (pc) opts.progressColor = pc;
3669
+ Fnon.Hint[theme](msg, opts);
3670
+ }
3671
+
3672
+ // ── Wait sandbox ──
3673
+ function runWaitSandbox() {
3674
+ var type = val("wait-type") || "Orbit";
3675
+ var text = val("wait-text") || "";
3676
+ var opts = {};
3677
+ var sc = val("wait-svgcolor");
3678
+ if (sc) opts.svgColor = sc;
3679
+ var cs = val("wait-containersize");
3680
+ if (cs) opts.containerSize = cs;
3681
+ var ff = val("wait-fontfamily");
3682
+ if (ff) opts.fontFamily = ff;
3683
+ opts.clickToClose = chk("wait-clicktoclose");
3684
+ Fnon.Wait[type](text, opts);
3685
+ }
3686
+
3687
+ // ── Box sandbox ──
3688
+ function runBoxSandbox() {
3689
+ var type = val("box-type") || "Orbit";
3690
+ var sel = val("box-selector") || "#box-target";
3691
+ var text = val("box-text") || "";
3692
+ var opts = {};
3693
+ var sc = val("box-svgcolor");
3694
+ if (sc) opts.svgColor = sc;
3695
+ var sw = num("box-svgw");
3696
+ var sh = num("box-svgh");
3697
+ if (sw !== undefined || sh !== undefined)
3698
+ opts.svgSize = { w: sw || 40, h: sh || 40 };
3699
+ Fnon.Box[type](sel, text, Object.keys(opts).length ? opts : undefined);
3700
+ }
3701
+
3702
+ // ── Alert sandbox ──
3703
+ function runAlertSandbox() {
3704
+ var theme = val("alert-theme") || "Primary";
3705
+ var msg = val("alert-msg") || "Hello!";
3706
+ var opts = { message: msg };
3707
+ var title = val("alert-title");
3708
+ if (title) opts.title = title;
3709
+ var btn = val("alert-btnok");
3710
+ if (btn) opts.btnOkText = btn;
3711
+ var w = val("alert-width");
3712
+ if (w) opts.width = w;
3713
+ var ff = val("alert-fontfamily");
3714
+ if (ff) opts.fontFamily = ff;
3715
+ opts.closeButton = chk("alert-showclose");
3716
+ opts.autoFocus = chk("alert-autofocus");
3717
+ Fnon.Alert[theme](opts);
3718
+ }
3719
+
3720
+ // ── Ask sandbox ──
3721
+ function runAskSandbox() {
3722
+ var theme = val("ask-theme") || "Primary";
3723
+ var msg = val("ask-msg") || "Proceed?";
3724
+ var opts = { message: msg };
3725
+ var title = val("ask-title");
3726
+ if (title) opts.title = title;
3727
+ var ok = val("ask-btnok");
3728
+ if (ok) opts.btnOkText = ok;
3729
+ var cancel = val("ask-btncancel");
3730
+ if (cancel) opts.btnCancelText = cancel;
3731
+ var w = val("ask-width");
3732
+ if (w) opts.width = w;
3733
+ opts.closeButton = chk("ask-showclose");
3734
+ Fnon.Ask[theme](opts);
3735
+ }
3736
+
3737
+ // ── Dialogue sandbox ──
3738
+ function runDialogueSandbox() {
3739
+ var msg = val("dlg-msg") || "Dialogue message";
3740
+ var opts = { message: msg };
3741
+ var title = val("dlg-title");
3742
+ if (title) opts.title = title;
3743
+ var w = val("dlg-width");
3744
+ if (w) opts.width = w;
3745
+ var lay = val("dlg-layout");
3746
+ if (lay) opts.layout = lay;
3747
+ var ff = val("dlg-fontfamily");
3748
+ if (ff) opts.fontFamily = ff;
3749
+ opts.closeButton = chk("dlg-showclose");
3750
+ opts.showMin = chk("dlg-showmin");
3751
+ opts.showMaxMin = chk("dlg-showmaxmin");
3752
+ opts.autoFocus = chk("dlg-autofocus");
3753
+ opts.buttons = [
3754
+ {
3755
+ text: "OK",
3756
+ style: val("dlg-theme") || "Primary",
3757
+ callback: function (c) {
3758
+ c();
3759
+ },
3760
+ },
3761
+ ];
3762
+ Fnon.Dialogue(opts);
3763
+ }
3764
+
3765
+ // ── Breaking sandbox ──
3766
+ function runBreakingSandbox() {
3767
+ var headline = val("breaking-headline") || "BREAKING";
3768
+ var msg = val("breaking-msg") || "Notification!";
3769
+ var opts = { headline: headline, message: msg };
3770
+ var th = val("breaking-theme");
3771
+ if (th) opts.theme = th;
3772
+ var anc = val("breaking-anchor");
3773
+ if (anc) opts.anchor = anc;
3774
+ var to = num("breaking-timeout");
3775
+ if (to !== undefined) opts.timeout = to;
3776
+ opts.closable = chk("breaking-closable");
3777
+ Fnon.Breaking(opts);
3778
+ }
3779
+
3780
+ // ── DatePicker sandbox ──
3781
+ function runDpSandbox() {
3782
+ var title = val("dp-title") || "Select a Date";
3783
+ var opts = { title: title };
3784
+ if (chk("dp-range")) opts.dateRange = true;
3785
+ if (chk("dp-time")) opts.withTime = true;
3786
+ var min = val("dp-min");
3787
+ if (min) opts.minDate = new Date(min + "T00:00:00");
3788
+ var max = val("dp-max");
3789
+ if (max) opts.maxDate = new Date(max + "T00:00:00");
3790
+ var isDark = document.documentElement.dataset.theme === "dark";
3791
+ if (isDark) {
3792
+ opts.isDark = true;
3793
+ opts.darkBgColor = "#1e293b";
3794
+ }
3795
+ Fnon.DatePicker(opts)
3796
+ .then(function (r) {
3797
+ if (r && r.start) {
3798
+ Fnon.Hint.Success(
3799
+ r.start.toLocaleDateString() +
3800
+ " — " +
3801
+ r.end.toLocaleDateString(),
3802
+ );
3803
+ } else {
3804
+ Fnon.Hint.Success(r.toLocaleDateString());
3805
+ }
3806
+ })
3807
+ .catch(function () {
3808
+ Fnon.Hint.Info("Cancelled");
3809
+ });
3810
+ }
3811
+
3812
+ // ── ContextMenu demo ──
3813
+ function openContextMenu(e) {
3814
+ e.preventDefault();
3815
+ Fnon.ContextMenu(e, [
3816
+ { text: 'Rename', onClick: function() { Fnon.Hint.Success('Renamed!'); } },
3817
+ { text: 'Duplicate', onClick: function() { Fnon.Hint.Primary('Duplicated'); } },
3818
+ { text: 'Share', items: [
3819
+ { text: 'Twitter', onClick: function() { Fnon.Hint.Info('Shared to Twitter'); } },
3820
+ { text: 'Facebook', onClick: function() { Fnon.Hint.Info('Shared to Facebook'); } },
3821
+ { text: 'Copy Link', onClick: function() { Fnon.Hint.Light('Link copied!'); } },
3822
+ ]},
3823
+ { text: 'Delete', disabled: true },
3824
+ ]);
3825
+ }
3826
+ function openContextMenuDark(e) {
3827
+ e.preventDefault();
3828
+ Fnon.Init({ isDark: true, darkBgColor: '#1e293b' });
3829
+ Fnon.ContextMenu(e, [
3830
+ { text: 'Edit', onClick: function() { Fnon.Hint.Primary('Edit'); } },
3831
+ { text: 'Delete', onClick: function() { Fnon.Hint.Danger('Deleted!'); } },
3832
+ ], function(item) {
3833
+ Fnon.Hint.Warning('Global: ' + item.text);
3834
+ });
3835
+ }
3836
+
3837
+ // ── Dark/light theme toggle ──
3838
+ function getPreferredTheme() {
3839
+ const saved = localStorage.getItem("fnon-demo-theme");
3840
+ if (saved) return saved;
3841
+ return window.matchMedia("(prefers-color-scheme: dark)").matches
3842
+ ? "dark"
3843
+ : "light";
3844
+ }
3845
+ function syncFnonTheme(isDark) {
3846
+ if (typeof Fnon !== "undefined") {
3847
+ Fnon.Init({ isDark, darkBgColor: "#1e293b" });
3848
+ }
3849
+ }
3850
+ function applyTheme(theme) {
3851
+ document.documentElement.dataset.theme = theme;
3852
+ document.getElementById("theme-toggle").textContent =
3853
+ theme === "dark" ? "☀️" : "🌙";
3854
+ localStorage.setItem("fnon-demo-theme", theme);
3855
+ syncFnonTheme(theme === "dark");
3856
+ }
3857
+ function toggleTheme() {
3858
+ const next =
3859
+ document.documentElement.dataset.theme === "dark" ? "light" : "dark";
3860
+ applyTheme(next);
3861
+ }
3862
+ applyTheme(getPreferredTheme());
3863
+
3864
+ // ── RTL/LTR direction toggle ──
3865
+ function syncFnonDir(dir) {
3866
+ if (typeof Fnon !== "undefined") {
3867
+ Fnon.Init({ dir });
3868
+ }
3869
+ }
3870
+ function toggleRtl() {
3871
+ const next = document.documentElement.dir === "rtl" ? "ltr" : "rtl";
3872
+ document.documentElement.dir = next;
3873
+ document.getElementById("rtl-toggle").textContent =
3874
+ next === "rtl" ? "←" : "→";
3875
+ localStorage.setItem("fnon-demo-dir", next);
3876
+ syncFnonDir(next);
3877
+ }
3878
+ (function initRtl() {
3879
+ const saved = localStorage.getItem("fnon-demo-dir");
3880
+ const dir = saved || "ltr";
3881
+ document.documentElement.dir = dir;
3882
+ document.getElementById("rtl-toggle").textContent =
3883
+ dir === "rtl" ? "←" : "→";
3884
+ if (dir === "rtl") syncFnonDir("rtl");
3885
+ })();
3886
+ </script>
3887
+ </body>
3888
+ </html>