@yuneta/gobj-ui 7.23.3 → 7.23.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.23.3",
3
+ "version": "7.23.5",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
@@ -24,6 +24,7 @@ import {
24
24
  sdata_flag_t,
25
25
  gclass_flag_t,
26
26
  gclass_create,
27
+ gclass_find_by_name,
27
28
  log_error,
28
29
  log_warning,
29
30
  trace_msg,
@@ -7633,6 +7634,12 @@ function create_gclass(gclass_name)
7633
7634
  ***************************************************************************/
7634
7635
  function register_c_g6_nodes_tree()
7635
7636
  {
7637
+ /* Idempotent: C_YUI_TREEDB_GRAPH auto-registers this engine, so an
7638
+ * app that also registers it explicitly (in either order) must not
7639
+ * trip "GClass ALREADY created". */
7640
+ if(gclass_find_by_name(GCLASS_NAME, false)) {
7641
+ return 0;
7642
+ }
7636
7643
  return create_gclass(GCLASS_NAME);
7637
7644
  }
7638
7645
 
@@ -40,6 +40,12 @@ import {
40
40
  } from "@yuneta/gobj-js";
41
41
 
42
42
  import {yui_toolbar} from "./yui_toolbar.js";
43
+ import {
44
+ yui_graph_camera_items,
45
+ yui_graph_fold_items,
46
+ yui_graph_refresh_item,
47
+ yui_graph_update_zoom,
48
+ } from "./yui_graph_camera.js";
43
49
 
44
50
  import {t} from "i18next";
45
51
 
@@ -535,40 +541,18 @@ function make_toolbar(gobj)
535
541
  let center_items = [];
536
542
  let right_items = [];
537
543
 
538
- let c_icons = [
539
- ["yi-magnifying-glass-plus", "EV_ZOOM_IN", false, 'i'],
540
- ["yi-magnifying-glass", "EV_ZOOM_RESET", false, 'i'],
541
- ["yi-magnifying-glass-minus", "EV_ZOOM_OUT", false, 'i'],
542
- ["yi-arrows-to-eye", "EV_CENTER", false, 'i'],
543
- ["yi-eye", "EV_EXPAND_ALL", false, 'i'],
544
- ["yi-eye-slash", "EV_COLLAPSE_ALL", false, 'i'],
545
- ["yi-arrows-rotate", "EV_REFRESH", false, 'i'],
546
- ];
547
-
548
- for(let item of c_icons) {
549
- let icon_name = item[0];
550
- let event_name = item[1];
551
- let disabled = item[2];
552
-
553
- let button = {
554
- class: `button ${event_name}`,
555
- style: {height: toolbar_wide, width: '2.5em'}
556
- };
557
- if(disabled) {
558
- button.disabled = true;
559
- }
560
- center_items.push(
561
- ['button', button,
562
- ['i', {style: 'font-size:1.5em; color:inherit;', class: icon_name}],
563
- {
564
- click: (evt) => {
565
- evt.stopPropagation();
566
- gobj_send_event(gobj, event_name, {evt: evt}, gobj);
567
- }
568
- }
569
- ]
570
- );
571
- }
544
+ /*
545
+ * The camera and the fold pair come from yui_graph_camera.js.
546
+ *
547
+ * This toolbar used to draw two of these its own way — a bare
548
+ * magnifier for actual size, `arrows-to-eye` for fit — and fold as
549
+ * an eye, which means show/hide and not open/closed. The same
550
+ * console shows this graph and the JSON one side by side, so the
551
+ * drawings have to be the same drawings.
552
+ */
553
+ center_items = yui_graph_camera_items(gobj, priv.graph, toolbar_wide)
554
+ .concat(yui_graph_fold_items(gobj, toolbar_wide));
555
+ center_items.push(yui_graph_refresh_item(gobj, toolbar_wide));
572
556
 
573
557
  /*
574
558
  * Layout selector
@@ -651,6 +635,12 @@ function build_graph(gobj)
651
635
  graph.setTheme(priv.theme);
652
636
  }
653
637
 
638
+ /* The readout follows ANY camera change, the wheel included — a
639
+ * wheel notch passes through no action of ours. */
640
+ graph.on('aftertransform', () => {
641
+ yui_graph_update_zoom(gobj_read_attr(gobj, "$container"), priv.graph);
642
+ });
643
+
654
644
  graph.on(NodeEvent.CLICK, (evt) => {
655
645
  gobj_send_event(gobj, "EV_NODE_CLICK", {evt: evt}, gobj);
656
646
  });
@@ -47,6 +47,12 @@ import {
47
47
  } from "@yuneta/gobj-js";
48
48
 
49
49
  import {yui_toolbar} from "./yui_toolbar.js";
50
+ import {
51
+ yui_graph_camera_items,
52
+ yui_graph_fold_items,
53
+ yui_graph_refresh_item,
54
+ yui_graph_update_zoom,
55
+ } from "./yui_graph_camera.js";
50
56
 
51
57
  import {t} from "i18next";
52
58
 
@@ -179,7 +185,6 @@ let PRIVATE_DATA = {
179
185
  find_timer: null, // rate-limits the find box
180
186
  fold_listener: null, // delegated click on the card fold handles
181
187
  $layout_select: null, // the layout picker
182
- $zoom_readout: null, // the zoom percentage
183
188
  $find_input: null,
184
189
  $find_result: null,
185
190
  $find_count: null,
@@ -455,77 +460,6 @@ function destroy_ui(gobj)
455
460
  }
456
461
  }
