@linkurious/ogma-react
Version:
A light adaptation of Ogma for React application
744 lines (743 loc) • 20.3 kB
JavaScript
import H, { createContext as Q, useContext as X, memo as V, forwardRef as S, useRef as U, useState as y, useImperativeHandle as C, useEffect as a, useLayoutEffect as p, useCallback as ee } from "react";
import { Ogma as te, Node as j, Edge as J } from "@linkurious/ogma";
import { renderToString as F } from "react-dom/server";
import { createPortal as G } from "react-dom";
const K = Q(null), w = () => {
const e = X(K);
if (!e) throw new Error("useOgma must be used within an OgmaProvider");
return e.ogma;
};
function z(e, n) {
Object.keys(e).forEach((t) => {
const o = e[t];
o && n(t, o);
});
}
function ne(e) {
return e.startsWith("on") && e.length > 2 ? e[2].toLowerCase() + e.substring(3) : null;
}
function I(e, n, t) {
const o = {};
Object.keys(n).forEach((i) => {
if (!i.startsWith("on")) return;
const r = ne(i), f = n[i];
r && typeof f == "function" && (o[r] = f);
}), z(t, (i, s) => {
o[i] || (e.events.off(s), delete t[i]);
}), z(o, (i, s) => {
const r = t[i];
r && r !== s && e.events.off(r), (!r || r !== s) && (e.events.on(i, s), t[i] = s);
});
}
function R(e, n) {
if (e === n)
return !0;
try {
return JSON.stringify(e) === JSON.stringify(n);
} catch {
return !1;
}
}
const _ = {}, oe = (e, n) => {
const {
options: t = _,
children: o,
graph: i,
onReady: s,
theme: r,
className: f = "ogma-container"
} = e, d = U({}), c = U(null), [m, P] = y(), [k, u] = y(), [T, h] = y(_), [L, b] = y();
return C(n, () => m, [m]), a(() => {
if (!c.current || m) return;
const g = new te({
container: c.current,
graph: i,
options: t
});
u(i), h(t), r && (b(r), g.styles.setTheme(r)), I(g, e, d.current), P(g), s && s(g);
}, [c.current]), p(() => {
const g = () => m?.view.forceResize();
return g(), window.addEventListener("resize", g), () => window.removeEventListener("resize", g);
}, []), a(() => {
m && (i && R(i, k) === !1 && (u(i), m.setGraph(i)), t && R(t, T) === !1 && (h(t), m.setOptions(t)));
}, [i, t]), a(() => {
m && r && R(r, L) === !1 && (b(r), m.styles.setTheme(r));
}, [r]), a(() => {
m && I(m, e, d.current);
}, [e]), /* @__PURE__ */ H.createElement(
"div",
{
style: { width: "100%", height: "100%" },
className: f,
ref: c
},
m && /* @__PURE__ */ H.createElement(
K.Provider,
{
value: {
ogma: m
}
},
o
)
);
}, ze = V(S(oe));
function Ie(e, n, t) {
return ee(n, t || []);
}
const se = S(
({ selector: e, attributes: n }, t) => {
const o = w(), [i, s] = y();
return C(t, () => i, [i]), a(() => {
const r = e ? o.styles.addNodeRule(e, n) : o.styles.addNodeRule(n);
return s(r), () => {
r.destroy(), s(void 0);
};
}, [e, n]), null;
}
), ie = ({
attributes: e,
fullOverwrite: n
}) => {
const t = w();
return a(() => (t.styles.setHoveredNodeAttributes(e, n), () => {
t.styles.setHoveredNodeAttributes(null);
}), [e, n]), null;
}, re = ({
attributes: e,
fullOverwrite: n
}) => {
const t = w();
return a(() => (t.styles.setSelectedNodeAttributes(e, n), () => {
t.styles.setSelectedNodeAttributes(null);
}), [e, n]), null;
}, _e = Object.assign(
se,
{
Hovered: ie,
Selected: re
}
), ae = S(
({ selector: e, attributes: n }, t) => {
const o = w(), [i, s] = y();
return C(t, () => i, [i]), a(() => {
const r = e ? o.styles.addEdgeRule(e, n) : o.styles.addEdgeRule(n);
return s(r), () => {
r.destroy(), s(void 0);
};
}, [e, n]), null;
}
), le = ({
attributes: e,
fullOverwrite: n
}) => {
const t = w();
return a(() => (t.styles.setHoveredEdgeAttributes(e, n), () => {
t.styles.setHoveredEdgeAttributes(null);
}), [e, n]), null;
}, de = ({
attributes: e,
fullOverwrite: n
}) => {
const t = w();
return a(() => (t.styles.setSelectedEdgeAttributes(e, n), () => {
t.styles.setSelectedEdgeAttributes(null);
}), [e, n]), null;
}, qe = Object.assign(
ae,
{
Hovered: le,
Selected: de
}
), ce = S(({
name: e,
edgeAttributes: n,
edgeDependencies: t,
edgeOutput: o,
nodeAttributes: i,
nodeDependencies: s,
nodeOutput: r
}, f) => {
const d = w(), [c, m] = y(
null
);
return C(f, () => c, [
c
]), a(() => {
const P = {
edgeAttributes: n,
edgeDependencies: t,
edgeOutput: o,
nodeAttributes: i,
nodeDependencies: s,
nodeOutput: r
};
async function k() {
const u = d.styles.createClass({ name: e, ...P });
await d.view.afterNextFrame(), m(u);
}
return k(), () => {
async function u() {
const T = d.styles.getClass(e);
T && await T.destroy(), m(null);
}
u();
};
}, []), a(() => {
c && c.update({
edgeAttributes: n,
edgeDependencies: t,
edgeOutput: o,
nodeAttributes: i,
nodeDependencies: s,
nodeOutput: r
});
}, [
n,
t,
o,
i,
s,
r
]), null;
}), ue = (e, n) => e.name === n.name && e.edgeAttributes === n.edgeAttributes && e.edgeDependencies === n.edgeDependencies && e.edgeOutput === n.edgeOutput && e.nodeAttributes === n.nodeAttributes && e.nodeDependencies === n.nodeDependencies && e.nodeOutput === n.nodeOutput, Me = V(ce, ue);
function fe(e, n) {
const t = w(), [o, i] = y(null);
return C(n, () => o, [o]), a(() => {
const { disabled: s, ...r } = e, f = t.transformations.addDrillDown({
...r,
enabled: !s
});
return i(f), () => {
f.destroy(), i(null);
};
}, []), a(() => {
if (o) {
const s = !!e.disabled, r = e.duration;
s ? o.disable(r) : o.enable(r);
}
}, [e.disabled]), a(() => {
const { disabled: s, ...r } = e;
o?.setOptions(r);
}, [
e.copyData,
e.depthPath,
e.duration,
e.easing,
e.nodeGenerator,
e.onDestroyed,
e.onDisabled,
e.onEnabled,
e.onGetSubgraph,
e.onGroupUpdate,
e.onSetIndex,
e.onUpdated,
e.padding,
e.parentPath,
e.showContents
]), null;
}
const Be = S(
fe
);
function x(e, n, t) {
n === e.isEnabled() && (n ? e.disable(t) : e.enable(t));
}
function N(e, n, t) {
const o = ({ target: c }) => {
c === n && e.onEnabled && e.onEnabled(n);
}, i = ({ target: c }) => {
c === n && e.onDisabled && e.onDisabled(n);
}, s = ({ target: c }) => {
c === n && e.onUpdated && e.onUpdated(n);
}, r = ({
target: c,
index: m
}) => {
c === n && e.onSetIndex && e.onSetIndex(n, m);
}, f = ({
target: c
}) => {
c === n && (e.onDestroyed && e.onDestroyed(n), t.events.off(o).off(i).off(s).off(r).off(f));
};
return t.events.on("transformationEnabled", o).on("transformationDisabled", i).on("transformationDestroyed", f).on("transformationSetIndex", r).on("transformationRefresh", s), () => {
t.events.off(o).off(i).off(s).off(r).off(f);
};
}
function ge(e, n) {
const t = w(), [o, i] = y();
return C(n, () => o, [o]), a(() => {
const s = t.transformations.addNodeGrouping({
...e,
enabled: !e.disabled
});
return N(e, s, t), i(s), () => {
s.destroy(), i(void 0);
};
}, []), a(() => {
o && x(o, !!e.disabled, e.duration);
}, [e.disabled, e.duration]), a(() => {
o?.setOptions(e);
}, [
e.groupIdFunction,
e.groupSelfLoopEdges,
e.edgeGenerator,
e.nodeGenerator,
e.groupEdges,
e.padding,
e.selector,
e.showContents,
e.separateEdgesByDirection
]), null;
}
const Ze = S(ge);
function me(e, n) {
const t = w(), [o, i] = y();
return C(n, () => o, [o]), a(() => {
const s = t.transformations.addEdgeGrouping({
...e,
enabled: !e.disabled
});
return N(e, s, t), i(s), () => {
s.destroy(), i(void 0);
};
}, []), a(() => {
o && x(o, !!e.disabled, e.duration);
}, [e.disabled, e.duration]), a(() => {
o?.setOptions(e);
}, [
e.selector,
e.generator,
e.groupIdFunction,
e.separateEdgesByDirection
]), null;
}
const Ve = S(me);
function ye(e, n) {
const t = w(), [o, i] = y();
return C(n, () => o, [o]), a(() => {
const s = t.transformations.addNodeCollapsing({
...e,
enabled: !e.disabled
});
return N(e, s, t), i(s), () => {
s.destroy(), i(void 0);
};
}, []), a(() => {
o && x(o, !!e.disabled, e.duration);
}, [e.disabled, e.duration]), a(() => {
o?.setOptions(e);
}, [e.edgeGenerator, e.selector]), null;
}
const je = S(ye);
function he(e, n) {
const t = w(), [o, i] = y();
return C(n, () => o, [o]), a(() => {
const s = t.transformations.addNeighborMerging({
...e,
enabled: !e.disabled
});
return N(e, s, t), i(s), () => {
s.destroy(), i(void 0);
};
}, []), a(() => {
o && x(o, !!e.disabled, e.duration);
}, [e.disabled, e.duration]), a(() => {
o?.setOptions(e);
}, [e.dataFunction, e.selector]), null;
}
const Je = S(he);
function be(e, n) {
const t = w(), [o, i] = y();
return C(n, () => o, [o]), a(() => {
const s = t.transformations.addNeighborGeneration({
...e,
enabled: !e.disabled
});
return N(e, s, t), i(s), () => {
s.destroy(), i(void 0);
};
}, []), a(() => {
o && x(o, !!e.disabled, e.duration);
}, [e.disabled, e.duration]), a(() => {
o?.setOptions(e);
}, [
e.edgeGenerator,
e.nodeGenerator,
e.neighborIdFunction,
e.selector
]), null;
}
const Ke = S(be);
function Ee(e, n) {
const t = w(), [o, i] = y();
return C(n, () => o, [o]), a(() => {
const s = t.transformations.addEdgeFilter({
...e,
enabled: !e.disabled
});
return N(e, s, t), i(s), () => {
s.destroy(), i(void 0);
};
}, []), a(() => {
o && x(o, !!e.disabled, e.duration);
}, [e.disabled]), a(() => {
o?.setOptions(e);
}, [e.criteria]), null;
}
const Ye = S(Ee);
function we(e, n) {
const t = w(), [o, i] = y();
return C(n, () => o, [o]), a(() => {
const s = t.transformations.addNodeFilter({
...e,
enabled: !e.disabled
});
return N(e, s, t), i(s), () => {
s.destroy(), i(void 0);
};
}, []), a(() => {
o && x(o, !!e.disabled, e.duration);
}, [e.disabled]), a(() => {
o?.setOptions(e);
}, [e.criteria]), null;
}
const Qe = S(we);
function Se(e, n, t, o) {
return typeof t == "string" ? t : typeof t == "function" ? F(t(e, n)) : F(o);
}
function D(e, n) {
return typeof e == "function" ? e(n) : e;
}
const q = (e, n) => `${e} ${e}--${n}`;
function Ce(e = "×", n) {
if (e) {
const t = typeof e == "string" ? e : F(e);
return `<div class=${n}>${t}</div>`;
}
return "";
}
function Te(e) {
return e.endsWith("Doubleclick") ? "doubleclick" : e.toLowerCase().endsWith("click") ? "click" : "mouseover";
}
function ve(e, n) {
if (e instanceof j) {
const t = e.getAttribute("radius"), o = M("outer", e, n), i = M("inner", e, n);
return t + o + i;
} else if (e instanceof J)
return e.size;
return 0;
}
function M(e, n, t) {
const o = n.getAttribute(
`${e}Stroke.width`
);
return n.getAttribute(`${e}Stroke.minVisibleSize`) > n.size ? 0 : n.getAttribute(`${e}Stroke.scalingMethod`) === "fixed" ? o : o / t;
}
function B(e, n, t) {
const o = ve(e, n), i = { x: 0, y: 0 };
switch (t) {
case "top":
i.y = -o;
break;
case "bottom":
i.y = o;
break;
case "left":
i.x = -o;
break;
case "right":
i.x = o;
break;
}
return i;
}
function Oe(e, n, t) {
return e.left < 0 ? "right" : e.right > n ? "left" : e.top < 0 ? "bottom" : e.bottom > t ? "top" : null;
}
function Z(e, n, t) {
switch (e || n) {
case "top":
return `translate(calc(-50% + ${t.x}px),calc(-100% + ${t.y}px))`;
case "bottom":
return `translate(calc(-50% + ${t.x}px),${t.y}px)`;
case "left":
return `translate(calc(-100% + ${t.x}px),calc(-50% + ${t.y}px))`;
default:
return `translate(${t.x}px,calc(-50% + ${t.y}px))`;
}
}
const W = { x: -9999, y: -9999 }, Pe = "ogma-popup--content", Le = "ogma-popup--close", ke = "ogma-popup--body", $e = "ogma-popup", xe = ({
content: e,
position: n,
children: t,
isOpen: o = !0,
closeButton: i,
onClose: s = () => {
},
placement: r = "top",
popupClass: f = $e,
closeButtonClass: d = Le,
contentClass: c = Pe,
popupBodyClass: m = ke,
size: P,
closeOnEsc: k = !0
}, u) => {
const T = w(), [h, L] = y(null);
if (C(u, () => h, [h]), a(() => {
let g = null, v = null, l = null;
if (o) {
const E = D(n, T) || W;
if (g = T.layers.addOverlay({
position: E || W,
element: `<div class="${q(f, r)}"/>
<div class="${m}">
${Ce(i, d)}
<div class="${c} "></div>
</div>
</div>`,
size: P || { width: "auto", height: "auto" },
scaled: !1
}), l = (O) => {
const $ = g?.element.querySelector(
`.${d}`
);
O.target && $.contains(O.target) && (O.stopPropagation(), O.preventDefault(), s());
}, v = ({ code: O }) => {
O === 27 && s();
}, k && T.events.on("keyup", v), g.element.addEventListener("click", l), L(g), e && !t) {
const O = g.element.querySelector(
`.${c}`
), $ = Se(T, E, e, t);
O && (O.innerHTML = $);
}
L(g);
}
return () => {
g && (l && g.element.removeEventListener("click", l), v && T.events.off(v), g.destroy(), L(null));
};
}, [o, T]), a(() => {
if (h && h.element) {
const g = D(n, T) || W;
if (h.setPosition(g), h.element.className = q(f, r), e && !t) {
const v = h.element.querySelector(`.${c}`);
v && (v.innerHTML = typeof e == "string" ? e : "");
}
o ? h.show() : h.hide();
}
}, [h, n, o, r, f, e, t]), !h || !o) return null;
const b = h.element.querySelector(`.${c}`);
return b && t ? G(t, b) : null;
}, Xe = S(xe), Ne = { x: -9999, y: -9999 }, Ge = ({
eventName: e,
position: n,
children: t,
placement: o = "top",
bodyClass: i = "ogma-tooltip--body",
translate: s = { x: 0, y: 0 },
size: r
}, f) => {
const d = w(), [c, m] = y(), [P, k] = y(), [u, T] = y(null);
C(f, () => u, [u]);
function h(b, g) {
if (n)
u?.show();
else {
const E = d.geo.enabled() ? d.geo.getZoom() : d.view.getZoom(), O = B(b, E, o), $ = {
x: g.x + O.x,
y: g.y + O.y
};
k(g), u?.setPosition($);
}
const v = Z(null, o, s), l = u.element.children[0];
l.style.transform = v;
}
function L() {
u?.hide(), m(void 0);
}
if (a(() => {
const b = d.layers.addOverlay({
position: n || Ne,
element: `
<div style="pointer-events: none">
<div class="${i}" style="pointer-events: auto">
</div>
</div>`,
size: r || { width: "auto", height: "auto" },
scaled: !1
});
T(b);
}, []), a(() => {
if (!u) return;
let b = () => null, g = () => null;
const v = Te(e);
return v === "mouseover" ? (b = (l) => {
if (e.startsWith("node")) {
if (l.target?.isNode) {
const E = l.target;
m(E), h(E, E.getPosition());
}
} else if (e.startsWith("edge") && l.target && !l.target.isNode) {
const E = d.view.screenToGraphCoordinates({
x: l.x,
y: l.y
});
m(l.target), h(l.target, E);
}
}, g = (l) => {
(e.startsWith("node") && l.target?.isNode || e.startsWith("edge") && l.target && !l.target.isNode) && L();
}, d.events.on("mouseout", g)) : (b = (l) => {
if (!(e.endsWith("Rightclick") && l.button === "left")) {
if (e.startsWith("background")) {
if (!l.target) {
const E = d.view.screenToGraphCoordinates({
x: l.x,
y: l.y
});
m(E), h("background", E);
}
} else if (e.startsWith("node")) {
if (l.target?.isNode) {
const E = l.target;
m(E), h(E, E.getPosition());
}
} else if (l.target && !l.target.isNode) {
const E = d.view.screenToGraphCoordinates({
x: l.x,
y: l.y
});
m(l.target), h(l.target, E);
}
}
}, g = (l) => {
e.startsWith("node") ? l.target?.isNode || L() : e.startsWith("edge") ? (!l.target || l.target.isNode) && L() : e.startsWith("background") && (l.target || e.endsWith("Rightclick") && l.button === "left" || e.endsWith("Click") && l.button === "right") && L();
}, d.events.on("click", g)), u.hide(), d.events.on(v, b), () => {
d.events.off(b), d.events.off(g), u && (u.destroy(), T(null));
};
}, [u]), a(() => {
!u || !u.element || (n && u.setPosition(n), (o || i) && (u.element.firstElementChild.className = "ogma-popup--body " + i), r && u.setSize(r));
}, [n, o, r, i]), a(() => {
if (!u || !u.element || !c) return;
const b = u.element, g = b.children[0].getBoundingClientRect(), v = b.ownerDocument.defaultView, l = Oe(
g,
v.innerWidth,
v.innerHeight
);
if (l) {
const E = d.geo.enabled() ? d.geo.getZoom() : d.view.getZoom(), O = c instanceof j || c instanceof J ? c : "background", $ = B(O, E, l);
u?.setPosition({
x: P.x + $.x,
y: P.y + $.y
});
const Y = Z(l, o, s);
b.children[0].style.transform = Y;
}
u.show();
}, [P]), !u || !u.element) return null;
if (t instanceof Function) {
if (!c) return null;
const b = t(c);
return b === null ? (u.hide(), null) : G(b, u.element.firstElementChild);
} else
return t ? G(t, u.element.firstElementChild) : null;
}, pe = S(Ge), De = ({
noClear: e = !1,
isStatic: n = !1,
render: t,
index: o,
visible: i = !0
}, s) => {
const r = w(), [f, d] = y(null);
return C(s, () => f, [f]), a(() => {
const c = r.layers.addCanvasLayer(
t,
{ isStatic: n, noClear: e },
o
);
return d(c), () => {
c && (c.destroy(), d(null));
};
}, []), a(() => {
f && (o !== void 0 && isFinite(o) && f.moveTo(o), i ? f.show() : f.hide());
}, [f, o, i]), null;
}, et = S(De), A = { x: -9999, y: -9999 }, Re = S(
({
position: e,
children: n,
className: t = "",
size: o,
scaled: i,
index: s
}, r) => {
const f = w(), [d, c] = y(null);
return C(r, () => d, [d]), a(() => {
const m = D(e, f) || A, P = document.createElement("div");
P.className = t;
const k = f.layers.addOverlay(
{
position: m || A,
element: P,
size: o || { width: "auto", height: "auto" },
scaled: i
},
s
);
return c(k), () => {
d && (d.destroy(), c(null));
};
}, []), a(() => {
if (d) {
const m = D(e, f) || A;
t && (d.element.className = t), s !== void 0 && isFinite(s) && d.moveTo(s), d.setPosition(m);
}
}, [e, t, s]), d ? G(n, d.element) : null;
}
), tt = Re, We = S(
({ children: e, className: n = "", index: t }, o) => {
const i = w(), [s, r] = y(null);
return C(o, () => s, [s]), a(() => {
const f = document.createElement("div");
f.className = n;
const d = i.layers.addLayer(f, t);
return r(d), () => {
s && (s.destroy(), r(null));
};
}, []), a(() => {
s && (n && (s.element.className = n), t !== void 0 && isFinite(t) && s.moveTo(t));
}, [n, t]), s ? G(e, s.element) : null;
}
), nt = We;
function ot({ enabled: e = !1, ...n }) {
const t = w();
return a(() => {
e ? t.geo.enable(n) : t.geo.disable();
}, [e]), null;
}
export {
et as CanvasLayer,
Ye as EdgeFilter,
Ve as EdgeGrouping,
qe as EdgeStyle,
qe as EdgeStyleRule,
ot as Geo,
nt as Layer,
Ke as NeighborGeneration,
Je as NeighborMerging,
je as NodeCollapsing,
ye as NodeCollapsingComponent,
Be as NodeDrilldown,
Qe as NodeFilter,
Ze as NodeGrouping,
_e as NodeStyle,
_e as NodeStyleRule,
ze as Ogma,
tt as Overlay,
Xe as Popup,
Me as StyleClass,
pe as Tooltip,
z as forEachEventHandler,
ne as getEventNameFromProp,
Ie as useEvent,
w as useOgma
};
//# sourceMappingURL=index.mjs.map