@yuneta/gobj-ui 7.25.2 → 7.25.3

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
@@ -2021,6 +2021,22 @@ and the column screen says so in a banner; the host sends `EV_REFRESH` after a
2021
2021
  save, and the reload forgets the drafts. The export (C literal and JSON) warns
2022
2022
  while there are drafts: the literal carries the versions a save publishes.
2023
2023
 
2024
+ **A draft survives a reload** (7.25.3). The mark of a write lived in the
2025
+ session's memory only, so a reload of the page, a reconnect or a refresh of
2026
+ the model showed no draft while `__system__` still differed from the file in
2027
+ use. The host tells the editor what IS a draft, from data: `C_TREEDB`'s
2028
+ `saved-schema` answers `draft_changed` per treedb (yunetas 7.25.3+), and the
2029
+ host sends it as `EV_DRAFTS`; `drafts_of_saved_answer()` reads the unnamed
2030
+ answer into the shape the event takes. What the session wrote stays marked;
2031
+ what the host said is applied again after every reload of the model.
2032
+
2033
+ ```js
2034
+ // The host, when its saved-schema answers land (rows = the unnamed answer's data)
2035
+ import {drafts_of_saved_answer} from "@yuneta/gobj-ui/src/host_drafts.js";
2036
+ gobj_send_event(editor, "EV_DRAFTS", {drafts: drafts_of_saved_answer(rows)}, gobj);
2037
+ // -> {drafts: {treedb_authzs: ["users"]}}
2038
+ ```
2039
+
2024
2040
  What the screens offer:
2025
2041
 
2026
2042
  | Screen | What it is for |
@@ -70255,6 +70255,44 @@ function register_c_yui_treedb_schema() {
70255
70255
  return create_gclass$2(GCLASS_NAME$2);
70256
70256
  }
70257
70257
  //#endregion
70258
+ //#region src/host_drafts.js
70259
+ /***********************************************************************
70260
+ * host_drafts.js
70261
+ *
70262
+ * The topics a schema editor shows as DRAFTS (edited in
70263
+ * __system__, not yet saved), told by the host from data. The
70264
+ * editor marks a topic when a write of this session lands on
70265
+ * it, and that mark lived in the memory of the session only:
70266
+ * a reload of the page, a reconnect or a refresh of the model
70267
+ * lost the chip, the banner and the export warning while
70268
+ * __system__ still differed from the file in use (N13 of the
70269
+ * 2026-09-22 review). The host reads that difference from
70270
+ * C_TREEDB's `saved-schema` (`draft_changed`, by treedb) and
70271
+ * hands it here; the editor applies it to its model.
70272
+ *
70273
+ * Copyright (c) 2026, ArtGins.
70274
+ * All Rights Reserved.
70275
+ ***********************************************************************/
70276
+ /************************************************************
70277
+ * Mark in `written` the topics of `model` the host names as
70278
+ * drafts: `drafts` is {treedb_name: [topic names]}. A treedb or a
70279
+ * topic the model does not hold is ignored. Returns how many were
70280
+ * marked.
70281
+ ************************************************************/
70282
+ function mark_host_drafts(written, model, drafts) {
70283
+ let marked = 0;
70284
+ if (!written || !model || !Array.isArray(model.treedbs) || !drafts) return marked;
70285
+ for (let treedb of model.treedbs) {
70286
+ const names = drafts[treedb.id];
70287
+ if (!Array.isArray(names) || names.length === 0) continue;
70288
+ for (let topic of treedb.topics || []) if (names.includes(topic.name)) {
70289
+ written[topic.id] = true;
70290
+ marked++;
70291
+ }
70292
+ }
70293
+ return marked;
70294
+ }
70295
+ //#endregion
70258
70296
  //#region src/schema_validate.js
