@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 CHANGED
@@ -3589,9 +3589,55 @@ makes Tabulator re-run a title formatter on the EXISTING header cell, which
3589
3589
  **A missing key is invisible:** i18next answers an unknown key **with the key
3590
3590
  itself**, so it renders (lower-case English) and simply never changes language.
3591
3591
  A **duplicate** key in a locale file is silent too — an object literal keeps the
3592
- last one. Both are caught by the apps' `scripts/validate-locales.mjs`, which
3593
- also scans the gobj-ui modules the app mounts: **the library translates through
3594
- the APP's i18next**, so every key it asks for must be defined by the app.
3592
+ last one. Both are caught by `validate-locales`, which this package publishes
3593
+ (since `7.26.25`) and every app runs from its `prebuild`, from its own
3594
+ directory:
3595
+
3596
+ ```json
3597
+ "scripts": {
3598
+ "validate-locales": "node node_modules/@yuneta/gobj-ui/scripts/validate-locales.mjs",
3599
+ "prebuild": "npm run validate-locales && npm run check-markup"
3600
+ }
3601
+ ```
3602
+
3603
+ It also scans the gobj-ui modules the app mounts -- deep, through the barrel,
3604
+ or by a dynamic `import()`: **the library translates through the APP's
3605
+ i18next**, so every key it asks for must be defined by the app. The library it
3606
+ scans is the one it ships in, the package the app builds with. The app's own
3607
+ source is read recursively. Code and catalogs shared by several apps are named
3608
+ with `--src <dir>` (repeatable) and `--locales <dir>` (yunovatios:
3609
+ `--src ../gui-common/src --locales ../gui-common/src/locales`); `--config`
3610
+ names a shell declaration other than `src/app_config.json`.
3611
+
3612
+ What is particular to an app goes in ITS `package.json`, not in the script:
3613
+ properties whose value is a key, and properties whose value is a list of keys
3614
+ (yunovatios hands its orchestrator `app_title: "central"` and
3615
+ `welcome_features: [...]`):
3616
+
3617
+ ```json
3618
+ "validate-locales": {
3619
+ "attr_keys": ["app_title", "app_subtitle", "welcome_title", "welcome_lead"],
3620
+ "array_keys": ["welcome_features"]
3621
+ }
3622
+ ```
3623
+
3624
+ Plurals are compared by language (since `7.26.26`): `songs_one` /
3625
+ `songs_few` / `songs_many` / `songs_other` are one key, and each locale must
3626
+ carry the categories of ITS language (`Intl.PluralRules`: English `one`,
3627
+ `other`; Russian `one`, `few`, `many`, `other`; Chinese `other`). The key with
3628
+ no suffix serves `other` and `many`, as i18next falls back to it; `zero`,
3629
+ `one`, `two` and `few` need their own form. A value of the shell declaration
3630
+ that is not lower-case ASCII (an endonym, `"中文"`, in a language menu) is a
3631
+ literal, not a key.
3632
+
3633
+ Up to `7.26.24` each app carried its own copy, and the copies had drifted into
3634
+ three families: only one read the codes of the schema editor, only the other
3635
+ two the barrel imports, and two read `t('topic') + ':'` as a key built by
3636
+ concatenation and skipped it -- so the gobj tree's *"topic:"* went untranslated
3637
+ in wattyzer and yunovatios. No app keeps a copy now, but one: `test-app`, this
3638
+ library's own demo, whose catalog has English as the source (a key IS the
3639
+ English text, Title Case included) and so follows other rules, in its own
3640
+ `test-app/scripts/validate-locales.mjs`.
3595
3641
 
3596
3642
  ### Dates: never hand-roll them again
3597
3643
 