457
462
 
458
- /************************************************************
459
- * One camera button.
460
- ************************************************************/
461
- function camera_button(gobj, icon, event_name, label_key, wide)
462
- {
463
- return ['button', {class: `button ${event_name}`, type: 'button',
464
- style: {height: wide, width: '2.5em'},
465
- title: t(label_key), 'data-i18n-title': label_key,
466
- 'aria-label': t(label_key), 'data-i18n-aria-label': label_key},
467
- ['i', {style: 'font-size:1.5em; color:inherit;', class: icon}],
468
- {
469
- click: (evt) => {
470
- evt.stopPropagation();
471
- gobj_send_event(gobj, event_name, {evt: evt}, gobj);
472
- }
473
- }
474
- ];
475
- }
476
-
477
- /************************************************************
478
- * The zoom, as a percentage. What the two magnifiers
479
- * change: an editor that offers a zoom shows the number.
480
- ************************************************************/
481
- function zoom_percent_text(gobj)
482
- {
483
- let graph = gobj.priv.graph;
484
- if(!graph) {
485
- return "100%";
486
- }
487
- try {
488
- return Math.round(graph.getZoom() * 100) + "%";
489
- } catch(e) {
490
- return "100%";
491
- }
492
- }
493
-
494
- /************************************************************
495
- * Repaint the readout after anything that moves the camera.
496
- ************************************************************/
497
- function update_zoom_readout(gobj)
498
- {
499
- let priv = gobj.priv;
500
- if(priv.$zoom_readout) {
501
- priv.$zoom_readout.textContent = zoom_percent_text(gobj);
502
- }
503
- }
504
-
505
- /************************************************************
506
- * One fold button. `open` rotates the chevron to the open
507
- * state, mirroring the per-node toggle of the lazy tree.
508
- ************************************************************/
509
- function graph_fold_button(gobj, event_name, label_key, open)
510
- {
511
- let icon_style = 'font-size:1.5em; color:inherit;' +
512
- (open? ' transform: rotate(90deg);': '');
513
-
514
- return ['button', {class: `button ${event_name}`,
515
- type: 'button',
516
- style: {height: gobj_read_attr(gobj, "wide"), width: '2.5em'},
517
- title: t(label_key), 'data-i18n-title': label_key,
518
- 'aria-label': t(label_key), 'data-i18n-aria-label': label_key},
519
- ['i', {style: icon_style, class: 'yi-chevron-right'}],
520
- {
521
- click: (evt) => {
522
- evt.stopPropagation();
523
- gobj_send_event(gobj, event_name, {}, gobj);
524
- }
525
- }
526
- ];
527
- }
528
-
529
463
  /************************************************************
530
464
  * Toolbar
531
465
  ************************************************************/
@@ -593,54 +527,12 @@ function make_toolbar(gobj)
593
527
  let center_items = [];
594
528
 
