@yuneta/gobj-ui 7.26.24 → 7.26.26
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 +49 -3
- package/dist/gobj-ui.cjs.js +77 -36
- package/dist/gobj-ui.es.js +77 -36
- package/package.json +3 -2
- package/scripts/validate-locales.mjs +667 -0
- package/src/c_g6_nodes_tree.js +37 -15
- package/src/c_yui_schema_editor.js +7 -1
- package/src/schema_editor_reload.wiring.test.js +33 -0
- package/src/schema_import.js +7 -3
- package/src/schema_import.test.js +4 -4
- package/src/yui_file_field.js +2 -2
package/src/c_g6_nodes_tree.js
CHANGED
|
@@ -5010,9 +5010,16 @@ function create_form_button_row(parent, buttons)
|
|
|
5010
5010
|
{
|
|
5011
5011
|
let btnRow = document.createElement('div');
|
|
5012
5012
|
btnRow.style.cssText = 'display:flex;gap:8px;';
|
|
5013
|
-
for(let {
|
|
5013
|
+
for(let {i18n, style, onClick} of buttons) {
|
|
5014
|
+
/* `i18n` is the key (named so that validate-locales sees a literal
|
|
5015
|
+
* one): the button carries it, and its name */
|
|
5014
5016
|
let btn = document.createElement('button');
|
|
5015
|
-
btn.textContent = t(
|
|
5017
|
+
btn.textContent = t(i18n);
|
|
5018
|
+
btn.setAttribute('data-i18n', i18n);
|
|
5019
|
+
btn.setAttribute('title', t(i18n));
|
|
5020
|
+
btn.setAttribute('data-i18n-title', i18n);
|
|
5021
|
+
btn.setAttribute('aria-label', t(i18n));
|
|
5022
|
+
btn.setAttribute('data-i18n-aria-label', i18n);
|
|
5016
5023
|
btn.style.cssText = style + touch_control_css();
|
|
5017
5024
|
btn.addEventListener('click', (e) => {
|
|
5018
5025
|
e.stopPropagation();
|
|
@@ -5059,12 +5066,12 @@ function show_confirm_popover(gobj, target_el, message, confirm_text, confirm_co
|
|
|
5059
5066
|
|
|
5060
5067
|
create_form_button_row(popover, [
|
|
5061
5068
|
{
|
|
5062
|
-
|
|
5069
|
+
i18n: 'cancel',
|
|
5063
5070
|
style: BTN_STYLE_CANCEL,
|
|
5064
5071
|
onClick: () => hide_overlay(gobj, priv_key),
|
|
5065
5072
|
},
|
|
5066
5073
|
{
|
|
5067
|
-
|
|
5074
|
+
i18n: confirm_text,
|
|
5068
5075
|
style: 'flex:1;padding:6px;background:' + confirm_color + ';color:#fff;border:none;' +
|
|
5069
5076
|
'border-radius:4px;cursor:pointer;font-size:13px;font-weight:500;',
|
|
5070
5077
|
onClick: onConfirm,
|
|
@@ -6148,14 +6155,14 @@ function show_port_popover(gobj)
|
|
|
6148
6155
|
|
|
6149
6156
|
create_form_button_row(popover, [
|
|
6150
6157
|
{
|
|
6151
|
-
|
|
6158
|
+
i18n: 'cancel',
|
|
6152
6159
|
style: BTN_STYLE_CANCEL,
|
|
6153
6160
|
onClick: () => {
|
|
6154
6161
|
hide_port_popover(gobj); /* which undoes the preview */
|
|
6155
6162
|
},
|
|
6156
6163
|
},
|
|
6157
6164
|
{
|
|
6158
|
-
|
|
6165
|
+
i18n: 'apply',
|
|
6159
6166
|
style: 'flex:1;padding:6px;background:#1890ff;color:#fff;border:none;' +
|
|
6160
6167
|
'border-radius:4px;cursor:pointer;font-size:13px;font-weight:500;',
|
|
6161
6168
|
onClick: () => {
|
|
@@ -7100,14 +7107,14 @@ function show_edge_popover(gobj)
|
|
|
7100
7107
|
|
|
7101
7108
|
create_form_button_row(popover, [
|
|
7102
7109
|
{
|
|
7103
|
-
|
|
7110
|
+
i18n: 'cancel',
|
|
7104
7111
|
style: BTN_STYLE_CANCEL,
|
|
7105
7112
|
onClick: () => {
|
|
7106
7113
|
deselect_edge(gobj); /* which undoes the preview */
|
|
7107
7114
|
},
|
|
7108
7115
|
},
|
|
7109
7116
|
{
|
|
7110
|
-
|
|
7117
|
+
i18n: 'apply',
|
|
7111
7118
|
style: 'flex:1;padding:6px;background:#52c41a;color:#fff;border:none;' +
|
|
7112
7119
|
'border-radius:4px;cursor:pointer;font-size:13px;font-weight:500;',
|
|
7113
7120
|
onClick: () => {
|
|
@@ -7349,14 +7356,14 @@ function show_node_popover(gobj)
|
|
|
7349
7356
|
|
|
7350
7357
|
create_form_button_row(popover, [
|
|
7351
7358
|
{
|
|
7352
|
-
|
|
7359
|
+
i18n: 'cancel',
|
|
7353
7360
|
style: BTN_STYLE_CANCEL,
|
|
7354
7361
|
onClick: () => {
|
|
7355
7362
|
hide_node_popover(gobj); /* which undoes the preview */
|
|
7356
7363
|
},
|
|
7357
7364
|
},
|
|
7358
7365
|
{
|
|
7359
|
-
|
|
7366
|
+
i18n: 'apply',
|
|
7360
7367
|
style: 'flex:1;padding:6px;background:#1890ff;color:#fff;border:none;' +
|
|
7361
7368
|
'border-radius:4px;cursor:pointer;font-size:13px;font-weight:500;',
|
|
7362
7369
|
onClick: () => {
|
|
@@ -8431,15 +8438,24 @@ function show_create_popover(gobj)
|
|
|
8431
8438
|
let titleEl = document.createElement('div');
|
|
8432
8439
|
titleEl.style.cssText = 'margin-bottom:10px;font-weight:600;font-size:14px;';
|
|
8433
8440
|
titleEl.textContent = t('create node');
|
|
8441
|
+
titleEl.setAttribute('data-i18n', 'create node');
|
|
8434
8442
|
popover.appendChild(titleEl);
|
|
8435
8443
|
|
|
8436
|
-
// Topic selector
|
|
8444
|
+
// Topic selector: the word carries its key, the colon is not translated
|
|
8437
8445
|
let topicLabel = document.createElement('div');
|
|
8438
8446
|
topicLabel.style.cssText = 'margin-bottom:4px;font-weight:500;';
|
|
8439
|
-
|
|
8447
|
+
let topicWord = document.createElement('span');
|
|
8448
|
+
topicWord.textContent = t('topic');
|
|
8449
|
+
topicWord.setAttribute('data-i18n', 'topic');
|
|
8450
|
+
topicLabel.appendChild(topicWord);
|
|
8451
|
+
topicLabel.appendChild(document.createTextNode(':'));
|
|
8440
8452
|
popover.appendChild(topicLabel);
|
|
8441
8453
|
|
|
8442
8454
|
let topicSelect = document.createElement('select');
|
|
8455
|
+
topicSelect.setAttribute('title', t('topic'));
|
|
8456
|
+
topicSelect.setAttribute('data-i18n-title', 'topic');
|
|
8457
|
+
topicSelect.setAttribute('aria-label', t('topic'));
|
|
8458
|
+
topicSelect.setAttribute('data-i18n-aria-label', 'topic');
|
|
8443
8459
|
topicSelect.style.cssText =
|
|
8444
8460
|
'width:100%;padding:6px;border:1px solid #d9d9d9;border-radius:4px;' +
|
|
8445
8461
|
'font-size:13px;margin-bottom:8px;' + touch_control_css();
|
|
@@ -8483,6 +8499,11 @@ function show_create_popover(gobj)
|
|
|
8483
8499
|
let idInput = document.createElement('input');
|
|
8484
8500
|
idInput.type = 'text';
|
|
8485
8501
|
idInput.placeholder = t('node id');
|
|
8502
|
+
idInput.setAttribute('data-i18n-placeholder', 'node id');
|
|
8503
|
+
idInput.setAttribute('title', t('node id'));
|
|
8504
|
+
idInput.setAttribute('data-i18n-title', 'node id');
|
|
8505
|
+
idInput.setAttribute('aria-label', t('node id'));
|
|
8506
|
+
idInput.setAttribute('data-i18n-aria-label', 'node id');
|
|
8486
8507
|
idInput.style.cssText =
|
|
8487
8508
|
'width:100%;padding:6px;border:1px solid #d9d9d9;border-radius:4px;' +
|
|
8488
8509
|
'font-size:13px;margin-bottom:10px;box-sizing:border-box;' +
|
|
@@ -8499,18 +8520,19 @@ function show_create_popover(gobj)
|
|
|
8499
8520
|
let createBtn; // need reference for Enter key handler
|
|
8500
8521
|
create_form_button_row(popover, [
|
|
8501
8522
|
{
|
|
8502
|
-
|
|
8523
|
+
i18n: 'cancel',
|
|
8503
8524
|
style: BTN_STYLE_CANCEL,
|
|
8504
8525
|
onClick: () => hide_create_popover(gobj),
|
|
8505
8526
|
},
|
|
8506
8527
|
{
|
|
8507
|
-
|
|
8528
|
+
i18n: 'create',
|
|
8508
8529
|
style: 'flex:1;padding:6px;background:#1890ff;color:#fff;border:none;' +
|
|
8509
8530
|
'border-radius:4px;cursor:pointer;font-size:13px;font-weight:500;',
|
|
8510
8531
|
onClick: () => {
|
|
8511
8532
|
let node_id = idInput.value.trim();
|
|
8512
8533
|
if(!node_id) {
|
|
8513
|
-
errorEl.textContent = t('node id
|
|
8534
|
+
errorEl.textContent = t('node id required');
|
|
8535
|
+
errorEl.setAttribute('data-i18n', 'node id required');
|
|
8514
8536
|
errorEl.style.display = 'block';
|
|
8515
8537
|
idInput.focus();
|
|
8516
8538
|
return;
|
|
@@ -2689,9 +2689,15 @@ function render_import_plan(gobj, plan)
|
|
|
2689
2689
|
let where = conflict.col
|
|
2690
2690
|
? `${conflict.topic}.${conflict.col}`
|
|
2691
2691
|
: conflict.topic;
|
|
2692
|
+
/* ...and, for a name of a hook map, the hook column it
|
|
2693
|
+
* is written in: where to fix it. */
|
|
2692
2694
|
return ["p", {class: "SCHEMA_IMPORT_CONFLICT is-size-7"}, [
|
|
2693
2695
|
["span", {i18n: conflict.code}, t(conflict.code)],
|
|
2694
|
-
where ? ["code", {class: "SCHEMA_IMPORT_CONFLICT_NAME ml-2"}, text_node(where)] : null
|
|
2696
|
+
where ? ["code", {class: "SCHEMA_IMPORT_CONFLICT_NAME ml-2"}, text_node(where)] : null,
|
|
2697
|
+
conflict.hook ? ["span", {class: "SCHEMA_IMPORT_CONFLICT_HOOK ml-2",
|
|
2698
|
+
i18n: "in the hook map of"}, t("in the hook map of")] : null,
|
|
2699
|
+
conflict.hook ? ["code", {class: "SCHEMA_IMPORT_CONFLICT_HOOK_NAME ml-1"},
|
|
2700
|
+
text_node(conflict.hook)] : null
|
|
2695
2701
|
].filter(Boolean)];
|
|
2696
2702
|
})]
|
|
2697
2703
|
));
|
|
@@ -1056,6 +1056,39 @@ describe("the import plan", () => {
|
|
|
1056
1056
|
});
|
|
1057
1057
|
});
|
|
1058
1058
|
|
|
1059
|
+
describe("the import panel says what each conflict is about (REV-CLOUD V17.3.5)", () => {
|
|
1060
|
+
|
|
1061
|
+
test("the bad name of a hook map is printed, and the hook column it is written in", () => {
|
|
1062
|
+
const {editor, host} = build("i3", "db");
|
|
1063
|
+
gobj_send_event(editor, "EV_IMPORT", {}, host);
|
|
1064
|
+
const dialog = modals[modals.length - 1];
|
|
1065
|
+
gobj_send_event(editor, "EV_PREVIEW_IMPORT", {prune: false, model_gen: editor.priv.model_gen,
|
|
1066
|
+
text: JSON.stringify({id: "db", topics: [{id: "p", pkey: "id", cols: {
|
|
1067
|
+
"id": {id: "id", type: "string"},
|
|
1068
|
+
"kids": {id: "kids", type: "dict", flag: ["hook"], hook: {"a/b": "x`y"}},
|
|
1069
|
+
"good": {id: "go`od", type: "string"}
|
|
1070
|
+
}}]})}, editor);
|
|
1071
|
+
|
|
1072
|
+
const lines = [...dialog.$content.querySelectorAll(".SCHEMA_IMPORT_CONFLICT")];
|
|
1073
|
+
const said = lines.map((p) => {
|
|
1074
|
+
const name = p.querySelector(".SCHEMA_IMPORT_CONFLICT_NAME");
|
|
1075
|
+
const hook = p.querySelector(".SCHEMA_IMPORT_CONFLICT_HOOK_NAME");
|
|
1076
|
+
return `${p.querySelector("span").textContent}|${name ? name.textContent : ""}|${hook ? hook.textContent : ""}`;
|
|
1077
|
+
});
|
|
1078
|
+
expect(said).toContain("topic name breaks the name rule|a/b|p.kids");
|
|
1079
|
+
expect(said).toContain("column name breaks the name rule|a/b.x`y|p.kids");
|
|
1080
|
+
/* Not from a hook map: no hook column said */
|
|
1081
|
+
expect(said).toContain("column name breaks the name rule|p.go`od|");
|
|
1082
|
+
/* The names are DATA, text and not translated: no key on them */
|
|
1083
|
+
for(const code of dialog.$content.querySelectorAll(".SCHEMA_IMPORT_CONFLICT_NAME, .SCHEMA_IMPORT_CONFLICT_HOOK_NAME")) {
|
|
1084
|
+
expect(code.getAttribute("i18n")).toBe(null);
|
|
1085
|
+
expect(code.getAttribute("data-i18n")).toBe(null);
|
|
1086
|
+
}
|
|
1087
|
+
expect(dialog.$content.querySelector(".SCHEMA_IMPORT_RUN").disabled).toBe(true);
|
|
1088
|
+
expect(errors()).toEqual([]);
|
|
1089
|
+
});
|
|
1090
|
+
});
|
|
1091
|
+
|
|
1059
1092
|
describe("a confirmation answered where no treedb is open", () => {
|
|
1060
1093
|
|
|
1061
1094
|
test("after a reload that landed on zero treedbs: refused as stale, not NOT DEFINED", () => {
|
package/src/schema_import.js
CHANGED
|
@@ -245,16 +245,20 @@ function plan_import(treedb, incoming, options)
|
|
|
245
245
|
* the treedb refuses too, even when no column declares them. */
|
|
246
246
|
let hook = (in_col.col || {}).hook;
|
|
247
247
|
if(hook && typeof hook === "object" && !Array.isArray(hook)) {
|
|
248
|
+
/* The parent's hook column the bad name comes from: a
|
|
249
|
+
* conflict that said only `child.fkey` did not say where
|
|
250
|
+
* to fix it (REV-CLOUD V17.3.5). */
|
|
251
|
+
let from_hook = `${name || ""}.${in_col.name}`;
|
|
248
252
|
for(let [child_topic, fkey_col] of Object.entries(hook)) {
|
|
249
253
|
/* The conflict names the BAD name (the child topic, its
|
|
250
|
-
* fkey),
|
|
254
|
+
* fkey), and `hook` the column that maps to it. */
|
|
251
255
|
if(topic_name_breaks_the_rule(child_topic)) {
|
|
252
256
|
conflicts.push({code: "topic name breaks the name rule",
|
|
253
|
-
topic: child_topic, col: ""});
|
|
257
|
+
topic: child_topic, col: "", hook: from_hook});
|
|
254
258
|
}
|
|
255
259
|
if(col_name_breaks_the_rule(fkey_col)) {
|
|
256
260
|
conflicts.push({code: "column name breaks the name rule",
|
|
257
|
-
topic: child_topic, col: String(fkey_col)});
|
|
261
|
+
topic: child_topic, col: String(fkey_col), hook: from_hook});
|
|
258
262
|
}
|
|
259
263
|
}
|
|
260
264
|
}
|
|
@@ -323,9 +323,9 @@ describe("a conflict names the bad name (REV-CLOUD V16.5)", () => {
|
|
|
323
323
|
}}]
|
|
324
324
|
});
|
|
325
325
|
expect(plan.writes).toEqual([]);
|
|
326
|
-
let named = plan.conflicts.map((c) => `${c.code}|${c.topic}|${c.col}`);
|
|
327
|
-
expect(named).toContain("topic name breaks the name rule|a/b
|
|
328
|
-
expect(named).toContain("column name breaks the name rule|a/b|x`y");
|
|
329
|
-
expect(named).toContain("column name breaks the name rule|p|go`od");
|
|
326
|
+
let named = plan.conflicts.map((c) => `${c.code}|${c.topic}|${c.col}|${c.hook || ""}`);
|
|
327
|
+
expect(named).toContain("topic name breaks the name rule|a/b||p.kids");
|
|
328
|
+
expect(named).toContain("column name breaks the name rule|a/b|x`y|p.kids");
|
|
329
|
+
expect(named).toContain("column name breaks the name rule|p|go`od|");
|
|
330
330
|
});
|
|
331
331
|
});
|
package/src/yui_file_field.js
CHANGED
|
@@ -328,11 +328,11 @@ function yui_file_control(gobj, {name, value, readonly, accept, on_pick})
|
|
|
328
328
|
if(picked) {
|
|
329
329
|
$state.classList.add("is-picked");
|
|
330
330
|
$state.setAttribute("data-i18n", "not saved yet");
|
|
331
|
-
$state.textContent = "not saved yet";
|
|
331
|
+
$state.textContent = t("not saved yet");
|
|
332
332
|
} else if(!kept) {
|
|
333
333
|
$state.classList.add("is-empty");
|
|
334
334
|
$state.setAttribute("data-i18n", "no file");
|
|
335
|
-
$state.textContent = "no file";
|
|
335
|
+
$state.textContent = t("no file");
|
|
336
336
|
}
|
|
337
337
|
|
|
338
338
|
$clear.hidden = readonly || (!picked && !kept);
|