@yuneta/gobj-ui 2.6.0 → 4.0.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 (56) hide show
  1. package/README.md +331 -11
  2. package/dist/gobj-ui.cjs.js +21386 -17919
  3. package/dist/gobj-ui.es.js +26059 -22613
  4. package/index.js +45 -9
  5. package/package.json +4 -4
  6. package/src/c_g6_nodes_tree.js +128 -40
  7. package/src/c_yui_form.css +9 -0
  8. package/src/c_yui_form.js +113 -53
  9. package/src/c_yui_gobj_tree_js.js +58 -36
  10. package/src/c_yui_json.css +139 -0
  11. package/src/c_yui_json.js +928 -0
  12. package/src/c_yui_json_graph.js +110 -20
  13. package/src/c_yui_map.js +18 -36
  14. package/src/c_yui_nav.js +6 -9
  15. package/src/c_yui_period.css +184 -0
  16. package/src/c_yui_period.js +1441 -0
  17. package/src/c_yui_shell.css +120 -0
  18. package/src/c_yui_shell.js +672 -111
  19. package/src/c_yui_treedb_graph.js +639 -37
  20. package/src/c_yui_treedb_schema.js +478 -0
  21. package/src/c_yui_treedb_topic_with_form.css +18 -0
  22. package/src/c_yui_treedb_topic_with_form.js +153 -103
  23. package/src/c_yui_treedb_topics.css +73 -0
  24. package/src/c_yui_treedb_topics.js +1070 -29
  25. package/src/c_yui_window.css +22 -0
  26. package/src/c_yui_window.js +182 -97
  27. package/src/c_yui_window_manager.js +38 -4
  28. package/src/json_view_helpers.js +214 -0
  29. package/src/json_view_helpers.test.js +135 -0
  30. package/src/route_map_model.js +317 -0
  31. package/src/route_map_model.test.js +209 -0
  32. package/src/route_resolver.js +24 -1
  33. package/src/route_resolver.test.js +40 -1
  34. package/src/shell_modals.js +162 -52
  35. package/src/shell_route_map.css +225 -0
  36. package/src/shell_route_map.js +363 -0
  37. package/src/tabulator.css +245 -0
  38. package/src/yui_dev.js +130 -8
  39. package/src/yui_frontend_view.js +106 -0
  40. package/src/yui_icons.css +62 -0
  41. package/src/yui_inputs.css +8 -3
  42. package/src/yui_inputs.js +51 -8
  43. package/src/yui_tabulator_i18n.js +128 -0
  44. package/src/yui_theme.js +147 -0
  45. package/src/yui_time.js +666 -0
  46. package/src/yui_time.test.js +330 -0
  47. package/src/yui_toolbar.css +28 -0
  48. package/src/yui_toolbar.js +86 -43
  49. package/src/c_yui_main.css +0 -501
  50. package/src/c_yui_main.js +0 -1623
  51. package/src/c_yui_routing.css +0 -18
  52. package/src/c_yui_routing.js +0 -837
  53. package/src/c_yui_tabs.js +0 -487
  54. package/src/themes.js +0 -215
  55. package/src/ytable.css +0 -63
  56. package/src/ytable.js +0 -505
package/index.js CHANGED
@@ -14,17 +14,15 @@
14
14
  /*
15
15
  * Components
16
16
  */
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
17
  export { register_c_yui_window } from "./src/c_yui_window.js";
19
18
  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
19
  export { register_c_yui_pager } from "./src/c_yui_pager.js";
22
20
  export { register_c_yui_wizard } from "./src/c_yui_wizard.js";
23
21
  export { register_c_yui_form } from "./src/c_yui_form.js";
24
- export { register_c_yui_routing } from "./src/c_yui_routing.js";
25
22
  export { register_c_yui_map } from "./src/c_yui_map.js";
26
23
  export { register_c_yui_uplot } from "./src/c_yui_uplot.js";
27
24
  export { register_c_yui_json_graph } from "./src/c_yui_json_graph.js";
25
+ export { register_c_yui_json } from "./src/c_yui_json.js";
28
26
  export { register_c_yui_gobj_tree_js } from "./src/c_yui_gobj_tree_js.js";
29
27
 
