@yuneta/gobj-ui 7.23.81 → 7.23.83
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 +42 -27
- package/dist/gobj-ui.cjs.js +308 -128
- package/dist/gobj-ui.es.js +308 -128
- package/package.json +1 -1
- package/src/c_g6_nodes_tree.js +272 -90
- package/src/c_yui_treedb_graph.js +67 -27
package/package.json
CHANGED
package/src/c_g6_nodes_tree.js
CHANGED
|
@@ -263,16 +263,34 @@ const PORT_R_CHILD = 8;
|
|
|
263
263
|
const PORT_LINE_WIDTH = 2;
|
|
264
264
|
|
|
265
265
|
/*
|
|
266
|
-
* The
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
266
|
+
* The three VIEWS of a record (`node_mode`), the same three the
|
|
267
|
+
* gobj tree view has: `expanded` is the card with its pills and
|
|
268
|
+
* its ports; `compact` is a one-line pill with the name inside
|
|
269
|
+
* and small ports, the half-way house; `shape` is a figure of the
|
|
270
|
+
* topic's colour -- a square, a circle, a diamond... chosen in the
|
|
271
|
+
* node's properties -- no ports, no text, topology and nothing
|
|
272
|
+
* else. The three tiers keep their order of size in every view,
|
|
273
|
+
* so a root is still bigger than a leaf when nothing says which
|
|
274
|
+
* is which.
|
|
270
275
|
*/
|
|
271
|
-
const
|
|
276
|
+
const NODE_MODES = ['expanded', 'compact', 'shape'];
|
|
277
|
+
|
|
278
|
+
const PILL_SIZE = {
|
|
279
|
+
hierarchical: [200, 30],
|
|
280
|
+
extended: [180, 28],
|
|
281
|
+
child: [160, 26],
|
|
282
|
+
};
|
|
283
|
+
const PORT_R_PILL = 6;
|
|
284
|
+
|
|
285
|
+
const SHAPE_SIZE = {
|
|
272
286
|
hierarchical: 32,
|
|
273
287
|
extended: 28,
|
|
274
288
|
child: 22,
|
|
275
289
|
};
|
|
290
|
+
/* The figures a `shape` node can be, by G6's own node types
|
|
291
|
+
* (`square` is G6's `rect` with rounded corners). */
|
|
292
|
+
const NODE_SHAPES = ['square', 'circle', 'diamond', 'triangle', 'hexagon', 'star'];
|
|
293
|
+
const DEFAULT_NODE_SHAPE = 'square';
|
|
276
294
|
|
|
277
295
|
/***************************************************************
|
|
278
296
|
* Internal layout and operation mode definitions
|
|
@@ -398,8 +416,8 @@ SDATA(data_type_t.DTP_STRING, "main_topic", 0, "", "The topic t
|
|
|
398
416
|
SDATA(data_type_t.DTP_LIST, "loose_topics", 0, "[]", "Topics whose loose records (no parent, in a topic the schema hangs from the main one) are shown as roots"),
|
|
399
417
|
|
|
400
418
|
/*---------------- Node shape ----------------*/
|
|
401
|
-
SDATA(data_type_t.DTP_STRING, "node_mode", 0, "expanded", "How a record is drawn: `expanded` = the card with its ports
|
|
402
|
-
SDATA(data_type_t.DTP_BOOLEAN, "node_labels", 0, true, "A
|
|
419
|
+
SDATA(data_type_t.DTP_STRING, "node_mode", 0, "expanded", "How a record is drawn: `expanded` = the card with its pills and ports; `compact` = a one-line pill with the name and small ports; `shape` = a figure of the topic's colour (square, circle, diamond... per node or topic, from the node properties), no ports, no text -- the topology alone. One node can be toggled against it (EV_TOGGLE_NODE_MODE)"),
|
|
420
|
+
SDATA(data_type_t.DTP_BOOLEAN, "node_labels", 0, true, "A `shape` node says its name under the figure. Off, it is a coloured figure and nothing else"),
|
|
403
421
|
SDATA(data_type_t.DTP_BOOLEAN, "confirm_delete_node", 0, true, "Ask confirmation before deleting a node"),
|
|
404
422
|
SDATA(data_type_t.DTP_BOOLEAN, "confirm_unlink_edge", 0, true, "Ask confirmation before unlinking an edge"),
|
|
405
423
|
|
|
@@ -2020,20 +2038,47 @@ function create_topic_node(gobj, desc, record)
|
|
|
2020
2038
|
************************************************************/
|
|
2021
2039
|
function node_shape_of(gobj, node_id, desc, record, geometry, pills_html)
|
|
2022
2040
|
{
|
|
2023
|
-
|
|
2024
|
-
|
|
2041
|
+
switch(node_mode_of(gobj, node_id)) {
|
|
2042
|
+
case 'shape':
|
|
2043
|
+
return figure_shape_of(gobj, desc, record, geometry, {});
|
|
2044
|
+
case 'compact':
|
|
2045
|
+
return pill_shape_of(gobj, desc, record, geometry);
|
|
2046
|
+
default:
|
|
2047
|
+
return card_shape_of(gobj, desc, record, geometry, pills_html);
|
|
2025
2048
|
}
|
|
2026
|
-
return card_shape_of(gobj, desc, record, geometry, pills_html);
|
|
2027
2049
|
}
|
|
2028
2050
|
|
|
2029
|
-
|
|
2051
|
+
/* The view of ONE node: the graph's `node_mode`, unless the node
|
|
2052
|
+
* was toggled against it -- an expanded node in a compact or shape
|
|
2053
|
+
* graph, a compact one in an expanded graph. */
|
|
2054
|
+
function node_mode_of(gobj, node_id)
|
|
2030
2055
|
{
|
|
2031
2056
|
let priv = gobj.priv;
|
|
2032
2057
|
if(!priv._open_nodes) {
|
|
2033
2058
|
priv._open_nodes = new Set();
|
|
2034
2059
|
}
|
|
2035
|
-
let
|
|
2036
|
-
|
|
2060
|
+
let mode = gobj_read_str_attr(gobj, "node_mode");
|
|
2061
|
+
if(!mode || !str_in_list(NODE_MODES, mode)) {
|
|
2062
|
+
mode = 'expanded';
|
|
2063
|
+
}
|
|
2064
|
+
if(priv._open_nodes.has(node_id)) {
|
|
2065
|
+
return (mode === 'expanded')? 'compact' : 'expanded';
|
|
2066
|
+
}
|
|
2067
|
+
return mode;
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
/* Not the full card: a pill or a figure. What the size bookkeeping
|
|
2071
|
+
* asks -- neither has a size anybody chose, or pills to grow for. */
|
|
2072
|
+
function node_is_compact(gobj, node_id)
|
|
2073
|
+
{
|
|
2074
|
+
return node_mode_of(gobj, node_id) !== 'expanded';
|
|
2075
|
+
}
|
|
2076
|
+
|
|
2077
|
+
function topic_defaults_of(gobj, topic_name)
|
|
2078
|
+
{
|
|
2079
|
+
let topic_props = gobj.priv._graph_properties[topic_name];
|
|
2080
|
+
return (is_object(topic_props) && is_object(topic_props.defaults))?
|
|
2081
|
+
topic_props.defaults : null;
|
|
2037
2082
|
}
|
|
2038
2083
|
|
|
2039
2084
|
/************************************************************
|
|
@@ -2115,74 +2160,139 @@ function card_shape_of(gobj, desc, record, geometry, pills_html)
|
|
|
2115
2160
|
style.portR = geometry.portR;
|
|
2116
2161
|
}
|
|
2117
2162
|
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
port: true,
|
|
2121
|
-
ports: ports,
|
|
2122
|
-
portR: node_treedb_type === 'child' ? PORT_R_CHILD : PORT_R_ENTITY,
|
|
2123
|
-
portLineWidth: PORT_LINE_WIDTH,
|
|
2124
|
-
});
|
|
2163
|
+
dress_ports(gobj, style, ports, geometry, topic_defaults,
|
|
2164
|
+
node_treedb_type === 'child' ? PORT_R_CHILD : PORT_R_ENTITY, false);
|
|
2125
2165
|
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
let default_port_sizes = topic_defaults ? topic_defaults.port_sizes : null;
|
|
2129
|
-
if(is_object(port_sizes) || is_object(default_port_sizes)) {
|
|
2130
|
-
for(let i = 0; i < style.ports.length; i++) {
|
|
2131
|
-
let key = style.ports[i].key;
|
|
2132
|
-
let r = is_object(port_sizes) ? port_sizes[key] : null;
|
|
2133
|
-
if(r == null && is_object(default_port_sizes)) {
|
|
2134
|
-
r = default_port_sizes[key];
|
|
2135
|
-
}
|
|
2136
|
-
if(r != null) {
|
|
2137
|
-
style.ports[i].r = r;
|
|
2138
|
-
}
|
|
2139
|
-
}
|
|
2140
|
-
}
|
|
2166
|
+
return {type: 'treedb-card', style: style};
|
|
2167
|
+
}
|
|
2141
2168
|
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
shape = default_port_shapes[key];
|
|
2153
|
-
}
|
|
2154
|
-
if(!shape) {
|
|
2155
|
-
shape = default_shape;
|
|
2156
|
-
}
|
|
2157
|
-
if(shape && str_in_list(PORT_SHAPES, shape)) {
|
|
2158
|
-
style.ports[i].shape = shape;
|
|
2159
|
-
}
|
|
2160
|
-
}
|
|
2161
|
-
} else {
|
|
2169
|
+
/************************************************************
|
|
2170
|
+
* The ports of a card or a pill, dressed: the node-level radius
|
|
2171
|
+
* (saved, topic default, or the tier's), each port's own radius
|
|
2172
|
+
* and shape (saved first, then the topic's per-port defaults, then
|
|
2173
|
+
* the topic's one shape for every port). `fixed_r` -- the pill --
|
|
2174
|
+
* ignores every saved radius: a pill has one size of port.
|
|
2175
|
+
************************************************************/
|
|
2176
|
+
function dress_ports(gobj, style, ports, geometry, topic_defaults, r_default, fixed_r)
|
|
2177
|
+
{
|
|
2178
|
+
if(!json_size(ports)) {
|
|
2162
2179
|
style.port = false;
|
|
2163
2180
|
style.ports = [];
|
|
2181
|
+
return;
|
|
2182
|
+
}
|
|
2183
|
+
json_object_update_missing(style, {
|
|
2184
|
+
port: true,
|
|
2185
|
+
ports: ports,
|
|
2186
|
+
portR: r_default,
|
|
2187
|
+
portLineWidth: PORT_LINE_WIDTH,
|
|
2188
|
+
});
|
|
2189
|
+
if(fixed_r) {
|
|
2190
|
+
style.portR = r_default;
|
|
2164
2191
|
}
|
|
2165
2192
|
|
|
2193
|
+
let port_sizes = fixed_r? null : geometry.port_sizes;
|
|
2194
|
+
let default_port_sizes = (!fixed_r && topic_defaults)? topic_defaults.port_sizes : null;
|
|
2195
|
+
let port_shapes = geometry.port_shapes;
|
|
2196
|
+
let default_port_shapes = topic_defaults? topic_defaults.port_shapes : null;
|
|
2197
|
+
let default_shape = topic_defaults? topic_defaults.port_shape : null;
|
|
2198
|
+
for(let i = 0; i < style.ports.length; i++) {
|
|
2199
|
+
let key = style.ports[i].key;
|
|
2200
|
+
let r = is_object(port_sizes)? port_sizes[key] : null;
|
|
2201
|
+
if(r == null && is_object(default_port_sizes)) {
|
|
2202
|
+
r = default_port_sizes[key];
|
|
2203
|
+
}
|
|
2204
|
+
if(r != null) {
|
|
2205
|
+
style.ports[i].r = r;
|
|
2206
|
+
}
|
|
2207
|
+
let shape = is_object(port_shapes)? port_shapes[key] : null;
|
|
2208
|
+
if(!shape && is_object(default_port_shapes)) {
|
|
2209
|
+
shape = default_port_shapes[key];
|
|
2210
|
+
}
|
|
2211
|
+
if(!shape) {
|
|
2212
|
+
shape = default_shape;
|
|
2213
|
+
}
|
|
2214
|
+
if(shape && str_in_list(PORT_SHAPES, shape)) {
|
|
2215
|
+
style.ports[i].shape = shape;
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
/************************************************************
|
|
2221
|
+
* The PILL: one line with the name inside, the tint and the
|
|
2222
|
+
* border of the topic, small ports on its edges -- the half-way
|
|
2223
|
+
* house between the card and the figure, and the same one-line
|
|
2224
|
+
* chip a leaf record already is, at the tier's width. No pills:
|
|
2225
|
+
* a hook is opened from the card (expand the node).
|
|
2226
|
+
************************************************************/
|
|
2227
|
+
function pill_shape_of(gobj, desc, record, geometry)
|
|
2228
|
+
{
|
|
2229
|
+
let priv = gobj.priv;
|
|
2230
|
+
let tier = desc.node_treedb_type;
|
|
2231
|
+
let size = PILL_SIZE[tier] || PILL_SIZE.hierarchical;
|
|
2232
|
+
let ports = build_ports(gobj, desc);
|
|
2233
|
+
|
|
2234
|
+
let style = {
|
|
2235
|
+
fill: desc.color,
|
|
2236
|
+
stroke: getStrokeColor(desc.color),
|
|
2237
|
+
lineWidth: 1,
|
|
2238
|
+
lineDash: [],
|
|
2239
|
+
halo: false,
|
|
2240
|
+
labelText: "",
|
|
2241
|
+
size: [...size],
|
|
2242
|
+
dx: -size[0] / 2,
|
|
2243
|
+
dy: -size[1] / 2,
|
|
2244
|
+
innerHTML: build_chip_innerHTML(
|
|
2245
|
+
desc.color, priv.theme, record.icon, node_label(desc, record), record.id
|
|
2246
|
+
),
|
|
2247
|
+
};
|
|
2248
|
+
dress_ports(gobj, style, ports, geometry, topic_defaults_of(gobj, desc.topic_name),
|
|
2249
|
+
PORT_R_PILL, true);
|
|
2166
2250
|
return {type: 'treedb-card', style: style};
|
|
2167
2251
|
}
|
|
2168
2252
|
|
|
2253
|
+
/* The pill's html with the flags it carries right now. */
|
|
2254
|
+
function pill_innerHTML_of(gobj, nd, theme, highlight, selected, anchored)
|
|
2255
|
+
{
|
|
2256
|
+
let desc = nd.data.desc;
|
|
2257
|
+
let record = nd.data.record || {};
|
|
2258
|
+
return build_chip_innerHTML(
|
|
2259
|
+
desc.color, theme, record.icon, node_label(desc, record), record.id,
|
|
2260
|
+
highlight, selected, anchored
|
|
2261
|
+
);
|
|
2262
|
+
}
|
|
2263
|
+
|
|
2264
|
+
/* The figure a `shape` node wears: the node's own, then the
|
|
2265
|
+
* topic's default (the node properties popover writes both), then
|
|
2266
|
+
* a square. */
|
|
2267
|
+
function node_figure_of(gobj, desc, geometry)
|
|
2268
|
+
{
|
|
2269
|
+
let shape = geometry? geometry.node_shape : null;
|
|
2270
|
+
if(!shape) {
|
|
2271
|
+
let defaults = topic_defaults_of(gobj, desc.topic_name);
|
|
2272
|
+
shape = defaults? defaults.node_shape : null;
|
|
2273
|
+
}
|
|
2274
|
+
return (shape && str_in_list(NODE_SHAPES, shape))? shape : DEFAULT_NODE_SHAPE;
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2169
2277
|
/************************************************************
|
|
2170
|
-
* The
|
|
2171
|
-
*
|
|
2172
|
-
*
|
|
2173
|
-
*
|
|
2174
|
-
*
|
|
2278
|
+
* The FIGURE: a native G6 node of the topic's colour -- a square,
|
|
2279
|
+
* a circle, a diamond, a triangle, a hexagon, a star -- no ports,
|
|
2280
|
+
* the name as a label under it when `node_labels` says so. Native
|
|
2281
|
+
* and not html on purpose: G6's own stroke and halo paint on it,
|
|
2282
|
+
* so the focus, the selection and the anchor are three keys of
|
|
2283
|
+
* the style instead of a rebuilt innerHTML.
|
|
2175
2284
|
*
|
|
2176
2285
|
* flags {highlight, selected, anchored}
|
|
2177
2286
|
************************************************************/
|
|
2178
|
-
function
|
|
2287
|
+
function figure_shape_of(gobj, desc, record, geometry, flags)
|
|
2179
2288
|
{
|
|
2180
2289
|
let priv = gobj.priv;
|
|
2181
2290
|
let dark = (priv.theme === "dark");
|
|
2182
2291
|
let f = flags || {};
|
|
2183
2292
|
let tier = desc.node_treedb_type;
|
|
2184
|
-
let side =
|
|
2293
|
+
let side = SHAPE_SIZE[tier] || SHAPE_SIZE.hierarchical;
|
|
2185
2294
|
let labels = gobj_read_bool_attr(gobj, "node_labels");
|
|
2295
|
+
let figure = node_figure_of(gobj, desc, geometry || {});
|
|
2186
2296
|
|
|
2187
2297
|
let stroke = getStrokeColor(desc.color);
|
|
2188
2298
|
let line_width = 1.5;
|
|
@@ -2207,7 +2317,7 @@ function compact_shape_of(gobj, desc, record, flags)
|
|
|
2207
2317
|
|
|
2208
2318
|
let style = {
|
|
2209
2319
|
size: [side, side],
|
|
2210
|
-
radius: Math.round(side / 5),
|
|
2320
|
+
radius: (figure === 'square')? Math.round(side / 5) : 0,
|
|
2211
2321
|
fill: desc.color,
|
|
2212
2322
|
stroke: stroke,
|
|
2213
2323
|
lineWidth: line_width,
|
|
@@ -2231,7 +2341,7 @@ function compact_shape_of(gobj, desc, record, flags)
|
|
|
2231
2341
|
labelBackgroundRadius: 3,
|
|
2232
2342
|
labelPadding: [1, 3],
|
|
2233
2343
|
};
|
|
2234
|
-
return {type: 'rect', style: style};
|
|
2344
|
+
return {type: (figure === 'square')? 'rect' : figure, style: style};
|
|
2235
2345
|
}
|
|
2236
2346
|
|
|
2237
2347
|
/************************************************************
|
|
@@ -6326,6 +6436,17 @@ function show_node_popover(gobj)
|
|
|
6326
6436
|
create_form_label(popover, 'line width');
|
|
6327
6437
|
let lwInput = create_form_number_input(popover, currentLW, 1, 20, preview_node);
|
|
6328
6438
|
|
|
6439
|
+
/* The figure the node wears in the `shape` view. No preview:
|
|
6440
|
+
* it shows only in that view, and applying it is the same
|
|
6441
|
+
* reshape either way. */
|
|
6442
|
+
create_form_label(popover, 'shape');
|
|
6443
|
+
let shapeSelect = create_form_select(popover, NODE_SHAPES.map((shape) => {
|
|
6444
|
+
return {value: shape, label: t(shape)};
|
|
6445
|
+
}));
|
|
6446
|
+
shapeSelect.value = nodeData.data && nodeData.data.desc
|
|
6447
|
+
? node_figure_of(gobj, nodeData.data.desc, nodeData.data.graph_props || {})
|
|
6448
|
+
: DEFAULT_NODE_SHAPE;
|
|
6449
|
+
|
|
6329
6450
|
// Apply-to scope
|
|
6330
6451
|
create_form_label(popover, 'apply to');
|
|
6331
6452
|
let scopeSelect = create_form_select(popover, [
|
|
@@ -6363,7 +6484,8 @@ function show_node_popover(gobj)
|
|
|
6363
6484
|
fillInput.value,
|
|
6364
6485
|
strokeInput.value,
|
|
6365
6486
|
parseInt(lwInput.value) || 1,
|
|
6366
|
-
scopeSelect.value
|
|
6487
|
+
scopeSelect.value,
|
|
6488
|
+
shapeSelect.value
|
|
6367
6489
|
);
|
|
6368
6490
|
},
|
|
6369
6491
|
},
|
|
@@ -6763,18 +6885,22 @@ function repaint_cards(gobj, ids)
|
|
|
6763
6885
|
continue; /* gone since it was listed */
|
|
6764
6886
|
}
|
|
6765
6887
|
let anchored = (priv.anchor_state === "on" && id === priv.anchor_id);
|
|
6766
|
-
/* A
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6888
|
+
/* A figure paints its flags on its own stroke; a pill in
|
|
6889
|
+
* its one-line html; a card in its own. */
|
|
6890
|
+
let mode = (nd && nd.data && nd.data.desc)? node_mode_of(gobj, id) : 'expanded';
|
|
6891
|
+
if(mode === 'shape') {
|
|
6892
|
+
updates.push({id: id, style: figure_shape_of(
|
|
6893
|
+
gobj, nd.data.desc, nd.data.record || {}, nd.data.graph_props || {},
|
|
6770
6894
|
{highlight: focus.has(id), selected: selected.has(id), anchored: anchored}
|
|
6771
6895
|
).style});
|
|
6772
6896
|
continue;
|
|
6773
6897
|
}
|
|
6774
|
-
let html =
|
|
6775
|
-
nd, priv.theme, focus.has(id), selected.has(id), anchored
|
|
6776
|
-
|
|
6777
|
-
|
|
6898
|
+
let html = (mode === 'compact')
|
|
6899
|
+
? pill_innerHTML_of(gobj, nd, priv.theme, focus.has(id), selected.has(id), anchored)
|
|
6900
|
+
: node_innerHTML_of(
|
|
6901
|
+
nd, priv.theme, focus.has(id), selected.has(id), anchored,
|
|
6902
|
+
pills_html_of(gobj, nd)
|
|
6903
|
+
);
|
|
6778
6904
|
if(html !== null) {
|
|
6779
6905
|
updates.push({id: id, style: {innerHTML: html}});
|
|
6780
6906
|
}
|
|
@@ -6831,17 +6957,20 @@ function refresh_html_nodes_theme(gobj, theme)
|
|
|
6831
6957
|
let id = nodes[i].id;
|
|
6832
6958
|
let nd = graph.getNodeData(id);
|
|
6833
6959
|
let anchored = (priv.anchor_state === "on" && id === priv.anchor_id);
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6960
|
+
let mode = (nd && nd.data && nd.data.desc)? node_mode_of(gobj, id) : 'expanded';
|
|
6961
|
+
if(mode === 'shape') {
|
|
6962
|
+
updates.push({id: id, style: figure_shape_of(
|
|
6963
|
+
gobj, nd.data.desc, nd.data.record || {}, nd.data.graph_props || {},
|
|
6837
6964
|
{highlight: highlighted.has(id), selected: selected.has(id), anchored: anchored}
|
|
6838
6965
|
).style});
|
|
6839
6966
|
continue;
|
|
6840
6967
|
}
|
|
6841
|
-
let html =
|
|
6842
|
-
nd, theme, highlighted.has(id), selected.has(id), anchored
|
|
6843
|
-
|
|
6844
|
-
|
|
6968
|
+
let html = (mode === 'compact')
|
|
6969
|
+
? pill_innerHTML_of(gobj, nd, theme, highlighted.has(id), selected.has(id), anchored)
|
|
6970
|
+
: node_innerHTML_of(
|
|
6971
|
+
nd, theme, highlighted.has(id), selected.has(id), anchored,
|
|
6972
|
+
pills_html_of(gobj, nd)
|
|
6973
|
+
);
|
|
6845
6974
|
if(html === null) {
|
|
6846
6975
|
html = more_innerHTML_of(gobj, nd, theme); /* a `+N` chip, or null */
|
|
6847
6976
|
}
|
|
@@ -7551,7 +7680,7 @@ function hide_unlink_confirm(gobj)
|
|
|
7551
7680
|
* Apply node properties to one or more nodes.
|
|
7552
7681
|
* scope: 'this', 'same_topic', 'all'
|
|
7553
7682
|
************************************************************/
|
|
7554
|
-
function apply_node_properties(gobj, node_id, fill, stroke, lineWidth, scope)
|
|
7683
|
+
function apply_node_properties(gobj, node_id, fill, stroke, lineWidth, scope, shape)
|
|
7555
7684
|
{
|
|
7556
7685
|
let priv = gobj.priv;
|
|
7557
7686
|
let graph = priv.graph;
|
|
@@ -7565,6 +7694,23 @@ function apply_node_properties(gobj, node_id, fill, stroke, lineWidth, scope)
|
|
|
7565
7694
|
let updates = [];
|
|
7566
7695
|
const nodes = graph.getData().nodes;
|
|
7567
7696
|
|
|
7697
|
+
/* The figure: written where Save reads it (the node's entry in
|
|
7698
|
+
* `__graphs__`), and as the topic's default for the wider
|
|
7699
|
+
* scopes, so a record that arrives later is born with it. */
|
|
7700
|
+
let figure = (shape && str_in_list(NODE_SHAPES, shape))? shape : "";
|
|
7701
|
+
let reshaped = [];
|
|
7702
|
+
if(figure && scope !== 'this') {
|
|
7703
|
+
let topics = (scope === 'same_topic')? [source_topic] : Object.keys(priv.descs || {});
|
|
7704
|
+
for(let topic_name of topics) {
|
|
7705
|
+
if(!is_object(priv._graph_properties[topic_name])) {
|
|
7706
|
+
priv._graph_properties[topic_name] = {};
|
|
7707
|
+
}
|
|
7708
|
+
let defaults = priv._graph_properties[topic_name].defaults || {};
|
|
7709
|
+
defaults.node_shape = figure;
|
|
7710
|
+
priv._graph_properties[topic_name].defaults = defaults;
|
|
7711
|
+
}
|
|
7712
|
+
}
|
|
7713
|
+
|
|
7568
7714
|
for(let i = 0; i < nodes.length; i++) {
|
|
7569
7715
|
let nd = graph.getNodeData(nodes[i].id);
|
|
7570
7716
|
if(!nd || !nd.data || !nd.data.desc) {
|
|
@@ -7578,6 +7724,13 @@ function apply_node_properties(gobj, node_id, fill, stroke, lineWidth, scope)
|
|
|
7578
7724
|
continue;
|
|
7579
7725
|
}
|
|
7580
7726
|
|
|
7727
|
+
if(figure) {
|
|
7728
|
+
node_props_entry(gobj, nd).node_shape = figure;
|
|
7729
|
+
if(node_mode_of(gobj, nodes[i].id) === 'shape') {
|
|
7730
|
+
reshaped.push(nodes[i].id);
|
|
7731
|
+
}
|
|
7732
|
+
}
|
|
7733
|
+
|
|
7581
7734
|
let updateStyle = { fill: fill, stroke: stroke, lineWidth: lineWidth };
|
|
7582
7735
|
if(nd.data.desc.node_treedb_type === 'hierarchical') {
|
|
7583
7736
|
let record = nd.data.record || {};
|
|
@@ -7593,6 +7746,9 @@ function apply_node_properties(gobj, node_id, fill, stroke, lineWidth, scope)
|
|
|
7593
7746
|
|
|
7594
7747
|
if(updates.length > 0) {
|
|
7595
7748
|
graph.updateNodeData(updates);
|
|
7749
|
+
if(reshaped.length) {
|
|
7750
|
+
reshape_nodes(gobj, reshaped);
|
|
7751
|
+
}
|
|
7596
7752
|
graph.draw().then(() => {
|
|
7597
7753
|
mark_graph_dirty(gobj);
|
|
7598
7754
|
});
|
|
@@ -7601,6 +7757,32 @@ function apply_node_properties(gobj, node_id, fill, stroke, lineWidth, scope)
|
|
|
7601
7757
|
hide_node_popover(gobj);
|
|
7602
7758
|
}
|
|
7603
7759
|
|
|
7760
|
+
/************************************************************
|
|
7761
|
+
* The node's entry in `_graph_properties` -- the object Save
|
|
7762
|
+
* reads and writes -- created if it has none yet, and the node's
|
|
7763
|
+
* `data.graph_props` pointed at it, so a key written here is
|
|
7764
|
+
* read by the next reshape and survives the next Save.
|
|
7765
|
+
************************************************************/
|
|
7766
|
+
function node_props_entry(gobj, nd)
|
|
7767
|
+
{
|
|
7768
|
+
let priv = gobj.priv;
|
|
7769
|
+
let topic_name = nd.data.topic_name;
|
|
7770
|
+
let record = nd.data.record || {};
|
|
7771
|
+
|
|
7772
|
+
if(!is_object(priv._graph_properties[topic_name])) {
|
|
7773
|
+
priv._graph_properties[topic_name] = {};
|
|
7774
|
+
}
|
|
7775
|
+
let topic_props = priv._graph_properties[topic_name];
|
|
7776
|
+
if(!is_object(topic_props.nodes)) {
|
|
7777
|
+
topic_props.nodes = {};
|
|
7778
|
+
}
|
|
7779
|
+
if(!is_object(topic_props.nodes[record.id])) {
|
|
7780
|
+
topic_props.nodes[record.id] = is_object(nd.data.graph_props)? nd.data.graph_props : {};
|
|
7781
|
+
}
|
|
7782
|
+
nd.data.graph_props = topic_props.nodes[record.id];
|
|
7783
|
+
return nd.data.graph_props;
|
|
7784
|
+
}
|
|
7785
|
+
|
|
7604
7786
|
/************************************************************
|
|
7605
7787
|
* Apply edge properties to one or more edges.
|
|
7606
7788
|
* scope: 'this', 'same_type', 'all'
|
|
@@ -7792,9 +7974,9 @@ function build_node_context_menu(gobj, node_id)
|
|
|
7792
7974
|
/* Open or close THIS node against the mode: the door a finger
|
|
7793
7975
|
* has to it (a double click is the mouse's). */
|
|
7794
7976
|
if(node_is_compact(gobj, node_id)) {
|
|
7795
|
-
items.push(ctx_item('g6-icon-fullscreen', t('
|
|
7977
|
+
items.push(ctx_item('g6-icon-fullscreen', t('expand node'), 'toggle_node_mode'));
|
|
7796
7978
|
} else {
|
|
7797
|
-
items.push(ctx_item('g6-icon-fullscreen-exit', t('
|
|
7979
|
+
items.push(ctx_item('g6-icon-fullscreen-exit', t('collapse node'), 'toggle_node_mode'));
|
|
7798
7980
|
}
|
|
7799
7981
|
|
|
7800
7982
|
if(gobj.priv.edit_mode && !node_is_compact(gobj, node_id)) {
|
|
@@ -8148,7 +8330,7 @@ function copy_size_to_ports(gobj, same_topic_only)
|
|
|
8148
8330
|
/* A card with pills is this much taller; the row sits under the subtitle. */
|
|
8149
8331
|
const PILL_ROW_HEIGHT = 24;
|
|
8150
8332
|
const MORE_CHIP_SIZE = [76, 26];
|
|
8151
|
-
/* The chip beside
|
|
8333
|
+
/* The chip beside pills and figures: a square of 22 next to a chip of 76
|
|
8152
8334
|
* read as the biggest thing in the row, so it takes the closed
|
|
8153
8335
|
* scale -- the count and nothing else. */
|
|
8154
8336
|
const MORE_CHIP_SIZE_COMPACT = [40, 22];
|
|
@@ -8162,7 +8344,7 @@ function more_chip_is_compact(gobj, nd)
|
|
|
8162
8344
|
if(parent_id) {
|
|
8163
8345
|
return node_is_compact(gobj, parent_id);
|
|
8164
8346
|
}
|
|
8165
|
-
return gobj_read_str_attr(gobj, "node_mode")
|
|
8347
|
+
return gobj_read_str_attr(gobj, "node_mode") !== "expanded";
|
|
8166
8348
|
}
|
|
8167
8349
|
|
|
8168
8350
|
function more_chip_style(gobj, nd, color, rest)
|
|
@@ -9678,7 +9860,7 @@ function ac_set_loose_topics(gobj, event, kw, src)
|
|
|
9678
9860
|
function ac_set_node_mode(gobj, event, kw, src)
|
|
9679
9861
|
{
|
|
9680
9862
|
let priv = gobj.priv;
|
|
9681
|
-
let mode = (kw && kw.node_mode
|
|
9863
|
+
let mode = (kw && kw.node_mode && str_in_list(NODE_MODES, kw.node_mode))? kw.node_mode : "expanded";
|
|
9682
9864
|
|
|
9683
9865
|
gobj_write_str_attr(gobj, "node_mode", mode);
|
|
9684
9866
|
priv._open_nodes = new Set();
|
|
@@ -9692,9 +9874,9 @@ function ac_set_node_mode(gobj, event, kw, src)
|
|
|
9692
9874
|
}
|
|
9693
9875
|
|
|
9694
9876
|
/************************************************************
|
|
9695
|
-
* The name under a
|
|
9696
|
-
*
|
|
9697
|
-
*
|
|
9877
|
+
* The name under a figure, on or off. Only the `shape` nodes
|
|
9878
|
+
* change, and nothing moves: the label hangs outside the figure
|
|
9879
|
+
* and the layout measures the figure.
|
|
9698
9880
|
************************************************************/
|
|
9699
9881
|
function ac_set_node_labels(gobj, event, kw, src)
|
|
9700
9882
|
{
|
|
@@ -9704,7 +9886,7 @@ function ac_set_node_labels(gobj, event, kw, src)
|
|
|
9704
9886
|
if(!priv.graph) {
|
|
9705
9887
|
return 0;
|
|
9706
9888
|
}
|
|
9707
|
-
let ids = all_record_node_ids(gobj).filter((id) =>
|
|
9889
|
+
let ids = all_record_node_ids(gobj).filter((id) => node_mode_of(gobj, id) === 'shape');
|
|
9708
9890
|
reshape_nodes(gobj, ids);
|
|
9709
9891
|
return 0;
|
|
9710
9892
|
}
|