595
529
  /*
596
- * The camera, in the SAME vocabulary the treedb graph uses
597
- * (`c_g6_nodes_tree.js`): zoom in / zoom out, then what those two
598
- * change — the zoom READOUT, because without it "1:1" is a jump to
599
- * a value nobody was told — then fit, then actual size.
600
- *
601
- * Two of these used to be a different picture for the same action
602
- * in a sibling view, which is the worst thing an icon can be:
603
- * `EV_ZOOM_RESET` was a bare magnifier and `EV_CENTER` was
604
- * `yi-arrows-to-eye`. Now fit is the same bracket glyph as the
605
- * sprite's `g6-icon-fit`, and actual size is WRITTEN — `1:1` is
606
- * written in every editor that offers it, never drawn, because
607
- * there is no glyph for it.
530
+ * The camera and the fold pair come from yui_graph_camera.js: the
531
+ * same drawings the gobj tree uses, because the two graphs sit in
532
+ * the same console and a copied toolbar drifts.
608
533
  */
609
- let c_icons = [
610
- ["yi-magnifying-glass-plus", "EV_ZOOM_IN", "zoom in"],
611
- ["yi-magnifying-glass-minus", "EV_ZOOM_OUT", "zoom out"],
612
- ];
613
-
614
- for(let item of c_icons) {
615
- center_items.push(camera_button(gobj, item[0], item[1], item[2], toolbar_wide));
616
- }
617
-
618
- priv.$zoom_readout = createElement2(
619
- ['span', {class: 'JSON_GRAPH_ZOOM_LEVEL is-flex is-align-items-center px-2 has-text-grey',
620
- style: 'font-size:.85rem; min-width:3.5em; justify-content:center;',
621
- title: t('zoom level'), 'data-i18n-title': 'zoom level'},
622
- zoom_percent_text(gobj)]
623
- );
624
- center_items.push(priv.$zoom_readout);
625
-
626
- center_items.push(camera_button(gobj, "yi-fit", "EV_CENTER", "auto fit", toolbar_wide));
627
-
628
- /* Written, not drawn — see above. */
629
- center_items.push(
630
- ['button', {class: 'button EV_ZOOM_RESET', type: 'button',
631
- style: {height: toolbar_wide, width: '2.5em'},
632
- title: t('actual size'), 'data-i18n-title': 'actual size',
633
- 'aria-label': t('actual size'), 'data-i18n-aria-label': 'actual size'},
634
- ['span', {style: 'font-weight:700;'}, '1:1'],
635
- {
636
- click: (evt) => {
637
- evt.stopPropagation();
638
- gobj_send_event(gobj, "EV_ZOOM_RESET", {evt: evt}, gobj);
639
- }
640
- }]
641
- );
642
-
643
- center_items.push(camera_button(gobj, "yi-arrows-rotate", "EV_REFRESH", "refresh", toolbar_wide));
534
+ center_items = yui_graph_camera_items(gobj, priv.graph, toolbar_wide);
535
+ center_items.push(yui_graph_refresh_item(gobj, toolbar_wide));
644
536
 
645
537
  /*
646
538
  * Right: fold the tree.
@@ -681,11 +573,8 @@ function make_toolbar(gobj)
681
573
  }]
682
574
  );
683
575
 
684
- let right_items = [
685
- $layout_select,
686
- graph_fold_button(gobj, "EV_EXPAND_ALL", "expand all", true),
687
- graph_fold_button(gobj, "EV_COLLAPSE_ALL", "collapse all", false),
688
- ];
576
+ let right_items = [$layout_select].concat(
577
+ yui_graph_fold_items(gobj, toolbar_wide));
689
578
 
690
579
  const $toolbar = yui_toolbar({}, [
691
580
  ['div', {class: 'yui-horizontal-toolbar-section left'}, left_items],
@@ -748,7 +637,7 @@ function build_graph(gobj)
748
637
  * node from each zoom action would have left the readout lying
749
638
  * after every notch. */
750
639
  graph.on('aftertransform', () => {
751
- update_zoom_readout(gobj);
640
+ yui_graph_update_zoom(gobj_read_attr(gobj, "$container"), priv.graph);
752
641
  });
753
642
 
754
643
  graph.on(CanvasEvent.CLICK, (evt) => {
@@ -1145,7 +1034,7 @@ function load_json(gobj)
1145
1034
  if(!preserve_view) {
1146
1035
  graph.fitView();
1147
1036
  }
1148
- update_zoom_readout(gobj);
1037
+ yui_graph_update_zoom(gobj_read_attr(gobj, "$container"), priv.graph);
1149
1038
  });