30
28
  /*
@@ -44,8 +42,19 @@ export {
44
42
  yui_shell_set_connection_state,
45
43
  yui_shell_set_toolbar_item_icon,
46
44
  yui_shell_close_dropdown,
45
+ yui_shell_register_event_handler,
46
+ yui_shell_set_sub_routes,
47
+ yui_shell_register_overlay,
48
+ yui_shell_overlay_dismissed,
49
+ yui_shell_unpark_route,
47
50
  } from "./src/c_yui_shell.js";
48
51
  export { register_c_yui_nav } from "./src/c_yui_nav.js";
52
+ /*
53
+ * Site map: the whole navigation surface (toolbar + account menu +
54
+ * nav + dynamic tabs + view-contributed sub-routes) as a clickable,
55
+ * printable tree — the app's basic documentation (ROUTING.md).
56
+ */
57
+ export { yui_shell_show_route_map } from "./src/shell_route_map.js";
49
58
  export {
50
59
  yui_shell_show_info,
51
60
  yui_shell_show_warning,
@@ -62,28 +71,55 @@ export {
62
71
  export { register_c_yui_treedb_topics } from "./src/c_yui_treedb_topics.js";
63
72
  export { register_c_yui_treedb_topic_with_form } from "./src/c_yui_treedb_topic_with_form.js";
64
73
  export { register_c_yui_treedb_graph } from "./src/c_yui_treedb_graph.js";
74
+ export { register_c_yui_treedb_schema } from "./src/c_yui_treedb_schema.js";
65
75
  export { register_c_g6_nodes_tree } from "./src/c_g6_nodes_tree.js";
66
76
 
77
+ /*
78
+ * Date navigator: granularity + arrows + calendar (C_YUI_PERIOD), on
79
+ * top of the period algebra of yui_time (unit + count, so an app gets
80
+ * quarters, semesters or bimesters by declaring them).
81
+ */
82
+ export { register_c_yui_period } from "./src/c_yui_period.js";
83
+ export {
84
+ YUI_PERIODS,
85
+ YUI_PERIODS_DEFAULT,
86
+ YUI_ROLLING,
87
+ safe_locale,
88
+ epoch_to_ms,
89
+ ms_to_epoch,
90
+ epoch_to_local_input,
91
+ local_input_to_epoch,
92
+ fmt_epoch,
93
+ iso_week,
94
+ period_spec,
95
+ period_start,
96
+ period_shift,
97
+ period_bounds,
98
+ period_bounds_epoch,
99
+ rolling_bounds,
100
+ is_current_period,
101
+ infer_period,
102
+ period_name,
103
+ period_label,
104
+ } from "./src/yui_time.js";
105
+
67
106
  /*
68
107
  * Libraries and utilities
69
108
  */
70
109
  export { addClasses, removeClasses, toggleClasses, removeChildElements, disableElements, enableElements, set_submit_state, set_cancel_state, set_active_state, getStrokeColor } from "./src/lib_graph.js";
71
110
  export { inject_svg_icons } from "./src/lib_icons.js";
72
111
  export { EditControl, MarkerControl } from "./src/lib_maplibre.js";
73
- export { themes } from "./src/themes.js";
74
- export { YTable, createYTable } from "./src/ytable.js";
75
112
  export { yui_toolbar } from "./src/yui_toolbar.js";
76
- export { attach_clear } from "./src/yui_inputs.js";
113
+ export { yui_theme_now, yui_is_dark, yui_watch_theme } from "./src/yui_theme.js";
114
+ export { attach_clear, refresh_clear } from "./src/yui_inputs.js";
77
115
  export { info_traffic, setup_dev, build_dev_panel, apply_dev_traces, dev_window_was_open } from "./src/yui_dev.js";
116
+ export { setup_frontend_view } from "./src/yui_frontend_view.js";
78
117
 
79
118
  /*
80
119
  * CSS - import these in your main entry point
81
120
  * Example:
82
- * import "gobj-ui/src/c_yui_main.css";
83
121
  * import "gobj-ui/src/c_yui_shell.css"; // declarative shell
84
122
  * import "gobj-ui/src/c_yui_map.css";
85
- * import "gobj-ui/src/c_yui_routing.css";
86
- * import "gobj-ui/src/ytable.css";
87
123
  * import "gobj-ui/src/yui_toolbar.css";
88
124
  * import "gobj-ui/src/lib_graph.css";
89
125
  * import "gobj-ui/src/yui_icons.css";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "2.6.0",
3
+ "version": "4.0.0",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
@@ -20,7 +20,7 @@
20
20
  "src/",
21
21
  "vite-plugin-yuneta-html.js"
22
22
  ],
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).",
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
24
  "scripts": {
25
25
  "build": "vite build",
26
26
  "test": "vitest run",
@@ -29,13 +29,13 @@
29
29
  "devDependencies": {
30
30
  "@rollup/plugin-terser": "^1.0.0",
31
31
  "@vitest/coverage-v8": "^4.1.6",
32
- "@yuneta/gobj-js": "^7.7.0",
32
+ "@yuneta/gobj-js": "^7.8.0",
33
33
  "vite": "^8.0.10",
34
34
  "vitest": "^4.1.6"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "@antv/g6": "^5.1.0",
38
- "@yuneta/gobj-js": "^7.7.0",
38
+ "@yuneta/gobj-js": "^7.8.0",
39
39
  "bulma": "^1.0.4",
40
40
  "i18next": "^26.0.7",
41
41
  "maplibre-gl": "^5.24.0",
@@ -39,12 +39,12 @@ import {
39
39
  gobj_subscribe_event,
40
40
  gobj_publish_event,
41
41
  gobj_send_event,
42
- gobj_find_service,
43
42
  gobj_save_persistent_attrs,
44
43
  clean_name,
45
44
  sprintf,
46
45
  is_string,
47
46
  is_array,
47
+ empty_string,
48
48
  is_object,
49
49
  kw_get_int,
50
50
  kw_get_str,
@@ -96,6 +96,7 @@ import i18next, {t} from "i18next";
96
96
 
97
97
  import {inject_svg_icons} from "./lib_icons.js";
98
98
  import {ensure_drag_canvas_patch} from "./g6_drag_canvas_touch.js";
99
+ import {yui_theme_now, yui_watch_theme} from "./yui_theme.js";
99
100
 
100
101
  /***************************************************************
101
102
  * YuiToolbar — G6 Toolbar subclass that adds per-item className
@@ -274,6 +275,9 @@ let PRIVATE_DATA = {
274
275
  _link_drag_svg: null, // SVG overlay for drag line
275
276
  _link_valid_hooks: [], // [{node_id, port_key}] compatible hook targets
276
277
  _link_saved_styles: [], // saved port styles to restore on cancel
278
+ _focus_topic: null, // topic currently focused (EV_FOCUS_TOPIC)
279
+ _focus_ids: [], // node ids carrying the focus 'active' state
280
+ _pending_focus_topic: null, // focus requested before data was loaded
277
281
  };
278
282
 
279
283
  let __gclass__ = null;
@@ -304,39 +308,20 @@ function mt_create(gobj)
304
308
  }
305
309
  gobj_subscribe_event(gobj, null, {}, subscriber);
306
310
 
307
- /* Optional legacy integration: EV_THEME from C_YUI_MAIN
308
- * (old shell). Under the new C_YUI_SHELL there is no
309
- * __yui_main__ look it up SILENTLY (no verbose) so its
310
- * absence is not logged as an error every mount. */
311
- let __yui_main__ = gobj_find_service("__yui_main__");
312
- if(__yui_main__) {
313
- gobj_subscribe_event(__yui_main__, "EV_THEME", {}, gobj);
314
- gobj_write_attr(gobj, "theme", gobj_read_str_attr(__yui_main__, "theme"));
315
- } else {
316
- /* New C_YUI_SHELL: no __yui_main__, no EV_THEME broadcast.
317
- * Read the theme from <html data-theme> (the Bulma/wz_theme
318
- * convention) and react to changes with a MutationObserver,
319
- * re-using ac_theme via EV_THEME. Without this priv.theme
320
- * stays "light" and the grid-line uses the light colour
321
- * (#EEEEEE) → glaring white grid on the dark canvas. */
322
- let $html = document.documentElement;
323
- let dt = $html.getAttribute("data-theme");
324
- if(dt === "dark" || dt === "light") {
325
- gobj_write_attr(gobj, "theme", dt);
326
- }
327
- if(typeof MutationObserver !== "undefined") {
328
- let mo = new MutationObserver(() => {
329
- let nt = $html.getAttribute("data-theme");
330
- if(nt === "dark" || nt === "light") {
331
- gobj_send_event(gobj, "EV_THEME", {theme: nt}, gobj);
332
- }
333
- });
334
- mo.observe($html, {
335
- attributes: true, attributeFilter: ["data-theme"]
336
- });
337
- gobj.priv.theme_observer = mo;
338
- }
339
- }
311
+ /* The theme is <html data-theme> (the Bulma/wz_theme convention) and
312
+ * the DOM mutation IS the notification: translate it into EV_THEME so
313
+ * ac_theme does the work inside the machine. Without this priv.theme
314
+ * stays "light" and the grid-line uses the light colour (#EEEEEE) →
315
+ * glaring white grid on the dark canvas.
316
+ *
317
+ * This used to be the ELSE of "did the app register a legacy
318
+ * C_YUI_MAIN __yui_main__ service?", whose `theme` attr we read and
319
+ * whose EV_THEME we subscribed to. That branch was retired: nothing
320
+ * ever WROTE that attr (it stayed at its "light" default for the life
321
+ * of the app) and no shell published EV_THEME, so merely having such a
322
+ * service swapped this working observer for a dead path. */
323
+ gobj_write_attr(gobj, "theme", yui_theme_now());
324
+ gobj.priv.theme_observer = yui_watch_theme(gobj);
340
325
 
341
326
  build_ui(gobj);
342
327
  register_layouts(gobj);
@@ -634,6 +619,15 @@ function build_graph(gobj)
634
619
  labelFill: '#000', // Force black; remove to let G6 dark theme control it
635
620
  labelFontWeight: 'normal',
636
621
  },
622
+ /* Topic focus highlight (EV_FOCUS_TOPIC): an amber halo on
623
+ * every node of the focused topic. */
624
+ active: {
625
+ lineWidth: 4,
626
+ stroke: '#f0a020',
627
+ halo: true,
628
+ haloStroke: '#f0a020',
629
+ haloLineWidth: 8,
630
+ },
637
631
  },