@@ -7181,11 +7181,11 @@ function yui_file_control(gobj, { name, value, readonly, accept, on_pick }) {
7181
7181
  if (picked) {
7182
7182
  $state.classList.add("is-picked");
7183
7183
  $state.setAttribute("data-i18n", "not saved yet");
7184
- $state.textContent = "not saved yet";
7184
+ $state.textContent = (0, i18next.t)("not saved yet");
7185
7185
  } else if (!kept) {
7186
7186
  $state.classList.add("is-empty");
7187
7187
  $state.setAttribute("data-i18n", "no file");
7188
- $state.textContent = "no file";
7188
+ $state.textContent = (0, i18next.t)("no file");
7189
7189
  }
7190
7190
  $clear.hidden = readonly || !picked && !kept;
7191
7191
  $pick.hidden = !!readonly;
@@ -63836,9 +63836,14 @@ var BTN_STYLE_CANCEL = "flex:1;padding:6px;background:var(--bulma-scheme-main-bi
63836
63836
  function create_form_button_row(parent, buttons) {
63837
63837
  let btnRow = document.createElement("div");
63838
63838
  btnRow.style.cssText = "display:flex;gap:8px;";
63839
- for (let { text, style, onClick } of buttons) {
63839
+ for (let { i18n, style, onClick } of buttons) {
63840
63840
  let btn = document.createElement("button");
63841
- btn.textContent = (0, i18next.t)(text);
63841
+ btn.textContent = (0, i18next.t)(i18n);
63842
+ btn.setAttribute("data-i18n", i18n);
63843
+ btn.setAttribute("title", (0, i18next.t)(i18n));
63844
+ btn.setAttribute("data-i18n-title", i18n);
63845
+ btn.setAttribute("aria-label", (0, i18next.t)(i18n));
63846
+ btn.setAttribute("data-i18n-aria-label", i18n);
63842
63847
  btn.style.cssText = style + touch_control_css();
63843
63848
  btn.addEventListener("click", (e) => {
63844
63849
  e.stopPropagation();
@@ -63872,11 +63877,11 @@ function show_confirm_popover(gobj, target_el, message, confirm_text, confirm_co
63872
63877
  msg.textContent = message;
63873
63878
  popover.appendChild(msg);
63874
63879
  create_form_button_row(popover, [{
63875
- text: "cancel",
63880
+ i18n: "cancel",
63876
63881
  style: BTN_STYLE_CANCEL,
63877
63882
  onClick: () => hide_overlay(gobj, priv_key)
63878
63883
  }, {
63879
- text: confirm_text,
63884
+ i18n: confirm_text,
63880
63885
  style: "flex:1;padding:6px;background:" + confirm_color + ";color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px;font-weight:500;",
63881
63886
  onClick: onConfirm
63882
63887
  }]);
@@ -64605,13 +64610,13 @@ function show_port_popover(gobj) {
64605
64610
  }
64606
64611
  ]);
64607
64612
  create_form_button_row(popover, [{
64608
- text: "cancel",
64613
+ i18n: "cancel",
64609
64614
  style: BTN_STYLE_CANCEL,
64610
64615
  onClick: () => {
64611
64616
  hide_port_popover(gobj);
64612
64617
  }
64613
64618
  }, {
64614
- text: "apply",
64619
+ i18n: "apply",
64615
64620
  style: "flex:1;padding:6px;background:#1890ff;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px;font-weight:500;",
64616
64621
  onClick: () => {
64617
64622
  drop_preview_undo(gobj, "_port_preview_undo");
@@ -65301,13 +65306,13 @@ function show_edge_popover(gobj) {
65301
65306
  }
65302
65307
  ]);
65303
65308
  create_form_button_row(popover, [{
65304
- text: "cancel",
65309
+ i18n: "cancel",
65305
65310
  style: BTN_STYLE_CANCEL,
65306
65311
  onClick: () => {
65307
65312
  deselect_edge(gobj);
65308
65313
  }
65309
65314
  }, {
65310
- text: "apply",
65315
+ i18n: "apply",
65311
65316
  style: "flex:1;padding:6px;background:#52c41a;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px;font-weight:500;",
65312
65317
  onClick: () => {
65313
65318
  drop_preview_undo(gobj, "_edge_preview_undo");
@@ -65462,13 +65467,13 @@ function show_node_popover(gobj) {
65462
65467
  }
65463
65468
  ]);
65464
65469
  create_form_button_row(popover, [{
65465
- text: "cancel",
65470
+ i18n: "cancel",
65466
65471
  style: BTN_STYLE_CANCEL,
65467
65472
  onClick: () => {
65468
65473
  hide_node_popover(gobj);
65469
65474
  }
65470
65475
  }, {
65471
- text: "apply",
65476
+ i18n: "apply",
65472
65477
  style: "flex:1;padding:6px;background:#1890ff;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px;font-weight:500;",
65473
65478
  onClick: () => {
65474
65479
  drop_preview_undo(gobj, "_node_preview_undo");
@@ -66122,12 +66127,21 @@ function show_create_popover(gobj) {
66122
66127
  let titleEl = document.createElement("div");
66123
66128
  titleEl.style.cssText = "margin-bottom:10px;font-weight:600;font-size:14px;";
66124
66129
  titleEl.textContent = (0, i18next.t)("create node");
66130
+ titleEl.setAttribute("data-i18n", "create node");
66125
66131
  popover.appendChild(titleEl);
66126
66132
  let topicLabel = document.createElement("div");
66127
66133
  topicLabel.style.cssText = "margin-bottom:4px;font-weight:500;";
66128
- topicLabel.textContent = (0, i18next.t)("topic") + ":";
66134
+ let topicWord = document.createElement("span");
66135
+ topicWord.textContent = (0, i18next.t)("topic");
66136
+ topicWord.setAttribute("data-i18n", "topic");
66137
+ topicLabel.appendChild(topicWord);
66138
+ topicLabel.appendChild(document.createTextNode(":"));
66129
66139
  popover.appendChild(topicLabel);
66130
66140
  let topicSelect = document.createElement("select");
66141
+ topicSelect.setAttribute("title", (0, i18next.t)("topic"));
66142
+ topicSelect.setAttribute("data-i18n-title", "topic");
66143
+ topicSelect.setAttribute("aria-label", (0, i18next.t)("topic"));
66144
+ topicSelect.setAttribute("data-i18n-aria-label", "topic");
66131
66145
  topicSelect.style.cssText = "width:100%;padding:6px;border:1px solid #d9d9d9;border-radius:4px;font-size:13px;margin-bottom:8px;" + touch_control_css();
66132
66146
  for (let i = 0; i < topics.length; i++) {
66133
66147
  let opt = document.createElement("option");
@@ -66161,6 +66175,11 @@ function show_create_popover(gobj) {
66161
66175
  let idInput = document.createElement("input");
66162
66176
  idInput.type = "text";
66163
66177
  idInput.placeholder = (0, i18next.t)("node id");
66178
+ idInput.setAttribute("data-i18n-placeholder", "node id");
66179
+ idInput.setAttribute("title", (0, i18next.t)("node id"));
66180
+ idInput.setAttribute("data-i18n-title", "node id");
66181
+ idInput.setAttribute("aria-label", (0, i18next.t)("node id"));
66182
+ idInput.setAttribute("data-i18n-aria-label", "node id");
66164
66183
  idInput.style.cssText = "width:100%;padding:6px;border:1px solid #d9d9d9;border-radius:4px;font-size:13px;margin-bottom:10px;box-sizing:border-box;" + touch_control_css();
66165
66184
  popover.appendChild(idInput);
66166
66185
  let errorEl = document.createElement("div");
@@ -66168,16 +66187,17 @@ function show_create_popover(gobj) {
66168
66187
  popover.appendChild(errorEl);
66169
66188
  let createBtn;
66170
66189
  create_form_button_row(popover, [{
66171
- text: "cancel",
66190
+ i18n: "cancel",
66172
66191
  style: BTN_STYLE_CANCEL,
66173
66192
  onClick: () => hide_create_popover(gobj)
66174
66193
  }, {
66175
- text: "create",
66194
+ i18n: "create",
66176
66195
  style: "flex:1;padding:6px;background:#1890ff;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px;font-weight:500;",
66177
66196
  onClick: () => {
66178
66197
  let node_id = idInput.value.trim();
66179
66198
  if (!node_id) {
66180
- errorEl.textContent = (0, i18next.t)("node id") + " required";
66199
+ errorEl.textContent = (0, i18next.t)("node id required");
66200
+ errorEl.setAttribute("data-i18n", "node id required");
66181
66201
  errorEl.style.display = "block";
66182
66202
  idInput.focus();
66183
66203
  return;
@@ -73651,17 +73671,22 @@ function plan_import(treedb, incoming, options) {
73651
73671
  col: inner_id
73652
73672
  });
73653
73673
  let hook = (in_col.col || {}).hook;
73654
- if (hook && typeof hook === "object" && !Array.isArray(hook)) for (let [child_topic, fkey_col] of Object.entries(hook)) {
73655
- if (topic_name_breaks_the_rule(child_topic)) conflicts.push({
73656
- code: "topic name breaks the name rule",
73657
- topic: child_topic,
73658
- col: ""
73659
- });
73660
- if (col_name_breaks_the_rule(fkey_col)) conflicts.push({
73661
- code: "column name breaks the name rule",
73662
- topic: child_topic,
73663
- col: String(fkey_col)
73664
- });
73674
+ if (hook && typeof hook === "object" && !Array.isArray(hook)) {
73675
+ let from_hook = `${name || ""}.${in_col.name}`;
73676
+ for (let [child_topic, fkey_col] of Object.entries(hook)) {
73677
+ if (topic_name_breaks_the_rule(child_topic)) conflicts.push({
73678
+ code: "topic name breaks the name rule",
73679
+ topic: child_topic,
73680
+ col: "",
73681
+ hook: from_hook
73682
+ });
73683
+ if (col_name_breaks_the_rule(fkey_col)) conflicts.push({
73684
+ code: "column name breaks the name rule",
73685
+ topic: child_topic,
73686
+ col: String(fkey_col),
73687
+ hook: from_hook
73688
+ });
73689
+ }
73665
73690
  }
73666
73691
  }
73667
73692
  }
@@ -76973,15 +76998,31 @@ function render_import_plan(gobj, plan) {
76973
76998
  return [
76974
76999
  "p",
76975
77000
  { class: "SCHEMA_IMPORT_CONFLICT is-size-7" },
76976
- [[
76977
- "span",
76978
- { i18n: conflict.code },
76979
- (0, i18next.t)(conflict.code)
76980
- ], where ? [
76981
- "code",
76982
- { class: "SCHEMA_IMPORT_CONFLICT_NAME ml-2" },
76983
- text_node(where)
76984
- ] : null].filter(Boolean)
77001
+ [
77002
+ [
77003
+ "span",
77004
+ { i18n: conflict.code },
77005
+ (0, i18next.t)(conflict.code)
77006
+ ],
77007
+ where ? [
77008
+ "code",
77009
+ { class: "SCHEMA_IMPORT_CONFLICT_NAME ml-2" },
77010
+ text_node(where)
77011
+ ] : null,
77012
+ conflict.hook ? [
77013
+ "span",
77014
+ {
77015
+ class: "SCHEMA_IMPORT_CONFLICT_HOOK ml-2",
77016
+ i18n: "in the hook map of"
77017
+ },
77018
+ (0, i18next.t)("in the hook map of")
77019
+ ] : null,
77020
+ conflict.hook ? [
77021
+ "code",
77022
+ { class: "SCHEMA_IMPORT_CONFLICT_HOOK_NAME ml-1" },
77023
+ text_node(conflict.hook)
77024
+ ] : null
77025
+ ].filter(Boolean)
76985
77026
  ];
76986
77027
  })
76987
77028
  ]));
@@ -7176,11 +7176,11 @@ function yui_file_control(gobj, { name, value, readonly, accept, on_pick }) {
7176
7176
  if (picked) {
7177
7177
  $state.classList.add("is-picked");
7178
7178
  $state.setAttribute("data-i18n", "not saved yet");
7179
- $state.textContent = "not saved yet";
7179
+ $state.textContent = t("not saved yet");
7180
7180
  } else if (!kept) {
7181
7181
  $state.classList.add("is-empty");
7182
7182
  $state.setAttribute("data-i18n", "no file");
7183
- $state.textContent = "no file";
7183
+ $state.textContent = t("no file");
7184
7184
  }
7185
7185
  $clear.hidden = readonly || !picked && !kept;
7186
7186
  $pick.hidden = !!readonly;
@@ -63831,9 +63831,14 @@ var BTN_STYLE_CANCEL = "flex:1;padding:6px;background:var(--bulma-scheme-main-bi
63831
63831
  function create_form_button_row(parent, buttons) {
63832
63832
  let btnRow = document.createElement("div");
63833
63833
  btnRow.style.cssText = "display:flex;gap:8px;";
63834
- for (let { text, style, onClick } of buttons) {
63834
+ for (let { i18n, style, onClick } of buttons) {
63835
63835
  let btn = document.createElement("button");
63836
- btn.textContent = t(text);
63836
+ btn.textContent = t(i18n);
63837
+ btn.setAttribute("data-i18n", i18n);
63838
+ btn.setAttribute("title", t(i18n));
63839
+ btn.setAttribute("data-i18n-title", i18n);
63840
+ btn.setAttribute("aria-label", t(i18n));
63841
+ btn.setAttribute("data-i18n-aria-label", i18n);
63837
63842
  btn.style.cssText = style + touch_control_css();
63838
63843
  btn.addEventListener("click", (e) => {
63839
63844
  e.stopPropagation();
@@ -63867,11 +63872,11 @@ function show_confirm_popover(gobj, target_el, message, confirm_text, confirm_co
63867
63872
  msg.textContent = message;
63868
63873
  popover.appendChild(msg);
63869
63874
  create_form_button_row(popover, [{
63870
- text: "cancel",
63875
+ i18n: "cancel",
63871
63876
  style: BTN_STYLE_CANCEL,
63872
63877
  onClick: () => hide_overlay(gobj, priv_key)
63873
63878
  }, {
63874
- text: confirm_text,
63879
+ i18n: confirm_text,
63875
63880
  style: "flex:1;padding:6px;background:" + confirm_color + ";color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px;font-weight:500;",
63876
63881
  onClick: onConfirm
63877
63882
  }]);
@@ -64600,13 +64605,13 @@ function show_port_popover(gobj) {
64600
64605
  }
64601
64606
  ]);
64602
64607
  create_form_button_row(popover, [{
64603
- text: "cancel",
64608
+ i18n: "cancel",
64604
64609
  style: BTN_STYLE_CANCEL,
64605
64610
  onClick: () => {
64606
64611
  hide_port_popover(gobj);
64607
64612
  }
64608
64613
  }, {
64609
- text: "apply",
64614
+ i18n: "apply",
64610
64615
  style: "flex:1;padding:6px;background:#1890ff;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px;font-weight:500;",
64611
64616
  onClick: () => {
64612
64617
  drop_preview_undo(gobj, "_port_preview_undo");
@@ -65296,13 +65301,13 @@ function show_edge_popover(gobj) {
65296
65301
  }
65297
65302
  ]);
65298
65303
  create_form_button_row(popover, [{
65299
- text: "cancel",
65304
+ i18n: "cancel",
65300
65305
  style: BTN_STYLE_CANCEL,
65301
65306
  onClick: () => {
65302
65307
  deselect_edge(gobj);
65303
65308
  }
65304
65309
  }, {
65305
- text: "apply",
65310
+ i18n: "apply",
65306
65311
  style: "flex:1;padding:6px;background:#52c41a;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px;font-weight:500;",
65307
65312
  onClick: () => {
65308
65313
  drop_preview_undo(gobj, "_edge_preview_undo");
@@ -65457,13 +65462,13 @@ function show_node_popover(gobj) {
65457
65462
  }
65458
65463
  ]);
65459
65464
  create_form_button_row(popover, [{
65460
- text: "cancel",
65465
+ i18n: "cancel",
65461
65466
  style: BTN_STYLE_CANCEL,
65462
65467
  onClick: () => {
65463
65468
  hide_node_popover(gobj);
65464
65469
  }
65465
65470
  }, {
65466
- text: "apply",
65471
+ i18n: "apply",
65467
65472
  style: "flex:1;padding:6px;background:#1890ff;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px;font-weight:500;",
65468
65473
  onClick: () => {
65469
65474
  drop_preview_undo(gobj, "_node_preview_undo");
@@ -66117,12 +66122,21 @@ function show_create_popover(gobj) {
66117
66122
  let titleEl = document.createElement("div");
66118
66123
  titleEl.style.cssText = "margin-bottom:10px;font-weight:600;font-size:14px;";
66119
66124
  titleEl.textContent = t("create node");
66125
+ titleEl.setAttribute("data-i18n", "create node");
66120
66126
  popover.appendChild(titleEl);
66121
66127
  let topicLabel = document.createElement("div");
66122
66128
  topicLabel.style.cssText = "margin-bottom:4px;font-weight:500;";
66123
- topicLabel.textContent = t("topic") + ":";
66129
+ let topicWord = document.createElement("span");
66130
+ topicWord.textContent = t("topic");
66131
+ topicWord.setAttribute("data-i18n", "topic");
66132
+ topicLabel.appendChild(topicWord);
66133
+ topicLabel.appendChild(document.createTextNode(":"));
66124
66134
  popover.appendChild(topicLabel);
66125
66135
  let topicSelect = document.createElement("select");
66136
+ topicSelect.setAttribute("title", t("topic"));
66137
+ topicSelect.setAttribute("data-i18n-title", "topic");
66138
+ topicSelect.setAttribute("aria-label", t("topic"));
66139
+ topicSelect.setAttribute("data-i18n-aria-label", "topic");
66126
66140
  topicSelect.style.cssText = "width:100%;padding:6px;border:1px solid #d9d9d9;border-radius:4px;font-size:13px;margin-bottom:8px;" + touch_control_css();
66127
66141
  for (let i = 0; i < topics.length; i++) {
66128
66142
  let opt = document.createElement("option");
@@ -66156,6 +66170,11 @@ function show_create_popover(gobj) {
66156
66170
  let idInput = document.createElement("input");
66157
66171
  idInput.type = "text";
66158
66172
  idInput.placeholder = t("node id");
66173
+ idInput.setAttribute("data-i18n-placeholder", "node id");
66174
+ idInput.setAttribute("title", t("node id"));
66175
+ idInput.setAttribute("data-i18n-title", "node id");
66176
+ idInput.setAttribute("aria-label", t("node id"));
66177
+ idInput.setAttribute("data-i18n-aria-label", "node id");
66159
66178
  idInput.style.cssText = "width:100%;padding:6px;border:1px solid #d9d9d9;border-radius:4px;font-size:13px;margin-bottom:10px;box-sizing:border-box;" + touch_control_css();
66160
66179
  popover.appendChild(idInput);
66161
66180
  let errorEl = document.createElement("div");
@@ -66163,16 +66182,17 @@ function show_create_popover(gobj) {
66163
66182
  popover.appendChild(errorEl);
66164
66183
  let createBtn;
66165
66184
  create_form_button_row(popover, [{
66166
- text: "cancel",
66185
+ i18n: "cancel",
66167
66186
  style: BTN_STYLE_CANCEL,
66168
66187
  onClick: () => hide_create_popover(gobj)
66169
66188
  }, {
66170
- text: "create",
66189
+ i18n: "create",
66171
66190
  style: "flex:1;padding:6px;background:#1890ff;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px;font-weight:500;",
66172
66191
  onClick: () => {
66173
66192
  let node_id = idInput.value.trim();
66174
66193
  if (!node_id) {
66175
- errorEl.textContent = t("node id") + " required";
66194
+ errorEl.textContent = t("node id required");
66195
+ errorEl.setAttribute("data-i18n", "node id required");
66176
66196
  errorEl.style.display = "block";
66177
66197
  idInput.focus();
66178
66198
  return;
@@ -73646,17 +73666,22 @@ function plan_import(treedb, incoming, options) {
73646
73666
  col: inner_id
73647
73667
  });
73648
73668
  let hook = (in_col.col || {}).hook;
73649
- if (hook && typeof hook === "object" && !Array.isArray(hook)) for (let [child_topic, fkey_col] of Object.entries(hook)) {
73650
- if (topic_name_breaks_the_rule(child_topic)) conflicts.push({
73651
- code: "topic name breaks the name rule",
73652
- topic: child_topic,
73653
- col: ""
73654
- });
73655
- if (col_name_breaks_the_rule(fkey_col)) conflicts.push({
73656
- code: "column name breaks the name rule",
73657
- topic: child_topic,
73658
- col: String(fkey_col)
73659
- });
73669
+ if (hook && typeof hook === "object" && !Array.isArray(hook)) {
73670
+ let from_hook = `${name || ""}.${in_col.name}`;
73671
+ for (let [child_topic, fkey_col] of Object.entries(hook)) {
73672
+ if (topic_name_breaks_the_rule(child_topic)) conflicts.push({
73673
+ code: "topic name breaks the name rule",
73674
+ topic: child_topic,
73675
+ col: "",
73676
+ hook: from_hook
73677
+ });
73678
+ if (col_name_breaks_the_rule(fkey_col)) conflicts.push({
73679
+ code: "column name breaks the name rule",
73680
+ topic: child_topic,
73681
+ col: String(fkey_col),
73682
+ hook: from_hook
73683
+ });
73684
+ }
73660
73685
  }
73661
73686
  }
73662
73687
  }
@@ -76968,15 +76993,31 @@ function render_import_plan(gobj, plan) {
76968
76993
  return [
76969
76994
  "p",
76970
76995
  { class: "SCHEMA_IMPORT_CONFLICT is-size-7" },
76971
- [[
76972
- "span",
76973
- { i18n: conflict.code },
76974
- t(conflict.code)
76975
- ], where ? [
76976
- "code",
76977
- { class: "SCHEMA_IMPORT_CONFLICT_NAME ml-2" },
76978
- text_node(where)
76979
- ] : null].filter(Boolean)
76996
+ [
76997
+ [
76998
+ "span",
76999
+ { i18n: conflict.code },
77000
+ t(conflict.code)
77001
+ ],
77002
+ where ? [
77003
+ "code",
77004
+ { class: "SCHEMA_IMPORT_CONFLICT_NAME ml-2" },
77005
+ text_node(where)
77006
+ ] : null,
77007
+ conflict.hook ? [
77008
+ "span",
77009
+ {
77010
+ class: "SCHEMA_IMPORT_CONFLICT_HOOK ml-2",
77011
+ i18n: "in the hook map of"
77012
+ },
77013
+ t("in the hook map of")
77014
+ ] : null,
77015
+ conflict.hook ? [
77016
+ "code",
77017
+ { class: "SCHEMA_IMPORT_CONFLICT_HOOK_NAME ml-1" },
77018
+ text_node(conflict.hook)
77019
+ ] : null
77020
+ ].filter(Boolean)
76980
77021
  ];
76981
77022
  })
76982
77023
  ]));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.26.24",
3
+ "version": "7.26.26",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
@@ -19,7 +19,8 @@
19
19
  "index.js",
20
20
  "src/",
21
21
  "vite-plugin-yuneta-html.js",
22
- "scripts/check-markup-strings.mjs"
22
+ "scripts/check-markup-strings.mjs",
23
+ "scripts/validate-locales.mjs"
23
24
  ],
24
25
  "description": "Yuneta UI Library — the active line: declarative shell (C_YUI_SHELL+NAV+PAGER+WIZARD) + windows, TreeDB views, forms, charts and maps. Consumed from the npm registry by the Yuneta SPAs (gui_agent, gui_treedb, wattyzer, yunovatios). The frozen legacy GClass GUI stack (C_YUI_MAIN/TABS/ROUTING) lives on the v1 branch, npm dist-tag 'legacy' (consumed by estadodelaire and hidraulia).",
25
26
  "scripts": {