@yuneta/gobj-ui 5.2.1 → 5.3.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/README.md +165 -0
- package/dist/gobj-ui.cjs.js +385 -41
- package/dist/gobj-ui.es.js +385 -41
- package/package.json +1 -1
- package/src/c_yui_json.css +23 -3
- package/src/c_yui_json.js +17 -3
- package/src/c_yui_nav.js +63 -3
- package/src/c_yui_node.css +77 -0
- package/src/c_yui_node.js +1567 -0
- package/src/c_yui_shell.css +18 -0
- package/src/c_yui_shell.js +206 -4
- package/src/c_yui_window.js +8 -3
- package/src/node_tree_model.js +463 -0
- package/src/node_tree_model.test.js +282 -0
- package/src/route_map_model.js +48 -0
- package/src/route_map_model.test.js +70 -0
- package/src/route_resolver.js +11 -0
- package/src/shell_route_map.css +36 -2
- package/src/shell_route_map.js +194 -45
- package/src/yui_dev.js +3 -3
- package/src/yui_icons.css +13 -0
package/package.json
CHANGED
package/src/c_yui_json.css
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
--json-null: #475ed0;
|
|
17
17
|
--json-key: var(--bulma-text-strong, #1a1a1a);
|
|
18
18
|
--json-row-hover: rgba(0, 0, 0, 0.05);
|
|
19
|
+
--json-guide: #b5b5b5;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
:root[data-theme="dark"] .C_YUI_JSON,
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
--json-null: #90a4f4;
|
|
27
28
|
--json-key: var(--bulma-text-strong, #e6e6e6);
|
|
28
29
|
--json-row-hover: rgba(255, 255, 255, 0.06);
|
|
30
|
+
--json-guide: #5a5a5a;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
@media (prefers-color-scheme: dark) {
|
|
@@ -36,6 +38,7 @@
|
|
|
36
38
|
--json-null: #90a4f4;
|
|
37
39
|
--json-key: var(--bulma-text-strong, #e6e6e6);
|
|
38
40
|
--json-row-hover: rgba(255, 255, 255, 0.06);
|
|
41
|
+
--json-guide: #5a5a5a;
|
|
39
42
|
}
|
|
40
43
|
}
|
|
41
44
|
|
|
@@ -55,10 +58,27 @@
|
|
|
55
58
|
align-items: flex-start;
|
|
56
59
|
white-space: pre;
|
|
57
60
|
padding-right: 0.5rem;
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
/* Depth guides. The rows are SIBLINGS with growing padding, not
|
|
62
|
+
* nested boxes, so there is no element to hang a border-left on:
|
|
63
|
+
* the guides are painted as a repeating gradient across the row's
|
|
64
|
+
* own indentation, one line per ancestor level. The tile is
|
|
65
|
+
* parked at the first level's column and its WIDTH is set per row
|
|
66
|
+
* (row_indent), so a row only ever draws the levels above it.
|
|
67
|
+
* Four characters, like every other indentation. */
|
|
68
|
+
background-image: repeating-linear-gradient(
|
|
69
|
+
to right,
|
|
70
|
+
var(--json-guide) 0 1px,
|
|
71
|
+
transparent 1px 4ch
|
|
72
|
+
);
|
|
73
|
+
background-position: 0.4ch 0;
|
|
74
|
+
background-repeat: no-repeat;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* background-COLOR, never the `background` shorthand: the shorthand
|
|
78
|
+
* resets background-image and the depth guides vanish under the
|
|
79
|
+
* cursor, on the one row the reader is looking at. */
|
|
60
80
|
.C_YUI_JSON .JSON_ROW:hover {
|
|
61
|
-
background: var(--json-row-hover);
|
|
81
|
+
background-color: var(--json-row-hover);
|
|
62
82
|
}
|
|
63
83
|
|
|
64
84
|
.C_YUI_JSON .JSON_ROW_BODY {
|
package/src/c_yui_json.js
CHANGED
|
@@ -548,11 +548,25 @@ function leaf_row(key, value, depth)
|
|
|
548
548
|
}
|
|
549
549
|
|
|
550
550
|
/************************************************************
|
|
551
|
-
* Depth indentation (inline style keeps it CSS-framework free)
|
|
551
|
+
* Depth indentation (inline style keeps it CSS-framework free).
|
|
552
|
+
*
|
|
553
|
+
* FOUR characters per level, house rule: structure is read as
|
|
554
|
+
* indentation, and it has to be the same four everywhere — this
|
|
555
|
+
* viewer, the raw dump behind it, the site map's tree. `ch` is
|
|
556
|
+
* the character width of the row's own font, so the rows line up
|
|
557
|
+
* with the monospace text they carry instead of drifting from it
|
|
558
|
+
* at some zoom level.
|
|
552
559
|
************************************************************/
|
|
560
|
+
const INDENT_CH = 4;
|
|
561
|
+
const INDENT_PAD = 0.4;
|
|
562
|
+
|
|
553
563
|
function row_indent(depth)
|
|
554
564
|
{
|
|
555
|
-
|
|
565
|
+
/* `background-size` bounds the depth guides painted by the CSS to
|
|
566
|
+
* this row's own indentation: one vertical line per ancestor
|
|
567
|
+
* level, none across the content. */
|
|
568
|
+
return 'padding-left:' + (INDENT_PAD + depth * INDENT_CH) + 'ch;' +
|
|
569
|
+
'background-size:' + (depth * INDENT_CH) + 'ch 100%;';
|
|
556
570
|
}
|
|
557
571
|
|
|
558
572
|
/************************************************************
|
|
@@ -760,7 +774,7 @@ function ac_copy_all(gobj, event, kw, src)
|
|
|
760
774
|
if(priv.root === null || priv.root === undefined) {
|
|
761
775
|
return 0;
|
|
762
776
|
}
|
|
763
|
-
let text = JSON.stringify(priv.root, null,
|
|
777
|
+
let text = JSON.stringify(priv.root, null, 4);
|
|
764
778
|
if(navigator.clipboard && navigator.clipboard.writeText) {
|
|
765
779
|
navigator.clipboard.writeText(text).catch(function(e) {
|
|
766
780
|
log_error(`${GCLASS_NAME}: clipboard write failed: ${e}`);
|
package/src/c_yui_nav.js
CHANGED
|
@@ -15,6 +15,13 @@
|
|
|
15
15
|
* "cards" — grid of tappable cards (section-index landing;
|
|
16
16
|
* mounted as a stage view by C_YUI_SHELL when a
|
|
17
17
|
* primary item declares submenu.index)
|
|
18
|
+
* "breadcrumb" — the PATH as one line ("Cards › Energy › South
|
|
19
|
+
* hall › Meter 3"), each crumb a link to that
|
|
20
|
+
* level. Unlike every other layout its items
|
|
21
|
+
* are not a node's children but the trail down
|
|
22
|
+
* to where the user is: the second way to show
|
|
23
|
+
* depth, for when stacking one strip per level
|
|
24
|
+
* costs more vertical room than it is worth.
|
|
18
25
|
* "backbar" — single "← <section>" link back to the section
|
|
19
26
|
* index; the mobile replacement of the tab strip
|
|
20
27
|
* for submenu.index sections (the shell collapses
|
|
@@ -76,7 +83,7 @@ const GCLASS_NAME = "C_YUI_NAV";
|
|
|
76
83
|
|
|
77
84
|
const SUPPORTED_LAYOUTS = [
|
|
78
85
|
"vertical", "icon-bar", "tabs", "drawer", "submenu", "accordion", "cards",
|
|
79
|
-
"backbar"
|
|
86
|
+
"backbar", "breadcrumb"
|
|
80
87
|
];
|
|
81
88
|
|
|
82
89
|
/* Decorative items have no `route` and never participate in
|
|
@@ -220,6 +227,7 @@ function build_ui(gobj)
|
|
|
220
227
|
case "accordion": $container = render_accordion(gobj, items); break;
|
|
221
228
|
case "cards": $container = render_cards(gobj, items); break;
|
|
222
229
|
case "backbar": $container = render_backbar(gobj); break;
|
|
230
|
+
case "breadcrumb": $container = render_breadcrumb(gobj, items); break;
|
|
223
231
|
default: $container = render_vertical(gobj, items);
|
|
224
232
|
}
|
|
225
233
|
$container.setAttribute("data-nav-zone", zone);
|
|
@@ -254,6 +262,13 @@ function build_ui(gobj)
|
|
|
254
262
|
wire_clicks(gobj, $container);
|
|
255
263
|
|
|
256
264
|
gobj_write_attr(gobj, "$container", $container);
|
|
265
|
+
|
|
266
|
+
/* A nav can be CREATED already knowing which item is active (a
|
|
267
|
+
* C_YUI_NODE builds its chrome for the child it is about to show).
|
|
268
|
+
* Applying the highlight only on rebuild lost it for every such
|
|
269
|
+
* nav — the shell never noticed because it always follows creation
|
|
270
|
+
* with an EV_ROUTE_CHANGED. */
|
|
271
|
+
apply_active_route(gobj);
|
|
257
272
|
}
|
|
258
273
|
|
|
259
274
|
/************************************************************
|
|
@@ -286,8 +301,6 @@ function rebuild(gobj)
|
|
|
286
301
|
if($old && $old.parentNode) {
|
|
287
302
|
$old.parentNode.removeChild($old);
|
|
288
303
|
}
|
|
289
|
-
|
|
290
|
-
apply_active_route(gobj);
|
|
291
304
|
}
|
|
292
305
|
|
|
293
306
|
/************************************************************
|
|
@@ -432,6 +445,53 @@ function render_cards(gobj, items)
|
|
|
432
445
|
* collapses the whole secondary zone (tabs and cards never
|
|
433
446
|
* coexist), so the bar needs no self-hiding logic.
|
|
434
447
|
************************************************************/
|
|
448
|
+
/************************************************************
|
|
449
|
+
* breadcrumb: the trail to here, one line.
|
|
450
|
+
*
|
|
451
|
+
* Bulma's own markup (nav.breadcrumb > ul > li, separators drawn by
|
|
452
|
+
* CSS), with the SAME data-route/data-item-id contract as every
|
|
453
|
+
* other layout — so the delegated click handler and the active
|
|
454
|
+
* highlight work unchanged, and the last crumb marks itself through
|
|
455
|
+
* `active_route` like a tab does.
|
|
456
|
+
************************************************************/
|
|
457
|
+
function render_breadcrumb(gobj, items)
|
|
458
|
+
{
|
|
459
|
+
let show_label = gobj_read_attr(gobj, "show_label");
|
|
460
|
+
let lis = [];
|
|
461
|
+
|
|
462
|
+
for(let it of (items || [])) {
|
|
463
|
+
if(is_decorative(it)) {
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
let children = [];
|
|
467
|
+
if(it.icon) {
|
|
468
|
+
children.push(["span", {class: "icon is-small"},
|
|
469
|
+
["i", {class: it.icon, "aria-hidden": "true"}]]);
|
|
470
|
+
}
|
|
471
|
+
if(show_label !== false) {
|
|
472
|
+
let label = it.name || it.id || "";
|
|
473
|
+
children.push(["span", {class: "yui-nav-label", i18n: label}, label]);
|
|
474
|
+
}
|
|
475
|
+
let a_attrs = {
|
|
476
|
+
class: "yui-nav-item yui-nav-crumb",
|
|
477
|
+
href: it.route ? "#" + it.route : "#",
|
|
478
|
+
"data-item-id": it.id,
|
|
479
|
+
"data-route": it.route || "",
|
|
480
|
+
"data-disabled": it.disabled ? "1" : "0"
|
|
481
|
+
};
|
|
482
|
+
if(it.name) {
|
|
483
|
+
a_attrs["aria-label"] = it.name;
|
|
484
|
+
a_attrs["data-i18n-aria-label"] = it.name;
|
|
485
|
+
}
|
|
486
|
+
lis.push(["li", {}, [["a", a_attrs, children]]]);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return createElement2(
|
|
490
|
+
["nav", {class: "breadcrumb is-small", "aria-label": "breadcrumbs"},
|
|
491
|
+
[["ul", {}, lis]]]
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
|
|
435
495
|
function render_backbar(gobj)
|
|
436
496
|
{
|
|
437
497
|
let back_route = gobj_read_attr(gobj, "back_route") || "";
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* c_yui_node.css
|
|
3
|
+
*
|
|
4
|
+
* Layout of C_YUI_NODE — deliberately thin: the projections are
|
|
5
|
+
* C_YUI_NAV renders and bring their own styling (cards, tabs,
|
|
6
|
+
* backbar…), so all this file owns is the vertical stack
|
|
7
|
+
* chrome / body and the nesting that lets a deep branch scroll.
|
|
8
|
+
*
|
|
9
|
+
* No colours beyond Bulma variables, and no transitions (house
|
|
10
|
+
* rule): a level change appears instantly.
|
|
11
|
+
*
|
|
12
|
+
* Copyright (c) 2026, ArtGins.
|
|
13
|
+
* All Rights Reserved.
|
|
14
|
+
***********************************************************************/
|
|
15
|
+
|
|
16
|
+
.NODE_VIEW {
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
min-height: 0;
|
|
20
|
+
height: 100%;
|
|
21
|
+
width: 100%;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/*--- chrome: the parent's projection while a child is showing ---*/
|
|
25
|
+
.NODE_CHROME {
|
|
26
|
+
flex: 0 0 auto;
|
|
27
|
+
}
|
|
28
|
+
.NODE_CHROME:empty {
|
|
29
|
+
display: none;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/*--- body: content, index projection, or the active child ---*/
|
|
33
|
+
.NODE_BODY {
|
|
34
|
+
display: flex;
|
|
35
|
+
flex-direction: column;
|
|
36
|
+
flex: 1 1 auto;
|
|
37
|
+
min-height: 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.NODE_CONTENT {
|
|
41
|
+
flex: 0 0 auto;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* The index projection (a card grid, a list…) is the page when this
|
|
45
|
+
* node is the tip: it takes the remaining height and scrolls. */
|
|
46
|
+
.NODE_INDEX {
|
|
47
|
+
flex: 1 1 auto;
|
|
48
|
+
min-height: 0;
|
|
49
|
+
overflow-y: auto;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* A node whose index projection targets a ZONE (the root, painting
|
|
53
|
+
* the rail) leaves this empty — an empty flex child would still eat
|
|
54
|
+
* the body's height. */
|
|
55
|
+
.NODE_INDEX:empty {
|
|
56
|
+
display: none;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* A node with BOTH content and children: the content keeps its
|
|
60
|
+
* natural height and the projection sits under it. */
|
|
61
|
+
.NODE_CONTENT + .NODE_INDEX {
|
|
62
|
+
border-top: 1px solid var(--bulma-border-weak, #dbdbdb);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* The active child's own NODE_VIEW nests here — min-height:0 all the
|
|
66
|
+
* way down or an inner overflow-y never gets a bounded height. */
|
|
67
|
+
.NODE_CHILD {
|
|
68
|
+
display: flex;
|
|
69
|
+
flex-direction: column;
|
|
70
|
+
flex: 1 1 auto;
|
|
71
|
+
min-height: 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.NODE_EMPTY {
|
|
75
|
+
padding: 1rem;
|
|
76
|
+
color: var(--bulma-text-weak, #7a7a7a);
|
|
77
|
+
}
|