1150
1039
  }
1151
1040
  }
@@ -78,6 +78,7 @@ import {
78
78
  } from "@yuneta/gobj-js";
79
79
 
80
80
  import {yui_toolbar} from "./yui_toolbar.js";
81
+ import {register_c_g6_nodes_tree} from "./c_g6_nodes_tree.js";
81
82
  import {
82
83
  removeChildElements,
83
84
  disableElements,
@@ -2594,6 +2595,17 @@ function create_gclass(gclass_name)
2594
2595
  ***************************************************************************/
2595
2596
  function register_c_yui_treedb_graph()
2596
2597
  {
2598
+ if(gclass_find_by_name(GCLASS_NAME, false)) {
2599
+ return 0;
2600
+ }
2601
+ /* This view hosts a C_G6_NODES_TREE child — the graph ENGINE — and
2602
+ * creates it BY NAME. Without its gclass the view builds fine, the
2603
+ * child does not, and the failure lands as "GClass not registered"
2604
+ * from inside a component the host never named. Same arrangement
2605
+ * C_YUI_JSON makes for C_YUI_JSON_GRAPH. */
2606
+ if(!gclass_find_by_name("C_G6_NODES_TREE", false)) {
2607
+ register_c_g6_nodes_tree();
2608
+ }
2597
2609
  return create_gclass(GCLASS_NAME);
2598
2610
  }
2599
2611
 
@@ -0,0 +1,150 @@
1
+ /***********************************************************************
2
+ * yui_graph_camera.js
3
+ *
4
+ * The camera and fold controls a G6 graph puts in a DOM toolbar,
5
+ * built in ONE place.
6
+ *
7
+ * This module exists because the same action was drawn three
8
+ * different ways across sibling views — actual size as a bare
9
+ * magnifier here and as `1:1` there, fit as `arrows-to-eye` here and
10
+ * as corner brackets there, fold as chevrons here and as an eye
11
+ * there. A user sees two of these graphs side by side in the same
12
+ * console, so a copied-and-drifted toolbar is not a detail.
13
+ *
14
+ * The VOCABULARY is not invented here. It is the one
15
+ * `c_g6_nodes_tree.js` settled on for the treedb graph, which draws
16
+ * its toolbar as a G6 plugin over an SVG sprite and therefore cannot
17
+ * share this code — only its decisions:
18
+ *
19
+ * zoom in / zoom out magnifiers
20
+ * zoom level a READOUT, because without it "1:1" is
21
+ * a jump to a value nobody was told
22
+ * fit corner brackets (`yi-fit`, the same
23
+ * path as the sprite's `g6-icon-fit`)
24
+ * actual size the WRITTEN `1:1` — the action is
25
+ * `zoomTo(1)`, and actual size is written
26
+ * in every editor that offers it, never
27
+ * drawn, because there is no glyph for it
28
+ *
29
+ * Fold is the one the treedb graph does not have, so its vocabulary
30
+ * comes from the other side of the family: the lazy tree viewer's own
31
+ * toolbar (`c_yui_json.js`) uses chevrons — open pointing down, closed
32
+ * pointing right — which is also the direction the per-node handles
33
+ * speak. An eye means show/hide, which is a different idea.
34
+ *
35
+ * Copyright (c) 2026, ArtGins.
36
+ * All Rights Reserved.
37
+ ***********************************************************************/
38
+ import {createElement2, gobj_send_event} from "@yuneta/gobj-js";
39
+
40
+ import {t} from "i18next";
41
+
42
+
43
+ /************************************************************
44
+ * One icon button that fires `event_name`.
45
+ ************************************************************/
46
+ function camera_button(gobj, icon, event_name, label_key, wide, extra_style)
47
+ {
48
+ return ['button', {class: `button ${event_name}`, type: 'button',
49
+ style: {height: wide, width: '2.5em'},
50
+ title: t(label_key), 'data-i18n-title': label_key,
51
+ 'aria-label': t(label_key), 'data-i18n-aria-label': label_key},
52
+ ['i', {style: 'font-size:1.5em; color:inherit;' + (extra_style || ''),
53
+ class: icon}],
54
+ {
55
+ click: (evt) => {
56
+ evt.stopPropagation();
57
+ gobj_send_event(gobj, event_name, {evt: evt}, gobj);
58
+ }
59
+ }
60
+ ];
61
+ }
62
+
63
+ /************************************************************
64
+ * The current zoom as a percentage.
65
+ ************************************************************/
66
+ export function yui_graph_zoom_text(graph)
67
+ {
68
+ if(!graph) {
69
+ return "100%";
70
+ }
71
+ try {
72
+ return Math.round(graph.getZoom() * 100) + "%";
73
+ } catch(e) {
74
+ return "100%";
75
+ }
76
+ }
77
+
78
+ /************************************************************
79
+ * Repaint the readout of a container that carries one.
80
+ *
81
+ * Call it from G6's `aftertransform`, which is the ONE hook
82
+ * that also covers the WHEEL — a camera change that passes
83
+ * through no action, so repainting from each zoom action
84
+ * leaves the number lying after every notch.
85
+ ************************************************************/
86
+ export function yui_graph_update_zoom($container, graph)
87
+ {
88
+ if(!$container) {
89
+ return;
90
+ }
91
+ let $readout = $container.querySelector('.GRAPH_ZOOM_LEVEL');
92
+ if(!$readout) {
93
+ return; /* a toolbar without a readout: nobody to report to */
94
+ }
95
+ $readout.textContent = yui_graph_zoom_text(graph);
96
+ }
97
+
98
+ /************************************************************
99
+ * The camera cluster: zoom in, zoom out, readout, fit,
100
+ * actual size. Returns createElement2 specs, in order.
101
+ *
102
+ * `wide` is the host toolbar's button height.
103
+ ************************************************************/
104
+ export function yui_graph_camera_items(gobj, graph, wide)
105
+ {
106
+ return [
107
+ camera_button(gobj, "yi-magnifying-glass-plus", "EV_ZOOM_IN", "zoom in", wide),
108
+ camera_button(gobj, "yi-magnifying-glass-minus", "EV_ZOOM_OUT", "zoom out", wide),
109
+ ['span', {class: 'GRAPH_ZOOM_LEVEL is-flex is-align-items-center px-2 has-text-grey',
110
+ style: 'font-size:.85rem; min-width:3.5em; justify-content:center;',
111
+ title: t('zoom level'), 'data-i18n-title': 'zoom level'},
112
+ yui_graph_zoom_text(graph)],
113
+ camera_button(gobj, "yi-fit", "EV_CENTER", "auto fit", wide),
114
+ /* Written, not drawn — see the header. */
115
+ ['button', {class: 'button EV_ZOOM_RESET', type: 'button',
116
+ style: {height: wide, width: '2.5em'},
117
+ title: t('actual size'), 'data-i18n-title': 'actual size',
118
+ 'aria-label': t('actual size'), 'data-i18n-aria-label': 'actual size'},
119
+ ['span', {style: 'font-weight:700;'}, '1:1'],
120
+ {
121
+ click: (evt) => {
122
+ evt.stopPropagation();
123
+ gobj_send_event(gobj, "EV_ZOOM_RESET", {evt: evt}, gobj);
124
+ }
125
+ }]
126
+ ];
127
+ }
128
+
129
+ /************************************************************
130
+ * The fold pair: expand all, collapse all. Chevrons, the
131
+ * open one rotated down — the same pair the lazy tree
132
+ * viewer's toolbar uses, and the same direction the
133
+ * per-node handles speak.
134
+ ************************************************************/
135
+ export function yui_graph_fold_items(gobj, wide)
136
+ {
137
+ return [
138
+ camera_button(gobj, "yi-chevron-right", "EV_EXPAND_ALL", "expand all", wide,
139
+ ' transform: rotate(90deg);'),
140
+ camera_button(gobj, "yi-chevron-right", "EV_COLLAPSE_ALL", "collapse all", wide),
141
+ ];
142
+ }
143
+
144
+ /************************************************************
145
+ * A refresh button, for the graphs that offer one.
146
+ ************************************************************/
147
+ export function yui_graph_refresh_item(gobj, wide)
148
+ {
149
+ return camera_button(gobj, "yi-arrows-rotate", "EV_REFRESH", "refresh", wide);
150
+ }