@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.es.js
CHANGED
|
@@ -6467,6 +6467,7 @@ function build_form_field_conf(gobj, field_desc) {
|
|
|
6467
6467
|
field_conf.extras.pattern = "^-?\\d+(\\.\\d+)?$";
|
|
6468
6468
|
break;
|
|
6469
6469
|
case "rowid":
|
|
6470
|
+
case "qualified":
|
|
6470
6471
|
field_conf.tag = "input";
|
|
6471
6472
|
field_conf.inputType = "text";
|
|
6472
6473
|
break;
|
|
@@ -6608,7 +6609,8 @@ function build_fkey_ref$1(gobj, field_desc, value) {
|
|
|
6608
6609
|
/************************************************************
|
|
6609
6610
|
* Apply the form mode to the pkey field:
|
|
6610
6611
|
* "update" -> pkey readonly, not required
|
|
6611
|
-
* "create" -> pkey writable and required
|
|
6612
|
+
* "create" -> pkey writable and required, unless the store
|
|
6613
|
+
* hands the key out itself (rowid, qualified)
|
|
6612
6614
|
* Empty form_mode keeps the template-declared behaviour
|
|
6613
6615
|
* (backward compatible).
|
|
6614
6616
|
************************************************************/
|
|
@@ -6621,8 +6623,8 @@ function apply_form_mode(gobj, $form) {
|
|
|
6621
6623
|
log_error(sprintf("%s: form_mode '%s' but form has no pkey field '%s'", gobj_short_name(gobj), mode, pkey));
|
|
6622
6624
|
return;
|
|
6623
6625
|
}
|
|
6624
|
-
let
|
|
6625
|
-
if (mode === "create" && !
|
|
6626
|
+
let type = $input.field_desc ? $input.field_desc.type : "";
|
|
6627
|
+
if (mode === "create" && !(type === "rowid" || type === "qualified")) {
|
|
6626
6628
|
$input.removeAttribute("readonly");
|
|
6627
6629
|
$input.setAttribute("required", "");
|
|
6628
6630
|
} else {
|
|
@@ -7400,6 +7402,7 @@ function template2columns(gobj, columns, template, sub_elements) {
|
|
|
7400
7402
|
};
|
|
7401
7403
|
break;
|
|
7402
7404
|
case "rowid":
|
|
7405
|
+
case "qualified":
|
|
7403
7406
|
column = null;
|
|
7404
7407
|
break;
|
|
7405
7408
|
case "boolean":
|
|
@@ -7765,6 +7768,7 @@ function treedb_value_2_form_value(gobj, field_desc, value) {
|
|
|
7765
7768
|
case "enum":
|
|
7766
7769
|
case "uuid":
|
|
7767
7770
|
case "rowid":
|
|
7771
|
+
case "qualified":
|
|
7768
7772
|
case "password":
|
|
7769
7773
|
case "email":
|
|
7770
7774
|
case "url": break;
|
|
@@ -7848,6 +7852,7 @@ function form_value_2_treedb_value(gobj, field_desc, value) {
|
|
|
7848
7852
|
case "enum":
|
|
7849
7853
|
case "uuid":
|
|
7850
7854
|
case "rowid":
|
|
7855
|
+
case "qualified":
|
|
7851
7856
|
case "password":
|
|
7852
7857
|
case "email":
|
|
7853
7858
|
case "url": break;
|
|
@@ -16925,7 +16930,8 @@ function transform__treedb_value_2_table_value(gobj, col, value, row, field) {
|
|
|
16925
16930
|
case "email":
|
|
16926
16931
|
case "tel":
|
|
16927
16932
|
case "url":
|
|
16928
|
-
case "rowid":
|
|
16933
|
+
case "rowid":
|
|
16934
|
+
case "qualified": break;
|
|
16929
16935
|
case "integer": break;
|
|
16930
16936
|
case "real": break;
|
|
16931
16937
|
case "boolean": break;
|
|
@@ -17388,6 +17394,7 @@ function transform__form_value_2_treedb_value(gobj, col, value, operation) {
|
|
|
17388
17394
|
const field_desc = treedb_get_field_desc(col);
|
|
17389
17395
|
switch (field_desc.type) {
|
|
17390
17396
|
case "rowid":
|
|
17397
|
+
case "qualified":
|
|
17391
17398
|
if (operation === "create") value = "";
|
|
17392
17399
|
break;
|
|
17393
17400
|
case "string":
|
|
@@ -19754,11 +19761,26 @@ function register_c_yui_treedb_graph() {
|
|
|
19754
19761
|
/***********************************************************************
|
|
19755
19762
|
* c_yui_treedb_schema.js
|
|
19756
19763
|
*
|
|
19757
|
-
* Schema-graph landing
|
|
19758
|
-
*
|
|
19759
|
-
*
|
|
19760
|
-
*
|
|
19761
|
-
* the
|
|
19764
|
+
* Schema-graph landing: the treedb drawn the way its `.c` literal
|
|
19765
|
+
* draws it in ASCII (treedb_schema_*.c, treedb_system_schema.c) —
|
|
19766
|
+
* one CARD per topic listing its fields in schema order, one edge
|
|
19767
|
+
* per hook, from the row that declares the hook to the fkey row of
|
|
19768
|
+
* the child it names. Built from the schema `descs` alone: no data,
|
|
19769
|
+
* no backend calls.
|
|
19770
|
+
*
|
|
19771
|
+
* WHY THE CARD AND NOT A DOT. A topic is its fields; a schema is
|
|
19772
|
+
* read to find out what a topic holds and what links to what. A
|
|
19773
|
+
* graph of labelled dots answers neither, and the node graph next
|
|
19774
|
+
* door (C_G6_NODES_TREE) answers a different question entirely —
|
|
19775
|
+
* it draws the RECORDS, so on a treedb whose records are schemas
|
|
19776
|
+
* it draws one box per column, hundreds of them, each labelled by
|
|
19777
|
+
* a pkey that may be a rowid. This view draws the schema itself.
|
|
19778
|
+
*
|
|
19779
|
+
* The marks are the notation of the `.c` literals, so the drawing
|
|
19780
|
+
* and the source read the same — see schema_rows().
|
|
19781
|
+
*
|
|
19782
|
+
* A node click opens that topic's table (a real hash navigation
|
|
19783
|
+
* via the host-supplied `node_route`). An alternate landing to the
|
|
19762
19784
|
* topic cards, in the spirit of "every treedb is a graph".
|
|
19763
19785
|
*
|
|
19764
19786
|
* Copyright (c) 2026, ArtGins.
|
|
@@ -19768,6 +19790,27 @@ function register_c_yui_treedb_graph() {
|
|
|
19768
19790
|
* Constants
|
|
19769
19791
|
***************************************************************/
|
|
19770
19792
|
var GCLASS_NAME$2 = "C_YUI_TREEDB_SCHEMA";
|
|
19793
|
+
/************************************************************
|
|
19794
|
+
* Geometry of a card, in one place: the row height is what
|
|
19795
|
+
* turns a field's index into the vertical position of its
|
|
19796
|
+
* port, so an edge leaves the very row that declares the
|
|
19797
|
+
* hook and lands on the row that declares the fkey.
|
|
19798
|
+
************************************************************/
|
|
19799
|
+
var CARD_HEADER_H = 30;
|
|
19800
|
+
var CARD_ROW_H = 19;
|
|
19801
|
+
var CARD_PAD_V = 7;
|
|
19802
|
+
var CARD_MIN_W = 210;
|
|
19803
|
+
var CARD_MAX_W = 340;
|
|
19804
|
+
var topic_colors = [
|
|
19805
|
+
"rgb(237, 201, 73)",
|
|
19806
|
+
"rgb(118, 183, 178)",
|
|
19807
|
+
"rgb(255, 157, 167)",
|
|
19808
|
+
"rgb(175, 122, 161)",
|
|
19809
|
+
"rgb(89, 161, 79)",
|
|
19810
|
+
"rgb(186, 176, 171)",
|
|
19811
|
+
"rgb(66, 146, 198)"
|
|
19812
|
+
];
|
|
19813
|
+
var SYSTEM_TOPIC_COLOR = "rgb(148, 163, 184)";
|
|
19771
19814
|
/***************************************************************
|
|
19772
19815
|
* Data
|
|
19773
19816
|
***************************************************************/
|
|
@@ -19829,48 +19872,254 @@ function build_ui$2(gobj) {
|
|
|
19829
19872
|
gobj_write_attr(gobj, "$container", $container);
|
|
19830
19873
|
}
|
|
19831
19874
|
/************************************************************
|
|
19875
|
+
* The shape of a hook/fkey field, from the type it declares.
|
|
19876
|
+
* `dict` and `object` are the SAME thing to treedb (both build
|
|
19877
|
+
* a json object keyed by child id), and so are `list` and
|
|
19878
|
+
* `array` — see the hook/fkey switches in tr_treedb.c. A
|
|
19879
|
+
* `string` holds one reference, so it draws as one.
|
|
19880
|
+
************************************************************/
|
|
19881
|
+
function hook_mark(type) {
|
|
19882
|
+
if (type === "dict" || type === "object") return "{}";
|
|
19883
|
+
if (type === "list" || type === "array") return "[]";
|
|
19884
|
+
return "()";
|
|
19885
|
+
}
|
|
19886
|
+
/************************************************************
|
|
19887
|
+
* The fields of one topic, as the schema declares them.
|
|
19888
|
+
*
|
|
19889
|
+
* The marks are the notation of the schema `.c` literals
|
|
19890
|
+
* (treedb_schema_*.c, treedb_system_schema.c), so the
|
|
19891
|
+
* drawing and the source read the same:
|
|
19892
|
+
* {} dict hook (N unique children)
|
|
19893
|
+
* [] list hook (n not-unique children)
|
|
19894
|
+
* () 1 child
|
|
19895
|
+
* (↖) 1 fkey (1 parent)
|
|
19896
|
+
* [↖] n fkeys (n parents)
|
|
19897
|
+
* {↖} N fkeys (N parents)
|
|
19898
|
+
* * required
|
|
19899
|
+
* # the primary key
|
|
19900
|
+
*
|
|
19901
|
+
* A column can be BOTH hook and fkey, so the flags are read
|
|
19902
|
+
* here rather than through treedb_get_field_desc's single
|
|
19903
|
+
* `type`, which keeps only the last flag it saw.
|
|
19904
|
+
************************************************************/
|
|
19905
|
+
function schema_rows(desc) {
|
|
19906
|
+
let rows = [];
|
|
19907
|
+
if (!is_object(desc) || !Array.isArray(desc.cols)) return rows;
|
|
19908
|
+
for (let col of desc.cols) {
|
|
19909
|
+
if (!is_object(col) || !col.id) continue;
|
|
19910
|
+
let flags = Array.isArray(col.flag) ? col.flag : [];
|
|
19911
|
+
let is_hook = flags.indexOf("hook") >= 0;
|
|
19912
|
+
let is_fkey = flags.indexOf("fkey") >= 0;
|
|
19913
|
+
let mark = "";
|
|
19914
|
+
if (is_hook) mark = hook_mark(col.type);
|
|
19915
|
+
if (is_fkey) {
|
|
19916
|
+
let fmark = hook_mark(col.type).replace("}", "↖}").replace("]", "↖]").replace(")", "↖)");
|
|
19917
|
+
mark = mark ? mark + " " + fmark : fmark;
|
|
19918
|
+
}
|
|
19919
|
+
rows.push({
|
|
19920
|
+
name: col.id,
|
|
19921
|
+
type: col.type || "",
|
|
19922
|
+
mark,
|
|
19923
|
+
is_hook,
|
|
19924
|
+
is_fkey,
|
|
19925
|
+
is_pkey: col.id === desc.pkey,
|
|
19926
|
+
required: flags.indexOf("required") >= 0 || flags.indexOf("notnull") >= 0
|
|
19927
|
+
});
|
|
19928
|
+
}
|
|
19929
|
+
return rows;
|
|
19930
|
+
}
|
|
19931
|
+
/************************************************************
|
|
19932
|
+
* Size of the card that holds those rows. The width follows
|
|
19933
|
+
* the longest line so a long field name is not clipped into
|
|
19934
|
+
* an ellipsis — the point of the drawing is to read them.
|
|
19935
|
+
************************************************************/
|
|
19936
|
+
function card_size(rows) {
|
|
19937
|
+
let longest = 0;
|
|
19938
|
+
for (let row of rows) {
|
|
19939
|
+
let len = row.name.length + (row.mark ? row.mark.length + 2 : 0) + 2;
|
|
19940
|
+
if (len > longest) longest = len;
|
|
19941
|
+
}
|
|
19942
|
+
let w = Math.round(26 + longest * 6.6);
|
|
19943
|
+
if (w < CARD_MIN_W) w = CARD_MIN_W;
|
|
19944
|
+
if (w > CARD_MAX_W) w = CARD_MAX_W;
|
|
19945
|
+
let h = 44 + rows.length * CARD_ROW_H;
|
|
19946
|
+
return [w, h];
|
|
19947
|
+
}
|
|
19948
|
+
/************************************************************
|
|
19949
|
+
* Vertical placement of a field's port, as a fraction of the
|
|
19950
|
+
* card height: the centre of its own row.
|
|
19951
|
+
************************************************************/
|
|
19952
|
+
function row_placement(index, height) {
|
|
19953
|
+
return (37 + index * CARD_ROW_H + CARD_ROW_H / 2) / height;
|
|
19954
|
+
}
|
|
19955
|
+
/************************************************************
|
|
19956
|
+
* The card itself. Header = topic name on its topic colour;
|
|
19957
|
+
* body = one line per field, in schema order.
|
|
19958
|
+
************************************************************/
|
|
19959
|
+
function build_card_innerHTML(topic, rows, color, dark) {
|
|
19960
|
+
let surface = dark ? "#1b2230" : "#ffffff";
|
|
19961
|
+
let bg = dark ? `color-mix(in srgb, ${color} 18%, #232b38)` : `color-mix(in srgb, ${color} 6%, ${surface})`;
|
|
19962
|
+
let border = dark ? `color-mix(in srgb, ${color} 85%, #ffffff)` : color;
|
|
19963
|
+
let header_bg = dark ? `color-mix(in srgb, ${color} 45%, #2c3542)` : `color-mix(in srgb, ${color} 28%, ${surface})`;
|
|
19964
|
+
let title_color = dark ? "#e8eaed" : "#0f172a";
|
|
19965
|
+
let text_color = dark ? "#d3d8de" : "#334155";
|
|
19966
|
+
let mute_color = dark ? "#98a2b0" : "#7c8694";
|
|
19967
|
+
let rule_color = dark ? "rgba(255,255,255,0.06)" : "rgba(15,23,42,0.05)";
|
|
19968
|
+
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)";
|
|
19969
|
+
let rows_html = "";
|
|
19970
|
+
for (let row of rows) {
|
|
19971
|
+
let lead = row.is_pkey ? "#" : row.required ? "*" : "";
|
|
19972
|
+
let name_weight = row.is_pkey || row.required ? "600" : "400";
|
|
19973
|
+
let name_color = row.is_hook || row.is_fkey ? title_color : text_color;
|
|
19974
|
+
rows_html += ` <div class="TREEDB_SCHEMA_FIELD" style="
|
|
19975
|
+
display: flex; align-items: center; gap: 6px;
|
|
19976
|
+
height: ${CARD_ROW_H}px; line-height: ${CARD_ROW_H}px;
|
|
19977
|
+
padding: 0 10px; border-top: 1px solid ${rule_color};
|
|
19978
|
+
">
|
|
19979
|
+
<span style="
|
|
19980
|
+
width: 8px; flex: 0 0 8px; text-align: center;
|
|
19981
|
+
color: ${mute_color}; font-size: 11px;
|
|
19982
|
+
">${lead}</span>
|
|
19983
|
+
<span style="
|
|
19984
|
+
flex: 1 1 auto; min-width: 0; overflow: hidden;
|
|
19985
|
+
text-overflow: ellipsis; white-space: nowrap;
|
|
19986
|
+
font-size: 12px; font-weight: ${name_weight}; color: ${name_color};
|
|
19987
|
+
">${escapeHtml(row.name)}</span>
|
|
19988
|
+
<span style="
|
|
19989
|
+
flex: 0 0 auto; font-size: 11px; color: ${mute_color};
|
|
19990
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
19991
|
+
">${escapeHtml(row.mark)}</span>
|
|
19992
|
+
</div>
|
|
19993
|
+
`;
|
|
19994
|
+
}
|
|
19995
|
+
return `
|
|
19996
|
+
<div class="TREEDB_SCHEMA_CARD" title="${escapeHtml(topic)}" style="
|
|
19997
|
+
box-sizing: border-box;
|
|
19998
|
+
width: 100%;
|
|
19999
|
+
height: 100%;
|
|
20000
|
+
background: ${bg};
|
|
20001
|
+
border: 1.5px solid ${border};
|
|
20002
|
+
border-radius: 8px;
|
|
20003
|
+
box-shadow: ${shadow};
|
|
20004
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
20005
|
+
display: flex;
|
|
20006
|
+
flex-direction: column;
|
|
20007
|
+
overflow: hidden;
|
|
20008
|
+
cursor: pointer;
|
|
20009
|
+
">
|
|
20010
|
+
<div class="TREEDB_SCHEMA_CARD_HEADER" style="
|
|
20011
|
+
height: ${CARD_HEADER_H}px; line-height: ${CARD_HEADER_H}px;
|
|
20012
|
+
flex: 0 0 ${CARD_HEADER_H}px;
|
|
20013
|
+
padding: 0 10px; background: ${header_bg}; color: ${title_color};
|
|
20014
|
+
font-size: 13px; font-weight: 700;
|
|
20015
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
20016
|
+
">${escapeHtml(topic)}</div>
|
|
20017
|
+
<div class="TREEDB_SCHEMA_CARD_BODY" style="
|
|
20018
|
+
flex: 1 1 auto; padding: ${CARD_PAD_V}px 0; overflow: hidden;
|
|
20019
|
+
">
|
|
20020
|
+
${rows_html} </div>
|
|
20021
|
+
</div>
|
|
20022
|
+
`;
|
|
20023
|
+
}
|
|
20024
|
+
/************************************************************
|
|
19832
20025
|
* Derive {nodes, edges} from the schema.
|
|
19833
|
-
*
|
|
19834
|
-
*
|
|
20026
|
+
*
|
|
20027
|
+
* Node = a topic, drawn as the card the `.c` literal draws in
|
|
20028
|
+
* ASCII: its name and its fields. Edge = a hook, from the row
|
|
20029
|
+
* that declares it to the fkey row of the child it names —
|
|
20030
|
+
* `'hook': {'users': 'departments'}` says both ends, so the
|
|
20031
|
+
* arrow can land where the `.c` drawing lands it. An fkey
|
|
20032
|
+
* whose parent declares no hook still gets its edge, or a
|
|
20033
|
+
* half-declared schema would draw as disconnected.
|
|
20034
|
+
*
|
|
19835
20035
|
* Left-to-right dagre follows the parent -> child data flow.
|
|
19836
20036
|
************************************************************/
|
|
19837
20037
|
function schema_to_graph(gobj) {
|
|
19838
20038
|
let descs = gobj_read_attr(gobj, "descs");
|
|
19839
20039
|
let system = gobj_read_bool_attr(gobj, "system");
|
|
20040
|
+
let dark = yui_is_dark();
|
|
19840
20041
|
let nodes = [];
|
|
19841
|
-
let
|
|
20042
|
+
let cards = {};
|
|
19842
20043
|
if (!is_object(descs)) return {
|
|
19843
20044
|
nodes,
|
|
19844
20045
|
edges: []
|
|
19845
20046
|
};
|
|
20047
|
+
let idx = 0;
|
|
19846
20048
|
for (let topic of Object.keys(descs)) {
|
|
19847
|
-
|
|
19848
|
-
|
|
20049
|
+
let is_system = topic.substring(0, 2) === "__";
|
|
20050
|
+
if (!system && is_system) continue;
|
|
20051
|
+
let rows = schema_rows(descs[topic]);
|
|
20052
|
+
let color;
|
|
20053
|
+
if (is_system) color = SYSTEM_TOPIC_COLOR;
|
|
20054
|
+
else {
|
|
20055
|
+
color = topic_colors[idx % topic_colors.length];
|
|
20056
|
+
idx++;
|
|
20057
|
+
}
|
|
20058
|
+
cards[topic] = {
|
|
20059
|
+
rows,
|
|
20060
|
+
size: card_size(rows),
|
|
20061
|
+
color
|
|
20062
|
+
};
|
|
20063
|
+
}
|
|
20064
|
+
for (let topic of Object.keys(cards)) {
|
|
20065
|
+
let card = cards[topic];
|
|
20066
|
+
let [w, h] = card.size;
|
|
20067
|
+
let ports = [];
|
|
20068
|
+
for (let i = 0; i < card.rows.length; i++) {
|
|
20069
|
+
let row = card.rows[i];
|
|
20070
|
+
if (!row.is_hook && !row.is_fkey) continue;
|
|
20071
|
+
ports.push({
|
|
20072
|
+
key: row.name,
|
|
20073
|
+
placement: [row.is_hook ? 1 : 0, row_placement(i, h)],
|
|
20074
|
+
fill: card.color,
|
|
20075
|
+
stroke: getStrokeColor(card.color)
|
|
20076
|
+
});
|
|
20077
|
+
}
|
|
19849
20078
|
nodes.push({
|
|
19850
20079
|
id: topic,
|
|
19851
|
-
|
|
20080
|
+
type: "html",
|
|
20081
|
+
style: {
|
|
20082
|
+
size: [w, h],
|
|
20083
|
+
dx: -w / 2,
|
|
20084
|
+
dy: -h / 2,
|
|
20085
|
+
innerHTML: build_card_innerHTML(topic, card.rows, card.color, dark),
|
|
20086
|
+
port: ports.length > 0,
|
|
20087
|
+
ports,
|
|
20088
|
+
portR: 3,
|
|
20089
|
+
portLineWidth: 1
|
|
20090
|
+
},
|
|
20091
|
+
data: {
|
|
20092
|
+
topic_name: topic,
|
|
20093
|
+
rows: card.rows,
|
|
20094
|
+
color: card.color
|
|
20095
|
+
}
|
|
19852
20096
|
});
|
|
19853
20097
|
}
|
|
19854
20098
|
let edge_seen = {};
|
|
19855
20099
|
let edges = [];
|
|
19856
|
-
let add_edge = (source, target) => {
|
|
19857
|
-
if (!
|
|
19858
|
-
let key = source
|
|
20100
|
+
let add_edge = (source, source_port, target, target_port) => {
|
|
20101
|
+
if (!cards[source] || !cards[target]) return;
|
|
20102
|
+
let key = `${source}|${source_port}|${target}|${target_port}`;
|
|
19859
20103
|
if (edge_seen[key]) return;
|
|
19860
20104
|
edge_seen[key] = true;
|
|
19861
20105
|
edges.push({
|
|
19862
20106
|
id: key,
|
|
20107
|
+
type: "cubic",
|
|
19863
20108
|
source,
|
|
19864
|
-
target
|
|
20109
|
+
target,
|
|
20110
|
+
style: {
|
|
20111
|
+
sourcePort: source_port,
|
|
20112
|
+
targetPort: target_port
|
|
20113
|
+
}
|
|
19865
20114
|
});
|
|
19866
20115
|
};
|
|
19867
|
-
for (let topic of Object.keys(
|
|
20116
|
+
for (let topic of Object.keys(cards)) {
|
|
19868
20117
|
let desc = descs[topic];
|
|
19869
20118
|
if (!is_object(desc) || !Array.isArray(desc.cols)) continue;
|
|
19870
20119
|
for (let col of desc.cols) {
|
|
19871
|
-
if (!col) continue;
|
|
19872
|
-
if (is_object(col.hook)) for (let child of Object.keys(col.hook)) add_edge(topic, child);
|
|
19873
|
-
|
|
20120
|
+
if (!is_object(col) || !col.id) continue;
|
|
20121
|
+
if (is_object(col.hook)) for (let child of Object.keys(col.hook)) add_edge(topic, col.id, child, col.hook[child]);
|
|
20122
|
+
if (is_object(col.fkey)) for (let parent of Object.keys(col.fkey)) add_edge(parent, col.fkey[parent], topic, col.id);
|
|
19874
20123
|
}
|
|
19875
20124
|
}
|
|
19876
20125
|
return {
|
|
@@ -19879,26 +20128,15 @@ function schema_to_graph(gobj) {
|
|
|
19879
20128
|
};
|
|
19880
20129
|
}
|
|
19881
20130
|
/************************************************************
|
|
19882
|
-
* The theme-dependent
|
|
19883
|
-
*
|
|
20131
|
+
* The theme-dependent edge style, in ONE place: applied as
|
|
20132
|
+
* the graph is built and re-applied on a theme switch
|
|
19884
20133
|
* (ac_theme), which restyles the LIVE graph — rebuilding it
|
|
19885
20134
|
* would drop the user's zoom/pan and any dragged node.
|
|
19886
20135
|
************************************************************/
|
|
19887
|
-
function node_style(dark) {
|
|
19888
|
-
return {
|
|
19889
|
-
size: 40,
|
|
19890
|
-
fill: "#5B8FF9",
|
|
19891
|
-
labelText: (d) => d.id,
|
|
19892
|
-
labelPlacement: "bottom",
|
|
19893
|
-
labelFill: dark ? "#e6e6e6" : "#333333",
|
|
19894
|
-
labelBackground: true,
|
|
19895
|
-
labelBackgroundFill: dark ? "rgba(0,0,0,0.5)" : "rgba(255,255,255,0.7)",
|
|
19896
|
-
cursor: "pointer"
|
|
19897
|
-
};
|
|
19898
|
-
}
|
|
19899
20136
|
function edge_style(dark) {
|
|
19900
20137
|
return {
|
|
19901
|
-
stroke: dark ? "#
|
|
20138
|
+
stroke: dark ? "#7a8593" : "#9aa4b2",
|
|
20139
|
+
lineWidth: 1.2,
|
|
19902
20140
|
endArrow: true
|
|
19903
20141
|
};
|
|
19904
20142
|
}
|
|
@@ -19930,13 +20168,12 @@ function build_graph$1(gobj) {
|
|
|
19930
20168
|
container: $container,
|
|
19931
20169
|
autoResize: true,
|
|
19932
20170
|
data,
|
|
19933
|
-
node: { style: node_style(dark) },
|
|
19934
20171
|
edge: { style: edge_style(dark) },
|
|
19935
20172
|
layout: {
|
|
19936
20173
|
type: "antv-dagre",
|
|
19937
20174
|
rankdir: "LR",
|
|
19938
|
-
nodesep:
|
|
19939
|
-
ranksep:
|
|
20175
|
+
nodesep: 28,
|
|
20176
|
+
ranksep: 90
|
|
19940
20177
|
},
|
|
19941
20178
|
behaviors: [
|
|
19942
20179
|
"zoom-canvas",
|
|
@@ -20021,8 +20258,17 @@ function ac_theme$1(gobj, event, kw, src) {
|
|
|
20021
20258
|
let dark = kw && kw.theme ? kw.theme === "dark" : yui_is_dark();
|
|
20022
20259
|
try {
|
|
20023
20260
|
graph.setTheme(dark ? "dark" : "light");
|
|
20024
|
-
graph.setNode({ style: node_style(dark) });
|
|
20025
20261
|
graph.setEdge({ style: edge_style(dark) });
|
|
20262
|
+
let updates = [];
|
|
20263
|
+
for (let node of graph.getNodeData()) {
|
|
20264
|
+
let data = node.data;
|
|
20265
|
+
if (!is_object(data) || !Array.isArray(data.rows)) continue;
|
|
20266
|
+
updates.push({
|
|
20267
|
+
id: node.id,
|
|
20268
|
+
style: { innerHTML: build_card_innerHTML(data.topic_name, data.rows, data.color, dark) }
|
|
20269
|
+
});
|
|
20270
|
+
}
|
|
20271
|
+
if (updates.length > 0) graph.updateNodeData(updates);
|
|
20026
20272
|
graph.draw().catch((e) => {
|
|
20027
20273
|
log_error(`${gobj_short_name(gobj)}: schema graph redraw failed: ${e}`);
|
|
20028
20274
|
});
|
|
@@ -20087,6 +20333,61 @@ function register_c_yui_treedb_schema() {
|
|
|
20087
20333
|
return create_gclass$2(GCLASS_NAME$2);
|
|
20088
20334
|
}
|
|
20089
20335
|
//#endregion
|
|
20336
|
+
//#region src/treedb_node_label.js
|
|
20337
|
+
/***********************************************************************
|
|
20338
|
+
* treedb_node_label.js
|
|
20339
|
+
*
|
|
20340
|
+
* Pure, testable logic behind the label of a node in the treedb
|
|
20341
|
+
* graph: WHAT A RECORD IS CALLED, which is not always what it is
|
|
20342
|
+
* keyed by. Kept out of the gclass so it can be unit-tested with
|
|
20343
|
+
* no DOM and no G6.
|
|
20344
|
+
*
|
|
20345
|
+
* Copyright (c) 2026, ArtGins.
|
|
20346
|
+
* All Rights Reserved.
|
|
20347
|
+
***********************************************************************/
|
|
20348
|
+
/************************************************************
|
|
20349
|
+
* What a node is CALLED, which is not always what it is
|
|
20350
|
+
* keyed by.
|
|
20351
|
+
*
|
|
20352
|
+
* A topic whose id column is flagged `rowid`, `uuid` or
|
|
20353
|
+
* `qualified` keys its records by something that is not the
|
|
20354
|
+
* name — a counter, a random string, or the name with every
|
|
20355
|
+
* ancestor in front of it — and the name a human knows the
|
|
20356
|
+
* record by lives in the secondary key the topic declares
|
|
20357
|
+
* (`pkey2s`, carried in the desc since SDK > 7.13.0). The
|
|
20358
|
+
* `topics` and `cols` topics of treedb_system_schema are the
|
|
20359
|
+
* case that forced this: named in `value`, so every card in
|
|
20360
|
+
* the graph read "181", "225", "193" while they were keyed by
|
|
20361
|
+
* rowid, and reads the whole path now that they are
|
|
20362
|
+
* qualified.
|
|
20363
|
+
*
|
|
20364
|
+
* The pkey stays reachable: it is the card's tooltip.
|
|
20365
|
+
*
|
|
20366
|
+
* Falls back to the id whenever the schema does not say
|
|
20367
|
+
* otherwise — an older node whose desc carries no `pkey2s`
|
|
20368
|
+
* included.
|
|
20369
|
+
************************************************************/
|
|
20370
|
+
function node_label(desc, record) {
|
|
20371
|
+
let id = record.id;
|
|
20372
|
+
let id_col = null;
|
|
20373
|
+
if (Array.isArray(desc.cols)) {
|
|
20374
|
+
for (let col of desc.cols) if (col && col.id === (desc.pkey || "id")) {
|
|
20375
|
+
id_col = col;
|
|
20376
|
+
break;
|
|
20377
|
+
}
|
|
20378
|
+
}
|
|
20379
|
+
if (!id_col || !Array.isArray(id_col.flag)) return id;
|
|
20380
|
+
if (id_col.flag.indexOf("rowid") < 0 && id_col.flag.indexOf("uuid") < 0 && id_col.flag.indexOf("qualified") < 0) return id;
|
|
20381
|
+
let pkey2s = desc.pkey2s;
|
|
20382
|
+
if (typeof pkey2s === "string") pkey2s = pkey2s ? [pkey2s] : [];
|
|
20383
|
+
if (!Array.isArray(pkey2s)) return id;
|
|
20384
|
+
for (let name of pkey2s) {
|
|
20385
|
+
let value = record[name];
|
|
20386
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
20387
|
+
}
|
|
20388
|
+
return id;
|
|
20389
|
+
}
|
|
20390
|
+
//#endregion
|
|
20090
20391
|
//#region node_modules/@babel/runtime/helpers/esm/typeof.js
|
|
20091
20392
|
function _typeof(o) {
|
|
20092
20393
|
"@babel/helpers - typeof";
|
|
@@ -47619,19 +47920,19 @@ function create_topic_node(gobj, desc, record) {
|
|
|
47619
47920
|
style.size = [116, 40];
|
|
47620
47921
|
style.dx = -58;
|
|
47621
47922
|
style.dy = -20;
|
|
47622
|
-
style.innerHTML = build_chip_innerHTML(desc.color, priv.theme, record.icon, record.id);
|
|
47923
|
+
style.innerHTML = build_chip_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), record.id);
|
|
47623
47924
|
} else if (node_treedb_type === "extended") {
|
|
47624
47925
|
node_graph_type = "html";
|
|
47625
47926
|
style.size = [144, 66];
|
|
47626
47927
|
style.dx = -72;
|
|
47627
47928
|
style.dy = -33;
|
|
47628
|
-
style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, record
|
|
47929
|
+
style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, true, record.id);
|
|
47629
47930
|
} else {
|
|
47630
47931
|
node_graph_type = "html";
|
|
47631
47932
|
style.size = [172, 96];
|
|
47632
47933
|
style.dx = -86;
|
|
47633
47934
|
style.dy = -48;
|
|
47634
|
-
style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, record
|
|
47935
|
+
style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, false, record.id);
|
|
47635
47936
|
}
|
|
47636
47937
|
let topic_props = priv._graph_properties[desc.topic_name];
|
|
47637
47938
|
let topic_defaults = is_object(topic_props) && is_object(topic_props.defaults) ? topic_props.defaults : null;
|
|
@@ -47699,15 +48000,15 @@ function update_topic_node(gobj, desc, node_name, record) {
|
|
|
47699
48000
|
nodedata.data.record = record;
|
|
47700
48001
|
if (desc.node_treedb_type === "child") graph.updateNodeData([{
|
|
47701
48002
|
id: node_name,
|
|
47702
|
-
style: { innerHTML: build_chip_innerHTML(desc.color, priv.theme, record.icon, record.id) }
|
|
48003
|
+
style: { innerHTML: build_chip_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), record.id) }
|
|
47703
48004
|
}]);
|
|
47704
48005
|
else if (desc.node_treedb_type === "extended") graph.updateNodeData([{
|
|
47705
48006
|
id: node_name,
|
|
47706
|
-
style: { innerHTML: build_node_innerHTML(desc.color, priv.theme, record.icon, record
|
|
48007
|
+
style: { innerHTML: build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, true, record.id) }
|
|
47707
48008
|
}]);
|
|
47708
48009
|
else if (desc.node_treedb_type === "hierarchical") graph.updateNodeData([{
|
|
47709
48010
|
id: node_name,
|
|
47710
|
-
style: { innerHTML: build_node_innerHTML(desc.color, priv.theme, record.icon, record
|
|
48011
|
+
style: { innerHTML: build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, false, record.id) }
|
|
47711
48012
|
}]);
|
|
47712
48013
|
} catch (e) {
|
|
47713
48014
|
log_error(e.message);
|
|
@@ -49614,7 +49915,7 @@ function show_node_popover(gobj) {
|
|
|
49614
49915
|
};
|
|
49615
49916
|
if (node_graph_type === "hierarchical") {
|
|
49616
49917
|
let record = nodeData.data.record || {};
|
|
49617
|
-
updateStyle.innerHTML = build_node_innerHTML(fill, priv.theme, record.icon,
|
|
49918
|
+
updateStyle.innerHTML = build_node_innerHTML(fill, priv.theme, record.icon, node_label(nodeData.data.desc, record), nodeData.data.desc.topic_name, false, record.id);
|
|
49618
49919
|
}
|
|
49619
49920
|
graph.updateNodeData([{
|
|
49620
49921
|
id: node_id,
|
|
@@ -49654,7 +49955,7 @@ function show_node_popover(gobj) {
|
|
|
49654
49955
|
};
|
|
49655
49956
|
if (node_graph_type === "hierarchical") {
|
|
49656
49957
|
let record = nodeData.data.record || {};
|
|
49657
|
-
restoreStyle.innerHTML = build_node_innerHTML(origFill, priv.theme, record.icon,
|
|
49958
|
+
restoreStyle.innerHTML = build_node_innerHTML(origFill, priv.theme, record.icon, node_label(nodeData.data.desc, record), nodeData.data.desc.topic_name, false, record.id);
|
|
49658
49959
|
}
|
|
49659
49960
|
graph.updateNodeData([{
|
|
49660
49961
|
id: node_id,
|
|
@@ -49755,15 +50056,15 @@ function refresh_html_nodes_theme(gobj, theme) {
|
|
|
49755
50056
|
let record = nd.data.record || {};
|
|
49756
50057
|
if (tt === "hierarchical") updates.push({
|
|
49757
50058
|
id: nodes[i].id,
|
|
49758
|
-
style: { innerHTML: build_node_innerHTML(nd.data.desc.color, theme, record.icon,
|
|
50059
|
+
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) }
|
|
49759
50060
|
});
|
|
49760
50061
|
else if (tt === "child") updates.push({
|
|
49761
50062
|
id: nodes[i].id,
|
|
49762
|
-
style: { innerHTML: build_chip_innerHTML(nd.data.desc.color, theme, record.icon, record.id) }
|
|
50063
|
+
style: { innerHTML: build_chip_innerHTML(nd.data.desc.color, theme, record.icon, node_label(nd.data.desc, record), record.id) }
|
|
49763
50064
|
});
|
|
49764
50065
|
else if (tt === "extended") updates.push({
|
|
49765
50066
|
id: nodes[i].id,
|
|
49766
|
-
style: { innerHTML: build_node_innerHTML(nd.data.desc.color, theme, record.icon,
|
|
50067
|
+
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) }
|
|
49767
50068
|
});
|
|
49768
50069
|
}
|
|
49769
50070
|
if (updates.length > 0) graph.updateNodeData(updates);
|
|
@@ -49795,7 +50096,8 @@ function refresh_default_edges_theme(gobj, theme) {
|
|
|
49795
50096
|
* but lighter (1px border, no shadow, single line). The name is
|
|
49796
50097
|
* always legible (ellipsis + native title tooltip on overflow).
|
|
49797
50098
|
************************************************************/
|
|
49798
|
-
function build_chip_innerHTML(color, theme, icon,
|
|
50099
|
+
function build_chip_innerHTML(color, theme, icon, label, key) {
|
|
50100
|
+
let title = key || label;
|
|
49799
50101
|
let dark = theme === "dark";
|
|
49800
50102
|
let bg = dark ? `color-mix(in srgb, ${color} 30%, #2c3542)` : `color-mix(in srgb, ${color} 10%, ${dark ? "#1b2230" : "#ffffff"})`;
|
|
49801
50103
|
let border = dark ? `color-mix(in srgb, ${color} 85%, #ffffff)` : color;
|
|
@@ -49806,7 +50108,7 @@ function build_chip_innerHTML(color, theme, icon, id) {
|
|
|
49806
50108
|
margin-right: 6px; flex: 0 0 auto;
|
|
49807
50109
|
"/>`;
|
|
49808
50110
|
return `
|
|
49809
|
-
<div title="${escapeHtml(
|
|
50111
|
+
<div title="${escapeHtml(title)}" style="
|
|
49810
50112
|
box-sizing: border-box;
|
|
49811
50113
|
width: 100%;
|
|
49812
50114
|
height: 100%;
|
|
@@ -49823,7 +50125,7 @@ function build_chip_innerHTML(color, theme, icon, id) {
|
|
|
49823
50125
|
">${icon_html}<span style="
|
|
49824
50126
|
font-size: 12px; font-weight: 600; line-height: 1;
|
|
49825
50127
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
49826
|
-
">${escapeHtml(
|
|
50128
|
+
">${escapeHtml(label)}</span>
|
|
49827
50129
|
</div>
|
|
49828
50130
|
`;
|
|
49829
50131
|
}
|
|
@@ -49837,7 +50139,8 @@ function build_chip_innerHTML(color, theme, icon, id) {
|
|
|
49837
50139
|
* colour is kept (per-topic differentiation) but softened via
|
|
49838
50140
|
* color-mix instead of a harsh saturated fill. Theme-aware.
|
|
49839
50141
|
************************************************************/
|
|
49840
|
-
function build_node_innerHTML(color, theme, icon,
|
|
50142
|
+
function build_node_innerHTML(color, theme, icon, label, topic_name, structural, key) {
|
|
50143
|
+
let title = key || label;
|
|
49841
50144
|
let dark = theme === "dark";
|
|
49842
50145
|
let surface = dark ? "#1b2230" : "#ffffff";
|
|
49843
50146
|
let bg, border, border_style;
|
|
@@ -49867,7 +50170,7 @@ function build_node_innerHTML(color, theme, icon, id, topic_name, structural) {
|
|
|
49867
50170
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
49868
50171
|
">${escapeHtml(topic_name)}</div>`;
|
|
49869
50172
|
return `
|
|
49870
|
-
<div title="${escapeHtml(
|
|
50173
|
+
<div title="${escapeHtml(title)}" style="
|
|
49871
50174
|
box-sizing: border-box;
|
|
49872
50175
|
width: 100%;
|
|
49873
50176
|
height: 100%;
|
|
@@ -49890,7 +50193,7 @@ function build_node_innerHTML(color, theme, icon, id, topic_name, structural) {
|
|
|
49890
50193
|
max-width: 100%; overflow: hidden; text-overflow: ellipsis;
|
|
49891
50194
|
display: -webkit-box; -webkit-line-clamp: 2;
|
|
49892
50195
|
-webkit-box-orient: vertical; word-break: break-word;
|
|
49893
|
-
">${escapeHtml(
|
|
50196
|
+
">${escapeHtml(label)}</div>${sub_html}
|
|
49894
50197
|
</div>
|
|
49895
50198
|
`;
|
|
49896
50199
|
}
|
|
@@ -50111,7 +50414,7 @@ function apply_node_properties(gobj, node_id, fill, stroke, lineWidth, scope) {
|
|
|
50111
50414
|
};
|
|
50112
50415
|
if (nd.data.desc.node_treedb_type === "hierarchical") {
|
|
50113
50416
|
let record = nd.data.record || {};
|
|
50114
|
-
updateStyle.innerHTML = build_node_innerHTML(fill, priv.theme, record.icon,
|
|
50417
|
+
updateStyle.innerHTML = build_node_innerHTML(fill, priv.theme, record.icon, node_label(nd.data.desc, record), nd.data.desc.topic_name, false, record.id);
|
|
50115
50418
|
}
|
|
50116
50419
|
updates.push({
|
|
50117
50420
|
id: nodes[i].id,
|