@yuneta/gobj-ui 5.16.0 → 6.0.0
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 +62 -10
- package/dist/gobj-ui.cjs.js +363 -60
- package/dist/gobj-ui.es.js +363 -60
- package/package.json +3 -3
- package/src/c_g6_nodes_tree.js +38 -24
- package/src/c_yui_form.js +11 -3
- package/src/c_yui_treedb_schema.js +348 -43
- package/src/c_yui_treedb_topic_with_form.js +5 -1
- package/src/treedb_node_label.js +79 -0
- package/src/treedb_node_label.test.js +106 -0
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -6472,6 +6472,7 @@ function build_form_field_conf(gobj, field_desc) {
|
|
|
6472
6472
|
field_conf.extras.pattern = "^-?\\d+(\\.\\d+)?$";
|
|
6473
6473
|
break;
|
|
6474
6474
|
case "rowid":
|
|
6475
|
+
case "qualified":
|
|
6475
6476
|
field_conf.tag = "input";
|
|
6476
6477
|
field_conf.inputType = "text";
|
|
6477
6478
|
break;
|
|
@@ -6613,7 +6614,8 @@ function build_fkey_ref$1(gobj, field_desc, value) {
|
|
|
6613
6614
|
/************************************************************
|
|
6614
6615
|
* Apply the form mode to the pkey field:
|
|
6615
6616
|
* "update" -> pkey readonly, not required
|
|
6616
|
-
* "create" -> pkey writable and required
|
|
6617
|
+
* "create" -> pkey writable and required, unless the store
|
|
6618
|
+
* hands the key out itself (rowid, qualified)
|
|
6617
6619
|
* Empty form_mode keeps the template-declared behaviour
|
|
6618
6620
|
* (backward compatible).
|
|
6619
6621
|
************************************************************/
|
|
@@ -6626,8 +6628,8 @@ function apply_form_mode(gobj, $form) {
|
|
|
6626
6628
|
(0, _yuneta_gobj_js.log_error)((0, _yuneta_gobj_js.sprintf)("%s: form_mode '%s' but form has no pkey field '%s'", (0, _yuneta_gobj_js.gobj_short_name)(gobj), mode, pkey));
|
|
6627
6629
|
return;
|
|
6628
6630
|
}
|
|
6629
|
-
let
|
|
6630
|
-
if (mode === "create" && !
|
|
6631
|
+
let type = $input.field_desc ? $input.field_desc.type : "";
|
|
6632
|
+
if (mode === "create" && !(type === "rowid" || type === "qualified")) {
|
|
6631
6633
|
$input.removeAttribute("readonly");
|
|
6632
6634
|
$input.setAttribute("required", "");
|
|
6633
6635
|
} else {
|
|
@@ -7405,6 +7407,7 @@ function template2columns(gobj, columns, template, sub_elements) {
|
|
|
7405
7407
|
};
|
|
7406
7408
|
break;
|
|
7407
7409
|
case "rowid":
|
|
7410
|
+
case "qualified":
|
|
7408
7411
|
column = null;
|
|
7409
7412
|
break;
|
|
7410
7413
|
case "boolean":
|
|
@@ -7770,6 +7773,7 @@ function treedb_value_2_form_value(gobj, field_desc, value) {
|
|
|
7770
7773
|
case "enum":
|
|
7771
7774
|
case "uuid":
|
|
7772
7775
|
case "rowid":
|
|
7776
|
+
case "qualified":
|
|
7773
7777
|
case "password":
|
|
7774
7778
|
case "email":
|
|
7775
7779
|
case "url": break;
|
|
@@ -7853,6 +7857,7 @@ function form_value_2_treedb_value(gobj, field_desc, value) {
|
|
|
7853
7857
|
case "enum":
|
|
7854
7858
|
case "uuid":
|
|
7855
7859
|
case "rowid":
|
|
7860
|
+
case "qualified":
|
|
7856
7861
|
case "password":
|
|
7857
7862
|
case "email":
|
|
7858
7863
|
case "url": break;
|
|
@@ -16930,7 +16935,8 @@ function transform__treedb_value_2_table_value(gobj, col, value, row, field) {
|
|
|
16930
16935
|
case "email":
|
|
16931
16936
|
case "tel":
|
|
16932
16937
|
case "url":
|
|
16933
|
-
case "rowid":
|
|
16938
|
+
case "rowid":
|
|
16939
|
+
case "qualified": break;
|
|
16934
16940
|
case "integer": break;
|
|
16935
16941
|
case "real": break;
|
|
16936
16942
|
case "boolean": break;
|
|
@@ -17393,6 +17399,7 @@ function transform__form_value_2_treedb_value(gobj, col, value, operation) {
|
|
|
17393
17399
|
const field_desc = (0, _yuneta_gobj_js.treedb_get_field_desc)(col);
|
|
17394
17400
|
switch (field_desc.type) {
|
|
17395
17401
|
case "rowid":
|
|
17402
|
+
case "qualified":
|
|
17396
17403
|
if (operation === "create") value = "";
|
|
17397
17404
|
break;
|
|
17398
17405
|
case "string":
|
|
@@ -19767,11 +19774,26 @@ function register_c_yui_treedb_graph() {
|
|
|
19767
19774
|
/***********************************************************************
|
|
19768
19775
|
* c_yui_treedb_schema.js
|
|
19769
19776
|
*
|
|
19770
|
-
* Schema-graph landing
|
|
19771
|
-
*
|
|
19772
|
-
*
|
|
19773
|
-
*
|
|
19774
|
-
* the
|
|
19777
|
+
* Schema-graph landing: the treedb drawn the way its `.c` literal
|
|
19778
|
+
* draws it in ASCII (treedb_schema_*.c, treedb_system_schema.c) —
|
|
19779
|
+
* one CARD per topic listing its fields in schema order, one edge
|
|
19780
|
+
* per hook, from the row that declares the hook to the fkey row of
|
|
19781
|
+
* the child it names. Built from the schema `descs` alone: no data,
|
|
19782
|
+
* no backend calls.
|
|
19783
|
+
*
|
|
19784
|
+
* WHY THE CARD AND NOT A DOT. A topic is its fields; a schema is
|
|
19785
|
+
* read to find out what a topic holds and what links to what. A
|
|
19786
|
+
* graph of labelled dots answers neither, and the node graph next
|
|
19787
|
+
* door (C_G6_NODES_TREE) answers a different question entirely —
|
|
19788
|
+
* it draws the RECORDS, so on a treedb whose records are schemas
|
|
19789
|
+
* it draws one box per column, hundreds of them, each labelled by
|
|
19790
|
+
* a pkey that may be a rowid. This view draws the schema itself.
|
|
19791
|
+
*
|
|
19792
|
+
* The marks are the notation of the `.c` literals, so the drawing
|
|
19793
|
+
* and the source read the same — see schema_rows().
|
|
19794
|
+
*
|
|
19795
|
+
* A node click opens that topic's table (a real hash navigation
|
|
19796
|
+
* via the host-supplied `node_route`). An alternate landing to the
|
|
19775
19797
|
* topic cards, in the spirit of "every treedb is a graph".
|
|
19776
19798
|
*
|
|
19777
19799
|
* Copyright (c) 2026, ArtGins.
|
|
@@ -19781,6 +19803,27 @@ function register_c_yui_treedb_graph() {
|
|
|
19781
19803
|
* Constants
|
|
19782
19804
|
***************************************************************/
|
|
19783
19805
|
var GCLASS_NAME$2 = "C_YUI_TREEDB_SCHEMA";
|
|
19806
|
+
/************************************************************
|
|
19807
|
+
* Geometry of a card, in one place: the row height is what
|
|
19808
|
+
* turns a field's index into the vertical position of its
|
|
19809
|
+
* port, so an edge leaves the very row that declares the
|
|
19810
|
+
* hook and lands on the row that declares the fkey.
|
|
19811
|
+
************************************************************/
|
|
19812
|
+
var CARD_HEADER_H = 30;
|
|
19813
|
+
var CARD_ROW_H = 19;
|
|
19814
|
+
var CARD_PAD_V = 7;
|
|
19815
|
+
var CARD_MIN_W = 210;
|
|
19816
|
+
var CARD_MAX_W = 340;
|
|
19817
|
+
var topic_colors = [
|
|
19818
|
+
"rgb(237, 201, 73)",
|
|
19819
|
+
"rgb(118, 183, 178)",
|
|
19820
|
+
"rgb(255, 157, 167)",
|
|
19821
|
+
"rgb(175, 122, 161)",
|
|
19822
|
+
"rgb(89, 161, 79)",
|
|
19823
|
+
"rgb(186, 176, 171)",
|
|
19824
|
+
"rgb(66, 146, 198)"
|
|
19825
|
+
];
|
|
19826
|
+
var SYSTEM_TOPIC_COLOR = "rgb(148, 163, 184)";
|
|
19784
19827
|
/***************************************************************
|
|
19785
19828
|
* Data
|
|
19786
19829
|
***************************************************************/
|
|
@@ -19842,48 +19885,254 @@ function build_ui$2(gobj) {
|
|
|
19842
19885
|
(0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", $container);
|
|
19843
19886
|
}
|
|
19844
19887
|
/************************************************************
|
|
19888
|
+
* The shape of a hook/fkey field, from the type it declares.
|
|
19889
|
+
* `dict` and `object` are the SAME thing to treedb (both build
|
|
19890
|
+
* a json object keyed by child id), and so are `list` and
|
|
19891
|
+
* `array` — see the hook/fkey switches in tr_treedb.c. A
|
|
19892
|
+
* `string` holds one reference, so it draws as one.
|
|
19893
|
+
************************************************************/
|
|
19894
|
+
function hook_mark(type) {
|
|
19895
|
+
if (type === "dict" || type === "object") return "{}";
|
|
19896
|
+
if (type === "list" || type === "array") return "[]";
|
|
19897
|
+
return "()";
|
|
19898
|
+
}
|
|
19899
|
+
/************************************************************
|
|
19900
|
+
* The fields of one topic, as the schema declares them.
|
|
19901
|
+
*
|
|
19902
|
+
* The marks are the notation of the schema `.c` literals
|
|
19903
|
+
* (treedb_schema_*.c, treedb_system_schema.c), so the
|
|
19904
|
+
* drawing and the source read the same:
|
|
19905
|
+
* {} dict hook (N unique children)
|
|
19906
|
+
* [] list hook (n not-unique children)
|
|
19907
|
+
* () 1 child
|
|
19908
|
+
* (↖) 1 fkey (1 parent)
|
|
19909
|
+
* [↖] n fkeys (n parents)
|
|
19910
|
+
* {↖} N fkeys (N parents)
|
|
19911
|
+
* * required
|
|
19912
|
+
* # the primary key
|
|
19913
|
+
*
|
|
19914
|
+
* A column can be BOTH hook and fkey, so the flags are read
|
|
19915
|
+
* here rather than through treedb_get_field_desc's single
|
|
19916
|
+
* `type`, which keeps only the last flag it saw.
|
|
19917
|
+
************************************************************/
|
|
19918
|
+
function schema_rows(desc) {
|
|
19919
|
+
let rows = [];
|
|
19920
|
+
if (!(0, _yuneta_gobj_js.is_object)(desc) || !Array.isArray(desc.cols)) return rows;
|
|
19921
|
+
for (let col of desc.cols) {
|
|
19922
|
+
if (!(0, _yuneta_gobj_js.is_object)(col) || !col.id) continue;
|
|
19923
|
+
let flags = Array.isArray(col.flag) ? col.flag : [];
|
|
19924
|
+
let is_hook = flags.indexOf("hook") >= 0;
|
|
19925
|
+
let is_fkey = flags.indexOf("fkey") >= 0;
|
|
19926
|
+
let mark = "";
|
|
19927
|
+
if (is_hook) mark = hook_mark(col.type);
|
|
19928
|
+
if (is_fkey) {
|
|
19929
|
+
let fmark = hook_mark(col.type).replace("}", "↖}").replace("]", "↖]").replace(")", "↖)");
|
|
19930
|
+
mark = mark ? mark + " " + fmark : fmark;
|
|
19931
|
+
}
|
|
19932
|
+
rows.push({
|
|
19933
|
+
name: col.id,
|
|
19934
|
+
type: col.type || "",
|
|
19935
|
+
mark,
|
|
19936
|
+
is_hook,
|
|
19937
|
+
is_fkey,
|
|
19938
|
+
is_pkey: col.id === desc.pkey,
|
|
19939
|
+
required: flags.indexOf("required") >= 0 || flags.indexOf("notnull") >= 0
|
|
19940
|
+
});
|
|
19941
|
+
}
|
|
19942
|
+
return rows;
|
|
19943
|
+
}
|
|
19944
|
+
/************************************************************
|
|
19945
|
+
* Size of the card that holds those rows. The width follows
|
|
19946
|
+
* the longest line so a long field name is not clipped into
|
|
19947
|
+
* an ellipsis — the point of the drawing is to read them.
|
|
19948
|
+
************************************************************/
|
|
19949
|
+
function card_size(rows) {
|
|
19950
|
+
let longest = 0;
|
|
19951
|
+
for (let row of rows) {
|
|
19952
|
+
let len = row.name.length + (row.mark ? row.mark.length + 2 : 0) + 2;
|
|
19953
|
+
if (len > longest) longest = len;
|
|
19954
|
+
}
|
|
19955
|
+
let w = Math.round(26 + longest * 6.6);
|
|
19956
|
+
if (w < CARD_MIN_W) w = CARD_MIN_W;
|
|
19957
|
+
if (w > CARD_MAX_W) w = CARD_MAX_W;
|
|
19958
|
+
let h = 44 + rows.length * CARD_ROW_H;
|
|
19959
|
+
return [w, h];
|
|
19960
|
+
}
|
|
19961
|
+
/************************************************************
|
|
19962
|
+
* Vertical placement of a field's port, as a fraction of the
|
|
19963
|
+
* card height: the centre of its own row.
|
|
19964
|
+
************************************************************/
|
|
19965
|
+
function row_placement(index, height) {
|
|
19966
|
+
return (37 + index * CARD_ROW_H + CARD_ROW_H / 2) / height;
|
|
19967
|
+
}
|
|
19968
|
+
/************************************************************
|
|
19969
|
+
* The card itself. Header = topic name on its topic colour;
|
|
19970
|
+
* body = one line per field, in schema order.
|
|
19971
|
+
************************************************************/
|
|
19972
|
+
function build_card_innerHTML(topic, rows, color, dark) {
|
|
19973
|
+
let surface = dark ? "#1b2230" : "#ffffff";
|
|
19974
|
+
let bg = dark ? `color-mix(in srgb, ${color} 18%, #232b38)` : `color-mix(in srgb, ${color} 6%, ${surface})`;
|
|
19975
|
+
let border = dark ? `color-mix(in srgb, ${color} 85%, #ffffff)` : color;
|
|
19976
|
+
let header_bg = dark ? `color-mix(in srgb, ${color} 45%, #2c3542)` : `color-mix(in srgb, ${color} 28%, ${surface})`;
|
|
19977
|
+
let title_color = dark ? "#e8eaed" : "#0f172a";
|
|
19978
|
+
let text_color = dark ? "#d3d8de" : "#334155";
|
|
19979
|
+
let mute_color = dark ? "#98a2b0" : "#7c8694";
|
|
19980
|
+
let rule_color = dark ? "rgba(255,255,255,0.06)" : "rgba(15,23,42,0.05)";
|
|
19981
|
+
let shadow = dark ? "0 1px 3px rgba(0,0,0,0.45), 0 1px 2px rgba(0,0,0,0.30)" : "0 1px 3px rgba(15,23,42,0.12), 0 1px 2px rgba(15,23,42,0.06)";
|
|
19982
|
+
let rows_html = "";
|
|
19983
|
+
for (let row of rows) {
|
|
19984
|
+
let lead = row.is_pkey ? "#" : row.required ? "*" : "";
|
|
19985
|
+
let name_weight = row.is_pkey || row.required ? "600" : "400";
|
|
19986
|
+
let name_color = row.is_hook || row.is_fkey ? title_color : text_color;
|
|
19987
|
+
rows_html += ` <div class="TREEDB_SCHEMA_FIELD" style="
|
|
19988
|
+
display: flex; align-items: center; gap: 6px;
|
|
19989
|
+
height: ${CARD_ROW_H}px; line-height: ${CARD_ROW_H}px;
|
|
19990
|
+
padding: 0 10px; border-top: 1px solid ${rule_color};
|
|
19991
|
+
">
|
|
19992
|
+
<span style="
|
|
19993
|
+
width: 8px; flex: 0 0 8px; text-align: center;
|
|
19994
|
+
color: ${mute_color}; font-size: 11px;
|
|
19995
|
+
">${lead}</span>
|
|
19996
|
+
<span style="
|
|
19997
|
+
flex: 1 1 auto; min-width: 0; overflow: hidden;
|
|
19998
|
+
text-overflow: ellipsis; white-space: nowrap;
|
|
19999
|
+
font-size: 12px; font-weight: ${name_weight}; color: ${name_color};
|
|
20000
|
+
">${(0, _yuneta_gobj_js.escapeHtml)(row.name)}</span>
|
|
20001
|
+
<span style="
|
|
20002
|
+
flex: 0 0 auto; font-size: 11px; color: ${mute_color};
|
|
20003
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
20004
|
+
">${(0, _yuneta_gobj_js.escapeHtml)(row.mark)}</span>
|
|
20005
|
+
</div>
|
|
20006
|
+
`;
|
|
20007
|
+
}
|
|
20008
|
+
return `
|
|
20009
|
+
<div class="TREEDB_SCHEMA_CARD" title="${(0, _yuneta_gobj_js.escapeHtml)(topic)}" style="
|
|
20010
|
+
box-sizing: border-box;
|
|
20011
|
+
width: 100%;
|
|
20012
|
+
height: 100%;
|
|
20013
|
+
background: ${bg};
|
|
20014
|
+
border: 1.5px solid ${border};
|
|
20015
|
+
border-radius: 8px;
|
|
20016
|
+
box-shadow: ${shadow};
|
|
20017
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
20018
|
+
display: flex;
|
|
20019
|
+
flex-direction: column;
|
|
20020
|
+
overflow: hidden;
|
|
20021
|
+
cursor: pointer;
|
|
20022
|
+
">
|
|
20023
|
+
<div class="TREEDB_SCHEMA_CARD_HEADER" style="
|
|
20024
|
+
height: ${CARD_HEADER_H}px; line-height: ${CARD_HEADER_H}px;
|
|
20025
|
+
flex: 0 0 ${CARD_HEADER_H}px;
|
|
20026
|
+
padding: 0 10px; background: ${header_bg}; color: ${title_color};
|
|
20027
|
+
font-size: 13px; font-weight: 700;
|
|
20028
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
20029
|
+
">${(0, _yuneta_gobj_js.escapeHtml)(topic)}</div>
|
|
20030
|
+
<div class="TREEDB_SCHEMA_CARD_BODY" style="
|
|
20031
|
+
flex: 1 1 auto; padding: ${CARD_PAD_V}px 0; overflow: hidden;
|
|
20032
|
+
">
|
|
20033
|
+
${rows_html} </div>
|
|
20034
|
+
</div>
|
|
20035
|
+
`;
|
|
20036
|
+
}
|
|
20037
|
+
/************************************************************
|
|
19845
20038
|
* Derive {nodes, edges} from the schema.
|
|
19846
|
-
*
|
|
19847
|
-
*
|
|
20039
|
+
*
|
|
20040
|
+
* Node = a topic, drawn as the card the `.c` literal draws in
|
|
20041
|
+
* ASCII: its name and its fields. Edge = a hook, from the row
|
|
20042
|
+
* that declares it to the fkey row of the child it names —
|
|
20043
|
+
* `'hook': {'users': 'departments'}` says both ends, so the
|
|
20044
|
+
* arrow can land where the `.c` drawing lands it. An fkey
|
|
20045
|
+
* whose parent declares no hook still gets its edge, or a
|
|
20046
|
+
* half-declared schema would draw as disconnected.
|
|
20047
|
+
*
|
|
19848
20048
|
* Left-to-right dagre follows the parent -> child data flow.
|
|
19849
20049
|
************************************************************/
|
|
19850
20050
|
function schema_to_graph(gobj) {
|
|
19851
20051
|
let descs = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "descs");
|
|
19852
20052
|
let system = (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "system");
|
|
20053
|
+
let dark = yui_is_dark();
|
|
19853
20054
|
let nodes = [];
|
|
19854
|
-
let
|
|
20055
|
+
let cards = {};
|
|
19855
20056
|
if (!(0, _yuneta_gobj_js.is_object)(descs)) return {
|
|
19856
20057
|
nodes,
|
|
19857
20058
|
edges: []
|
|
19858
20059
|
};
|
|
20060
|
+
let idx = 0;
|
|
19859
20061
|
for (let topic of Object.keys(descs)) {
|
|
19860
|
-
|
|
19861
|
-
|
|
20062
|
+
let is_system = topic.substring(0, 2) === "__";
|
|
20063
|
+
if (!system && is_system) continue;
|
|
20064
|
+
let rows = schema_rows(descs[topic]);
|
|
20065
|
+
let color;
|
|
20066
|
+
if (is_system) color = SYSTEM_TOPIC_COLOR;
|
|
20067
|
+
else {
|
|
20068
|
+
color = topic_colors[idx % topic_colors.length];
|
|
20069
|
+
idx++;
|
|
20070
|
+
}
|
|
20071
|
+
cards[topic] = {
|
|
20072
|
+
rows,
|
|
20073
|
+
size: card_size(rows),
|
|
20074
|
+
color
|
|
20075
|
+
};
|
|
20076
|
+
}
|
|
20077
|
+
for (let topic of Object.keys(cards)) {
|
|
20078
|
+
let card = cards[topic];
|
|
20079
|
+
let [w, h] = card.size;
|
|
20080
|
+
let ports = [];
|
|
20081
|
+
for (let i = 0; i < card.rows.length; i++) {
|
|
20082
|
+
let row = card.rows[i];
|
|
20083
|
+
if (!row.is_hook && !row.is_fkey) continue;
|
|
20084
|
+
ports.push({
|
|
20085
|
+
key: row.name,
|
|
20086
|
+
placement: [row.is_hook ? 1 : 0, row_placement(i, h)],
|
|
20087
|
+
fill: card.color,
|
|
20088
|
+
stroke: getStrokeColor(card.color)
|
|
20089
|
+
});
|
|
20090
|
+
}
|
|
19862
20091
|
nodes.push({
|
|
19863
20092
|
id: topic,
|
|
19864
|
-
|
|
20093
|
+
type: "html",
|
|
20094
|
+
style: {
|
|
20095
|
+
size: [w, h],
|
|
20096
|
+
dx: -w / 2,
|
|
20097
|
+
dy: -h / 2,
|
|
20098
|
+
innerHTML: build_card_innerHTML(topic, card.rows, card.color, dark),
|
|
20099
|
+
port: ports.length > 0,
|
|
20100
|
+
ports,
|
|
20101
|
+
portR: 3,
|
|
20102
|
+
portLineWidth: 1
|
|
20103
|
+
},
|
|
20104
|
+
data: {
|
|
20105
|
+
topic_name: topic,
|
|
20106
|
+
rows: card.rows,
|
|
20107
|
+
color: card.color
|
|
20108
|
+
}
|
|
19865
20109
|
});
|
|
19866
20110
|
}
|
|
19867
20111
|
let edge_seen = {};
|
|
19868
20112
|
let edges = [];
|
|
19869
|
-
let add_edge = (source, target) => {
|
|
19870
|
-
if (!
|
|
19871
|
-
let key = source
|
|
20113
|
+
let add_edge = (source, source_port, target, target_port) => {
|
|
20114
|
+
if (!cards[source] || !cards[target]) return;
|
|
20115
|
+
let key = `${source}|${source_port}|${target}|${target_port}`;
|
|
19872
20116
|
if (edge_seen[key]) return;
|
|
19873
20117
|
edge_seen[key] = true;
|
|
19874
20118
|
edges.push({
|
|
19875
20119
|
id: key,
|
|
20120
|
+
type: "cubic",
|
|
19876
20121
|
source,
|
|
19877
|
-
target
|
|
20122
|
+
target,
|
|
20123
|
+
style: {
|
|
20124
|
+
sourcePort: source_port,
|
|
20125
|
+
targetPort: target_port
|
|
20126
|
+
}
|
|
19878
20127
|
});
|
|
19879
20128
|
};
|
|
19880
|
-
for (let topic of Object.keys(
|
|
20129
|
+
for (let topic of Object.keys(cards)) {
|
|
19881
20130
|
let desc = descs[topic];
|
|
19882
20131
|
if (!(0, _yuneta_gobj_js.is_object)(desc) || !Array.isArray(desc.cols)) continue;
|
|
19883
20132
|
for (let col of desc.cols) {
|
|
19884
|
-
if (!col) continue;
|
|
19885
|
-
if ((0, _yuneta_gobj_js.is_object)(col.hook)) for (let child of Object.keys(col.hook)) add_edge(topic, child);
|
|
19886
|
-
|
|
20133
|
+
if (!(0, _yuneta_gobj_js.is_object)(col) || !col.id) continue;
|
|
20134
|
+
if ((0, _yuneta_gobj_js.is_object)(col.hook)) for (let child of Object.keys(col.hook)) add_edge(topic, col.id, child, col.hook[child]);
|
|
20135
|
+
if ((0, _yuneta_gobj_js.is_object)(col.fkey)) for (let parent of Object.keys(col.fkey)) add_edge(parent, col.fkey[parent], topic, col.id);
|
|
19887
20136
|
}
|
|
19888
20137
|
}
|
|
19889
20138
|
return {
|
|
@@ -19892,26 +20141,15 @@ function schema_to_graph(gobj) {
|
|
|
19892
20141
|
};
|
|
19893
20142
|
}
|
|
19894
20143
|
/************************************************************
|
|
19895
|
-
* The theme-dependent
|
|
19896
|
-
*
|
|
20144
|
+
* The theme-dependent edge style, in ONE place: applied as
|
|
20145
|
+
* the graph is built and re-applied on a theme switch
|
|
19897
20146
|
* (ac_theme), which restyles the LIVE graph — rebuilding it
|
|
19898
20147
|
* would drop the user's zoom/pan and any dragged node.
|
|
19899
20148
|
************************************************************/
|
|
19900
|
-
function node_style(dark) {
|
|
19901
|
-
return {
|
|
19902
|
-
size: 40,
|
|
19903
|
-
fill: "#5B8FF9",
|
|
19904
|
-
labelText: (d) => d.id,
|
|
19905
|
-
labelPlacement: "bottom",
|
|
19906
|
-
labelFill: dark ? "#e6e6e6" : "#333333",
|
|
19907
|
-
labelBackground: true,
|
|
19908
|
-
labelBackgroundFill: dark ? "rgba(0,0,0,0.5)" : "rgba(255,255,255,0.7)",
|
|
19909
|
-
cursor: "pointer"
|
|
19910
|
-
};
|
|
19911
|
-
}
|
|
19912
20149
|
function edge_style(dark) {
|
|
19913
20150
|
return {
|
|
19914
|
-
stroke: dark ? "#
|
|
20151
|
+
stroke: dark ? "#7a8593" : "#9aa4b2",
|
|
20152
|
+
lineWidth: 1.2,
|
|
19915
20153
|
endArrow: true
|
|
19916
20154
|
};
|
|
19917
20155
|
}
|
|
@@ -19943,13 +20181,12 @@ function build_graph$1(gobj) {
|
|
|
19943
20181
|
container: $container,
|
|
19944
20182
|
autoResize: true,
|
|
19945
20183
|
data,
|
|
19946
|
-
node: { style: node_style(dark) },
|
|
19947
20184
|
edge: { style: edge_style(dark) },
|
|
19948
20185
|
layout: {
|
|
19949
20186
|
type: "antv-dagre",
|
|
19950
20187
|
rankdir: "LR",
|
|
19951
|
-
nodesep:
|
|
19952
|
-
ranksep:
|
|
20188
|
+
nodesep: 28,
|
|
20189
|
+
ranksep: 90
|
|
19953
20190
|
},
|
|
19954
20191
|
behaviors: [
|
|
19955
20192
|
"zoom-canvas",
|
|
@@ -20034,8 +20271,17 @@ function ac_theme$1(gobj, event, kw, src) {
|
|
|
20034
20271
|
let dark = kw && kw.theme ? kw.theme === "dark" : yui_is_dark();
|
|
20035
20272
|
try {
|
|
20036
20273
|
graph.setTheme(dark ? "dark" : "light");
|
|
20037
|
-
graph.setNode({ style: node_style(dark) });
|
|
20038
20274
|
graph.setEdge({ style: edge_style(dark) });
|
|
20275
|
+
let updates = [];
|
|
20276
|
+
for (let node of graph.getNodeData()) {
|
|
20277
|
+
let data = node.data;
|
|
20278
|
+
if (!(0, _yuneta_gobj_js.is_object)(data) || !Array.isArray(data.rows)) continue;
|
|
20279
|
+
updates.push({
|
|
20280
|
+
id: node.id,
|
|
20281
|
+
style: { innerHTML: build_card_innerHTML(data.topic_name, data.rows, data.color, dark) }
|
|
20282
|
+
});
|
|
20283
|
+
}
|
|
20284
|
+
if (updates.length > 0) graph.updateNodeData(updates);
|
|
20039
20285
|
graph.draw().catch((e) => {
|
|
20040
20286
|
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: schema graph redraw failed: ${e}`);
|
|
20041
20287
|
});
|
|
@@ -20100,6 +20346,61 @@ function register_c_yui_treedb_schema() {
|
|
|
20100
20346
|
return create_gclass$2(GCLASS_NAME$2);
|
|
20101
20347
|
}
|
|
20102
20348
|
//#endregion
|
|
20349
|
+
//#region src/treedb_node_label.js
|
|
20350
|
+
/***********************************************************************
|
|
20351
|
+
* treedb_node_label.js
|
|
20352
|
+
*
|
|
20353
|
+
* Pure, testable logic behind the label of a node in the treedb
|
|
20354
|
+
* graph: WHAT A RECORD IS CALLED, which is not always what it is
|
|
20355
|
+
* keyed by. Kept out of the gclass so it can be unit-tested with
|
|
20356
|
+
* no DOM and no G6.
|
|
20357
|
+
*
|
|
20358
|
+
* Copyright (c) 2026, ArtGins.
|
|
20359
|
+
* All Rights Reserved.
|
|
20360
|
+
***********************************************************************/
|
|
20361
|
+
/************************************************************
|
|
20362
|
+
* What a node is CALLED, which is not always what it is
|
|
20363
|
+
* keyed by.
|
|
20364
|
+
*
|
|
20365
|
+
* A topic whose id column is flagged `rowid`, `uuid` or
|
|
20366
|
+
* `qualified` keys its records by something that is not the
|
|
20367
|
+
* name — a counter, a random string, or the name with every
|
|
20368
|
+
* ancestor in front of it — and the name a human knows the
|
|
20369
|
+
* record by lives in the secondary key the topic declares
|
|
20370
|
+
* (`pkey2s`, carried in the desc since SDK > 7.13.0). The
|
|
20371
|
+
* `topics` and `cols` topics of treedb_system_schema are the
|
|
20372
|
+
* case that forced this: named in `value`, so every card in
|
|
20373
|
+
* the graph read "181", "225", "193" while they were keyed by
|
|
20374
|
+
* rowid, and reads the whole path now that they are
|
|
20375
|
+
* qualified.
|
|
20376
|
+
*
|
|
20377
|
+
* The pkey stays reachable: it is the card's tooltip.
|
|
20378
|
+
*
|
|
20379
|
+
* Falls back to the id whenever the schema does not say
|
|
20380
|
+
* otherwise — an older node whose desc carries no `pkey2s`
|
|
20381
|
+
* included.
|
|
20382
|
+
************************************************************/
|
|
20383
|
+
function node_label(desc, record) {
|
|
20384
|
+
let id = record.id;
|
|
20385
|
+
let id_col = null;
|
|
20386
|
+
if (Array.isArray(desc.cols)) {
|
|
20387
|
+
for (let col of desc.cols) if (col && col.id === (desc.pkey || "id")) {
|
|
20388
|
+
id_col = col;
|
|
20389
|
+
break;
|
|
20390
|
+
}
|
|
20391
|
+
}
|
|
20392
|
+
if (!id_col || !Array.isArray(id_col.flag)) return id;
|
|
20393
|
+
if (id_col.flag.indexOf("rowid") < 0 && id_col.flag.indexOf("uuid") < 0 && id_col.flag.indexOf("qualified") < 0) return id;
|
|
20394
|
+
let pkey2s = desc.pkey2s;
|
|
20395
|
+
if (typeof pkey2s === "string") pkey2s = pkey2s ? [pkey2s] : [];
|
|
20396
|
+
if (!Array.isArray(pkey2s)) return id;
|
|
20397
|
+
for (let name of pkey2s) {
|
|
20398
|
+
let value = record[name];
|
|
20399
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
20400
|
+
}
|
|
20401
|
+
return id;
|
|
20402
|
+
}
|
|
20403
|
+
//#endregion
|
|
20103
20404
|
//#region node_modules/@babel/runtime/helpers/esm/typeof.js
|
|
20104
20405
|
function _typeof(o) {
|
|
20105
20406
|
"@babel/helpers - typeof";
|
|
@@ -47632,19 +47933,19 @@ function create_topic_node(gobj, desc, record) {
|
|
|
47632
47933
|
style.size = [116, 40];
|
|
47633
47934
|
style.dx = -58;
|
|
47634
47935
|
style.dy = -20;
|
|
47635
|
-
style.innerHTML = build_chip_innerHTML(desc.color, priv.theme, record.icon, record.id);
|
|
47936
|
+
style.innerHTML = build_chip_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), record.id);
|
|
47636
47937
|
} else if (node_treedb_type === "extended") {
|
|
47637
47938
|
node_graph_type = "html";
|
|
47638
47939
|
style.size = [144, 66];
|
|
47639
47940
|
style.dx = -72;
|
|
47640
47941
|
style.dy = -33;
|
|
47641
|
-
style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, record
|
|
47942
|
+
style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, true, record.id);
|
|
47642
47943
|
} else {
|
|
47643
47944
|
node_graph_type = "html";
|
|
47644
47945
|
style.size = [172, 96];
|
|
47645
47946
|
style.dx = -86;
|
|
47646
47947
|
style.dy = -48;
|
|
47647
|
-
style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, record
|
|
47948
|
+
style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, false, record.id);
|
|
47648
47949
|
}
|
|
47649
47950
|
let topic_props = priv._graph_properties[desc.topic_name];
|
|
47650
47951
|
let topic_defaults = (0, _yuneta_gobj_js.is_object)(topic_props) && (0, _yuneta_gobj_js.is_object)(topic_props.defaults) ? topic_props.defaults : null;
|
|
@@ -47712,15 +48013,15 @@ function update_topic_node(gobj, desc, node_name, record) {
|
|
|
47712
48013
|
nodedata.data.record = record;
|
|
47713
48014
|
if (desc.node_treedb_type === "child") graph.updateNodeData([{
|
|
47714
48015
|
id: node_name,
|
|
47715
|
-
style: { innerHTML: build_chip_innerHTML(desc.color, priv.theme, record.icon, record.id) }
|
|
48016
|
+
style: { innerHTML: build_chip_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), record.id) }
|
|
47716
48017
|
}]);
|
|
47717
48018
|
else if (desc.node_treedb_type === "extended") graph.updateNodeData([{
|
|
47718
48019
|
id: node_name,
|
|
47719
|
-
style: { innerHTML: build_node_innerHTML(desc.color, priv.theme, record.icon, record
|
|
48020
|
+
style: { innerHTML: build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, true, record.id) }
|
|
47720
48021
|
}]);
|
|
47721
48022
|
else if (desc.node_treedb_type === "hierarchical") graph.updateNodeData([{
|
|
47722
48023
|
id: node_name,
|
|
47723
|
-
style: { innerHTML: build_node_innerHTML(desc.color, priv.theme, record.icon, record
|
|
48024
|
+
style: { innerHTML: build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, false, record.id) }
|
|
47724
48025
|
}]);
|
|
47725
48026
|
} catch (e) {
|
|
47726
48027
|
(0, _yuneta_gobj_js.log_error)(e.message);
|
|
@@ -49627,7 +49928,7 @@ function show_node_popover(gobj) {
|
|
|
49627
49928
|
};
|
|
49628
49929
|
if (node_graph_type === "hierarchical") {
|
|
49629
49930
|
let record = nodeData.data.record || {};
|
|
49630
|
-
updateStyle.innerHTML = build_node_innerHTML(fill, priv.theme, record.icon,
|
|
49931
|
+
updateStyle.innerHTML = build_node_innerHTML(fill, priv.theme, record.icon, node_label(nodeData.data.desc, record), nodeData.data.desc.topic_name, false, record.id);
|
|
49631
49932
|
}
|
|
49632
49933
|
graph.updateNodeData([{
|
|
49633
49934
|
id: node_id,
|
|
@@ -49667,7 +49968,7 @@ function show_node_popover(gobj) {
|
|
|
49667
49968
|
};
|
|
49668
49969
|
if (node_graph_type === "hierarchical") {
|
|
49669
49970
|
let record = nodeData.data.record || {};
|
|
49670
|
-
restoreStyle.innerHTML = build_node_innerHTML(origFill, priv.theme, record.icon,
|
|
49971
|
+
restoreStyle.innerHTML = build_node_innerHTML(origFill, priv.theme, record.icon, node_label(nodeData.data.desc, record), nodeData.data.desc.topic_name, false, record.id);
|
|
49671
49972
|
}
|
|
49672
49973
|
graph.updateNodeData([{
|
|
49673
49974
|
id: node_id,
|
|
@@ -49768,15 +50069,15 @@ function refresh_html_nodes_theme(gobj, theme) {
|
|
|
49768
50069
|
let record = nd.data.record || {};
|
|
49769
50070
|
if (tt === "hierarchical") updates.push({
|
|
49770
50071
|
id: nodes[i].id,
|
|
49771
|
-
style: { innerHTML: build_node_innerHTML(nd.data.desc.color, theme, record.icon,
|
|
50072
|
+
style: { innerHTML: build_node_innerHTML(nd.data.desc.color, theme, record.icon, node_label(nd.data.desc, record), nd.data.desc.topic_name, false, record.id) }
|
|
49772
50073
|
});
|
|
49773
50074
|
else if (tt === "child") updates.push({
|
|
49774
50075
|
id: nodes[i].id,
|
|
49775
|
-
style: { innerHTML: build_chip_innerHTML(nd.data.desc.color, theme, record.icon, record.id) }
|
|
50076
|
+
style: { innerHTML: build_chip_innerHTML(nd.data.desc.color, theme, record.icon, node_label(nd.data.desc, record), record.id) }
|
|
49776
50077
|
});
|
|
49777
50078
|
else if (tt === "extended") updates.push({
|
|
49778
50079
|
id: nodes[i].id,
|
|
49779
|
-
style: { innerHTML: build_node_innerHTML(nd.data.desc.color, theme, record.icon,
|
|
50080
|
+
style: { innerHTML: build_node_innerHTML(nd.data.desc.color, theme, record.icon, node_label(nd.data.desc, record), nd.data.desc.topic_name, true, record.id) }
|
|
49780
50081
|
});
|
|
49781
50082
|
}
|
|
49782
50083
|
if (updates.length > 0) graph.updateNodeData(updates);
|
|
@@ -49808,7 +50109,8 @@ function refresh_default_edges_theme(gobj, theme) {
|
|
|
49808
50109
|
* but lighter (1px border, no shadow, single line). The name is
|
|
49809
50110
|
* always legible (ellipsis + native title tooltip on overflow).
|
|
49810
50111
|
************************************************************/
|
|
49811
|
-
function build_chip_innerHTML(color, theme, icon,
|
|
50112
|
+
function build_chip_innerHTML(color, theme, icon, label, key) {
|
|
50113
|
+
let title = key || label;
|
|
49812
50114
|
let dark = theme === "dark";
|
|
49813
50115
|
let bg = dark ? `color-mix(in srgb, ${color} 30%, #2c3542)` : `color-mix(in srgb, ${color} 10%, ${dark ? "#1b2230" : "#ffffff"})`;
|
|
49814
50116
|
let border = dark ? `color-mix(in srgb, ${color} 85%, #ffffff)` : color;
|
|
@@ -49819,7 +50121,7 @@ function build_chip_innerHTML(color, theme, icon, id) {
|
|
|
49819
50121
|
margin-right: 6px; flex: 0 0 auto;
|
|
49820
50122
|
"/>`;
|
|
49821
50123
|
return `
|
|
49822
|
-
<div title="${(0, _yuneta_gobj_js.escapeHtml)(
|
|
50124
|
+
<div title="${(0, _yuneta_gobj_js.escapeHtml)(title)}" style="
|
|
49823
50125
|
box-sizing: border-box;
|
|
49824
50126
|
width: 100%;
|
|
49825
50127
|
height: 100%;
|
|
@@ -49836,7 +50138,7 @@ function build_chip_innerHTML(color, theme, icon, id) {
|
|
|
49836
50138
|
">${icon_html}<span style="
|
|
49837
50139
|
font-size: 12px; font-weight: 600; line-height: 1;
|
|
49838
50140
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
49839
|
-
">${(0, _yuneta_gobj_js.escapeHtml)(
|
|
50141
|
+
">${(0, _yuneta_gobj_js.escapeHtml)(label)}</span>
|
|
49840
50142
|
</div>
|
|
49841
50143
|
`;
|
|
49842
50144
|
}
|
|
@@ -49850,7 +50152,8 @@ function build_chip_innerHTML(color, theme, icon, id) {
|
|
|
49850
50152
|
* colour is kept (per-topic differentiation) but softened via
|
|
49851
50153
|
* color-mix instead of a harsh saturated fill. Theme-aware.
|
|
49852
50154
|
************************************************************/
|
|
49853
|
-
function build_node_innerHTML(color, theme, icon,
|
|
50155
|
+
function build_node_innerHTML(color, theme, icon, label, topic_name, structural, key) {
|
|
50156
|
+
let title = key || label;
|
|
49854
50157
|
let dark = theme === "dark";
|
|
49855
50158
|
let surface = dark ? "#1b2230" : "#ffffff";
|
|
49856
50159
|
let bg, border, border_style;
|
|
@@ -49880,7 +50183,7 @@ function build_node_innerHTML(color, theme, icon, id, topic_name, structural) {
|
|
|
49880
50183
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
49881
50184
|
">${(0, _yuneta_gobj_js.escapeHtml)(topic_name)}</div>`;
|
|
49882
50185
|
return `
|
|
49883
|
-
<div title="${(0, _yuneta_gobj_js.escapeHtml)(
|
|
50186
|
+
<div title="${(0, _yuneta_gobj_js.escapeHtml)(title)}" style="
|
|
49884
50187
|
box-sizing: border-box;
|
|
49885
50188
|
width: 100%;
|
|
49886
50189
|
height: 100%;
|
|
@@ -49903,7 +50206,7 @@ function build_node_innerHTML(color, theme, icon, id, topic_name, structural) {
|
|
|
49903
50206
|
max-width: 100%; overflow: hidden; text-overflow: ellipsis;
|
|
49904
50207
|
display: -webkit-box; -webkit-line-clamp: 2;
|
|
49905
50208
|
-webkit-box-orient: vertical; word-break: break-word;
|
|
49906
|
-
">${(0, _yuneta_gobj_js.escapeHtml)(
|
|
50209
|
+
">${(0, _yuneta_gobj_js.escapeHtml)(label)}</div>${sub_html}
|
|
49907
50210
|
</div>
|
|
49908
50211
|
`;
|
|
49909
50212
|
}
|
|
@@ -50124,7 +50427,7 @@ function apply_node_properties(gobj, node_id, fill, stroke, lineWidth, scope) {
|
|
|
50124
50427
|
};
|
|
50125
50428
|
if (nd.data.desc.node_treedb_type === "hierarchical") {
|
|
50126
50429
|
let record = nd.data.record || {};
|
|
50127
|
-
updateStyle.innerHTML = build_node_innerHTML(fill, priv.theme, record.icon,
|
|
50430
|
+
updateStyle.innerHTML = build_node_innerHTML(fill, priv.theme, record.icon, node_label(nd.data.desc, record), nd.data.desc.topic_name, false, record.id);
|
|
50128
50431
|
}
|
|
50129
50432
|
updates.push({
|
|
50130
50433
|
id: nodes[i].id,
|