@yuneta/gobj-ui 6.1.0 → 6.1.1
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 +11 -1
- package/dist/gobj-ui.cjs.js +232 -77
- package/dist/gobj-ui.es.js +232 -77
- package/package.json +1 -1
- package/src/c_yui_schema_editor.js +119 -33
- 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
|
@@ -631,11 +631,21 @@ else.
|
|
|
631
631
|
`ST_LOADING`, `ST_EMPTY`, `ST_TREEDBS`, `ST_TOPICS`, `ST_DIAGRAM`,
|
|
632
632
|
`ST_COLUMNS`, `ST_SAVING`.
|
|
633
633
|
|
|
634
|
+
**Three words decide whether a write does what it says** (`schema_write_options.js`,
|
|
635
|
+
and they are tested because getting one wrong costs a store repaired by hand):
|
|
636
|
+
a create goes through `update-node` and not `create-node`, because only that
|
|
637
|
+
path carries **`autolink`** — and without a link a new column belongs to no
|
|
638
|
+
topic. `autolink` rewrites a node's links from the fkey fields the record
|
|
639
|
+
carries, so it goes with a create, where the record has one, **and with nothing
|
|
640
|
+
else**: on a partial update it finds none, reads that as "no parents", and
|
|
641
|
+
detaches the node. Raising a topic's version with it on unlinked that topic
|
|
642
|
+
from its treedb, and the write answered success.
|
|
643
|
+
|
|
634
644
|
The logic is pure and tested apart from the view: `schema_model.js` (the three
|
|
635
645
|
lists regrouped — grouping follows the **fkey**, not a split of the qualified
|
|
636
646
|
id on `.`, which works right up to the first name that carries one),
|
|
637
647
|
`schema_validate.js`, `schema_descs.js`, `schema_to_c.js`, `schema_import.js`,
|
|
638
|
-
`schema_flags.js`. Since 6.1.0.
|
|
648
|
+
`schema_flags.js`, `schema_write_options.js`. Since 6.1.0.
|
|
639
649
|
|
|
640
650
|
### Frontend view — `setup_frontend_view`
|
|
641
651
|
|
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -20587,6 +20587,22 @@ function build_schema_model(records) {
|
|
|
20587
20587
|
};
|
|
20588
20588
|
}
|
|
20589
20589
|
/***************************************************************
|
|
20590
|
+
* Is there a value here at all?
|
|
20591
|
+
*
|
|
20592
|
+
* The store answers a `blob` column that was never set with an
|
|
20593
|
+
* EMPTY COLLECTION, not with nothing: `enum: {}`, `hook: {}`,
|
|
20594
|
+
* `default: {}`. Read as "a value", those turn every export into
|
|
20595
|
+
* a schema full of empty objects — and `'hook': {}` in a literal
|
|
20596
|
+
* is a hook with no mapping, which is a link the treedb builds
|
|
20597
|
+
* and nothing writes.
|
|
20598
|
+
***************************************************************/
|
|
20599
|
+
function is_empty_value(value) {
|
|
20600
|
+
if (value === null || value === void 0 || value === "") return true;
|
|
20601
|
+
if (Array.isArray(value)) return value.length === 0;
|
|
20602
|
+
if (typeof value === "object") return Object.keys(value).length === 0;
|
|
20603
|
+
return false;
|
|
20604
|
+
}
|
|
20605
|
+
/***************************************************************
|
|
20590
20606
|
* The fields of a column that are DECLARED as one thing and
|
|
20591
20607
|
* STORED as another.
|
|
20592
20608
|
*
|
|
@@ -20924,6 +20940,7 @@ var COL_STORAGE_FIELDS = [
|
|
|
20924
20940
|
"value",
|
|
20925
20941
|
"topics",
|
|
20926
20942
|
"order",
|
|
20943
|
+
"_geometry",
|
|
20927
20944
|
"__md_treedb__"
|
|
20928
20945
|
];
|
|
20929
20946
|
var TOPIC_STORAGE_FIELDS = [
|
|
@@ -20932,6 +20949,7 @@ var TOPIC_STORAGE_FIELDS = [
|
|
|
20932
20949
|
"treedbs",
|
|
20933
20950
|
"cols",
|
|
20934
20951
|
"order",
|
|
20952
|
+
"_geometry",
|
|
20935
20953
|
"__md_treedb__"
|
|
20936
20954
|
];
|
|
20937
20955
|
var COL_JSON_FIELDS = [
|
|
@@ -20955,13 +20973,16 @@ function col_desc(col) {
|
|
|
20955
20973
|
let desc = {};
|
|
20956
20974
|
for (let [key, value] of Object.entries(record)) {
|
|
20957
20975
|
if (COL_STORAGE_FIELDS.indexOf(key) >= 0) continue;
|
|
20958
|
-
if (value
|
|
20959
|
-
|
|
20976
|
+
if (is_empty_value(value)) continue;
|
|
20977
|
+
let read = COL_JSON_FIELDS.indexOf(key) >= 0 ? as_json(value) : value;
|
|
20978
|
+
if (is_empty_value(read)) continue;
|
|
20979
|
+
desc[key] = read;
|
|
20960
20980
|
}
|
|
20961
20981
|
desc.id = col.name;
|
|
20962
20982
|
desc.flag = col_flags(record);
|
|
20963
20983
|
let hook = col_hook(record);
|
|
20964
|
-
if (hook) desc.hook = hook;
|
|
20984
|
+
if (hook && !is_empty_value(hook)) desc.hook = hook;
|
|
20985
|
+
else delete desc.hook;
|
|
20965
20986
|
let e = col_enum(record);
|
|
20966
20987
|
if (e.length > 0) desc.enum = e;
|
|
20967
20988
|
return desc;
|
|
@@ -20979,7 +21000,7 @@ function topic_descs(treedb) {
|
|
|
20979
21000
|
let desc = {};
|
|
20980
21001
|
for (let [key, value] of Object.entries(record)) {
|
|
20981
21002
|
if (TOPIC_STORAGE_FIELDS.indexOf(key) >= 0) continue;
|
|
20982
|
-
if (value
|
|
21003
|
+
if (is_empty_value(value)) continue;
|
|
20983
21004
|
desc[key] = value;
|
|
20984
21005
|
}
|
|
20985
21006
|
desc.topic_name = topic.name;
|
|
@@ -21036,6 +21057,7 @@ var COL_SKIP = [
|
|
|
21036
21057
|
"value",
|
|
21037
21058
|
"topics",
|
|
21038
21059
|
"order",
|
|
21060
|
+
"_geometry",
|
|
21039
21061
|
"__md_treedb__"
|
|
21040
21062
|
];
|
|
21041
21063
|
var TOPIC_SKIP = [
|
|
@@ -21044,6 +21066,7 @@ var TOPIC_SKIP = [
|
|
|
21044
21066
|
"treedbs",
|
|
21045
21067
|
"cols",
|
|
21046
21068
|
"order",
|
|
21069
|
+
"_geometry",
|
|
21047
21070
|
"__md_treedb__"
|
|
21048
21071
|
];
|
|
21049
21072
|
var JSON_FIELDS$1 = [
|
|
@@ -21097,8 +21120,10 @@ function schema_to_json(treedb) {
|
|
|
21097
21120
|
let jn_topic = { id: topic.name };
|
|
21098
21121
|
for (let [key, value] of Object.entries(record)) {
|
|
21099
21122
|
if (TOPIC_SKIP.indexOf(key) >= 0) continue;
|
|
21100
|
-
if (value
|
|
21101
|
-
|
|
21123
|
+
if (is_empty_value(value)) continue;
|
|
21124
|
+
let read = JSON_FIELDS$1.indexOf(key) >= 0 ? as_json(value) : value;
|
|
21125
|
+
if (is_empty_value(read)) continue;
|
|
21126
|
+
jn_topic[key] = read;
|
|
21102
21127
|
}
|
|
21103
21128
|
if (jn_topic.topic_version !== void 0) jn_topic.topic_version = version(jn_topic.topic_version);
|
|
21104
21129
|
let pkey2s = topic_pkey2s(record);
|
|
@@ -21111,14 +21136,17 @@ function schema_to_json(treedb) {
|
|
|
21111
21136
|
let jn_col = {};
|
|
21112
21137
|
for (let [key, value] of Object.entries(col_record)) {
|
|
21113
21138
|
if (COL_SKIP.indexOf(key) >= 0) continue;
|
|
21114
|
-
if (value
|
|
21115
|
-
|
|
21139
|
+
if (is_empty_value(value)) continue;
|
|
21140
|
+
let read = JSON_FIELDS$1.indexOf(key) >= 0 ? as_json(value) : value;
|
|
21141
|
+
if (is_empty_value(read)) continue;
|
|
21142
|
+
jn_col[key] = read;
|
|
21116
21143
|
}
|
|
21117
21144
|
let flags = col_flags(col_record);
|
|
21118
21145
|
if (flags.length > 0) jn_col.flag = flags;
|
|
21119
21146
|
else delete jn_col.flag;
|
|
21120
21147
|
let hook = col_hook(col_record);
|
|
21121
|
-
if (hook) jn_col.hook = hook;
|
|
21148
|
+
if (hook && !is_empty_value(hook)) jn_col.hook = hook;
|
|
21149
|
+
else delete jn_col.hook;
|
|
21122
21150
|
let e = col_enum(col_record);
|
|
21123
21151
|
if (e.length > 0) jn_col.enum = e;
|
|
21124
21152
|
let ordered_col = {};
|
|
@@ -21303,7 +21331,7 @@ function same_value(field, a, b) {
|
|
|
21303
21331
|
list = list.filter((f) => typeof f === "string" && f.length > 0).sort();
|
|
21304
21332
|
return JSON.stringify(list);
|
|
21305
21333
|
}
|
|
21306
|
-
if (v
|
|
21334
|
+
if (is_empty_value(v)) return "";
|
|
21307
21335
|
if (typeof v === "object") return JSON.stringify(v);
|
|
21308
21336
|
return String(v);
|
|
21309
21337
|
};
|
|
@@ -21316,8 +21344,10 @@ function incoming_col_fields(col) {
|
|
|
21316
21344
|
let out = {};
|
|
21317
21345
|
for (let field of COL_FIELDS) {
|
|
21318
21346
|
let value = col ? col[field] : void 0;
|
|
21319
|
-
if (value
|
|
21320
|
-
|
|
21347
|
+
if (is_empty_value(value)) continue;
|
|
21348
|
+
let read = JSON_FIELDS.indexOf(field) >= 0 ? as_json(value) : value;
|
|
21349
|
+
if (is_empty_value(read)) continue;
|
|
21350
|
+
out[field] = read;
|
|
21321
21351
|
}
|
|
21322
21352
|
if (Array.isArray(out.flag)) out.flag = out.flag.filter((f) => typeof f === "string" && f.length > 0);
|
|
21323
21353
|
else if (typeof out.flag === "string" && out.flag) out.flag = [out.flag];
|
|
@@ -21330,14 +21360,17 @@ function stored_col_fields(record) {
|
|
|
21330
21360
|
let out = {};
|
|
21331
21361
|
for (let field of COL_FIELDS) {
|
|
21332
21362
|
let value = record ? record[field] : void 0;
|
|
21333
|
-
if (value
|
|
21334
|
-
|
|
21363
|
+
if (is_empty_value(value)) continue;
|
|
21364
|
+
let read = JSON_FIELDS.indexOf(field) >= 0 ? as_json(value) : value;
|
|
21365
|
+
if (is_empty_value(read)) continue;
|
|
21366
|
+
out[field] = read;
|
|
21335
21367
|
}
|
|
21336
21368
|
let flags = col_flags(record);
|
|
21337
21369
|
if (flags.length > 0) out.flag = flags;
|
|
21338
21370
|
else delete out.flag;
|
|
21339
21371
|
let hook = col_hook(record);
|
|
21340
|
-
if (hook) out.hook = hook;
|
|
21372
|
+
if (hook && !is_empty_value(hook)) out.hook = hook;
|
|
21373
|
+
else delete out.hook;
|
|
21341
21374
|
let e = col_enum(record);
|
|
21342
21375
|
if (e.length > 0) out.enum = e;
|
|
21343
21376
|
return out;
|
|
@@ -21962,6 +21995,61 @@ function toggle_flag(current, name, on) {
|
|
|
21962
21995
|
return out;
|
|
21963
21996
|
}
|
|
21964
21997
|
//#endregion
|
|
21998
|
+
//#region src/schema_write_options.js
|
|
21999
|
+
/***********************************************************************
|
|
22000
|
+
* schema_write_options.js
|
|
22001
|
+
*
|
|
22002
|
+
* The OPTIONS a schema write travels with, and why each one.
|
|
22003
|
+
*
|
|
22004
|
+
* Three words decide whether a write does what it says, and all
|
|
22005
|
+
* three are easy to get wrong in a way that succeeds:
|
|
22006
|
+
*
|
|
22007
|
+
* `create` a node that is not there yet is written by
|
|
22008
|
+
* `update-node` with this, not by `create-node`. The
|
|
22009
|
+
* two are not the same call: only the update path
|
|
22010
|
+
* carries `autolink`, and without a link a new column
|
|
22011
|
+
* belongs to no topic.
|
|
22012
|
+
*
|
|
22013
|
+
* `autolink` turns the fkey carried IN THE RECORD into a link.
|
|
22014
|
+
* It rewrites the node's links from the fkey fields
|
|
22015
|
+
* the record has — so on a PARTIAL update, which
|
|
22016
|
+
* carries none, it reads that as "no parents" and
|
|
22017
|
+
* UNLINKS the node. Raising a topic's version with it
|
|
22018
|
+
* on detached that topic from its treedb, and the
|
|
22019
|
+
* write returned success. It goes with a create,
|
|
22020
|
+
* where the record carries its fkey, and with nothing
|
|
22021
|
+
* else.
|
|
22022
|
+
*
|
|
22023
|
+
* `force` a delete of a node something points at. A column is
|
|
22024
|
+
* pointed at by the topic that owns it, which is not
|
|
22025
|
+
* a reason to refuse.
|
|
22026
|
+
*
|
|
22027
|
+
* Pure and tested, because the cost of getting it wrong is a
|
|
22028
|
+
* store that has to be repaired by hand, and nothing in the
|
|
22029
|
+
* answer says it happened.
|
|
22030
|
+
*
|
|
22031
|
+
* Copyright (c) 2026, ArtGins.
|
|
22032
|
+
* All Rights Reserved.
|
|
22033
|
+
***********************************************************************/
|
|
22034
|
+
/***************************************************************
|
|
22035
|
+
* write_command(op) -> the treedb command that performs it
|
|
22036
|
+
***************************************************************/
|
|
22037
|
+
function write_command(op) {
|
|
22038
|
+
return op === "delete" ? "delete-node" : "update-node";
|
|
22039
|
+
}
|
|
22040
|
+
/***************************************************************
|
|
22041
|
+
* write_options(op) -> the options it travels with
|
|
22042
|
+
***************************************************************/
|
|
22043
|
+
function write_options(op) {
|
|
22044
|
+
if (op === "delete") return { force: true };
|
|
22045
|
+
if (op === "create") return {
|
|
22046
|
+
list_dict: true,
|
|
22047
|
+
create: true,
|
|
22048
|
+
autolink: true
|
|
22049
|
+
};
|
|
22050
|
+
return { list_dict: true };
|
|
22051
|
+
}
|
|
22052
|
+
//#endregion
|
|
21965
22053
|
//#region src/c_yui_schema_editor.js
|
|
21966
22054
|
/***********************************************************************
|
|
21967
22055
|
* c_yui_schema_editor.js
|
|
@@ -22281,10 +22369,12 @@ function remote_command(gobj, command, kw) {
|
|
|
22281
22369
|
service: treedb_name,
|
|
22282
22370
|
treedb_name
|
|
22283
22371
|
}, kw || {});
|
|
22284
|
-
full_kw.__md_command__ =
|
|
22372
|
+
full_kw.__md_command__ = {
|
|
22285
22373
|
purpose: PURPOSE,
|
|
22286
|
-
|
|
22287
|
-
|
|
22374
|
+
topic_name: kw && kw.topic_name || "",
|
|
22375
|
+
record_id: kw && kw.record_id || ""
|
|
22376
|
+
};
|
|
22377
|
+
delete full_kw.record_id;
|
|
22288
22378
|
let ret = (0, _yuneta_gobj_js.gobj_command)(remote, command, full_kw, gobj);
|
|
22289
22379
|
if (ret) {
|
|
22290
22380
|
(0, _yuneta_gobj_js.log_error)(ret);
|
|
@@ -22533,9 +22623,9 @@ function render_toolbar(gobj) {
|
|
|
22533
22623
|
let $right = [];
|
|
22534
22624
|
if (priv.treedb_id && has_model) {
|
|
22535
22625
|
$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-
|
|
22626
|
+
$right.push(toolbar_button("SCHEMA_VALIDATE_BTN", "yi-square-check", "check", "EV_VALIDATE", false));
|
|
22627
|
+
$right.push(toolbar_button("SCHEMA_EXPORT_BTN", "yi-download", "export", "EV_EXPORT", false));
|
|
22628
|
+
if (!readonly) $right.push(toolbar_button("SCHEMA_IMPORT_BTN", "yi-upload", "import", "EV_IMPORT", false));
|
|
22539
22629
|
}
|
|
22540
22630
|
if (priv.topic_name && !readonly && !priv.diagram) $right.push(toolbar_button("SCHEMA_ADD_COL_BTN", "yi-plus", "new column", "EV_ADD_COLUMN", false));
|
|
22541
22631
|
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 +22828,16 @@ function render_topics(gobj, $body) {
|
|
|
22738
22828
|
return;
|
|
22739
22829
|
}
|
|
22740
22830
|
let readonly = is_readonly(gobj);
|
|
22831
|
+
let twice = repeated_names(treedb.topics);
|
|
22741
22832
|
let $rows = [];
|
|
22742
22833
|
for (let topic of treedb.topics) {
|
|
22743
22834
|
let pending = priv.written[topic.id] && String(priv.baseline[topic.id]) === String(topic.topic_version);
|
|
22835
|
+
let ambiguous = !!twice[topic.name];
|
|
22836
|
+
let address = ambiguous ? topic.id : topic.name;
|
|
22744
22837
|
let $actions = [];
|
|
22745
22838
|
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:
|
|
22839
|
+
$actions.push(row_icon(gobj, "SCHEMA_TOPIC_EDIT", "yi-pen", "edit topic", "EV_EDIT_TOPIC", { topic: address }));
|
|
22840
|
+
$actions.push(row_icon(gobj, "SCHEMA_TOPIC_DELETE", "yi-trash", "delete topic", "EV_DELETE_TOPIC", { topic: address }));
|
|
22748
22841
|
}
|
|
22749
22842
|
$rows.push([
|
|
22750
22843
|
"tr",
|
|
@@ -22757,22 +22850,38 @@ function render_topics(gobj, $body) {
|
|
|
22757
22850
|
[
|
|
22758
22851
|
"td",
|
|
22759
22852
|
{ class: "SCHEMA_TOPIC_NAME" },
|
|
22760
|
-
[
|
|
22761
|
-
|
|
22762
|
-
|
|
22763
|
-
|
|
22764
|
-
|
|
22765
|
-
|
|
22766
|
-
|
|
22767
|
-
|
|
22768
|
-
|
|
22769
|
-
|
|
22770
|
-
|
|
22771
|
-
|
|
22772
|
-
|
|
22773
|
-
|
|
22774
|
-
|
|
22775
|
-
|
|
22853
|
+
[
|
|
22854
|
+
[
|
|
22855
|
+
"span",
|
|
22856
|
+
{ class: "has-text-weight-semibold" },
|
|
22857
|
+
`${topic.name}`
|
|
22858
|
+
],
|
|
22859
|
+
topic.system_topic ? [
|
|
22860
|
+
"span",
|
|
22861
|
+
{
|
|
22862
|
+
class: "SCHEMA_TOPIC_SYSTEM tag is-light ml-2",
|
|
22863
|
+
i18n: "system"
|
|
22864
|
+
},
|
|
22865
|
+
(0, i18next.t)("system")
|
|
22866
|
+
] : [
|
|
22867
|
+
"span",
|
|
22868
|
+
{},
|
|
22869
|
+
""
|
|
22870
|
+
],
|
|
22871
|
+
ambiguous ? [
|
|
22872
|
+
"div",
|
|
22873
|
+
{ class: "SCHEMA_TOPIC_ID is-size-7 has-text-grey" },
|
|
22874
|
+
[[
|
|
22875
|
+
"code",
|
|
22876
|
+
{},
|
|
22877
|
+
`${topic.id}`
|
|
22878
|
+
]]
|
|
22879
|
+
] : [
|
|
22880
|
+
"span",
|
|
22881
|
+
{},
|
|
22882
|
+
""
|
|
22883
|
+
]
|
|
22884
|
+
]
|
|
22776
22885
|
],
|
|
22777
22886
|
[
|
|
22778
22887
|
"td",
|
|
@@ -22806,12 +22915,12 @@ function render_topics(gobj, $body) {
|
|
|
22806
22915
|
{
|
|
22807
22916
|
click: (evt) => {
|
|
22808
22917
|
if (evt.target.closest(".SCHEMA_TOPIC_ACTIONS")) return;
|
|
22809
|
-
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SELECT_TOPIC", { topic:
|
|
22918
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SELECT_TOPIC", { topic: address }, gobj);
|
|
22810
22919
|
},
|
|
22811
22920
|
keydown: (evt) => {
|
|
22812
22921
|
if (evt.key !== "Enter" && evt.key !== " ") return;
|
|
22813
22922
|
evt.preventDefault();
|
|
22814
|
-
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SELECT_TOPIC", { topic:
|
|
22923
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SELECT_TOPIC", { topic: address }, gobj);
|
|
22815
22924
|
}
|
|
22816
22925
|
}
|
|
22817
22926
|
]);
|
|
@@ -22900,6 +23009,26 @@ function row_icon(gobj, logical, icon, key, event, kw) {
|
|
|
22900
23009
|
];
|
|
22901
23010
|
}
|
|
22902
23011
|
/***************************************************************
|
|
23012
|
+
* Which names appear more than once.
|
|
23013
|
+
*
|
|
23014
|
+
* A store can hold TWO GENERATIONS of the same schema: the
|
|
23015
|
+
* projector never deletes, so a treedb re-keyed by a newer SDK
|
|
23016
|
+
* keeps its old rowid-keyed rows next to the qualified ones, and
|
|
23017
|
+
* both are children of the same treedb with the same name. Drawn
|
|
23018
|
+
* as two identical rows that behave differently, that reads as a
|
|
23019
|
+
* bug in the editor; drawn with what tells them apart, it reads
|
|
23020
|
+
* as what it is.
|
|
23021
|
+
***************************************************************/
|
|
23022
|
+
function repeated_names(list) {
|
|
23023
|
+
let seen = {};
|
|
23024
|
+
let twice = {};
|
|
23025
|
+
for (let entry of list) {
|
|
23026
|
+
if (seen[entry.name]) twice[entry.name] = true;
|
|
23027
|
+
seen[entry.name] = true;
|
|
23028
|
+
}
|
|
23029
|
+
return twice;
|
|
23030
|
+
}
|
|
23031
|
+
/***************************************************************
|
|
22903
23032
|
* ST_COLUMNS — the columns of one topic, in schema order.
|
|
22904
23033
|
*
|
|
22905
23034
|
* The order is a FIELD (`order`), so the rows are draggable and
|
|
@@ -22925,8 +23054,9 @@ function render_columns(gobj, $body) {
|
|
|
22925
23054
|
]);
|
|
22926
23055
|
let $banner = version_banner(gobj, topic);
|
|
22927
23056
|
if ($banner) $wrap.appendChild($banner);
|
|
23057
|
+
let twice = repeated_names(topic.cols);
|
|
22928
23058
|
let $rows = [];
|
|
22929
|
-
for (let i = 0; i < topic.cols.length; i++) $rows.push(column_row(gobj, topic, topic.cols[i], i, readonly));
|
|
23059
|
+
for (let i = 0; i < topic.cols.length; i++) $rows.push(column_row(gobj, topic, topic.cols[i], i, readonly, twice));
|
|
22930
23060
|
let $table = (0, _yuneta_gobj_js.createElement2)([
|
|
22931
23061
|
"div",
|
|
22932
23062
|
{
|
|
@@ -23047,7 +23177,7 @@ function version_banner(gobj, topic) {
|
|
|
23047
23177
|
[[
|
|
23048
23178
|
"span",
|
|
23049
23179
|
{ class: "icon" },
|
|
23050
|
-
[["i", { class: "yi-
|
|
23180
|
+
[["i", { class: "yi-chevron-up" }]]
|
|
23051
23181
|
], [
|
|
23052
23182
|
"span",
|
|
23053
23183
|
{ i18n: "raise" },
|
|
@@ -23068,8 +23198,10 @@ function version_banner(gobj, topic) {
|
|
|
23068
23198
|
* Yuneta event is plain JSON, so a DOM node never travels in
|
|
23069
23199
|
* one.
|
|
23070
23200
|
***************************************************************/
|
|
23071
|
-
function column_row(gobj, topic, col, index, readonly) {
|
|
23201
|
+
function column_row(gobj, topic, col, index, readonly, twice) {
|
|
23072
23202
|
let record = col.record || {};
|
|
23203
|
+
let ambiguous = !!(twice && twice[col.name]);
|
|
23204
|
+
let address = ambiguous ? col.id : col.name;
|
|
23073
23205
|
let flags = col_flags(record);
|
|
23074
23206
|
let hook = col_hook(record);
|
|
23075
23207
|
let is_pkey = col.name === (topic.pkey || "id");
|
|
@@ -23081,13 +23213,13 @@ function column_row(gobj, topic, col, index, readonly) {
|
|
|
23081
23213
|
];
|
|
23082
23214
|
});
|
|
23083
23215
|
let link = "";
|
|
23084
|
-
if (hook) link = "→ " + Object.keys(hook).join(", ");
|
|
23216
|
+
if (hook && !is_empty_value(hook)) link = "→ " + Object.keys(hook).join(", ");
|
|
23085
23217
|
else if (flags.indexOf("fkey") >= 0) link = "↖";
|
|
23086
23218
|
let $actions = [];
|
|
23087
23219
|
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:
|
|
23220
|
+
$actions.push(row_icon(gobj, "SCHEMA_COL_EDIT", "yi-pen", "edit column", "EV_EDIT_COLUMN", { col: address }));
|
|
23221
|
+
$actions.push(row_icon(gobj, "SCHEMA_COL_DUPLICATE", "yi-copy", "duplicate column", "EV_DUPLICATE_COLUMN", { col: address }));
|
|
23222
|
+
$actions.push(row_icon(gobj, "SCHEMA_COL_DELETE", "yi-trash", "delete column", "EV_DELETE_COLUMN", { col: address }));
|
|
23091
23223
|
}
|
|
23092
23224
|
let $row = (0, _yuneta_gobj_js.createElement2)([
|
|
23093
23225
|
"tr",
|
|
@@ -23109,22 +23241,38 @@ function column_row(gobj, topic, col, index, readonly) {
|
|
|
23109
23241
|
[
|
|
23110
23242
|
"td",
|
|
23111
23243
|
{ class: "SCHEMA_COL_NAME" },
|
|
23112
|
-
[
|
|
23113
|
-
|
|
23114
|
-
|
|
23115
|
-
|
|
23116
|
-
|
|
23117
|
-
|
|
23118
|
-
|
|
23119
|
-
|
|
23120
|
-
|
|
23121
|
-
|
|
23122
|
-
|
|
23123
|
-
|
|
23124
|
-
|
|
23125
|
-
|
|
23126
|
-
|
|
23127
|
-
|
|
23244
|
+
[
|
|
23245
|
+
[
|
|
23246
|
+
"code",
|
|
23247
|
+
{},
|
|
23248
|
+
`${col.name}`
|
|
23249
|
+
],
|
|
23250
|
+
ambiguous ? [
|
|
23251
|
+
"span",
|
|
23252
|
+
{ class: "SCHEMA_COL_ID is-size-7 has-text-grey ml-2" },
|
|
23253
|
+
[[
|
|
23254
|
+
"code",
|
|
23255
|
+
{},
|
|
23256
|
+
`${col.id}`
|
|
23257
|
+
]]
|
|
23258
|
+
] : [
|
|
23259
|
+
"span",
|
|
23260
|
+
{},
|
|
23261
|
+
""
|
|
23262
|
+
],
|
|
23263
|
+
is_pkey ? [
|
|
23264
|
+
"span",
|
|
23265
|
+
{
|
|
23266
|
+
class: "SCHEMA_COL_PKEY tag is-info is-light ml-2",
|
|
23267
|
+
i18n: "pkey"
|
|
23268
|
+
},
|
|
23269
|
+
(0, i18next.t)("pkey")
|
|
23270
|
+
] : [
|
|
23271
|
+
"span",
|
|
23272
|
+
{},
|
|
23273
|
+
""
|
|
23274
|
+
]
|
|
23275
|
+
]
|
|
23128
23276
|
],
|
|
23129
23277
|
[
|
|
23130
23278
|
"td",
|
|
@@ -23167,7 +23315,7 @@ function column_row(gobj, topic, col, index, readonly) {
|
|
|
23167
23315
|
if (!readonly) wire_row_drag(gobj, $row);
|
|
23168
23316
|
$row.addEventListener("dblclick", (evt) => {
|
|
23169
23317
|
if (readonly || evt.target.closest(".SCHEMA_COL_ACTIONS")) return;
|
|
23170
|
-
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_EDIT_COLUMN", { col:
|
|
23318
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_EDIT_COLUMN", { col: address }, gobj);
|
|
23171
23319
|
});
|
|
23172
23320
|
return $row;
|
|
23173
23321
|
}
|
|
@@ -23504,7 +23652,8 @@ function open_column_form(gobj, topic, col, prefill) {
|
|
|
23504
23652
|
let record = prefill || col && col.record || {};
|
|
23505
23653
|
let creating = !col;
|
|
23506
23654
|
let type = record.type || "string";
|
|
23507
|
-
let hook = col_hook(record)
|
|
23655
|
+
let hook = col_hook(record);
|
|
23656
|
+
if (is_empty_value(hook)) hook = {};
|
|
23508
23657
|
let hook_topic = Object.keys(hook)[0] || "";
|
|
23509
23658
|
let hook_col = hook_topic ? hook[hook_topic] : "";
|
|
23510
23659
|
let treedb = current_treedb(gobj);
|
|
@@ -23654,7 +23803,7 @@ function open_topic_form(gobj, treedb, topic) {
|
|
|
23654
23803
|
[
|
|
23655
23804
|
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
23805
|
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
|
|
23806
|
+
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
23807
|
field("SCHEMA_TOPIC_FORM_SYSTEM_FLAG", "system_flag", "system flag", text_input("system_flag", record.system_flag || "sf_string_key", "")),
|
|
23659
23808
|
field("SCHEMA_TOPIC_FORM_TKEY", "tkey", "time key", text_input("tkey", record.tkey, "")),
|
|
23660
23809
|
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 +24218,7 @@ function open_import(gobj, treedb) {
|
|
|
24069
24218
|
[[
|
|
24070
24219
|
"span",
|
|
24071
24220
|
{ class: "icon" },
|
|
24072
|
-
[["i", { class: "yi-
|
|
24221
|
+
[["i", { class: "yi-upload" }]]
|
|
24073
24222
|
], [
|
|
24074
24223
|
"span",
|
|
24075
24224
|
{ i18n: "import" },
|
|
@@ -24334,10 +24483,12 @@ function run_next_write(gobj) {
|
|
|
24334
24483
|
let priv = gobj.priv;
|
|
24335
24484
|
if (priv.save_queue.length === 0) return end_writes(gobj, "");
|
|
24336
24485
|
let write = priv.save_queue[0];
|
|
24337
|
-
|
|
24486
|
+
let command = write_command(write.op);
|
|
24487
|
+
let options = write_options(write.op);
|
|
24488
|
+
if (remote_command(gobj, command, {
|
|
24338
24489
|
topic_name: write.topic_name,
|
|
24339
24490
|
record: write.record,
|
|
24340
|
-
options
|
|
24491
|
+
options,
|
|
24341
24492
|
record_id: write.record.id || ""
|
|
24342
24493
|
}) < 0) return end_writes(gobj, (0, i18next.t)("cannot reach the treedb"));
|
|
24343
24494
|
return 0;
|
|
@@ -24392,7 +24543,8 @@ function column_record(gobj, topic, col, values) {
|
|
|
24392
24543
|
record.header = values.header || "";
|
|
24393
24544
|
record.type = values.type || "string";
|
|
24394
24545
|
record.flag = flags;
|
|
24395
|
-
|
|
24546
|
+
if (String(values.fillspace).trim() !== "") record.fillspace = Number(values.fillspace);
|
|
24547
|
+
else if (!creating) record.fillspace = "";
|
|
24396
24548
|
record.placeholder = values.placeholder || "";
|
|
24397
24549
|
record.description = values.description || "";
|
|
24398
24550
|
record.default = values.default || "";
|
|
@@ -24490,7 +24642,7 @@ function ac_mt_command_answer(gobj, event, kw, src) {
|
|
|
24490
24642
|
let __command__ = (0, _yuneta_gobj_js.msg_iev_get_stack)(gobj, kw, "command_stack", true);
|
|
24491
24643
|
let command = (0, _yuneta_gobj_js.kw_get_str)(gobj, __command__, "command", "", 0);
|
|
24492
24644
|
let kw_command = (0, _yuneta_gobj_js.kw_get_dict)(gobj, __command__, "kw", {}, 0);
|
|
24493
|
-
let md = (0, _yuneta_gobj_js.
|
|
24645
|
+
let md = (0, _yuneta_gobj_js.is_object)(kw_command.__md_command__) ? kw_command.__md_command__ : kw_command;
|
|
24494
24646
|
if ((0, _yuneta_gobj_js.kw_get_str)(gobj, md, "purpose", "", 0) !== PURPOSE) return 0;
|
|
24495
24647
|
if (command === "nodes") {
|
|
24496
24648
|
let topic_name = (0, _yuneta_gobj_js.kw_get_str)(gobj, md, "topic_name", "", 0);
|
|
@@ -24507,11 +24659,11 @@ function ac_mt_command_answer(gobj, event, kw, src) {
|
|
|
24507
24659
|
build_model(gobj);
|
|
24508
24660
|
return go(gobj, priv.treedb_id, priv.topic_name, priv.diagram, false);
|
|
24509
24661
|
}
|
|
24510
|
-
if (command === "
|
|
24511
|
-
let topic_name = (0, _yuneta_gobj_js.kw_get_str)(gobj,
|
|
24662
|
+
if (command === "update-node" || command === "delete-node") {
|
|
24663
|
+
let topic_name = (0, _yuneta_gobj_js.kw_get_str)(gobj, md, "topic_name", "", 0);
|
|
24512
24664
|
if (result < 0) return end_writes(gobj, comment || (0, i18next.t)("the treedb refused the write"));
|
|
24513
24665
|
priv.save_wrote = true;
|
|
24514
|
-
if (command === "delete-node") forget_record(gobj, topic_name, (0, _yuneta_gobj_js.kw_get_str)(gobj,
|
|
24666
|
+
if (command === "delete-node") forget_record(gobj, topic_name, (0, _yuneta_gobj_js.kw_get_str)(gobj, md, "record_id", "", 0));
|
|
24515
24667
|
else {
|
|
24516
24668
|
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
24669
|
if (record) patch_record(gobj, topic_name, record);
|
|
@@ -24522,7 +24674,10 @@ function ac_mt_command_answer(gobj, event, kw, src) {
|
|
|
24522
24674
|
return 0;
|
|
24523
24675
|
}
|
|
24524
24676
|
}
|
|
24525
|
-
if (topic_name === T_TOPICS || topic_name === T_COLS)
|
|
24677
|
+
if (topic_name === T_TOPICS || topic_name === T_COLS) {
|
|
24678
|
+
let write = priv.save_queue[0];
|
|
24679
|
+
mark_written(gobj, topic_name, write ? write.record : null);
|
|
24680
|
+
}
|
|
24526
24681
|
priv.save_queue.shift();
|
|
24527
24682
|
return run_next_write(gobj);
|
|
24528
24683
|
}
|
|
@@ -24915,7 +25070,7 @@ function ac_preview_import(gobj, event, kw, src) {
|
|
|
24915
25070
|
writes: [],
|
|
24916
25071
|
summary: {},
|
|
24917
25072
|
conflicts: [{
|
|
24918
|
-
code: "that is not
|
|
25073
|
+
code: "that is not json",
|
|
24919
25074
|
topic: "",
|
|
24920
25075
|
col: ""
|
|
24921
25076
|
}]
|