638
632
  },
639
633
 
@@ -2066,6 +2060,66 @@ async function graph_fitview(gobj)
2066
2060
  await graph.fitView();
2067
2061
  }
2068
2062
 
2063
+ /************************************************************
2064
+ * Focus the graph on one topic: highlight (amber 'active' state)
2065
+ * every node of that topic and centre the viewport on them. An
2066
+ * empty topic clears the highlight. If the graph is not rendered
2067
+ * or its data has not arrived, remember the request and replay it
2068
+ * once the data loads (see ac_load_data). Guarded end-to-end — a
2069
+ * missing G6 API or an unknown topic logs and no-ops, never throws.
2070
+ ************************************************************/
2071
+ function graph_focus_topic(gobj, topic)
2072
+ {
2073
+ let priv = gobj.priv;
2074
+ let graph = priv.graph;
2075
+ if(!graph || !priv.graph_rendered) {
2076
+ priv._pending_focus_topic = topic;
2077
+ return;
2078
+ }
2079
+
2080
+ let data = {};
2081
+ try {
2082
+ data = graph.getData() || {};
2083
+ } catch(e) {
2084
+ log_error(`${gobj_short_name(gobj)}: graph.getData() failed: ${e}`);
2085
+ return;
2086
+ }
2087
+ let nodes = is_array(data.nodes) ? data.nodes : [];
2088
+
2089
+ /* Clear the previous focus highlight, then set the new one. */
2090
+ let states = {};
2091
+ for(let id of (priv._focus_ids || [])) {
2092
+ states[id] = [];
2093
+ }
2094
+ let ids = [];
2095
+ if(!empty_string(topic)) {
2096
+ for(let n of nodes) {
2097
+ if(n && n.data && n.data.topic_name === topic) {
2098
+ ids.push(n.id);
2099
+ }
2100
+ }
2101
+ if(ids.length === 0) {
2102
+ log_warning(`${gobj_short_name(gobj)}: focus topic '${topic}' has no nodes`);
2103
+ }
2104
+ for(let id of ids) {
2105
+ states[id] = ['active'];
2106
+ }
2107
+ }
2108
+ priv._focus_ids = ids;
2109
+ priv._focus_topic = topic || null;
2110
+
2111
+ try {
2112
+ if(typeof graph.setElementState === "function") {
2113
+ graph.setElementState(states);
2114
+ }
2115
+ if(ids.length && typeof graph.focusElement === "function") {
2116
+ graph.focusElement(ids);
2117
+ }
2118
+ } catch(e) {
2119
+ log_error(`${gobj_short_name(gobj)}: focus topic apply failed: ${e}`);
2120
+ }
2121
+ }
2122
+
2069
2123
  async function graph_zoom_in(gobj)
