@yuneta/gobj-ui 7.26.22 → 7.26.23

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.
@@ -73618,6 +73618,11 @@ function plan_import(treedb, incoming, options) {
73618
73618
  conflicts
73619
73619
  };
73620
73620
  }
73621
+ if (topic_name_breaks_the_rule(treedb_id)) conflicts.push({
73622
+ code: "treedb name breaks the name rule",
73623
+ topic: "",
73624
+ col: ""
73625
+ });
73621
73626
  for (let in_topic of incoming.topics) {
73622
73627
  let name = (in_topic || {}).id;
73623
73628
  if (name && topic_name_breaks_the_rule(name)) conflicts.push({
@@ -73625,11 +73630,34 @@ function plan_import(treedb, incoming, options) {
73625
73630
  topic: name,
73626
73631
  col: ""
73627
73632
  });
73628
- for (let in_col of incoming_cols(in_topic || {})) if (col_name_breaks_the_rule(in_col.name)) conflicts.push({
73629
- code: "column name breaks the name rule",
73630
- topic: name || "",
73631
- col: in_col.name
73632
- });
73633
+ let cols = (in_topic || {}).cols;
73634
+ if (Array.isArray(cols)) {
73635
+ for (let c of cols) if (!c || !c.id) conflicts.push({
73636
+ code: "column has no name",
73637
+ topic: name || "",
73638
+ col: ""
73639
+ });
73640
+ }
73641
+ for (let in_col of incoming_cols(in_topic || {})) {
73642
+ if (col_name_breaks_the_rule(in_col.name)) conflicts.push({
73643
+ code: "column name breaks the name rule",
73644
+ topic: name || "",
73645
+ col: in_col.name
73646
+ });
73647
+ let hook = (in_col.col || {}).hook;
73648
+ if (hook && typeof hook === "object" && !Array.isArray(hook)) for (let [child_topic, fkey_col] of Object.entries(hook)) {
73649
+ if (topic_name_breaks_the_rule(child_topic)) conflicts.push({
73650
+ code: "topic name breaks the name rule",
73651
+ topic: name || "",
73652
+ col: in_col.name
73653
+ });
73654
+ if (col_name_breaks_the_rule(fkey_col)) conflicts.push({
73655
+ code: "column name breaks the name rule",
73656
+ topic: name || "",
73657
+ col: in_col.name
73658
+ });
73659
+ }
73660
+ }
73633
73661
  }
73634
73662
  if (conflicts.length > 0) return {
73635
73663
  writes,
@@ -74436,12 +74464,22 @@ function mt_destroy$1(gobj) {
74436
74464
  * name field is marked aria-invalid for a reader.
74437
74465
  ************************************************************/
74438
74466
  function refuse_bad_name(gobj, $message) {
74439
- let priv = gobj.priv;
74440
- let $value = priv.dialog_content ? priv.dialog_content.querySelector("[data-name=\"value\"]") : null;
74441
- if ($value) $value.setAttribute("aria-invalid", "true");
74467
+ mark_name_field(gobj, true);
74442
74468
  yui_shell_show_error(yui_shell_of(gobj), $message, { t: i18next.t });
74443
74469
  return -1;
74444
74470
  }
74471
+ /************************************************************
74472
+ * The name field says whether the LAST save refused its name:
74473
+ * set by every refusal of the name (empty, a rule, a duplicate),
74474
+ * cleared by a save that gets past them.
74475
+ ************************************************************/
74476
+ function mark_name_field(gobj, invalid) {
74477
+ let priv = gobj.priv;
74478
+ let $value = priv.dialog_content ? priv.dialog_content.querySelector("[data-name=\"value\"]") : null;
74479
+ if (!$value) return;
74480
+ if (invalid) $value.setAttribute("aria-invalid", "true");
74481
+ else $value.removeAttribute("aria-invalid");
74482
+ }
74445
74483
  /***************************************************************
74446
74484
  * The frame: a toolbar that never changes place, a notice line
74447
74485
  * for what the whole view has to say, and one body the screens
@@ -77954,6 +77992,7 @@ function ac_save_column(gobj, event, kw, src) {
77954
77992
  }
77955
77993
  let values = kw.values || {};
77956
77994
  if (kw.creating && (0, _yuneta_gobj_js.empty_string)(String(values.value || "").trim())) {
77995
+ mark_name_field(gobj, true);
77957
77996
  yui_shell_show_error(yui_shell_of(gobj), "a column needs a name", { t: i18next.t });
77958
77997
  return -1;
77959
77998
  }
@@ -77963,9 +78002,11 @@ function ac_save_column(gobj, event, kw, src) {
77963
78002
  (0, i18next.t)("invalid column name")
77964
78003
  ]));
77965
78004
  if (kw.creating && find_col(priv.model, priv.treedb_id, kw.topic, String(values.value).trim())) {
78005
+ mark_name_field(gobj, true);
77966
78006
  yui_shell_show_error(yui_shell_of(gobj), "that column is already there", { t: i18next.t });
77967
78007
  return -1;
77968
78008
  }
78009
+ mark_name_field(gobj, false);
77969
78010
  close_dialog(gobj);
77970
78011
  return queue_writes(gobj, [{
77971
78012
  op: kw.creating ? "create" : "update",
@@ -78213,6 +78254,7 @@ function ac_save_topic(gobj, event, kw, src) {
78213
78254
  if (kw.creating) {
78214
78255
  let name = String(values.value || "").trim();
78215
78256
  if ((0, _yuneta_gobj_js.empty_string)(name)) {
78257
+ mark_name_field(gobj, true);
78216
78258
  yui_shell_show_error(yui_shell_of(gobj), "a topic needs a name", { t: i18next.t });
78217
78259
  return -1;
78218
78260
  }
@@ -78222,9 +78264,11 @@ function ac_save_topic(gobj, event, kw, src) {
78222
78264
  (0, i18next.t)("invalid topic name")
78223
78265
  ]));
78224
78266
  if (find_topic(priv.model, priv.treedb_id, name)) {
78267
+ mark_name_field(gobj, true);
78225
78268
  yui_shell_show_error(yui_shell_of(gobj), "that topic is already there", { t: i18next.t });
78226
78269
  return -1;
78227
78270
  }
78271
+ mark_name_field(gobj, false);
78228
78272
  }
78229
78273
  close_dialog(gobj);
78230
78274
  return queue_writes(gobj, [{
@@ -73613,6 +73613,11 @@ function plan_import(treedb, incoming, options) {
73613
73613
  conflicts
73614
73614
  };
73615
73615
  }
73616
+ if (topic_name_breaks_the_rule(treedb_id)) conflicts.push({
73617
+ code: "treedb name breaks the name rule",
73618
+ topic: "",
73619
+ col: ""
73620
+ });
73616
73621
  for (let in_topic of incoming.topics) {
73617
73622
  let name = (in_topic || {}).id;
73618
73623
  if (name && topic_name_breaks_the_rule(name)) conflicts.push({
@@ -73620,11 +73625,34 @@ function plan_import(treedb, incoming, options) {
73620
73625
  topic: name,
73621
73626
  col: ""
73622
73627
  });
73623
- for (let in_col of incoming_cols(in_topic || {})) if (col_name_breaks_the_rule(in_col.name)) conflicts.push({
73624
- code: "column name breaks the name rule",
73625
- topic: name || "",
73626
- col: in_col.name
73627
- });
73628
+ let cols = (in_topic || {}).cols;
73629
+ if (Array.isArray(cols)) {
73630
+ for (let c of cols) if (!c || !c.id) conflicts.push({
73631
+ code: "column has no name",
73632
+ topic: name || "",
73633
+ col: ""
73634
+ });
73635
+ }
73636
+ for (let in_col of incoming_cols(in_topic || {})) {
73637
+ if (col_name_breaks_the_rule(in_col.name)) conflicts.push({
73638
+ code: "column name breaks the name rule",
73639
+ topic: name || "",
73640
+ col: in_col.name
73641
+ });
73642
+ let hook = (in_col.col || {}).hook;
73643
+ if (hook && typeof hook === "object" && !Array.isArray(hook)) for (let [child_topic, fkey_col] of Object.entries(hook)) {
73644
+ if (topic_name_breaks_the_rule(child_topic)) conflicts.push({
73645
+ code: "topic name breaks the name rule",
73646
+ topic: name || "",
73647
+ col: in_col.name
73648
+ });
73649
+ if (col_name_breaks_the_rule(fkey_col)) conflicts.push({
73650
+ code: "column name breaks the name rule",
73651
+ topic: name || "",
73652
+ col: in_col.name
73653
+ });
73654
+ }
73655
+ }
73628
73656
  }
73629
73657
  if (conflicts.length > 0) return {
73630
73658
  writes,
@@ -74431,12 +74459,22 @@ function mt_destroy$1(gobj) {
74431
74459
  * name field is marked aria-invalid for a reader.
74432
74460
  ************************************************************/
74433
74461
  function refuse_bad_name(gobj, $message) {
74434
- let priv = gobj.priv;
74435
- let $value = priv.dialog_content ? priv.dialog_content.querySelector("[data-name=\"value\"]") : null;
74436
- if ($value) $value.setAttribute("aria-invalid", "true");
74462
+ mark_name_field(gobj, true);
74437
74463
  yui_shell_show_error(yui_shell_of(gobj), $message, { t });
74438
74464
  return -1;
74439
74465
  }
74466
+ /************************************************************
74467
+ * The name field says whether the LAST save refused its name:
74468
+ * set by every refusal of the name (empty, a rule, a duplicate),
74469
+ * cleared by a save that gets past them.
74470
+ ************************************************************/
74471
+ function mark_name_field(gobj, invalid) {
74472
+ let priv = gobj.priv;
74473
+ let $value = priv.dialog_content ? priv.dialog_content.querySelector("[data-name=\"value\"]") : null;
74474
+ if (!$value) return;
74475
+ if (invalid) $value.setAttribute("aria-invalid", "true");
74476
+ else $value.removeAttribute("aria-invalid");
74477
+ }
74440
74478
  /***************************************************************
74441
74479
  * The frame: a toolbar that never changes place, a notice line
74442
74480
  * for what the whole view has to say, and one body the screens
@@ -77949,6 +77987,7 @@ function ac_save_column(gobj, event, kw, src) {
77949
77987
  }
77950
77988
  let values = kw.values || {};
77951
77989
  if (kw.creating && empty_string(String(values.value || "").trim())) {
77990
+ mark_name_field(gobj, true);
77952
77991
  yui_shell_show_error(yui_shell_of(gobj), "a column needs a name", { t });
77953
77992
  return -1;
77954
77993
  }
@@ -77958,9 +77997,11 @@ function ac_save_column(gobj, event, kw, src) {
77958
77997
  t("invalid column name")
77959
77998
  ]));
77960
77999
  if (kw.creating && find_col(priv.model, priv.treedb_id, kw.topic, String(values.value).trim())) {
78000
+ mark_name_field(gobj, true);
77961
78001
  yui_shell_show_error(yui_shell_of(gobj), "that column is already there", { t });
77962
78002
  return -1;
77963
78003
  }
78004
+ mark_name_field(gobj, false);
77964
78005
  close_dialog(gobj);
77965
78006
  return queue_writes(gobj, [{
77966
78007
  op: kw.creating ? "create" : "update",
@@ -78208,6 +78249,7 @@ function ac_save_topic(gobj, event, kw, src) {
78208
78249
  if (kw.creating) {
78209
78250
  let name = String(values.value || "").trim();
78210
78251
  if (empty_string(name)) {
78252
+ mark_name_field(gobj, true);
78211
78253
  yui_shell_show_error(yui_shell_of(gobj), "a topic needs a name", { t });
78212
78254
  return -1;
78213
78255
  }
@@ -78217,9 +78259,11 @@ function ac_save_topic(gobj, event, kw, src) {
78217
78259
  t("invalid topic name")
78218
78260
  ]));
78219
78261
  if (find_topic(priv.model, priv.treedb_id, name)) {
78262
+ mark_name_field(gobj, true);
78220
78263
  yui_shell_show_error(yui_shell_of(gobj), "that topic is already there", { t });
78221
78264
  return -1;
78222
78265
  }
78266
+ mark_name_field(gobj, false);
78223
78267
  }
78224
78268
  close_dialog(gobj);
78225
78269
  return queue_writes(gobj, [{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.26.22",
3
+ "version": "7.26.23",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
@@ -371,15 +371,30 @@ function mt_destroy(gobj)
371
371
  * name field is marked aria-invalid for a reader.
372
372
  ************************************************************/
373
373
  function refuse_bad_name(gobj, $message)
374
+ {
375
+ mark_name_field(gobj, true);
376
+ yui_shell_show_error(yui_shell_of(gobj), $message, {t: t});
377
+ return -1;
378
+ }
379
+
380
+ /************************************************************
381
+ * The name field says whether the LAST save refused its name:
382
+ * set by every refusal of the name (empty, a rule, a duplicate),
383
+ * cleared by a save that gets past them.
384
+ ************************************************************/
385
+ function mark_name_field(gobj, invalid)
374
386
  {
375
387
  let priv = gobj.priv;
376
388
  let $value = priv.dialog_content?
377
389
  priv.dialog_content.querySelector('[data-name="value"]') : null;
378
- if($value) {
390
+ if(!$value) {
391
+ return;
392
+ }
393
+ if(invalid) {
379
394
  $value.setAttribute("aria-invalid", "true");
395
+ } else {
396
+ $value.removeAttribute("aria-invalid");
380
397
  }
381
- yui_shell_show_error(yui_shell_of(gobj), $message, {t: t});
382
- return -1;
383
398
  }
384
399
 
385
400
  /***************************************************************
@@ -4047,6 +4062,7 @@ function ac_save_column(gobj, event, kw, src)
4047
4062
 
4048
4063
  let values = kw.values || {};
4049
4064
  if(kw.creating && empty_string(String(values.value || "").trim())) {
4065
+ mark_name_field(gobj, true);
4050
4066
  yui_shell_show_error(yui_shell_of(gobj), "a column needs a name", {t: t});
4051
4067
  return -1;
4052
4068
  }
@@ -4055,9 +4071,11 @@ function ac_save_column(gobj, event, kw, src)
4055
4071
  ["span", {i18n: "invalid column name"}, t("invalid column name")]));
4056
4072
  }
4057
4073
  if(kw.creating && find_col(priv.model, priv.treedb_id, kw.topic, String(values.value).trim())) {
4074
+ mark_name_field(gobj, true);
4058
4075
  yui_shell_show_error(yui_shell_of(gobj), "that column is already there", {t: t});
4059
4076
  return -1;
4060
4077
  }
4078
+ mark_name_field(gobj, false);
4061
4079
 
4062
4080
  close_dialog(gobj);
4063
4081
  return queue_writes(gobj, [{
@@ -4358,6 +4376,7 @@ function ac_save_topic(gobj, event, kw, src)
4358
4376
  if(kw.creating) {
4359
4377
  let name = String(values.value || "").trim();
4360
4378
  if(empty_string(name)) {
4379
+ mark_name_field(gobj, true);
4361
4380
  yui_shell_show_error(yui_shell_of(gobj), "a topic needs a name", {t: t});
4362
4381
  return -1;
4363
4382
  }
@@ -4366,9 +4385,11 @@ function ac_save_topic(gobj, event, kw, src)
4366
4385
  ["span", {i18n: "invalid topic name"}, t("invalid topic name")]));
4367
4386
  }
4368
4387
  if(find_topic(priv.model, priv.treedb_id, name)) {
4388
+ mark_name_field(gobj, true);
4369
4389
  yui_shell_show_error(yui_shell_of(gobj), "that topic is already there", {t: t});
4370
4390
  return -1;
4371
4391
  }
4392
+ mark_name_field(gobj, false);
4372
4393
  }
4373
4394
 
4374
4395
  close_dialog(gobj);
@@ -1748,3 +1748,28 @@ describe("names the treedb refuses", () => {
1748
1748
  }
1749
1749
  });
1750
1750
  });
1751
+
1752
+
1753
+ /*
1754
+ * The name field says whether the LAST save refused its name (REV-CLOUD
1755
+ * V15.4): set by every refusal of the name, cleared by a save that gets
1756
+ * past them. It was set by the rule only, and never cleared.
1757
+ */
1758
+ describe("aria-invalid of the name field", () => {
1759
+ test("set by a bad name and by a duplicate, cleared by a good one", () => {
1760
+ const {editor, host} = build("aria_topic", "db");
1761
+ gobj_send_event(editor, "EV_ADD_TOPIC", {}, host);
1762
+ const form = modals[modals.length - 1];
1763
+ const $value = form.$content.querySelector('[data-name="value"]');
1764
+ const save = (name) => {
1765
+ gobj_send_event(editor, "EV_SAVE_TOPIC", {
1766
+ creating: true, topic: "", values: {value: name}
1767
+ }, host);
1768
+ };
1769
+ save("a/b");
1770
+ expect($value.getAttribute("aria-invalid")).toBe("true");
1771
+ save("ok_name");
1772
+ expect($value.getAttribute("aria-invalid")).toBe(null);
1773
+ take("update-node");
1774
+ });
1775
+ });
@@ -211,15 +211,43 @@ function plan_import(treedb, incoming, options)
211
211
  * stop the plan BEFORE any write: run through, the backend refused
212
212
  * them half way, with the writes before them already done.
213
213
  */
214
+ if(topic_name_breaks_the_rule(treedb_id)) {
215
+ /* The treedb is a directory too (treedb_open_db() refuses it) */
216
+ conflicts.push({code: "treedb name breaks the name rule", topic: "", col: ""});
217
+ }
214
218
  for(let in_topic of incoming.topics) {
215
219
  let name = (in_topic || {}).id;
216
220
  if(name && topic_name_breaks_the_rule(name)) {
217
221
  conflicts.push({code: "topic name breaks the name rule", topic: name, col: ""});
218
222
  }
223
+ let cols = (in_topic || {}).cols;
224
+ if(Array.isArray(cols)) {
225
+ /* incoming_cols() drops a column with no id: said, not dropped */
226
+ for(let c of cols) {
227
+ if(!c || !c.id) {
228
+ conflicts.push({code: "column has no name", topic: name || "", col: ""});
229
+ }
230
+ }
231
+ }
219
232
  for(let in_col of incoming_cols(in_topic || {})) {
220
233
  if(col_name_breaks_the_rule(in_col.name)) {
221
234
  conflicts.push({code: "column name breaks the name rule", topic: name || "", col: in_col.name});
222
235
  }
236
+ /* The hook map names a child topic and its fkey column: names
237
+ * the treedb refuses too, even when no column declares them. */
238
+ let hook = (in_col.col || {}).hook;
239
+ if(hook && typeof hook === "object" && !Array.isArray(hook)) {
240
+ for(let [child_topic, fkey_col] of Object.entries(hook)) {
241
+ if(topic_name_breaks_the_rule(child_topic)) {
242
+ conflicts.push({code: "topic name breaks the name rule",
243
+ topic: name || "", col: in_col.name});
244
+ }
245
+ if(col_name_breaks_the_rule(fkey_col)) {
246
+ conflicts.push({code: "column name breaks the name rule",
247
+ topic: name || "", col: in_col.name});
248
+ }
249
+ }
250
+ }
223
251
  }
224
252
  }
225
253
  if(conflicts.length > 0) {
@@ -282,3 +282,31 @@ describe("names the treedb refuses stop the plan before any write (REV-CLOUD V14
282
282
  expect(plan.writes.length).toBeGreaterThan(0);
283
283
  });
284
284
  });
285
+
286
+
287
+ describe("the import reads the hook map and the treedb id (REV-CLOUD V15.4)", () => {
288
+ test("a hook to an fkey x`y or to a topic a/b that no column declares: conflicts", () => {
289
+ let plan = plan_import(null, {
290
+ id: "db", schema_version: "1",
291
+ topics: [{id: "p", pkey: "id", cols: {
292
+ "id": {id: "id", type: "string"},
293
+ "kids": {id: "kids", type: "dict", flag: ["hook"], hook: {"k": "x`y", "a/b": "pid"}},
294
+ }}]
295
+ });
296
+ expect(plan.writes).toEqual([]);
297
+ let codes = plan.conflicts.map((c) => c.code);
298
+ expect(codes).toContain("column name breaks the name rule");
299
+ expect(codes).toContain("topic name breaks the name rule");
300
+ });
301
+
302
+ test("a treedb a/b, and a column with no id in an array: conflicts, not dropped", () => {
303
+ let plan = plan_import(null, {
304
+ id: "a/b", schema_version: "1",
305
+ topics: [{id: "t", pkey: "id", cols: [{id: "id", type: "string"}, {id: "", type: "string"}]}]
306
+ });
307
+ expect(plan.writes).toEqual([]);
308
+ let codes = plan.conflicts.map((c) => c.code);
309
+ expect(codes).toContain("treedb name breaks the name rule");
310
+ expect(codes).toContain("column has no name");
311
+ });
312
+ });