@yuneta/gobj-ui 1.0.1 → 2.2.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.
Files changed (46) hide show
  1. package/README.md +40 -363
  2. package/dist/gobj-ui.cjs.js +11154 -5506
  3. package/dist/gobj-ui.es.js +11131 -5508
  4. package/index.js +41 -14
  5. package/package.json +11 -9
  6. package/src/c_g6_nodes_tree.js +6 -1
  7. package/src/c_yui_form.js +1 -1
  8. package/src/c_yui_gobj_tree_js.js +1 -1
  9. package/src/c_yui_json_graph.js +1 -1
  10. package/src/c_yui_main.js +3 -3
  11. package/src/c_yui_map.js +6 -1
  12. package/src/c_yui_nav.js +881 -0
  13. package/src/c_yui_pager.js +545 -0
  14. package/src/c_yui_routing.css +1 -1
  15. package/src/c_yui_routing.js +1 -1
  16. package/src/c_yui_shell.css +571 -0
  17. package/src/c_yui_shell.js +2474 -0
  18. package/src/c_yui_tabs.js +1 -1
  19. package/src/c_yui_treedb_graph.js +35 -9
  20. package/src/c_yui_treedb_topic_with_form.js +1 -1
  21. package/src/c_yui_treedb_topics.js +36 -8
  22. package/src/c_yui_uplot.js +1 -1
  23. package/src/c_yui_window.js +242 -21
  24. package/src/c_yui_window_manager.js +602 -0
  25. package/src/c_yui_wizard.js +612 -0
  26. package/src/pager_helpers.js +138 -0
  27. package/src/pager_helpers.test.js +140 -0
  28. package/src/route_resolver.js +53 -0
  29. package/src/route_resolver.test.js +82 -0
  30. package/src/shell_focus_trap.js +123 -0
  31. package/src/shell_focus_trap.test.js +299 -0
  32. package/src/shell_modals.js +445 -0
  33. package/src/shell_show_on.js +91 -0
  34. package/src/shell_show_on.test.js +86 -0
  35. package/src/shell_toolbar_helpers.js +221 -0
  36. package/src/shell_toolbar_helpers.test.js +207 -0
  37. package/src/tabulator.css +53 -0
  38. package/src/wizard_helpers.js +117 -0
  39. package/src/wizard_helpers.test.js +122 -0
  40. package/src/yui_dev.js +1257 -362
  41. package/src/yui_icons.css +5 -0
  42. package/src/yui_inputs.css +32 -0
  43. package/src/yui_inputs.js +71 -0
  44. package/vite-plugin-yuneta-html.js +2 -2
  45. package/skeleton/config.json +0 -20
  46. package/skeleton/index.html +0 -37