2070
2124
  {
2071
2125
  let priv = gobj.priv;
@@ -2442,7 +2496,13 @@ function create_popover_base(left, top, className, borderColor, minWidth)
2442
2496
  'position:absolute;' +
2443
2497
  'left:' + left + 'px;' +
2444
2498
  'top:' + top + 'px;' +
2445
- 'background:#fff;border:1px solid ' + borderColor + ';border-radius:6px;' +
2499
+ /* Bulma scheme vars, not #fff: the labels inside inherit their
2500
+ * colour, so a hardcoded white card turned into light-on-white —
2501
+ * invisible — the moment the app went dark. The vars flip with
2502
+ * <html data-theme>, so these follow the theme with no redraw. */
2503
+ 'background:var(--bulma-scheme-main, #fff);' +
2504
+ 'color:var(--bulma-text-strong, #1A1A1A);' +
2505
+ 'border:1px solid ' + borderColor + ';border-radius:6px;' +
2446
2506
  'padding:12px;z-index:100;pointer-events:all;' +
2447
2507
  'box-shadow:0 4px 12px rgba(0,0,0,0.15);' +
2448
2508
  'min-width:' + minWidth + 'px;font-size:13px;';
@@ -2466,7 +2526,8 @@ function create_form_color_input(parent, value, onInput)
2466
2526
  input.type = 'color';
2467
2527
  input.value = value;
2468
2528
  input.style.cssText =
2469
- 'width:100%;height:30px;padding:0;border:1px solid #d9d9d9;border-radius:4px;' +
2529
+ 'width:100%;height:30px;padding:0;' +
2530
+ 'border:1px solid var(--bulma-border-weak, #d9d9d9);border-radius:4px;' +
2470
2531
  'cursor:pointer;margin-bottom:10px;';
2471
2532
  if(onInput) {
2472
2533
  input.addEventListener('input', onInput);
@@ -2482,8 +2543,15 @@ function create_form_number_input(parent, value, min, max, onInput)
2482
2543
  input.min = String(min);
2483
2544
  input.max = String(max);
2484
2545
  input.value = value;
2546
+ /* Set the colours explicitly: a native control with only a border
2547
+ * styled falls back to the BROWSER default (white + black), which is
2548
+ * what kept it light inside a dark popover. The spinner arrows are
2549
+ * OS-drawn and follow `color-scheme` (see c_yui_shell.css), not this. */
2485
2550
  input.style.cssText =
2486
- 'width:100%;padding:4px 6px;border:1px solid #d9d9d9;border-radius:4px;' +
2551
+ 'width:100%;padding:4px 6px;' +
2552
+ 'background:var(--bulma-scheme-main-bis, #fff);' +
2553
+ 'color:var(--bulma-text-strong, #333);' +
2554
+ 'border:1px solid var(--bulma-border-weak, #d9d9d9);border-radius:4px;' +
2487
2555
  'box-sizing:border-box;margin-bottom:10px;';
2488
2556
  if(onInput) {
2489
2557
  input.addEventListener('input', onInput);
@@ -2496,7 +2564,10 @@ function create_form_select(parent, options, marginBottom)
2496
2564
  {
2497
2565
  let select = document.createElement('select');
2498
2566
  select.style.cssText =
2499
- 'width:100%;padding:4px 6px;border:1px solid #d9d9d9;border-radius:4px;' +
2567
+ 'width:100%;padding:4px 6px;' +
2568
+ 'background:var(--bulma-scheme-main-bis, #fff);' +
2569
+ 'color:var(--bulma-text-strong, #333);' +
2570
+ 'border:1px solid var(--bulma-border-weak, #d9d9d9);border-radius:4px;' +
2500
2571
  'box-sizing:border-box;margin-bottom:' + (marginBottom || '12px') + ';';
2501
2572
  for(let opt of options) {
2502
2573
  let o = document.createElement('option');
@@ -2509,7 +2580,9 @@ function create_form_select(parent, options, marginBottom)
2509
2580
  }
2510
2581
 
2511
2582
  const BTN_STYLE_CANCEL =
2512
- 'flex:1;padding:6px;background:#fff;color:#333;border:1px solid #d9d9d9;' +
2583
+ 'flex:1;padding:6px;background:var(--bulma-scheme-main-bis, #fff);' +
2584
+ 'color:var(--bulma-text-strong, #333);' +
2585
+ 'border:1px solid var(--bulma-border-weak, #d9d9d9);' +
2513
2586
  'border-radius:4px;cursor:pointer;font-size:13px;font-weight:500;';
2514
2587
 
2515
2588
  function create_form_button_row(parent, buttons)
@@ -5452,6 +5525,13 @@ function ac_load_data(gobj, event, kw, src)
5452
5525
  } else {
5453
5526
  graph_remove_plugin(gobj, "history");
5454
5527
  }
5528
+ /* Nodes exist and are positioned now: apply a focus
5529
+ * requested before the data was ready (deep link). */
5530
+ if(priv._pending_focus_topic !== null) {
5531
+ let ft = priv._pending_focus_topic;
5532
+ priv._pending_focus_topic = null;
5533
+ graph_focus_topic(gobj, ft);
5534
+ }
5455
5535
  });
5456
5536
  });
