@yuneta/gobj-ui 6.1.0 → 6.1.2
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 +20 -7
- package/dist/gobj-ui.cjs.js +248 -84
- package/dist/gobj-ui.es.js +248 -84
- package/package.json +1 -1
- package/src/c_yui_schema_editor.js +124 -36
- package/src/c_yui_treedb_schema.js +17 -7
- package/src/schema_descs.js +14 -6
- package/src/schema_descs.test.js +25 -0
- package/src/schema_import.js +17 -6
- package/src/schema_import.test.js +21 -0
- package/src/schema_model.js +26 -0
- package/src/schema_model.test.js +25 -0
- package/src/schema_to_c.js +22 -8
- package/src/schema_to_c.test.js +47 -0
- package/src/schema_write_options.js +60 -0
- package/src/schema_write_options.test.js +65 -0
- package/src/yui_icons.css +14 -0
package/README.md
CHANGED
|
@@ -539,12 +539,15 @@ schema, so the drawing can be held against the ASCII one in its `.c`.
|
|
|
539
539
|
- Events: `EV_SHOW`, `EV_REBUILD`, `EV_THEME` (restyle — it repaints the G6
|
|
540
540
|
graph in place, preserving the user's zoom/pan), plus `EV_NODE_CLICK`, which
|
|
541
541
|
a node click sends into the FSM. **With a `node_route` the click IS a
|
|
542
|
-
navigation** and this view makes it
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
542
|
+
navigation** and this view makes it. **Without one the click is dropped,
|
|
543
|
+
unless the host asked for it with `with_node_click`** — then it is published
|
|
544
|
+
as `{topic}`, for a host that draws the same picture inside its own screens
|
|
545
|
+
and opens the topic in place, with no hash involved. That host must declare
|
|
546
|
+
`EV_NODE_CLICK` in its own FSM, as with every event a child publishes, which
|
|
547
|
+
is exactly why it is opt-in: the CHILD subscription model subscribes a host
|
|
548
|
+
to ALL of this view's events, so publishing one unasked turns a click into
|
|
549
|
+
"Event NOT DEFINED in state" underneath a host that never wanted it. Since
|
|
550
|
+
6.1.2 (6.1.0 and 6.1.1 published it unconditionally).
|
|
548
551
|
|
|
549
552
|
Barrel-exported and public from 4.0.0. Renders with `@antv/g6`; the cards are
|
|
550
553
|
HTML nodes carrying their own inline colours, so a theme switch repaints them in
|
|
@@ -631,11 +634,21 @@ else.
|
|
|
631
634
|
`ST_LOADING`, `ST_EMPTY`, `ST_TREEDBS`, `ST_TOPICS`, `ST_DIAGRAM`,
|
|
632
635
|
`ST_COLUMNS`, `ST_SAVING`.
|
|
633
636
|
|
|
637
|
+
**Three words decide whether a write does what it says** (`schema_write_options.js`,
|
|
638
|
+
and they are tested because getting one wrong costs a store repaired by hand):
|
|
639
|
+
a create goes through `update-node` and not `create-node`, because only that
|
|
640
|
+
path carries **`autolink`** — and without a link a new column belongs to no
|
|
641
|
+
topic. `autolink` rewrites a node's links from the fkey fields the record
|
|
642
|
+
carries, so it goes with a create, where the record has one, **and with nothing
|
|
643
|
+
else**: on a partial update it finds none, reads that as "no parents", and
|
|
644
|
+
detaches the node. Raising a topic's version with it on unlinked that topic
|
|
645
|
+
from its treedb, and the write answered success.
|
|
646
|
+
|
|
634
647
|
The logic is pure and tested apart from the view: `schema_model.js` (the three
|
|
635
648
|
lists regrouped — grouping follows the **fkey**, not a split of the qualified
|
|
636
649
|
id on `.`, which works right up to the first name that carries one),
|
|
637
650
|
`schema_validate.js`, `schema_descs.js`, `schema_to_c.js`, `schema_import.js`,
|
|
638
|
-
`schema_flags.js`. Since 6.1.0.
|
|
651
|
+
`schema_flags.js`, `schema_write_options.js`. Since 6.1.0.
|
|
639
652
|
|
|
640
653
|
### Frontend view — `setup_frontend_view`
|
|
641
654
|
|
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -19831,6 +19831,7 @@ var attrs_table$3 = [
|
|
|
19831
19831
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "subscriber", 0, null, "Subscriber of output events"),
|
|
19832
19832
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "descs", 0, null, "Treedb schema: {topic_name: desc}"),
|
|
19833
19833
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "node_route", 0, "", "Hash-route template with a {topic} placeholder: a node click opens that topic (e.g. '#/topics/db/<sel>/{topic}')"),
|
|
19834
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_node_click", 0, false, "With no `node_route`, PUBLISH a node click as EV_NODE_CLICK instead of dropping it. Opt-in, because a subscriber has to DECLARE the event: the hosts that route by hash, and the ones that want nothing, must not have it appear underneath them"),
|
|
19834
19835
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "system", 0, false, "Include system topics (__*__) too"),
|
|
19835
19836
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "$container", 0, null, "Root HTML element"),
|
|
19836
19837
|
(0, _yuneta_gobj_js.SDATA_END)()
|
|
@@ -20235,19 +20236,26 @@ function destroy_graph(gobj) {
|
|
|
20235
20236
|
* With a `node_route` the click IS a navigation and this view
|
|
20236
20237
|
* makes it: that is what the route was handed down for.
|
|
20237
20238
|
*
|
|
20238
|
-
* Without one it is still an action, and it
|
|
20239
|
-
* mounted this view — the schema editor draws the same
|
|
20240
|
-
* inside its own screens, where a topic opens in place
|
|
20241
|
-
* hash is involved.
|
|
20242
|
-
*
|
|
20243
|
-
*
|
|
20239
|
+
* Without one it is still an action, and it can belong to
|
|
20240
|
+
* whoever mounted this view — the schema editor draws the same
|
|
20241
|
+
* picture inside its own screens, where a topic opens in place
|
|
20242
|
+
* and no hash is involved. That host asks for it with
|
|
20243
|
+
* `with_node_click` and DECLARES EV_NODE_CLICK in its own FSM,
|
|
20244
|
+
* as with every event a child publishes.
|
|
20245
|
+
*
|
|
20246
|
+
* It is opt-in and not the default for exactly that reason: the
|
|
20247
|
+
* CHILD subscription model subscribes the host to ALL of this
|
|
20248
|
+
* view's events, so publishing one unasked turns a click into
|
|
20249
|
+
* "Event NOT DEFINED in state" in every host that never wanted
|
|
20250
|
+
* it — the topics view that mounts this as its schema landing,
|
|
20251
|
+
* and the offline demo that mounts it against a json file.
|
|
20244
20252
|
************************************************************/
|
|
20245
20253
|
function ac_node_click$2(gobj, event, kw, src) {
|
|
20246
20254
|
let topic = kw && kw.node_id;
|
|
20247
20255
|
let route = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "node_route");
|
|
20248
20256
|
if (!topic) return 0;
|
|
20249
20257
|
if (!route) {
|
|
20250
|
-
(0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_NODE_CLICK", { topic });
|
|
20258
|
+
if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_node_click")) (0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_NODE_CLICK", { topic });
|
|
20251
20259
|
return 0;
|
|
20252
20260
|
}
|
|
20253
20261
|
let href = route.replace("{topic}", topic);
|
|
@@ -20587,6 +20595,22 @@ function build_schema_model(records) {
|
|
|
20587
20595
|
};
|
|
20588
20596
|
}
|
|
20589
20597
|
/***************************************************************
|
|
20598
|
+
* Is there a value here at all?
|
|
20599
|
+
*
|
|
20600
|
+
* The store answers a `blob` column that was never set with an
|
|
20601
|
+
* EMPTY COLLECTION, not with nothing: `enum: {}`, `hook: {}`,
|
|
20602
|
+
* `default: {}`. Read as "a value", those turn every export into
|
|
20603
|
+
* a schema full of empty objects — and `'hook': {}` in a literal
|
|
20604
|
+
* is a hook with no mapping, which is a link the treedb builds
|
|
20605
|
+
* and nothing writes.
|
|
20606
|
+
***************************************************************/
|
|
20607
|
+
function is_empty_value(value) {
|
|
20608
|
+
if (value === null || value === void 0 || value === "") return true;
|
|
20609
|
+
if (Array.isArray(value)) return value.length === 0;
|
|
20610
|
+
if (typeof value === "object") return Object.keys(value).length === 0;
|
|
20611
|
+
return false;
|
|
20612
|
+
}
|
|
20613
|
+
/***************************************************************
|
|
20590
20614
|
* The fields of a column that are DECLARED as one thing and
|
|
20591
20615
|
* STORED as another.
|
|
20592
20616
|
*
|
|
@@ -20924,6 +20948,7 @@ var COL_STORAGE_FIELDS = [
|
|
|
20924
20948
|
"value",
|
|
20925
20949
|
"topics",
|
|
20926
20950
|
"order",
|
|
20951
|
+
"_geometry",
|
|
20927
20952
|
"__md_treedb__"
|
|
20928
20953
|
];
|
|
20929
20954
|
var TOPIC_STORAGE_FIELDS = [
|
|
@@ -20932,6 +20957,7 @@ var TOPIC_STORAGE_FIELDS = [
|
|
|
20932
20957
|
"treedbs",
|
|
20933
20958
|
"cols",
|
|
20934
20959
|
"order",
|
|
20960
|
+
"_geometry",
|
|
20935
20961
|
"__md_treedb__"
|
|
20936
20962
|
];
|
|
20937
20963
|
var COL_JSON_FIELDS = [
|
|
@@ -20955,13 +20981,16 @@ function col_desc(col) {
|
|
|
20955
20981
|
let desc = {};
|
|
20956
20982
|
for (let [key, value] of Object.entries(record)) {
|
|
20957
20983
|
if (COL_STORAGE_FIELDS.indexOf(key) >= 0) continue;
|
|
20958
|
-
if (value
|
|
20959
|
-
|
|
20984
|
+
if (is_empty_value(value)) continue;
|
|
20985
|
+
let read = COL_JSON_FIELDS.indexOf(key) >= 0 ? as_json(value) : value;
|
|
20986
|
+
if (is_empty_value(read)) continue;
|
|
20987
|
+
desc[key] = read;
|
|
20960
20988
|
}
|
|
20961
20989
|
desc.id = col.name;
|
|
20962
20990
|
desc.flag = col_flags(record);
|
|
20963
20991
|
let hook = col_hook(record);
|
|
20964
|
-
if (hook) desc.hook = hook;
|
|
20992
|
+
if (hook && !is_empty_value(hook)) desc.hook = hook;
|
|
20993
|
+
else delete desc.hook;
|
|
20965
20994
|
let e = col_enum(record);
|
|
20966
20995
|
if (e.length > 0) desc.enum = e;
|
|
20967
20996
|
return desc;
|
|
@@ -20979,7 +21008,7 @@ function topic_descs(treedb) {
|
|
|
20979
21008
|
let desc = {};
|
|
20980
21009
|
for (let [key, value] of Object.entries(record)) {
|
|
20981
21010
|
if (TOPIC_STORAGE_FIELDS.indexOf(key) >= 0) continue;
|
|
20982
|
-
if (value
|
|
21011
|
+
if (is_empty_value(value)) continue;
|
|
20983
21012
|
desc[key] = value;
|
|
20984
21013
|
}
|
|
20985
21014
|
desc.topic_name = topic.name;
|
|
@@ -21036,6 +21065,7 @@ var COL_SKIP = [
|
|
|
21036
21065
|
"value",
|
|
21037
21066
|
"topics",
|
|
21038
21067
|
"order",
|
|
21068
|
+
"_geometry",
|
|
21039
21069
|
"__md_treedb__"
|
|
21040
21070
|
];
|
|
21041
21071
|
var TOPIC_SKIP = [
|
|
@@ -21044,6 +21074,7 @@ var TOPIC_SKIP = [
|
|
|
21044
21074
|
"treedbs",
|
|
21045
21075
|
"cols",
|
|
21046
21076
|
"order",
|
|
21077
|
+
"_geometry",
|
|
21047
21078
|
"__md_treedb__"
|
|
21048
21079
|
];
|
|
21049
21080
|
var JSON_FIELDS$1 = [
|
|
@@ -21097,8 +21128,10 @@ function schema_to_json(treedb) {
|
|
|
21097
21128
|
let jn_topic = { id: topic.name };
|
|
21098
21129
|
for (let [key, value] of Object.entries(record)) {
|
|
21099
21130
|
if (TOPIC_SKIP.indexOf(key) >= 0) continue;
|
|
21100
|
-
if (value
|
|
21101
|
-
|
|
21131
|
+
if (is_empty_value(value)) continue;
|
|
21132
|
+
let read = JSON_FIELDS$1.indexOf(key) >= 0 ? as_json(value) : value;
|
|
21133
|
+
if (is_empty_value(read)) continue;
|
|
21134
|
+
jn_topic[key] = read;
|
|
21102
21135
|
}
|
|
21103
21136
|
if (jn_topic.topic_version !== void 0) jn_topic.topic_version = version(jn_topic.topic_version);
|
|
21104
21137
|
let pkey2s = topic_pkey2s(record);
|
|
@@ -21111,14 +21144,17 @@ function schema_to_json(treedb) {
|
|
|
21111
21144
|
let jn_col = {};
|
|
21112
21145
|
for (let [key, value] of Object.entries(col_record)) {
|
|
21113
21146
|
if (COL_SKIP.indexOf(key) >= 0) continue;
|
|
21114
|
-
if (value
|
|
21115
|
-
|
|
21147
|
+
if (is_empty_value(value)) continue;
|
|
21148
|
+
let read = JSON_FIELDS$1.indexOf(key) >= 0 ? as_json(value) : value;
|
|
21149
|
+
if (is_empty_value(read)) continue;
|
|
21150
|
+
jn_col[key] = read;
|
|
21116
21151
|
}
|
|
21117
21152
|
let flags = col_flags(col_record);
|
|
21118
21153
|
if (flags.length > 0) jn_col.flag = flags;
|
|
21119
21154
|
else delete jn_col.flag;
|
|
21120
21155
|
let hook = col_hook(col_record);
|
|
21121
|
-
if (hook) jn_col.hook = hook;
|
|
21156
|
+
if (hook && !is_empty_value(hook)) jn_col.hook = hook;
|
|
21157
|
+
else delete jn_col.hook;
|
|
21122
21158
|
let e = col_enum(col_record);
|
|
21123
21159
|
if (e.length > 0) jn_col.enum = e;
|
|
21124
21160
|
let ordered_col = {};
|
|
@@ -21303,7 +21339,7 @@ function same_value(field, a, b) {
|
|
|
21303
21339
|
list = list.filter((f) => typeof f === "string" && f.length > 0).sort();
|
|
21304
21340
|
return JSON.stringify(list);
|
|
21305
21341
|
}
|
|
21306
|
-
if (v
|
|
21342
|
+
if (is_empty_value(v)) return "";
|
|
21307
21343
|
if (typeof v === "object") return JSON.stringify(v);
|
|
21308
21344
|
return String(v);
|
|
21309
21345
|
};
|
|
@@ -21316,8 +21352,10 @@ function incoming_col_fields(col) {
|
|
|
21316
21352
|
let out = {};
|
|
21317
21353
|
for (let field of COL_FIELDS) {
|
|
21318
21354
|
let value = col ? col[field] : void 0;
|
|
21319
|
-
if (value
|
|
21320
|
-
|
|
21355
|
+
if (is_empty_value(value)) continue;
|
|
21356
|
+
let read = JSON_FIELDS.indexOf(field) >= 0 ? as_json(value) : value;
|
|
21357
|
+
if (is_empty_value(read)) continue;
|
|
21358
|
+
out[field] = read;
|
|
21321
21359
|
}
|
|
21322
21360
|
if (Array.isArray(out.flag)) out.flag = out.flag.filter((f) => typeof f === "string" && f.length > 0);
|
|
21323
21361
|
else if (typeof out.flag === "string" && out.flag) out.flag = [out.flag];
|
|
@@ -21330,14 +21368,17 @@ function stored_col_fields(record) {
|
|
|
21330
21368
|
let out = {};
|
|
21331
21369
|
for (let field of COL_FIELDS) {
|
|
21332
21370
|
let value = record ? record[field] : void 0;
|
|
21333
|
-
if (value
|
|
21334
|
-
|
|
21371
|
+
if (is_empty_value(value)) continue;
|
|
21372
|
+
let read = JSON_FIELDS.indexOf(field) >= 0 ? as_json(value) : value;
|
|
21373
|
+
if (is_empty_value(read)) continue;
|
|
21374
|
+
out[field] = read;
|
|
21335
21375
|
}
|
|
21336
21376
|
let flags = col_flags(record);
|
|
21337
21377
|
if (flags.length > 0) out.flag = flags;
|
|
21338
21378
|
else delete out.flag;
|
|
21339
21379
|
let hook = col_hook(record);
|
|
21340
|
-
if (hook) out.hook = hook;
|
|
21380
|
+
if (hook && !is_empty_value(hook)) out.hook = hook;
|
|
21381
|
+
else delete out.hook;
|
|
21341
21382
|
let e = col_enum(record);
|
|
21342
21383
|
if (e.length > 0) out.enum = e;
|
|
21343
21384
|
return out;
|
|
@@ -21962,6 +22003,61 @@ function toggle_flag(current, name, on) {
|
|
|
21962
22003
|
return out;
|
|
21963
22004
|
}
|
|
21964
22005
|
//#endregion
|
|
22006
|
+
//#region src/schema_write_options.js
|
|
22007
|
+
/***********************************************************************
|
|
22008
|
+
* schema_write_options.js
|
|
22009
|
+
*
|
|
22010
|
+
* The OPTIONS a schema write travels with, and why each one.
|
|
22011
|
+
*
|
|
22012
|
+
* Three words decide whether a write does what it says, and all
|
|
22013
|
+
* three are easy to get wrong in a way that succeeds:
|
|
22014
|
+
*
|
|
22015
|
+
* `create` a node that is not there yet is written by
|
|
22016
|
+
* `update-node` with this, not by `create-node`. The
|
|
22017
|
+
* two are not the same call: only the update path
|
|
22018
|
+
* carries `autolink`, and without a link a new column
|
|
22019
|
+
* belongs to no topic.
|
|
22020
|
+
*
|
|
22021
|
+
* `autolink` turns the fkey carried IN THE RECORD into a link.
|
|
22022
|
+
* It rewrites the node's links from the fkey fields
|
|
22023
|
+
* the record has — so on a PARTIAL update, which
|
|
22024
|
+
* carries none, it reads that as "no parents" and
|
|
22025
|
+
* UNLINKS the node. Raising a topic's version with it
|
|
22026
|
+
* on detached that topic from its treedb, and the
|
|
22027
|
+
* write returned success. It goes with a create,
|
|
22028
|
+
* where the record carries its fkey, and with nothing
|
|
22029
|
+
* else.
|
|
22030
|
+
*
|
|
22031
|
+
* `force` a delete of a node something points at. A column is
|
|
22032
|
+
* pointed at by the topic that owns it, which is not
|
|
22033
|
+
* a reason to refuse.
|
|
22034
|
+
*
|
|
22035
|
+
* Pure and tested, because the cost of getting it wrong is a
|
|
22036
|
+
* store that has to be repaired by hand, and nothing in the
|
|
22037
|
+
* answer says it happened.
|
|
22038
|
+
*
|
|
22039
|
+
* Copyright (c) 2026, ArtGins.
|
|
22040
|
+
* All Rights Reserved.
|
|
22041
|
+
***********************************************************************/
|
|
22042
|
+
/***************************************************************
|
|
22043
|
+
* write_command(op) -> the treedb command that performs it
|
|
22044
|
+
***************************************************************/
|
|
22045
|
+
function write_command(op) {
|
|
22046
|
+
return op === "delete" ? "delete-node" : "update-node";
|
|
22047
|
+
}
|
|
22048
|
+
/***************************************************************
|
|
22049
|
+
* write_options(op) -> the options it travels with
|
|
22050
|
+
***************************************************************/
|
|
22051
|
+
function write_options(op) {
|
|
22052
|
+
if (op === "delete") return { force: true };
|
|
22053
|
+
if (op === "create") return {
|
|
22054
|
+
list_dict: true,
|
|
22055
|
+
create: true,
|
|
22056
|
+
autolink: true
|
|
22057
|
+
};
|
|
22058
|
+
return { list_dict: true };
|
|
22059
|
+
}
|
|
22060
|
+
//#endregion
|
|
21965
22061
|
//#region src/c_yui_schema_editor.js
|
|
21966
22062
|
/***********************************************************************
|
|
21967
22063
|
* c_yui_schema_editor.js
|
|
@@ -22281,10 +22377,12 @@ function remote_command(gobj, command, kw) {
|
|
|
22281
22377
|
service: treedb_name,
|
|
22282
22378
|
treedb_name
|
|
22283
22379
|
}, kw || {});
|
|
22284
|
-
full_kw.__md_command__ =
|
|
22380
|
+
full_kw.__md_command__ = {
|
|
22285
22381
|
purpose: PURPOSE,
|
|
22286
|
-
|
|
22287
|
-
|
|
22382
|
+
topic_name: kw && kw.topic_name || "",
|
|
22383
|
+
record_id: kw && kw.record_id || ""
|
|
22384
|
+
};
|
|
22385
|
+
delete full_kw.record_id;
|
|
22288
22386
|
let ret = (0, _yuneta_gobj_js.gobj_command)(remote, command, full_kw, gobj);
|
|
22289
22387
|
if (ret) {
|
|
22290
22388
|
(0, _yuneta_gobj_js.log_error)(ret);
|
|
@@ -22533,9 +22631,9 @@ function render_toolbar(gobj) {
|
|
|
22533
22631
|
let $right = [];
|
|
22534
22632
|
if (priv.treedb_id && has_model) {
|
|
22535
22633
|
$right.push(toolbar_button("SCHEMA_DIAGRAM_BTN", "yi-hexagon-nodes", priv.diagram ? "topics" : "diagram", "EV_TOGGLE_DIAGRAM", false));
|
|
22536
|
-
$right.push(toolbar_button("SCHEMA_VALIDATE_BTN", "yi-
|
|
22537
|
-
$right.push(toolbar_button("SCHEMA_EXPORT_BTN", "yi-
|
|
22538
|
-
if (!readonly) $right.push(toolbar_button("SCHEMA_IMPORT_BTN", "yi-
|
|
22634
|
+
$right.push(toolbar_button("SCHEMA_VALIDATE_BTN", "yi-square-check", "check", "EV_VALIDATE", false));
|
|
22635
|
+
$right.push(toolbar_button("SCHEMA_EXPORT_BTN", "yi-download", "export", "EV_EXPORT", false));
|
|
22636
|
+
if (!readonly) $right.push(toolbar_button("SCHEMA_IMPORT_BTN", "yi-upload", "import", "EV_IMPORT", false));
|
|
22539
22637
|
}
|
|
22540
22638
|
if (priv.topic_name && !readonly && !priv.diagram) $right.push(toolbar_button("SCHEMA_ADD_COL_BTN", "yi-plus", "new column", "EV_ADD_COLUMN", false));
|
|
22541
22639
|
if (priv.treedb_id && !priv.topic_name && !readonly && !priv.diagram) $right.push(toolbar_button("SCHEMA_ADD_TOPIC_BTN", "yi-plus", "new topic", "EV_ADD_TOPIC", false));
|
|
@@ -22738,13 +22836,16 @@ function render_topics(gobj, $body) {
|
|
|
22738
22836
|
return;
|
|
22739
22837
|
}
|
|
22740
22838
|
let readonly = is_readonly(gobj);
|
|
22839
|
+
let twice = repeated_names(treedb.topics);
|
|
22741
22840
|
let $rows = [];
|
|
22742
22841
|
for (let topic of treedb.topics) {
|
|
22743
22842
|
let pending = priv.written[topic.id] && String(priv.baseline[topic.id]) === String(topic.topic_version);
|
|
22843
|
+
let ambiguous = !!twice[topic.name];
|
|
22844
|
+
let address = ambiguous ? topic.id : topic.name;
|
|
22744
22845
|
let $actions = [];
|
|
22745
22846
|
if (!readonly) {
|
|
22746
|
-
$actions.push(row_icon(gobj, "SCHEMA_TOPIC_EDIT", "yi-pen", "edit topic", "EV_EDIT_TOPIC", { topic:
|
|
22747
|
-
$actions.push(row_icon(gobj, "SCHEMA_TOPIC_DELETE", "yi-trash", "delete topic", "EV_DELETE_TOPIC", { topic:
|
|
22847
|
+
$actions.push(row_icon(gobj, "SCHEMA_TOPIC_EDIT", "yi-pen", "edit topic", "EV_EDIT_TOPIC", { topic: address }));
|
|
22848
|
+
$actions.push(row_icon(gobj, "SCHEMA_TOPIC_DELETE", "yi-trash", "delete topic", "EV_DELETE_TOPIC", { topic: address }));
|
|
22748
22849
|
}
|
|
22749
22850
|
$rows.push([
|
|
22750
22851
|
"tr",
|
|
@@ -22757,22 +22858,38 @@ function render_topics(gobj, $body) {
|
|
|
22757
22858
|
[
|
|
22758
22859
|
"td",
|
|
22759
22860
|
{ class: "SCHEMA_TOPIC_NAME" },
|
|
22760
|
-
[
|
|
22761
|
-
|
|
22762
|
-
|
|
22763
|
-
|
|
22764
|
-
|
|
22765
|
-
|
|
22766
|
-
|
|
22767
|
-
|
|
22768
|
-
|
|
22769
|
-
|
|
22770
|
-
|
|
22771
|
-
|
|
22772
|
-
|
|
22773
|
-
|
|
22774
|
-
|
|
22775
|
-
|
|
22861
|
+
[
|
|
22862
|
+
[
|
|
22863
|
+
"span",
|
|
22864
|
+
{ class: "has-text-weight-semibold" },
|
|
22865
|
+
`${topic.name}`
|
|
22866
|
+
],
|
|
22867
|
+
topic.system_topic ? [
|
|
22868
|
+
"span",
|
|
22869
|
+
{
|
|
22870
|
+
class: "SCHEMA_TOPIC_SYSTEM tag is-light ml-2",
|
|
22871
|
+
i18n: "system"
|
|
22872
|
+
},
|
|
22873
|
+
(0, i18next.t)("system")
|
|
22874
|
+
] : [
|
|
22875
|
+
"span",
|
|
22876
|
+
{},
|
|
22877
|
+
""
|
|
22878
|
+
],
|
|
22879
|
+
ambiguous ? [
|
|
22880
|
+
"div",
|
|
22881
|
+
{ class: "SCHEMA_TOPIC_ID is-size-7 has-text-grey" },
|
|
22882
|
+
[[
|
|
22883
|
+
"code",
|
|
22884
|
+
{},
|
|
22885
|
+
`${topic.id}`
|
|
22886
|
+
]]
|
|
22887
|
+
] : [
|
|
22888
|
+
"span",
|
|
22889
|
+
{},
|
|
22890
|
+
""
|
|
22891
|
+
]
|
|
22892
|
+
]
|
|
22776
22893
|
],
|
|
22777
22894
|
[
|
|
22778
22895
|
"td",
|
|
@@ -22806,12 +22923,12 @@ function render_topics(gobj, $body) {
|
|
|
22806
22923
|
{
|
|
22807
22924
|
click: (evt) => {
|
|
22808
22925
|
if (evt.target.closest(".SCHEMA_TOPIC_ACTIONS")) return;
|
|
22809
|
-
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SELECT_TOPIC", { topic:
|
|
22926
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SELECT_TOPIC", { topic: address }, gobj);
|
|
22810
22927
|
},
|
|
22811
22928
|
keydown: (evt) => {
|
|
22812
22929
|
if (evt.key !== "Enter" && evt.key !== " ") return;
|
|
22813
22930
|
evt.preventDefault();
|
|
22814
|
-
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SELECT_TOPIC", { topic:
|
|
22931
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SELECT_TOPIC", { topic: address }, gobj);
|
|
22815
22932
|
}
|
|
22816
22933
|
}
|
|
22817
22934
|
]);
|
|
@@ -22900,6 +23017,26 @@ function row_icon(gobj, logical, icon, key, event, kw) {
|
|
|
22900
23017
|
];
|
|
22901
23018
|
}
|
|
22902
23019
|
/***************************************************************
|
|
23020
|
+
* Which names appear more than once.
|
|
23021
|
+
*
|
|
23022
|
+
* A store can hold TWO GENERATIONS of the same schema: the
|
|
23023
|
+
* projector never deletes, so a treedb re-keyed by a newer SDK
|
|
23024
|
+
* keeps its old rowid-keyed rows next to the qualified ones, and
|
|
23025
|
+
* both are children of the same treedb with the same name. Drawn
|
|
23026
|
+
* as two identical rows that behave differently, that reads as a
|
|
23027
|
+
* bug in the editor; drawn with what tells them apart, it reads
|
|
23028
|
+
* as what it is.
|
|
23029
|
+
***************************************************************/
|
|
23030
|
+
function repeated_names(list) {
|
|
23031
|
+
let seen = {};
|
|
23032
|
+
let twice = {};
|
|
23033
|
+
for (let entry of list) {
|
|
23034
|
+
if (seen[entry.name]) twice[entry.name] = true;
|
|
23035
|
+
seen[entry.name] = true;
|
|
23036
|
+
}
|
|
23037
|
+
return twice;
|
|
23038
|
+
}
|
|
23039
|
+
/***************************************************************
|
|
22903
23040
|
* ST_COLUMNS — the columns of one topic, in schema order.
|
|
22904
23041
|
*
|
|
22905
23042
|
* The order is a FIELD (`order`), so the rows are draggable and
|
|
@@ -22925,8 +23062,9 @@ function render_columns(gobj, $body) {
|
|
|
22925
23062
|
]);
|
|
22926
23063
|
let $banner = version_banner(gobj, topic);
|
|
22927
23064
|
if ($banner) $wrap.appendChild($banner);
|
|
23065
|
+
let twice = repeated_names(topic.cols);
|
|
22928
23066
|
let $rows = [];
|
|
22929
|
-
for (let i = 0; i < topic.cols.length; i++) $rows.push(column_row(gobj, topic, topic.cols[i], i, readonly));
|
|
23067
|
+
for (let i = 0; i < topic.cols.length; i++) $rows.push(column_row(gobj, topic, topic.cols[i], i, readonly, twice));
|
|
22930
23068
|
let $table = (0, _yuneta_gobj_js.createElement2)([
|
|
22931
23069
|
"div",
|
|
22932
23070
|
{
|
|
@@ -23047,7 +23185,7 @@ function version_banner(gobj, topic) {
|
|
|
23047
23185
|
[[
|
|
23048
23186
|
"span",
|
|
23049
23187
|
{ class: "icon" },
|
|
23050
|
-
[["i", { class: "yi-
|
|
23188
|
+
[["i", { class: "yi-chevron-up" }]]
|
|
23051
23189
|
], [
|
|
23052
23190
|
"span",
|
|
23053
23191
|
{ i18n: "raise" },
|
|
@@ -23068,8 +23206,10 @@ function version_banner(gobj, topic) {
|
|
|
23068
23206
|
* Yuneta event is plain JSON, so a DOM node never travels in
|
|
23069
23207
|
* one.
|
|
23070
23208
|
***************************************************************/
|
|
23071
|
-
function column_row(gobj, topic, col, index, readonly) {
|
|
23209
|
+
function column_row(gobj, topic, col, index, readonly, twice) {
|
|
23072
23210
|
let record = col.record || {};
|
|
23211
|
+
let ambiguous = !!(twice && twice[col.name]);
|
|
23212
|
+
let address = ambiguous ? col.id : col.name;
|
|
23073
23213
|
let flags = col_flags(record);
|
|
23074
23214
|
let hook = col_hook(record);
|
|
23075
23215
|
let is_pkey = col.name === (topic.pkey || "id");
|
|
@@ -23081,13 +23221,13 @@ function column_row(gobj, topic, col, index, readonly) {
|
|
|
23081
23221
|
];
|
|
23082
23222
|
});
|
|
23083
23223
|
let link = "";
|
|
23084
|
-
if (hook) link = "→ " + Object.keys(hook).join(", ");
|
|
23224
|
+
if (hook && !is_empty_value(hook)) link = "→ " + Object.keys(hook).join(", ");
|
|
23085
23225
|
else if (flags.indexOf("fkey") >= 0) link = "↖";
|
|
23086
23226
|
let $actions = [];
|
|
23087
23227
|
if (!readonly) {
|
|
23088
|
-
$actions.push(row_icon(gobj, "SCHEMA_COL_EDIT", "yi-pen", "edit column", "EV_EDIT_COLUMN", { col:
|
|
23089
|
-
$actions.push(row_icon(gobj, "SCHEMA_COL_DUPLICATE", "yi-copy", "duplicate column", "EV_DUPLICATE_COLUMN", { col:
|
|
23090
|
-
$actions.push(row_icon(gobj, "SCHEMA_COL_DELETE", "yi-trash", "delete column", "EV_DELETE_COLUMN", { col:
|
|
23228
|
+
$actions.push(row_icon(gobj, "SCHEMA_COL_EDIT", "yi-pen", "edit column", "EV_EDIT_COLUMN", { col: address }));
|
|
23229
|
+
$actions.push(row_icon(gobj, "SCHEMA_COL_DUPLICATE", "yi-copy", "duplicate column", "EV_DUPLICATE_COLUMN", { col: address }));
|
|
23230
|
+
$actions.push(row_icon(gobj, "SCHEMA_COL_DELETE", "yi-trash", "delete column", "EV_DELETE_COLUMN", { col: address }));
|
|
23091
23231
|
}
|
|
23092
23232
|
let $row = (0, _yuneta_gobj_js.createElement2)([
|
|
23093
23233
|
"tr",
|
|
@@ -23109,22 +23249,38 @@ function column_row(gobj, topic, col, index, readonly) {
|
|
|
23109
23249
|
[
|
|
23110
23250
|
"td",
|
|
23111
23251
|
{ class: "SCHEMA_COL_NAME" },
|
|
23112
|
-
[
|
|
23113
|
-
|
|
23114
|
-
|
|
23115
|
-
|
|
23116
|
-
|
|
23117
|
-
|
|
23118
|
-
|
|
23119
|
-
|
|
23120
|
-
|
|
23121
|
-
|
|
23122
|
-
|
|
23123
|
-
|
|
23124
|
-
|
|
23125
|
-
|
|
23126
|
-
|
|
23127
|
-
|
|
23252
|
+
[
|
|
23253
|
+
[
|
|
23254
|
+
"code",
|
|
23255
|
+
{},
|
|
23256
|
+
`${col.name}`
|
|
23257
|
+
],
|
|
23258
|
+
ambiguous ? [
|
|
23259
|
+
"span",
|
|
23260
|
+
{ class: "SCHEMA_COL_ID is-size-7 has-text-grey ml-2" },
|
|
23261
|
+
[[
|
|
23262
|
+
"code",
|
|
23263
|
+
{},
|
|
23264
|
+
`${col.id}`
|
|
23265
|
+
]]
|
|
23266
|
+
] : [
|
|
23267
|
+
"span",
|
|
23268
|
+
{},
|
|
23269
|
+
""
|
|
23270
|
+
],
|
|
23271
|
+
is_pkey ? [
|
|
23272
|
+
"span",
|
|
23273
|
+
{
|
|
23274
|
+
class: "SCHEMA_COL_PKEY tag is-info is-light ml-2",
|
|
23275
|
+
i18n: "pkey"
|
|
23276
|
+
},
|
|
23277
|
+
(0, i18next.t)("pkey")
|
|
23278
|
+
] : [
|
|
23279
|
+
"span",
|
|
23280
|
+
{},
|
|
23281
|
+
""
|
|
23282
|
+
]
|
|
23283
|
+
]
|
|
23128
23284
|
],
|
|
23129
23285
|
[
|
|
23130
23286
|
"td",
|
|
@@ -23167,7 +23323,7 @@ function column_row(gobj, topic, col, index, readonly) {
|
|
|
23167
23323
|
if (!readonly) wire_row_drag(gobj, $row);
|
|
23168
23324
|
$row.addEventListener("dblclick", (evt) => {
|
|
23169
23325
|
if (readonly || evt.target.closest(".SCHEMA_COL_ACTIONS")) return;
|
|
23170
|
-
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_EDIT_COLUMN", { col:
|
|
23326
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_EDIT_COLUMN", { col: address }, gobj);
|
|
23171
23327
|
});
|
|
23172
23328
|
return $row;
|
|
23173
23329
|
}
|
|
@@ -23267,6 +23423,7 @@ function render_diagram(gobj) {
|
|
|
23267
23423
|
subscriber: gobj,
|
|
23268
23424
|
descs,
|
|
23269
23425
|
node_route: "",
|
|
23426
|
+
with_node_click: true,
|
|
23270
23427
|
system: true
|
|
23271
23428
|
}, gobj);
|
|
23272
23429
|
if (!diagram) {
|
|
@@ -23504,7 +23661,8 @@ function open_column_form(gobj, topic, col, prefill) {
|
|
|
23504
23661
|
let record = prefill || col && col.record || {};
|
|
23505
23662
|
let creating = !col;
|
|
23506
23663
|
let type = record.type || "string";
|
|
23507
|
-
let hook = col_hook(record)
|
|
23664
|
+
let hook = col_hook(record);
|
|
23665
|
+
if (is_empty_value(hook)) hook = {};
|
|
23508
23666
|
let hook_topic = Object.keys(hook)[0] || "";
|
|
23509
23667
|
let hook_col = hook_topic ? hook[hook_topic] : "";
|
|
23510
23668
|
let treedb = current_treedb(gobj);
|
|
@@ -23654,7 +23812,7 @@ function open_topic_form(gobj, treedb, topic) {
|
|
|
23654
23812
|
[
|
|
23655
23813
|
field("SCHEMA_TOPIC_FORM_NAME", "value", "topic", text_input("value", creating ? "" : topic.name, (0, i18next.t)("topic name"), !creating), creating ? "" : "a topic is keyed by its whole path: renaming it is creating another one"),
|
|
23656
23814
|
field("SCHEMA_TOPIC_FORM_PKEY", "pkey", "pkey", creating ? text_input("pkey", "id", "id") : select_input("pkey", record.pkey || "id", col_names.length > 0 ? col_names : [record.pkey || "id"])),
|
|
23657
|
-
field("SCHEMA_TOPIC_FORM_PKEY2S", "pkey2s", "secondary keys", text_input("pkey2s", stringify_field(record.pkey2s), ""), "the column a record is
|
|
23815
|
+
field("SCHEMA_TOPIC_FORM_PKEY2S", "pkey2s", "secondary keys", text_input("pkey2s", stringify_field(record.pkey2s), ""), "the column a record is known by when its key is composed"),
|
|
23658
23816
|
field("SCHEMA_TOPIC_FORM_SYSTEM_FLAG", "system_flag", "system flag", text_input("system_flag", record.system_flag || "sf_string_key", "")),
|
|
23659
23817
|
field("SCHEMA_TOPIC_FORM_TKEY", "tkey", "time key", text_input("tkey", record.tkey, "")),
|
|
23660
23818
|
field("SCHEMA_TOPIC_FORM_VERSION", "topic_version", "version", number_input("topic_version", record.topic_version === void 0 ? 1 : record.topic_version), "raising it is what publishes a change of the columns"),
|
|
@@ -24069,7 +24227,7 @@ function open_import(gobj, treedb) {
|
|
|
24069
24227
|
[[
|
|
24070
24228
|
"span",
|
|
24071
24229
|
{ class: "icon" },
|
|
24072
|
-
[["i", { class: "yi-
|
|
24230
|
+
[["i", { class: "yi-upload" }]]
|
|
24073
24231
|
], [
|
|
24074
24232
|
"span",
|
|
24075
24233
|
{ i18n: "import" },
|
|
@@ -24334,10 +24492,12 @@ function run_next_write(gobj) {
|
|
|
24334
24492
|
let priv = gobj.priv;
|
|
24335
24493
|
if (priv.save_queue.length === 0) return end_writes(gobj, "");
|
|
24336
24494
|
let write = priv.save_queue[0];
|
|
24337
|
-
|
|
24495
|
+
let command = write_command(write.op);
|
|
24496
|
+
let options = write_options(write.op);
|
|
24497
|
+
if (remote_command(gobj, command, {
|
|
24338
24498
|
topic_name: write.topic_name,
|
|
24339
24499
|
record: write.record,
|
|
24340
|
-
options
|
|
24500
|
+
options,
|
|
24341
24501
|
record_id: write.record.id || ""
|
|
24342
24502
|
}) < 0) return end_writes(gobj, (0, i18next.t)("cannot reach the treedb"));
|
|
24343
24503
|
return 0;
|
|
@@ -24392,7 +24552,8 @@ function column_record(gobj, topic, col, values) {
|
|
|
24392
24552
|
record.header = values.header || "";
|
|
24393
24553
|
record.type = values.type || "string";
|
|
24394
24554
|
record.flag = flags;
|
|
24395
|
-
|
|
24555
|
+
if (String(values.fillspace).trim() !== "") record.fillspace = Number(values.fillspace);
|
|
24556
|
+
else if (!creating) record.fillspace = "";
|
|
24396
24557
|
record.placeholder = values.placeholder || "";
|
|
24397
24558
|
record.description = values.description || "";
|
|
24398
24559
|
record.default = values.default || "";
|
|
@@ -24490,7 +24651,7 @@ function ac_mt_command_answer(gobj, event, kw, src) {
|
|
|
24490
24651
|
let __command__ = (0, _yuneta_gobj_js.msg_iev_get_stack)(gobj, kw, "command_stack", true);
|
|
24491
24652
|
let command = (0, _yuneta_gobj_js.kw_get_str)(gobj, __command__, "command", "", 0);
|
|
24492
24653
|
let kw_command = (0, _yuneta_gobj_js.kw_get_dict)(gobj, __command__, "kw", {}, 0);
|
|
24493
|
-
let md = (0, _yuneta_gobj_js.
|
|
24654
|
+
let md = (0, _yuneta_gobj_js.is_object)(kw_command.__md_command__) ? kw_command.__md_command__ : kw_command;
|
|
24494
24655
|
if ((0, _yuneta_gobj_js.kw_get_str)(gobj, md, "purpose", "", 0) !== PURPOSE) return 0;
|
|
24495
24656
|
if (command === "nodes") {
|
|
24496
24657
|
let topic_name = (0, _yuneta_gobj_js.kw_get_str)(gobj, md, "topic_name", "", 0);
|
|
@@ -24507,11 +24668,11 @@ function ac_mt_command_answer(gobj, event, kw, src) {
|
|
|
24507
24668
|
build_model(gobj);
|
|
24508
24669
|
return go(gobj, priv.treedb_id, priv.topic_name, priv.diagram, false);
|
|
24509
24670
|
}
|
|
24510
|
-
if (command === "
|
|
24511
|
-
let topic_name = (0, _yuneta_gobj_js.kw_get_str)(gobj,
|
|
24671
|
+
if (command === "update-node" || command === "delete-node") {
|
|
24672
|
+
let topic_name = (0, _yuneta_gobj_js.kw_get_str)(gobj, md, "topic_name", "", 0);
|
|
24512
24673
|
if (result < 0) return end_writes(gobj, comment || (0, i18next.t)("the treedb refused the write"));
|
|
24513
24674
|
priv.save_wrote = true;
|
|
24514
|
-
if (command === "delete-node") forget_record(gobj, topic_name, (0, _yuneta_gobj_js.kw_get_str)(gobj,
|
|
24675
|
+
if (command === "delete-node") forget_record(gobj, topic_name, (0, _yuneta_gobj_js.kw_get_str)(gobj, md, "record_id", "", 0));
|
|
24515
24676
|
else {
|
|
24516
24677
|
let record = (0, _yuneta_gobj_js.is_object)(data) ? data : Array.isArray(data) && (0, _yuneta_gobj_js.is_object)(data[0]) ? data[0] : null;
|
|
24517
24678
|
if (record) patch_record(gobj, topic_name, record);
|
|
@@ -24522,7 +24683,10 @@ function ac_mt_command_answer(gobj, event, kw, src) {
|
|
|
24522
24683
|
return 0;
|
|
24523
24684
|
}
|
|
24524
24685
|
}
|
|
24525
|
-
if (topic_name === T_TOPICS || topic_name === T_COLS)
|
|
24686
|
+
if (topic_name === T_TOPICS || topic_name === T_COLS) {
|
|
24687
|
+
let write = priv.save_queue[0];
|
|
24688
|
+
mark_written(gobj, topic_name, write ? write.record : null);
|
|
24689
|
+
}
|
|
24526
24690
|
priv.save_queue.shift();
|
|
24527
24691
|
return run_next_write(gobj);
|
|
24528
24692
|
}
|
|
@@ -24915,7 +25079,7 @@ function ac_preview_import(gobj, event, kw, src) {
|
|
|
24915
25079
|
writes: [],
|
|
24916
25080
|
summary: {},
|
|
24917
25081
|
conflicts: [{
|
|
24918
|
-
code: "that is not
|
|
25082
|
+
code: "that is not json",
|
|
24919
25083
|
topic: "",
|
|
24920
25084
|
col: ""
|
|
24921
25085
|
}]
|