@yuneta/gobj-ui 7.26.40 → 7.26.41
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 +21 -2
- package/dist/gobj-ui.cjs.js +66 -41
- package/dist/gobj-ui.es.js +66 -41
- package/package.json +1 -1
- package/src/c_yui_form.js +80 -68
- package/src/form_table_edits_ask.wiring.test.js +398 -0
- package/src/form_table_rearm.wiring.test.js +9 -7
- package/src/yui_tabulator_i18n.js +21 -1
- package/src/yui_tabulator_i18n.test.js +39 -0
- package/src/yui_text.js +10 -0
- package/src/yui_text.test.js +13 -0
package/README.md
CHANGED
|
@@ -3422,6 +3422,15 @@ true. A record that lacks a defaulted field opens changed and closes without
|
|
|
3422
3422
|
a question; until 7.26.39 it asked on every close, about nothing the reader
|
|
3423
3423
|
would lose.
|
|
3424
3424
|
|
|
3425
|
+
What the reader does to a TABLE of the form -- edits a cell, adds a row,
|
|
3426
|
+
deletes one, in a nested table too -- is `edited` like anything typed, whatever
|
|
3427
|
+
the record the form opened with. Each table hears it from the moment it is
|
|
3428
|
+
built (a nested one is built by its row, whenever the row is drawn); the rows
|
|
3429
|
+
the form loads itself say nothing. In 7.26.39 and 7.26.40 a form that opened
|
|
3430
|
+
changed did not hear its tables at all, so closing it lost a table edit without
|
|
3431
|
+
a question. And the titles of those tables change language with the shell's
|
|
3432
|
+
`EV_LANGUAGE_CHANGED` (7.26.41+), like the rest of the form.
|
|
3433
|
+
|
|
3425
3434
|
```js
|
|
3426
3435
|
const kw = {abort_close: false};
|
|
3427
3436
|
gobj_send_event(form, "EV_WINDOW_TO_CLOSE", kw, gobj);
|
|
@@ -3917,14 +3926,24 @@ loading/error notices) and it never went through i18n. Use:
|
|
|
3917
3926
|
```js
|
|
3918
3927
|
new Tabulator($el, {...settings, ...yui_tabulator_lang(t)}); // at build
|
|
3919
3928
|
yui_tabulator_relocalize(table, t); // on the event
|
|
3929
|
+
yui_tabulator_relocalize(table, t, {device_id: t("device id")}); // and the titles
|
|
3920
3930
|
```
|
|
3921
3931
|
|
|
3922
3932
|
Every key falls back to the English string Tabulator used to render
|
|
3923
3933
|
(`defaultValue`), so an app that defines none of them sees no change. Two traps
|
|
3924
3934
|
the implementation already handles: `setLocale()` with the locale name already
|
|
3925
3935
|
in force is a **no-op** (hence a fresh name per switch), and re-applying a locale
|
|
3926
|
-
makes Tabulator
|
|
3927
|
-
**
|
|
3936
|
+
makes Tabulator run every title formatter again on the EXISTING header cell, and
|
|
3937
|
+
**append** what one returns when it is a Node. `text_title_formatter` empties
|
|
3938
|
+
the cell first (7.26.41; until then each change of language added one more copy
|
|
3939
|
+
of every title, *"Id dispositivoId dispositivo"*); a title formatter of your own
|
|
3940
|
+
that returns a Node must do the same (`cell.getElement().textContent = ""`).
|
|
3941
|
+
|
|
3942
|
+
The column TITLES change language through the locale too: hand them, already
|
|
3943
|
+
translated, by field (the third argument). Tabulator binds each title to its
|
|
3944
|
+
locale, so they are drawn again and no column is rebuilt -- `updateDefinition()`
|
|
3945
|
+
re-renders every row, and a row formatter that builds something in its row (a
|
|
3946
|
+
nested table) builds it again, empty.
|
|
3928
3947
|
|
|
3929
3948
|
**A missing key is invisible:** i18next answers an unknown key **with the key
|
|
3930
3949
|
itself**, so it renders (lower-case English) and simply never changes language.
|
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -505,8 +505,16 @@ function text_node(value) {
|
|
|
505
505
|
* Tabulator puts a STRING title in with innerHTML, so a title taken
|
|
506
506
|
* from a schema (`header`, which the schema editor writes) is markup
|
|
507
507
|
* unless the column carries a formatter that returns a Node.
|
|
508
|
+
*
|
|
509
|
+
* And it APPENDS that Node to the title's holder, which it never
|
|
510
|
+
* empties -- while it runs the formatter again on every setLocale()
|
|
511
|
+
* (yui_tabulator_relocalize()). Until 7.26.40 each change of language
|
|
512
|
+
* added one more copy of every title ("Id dispositivoId dispositivo").
|
|
513
|
+
* The holder (the mock cell's element) is emptied first.
|
|
508
514
|
************************************************************/
|
|
509
515
|
function text_title_formatter(cell) {
|
|
516
|
+
const $holder = typeof cell.getElement === "function" ? cell.getElement() : null;
|
|
517
|
+
if ($holder) $holder.textContent = "";
|
|
510
518
|
return text_node(cell.getValue());
|
|
511
519
|
}
|
|
512
520
|
/************************************************************
|
|
@@ -7106,6 +7114,12 @@ function register_c_yui_wizard() {
|
|
|
7106
7114
|
* new Tabulator($el, {..., ...yui_tabulator_lang(t)});
|
|
7107
7115
|
* yui_tabulator_relocalize(table, t); // on a language change
|
|
7108
7116
|
*
|
|
7117
|
+
* A view whose COLUMN TITLES change language hands them too, in the
|
|
7118
|
+
* new language, by field -- Tabulator binds each column's title to its
|
|
7119
|
+
* locale, so they change with it and no column is rebuilt:
|
|
7120
|
+
*
|
|
7121
|
+
* yui_tabulator_relocalize(table, t, {device_id: t("device id")});
|
|
7122
|
+
*
|
|
7109
7123
|
* Every key carries an English `defaultValue`, so an app that does not
|
|
7110
7124
|
* define it renders exactly what Tabulator rendered before — the
|
|
7111
7125
|
* keys are an OPPORTUNITY to translate, never a requirement.
|
|
@@ -7264,11 +7278,23 @@ function yui_tabulator_name_editor(column_def, name_of) {
|
|
|
7264
7278
|
return Object.assign({}, params, { elementAttributes: attrs });
|
|
7265
7279
|
};
|
|
7266
7280
|
}
|
|
7267
|
-
|
|
7281
|
+
/***************************************************************
|
|
7282
|
+
* yui_tabulator_relocalize() -- see "Put an EXISTING table in the
|
|
7283
|
+
* current language" above.
|
|
7284
|
+
*
|
|
7285
|
+
* `titles`, optional: {field: title}, the titles of the columns in
|
|
7286
|
+
* the new language, already translated. They go in the locale
|
|
7287
|
+
* (`columns`), where Tabulator binds each column's title: setLocale()
|
|
7288
|
+
* draws them again without rebuilding a column -- updateDefinition()
|
|
7289
|
+
* re-renders every row, and a row whose formatter builds something (a
|
|
7290
|
+
* nested table) builds it anew. A field left out keeps its title.
|
|
7291
|
+
***************************************************************/
|
|
7292
|
+
function yui_tabulator_relocalize(table, t, titles) {
|
|
7268
7293
|
if (!table) return;
|
|
7269
7294
|
try {
|
|
7270
7295
|
let name = next_lang_name();
|
|
7271
7296
|
let strings = tabulator_strings(t);
|
|
7297
|
+
if (titles && typeof titles === "object") strings.columns = Object.assign({}, titles);
|
|
7272
7298
|
table.options.langs = table.options.langs || {};
|
|
7273
7299
|
table.options.langs[name] = strings;
|
|
7274
7300
|
let localize = table.modules && table.modules.localize;
|
|
@@ -9487,7 +9513,7 @@ var attrs_table$17 = [
|
|
|
9487
9513
|
(0, _yuneta_gobj_js.SDATA_END)()
|
|
9488
9514
|
];
|
|
9489
9515
|
var PRIVATE_DATA$17 = {
|
|
9490
|
-
|
|
9516
|
+
loading_rows: 0,
|
|
9491
9517
|
tables: null,
|
|
9492
9518
|
table_seq: 0,
|
|
9493
9519
|
json_names_posted: false,
|
|
@@ -9543,18 +9569,12 @@ function mt_stop$17(gobj) {
|
|
|
9543
9569
|
* Local Methods
|
|
9544
9570
|
***************************/
|
|
9545
9571
|
/************************************************************
|
|
9546
|
-
*
|
|
9547
|
-
|
|
9548
|
-
function isEventSet(table, event) {
|
|
9549
|
-
let eventListeners = table.externalEvents.events;
|
|
9550
|
-
return eventListeners && eventListeners[event] && eventListeners[event].length > 0;
|
|
9551
|
-
}
|
|
9552
|
-
/************************************************************
|
|
9553
|
-
*
|
|
9572
|
+
* `changes` and the save/undo buttons. What the tables say is
|
|
9573
|
+
* not switched here: they always say it (create_tabulator).
|
|
9554
9574
|
************************************************************/
|
|
9555
9575
|
function set_changed_stated(gobj, changed) {
|
|
9556
9576
|
let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
|
|
9557
|
-
|
|
9577
|
+
$container.querySelector("form");
|
|
9558
9578
|
const enable = ($btn, on, color) => {
|
|
9559
9579
|
if (!$btn) return;
|
|
9560
9580
|
if (on) $btn.removeAttribute("disabled");
|
|
@@ -9569,35 +9589,11 @@ function set_changed_stated(gobj, changed) {
|
|
|
9569
9589
|
(0, _yuneta_gobj_js.gobj_write_bool_attr)(gobj, "changes", true);
|
|
9570
9590
|
enable($button_save, true, "MediumSeaGreen");
|
|
9571
9591
|
enable($button_undo, true, "Magenta");
|
|
9572
|
-
$form.querySelectorAll(".yui-tabulator-table").forEach(($table) => {
|
|
9573
|
-
if ($table.tabulator && isEventSet($table.tabulator, "dataChanged")) $table.tabulator.off("dataChanged");
|
|
9574
|
-
$table.querySelectorAll(".yui-tabulator-table-nested").forEach(($table_nested) => {
|
|
9575
|
-
if ($table_nested.tabulator && isEventSet($table_nested.tabulator, "dataChanged")) $table_nested.tabulator.off("dataChanged");
|
|
9576
|
-
});
|
|
9577
|
-
});
|
|
9578
9592
|
} else {
|
|
9579
9593
|
(0, _yuneta_gobj_js.gobj_write_bool_attr)(gobj, "changes", false);
|
|
9580
9594
|
(0, _yuneta_gobj_js.gobj_write_bool_attr)(gobj, "edited", false);
|
|
9581
9595
|
enable($button_save, false, OFF_COLOR);
|
|
9582
9596
|
enable($button_undo, false, OFF_COLOR);
|
|
9583
|
-
if (gobj.priv.rearm_timer) clearTimeout(gobj.priv.rearm_timer);
|
|
9584
|
-
gobj.priv.rearm_timer = setTimeout(function() {
|
|
9585
|
-
gobj.priv.rearm_timer = null;
|
|
9586
|
-
if ((0, _yuneta_gobj_js.gobj_is_destroying)(gobj)) return;
|
|
9587
|
-
const rearm = (tabulator) => {
|
|
9588
|
-
if (!tabulator) return;
|
|
9589
|
-
if (isEventSet(tabulator, "dataChanged")) tabulator.off("dataChanged");
|
|
9590
|
-
tabulator.on("dataChanged", function(data) {
|
|
9591
|
-
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_RECORD_CHANGED", {}, gobj);
|
|
9592
|
-
});
|
|
9593
|
-
};
|
|
9594
|
-
$form.querySelectorAll(".yui-tabulator-table").forEach(($table) => {
|
|
9595
|
-
rearm($table.tabulator);
|
|
9596
|
-
$table.querySelectorAll(".yui-tabulator-table-nested").forEach(($table_nested) => {
|
|
9597
|
-
rearm($table_nested.tabulator);
|
|
9598
|
-
});
|
|
9599
|
-
});
|
|
9600
|
-
}, 200);
|
|
9601
9597
|
}
|
|
9602
9598
|
}
|
|
9603
9599
|
/************************************************************
|
|
@@ -9834,10 +9830,6 @@ function build_ui$15(gobj) {
|
|
|
9834
9830
|
* Destroy UI
|
|
9835
9831
|
************************************************************/
|
|
9836
9832
|
function destroy_ui$10(gobj) {
|
|
9837
|
-
if (gobj.priv && gobj.priv.rearm_timer) {
|
|
9838
|
-
clearTimeout(gobj.priv.rearm_timer);
|
|
9839
|
-
gobj.priv.rearm_timer = null;
|
|
9840
|
-
}
|
|
9841
9833
|
let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
|
|
9842
9834
|
if ($container) {
|
|
9843
9835
|
destroy_widgets(gobj, $container);
|
|
@@ -11231,6 +11223,10 @@ function create_tabulator$1(gobj, $extend, name, template) {
|
|
|
11231
11223
|
hozAlign: "center"
|
|
11232
11224
|
};
|
|
11233
11225
|
let tabulator = new tabulator_tables.TabulatorFull($extend, tabulator_settings);
|
|
11226
|
+
tabulator.on("dataChanged", function() {
|
|
11227
|
+
if (gobj.priv.loading_rows > 0) return;
|
|
11228
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_RECORD_CHANGED", {}, gobj);
|
|
11229
|
+
});
|
|
11234
11230
|
tabulator.on("renderComplete", function() {
|
|
11235
11231
|
yui_tabulator_name_row_selects(tabulator, i18next.t);
|
|
11236
11232
|
});
|
|
@@ -11248,6 +11244,24 @@ function create_tabulator$1(gobj, $extend, name, template) {
|
|
|
11248
11244
|
return tabulator;
|
|
11249
11245
|
}
|
|
11250
11246
|
/******************************************************************
|
|
11247
|
+
* The title of a table's column: the schema's header, or the
|
|
11248
|
+
* field's name, translated.
|
|
11249
|
+
******************************************************************/
|
|
11250
|
+
function column_title(field_desc) {
|
|
11251
|
+
return (0, i18next.t)(field_desc.header || field_desc.name);
|
|
11252
|
+
}
|
|
11253
|
+
/******************************************************************
|
|
11254
|
+
* {field: title} of a table's template, in the current language.
|
|
11255
|
+
******************************************************************/
|
|
11256
|
+
function table_column_titles(template) {
|
|
11257
|
+
let titles = {};
|
|
11258
|
+
Object.entries(template || {}).forEach(([key, value]) => {
|
|
11259
|
+
const field_desc = (0, _yuneta_gobj_js.template_get_field_desc)(key, value);
|
|
11260
|
+
titles[field_desc.name] = column_title(field_desc);
|
|
11261
|
+
});
|
|
11262
|
+
return titles;
|
|
11263
|
+
}
|
|
11264
|
+
/******************************************************************
|
|
11251
11265
|
* Convert a template's items in tabulator columns
|
|
11252
11266
|
******************************************************************/
|
|
11253
11267
|
function template2columns(gobj, columns, template, sub_elements) {
|
|
@@ -11260,7 +11274,7 @@ function template2columns(gobj, columns, template, sub_elements) {
|
|
|
11260
11274
|
let validator = [];
|
|
11261
11275
|
let column = {
|
|
11262
11276
|
field: field_desc.name,
|
|
11263
|
-
title: (
|
|
11277
|
+
title: column_title(field_desc),
|
|
11264
11278
|
titleFormatter: text_title_formatter,
|
|
11265
11279
|
editable: get_editable_state,
|
|
11266
11280
|
editor: false,
|
|
@@ -12115,7 +12129,14 @@ function ac_add_table_row(gobj, event, kw, src) {
|
|
|
12115
12129
|
}
|
|
12116
12130
|
let record = kw.record;
|
|
12117
12131
|
let loading = kw.loading;
|
|
12118
|
-
|
|
12132
|
+
let added;
|
|
12133
|
+
if (loading) gobj.priv.loading_rows++;
|
|
12134
|
+
try {
|
|
12135
|
+
added = tabulator.addRow(record);
|
|
12136
|
+
} finally {
|
|
12137
|
+
if (loading) gobj.priv.loading_rows--;
|
|
12138
|
+
}
|
|
12139
|
+
added.then(function(row) {
|
|
12119
12140
|
if (loading) {
|
|
12120
12141
|
let template = $table.template;
|
|
12121
12142
|
Object.entries(template).forEach(([key, template_value]) => {
|
|
@@ -12222,6 +12243,10 @@ function ac_language_changed$10(gobj, event, kw, src) {
|
|
|
12222
12243
|
let editor = $editor.jsoneditor;
|
|
12223
12244
|
if (editor && typeof editor.updateProps === "function") editor.updateProps({ onRenderMenu: json_editor_render_menu(gobj) });
|
|
12224
12245
|
}
|
|
12246
|
+
for (let $table of $container.querySelectorAll(".yui-tabulator-table, .yui-tabulator-table-nested")) {
|
|
12247
|
+
let tabulator = $table.tabulator;
|
|
12248
|
+
if (tabulator && tabulator.initialized && $table.template) yui_tabulator_relocalize(tabulator, i18next.t, table_column_titles($table.template));
|
|
12249
|
+
}
|
|
12225
12250
|
return 0;
|
|
12226
12251
|
}
|
|
12227
12252
|
/***************************
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -500,8 +500,16 @@ function text_node(value) {
|
|
|
500
500
|
* Tabulator puts a STRING title in with innerHTML, so a title taken
|
|
501
501
|
* from a schema (`header`, which the schema editor writes) is markup
|
|
502
502
|
* unless the column carries a formatter that returns a Node.
|
|
503
|
+
*
|
|
504
|
+
* And it APPENDS that Node to the title's holder, which it never
|
|
505
|
+
* empties -- while it runs the formatter again on every setLocale()
|
|
506
|
+
* (yui_tabulator_relocalize()). Until 7.26.40 each change of language
|
|
507
|
+
* added one more copy of every title ("Id dispositivoId dispositivo").
|
|
508
|
+
* The holder (the mock cell's element) is emptied first.
|
|
503
509
|
************************************************************/
|
|
504
510
|
function text_title_formatter(cell) {
|
|
511
|
+
const $holder = typeof cell.getElement === "function" ? cell.getElement() : null;
|
|
512
|
+
if ($holder) $holder.textContent = "";
|
|
505
513
|
return text_node(cell.getValue());
|
|
506
514
|
}
|
|
507
515
|
/************************************************************
|
|
@@ -7101,6 +7109,12 @@ function register_c_yui_wizard() {
|
|
|
7101
7109
|
* new Tabulator($el, {..., ...yui_tabulator_lang(t)});
|
|
7102
7110
|
* yui_tabulator_relocalize(table, t); // on a language change
|
|
7103
7111
|
*
|
|
7112
|
+
* A view whose COLUMN TITLES change language hands them too, in the
|
|
7113
|
+
* new language, by field -- Tabulator binds each column's title to its
|
|
7114
|
+
* locale, so they change with it and no column is rebuilt:
|
|
7115
|
+
*
|
|
7116
|
+
* yui_tabulator_relocalize(table, t, {device_id: t("device id")});
|
|
7117
|
+
*
|
|
7104
7118
|
* Every key carries an English `defaultValue`, so an app that does not
|
|
7105
7119
|
* define it renders exactly what Tabulator rendered before — the
|
|
7106
7120
|
* keys are an OPPORTUNITY to translate, never a requirement.
|
|
@@ -7259,11 +7273,23 @@ function yui_tabulator_name_editor(column_def, name_of) {
|
|
|
7259
7273
|
return Object.assign({}, params, { elementAttributes: attrs });
|
|
7260
7274
|
};
|
|
7261
7275
|
}
|
|
7262
|
-
|
|
7276
|
+
/***************************************************************
|
|
7277
|
+
* yui_tabulator_relocalize() -- see "Put an EXISTING table in the
|
|
7278
|
+
* current language" above.
|
|
7279
|
+
*
|
|
7280
|
+
* `titles`, optional: {field: title}, the titles of the columns in
|
|
7281
|
+
* the new language, already translated. They go in the locale
|
|
7282
|
+
* (`columns`), where Tabulator binds each column's title: setLocale()
|
|
7283
|
+
* draws them again without rebuilding a column -- updateDefinition()
|
|
7284
|
+
* re-renders every row, and a row whose formatter builds something (a
|
|
7285
|
+
* nested table) builds it anew. A field left out keeps its title.
|
|
7286
|
+
***************************************************************/
|
|
7287
|
+
function yui_tabulator_relocalize(table, t, titles) {
|
|
7263
7288
|
if (!table) return;
|
|
7264
7289
|
try {
|
|
7265
7290
|
let name = next_lang_name();
|
|
7266
7291
|
let strings = tabulator_strings(t);
|
|
7292
|
+
if (titles && typeof titles === "object") strings.columns = Object.assign({}, titles);
|
|
7267
7293
|
table.options.langs = table.options.langs || {};
|
|
7268
7294
|
table.options.langs[name] = strings;
|
|
7269
7295
|
let localize = table.modules && table.modules.localize;
|
|
@@ -9482,7 +9508,7 @@ var attrs_table$17 = [
|
|
|
9482
9508
|
SDATA_END()
|
|
9483
9509
|
];
|
|
9484
9510
|
var PRIVATE_DATA$17 = {
|
|
9485
|
-
|
|
9511
|
+
loading_rows: 0,
|
|
9486
9512
|
tables: null,
|
|
9487
9513
|
table_seq: 0,
|
|
9488
9514
|
json_names_posted: false,
|
|
@@ -9538,18 +9564,12 @@ function mt_stop$17(gobj) {
|
|
|
9538
9564
|
* Local Methods
|
|
9539
9565
|
***************************/
|
|
9540
9566
|
/************************************************************
|
|
9541
|
-
*
|
|
9542
|
-
|
|
9543
|
-
function isEventSet(table, event) {
|
|
9544
|
-
let eventListeners = table.externalEvents.events;
|
|
9545
|
-
return eventListeners && eventListeners[event] && eventListeners[event].length > 0;
|
|
9546
|
-
}
|
|
9547
|
-
/************************************************************
|
|
9548
|
-
*
|
|
9567
|
+
* `changes` and the save/undo buttons. What the tables say is
|
|
9568
|
+
* not switched here: they always say it (create_tabulator).
|
|
9549
9569
|
************************************************************/
|
|
9550
9570
|
function set_changed_stated(gobj, changed) {
|
|
9551
9571
|
let $container = gobj_read_attr(gobj, "$container");
|
|
9552
|
-
|
|
9572
|
+
$container.querySelector("form");
|
|
9553
9573
|
const enable = ($btn, on, color) => {
|
|
9554
9574
|
if (!$btn) return;
|
|
9555
9575
|
if (on) $btn.removeAttribute("disabled");
|
|
@@ -9564,35 +9584,11 @@ function set_changed_stated(gobj, changed) {
|
|
|
9564
9584
|
gobj_write_bool_attr(gobj, "changes", true);
|
|
9565
9585
|
enable($button_save, true, "MediumSeaGreen");
|
|
9566
9586
|
enable($button_undo, true, "Magenta");
|
|
9567
|
-
$form.querySelectorAll(".yui-tabulator-table").forEach(($table) => {
|
|
9568
|
-
if ($table.tabulator && isEventSet($table.tabulator, "dataChanged")) $table.tabulator.off("dataChanged");
|
|
9569
|
-
$table.querySelectorAll(".yui-tabulator-table-nested").forEach(($table_nested) => {
|
|
9570
|
-
if ($table_nested.tabulator && isEventSet($table_nested.tabulator, "dataChanged")) $table_nested.tabulator.off("dataChanged");
|
|
9571
|
-
});
|
|
9572
|
-
});
|
|
9573
9587
|
} else {
|
|
9574
9588
|
gobj_write_bool_attr(gobj, "changes", false);
|
|
9575
9589
|
gobj_write_bool_attr(gobj, "edited", false);
|
|
9576
9590
|
enable($button_save, false, OFF_COLOR);
|
|
9577
9591
|
enable($button_undo, false, OFF_COLOR);
|
|
9578
|
-
if (gobj.priv.rearm_timer) clearTimeout(gobj.priv.rearm_timer);
|
|
9579
|
-
gobj.priv.rearm_timer = setTimeout(function() {
|
|
9580
|
-
gobj.priv.rearm_timer = null;
|
|
9581
|
-
if (gobj_is_destroying(gobj)) return;
|
|
9582
|
-
const rearm = (tabulator) => {
|
|
9583
|
-
if (!tabulator) return;
|
|
9584
|
-
if (isEventSet(tabulator, "dataChanged")) tabulator.off("dataChanged");
|
|
9585
|
-
tabulator.on("dataChanged", function(data) {
|
|
9586
|
-
gobj_send_event(gobj, "EV_RECORD_CHANGED", {}, gobj);
|
|
9587
|
-
});
|
|
9588
|
-
};
|
|
9589
|
-
$form.querySelectorAll(".yui-tabulator-table").forEach(($table) => {
|
|
9590
|
-
rearm($table.tabulator);
|
|
9591
|
-
$table.querySelectorAll(".yui-tabulator-table-nested").forEach(($table_nested) => {
|
|
9592
|
-
rearm($table_nested.tabulator);
|
|
9593
|
-
});
|
|
9594
|
-
});
|
|
9595
|
-
}, 200);
|
|
9596
9592
|
}
|
|
9597
9593
|
}
|
|
9598
9594
|
/************************************************************
|
|
@@ -9829,10 +9825,6 @@ function build_ui$15(gobj) {
|
|
|
9829
9825
|
* Destroy UI
|
|
9830
9826
|
************************************************************/
|
|
9831
9827
|
function destroy_ui$10(gobj) {
|
|
9832
|
-
if (gobj.priv && gobj.priv.rearm_timer) {
|
|
9833
|
-
clearTimeout(gobj.priv.rearm_timer);
|
|
9834
|
-
gobj.priv.rearm_timer = null;
|
|
9835
|
-
}
|
|
9836
9828
|
let $container = gobj_read_attr(gobj, "$container");
|
|
9837
9829
|
if ($container) {
|
|
9838
9830
|
destroy_widgets(gobj, $container);
|
|
@@ -11226,6 +11218,10 @@ function create_tabulator$1(gobj, $extend, name, template) {
|
|
|
11226
11218
|
hozAlign: "center"
|
|
11227
11219
|
};
|
|
11228
11220
|
let tabulator = new TabulatorFull($extend, tabulator_settings);
|
|
11221
|
+
tabulator.on("dataChanged", function() {
|
|
11222
|
+
if (gobj.priv.loading_rows > 0) return;
|
|
11223
|
+
gobj_send_event(gobj, "EV_RECORD_CHANGED", {}, gobj);
|
|
11224
|
+
});
|
|
11229
11225
|
tabulator.on("renderComplete", function() {
|
|
11230
11226
|
yui_tabulator_name_row_selects(tabulator, t);
|
|
11231
11227
|
});
|
|
@@ -11243,6 +11239,24 @@ function create_tabulator$1(gobj, $extend, name, template) {
|
|
|
11243
11239
|
return tabulator;
|
|
11244
11240
|
}
|
|
11245
11241
|
/******************************************************************
|
|
11242
|
+
* The title of a table's column: the schema's header, or the
|
|
11243
|
+
* field's name, translated.
|
|
11244
|
+
******************************************************************/
|
|
11245
|
+
function column_title(field_desc) {
|
|
11246
|
+
return t(field_desc.header || field_desc.name);
|
|
11247
|
+
}
|
|
11248
|
+
/******************************************************************
|
|
11249
|
+
* {field: title} of a table's template, in the current language.
|
|
11250
|
+
******************************************************************/
|
|
11251
|
+
function table_column_titles(template) {
|
|
11252
|
+
let titles = {};
|
|
11253
|
+
Object.entries(template || {}).forEach(([key, value]) => {
|
|
11254
|
+
const field_desc = template_get_field_desc(key, value);
|
|
11255
|
+
titles[field_desc.name] = column_title(field_desc);
|
|
11256
|
+
});
|
|
11257
|
+
return titles;
|
|
11258
|
+
}
|
|
11259
|
+
/******************************************************************
|
|
11246
11260
|
* Convert a template's items in tabulator columns
|
|
11247
11261
|
******************************************************************/
|
|
11248
11262
|
function template2columns(gobj, columns, template, sub_elements) {
|
|
@@ -11255,7 +11269,7 @@ function template2columns(gobj, columns, template, sub_elements) {
|
|
|
11255
11269
|
let validator = [];
|
|
11256
11270
|
let column = {
|
|
11257
11271
|
field: field_desc.name,
|
|
11258
|
-
title:
|
|
11272
|
+
title: column_title(field_desc),
|
|
11259
11273
|
titleFormatter: text_title_formatter,
|
|
11260
11274
|
editable: get_editable_state,
|
|
11261
11275
|
editor: false,
|
|
@@ -12110,7 +12124,14 @@ function ac_add_table_row(gobj, event, kw, src) {
|
|
|
12110
12124
|
}
|
|
12111
12125
|
let record = kw.record;
|
|
12112
12126
|
let loading = kw.loading;
|
|
12113
|
-
|
|
12127
|
+
let added;
|
|
12128
|
+
if (loading) gobj.priv.loading_rows++;
|
|
12129
|
+
try {
|
|
12130
|
+
added = tabulator.addRow(record);
|
|
12131
|
+
} finally {
|
|
12132
|
+
if (loading) gobj.priv.loading_rows--;
|
|
12133
|
+
}
|
|
12134
|
+
added.then(function(row) {
|
|
12114
12135
|
if (loading) {
|
|
12115
12136
|
let template = $table.template;
|
|
12116
12137
|
Object.entries(template).forEach(([key, template_value]) => {
|
|
@@ -12217,6 +12238,10 @@ function ac_language_changed$10(gobj, event, kw, src) {
|
|
|
12217
12238
|
let editor = $editor.jsoneditor;
|
|
12218
12239
|
if (editor && typeof editor.updateProps === "function") editor.updateProps({ onRenderMenu: json_editor_render_menu(gobj) });
|
|
12219
12240
|
}
|
|
12241
|
+
for (let $table of $container.querySelectorAll(".yui-tabulator-table, .yui-tabulator-table-nested")) {
|
|
12242
|
+
let tabulator = $table.tabulator;
|
|
12243
|
+
if (tabulator && tabulator.initialized && $table.template) yui_tabulator_relocalize(tabulator, t, table_column_titles($table.template));
|
|
12244
|
+
}
|
|
12220
12245
|
return 0;
|
|
12221
12246
|
}
|
|
12222
12247
|
/***************************
|
package/package.json
CHANGED
package/src/c_yui_form.js
CHANGED
|
@@ -55,7 +55,11 @@ import {
|
|
|
55
55
|
|
|
56
56
|
import {t} from "i18next";
|
|
57
57
|
|
|
58
|
-
import {
|
|
58
|
+
import {
|
|
59
|
+
yui_tabulator_name_row_selects,
|
|
60
|
+
yui_tabulator_name_editor,
|
|
61
|
+
yui_tabulator_relocalize
|
|
62
|
+
} from "./yui_tabulator_i18n.js";
|
|
59
63
|
import "tom-select/dist/css/tom-select.css"; // Import Tom-Select CSS
|
|
60
64
|
import TomSelect from "tom-select"; // Import Tom-Select JS
|
|
61
65
|
|
|
@@ -172,9 +176,10 @@ SDATA_END()
|
|
|
172
176
|
];
|
|
173
177
|
|
|
174
178
|
let PRIVATE_DATA = {
|
|
175
|
-
/*
|
|
176
|
-
*
|
|
177
|
-
|
|
179
|
+
/* > 0 while the FORM adds rows (a record loaded, a nested table
|
|
180
|
+
* filled): what a table says then is no change of the reader's
|
|
181
|
+
* (see create_tabulator's dataChanged). */
|
|
182
|
+
loading_rows: 0,
|
|
178
183
|
/* table_id -> WeakRef of its element: EV_ADD_TABLE_ROW names a
|
|
179
184
|
* table by id (a kw is plain json), see table_identity(). */
|
|
180
185
|
tables: null,
|
|
@@ -281,17 +286,8 @@ function mt_stop(gobj)
|
|
|
281
286
|
|
|
282
287
|
|
|
283
288
|
/************************************************************
|
|
284
|
-
*
|
|
285
|
-
|
|
286
|
-
function isEventSet(table, event)
|
|
287
|
-
{
|
|
288
|
-
// Access the event listeners directly from the Tabulator instance
|
|
289
|
-
let eventListeners = table.externalEvents.events;
|
|
290
|
-
return eventListeners && eventListeners[event] && eventListeners[event].length > 0;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/************************************************************
|
|
294
|
-
*
|
|
289
|
+
* `changes` and the save/undo buttons. What the tables say is
|
|
290
|
+
* not switched here: they always say it (create_tabulator).
|
|
295
291
|
************************************************************/
|
|
296
292
|
function set_changed_stated(gobj, changed)
|
|
297
293
|
{
|
|
@@ -333,18 +329,6 @@ function set_changed_stated(gobj, changed)
|
|
|
333
329
|
enable($button_save, true, "MediumSeaGreen");
|
|
334
330
|
enable($button_undo, true, "Magenta");
|
|
335
331
|
|
|
336
|
-
$form.querySelectorAll('.yui-tabulator-table').forEach($table => {
|
|
337
|
-
if($table.tabulator && isEventSet($table.tabulator, "dataChanged")) {
|
|
338
|
-
$table.tabulator.off("dataChanged");
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
$table.querySelectorAll('.yui-tabulator-table-nested').forEach($table_nested => {
|
|
342
|
-
if($table_nested.tabulator && isEventSet($table_nested.tabulator, "dataChanged")) {
|
|
343
|
-
$table_nested.tabulator.off("dataChanged");
|
|
344
|
-
}
|
|
345
|
-
});
|
|
346
|
-
});
|
|
347
|
-
|
|
348
332
|
} else {
|
|
349
333
|
/*
|
|
350
334
|
* Data not changed, disable buttons to save/undo
|
|
@@ -354,41 +338,6 @@ function set_changed_stated(gobj, changed)
|
|
|
354
338
|
|
|
355
339
|
enable($button_save, false, OFF_COLOR);
|
|
356
340
|
enable($button_undo, false, OFF_COLOR);
|
|
357
|
-
|
|
358
|
-
/*
|
|
359
|
-
* TODO improve. The tables are re-wired a moment later, once the
|
|
360
|
-
* data just loaded has settled. The timer is kept so that
|
|
361
|
-
* destroy_ui() cancels it: a form closed right after a save (what
|
|
362
|
-
* a dialog does) destroyed its widgets first, and the timer then
|
|
363
|
-
* called .on() on a null tabulator -- an uncaught TypeError.
|
|
364
|
-
*/
|
|
365
|
-
if(gobj.priv.rearm_timer) {
|
|
366
|
-
clearTimeout(gobj.priv.rearm_timer);
|
|
367
|
-
}
|
|
368
|
-
gobj.priv.rearm_timer = setTimeout(function() {
|
|
369
|
-
gobj.priv.rearm_timer = null;
|
|
370
|
-
if(gobj_is_destroying(gobj)) {
|
|
371
|
-
return; // destroy_ui() cancels it; this is the belt
|
|
372
|
-
}
|
|
373
|
-
const rearm = (tabulator) => {
|
|
374
|
-
if(!tabulator) {
|
|
375
|
-
return; // destroyed with its form
|
|
376
|
-
}
|
|
377
|
-
if(isEventSet(tabulator, "dataChanged")) {
|
|
378
|
-
tabulator.off("dataChanged");
|
|
379
|
-
}
|
|
380
|
-
tabulator.on("dataChanged", function(data) {
|
|
381
|
-
//data - the updated table data
|
|
382
|
-
gobj_send_event(gobj, "EV_RECORD_CHANGED", {}, gobj);
|
|
383
|
-
});
|
|
384
|
-
};
|
|
385
|
-
$form.querySelectorAll('.yui-tabulator-table').forEach($table => {
|
|
386
|
-
rearm($table.tabulator);
|
|
387
|
-
$table.querySelectorAll('.yui-tabulator-table-nested').forEach($table_nested => {
|
|
388
|
-
rearm($table_nested.tabulator);
|
|
389
|
-
});
|
|
390
|
-
});
|
|
391
|
-
}, 200);
|
|
392
341
|
}
|
|
393
342
|
}
|
|
394
343
|
|
|
@@ -562,10 +511,6 @@ function build_ui(gobj)
|
|
|
562
511
|
************************************************************/
|
|
563
512
|
function destroy_ui(gobj)
|
|
564
513
|
{
|
|
565
|
-
if(gobj.priv && gobj.priv.rearm_timer) {
|
|
566
|
-
clearTimeout(gobj.priv.rearm_timer);
|
|
567
|
-
gobj.priv.rearm_timer = null;
|
|
568
|
-
}
|
|
569
514
|
let $container = gobj_read_attr(gobj, "$container");
|
|
570
515
|
if($container) {
|
|
571
516
|
destroy_widgets(gobj, $container);
|
|
@@ -2332,6 +2277,22 @@ function create_tabulator(gobj, $extend, name, template)
|
|
|
2332
2277
|
$extend,
|
|
2333
2278
|
tabulator_settings
|
|
2334
2279
|
);
|
|
2280
|
+
/* What the READER does to a table -- edits a cell, adds or deletes a
|
|
2281
|
+
* row -- reaches the form only through `dataChanged`. Wired here, on
|
|
2282
|
+
* every table, nested ones included (a row's formatter builds them
|
|
2283
|
+
* whenever the row is drawn), and never switched off: until 7.26.40
|
|
2284
|
+
* it was unwired whenever the form was CHANGED and re-wired only by a
|
|
2285
|
+
* save, an undo or a load of what is stored, so a form that opened
|
|
2286
|
+
* changed (a field its record lacks, filled with its default) never
|
|
2287
|
+
* heard its tables -- an edit there was not `edited`, and closing lost
|
|
2288
|
+
* it without a question. The rows the form adds itself say nothing
|
|
2289
|
+
* (`loading_rows`, ac_add_table_row). */
|
|
2290
|
+
tabulator.on("dataChanged", function() {
|
|
2291
|
+
if(gobj.priv.loading_rows > 0) {
|
|
2292
|
+
return;
|
|
2293
|
+
}
|
|
2294
|
+
gobj_send_event(gobj, "EV_RECORD_CHANGED", {}, gobj);
|
|
2295
|
+
});
|
|
2335
2296
|
/* Tabulator hard-codes `aria-label="Select Row"` on the box its
|
|
2336
2297
|
* rowSelection formatter draws, and it draws a fresh one on EVERY
|
|
2337
2298
|
* render -- so this hangs off `renderComplete`, not `tableBuilt`:
|
|
@@ -2369,6 +2330,28 @@ function create_tabulator(gobj, $extend, name, template)
|
|
|
2369
2330
|
return tabulator;
|
|
2370
2331
|
}
|
|
2371
2332
|
|
|
2333
|
+
/******************************************************************
|
|
2334
|
+
* The title of a table's column: the schema's header, or the
|
|
2335
|
+
* field's name, translated.
|
|
2336
|
+
******************************************************************/
|
|
2337
|
+
function column_title(field_desc)
|
|
2338
|
+
{
|
|
2339
|
+
return t(/* i18n: data */ field_desc.header || field_desc.name); // the host's schema
|
|
2340
|
+
}
|
|
2341
|
+
|
|
2342
|
+
/******************************************************************
|
|
2343
|
+
* {field: title} of a table's template, in the current language.
|
|
2344
|
+
******************************************************************/
|
|
2345
|
+
function table_column_titles(template)
|
|
2346
|
+
{
|
|
2347
|
+
let titles = {};
|
|
2348
|
+
Object.entries(template || {}).forEach(([key, value]) => {
|
|
2349
|
+
const field_desc = template_get_field_desc(key, value);
|
|
2350
|
+
titles[field_desc.name] = column_title(field_desc);
|
|
2351
|
+
});
|
|
2352
|
+
return titles;
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2372
2355
|
/******************************************************************
|
|
2373
2356
|
* Convert a template's items in tabulator columns
|
|
2374
2357
|
******************************************************************/
|
|
@@ -2388,7 +2371,7 @@ function template2columns(gobj, columns, template, sub_elements)
|
|
|
2388
2371
|
let validator = [];
|
|
2389
2372
|
let column = {
|
|
2390
2373
|
field: field_desc.name,
|
|
2391
|
-
title:
|
|
2374
|
+
title: column_title(field_desc),
|
|
2392
2375
|
titleFormatter: text_title_formatter,
|
|
2393
2376
|
editable: get_editable_state,
|
|
2394
2377
|
editor: false,
|
|
@@ -3793,7 +3776,23 @@ function ac_add_table_row(gobj, event, kw, src)
|
|
|
3793
3776
|
let record = kw.record;
|
|
3794
3777
|
let loading = kw.loading;
|
|
3795
3778
|
|
|
3796
|
-
|
|
3779
|
+
/* Tabulator says `dataChanged` INSIDE addRow(), synchronously: a row
|
|
3780
|
+
* loaded from the record is muted there, whenever it lands -- also
|
|
3781
|
+
* from a `tableBuilt` that comes after the load returned, which is
|
|
3782
|
+
* why no timer can tell the two apart. */
|
|
3783
|
+
let added;
|
|
3784
|
+
if(loading) {
|
|
3785
|
+
gobj.priv.loading_rows++;
|
|
3786
|
+
}
|
|
3787
|
+
try {
|
|
3788
|
+
added = tabulator.addRow(record);
|
|
3789
|
+
} finally {
|
|
3790
|
+
if(loading) {
|
|
3791
|
+
gobj.priv.loading_rows--;
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
|
|
3795
|
+
added
|
|
3797
3796
|
.then(function(row) {
|
|
3798
3797
|
if(loading) {
|
|
3799
3798
|
/*
|
|
@@ -3955,6 +3954,19 @@ function ac_language_changed(gobj, event, kw, src)
|
|
|
3955
3954
|
editor.updateProps({onRenderMenu: json_editor_render_menu(gobj)});
|
|
3956
3955
|
}
|
|
3957
3956
|
}
|
|
3957
|
+
|
|
3958
|
+
/* The titles of the columns of every table, nested ones included:
|
|
3959
|
+
* Tabulator draws them at build, so they stayed in the language the
|
|
3960
|
+
* form was built in. Through the table's LOCALE, never by
|
|
3961
|
+
* updateDefinition(): rebuilding a column re-renders every row, and
|
|
3962
|
+
* a row's formatter builds its nested tables anew -- empty. A table
|
|
3963
|
+
* not built yet is drawn in the current language when it is. */
|
|
3964
|
+
for(let $table of $container.querySelectorAll(".yui-tabulator-table, .yui-tabulator-table-nested")) {
|
|
3965
|
+
let tabulator = $table.tabulator;
|
|
3966
|
+
if(tabulator && tabulator.initialized && $table.template) {
|
|
3967
|
+
yui_tabulator_relocalize(tabulator, t, table_column_titles($table.template));
|
|
3968
|
+
}
|
|
3969
|
+
}
|
|
3958
3970
|
return 0;
|
|
3959
3971
|
}
|
|
3960
3972
|
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* form_table_edits_ask.wiring.test.js
|
|
3
|
+
*
|
|
4
|
+
* What the reader does to the TABLES of a C_YUI_FORM is `edited`,
|
|
5
|
+
* whatever the record the form opened with -- so closing asks.
|
|
6
|
+
*
|
|
7
|
+
* A form opens CHANGED when its record lacks a field the template
|
|
8
|
+
* fills with its default. Until 7.26.40 that also unwired the
|
|
9
|
+
* tables' `dataChanged` (and only a save, an undo or a load of what
|
|
10
|
+
* is stored wired it again), so adding a row, editing a cell,
|
|
11
|
+
* deleting a row or editing a nested table marked nothing: since
|
|
12
|
+
* 7.26.39 the close asks only for `edited`, and it closed without a
|
|
13
|
+
* question -- the edit lost. In estadodelaire that is the settings of
|
|
14
|
+
* every device without its alarm colours, whose alarms are tables.
|
|
15
|
+
*
|
|
16
|
+
* And the column titles of those tables change language: they were
|
|
17
|
+
* drawn at build and never again.
|
|
18
|
+
*
|
|
19
|
+
* Driven through the real gclass on a document double, with a
|
|
20
|
+
* Tabulator double that, like Tabulator, draws a row (its formatter
|
|
21
|
+
* runs) and says `dataChanged` INSIDE addRow(), synchronously.
|
|
22
|
+
*
|
|
23
|
+
* Copyright (c) 2026, ArtGins.
|
|
24
|
+
* All Rights Reserved.
|
|
25
|
+
***********************************************************************/
|
|
26
|
+
import {describe, test, expect, beforeAll, beforeEach, vi} from "vitest";
|
|
27
|
+
import {install_dom_double} from "../test/dom_double.js";
|
|
28
|
+
|
|
29
|
+
install_dom_double();
|
|
30
|
+
|
|
31
|
+
if(typeof globalThis.ResizeObserver === "undefined") {
|
|
32
|
+
globalThis.ResizeObserver = class {
|
|
33
|
+
observe() {}
|
|
34
|
+
unobserve() {}
|
|
35
|
+
disconnect() {}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const tables = vi.hoisted(() => []);
|
|
40
|
+
vi.mock("tabulator-tables", () => {
|
|
41
|
+
class FakeTabulator {
|
|
42
|
+
constructor($el, settings) {
|
|
43
|
+
this.element = $el;
|
|
44
|
+
this.settings = settings;
|
|
45
|
+
this.options = {};
|
|
46
|
+
this.handlers = {};
|
|
47
|
+
this.initialized = true;
|
|
48
|
+
this.rows = [];
|
|
49
|
+
this.installed = {};
|
|
50
|
+
this.locale = null;
|
|
51
|
+
this.modules = {
|
|
52
|
+
localize: {
|
|
53
|
+
installLang: (name, strings) => {
|
|
54
|
+
this.installed[name] = strings;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
tables.push(this);
|
|
59
|
+
return new Proxy(this, {
|
|
60
|
+
get(target, prop) {
|
|
61
|
+
if(prop in target) {
|
|
62
|
+
return target[prop];
|
|
63
|
+
}
|
|
64
|
+
return () => [];
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
on(ev, fn) {
|
|
69
|
+
(this.handlers[ev] = this.handlers[ev] || []).push(fn);
|
|
70
|
+
}
|
|
71
|
+
off(ev) {
|
|
72
|
+
delete this.handlers[ev];
|
|
73
|
+
}
|
|
74
|
+
emit(ev) {
|
|
75
|
+
for(const fn of (this.handlers[ev] || [])) {
|
|
76
|
+
fn([]);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
addRow(record) {
|
|
80
|
+
/* The row's element lives in the table's, as in Tabulator:
|
|
81
|
+
* a nested table drawn in it is inside the form. */
|
|
82
|
+
const $row = document.createElement("div");
|
|
83
|
+
this.element.appendChild($row);
|
|
84
|
+
const row = {getElement: () => $row, getData: () => record};
|
|
85
|
+
if(this.settings && typeof this.settings.rowFormatter === "function") {
|
|
86
|
+
this.settings.rowFormatter(row);
|
|
87
|
+
}
|
|
88
|
+
this.rows.push(row);
|
|
89
|
+
this.emit("dataChanged");
|
|
90
|
+
return Promise.resolve(row);
|
|
91
|
+
}
|
|
92
|
+
/* What the trash of a row ends in (row.delete()). */
|
|
93
|
+
delete_row() {
|
|
94
|
+
const row = this.rows.pop();
|
|
95
|
+
row.getElement().remove();
|
|
96
|
+
this.emit("dataChanged");
|
|
97
|
+
}
|
|
98
|
+
/* What an edited cell says. */
|
|
99
|
+
edit_cell() {
|
|
100
|
+
this.emit("dataChanged");
|
|
101
|
+
}
|
|
102
|
+
validate() {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
clearData() {
|
|
106
|
+
for(const row of this.rows) {
|
|
107
|
+
row.getElement().remove();
|
|
108
|
+
}
|
|
109
|
+
this.rows = [];
|
|
110
|
+
}
|
|
111
|
+
setLocale(name) {
|
|
112
|
+
this.locale = name;
|
|
113
|
+
}
|
|
114
|
+
destroy() {
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return {TabulatorFull: FakeTabulator, Tabulator: FakeTabulator};
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const {
|
|
121
|
+
SDATA_END, gclass_create,
|
|
122
|
+
gobj_start_up, gobj_create_yuno, gobj_create_pure_child,
|
|
123
|
+
gobj_start, gobj_send_event, gobj_read_attr,
|
|
124
|
+
set_log_callback,
|
|
125
|
+
} = await import("@yuneta/gobj-js");
|
|
126
|
+
const {register_c_yui_form} = await import("./c_yui_form.js");
|
|
127
|
+
const i18next = (await import("i18next")).default;
|
|
128
|
+
|
|
129
|
+
const logged = [];
|
|
130
|
+
let yuno = null;
|
|
131
|
+
|
|
132
|
+
/* A device's settings, cut down: a defaulted field, and the alarms as
|
|
133
|
+
* a table whose rows carry a nested table of triggers. */
|
|
134
|
+
const TEMPLATE = {
|
|
135
|
+
template_version: {
|
|
136
|
+
type: "integer",
|
|
137
|
+
default: 7,
|
|
138
|
+
flag: ["hidden", "persistent"]
|
|
139
|
+
},
|
|
140
|
+
level: {
|
|
141
|
+
type: "integer",
|
|
142
|
+
default: 3,
|
|
143
|
+
flag: ["writable", "persistent"]
|
|
144
|
+
},
|
|
145
|
+
alarms: {
|
|
146
|
+
type: "array",
|
|
147
|
+
flag: ["writable", "persistent", "table"],
|
|
148
|
+
table: {
|
|
149
|
+
name: {
|
|
150
|
+
type: "string",
|
|
151
|
+
header: "alarm name",
|
|
152
|
+
flag: ["writable", "persistent"]
|
|
153
|
+
},
|
|
154
|
+
triggers: {
|
|
155
|
+
type: "array",
|
|
156
|
+
flag: ["writable", "persistent", "table"],
|
|
157
|
+
table: {
|
|
158
|
+
measure: {
|
|
159
|
+
type: "string",
|
|
160
|
+
header: "measure",
|
|
161
|
+
flag: ["writable", "persistent"]
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/* `level` missing: the form opens changed. */
|
|
170
|
+
const LACKING = {
|
|
171
|
+
template_version: 7,
|
|
172
|
+
alarms: [{name: "hot", triggers: [{measure: "temperature"}]}]
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const COMPLETE = {
|
|
176
|
+
template_version: 7,
|
|
177
|
+
level: 3,
|
|
178
|
+
alarms: [{name: "hot", triggers: [{measure: "temperature"}]}]
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
beforeAll(async () => {
|
|
182
|
+
await i18next.init({
|
|
183
|
+
lng: "en",
|
|
184
|
+
resources: {
|
|
185
|
+
en: {translation: {"alarm name": "Alarm name", "measure": "Measure"}},
|
|
186
|
+
es: {translation: {"alarm name": "Nombre de la alarma", "measure": "Medida"}}
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
gobj_start_up(null, null, null, null, null, null, null);
|
|
190
|
+
set_log_callback((level, msg) => {
|
|
191
|
+
logged.push({level: String(level), msg: String(msg)});
|
|
192
|
+
});
|
|
193
|
+
gclass_create("C_TEST_TE_HOST", [], [["ST_IDLE", []]], {}, 0, [SDATA_END()], {}, 0, 0, 0, 0);
|
|
194
|
+
gclass_create(
|
|
195
|
+
"C_TEST_TE_APP",
|
|
196
|
+
[["EV_SAVE_RECORD", 0]],
|
|
197
|
+
[["ST_IDLE", [
|
|
198
|
+
["EV_SAVE_RECORD", () => 0, null],
|
|
199
|
+
]]],
|
|
200
|
+
{}, 0, [SDATA_END()], {}, 0, 0, 0, 0
|
|
201
|
+
);
|
|
202
|
+
register_c_yui_form();
|
|
203
|
+
yuno = gobj_create_yuno("form_table_edits_yuno", "C_TEST_TE_HOST", {});
|
|
204
|
+
gobj_start(yuno);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
beforeEach(async () => {
|
|
208
|
+
logged.length = 0;
|
|
209
|
+
await i18next.changeLanguage("en");
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
let seq = 0;
|
|
213
|
+
|
|
214
|
+
function build(record)
|
|
215
|
+
{
|
|
216
|
+
const first = tables.length;
|
|
217
|
+
const app = gobj_create_pure_child(`teapp${++seq}`, "C_TEST_TE_APP", {}, yuno);
|
|
218
|
+
gobj_start(app);
|
|
219
|
+
const form = gobj_create_pure_child(`teform${seq}`, "C_YUI_FORM", {
|
|
220
|
+
template: TEMPLATE,
|
|
221
|
+
record: record,
|
|
222
|
+
editable: true,
|
|
223
|
+
}, app);
|
|
224
|
+
gobj_start(form);
|
|
225
|
+
return {form, first};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* The tables the form built since `first`: the alarms table first,
|
|
229
|
+
* then a triggers table per alarm row drawn. */
|
|
230
|
+
function built(first)
|
|
231
|
+
{
|
|
232
|
+
return tables.slice(first);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/* The microtasks of addRow().then(): the nested rows load there. */
|
|
236
|
+
async function settle()
|
|
237
|
+
{
|
|
238
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function close_asks(form)
|
|
242
|
+
{
|
|
243
|
+
const kw = {abort_close: false};
|
|
244
|
+
gobj_send_event(form, "EV_WINDOW_TO_CLOSE", kw, form);
|
|
245
|
+
return kw.abort_close ? (kw.warning || "(no warning)") : "";
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function errors()
|
|
249
|
+
{
|
|
250
|
+
return logged.filter(l => /error/i.test(l.level) || /ERROR/.test(l.msg));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
describe("a form that opens changed hears what the reader does to its tables", () => {
|
|
254
|
+
test("it opens changed, not edited, and closes without asking", async () => {
|
|
255
|
+
const {form, first} = build(LACKING);
|
|
256
|
+
await settle();
|
|
257
|
+
expect(gobj_read_attr(form, "changes")).toBe(true);
|
|
258
|
+
expect(gobj_read_attr(form, "edited")).toBe(false);
|
|
259
|
+
expect(built(first).length).toBe(2); // the alarms, and the triggers of its row
|
|
260
|
+
expect(close_asks(form)).toBe("");
|
|
261
|
+
expect(errors()).toEqual([]);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test("an edited cell makes it ask", async () => {
|
|
265
|
+
const {form, first} = build(LACKING);
|
|
266
|
+
await settle();
|
|
267
|
+
built(first)[0].edit_cell();
|
|
268
|
+
expect(gobj_read_attr(form, "edited")).toBe(true);
|
|
269
|
+
expect(close_asks(form)).toBe("all changes will be lost");
|
|
270
|
+
expect(errors()).toEqual([]);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test("a deleted row makes it ask", async () => {
|
|
274
|
+
const {form, first} = build(LACKING);
|
|
275
|
+
await settle();
|
|
276
|
+
built(first)[0].delete_row();
|
|
277
|
+
expect(close_asks(form)).toBe("all changes will be lost");
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
test("a row the reader adds makes it ask; the rows the form loads do not", async () => {
|
|
281
|
+
const {form, first} = build(LACKING);
|
|
282
|
+
await settle();
|
|
283
|
+
const alarms = built(first)[0];
|
|
284
|
+
expect(alarms.rows.length).toBe(1); // loaded: no question
|
|
285
|
+
expect(close_asks(form)).toBe("");
|
|
286
|
+
|
|
287
|
+
/* What the table's add button sends: the reader's row. */
|
|
288
|
+
const tables_before = tables.length;
|
|
289
|
+
gobj_send_event(form, "EV_ADD_TABLE_ROW", {
|
|
290
|
+
table_id: alarms.element.yui_table_id,
|
|
291
|
+
record: {},
|
|
292
|
+
loading: false
|
|
293
|
+
}, form);
|
|
294
|
+
expect(alarms.rows.length).toBe(2);
|
|
295
|
+
expect(tables.length).toBe(tables_before + 1); // the new row's triggers table
|
|
296
|
+
expect(gobj_read_attr(form, "edited")).toBe(true);
|
|
297
|
+
expect(close_asks(form)).toBe("all changes will be lost");
|
|
298
|
+
expect(errors()).toEqual([]);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
test("an edit of a NESTED table makes it ask, also one drawn after the load", async () => {
|
|
302
|
+
const loaded = build(LACKING);
|
|
303
|
+
await settle();
|
|
304
|
+
const triggers = built(loaded.first)[1];
|
|
305
|
+
expect(triggers.rows.length).toBe(1); // loaded in its row: no question
|
|
306
|
+
expect(close_asks(loaded.form)).toBe("");
|
|
307
|
+
triggers.edit_cell();
|
|
308
|
+
expect(close_asks(loaded.form)).toBe("all changes will be lost");
|
|
309
|
+
|
|
310
|
+
/* An undo draws the rows again, and each row builds its triggers
|
|
311
|
+
* table then: a table that did not exist when the form loaded. */
|
|
312
|
+
const before_undo = tables.length;
|
|
313
|
+
gobj_send_event(loaded.form, "EV_UNDO_RECORD", {}, loaded.form);
|
|
314
|
+
await settle();
|
|
315
|
+
expect(close_asks(loaded.form)).toBe("");
|
|
316
|
+
const redrawn = tables.slice(before_undo);
|
|
317
|
+
expect(redrawn.length).toBe(1);
|
|
318
|
+
expect(redrawn[0].rows.length).toBe(1);
|
|
319
|
+
redrawn[0].edit_cell();
|
|
320
|
+
expect(close_asks(loaded.form)).toBe("all changes will be lost");
|
|
321
|
+
expect(errors()).toEqual([]);
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
test("a record loaded LATER that lacks a field (a reload, a refused save) still hears them", async () => {
|
|
325
|
+
const {form, first} = build(COMPLETE);
|
|
326
|
+
await settle();
|
|
327
|
+
expect(gobj_read_attr(form, "changes")).toBe(false);
|
|
328
|
+
gobj_send_event(form, "EV_LOAD_RECORD", LACKING, form);
|
|
329
|
+
await settle();
|
|
330
|
+
expect(gobj_read_attr(form, "changes")).toBe(true);
|
|
331
|
+
expect(gobj_read_attr(form, "edited")).toBe(false);
|
|
332
|
+
expect(close_asks(form)).toBe("");
|
|
333
|
+
built(first)[0].edit_cell();
|
|
334
|
+
expect(close_asks(form)).toBe("all changes will be lost");
|
|
335
|
+
expect(errors()).toEqual([]);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
test("a table built after the load (tableBuilt) takes its rows without a question", async () => {
|
|
339
|
+
const {form, first} = build(COMPLETE);
|
|
340
|
+
await settle();
|
|
341
|
+
const alarms = built(first)[0];
|
|
342
|
+
/* A table not built yet: no rows, and the ones it is given wait
|
|
343
|
+
* for its `tableBuilt`. */
|
|
344
|
+
alarms.clearData();
|
|
345
|
+
alarms.initialized = false;
|
|
346
|
+
gobj_send_event(form, "EV_LOAD_RECORD", LACKING, form);
|
|
347
|
+
await settle();
|
|
348
|
+
expect(alarms.rows.length).toBe(0);
|
|
349
|
+
/* Tabulator builds the table now, long after the load returned. */
|
|
350
|
+
alarms.initialized = true;
|
|
351
|
+
alarms.emit("tableBuilt");
|
|
352
|
+
await settle();
|
|
353
|
+
expect(alarms.rows.length).toBe(1);
|
|
354
|
+
expect(gobj_read_attr(form, "edited")).toBe(false);
|
|
355
|
+
expect(close_asks(form)).toBe("");
|
|
356
|
+
expect(errors()).toEqual([]);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
test("with a complete record it asks as before", async () => {
|
|
360
|
+
/* The waits are the 200 ms after which 7.26.40 wired the tables
|
|
361
|
+
* again: with them this case passes on the code before too. */
|
|
362
|
+
const wait = () => new Promise(resolve => setTimeout(resolve, 260));
|
|
363
|
+
const {form, first} = build(COMPLETE);
|
|
364
|
+
await wait();
|
|
365
|
+
expect(gobj_read_attr(form, "changes")).toBe(false);
|
|
366
|
+
built(first)[1].edit_cell();
|
|
367
|
+
expect(close_asks(form)).toBe("all changes will be lost");
|
|
368
|
+
gobj_send_event(form, "EV_SAVE_RECORD", {}, form);
|
|
369
|
+
expect(close_asks(form)).toBe("");
|
|
370
|
+
await wait();
|
|
371
|
+
built(first)[0].edit_cell();
|
|
372
|
+
expect(close_asks(form)).toBe("all changes will be lost");
|
|
373
|
+
expect(errors()).toEqual([]);
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
describe("the column titles of the tables change language", () => {
|
|
378
|
+
test("each table, nested ones too, is given its titles in the new language", async () => {
|
|
379
|
+
const {form, first} = build(COMPLETE);
|
|
380
|
+
await settle();
|
|
381
|
+
const [alarms, triggers] = built(first);
|
|
382
|
+
|
|
383
|
+
await i18next.changeLanguage("es");
|
|
384
|
+
gobj_send_event(form, "EV_LANGUAGE_CHANGED", {}, form);
|
|
385
|
+
|
|
386
|
+
expect(alarms.locale).toBeTruthy();
|
|
387
|
+
expect(alarms.installed[alarms.locale].columns).toEqual({
|
|
388
|
+
name: "Nombre de la alarma",
|
|
389
|
+
triggers: "triggers"
|
|
390
|
+
});
|
|
391
|
+
expect(triggers.installed[triggers.locale].columns).toEqual({measure: "Medida"});
|
|
392
|
+
/* No column was rebuilt to get there: the rows (and the nested
|
|
393
|
+
* table) are the ones loaded. */
|
|
394
|
+
expect(alarms.rows.length).toBe(1);
|
|
395
|
+
expect(triggers.rows.length).toBe(1);
|
|
396
|
+
expect(errors()).toEqual([]);
|
|
397
|
+
});
|
|
398
|
+
});
|
|
@@ -5,11 +5,13 @@
|
|
|
5
5
|
* a save or an undo -- which is what a dialog does: it closes the
|
|
6
6
|
* moment the record is saved.
|
|
7
7
|
*
|
|
8
|
-
* set_changed_stated(false) re-
|
|
9
|
-
* a 200 ms timer. Since 7.26.7 destroy_ui() destroys the widgets
|
|
10
|
-
* nulls `$table.tabulator`, and the timer, left running, called
|
|
11
|
-
* `.on()` on that null: an uncaught TypeError.
|
|
12
|
-
*
|
|
8
|
+
* set_changed_stated(false) used to re-wire the tables' `dataChanged`
|
|
9
|
+
* from a 200 ms timer. Since 7.26.7 destroy_ui() destroys the widgets
|
|
10
|
+
* and nulls `$table.tabulator`, and the timer, left running, called
|
|
11
|
+
* `.on()` on that null: an uncaught TypeError. Since 7.26.41 there is
|
|
12
|
+
* no timer: each table is wired once, when it is built, and stays
|
|
13
|
+
* wired (form_table_edits_ask.wiring.test.js says why). These cases
|
|
14
|
+
* keep guarding the destroy right after a save or an undo.
|
|
13
15
|
*
|
|
14
16
|
* Copyright (c) 2026, ArtGins.
|
|
15
17
|
* All Rights Reserved.
|
|
@@ -117,7 +119,7 @@ function build(rows)
|
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
describe("a form with a table, destroyed right after", () => {
|
|
120
|
-
test("an undo
|
|
122
|
+
test("an undo, then the destroy: nothing left behind throws", async () => {
|
|
121
123
|
const {form} = build();
|
|
122
124
|
expect(tables.length).toBeGreaterThan(0);
|
|
123
125
|
const errors = [];
|
|
@@ -135,7 +137,7 @@ describe("a form with a table, destroyed right after", () => {
|
|
|
135
137
|
expect(errors).toEqual([]);
|
|
136
138
|
});
|
|
137
139
|
|
|
138
|
-
test("a live form
|
|
140
|
+
test("a live form's table stays wired, once", async () => {
|
|
139
141
|
const {form} = build();
|
|
140
142
|
const table = tables[tables.length - 1];
|
|
141
143
|
gobj_send_event(form, "EV_UNDO_RECORD", {}, form);
|
|
@@ -14,6 +14,12 @@
|
|
|
14
14
|
* new Tabulator($el, {..., ...yui_tabulator_lang(t)});
|
|
15
15
|
* yui_tabulator_relocalize(table, t); // on a language change
|
|
16
16
|
*
|
|
17
|
+
* A view whose COLUMN TITLES change language hands them too, in the
|
|
18
|
+
* new language, by field -- Tabulator binds each column's title to its
|
|
19
|
+
* locale, so they change with it and no column is rebuilt:
|
|
20
|
+
*
|
|
21
|
+
* yui_tabulator_relocalize(table, t, {device_id: t("device id")});
|
|
22
|
+
*
|
|
17
23
|
* Every key carries an English `defaultValue`, so an app that does not
|
|
18
24
|
* define it renders exactly what Tabulator rendered before — the
|
|
19
25
|
* keys are an OPPORTUNITY to translate, never a requirement.
|
|
@@ -236,7 +242,18 @@ function yui_tabulator_name_editor(column_def, name_of)
|
|
|
236
242
|
};
|
|
237
243
|
}
|
|
238
244
|
|
|
239
|
-
|
|
245
|
+
/***************************************************************
|
|
246
|
+
* yui_tabulator_relocalize() -- see "Put an EXISTING table in the
|
|
247
|
+
* current language" above.
|
|
248
|
+
*
|
|
249
|
+
* `titles`, optional: {field: title}, the titles of the columns in
|
|
250
|
+
* the new language, already translated. They go in the locale
|
|
251
|
+
* (`columns`), where Tabulator binds each column's title: setLocale()
|
|
252
|
+
* draws them again without rebuilding a column -- updateDefinition()
|
|
253
|
+
* re-renders every row, and a row whose formatter builds something (a
|
|
254
|
+
* nested table) builds it anew. A field left out keeps its title.
|
|
255
|
+
***************************************************************/
|
|
256
|
+
function yui_tabulator_relocalize(table, t, titles)
|
|
240
257
|
{
|
|
241
258
|
if(!table) {
|
|
242
259
|
return;
|
|
@@ -244,6 +261,9 @@ function yui_tabulator_relocalize(table, t)
|
|
|
244
261
|
try {
|
|
245
262
|
let name = next_lang_name();
|
|
246
263
|
let strings = tabulator_strings(t);
|
|
264
|
+
if(titles && typeof titles === "object") {
|
|
265
|
+
strings.columns = Object.assign({}, titles);
|
|
266
|
+
}
|
|
247
267
|
|
|
248
268
|
/* Tabulator DEEP-CLONES options.langs into its localize module when
|
|
249
269
|
* the table is built, and never looks at the option again: writing a
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
yui_tabulator_name_filters,
|
|
16
16
|
yui_tabulator_name_row_selects,
|
|
17
17
|
yui_tabulator_name_editor,
|
|
18
|
+
yui_tabulator_relocalize,
|
|
18
19
|
} from "./yui_tabulator_i18n.js";
|
|
19
20
|
|
|
20
21
|
/* An element double: attributes, and the one child a selector finds. */
|
|
@@ -125,3 +126,41 @@ describe("yui_tabulator_name_editor", () => {
|
|
|
125
126
|
expect(def.editorParams({})).toEqual({a: 1});
|
|
126
127
|
});
|
|
127
128
|
});
|
|
129
|
+
|
|
130
|
+
describe("yui_tabulator_relocalize", () => {
|
|
131
|
+
/* A table double: what the localize module is given, and the
|
|
132
|
+
* locale it is set to. */
|
|
133
|
+
function table()
|
|
134
|
+
{
|
|
135
|
+
const installed = {};
|
|
136
|
+
const tb = {
|
|
137
|
+
options: {},
|
|
138
|
+
installed,
|
|
139
|
+
locale: null,
|
|
140
|
+
modules: {localize: {installLang: (name, strings) => {
|
|
141
|
+
installed[name] = strings;
|
|
142
|
+
}}},
|
|
143
|
+
setLocale: (name) => {
|
|
144
|
+
tb.locale = name;
|
|
145
|
+
},
|
|
146
|
+
getColumns: () => [],
|
|
147
|
+
element: element(null),
|
|
148
|
+
};
|
|
149
|
+
return tb;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
it("hands the column titles to the locale, where Tabulator binds them", () => {
|
|
153
|
+
const tb = table();
|
|
154
|
+
yui_tabulator_relocalize(tb, t, {device_id: "Id dispositivo", value: "Valor"});
|
|
155
|
+
expect(tb.locale).toBeTruthy();
|
|
156
|
+
expect(tb.installed[tb.locale].columns).toEqual(
|
|
157
|
+
{device_id: "Id dispositivo", value: "Valor"});
|
|
158
|
+
expect(tb.installed[tb.locale].pagination).toBeTruthy();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("without titles, the locale carries none, as before", () => {
|
|
162
|
+
const tb = table();
|
|
163
|
+
yui_tabulator_relocalize(tb, t);
|
|
164
|
+
expect(tb.installed[tb.locale].columns).toBe(undefined);
|
|
165
|
+
});
|
|
166
|
+
});
|
package/src/yui_text.js
CHANGED
|
@@ -36,9 +36,19 @@ export function text_node(value)
|
|
|
36
36
|
* Tabulator puts a STRING title in with innerHTML, so a title taken
|
|
37
37
|
* from a schema (`header`, which the schema editor writes) is markup
|
|
38
38
|
* unless the column carries a formatter that returns a Node.
|
|
39
|
+
*
|
|
40
|
+
* And it APPENDS that Node to the title's holder, which it never
|
|
41
|
+
* empties -- while it runs the formatter again on every setLocale()
|
|
42
|
+
* (yui_tabulator_relocalize()). Until 7.26.40 each change of language
|
|
43
|
+
* added one more copy of every title ("Id dispositivoId dispositivo").
|
|
44
|
+
* The holder (the mock cell's element) is emptied first.
|
|
39
45
|
************************************************************/
|
|
40
46
|
export function text_title_formatter(cell)
|
|
41
47
|
{
|
|
48
|
+
const $holder = (typeof cell.getElement === "function")? cell.getElement() : null;
|
|
49
|
+
if($holder) {
|
|
50
|
+
$holder.textContent = "";
|
|
51
|
+
}
|
|
42
52
|
return text_node(cell.getValue());
|
|
43
53
|
}
|
|
44
54
|
|
package/src/yui_text.test.js
CHANGED
|
@@ -48,6 +48,19 @@ describe("text_node()", () => {
|
|
|
48
48
|
expect($title.textContent).toBe(EVIL);
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
+
test("a title formatted again (every setLocale) is not appended to the old one", () => {
|
|
52
|
+
/* What Tabulator does: it appends the Node into the title's
|
|
53
|
+
* holder, and on a change of locale runs the formatter again
|
|
54
|
+
* on the same holder. */
|
|
55
|
+
const $holder = document.createElement("div");
|
|
56
|
+
const cell = (title) => ({getValue: () => title, getElement: () => $holder});
|
|
57
|
+
$holder.appendChild(text_title_formatter(cell("Id dispositivo")));
|
|
58
|
+
$holder.appendChild(text_title_formatter(cell("Device id")));
|
|
59
|
+
$holder.appendChild(text_title_formatter(cell("Id dispositivo")));
|
|
60
|
+
expect($holder.textContent).toBe("Id dispositivo");
|
|
61
|
+
expect($holder.childNodes.length).toBe(1);
|
|
62
|
+
});
|
|
63
|
+
|
|
51
64
|
test("the accordion nav shows a section named by a backend as text", async () => {
|
|
52
65
|
const i18next = (await import("i18next")).default;
|
|
53
66
|
await i18next.init({lng: "en", resources: {}});
|