@yuneta/gobj-ui 5.3.3 → 5.5.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.js CHANGED
@@ -24,6 +24,11 @@ export { register_c_yui_uplot } from "./src/c_yui_uplot.js";
24
24
  export { register_c_yui_json_graph } from "./src/c_yui_json_graph.js";
25
25
  export { register_c_yui_json } from "./src/c_yui_json.js";
26
26
  export { register_c_yui_gobj_tree_js } from "./src/c_yui_gobj_tree_js.js";
27
+ export {
28
+ register_c_yui_service_view,
29
+ yui_mount_service_view,
30
+ expose_view_container,
31
+ } from "./src/c_yui_service_view.js";
27
32
 
28
33
  /*
29
34
  * Declarative shell + menu navigation (new in v7.4)
package/package.json CHANGED
@@ -1,47 +1,47 @@
1
1
  {
2
- "name": "@yuneta/gobj-ui",
3
- "version": "5.3.3",
4
- "type": "module",
5
- "main": "dist/gobj-ui.cjs.js",
6
- "module": "dist/gobj-ui.es.js",
7
- "exports": {
8
- ".": {
9
- "import": "./dist/gobj-ui.es.js",
10
- "require": "./dist/gobj-ui.cjs.js"
11
- },
12
- "./index.js": "./index.js",
13
- "./vite-plugin-yuneta-html.js": "./vite-plugin-yuneta-html.js",
14
- "./src/*": "./src/*"
2
+ "name": "@yuneta/gobj-ui",
3
+ "version": "5.5.0",
4
+ "type": "module",
5
+ "main": "dist/gobj-ui.cjs.js",
6
+ "module": "dist/gobj-ui.es.js",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/gobj-ui.es.js",
10
+ "require": "./dist/gobj-ui.cjs.js"
15
11
  },
16
- "files": [
17
- "dist/gobj-ui.es.js",
18
- "dist/gobj-ui.cjs.js",
19
- "index.js",
20
- "src/",
21
- "vite-plugin-yuneta-html.js"
22
- ],
23
- "description": "Yuneta UI Library — the active line: declarative shell (C_YUI_SHELL+NAV+PAGER+WIZARD) + windows, TreeDB views, forms, charts and maps. Consumed by wattyzer and the yunetas JS yunos (gui_agent, gui_treedb). The frozen legacy GClass GUI stack (C_YUI_MAIN/TABS/ROUTING) lives on the v1 branch, npm dist-tag 'legacy' (consumed by estadodelaire and hidraulia).",
24
- "scripts": {
25
- "build": "vite build",
26
- "test": "vitest run",
27
- "prepublishOnly": "npm run build"
28
- },
29
- "devDependencies": {
30
- "@rollup/plugin-terser": "^1.0.0",
31
- "@vitest/coverage-v8": "^4.1.10",
32
- "@yuneta/gobj-js": "^7.8.7",
33
- "vite": "^8.1.5",
34
- "vitest": "^4.1.10"
35
- },
36
- "peerDependencies": {
37
- "@antv/g6": "^5.1.0",
38
- "@yuneta/gobj-js": "^7.8.7",
39
- "bulma": "^1.0.4",
40
- "i18next": "^26.3.6",
41
- "maplibre-gl": "^6.0.0",
42
- "tabulator-tables": "^6.5.2",
43
- "tom-select": "^2.6.2",
44
- "uplot": "^1.6.32",
45
- "vanilla-jsoneditor": "^3.13.0"
46
- }
12
+ "./index.js": "./index.js",
13
+ "./vite-plugin-yuneta-html.js": "./vite-plugin-yuneta-html.js",
14
+ "./src/*": "./src/*"
15
+ },
16
+ "files": [
17
+ "dist/gobj-ui.es.js",
18
+ "dist/gobj-ui.cjs.js",
19
+ "index.js",
20
+ "src/",
21
+ "vite-plugin-yuneta-html.js"
22
+ ],
23
+ "description": "Yuneta UI Library — the active line: declarative shell (C_YUI_SHELL+NAV+PAGER+WIZARD) + windows, TreeDB views, forms, charts and maps. Consumed by wattyzer and the yunetas JS yunos (gui_agent, gui_treedb). The frozen legacy GClass GUI stack (C_YUI_MAIN/TABS/ROUTING) lives on the v1 branch, npm dist-tag 'legacy' (consumed by estadodelaire and hidraulia).",
24
+ "scripts": {
25
+ "build": "vite build",
26
+ "test": "vitest run",
27
+ "prepublishOnly": "npm run build"
28
+ },
29
+ "devDependencies": {
30
+ "@rollup/plugin-terser": "^1.0.0",
31
+ "@vitest/coverage-v8": "^4.1.10",
32
+ "@yuneta/gobj-js": "^7.8.7",
33
+ "vite": "^8.1.5",
34
+ "vitest": "^4.1.10"
35
+ },
36
+ "peerDependencies": {
37
+ "@antv/g6": "^5.1.0",
38
+ "@yuneta/gobj-js": "^7.8.7",
39
+ "bulma": "^1.0.4",
40
+ "i18next": "^26.3.6",
41
+ "maplibre-gl": "^6.0.0",
42
+ "tabulator-tables": "^6.5.2",
43
+ "tom-select": "^2.6.2",
44
+ "uplot": "^1.6.32",
45
+ "vanilla-jsoneditor": "^3.13.0"
46
+ }
47
47
  }
package/src/c_yui_node.js CHANGED
@@ -126,6 +126,10 @@ import {
126
126
  child_nav_items,
127
127
  chrome_visible,
128
128
  normalize_spec,
129
+ is_nav_mode,
130
+ nav_mode_renders,
131
+ nav_mode_depth,
132
+ NAV_MODES,
129
133
  } from "./node_tree_model.js";
130
134
 
131
135
  import {
@@ -133,6 +137,7 @@ import {
133
137
  yui_shell_zone,
134
138
  yui_shell_navigate,
135
139
  yui_shell_set_sub_routes,
140
+ yui_shell_translate,
136
141
  } from "./c_yui_shell.js";
137
142
 
138
143
  import "./c_yui_node.css";
@@ -169,6 +174,7 @@ SDATA(data_type_t.DTP_JSON, "children", 0, null, "Declared child spec
169
174
 
170
175
  SDATA(data_type_t.DTP_STRING, "base_route", 0, "", "ROOT only: the declared route the whole tree hangs from"),
171
176
  SDATA(data_type_t.DTP_STRING, "tree_version", 0, "", "ROOT only: version of the tree CONTRACT (its paths are public urls)"),
177
+ SDATA(data_type_t.DTP_STRING, "nav_mode", 0, "stack","ROOT only: how the way in is shown — stack|back|path"),
172
178
 
173
179
  SDATA(data_type_t.DTP_POINTER, "$container", 0, null, "Root HTMLElement (shell view contract)"),
174
180
  SDATA_END()
@@ -389,6 +395,29 @@ function tree_root_of(gobj)
389
395
  }
390
396
  }
391
397
 
398
+ /************************************************************
399
+ * The navigation mode in force for this node.
400
+ *
401
+ * It is ONE knob per tree, held by the root: a tree whose ancestors
402
+ * stacked strips while a descendant drew a breadcrumb would be
403
+ * showing the way in twice, in two languages. Descendants read it
404
+ * from the root rather than keeping a copy, so a node added at
405
+ * runtime is born in the mode the tree is already in.
406
+ ************************************************************/
407
+ function nav_mode_of(gobj)
408
+ {
409
+ let mode = gobj_read_attr(tree_root_of(gobj), "nav_mode") || "stack";
410
+
411
+ if(!is_nav_mode(mode)) {
412
+ log_error(
413
+ `${GCLASS_NAME} '${node_path_str(gobj)}': unknown nav_mode ` +
414
+ `'${mode}' — expected one of ${NAV_MODES.join("|")}`
415
+ );
416
+ return "stack";
417
+ }
418
+ return mode;
419
+ }
420
+
392
421
  /************************************************************
393
422
  * Node ids from the root DOWN TO this node, root excluded
394
423
  * (the root's own segment is already inside base_route).
@@ -494,12 +523,14 @@ function resolve_path_info(gobj, subpath, inherited)
494
523
  {
495
524
  let effective = (typeof inherited === "number") ? inherited : null;
496
525
 
526
+ let mode = nav_mode_of(gobj);
527
+
497
528
  let own = gobj_read_attr(gobj, "chrome_depth");
498
529
  if(typeof own === "number" && own >= 0) {
499
530
  effective = own;
500
531
  }
501
532
  if(has_link(gobj)) {
502
- return {distance: 0, chrome_depth: effective, chain: []};
533
+ return {distance: 0, chrome_depth: nav_mode_depth(effective, mode), chain: []};
503
534
  }
504
535
 
505
536
  /* Distance is counted in STRUCTURAL segments: the tail a viewer
@@ -524,7 +555,11 @@ function resolve_path_info(gobj, subpath, inherited)
524
555
  break;
525
556
  }
526
557
  }
527
- return {distance: distance, chrome_depth: effective, chain: chain};
558
+ return {
559
+ distance: distance,
560
+ chrome_depth: nav_mode_depth(effective, mode),
561
+ chain: chain
562
+ };
528
563
  }
529
564
 
530
565
  /************************************************************
@@ -548,6 +583,30 @@ function walk_path(node, path)
548
583
  return {node: g, missing: segs.slice(i)};
549
584
  }
550
585
 
586
+ /************************************************************
587
+ * Translate a subtree this node just built.
588
+ *
589
+ * A node renders its chrome WHEN YOU WALK INTO IT, long after
590
+ * the host's one-shot refresh_language over the shell tree, and
591
+ * its zone navs are appended into shell zones, outside this
592
+ * node's own $container. Neither pass reaches them, so without
593
+ * this every strip renders its raw i18n key: `treedb`,
594
+ * `central database`, `data` in lower-case English next to a
595
+ * translated menu — which reads as a MISSING key and sent more
596
+ * than one person looking for the wrong bug.
597
+ *
598
+ * This is library-built DOM, so it goes through the shell's
599
+ * registered translator. What a node MOUNTS is not ours: an app
600
+ * view gclass translates its own DOM.
601
+ ************************************************************/
602
+ function translate_own_dom(gobj, $el)
603
+ {
604
+ if(!$el) {
605
+ return;
606
+ }
607
+ yui_shell_translate(yui_shell_of(gobj), $el);
608
+ }
609
+
551
610
  /************************************************************
552
611
  * Destroy the C_YUI_NAV gobjs rendering this node's projection.
553
612
  ************************************************************/
