From 899fb7048d90881ccfa7346319ae7e01994a87ae Mon Sep 17 00:00:00 2001 From: Benoit Simard Date: Thu, 22 Aug 2024 16:05:58 +0200 Subject: [PATCH] [sigma] mouse event and showdom Fix #1400 --- packages/sigma/src/core/captors/mouse.ts | 2 +- .../stories/events-shadowdom/index.html | 30 +++++ .../stories/events-shadowdom/index.ts | 109 ++++++++++++++++++ .../stories/events-shadowdom/stories.ts | 24 ++++ 4 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 packages/storybook/stories/events-shadowdom/index.html create mode 100644 packages/storybook/stories/events-shadowdom/index.ts create mode 100644 packages/storybook/stories/events-shadowdom/stories.ts diff --git a/packages/sigma/src/core/captors/mouse.ts b/packages/sigma/src/core/captors/mouse.ts index 749d00e72..cbc851f4c 100644 --- a/packages/sigma/src/core/captors/mouse.ts +++ b/packages/sigma/src/core/captors/mouse.ts @@ -238,7 +238,7 @@ export default class MouseCaptor< // Only trigger the "mousemove" event when the mouse is actually hovering // the container, to avoid weirdly hovering nodes and/or edges when the // mouse is not hover the container: - if (e.target === this.container) { + if (e.target === this.container || e.composedPath()[0] === this.container) { this.emit("mousemove", mouseCoords); } diff --git a/packages/storybook/stories/events-shadowdom/index.html b/packages/storybook/stories/events-shadowdom/index.html new file mode 100644 index 000000000..fe558a2dc --- /dev/null +++ b/packages/storybook/stories/events-shadowdom/index.html @@ -0,0 +1,30 @@ + + +
diff --git a/packages/storybook/stories/events-shadowdom/index.ts b/packages/storybook/stories/events-shadowdom/index.ts new file mode 100644 index 000000000..7d1782646 --- /dev/null +++ b/packages/storybook/stories/events-shadowdom/index.ts @@ -0,0 +1,109 @@ +import Graph from "graphology"; +import Sigma from "sigma"; +import { MouseCoords } from "sigma/types"; + +import data from "../_data/data.json"; + +/** + * This is a minimal example of sigma. You can use it as a base to write new + * examples, or reproducible test cases for new issues, for instance. + */ +export default () => { + const logsDOM = document.getElementById("sigma-logs") as HTMLElement; + const graph = new Graph(); + graph.import(data); + + function logEvent(event: string, itemType: "node" | "edge" | "positions", item: string | MouseCoords): void { + const div = document.createElement("div"); + let message = `Event "${event}"`; + if (item && itemType) { + if (itemType === "positions") { + item = item as MouseCoords; + message += `, x ${item.x}, y ${item.y}`; + } else { + const label = + itemType === "node" ? graph.getNodeAttribute(item, "label") : graph.getEdgeAttribute(item, "label"); + message += `, ${itemType} ${label || "with no label"} (id "${item}")`; + + if (itemType === "edge") { + message += `, source ${graph.getSourceAttribute(item, "label")}, target: ${graph.getTargetAttribute( + item, + "label", + )}`; + } + } + } + div.innerHTML = `${message}`; + logsDOM.appendChild(div); + logsDOM.scrollTo({ top: logsDOM.scrollHeight }); + + if (logsDOM.children.length > 50) logsDOM.children[0].remove(); + } + + window.customElements.define( + "sigma-shadow", + class extends HTMLElement { + _container: HTMLDivElement | null = null; + + constructor() { + super(); + } + + connectedCallback() { + const shadowRoot = this.attachShadow({ mode: "open" }); + const container = document.createElement("div"); + container.style.cssText = ` + width: 100%; + height: 100%; + margin: 0; + padding: 0; + overflow: hidden; + `; + this._container = container; + shadowRoot.appendChild(container); + + let hoveredEdge: null | string = null; + const renderer = new Sigma(graph, this._container, { + enableEdgeEvents: true, + edgeReducer(edge, data) { + const res = { ...data }; + if (edge === hoveredEdge) res.color = "#cc0000"; + return res; + }, + }); + + const nodeEvents = [ + "enterNode", + "leaveNode", + "downNode", + "clickNode", + "rightClickNode", + "doubleClickNode", + "wheelNode", + ] as const; + const edgeEvents = ["downEdge", "clickEdge", "rightClickEdge", "doubleClickEdge", "wheelEdge"] as const; + const stageEvents = ["downStage", "clickStage", "doubleClickStage", "wheelStage"] as const; + + nodeEvents.forEach((eventType) => renderer.on(eventType, ({ node }) => logEvent(eventType, "node", node))); + edgeEvents.forEach((eventType) => renderer.on(eventType, ({ edge }) => logEvent(eventType, "edge", edge))); + + renderer.on("enterEdge", ({ edge }) => { + logEvent("enterEdge", "edge", edge); + hoveredEdge = edge; + renderer.refresh(); + }); + renderer.on("leaveEdge", ({ edge }) => { + logEvent("leaveEdge", "edge", edge); + hoveredEdge = null; + renderer.refresh(); + }); + + stageEvents.forEach((eventType) => { + renderer.on(eventType, ({ event }) => { + logEvent(eventType, "positions", event); + }); + }); + } + }, + ); +}; diff --git a/packages/storybook/stories/events-shadowdom/stories.ts b/packages/storybook/stories/events-shadowdom/stories.ts new file mode 100644 index 000000000..81ce9af06 --- /dev/null +++ b/packages/storybook/stories/events-shadowdom/stories.ts @@ -0,0 +1,24 @@ +import { Meta, StoryObj } from "@storybook/html"; + +import play from "."; +import template from "./index.html?raw"; +import source from "./index?raw"; + +const meta: Meta = { + id: "events-shadowdom", + title: "Examples", +}; +export default meta; + +type Story = StoryObj; + +export const story: Story = { + name: "Events ShadowDom", + render: () => template, + play: play, + parameters: { + storySource: { + source: source, + }, + }, +};