161 lines
3.8 KiB
Svelte
161 lines
3.8 KiB
Svelte
<script lang="ts" context="module">
|
|
import { writable } from 'svelte/store';
|
|
|
|
export const selected = writable<string[]>([]);
|
|
</script>
|
|
|
|
<script lang="ts">
|
|
import { onMount } from 'svelte';
|
|
import { i18n } from '../i18n';
|
|
|
|
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 selectAllAddresses: string[] = [];
|
|
let addressesToRemove = new Set();
|
|
document.addEventListener('mousedown', (ev) => {
|
|
if (!ctx) return;
|
|
|
|
if (ev.ctrlKey || ev.metaKey) {
|
|
ev.preventDefault();
|
|
|
|
selecting = true;
|
|
addressesToRemove = new Set();
|
|
|
|
const el = document.elementFromPoint(ev.clientX, ev.clientY) as HTMLElement;
|
|
|
|
const multiElement = el.closest('[data-address-multi]') as HTMLElement | undefined;
|
|
|
|
if (multiElement) {
|
|
const banner = multiElement.querySelector('h2');
|
|
|
|
if (banner) {
|
|
const rect = banner.getBoundingClientRect();
|
|
selectAllArea = rect;
|
|
selectAllAddresses = multiElement.dataset.addressMulti?.split(',') || [];
|
|
|
|
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';
|
|
const fix = ctx.measureText('M').actualBoundingBoxDescent / 2;
|
|
ctx.fillText(
|
|
$i18n.t('Select All'),
|
|
rect.left + rect.width / 2,
|
|
rect.top + rect.height / 2 + fix
|
|
);
|
|
}
|
|
}
|
|
|
|
ctx.strokeStyle = '#dc322f77';
|
|
ctx.lineWidth = 7;
|
|
ctx.beginPath();
|
|
ctx.moveTo(ev.clientX, ev.clientY);
|
|
}
|
|
});
|
|
|
|
document.addEventListener('mousemove', (ev) => {
|
|
if (!ctx) return;
|
|
|
|
if (selecting) {
|
|
ev.preventDefault();
|
|
|
|
if (selectAllArea) {
|
|
if (
|
|
ev.clientX > selectAllArea.left &&
|
|
ev.clientX < selectAllArea.right &&
|
|
ev.clientY > selectAllArea.top &&
|
|
ev.clientY < selectAllArea.bottom
|
|
) {
|
|
selected.update((selected) => {
|
|
return [
|
|
...selected,
|
|
...selectAllAddresses.filter((a) => {
|
|
return !selected.includes(a);
|
|
})
|
|
];
|
|
});
|
|
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 (address && !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;
|
|
selectAllAddresses = [];
|
|
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;
|
|
overflow: hidden;
|
|
|
|
pointer-events: none;
|
|
|
|
canvas {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
}
|
|
</style>
|