2023-10-21 22:53:36 +02:00
|
|
|
<script lang="ts" context="module">
|
|
|
|
import { writable } from "svelte/store";
|
2023-10-22 13:56:27 +02:00
|
|
|
import api from "../lib/api";
|
2023-10-21 22:53:36 +02:00
|
|
|
|
|
|
|
export const selected = writable<string[]>([]);
|
2023-10-22 13:56:27 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2023-10-21 22:53:36 +02:00
|
|
|
</script>
|
|
|
|
|
|
|
|
<script lang="ts">
|
|
|
|
import { onMount } from "svelte";
|
2023-10-22 13:56:27 +02:00
|
|
|
import { i18n } from "../i18n";
|
|
|
|
|
|
|
|
import { Query } from "@upnd/upend";
|
|
|
|
import { ATTR_IN } from "@upnd/upend/constants";
|
2023-10-21 22:53:36 +02:00
|
|
|
|
|
|
|
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;
|
2023-10-22 13:56:27 +02:00
|
|
|
let selectAllArea: DOMRect | undefined = undefined;
|
|
|
|
let selectAllAddress: string | undefined = undefined;
|
2023-10-22 15:45:55 +02:00
|
|
|
let addressesToRemove = new Set();
|
2023-10-21 22:53:36 +02:00
|
|
|
document.addEventListener("mousedown", (ev) => {
|
|
|
|
if (ev.ctrlKey) {
|
|
|
|
ev.preventDefault();
|
|
|
|
|
|
|
|
selecting = true;
|
2023-10-22 15:45:55 +02:00
|
|
|
addressesToRemove = new Set();
|
2023-10-22 13:56:27 +02:00
|
|
|
|
|
|
|
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";
|
2023-10-22 20:46:03 +02:00
|
|
|
const fix = ctx.measureText("M").actualBoundingBoxDescent / 2;
|
2023-10-22 13:56:27 +02:00
|
|
|
ctx.fillText(
|
|
|
|
$i18n.t("Select All"),
|
|
|
|
rect.left + rect.width / 2,
|
2023-10-22 20:46:03 +02:00
|
|
|
rect.top + rect.height / 2 + fix,
|
2023-10-22 13:56:27 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-21 22:53:36 +02:00
|
|
|
ctx.strokeStyle = "#dc322f77";
|
|
|
|
ctx.lineWidth = 7;
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.moveTo(ev.clientX, ev.clientY);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
document.addEventListener("mousemove", (ev) => {
|
|
|
|
if (selecting) {
|
|
|
|
ev.preventDefault();
|
|
|
|
|
2023-10-22 13:56:27 +02:00
|
|
|
if (selectAllArea) {
|
|
|
|
if (
|
|
|
|
ev.clientX > selectAllArea.left &&
|
|
|
|
ev.clientX < selectAllArea.right &&
|
|
|
|
ev.clientY > selectAllArea.top &&
|
|
|
|
ev.clientY < selectAllArea.bottom
|
|
|
|
) {
|
|
|
|
selectGroup(selectAllAddress);
|
|
|
|
stop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-21 22:53:36 +02:00
|
|
|
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;
|
2023-10-22 15:45:55 +02:00
|
|
|
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);
|
|
|
|
}
|
2023-10-21 22:53:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ctx.lineTo(ev.clientX, ev.clientY);
|
|
|
|
ctx.stroke();
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.moveTo(ev.clientX, ev.clientY);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
document.addEventListener("mouseup", () => {
|
2023-10-22 13:56:27 +02:00
|
|
|
stop();
|
|
|
|
});
|
|
|
|
|
|
|
|
function stop() {
|
|
|
|
selectAllArea = undefined;
|
|
|
|
selectAllAddress = undefined;
|
2023-10-21 22:53:36 +02:00
|
|
|
selecting = false;
|
|
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
2023-10-22 15:45:55 +02:00
|
|
|
for (const address of addressesToRemove) {
|
|
|
|
selected.update((selected) => {
|
|
|
|
return selected.filter((a) => a !== address);
|
|
|
|
});
|
|
|
|
}
|
2023-10-22 13:56:27 +02:00
|
|
|
}
|
2023-10-21 22:53:36 +02:00
|
|
|
});
|
|
|
|
</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>
|