@yuneta/gobj-ui 7.23.156 → 7.23.158
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 +38 -71
- package/dist/gobj-ui.cjs.js +299 -959
- package/dist/gobj-ui.es.js +300 -960
- package/package.json +1 -1
- package/src/c_g6_nodes_tree.js +227 -241
- package/src/c_yui_treedb_graph.js +91 -13
- package/src/treedb_elbow.js +13 -344
- package/src/treedb_elbow.test.js +1 -236
- package/src/treedb_layout.js +39 -620
- package/src/treedb_layout.test.js +3 -269
package/package.json
CHANGED
package/src/c_g6_nodes_tree.js
CHANGED
|
@@ -132,11 +132,8 @@ import {
|
|
|
132
132
|
fold_split_group_key,
|
|
133
133
|
fold_root_group_key,
|
|
134
134
|
} from "./treedb_fold_model.js";
|
|
135
|
-
import {layout_tree, layout_radial
|
|
136
|
-
import {
|
|
137
|
-
elbow_lane, elbow_rank, elbow_route, elbow_combs, curve_hits, rounded_path, CURVE_RADIUS,
|
|
138
|
-
ELBOW_STEP,
|
|
139
|
-
} from "./treedb_elbow.js";
|
|
135
|
+
import {layout_tree, layout_radial} from "./treedb_layout.js";
|
|
136
|
+
import {elbow_lane, elbow_route} from "./treedb_elbow.js";
|
|
140
137
|
|
|
141
138
|
import {
|
|
142
139
|
BaseLayout,
|
|
@@ -151,8 +148,6 @@ import {
|
|
|
151
148
|
Toolbar,
|
|
152
149
|
register,
|
|
153
150
|
Polyline,
|
|
154
|
-
Cubic,
|
|
155
|
-
CubicHorizontal,
|
|
156
151
|
} from '@antv/g6';
|
|
157
152
|
|
|
158
153
|
import {Circle as CircleGeometry, Rect as RectGeometry} from '@antv/g';
|
|
@@ -357,16 +352,6 @@ const _layouts = {
|
|
|
357
352
|
nodesep: 18,
|
|
358
353
|
ranksep: 90,
|
|
359
354
|
},
|
|
360
|
-
/* The same tree packed by CONTOUR (Moen, as mxGraph drew it): a
|
|
361
|
-
* closed branch sits beside an open one at the depth they share,
|
|
362
|
-
* instead of beside the whole block of the open one. Same rows
|
|
363
|
-
* and order as `treedb-tree` (treedb_layout.js). */
|
|
364
|
-
"compact-tree": {
|
|
365
|
-
type: 'treedb-compact',
|
|
366
|
-
direction: 'TB',
|
|
367
|
-
nodesep: 18,
|
|
368
|
-
ranksep: 90,
|
|
369
|
-
},
|
|
370
355
|
"dagre": {
|
|
371
356
|
type: 'dagre',
|
|
372
357
|
rankdir: 'LR',
|
|
@@ -558,8 +543,9 @@ let PRIVATE_DATA = {
|
|
|
558
543
|
// it exists to diff the repaint)
|
|
559
544
|
_pending_focus_topic: null, // focus requested before data was loaded
|
|
560
545
|
_pending_focus_all: false, // ...and whether it reveals the whole topic
|
|
561
|
-
|
|
562
|
-
|
|
546
|
+
_find_term: "", // the find box's term, repainted after every reconcile
|
|
547
|
+
_find_current: "", // node id the last Enter of the find centred on
|
|
548
|
+
_find_found: null, // ...and its last count (see find_matches)
|
|
563
549
|
_layout_asked: "", // layout the host asked for at create (see mt_create)
|
|
564
550
|
_nodes_total: 0, // records of the treedb (auto_layout)
|
|
565
551
|
_nodes_placed: 0, // ...of which carry a saved position
|
|
@@ -944,11 +930,8 @@ function register_layouts(gobj)
|
|
|
944
930
|
_g6_extensions_registered = true;
|
|
945
931
|
register(ExtensionCategory.LAYOUT, 'manual', ManualLayout);
|
|
946
932
|
register(ExtensionCategory.LAYOUT, 'treedb-tree', TreedbTreeLayout);
|
|
947
|
-
register(ExtensionCategory.LAYOUT, 'treedb-compact', TreedbCompactLayout);
|
|
948
933
|
register(ExtensionCategory.EDGE, 'treedb-elbow-v', TreedbElbowV);
|
|
949
934
|
register(ExtensionCategory.EDGE, 'treedb-elbow-h', TreedbElbowH);
|
|
950
|
-
register(ExtensionCategory.EDGE, 'treedb-curve-v', TreedbCurveV);
|
|
951
|
-
register(ExtensionCategory.EDGE, 'treedb-curve-h', TreedbCurveH);
|
|
952
935
|
register(ExtensionCategory.LAYOUT, 'treedb-radial', TreedbRadialLayout);
|
|
953
936
|
register(ExtensionCategory.NODE, 'light', LightNode);
|
|
954
937
|
register(ExtensionCategory.NODE, 'treedb-card', TreedbCard);
|
|
@@ -2049,20 +2032,16 @@ function layout_direction(gobj)
|
|
|
2049
2032
|
return str_in_list(LR_LAYOUTS, gobj.priv.layout)? "LR" : "TB";
|
|
2050
2033
|
}
|
|
2051
2034
|
|
|
2052
|
-
/*
|
|
2053
|
-
*
|
|
2054
|
-
* cubic on a layout with no rows to go round by. */
|
|
2035
|
+
/* A curve, or an elbow when the reader asked for one and the layout
|
|
2036
|
+
* has rows to run it between. */
|
|
2055
2037
|
function edge_type_for(gobj)
|
|
2056
2038
|
{
|
|
2057
2039
|
let lr = (layout_direction(gobj) === "LR");
|
|
2058
2040
|
let rowless = gobj_read_attr(gobj, "rowless_layouts") || [];
|
|
2059
|
-
|
|
2060
|
-
|
|
2041
|
+
if(gobj_read_str_attr(gobj, "edge_shape") === "elbow" &&
|
|
2042
|
+
!str_in_list(rowless, gobj.priv.layout)) {
|
|
2061
2043
|
return lr? "treedb-elbow-h" : "treedb-elbow-v";
|
|
2062
2044
|
}
|
|
2063
|
-
if(rows) {
|
|
2064
|
-
return lr? "treedb-curve-h" : "treedb-curve-v";
|
|
2065
|
-
}
|
|
2066
2045
|
return lr? "cubic-horizontal" : "cubic";
|
|
2067
2046
|
}
|
|
2068
2047
|
|
|
@@ -3676,6 +3655,18 @@ function graph_focus_topic(gobj, topic, reveal_all)
|
|
|
3676
3655
|
let model = priv._fold_model;
|
|
3677
3656
|
let state = priv._fold_state;
|
|
3678
3657
|
|
|
3658
|
+
/* The two share the highlight slot. A topic takes it from the
|
|
3659
|
+
* find; an EMPTY topic (nothing focused) leaves a live find
|
|
3660
|
+
* alone, or clearing the legend's mark would wipe the find. */
|
|
3661
|
+
if(empty_string(topic) && priv._find_term) {
|
|
3662
|
+
priv._focus_topic = null;
|
|
3663
|
+
return;
|
|
3664
|
+
}
|
|
3665
|
+
if(!empty_string(topic)) {
|
|
3666
|
+
priv._find_term = "";
|
|
3667
|
+
priv._find_current = "";
|
|
3668
|
+
}
|
|
3669
|
+
|
|
3679
3670
|
/* "Show me these": the ones on screen are highlighted, and the
|
|
3680
3671
|
* hidden ones are opened up to. How many depends on who asks.
|
|
3681
3672
|
* A click on the LEGEND (`reveal_all`) means every record of the
|
|
@@ -3711,18 +3702,18 @@ function graph_focus_topic(gobj, topic, reveal_all)
|
|
|
3711
3702
|
priv._focus_topic = topic || null;
|
|
3712
3703
|
|
|
3713
3704
|
reconcile_fold(gobj, {}).then(() => {
|
|
3714
|
-
apply_focus_ids(gobj, keys, "focus topic");
|
|
3705
|
+
apply_focus_ids(gobj, keys, "focus topic", true);
|
|
3715
3706
|
});
|
|
3716
3707
|
}
|
|
3717
3708
|
|
|
3718
3709
|
/************************************************************
|
|
3719
|
-
* Paint the highlight on these records (by model key) and
|
|
3720
|
-
* the first of them in the middle. Shared by the
|
|
3721
|
-
* topic focus
|
|
3722
|
-
*
|
|
3723
|
-
*
|
|
3710
|
+
* Paint the highlight on these records (by model key) and, with
|
|
3711
|
+
* `center`, put the first of them in the middle. Shared by the
|
|
3712
|
+
* find and the topic focus. Keys that are not on screen are
|
|
3713
|
+
* skipped: a match beyond the page a reveal opened is counted,
|
|
3714
|
+
* not painted.
|
|
3724
3715
|
************************************************************/
|
|
3725
|
-
function apply_focus_ids(gobj, keys, what)
|
|
3716
|
+
function apply_focus_ids(gobj, keys, what, center)
|
|
3726
3717
|
{
|
|
3727
3718
|
let priv = gobj.priv;
|
|
3728
3719
|
let graph = priv.graph;
|
|
@@ -3756,7 +3747,7 @@ function apply_focus_ids(gobj, keys, what)
|
|
|
3756
3747
|
graph.setElementState(states);
|
|
3757
3748
|
}
|
|
3758
3749
|
apply_node_highlight(gobj, prev_ids, ids);
|
|
3759
|
-
if(ids.length) {
|
|
3750
|
+
if(center && ids.length) {
|
|
3760
3751
|
yui_graph_center_on(graph, ids[0]);
|
|
3761
3752
|
}
|
|
3762
3753
|
} catch(e) {
|
|
@@ -3775,94 +3766,186 @@ function node_id_of_key(gobj, key)
|
|
|
3775
3766
|
}
|
|
3776
3767
|
|
|
3777
3768
|
/************************************************************
|
|
3778
|
-
*
|
|
3779
|
-
*
|
|
3780
|
-
*
|
|
3781
|
-
*
|
|
3782
|
-
*
|
|
3783
|
-
*
|
|
3769
|
+
* The find box: highlight the cards ON SCREEN whose name, id or
|
|
3770
|
+
* topic contains `term`, and change nothing else -- no group is
|
|
3771
|
+
* opened, no layout runs, the camera does not move. The graph the
|
|
3772
|
+
* reader arranged is the graph they are searching; emptying the
|
|
3773
|
+
* box takes the amber off and leaves it exactly as it was.
|
|
3774
|
+
*
|
|
3775
|
+
* It used to search the whole treedb and unfold a page of what it
|
|
3776
|
+
* found, which re-laid the graph out on every keystroke and left
|
|
3777
|
+
* the unfolded groups behind when the box was cleared. Opening up
|
|
3778
|
+
* to a topic is the legend's focus (graph_focus_topic); this only
|
|
3779
|
+
* LOOKS.
|
|
3780
|
+
*
|
|
3781
|
+
* The term is kept and repainted after every reconcile (see
|
|
3782
|
+
* reconcile_fold_now): a group opened by hand shows its matches
|
|
3783
|
+
* already lit, and the count follows what is on screen.
|
|
3784
3784
|
*
|
|
3785
3785
|
* It shares the highlight slot with graph_focus_topic on purpose —
|
|
3786
|
-
* two amber sets at once would say nothing about either.
|
|
3787
|
-
*
|
|
3788
|
-
*
|
|
3789
|
-
*
|
|
3786
|
+
* two amber sets at once would say nothing about either. Matching
|
|
3787
|
+
* reads the node's LABEL and not only its id: on a topic keyed by
|
|
3788
|
+
* rowid or by a qualified path, the id is a counter and the name a
|
|
3789
|
+
* human knows is elsewhere (see node_label).
|
|
3790
3790
|
************************************************************/
|
|
3791
3791
|
function graph_find_nodes(gobj, term)
|
|
3792
3792
|
{
|
|
3793
3793
|
let priv = gobj.priv;
|
|
3794
|
-
let
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3794
|
+
let next = empty_string(term)? "" : String(term);
|
|
3795
|
+
let was = priv._find_term;
|
|
3796
|
+
|
|
3797
|
+
priv._find_term = next;
|
|
3798
|
+
priv._find_current = "";
|
|
3799
|
+
priv._find_found = null;
|
|
3800
|
+
|
|
3801
|
+
let ready = priv.graph && priv.graph_rendered && priv._fold_model;
|
|
3802
|
+
if(!next) {
|
|
3803
|
+
/* Only a find that HELD the slot is taken off: an empty box
|
|
3804
|
+
* that was already empty must not wipe a topic focus. */
|
|
3805
|
+
if(was && ready && !priv._focus_topic) {
|
|
3806
|
+
apply_focus_ids(gobj, [], "find", false);
|
|
3807
|
+
}
|
|
3808
|
+
publish_find_result(gobj);
|
|
3809
|
+
return;
|
|
3810
|
+
}
|
|
3811
|
+
|
|
3812
|
+
priv._focus_topic = null; /* a find takes over the highlight */
|
|
3813
|
+
if(!ready) {
|
|
3814
|
+
/* Painted by the first reconcile, when the data arrives. */
|
|
3815
|
+
return;
|
|
3816
|
+
}
|
|
3817
|
+
refresh_find(gobj, fold_visible_set(priv._fold_model, priv._fold_state));
|
|
3818
|
+
}
|
|
3819
|
+
|
|
3820
|
+
/************************************************************
|
|
3821
|
+
* Repaint the find's highlight over the `visible` set and publish
|
|
3822
|
+
* the count. A no-op while no term is live, so the topic focus
|
|
3823
|
+
* keeps the slot.
|
|
3824
|
+
************************************************************/
|
|
3825
|
+
function refresh_find(gobj, visible)
|
|
3826
|
+
{
|
|
3827
|
+
let priv = gobj.priv;
|
|
3828
|
+
if(!priv._find_term) {
|
|
3829
|
+
return;
|
|
3798
3830
|
}
|
|
3799
3831
|
|
|
3832
|
+
let found = find_matches(gobj, priv._find_term, visible);
|
|
3833
|
+
apply_focus_ids(gobj, found.keys, "find", false);
|
|
3834
|
+
if(priv._find_current && (priv._focus_ids || []).indexOf(priv._find_current) < 0) {
|
|
3835
|
+
priv._find_current = "";
|
|
3836
|
+
}
|
|
3837
|
+
priv._find_found = found;
|
|
3838
|
+
publish_find_result(gobj);
|
|
3839
|
+
}
|
|
3840
|
+
|
|
3841
|
+
/************************************************************
|
|
3842
|
+
* The records matching `term`: the keys ON SCREEN, and how many
|
|
3843
|
+
* more match that are NOT -- folded away in the fold model, or in
|
|
3844
|
+
* a hidden topic. Those two are only counted: "0" alone would read
|
|
3845
|
+
* as "does not exist" when it means "is not on screen".
|
|
3846
|
+
************************************************************/
|
|
3847
|
+
function find_matches(gobj, term, visible)
|
|
3848
|
+
{
|
|
3849
|
+
let priv = gobj.priv;
|
|
3800
3850
|
let model = priv._fold_model;
|
|
3801
|
-
let
|
|
3851
|
+
let found = {keys: [], folded: 0, hidden: 0};
|
|
3852
|
+
if(!model || empty_string(term)) {
|
|
3853
|
+
return found;
|
|
3854
|
+
}
|
|
3802
3855
|
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
if(matches(node.topic_name, node.record || {})) {
|
|
3826
|
-
keys.push(node.key);
|
|
3856
|
+
let needle = String(term).toLowerCase();
|
|
3857
|
+
let matches = (topic_name, record) => {
|
|
3858
|
+
let label = "";
|
|
3859
|
+
try {
|
|
3860
|
+
label = node_label(priv.descs[topic_name] || {}, record) || "";
|
|
3861
|
+
} catch(e) {
|
|
3862
|
+
label = "";
|
|
3863
|
+
}
|
|
3864
|
+
let haystack = [
|
|
3865
|
+
label,
|
|
3866
|
+
record.id,
|
|
3867
|
+
topic_name
|
|
3868
|
+
].filter((v) => typeof v === "string").join(" ").toLowerCase();
|
|
3869
|
+
return haystack.includes(needle);
|
|
3870
|
+
};
|
|
3871
|
+
|
|
3872
|
+
for(let node of model.nodes.values()) {
|
|
3873
|
+
if(matches(node.topic_name, node.record || {})) {
|
|
3874
|
+
if(visible.has(node.key)) {
|
|
3875
|
+
found.keys.push(node.key);
|
|
3876
|
+
} else {
|
|
3877
|
+
found.folded++;
|
|
3827
3878
|
}
|
|
3828
3879
|
}
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
if(is_object(record) && matches(topic_name, record)) {
|
|
3835
|
-
hidden_matches++;
|
|
3836
|
-
}
|
|
3880
|
+
}
|
|
3881
|
+
for(let topic_name of model.hidden) {
|
|
3882
|
+
for(let record of (priv.records[topic_name] || [])) {
|
|
3883
|
+
if(is_object(record) && matches(topic_name, record)) {
|
|
3884
|
+
found.hidden++;
|
|
3837
3885
|
}
|
|
3838
3886
|
}
|
|
3839
3887
|
}
|
|
3840
|
-
|
|
3888
|
+
return found;
|
|
3889
|
+
}
|
|
3841
3890
|
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
break;
|
|
3854
|
-
}
|
|
3855
|
-
fold_reveal(model, state, key);
|
|
3856
|
-
revealed++;
|
|
3891
|
+
/************************************************************
|
|
3892
|
+
* The lit cards in reading order: top to bottom, then left to
|
|
3893
|
+
* right. The order Enter walks, and the one the "k/N" counts in.
|
|
3894
|
+
************************************************************/
|
|
3895
|
+
function find_order(gobj)
|
|
3896
|
+
{
|
|
3897
|
+
let priv = gobj.priv;
|
|
3898
|
+
let graph = priv.graph;
|
|
3899
|
+
let ids = (priv._focus_ids || []).slice();
|
|
3900
|
+
if(!graph) {
|
|
3901
|
+
return ids;
|
|
3857
3902
|
}
|
|
3858
3903
|
|
|
3859
|
-
|
|
3904
|
+
let pos = {};
|
|
3905
|
+
for(let id of ids) {
|
|
3906
|
+
let p = null;
|
|
3907
|
+
try {
|
|
3908
|
+
p = graph.getElementPosition(id);
|
|
3909
|
+
} catch(e) {
|
|
3910
|
+
p = null;
|
|
3911
|
+
}
|
|
3912
|
+
pos[id] = p || [0, 0];
|
|
3913
|
+
}
|
|
3914
|
+
ids.sort((a, b) => (pos[a][1] - pos[b][1]) || (pos[a][0] - pos[b][0]));
|
|
3915
|
+
return ids;
|
|
3916
|
+
}
|
|
3860
3917
|
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3918
|
+
/************************************************************
|
|
3919
|
+
* Enter in the find box: centre the next lit card (`back`: the
|
|
3920
|
+
* previous one). The one camera move of the find, and the reader
|
|
3921
|
+
* asks for it: the highlight alone cannot take anybody to a match
|
|
3922
|
+
* that is outside the frame.
|
|
3923
|
+
************************************************************/
|
|
3924
|
+
function graph_find_step(gobj, back)
|
|
3925
|
+
{
|
|
3926
|
+
let priv = gobj.priv;
|
|
3927
|
+
let graph = priv.graph;
|
|
3928
|
+
if(!graph || !priv._find_term) {
|
|
3929
|
+
return;
|
|
3930
|
+
}
|
|
3931
|
+
let ids = find_order(gobj);
|
|
3932
|
+
if(ids.length === 0) {
|
|
3933
|
+
return;
|
|
3934
|
+
}
|
|
3864
3935
|
|
|
3865
|
-
|
|
3936
|
+
let i = ids.indexOf(priv._find_current);
|
|
3937
|
+
if(i < 0) {
|
|
3938
|
+
i = back? ids.length - 1 : 0;
|
|
3939
|
+
} else {
|
|
3940
|
+
i = (i + (back? ids.length - 1 : 1)) % ids.length;
|
|
3941
|
+
}
|
|
3942
|
+
priv._find_current = ids[i];
|
|
3943
|
+
try {
|
|
3944
|
+
yui_graph_center_on(graph, ids[i]);
|
|
3945
|
+
} catch(e) {
|
|
3946
|
+
log_error(`${gobj_short_name(gobj)}: find step failed: ${e}`);
|
|
3947
|
+
}
|
|
3948
|
+
publish_find_result(gobj);
|
|
3866
3949
|
}
|
|
3867
3950
|
|
|
3868
3951
|
async function graph_zoom_in(gobj)
|
|
@@ -4235,9 +4318,8 @@ class TreedbTreeLayout extends BaseLayout
|
|
|
4235
4318
|
* dragged card, a fold or a layout needs nothing else. `-v` runs
|
|
4236
4319
|
* between rows (top to bottom), `-h` between columns. An edge that
|
|
4237
4320
|
* does not run forward goes round the two cards, the edges joining
|
|
4238
|
-
* the same two cards take lanes,
|
|
4239
|
-
*
|
|
4240
|
-
* routed round it (treedb_elbow.js).
|
|
4321
|
+
* the same two cards take lanes, and a line that would cross any
|
|
4322
|
+
* other card is routed round it (treedb_elbow.js).
|
|
4241
4323
|
************************************************************/
|
|
4242
4324
|
const ELBOW_RADIUS = 6;
|
|
4243
4325
|
|
|
@@ -4309,73 +4391,12 @@ function elbow_lane_of(edge)
|
|
|
4309
4391
|
}
|
|
4310
4392
|
}
|
|
4311
4393
|
|
|
4312
|
-
/* Where `edge` stands among the forward edges sharing one of its
|
|
4313
|
-
* ends, for the stagger (treedb_elbow.js): the ones leaving its
|
|
4314
|
-
* source card, and the ones reaching its target card. How far each
|
|
4315
|
-
* runs ACROSS is measured from the real port it leaves by -- the
|
|
4316
|
-
* hooks of one card sit at different places along its edge. */
|
|
4317
|
-
function elbow_stagger_of(edge, vertical)
|
|
4318
|
-
{
|
|
4319
|
-
let graph = edge.context && edge.context.graph;
|
|
4320
|
-
let element = edge.context && edge.context.element;
|
|
4321
|
-
let s = edge.sourceNode && edge.sourceNode.id;
|
|
4322
|
-
let t = edge.targetNode && edge.targetNode.id;
|
|
4323
|
-
if(!graph || !element || s === undefined || t === undefined) {
|
|
4324
|
-
return null;
|
|
4325
|
-
}
|
|
4326
|
-
let across = vertical? 0 : 1;
|
|
4327
|
-
let along = vertical? 1 : 0;
|
|
4328
|
-
let pos = (id) => graph.getElementPosition(id);
|
|
4329
|
-
let port_of = (node_id, key) => {
|
|
4330
|
-
let node = element.getElement(node_id);
|
|
4331
|
-
let ports = (node && typeof node.getPorts === 'function')? node.getPorts() : {};
|
|
4332
|
-
let port = key? ports[key] : null;
|
|
4333
|
-
return port? port.getPosition() : pos(node_id);
|
|
4334
|
-
};
|
|
4335
|
-
try {
|
|
4336
|
-
let ps = pos(s);
|
|
4337
|
-
let pt = pos(t);
|
|
4338
|
-
let leaving = graph.getRelatedEdgesData(s, 'out')
|
|
4339
|
-
.filter((d) => d.source === s && pos(d.target)[along] > ps[along]);
|
|
4340
|
-
/* The cards they reach, for the combs of a stack; a comb's
|
|
4341
|
-
* run ends at its lane, so that is what its span measures. */
|
|
4342
|
-
let combs = elbow_combs(leaving.map((d) => ({
|
|
4343
|
-
id: d.id,
|
|
4344
|
-
box: elbow_box(element.getElement(d.target)),
|
|
4345
|
-
})), vertical);
|
|
4346
|
-
let dep = leaving.map((d) => {
|
|
4347
|
-
let comb = combs.get(d.id);
|
|
4348
|
-
let far = comb? comb.lane : pos(d.target)[across];
|
|
4349
|
-
return {
|
|
4350
|
-
id: d.id,
|
|
4351
|
-
span: Math.abs(far - port_of(s, d.style && d.style.sourcePort)[across]),
|
|
4352
|
-
};
|
|
4353
|
-
});
|
|
4354
|
-
let arr = graph.getRelatedEdgesData(t, 'in')
|
|
4355
|
-
.filter((d) => d.target === t && pos(d.source)[along] < pt[along])
|
|
4356
|
-
.map((d) => ({
|
|
4357
|
-
id: d.id,
|
|
4358
|
-
span: Math.abs(port_of(d.source, d.style && d.style.sourcePort)[across] -
|
|
4359
|
-
pt[across]),
|
|
4360
|
-
}));
|
|
4361
|
-
return {
|
|
4362
|
-
dep: elbow_rank(dep, edge.id),
|
|
4363
|
-
arr: elbow_rank(arr, edge.id),
|
|
4364
|
-
comb: combs.get(edge.id) || null,
|
|
4365
|
-
};
|
|
4366
|
-
} catch(e) {
|
|
4367
|
-
log_error(`elbow edge ${edge.id}: cannot read the edges sharing its ends: ${e}`);
|
|
4368
|
-
return null;
|
|
4369
|
-
}
|
|
4370
|
-
}
|
|
4371
|
-
|
|
4372
4394
|
class TreedbElbowV extends Polyline
|
|
4373
4395
|
{
|
|
4374
4396
|
getControlPoints(attributes) {
|
|
4375
4397
|
let [s, t] = this.getEndpoints(attributes, false);
|
|
4376
4398
|
return elbow_route(s, t, elbow_box(this.sourceNode), elbow_box(this.targetNode),
|
|
4377
|
-
elbow_lane_of(this), true, elbow_obstacles(this)
|
|
4378
|
-
elbow_stagger_of(this, true));
|
|
4399
|
+
elbow_lane_of(this), true, elbow_obstacles(this));
|
|
4379
4400
|
}
|
|
4380
4401
|
|
|
4381
4402
|
getKeyPath(attributes) {
|
|
@@ -4389,8 +4410,7 @@ class TreedbElbowH extends Polyline
|
|
|
4389
4410
|
getControlPoints(attributes) {
|
|
4390
4411
|
let [s, t] = this.getEndpoints(attributes, false);
|
|
4391
4412
|
return elbow_route(s, t, elbow_box(this.sourceNode), elbow_box(this.targetNode),
|
|
4392
|
-
elbow_lane_of(this), false, elbow_obstacles(this)
|
|
4393
|
-
elbow_stagger_of(this, false));
|
|
4413
|
+
elbow_lane_of(this), false, elbow_obstacles(this));
|
|
4394
4414
|
}
|
|
4395
4415
|
|
|
4396
4416
|
getKeyPath(attributes) {
|
|
@@ -4399,59 +4419,6 @@ class TreedbElbowH extends Polyline
|
|
|
4399
4419
|
}
|
|
4400
4420
|
}
|
|
4401
4421
|
|
|
4402
|
-
/************************************************************
|
|
4403
|
-
* A CURVED edge that keeps off the cards. G6's cubic, as before,
|
|
4404
|
-
* unless that very curve -- sampled, not guessed from the
|
|
4405
|
-
* rectangle round it -- would cross a card other than its own two,
|
|
4406
|
-
* or when it does not run forward and so would cross its own:
|
|
4407
|
-
* the card above a lower row of a stack, a row between a parent
|
|
4408
|
-
* and a far child. Then it goes the way an elbow would (the comb,
|
|
4409
|
-
* the detour, the route: the same lanes and staggered turns) with
|
|
4410
|
-
* round corners, so it still reads as a curve. `-v` for rows
|
|
4411
|
-
* stacked top to bottom, `-h` for columns.
|
|
4412
|
-
************************************************************/
|
|
4413
|
-
function curve_key_path(edge, attributes, vertical, plain)
|
|
4414
|
-
{
|
|
4415
|
-
let [s, t] = edge.getEndpoints(attributes, false);
|
|
4416
|
-
let along = vertical? 1 : 0;
|
|
4417
|
-
let box_s = elbow_box(edge.sourceNode);
|
|
4418
|
-
let box_t = elbow_box(edge.targetNode);
|
|
4419
|
-
let boxes = elbow_obstacles(edge);
|
|
4420
|
-
/* An edge that does not run forward -- one of every reciprocal
|
|
4421
|
-
* pair, an edge within one row -- runs through its OWN two cards
|
|
4422
|
-
* as a cubic, which curve_hits does not look at: it goes round
|
|
4423
|
-
* them, as its elbow does. */
|
|
4424
|
-
let forward = (t[along] - s[along] > 2 * ELBOW_STEP);
|
|
4425
|
-
if(forward && !curve_hits(plain, boxes, box_s, box_t)) {
|
|
4426
|
-
return plain;
|
|
4427
|
-
}
|
|
4428
|
-
let pts = elbow_route(s, t, box_s, box_t, elbow_lane_of(edge), vertical, boxes,
|
|
4429
|
-
elbow_stagger_of(edge, vertical));
|
|
4430
|
-
return rounded_path([s, ...pts, t], CURVE_RADIUS);
|
|
4431
|
-
}
|
|
4432
|
-
|
|
4433
|
-
class TreedbCurveV extends Cubic
|
|
4434
|
-
{
|
|
4435
|
-
getKeyPath(attributes) {
|
|
4436
|
-
return curve_key_path(this, attributes, true, super.getKeyPath(attributes));
|
|
4437
|
-
}
|
|
4438
|
-
}
|
|
4439
|
-
|
|
4440
|
-
class TreedbCurveH extends CubicHorizontal
|
|
4441
|
-
{
|
|
4442
|
-
getKeyPath(attributes) {
|
|
4443
|
-
return curve_key_path(this, attributes, false, super.getKeyPath(attributes));
|
|
4444
|
-
}
|
|
4445
|
-
}
|
|
4446
|
-
|
|
4447
|
-
class TreedbCompactLayout extends BaseLayout
|
|
4448
|
-
{
|
|
4449
|
-
async execute(data, options) {
|
|
4450
|
-
let input = treedb_layout_input(data);
|
|
4451
|
-
return treedb_layout_output(layout_compact(input.nodes, input.edges, options));
|
|
4452
|
-
}
|
|
4453
|
-
}
|
|
4454
|
-
|
|
4455
4422
|
class TreedbRadialLayout extends BaseLayout
|
|
4456
4423
|
{
|
|
4457
4424
|
async execute(data, options) {
|
|
@@ -9786,6 +9753,7 @@ async function reconcile_fold_now(gobj, opts)
|
|
|
9786
9753
|
history_resume(gobj);
|
|
9787
9754
|
}
|
|
9788
9755
|
publish_legend_state(gobj, visible);
|
|
9756
|
+
refresh_find(gobj, visible);
|
|
9789
9757
|
return changed;
|
|
9790
9758
|
}
|
|
9791
9759
|
|
|
@@ -10361,11 +10329,6 @@ function ac_load_data(gobj, event, kw, src)
|
|
|
10361
10329
|
priv._pending_focus_all = false;
|
|
10362
10330
|
graph_focus_topic(gobj, ft, all);
|
|
10363
10331
|
}
|
|
10364
|
-
if(priv._pending_find !== null) {
|
|
10365
|
-
let term = priv._pending_find;
|
|
10366
|
-
priv._pending_find = null;
|
|
10367
|
-
publish_find_result(gobj, term, graph_find_nodes(gobj, term));
|
|
10368
|
-
}
|
|
10369
10332
|
});
|
|
10370
10333
|
}
|
|
10371
10334
|
|
|
@@ -11192,16 +11155,30 @@ function ac_node_dblclick(gobj, event, kw, src)
|
|
|
11192
11155
|
}
|
|
11193
11156
|
|
|
11194
11157
|
/************************************************************
|
|
11195
|
-
* Tell whoever asked how many
|
|
11196
|
-
*
|
|
11197
|
-
*
|
|
11158
|
+
* Tell whoever asked how many cards the term lit. The count is the
|
|
11159
|
+
* answer to the question the box asks; without it "nothing lit" and
|
|
11160
|
+
* "nothing matched" look the same.
|
|
11161
|
+
*
|
|
11162
|
+
* matches lit cards (on screen)
|
|
11163
|
+
* folded_matches matches folded away, not on screen
|
|
11164
|
+
* hidden_matches matches in hidden topics
|
|
11165
|
+
* current 1-based place of the card Enter centred on
|
|
11166
|
+
* among the lit ones (reading order), 0 = none
|
|
11198
11167
|
************************************************************/
|
|
11199
|
-
function publish_find_result(gobj
|
|
11168
|
+
function publish_find_result(gobj)
|
|
11200
11169
|
{
|
|
11170
|
+
let priv = gobj.priv;
|
|
11171
|
+
let found = priv._find_found || {keys: [], folded: 0, hidden: 0};
|
|
11172
|
+
let current = 0;
|
|
11173
|
+
if(priv._find_term && priv._find_current) {
|
|
11174
|
+
current = find_order(gobj).indexOf(priv._find_current) + 1;
|
|
11175
|
+
}
|
|
11201
11176
|
gobj_publish_event(gobj, "EV_FIND_RESULT", {
|
|
11202
|
-
term:
|
|
11203
|
-
matches:
|
|
11204
|
-
|
|
11177
|
+
term: priv._find_term,
|
|
11178
|
+
matches: priv._find_term? (priv._focus_ids || []).length : 0,
|
|
11179
|
+
folded_matches: found.folded,
|
|
11180
|
+
hidden_matches: found.hidden,
|
|
11181
|
+
current: current,
|
|
11205
11182
|
});
|
|
11206
11183
|
}
|
|
11207
11184
|
|
|
@@ -11210,9 +11187,16 @@ function publish_find_result(gobj, term, matches)
|
|
|
11210
11187
|
************************************************************/
|
|
11211
11188
|
function ac_find_nodes(gobj, event, kw, src)
|
|
11212
11189
|
{
|
|
11213
|
-
|
|
11214
|
-
|
|
11215
|
-
|
|
11190
|
+
graph_find_nodes(gobj, (kw && kw.text) || "");
|
|
11191
|
+
return 0;
|
|
11192
|
+
}
|
|
11193
|
+
|
|
11194
|
+
/************************************************************
|
|
11195
|
+
* Enter in the find box: {back: true} for Shift+Enter.
|
|
11196
|
+
************************************************************/
|
|
11197
|
+
function ac_find_next(gobj, event, kw, src)
|
|
11198
|
+
{
|
|
11199
|
+
graph_find_step(gobj, !!(kw && kw.back));
|
|
11216
11200
|
return 0;
|
|
11217
11201
|
}
|
|
11218
11202
|
|
|
@@ -11774,6 +11758,7 @@ function create_gclass(gclass_name)
|
|
|
11774
11758
|
["EV_ZOOM_SELECTION", ac_zoom_selection, null],
|
|
11775
11759
|
["EV_FOCUS_TOPIC", ac_focus_topic, null],
|
|
11776
11760
|
["EV_FIND_NODES", ac_find_nodes, null],
|
|
11761
|
+
["EV_FIND_NEXT", ac_find_next, null],
|
|
11777
11762
|
["EV_TOGGLE_FOLD", ac_toggle_fold, null],
|
|
11778
11763
|
["EV_SHOW_MORE", ac_show_more, null],
|
|
11779
11764
|
["EV_SET_FOLD_LEVEL", ac_set_fold_level, null],
|
|
@@ -11837,6 +11822,7 @@ function create_gclass(gclass_name)
|
|
|
11837
11822
|
["EV_ZOOM_SELECTION", 0],
|
|
11838
11823
|
["EV_FOCUS_TOPIC", 0],
|
|
11839
11824
|
["EV_FIND_NODES", 0],
|
|
11825
|
+
["EV_FIND_NEXT", 0],
|
|
11840
11826
|
["EV_FIND_RESULT", event_flag_t.EVF_OUTPUT_EVENT],
|
|
11841
11827
|
["EV_LAYOUT_AUTOSET", event_flag_t.EVF_OUTPUT_EVENT],
|
|
11842
11828
|
/* Optional: a host that does not persist the viewport is not
|