70259
70297
  /***********************************************************************
70260
70298
  * schema_validate.js
@@ -71981,6 +72019,7 @@ function mt_create$1(gobj) {
71981
72019
  let priv = gobj.priv;
71982
72020
  priv.baseline = {};
71983
72021
  priv.written = {};
72022
+ priv.host_drafts = {};
71984
72023
  priv.save_queue = [];
71985
72024
  build_ui$1(gobj);
71986
72025
  let subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
@@ -72222,6 +72261,7 @@ function start_measuring(gobj) {
72222
72261
  priv.written = {};
72223
72262
  if (!priv.model) return;
72224
72263
  for (let treedb of priv.model.treedbs) for (let topic of treedb.topics) priv.baseline[topic.id] = topic.topic_version;
72264
+ mark_host_drafts(priv.written, priv.model, priv.host_drafts);
72225
72265
  }
72226
72266
  /***************************************************************
72227
72267
  * "This topic was written in this session and is not saved."
@@ -74659,6 +74699,23 @@ function ac_refresh$1(gobj, event, kw, src) {
74659
74699
  return request_model(gobj);
74660
74700
  }
74661
74701
  /***************************************************************
74702
+ * {drafts: {treedb_name: [topic names]}} -- the host says which
74703
+ * topics hold a draft in __system__ that the file in use does not
74704
+ * have (C_TREEDB's saved-schema, `draft_changed`). The mark of a
74705
+ * write lived in this session's memory only, so a reload of the
74706
+ * page, a reconnect or a refresh showed no draft while __system__
74707
+ * still differed from the file (N13 of the 2026-09-22 review).
74708
+ * Replaces what the host said before; what this session wrote
74709
+ * stays marked.
74710
+ ***************************************************************/
74711
+ function ac_drafts(gobj, event, kw, src) {
74712
+ let priv = gobj.priv;
74713
+ priv.host_drafts = kw && kw.drafts && typeof kw.drafts === "object" ? kw.drafts : {};
74714
+ mark_host_drafts(priv.written, priv.model, priv.host_drafts);
74715
+ if (priv.model) render(gobj);
74716
+ return 0;
74717
+ }
74718
+ /***************************************************************
74662
74719
  * Moving about.
74663
74720
  ***************************************************************/
74664
74721
  function ac_select_treedb(gobj, event, kw, src) {
@@ -75132,6 +75189,11 @@ function create_gclass$1(gclass_name) {
75132
75189
  "EV_REFRESH",
75133
75190
  ac_refresh$1,
75134
75191
  null
75192
+ ],
75193
+ [
75194
+ "EV_DRAFTS",
75195
+ ac_drafts,
75196
+ null
75135
75197
  ]
75136
75198
  ];