@@ -0,0 +1,571 @@
1
+ /***********************************************************************
2
+ * c_yui_shell.css — Layers + zones + nav layouts
3
+ *
4
+ * Depends on Bulma being loaded (uses .tabs, .menu, .level, is-*, etc.)
5
+ ***********************************************************************/
6
+
7
+ /*============================================================
8
+ * Root + layers
9
+ *============================================================*/
10
+ .yui-shell {
11
+ position: fixed;
12
+ inset: 0;
13
+ width: 100%;
14
+ height: 100%;
15
+ }
16
+ .yui-layer {
17
+ position: absolute;
18
+ inset: 0;
19
+ pointer-events: none;
20
+ }
21
+ .yui-layer > * {
22
+ pointer-events: auto;
23
+ }
24
+
25
+ .yui-layer-base { z-index: 1; }
26
+ .yui-layer-overlay { z-index: 15; }
27
+ .yui-layer-popup { z-index: 20; }
28
+ .yui-layer-modal { z-index: 99; }
29
+ .yui-layer-notification { z-index: 120; }
30
+ .yui-layer-loading { z-index: 150; }
31
+
32
+ .yui-layer-notification,
33
+ .yui-layer-loading {
34
+ pointer-events: none;
35
+ }
36
+ .yui-layer-loading.is-active,
37
+ .yui-layer-modal.is-active { pointer-events: auto; }
38
+
39
+ /*============================================================
40
+ * Base grid (zones)
41
+ *
42
+ * grid-template-areas:
43
+ * top top top
44
+ * left topsub right
45
+ * left center right
46
+ * botsub botsub botsub
47
+ * bottom bottom bottom
48
+ *
49
+ * `top-sub` sits at the TOP OF THE CONTENT column (above
50
+ * `center`), not as a full-width band — so on desktop the
51
+ * secondary nav (tabs) does not span over the `left` primary
52
+ * menu, which keeps its full height beside the content. On
53
+ * mobile `left`/`right` are hidden, their `auto` columns
54
+ * collapse to 0, and `top-sub`/`center` take the full width.
55
+ *============================================================*/
56
+ .yui-base-grid {
57
+ display: grid;
58
+ grid-template-columns: auto minmax(0, 1fr) auto;
59
+ grid-template-rows: auto auto minmax(0, 1fr) auto auto;
60
+ grid-template-areas:
61
+ "top top top"
62
+ "left topsub right"
63
+ "left center right"
64
+ "botsub botsub botsub"
65
+ "bottom bottom bottom";
66
+ width: 100%;
67
+ height: 100%;
68
+ }
69
+
70
+ .yui-zone {
71
+ min-width: 0;
72
+ min-height: 0;
73
+ overflow: auto;
74
+ display: flex;
75
+ flex-direction: column;
76
+ }
77
+ .yui-zone-top,
78
+ .yui-zone-bottom,
79
+ .yui-zone-top-sub,
80
+ .yui-zone-bottom-sub {
81
+ overflow: hidden;
82
+ }
83
+ .yui-zone-left,
84
+ .yui-zone-right {
85
+ border-inline: 1px solid var(--bulma-border-weak, #dbdbdb);
86
+ }
87
+ .yui-zone-top,
88
+ .yui-zone-top-sub {
89
+ border-bottom: 1px solid var(--bulma-border-weak, #dbdbdb);
90
+ }
91
+ .yui-zone-bottom,
92
+ .yui-zone-bottom-sub {
93
+ border-top: 1px solid var(--bulma-border-weak, #dbdbdb);
94
+ }
95
+
96
+ /* The center zone is a stage: its routed gobj $container fills it.
97
+ * flex-shrink MUST be 0: with shrink:1 + min-height:0 a view whose
98
+ * content is taller than the zone gets shrunk below its content,
99
+ * the content overflows the (short) box with no scrollbar, and the
100
+ * view background only covers that short box (white/blank below).
101
+ * shrink:0 → short content: flex-grow fills the zone (bg covers
102
+ * all); tall content: the box keeps its content height and the
103
+ * zone (overflow:auto) scrolls. Correct at every screen size. */
104
+ .yui-zone-center > * {
105
+ flex: 1 0 auto;
106
+ min-height: 0;
107
+ }
108
+
109
+ /*============================================================
110
+ * Show-on visibility helpers
111
+ *
112
+ * Bulma has is-hidden-mobile/-tablet/-desktop/-widescreen/-fullhd
113
+ * but does NOT have "tablet-only" or "desktop-only" hiders per
114
+ * single breakpoint slice. Define ours.
115
+ *============================================================*/
116
+ @media (max-width: 768px) {
117
+ .yui-hidden-mobile { display: none !important; }
118
+ }
119
+ @media (min-width: 769px) and (max-width: 1023px) {
120
+ .yui-hidden-tablet-only { display: none !important; }
121
+ }
122
+ @media (min-width: 1024px) and (max-width: 1215px) {
123
+ .yui-hidden-desktop-only { display: none !important; }
124
+ }
125
+ @media (min-width: 1216px) and (max-width: 1407px) {
126
+ .yui-hidden-widescreen-only { display: none !important; }
127
+ }
128
+ @media (min-width: 1408px) {
129
+ .yui-hidden-fullhd { display: none !important; }
130
+ }
131
+
132
+ /*============================================================
133
+ * Nav layouts
134
+ *============================================================*/
135
+
136
+ /*--- vertical (Bulma .menu) ---*/
137
+ .yui-nav-vertical {
138
+ width: 100%;
139
+ }
140
+ .yui-nav-vertical .menu-list a { display: flex; align-items: center; gap: 0.5rem; }
141
+ .yui-nav-vertical .yui-nav-label { white-space: nowrap; }
142
+
143
+ /*--- icon-bar (bottom/top icon bar) ---*/
144
+ .yui-nav-iconbar {
145
+ display: flex;
146
+ justify-content: space-around;
147
+ align-items: stretch;
148
+ margin: 0;
149
+ padding: 0.25rem 0;
150
+ width: 100%;
151
+ background: var(--bulma-scheme-main, #fff);
152
+ }
153
+ .yui-nav-iconbar .level-item { flex: 1 1 0; justify-content: center; }
154
+ .yui-nav-iconbar .yui-nav-item {
155
+ display: flex;
156
+ flex-direction: column;
157
+ align-items: center;
158
+ justify-content: center;
159
+ padding: 0.25rem 0.5rem;
160
+ gap: 0.125rem;
161
+ min-width: 0;
162
+ line-height: 1.1;
163
+ text-align: center;
164
+ color: var(--bulma-text, inherit);
165
+ text-decoration: none;
166
+ border-radius: 0.375rem;
167
+ }
168
+ .yui-nav-iconbar .yui-nav-item.is-active,
169
+ .yui-nav-iconbar li.is-active > .yui-nav-item {
170
+ color: var(--bulma-link, #485fc7);
171
+ background-color: var(--bulma-link-light, rgba(72,95,199,0.1));
172
+ }
173
+
174
+ /*--- tabs (Bulma .tabs) ---*/
175
+ .yui-nav-tabs .tabs ul { flex-wrap: wrap; }
176
+
177
+ /*--- closable tab: trailing ✕ affordance (item.closable) ---*/
178
+ .yui-nav-close {
179
+ opacity: 0.55;
180
+ cursor: pointer;
181
+ border-radius: 3px;
182
+ }
183
+ .yui-nav-close:hover {
184
+ opacity: 1;
185
+ background: var(--bulma-danger, #f14668);
186
+ color: #fff;
187
+ }
188
+
189
+ /*--- per-item state class (item.class) — generic disconnected tint ---*/
190
+ .yui-nav-tabs .tabs li.yui-nav-disconnected a { color: var(--bulma-danger, #f14668); }
191
+
192
+ /*--- stacked item (icon above label) ---*/
193
+ .yui-nav-stacked {
194
+ display: inline-flex;
195
+ flex-direction: column;
196
+ align-items: center;
197
+ gap: 0.125rem;
198
+ }
199
+
200
+ /*--- drawer ---*/
201
+ .yui-drawer {
202
+ position: fixed;
203
+ inset: 0;
204
+ z-index: 18;
205
+ display: none;
206
+ }
207
+ .yui-drawer.is-active { display: block; }
208
+ .yui-drawer-backdrop {
209
+ position: absolute;
210
+ inset: 0;
211
+ background: rgba(0,0,0,0.4);
212
+ }
213
+ .yui-drawer-panel {
214
+ position: absolute;
215
+ top: 0;
216
+ bottom: 0;
217
+ left: 0;
218
+ width: 18rem;
219
+ max-width: 85vw;
220
+ background: var(--bulma-scheme-main, #fff);
221
+ box-shadow: 2px 0 8px rgba(0,0,0,0.2);
222
+ overflow: auto;
223
+ }
224
+
225
+ /*--- accordion ---*/
226
+ .yui-nav-accordion .yui-accordion-head {
227
+ cursor: pointer;
228
+ user-select: none;
229
+ }
230
+ .yui-nav-accordion .yui-accordion-head::after {
231
+ content: "▾";
232
+ float: right;
233
+ transition: transform 0.15s ease;
234
+ transform: rotate(-90deg);
235
+ }
236
+ .yui-nav-accordion .yui-accordion-head.is-open::after {
237
+ transform: none;
238
+ }
239
+
240
+ /*--- section header / divider (decorative items inside vertical/submenu) ---*/
241
+ .yui-nav .menu-list .yui-nav-section-header {
242
+ list-style: none;
243
+ margin: 0.75rem 0 0.125rem;
244
+ padding: 0.125rem 0.5rem;
245
+ }
246
+ .yui-nav .menu-list .yui-nav-section-header:first-child {
247
+ margin-top: 0.125rem;
248
+ }
249
+ .yui-nav .menu-list .yui-nav-section-header-label {
250
+ display: inline-block;
251
+ font-size: 0.7rem;
252
+ text-transform: uppercase;
253
+ letter-spacing: 0.06em;
254
+ font-weight: 600;
255
+ color: var(--bulma-text-weak, #6b7280);
256
+ }
257
+ .yui-nav .menu-list .yui-nav-section-divider {
258
+ list-style: none;
259
+ height: 1px;
260
+ margin: 0.5rem 0.5rem;
261
+ background-color: var(--bulma-border-weak, #dbdbdb);
262
+ }
263
+
264
+ /*============================================================
265
+ * Active item highlight (Bulma already styles .is-active
266
+ * on .menu-list a and on .tabs li; we just ensure a
267
+ * consistent look when we toggle it ourselves).
268
+ *============================================================*/
269
+ .yui-nav .menu-list a.is-active {
270
+ background-color: var(--bulma-link, #485fc7);
271
+ color: var(--bulma-link-invert, #fff);
272
+ }
273
+
274
+ /*============================================================
275
+ * Toolbar
276
+ *============================================================*/
277
+ .yui-toolbar {
278
+ min-height: 2.5rem;
279
+ }
280
+ /* Fixed app toolbar: ALWAYS a single row. Bulma's .navbar stacks
281
+ * .navbar-end below .navbar-brand on touch (<1024px) because the end
282
+ * group is not wrapped in a burger .navbar-menu. For a fixed shell
283
+ * toolbar that is wrong — override Bulma's responsive navbar so the
284
+ * start and end groups stay on one line at every breakpoint. */
285
+ .yui-toolbar.navbar {
286
+ display: flex;
287
+ flex-wrap: nowrap;
288
+ align-items: center;
289
+ }
290
+ .yui-toolbar .yui-toolbar-start {
291
+ display: flex;
292
+ flex: 1 1 auto;
293
+ min-width: 0;
294
+ align-items: center;
295
+ flex-wrap: nowrap;
296
+ }
297
+ .yui-toolbar .yui-toolbar-end {
298
+ display: flex;
299
+ flex: 0 0 auto;
300
+ align-items: center;
301
+ flex-wrap: nowrap;
302
+ margin-left: auto;
303
+ }
304
+ /* Phones: keep the toolbar compact — drop the brand wordmark and the
305
+ * item text labels, keep the small logo + icons (icon-only toolbar). */
306
+ @media screen and (max-width: 768px) {
307
+ .yui-toolbar-brand-wordmark,
308
+ .yui-toolbar-item-label {
309
+ display: none;
310
+ }
311
+ }
312
+ .yui-toolbar-item {
313
+ background: transparent;
314
+ border: none;
315
+ cursor: pointer;
316
+ /* Generous touch target: comfortable for a thumb (>= ~44px). */
317
+ padding: 0.5rem 0.85rem;
318
+ min-width: 2.75rem;
319
+ min-height: 2.75rem;
320
+ display: inline-flex;
321
+ align-items: center;
322
+ justify-content: center;
323
+ gap: 0.4rem;
324
+ color: var(--bulma-text, inherit);
325
+ }
326
+ .yui-toolbar-item .icon {
327
+ /* yi-* glyphs are 1em (mask) — scale them up so they are easy
328
+ * to hit/read in the toolbar. */
329
+ font-size: 1.35rem;
330
+ }
331
+ .yui-toolbar-item:hover {
332
+ background-color: var(--bulma-scheme-main-bis, rgba(0,0,0,0.04));
333
+ }
334
+
335
+ /*============================================================
336
+ * Toolbar — type:"brand"
337
+ * Logo image + wordmark text. Renders as <button> when
338
+ * `action` is set, otherwise as a passive <div>.
339
+ *============================================================*/
340
+ .yui-toolbar-brand {
341
+ gap: 0.5rem;
342
+ text-decoration: none;
343
+ color: inherit;
344
+ }
345
+ .yui-toolbar-brand-logo {
346
+ height: 1.75rem;
347
+ width: auto;
348
+ flex: 0 0 auto;
349
+ display: block;
350
+ }
351
+ .yui-toolbar-brand-wordmark {
352
+ font-weight: 600;
353
+ font-size: 1.05rem;
354
+ letter-spacing: 0.01em;
355
+ white-space: nowrap;
356
+ }
357
+
358
+ /*============================================================
359
+ * Toolbar — type:"avatar"
360
+ * Circular badge populated by the host-registered provider
361
+ * via yui_shell_set_avatar_provider().
362
+ *============================================================*/
363
+ .yui-toolbar-avatar .yui-avatar {
364
+ display: inline-flex;
365
+ align-items: center;
366
+ justify-content: center;
367
+ width: 2rem;
368
+ height: 2rem;
369
+ border-radius: 9999px;
370
+ background-color: var(--bulma-link, #485fc7);
371
+ color: var(--bulma-link-invert, #ffffff);
372
+ font-size: 0.85rem;
373
+ font-weight: 600;
374
+ text-transform: uppercase;
375
+ line-height: 1;
376
+ }
377
+
378
+ /*============================================================
379
+ * Toolbar — type:"connection"
380
+ * Backend-connection status dot. State toggled by the
381
+ * host via yui_shell_set_connection_state(shell, bool).
382
+ *============================================================*/
383
+ .yui-toolbar-conn .yui-conn-dot {
384
+ display: inline-block;
385
+ width: 0.7rem;
386
+ height: 0.7rem;
387
+ border-radius: 9999px;
388
+ background-color: #9aa5b1;
389
+ box-shadow: 0 0 0 2px rgba(154, 165, 177, 0.25);
390
+ transition: background-color 0.15s ease, box-shadow 0.15s ease;
391
+ }
392
+ .yui-toolbar-conn.is-connected .yui-conn-dot {
393
+ background-color: #1FAE6F;
394
+ box-shadow: 0 0 0 2px rgba(31, 174, 111, 0.30);
395
+ }
396
+ .yui-toolbar-conn.is-disconnected .yui-conn-dot {
397
+ background-color: #D64545;
398
+ box-shadow: 0 0 0 2px rgba(214, 69, 69, 0.30);
399
+ }
400
+
401
+ /*============================================================
402
+ * Toolbar — action.type:"dropdown"
403
+ * Panel anchored to the trigger; mounted on the popup
404
+ * layer so it sits above the base grid but below modals.
405
+ *============================================================*/
406
+ .yui-toolbar-dropdown-panel {
407
+ background-color: var(--bulma-scheme-main, #ffffff);
408
+ color: var(--bulma-text, inherit);
409
+ border: 1px solid var(--bulma-border, rgba(0, 0, 0, 0.1));
410
+ border-radius: 0.375rem;
411
+ box-shadow: 0 8px 16px rgba(0, 0, 0, 0.12);
412
+ min-width: 12rem;
413
+ padding: 0.25rem 0;
414
+ margin-top: 0.25rem;
415
+ }
416
+ .yui-toolbar-dropdown-item {
417
+ display: flex;
418
+ align-items: center;
419
+ gap: 0.5rem;
420
+ width: 100%;
421
+ border: none;
422
+ background: transparent;
423
+ padding: 0.5rem 0.875rem;
424
+ cursor: pointer;
425
+ text-align: left;
426
+ color: inherit;
427
+ font: inherit;
428
+ }
429
+ .yui-toolbar-dropdown-item:hover {
430
+ background-color: var(--bulma-scheme-main-bis, rgba(0, 0, 0, 0.06));
431
+ }
432
+ .yui-toolbar-dropdown-item:focus-visible {
433
+ outline: 2px solid var(--bulma-link, #485fc7);
434
+ outline-offset: -2px;
435
+ }
436
+ .yui-toolbar-dropdown-divider {
437
+ height: 1px;
438
+ margin: 0.25rem 0;
439
+ background-color: var(--bulma-border, rgba(0, 0, 0, 0.1));
440
+ }
441
+
442
+ /*============================================================
443
+ * Keyboard focus (accessibility)
444
+ * Use :focus-visible so we don't show an outline on click.
445
+ *============================================================*/
446
+ .yui-nav a:focus-visible,
447
+ .yui-nav button:focus-visible,
448
+ .yui-toolbar-item:focus-visible,
449
+ .yui-accordion-head:focus-visible {
450
+ outline: 2px solid var(--bulma-link, #485fc7);
451
+ outline-offset: 2px;
452
+ border-radius: 0.25rem;
453
+ }
454
+
455
+ /*============================================================
456
+ * Shell placeholder (shown when navigation fails silently)
457
+ *============================================================*/
458
+ .yui-shell-placeholder {
459
+ max-width: 60rem;
460
+ }
461
+
462
+ /*============================================================
463
+ * Accordion head styling — needed because it is now a
464
+ * <button>, which has default button styles to override.
465
+ *============================================================*/
466
+ .yui-nav-accordion .yui-accordion-head {
467
+ display: block;
468
+ width: 100%;
469
+ text-align: inherit;
470
+ background: transparent;
471
+ border: none;
472
+ padding: 0.5em 0.75em;
473
+ margin: 0;
474
+ font: inherit;
475
+ color: inherit;
476
+ }
477
+
478
+ /*============================================================
479
+ * Adaptive dialog (yui_shell_show_modal, opts.dialog:true)
480
+ *
481
+ * The standardized "single window / popup" chrome:
482
+ * - Desktop : a centered card, close X at the top-right.
483
+ * - Mobile : a full-screen sheet, back arrow at the top-left.
484
+ * One dismiss control shows per breakpoint; both call close().
485
+ * 768px is the shell-wide mobile breakpoint (matches the dock).
486
+ *============================================================*/
487
+ /* Lighter backdrop than Bulma's default (0.86): a popup should dim the
488
+ * page, not black it out. */
489
+ .yui-dialog .modal-background {
490
+ background-color: rgba(10, 10, 10, 0.4);
491
+ }
492
+ .yui-dialog .yui-dialog-content {
493
+ display: flex;
494
+ flex-direction: column;
495
+ width: 100%;
496
+ max-width: 640px;
497
+ max-height: 90vh;
498
+ background: var(--bulma-scheme-main, #fff);
499
+ border-radius: 8px;
500
+ overflow: hidden;
501
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.35);
502
+ }
503
+ .yui-dialog-header {
504
+ display: flex;
505
+ align-items: center;
506
+ gap: 0.5rem;
507
+ flex: 0 0 auto;
508
+ padding: 0.5rem 0.6rem;
509
+ border-bottom: 1px solid var(--bulma-border-weak, #dbdbdb);
510
+ }
511
+ .yui-dialog-title {
512
+ flex: 1 1 auto;
513
+ min-width: 0;
514
+ overflow: hidden;
515
+ text-overflow: ellipsis;
516
+ white-space: nowrap;
517
+ font-weight: 600;
518
+ }
519
+ .yui-dialog-back,
520
+ .yui-dialog-x {
521
+ flex: 0 0 auto;
522
+ width: 2rem;
523
+ height: 2rem;
524
+ display: inline-flex;
525
+ align-items: center;
526
+ justify-content: center;
527
+ padding: 0;
528
+ border: 0;
529
+ border-radius: 6px;
530
+ background: transparent;
531
+ color: var(--bulma-text, currentColor);
532
+ cursor: pointer;
533
+ }
534
+ .yui-dialog-back:hover,
535
+ .yui-dialog-x:hover {
536
+ background: var(--bulma-background, rgba(0, 0, 0, 0.06));
537
+ }
538
+ .yui-dialog-body {
539
+ flex: 1 1 auto;
540
+ min-height: 0;
541
+ overflow: auto;
542
+ padding: 1rem;
543
+ }
544
+
545
+ /* Desktop: X (right) shown, back arrow hidden. */
546
+ .yui-dialog-back {
547
+ display: none;
548
+ }
549
+
550
+ /* Mobile: full-screen sheet, back arrow (left) shown, X hidden. */
551
+ @media (max-width: 768px) {
552
+ .yui-dialog .modal-content {
553
+ margin: 0;
554
+ width: 100%;
555
+ max-height: 100%;
556
+ height: 100%;
557
+ }
558
+ .yui-dialog .yui-dialog-content {
559
+ max-width: 100%;
560
+ max-height: 100%;
561
+ height: 100%;
562
+ border-radius: 0;
563
+ box-shadow: none;
564
+ }
565
+ .yui-dialog-back {
566
+ display: inline-flex;
567
+ }
568
+ .yui-dialog-x {
569
+ display: none;
570
+ }
571
+ }