upend/webui/src/components/EntitySelect.svelte

179 lines
4.6 KiB
Svelte
Raw Normal View History

<script lang="ts" context="module">
import { writable } from "svelte/store";
import api from "../lib/api";
export const selected = writable<string[]>([]);
export async function selectGroup(address: string) {
const result = await api.query(
new Query().matches(undefined, ATTR_IN, `@${address}`),
);
result.entities
.map((e) => e.replace(/^@/, ""))
.forEach((entity) => {
selected.update((selected) => {
if (!selected.includes(entity)) {
return [...selected, entity];
} else {
return selected;
}
});
});
}
</script>
<script lang="ts">
import { onMount } from "svelte";
import { i18n } from "../i18n";
import { Query } from "@upnd/upend";
import { ATTR_IN } from "@upnd/upend/constants";
let canvas: HTMLCanvasElement;
onMount(() => {
const ctx = canvas.getContext("2d");
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener("resize", resizeCanvas);
resizeCanvas();
let selecting = false;
let selectAllArea: DOMRect | undefined = undefined;
let selectAllAddress: string | undefined = undefined;
let addressesToRemove = new Set();
document.addEventListener("mousedown", (ev) => {
if (ev.ctrlKey) {
ev.preventDefault();
selecting = true;
addressesToRemove = new Set();
const el = document.elementFromPoint(
ev.clientX,
ev.clientY,
) as HTMLElement;
const groupElement = el.closest("[data-address-group]") as
| HTMLElement
| undefined;
if (groupElement) {
const banner = groupElement.querySelector("h2 .banner");
if (banner) {
const rect = banner.getBoundingClientRect();
selectAllArea = rect;
selectAllAddress = groupElement.dataset.addressGroup;
ctx.rect(rect.left, rect.top, rect.width, rect.height);
ctx.fillStyle = "#dc322f33";
ctx.fill();
ctx.fillStyle = "#dc322f77";
ctx.font = `bold ${rect.height / 2}px Inter`;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(
$i18n.t("Select All"),
rect.left + rect.width / 2,
rect.top + rect.height / 2,
);
}
}
ctx.strokeStyle = "#dc322f77";
ctx.lineWidth = 7;
ctx.beginPath();
ctx.moveTo(ev.clientX, ev.clientY);
}
});
document.addEventListener("mousemove", (ev) => {
if (selecting) {
ev.preventDefault();
if (selectAllArea) {
if (
ev.clientX > selectAllArea.left &&
ev.clientX < selectAllArea.right &&
ev.clientY > selectAllArea.top &&
ev.clientY < selectAllArea.bottom
) {
selectGroup(selectAllAddress);
stop();
}
}
const el = document.elementFromPoint(
ev.clientX,
ev.clientY,
) as HTMLElement;
const addressElement = el.closest("[data-address]") as
| HTMLElement
| undefined;
if (addressElement) {
const address = addressElement.dataset.address;
const selectMode = addressElement.dataset.selectMode;
if (selectMode === "add" || selectMode === undefined) {
selected.update((selected) => {
if (!selected.includes(address)) {
return [...selected, address];
} else {
return selected;
}
});
} else if (selectMode === "remove") {
addressesToRemove.add(address);
}
}
ctx.lineTo(ev.clientX, ev.clientY);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(ev.clientX, ev.clientY);
}
});
document.addEventListener("mouseup", () => {
stop();
});
function stop() {
selectAllArea = undefined;
selectAllAddress = undefined;
selecting = false;
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (const address of addressesToRemove) {
selected.update((selected) => {
return selected.filter((a) => a !== address);
});
}
}
});
</script>
<div class="selectIndicator">
<canvas bind:this={canvas}></canvas>
</div>
<style lang="scss">
.selectIndicator {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
pointer-events: none;
canvas {
width: 100%;
height: 100%;
}
}
</style>