@yuneta/gobj-ui 7.23.8 → 7.23.9
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 +53 -0
- package/dist/gobj-ui.cjs.js +538 -71
- package/dist/gobj-ui.es.js +539 -72
- package/package.json +1 -1
- package/src/c_g6_nodes_tree.js +415 -55
- package/src/c_yui_gobj_tree_js.js +2 -0
- package/src/c_yui_json_graph.js +2 -0
- package/src/g6_drag_canvas_touch.js +21 -0
- package/src/g6_touch_gestures.js +413 -0
- package/src/lib_graph.css +19 -0
- package/src/lib_icons.js +14 -0
package/dist/gobj-ui.es.js
CHANGED
|
@@ -5,7 +5,7 @@ import { createJSONEditor } from "vanilla-jsoneditor";
|
|
|
5
5
|
import { TabulatorFull } from "tabulator-tables";
|
|
6
6
|
import * as maplibregl from "maplibre-gl";
|
|
7
7
|
import uPlot from "uplot";
|
|
8
|
-
import { BaseLayout, CanvasEvent, Circle, CommonEvent, DragCanvas, EdgeEvent, ExtensionCategory, Graph, HistoryEvent, NodeEvent, Toolbar, register } from "@antv/g6";
|
|
8
|
+
import { BaseLayout, CanvasEvent, Circle, CommonEvent, DragCanvas, EdgeEvent, ExtensionCategory, Graph, HistoryEvent, NodeEvent, Toolbar, ZoomCanvas, register } from "@antv/g6";
|
|
9
9
|
//#region \0rolldown/runtime.js
|
|
10
10
|
var __create = Object.create;
|
|
11
11
|
var __defProp = Object.defineProperty;
|
|
@@ -9499,6 +9499,275 @@ function yui_graph_refresh_item(gobj, wide) {
|
|
|
9499
9499
|
return camera_button(gobj, "yi-arrows-rotate", "EV_REFRESH", "refresh", wide);
|
|
9500
9500
|
}
|
|
9501
9501
|
//#endregion
|
|
9502
|
+
//#region src/g6_touch_gestures.js
|
|
9503
|
+
/***********************************************************************
|
|
9504
|
+
* g6_touch_gestures.js
|
|
9505
|
+
*
|
|
9506
|
+
* The two gestures a G6 graph needs on a touch screen and
|
|
9507
|
+
* does not have: PINCH to zoom, and LONG PRESS to open the
|
|
9508
|
+
* context menu.
|
|
9509
|
+
*
|
|
9510
|
+
* Why this exists
|
|
9511
|
+
* ----------------
|
|
9512
|
+
* G6 v5 draws a graph that reads fine on a phone and cannot
|
|
9513
|
+
* be OPERATED on one. Two gaps, both structural:
|
|
9514
|
+
*
|
|
9515
|
+
* 1. `zoom-canvas` binds the WHEEL and nothing else
|
|
9516
|
+
* (`trigger: []`). There is no wheel on a phone, so the
|
|
9517
|
+
* only zoom is the toolbar's +/- buttons. G6 does ship a
|
|
9518
|
+
* pinch recogniser, and `zoom-canvas` will use it -- with
|
|
9519
|
+
* `trigger: ['pinch']`, which its `bindEvents` treats as
|
|
9520
|
+
* an ALTERNATIVE to the wheel (`if pinch ... else wheel`),
|
|
9521
|
+
* so opting in costs the mouse. Worse, its `PinchHandler`
|
|
9522
|
+
* keeps its instance and its callback list in STATICS: on
|
|
9523
|
+
* a page with two graphs the second one registers its
|
|
9524
|
+
* callback against the FIRST one's emitter, so pinching
|
|
9525
|
+
* graph A zooms both and pinching graph B does nothing.
|
|
9526
|
+
* We recognise the gesture ourselves, per graph.
|
|
9527
|
+
*
|
|
9528
|
+
* 2. The context menu is bound to `contextmenu`, and G6 does
|
|
9529
|
+
* not take that from the DOM: its `BehaviorController`
|
|
9530
|
+
* SYNTHESISES it from `pointerdown` with `button === 2`.
|
|
9531
|
+
* A long press therefore never reaches the plugin on ANY
|
|
9532
|
+
* platform, whatever the browser does with the gesture.
|
|
9533
|
+
* So we synthesise it the same way G6 does, from the same
|
|
9534
|
+
* forwarded event object -- which is why the plugin's
|
|
9535
|
+
* `getItems(e)` receives exactly the shape it expects.
|
|
9536
|
+
*
|
|
9537
|
+
* Both are built on ONE per-graph record of the fingers
|
|
9538
|
+
* on the glass, read from the NATIVE touch events -- see
|
|
9539
|
+
* `touch_state_of` for why the pointer stream, which looks
|
|
9540
|
+
* like the right source, is not. That record is also what
|
|
9541
|
+
* tells `drag-canvas` to stop panning while two fingers are
|
|
9542
|
+
* down (`is_pinching`), the guard G6's own drag-canvas has
|
|
9543
|
+
* and our replacement dropped.
|
|
9544
|
+
*
|
|
9545
|
+
* Copyright (c) 2026, ArtGins.
|
|
9546
|
+
* All Rights Reserved.
|
|
9547
|
+
***********************************************************************/
|
|
9548
|
+
/***************************************************************
|
|
9549
|
+
* Constants
|
|
9550
|
+
***************************************************************/
|
|
9551
|
+
var LONG_PRESS_MS = 500;
|
|
9552
|
+
var LONG_PRESS_SLOP = 10;
|
|
9553
|
+
/***************************************************************
|
|
9554
|
+
* Per-graph record of the fingers on the glass
|
|
9555
|
+
***************************************************************/
|
|
9556
|
+
var __touch_state__ = /* @__PURE__ */ new WeakMap();
|
|
9557
|
+
/************************************************************
|
|
9558
|
+
* The touch record of one graph, installing its listeners
|
|
9559
|
+
* the first time it is asked for.
|
|
9560
|
+
*
|
|
9561
|
+
* Read from the NATIVE touch events, not from G6's
|
|
9562
|
+
* forwarded pointer stream.
|
|
9563
|
+
*
|
|
9564
|
+
* The pointer stream looks like the right source and is
|
|
9565
|
+
* not: @antv/g re-issues pointer ids in the middle of a
|
|
9566
|
+
* two-finger gesture (measured: a pinch that started on
|
|
9567
|
+
* ids 2 and 3 finished on id 1), so anything that keys a
|
|
9568
|
+
* Map on `pointerId` loses one of the two fingers halfway
|
|
9569
|
+
* and reads the gesture as a fraction of what it was.
|
|
9570
|
+
* G6's own `PinchHandler` is built exactly that way.
|
|
9571
|
+
*
|
|
9572
|
+
* `event.touches` needs no bookkeeping at all: it IS the
|
|
9573
|
+
* list of fingers currently down, re-stated on every event.
|
|
9574
|
+
************************************************************/
|
|
9575
|
+
function touch_state_of(graph) {
|
|
9576
|
+
let state = __touch_state__.get(graph);
|
|
9577
|
+
if (state) return state;
|
|
9578
|
+
state = {
|
|
9579
|
+
count: 0,
|
|
9580
|
+
container: null
|
|
9581
|
+
};
|
|
9582
|
+
__touch_state__.set(graph, state);
|
|
9583
|
+
let container = graph.getCanvas?.()?.getContainer?.();
|
|
9584
|
+
if (!container) return state;
|
|
9585
|
+
state.container = container;
|
|
9586
|
+
let read = (event) => {
|
|
9587
|
+
state.count = event.touches ? event.touches.length : 0;
|
|
9588
|
+
};
|
|
9589
|
+
container.addEventListener("touchstart", read, { passive: true });
|
|
9590
|
+
container.addEventListener("touchmove", read, { passive: true });
|
|
9591
|
+
container.addEventListener("touchend", read, { passive: true });
|
|
9592
|
+
container.addEventListener("touchcancel", read, { passive: true });
|
|
9593
|
+
return state;
|
|
9594
|
+
}
|
|
9595
|
+
/************************************************************
|
|
9596
|
+
* Is a two-finger gesture in progress on this graph?
|
|
9597
|
+
*
|
|
9598
|
+
* Read by our `drag-canvas` replacement: panning and zooming
|
|
9599
|
+
* the same drag at the same time fights the user for the
|
|
9600
|
+
* camera.
|
|
9601
|
+
************************************************************/
|
|
9602
|
+
function is_pinching(graph) {
|
|
9603
|
+
if (!graph) return false;
|
|
9604
|
+
let state = __touch_state__.get(graph);
|
|
9605
|
+
if (!state) return false;
|
|
9606
|
+
return state.count >= 2;
|
|
9607
|
+
}
|
|
9608
|
+
/***************************
|
|
9609
|
+
* Pinch to zoom
|
|
9610
|
+
***************************/
|
|
9611
|
+
/************************************************************
|
|
9612
|
+
* `zoom-canvas` that also answers the pinch.
|
|
9613
|
+
*
|
|
9614
|
+
* The base class keeps the wheel; the pinch is added on top
|
|
9615
|
+
* instead of replacing it, so one build serves the mouse and
|
|
9616
|
+
* the finger. The scale is ABSOLUTE against the zoom the
|
|
9617
|
+
* gesture started at -- G6's own pinch feeds `zoom()` an
|
|
9618
|
+
* increment per frame, which drifts away from the fingers
|
|
9619
|
+
* over a long gesture.
|
|
9620
|
+
************************************************************/
|
|
9621
|
+
var ZoomCanvasPinch = class extends ZoomCanvas {
|
|
9622
|
+
constructor(context, options) {
|
|
9623
|
+
super(context, options);
|
|
9624
|
+
this._pinch_dist0 = 0;
|
|
9625
|
+
this._pinch_zoom0 = 1;
|
|
9626
|
+
this._on_touch_move = null;
|
|
9627
|
+
let { trigger } = this.options;
|
|
9628
|
+
if (Array.isArray(trigger) && trigger.includes(CommonEvent.PINCH)) return;
|
|
9629
|
+
let graph = this.context.graph;
|
|
9630
|
+
let state = touch_state_of(graph);
|
|
9631
|
+
if (!state.container) return;
|
|
9632
|
+
this._on_touch_move = (event) => {
|
|
9633
|
+
let touches = event.touches;
|
|
9634
|
+
if (!touches || touches.length !== 2) {
|
|
9635
|
+
this._pinch_dist0 = 0;
|
|
9636
|
+
return;
|
|
9637
|
+
}
|
|
9638
|
+
let dx = touches[0].clientX - touches[1].clientX;
|
|
9639
|
+
let dy = touches[0].clientY - touches[1].clientY;
|
|
9640
|
+
let dist = Math.sqrt(dx * dx + dy * dy);
|
|
9641
|
+
if (dist <= 0) return;
|
|
9642
|
+
if (this._pinch_dist0 === 0) {
|
|
9643
|
+
this._pinch_dist0 = dist;
|
|
9644
|
+
this._pinch_zoom0 = graph.getZoom();
|
|
9645
|
+
return;
|
|
9646
|
+
}
|
|
9647
|
+
this._pinch_to(dist / this._pinch_dist0, touches, event);
|
|
9648
|
+
};
|
|
9649
|
+
state.container.addEventListener("touchmove", this._on_touch_move, { passive: true });
|
|
9650
|
+
}
|
|
9651
|
+
/************************************************************
|
|
9652
|
+
* Zoom to `scale` times the zoom the pinch started at,
|
|
9653
|
+
* about the midpoint between the two fingers.
|
|
9654
|
+
*
|
|
9655
|
+
* ABSOLUTE against the start of the gesture, where G6's
|
|
9656
|
+
* own pinch feeds `zoom()` an increment per frame: an
|
|
9657
|
+
* increment drifts away from the fingers over a long
|
|
9658
|
+
* gesture, and cannot come back when they close again.
|
|
9659
|
+
************************************************************/
|
|
9660
|
+
_pinch_to(scale, touches, event) {
|
|
9661
|
+
if (!this.validate(event)) return;
|
|
9662
|
+
let graph = this.context.graph;
|
|
9663
|
+
let state = touch_state_of(graph);
|
|
9664
|
+
let origin = null;
|
|
9665
|
+
let rect = state.container?.getBoundingClientRect();
|
|
9666
|
+
if (rect) origin = [(touches[0].clientX + touches[1].clientX) / 2 - rect.left, (touches[0].clientY + touches[1].clientY) / 2 - rect.top];
|
|
9667
|
+
graph.zoomTo(this._pinch_zoom0 * scale, false, origin);
|
|
9668
|
+
this.options.onFinish?.();
|
|
9669
|
+
}
|
|
9670
|
+
destroy() {
|
|
9671
|
+
if (this._on_touch_move) {
|
|
9672
|
+
__touch_state__.get(this.context.graph)?.container?.removeEventListener("touchmove", this._on_touch_move);
|
|
9673
|
+
this._on_touch_move = null;
|
|
9674
|
+
}
|
|
9675
|
+
super.destroy();
|
|
9676
|
+
}
|
|
9677
|
+
};
|
|
9678
|
+
/************************************************************
|
|
9679
|
+
* Register once, over the built-in `zoom-canvas`, so every
|
|
9680
|
+
* graph (tree, json, treedb, editor) pinches without
|
|
9681
|
+
* touching its behaviors list.
|
|
9682
|
+
************************************************************/
|
|
9683
|
+
var __zoom_canvas_patched__ = false;
|
|
9684
|
+
function ensure_pinch_zoom_patch() {
|
|
9685
|
+
if (__zoom_canvas_patched__) return;
|
|
9686
|
+
register(ExtensionCategory.BEHAVIOR, "zoom-canvas", ZoomCanvasPinch);
|
|
9687
|
+
__zoom_canvas_patched__ = true;
|
|
9688
|
+
}
|
|
9689
|
+
/***************************
|
|
9690
|
+
* Long press
|
|
9691
|
+
***************************/
|
|
9692
|
+
/************************************************************
|
|
9693
|
+
* Make a long press open the context menu.
|
|
9694
|
+
*
|
|
9695
|
+
* Returns the function that uninstalls it.
|
|
9696
|
+
*
|
|
9697
|
+
* The menu the press opens is whatever the `contextmenu`
|
|
9698
|
+
* plugin builds: we re-emit G6's own forwarded event under
|
|
9699
|
+
* the name the plugin listens for, so `getItems(e)` and
|
|
9700
|
+
* `enable(e)` see the same object a right-click would give
|
|
9701
|
+
* them -- same `target`, same `targetType`, same `client`.
|
|
9702
|
+
************************************************************/
|
|
9703
|
+
function install_long_press_contextmenu(graph) {
|
|
9704
|
+
if (!graph) return () => {};
|
|
9705
|
+
let state = touch_state_of(graph);
|
|
9706
|
+
let pending = null;
|
|
9707
|
+
let cancel = () => {
|
|
9708
|
+
if (pending) {
|
|
9709
|
+
clearTimeout(pending.timer);
|
|
9710
|
+
pending = null;
|
|
9711
|
+
}
|
|
9712
|
+
};
|
|
9713
|
+
let fire = (event) => {
|
|
9714
|
+
pending = null;
|
|
9715
|
+
let swallow = (evt) => {
|
|
9716
|
+
evt.stopPropagation();
|
|
9717
|
+
document.removeEventListener("click", swallow, true);
|
|
9718
|
+
};
|
|
9719
|
+
document.addEventListener("click", swallow, true);
|
|
9720
|
+
setTimeout(() => {
|
|
9721
|
+
document.removeEventListener("click", swallow, true);
|
|
9722
|
+
}, 1e3);
|
|
9723
|
+
let container = graph.getCanvas?.()?.getContainer?.();
|
|
9724
|
+
if (container) container.addEventListener("contextmenu", (evt) => {
|
|
9725
|
+
evt.preventDefault();
|
|
9726
|
+
}, {
|
|
9727
|
+
once: true,
|
|
9728
|
+
capture: true
|
|
9729
|
+
});
|
|
9730
|
+
graph.emit(`${event.targetType}:${CommonEvent.CONTEXT_MENU}`, event);
|
|
9731
|
+
graph.emit(CommonEvent.CONTEXT_MENU, event);
|
|
9732
|
+
};
|
|
9733
|
+
let on_down = (event) => {
|
|
9734
|
+
cancel();
|
|
9735
|
+
if (event.pointerType !== "touch") return;
|
|
9736
|
+
if (state.count > 1) return;
|
|
9737
|
+
let x = event.client?.x;
|
|
9738
|
+
let y = event.client?.y;
|
|
9739
|
+
if (x === void 0 || y === void 0) return;
|
|
9740
|
+
let frozen = Object.assign({}, event);
|
|
9741
|
+
pending = {
|
|
9742
|
+
x,
|
|
9743
|
+
y,
|
|
9744
|
+
timer: setTimeout(() => {
|
|
9745
|
+
fire(frozen);
|
|
9746
|
+
}, LONG_PRESS_MS)
|
|
9747
|
+
};
|
|
9748
|
+
};
|
|
9749
|
+
let on_move = (event) => {
|
|
9750
|
+
if (!pending) return;
|
|
9751
|
+
if (state.count > 1) {
|
|
9752
|
+
cancel();
|
|
9753
|
+
return;
|
|
9754
|
+
}
|
|
9755
|
+
let x = event.client?.x;
|
|
9756
|
+
let y = event.client?.y;
|
|
9757
|
+
if (x === void 0 || y === void 0) return;
|
|
9758
|
+
if (Math.abs(x - pending.x) > LONG_PRESS_SLOP || Math.abs(y - pending.y) > LONG_PRESS_SLOP) cancel();
|
|
9759
|
+
};
|
|
9760
|
+
graph.on(CommonEvent.POINTER_DOWN, on_down);
|
|
9761
|
+
graph.on(CommonEvent.POINTER_MOVE, on_move);
|
|
9762
|
+
graph.on(CommonEvent.POINTER_UP, cancel);
|
|
9763
|
+
return () => {
|
|
9764
|
+
cancel();
|
|
9765
|
+
graph.off(CommonEvent.POINTER_DOWN, on_down);
|
|
9766
|
+
graph.off(CommonEvent.POINTER_MOVE, on_move);
|
|
9767
|
+
graph.off(CommonEvent.POINTER_UP, cancel);
|
|
9768
|
+
};
|
|
9769
|
+
}
|
|
9770
|
+
//#endregion
|
|
9502
9771
|
//#region src/g6_drag_canvas_touch.js
|
|
9503
9772
|
/***********************************************************************
|
|
9504
9773
|
* g6_drag_canvas_touch.js
|
|
@@ -9543,6 +9812,12 @@ function yui_graph_refresh_item(gobj, wide) {
|
|
|
9543
9812
|
* every graph (tree, json, treedb, editor) is fixed without
|
|
9544
9813
|
* touching each consumer's behaviors list.
|
|
9545
9814
|
*
|
|
9815
|
+
* It also has to keep the ONE guard the base class has and
|
|
9816
|
+
* this replacement dropped: G6's `onDrag` refuses to pan
|
|
9817
|
+
* while a pinch is in progress. See `is_pinching` in
|
|
9818
|
+
* `g6_touch_gestures.js`, which is where the pinch that
|
|
9819
|
+
* matters is now recognised.
|
|
9820
|
+
*
|
|
9546
9821
|
* Copyright (c) 2026, ArtGins.
|
|
9547
9822
|
* All Rights Reserved.
|
|
9548
9823
|
***********************************************************************/
|
|
@@ -9558,6 +9833,10 @@ var DragCanvasTouch = class extends DragCanvas {
|
|
|
9558
9833
|
this._lastVP = null;
|
|
9559
9834
|
return;
|
|
9560
9835
|
}
|
|
9836
|
+
if (is_pinching(this.context.graph)) {
|
|
9837
|
+
this._lastVP = null;
|
|
9838
|
+
return;
|
|
9839
|
+
}
|
|
9561
9840
|
let vp = event.viewport;
|
|
9562
9841
|
if (!vp || typeof vp.x !== "number" || typeof vp.y !== "number") return;
|
|
9563
9842
|
if (this._lastVP === null) {
|
|
@@ -9787,6 +10066,7 @@ function mt_create$11(gobj) {
|
|
|
9787
10066
|
priv.theme_observer = yui_watch_theme(gobj);
|
|
9788
10067
|
build_ui$10(gobj);
|
|
9789
10068
|
ensure_drag_canvas_patch();
|
|
10069
|
+
ensure_pinch_zoom_patch();
|
|
9790
10070
|
if (!__layout_registered__$1) {
|
|
9791
10071
|
register(ExtensionCategory.LAYOUT, "json-tree-layout", JsonTreeLayout);
|
|
9792
10072
|
__layout_registered__$1 = true;
|
|
@@ -12415,6 +12695,7 @@ function mt_create$9(gobj) {
|
|
|
12415
12695
|
priv.theme_observer = yui_watch_theme(gobj);
|
|
12416
12696
|
build_ui$8(gobj);
|
|
12417
12697
|
ensure_drag_canvas_patch();
|
|
12698
|
+
ensure_pinch_zoom_patch();
|
|
12418
12699
|
if (!__layout_registered__) {
|
|
12419
12700
|
register(ExtensionCategory.LAYOUT, "gobj-tree-v", GobjTreeVLayout);
|
|
12420
12701
|
register(ExtensionCategory.LAYOUT, "gobj-tree-h", GobjTreeHLayout);
|
|
@@ -47185,6 +47466,20 @@ var ICONS = `<svg xmlns="http://www.w3.org/2000/svg">
|
|
|
47185
47466
|
<symbol id="g6-icon-plus" viewBox="0 0 512 512">
|
|
47186
47467
|
<path fill="currentColor" d="M316 32 L316 196 L480 196 L480 316 L316 316 L316 480 L196 480 L196 316 L32 316 L32 196 L196 196 L196 32 Z"/>
|
|
47187
47468
|
</symbol>
|
|
47469
|
+
<!-- ellipsis-vertical (unfold the floating toolbar).
|
|
47470
|
+
The strip it stands for is a vertical stack, and a vertical
|
|
47471
|
+
ellipsis is what "there is more of this here" is drawn as
|
|
47472
|
+
everywhere else. -->
|
|
47473
|
+
<symbol id="g6-icon-toolbar-show" viewBox="0 0 128 512">
|
|
47474
|
+
<path fill="currentColor" d="M64 360a56 56 0 1 0 0 112 56 56 0 1 0 0-112zm0-160a56 56 0 1 0 0 112 56 56 0 1 0 0-112zM120 96A56 56 0 1 0 8 96a56 56 0 1 0 112 0z"/>
|
|
47475
|
+
</symbol>
|
|
47476
|
+
<!-- xmark (fold the floating toolbar away).
|
|
47477
|
+
Not a chevron: the two toolbars sit on OPPOSITE edges, so a
|
|
47478
|
+
direction would point the wrong way on one of them. What the
|
|
47479
|
+
button does is close an overlay, and that is an x. -->
|
|
47480
|
+
<symbol id="g6-icon-toolbar-hide" viewBox="0 0 384 512">
|
|
47481
|
+
<path fill="currentColor" d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"/>
|
|
47482
|
+
</symbol>
|
|
47188
47483
|
<!-- floppy-disk (save) -->
|
|
47189
47484
|
<symbol id="g6-icon-save" viewBox="0 0 448 512">
|
|
47190
47485
|
<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"/>
|
|
@@ -47257,6 +47552,7 @@ var YuiToolbar = class extends Toolbar {
|
|
|
47257
47552
|
};
|
|
47258
47553
|
register(ExtensionCategory.PLUGIN, "yui-toolbar", YuiToolbar);
|
|
47259
47554
|
ensure_drag_canvas_patch();
|
|
47555
|
+
ensure_pinch_zoom_patch();
|
|
47260
47556
|
/***************************************************************
|
|
47261
47557
|
* Constants
|
|
47262
47558
|
***************************************************************/
|
|
@@ -47376,6 +47672,9 @@ var PRIVATE_DATA$4 = {
|
|
|
47376
47672
|
_focus_topic: null,
|
|
47377
47673
|
_focus_ids: [],
|
|
47378
47674
|
_on_pointerdown_focus: null,
|
|
47675
|
+
_uninstall_long_press: null,
|
|
47676
|
+
toolbar_collapsed: true,
|
|
47677
|
+
_toolbars_could_collapse: null,
|
|
47379
47678
|
_on_focusout_restore: null,
|
|
47380
47679
|
_selected_paint_ids: [],
|
|
47381
47680
|
_pending_focus_topic: null,
|
|
@@ -47461,6 +47760,10 @@ function mt_destroy$4(gobj) {
|
|
|
47461
47760
|
cancelAnimationFrame(priv._resize_raf);
|
|
47462
47761
|
priv._resize_raf = 0;
|
|
47463
47762
|
}
|
|
47763
|
+
if (priv._uninstall_long_press) {
|
|
47764
|
+
priv._uninstall_long_press();
|
|
47765
|
+
priv._uninstall_long_press = null;
|
|
47766
|
+
}
|
|
47464
47767
|
if (priv.graph) {
|
|
47465
47768
|
priv.graph.destroy();
|
|
47466
47769
|
priv.graph = null;
|
|
@@ -47675,6 +47978,8 @@ function configure_plugins(gobj) {
|
|
|
47675
47978
|
return e.targetType === "node" || e.targetType === "edge";
|
|
47676
47979
|
}
|
|
47677
47980
|
});
|
|
47981
|
+
if (priv._uninstall_long_press) priv._uninstall_long_press();
|
|
47982
|
+
priv._uninstall_long_press = install_long_press_contextmenu(priv.graph);
|
|
47678
47983
|
if (gobj_read_bool_attr(gobj, "with_toolbar")) {
|
|
47679
47984
|
configure_toolbar(gobj);
|
|
47680
47985
|
configure_toolbar_edit(gobj);
|
|
@@ -47704,6 +48009,55 @@ function update_toolbar(gobj) {
|
|
|
47704
48009
|
* flipping now; the fallbacks are the old forced-light values, so
|
|
47705
48010
|
* a host without Bulma gets exactly what it had.
|
|
47706
48011
|
************************************************************/
|
|
48012
|
+
/************************************************************
|
|
48013
|
+
* Is the canvas too narrow to give both floating toolbars a
|
|
48014
|
+
* strip of it?
|
|
48015
|
+
*
|
|
48016
|
+
* They are drawn INSIDE the canvas, one on each edge. On a
|
|
48017
|
+
* desktop that is a corner; on a 356px-wide phone canvas the
|
|
48018
|
+
* two of them take a third of the drawing area and stand on
|
|
48019
|
+
* top of the nodes -- and a node under a toolbar cannot be
|
|
48020
|
+
* read, tapped or dragged out from under it.
|
|
48021
|
+
*
|
|
48022
|
+
* Measured on the CONTAINER, not on the window: the same
|
|
48023
|
+
* graph is a full page in one app and a card in a column in
|
|
48024
|
+
* another, and it is the box it actually got that decides.
|
|
48025
|
+
************************************************************/
|
|
48026
|
+
var TOOLBAR_COLLAPSE_WIDTH = 480;
|
|
48027
|
+
function toolbars_can_collapse(gobj) {
|
|
48028
|
+
let $container = gobj.priv.$container;
|
|
48029
|
+
if (!$container) return false;
|
|
48030
|
+
let width = $container.getBoundingClientRect().width;
|
|
48031
|
+
if (!width) return false;
|
|
48032
|
+
return width < TOOLBAR_COLLAPSE_WIDTH;
|
|
48033
|
+
}
|
|
48034
|
+
/************************************************************
|
|
48035
|
+
* Are they folded RIGHT NOW? Wide enough and the question
|
|
48036
|
+
* does not arise -- the desktop toolbar never grows a button
|
|
48037
|
+
* it does not need.
|
|
48038
|
+
************************************************************/
|
|
48039
|
+
function toolbars_collapsed(gobj) {
|
|
48040
|
+
if (!toolbars_can_collapse(gobj)) return false;
|
|
48041
|
+
return gobj.priv.toolbar_collapsed !== false;
|
|
48042
|
+
}
|
|
48043
|
+
/************************************************************
|
|
48044
|
+
* The one item a folded toolbar shows, and the one an
|
|
48045
|
+
* unfolded narrow toolbar adds to fold itself again.
|
|
48046
|
+
************************************************************/
|
|
48047
|
+
function toolbar_fold_item(gobj) {
|
|
48048
|
+
if (toolbars_collapsed(gobj)) return {
|
|
48049
|
+
id: "g6-icon-toolbar-show",
|
|
48050
|
+
value: "toolbar-toggle",
|
|
48051
|
+
className: "EV_TOGGLE_TOOLBARS",
|
|
48052
|
+
title: t("show toolbar")
|
|
48053
|
+
};
|
|
48054
|
+
return {
|
|
48055
|
+
id: "g6-icon-toolbar-hide",
|
|
48056
|
+
value: "toolbar-toggle",
|
|
48057
|
+
className: "EV_TOGGLE_TOOLBARS",
|
|
48058
|
+
title: t("hide toolbar")
|
|
48059
|
+
};
|
|
48060
|
+
}
|
|
47707
48061
|
function toolbar_style(extra) {
|
|
47708
48062
|
return Object.assign({
|
|
47709
48063
|
backgroundColor: "var(--bulma-scheme-main, #f5f5f5)",
|
|
@@ -47761,6 +48115,7 @@ function configure_toolbar(gobj) {
|
|
|
47761
48115
|
marginLeft: "12px"
|
|
47762
48116
|
}),
|
|
47763
48117
|
getItems: () => {
|
|
48118
|
+
if (toolbars_collapsed(gobj)) return [toolbar_fold_item(gobj)];
|
|
47764
48119
|
let items = [
|
|
47765
48120
|
{
|
|
47766
48121
|
id: "g6-icon-zoom-in",
|
|
@@ -47816,6 +48171,10 @@ function configure_toolbar(gobj) {
|
|
|
47816
48171
|
title: t("enter full screen")
|
|
47817
48172
|
});
|
|
47818
48173
|
}
|
|
48174
|
+
if (toolbars_can_collapse(gobj)) {
|
|
48175
|
+
items.push({ separator: true });
|
|
48176
|
+
items.push(toolbar_fold_item(gobj));
|
|
48177
|
+
}
|
|
47819
48178
|
return items;
|
|
47820
48179
|
},
|
|
47821
48180
|
onClick: (value) => {
|
|
@@ -47841,7 +48200,10 @@ function configure_toolbar(gobj) {
|
|
|
47841
48200
|
case "request-fullscreen":
|
|
47842
48201
|
gobj_send_event(gobj, "EV_REQUEST_FULLSCREEN", {}, gobj);
|
|
47843
48202
|
break;
|
|
47844
|
-
case "exit-fullscreen":
|
|
48203
|
+
case "exit-fullscreen":
|
|
48204
|
+
gobj_send_event(gobj, "EV_EXIT_FULLSCREEN", {}, gobj);
|
|
48205
|
+
break;
|
|
48206
|
+
case "toolbar-toggle": gobj_send_event(gobj, "EV_TOGGLE_TOOLBARS", {}, gobj);
|
|
47845
48207
|
}
|
|
47846
48208
|
}
|
|
47847
48209
|
});
|
|
@@ -47851,7 +48213,7 @@ function configure_toolbar(gobj) {
|
|
|
47851
48213
|
* Contains edit-mode buttons: Undo, Redo, Save
|
|
47852
48214
|
************************************************************/
|
|
47853
48215
|
function configure_toolbar_edit(gobj) {
|
|
47854
|
-
if (!gobj.priv.edit_mode) {
|
|
48216
|
+
if (!gobj.priv.edit_mode || toolbars_collapsed(gobj)) {
|
|
47855
48217
|
graph_remove_plugin(gobj, "toolbar-edit");
|
|
47856
48218
|
return;
|
|
47857
48219
|
}
|
|
@@ -47959,6 +48321,7 @@ function configure_behaviour(gobj) {
|
|
|
47959
48321
|
break;
|
|
47960
48322
|
case "operation":
|
|
47961
48323
|
priv.edit_mode = false;
|
|
48324
|
+
behaviors = ["drag-canvas", "zoom-canvas"];
|
|
47962
48325
|
break;
|
|
47963
48326
|
default: log_error(`operation mode unknown: ${operation_mode}`);
|
|
47964
48327
|
}
|
|
@@ -48921,6 +49284,14 @@ var ManualLayout = class extends BaseLayout {
|
|
|
48921
49284
|
************************************************************/
|
|
48922
49285
|
var RESIZE_HANDLE_SIZE = 8;
|
|
48923
49286
|
var RESIZE_MIN_VP = 20;
|
|
49287
|
+
var RESIZE_HANDLE_VISUAL_COARSE = 14;
|
|
49288
|
+
var RESIZE_HANDLE_HIT_COARSE = 44;
|
|
49289
|
+
function resize_handle_visual() {
|
|
49290
|
+
return coarse_pointer() ? RESIZE_HANDLE_VISUAL_COARSE : RESIZE_HANDLE_SIZE;
|
|
49291
|
+
}
|
|
49292
|
+
function resize_handle_hit() {
|
|
49293
|
+
return coarse_pointer() ? RESIZE_HANDLE_HIT_COARSE : RESIZE_HANDLE_SIZE;
|
|
49294
|
+
}
|
|
48924
49295
|
/************************************************************
|
|
48925
49296
|
* SVG icons for floating action buttons (28×28 circles).
|
|
48926
49297
|
* Each value is a raw SVG string sized 18×18 in a 24×24 viewBox.
|
|
@@ -48933,7 +49304,50 @@ var SVG_ICONS = {
|
|
|
48933
49304
|
plus: "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\"/><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/></svg>"
|
|
48934
49305
|
};
|
|
48935
49306
|
/************************************************************
|
|
48936
|
-
*
|
|
49307
|
+
* Is the primary pointer a FINGER?
|
|
49308
|
+
*
|
|
49309
|
+
* Everything this gclass draws over the canvas is sized from
|
|
49310
|
+
* this answer. A mouse is a pixel and can hit an 8px square;
|
|
49311
|
+
* a fingertip covers about 40, and the overlay it lands on
|
|
49312
|
+
* decides between "open the properties" and "delete the node".
|
|
49313
|
+
*
|
|
49314
|
+
* Asked per call, not cached: a tablet with a keyboard dock
|
|
49315
|
+
* changes the answer without reloading the page.
|
|
49316
|
+
************************************************************/
|
|
49317
|
+
function coarse_pointer() {
|
|
49318
|
+
if (typeof window.matchMedia !== "function") return false;
|
|
49319
|
+
try {
|
|
49320
|
+
return window.matchMedia("(pointer: coarse)").matches === true;
|
|
49321
|
+
} catch (e) {
|
|
49322
|
+
return false;
|
|
49323
|
+
}
|
|
49324
|
+
}
|
|
49325
|
+
/************************************************************
|
|
49326
|
+
* The size of the round floating buttons (properties,
|
|
49327
|
+
* delete, link), and the GAP between two of them.
|
|
49328
|
+
*
|
|
49329
|
+
* The gap matters as much as the size: `node properties` and
|
|
49330
|
+
* `delete node` are drawn one under the other, and at 28px
|
|
49331
|
+
* with 4px between them a fingertip covers both -- with the
|
|
49332
|
+
* destructive one underneath.
|
|
49333
|
+
************************************************************/
|
|
49334
|
+
function floating_icon_size() {
|
|
49335
|
+
return coarse_pointer() ? 44 : 28;
|
|
49336
|
+
}
|
|
49337
|
+
function floating_icon_gap() {
|
|
49338
|
+
return coarse_pointer() ? 12 : 4;
|
|
49339
|
+
}
|
|
49340
|
+
function floating_icon_dy() {
|
|
49341
|
+
return -floating_icon_size() / 2;
|
|
49342
|
+
}
|
|
49343
|
+
function floating_icon_step() {
|
|
49344
|
+
return floating_icon_size() + floating_icon_gap();
|
|
49345
|
+
}
|
|
49346
|
+
function floating_popover_dx() {
|
|
49347
|
+
return 4 + floating_icon_size() + 4;
|
|
49348
|
+
}
|
|
49349
|
+
/************************************************************
|
|
49350
|
+
* Create a floating circular icon button.
|
|
48937
49351
|
* @param {string} svgKey - key into SVG_ICONS
|
|
48938
49352
|
* @param {string} color - border & text color (e.g. '#1890ff')
|
|
48939
49353
|
* @param {number} left - CSS left in px
|
|
@@ -48943,10 +49357,18 @@ var SVG_ICONS = {
|
|
|
48943
49357
|
* @returns {HTMLElement}
|
|
48944
49358
|
************************************************************/
|
|
48945
49359
|
function create_floating_icon(svgKey, color, left, top, title, onClick) {
|
|
49360
|
+
const size = floating_icon_size();
|
|
48946
49361
|
const el = document.createElement("div");
|
|
48947
49362
|
el.title = title;
|
|
49363
|
+
el.setAttribute("aria-label", title);
|
|
48948
49364
|
el.innerHTML = SVG_ICONS[svgKey] || "";
|
|
48949
|
-
el.style.cssText = "position:absolute;left:" + left + "px;top:" + top + "px;width:
|
|
49365
|
+
el.style.cssText = "position:absolute;left:" + left + "px;top:" + top + "px;width:" + size + "px;height:" + size + "px;display:flex;align-items:center;justify-content:center;background:#fff;border:1px solid " + color + ";border-radius:50%;cursor:pointer;pointer-events:all;z-index:11;touch-action:none;box-shadow:0 2px 6px rgba(0,0,0,0.15);color:" + color + ";";
|
|
49366
|
+
const $svg = el.querySelector("svg");
|
|
49367
|
+
if ($svg) {
|
|
49368
|
+
const glyph = Math.round(size * .5);
|
|
49369
|
+
$svg.setAttribute("width", String(glyph));
|
|
49370
|
+
$svg.setAttribute("height", String(glyph));
|
|
49371
|
+
}
|
|
48950
49372
|
el.addEventListener("click", (e) => {
|
|
48951
49373
|
e.stopPropagation();
|
|
48952
49374
|
onClick();
|
|
@@ -48992,7 +49414,7 @@ function show_dual_icons(gobj, x, y, gear_title, gear_click, trash_title, trash_
|
|
|
48992
49414
|
let priv = gobj.priv;
|
|
48993
49415
|
const icon_el = create_floating_icon("gear", "#1890ff", x, y, gear_title, gear_click);
|
|
48994
49416
|
priv.$container.appendChild(icon_el);
|
|
48995
|
-
const delete_el = create_floating_icon("trash", "#ff4d4f", x, y +
|
|
49417
|
+
const delete_el = create_floating_icon("trash", "#ff4d4f", x, y + floating_icon_step(), trash_title, trash_click);
|
|
48996
49418
|
priv.$container.appendChild(delete_el);
|
|
48997
49419
|
return {
|
|
48998
49420
|
icon_el,
|
|
@@ -49010,6 +49432,21 @@ function create_popover_base(left, top, className, borderColor, minWidth) {
|
|
|
49010
49432
|
popover.addEventListener("pointerdown", (e) => e.stopPropagation());
|
|
49011
49433
|
return popover;
|
|
49012
49434
|
}
|
|
49435
|
+
/************************************************************
|
|
49436
|
+
* Extra CSS every control inside a popover carries on a
|
|
49437
|
+
* touch screen.
|
|
49438
|
+
*
|
|
49439
|
+
* A popover is where the graph is TYPED into -- a colour, a
|
|
49440
|
+
* radius, an id -- and its controls were sized by their
|
|
49441
|
+
* padding alone, which lands them at 25-30px. A finger needs
|
|
49442
|
+
* 44, and the number is not ours: it is the floor both Apple
|
|
49443
|
+
* and Google publish, and the one every native control on the
|
|
49444
|
+
* device already meets.
|
|
49445
|
+
************************************************************/
|
|
49446
|
+
function touch_control_css() {
|
|
49447
|
+
if (!coarse_pointer()) return "";
|
|
49448
|
+
return "min-height:44px;font-size:16px;";
|
|
49449
|
+
}
|
|
49013
49450
|
function create_form_label(parent, text) {
|
|
49014
49451
|
let label = document.createElement("label");
|
|
49015
49452
|
label.textContent = t(text);
|
|
@@ -49021,7 +49458,7 @@ function create_form_color_input(parent, value, onInput) {
|
|
|
49021
49458
|
let input = document.createElement("input");
|
|
49022
49459
|
input.type = "color";
|
|
49023
49460
|
input.value = value;
|
|
49024
|
-
input.style.cssText = "width:100%;height:30px;padding:0;border:1px solid var(--bulma-border-weak, #d9d9d9);border-radius:4px;cursor:pointer;margin-bottom:10px;";
|
|
49461
|
+
input.style.cssText = "width:100%;height:30px;padding:0;border:1px solid var(--bulma-border-weak, #d9d9d9);border-radius:4px;cursor:pointer;margin-bottom:10px;" + touch_control_css();
|
|
49025
49462
|
if (onInput) input.addEventListener("input", onInput);
|
|
49026
49463
|
parent.appendChild(input);
|
|
49027
49464
|
return input;
|
|
@@ -49032,14 +49469,14 @@ function create_form_number_input(parent, value, min, max, onInput) {
|
|
|
49032
49469
|
input.min = String(min);
|
|
49033
49470
|
input.max = String(max);
|
|
49034
49471
|
input.value = value;
|
|
49035
|
-
input.style.cssText = "width:100%;padding:4px 6px;background:var(--bulma-scheme-main-bis, #fff);color:var(--bulma-text-strong, #333);border:1px solid var(--bulma-border-weak, #d9d9d9);border-radius:4px;box-sizing:border-box;margin-bottom:10px;";
|
|
49472
|
+
input.style.cssText = "width:100%;padding:4px 6px;background:var(--bulma-scheme-main-bis, #fff);color:var(--bulma-text-strong, #333);border:1px solid var(--bulma-border-weak, #d9d9d9);border-radius:4px;box-sizing:border-box;margin-bottom:10px;" + touch_control_css();
|
|
49036
49473
|
if (onInput) input.addEventListener("input", onInput);
|
|
49037
49474
|
parent.appendChild(input);
|
|
49038
49475
|
return input;
|
|
49039
49476
|
}
|
|
49040
49477
|
function create_form_select(parent, options, marginBottom) {
|
|
49041
49478
|
let select = document.createElement("select");
|
|
49042
|
-
select.style.cssText = "width:100%;padding:4px 6px;background:var(--bulma-scheme-main-bis, #fff);color:var(--bulma-text-strong, #333);border:1px solid var(--bulma-border-weak, #d9d9d9);border-radius:4px;box-sizing:border-box;margin-bottom:" + (marginBottom || "12px") + ";";
|
|
49479
|
+
select.style.cssText = "width:100%;padding:4px 6px;background:var(--bulma-scheme-main-bis, #fff);color:var(--bulma-text-strong, #333);border:1px solid var(--bulma-border-weak, #d9d9d9);border-radius:4px;box-sizing:border-box;margin-bottom:" + (marginBottom || "12px") + ";" + touch_control_css();
|
|
49043
49480
|
for (let opt of options) {
|
|
49044
49481
|
let o = document.createElement("option");
|
|
49045
49482
|
o.value = opt.value;
|
|
@@ -49056,7 +49493,7 @@ function create_form_button_row(parent, buttons) {
|
|
|
49056
49493
|
for (let { text, style, onClick } of buttons) {
|
|
49057
49494
|
let btn = document.createElement("button");
|
|
49058
49495
|
btn.textContent = t(text);
|
|
49059
|
-
btn.style.cssText = style;
|
|
49496
|
+
btn.style.cssText = style + touch_control_css();
|
|
49060
49497
|
btn.addEventListener("click", (e) => {
|
|
49061
49498
|
e.stopPropagation();
|
|
49062
49499
|
onClick();
|
|
@@ -49294,7 +49731,7 @@ function show_resize_handles(gobj) {
|
|
|
49294
49731
|
container.style.cssText = "position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:10;";
|
|
49295
49732
|
const selRect = document.createElement("div");
|
|
49296
49733
|
container.appendChild(selRect);
|
|
49297
|
-
|
|
49734
|
+
let handle_defs = [
|
|
49298
49735
|
{
|
|
49299
49736
|
cursor: "nw-resize",
|
|
49300
49737
|
mx: -1,
|
|
@@ -49336,10 +49773,16 @@ function show_resize_handles(gobj) {
|
|
|
49336
49773
|
my: 1
|
|
49337
49774
|
}
|
|
49338
49775
|
];
|
|
49776
|
+
const hit = resize_handle_hit();
|
|
49777
|
+
const visual = resize_handle_visual();
|
|
49778
|
+
if (coarse_pointer()) handle_defs = handle_defs.filter((def) => def.mx !== 0 && def.my !== 0);
|
|
49339
49779
|
const handles = [];
|
|
49340
49780
|
for (const def of handle_defs) {
|
|
49341
49781
|
const el = document.createElement("div");
|
|
49342
|
-
el.style.cssText = "position:absolute;width:
|
|
49782
|
+
el.style.cssText = "position:absolute;width:" + hit + "px;height:" + hit + "px;display:flex;align-items:center;justify-content:center;cursor:" + def.cursor + ";pointer-events:all;touch-action:none;box-sizing:border-box;";
|
|
49783
|
+
const mark = document.createElement("div");
|
|
49784
|
+
mark.style.cssText = "width:" + visual + "px;height:" + visual + "px;background:#fff;border:1px solid #1890ff;pointer-events:none;box-sizing:border-box;";
|
|
49785
|
+
el.appendChild(mark);
|
|
49343
49786
|
el.addEventListener("pointerdown", (e) => {
|
|
49344
49787
|
start_node_resize(gobj, e, def.mx, def.my);
|
|
49345
49788
|
});
|
|
@@ -49379,52 +49822,18 @@ function update_resize_handles_position(gobj) {
|
|
|
49379
49822
|
}
|
|
49380
49823
|
function apply_handles_to_rect(gobj, rect) {
|
|
49381
49824
|
let priv = gobj.priv;
|
|
49382
|
-
const HALF =
|
|
49825
|
+
const HALF = resize_handle_hit() / 2;
|
|
49383
49826
|
const left = rect.left;
|
|
49384
49827
|
const top = rect.top;
|
|
49385
49828
|
const w = rect.right - rect.left;
|
|
49386
49829
|
const h = rect.bottom - rect.top;
|
|
49387
49830
|
const selRect = priv._resize_sel_rect;
|
|
49388
49831
|
if (selRect) selRect.style.cssText = "position:absolute;left:" + left + "px;top:" + top + "px;width:" + w + "px;height:" + h + "px;border:1px dashed #1890ff;pointer-events:none;box-sizing:border-box;";
|
|
49389
|
-
const
|
|
49390
|
-
|
|
49391
|
-
|
|
49392
|
-
|
|
49393
|
-
|
|
49394
|
-
{
|
|
49395
|
-
x: left + w / 2,
|
|
49396
|
-
y: top
|
|
49397
|
-
},
|
|
49398
|
-
{
|
|
49399
|
-
x: left + w,
|
|
49400
|
-
y: top
|
|
49401
|
-
},
|
|
49402
|
-
{
|
|
49403
|
-
x: left,
|
|
49404
|
-
y: top + h / 2
|
|
49405
|
-
},
|
|
49406
|
-
{
|
|
49407
|
-
x: left + w,
|
|
49408
|
-
y: top + h / 2
|
|
49409
|
-
},
|
|
49410
|
-
{
|
|
49411
|
-
x: left,
|
|
49412
|
-
y: top + h
|
|
49413
|
-
},
|
|
49414
|
-
{
|
|
49415
|
-
x: left + w / 2,
|
|
49416
|
-
y: top + h
|
|
49417
|
-
},
|
|
49418
|
-
{
|
|
49419
|
-
x: left + w,
|
|
49420
|
-
y: top + h
|
|
49421
|
-
}
|
|
49422
|
-
];
|
|
49423
|
-
for (let i = 0; i < priv._resize_handles.length; i++) {
|
|
49424
|
-
const handle = priv._resize_handles[i];
|
|
49425
|
-
const p = positions[i];
|
|
49426
|
-
handle.el.style.left = p.x - HALF + "px";
|
|
49427
|
-
handle.el.style.top = p.y - HALF + "px";
|
|
49832
|
+
for (const handle of priv._resize_handles) {
|
|
49833
|
+
const x = left + (handle.mx + 1) / 2 * w;
|
|
49834
|
+
const y = top + (handle.my + 1) / 2 * h;
|
|
49835
|
+
handle.el.style.left = x - HALF + "px";
|
|
49836
|
+
handle.el.style.top = y - HALF + "px";
|
|
49428
49837
|
}
|
|
49429
49838
|
}
|
|
49430
49839
|
function start_node_resize(gobj, e, mx, my) {
|
|
@@ -49500,6 +49909,14 @@ function start_node_resize(gobj, e, mx, my) {
|
|
|
49500
49909
|
* Port selection and individual port resizing
|
|
49501
49910
|
************************************************************/
|
|
49502
49911
|
var PORT_HANDLE_SIZE = 8;
|
|
49912
|
+
var PORT_HANDLE_VISUAL_COARSE = 12;
|
|
49913
|
+
var PORT_HANDLE_HIT_COARSE = 32;
|
|
49914
|
+
function port_handle_visual() {
|
|
49915
|
+
return coarse_pointer() ? PORT_HANDLE_VISUAL_COARSE : PORT_HANDLE_SIZE;
|
|
49916
|
+
}
|
|
49917
|
+
function port_handle_hit() {
|
|
49918
|
+
return coarse_pointer() ? PORT_HANDLE_HIT_COARSE : PORT_HANDLE_SIZE;
|
|
49919
|
+
}
|
|
49503
49920
|
/************************************************************
|
|
49504
49921
|
* Compute the canvas (world) position of a port on a node.
|
|
49505
49922
|
* Returns {x, y} in world coordinates.
|
|
@@ -49532,6 +49949,26 @@ function get_port_radius(gobj, node_id, port_key) {
|
|
|
49532
49949
|
return style.portR || 6;
|
|
49533
49950
|
}
|
|
49534
49951
|
/************************************************************
|
|
49952
|
+
* How much wider than the port itself its hit area is,
|
|
49953
|
+
* measured on the SCREEN and converted to world units.
|
|
49954
|
+
*
|
|
49955
|
+
* It used to be a flat `+4` in WORLD units, which is a
|
|
49956
|
+
* different target on every zoom: at the 50% a phone lands on
|
|
49957
|
+
* after fit, a 6-unit port plus 4 is a 5px radius -- one
|
|
49958
|
+
* fifth of a fingertip, and picking a port is the first step
|
|
49959
|
+
* of every link.
|
|
49960
|
+
************************************************************/
|
|
49961
|
+
function port_hit_slop_world(graph) {
|
|
49962
|
+
let slop = coarse_pointer() ? 22 : 6;
|
|
49963
|
+
let zoom = 1;
|
|
49964
|
+
try {
|
|
49965
|
+
zoom = graph.getZoom() || 1;
|
|
49966
|
+
} catch (e) {
|
|
49967
|
+
zoom = 1;
|
|
49968
|
+
}
|
|
49969
|
+
return slop / zoom;
|
|
49970
|
+
}
|
|
49971
|
+
/************************************************************
|
|
49535
49972
|
* Detect if a click in canvas coordinates hits a port.
|
|
49536
49973
|
* Returns the port key string, or null if no port hit.
|
|
49537
49974
|
************************************************************/
|
|
@@ -49544,6 +49981,7 @@ function detect_port_click(gobj, node_id, canvasX, canvasY) {
|
|
|
49544
49981
|
const defaultR = style.portR || 6;
|
|
49545
49982
|
let best_key = null;
|
|
49546
49983
|
let best_dist = Infinity;
|
|
49984
|
+
let slop = port_hit_slop_world(graph);
|
|
49547
49985
|
for (let i = 0; i < ports.length; i++) {
|
|
49548
49986
|
let pl = ports[i].placement || [.5, .5];
|
|
49549
49987
|
let px = pos[0] + (pl[0] - .5) * w;
|
|
@@ -49552,7 +49990,7 @@ function detect_port_click(gobj, node_id, canvasX, canvasY) {
|
|
|
49552
49990
|
let dx = canvasX - px;
|
|
49553
49991
|
let dy = canvasY - py;
|
|
49554
49992
|
let dist = Math.sqrt(dx * dx + dy * dy);
|
|
49555
|
-
if (dist <= r +
|
|
49993
|
+
if (dist <= r + slop && dist < best_dist) {
|
|
49556
49994
|
best_dist = dist;
|
|
49557
49995
|
best_key = ports[i].key;
|
|
49558
49996
|
}
|
|
@@ -49600,7 +50038,7 @@ function show_link_icon_if_fkey(gobj) {
|
|
|
49600
50038
|
let canvasPos = get_port_canvas_position(gobj, priv._selected_node_id, priv._selected_port_key);
|
|
49601
50039
|
if (!canvasPos) return;
|
|
49602
50040
|
let vp = graph.getViewportByCanvas([canvasPos.x, canvasPos.y]);
|
|
49603
|
-
let icon = create_floating_icon("link", "#52c41a", vp[0] +
|
|
50041
|
+
let icon = create_floating_icon("link", "#52c41a", vp[0] + floating_icon_size() / 2 + 4, vp[1] + floating_icon_dy(), t("link to hook"), () => {
|
|
49604
50042
|
if (!priv._linking_mode) enter_linking_mode(gobj);
|
|
49605
50043
|
});
|
|
49606
50044
|
icon.addEventListener("pointerdown", (e) => {
|
|
@@ -49625,8 +50063,8 @@ function update_link_icon_position(gobj) {
|
|
|
49625
50063
|
return;
|
|
49626
50064
|
}
|
|
49627
50065
|
let vp = graph.getViewportByCanvas([canvasPos.x, canvasPos.y]);
|
|
49628
|
-
priv._link_icon_el.style.left = vp[0] +
|
|
49629
|
-
priv._link_icon_el.style.top = vp[1]
|
|
50066
|
+
priv._link_icon_el.style.left = vp[0] + floating_icon_size() / 2 + 4 + "px";
|
|
50067
|
+
priv._link_icon_el.style.top = vp[1] + floating_icon_dy() + "px";
|
|
49630
50068
|
} catch (e) {
|
|
49631
50069
|
hide_link_icon(gobj);
|
|
49632
50070
|
}
|
|
@@ -49894,10 +50332,15 @@ function show_port_resize_handles(gobj) {
|
|
|
49894
50332
|
dy: 0
|
|
49895
50333
|
}
|
|
49896
50334
|
];
|
|
50335
|
+
const hit = port_handle_hit();
|
|
50336
|
+
const visual = port_handle_visual();
|
|
49897
50337
|
const handles = [];
|
|
49898
50338
|
for (const def of handle_defs) {
|
|
49899
50339
|
const el = document.createElement("div");
|
|
49900
|
-
el.style.cssText = "position:absolute;width:
|
|
50340
|
+
el.style.cssText = "position:absolute;width:" + hit + "px;height:" + hit + "px;display:flex;align-items:center;justify-content:center;cursor:" + def.cursor + ";pointer-events:all;touch-action:none;box-sizing:border-box;";
|
|
50341
|
+
const mark = document.createElement("div");
|
|
50342
|
+
mark.style.cssText = "width:" + visual + "px;height:" + visual + "px;background:#fff;border:1px solid #fa8c16;border-radius:50%;pointer-events:none;box-sizing:border-box;";
|
|
50343
|
+
el.appendChild(mark);
|
|
49901
50344
|
el.addEventListener("pointerdown", (e) => {
|
|
49902
50345
|
start_port_resize(gobj, e);
|
|
49903
50346
|
});
|
|
@@ -49951,7 +50394,7 @@ function update_port_resize_handles_position(gobj) {
|
|
|
49951
50394
|
************************************************************/
|
|
49952
50395
|
function apply_port_handles(gobj, cx, cy, vpR) {
|
|
49953
50396
|
let priv = gobj.priv;
|
|
49954
|
-
const HALF =
|
|
50397
|
+
const HALF = port_handle_hit() / 2;
|
|
49955
50398
|
const ring = priv._port_ring;
|
|
49956
50399
|
if (ring) {
|
|
49957
50400
|
const d = vpR * 2;
|
|
@@ -50096,7 +50539,7 @@ function show_edge_icon(gobj) {
|
|
|
50096
50539
|
if (!priv._selected_edge_id) return;
|
|
50097
50540
|
let mid = get_edge_viewport_midpoint(gobj, priv._selected_edge_id);
|
|
50098
50541
|
if (!mid) return;
|
|
50099
|
-
let icons = show_dual_icons(gobj, mid.x + 4, mid.y
|
|
50542
|
+
let icons = show_dual_icons(gobj, mid.x + 4, mid.y + floating_icon_dy(), t("edge properties"), () => toggle_edge_popover(gobj), t("unlink"), () => request_unlink_edge(gobj));
|
|
50100
50543
|
priv._edge_icon_el = icons.icon_el;
|
|
50101
50544
|
priv._edge_delete_el = icons.delete_el;
|
|
50102
50545
|
}
|
|
@@ -50114,14 +50557,14 @@ function update_edge_icon_position(gobj) {
|
|
|
50114
50557
|
return;
|
|
50115
50558
|
}
|
|
50116
50559
|
priv._edge_icon_el.style.left = mid.x + 4 + "px";
|
|
50117
|
-
priv._edge_icon_el.style.top = mid.y
|
|
50560
|
+
priv._edge_icon_el.style.top = mid.y + floating_icon_dy() + "px";
|
|
50118
50561
|
if (priv._edge_delete_el) {
|
|
50119
50562
|
priv._edge_delete_el.style.left = mid.x + 4 + "px";
|
|
50120
|
-
priv._edge_delete_el.style.top = mid.y +
|
|
50563
|
+
priv._edge_delete_el.style.top = mid.y + floating_icon_dy() + floating_icon_step() + "px";
|
|
50121
50564
|
}
|
|
50122
50565
|
if (priv._edge_popover_el) {
|
|
50123
|
-
priv._edge_popover_el.style.left = mid.x +
|
|
50124
|
-
priv._edge_popover_el.style.top = mid.y
|
|
50566
|
+
priv._edge_popover_el.style.left = mid.x + floating_popover_dx() + "px";
|
|
50567
|
+
priv._edge_popover_el.style.top = mid.y + floating_icon_dy() + "px";
|
|
50125
50568
|
clamp_popover_position(gobj, priv._edge_popover_el);
|
|
50126
50569
|
}
|
|
50127
50570
|
} catch (e) {
|
|
@@ -50159,7 +50602,7 @@ function show_edge_popover(gobj) {
|
|
|
50159
50602
|
});
|
|
50160
50603
|
let mid = get_edge_viewport_midpoint(gobj, edge_id);
|
|
50161
50604
|
if (!mid) return;
|
|
50162
|
-
const popover = create_popover_base(mid.x +
|
|
50605
|
+
const popover = create_popover_base(mid.x + floating_popover_dx(), mid.y + floating_icon_dy(), "g6-edge-popover", "#d9d9d9", 180);
|
|
50163
50606
|
function preview_edge() {
|
|
50164
50607
|
let lw = parseInt(lwInput.value) || 2;
|
|
50165
50608
|
let color = colorInput.value;
|
|
@@ -50246,7 +50689,7 @@ function show_node_icon(gobj) {
|
|
|
50246
50689
|
if (!priv._selected_node_id || priv._selected_port_key) return;
|
|
50247
50690
|
let rect = get_node_viewport_rect(gobj, priv._selected_node_id);
|
|
50248
50691
|
if (!rect) return;
|
|
50249
|
-
let icons = show_dual_icons(gobj, rect.right + 4, rect.top
|
|
50692
|
+
let icons = show_dual_icons(gobj, rect.right + 4, rect.top + floating_icon_dy(), t("node properties"), () => toggle_node_popover(gobj), t("delete node"), () => request_delete_node(gobj));
|
|
50250
50693
|
priv._node_icon_el = icons.icon_el;
|
|
50251
50694
|
priv._node_delete_el = icons.delete_el;
|
|
50252
50695
|
}
|
|
@@ -50268,14 +50711,14 @@ function update_node_icon_position(gobj) {
|
|
|
50268
50711
|
return;
|
|
50269
50712
|
}
|
|
50270
50713
|
priv._node_icon_el.style.left = rect.right + 4 + "px";
|
|
50271
|
-
priv._node_icon_el.style.top = rect.top
|
|
50714
|
+
priv._node_icon_el.style.top = rect.top + floating_icon_dy() + "px";
|
|
50272
50715
|
if (priv._node_delete_el) {
|
|
50273
50716
|
priv._node_delete_el.style.left = rect.right + 4 + "px";
|
|
50274
|
-
priv._node_delete_el.style.top = rect.top +
|
|
50717
|
+
priv._node_delete_el.style.top = rect.top + floating_icon_dy() + floating_icon_step() + "px";
|
|
50275
50718
|
}
|
|
50276
50719
|
if (priv._node_popover_el) {
|
|
50277
|
-
priv._node_popover_el.style.left = rect.right +
|
|
50278
|
-
priv._node_popover_el.style.top = rect.top
|
|
50720
|
+
priv._node_popover_el.style.left = rect.right + floating_popover_dx() + "px";
|
|
50721
|
+
priv._node_popover_el.style.top = rect.top + floating_icon_dy() + "px";
|
|
50279
50722
|
clamp_popover_position(gobj, priv._node_popover_el);
|
|
50280
50723
|
}
|
|
50281
50724
|
} catch (e) {
|
|
@@ -50308,7 +50751,7 @@ function show_node_popover(gobj) {
|
|
|
50308
50751
|
let origLW = currentLW;
|
|
50309
50752
|
let rect = get_node_viewport_rect(gobj, node_id);
|
|
50310
50753
|
if (!rect) return;
|
|
50311
|
-
const popover = create_popover_base(rect.right +
|
|
50754
|
+
const popover = create_popover_base(rect.right + floating_popover_dx(), rect.top + floating_icon_dy(), "g6-node-popover", "#d9d9d9", 180);
|
|
50312
50755
|
let node_graph_type = nodeData.data && nodeData.data.desc ? nodeData.data.desc.node_treedb_type : null;
|
|
50313
50756
|
function preview_node() {
|
|
50314
50757
|
let fill = fillInput.value;
|
|
@@ -50939,7 +51382,7 @@ function show_create_popover(gobj) {
|
|
|
50939
51382
|
topicLabel.textContent = t("topic") + ":";
|
|
50940
51383
|
popover.appendChild(topicLabel);
|
|
50941
51384
|
let topicSelect = document.createElement("select");
|
|
50942
|
-
topicSelect.style.cssText = "width:100%;padding:6px;border:1px solid #d9d9d9;border-radius:4px;font-size:13px;margin-bottom:8px;";
|
|
51385
|
+
topicSelect.style.cssText = "width:100%;padding:6px;border:1px solid #d9d9d9;border-radius:4px;font-size:13px;margin-bottom:8px;" + touch_control_css();
|
|
50943
51386
|
for (let i = 0; i < topics.length; i++) {
|
|
50944
51387
|
let opt = document.createElement("option");
|
|
50945
51388
|
opt.value = topics[i].topic_name;
|
|
@@ -50972,7 +51415,7 @@ function show_create_popover(gobj) {
|
|
|
50972
51415
|
let idInput = document.createElement("input");
|
|
50973
51416
|
idInput.type = "text";
|
|
50974
51417
|
idInput.placeholder = t("node id");
|
|
50975
|
-
idInput.style.cssText = "width:100%;padding:6px;border:1px solid #d9d9d9;border-radius:4px;font-size:13px;margin-bottom:10px;box-sizing:border-box;";
|
|
51418
|
+
idInput.style.cssText = "width:100%;padding:6px;border:1px solid #d9d9d9;border-radius:4px;font-size:13px;margin-bottom:10px;box-sizing:border-box;" + touch_control_css();
|
|
50976
51419
|
popover.appendChild(idInput);
|
|
50977
51420
|
let errorEl = document.createElement("div");
|
|
50978
51421
|
errorEl.style.cssText = "color:#ff4d4f;font-size:12px;margin-bottom:8px;display:none;";
|
|
@@ -51662,6 +52105,11 @@ function ac_resize(gobj, event, kw, src) {
|
|
|
51662
52105
|
if (h < 0) h = 80;
|
|
51663
52106
|
graph_resize(gobj, rect.width, h);
|
|
51664
52107
|
}
|
|
52108
|
+
let can_collapse = toolbars_can_collapse(gobj);
|
|
52109
|
+
if (can_collapse !== priv._toolbars_could_collapse) {
|
|
52110
|
+
priv._toolbars_could_collapse = can_collapse;
|
|
52111
|
+
update_toolbar(gobj);
|
|
52112
|
+
}
|
|
51665
52113
|
return 0;
|
|
51666
52114
|
}
|
|
51667
52115
|
/************************************************************
|
|
@@ -51700,6 +52148,19 @@ function ac_zoom_reset(gobj, event, kw, src) {
|
|
|
51700
52148
|
gobj.priv.graph.zoomTo(1);
|
|
51701
52149
|
return 0;
|
|
51702
52150
|
}
|
|
52151
|
+
/************************************************************
|
|
52152
|
+
* Fold / unfold the floating toolbars.
|
|
52153
|
+
*
|
|
52154
|
+
* One flag for BOTH: they are one wall over the canvas, and
|
|
52155
|
+
* the button that unfolds them has to be the same button
|
|
52156
|
+
* whichever edge the user reaches for.
|
|
52157
|
+
************************************************************/
|
|
52158
|
+
function ac_toggle_toolbars(gobj, event, kw, src) {
|
|
52159
|
+
let priv = gobj.priv;
|
|
52160
|
+
priv.toolbar_collapsed = !toolbars_collapsed(gobj);
|
|
52161
|
+
update_toolbar(gobj);
|
|
52162
|
+
return 0;
|
|
52163
|
+
}
|
|
51703
52164
|
function ac_auto_fit(gobj, event, kw, src) {
|
|
51704
52165
|
graph_fitview(gobj);
|
|
51705
52166
|
return 0;
|
|
@@ -52084,6 +52545,11 @@ function create_gclass$4(gclass_name) {
|
|
|
52084
52545
|
ac_zoom_reset,
|
|
52085
52546
|
null
|
|
52086
52547
|
],
|
|
52548
|
+
[
|
|
52549
|
+
"EV_TOGGLE_TOOLBARS",
|
|
52550
|
+
ac_toggle_toolbars,
|
|
52551
|
+
null
|
|
52552
|
+
],
|
|
52087
52553
|
[
|
|
52088
52554
|
"EV_AUTO_FIT",
|
|
52089
52555
|
ac_auto_fit,
|
|
@@ -52187,6 +52653,7 @@ function create_gclass$4(gclass_name) {
|
|
|
52187
52653
|
["EV_ZOOM_IN", 0],
|
|
52188
52654
|
["EV_ZOOM_OUT", 0],
|
|
52189
52655
|
["EV_ZOOM_RESET", 0],
|
|
52656
|
+
["EV_TOGGLE_TOOLBARS", 0],
|
|
52190
52657
|
["EV_AUTO_FIT", 0],
|
|
52191
52658
|
["EV_ZOOM_SELECTION", 0],
|
|
52192
52659
|
["EV_FOCUS_TOPIC", 0],
|