@yuneta/gobj-ui 3.0.0 → 5.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 +335 -6
- package/dist/gobj-ui.cjs.js +10930 -5027
- package/dist/gobj-ui.es.js +10948 -5080
- package/index.js +45 -1
- package/package.json +11 -11
- package/src/c_g6_nodes_tree.js +128 -40
- package/src/c_yui_form.js +112 -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 +19 -37
- 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 +33 -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 +7 -42
- package/src/c_yui_treedb_topic_with_form.js +152 -103
- package/src/c_yui_treedb_topics.css +73 -0
- package/src/c_yui_treedb_topics.js +1070 -29
- package/src/c_yui_window.js +180 -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 +117 -39
- package/src/shell_route_map.css +225 -0
- package/src/shell_route_map.js +363 -0
- package/src/tabulator.css +67 -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.js +86 -43
|
@@ -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,15 +61,17 @@ 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
|
|
|
@@ -119,7 +122,7 @@ SDATA(data_type_t.DTP_JSON, "tabulator_settings", 0, {
|
|
|
119
122
|
pagination: true,
|
|
120
123
|
paginationSize: 25,
|
|
121
124
|
paginationSizeSelector: [25, 50, 100, true],
|
|
122
|
-
placeholder: "No data available",
|
|
125
|
+
placeholder: "No data available", /* overridden per-language at create */
|
|
123
126
|
maxHeight: "100%"
|
|
124
127
|
}, "Default settings for Tabulator"),
|
|
125
128
|
|
|
@@ -139,10 +142,7 @@ let PRIVATE_DATA = {
|
|
|
139
142
|
topic_name: "",
|
|
140
143
|
tabulator: null,
|
|
141
144
|
form: null, // hosted C_YUI_FORM child (while dialog open)
|
|
142
|
-
|
|
143
|
-
on_keydown: null, // document Escape handler (no-shell fallback)
|
|
144
|
-
escape_shell: null, // shell whose Escape chain holds escape_handler
|
|
145
|
-
escape_handler: null, // close handler pushed on the shell chain
|
|
145
|
+
form_modal: null, // { close } handle of the adaptive dialog
|
|
146
146
|
};
|
|
147
147
|
|
|
148
148
|
let __gclass__ = null;
|
|
@@ -200,6 +200,14 @@ function mt_create(gobj)
|
|
|
200
200
|
***************************************************************/
|
|
201
201
|
function mt_start(gobj)
|
|
202
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
|
+
}
|
|
203
211
|
create_tabulator(gobj);
|
|
204
212
|
}
|
|
205
213
|
|
|
@@ -208,6 +216,10 @@ function mt_start(gobj)
|
|
|
208
216
|
***************************************************************/
|
|
209
217
|
function mt_stop(gobj)
|
|
210
218
|
{
|
|
219
|
+
let shell = yui_shell_of(gobj);
|
|
220
|
+
if(shell) {
|
|
221
|
+
gobj_unsubscribe_event(shell, "EV_LANGUAGE_CHANGED", {}, gobj);
|
|
222
|
+
}
|
|
211
223
|
close_form_dialog(gobj);
|
|
212
224
|
table__destroy(gobj);
|
|
213
225
|
}
|
|
@@ -459,7 +471,7 @@ function build_ui(gobj)
|
|
|
459
471
|
if(with_search_button) {
|
|
460
472
|
let search_id = `${toolbar_id}_search`;
|
|
461
473
|
let $search_box = createElement2(
|
|
462
|
-
['div', {class: 'control has-icons-left
|
|
474
|
+
['div', {class: 'control has-icons-left mr-1'}, [
|
|
463
475
|
['input', {
|
|
464
476
|
id: search_id,
|
|
465
477
|
class: 'input',
|
|
@@ -469,19 +481,13 @@ function build_ui(gobj)
|
|
|
469
481
|
}],
|
|
470
482
|
['span', {class: 'icon is-left'}, [
|
|
471
483
|
['i', {class: 'yi-magnifying-glass'}]
|
|
472
|
-
]]
|
|
473
|
-
['span', {class: 'icon is-right', style: 'cursor:pointer; pointer-events:all;', title: 'clear search'}, [
|
|
474
|
-
['i', {class: 'yi-xmark'}]
|
|
475
|
-
], {
|
|
476
|
-
'click': (event) => {
|
|
477
|
-
event.stopPropagation();
|
|
478
|
-
let $input = document.getElementById(search_id);
|
|
479
|
-
if($input) { $input.value = ''; $input.dispatchEvent(new Event('input')); }
|
|
480
|
-
}
|
|
481
|
-
}]
|
|
484
|
+
]]
|
|
482
485
|
]]
|
|
483
486
|
);
|
|
484
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);
|
|
485
491
|
if($search_input) {
|
|
486
492
|
$search_input.addEventListener('input', (event) => {
|
|
487
493
|
let searchText = event.target.value.trim().toLowerCase();
|
|
@@ -511,7 +517,7 @@ function build_ui(gobj)
|
|
|
511
517
|
|
|
512
518
|
if(with_refresh_button) {
|
|
513
519
|
let $refresh = createElement2(
|
|
514
|
-
['button', {class: 'button mr-1', title: 'refresh'}, [
|
|
520
|
+
['button', {class: 'button mr-1', title: t('refresh'), 'data-i18n-title': 'refresh'}, [
|
|
515
521
|
['i', {class: 'yi-arrows-rotate'}],
|
|
516
522
|
['span', {class: 'is-hidden-mobile', i18n: 'refresh', style: 'padding-left:5px;'}, 'refresh']
|
|
517
523
|
], {
|
|
@@ -820,7 +826,12 @@ function create_tabulator(gobj)
|
|
|
820
826
|
}
|
|
821
827
|
|
|
822
828
|
let pkey = desc.pkey || "id";
|
|
823
|
-
|
|
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);
|
|
824
835
|
|
|
825
836
|
let tabulator_settings = json_deep_copy(gobj_read_attr(gobj, "tabulator_settings"));
|
|
826
837
|
|
|
@@ -848,7 +859,26 @@ function create_tabulator(gobj)
|
|
|
848
859
|
log_error(`${gobj_short_name(gobj)}: table element '#${table_id}' not found in $container`);
|
|
849
860
|
return;
|
|
850
861
|
}
|
|
851
|
-
|
|
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
|
+
}
|
|
852
882
|
|
|
853
883
|
/* Keep the footer in sync with the visible (active) row count,
|
|
854
884
|
* and hide the pagination chrome while everything fits in one
|
|
@@ -1134,9 +1164,14 @@ function open_form_dialog(gobj, mode, record)
|
|
|
1134
1164
|
|
|
1135
1165
|
let priv = gobj.priv;
|
|
1136
1166
|
let desc = gobj_read_attr(gobj, "desc");
|
|
1137
|
-
let popup_id = gobj_read_str_attr(gobj, "popup_id");
|
|
1138
1167
|
let topic_name = gobj_read_str_attr(gobj, "topic_name");
|
|
1139
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
|
+
|
|
1140
1175
|
/* Title says what you are doing: "new <topic>" on create,
|
|
1141
1176
|
* "<topic> — <pkey>" on update. */
|
|
1142
1177
|
let pkey = desc.pkey || "id";
|
|
@@ -1148,36 +1183,12 @@ function open_form_dialog(gobj, mode, record)
|
|
|
1148
1183
|
title = t(topic_name) + (empty_string(rid) ? "" : " — " + rid);
|
|
1149
1184
|
}
|
|
1150
1185
|
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
['button', {class: 'delete is-large', 'aria-label': 'close'}, '', {
|
|
1158
|
-
click: function(evt) {
|
|
1159
|
-
evt.stopPropagation();
|
|
1160
|
-
request_close_form_dialog(gobj);
|
|
1161
|
-
}
|
|
1162
|
-
}]
|
|
1163
|
-
]],
|
|
1164
|
-
/* flex column so the hosted form takes the dialog
|
|
1165
|
-
* height: fields scroll internally and the form's
|
|
1166
|
-
* bottom toolbar stays visible (dialog footer) */
|
|
1167
|
-
['section', {
|
|
1168
|
-
class: 'modal-card-body p-2',
|
|
1169
|
-
style: 'display:flex; flex-direction:column;'
|
|
1170
|
-
}, []]
|
|
1171
|
-
]]
|
|
1172
|
-
], {
|
|
1173
|
-
click: function(evt) {
|
|
1174
|
-
// Capture bubble events
|
|
1175
|
-
evt.stopPropagation();
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
]);
|
|
1179
|
-
|
|
1180
|
-
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
|
+
);
|
|
1181
1192
|
|
|
1182
1193
|
let form = gobj_create_pure_child(
|
|
1183
1194
|
"form_" + clean_name(gobj_name(gobj)),
|
|
@@ -1187,6 +1198,10 @@ function open_form_dialog(gobj, mode, record)
|
|
|
1187
1198
|
record: record,
|
|
1188
1199
|
fkey_options: build_fkey_options(gobj),
|
|
1189
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",
|
|
1190
1205
|
pkey: desc.pkey || "id",
|
|
1191
1206
|
topic_name: topic_name,
|
|
1192
1207
|
editable: true,
|
|
@@ -1195,7 +1210,6 @@ function open_form_dialog(gobj, mode, record)
|
|
|
1195
1210
|
gobj
|
|
1196
1211
|
);
|
|
1197
1212
|
priv.form = form;
|
|
1198
|
-
priv.$popup = $element;
|
|
1199
1213
|
|
|
1200
1214
|
let $form_container = gobj_read_attr(form, "$container");
|
|
1201
1215
|
if($form_container) {
|
|
@@ -1203,38 +1217,33 @@ function open_form_dialog(gobj, mode, record)
|
|
|
1203
1217
|
$form_container.style.minHeight = "0";
|
|
1204
1218
|
}
|
|
1205
1219
|
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
*
|
|
1211
|
-
*
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
evt.stopPropagation();
|
|
1225
|
-
request_close_form_dialog(gobj);
|
|
1226
|
-
}
|
|
1227
|
-
};
|
|
1228
|
-
document.addEventListener("keydown", priv.on_keydown);
|
|
1229
|
-
}
|
|
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
|
+
});
|
|
1230
1238
|
|
|
1231
|
-
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
|
|
1232
1241
|
|
|
1233
1242
|
/* Focus the pkey unless the mode made it readonly (update) —
|
|
1234
1243
|
* then the first editable field. */
|
|
1235
|
-
let $with_focus = $
|
|
1244
|
+
let $with_focus = $body.querySelector('.with-focus:not([readonly])');
|
|
1236
1245
|
if(!$with_focus) {
|
|
1237
|
-
$with_focus = $
|
|
1246
|
+
$with_focus = $body.querySelector(
|
|
1238
1247
|
'input.input:not([readonly]), textarea, select'
|
|
1239
1248
|
);
|
|
1240
1249
|
}
|
|
@@ -1244,10 +1253,12 @@ function open_form_dialog(gobj, mode, record)
|
|
|
1244
1253
|
}
|
|
1245
1254
|
|
|
1246
1255
|
/************************************************************
|
|
1247
|
-
*
|
|
1248
|
-
*
|
|
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).
|
|
1249
1260
|
************************************************************/
|
|
1250
|
-
function
|
|
1261
|
+
function form_may_close(gobj)
|
|
1251
1262
|
{
|
|
1252
1263
|
let priv = gobj.priv;
|
|
1253
1264
|
if(priv.form) {
|
|
@@ -1259,31 +1270,24 @@ function request_close_form_dialog(gobj)
|
|
|
1259
1270
|
'All changes will be lost. Are you sure?',
|
|
1260
1271
|
{t: t, yes_label: "yes", no_label: "no", cancel_label: "cancel"}
|
|
1261
1272
|
).then(function(answer) {
|
|
1262
|
-
if(answer === "yes") {
|
|
1263
|
-
|
|
1273
|
+
if(answer === "yes" && priv.form_modal) {
|
|
1274
|
+
priv.form_modal.close();
|
|
1264
1275
|
}
|
|
1265
1276
|
});
|
|
1266
|
-
return;
|
|
1277
|
+
return false;
|
|
1267
1278
|
}
|
|
1268
1279
|
}
|
|
1269
|
-
|
|
1280
|
+
return true;
|
|
1270
1281
|
}
|
|
1271
1282
|
|
|
1272
1283
|
/************************************************************
|
|
1273
|
-
* 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.
|
|
1274
1287
|
************************************************************/
|
|
1275
|
-
function
|
|
1288
|
+
function teardown_form_child(gobj)
|
|
1276
1289
|
{
|
|
1277
1290
|
let priv = gobj.priv;
|
|
1278
|
-
if(priv.escape_handler) {
|
|
1279
|
-
yui_shell_pop_escape(priv.escape_shell, priv.escape_handler);
|
|
1280
|
-
priv.escape_shell = null;
|
|
1281
|
-
priv.escape_handler = null;
|
|
1282
|
-
}
|
|
1283
|
-
if(priv.on_keydown) {
|
|
1284
|
-
document.removeEventListener("keydown", priv.on_keydown);
|
|
1285
|
-
priv.on_keydown = null;
|
|
1286
|
-
}
|
|
1287
1291
|
if(priv.form) {
|
|
1288
1292
|
if(gobj_is_running(priv.form)) {
|
|
1289
1293
|
gobj_stop(priv.form);
|
|
@@ -1291,12 +1295,24 @@ function close_form_dialog(gobj)
|
|
|
1291
1295
|
gobj_destroy(priv.form);
|
|
1292
1296
|
priv.form = null;
|
|
1293
1297
|
}
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
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;
|
|
1299
1314
|
}
|
|
1315
|
+
teardown_form_child(gobj);
|
|
1300
1316
|
}
|
|
1301
1317
|
|
|
1302
1318
|
/************************************************************
|
|
@@ -1720,6 +1736,37 @@ function show_dropdown_popup_menu(gobj, x, y, items, callback)
|
|
|
1720
1736
|
/************************************************************
|
|
1721
1737
|
* From external, at the beginning, load all topic data
|
|
1722
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
|
+
|
|
1723
1770
|
function ac_load_nodes(gobj, event, kw, src)
|
|
1724
1771
|
{
|
|
1725
1772
|
let data = kw;
|
|
@@ -2280,6 +2327,7 @@ function create_gclass(gclass_name)
|
|
|
2280
2327
|
*---------------------------------------------*/
|
|
2281
2328
|
const states = [
|
|
2282
2329
|
["ST_IDLE", [
|
|
2330
|
+
["EV_LANGUAGE_CHANGED", ac_language_changed, null],
|
|
2283
2331
|
["EV_LOAD_NODES", ac_load_nodes, null],
|
|
2284
2332
|
["EV_LOAD_NODE_CREATED", ac_load_node_created, null],
|
|
2285
2333
|
["EV_LOAD_NODE_UPDATED", ac_load_node_updated, null],
|
|
@@ -2306,6 +2354,7 @@ function create_gclass(gclass_name)
|
|
|
2306
2354
|
* Events
|
|
2307
2355
|
*---------------------------------------------*/
|
|
2308
2356
|
const event_types = [
|
|
2357
|
+
["EV_LANGUAGE_CHANGED", 0],
|
|
2309
2358
|
["EV_LOAD_NODES", 0],
|
|
2310
2359
|
["EV_LOAD_NODE_CREATED", 0],
|
|
2311
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
|
+
}
|