@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.
- package/README.md +331 -11
- package/dist/gobj-ui.cjs.js +21386 -17919
- package/dist/gobj-ui.es.js +26059 -22613
- package/index.js +45 -9
- package/package.json +4 -4
- package/src/c_g6_nodes_tree.js +128 -40
- package/src/c_yui_form.css +9 -0
- package/src/c_yui_form.js +113 -53
- package/src/c_yui_gobj_tree_js.js +58 -36
- package/src/c_yui_json.css +139 -0
- package/src/c_yui_json.js +928 -0
- package/src/c_yui_json_graph.js +110 -20
- package/src/c_yui_map.js +18 -36
- package/src/c_yui_nav.js +6 -9
- package/src/c_yui_period.css +184 -0
- package/src/c_yui_period.js +1441 -0
- package/src/c_yui_shell.css +120 -0
- package/src/c_yui_shell.js +672 -111
- package/src/c_yui_treedb_graph.js +639 -37
- package/src/c_yui_treedb_schema.js +478 -0
- package/src/c_yui_treedb_topic_with_form.css +18 -0
- package/src/c_yui_treedb_topic_with_form.js +153 -103
- package/src/c_yui_treedb_topics.css +73 -0
- package/src/c_yui_treedb_topics.js +1070 -29
- package/src/c_yui_window.css +22 -0
- package/src/c_yui_window.js +182 -97
- package/src/c_yui_window_manager.js +38 -4
- package/src/json_view_helpers.js +214 -0
- package/src/json_view_helpers.test.js +135 -0
- package/src/route_map_model.js +317 -0
- package/src/route_map_model.test.js +209 -0
- package/src/route_resolver.js +24 -1
- package/src/route_resolver.test.js +40 -1
- package/src/shell_modals.js +162 -52
- package/src/shell_route_map.css +225 -0
- package/src/shell_route_map.js +363 -0
- package/src/tabulator.css +245 -0
- package/src/yui_dev.js +130 -8
- package/src/yui_frontend_view.js +106 -0
- package/src/yui_icons.css +62 -0
- package/src/yui_inputs.css +8 -3
- package/src/yui_inputs.js +51 -8
- package/src/yui_tabulator_i18n.js +128 -0
- package/src/yui_theme.js +147 -0
- package/src/yui_time.js +666 -0
- package/src/yui_time.test.js +330 -0
- package/src/yui_toolbar.css +28 -0
- package/src/yui_toolbar.js +86 -43
- package/src/c_yui_main.css +0 -501
- package/src/c_yui_main.js +0 -1623
- package/src/c_yui_routing.css +0 -18
- package/src/c_yui_routing.js +0 -837
- package/src/c_yui_tabs.js +0 -487
- package/src/themes.js +0 -215
- package/src/ytable.css +0 -63
- package/src/ytable.js +0 -505
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
log_error,
|
|
17
17
|
gobj_read_pointer_attr,
|
|
18
18
|
gobj_subscribe_event,
|
|
19
|
+
gobj_unsubscribe_event,
|
|
19
20
|
gobj_parent,
|
|
20
21
|
sprintf,
|
|
21
22
|
gobj_read_attr,
|
|
@@ -60,19 +61,22 @@ import {
|
|
|
60
61
|
yui_shell_confirm_yesnocancel,
|
|
61
62
|
yui_shell_confirm_ok,
|
|
62
63
|
yui_shell_popup_layer,
|
|
64
|
+
yui_shell_show_modal,
|
|
63
65
|
} from "./shell_modals.js";
|
|
64
66
|
|
|
65
67
|
import {
|
|
66
68
|
yui_shell_of,
|
|
67
|
-
yui_shell_push_escape,
|
|
68
|
-
yui_shell_pop_escape,
|
|
69
69
|
} from "./c_yui_shell.js";
|
|
70
70
|
|
|
71
71
|
import {register_c_yui_form} from "./c_yui_form.js";
|
|
72
|
+
import {attach_clear} from "./yui_inputs.js";
|
|
73
|
+
|
|
74
|
+
import {yui_tabulator_lang, yui_tabulator_relocalize} from "./yui_tabulator_i18n.js";
|
|
72
75
|
|
|
73
76
|
import {t} from "i18next";
|
|
74
77
|
|
|
75
78
|
import "./c_yui_treedb_topic_with_form.css";
|
|
79
|
+
import "./tabulator.css";
|
|
76
80
|
|
|
77
81
|
import { TabulatorFull as Tabulator } from "tabulator-tables";
|
|
78
82
|
|
|
@@ -118,7 +122,7 @@ SDATA(data_type_t.DTP_JSON, "tabulator_settings", 0, {
|
|
|
118
122
|
pagination: true,
|
|
119
123
|
paginationSize: 25,
|
|
120
124
|
paginationSizeSelector: [25, 50, 100, true],
|
|
121
|
-
placeholder: "No data available",
|
|
125
|
+
placeholder: "No data available", /* overridden per-language at create */
|
|
122
126
|
maxHeight: "100%"
|
|
123
127
|
}, "Default settings for Tabulator"),
|
|
124
128
|
|
|
@@ -138,10 +142,7 @@ let PRIVATE_DATA = {
|
|
|
138
142
|
topic_name: "",
|
|
139
143
|
tabulator: null,
|
|
140
144
|
form: null, // hosted C_YUI_FORM child (while dialog open)
|
|
141
|
-
|
|
142
|
-
on_keydown: null, // document Escape handler (no-shell fallback)
|
|
143
|
-
escape_shell: null, // shell whose Escape chain holds escape_handler
|
|
144
|
-
escape_handler: null, // close handler pushed on the shell chain
|
|
145
|
+
form_modal: null, // { close } handle of the adaptive dialog
|
|
145
146
|
};
|
|
146
147
|
|
|
147
148
|
let __gclass__ = null;
|
|
@@ -199,6 +200,14 @@ function mt_create(gobj)
|
|
|
199
200
|
***************************************************************/
|
|
200
201
|
function mt_start(gobj)
|
|
201
202
|
{
|
|
203
|
+
/* The shell publishes the language switch (yui_shell_language_changed):
|
|
204
|
+
* the column headers re-translate themselves (they carry data-i18n — see
|
|
205
|
+
* col_label), but Tabulator's OWN chrome is drawn once and needs to be
|
|
206
|
+
* told. */
|
|
207
|
+
let shell = yui_shell_of(gobj);
|
|
208
|
+
if(shell) {
|
|
209
|
+
gobj_subscribe_event(shell, "EV_LANGUAGE_CHANGED", {}, gobj);
|
|
210
|
+
}
|
|
202
211
|
create_tabulator(gobj);
|
|
203
212
|
}
|
|
204
213
|
|
|
@@ -207,6 +216,10 @@ function mt_start(gobj)
|
|
|
207
216
|
***************************************************************/
|
|
208
217
|
function mt_stop(gobj)
|
|
209
218
|
{
|
|
219
|
+
let shell = yui_shell_of(gobj);
|
|
220
|
+
if(shell) {
|
|
221
|
+
gobj_unsubscribe_event(shell, "EV_LANGUAGE_CHANGED", {}, gobj);
|
|
222
|
+
}
|
|
210
223
|
close_form_dialog(gobj);
|
|
211
224
|
table__destroy(gobj);
|
|
212
225
|
}
|
|
@@ -458,7 +471,7 @@ function build_ui(gobj)
|
|
|
458
471
|
if(with_search_button) {
|
|
459
472
|
let search_id = `${toolbar_id}_search`;
|
|
460
473
|
let $search_box = createElement2(
|
|
461
|
-
['div', {class: 'control has-icons-left
|
|
474
|
+
['div', {class: 'control has-icons-left mr-1'}, [
|
|
462
475
|
['input', {
|
|
463
476
|
id: search_id,
|
|
464
477
|
class: 'input',
|
|
@@ -468,19 +481,13 @@ function build_ui(gobj)
|
|
|
468
481
|
}],
|
|
469
482
|
['span', {class: 'icon is-left'}, [
|
|
470
483
|
['i', {class: 'yi-magnifying-glass'}]
|
|
471
|
-
]]
|
|
472
|
-
['span', {class: 'icon is-right', style: 'cursor:pointer; pointer-events:all;', title: 'clear search'}, [
|
|
473
|
-
['i', {class: 'yi-xmark'}]
|
|
474
|
-
], {
|
|
475
|
-
'click': (event) => {
|
|
476
|
-
event.stopPropagation();
|
|
477
|
-
let $input = document.getElementById(search_id);
|
|
478
|
-
if($input) { $input.value = ''; $input.dispatchEvent(new Event('input')); }
|
|
479
|
-
}
|
|
480
|
-
}]
|
|
484
|
+
]]
|
|
481
485
|
]]
|
|
482
486
|
);
|
|
483
487
|
let $search_input = $search_box.querySelector(`#${search_id}`);
|
|
488
|
+
/* NORM clear (✕): dispatches a synthetic `input`, so the tabulator
|
|
489
|
+
* filter below re-runs (empty term → clearFilter). */
|
|
490
|
+
attach_clear($search_box, $search_input);
|
|
484
491
|
if($search_input) {
|
|
485
492
|
$search_input.addEventListener('input', (event) => {
|
|
486
493
|
let searchText = event.target.value.trim().toLowerCase();
|
|
@@ -510,7 +517,7 @@ function build_ui(gobj)
|
|
|
510
517
|
|
|
511
518
|
if(with_refresh_button) {
|
|
512
519
|
let $refresh = createElement2(
|
|
513
|
-
['button', {class: 'button mr-1', title: 'refresh'}, [
|
|
520
|
+
['button', {class: 'button mr-1', title: t('refresh'), 'data-i18n-title': 'refresh'}, [
|
|
514
521
|
['i', {class: 'yi-arrows-rotate'}],
|
|
515
522
|
['span', {class: 'is-hidden-mobile', i18n: 'refresh', style: 'padding-left:5px;'}, 'refresh']
|
|
516
523
|
], {
|
|
@@ -819,7 +826,12 @@ function create_tabulator(gobj)
|
|
|
819
826
|
}
|
|
820
827
|
|
|
821
828
|
let pkey = desc.pkey || "id";
|
|
822
|
-
|
|
829
|
+
/* Selection is driven ONLY by the checkbox column, never by clicking
|
|
830
|
+
* the row: "highlight" keeps the rowSelection checkbox fully working
|
|
831
|
+
* (it calls toggleSelect() directly) while disabling click-to-select,
|
|
832
|
+
* so opening the edit (yi-pen) form no longer implicitly ticks the
|
|
833
|
+
* row. Radio keeps single click-select (its own widget). */
|
|
834
|
+
let selectable = with_checkbox ? "highlight" : (with_radio ? 1 : false);
|
|
823
835
|
|
|
824
836
|
let tabulator_settings = json_deep_copy(gobj_read_attr(gobj, "tabulator_settings"));
|
|
825
837
|
|
|
@@ -847,7 +859,26 @@ function create_tabulator(gobj)
|
|
|
847
859
|
log_error(`${gobj_short_name(gobj)}: table element '#${table_id}' not found in $container`);
|
|
848
860
|
return;
|
|
849
861
|
}
|
|
850
|
-
|
|
862
|
+
/* Tabulator renders its own chrome — the paginator, the placeholder, the
|
|
863
|
+
* loading/error notices — and nothing ever passed those through i18n: the
|
|
864
|
+
* table sat in English inside a Spanish view. Hand it the current
|
|
865
|
+
* language (every key falls back to what it used to render, so an app
|
|
866
|
+
* that defines none of them sees no change). */
|
|
867
|
+
let tabulator = new Tabulator($table_el,
|
|
868
|
+
Object.assign({}, tabulator_settings, yui_tabulator_lang(t), {
|
|
869
|
+
/* The placeholder ships as an English literal in the settings
|
|
870
|
+
* default; hand it the current language (falling back to exactly
|
|
871
|
+
* what it said before for an app that does not define the key). */
|
|
872
|
+
placeholder: t("no data available", {defaultValue: "No data available"})
|
|
873
|
+
}));
|
|
874
|
+
|
|
875
|
+
/* "highlight" still paints the whole-row hover wash (and a pointer
|
|
876
|
+
* cursor), which reads as a selection. This table selects only via the
|
|
877
|
+
* checkbox, so suppress the hover: rows stay visually static, and only
|
|
878
|
+
* a checkbox-selected row changes colour (see tabulator.css). */
|
|
879
|
+
if(with_checkbox) {
|
|
880
|
+
$table_el.classList.add("yui-no-row-hover");
|
|
881
|
+
}
|
|
851
882
|
|
|
852
883
|
/* Keep the footer in sync with the visible (active) row count,
|
|
853
884
|
* and hide the pagination chrome while everything fits in one
|
|
@@ -1133,9 +1164,14 @@ function open_form_dialog(gobj, mode, record)
|
|
|
1133
1164
|
|
|
1134
1165
|
let priv = gobj.priv;
|
|
1135
1166
|
let desc = gobj_read_attr(gobj, "desc");
|
|
1136
|
-
let popup_id = gobj_read_str_attr(gobj, "popup_id");
|
|
1137
1167
|
let topic_name = gobj_read_str_attr(gobj, "topic_name");
|
|
1138
1168
|
|
|
1169
|
+
let shell = yui_shell_of(gobj);
|
|
1170
|
+
if(!shell) {
|
|
1171
|
+
log_error(`${gobj_short_name(gobj)}: no shell, cannot open the edit form`);
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1139
1175
|
/* Title says what you are doing: "new <topic>" on create,
|
|
1140
1176
|
* "<topic> — <pkey>" on update. */
|
|
1141
1177
|
let pkey = desc.pkey || "id";
|
|
@@ -1147,36 +1183,12 @@ function open_form_dialog(gobj, mode, record)
|
|
|
1147
1183
|
title = t(topic_name) + (empty_string(rid) ? "" : " — " + rid);
|
|
1148
1184
|
}
|
|
1149
1185
|
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
['button', {class: 'delete is-large', 'aria-label': 'close'}, '', {
|
|
1157
|
-
click: function(evt) {
|
|
1158
|
-
evt.stopPropagation();
|
|
1159
|
-
request_close_form_dialog(gobj);
|
|
1160
|
-
}
|
|
1161
|
-
}]
|
|
1162
|
-
]],
|
|
1163
|
-
/* flex column so the hosted form takes the dialog
|
|
1164
|
-
* height: fields scroll internally and the form's
|
|
1165
|
-
* bottom toolbar stays visible (dialog footer) */
|
|
1166
|
-
['section', {
|
|
1167
|
-
class: 'modal-card-body p-2',
|
|
1168
|
-
style: 'display:flex; flex-direction:column;'
|
|
1169
|
-
}, []]
|
|
1170
|
-
]]
|
|
1171
|
-
], {
|
|
1172
|
-
click: function(evt) {
|
|
1173
|
-
// Capture bubble events
|
|
1174
|
-
evt.stopPropagation();
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
]);
|
|
1178
|
-
|
|
1179
|
-
let $body = $element.querySelector('.modal-card-body');
|
|
1186
|
+
/* A flex column so the hosted form takes the dialog height: fields
|
|
1187
|
+
* scroll internally and the form's bottom toolbar stays visible. */
|
|
1188
|
+
let $body = createElement2(
|
|
1189
|
+
['div', {class: 'TREEDB_FORM_BODY',
|
|
1190
|
+
style: 'display:flex; flex-direction:column; height:100%;'}, []]
|
|
1191
|
+
);
|
|
1180
1192
|
|
|
1181
1193
|
let form = gobj_create_pure_child(
|
|
1182
1194
|
"form_" + clean_name(gobj_name(gobj)),
|
|
@@ -1186,6 +1198,10 @@ function open_form_dialog(gobj, mode, record)
|
|
|
1186
1198
|
record: record,
|
|
1187
1199
|
fkey_options: build_fkey_options(gobj),
|
|
1188
1200
|
form_mode: mode,
|
|
1201
|
+
/* Editing the raw topic record: structured cols
|
|
1202
|
+
* (template / table / coordinates) are raw JSON editors,
|
|
1203
|
+
* not interpreted into sub-widgets (the pre-merge behaviour). */
|
|
1204
|
+
render_mode: "edit",
|
|
1189
1205
|
pkey: desc.pkey || "id",
|
|
1190
1206
|
topic_name: topic_name,
|
|
1191
1207
|
editable: true,
|
|
@@ -1194,7 +1210,6 @@ function open_form_dialog(gobj, mode, record)
|
|
|
1194
1210
|
gobj
|
|
1195
1211
|
);
|
|
1196
1212
|
priv.form = form;
|
|
1197
|
-
priv.$popup = $element;
|
|
1198
1213
|
|
|
1199
1214
|
let $form_container = gobj_read_attr(form, "$container");
|
|
1200
1215
|
if($form_container) {
|
|
@@ -1202,38 +1217,33 @@ function open_form_dialog(gobj, mode, record)
|
|
|
1202
1217
|
$form_container.style.minHeight = "0";
|
|
1203
1218
|
}
|
|
1204
1219
|
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
*
|
|
1210
|
-
*
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
evt.stopPropagation();
|
|
1224
|
-
request_close_form_dialog(gobj);
|
|
1225
|
-
}
|
|
1226
|
-
};
|
|
1227
|
-
document.addEventListener("keydown", priv.on_keydown);
|
|
1228
|
-
}
|
|
1220
|
+
/* The standardized adaptive dialog, like the sibling treedb views: a
|
|
1221
|
+
* centered card with the X at the top-right on desktop, a full-screen
|
|
1222
|
+
* sheet with a back arrow on mobile; Escape / browser Back / backdrop
|
|
1223
|
+
* are wired by the shell. `before_close` preserves the unsaved-changes
|
|
1224
|
+
* guard — it vetoes the dismiss and, on confirm, closes the modal
|
|
1225
|
+
* itself. `on_close` tears the hosted form child down. */
|
|
1226
|
+
priv.form_modal = yui_shell_show_modal(shell, $body, {
|
|
1227
|
+
dialog: true,
|
|
1228
|
+
logical_class: "TREEDB_FORM_SHEET",
|
|
1229
|
+
title: title,
|
|
1230
|
+
t: t,
|
|
1231
|
+
before_close: function() {
|
|
1232
|
+
return form_may_close(gobj);
|
|
1233
|
+
},
|
|
1234
|
+
on_close: function() {
|
|
1235
|
+
teardown_form_child(gobj);
|
|
1236
|
+
}
|
|
1237
|
+
});
|
|
1229
1238
|
|
|
1230
|
-
gobj_start(form);
|
|
1239
|
+
gobj_start(form); // mt_start loads `record` into the form
|
|
1240
|
+
refresh_language($body, t); // translate anything the form emitted on start
|
|
1231
1241
|
|
|
1232
1242
|
/* Focus the pkey unless the mode made it readonly (update) —
|
|
1233
1243
|
* then the first editable field. */
|
|
1234
|
-
let $with_focus = $
|
|
1244
|
+
let $with_focus = $body.querySelector('.with-focus:not([readonly])');
|
|
1235
1245
|
if(!$with_focus) {
|
|
1236
|
-
$with_focus = $
|
|
1246
|
+
$with_focus = $body.querySelector(
|
|
1237
1247
|
'input.input:not([readonly]), textarea, select'
|
|
1238
1248
|
);
|
|
1239
1249
|
}
|
|
@@ -1243,10 +1253,12 @@ function open_form_dialog(gobj, mode, record)
|
|
|
1243
1253
|
}
|
|
1244
1254
|
|
|
1245
1255
|
/************************************************************
|
|
1246
|
-
*
|
|
1247
|
-
*
|
|
1256
|
+
* Unsaved-changes guard for the dialog's `before_close` hook.
|
|
1257
|
+
* Returns true to allow the dismiss; false to VETO it (the form
|
|
1258
|
+
* has pending edits) and pop the confirm — which, on "yes",
|
|
1259
|
+
* closes the modal itself (EV_WINDOW_TO_CLOSE contract).
|
|
1248
1260
|
************************************************************/
|
|
1249
|
-
function
|
|
1261
|
+
function form_may_close(gobj)
|
|
1250
1262
|
{
|
|
1251
1263
|
let priv = gobj.priv;
|
|
1252
1264
|
if(priv.form) {
|
|
@@ -1258,31 +1270,24 @@ function request_close_form_dialog(gobj)
|
|
|
1258
1270
|
'All changes will be lost. Are you sure?',
|
|
1259
1271
|
{t: t, yes_label: "yes", no_label: "no", cancel_label: "cancel"}
|
|
1260
1272
|
).then(function(answer) {
|
|
1261
|
-
if(answer === "yes") {
|
|
1262
|
-
|
|
1273
|
+
if(answer === "yes" && priv.form_modal) {
|
|
1274
|
+
priv.form_modal.close();
|
|
1263
1275
|
}
|
|
1264
1276
|
});
|
|
1265
|
-
return;
|
|
1277
|
+
return false;
|
|
1266
1278
|
}
|
|
1267
1279
|
}
|
|
1268
|
-
|
|
1280
|
+
return true;
|
|
1269
1281
|
}
|
|
1270
1282
|
|
|
1271
1283
|
/************************************************************
|
|
1272
|
-
* Destroy the hosted
|
|
1284
|
+
* Destroy the hosted C_YUI_FORM child. Called from the modal's
|
|
1285
|
+
* on_close — the shell has already removed the dialog DOM and
|
|
1286
|
+
* retired its Escape / history entries.
|
|
1273
1287
|
************************************************************/
|
|
1274
|
-
function
|
|
1288
|
+
function teardown_form_child(gobj)
|
|
1275
1289
|
{
|
|
1276
1290
|
let priv = gobj.priv;
|
|
1277
|
-
if(priv.escape_handler) {
|
|
1278
|
-
yui_shell_pop_escape(priv.escape_shell, priv.escape_handler);
|
|
1279
|
-
priv.escape_shell = null;
|
|
1280
|
-
priv.escape_handler = null;
|
|
1281
|
-
}
|
|
1282
|
-
if(priv.on_keydown) {
|
|
1283
|
-
document.removeEventListener("keydown", priv.on_keydown);
|
|
1284
|
-
priv.on_keydown = null;
|
|
1285
|
-
}
|
|
1286
1291
|
if(priv.form) {
|
|
1287
1292
|
if(gobj_is_running(priv.form)) {
|
|
1288
1293
|
gobj_stop(priv.form);
|
|
@@ -1290,12 +1295,24 @@ function close_form_dialog(gobj)
|
|
|
1290
1295
|
gobj_destroy(priv.form);
|
|
1291
1296
|
priv.form = null;
|
|
1292
1297
|
}
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
+
priv.form_modal = null;
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
/************************************************************
|
|
1302
|
+
* Force the dialog down (bypasses the unsaved guard): used after a
|
|
1303
|
+
* successful save and on teardown. Closing the modal runs its
|
|
1304
|
+
* on_close, which tears the form child down.
|
|
1305
|
+
************************************************************/
|
|
1306
|
+
function close_form_dialog(gobj)
|
|
1307
|
+
{
|
|
1308
|
+
let priv = gobj.priv;
|
|
1309
|
+
if(priv.form_modal) {
|
|
1310
|
+
let modal = priv.form_modal;
|
|
1311
|
+
priv.form_modal = null;
|
|
1312
|
+
modal.close(); // -> on_close -> teardown_form_child
|
|
1313
|
+
return;
|
|
1298
1314
|
}
|
|
1315
|
+
teardown_form_child(gobj);
|
|
1299
1316
|
}
|
|
1300
1317
|
|
|
1301
1318
|
/************************************************************
|
|
@@ -1719,6 +1736,37 @@ function show_dropdown_popup_menu(gobj, x, y, items, callback)
|
|
|
1719
1736
|
/************************************************************
|
|
1720
1737
|
* From external, at the beginning, load all topic data
|
|
1721
1738
|
************************************************************/
|
|
1739
|
+
/***************************************************************
|
|
1740
|
+
* The language changed (the shell publishes it after the app switched).
|
|
1741
|
+
*
|
|
1742
|
+
* The column headers carry their key (col_label's title formatter emits a
|
|
1743
|
+
* data-i18n span), so refresh_language() has already re-translated them.
|
|
1744
|
+
* What it cannot reach is what TABULATOR itself renders: its paginator, its
|
|
1745
|
+
* placeholder, its loading/error notices — drawn once, from its own lang
|
|
1746
|
+
* dict. Re-apply it in the new language.
|
|
1747
|
+
***************************************************************/
|
|
1748
|
+
function ac_language_changed(gobj, event, kw, src)
|
|
1749
|
+
{
|
|
1750
|
+
let tabulator = gobj_read_attr(gobj, "tabulator");
|
|
1751
|
+
if(!tabulator) {
|
|
1752
|
+
return 0;
|
|
1753
|
+
}
|
|
1754
|
+
yui_tabulator_relocalize(tabulator, t);
|
|
1755
|
+
try {
|
|
1756
|
+
tabulator.options.placeholder =
|
|
1757
|
+
t("no data available", {defaultValue: "No data available"});
|
|
1758
|
+
/* Re-setting the locale makes Tabulator re-run the title formatter on
|
|
1759
|
+
* the EXISTING header cell, which APPENDS its span to the one already
|
|
1760
|
+
* there ("Device GroupDevice Group"). Rebuilding the columns from
|
|
1761
|
+
* their own definitions renders each header once, from scratch. */
|
|
1762
|
+
tabulator.setColumns(tabulator.getColumnDefinitions());
|
|
1763
|
+
} catch(e) {
|
|
1764
|
+
log_error(`${gobj_short_name(gobj)}: cannot re-render the table: ${e}`);
|
|
1765
|
+
return -1;
|
|
1766
|
+
}
|
|
1767
|
+
return 0;
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1722
1770
|
function ac_load_nodes(gobj, event, kw, src)
|
|
1723
1771
|
{
|
|
1724
1772
|
let data = kw;
|
|
@@ -2279,6 +2327,7 @@ function create_gclass(gclass_name)
|
|
|
2279
2327
|
*---------------------------------------------*/
|
|
2280
2328
|
const states = [
|
|
2281
2329
|
["ST_IDLE", [
|
|
2330
|
+
["EV_LANGUAGE_CHANGED", ac_language_changed, null],
|
|
2282
2331
|
["EV_LOAD_NODES", ac_load_nodes, null],
|
|
2283
2332
|
["EV_LOAD_NODE_CREATED", ac_load_node_created, null],
|
|
2284
2333
|
["EV_LOAD_NODE_UPDATED", ac_load_node_updated, null],
|
|
@@ -2305,6 +2354,7 @@ function create_gclass(gclass_name)
|
|
|
2305
2354
|
* Events
|
|
2306
2355
|
*---------------------------------------------*/
|
|
2307
2356
|
const event_types = [
|
|
2357
|
+
["EV_LANGUAGE_CHANGED", 0],
|
|
2308
2358
|
["EV_LOAD_NODES", 0],
|
|
2309
2359
|
["EV_LOAD_NODE_CREATED", 0],
|
|
2310
2360
|
["EV_LOAD_NODE_UPDATED", 0],
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* c_yui_treedb_topics.css
|
|
3
|
+
*
|
|
4
|
+
* Styling for C_YUI_TREEDB_TOPICS' cards-landing (list -> detail):
|
|
5
|
+
* the topic cards (three hash-routed icon actions) and the routed
|
|
6
|
+
* topic-info panel. Theme-aware via Bulma variables.
|
|
7
|
+
*
|
|
8
|
+
* Copyright (c) 2026, ArtGins.
|
|
9
|
+
* All Rights Reserved.
|
|
10
|
+
***********************************************************************/
|
|
11
|
+
|
|
12
|
+
/* One topic card: name on top, a row of icon actions below.
|
|
13
|
+
* The grid itself is the shell's .yui-nav-cards. */
|
|
14
|
+
.TREEDB_TOPIC_CARD {
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
align-items: center;
|
|
18
|
+
gap: 0.5rem;
|
|
19
|
+
padding: 1rem 0.75rem;
|
|
20
|
+
border: 1px solid var(--bulma-border-weak, #dbdbdb);
|
|
21
|
+
border-radius: 0.5rem;
|
|
22
|
+
background: var(--bulma-scheme-main, #fff);
|
|
23
|
+
color: var(--bulma-text, inherit);
|
|
24
|
+
}
|
|
25
|
+
.TREEDB_TOPIC_CARD {
|
|
26
|
+
cursor: pointer;
|
|
27
|
+
}
|
|
28
|
+
/* Selected (clicked) card: link-coloured border + soft ring. */
|
|
29
|
+
.TREEDB_TOPIC_CARD.is-selected {
|
|
30
|
+
border-color: var(--bulma-link, #485fc7);
|
|
31
|
+
box-shadow: 0 0 0 2px var(--bulma-link, #485fc7) inset;
|
|
32
|
+
}
|
|
33
|
+
.TREEDB_TOPIC_CARD .TREEDB_TOPIC_CARD_NAME {
|
|
34
|
+
font-weight: 600;
|
|
35
|
+
text-align: center;
|
|
36
|
+
line-height: 1.2;
|
|
37
|
+
word-break: break-word;
|
|
38
|
+
}
|
|
39
|
+
.TREEDB_TOPIC_CARD_ACTIONS {
|
|
40
|
+
display: flex;
|
|
41
|
+
justify-content: center;
|
|
42
|
+
gap: 0.25rem;
|
|
43
|
+
}
|
|
44
|
+
.TREEDB_TOPIC_CARD_ACTIONS > a {
|
|
45
|
+
display: inline-flex;
|
|
46
|
+
align-items: center;
|
|
47
|
+
justify-content: center;
|
|
48
|
+
width: 2.25rem;
|
|
49
|
+
height: 2.25rem;
|
|
50
|
+
border-radius: 0.375rem;
|
|
51
|
+
color: var(--bulma-text, inherit);
|
|
52
|
+
text-decoration: none;
|
|
53
|
+
}
|
|
54
|
+
.TREEDB_TOPIC_CARD_ACTIONS > a .icon {
|
|
55
|
+
font-size: 1.15rem;
|
|
56
|
+
}
|
|
57
|
+
.TREEDB_TOPIC_CARD_ACTIONS > a:hover,
|
|
58
|
+
.TREEDB_TOPIC_CARD_ACTIONS > a:focus-visible {
|
|
59
|
+
background: var(--bulma-background, #f5f5f5);
|
|
60
|
+
color: var(--bulma-link, #485fc7);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* Routed topic-info panel. */
|
|
64
|
+
.TREEDB_TOPIC_INFO {
|
|
65
|
+
padding: 1rem;
|
|
66
|
+
}
|
|
67
|
+
.TREEDB_TOPIC_INFO .TREEDB_TOPIC_INFO_CARD {
|
|
68
|
+
max-width: 48rem;
|
|
69
|
+
}
|
|
70
|
+
.TREEDB_TOPIC_INFO table code {
|
|
71
|
+
background: transparent;
|
|
72
|
+
padding: 0;
|
|
73
|
+
}
|