75137
75199
  const TREEDB_WIDE = [
@@ -75280,6 +75342,7 @@ function create_gclass$1(gclass_name) {
75280
75342
  ["EV_TRANSPORT_STATE", 0],
75281
75343
  ["EV_LANGUAGE_CHANGED", 0],
75282
75344
  ["EV_REFRESH", 0],
75345
+ ["EV_DRAFTS", 0],
75283
75346
  ["EV_SELECT_TREEDB", 0],
75284
75347
  ["EV_SELECT_TOPIC", 0],
75285
75348
  ["EV_BACK", 0],
@@ -70250,6 +70250,44 @@ function register_c_yui_treedb_schema() {
70250
70250
  return create_gclass$2(GCLASS_NAME$2);
70251
70251
  }
70252
70252
  //#endregion
70253
+ //#region src/host_drafts.js
70254
+ /***********************************************************************
70255
+ * host_drafts.js
70256
+ *
70257
+ * The topics a schema editor shows as DRAFTS (edited in
70258
+ * __system__, not yet saved), told by the host from data. The
70259
+ * editor marks a topic when a write of this session lands on
70260
+ * it, and that mark lived in the memory of the session only:
70261
+ * a reload of the page, a reconnect or a refresh of the model
70262
+ * lost the chip, the banner and the export warning while
70263
+ * __system__ still differed from the file in use (N13 of the
70264
+ * 2026-09-22 review). The host reads that difference from
70265
+ * C_TREEDB's `saved-schema` (`draft_changed`, by treedb) and
70266
+ * hands it here; the editor applies it to its model.
70267
+ *
70268
+ * Copyright (c) 2026, ArtGins.
70269
+ * All Rights Reserved.
70270
+ ***********************************************************************/
70271
+ /************************************************************
70272
+ * Mark in `written` the topics of `model` the host names as
70273
+ * drafts: `drafts` is {treedb_name: [topic names]}. A treedb or a
70274
+ * topic the model does not hold is ignored. Returns how many were
70275
+ * marked.
70276
+ ************************************************************/
70277
+ function mark_host_drafts(written, model, drafts) {
70278
+ let marked = 0;
70279
+ if (!written || !model || !Array.isArray(model.treedbs) || !drafts) return marked;
70280
+ for (let treedb of model.treedbs) {
70281
+ const names = drafts[treedb.id];
70282
+ if (!Array.isArray(names) || names.length === 0) continue;
70283
+ for (let topic of treedb.topics || []) if (names.includes(topic.name)) {
70284
+ written[topic.id] = true;
70285
+ marked++;
70286
+ }
70287
+ }
70288
+ return marked;
70289
+ }
70290
+ //#endregion
70253
70291
  //#region src/schema_validate.js
70254
70292
  /***********************************************************************
70255
70293
  * schema_validate.js
@@ -71976,6 +72014,7 @@ function mt_create$1(gobj) {
71976
72014
  let priv = gobj.priv;
71977
72015
  priv.baseline = {};
71978
72016
  priv.written = {};
72017
+ priv.host_drafts = {};
71979
72018
  priv.save_queue = [];
71980
72019
  build_ui$1(gobj);
71981
72020
  let subscriber = gobj_read_pointer_attr(gobj, "subscriber");
@@ -72217,6 +72256,7 @@ function start_measuring(gobj) {
72217
72256
  priv.written = {};
72218
72257
  if (!priv.model) return;
72219
72258
  for (let treedb of priv.model.treedbs) for (let topic of treedb.topics) priv.baseline[topic.id] = topic.topic_version;
72259
+ mark_host_drafts(priv.written, priv.model, priv.host_drafts);
72220
72260
  }
72221
72261
  /***************************************************************
72222
72262
  * "This topic was written in this session and is not saved."
@@ -74654,6 +74694,23 @@ function ac_refresh$1(gobj, event, kw, src) {
74654
74694
  return request_model(gobj);
74655
74695
  }
74656
74696
  /***************************************************************
74697
+ * {drafts: {treedb_name: [topic names]}} -- the host says which
74698
+ * topics hold a draft in __system__ that the file in use does not
74699
+ * have (C_TREEDB's saved-schema, `draft_changed`). The mark of a
74700
+ * write lived in this session's memory only, so a reload of the
74701
+ * page, a reconnect or a refresh showed no draft while __system__
74702
+ * still differed from the file (N13 of the 2026-09-22 review).
74703
+ * Replaces what the host said before; what this session wrote
74704
+ * stays marked.
74705
+ ***************************************************************/
74706
+ function ac_drafts(gobj, event, kw, src) {
74707
+ let priv = gobj.priv;
74708
+ priv.host_drafts = kw && kw.drafts && typeof kw.drafts === "object" ? kw.drafts : {};
74709
+ mark_host_drafts(priv.written, priv.model, priv.host_drafts);
74710
+ if (priv.model) render(gobj);
74711
+ return 0;
74712
+ }
74713
+ /***************************************************************
74657
74714
  * Moving about.
74658
74715
  ***************************************************************/
74659
74716
  function ac_select_treedb(gobj, event, kw, src) {
@@ -75127,6 +75184,11 @@ function create_gclass$1(gclass_name) {
75127
75184
  "EV_REFRESH",
75128
75185
  ac_refresh$1,
75129
75186
  null
75187
+ ],
75188
+ [
75189
+ "EV_DRAFTS",
75190
+ ac_drafts,
75191
+ null
75130
75192
  ]
75131
75193
  ];
75132
75194
  const TREEDB_WIDE = [
@@ -75275,6 +75337,7 @@ function create_gclass$1(gclass_name) {
75275
75337
  ["EV_TRANSPORT_STATE", 0],
75276
75338
  ["EV_LANGUAGE_CHANGED", 0],
75277
75339
  ["EV_REFRESH", 0],
75340
+ ["EV_DRAFTS", 0],
75278
75341
  ["EV_SELECT_TREEDB", 0],
75279
75342
  ["EV_SELECT_TOPIC", 0],
75280
75343
  ["EV_BACK", 0],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.25.2",
3
+ "version": "7.25.3",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
@@ -113,6 +113,7 @@ import {
113
113
  empty_string,
114
114
  } from "@yuneta/gobj-js";
115
115
  import {yui_tint} from "./bulma_tint.js";
116
+ import {mark_host_drafts} from "./host_drafts.js";
116
117
 
117
118
  import "./c_yui_schema_editor.css";
118
119
 
@@ -248,6 +249,7 @@ function mt_create(gobj)
248
249
 
249
250
  priv.baseline = {};
250
251
  priv.written = {};
252
+ priv.host_drafts = {}; /* what the host says is a draft, {treedb: [topics]} (EV_DRAFTS) */
251
253
  priv.save_queue = [];
252
254
 
253
255
  build_ui(gobj);
@@ -571,6 +573,9 @@ function start_measuring(gobj)
571
573
  priv.baseline[topic.id] = topic.topic_version;
572
574
  }
573
575
  }
576
+ /* A draft is a draft after a reload too: what the host said stays
577
+ * marked (host_drafts.js) until the host says otherwise. */
578
+ mark_host_drafts(priv.written, priv.model, priv.host_drafts);
574
579
  }
575
580
 
576
581
  /***************************************************************
@@ -2755,6 +2760,28 @@ function ac_refresh(gobj, event, kw, src)
2755
2760
  return request_model(gobj);
2756
2761
  }
2757
2762
 
2763
+ /***************************************************************
2764
+ * {drafts: {treedb_name: [topic names]}} -- the host says which
2765
+ * topics hold a draft in __system__ that the file in use does not
2766
+ * have (C_TREEDB's saved-schema, `draft_changed`). The mark of a
2767
+ * write lived in this session's memory only, so a reload of the
2768
+ * page, a reconnect or a refresh showed no draft while __system__
2769
+ * still differed from the file (N13 of the 2026-09-22 review).
2770
+ * Replaces what the host said before; what this session wrote
2771
+ * stays marked.
2772
+ ***************************************************************/
2773
+ function ac_drafts(gobj, event, kw, src)
2774
+ {
2775
+ let priv = gobj.priv;
2776
+
2777
+ priv.host_drafts = (kw && kw.drafts && typeof kw.drafts === "object")? kw.drafts: {};
2778
+ mark_host_drafts(priv.written, priv.model, priv.host_drafts);
2779
+ if(priv.model) {
2780
+ render(gobj);
2781
+ }
2782
+ return 0;
2783
+ }
2784
+
2758
2785
  /***************************************************************
2759
2786
  * Moving about.
2760
2787
  ***************************************************************/
@@ -3328,7 +3355,8 @@ function create_gclass(gclass_name)
3328
3355
  ["EV_HIDE", ac_hide, null],
3329
3356
  ["EV_TRANSPORT_STATE", ac_transport_state, null],
3330
3357
  ["EV_LANGUAGE_CHANGED", ac_language_changed, null],
3331
- ["EV_REFRESH", ac_refresh, null]
3358
+ ["EV_REFRESH", ac_refresh, null],
3359
+ ["EV_DRAFTS", ac_drafts, null]
3332
3360
  ];
