@yuneta/gobj-ui 7.23.17 → 7.23.19
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/dist/gobj-ui.cjs.js +567 -170
- package/dist/gobj-ui.es.js +559 -162
- package/package.json +1 -1
- package/src/c_g6_nodes_tree.js +135 -3
- package/src/c_yui_gobj_tree_js.js +185 -7
- package/src/c_yui_json.css +11 -2
- package/src/c_yui_json_graph.js +237 -30
- package/src/lib_icons.js +7 -0
- package/src/yui_graph_camera.js +95 -0
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -9350,6 +9350,131 @@ function yui_toolbar(attrs = {}, items = []) {
|
|
|
9350
9350
|
return $toolbar;
|
|
9351
9351
|
}
|
|
9352
9352
|
//#endregion
|
|
9353
|
+
//#region src/lib_graph.js
|
|
9354
|
+
/***********************************************************************
|
|
9355
|
+
* lib_graph.js
|
|
9356
|
+
*
|
|
9357
|
+
* Utilities for graph, using Bulma
|
|
9358
|
+
*
|
|
9359
|
+
* Copyright (c) 2025, ArtGins.
|
|
9360
|
+
* All Rights Reserved.
|
|
9361
|
+
***********************************************************************/
|
|
9362
|
+
/************************************************************
|
|
9363
|
+
* Add class to elements selected
|
|
9364
|
+
************************************************************/
|
|
9365
|
+
function addClasses($container, selector, ...classNames) {
|
|
9366
|
+
$container.querySelectorAll(selector).forEach((el) => el.classList.add(...classNames));
|
|
9367
|
+
}
|
|
9368
|
+
/************************************************************
|
|
9369
|
+
* Remove class from elements selected
|
|
9370
|
+
************************************************************/
|
|
9371
|
+
function removeClasses($container, selector, ...classNames) {
|
|
9372
|
+
$container.querySelectorAll(selector).forEach((el) => el.classList.remove(...classNames));
|
|
9373
|
+
}
|
|
9374
|
+
/************************************************************
|
|
9375
|
+
* Toggle class to elements selected
|
|
9376
|
+
************************************************************/
|
|
9377
|
+
function toggleClasses($container, selector, ...classNames) {
|
|
9378
|
+
$container.querySelectorAll(selector).forEach((el) => classNames.forEach((cls) => el.classList.toggle(cls)));
|
|
9379
|
+
}
|
|
9380
|
+
/************************************************************
|
|
9381
|
+
* Remove child elements
|
|
9382
|
+
************************************************************/
|
|
9383
|
+
function removeChildElements($element) {
|
|
9384
|
+
$element.innerHTML = "";
|
|
9385
|
+
}
|
|
9386
|
+
/************************************************************
|
|
9387
|
+
* Disable/Enable elements
|
|
9388
|
+
************************************************************/
|
|
9389
|
+
function disableElements($container, selector) {
|
|
9390
|
+
$container.querySelectorAll(selector).forEach((el) => {
|
|
9391
|
+
el.setAttribute("disabled", "");
|
|
9392
|
+
});
|
|
9393
|
+
}
|
|
9394
|
+
function enableElements($container, selector) {
|
|
9395
|
+
$container.querySelectorAll(selector).forEach((el) => {
|
|
9396
|
+
if (el.hasAttribute("disabled")) el.removeAttribute("disabled");
|
|
9397
|
+
});
|
|
9398
|
+
}
|
|
9399
|
+
/************************************************************
|
|
9400
|
+
* Set or reset the color of 'enable submit action' state
|
|
9401
|
+
* Color green
|
|
9402
|
+
************************************************************/
|
|
9403
|
+
function set_submit_state($container, selector, set) {
|
|
9404
|
+
if (set) addClasses($container, selector, "color_submit_state");
|
|
9405
|
+
else removeClasses($container, selector, "color_submit_state");
|
|
9406
|
+
}
|
|
9407
|
+
/************************************************************
|
|
9408
|
+
* Set or reset the color of 'enable cancel action' state
|
|
9409
|
+
* Color red
|
|
9410
|
+
************************************************************/
|
|
9411
|
+
function set_cancel_state($container, selector, set) {
|
|
9412
|
+
if (set) addClasses($container, selector, "color_cancel_state");
|
|
9413
|
+
else removeClasses($container, selector, "color_cancel_state");
|
|
9414
|
+
}
|
|
9415
|
+
/************************************************************
|
|
9416
|
+
* Set or reset the color of 'enable actived' state
|
|
9417
|
+
* Color orange
|
|
9418
|
+
************************************************************/
|
|
9419
|
+
function set_active_state($container, selector, set) {
|
|
9420
|
+
if (set) addClasses($container, selector, "color_active_state");
|
|
9421
|
+
else removeClasses($container, selector, "color_active_state");
|
|
9422
|
+
}
|
|
9423
|
+
/************************************************************
|
|
9424
|
+
* Set or reset the 'pressed' state of a TOGGLE.
|
|
9425
|
+
*
|
|
9426
|
+
* Not one of the colours above: each of those names a KIND of
|
|
9427
|
+
* action (create, pending, history, destructive), and a toggle
|
|
9428
|
+
* is not an action -- it is a state the control is IN. So it
|
|
9429
|
+
* looks pressed instead of changing category, which also keeps
|
|
9430
|
+
* it out of the way of a neighbour wearing the same hue.
|
|
9431
|
+
************************************************************/
|
|
9432
|
+
function set_pressed_state($container, selector, set) {
|
|
9433
|
+
if (set) addClasses($container, selector, "pressed_state");
|
|
9434
|
+
else removeClasses($container, selector, "pressed_state");
|
|
9435
|
+
}
|
|
9436
|
+
/**
|
|
9437
|
+
* Returns a smart stroke color in `rgba()` format based on:
|
|
9438
|
+
* - The given fill color (hex or rgb[a])
|
|
9439
|
+
* - The current theme ('light' or 'dark')
|
|
9440
|
+
* - The adjustment factor (default 0.2)
|
|
9441
|
+
*
|
|
9442
|
+
* @param {string} fillColor - Fill color string
|
|
9443
|
+
* @param {string} theme - 'light' or 'dark'
|
|
9444
|
+
* @param {number} factor - Lighten/darken factor (default 0.2)
|
|
9445
|
+
* @returns {string} rgba(r, g, b, a)
|
|
9446
|
+
*/
|
|
9447
|
+
function getStrokeColor(fillColor, theme = "light", factor = .2) {
|
|
9448
|
+
let r, g, b, a = 1;
|
|
9449
|
+
if (fillColor.startsWith("#")) {
|
|
9450
|
+
let hex = fillColor.slice(1);
|
|
9451
|
+
if (hex.length === 3 || hex.length === 4) hex = hex.split("").map((c) => c + c).join("");
|
|
9452
|
+
if (hex.length === 6) {
|
|
9453
|
+
r = parseInt(hex.slice(0, 2), 16);
|
|
9454
|
+
g = parseInt(hex.slice(2, 4), 16);
|
|
9455
|
+
b = parseInt(hex.slice(4, 6), 16);
|
|
9456
|
+
} else if (hex.length === 8) {
|
|
9457
|
+
r = parseInt(hex.slice(0, 2), 16);
|
|
9458
|
+
g = parseInt(hex.slice(2, 4), 16);
|
|
9459
|
+
b = parseInt(hex.slice(4, 6), 16);
|
|
9460
|
+
a = parseInt(hex.slice(6, 8), 16) / 255;
|
|
9461
|
+
} else throw new Error("Unsupported hex format");
|
|
9462
|
+
} else if (fillColor.startsWith("rgb")) {
|
|
9463
|
+
const match = fillColor.match(/rgba?\(\s*(\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\s*\)/);
|
|
9464
|
+
if (!match) throw new Error("Invalid rgb()/rgba() format.");
|
|
9465
|
+
r = parseInt(match[1], 10);
|
|
9466
|
+
g = parseInt(match[2], 10);
|
|
9467
|
+
b = parseInt(match[3], 10);
|
|
9468
|
+
if (match[4] !== void 0) a = parseFloat(match[4]);
|
|
9469
|
+
} else throw new Error("Unsupported color format.");
|
|
9470
|
+
const luminance = (.299 * r + .587 * g + .114 * b) / 255;
|
|
9471
|
+
let lighten;
|
|
9472
|
+
if (theme === "dark") lighten = luminance < .7;
|
|
9473
|
+
else lighten = luminance < .3;
|
|
9474
|
+
const adjust = (v) => lighten ? Math.min(255, Math.floor(v + (255 - v) * factor)) : Math.max(0, Math.floor(v * (1 - factor)));
|
|
9475
|
+
return `rgba(${adjust(r)}, ${adjust(g)}, ${adjust(b)}, ${a.toFixed(3)})`;
|
|
9476
|
+
}
|
|
9477
|
+
//#endregion
|
|
9353
9478
|
//#region src/yui_graph_camera.js
|
|
9354
9479
|
/***********************************************************************
|
|
9355
9480
|
* yui_graph_camera.js
|
|
@@ -9498,6 +9623,79 @@ function yui_graph_fold_items(gobj, wide) {
|
|
|
9498
9623
|
return [camera_button(gobj, "yi-chevron-right", "EV_EXPAND_ALL", "expand all", wide, " transform: rotate(90deg);"), camera_button(gobj, "yi-chevron-right", "EV_COLLAPSE_ALL", "collapse all", wide)];
|
|
9499
9624
|
}
|
|
9500
9625
|
/************************************************************
|
|
9626
|
+
* The ANCHOR toggle: pick one element and every camera move
|
|
9627
|
+
* from then on leaves it in the MIDDLE.
|
|
9628
|
+
*
|
|
9629
|
+
* Why it exists: a graph that fits on screen is unreadable at
|
|
9630
|
+
* the zoom that makes it fit, and the zoom that makes it
|
|
9631
|
+
* readable does not fit -- so the useful view is always a
|
|
9632
|
+
* fraction of the document, and which fraction is the whole
|
|
9633
|
+
* question. Without an anchor, `1:1` lands wherever the
|
|
9634
|
+
* layout's origin happens to be and the reader hunts for the
|
|
9635
|
+
* node they were looking at.
|
|
9636
|
+
*
|
|
9637
|
+
* Crosshairs and not a thumbtack. A tack says "do not move
|
|
9638
|
+
* this", which is what a pinned NODE would mean in a graph you
|
|
9639
|
+
* can drag nodes around in; this pins the CAMERA to a target,
|
|
9640
|
+
* which is what a crosshair says and what every map that
|
|
9641
|
+
* offers it draws. It is also in the icon set already, and a
|
|
9642
|
+
* `yi-*` class that is not is a black square.
|
|
9643
|
+
*
|
|
9644
|
+
* Three states, because two would not say what a press does:
|
|
9645
|
+
*
|
|
9646
|
+
* off no target; the camera behaves as it always did
|
|
9647
|
+
* arming waiting for you to click the element you mean
|
|
9648
|
+
* on locked; every zoom re-centres on it
|
|
9649
|
+
************************************************************/
|
|
9650
|
+
function yui_graph_anchor_item(gobj, wide) {
|
|
9651
|
+
return camera_button(gobj, "yi-location-crosshairs", "EV_TOGGLE_ANCHOR", "anchor view", wide);
|
|
9652
|
+
}
|
|
9653
|
+
/************************************************************
|
|
9654
|
+
* Paint the anchor button. `state` is "off" | "arming" | "on".
|
|
9655
|
+
*
|
|
9656
|
+
* Both live states look PRESSED, because both are states the
|
|
9657
|
+
* control is IN rather than actions; what separates them is the
|
|
9658
|
+
* colour, and only the waiting one takes a palette colour --
|
|
9659
|
+
* it is the one that needs the reader to do something next.
|
|
9660
|
+
************************************************************/
|
|
9661
|
+
function yui_graph_update_anchor($container, state) {
|
|
9662
|
+
if (!$container) return;
|
|
9663
|
+
let $btn = $container.querySelector(".EV_TOGGLE_ANCHOR");
|
|
9664
|
+
if (!$btn) return;
|
|
9665
|
+
set_pressed_state($container, ".EV_TOGGLE_ANCHOR", state !== "off");
|
|
9666
|
+
$btn.classList.toggle("color_pending_state", state === "arming");
|
|
9667
|
+
$btn.setAttribute("aria-pressed", state === "on" ? "true" : "false");
|
|
9668
|
+
let key = state === "arming" ? "click the element to centre on" : state === "on" ? "centred: click to release" : "anchor view";
|
|
9669
|
+
$btn.setAttribute("title", (0, i18next.t)(key));
|
|
9670
|
+
$btn.setAttribute("data-i18n-title", key);
|
|
9671
|
+
$btn.setAttribute("aria-label", (0, i18next.t)(key));
|
|
9672
|
+
$btn.setAttribute("data-i18n-aria-label", key);
|
|
9673
|
+
}
|
|
9674
|
+
/************************************************************
|
|
9675
|
+
* Put `node_id` back in the middle of the viewport.
|
|
9676
|
+
*
|
|
9677
|
+
* Called AFTER the camera moved, never instead of moving it:
|
|
9678
|
+
* the zoom is what the reader asked for, the centring is what
|
|
9679
|
+
* keeps the thing they are reading under their eyes.
|
|
9680
|
+
*
|
|
9681
|
+
* Guarded on every side because it runs on every wheel notch:
|
|
9682
|
+
* a graph mid-rebuild, an anchor whose node went away with a
|
|
9683
|
+
* fold, a G6 version without `focusElement` -- none of those is
|
|
9684
|
+
* an error worth a log line per notch, they are all "there is
|
|
9685
|
+
* nothing to centre right now".
|
|
9686
|
+
************************************************************/
|
|
9687
|
+
function yui_graph_center_on(graph, node_id) {
|
|
9688
|
+
if (!graph || !node_id) return false;
|
|
9689
|
+
try {
|
|
9690
|
+
if (typeof graph.getElementData === "function" && !graph.getElementData(node_id)) return false;
|
|
9691
|
+
if (typeof graph.focusElement !== "function") return false;
|
|
9692
|
+
graph.focusElement(node_id);
|
|
9693
|
+
return true;
|
|
9694
|
+
} catch (e) {
|
|
9695
|
+
return false;
|
|
9696
|
+
}
|
|
9697
|
+
}
|
|
9698
|
+
/************************************************************
|
|
9501
9699
|
* A refresh button, for the graphs that offer one.
|
|
9502
9700
|
************************************************************/
|
|
9503
9701
|
function yui_graph_refresh_item(gobj, wide) {
|
|
@@ -10075,21 +10273,48 @@ var PRIVATE_DATA$11 = {
|
|
|
10075
10273
|
$layout_select: null,
|
|
10076
10274
|
$find_input: null,
|
|
10077
10275
|
$find_result: null,
|
|
10078
|
-
$find_count: null
|
|
10276
|
+
$find_count: null,
|
|
10277
|
+
anchor_id: "",
|
|
10278
|
+
anchor_path: "",
|
|
10279
|
+
anchor_state: "off",
|
|
10280
|
+
last_zoom: 1
|
|
10079
10281
|
};
|
|
10080
10282
|
var __gclass__$11 = null;
|
|
10081
10283
|
var __layout_registered__$1 = false;
|
|
10082
10284
|
/***************************************************************
|
|
10083
|
-
* Type
|
|
10084
|
-
|
|
10085
|
-
|
|
10285
|
+
* Type colours, ONE EXPLICIT PALETTE PER THEME and not a
|
|
10286
|
+
* derivation, for the same reason the tree keeps two sets of
|
|
10287
|
+
* CSS tokens: a colour that reads on white is not the same
|
|
10288
|
+
* colour lightened, and deriving one from the other made the
|
|
10289
|
+
* two views of one document disagree about what a string is.
|
|
10290
|
+
* These ARE the tree's tokens (`c_yui_json.css`), so the tree
|
|
10291
|
+
* and the graph now say green with the same green.
|
|
10292
|
+
*
|
|
10293
|
+
* The light values are darker than the `mx_json_viewer` ones
|
|
10294
|
+
* they came from, because those could not be read: `#FF8C00`
|
|
10295
|
+
* is mid-luminance, so it reaches at most **2.33:1 against any
|
|
10296
|
+
* background that exists** -- no card colour and no highlight
|
|
10297
|
+
* could have fixed it, only the colour itself. Orange and blue
|
|
10298
|
+
* moved until every type clears 4.5:1 on all five surfaces this
|
|
10299
|
+
* viewer paints (two light cards, the match chip, two dark
|
|
10300
|
+
* cards); red and green were already there.
|
|
10301
|
+
***************************************************************/
|
|
10302
|
+
var TYPE_COLORS_LIGHT = {
|
|
10086
10303
|
string: "#006000",
|
|
10087
|
-
number: "#
|
|
10088
|
-
boolean: "#
|
|
10089
|
-
null: "#
|
|
10304
|
+
number: "#b5330b",
|
|
10305
|
+
boolean: "#8C4D00",
|
|
10306
|
+
null: "#4359C6",
|
|
10090
10307
|
list: "#4C0099",
|
|
10091
10308
|
dict: "#4C0099"
|
|
10092
10309
|
};
|
|
10310
|
+
var TYPE_COLORS_DARK = {
|
|
10311
|
+
string: "#7ec87e",
|
|
10312
|
+
number: "#ff8a65",
|
|
10313
|
+
boolean: "#ffb74d",
|
|
10314
|
+
null: "#90a4f4",
|
|
10315
|
+
list: "#B799D6",
|
|
10316
|
+
dict: "#B799D6"
|
|
10317
|
+
};
|
|
10093
10318
|
/***************************************************************
|
|
10094
10319
|
* Group (card) colours. `fill` is the light-theme card; `tint`
|
|
10095
10320
|
* is the hue that fill hints at, used to derive the dark card —
|
|
@@ -10143,21 +10368,15 @@ function json_card_style(group, dark) {
|
|
|
10143
10368
|
};
|
|
10144
10369
|
}
|
|
10145
10370
|
/***************************************************************
|
|
10146
|
-
* A scalar's colour by type
|
|
10147
|
-
*
|
|
10148
|
-
*
|
|
10149
|
-
*
|
|
10150
|
-
*
|
|
10151
|
-
*
|
|
10152
|
-
* card (the worst type goes from 2.88:1 to 4.74:1) and washes the
|
|
10153
|
-
* six types toward one pastel, and past this point red, orange
|
|
10154
|
-
* and purple stop being tellable apart, which is the only reason
|
|
10155
|
-
* they are coloured at all.
|
|
10371
|
+
* A scalar's colour by type, straight out of the palette for
|
|
10372
|
+
* the theme. It used to be the light colour mixed with white,
|
|
10373
|
+
* which is how a document ended up green in one view and a
|
|
10374
|
+
* paler green in the other, and how the amount of white became
|
|
10375
|
+
* a knob nobody could set: enough of it to read on a dark card
|
|
10376
|
+
* washed the six types into one pastel.
|
|
10156
10377
|
***************************************************************/
|
|
10157
10378
|
function type_color(type, dark) {
|
|
10158
|
-
|
|
10159
|
-
if (!dark) return color;
|
|
10160
|
-
return `color-mix(in srgb, ${color} 40%, #ffffff)`;
|
|
10379
|
+
return (dark ? TYPE_COLORS_DARK : TYPE_COLORS_LIGHT)[type] || (dark ? "#e8eaed" : "black");
|
|
10161
10380
|
}
|
|
10162
10381
|
/******************************
|
|
10163
10382
|
* Framework Methods
|
|
@@ -10351,6 +10570,7 @@ function make_toolbar$3(gobj) {
|
|
|
10351
10570
|
]]);
|
|
10352
10571
|
let center_items = [];
|
|
10353
10572
|
center_items = yui_graph_camera_items(gobj, priv.graph, toolbar_wide);
|
|
10573
|
+
center_items.push(yui_graph_anchor_item(gobj, toolbar_wide));
|
|
10354
10574
|
center_items.push(yui_graph_refresh_item(gobj, toolbar_wide));
|
|
10355
10575
|
let current_layout = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "layout") || DEFAULT_LAYOUT;
|
|
10356
10576
|
let options = Object.keys(LAYOUTS$1).map(function(key) {
|
|
@@ -10433,6 +10653,15 @@ function build_graph$3(gobj) {
|
|
|
10433
10653
|
});
|
|
10434
10654
|
graph.on("aftertransform", () => {
|
|
10435
10655
|
yui_graph_update_zoom((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.graph);
|
|
10656
|
+
let z = priv.last_zoom;
|
|
10657
|
+
try {
|
|
10658
|
+
z = priv.graph.getZoom();
|
|
10659
|
+
} catch (e) {
|
|
10660
|
+
return;
|
|
10661
|
+
}
|
|
10662
|
+
if (z === priv.last_zoom) return;
|
|
10663
|
+
priv.last_zoom = z;
|
|
10664
|
+
if (priv.anchor_state === "on" && priv.anchor_id) yui_graph_center_on(priv.graph, priv.anchor_id);
|
|
10436
10665
|
});
|
|
10437
10666
|
graph.on(_antv_g6.CanvasEvent.CLICK, (evt) => {});
|
|
10438
10667
|
if (typeof ResizeObserver !== "undefined") {
|
|
@@ -10611,14 +10840,17 @@ function build_json_nodes(gobj, path, kw, nodes, edges, parent_id) {
|
|
|
10611
10840
|
card_matched = true;
|
|
10612
10841
|
priv.match_count++;
|
|
10613
10842
|
}
|
|
10614
|
-
|
|
10615
|
-
|
|
10616
|
-
|
|
10617
|
-
|
|
10618
|
-
|
|
10619
|
-
|
|
10843
|
+
if ((0, _yuneta_gobj_js.is_object)(value) && (0, _yuneta_gobj_js.json_object_size)(value) > 0 || (0, _yuneta_gobj_js.is_array)(value) && value.length > 0) {
|
|
10844
|
+
pending_complex.push({
|
|
10845
|
+
key,
|
|
10846
|
+
value
|
|
10847
|
+
});
|
|
10848
|
+
continue;
|
|
10849
|
+
}
|
|
10850
|
+
lines.push(build_cell_html(key, value, type, dark, matched));
|
|
10620
10851
|
}
|
|
10621
10852
|
let label = get_group_label(path) || (is_dict ? "{}" : "[]");
|
|
10853
|
+
if (pending_complex.length > 0) label += is_dict ? ` {${pending_complex.length}}` : ` [${pending_complex.length}]`;
|
|
10622
10854
|
let foldable = pending_complex.length > 0;
|
|
10623
10855
|
let folded = foldable && priv.collapsed.has(path);
|
|
10624
10856
|
let colors = json_card_style(is_dict ? GROUP_COLORS.dict : GROUP_COLORS.list, dark);
|
|
@@ -10694,13 +10926,22 @@ function load_json(gobj) {
|
|
|
10694
10926
|
let preserve_view = !!priv.pending_preserve_view;
|
|
10695
10927
|
priv.pending_preserve_view = false;
|
|
10696
10928
|
if (nodes.length > 0) {
|
|
10929
|
+
let root_id = nodes[0].id;
|
|
10697
10930
|
graph.setData({
|
|
10698
10931
|
nodes,
|
|
10699
10932
|
edges
|
|
10700
10933
|
});
|
|
10701
10934
|
graph.render().then(() => {
|
|
10702
|
-
|
|
10703
|
-
|
|
10935
|
+
reanchor$1(gobj);
|
|
10936
|
+
if (preserve_view) {
|
|
10937
|
+
yui_graph_update_zoom((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.graph);
|
|
10938
|
+
return;
|
|
10939
|
+
}
|
|
10940
|
+
Promise.resolve(graph.zoomTo(1)).then(() => {
|
|
10941
|
+
if (!yui_graph_center_on(graph, priv.anchor_id)) yui_graph_center_on(graph, root_id);
|
|
10942
|
+
priv.last_zoom = 1;
|
|
10943
|
+
yui_graph_update_zoom((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.graph);
|
|
10944
|
+
});
|
|
10704
10945
|
});
|
|
10705
10946
|
}
|
|
10706
10947
|
}
|
|
@@ -10963,13 +11204,79 @@ function ac_theme$3(gobj, event, kw, src) {
|
|
|
10963
11204
|
return 0;
|
|
10964
11205
|
}
|
|
10965
11206
|
/************************************************************
|
|
11207
|
+
* Every camera action ends the same way once an anchor is
|
|
11208
|
+
* set: the thing you were reading goes back to the middle.
|
|
11209
|
+
*
|
|
11210
|
+
* Chained on the promise G6 returns rather than run beside it
|
|
11211
|
+
* -- centring before the zoom has finished centres the OLD
|
|
11212
|
+
* view, and the reader watches the node slide away and come
|
|
11213
|
+
* back.
|
|
11214
|
+
************************************************************/
|
|
11215
|
+
function after_camera$2(gobj, moved) {
|
|
11216
|
+
let priv = gobj.priv;
|
|
11217
|
+
if (priv.anchor_state !== "on" || !priv.anchor_id) return;
|
|
11218
|
+
Promise.resolve(moved).then(function() {
|
|
11219
|
+
yui_graph_center_on(priv.graph, priv.anchor_id);
|
|
11220
|
+
}).catch(function() {});
|
|
11221
|
+
}
|
|
11222
|
+
/************************************************************
|
|
11223
|
+
* Point the anchor back at its node after a rebuild.
|
|
11224
|
+
*
|
|
11225
|
+
* Node ids are generated per build, so the id the reader
|
|
11226
|
+
* chose does not survive a refresh, a fold or a layout
|
|
11227
|
+
* change -- the PATH does, and it is what the anchor really
|
|
11228
|
+
* means: "this place in the document", not "this card".
|
|
11229
|
+
*
|
|
11230
|
+
* A path that is no longer drawn (its parent got folded)
|
|
11231
|
+
* releases the anchor rather than leaving a button that says
|
|
11232
|
+
* it is locked onto something invisible.
|
|
11233
|
+
************************************************************/
|
|
11234
|
+
function reanchor$1(gobj) {
|
|
11235
|
+
let priv = gobj.priv;
|
|
11236
|
+
if (priv.anchor_state !== "on" || !priv.anchor_path) return;
|
|
11237
|
+
let found = "";
|
|
11238
|
+
try {
|
|
11239
|
+
for (let node of priv.graph.getNodeData()) if (node && node.data && node.data.path === priv.anchor_path) {
|
|
11240
|
+
found = node.id;
|
|
11241
|
+
break;
|
|
11242
|
+
}
|
|
11243
|
+
} catch (e) {
|
|
11244
|
+
return;
|
|
11245
|
+
}
|
|
11246
|
+
priv.anchor_id = found;
|
|
11247
|
+
if (!found) {
|
|
11248
|
+
priv.anchor_state = "off";
|
|
11249
|
+
priv.anchor_path = "";
|
|
11250
|
+
}
|
|
11251
|
+
yui_graph_update_anchor((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.anchor_state);
|
|
11252
|
+
}
|
|
11253
|
+
/************************************************************
|
|
11254
|
+
* Arm the anchor, or let it go.
|
|
11255
|
+
*
|
|
11256
|
+
* Three states and one button, so a press ADVANCES: with no
|
|
11257
|
+
* target it starts waiting for one, and with a target it
|
|
11258
|
+
* releases. Pressing it while it waits is a cancel, which is
|
|
11259
|
+
* what somebody who pressed it by mistake will try.
|
|
11260
|
+
************************************************************/
|
|
11261
|
+
function ac_toggle_anchor$2(gobj, event, kw, src) {
|
|
11262
|
+
let priv = gobj.priv;
|
|
11263
|
+
if (priv.anchor_state === "off") priv.anchor_state = "arming";
|
|
11264
|
+
else {
|
|
11265
|
+
priv.anchor_state = "off";
|
|
11266
|
+
priv.anchor_id = "";
|
|
11267
|
+
priv.anchor_path = "";
|
|
11268
|
+
}
|
|
11269
|
+
yui_graph_update_anchor((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.anchor_state);
|
|
11270
|
+
return 0;
|
|
11271
|
+
}
|
|
11272
|
+
/************************************************************
|
|
10966
11273
|
*
|
|
10967
11274
|
************************************************************/
|
|
10968
11275
|
function ac_zoom_in$2(gobj, event, kw, src) {
|
|
10969
11276
|
let graph = gobj.priv.graph;
|
|
10970
11277
|
if (graph) {
|
|
10971
11278
|
let z = graph.getZoom();
|
|
10972
|
-
graph.zoomTo(z * 1.2);
|
|
11279
|
+
after_camera$2(gobj, graph.zoomTo(z * 1.2));
|
|
10973
11280
|
}
|
|
10974
11281
|
return 0;
|
|
10975
11282
|
}
|
|
@@ -10980,7 +11287,7 @@ function ac_zoom_out$2(gobj, event, kw, src) {
|
|
|
10980
11287
|
let graph = gobj.priv.graph;
|
|
10981
11288
|
if (graph) {
|
|
10982
11289
|
let z = graph.getZoom();
|
|
10983
|
-
graph.zoomTo(z * .8);
|
|
11290
|
+
after_camera$2(gobj, graph.zoomTo(z * .8));
|
|
10984
11291
|
}
|
|
10985
11292
|
return 0;
|
|
10986
11293
|
}
|
|
@@ -10988,10 +11295,14 @@ function ac_zoom_out$2(gobj, event, kw, src) {
|
|
|
10988
11295
|
*
|
|
10989
11296
|
************************************************************/
|
|
10990
11297
|
function ac_zoom_reset$2(gobj, event, kw, src) {
|
|
10991
|
-
let
|
|
11298
|
+
let priv = gobj.priv;
|
|
11299
|
+
let graph = priv.graph;
|
|
10992
11300
|
if (graph && graph.rendered) {
|
|
10993
|
-
graph.zoomTo(1);
|
|
10994
|
-
|
|
11301
|
+
if (priv.anchor_state === "on" && priv.anchor_id) after_camera$2(gobj, graph.zoomTo(1));
|
|
11302
|
+
else {
|
|
11303
|
+
graph.zoomTo(1);
|
|
11304
|
+
graph.translateTo([0, 0]);
|
|
11305
|
+
}
|
|
10995
11306
|
}
|
|
10996
11307
|
return 0;
|
|
10997
11308
|
}
|
|
@@ -11010,12 +11321,21 @@ function ac_center$2(gobj, event, kw, src) {
|
|
|
11010
11321
|
*
|
|
11011
11322
|
************************************************************/
|
|
11012
11323
|
function ac_node_click$4(gobj, event, kw, src) {
|
|
11013
|
-
let
|
|
11324
|
+
let priv = gobj.priv;
|
|
11325
|
+
let graph = priv.graph;
|
|
11014
11326
|
let node_id = kw.evt.target.id;
|
|
11015
11327
|
let nodedata;
|
|
11016
11328
|
try {
|
|
11017
11329
|
nodedata = graph.getNodeData(node_id);
|
|
11018
11330
|
} catch (e) {}
|
|
11331
|
+
if (priv.anchor_state === "arming") {
|
|
11332
|
+
priv.anchor_id = node_id;
|
|
11333
|
+
priv.anchor_path = nodedata && nodedata.data ? nodedata.data.path : "";
|
|
11334
|
+
priv.anchor_state = "on";
|
|
11335
|
+
yui_graph_update_anchor((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.anchor_state);
|
|
11336
|
+
yui_graph_center_on(graph, node_id);
|
|
11337
|
+
return 0;
|
|
11338
|
+
}
|
|
11019
11339
|
if (nodedata && nodedata.data) (0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_JSON_ITEM_CLICKED", {
|
|
11020
11340
|
path: nodedata.data.path,
|
|
11021
11341
|
id: node_id
|
|
@@ -11149,6 +11469,11 @@ function create_gclass$11(gclass_name) {
|
|
|
11149
11469
|
ac_node_click$4,
|
|
11150
11470
|
null
|
|
11151
11471
|
],
|
|
11472
|
+
[
|
|
11473
|
+
"EV_TOGGLE_ANCHOR",
|
|
11474
|
+
ac_toggle_anchor$2,
|
|
11475
|
+
null
|
|
11476
|
+
],
|
|
11152
11477
|
[
|
|
11153
11478
|
"EV_RESIZE",
|
|
11154
11479
|
ac_resize$2,
|
|
@@ -11179,6 +11504,7 @@ function create_gclass$11(gclass_name) {
|
|
|
11179
11504
|
["EV_ZOOM_RESET", 0],
|
|
11180
11505
|
["EV_CENTER", 0],
|
|
11181
11506
|
["EV_NODE_CLICK", 0],
|
|
11507
|
+
["EV_TOGGLE_ANCHOR", 0],
|
|
11182
11508
|
["EV_RESIZE", 0],
|
|
11183
11509
|
["EV_SHOW", 0],
|
|
11184
11510
|
["EV_HIDE", 0],
|
|
@@ -12700,7 +13026,11 @@ var PRIVATE_DATA$9 = {
|
|
|
12700
13026
|
node_by_id: null,
|
|
12701
13027
|
collapse_state: null,
|
|
12702
13028
|
resize_observer: null,
|
|
12703
|
-
_resize_raf: 0
|
|
13029
|
+
_resize_raf: 0,
|
|
13030
|
+
anchor_id: "",
|
|
13031
|
+
anchor_name: "",
|
|
13032
|
+
anchor_state: "off",
|
|
13033
|
+
last_zoom: 1
|
|
12704
13034
|
};
|
|
12705
13035
|
var __gclass__$9 = null;
|
|
12706
13036
|
var __layout_registered__ = false;
|
|
@@ -13093,6 +13423,7 @@ function make_toolbar$1(gobj) {
|
|
|
13093
13423
|
let center_items = [];
|
|
13094
13424
|
let right_items = [];
|
|
13095
13425
|
center_items = yui_graph_camera_items(gobj, priv.graph, toolbar_wide);
|
|
13426
|
+
center_items.push(yui_graph_anchor_item(gobj, toolbar_wide));
|
|
13096
13427
|
center_items.push(yui_graph_refresh_item(gobj, toolbar_wide));
|
|
13097
13428
|
let options = [];
|
|
13098
13429
|
for (let key of Object.keys(LAYOUTS)) {
|
|
@@ -13168,6 +13499,15 @@ function build_graph$2(gobj) {
|
|
|
13168
13499
|
if (priv.theme) graph.setTheme(priv.theme);
|
|
13169
13500
|
graph.on("aftertransform", () => {
|
|
13170
13501
|
yui_graph_update_zoom((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.graph);
|
|
13502
|
+
let z = priv.last_zoom;
|
|
13503
|
+
try {
|
|
13504
|
+
z = priv.graph.getZoom();
|
|
13505
|
+
} catch (e) {
|
|
13506
|
+
return;
|
|
13507
|
+
}
|
|
13508
|
+
if (z === priv.last_zoom) return;
|
|
13509
|
+
priv.last_zoom = z;
|
|
13510
|
+
if (priv.anchor_state === "on" && priv.anchor_id) yui_graph_center_on(priv.graph, priv.anchor_id);
|
|
13171
13511
|
});
|
|
13172
13512
|
graph.on(_antv_g6.NodeEvent.CLICK, (evt) => {
|
|
13173
13513
|
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_NODE_CLICK", { evt }, gobj);
|
|
@@ -13455,13 +13795,23 @@ function load_tree(gobj) {
|
|
|
13455
13795
|
let edges = [];
|
|
13456
13796
|
build_gobj_nodes(gobj, yuno, nodes, edges, null, true, layout_cfg.compact);
|
|
13457
13797
|
if (nodes.length > 0) {
|
|
13798
|
+
let root_id = nodes[0].id;
|
|
13458
13799
|
graph.setData({
|
|
13459
13800
|
nodes,
|
|
13460
13801
|
edges
|
|
13461
13802
|
});
|
|
13462
13803
|
graph.render().then(() => {
|
|
13463
|
-
|
|
13464
|
-
|
|
13804
|
+
reanchor(gobj);
|
|
13805
|
+
if (anchor) {
|
|
13806
|
+
restore_view_anchored(gobj, anchor);
|
|
13807
|
+
return;
|
|
13808
|
+
}
|
|
13809
|
+
if (preserve_view) return;
|
|
13810
|
+
Promise.resolve(graph.zoomTo(1)).then(() => {
|
|
13811
|
+
if (!yui_graph_center_on(graph, priv.anchor_id)) yui_graph_center_on(graph, root_id);
|
|
13812
|
+
priv.last_zoom = 1;
|
|
13813
|
+
yui_graph_update_zoom((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.graph);
|
|
13814
|
+
});
|
|
13465
13815
|
});
|
|
13466
13816
|
}
|
|
13467
13817
|
}
|
|
@@ -13963,13 +14313,77 @@ function ac_collapse_all(gobj, event, kw, src) {
|
|
|
13963
14313
|
return 0;
|
|
13964
14314
|
}
|
|
13965
14315
|
/************************************************************
|
|
14316
|
+
* Every camera action ends the same way once an anchor is
|
|
14317
|
+
* set: the thing you were reading goes back to the middle.
|
|
14318
|
+
*
|
|
14319
|
+
* Chained on the promise G6 returns rather than run beside it
|
|
14320
|
+
* -- centring before the zoom has finished centres the OLD
|
|
14321
|
+
* view, and the reader watches the node slide away and come
|
|
14322
|
+
* back.
|
|
14323
|
+
************************************************************/
|
|
14324
|
+
function after_camera$1(gobj, moved) {
|
|
14325
|
+
let priv = gobj.priv;
|
|
14326
|
+
if (priv.anchor_state !== "on" || !priv.anchor_id) return;
|
|
14327
|
+
Promise.resolve(moved).then(function() {
|
|
14328
|
+
yui_graph_center_on(priv.graph, priv.anchor_id);
|
|
14329
|
+
}).catch(function() {});
|
|
14330
|
+
}
|
|
14331
|
+
/************************************************************
|
|
14332
|
+
* Point the anchor back at its node after a rebuild.
|
|
14333
|
+
*
|
|
14334
|
+
* Node ids are generated per build, so the id the reader chose
|
|
14335
|
+
* does not survive an expand, a collapse or a refresh -- the
|
|
14336
|
+
* `full_name` does, and it is what the anchor really means:
|
|
14337
|
+
* "this gobj", not "this card".
|
|
14338
|
+
*
|
|
14339
|
+
* A gobj no longer drawn (its branch got collapsed, or it went
|
|
14340
|
+
* away) releases the anchor rather than leaving a button that
|
|
14341
|
+
* claims to be locked onto something invisible.
|
|
14342
|
+
************************************************************/
|
|
14343
|
+
function reanchor(gobj) {
|
|
14344
|
+
let priv = gobj.priv;
|
|
14345
|
+
if (priv.anchor_state !== "on" || !priv.anchor_name) return;
|
|
14346
|
+
let found = "";
|
|
14347
|
+
if (priv.node_by_id) {
|
|
14348
|
+
for (let id of Object.keys(priv.node_by_id)) if (priv.node_by_id[id].full_name === priv.anchor_name) {
|
|
14349
|
+
found = id;
|
|
14350
|
+
break;
|
|
14351
|
+
}
|
|
14352
|
+
}
|
|
14353
|
+
priv.anchor_id = found;
|
|
14354
|
+
if (!found) {
|
|
14355
|
+
priv.anchor_state = "off";
|
|
14356
|
+
priv.anchor_name = "";
|
|
14357
|
+
}
|
|
14358
|
+
yui_graph_update_anchor((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.anchor_state);
|
|
14359
|
+
}
|
|
14360
|
+
/************************************************************
|
|
14361
|
+
* Arm the anchor, or let it go.
|
|
14362
|
+
*
|
|
14363
|
+
* Three states and one button, so a press ADVANCES: with no
|
|
14364
|
+
* target it starts waiting for one, and with a target it
|
|
14365
|
+
* releases. Pressing it while it waits is a cancel, which is
|
|
14366
|
+
* what somebody who pressed it by mistake will try.
|
|
14367
|
+
************************************************************/
|
|
14368
|
+
function ac_toggle_anchor$1(gobj, event, kw, src) {
|
|
14369
|
+
let priv = gobj.priv;
|
|
14370
|
+
if (priv.anchor_state === "off") priv.anchor_state = "arming";
|
|
14371
|
+
else {
|
|
14372
|
+
priv.anchor_state = "off";
|
|
14373
|
+
priv.anchor_id = "";
|
|
14374
|
+
priv.anchor_name = "";
|
|
14375
|
+
}
|
|
14376
|
+
yui_graph_update_anchor((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.anchor_state);
|
|
14377
|
+
return 0;
|
|
14378
|
+
}
|
|
14379
|
+
/************************************************************
|
|
13966
14380
|
*
|
|
13967
14381
|
************************************************************/
|
|
13968
14382
|
function ac_zoom_in$1(gobj, event, kw, src) {
|
|
13969
14383
|
let graph = gobj.priv.graph;
|
|
13970
14384
|
if (graph) {
|
|
13971
14385
|
let z = graph.getZoom();
|
|
13972
|
-
graph.zoomTo(z * 1.2);
|
|
14386
|
+
after_camera$1(gobj, graph.zoomTo(z * 1.2));
|
|
13973
14387
|
}
|
|
13974
14388
|
return 0;
|
|
13975
14389
|
}
|
|
@@ -13980,7 +14394,7 @@ function ac_zoom_out$1(gobj, event, kw, src) {
|
|
|
13980
14394
|
let graph = gobj.priv.graph;
|
|
13981
14395
|
if (graph) {
|
|
13982
14396
|
let z = graph.getZoom();
|
|
13983
|
-
graph.zoomTo(z * .8);
|
|
14397
|
+
after_camera$1(gobj, graph.zoomTo(z * .8));
|
|
13984
14398
|
}
|
|
13985
14399
|
return 0;
|
|
13986
14400
|
}
|
|
@@ -13988,10 +14402,14 @@ function ac_zoom_out$1(gobj, event, kw, src) {
|
|
|
13988
14402
|
*
|
|
13989
14403
|
************************************************************/
|
|
13990
14404
|
function ac_zoom_reset$1(gobj, event, kw, src) {
|
|
13991
|
-
let
|
|
14405
|
+
let priv = gobj.priv;
|
|
14406
|
+
let graph = priv.graph;
|
|
13992
14407
|
if (graph && graph.rendered) {
|
|
13993
|
-
graph.zoomTo(1);
|
|
13994
|
-
|
|
14408
|
+
if (priv.anchor_state === "on" && priv.anchor_id) after_camera$1(gobj, graph.zoomTo(1));
|
|
14409
|
+
else {
|
|
14410
|
+
graph.zoomTo(1);
|
|
14411
|
+
graph.translateTo([0, 0]);
|
|
14412
|
+
}
|
|
13995
14413
|
}
|
|
13996
14414
|
return 0;
|
|
13997
14415
|
}
|
|
@@ -14019,6 +14437,14 @@ function ac_node_click$3(gobj, event, kw, src) {
|
|
|
14019
14437
|
if (nd && nd.data) node_data = nd.data;
|
|
14020
14438
|
} catch (e) {}
|
|
14021
14439
|
if (!node_data) return 0;
|
|
14440
|
+
if (priv.anchor_state === "arming") {
|
|
14441
|
+
priv.anchor_id = node_id;
|
|
14442
|
+
priv.anchor_name = node_data.full_name || "";
|
|
14443
|
+
priv.anchor_state = "on";
|
|
14444
|
+
yui_graph_update_anchor((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.anchor_state);
|
|
14445
|
+
yui_graph_center_on(graph, node_id);
|
|
14446
|
+
return 0;
|
|
14447
|
+
}
|
|
14022
14448
|
let client_x = kw.evt && kw.evt.client ? kw.evt.client.x : 0;
|
|
14023
14449
|
let client_y = kw.evt && kw.evt.client ? kw.evt.client.y : 0;
|
|
14024
14450
|
show_popover(gobj, node_data, client_x, client_y);
|
|
@@ -14099,6 +14525,7 @@ function create_gclass$9(gclass_name) {
|
|
|
14099
14525
|
["EV_ZOOM_RESET", 0],
|
|
14100
14526
|
["EV_CENTER", 0],
|
|
14101
14527
|
["EV_NODE_CLICK", 0],
|
|
14528
|
+
["EV_TOGGLE_ANCHOR", 0],
|
|
14102
14529
|
["EV_RESIZE", 0],
|
|
14103
14530
|
["EV_SHOW", 0],
|
|
14104
14531
|
["EV_HIDE", 0]
|
|
@@ -14158,6 +14585,11 @@ function create_gclass$9(gclass_name) {
|
|
|
14158
14585
|
ac_node_click$3,
|
|
14159
14586
|
null
|
|
14160
14587
|
],
|
|
14588
|
+
[
|
|
14589
|
+
"EV_TOGGLE_ANCHOR",
|
|
14590
|
+
ac_toggle_anchor$1,
|
|
14591
|
+
null
|
|
14592
|
+
],
|
|
14161
14593
|
[
|
|
14162
14594
|
"EV_RESIZE",
|
|
14163
14595
|
ac_resize$1,
|
|
@@ -20720,131 +21152,6 @@ function register_c_yui_treedb_topic_with_form() {
|
|
|
20720
21152
|
return create_gclass$5(GCLASS_NAME$5);
|
|
20721
21153
|
}
|
|
20722
21154
|
//#endregion
|
|
20723
|
-
//#region src/lib_graph.js
|
|
20724
|
-
/***********************************************************************
|
|
20725
|
-
* lib_graph.js
|
|
20726
|
-
*
|
|
20727
|
-
* Utilities for graph, using Bulma
|
|
20728
|
-
*
|
|
20729
|
-
* Copyright (c) 2025, ArtGins.
|
|
20730
|
-
* All Rights Reserved.
|
|
20731
|
-
***********************************************************************/
|
|
20732
|
-
/************************************************************
|
|
20733
|
-
* Add class to elements selected
|
|
20734
|
-
************************************************************/
|
|
20735
|
-
function addClasses($container, selector, ...classNames) {
|
|
20736
|
-
$container.querySelectorAll(selector).forEach((el) => el.classList.add(...classNames));
|
|
20737
|
-
}
|
|
20738
|
-
/************************************************************
|
|
20739
|
-
* Remove class from elements selected
|
|
20740
|
-
************************************************************/
|
|
20741
|
-
function removeClasses($container, selector, ...classNames) {
|
|
20742
|
-
$container.querySelectorAll(selector).forEach((el) => el.classList.remove(...classNames));
|
|
20743
|
-
}
|
|
20744
|
-
/************************************************************
|
|
20745
|
-
* Toggle class to elements selected
|
|
20746
|
-
************************************************************/
|
|
20747
|
-
function toggleClasses($container, selector, ...classNames) {
|
|
20748
|
-
$container.querySelectorAll(selector).forEach((el) => classNames.forEach((cls) => el.classList.toggle(cls)));
|
|
20749
|
-
}
|
|
20750
|
-
/************************************************************
|
|
20751
|
-
* Remove child elements
|
|
20752
|
-
************************************************************/
|
|
20753
|
-
function removeChildElements($element) {
|
|
20754
|
-
$element.innerHTML = "";
|
|
20755
|
-
}
|
|
20756
|
-
/************************************************************
|
|
20757
|
-
* Disable/Enable elements
|
|
20758
|
-
************************************************************/
|
|
20759
|
-
function disableElements($container, selector) {
|
|
20760
|
-
$container.querySelectorAll(selector).forEach((el) => {
|
|
20761
|
-
el.setAttribute("disabled", "");
|
|
20762
|
-
});
|
|
20763
|
-
}
|
|
20764
|
-
function enableElements($container, selector) {
|
|
20765
|
-
$container.querySelectorAll(selector).forEach((el) => {
|
|
20766
|
-
if (el.hasAttribute("disabled")) el.removeAttribute("disabled");
|
|
20767
|
-
});
|
|
20768
|
-
}
|
|
20769
|
-
/************************************************************
|
|
20770
|
-
* Set or reset the color of 'enable submit action' state
|
|
20771
|
-
* Color green
|
|
20772
|
-
************************************************************/
|
|
20773
|
-
function set_submit_state($container, selector, set) {
|
|
20774
|
-
if (set) addClasses($container, selector, "color_submit_state");
|
|
20775
|
-
else removeClasses($container, selector, "color_submit_state");
|
|
20776
|
-
}
|
|
20777
|
-
/************************************************************
|
|
20778
|
-
* Set or reset the color of 'enable cancel action' state
|
|
20779
|
-
* Color red
|
|
20780
|
-
************************************************************/
|
|
20781
|
-
function set_cancel_state($container, selector, set) {
|
|
20782
|
-
if (set) addClasses($container, selector, "color_cancel_state");
|
|
20783
|
-
else removeClasses($container, selector, "color_cancel_state");
|
|
20784
|
-
}
|
|
20785
|
-
/************************************************************
|
|
20786
|
-
* Set or reset the color of 'enable actived' state
|
|
20787
|
-
* Color orange
|
|
20788
|
-
************************************************************/
|
|
20789
|
-
function set_active_state($container, selector, set) {
|
|
20790
|
-
if (set) addClasses($container, selector, "color_active_state");
|
|
20791
|
-
else removeClasses($container, selector, "color_active_state");
|
|
20792
|
-
}
|
|
20793
|
-
/************************************************************
|
|
20794
|
-
* Set or reset the 'pressed' state of a TOGGLE.
|
|
20795
|
-
*
|
|
20796
|
-
* Not one of the colours above: each of those names a KIND of
|
|
20797
|
-
* action (create, pending, history, destructive), and a toggle
|
|
20798
|
-
* is not an action -- it is a state the control is IN. So it
|
|
20799
|
-
* looks pressed instead of changing category, which also keeps
|
|
20800
|
-
* it out of the way of a neighbour wearing the same hue.
|
|
20801
|
-
************************************************************/
|
|
20802
|
-
function set_pressed_state($container, selector, set) {
|
|
20803
|
-
if (set) addClasses($container, selector, "pressed_state");
|
|
20804
|
-
else removeClasses($container, selector, "pressed_state");
|
|
20805
|
-
}
|
|
20806
|
-
/**
|
|
20807
|
-
* Returns a smart stroke color in `rgba()` format based on:
|
|
20808
|
-
* - The given fill color (hex or rgb[a])
|
|
20809
|
-
* - The current theme ('light' or 'dark')
|
|
20810
|
-
* - The adjustment factor (default 0.2)
|
|
20811
|
-
*
|
|
20812
|
-
* @param {string} fillColor - Fill color string
|
|
20813
|
-
* @param {string} theme - 'light' or 'dark'
|
|
20814
|
-
* @param {number} factor - Lighten/darken factor (default 0.2)
|
|
20815
|
-
* @returns {string} rgba(r, g, b, a)
|
|
20816
|
-
*/
|
|
20817
|
-
function getStrokeColor(fillColor, theme = "light", factor = .2) {
|
|
20818
|
-
let r, g, b, a = 1;
|
|
20819
|
-
if (fillColor.startsWith("#")) {
|
|
20820
|
-
let hex = fillColor.slice(1);
|
|
20821
|
-
if (hex.length === 3 || hex.length === 4) hex = hex.split("").map((c) => c + c).join("");
|
|
20822
|
-
if (hex.length === 6) {
|
|
20823
|
-
r = parseInt(hex.slice(0, 2), 16);
|
|
20824
|
-
g = parseInt(hex.slice(2, 4), 16);
|
|
20825
|
-
b = parseInt(hex.slice(4, 6), 16);
|
|
20826
|
-
} else if (hex.length === 8) {
|
|
20827
|
-
r = parseInt(hex.slice(0, 2), 16);
|
|
20828
|
-
g = parseInt(hex.slice(2, 4), 16);
|
|
20829
|
-
b = parseInt(hex.slice(4, 6), 16);
|
|
20830
|
-
a = parseInt(hex.slice(6, 8), 16) / 255;
|
|
20831
|
-
} else throw new Error("Unsupported hex format");
|
|
20832
|
-
} else if (fillColor.startsWith("rgb")) {
|
|
20833
|
-
const match = fillColor.match(/rgba?\(\s*(\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\s*\)/);
|
|
20834
|
-
if (!match) throw new Error("Invalid rgb()/rgba() format.");
|
|
20835
|
-
r = parseInt(match[1], 10);
|
|
20836
|
-
g = parseInt(match[2], 10);
|
|
20837
|
-
b = parseInt(match[3], 10);
|
|
20838
|
-
if (match[4] !== void 0) a = parseFloat(match[4]);
|
|
20839
|
-
} else throw new Error("Unsupported color format.");
|
|
20840
|
-
const luminance = (.299 * r + .587 * g + .114 * b) / 255;
|
|
20841
|
-
let lighten;
|
|
20842
|
-
if (theme === "dark") lighten = luminance < .7;
|
|
20843
|
-
else lighten = luminance < .3;
|
|
20844
|
-
const adjust = (v) => lighten ? Math.min(255, Math.floor(v + (255 - v) * factor)) : Math.max(0, Math.floor(v * (1 - factor)));
|
|
20845
|
-
return `rgba(${adjust(r)}, ${adjust(g)}, ${adjust(b)}, ${a.toFixed(3)})`;
|
|
20846
|
-
}
|
|
20847
|
-
//#endregion
|
|
20848
21155
|
//#region src/treedb_node_label.js
|
|
20849
21156
|
/***********************************************************************
|
|
20850
21157
|
* treedb_node_label.js
|
|
@@ -47712,6 +48019,13 @@ var ICONS = `<svg xmlns="http://www.w3.org/2000/svg">
|
|
|
47712
48019
|
stroke="currentColor" stroke-width="40"
|
|
47713
48020
|
stroke-dasharray="59 36"/>
|
|
47714
48021
|
</symbol>
|
|
48022
|
+
<!-- location-crosshairs (anchor the camera on one element).
|
|
48023
|
+
The SAME path as the DOM toolbars and their yi-location-crosshairs
|
|
48024
|
+
mask: the two toolbars sit in one console, and a control that
|
|
48025
|
+
does one thing has to be drawn one way. -->
|
|
48026
|
+
<symbol id="g6-icon-anchor" viewBox="0 0 576 512">
|
|
48027
|
+
<path fill="currentColor" d="M288-16c17.7 0 32 14.3 32 32l0 18.3c98.1 14 175.7 91.6 189.7 189.7l18.3 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-18.3 0c-14 98.1-91.6 175.7-189.7 189.7l0 18.3c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-18.3C157.9 463.7 80.3 386.1 66.3 288L48 288c-17.7 0-32-14.3-32-32s14.3-32 32-32l18.3 0C80.3 125.9 157.9 48.3 256 34.3L256 16c0-17.7 14.3-32 32-32zM128 256a160 160 0 1 0 320 0 160 160 0 1 0 -320 0zm160-96a96 96 0 1 1 0 192 96 96 0 1 1 0-192z"/>
|
|
48028
|
+
</symbol>
|
|
47715
48029
|
<!-- floppy-disk (save) -->
|
|
47716
48030
|
<symbol id="g6-icon-save" viewBox="0 0 448 512">
|
|
47717
48031
|
<path fill="currentColor" d="M64 32C28.7 32 0 60.7 0 96L0 416c0 35.3 28.7 64 64 64l320 0c35.3 0 64-28.7 64-64l0-242.7c0-17-6.7-33.3-18.7-45.3L352 50.7C340 38.7 323.7 32 306.7 32L64 32zm32 96c0-17.7 14.3-32 32-32l160 0c17.7 0 32 14.3 32 32l0 64c0 17.7-14.3 32-32 32l-160 0c-17.7 0-32-14.3-32-32l0-64zM224 288a64 64 0 1 1 0 128 64 64 0 1 1 0-128z"/>
|
|
@@ -47905,6 +48219,9 @@ var attrs_table$4 = [
|
|
|
47905
48219
|
(0, _yuneta_gobj_js.SDATA_END)()
|
|
47906
48220
|
];
|
|
47907
48221
|
var PRIVATE_DATA$4 = {
|
|
48222
|
+
anchor_id: "",
|
|
48223
|
+
anchor_state: "off",
|
|
48224
|
+
last_zoom: 1,
|
|
47908
48225
|
_xy: 100,
|
|
47909
48226
|
_edge_seq: 0,
|
|
47910
48227
|
_history_paused: false,
|
|
@@ -48201,6 +48518,15 @@ function configure_events(gobj) {
|
|
|
48201
48518
|
update_node_icon_position(gobj);
|
|
48202
48519
|
update_link_icon_position(gobj);
|
|
48203
48520
|
update_zoom_readout(gobj);
|
|
48521
|
+
let z = priv.last_zoom;
|
|
48522
|
+
try {
|
|
48523
|
+
z = priv.graph.getZoom();
|
|
48524
|
+
} catch (e) {
|
|
48525
|
+
return;
|
|
48526
|
+
}
|
|
48527
|
+
if (z === priv.last_zoom) return;
|
|
48528
|
+
priv.last_zoom = z;
|
|
48529
|
+
if (priv.anchor_state === "on" && priv.anchor_id) yui_graph_center_on(priv.graph, priv.anchor_id);
|
|
48204
48530
|
});
|
|
48205
48531
|
priv._on_language_changed = () => {
|
|
48206
48532
|
update_toolbar(gobj);
|
|
@@ -48438,6 +48764,12 @@ function configure_toolbar(gobj) {
|
|
|
48438
48764
|
value: "reset",
|
|
48439
48765
|
className: "EV_ZOOM_RESET",
|
|
48440
48766
|
title: (0, i18next.t)("actual size")
|
|
48767
|
+
},
|
|
48768
|
+
{
|
|
48769
|
+
id: "g6-icon-anchor",
|
|
48770
|
+
value: "anchor",
|
|
48771
|
+
className: "EV_TOGGLE_ANCHOR" + (priv.anchor_state !== "off" ? " pressed_state" : "") + (priv.anchor_state === "arming" ? " color_pending_state" : ""),
|
|
48772
|
+
title: anchor_title(priv.anchor_state)
|
|
48441
48773
|
}
|
|
48442
48774
|
];
|
|
48443
48775
|
if (priv.edit_mode) items.push({
|
|
@@ -48479,6 +48811,9 @@ function configure_toolbar(gobj) {
|
|
|
48479
48811
|
case "reset":
|
|
48480
48812
|
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_ZOOM_RESET", {}, gobj);
|
|
48481
48813
|
break;
|
|
48814
|
+
case "anchor":
|
|
48815
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_TOGGLE_ANCHOR", {}, gobj);
|
|
48816
|
+
break;
|
|
48482
48817
|
case "auto-fit":
|
|
48483
48818
|
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_AUTO_FIT", {}, gobj);
|
|
48484
48819
|
break;
|
|
@@ -49489,12 +49824,12 @@ function graph_find_nodes(gobj, term) {
|
|
|
49489
49824
|
async function graph_zoom_in(gobj) {
|
|
49490
49825
|
let graph = gobj.priv.graph;
|
|
49491
49826
|
const z = graph.getZoom();
|
|
49492
|
-
|
|
49827
|
+
after_camera(gobj, graph.zoomTo(z * 1.1));
|
|
49493
49828
|
}
|
|
49494
49829
|
async function graph_zoom_out(gobj) {
|
|
49495
49830
|
let graph = gobj.priv.graph;
|
|
49496
49831
|
const z = graph.getZoom();
|
|
49497
|
-
|
|
49832
|
+
after_camera(gobj, graph.zoomTo(z * .9));
|
|
49498
49833
|
}
|
|
49499
49834
|
async function graph_render(gobj) {
|
|
49500
49835
|
await gobj.priv.graph.render();
|
|
@@ -52495,7 +52830,56 @@ function ac_zoom_out(gobj, event, kw, src) {
|
|
|
52495
52830
|
return 0;
|
|
52496
52831
|
}
|
|
52497
52832
|
function ac_zoom_reset(gobj, event, kw, src) {
|
|
52498
|
-
gobj.priv
|
|
52833
|
+
let priv = gobj.priv;
|
|
52834
|
+
let graph = priv.graph;
|
|
52835
|
+
if (priv.anchor_state === "on" && priv.anchor_id) after_camera(gobj, graph.zoomTo(1));
|
|
52836
|
+
else graph.zoomTo(1);
|
|
52837
|
+
return 0;
|
|
52838
|
+
}
|
|
52839
|
+
/************************************************************
|
|
52840
|
+
* What the anchor button says it will do next.
|
|
52841
|
+
************************************************************/
|
|
52842
|
+
function anchor_title(state) {
|
|
52843
|
+
if (state === "arming") return (0, i18next.t)("click the element to centre on");
|
|
52844
|
+
if (state === "on") return (0, i18next.t)("centred: click to release");
|
|
52845
|
+
return (0, i18next.t)("anchor view");
|
|
52846
|
+
}
|
|
52847
|
+
/************************************************************
|
|
52848
|
+
* Every camera action ends the same way once an anchor is
|
|
52849
|
+
* set: the thing you were reading goes back to the middle.
|
|
52850
|
+
*
|
|
52851
|
+
* Chained on the promise G6 returns rather than run beside it
|
|
52852
|
+
* -- centring before the zoom has finished centres the OLD
|
|
52853
|
+
* view, and the reader watches the node slide away and come
|
|
52854
|
+
* back.
|
|
52855
|
+
************************************************************/
|
|
52856
|
+
function after_camera(gobj, moved) {
|
|
52857
|
+
let priv = gobj.priv;
|
|
52858
|
+
if (priv.anchor_state !== "on" || !priv.anchor_id) return;
|
|
52859
|
+
Promise.resolve(moved).then(function() {
|
|
52860
|
+
yui_graph_center_on(priv.graph, priv.anchor_id);
|
|
52861
|
+
}).catch(function() {});
|
|
52862
|
+
}
|
|
52863
|
+
/************************************************************
|
|
52864
|
+
* Arm the anchor, or let it go.
|
|
52865
|
+
*
|
|
52866
|
+
* Three states and one button, so a press ADVANCES: with no
|
|
52867
|
+
* target it starts waiting for one, and with a target it
|
|
52868
|
+
* releases. Pressing it while it waits is a cancel, which is
|
|
52869
|
+
* what somebody who pressed it by mistake will try.
|
|
52870
|
+
*
|
|
52871
|
+
* Unlike the DOM toolbars, the button is not repainted in
|
|
52872
|
+
* place: this toolbar is a G6 plugin that rebuilds its whole
|
|
52873
|
+
* item list, and the pressed classes are baked into that list.
|
|
52874
|
+
************************************************************/
|
|
52875
|
+
function ac_toggle_anchor(gobj, event, kw, src) {
|
|
52876
|
+
let priv = gobj.priv;
|
|
52877
|
+
if (priv.anchor_state === "off") priv.anchor_state = "arming";
|
|
52878
|
+
else {
|
|
52879
|
+
priv.anchor_state = "off";
|
|
52880
|
+
priv.anchor_id = "";
|
|
52881
|
+
}
|
|
52882
|
+
update_toolbar(gobj);
|
|
52499
52883
|
return 0;
|
|
52500
52884
|
}
|
|
52501
52885
|
/************************************************************
|
|
@@ -52669,6 +53053,13 @@ function ac_node_click$2(gobj, event, kw, src) {
|
|
|
52669
53053
|
let priv = gobj.priv;
|
|
52670
53054
|
let graph = priv.graph;
|
|
52671
53055
|
let node_id = kw.evt.target.id;
|
|
53056
|
+
if (priv.anchor_state === "arming") {
|
|
53057
|
+
priv.anchor_id = node_id;
|
|
53058
|
+
priv.anchor_state = "on";
|
|
53059
|
+
update_toolbar(gobj);
|
|
53060
|
+
yui_graph_center_on(graph, node_id);
|
|
53061
|
+
return 0;
|
|
53062
|
+
}
|
|
52672
53063
|
try {
|
|
52673
53064
|
let nodedata = graph.getNodeData(node_id);
|
|
52674
53065
|
if (nodedata && nodedata.data && nodedata.data.desc) {
|
|
@@ -52930,6 +53321,11 @@ function create_gclass$4(gclass_name) {
|
|
|
52930
53321
|
ac_zoom_reset,
|
|
52931
53322
|
null
|
|
52932
53323
|
],
|
|
53324
|
+
[
|
|
53325
|
+
"EV_TOGGLE_ANCHOR",
|
|
53326
|
+
ac_toggle_anchor,
|
|
53327
|
+
null
|
|
53328
|
+
],
|
|
52933
53329
|
[
|
|
52934
53330
|
"EV_TOGGLE_TOOLBARS",
|
|
52935
53331
|
ac_toggle_toolbars,
|
|
@@ -53043,6 +53439,7 @@ function create_gclass$4(gclass_name) {
|
|
|
53043
53439
|
["EV_ZOOM_IN", 0],
|
|
53044
53440
|
["EV_ZOOM_OUT", 0],
|
|
53045
53441
|
["EV_ZOOM_RESET", 0],
|
|
53442
|
+
["EV_TOGGLE_ANCHOR", 0],
|
|
53046
53443
|
["EV_TOGGLE_TOOLBARS", 0],
|
|
53047
53444
|
["EV_AUTO_FIT", 0],
|
|
53048
53445
|
["EV_ZOOM_SELECTION", 0],
|