@@ -575,7 +634,8 @@ function render_projection(gobj, mode, $where, active_route, active_id, zones_on
575
634
  if(!priv.children.length) {
576
635
  return;
577
636
  }
578
- let renders = projection_renders(gobj_read_attr(gobj, "projection"), mode);
637
+ let renders = nav_mode_renders(gobj_read_attr(gobj, "projection"),
638
+ nav_mode_of(gobj), mode, priv.is_root);
579
639
  if(!renders.length) {
580
640
  return;
581
641
  }
@@ -634,6 +694,7 @@ function render_projection(gobj, mode, $where, active_route, active_id, zones_on
634
694
  let $nav = gobj_read_attr(nav, "$container");
635
695
  if($nav) {
636
696
  $target.appendChild($nav);
697
+ translate_own_dom(gobj, $nav);
637
698
  }
638
699
  priv.navs.push(nav);
639
700
  i++;
@@ -681,11 +742,14 @@ function project_into_zone(gobj, render, items, active_route, active_id)
681
742
  let $nav = gobj_read_attr(nav, "$container");
682
743
  if($nav) {
683
744
  $zone.appendChild($nav);
745
+ translate_own_dom(gobj, $nav);
684
746
  }
685
747
  priv.zone_navs[key] = nav;
686
748
  priv.zone_sig[key] = sig;
687
749
  } else if(priv.zone_sig[key] !== sig) {
688
750
  gobj_send_event(nav, "EV_SET_ITEMS", {items: items}, gobj);
751
+ /* EV_SET_ITEMS rebuilt the nav's DOM: translate it again. */
752
+ translate_own_dom(gobj, gobj_read_attr(nav, "$container"));
689
753
  priv.zone_sig[key] = sig;
690
754
  }
691
755
 
@@ -786,10 +850,12 @@ function render_self(gobj)
786
850
  }