3333
3361
 
3334
3362
  /* What a treedb is open for: reading it whole, and writing it
@@ -3411,6 +3439,7 @@ function create_gclass(gclass_name)
3411
3439
  ["EV_TRANSPORT_STATE", 0],
3412
3440
  ["EV_LANGUAGE_CHANGED", 0],
3413
3441
  ["EV_REFRESH", 0],
3442
+ ["EV_DRAFTS", 0],
3414
3443
 
3415
3444
  /* From this view's own screens. */
3416
3445
  ["EV_SELECT_TREEDB", 0],
@@ -0,0 +1,68 @@
1
+ /***********************************************************************
2
+ * host_drafts.js
3
+ *
4
+ * The topics a schema editor shows as DRAFTS (edited in
5
+ * __system__, not yet saved), told by the host from data. The
6
+ * editor marks a topic when a write of this session lands on
7
+ * it, and that mark lived in the memory of the session only:
8
+ * a reload of the page, a reconnect or a refresh of the model
9
+ * lost the chip, the banner and the export warning while
10
+ * __system__ still differed from the file in use (N13 of the
11
+ * 2026-09-22 review). The host reads that difference from
12
+ * C_TREEDB's `saved-schema` (`draft_changed`, by treedb) and
13
+ * hands it here; the editor applies it to its model.
14
+ *
15
+ * Copyright (c) 2026, ArtGins.
16
+ * All Rights Reserved.
17
+ ***********************************************************************/
18
+
19
+ /************************************************************
20
+ * Mark in `written` the topics of `model` the host names as
21
+ * drafts: `drafts` is {treedb_name: [topic names]}. A treedb or a
22
+ * topic the model does not hold is ignored. Returns how many were
23
+ * marked.
24
+ ************************************************************/
25
+ function mark_host_drafts(written, model, drafts)
26
+ {
27
+ let marked = 0;
28
+ if(!written || !model || !Array.isArray(model.treedbs) || !drafts) {
29
+ return marked;
30
+ }
31
+ for(let treedb of model.treedbs) {
32
+ const names = drafts[treedb.id];
33
+ if(!Array.isArray(names) || names.length === 0) {
34
+ continue;
35
+ }
36
+ for(let topic of treedb.topics || []) {
37
+ if(names.includes(topic.name)) {
38
+ written[topic.id] = true;
39
+ marked++;
40
+ }
41
+ }
42
+ }
43
+ return marked;
44
+ }
45
+
46
+ /************************************************************
47
+ * What a `saved-schema` answer says, as {treedb_name: [topic names]}:
48
+ * its `data` is one row per treedb (the unnamed form of the command),
49
+ * each carrying `draft_changed` {topic: true}.
50
+ ************************************************************/
51
+ function drafts_of_saved_answer(rows, into)
52
+ {
53
+ const drafts = into || {};
54
+ for(let row of Array.isArray(rows)? rows: []) {
55
+ const treedb_name = row && row.treedb_name;
56
+ const changed = row && row.data && row.data.draft_changed;
57
+ if(!treedb_name || !changed || typeof changed !== "object") {
58
+ continue;
59
+ }
60
+ drafts[treedb_name] = Object.keys(changed).filter((k) => !!changed[k]);
61
+ }
62
+ return drafts;
63
+ }
64
+
65
+ export {
66
+ mark_host_drafts,
67
+ drafts_of_saved_answer,
68
+ };
@@ -0,0 +1,62 @@
1
+ import {describe, it, expect} from "vitest";
2
+ import {mark_host_drafts, drafts_of_saved_answer} from "./host_drafts.js";
3
+
4
+ const model = {
5
+ treedbs: [
6
+ {id: "treedb_a", name: "treedb_a", topics: [
7
+ {id: "treedb_a^users", name: "users"},
8
+ {id: "treedb_a^roles", name: "roles"},
9
+ ]},
10
+ {id: "treedb_b", name: "treedb_b", topics: [
11
+ {id: "treedb_b^users", name: "users"},
12
+ ]},
13
+ ],
14
+ };
15
+
16
+ describe("mark_host_drafts", () => {
17
+
18
+ it("marks the topics the host names, by treedb (N13)", () => {
19
+ /* The mark lived in the memory of one session: after a reload the
20
+ * editor showed no draft while __system__ still differed from the
21
+ * file in use. Rebuilt from the host's data. */
22
+ const written = {};
23
+ const n = mark_host_drafts(written, model, {treedb_a: ["users"]});
24
+ expect(n).toBe(1);
25
+ expect(written).toEqual({"treedb_a^users": true});
26
+ });
27
+
28
+ it("a topic of the same name in another treedb is another topic", () => {
29
+ const written = {};
30
+ mark_host_drafts(written, model, {treedb_b: ["users"]});
31
+ expect(written).toEqual({"treedb_b^users": true});
32
+ });
33
+
34
+ it("keeps what the session wrote, ignores what the model does not hold", () => {
35
+ const written = {"treedb_a^roles": true};
36
+ const n = mark_host_drafts(written, model, {treedb_a: ["nobody"], treedb_z: ["users"]});
37
+ expect(n).toBe(0);
38
+ expect(written).toEqual({"treedb_a^roles": true});
39
+ });
40
+
41
+ it("no model, no drafts: nothing", () => {
42
+ expect(mark_host_drafts({}, null, {treedb_a: ["users"]})).toBe(0);
43
+ expect(mark_host_drafts({}, model, null)).toBe(0);
44
+ });
45
+ });
46
+
47
+ describe("drafts_of_saved_answer", () => {
48
+
49
+ it("reads draft_changed of each treedb row", () => {
50
+ const rows = [
51
+ {treedb_name: "treedb_a", result: 0, data: {draft_changed: {users: true, roles: false}}},
52
+ {treedb_name: "treedb_b", result: 0, data: {draft_changed: {}}},
53
+ {treedb_name: "treedb_c", result: -1, comment: "not open here"},
54
+ ];
55
+ expect(drafts_of_saved_answer(rows)).toEqual({treedb_a: ["users"], treedb_b: []});
56
+ });
57
+
58
+ it("a node older than draft_changed says nothing", () => {
59
+ expect(drafts_of_saved_answer([{treedb_name: "t", data: {can_apply: false}}])).toEqual({});
60
+ expect(drafts_of_saved_answer(null)).toEqual({});
61
+ });
62
+ });