@yuneta/gobj-ui 5.16.0 → 5.17.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 +60 -10
- package/dist/gobj-ui.cjs.js +349 -56
- package/dist/gobj-ui.es.js +349 -56
- package/package.json +1 -1
- package/src/c_g6_nodes_tree.js +38 -24
- package/src/c_yui_treedb_schema.js +348 -43
- package/src/treedb_node_label.js +73 -0
- package/src/treedb_node_label.test.js +92 -0
package/dist/gobj-ui.es.js
CHANGED
|
@@ -19754,11 +19754,26 @@ function register_c_yui_treedb_graph() {
|
|
|
19754
19754
|
/***********************************************************************
|
|
19755
19755
|
* c_yui_treedb_schema.js
|
|
19756
19756
|
*
|
|
19757
|
-
* Schema-graph landing
|
|
19758
|
-
*
|
|
19759
|
-
*
|
|
19760
|
-
*
|
|
19761
|
-
* the
|
|
19757
|
+
* Schema-graph landing: the treedb drawn the way its `.c` literal
|
|
19758
|
+
* draws it in ASCII (treedb_schema_*.c, treedb_system_schema.c) —
|
|
19759
|
+
* one CARD per topic listing its fields in schema order, one edge
|
|
19760
|
+
* per hook, from the row that declares the hook to the fkey row of
|
|
19761
|
+
* the child it names. Built from the schema `descs` alone: no data,
|
|
19762
|
+
* no backend calls.
|
|
19763
|
+
*
|
|
19764
|
+
* WHY THE CARD AND NOT A DOT. A topic is its fields; a schema is
|
|
19765
|
+
* read to find out what a topic holds and what links to what. A
|
|
19766
|
+
* graph of labelled dots answers neither, and the node graph next
|
|
19767
|
+
* door (C_G6_NODES_TREE) answers a different question entirely —
|
|
19768
|
+
* it draws the RECORDS, so on a treedb whose records are schemas
|
|
19769
|
+
* it draws one box per column, hundreds of them, each labelled by
|
|
19770
|
+
* a pkey that may be a rowid. This view draws the schema itself.
|
|
19771
|
+
*
|
|
19772
|
+
* The marks are the notation of the `.c` literals, so the drawing
|
|
19773
|
+
* and the source read the same — see schema_rows().
|
|
19774
|
+
*
|
|
19775
|
+
* A node click opens that topic's table (a real hash navigation
|
|
19776
|
+
* via the host-supplied `node_route`). An alternate landing to the
|
|
19762
19777
|
* topic cards, in the spirit of "every treedb is a graph".
|
|
19763
19778
|
*
|
|
19764
19779
|
* Copyright (c) 2026, ArtGins.
|
|
@@ -19768,6 +19783,27 @@ function register_c_yui_treedb_graph() {
|
|
|
19768
19783
|
* Constants
|
|
19769
19784
|
***************************************************************/
|
|
19770
19785
|
var GCLASS_NAME$2 = "C_YUI_TREEDB_SCHEMA";
|
|
19786
|
+
/************************************************************
|
|
19787
|
+
* Geometry of a card, in one place: the row height is what
|
|
19788
|
+
* turns a field's index into the vertical position of its
|
|
19789
|
+
* port, so an edge leaves the very row that declares the
|
|
19790
|
+
* hook and lands on the row that declares the fkey.
|
|
19791
|
+
************************************************************/
|
|
19792
|
+
var CARD_HEADER_H = 30;
|
|
19793
|
+
var CARD_ROW_H = 19;
|
|
19794
|
+
var CARD_PAD_V = 7;
|
|
19795
|
+
var CARD_MIN_W = 210;
|
|
19796
|
+
var CARD_MAX_W = 340;
|
|
19797
|
+
var topic_colors = [
|
|
19798
|
+
"rgb(237, 201, 73)",
|
|
19799
|
+
"rgb(118, 183, 178)",
|
|
19800
|
+
"rgb(255, 157, 167)",
|
|
19801
|
+
"rgb(175, 122, 161)",
|
|
19802
|
+
"rgb(89, 161, 79)",
|
|
19803
|
+
"rgb(186, 176, 171)",
|
|
19804
|
+
"rgb(66, 146, 198)"
|
|
19805
|
+
];
|
|
19806
|
+
var SYSTEM_TOPIC_COLOR = "rgb(148, 163, 184)";
|
|
19771
19807
|
/***************************************************************
|
|
19772
19808
|
* Data
|
|
19773
19809
|
***************************************************************/
|
|
@@ -19829,48 +19865,254 @@ function build_ui$2(gobj) {
|
|
|
19829
19865
|
gobj_write_attr(gobj, "$container", $container);
|
|
19830
19866
|
}
|
|
19831
19867
|
/************************************************************
|
|
19868
|
+
* The shape of a hook/fkey field, from the type it declares.
|
|
19869
|
+
* `dict` and `object` are the SAME thing to treedb (both build
|
|
19870
|
+
* a json object keyed by child id), and so are `list` and
|
|
19871
|
+
* `array` — see the hook/fkey switches in tr_treedb.c. A
|
|
19872
|
+
* `string` holds one reference, so it draws as one.
|
|
19873
|
+
************************************************************/
|
|
19874
|
+
function hook_mark(type) {
|
|
19875
|
+
if (type === "dict" || type === "object") return "{}";
|
|
19876
|
+
if (type === "list" || type === "array") return "[]";
|
|
19877
|
+
return "()";
|
|
19878
|
+
}
|
|
19879
|
+
/************************************************************
|
|
19880
|
+
* The fields of one topic, as the schema declares them.
|
|
19881
|
+
*
|
|
19882
|
+
* The marks are the notation of the schema `.c` literals
|
|
19883
|
+
* (treedb_schema_*.c, treedb_system_schema.c), so the
|
|
19884
|
+
* drawing and the source read the same:
|
|
19885
|
+
* {} dict hook (N unique children)
|
|
19886
|
+
* [] list hook (n not-unique children)
|
|
19887
|
+
* () 1 child
|
|
19888
|
+
* (↖) 1 fkey (1 parent)
|
|
19889
|
+
* [↖] n fkeys (n parents)
|
|
19890
|
+
* {↖} N fkeys (N parents)
|
|
19891
|
+
* * required
|
|
19892
|
+
* # the primary key
|
|
19893
|
+
*
|
|
19894
|
+
* A column can be BOTH hook and fkey, so the flags are read
|
|
19895
|
+
* here rather than through treedb_get_field_desc's single
|
|
19896
|
+
* `type`, which keeps only the last flag it saw.
|
|
19897
|
+
************************************************************/
|
|
19898
|
+
function schema_rows(desc) {
|
|
19899
|
+
let rows = [];
|
|
19900
|
+
if (!is_object(desc) || !Array.isArray(desc.cols)) return rows;
|
|
19901
|
+
for (let col of desc.cols) {
|
|
19902
|
+
if (!is_object(col) || !col.id) continue;
|
|
19903
|
+
let flags = Array.isArray(col.flag) ? col.flag : [];
|
|
19904
|
+
let is_hook = flags.indexOf("hook") >= 0;
|
|
19905
|
+
let is_fkey = flags.indexOf("fkey") >= 0;
|
|
19906
|
+
let mark = "";
|
|
19907
|
+
if (is_hook) mark = hook_mark(col.type);
|
|
19908
|
+
if (is_fkey) {
|
|
19909
|
+
let fmark = hook_mark(col.type).replace("}", "↖}").replace("]", "↖]").replace(")", "↖)");
|
|
19910
|
+
mark = mark ? mark + " " + fmark : fmark;
|
|
19911
|
+
}
|
|
19912
|
+
rows.push({
|
|
19913
|
+
name: col.id,
|
|
19914
|
+
type: col.type || "",
|
|
19915
|
+
mark,
|
|
19916
|
+
is_hook,
|
|
19917
|
+
is_fkey,
|
|
19918
|
+
is_pkey: col.id === desc.pkey,
|
|
19919
|
+
required: flags.indexOf("required") >= 0 || flags.indexOf("notnull") >= 0
|
|
19920
|
+
});
|
|
19921
|
+
}
|
|
19922
|
+
return rows;
|
|
19923
|
+
}
|
|
19924
|
+
/************************************************************
|
|
19925
|
+
* Size of the card that holds those rows. The width follows
|
|
19926
|
+
* the longest line so a long field name is not clipped into
|
|
19927
|
+
* an ellipsis — the point of the drawing is to read them.
|
|
19928
|
+
************************************************************/
|
|
19929
|
+
function card_size(rows) {
|
|
19930
|
+
let longest = 0;
|
|
19931
|
+
for (let row of rows) {
|
|
19932
|
+
let len = row.name.length + (row.mark ? row.mark.length + 2 : 0) + 2;
|
|
19933
|
+
if (len > longest) longest = len;
|
|
19934
|
+
}
|
|
19935
|
+
let w = Math.round(26 + longest * 6.6);
|
|
19936
|
+
if (w < CARD_MIN_W) w = CARD_MIN_W;
|
|
19937
|
+
if (w > CARD_MAX_W) w = CARD_MAX_W;
|
|
19938
|
+
let h = 44 + rows.length * CARD_ROW_H;
|
|
19939
|
+
return [w, h];
|
|
19940
|
+
}
|
|
19941
|
+
/************************************************************
|
|
19942
|
+
* Vertical placement of a field's port, as a fraction of the
|
|
19943
|
+
* card height: the centre of its own row.
|
|
19944
|
+
************************************************************/
|
|
19945
|
+
function row_placement(index, height) {
|
|
19946
|
+
return (37 + index * CARD_ROW_H + CARD_ROW_H / 2) / height;
|
|
19947
|
+
}
|
|
19948
|
+
/************************************************************
|
|
19949
|
+
* The card itself. Header = topic name on its topic colour;
|
|
19950
|
+
* body = one line per field, in schema order.
|
|
19951
|
+
************************************************************/
|
|
19952
|
+
function build_card_innerHTML(topic, rows, color, dark) {
|
|
19953
|
+
let surface = dark ? "#1b2230" : "#ffffff";
|
|
19954
|
+
let bg = dark ? `color-mix(in srgb, ${color} 18%, #232b38)` : `color-mix(in srgb, ${color} 6%, ${surface})`;
|
|
19955
|
+
let border = dark ? `color-mix(in srgb, ${color} 85%, #ffffff)` : color;
|
|
19956
|
+
let header_bg = dark ? `color-mix(in srgb, ${color} 45%, #2c3542)` : `color-mix(in srgb, ${color} 28%, ${surface})`;
|
|
19957
|
+
let title_color = dark ? "#e8eaed" : "#0f172a";
|
|
19958
|
+
let text_color = dark ? "#d3d8de" : "#334155";
|
|
19959
|
+
let mute_color = dark ? "#98a2b0" : "#7c8694";
|
|
19960
|
+
let rule_color = dark ? "rgba(255,255,255,0.06)" : "rgba(15,23,42,0.05)";
|
|
19961
|
+
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)";
|
|
19962
|
+
let rows_html = "";
|
|
19963
|
+
for (let row of rows) {
|
|
19964
|
+
let lead = row.is_pkey ? "#" : row.required ? "*" : "";
|
|
19965
|
+
let name_weight = row.is_pkey || row.required ? "600" : "400";
|
|
19966
|
+
let name_color = row.is_hook || row.is_fkey ? title_color : text_color;
|
|
19967
|
+
rows_html += ` <div class="TREEDB_SCHEMA_FIELD" style="
|
|
19968
|
+
display: flex; align-items: center; gap: 6px;
|
|
19969
|
+
height: ${CARD_ROW_H}px; line-height: ${CARD_ROW_H}px;
|
|
19970
|
+
padding: 0 10px; border-top: 1px solid ${rule_color};
|
|
19971
|
+
">
|
|
19972
|
+
<span style="
|
|
19973
|
+
width: 8px; flex: 0 0 8px; text-align: center;
|
|
19974
|
+
color: ${mute_color}; font-size: 11px;
|
|
19975
|
+
">${lead}</span>
|
|
19976
|
+
<span style="
|
|
19977
|
+
flex: 1 1 auto; min-width: 0; overflow: hidden;
|
|
19978
|
+
text-overflow: ellipsis; white-space: nowrap;
|
|
19979
|
+
font-size: 12px; font-weight: ${name_weight}; color: ${name_color};
|
|
19980
|
+
">${escapeHtml(row.name)}</span>
|
|
19981
|
+
<span style="
|
|
19982
|
+
flex: 0 0 auto; font-size: 11px; color: ${mute_color};
|
|
19983
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
19984
|
+
">${escapeHtml(row.mark)}</span>
|
|
19985
|
+
</div>
|
|
19986
|
+
`;
|
|
19987
|
+
}
|
|
19988
|
+
return `
|
|
19989
|
+
<div class="TREEDB_SCHEMA_CARD" title="${escapeHtml(topic)}" style="
|
|
19990
|
+
box-sizing: border-box;
|
|
19991
|
+
width: 100%;
|
|
19992
|
+
height: 100%;
|
|
19993
|
+
background: ${bg};
|
|
19994
|
+
border: 1.5px solid ${border};
|
|
19995
|
+
border-radius: 8px;
|
|
19996
|
+
box-shadow: ${shadow};
|
|
19997
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
19998
|
+
display: flex;
|
|
19999
|
+
flex-direction: column;
|
|
20000
|
+
overflow: hidden;
|
|
20001
|
+
cursor: pointer;
|
|
20002
|
+
">
|
|
20003
|
+
<div class="TREEDB_SCHEMA_CARD_HEADER" style="
|
|
20004
|
+
height: ${CARD_HEADER_H}px; line-height: ${CARD_HEADER_H}px;
|
|
20005
|
+
flex: 0 0 ${CARD_HEADER_H}px;
|
|
20006
|
+
padding: 0 10px; background: ${header_bg}; color: ${title_color};
|
|
20007
|
+
font-size: 13px; font-weight: 700;
|
|
20008
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
20009
|
+
">${escapeHtml(topic)}</div>
|
|
20010
|
+
<div class="TREEDB_SCHEMA_CARD_BODY" style="
|
|
20011
|
+
flex: 1 1 auto; padding: ${CARD_PAD_V}px 0; overflow: hidden;
|
|
20012
|
+
">
|
|
20013
|
+
${rows_html} </div>
|
|
20014
|
+
</div>
|
|
20015
|
+
`;
|
|
20016
|
+
}
|
|
20017
|
+
/************************************************************
|
|
19832
20018
|
* Derive {nodes, edges} from the schema.
|
|
19833
|
-
*
|
|
19834
|
-
*
|
|
20019
|
+
*
|
|
20020
|
+
* Node = a topic, drawn as the card the `.c` literal draws in
|
|
20021
|
+
* ASCII: its name and its fields. Edge = a hook, from the row
|
|
20022
|
+
* that declares it to the fkey row of the child it names —
|
|
20023
|
+
* `'hook': {'users': 'departments'}` says both ends, so the
|
|
20024
|
+
* arrow can land where the `.c` drawing lands it. An fkey
|
|
20025
|
+
* whose parent declares no hook still gets its edge, or a
|
|
20026
|
+
* half-declared schema would draw as disconnected.
|
|
20027
|
+
*
|
|
19835
20028
|
* Left-to-right dagre follows the parent -> child data flow.
|
|
19836
20029
|
************************************************************/
|
|
19837
20030
|
function schema_to_graph(gobj) {
|
|
19838
20031
|
let descs = gobj_read_attr(gobj, "descs");
|
|
19839
20032
|
let system = gobj_read_bool_attr(gobj, "system");
|
|
20033
|
+
let dark = yui_is_dark();
|
|
19840
20034
|
let nodes = [];
|
|
19841
|
-
let
|
|
20035
|
+
let cards = {};
|
|
19842
20036
|
if (!is_object(descs)) return {
|
|
19843
20037
|
nodes,
|
|
19844
20038
|
edges: []
|
|
19845
20039
|
};
|
|
20040
|
+
let idx = 0;
|
|
19846
20041
|
for (let topic of Object.keys(descs)) {
|
|
19847
|
-
|
|
19848
|
-
|
|
20042
|
+
let is_system = topic.substring(0, 2) === "__";
|
|
20043
|
+
if (!system && is_system) continue;
|
|
20044
|
+
let rows = schema_rows(descs[topic]);
|
|
20045
|
+
let color;
|
|
20046
|
+
if (is_system) color = SYSTEM_TOPIC_COLOR;
|
|
20047
|
+
else {
|
|
20048
|
+
color = topic_colors[idx % topic_colors.length];
|
|
20049
|
+
idx++;
|
|
20050
|
+
}
|
|
20051
|
+
cards[topic] = {
|
|
20052
|
+
rows,
|
|
20053
|
+
size: card_size(rows),
|
|
20054
|
+
color
|
|
20055
|
+
};
|
|
20056
|
+
}
|
|
20057
|
+
for (let topic of Object.keys(cards)) {
|
|
20058
|
+
let card = cards[topic];
|
|
20059
|
+
let [w, h] = card.size;
|
|
20060
|
+
let ports = [];
|
|
20061
|
+
for (let i = 0; i < card.rows.length; i++) {
|
|
20062
|
+
let row = card.rows[i];
|
|
20063
|
+
if (!row.is_hook && !row.is_fkey) continue;
|
|
20064
|
+
ports.push({
|
|
20065
|
+
key: row.name,
|
|
20066
|
+
placement: [row.is_hook ? 1 : 0, row_placement(i, h)],
|
|
20067
|
+
fill: card.color,
|
|
20068
|
+
stroke: getStrokeColor(card.color)
|
|
20069
|
+
});
|
|
20070
|
+
}
|
|
19849
20071
|
nodes.push({
|
|
19850
20072
|
id: topic,
|
|
19851
|
-
|
|
20073
|
+
type: "html",
|
|
20074
|
+
style: {
|
|
20075
|
+
size: [w, h],
|
|
20076
|
+
dx: -w / 2,
|
|
20077
|
+
dy: -h / 2,
|
|
20078
|
+
innerHTML: build_card_innerHTML(topic, card.rows, card.color, dark),
|
|
20079
|
+
port: ports.length > 0,
|
|
20080
|
+
ports,
|
|
20081
|
+
portR: 3,
|
|
20082
|
+
portLineWidth: 1
|
|
20083
|
+
},
|
|
20084
|
+
data: {
|
|
20085
|
+
topic_name: topic,
|
|
20086
|
+
rows: card.rows,
|
|
20087
|
+
color: card.color
|
|
20088
|
+
}
|
|
19852
20089
|
});
|
|
19853
20090
|
}
|
|
19854
20091
|
let edge_seen = {};
|
|
19855
20092
|
let edges = [];
|
|
19856
|
-
let add_edge = (source, target) => {
|
|
19857
|
-
if (!
|
|
19858
|
-
let key = source
|
|
20093
|
+
let add_edge = (source, source_port, target, target_port) => {
|
|
20094
|
+
if (!cards[source] || !cards[target]) return;
|
|
20095
|
+
let key = `${source}|${source_port}|${target}|${target_port}`;
|
|
19859
20096
|
if (edge_seen[key]) return;
|
|
19860
20097
|
edge_seen[key] = true;
|
|
19861
20098
|
edges.push({
|
|
19862
20099
|
id: key,
|
|
20100
|
+
type: "cubic",
|
|
19863
20101
|
source,
|
|
19864
|
-
target
|
|
20102
|
+
target,
|
|
20103
|
+
style: {
|
|
20104
|
+
sourcePort: source_port,
|
|
20105
|
+
targetPort: target_port
|
|
20106
|
+
}
|
|
19865
20107
|
});
|
|
19866
20108
|
};
|
|
19867
|
-
for (let topic of Object.keys(
|
|
20109
|
+
for (let topic of Object.keys(cards)) {
|
|
19868
20110
|
let desc = descs[topic];
|
|
19869
20111
|
if (!is_object(desc) || !Array.isArray(desc.cols)) continue;
|
|
19870
20112
|
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
|
-
|
|
20113
|
+
if (!is_object(col) || !col.id) continue;
|
|
20114
|
+
if (is_object(col.hook)) for (let child of Object.keys(col.hook)) add_edge(topic, col.id, child, col.hook[child]);
|
|
20115
|
+
if (is_object(col.fkey)) for (let parent of Object.keys(col.fkey)) add_edge(parent, col.fkey[parent], topic, col.id);
|
|
19874
20116
|
}
|
|
19875
20117
|
}
|
|
19876
20118
|
return {
|
|
@@ -19879,26 +20121,15 @@ function schema_to_graph(gobj) {
|
|
|
19879
20121
|
};
|
|
19880
20122
|
}
|
|
19881
20123
|
/************************************************************
|
|
19882
|
-
* The theme-dependent
|
|
19883
|
-
*
|
|
20124
|
+
* The theme-dependent edge style, in ONE place: applied as
|
|
20125
|
+
* the graph is built and re-applied on a theme switch
|
|
19884
20126
|
* (ac_theme), which restyles the LIVE graph — rebuilding it
|
|
19885
20127
|
* would drop the user's zoom/pan and any dragged node.
|
|
19886
20128
|
************************************************************/
|
|
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
20129
|
function edge_style(dark) {
|
|
19900
20130
|
return {
|
|
19901
|
-
stroke: dark ? "#
|
|
20131
|
+
stroke: dark ? "#7a8593" : "#9aa4b2",
|
|
20132
|
+
lineWidth: 1.2,
|
|
19902
20133
|
endArrow: true
|
|
19903
20134
|
};
|
|
19904
20135
|
}
|
|
@@ -19930,13 +20161,12 @@ function build_graph$1(gobj) {
|
|
|
19930
20161
|
container: $container,
|
|
19931
20162
|
autoResize: true,
|
|
19932
20163
|
data,
|
|
19933
|
-
node: { style: node_style(dark) },
|
|
19934
20164
|
edge: { style: edge_style(dark) },
|
|
19935
20165
|
layout: {
|
|
19936
20166
|
type: "antv-dagre",
|
|
19937
20167
|
rankdir: "LR",
|
|
19938
|
-
nodesep:
|
|
19939
|
-
ranksep:
|
|
20168
|
+
nodesep: 28,
|
|
20169
|
+
ranksep: 90
|
|
19940
20170
|
},
|
|
19941
20171
|
behaviors: [
|
|
19942
20172
|
"zoom-canvas",
|
|
@@ -20021,8 +20251,17 @@ function ac_theme$1(gobj, event, kw, src) {
|
|
|
20021
20251
|
let dark = kw && kw.theme ? kw.theme === "dark" : yui_is_dark();
|
|
20022
20252
|
try {
|
|
20023
20253
|
graph.setTheme(dark ? "dark" : "light");
|
|
20024
|
-
graph.setNode({ style: node_style(dark) });
|
|
20025
20254
|
graph.setEdge({ style: edge_style(dark) });
|
|
20255
|
+
let updates = [];
|
|
20256
|
+
for (let node of graph.getNodeData()) {
|
|
20257
|
+
let data = node.data;
|
|
20258
|
+
if (!is_object(data) || !Array.isArray(data.rows)) continue;
|
|
20259
|
+
updates.push({
|
|
20260
|
+
id: node.id,
|
|
20261
|
+
style: { innerHTML: build_card_innerHTML(data.topic_name, data.rows, data.color, dark) }
|
|
20262
|
+
});
|
|
20263
|
+
}
|
|
20264
|
+
if (updates.length > 0) graph.updateNodeData(updates);
|
|
20026
20265
|
graph.draw().catch((e) => {
|
|
20027
20266
|
log_error(`${gobj_short_name(gobj)}: schema graph redraw failed: ${e}`);
|
|
20028
20267
|
});
|
|
@@ -20087,6 +20326,58 @@ function register_c_yui_treedb_schema() {
|
|
|
20087
20326
|
return create_gclass$2(GCLASS_NAME$2);
|
|
20088
20327
|
}
|
|
20089
20328
|
//#endregion
|
|
20329
|
+
//#region src/treedb_node_label.js
|
|
20330
|
+
/***********************************************************************
|
|
20331
|
+
* treedb_node_label.js
|
|
20332
|
+
*
|
|
20333
|
+
* Pure, testable logic behind the label of a node in the treedb
|
|
20334
|
+
* graph: WHAT A RECORD IS CALLED, which is not always what it is
|
|
20335
|
+
* keyed by. Kept out of the gclass so it can be unit-tested with
|
|
20336
|
+
* no DOM and no G6.
|
|
20337
|
+
*
|
|
20338
|
+
* Copyright (c) 2026, ArtGins.
|
|
20339
|
+
* All Rights Reserved.
|
|
20340
|
+
***********************************************************************/
|
|
20341
|
+
/************************************************************
|
|
20342
|
+
* What a node is CALLED, which is not always what it is
|
|
20343
|
+
* keyed by.
|
|
20344
|
+
*
|
|
20345
|
+
* A topic whose id column is flagged `rowid` or `uuid` keys
|
|
20346
|
+
* its records by a value nobody reads — that is the point of
|
|
20347
|
+
* those flags — and the name a human knows the record by
|
|
20348
|
+
* lives in the secondary key the topic declares (`pkey2s`,
|
|
20349
|
+
* carried in the desc since SDK > 7.13.0). The `topics` and
|
|
20350
|
+
* `cols` topics of treedb_system_schema are the case that
|
|
20351
|
+
* forced this: keyed by rowid, named in `value`, so every
|
|
20352
|
+
* card in the graph read "181", "225", "193".
|
|
20353
|
+
*
|
|
20354
|
+
* The pkey stays reachable: it is the card's tooltip.
|
|
20355
|
+
*
|
|
20356
|
+
* Falls back to the id whenever the schema does not say
|
|
20357
|
+
* otherwise — an older node whose desc carries no `pkey2s`
|
|
20358
|
+
* included.
|
|
20359
|
+
************************************************************/
|
|
20360
|
+
function node_label(desc, record) {
|
|
20361
|
+
let id = record.id;
|
|
20362
|
+
let id_col = null;
|
|
20363
|
+
if (Array.isArray(desc.cols)) {
|
|
20364
|
+
for (let col of desc.cols) if (col && col.id === (desc.pkey || "id")) {
|
|
20365
|
+
id_col = col;
|
|
20366
|
+
break;
|
|
20367
|
+
}
|
|
20368
|
+
}
|
|
20369
|
+
if (!id_col || !Array.isArray(id_col.flag)) return id;
|
|
20370
|
+
if (id_col.flag.indexOf("rowid") < 0 && id_col.flag.indexOf("uuid") < 0) return id;
|
|
20371
|
+
let pkey2s = desc.pkey2s;
|
|
20372
|
+
if (typeof pkey2s === "string") pkey2s = pkey2s ? [pkey2s] : [];
|
|
20373
|
+
if (!Array.isArray(pkey2s)) return id;
|
|
20374
|
+
for (let name of pkey2s) {
|
|
20375
|
+
let value = record[name];
|
|
20376
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
20377
|
+
}
|
|
20378
|
+
return id;
|
|
20379
|
+
}
|
|
20380
|
+
//#endregion
|
|
20090
20381
|
//#region node_modules/@babel/runtime/helpers/esm/typeof.js
|
|
20091
20382
|
function _typeof(o) {
|
|
20092
20383
|
"@babel/helpers - typeof";
|
|
@@ -47619,19 +47910,19 @@ function create_topic_node(gobj, desc, record) {
|
|
|
47619
47910
|
style.size = [116, 40];
|
|
47620
47911
|
style.dx = -58;
|
|
47621
47912
|
style.dy = -20;
|
|
47622
|
-
style.innerHTML = build_chip_innerHTML(desc.color, priv.theme, record.icon, record.id);
|
|
47913
|
+
style.innerHTML = build_chip_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), record.id);
|
|
47623
47914
|
} else if (node_treedb_type === "extended") {
|
|
47624
47915
|
node_graph_type = "html";
|
|
47625
47916
|
style.size = [144, 66];
|
|
47626
47917
|
style.dx = -72;
|
|
47627
47918
|
style.dy = -33;
|
|
47628
|
-
style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, record
|
|
47919
|
+
style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, true, record.id);
|
|
47629
47920
|
} else {
|
|
47630
47921
|
node_graph_type = "html";
|
|
47631
47922
|
style.size = [172, 96];
|
|
47632
47923
|
style.dx = -86;
|
|
47633
47924
|
style.dy = -48;
|
|
47634
|
-
style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, record
|
|
47925
|
+
style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, false, record.id);
|
|
47635
47926
|
}
|
|
47636
47927
|
let topic_props = priv._graph_properties[desc.topic_name];
|
|
47637
47928
|
let topic_defaults = is_object(topic_props) && is_object(topic_props.defaults) ? topic_props.defaults : null;
|
|
@@ -47699,15 +47990,15 @@ function update_topic_node(gobj, desc, node_name, record) {
|
|
|
47699
47990
|
nodedata.data.record = record;
|
|
47700
47991
|
if (desc.node_treedb_type === "child") graph.updateNodeData([{
|
|
47701
47992
|
id: node_name,
|
|
47702
|
-
style: { innerHTML: build_chip_innerHTML(desc.color, priv.theme, record.icon, record.id) }
|
|
47993
|
+
style: { innerHTML: build_chip_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), record.id) }
|
|
47703
47994
|
}]);
|
|
47704
47995
|
else if (desc.node_treedb_type === "extended") graph.updateNodeData([{
|
|
47705
47996
|
id: node_name,
|
|
47706
|
-
style: { innerHTML: build_node_innerHTML(desc.color, priv.theme, record.icon, record
|
|
47997
|
+
style: { innerHTML: build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, true, record.id) }
|
|
47707
47998
|
}]);
|
|
47708
47999
|
else if (desc.node_treedb_type === "hierarchical") graph.updateNodeData([{
|
|
47709
48000
|
id: node_name,
|
|
47710
|
-
style: { innerHTML: build_node_innerHTML(desc.color, priv.theme, record.icon, record
|
|
48001
|
+
style: { innerHTML: build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, false, record.id) }
|
|
47711
48002
|
}]);
|
|
47712
48003
|
} catch (e) {
|
|
47713
48004
|
log_error(e.message);
|
|
@@ -49614,7 +49905,7 @@ function show_node_popover(gobj) {
|
|
|
49614
49905
|
};
|
|
49615
49906
|
if (node_graph_type === "hierarchical") {
|
|
49616
49907
|
let record = nodeData.data.record || {};
|
|
49617
|
-
updateStyle.innerHTML = build_node_innerHTML(fill, priv.theme, record.icon,
|
|
49908
|
+
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
49909
|
}
|
|
49619
49910
|
graph.updateNodeData([{
|
|
49620
49911
|
id: node_id,
|
|
@@ -49654,7 +49945,7 @@ function show_node_popover(gobj) {
|
|
|
49654
49945
|
};
|
|
49655
49946
|
if (node_graph_type === "hierarchical") {
|
|
49656
49947
|
let record = nodeData.data.record || {};
|
|
49657
|
-
restoreStyle.innerHTML = build_node_innerHTML(origFill, priv.theme, record.icon,
|
|
49948
|
+
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
49949
|
}
|
|
49659
49950
|
graph.updateNodeData([{
|
|
49660
49951
|
id: node_id,
|
|
@@ -49755,15 +50046,15 @@ function refresh_html_nodes_theme(gobj, theme) {
|
|
|
49755
50046
|
let record = nd.data.record || {};
|
|
49756
50047
|
if (tt === "hierarchical") updates.push({
|
|
49757
50048
|
id: nodes[i].id,
|
|
49758
|
-
style: { innerHTML: build_node_innerHTML(nd.data.desc.color, theme, record.icon,
|
|
50049
|
+
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
50050
|
});
|
|
49760
50051
|
else if (tt === "child") updates.push({
|
|
49761
50052
|
id: nodes[i].id,
|
|
49762
|
-
style: { innerHTML: build_chip_innerHTML(nd.data.desc.color, theme, record.icon, record.id) }
|
|
50053
|
+
style: { innerHTML: build_chip_innerHTML(nd.data.desc.color, theme, record.icon, node_label(nd.data.desc, record), record.id) }
|
|
49763
50054
|
});
|
|
49764
50055
|
else if (tt === "extended") updates.push({
|
|
49765
50056
|
id: nodes[i].id,
|
|
49766
|
-
style: { innerHTML: build_node_innerHTML(nd.data.desc.color, theme, record.icon,
|
|
50057
|
+
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
50058
|
});
|
|
49768
50059
|
}
|
|
49769
50060
|
if (updates.length > 0) graph.updateNodeData(updates);
|
|
@@ -49795,7 +50086,8 @@ function refresh_default_edges_theme(gobj, theme) {
|
|
|
49795
50086
|
* but lighter (1px border, no shadow, single line). The name is
|
|
49796
50087
|
* always legible (ellipsis + native title tooltip on overflow).
|
|
49797
50088
|
************************************************************/
|
|
49798
|
-
function build_chip_innerHTML(color, theme, icon,
|
|
50089
|
+
function build_chip_innerHTML(color, theme, icon, label, key) {
|
|
50090
|
+
let title = key || label;
|
|
49799
50091
|
let dark = theme === "dark";
|
|
49800
50092
|
let bg = dark ? `color-mix(in srgb, ${color} 30%, #2c3542)` : `color-mix(in srgb, ${color} 10%, ${dark ? "#1b2230" : "#ffffff"})`;
|
|
49801
50093
|
let border = dark ? `color-mix(in srgb, ${color} 85%, #ffffff)` : color;
|
|
@@ -49806,7 +50098,7 @@ function build_chip_innerHTML(color, theme, icon, id) {
|
|
|
49806
50098
|
margin-right: 6px; flex: 0 0 auto;
|
|
49807
50099
|
"/>`;
|
|
49808
50100
|
return `
|
|
49809
|
-
<div title="${escapeHtml(
|
|
50101
|
+
<div title="${escapeHtml(title)}" style="
|
|
49810
50102
|
box-sizing: border-box;
|
|
49811
50103
|
width: 100%;
|
|
49812
50104
|
height: 100%;
|
|
@@ -49823,7 +50115,7 @@ function build_chip_innerHTML(color, theme, icon, id) {
|
|
|
49823
50115
|
">${icon_html}<span style="
|
|
49824
50116
|
font-size: 12px; font-weight: 600; line-height: 1;
|
|
49825
50117
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
49826
|
-
">${escapeHtml(
|
|
50118
|
+
">${escapeHtml(label)}</span>
|
|
49827
50119
|
</div>
|
|
49828
50120
|
`;
|
|
49829
50121
|
}
|
|
@@ -49837,7 +50129,8 @@ function build_chip_innerHTML(color, theme, icon, id) {
|
|
|
49837
50129
|
* colour is kept (per-topic differentiation) but softened via
|
|
49838
50130
|
* color-mix instead of a harsh saturated fill. Theme-aware.
|
|
49839
50131
|
************************************************************/
|
|
49840
|
-
function build_node_innerHTML(color, theme, icon,
|
|
50132
|
+
function build_node_innerHTML(color, theme, icon, label, topic_name, structural, key) {
|
|
50133
|
+
let title = key || label;
|
|
49841
50134
|
let dark = theme === "dark";
|
|
49842
50135
|
let surface = dark ? "#1b2230" : "#ffffff";
|
|
49843
50136
|
let bg, border, border_style;
|
|
@@ -49867,7 +50160,7 @@ function build_node_innerHTML(color, theme, icon, id, topic_name, structural) {
|
|
|
49867
50160
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
49868
50161
|
">${escapeHtml(topic_name)}</div>`;
|
|
49869
50162
|
return `
|
|
49870
|
-
<div title="${escapeHtml(
|
|
50163
|
+
<div title="${escapeHtml(title)}" style="
|
|
49871
50164
|
box-sizing: border-box;
|
|
49872
50165
|
width: 100%;
|
|
49873
50166
|
height: 100%;
|
|
@@ -49890,7 +50183,7 @@ function build_node_innerHTML(color, theme, icon, id, topic_name, structural) {
|
|
|
49890
50183
|
max-width: 100%; overflow: hidden; text-overflow: ellipsis;
|
|
49891
50184
|
display: -webkit-box; -webkit-line-clamp: 2;
|
|
49892
50185
|
-webkit-box-orient: vertical; word-break: break-word;
|
|
49893
|
-
">${escapeHtml(
|
|
50186
|
+
">${escapeHtml(label)}</div>${sub_html}
|
|
49894
50187
|
</div>
|
|
49895
50188
|
`;
|
|
49896
50189
|
}
|
|
@@ -50111,7 +50404,7 @@ function apply_node_properties(gobj, node_id, fill, stroke, lineWidth, scope) {
|
|
|
50111
50404
|
};
|
|
50112
50405
|
if (nd.data.desc.node_treedb_type === "hierarchical") {
|
|
50113
50406
|
let record = nd.data.record || {};
|
|
50114
|
-
updateStyle.innerHTML = build_node_innerHTML(fill, priv.theme, record.icon,
|
|
50407
|
+
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
50408
|
}
|
|
50116
50409
|
updates.push({
|
|
50117
50410
|
id: nodes[i].id,
|