787
851
 
788
852
  if(!view) {
789
- priv.$body.appendChild(createElement2(
853
+ let $empty = createElement2(
790
854
  ["div", {class: "NODE_EMPTY", i18n: "nothing here yet"},
791
855
  "Nothing here yet."]
792
- ));
856
+ );
857
+ priv.$body.appendChild($empty);
858
+ translate_own_dom(gobj, $empty);
793
859
  }
794
860
  }
795
861
 
@@ -833,7 +899,8 @@ function render_child(gobj, child)
833
899
  function render_path(gobj, $where)
834
900
  {
835
901
  let priv = gobj.priv;
836
- let renders = projection_renders(gobj_read_attr(gobj, "projection"), "path");
902
+ let renders = nav_mode_renders(gobj_read_attr(gobj, "projection"),
903
+ nav_mode_of(gobj), "path", priv.is_root);
837
904
 
838
905
  if(!renders.length) {
839
906
  return;
@@ -881,6 +948,7 @@ function render_path(gobj, $where)
881
948
  let $nav = gobj_read_attr(nav, "$container");
882
949
  if($nav) {
883
950
  $where.appendChild($nav);
951
+ translate_own_dom(gobj, $nav);
884
952
  }
885
953
  priv.navs.push(nav);
886
954
  i++;
@@ -1328,6 +1396,54 @@ function ac_set_chrome_depth(gobj, event, kw, src)
1328
1396
  return 0;
1329
1397
  }
1330
1398
 
1399
+ /************************************************************
1400
+ * Runtime API: change how this TREE shows the way in.
1401
+ *
1402
+ * One knob for the whole tree, and deliberately root-only: the
1403
+ * mode answers "how deep am I and how do I get back", a question
1404
+ * that has one answer per tree. Set on a middle node it would
1405
+ * silently do nothing (descendants read the root's), and a knob
1406
+ * that accepts a call and ignores it is worse than one that
1407
+ * refuses it.
1408
+ *
1409
+ * Nothing declared is rewritten — the mode filters the renders as
1410
+ * they are asked for — so going back to "stack" restores exactly
1411
+ * what the app declared, per branch, including layouts this mode
1412
+ * never mentions.
1413
+ ************************************************************/
1414
+ function ac_set_nav_mode(gobj, event, kw, src)
1415
+ {
1416
+ let priv = gobj.priv;
1417
+ let mode = kw.nav_mode || "";
1418
+
1419
+ if(!is_nav_mode(mode)) {
1420
+ log_error(
1421
+ `${GCLASS_NAME} '${node_path_str(gobj)}': unknown nav_mode ` +
1422
+ `'${mode}' — expected one of ${NAV_MODES.join("|")}`
1423
+ );
1424
+ return -1;
1425
+ }
1426
+ if(!priv.is_root) {
1427
+ log_error(
1428
+ `${GCLASS_NAME} '${node_path_str(gobj)}': nav_mode belongs to the ` +
1429
+ `tree ROOT — set it there, the whole tree reads it`
1430
+ );
1431
+ return -1;
1432
+ }
1433
+ gobj_write_attr(gobj, "nav_mode", mode);
1434
+
1435
+ /* Same reason as EV_SET_CHROME_DEPTH: what changes is the shape
1436
+ * of the ACTIVE PATH, whose strips are painted by the ancestors,
1437
+ * so the tree is asked to re-apply the current route instead of
1438
+ * repainting one node. */
1439
+ let shell = yui_shell_of(gobj);
1440
+ if(shell) {
1441
+ yui_shell_navigate(shell, gobj_read_attr(shell, "current_route") ||
1442
+ route_of(gobj), {replace: true});
1443
+ }
1444
+ return 0;
1445
+ }
1446
+
1331
1447
  /************************************************************
1332
1448
  * Runtime API: change (or set) my content.
1333
1449
  ************************************************************/
@@ -1402,6 +1518,20 @@ function yui_node_set_chrome_depth(node, depth)
1402
1518
  {chrome_depth: depth}, node);