5457
5537
  });
@@ -5731,7 +5811,7 @@ function ac_resize(gobj, event, kw, src)
5731
5811
  }
5732
5812
 
5733
5813
  /************************************************************
5734
- * Theme change (subscribed from __yui_main__)
5814
+ * Theme change (<html data-theme>, via yui_watch_theme)
5735
5815
  ************************************************************/
5736
5816
  function ac_theme(gobj, event, kw, src)
5737
5817
  {
@@ -5791,6 +5871,12 @@ function ac_auto_fit(gobj, event, kw, src)
5791
5871
  return 0;
5792
5872
  }
5793
5873
 
5874
+ function ac_focus_topic(gobj, event, kw, src)
5875
+ {
5876
+ graph_focus_topic(gobj, kw && kw.topic);
5877
+ return 0;
5878
+ }
5879
+
5794
5880
  function ac_center(gobj, event, kw, src)
5795
5881
  {
5796
5882
  graph_center(gobj);
@@ -6153,6 +6239,7 @@ function create_gclass(gclass_name)
6153
6239
  ["EV_ZOOM_OUT", ac_zoom_out, null],
6154
6240
  ["EV_ZOOM_RESET", ac_zoom_reset, null],
6155
6241
  ["EV_AUTO_FIT", ac_auto_fit, null],
6242
+ ["EV_FOCUS_TOPIC", ac_focus_topic, null],
6156
6243
  ["EV_CENTER", ac_center, null],
6157
6244
  ["EV_FULLSCREEN", ac_fullscreen, null],
6158
6245
  ["EV_SET_LAYOUT", ac_set_layout, null],
@@ -6195,6 +6282,7 @@ function create_gclass(gclass_name)
6195
6282
  ["EV_ZOOM_OUT", 0],
6196
6283
  ["EV_ZOOM_RESET", 0],
6197
6284
  ["EV_AUTO_FIT", 0],
6285
+ ["EV_FOCUS_TOPIC", 0],
6198
6286
  ["EV_CENTER", 0],
6199
6287
  ["EV_FULLSCREEN", 0],
6200
6288
  ["EV_SET_LAYOUT", 0],
@@ -48,3 +48,12 @@
48
48
  border-color: var(--bulma-border, #dbdbdb);
49
49
  cursor: default;
50
50
  }
51
+
52
+ /* Moved from c_yui_main.css (2.6.1): scroll containment for the fields area.
53
+ * Self-contained here so every consumer gets it without the
54
+ * legacy stack's stylesheet. */
55
+
56
+ .overscroll-contain {
57
+ overscroll-behavior: contain;
58
+ }
59
+