@yuneta/gobj-ui 2.0.0 → 2.2.3
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 +25 -20
- package/dist/gobj-ui.cjs.js +2340 -528
- package/dist/gobj-ui.es.js +2340 -530
- package/index.js +32 -31
- package/package.json +6 -7
- package/{c_g6_nodes_tree.js → src/c_g6_nodes_tree.js} +6 -1
- package/{c_yui_form.js → src/c_yui_form.js} +1 -1
- package/{c_yui_gobj_tree_js.js → src/c_yui_gobj_tree_js.js} +1 -1
- package/{c_yui_json_graph.js → src/c_yui_json_graph.js} +1 -1
- package/{c_yui_main.js → src/c_yui_main.js} +3 -3
- package/{c_yui_map.js → src/c_yui_map.js} +6 -1
- package/{c_yui_nav.js → src/c_yui_nav.js} +134 -26
- package/{c_yui_pager.js → src/c_yui_pager.js} +1 -1
- package/{c_yui_routing.css → src/c_yui_routing.css} +1 -1
- package/{c_yui_routing.js → src/c_yui_routing.js} +1 -1
- package/{c_yui_shell.css → src/c_yui_shell.css} +110 -0
- package/{c_yui_shell.js → src/c_yui_shell.js} +175 -13
- package/{c_yui_tabs.js → src/c_yui_tabs.js} +1 -1
- package/{c_yui_treedb_graph.js → src/c_yui_treedb_graph.js} +35 -9
- package/{c_yui_treedb_topic_with_form.js → src/c_yui_treedb_topic_with_form.js} +1 -1
- package/{c_yui_treedb_topics.js → src/c_yui_treedb_topics.js} +36 -8
- package/{c_yui_uplot.js → src/c_yui_uplot.js} +1 -1
- package/{c_yui_window.js → src/c_yui_window.js} +242 -21
- package/src/c_yui_window_manager.js +602 -0
- package/{c_yui_wizard.js → src/c_yui_wizard.js} +1 -1
- package/{shell_modals.js → src/shell_modals.js} +53 -13
- package/src/tabulator.css +53 -0
- package/src/yui_dev.js +1484 -0
- package/{yui_icons.css → src/yui_icons.css} +5 -0
- package/src/yui_inputs.css +32 -0
- package/src/yui_inputs.js +71 -0
- package/vite.config.js +0 -131
- package/yui_dev.js +0 -583
- /package/{c_yui_main.css → src/c_yui_main.css} +0 -0
- /package/{c_yui_map.css → src/c_yui_map.css} +0 -0
- /package/{c_yui_treedb_topic_with_form.css → src/c_yui_treedb_topic_with_form.css} +0 -0
- /package/{g6_drag_canvas_touch.js → src/g6_drag_canvas_touch.js} +0 -0
- /package/{lib_graph.css → src/lib_graph.css} +0 -0
- /package/{lib_graph.js → src/lib_graph.js} +0 -0
- /package/{lib_icons.js → src/lib_icons.js} +0 -0
- /package/{lib_maplibre.js → src/lib_maplibre.js} +0 -0
- /package/{pager_helpers.js → src/pager_helpers.js} +0 -0
- /package/{pager_helpers.test.js → src/pager_helpers.test.js} +0 -0
- /package/{route_resolver.js → src/route_resolver.js} +0 -0
- /package/{route_resolver.test.js → src/route_resolver.test.js} +0 -0
- /package/{shell_focus_trap.js → src/shell_focus_trap.js} +0 -0
- /package/{shell_focus_trap.test.js → src/shell_focus_trap.test.js} +0 -0
- /package/{shell_show_on.js → src/shell_show_on.js} +0 -0
- /package/{shell_show_on.test.js → src/shell_show_on.test.js} +0 -0
- /package/{shell_toolbar_helpers.js → src/shell_toolbar_helpers.js} +0 -0
- /package/{shell_toolbar_helpers.test.js → src/shell_toolbar_helpers.test.js} +0 -0
- /package/{themes.js → src/themes.js} +0 -0
- /package/{wizard_helpers.js → src/wizard_helpers.js} +0 -0
- /package/{wizard_helpers.test.js → src/wizard_helpers.test.js} +0 -0
- /package/{ytable.css → src/ytable.css} +0 -0
- /package/{ytable.js → src/ytable.js} +0 -0
- /package/{yui_toolbar.css → src/yui_toolbar.css} +0 -0
- /package/{yui_toolbar.js → src/yui_toolbar.js} +0 -0
package/index.js
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
/***********************************************************************
|
|
2
|
-
*
|
|
2
|
+
* @yuneta/gobj-ui/index.js (v2 / main line)
|
|
3
3
|
*
|
|
4
4
|
* Yuneta UI Library - Reusable GUI components.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* yunetas/kernel/js/gobj-ui and restore the package dep.
|
|
6
|
+
* Barrel re-exports for the v2 source, which lives in src/.
|
|
7
|
+
* This is the canonical line, embedded as the yunetas submodule
|
|
8
|
+
* kernel/js/gobj-ui and consumed by wattyzer via a file: dep.
|
|
10
9
|
*
|
|
11
|
-
* Copyright (c)
|
|
10
|
+
* Copyright (c) 2024-2026, ArtGins.
|
|
12
11
|
* All Rights Reserved.
|
|
13
12
|
***********************************************************************/
|
|
14
13
|
|
|
15
14
|
/*
|
|
16
15
|
* Components
|
|
17
16
|
*/
|
|
18
|
-
export { register_c_yui_main, display_volatil_modal, display_info_message, display_warning_message, display_error_message, get_yesnocancel, get_yesno, get_ok } from "./c_yui_main.js";
|
|
19
|
-
export { register_c_yui_window } from "./c_yui_window.js";
|
|
20
|
-
export {
|
|
21
|
-
export {
|
|
22
|
-
export {
|
|
23
|
-
export {
|
|
24
|
-
export {
|
|
25
|
-
export {
|
|
26
|
-
export {
|
|
27
|
-
export {
|
|
28
|
-
export {
|
|
17
|
+
export { register_c_yui_main, display_volatil_modal, display_info_message, display_warning_message, display_error_message, get_yesnocancel, get_yesno, get_ok } from "./src/c_yui_main.js";
|
|
18
|
+
export { register_c_yui_window } from "./src/c_yui_window.js";
|
|
19
|
+
export { register_c_yui_window_manager } from "./src/c_yui_window_manager.js";
|
|
20
|
+
export { register_c_yui_tabs } from "./src/c_yui_tabs.js";
|
|
21
|
+
export { register_c_yui_pager } from "./src/c_yui_pager.js";
|
|
22
|
+
export { register_c_yui_wizard } from "./src/c_yui_wizard.js";
|
|
23
|
+
export { register_c_yui_form } from "./src/c_yui_form.js";
|
|
24
|
+
export { register_c_yui_routing } from "./src/c_yui_routing.js";
|
|
25
|
+
export { register_c_yui_map } from "./src/c_yui_map.js";
|
|
26
|
+
export { register_c_yui_uplot } from "./src/c_yui_uplot.js";
|
|
27
|
+
export { register_c_yui_json_graph } from "./src/c_yui_json_graph.js";
|
|
28
|
+
export { register_c_yui_gobj_tree_js } from "./src/c_yui_gobj_tree_js.js";
|
|
29
29
|
|
|
30
30
|
/*
|
|
31
31
|
* Declarative shell + menu navigation (new in v7.4)
|
|
@@ -44,8 +44,8 @@ export {
|
|
|
44
44
|
yui_shell_set_connection_state,
|
|
45
45
|
yui_shell_set_toolbar_item_icon,
|
|
46
46
|
yui_shell_close_dropdown,
|
|
47
|
-
} from "./c_yui_shell.js";
|
|
48
|
-
export { register_c_yui_nav } from "./c_yui_nav.js";
|
|
47
|
+
} from "./src/c_yui_shell.js";
|
|
48
|
+
export { register_c_yui_nav } from "./src/c_yui_nav.js";
|
|
49
49
|
export {
|
|
50
50
|
yui_shell_show_info,
|
|
51
51
|
yui_shell_show_warning,
|
|
@@ -54,26 +54,27 @@ export {
|
|
|
54
54
|
yui_shell_confirm_ok,
|
|
55
55
|
yui_shell_confirm_yesno,
|
|
56
56
|
yui_shell_confirm_yesnocancel,
|
|
57
|
-
} from "./shell_modals.js";
|
|
57
|
+
} from "./src/shell_modals.js";
|
|
58
58
|
|
|
59
59
|
/*
|
|
60
60
|
* TreeDB components
|
|
61
61
|
*/
|
|
62
|
-
export { register_c_yui_treedb_topics } from "./c_yui_treedb_topics.js";
|
|
63
|
-
export { register_c_yui_treedb_topic_with_form } from "./c_yui_treedb_topic_with_form.js";
|
|
64
|
-
export { register_c_yui_treedb_graph } from "./c_yui_treedb_graph.js";
|
|
65
|
-
export { register_c_g6_nodes_tree } from "./c_g6_nodes_tree.js";
|
|
62
|
+
export { register_c_yui_treedb_topics } from "./src/c_yui_treedb_topics.js";
|
|
63
|
+
export { register_c_yui_treedb_topic_with_form } from "./src/c_yui_treedb_topic_with_form.js";
|
|
64
|
+
export { register_c_yui_treedb_graph } from "./src/c_yui_treedb_graph.js";
|
|
65
|
+
export { register_c_g6_nodes_tree } from "./src/c_g6_nodes_tree.js";
|
|
66
66
|
|
|
67
67
|
/*
|
|
68
68
|
* Libraries and utilities
|
|
69
69
|
*/
|
|
70
|
-
export { addClasses, removeClasses, toggleClasses, removeChildElements, disableElements, enableElements, set_submit_state, set_cancel_state, set_active_state, getStrokeColor } from "./lib_graph.js";
|
|
71
|
-
export { inject_svg_icons } from "./lib_icons.js";
|
|
72
|
-
export { EditControl, MarkerControl } from "./lib_maplibre.js";
|
|
73
|
-
export { themes } from "./themes.js";
|
|
74
|
-
export { YTable, createYTable } from "./ytable.js";
|
|
75
|
-
export { yui_toolbar } from "./yui_toolbar.js";
|
|
76
|
-
export {
|
|
70
|
+
export { addClasses, removeClasses, toggleClasses, removeChildElements, disableElements, enableElements, set_submit_state, set_cancel_state, set_active_state, getStrokeColor } from "./src/lib_graph.js";
|
|
71
|
+
export { inject_svg_icons } from "./src/lib_icons.js";
|
|
72
|
+
export { EditControl, MarkerControl } from "./src/lib_maplibre.js";
|
|
73
|
+
export { themes } from "./src/themes.js";
|
|
74
|
+
export { YTable, createYTable } from "./src/ytable.js";
|
|
75
|
+
export { yui_toolbar } from "./src/yui_toolbar.js";
|
|
76
|
+
export { attach_clear } from "./src/yui_inputs.js";
|
|
77
|
+
export { info_traffic, setup_dev, build_dev_panel, apply_dev_traces, dev_window_was_open } from "./src/yui_dev.js";
|
|
77
78
|
|
|
78
79
|
/*
|
|
79
80
|
* CSS - import these in your main entry point
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yuneta/gobj-ui",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/gobj-ui.cjs.js",
|
|
6
6
|
"module": "dist/gobj-ui.es.js",
|
|
@@ -11,14 +11,13 @@
|
|
|
11
11
|
},
|
|
12
12
|
"./index.js": "./index.js",
|
|
13
13
|
"./vite-plugin-yuneta-html.js": "./vite-plugin-yuneta-html.js",
|
|
14
|
-
"
|
|
14
|
+
"./src/*": "./src/*"
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
17
|
"dist/gobj-ui.es.js",
|
|
18
18
|
"dist/gobj-ui.cjs.js",
|
|
19
19
|
"index.js",
|
|
20
|
-
"
|
|
21
|
-
"/*.css",
|
|
20
|
+
"src/",
|
|
22
21
|
"vite-plugin-yuneta-html.js"
|
|
23
22
|
],
|
|
24
23
|
"description": "Yuneta UI Library — v2 (active development) declarative shell (C_YUI_SHELL+NAV+PAGER+WIZARD) + legacy GClass GUI stack (C_YUI_MAIN/WINDOW/TABS/ROUTING + TreeDB editors + charts/maps). Consumed by wattyzer. The frozen legacy-only line lives on the v1 branch (consumed by estadodelaire and hidraulia).",
|
|
@@ -30,17 +29,17 @@
|
|
|
30
29
|
"devDependencies": {
|
|
31
30
|
"@rollup/plugin-terser": "^1.0.0",
|
|
32
31
|
"@vitest/coverage-v8": "^4.1.6",
|
|
33
|
-
"@yuneta/gobj-js": "^7.
|
|
32
|
+
"@yuneta/gobj-js": "^7.7.0",
|
|
34
33
|
"vite": "^8.0.10",
|
|
35
34
|
"vitest": "^4.1.6"
|
|
36
35
|
},
|
|
37
36
|
"peerDependencies": {
|
|
38
|
-
"@yuneta/gobj-js": "^7.3.4",
|
|
39
37
|
"@antv/g6": "^5.1.0",
|
|
38
|
+
"@yuneta/gobj-js": "^7.7.0",
|
|
40
39
|
"bulma": "^1.0.4",
|
|
41
40
|
"i18next": "^26.0.7",
|
|
42
41
|
"maplibre-gl": "^5.24.0",
|
|
43
|
-
"tabulator-tables": "^6.
|
|
42
|
+
"tabulator-tables": "^6.5.2",
|
|
44
43
|
"tom-select": "^2.6.0",
|
|
45
44
|
"uplot": "^1.6.32",
|
|
46
45
|
"vanilla-jsoneditor": "^0.23.0"
|
|
@@ -575,7 +575,12 @@ function get_default_ne_xy(gobj)
|
|
|
575
575
|
************************************************************/
|
|
576
576
|
function build_ui(gobj)
|
|
577
577
|
{
|
|
578
|
-
//
|
|
578
|
+
// $container set externally; tag it so the gclass owning this
|
|
579
|
+
// G6 canvas is identifiable in the browser Inspector.
|
|
580
|
+
let $container = gobj_read_attr(gobj, "$container");
|
|
581
|
+
if($container) {
|
|
582
|
+
$container.classList.add(GCLASS_NAME);
|
|
583
|
+
}
|
|
579
584
|
}
|
|
580
585
|
|
|
581
586
|
/************************************************************
|
|
@@ -284,7 +284,7 @@ function build_ui(gobj)
|
|
|
284
284
|
*
|
|
285
285
|
*----------------------------------------------*/
|
|
286
286
|
let $container = createElement2(
|
|
287
|
-
['div', {class: '
|
|
287
|
+
['div', {class: 'C_YUI_FORM is-flex is-flex-direction-row overscroll-contain', style:'height:100%; width:100%; box-sizing:border-box;'}, [
|
|
288
288
|
|
|
289
289
|
/*----------------------------*
|
|
290
290
|
* Form
|
|
@@ -385,7 +385,7 @@ function build_ui(gobj)
|
|
|
385
385
|
let $toolbar = make_toolbar(gobj);
|
|
386
386
|
|
|
387
387
|
let $container = createElement2(
|
|
388
|
-
['div', {class: '
|
|
388
|
+
['div', {class: 'C_YUI_GOBJ_TREE_JS', style: 'height:100%; display:flex; flex-direction:column; position:relative;'}, [
|
|
389
389
|
['div', {class: 'is-flex-grow-0 is-flex toolbar_yui_gobj_tree'}, $toolbar],
|
|
390
390
|
['div', {class: 'is-flex-grow-1', style: 'height:100%; min-height:0; overflow:hidden;'}, [
|
|
391
391
|
['div', {id: priv.canvas_id, class: 'gobj-tree-container', style: 'height:100%; min-height:0; border: 1px solid var(--bulma-border-weak); border-radius:0.2rem;'}, [
|
|
@@ -221,7 +221,7 @@ function build_ui(gobj)
|
|
|
221
221
|
let $toolbar = make_toolbar(gobj);
|
|
222
222
|
|
|
223
223
|
let $container = createElement2(
|
|
224
|
-
['div', {class: '
|
|
224
|
+
['div', {class: 'C_YUI_JSON_GRAPH', style: 'height:100%; display:flex; flex-direction:column;'}, [
|
|
225
225
|
['div', {class: 'is-flex-grow-0 is-flex toolbar_yui_json_graph'}, $toolbar],
|
|
226
226
|
['div', {class: 'is-flex-grow-1', style: 'height:100%; min-height:0; overflow:hidden;'}, [
|
|
227
227
|
['div', {id: priv.canvas_id, class: 'json-graph-container', style: 'height:100%; min-height:0; border: 1px solid var(--bulma-border-weak); border-radius:0.2rem;'}, [
|
|
@@ -167,8 +167,8 @@ function build_ui(gobj)
|
|
|
167
167
|
/*----------------------------------------------*
|
|
168
168
|
* Layout Schema
|
|
169
169
|
*----------------------------------------------*/
|
|
170
|
-
const $
|
|
171
|
-
['div', {id: 'root', class: 'root'}, [
|
|
170
|
+
const $container = createElement2(
|
|
171
|
+
['div', {id: 'root', class: 'C_YUI_MAIN root'}, [
|
|
172
172
|
['div', {id: 'top-layer', class: 'top-layer'}],
|
|
173
173
|
['div', {id: 'content-layer', class: 'content-layer is-hidden'}],
|
|
174
174
|
['div', {id: 'bottom-layer', class: 'bottom-layer'}],
|
|
@@ -178,7 +178,7 @@ function build_ui(gobj)
|
|
|
178
178
|
`<iframe id="iframe-publi-page" src="./${gobj_read_attr(gobj, "publi_page")}/index.html" width="100%" height="100%"></iframe>`]
|
|
179
179
|
]]
|
|
180
180
|
);
|
|
181
|
-
document.body.appendChild($
|
|
181
|
+
document.body.appendChild($container);
|
|
182
182
|
|
|
183
183
|
const iframe = document.getElementById('iframe-publi-page');
|
|
184
184
|
if(iframe) {
|
|
@@ -111,8 +111,13 @@ function mt_create(gobj)
|
|
|
111
111
|
|
|
112
112
|
let map_settings = json_deep_copy(gobj_read_attr(gobj, "map_settings"));
|
|
113
113
|
|
|
114
|
+
let $map = gobj_read_attr(gobj, "$map");
|
|
115
|
+
if($map) {
|
|
116
|
+
/* Tag the external mount so this gclass is identifiable in the Inspector */
|
|
117
|
+
$map.classList.add(GCLASS_NAME);
|
|
118
|
+
}
|
|
114
119
|
Object.assign(map_settings, {
|
|
115
|
-
container:
|
|
120
|
+
container: $map,
|
|
116
121
|
});
|
|
117
122
|
|
|
118
123
|
/*-----------------------------*
|
|
@@ -15,6 +15,15 @@
|
|
|
15
15
|
*
|
|
16
16
|
* Each item supports:
|
|
17
17
|
* id, name, icon (CSS class or svg id), route, badge, disabled
|
|
18
|
+
* class — extra CSS class(es) on the item (tabs layout), e.g.
|
|
19
|
+
* a per-item state colour like "yui-nav-disconnected"
|
|
20
|
+
* closable — render a trailing ✕ that emits EV_NAV_ITEM_CLOSE
|
|
21
|
+
* instead of navigating (tabs layout)
|
|
22
|
+
*
|
|
23
|
+
* Items can be replaced at runtime by sending EV_SET_ITEMS
|
|
24
|
+
* {items:[...]} to the nav; the DOM is rebuilt in place and the
|
|
25
|
+
* active highlight restored. The app_config path is simply the
|
|
26
|
+
* first, startup caller of the same mechanism.
|
|
18
27
|
*
|
|
19
28
|
* In addition to navigable items, secondary navs accept two
|
|
20
29
|
* decorative item kinds for in-place visual grouping:
|
|
@@ -182,34 +191,90 @@ function build_ui(gobj)
|
|
|
182
191
|
let items = gobj_read_attr(gobj, "menu_items") || [];
|
|
183
192
|
let zone = gobj_read_attr(gobj, "zone") || "";
|
|
184
193
|
let menu_id = gobj_read_attr(gobj, "menu_id") || "nav";
|
|
185
|
-
let $
|
|
194
|
+
let $container;
|
|
186
195
|
|
|
187
196
|
switch(layout) {
|
|
188
|
-
case "vertical": $
|
|
189
|
-
case "icon-bar": $
|
|
190
|
-
case "tabs": $
|
|
191
|
-
case "drawer": $
|
|
192
|
-
case "submenu": $
|
|
193
|
-
case "accordion": $
|
|
194
|
-
default: $
|
|
195
|
-
}
|
|
196
|
-
$
|
|
197
|
-
$
|
|
198
|
-
$
|
|
197
|
+
case "vertical": $container = render_vertical(gobj, items); break;
|
|
198
|
+
case "icon-bar": $container = render_icon_bar(gobj, items); break;
|
|
199
|
+
case "tabs": $container = render_tabs(gobj, items); break;
|
|
200
|
+
case "drawer": $container = render_drawer(gobj, items); break;
|
|
201
|
+
case "submenu": $container = render_submenu(gobj, items); break;
|
|
202
|
+
case "accordion": $container = render_accordion(gobj, items); break;
|
|
203
|
+
default: $container = render_vertical(gobj, items);
|
|
204
|
+
}
|
|
205
|
+
$container.setAttribute("data-nav-zone", zone);
|
|
206
|
+
$container.setAttribute("data-nav-layout", layout);
|
|
207
|
+
$container.classList.add("C_YUI_NAV", "yui-nav", `yui-nav-${layout}`);
|
|
199
208
|
|
|
200
209
|
/* Accessibility: every nav root is a landmark. For the drawer we
|
|
201
210
|
* tag the wrapper as role=dialog and the panel as role=navigation. */
|
|
202
211
|
if(layout !== "drawer") {
|
|
203
|
-
$
|
|
204
|
-
if(!$
|
|
212
|
+
$container.setAttribute("role", "navigation");
|
|
213
|
+
if(!$container.hasAttribute("aria-label")) {
|
|
205
214
|
let label = gobj_read_attr(gobj, "nav_label") || menu_id;
|
|
206
|
-
$
|
|
215
|
+
$container.setAttribute("aria-label", label);
|
|
207
216
|
}
|
|
208
217
|
}
|
|
209
218
|
|
|
210
|
-
wire_clicks(gobj, $
|
|
219
|
+
wire_clicks(gobj, $container);
|
|
220
|
+
|
|
221
|
+
gobj_write_attr(gobj, "$container", $container);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/************************************************************
|
|
225
|
+
* Rebuild the nav DOM in place from the current menu_items.
|
|
226
|
+
* Used by EV_SET_ITEMS so a nav's tabs can be changed at runtime
|
|
227
|
+
* (the app_config path is just the first, startup caller).
|
|
228
|
+
************************************************************/
|
|
229
|
+
function rebuild(gobj)
|
|
230
|
+
{
|
|
231
|
+
let $old = gobj_read_attr(gobj, "$container");
|
|
232
|
+
let priv = gobj_read_attr(gobj, "priv") || {};
|
|
233
|
+
let parent = $old ? $old.parentNode : null;
|
|
234
|
+
let next = $old ? $old.nextSibling : null;
|
|
235
|
+
let was_hidden = !!($old && $old.classList.contains("is-hidden"));
|
|
236
|
+
|
|
237
|
+
/* Drop the old delegated click listener before discarding the node. */
|
|
238
|
+
if($old && priv.click_handler) {
|
|
239
|
+
$old.removeEventListener("click", priv.click_handler);
|
|
240
|
+
}
|
|
211
241
|
|
|
212
|
-
|
|
242
|
+
build_ui(gobj); /* writes a fresh $container + wires new clicks */
|
|
243
|
+
|
|
244
|
+
let $new = gobj_read_attr(gobj, "$container");
|
|
245
|
+
if($new && was_hidden) {
|
|
246
|
+
$new.classList.add("is-hidden");
|
|
247
|
+
}
|
|
248
|
+
if(parent && $new) {
|
|
249
|
+
parent.insertBefore($new, next);
|
|
250
|
+
}
|
|
251
|
+
if($old && $old.parentNode) {
|
|
252
|
+
$old.parentNode.removeChild($old);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
apply_active_route(gobj);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/************************************************************
|
|
259
|
+
* Re-apply the active-route highlight after a rebuild (the
|
|
260
|
+
* shell only pushes EV_ROUTE_CHANGED on navigation, not on a
|
|
261
|
+
* self-triggered items change).
|
|
262
|
+
************************************************************/
|
|
263
|
+
function apply_active_route(gobj)
|
|
264
|
+
{
|
|
265
|
+
let $c = gobj_read_attr(gobj, "$container");
|
|
266
|
+
let route = gobj_read_attr(gobj, "active_route") || "";
|
|
267
|
+
if(!$c || empty_string(route)) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
let $a = $c.querySelector(`a[data-route="${css_escape(route)}"]`);
|
|
271
|
+
if($a) {
|
|
272
|
+
let $li = $a.closest("li");
|
|
273
|
+
if($li) {
|
|
274
|
+
$li.classList.add("is-active");
|
|
275
|
+
}
|
|
276
|
+
$a.classList.add("is-active");
|
|
277
|
+
}
|
|
213
278
|
}
|
|
214
279
|
|
|
215
280
|
/************************************************************
|
|
@@ -283,6 +348,15 @@ function render_tabs(gobj, items)
|
|
|
283
348
|
if(show_label && !empty_string(it.name)) {
|
|
284
349
|
children.push(["span", {i18n: it.name}, it.name]);
|
|
285
350
|
}
|
|
351
|
+
/* Optional close affordance: a trailing ✕ that emits
|
|
352
|
+
* EV_NAV_ITEM_CLOSE instead of navigating (caught first in
|
|
353
|
+
* wire_clicks by its data-close-item marker). */
|
|
354
|
+
if(it.closable) {
|
|
355
|
+
children.push(["span", {class: "icon is-small yui-nav-close ml-2",
|
|
356
|
+
"data-close-item": it.id, role: "button",
|
|
357
|
+
"aria-label": "close", title: "close"},
|
|
358
|
+
["i", {class: "yi-xmark", "aria-hidden":"true"}]]);
|
|
359
|
+
}
|
|
286
360
|
let a_attrs = {
|
|
287
361
|
href: it.route ? "#" + it.route : "#",
|
|
288
362
|
"data-item-id": it.id,
|
|
@@ -293,8 +367,11 @@ function render_tabs(gobj, items)
|
|
|
293
367
|
a_attrs.title = tip;
|
|
294
368
|
a_attrs["data-i18n-title"] = tip;
|
|
295
369
|
}
|
|
370
|
+
/* Per-item state class (e.g. a "disconnected" colour) rides on
|
|
371
|
+
* the <li>, alongside Bulma's own is-active. */
|
|
372
|
+
let li_class = empty_string(it.class) ? "" : String(it.class);
|
|
296
373
|
lis.push(
|
|
297
|
-
["li", {class:
|
|
374
|
+
["li", {class: li_class, "data-item-id": it.id, "data-route": it.route || ""},
|
|
298
375
|
["a", a_attrs, children]
|
|
299
376
|
]
|
|
300
377
|
);
|
|
@@ -369,7 +446,7 @@ function render_accordion(gobj, items)
|
|
|
369
446
|
let show_label = gobj_read_attr(gobj, "show_label");
|
|
370
447
|
let icon_pos = gobj_read_attr(gobj, "icon_pos");
|
|
371
448
|
|
|
372
|
-
let $
|
|
449
|
+
let $container = createElement2(["aside", {class: "menu yui-nav-accordion p-2"}]);
|
|
373
450
|
for(let it of items) {
|
|
374
451
|
if(is_decorative(it)) {
|
|
375
452
|
continue; /* primary-level decorations don't apply to accordion sections */
|
|
@@ -392,7 +469,7 @@ function render_accordion(gobj, items)
|
|
|
392
469
|
head_attrs.i18n = head_text;
|
|
393
470
|
}
|
|
394
471
|
let $hdr = createElement2(["button", head_attrs, head_text]);
|
|
395
|
-
$
|
|
472
|
+
$container.appendChild($hdr);
|
|
396
473
|
|
|
397
474
|
let $ul = createElement2(
|
|
398
475
|
["ul", {id: body_id,
|
|
@@ -407,9 +484,9 @@ function render_accordion(gobj, items)
|
|
|
407
484
|
));
|
|
408
485
|
}
|
|
409
486
|
}
|
|
410
|
-
$
|
|
487
|
+
$container.appendChild($ul);
|
|
411
488
|
}
|
|
412
|
-
return $
|
|
489
|
+
return $container;
|
|
413
490
|
}
|
|
414
491
|
|
|
415
492
|
/************************************************************
|
|
@@ -557,7 +634,7 @@ function item_iconbar(gobj, it, opts)
|
|
|
557
634
|
];
|
|
558
635
|
}
|
|
559
636
|
|
|
560
|
-
function wire_clicks(gobj, $
|
|
637
|
+
function wire_clicks(gobj, $container)
|
|
561
638
|
{
|
|
562
639
|
let priv = gobj_read_attr(gobj, "priv") || {};
|
|
563
640
|
|
|
@@ -567,6 +644,22 @@ function wire_clicks(gobj, $root)
|
|
|
567
644
|
return;
|
|
568
645
|
}
|
|
569
646
|
|
|
647
|
+
/* Close affordance (✕ on a closable tab): emit the intent and
|
|
648
|
+
* let the shell/app remove the item; never navigate. */
|
|
649
|
+
let $close = target.closest("[data-close-item]");
|
|
650
|
+
if($close) {
|
|
651
|
+
let $routed = $close.closest("[data-route]");
|
|
652
|
+
gobj_publish_event(gobj, "EV_NAV_ITEM_CLOSE", {
|
|
653
|
+
item_id: $close.getAttribute("data-close-item") || "",
|
|
654
|
+
route: ($routed && $routed.getAttribute("data-route")) || "",
|
|
655
|
+
menu_id: gobj_read_attr(gobj, "menu_id") || "",
|
|
656
|
+
zone: gobj_read_attr(gobj, "zone") || ""
|
|
657
|
+
});
|
|
658
|
+
ev.preventDefault();
|
|
659
|
+
ev.stopPropagation();
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
|
|
570
663
|
/* Drawer backdrop close. Don't mutate the DOM directly —
|
|
571
664
|
* the shell owns the drawer state (focus-trap, escape
|
|
572
665
|
* stack) and must run its full close path. Publish the
|
|
@@ -584,7 +677,7 @@ function wire_clicks(gobj, $root)
|
|
|
584
677
|
/* Accordion head toggle takes precedence over navigation: the
|
|
585
678
|
* head is a container for its sub-items, not a destination. */
|
|
586
679
|
let $head = target.closest(".yui-accordion-head");
|
|
587
|
-
if($head && $
|
|
680
|
+
if($head && $container.contains($head)) {
|
|
588
681
|
let $next = $head.nextElementSibling;
|
|
589
682
|
if($next && $next.classList.contains("yui-accordion-body")) {
|
|
590
683
|
let open = $next.classList.contains("is-hidden");
|
|
@@ -622,7 +715,7 @@ function wire_clicks(gobj, $root)
|
|
|
622
715
|
});
|
|
623
716
|
};
|
|
624
717
|
|
|
625
|
-
$
|
|
718
|
+
$container.addEventListener("click", handler);
|
|
626
719
|
priv.click_handler = handler;
|
|
627
720
|
}
|
|
628
721
|
|
|
@@ -636,6 +729,18 @@ function wire_clicks(gobj, $root)
|
|
|
636
729
|
|
|
637
730
|
|
|
638
731
|
|
|
732
|
+
/************************************************************
|
|
733
|
+
* EV_SET_ITEMS — replace this nav's items and re-render in place.
|
|
734
|
+
* This is the runtime counterpart of the startup config path:
|
|
735
|
+
* the shell (or app, via the shell) drives dynamic navs through it.
|
|
736
|
+
************************************************************/
|
|
737
|
+
function ac_set_items(gobj, event, kw, src)
|
|
738
|
+
{
|
|
739
|
+
gobj_write_attr(gobj, "menu_items", is_array(kw.items) ? kw.items : []);
|
|
740
|
+
rebuild(gobj);
|
|
741
|
+
return 0;
|
|
742
|
+
}
|
|
743
|
+
|
|
639
744
|
/************************************************************
|
|
640
745
|
* EV_ROUTE_CHANGED from the shell — refresh active highlight
|
|
641
746
|
************************************************************/
|
|
@@ -739,13 +844,16 @@ function create_gclass(gclass_name)
|
|
|
739
844
|
|
|
740
845
|
const states = [
|
|
741
846
|
["ST_IDLE", [
|
|
742
|
-
["EV_ROUTE_CHANGED", ac_route_changed, null]
|
|
847
|
+
["EV_ROUTE_CHANGED", ac_route_changed, null],
|
|
848
|
+
["EV_SET_ITEMS", ac_set_items, null]
|
|
743
849
|
]]
|
|
744
850
|
];
|
|
745
851
|
|
|
746
852
|
const event_types = [
|
|
747
853
|
["EV_ROUTE_CHANGED", 0],
|
|
854
|
+
["EV_SET_ITEMS", 0],
|
|
748
855
|
["EV_NAV_CLICKED", event_flag_t.EVF_OUTPUT_EVENT|event_flag_t.EVF_PUBLIC_EVENT],
|
|
856
|
+
["EV_NAV_ITEM_CLOSE", event_flag_t.EVF_OUTPUT_EVENT|event_flag_t.EVF_PUBLIC_EVENT],
|
|
749
857
|
["EV_DRAWER_CLOSE_REQUESTED", event_flag_t.EVF_OUTPUT_EVENT|event_flag_t.EVF_PUBLIC_EVENT]
|
|
750
858
|
];
|
|
751
859
|
|
|
@@ -145,7 +145,7 @@ function mt_destroy(gobj)
|
|
|
145
145
|
function build_ui(gobj)
|
|
146
146
|
{
|
|
147
147
|
let $container = createElement2(
|
|
148
|
-
['div', {class: '
|
|
148
|
+
['div', {class: 'C_YUI_PAGER', style: 'height:100%; display:flex; flex-direction:column;'}, [
|
|
149
149
|
['div', {class: 'yui-pager-header is-flex is-align-items-center is-flex-grow-0',
|
|
150
150
|
style: 'gap:.25rem; padding:.25rem .25rem;'}, [
|
|
151
151
|
['button', {class: 'yui-pager-back button is-white is-hidden', 'aria-label': 'back'}, [
|
|
@@ -301,7 +301,7 @@ function build_layout(gobj)
|
|
|
301
301
|
* Layout Schema
|
|
302
302
|
*----------------------------------------------*/
|
|
303
303
|
const $container = createElement2(
|
|
304
|
-
['div', {class: '
|
|
304
|
+
['div', {class: 'C_YUI_ROUTING', style: ''}, [
|
|
305
305
|
['div', {class: 'container-grid-2col-left'},
|
|
306
306
|
['aside', {class: 'menu'}, [
|
|
307
307
|
['ul', {class: 'menu-list'}, []]
|
|
@@ -174,6 +174,21 @@
|
|
|
174
174
|
/*--- tabs (Bulma .tabs) ---*/
|
|
175
175
|
.yui-nav-tabs .tabs ul { flex-wrap: wrap; }
|
|
176
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
|
+
|
|
177
192
|
/*--- stacked item (icon above label) ---*/
|
|
178
193
|
.yui-nav-stacked {
|
|
179
194
|
display: inline-flex;
|
|
@@ -459,3 +474,98 @@
|
|
|
459
474
|
font: inherit;
|
|
460
475
|
color: inherit;
|
|
461
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
|
+
}
|