1403
1519
  }
1404
1520
 
1521
+ /************************************************************
1522
+ * The tree's navigation mode: "stack" | "back" | "path".
1523
+ * Send it to the ROOT — the whole tree reads it from there.
1524
+ ************************************************************/
1525
+ function yui_node_set_nav_mode(node, mode)
1526
+ {
1527
+ return gobj_send_event(node, "EV_SET_NAV_MODE", {nav_mode: mode}, node);
1528
+ }
1529
+
1530
+ function yui_node_nav_mode(node)
1531
+ {
1532
+ return gobj_read_attr(tree_root_of(node), "nav_mode") || "stack";
1533
+ }
1534
+
1405
1535
  /************************************************************
1406
1536
  * Resolve a path of ids from a node ("" = the node itself).
1407
1537
  * Returns null when any segment is missing — callers that mean
@@ -1484,6 +1614,7 @@ function create_gclass(gclass_name)
1484
1614
  ["EV_REMOVE_NODE", ac_remove_node, null],
1485
1615
  ["EV_SET_PROJECTION", ac_set_projection, null],
1486
1616
  ["EV_SET_CHROME_DEPTH", ac_set_chrome_depth, null],
1617
+ ["EV_SET_NAV_MODE", ac_set_nav_mode, null],
1487
1618
  ["EV_SET_CONTENT", ac_set_content, null],
1488
1619
  ["EV_NODE_TREE_CHANGED", ac_tree_changed, null]
1489
1620
  ]],
@@ -1496,6 +1627,7 @@ function create_gclass(gclass_name)
1496
1627
  ["EV_REMOVE_NODE", ac_remove_node, null],
1497
1628
  ["EV_SET_PROJECTION", ac_set_projection, null],
1498
1629
  ["EV_SET_CHROME_DEPTH", ac_set_chrome_depth, null],
1630
+ ["EV_SET_NAV_MODE", ac_set_nav_mode, null],
1499
1631
  ["EV_SET_CONTENT", ac_set_content, null],
1500
1632
  ["EV_NODE_TREE_CHANGED", ac_tree_changed, null]
1501
1633
  ]],
@@ -1508,6 +1640,7 @@ function create_gclass(gclass_name)
1508
1640
  ["EV_REMOVE_NODE", ac_remove_node, null],
1509
1641
  ["EV_SET_PROJECTION", ac_set_projection, null],
1510
1642
  ["EV_SET_CHROME_DEPTH", ac_set_chrome_depth, null],
1643
+ ["EV_SET_NAV_MODE", ac_set_nav_mode, null],
1511
1644
  ["EV_SET_CONTENT", ac_set_content, null],
1512
1645
  ["EV_NODE_TREE_CHANGED", ac_tree_changed, null]
1513
1646
  ]]
@@ -1525,6 +1658,7 @@ function create_gclass(gclass_name)
1525
1658
  ["EV_REMOVE_NODE", 0],
1526
1659
  ["EV_SET_PROJECTION", 0],
1527
1660
  ["EV_SET_CHROME_DEPTH", 0],
1661
+ ["EV_SET_NAV_MODE", 0],
1528
1662
  ["EV_SET_CONTENT", 0],
1529
1663
  ["EV_NODE_TREE_CHANGED", 0]
1530
1664
  ];
@@ -1565,6 +1699,8 @@ export {
1565
1699
  yui_node_set_projection,
1566
1700
  yui_node_set_content,
1567
1701
  yui_node_set_chrome_depth,
1702
+ yui_node_set_nav_mode,
1703
+ yui_node_nav_mode,
1568
1704
  yui_node_find,
1569
1705
  yui_node_route,
1570
1706
  yui_node_tree_version