feat(webui): 🚧 selection via ctrl+drag
ci/woodpecker/push/woodpecker Pipeline was successful
Details
ci/woodpecker/push/woodpecker Pipeline was successful
Details
parent
de8d6b1c59
commit
377f0af161
|
@ -0,0 +1,92 @@
|
|||
<script lang="ts" context="module">
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
export const selected = writable<string[]>([]);
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
|
||||
let canvas: HTMLCanvasElement;
|
||||
|
||||
onMount(() => {
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
function resizeCanvas() {
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
}
|
||||
window.addEventListener("resize", resizeCanvas);
|
||||
resizeCanvas();
|
||||
|
||||
// if ctrl is pressed while right button is dragged over screen, draw a red line on canvas
|
||||
let selecting = false;
|
||||
document.addEventListener("mousedown", (ev) => {
|
||||
if (ev.ctrlKey) {
|
||||
ev.preventDefault();
|
||||
|
||||
selecting = true;
|
||||
ctx.strokeStyle = "#dc322f77";
|
||||
ctx.lineWidth = 7;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(ev.clientX, ev.clientY);
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("mousemove", (ev) => {
|
||||
if (selecting) {
|
||||
ev.preventDefault();
|
||||
|
||||
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;
|
||||
selected.update((selected) => {
|
||||
if (!selected.includes(address)) {
|
||||
return [...selected, address];
|
||||
} else {
|
||||
return selected;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ctx.lineTo(ev.clientX, ev.clientY);
|
||||
ctx.stroke();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(ev.clientX, ev.clientY);
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("mouseup", () => {
|
||||
selecting = false;
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
});
|
||||
});
|
||||
</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>
|
|
@ -0,0 +1,20 @@
|
|||
<script lang="ts">
|
||||
import { i18n } from "../i18n";
|
||||
import { selected } from "./EntitySelect.svelte";
|
||||
import EntityList from "./widgets/EntityList.svelte";
|
||||
</script>
|
||||
|
||||
<div class="view">
|
||||
<h1>{$i18n.t("Selected entities")}</h1>
|
||||
<EntityList entities={$selected} />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.view {
|
||||
background: var(--background-lighter);
|
||||
color: var(--foreground-lighter);
|
||||
border: 1px solid var(--foreground-lightest);
|
||||
border-radius: 0.5em;
|
||||
padding: 1rem;
|
||||
}
|
||||
</style>
|
|
@ -7,12 +7,12 @@
|
|||
import UpObjectCard from "../display/UpObjectCard.svelte";
|
||||
import { ATTR_LABEL } from "@upnd/upend/constants";
|
||||
import { i18n } from "../../i18n";
|
||||
import Icon from "../utils/Icon.svelte";
|
||||
import IconButton from "../utils/IconButton.svelte";
|
||||
import Selector from "../utils/Selector.svelte";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import type { WidgetChange } from "src/types/base";
|
||||
import debug from "debug";
|
||||
import { selected } from "../EntitySelect.svelte";
|
||||
const dispatch = createEventDispatcher();
|
||||
const dbg = debug(`kestrel:EntityList`);
|
||||
|
||||
|
@ -169,7 +169,12 @@
|
|||
{/if}
|
||||
<div class="items">
|
||||
{#each sortedEntities as entity (entity)}
|
||||
<div data-address={entity} use:observe class="item">
|
||||
<div
|
||||
data-address={entity}
|
||||
use:observe
|
||||
class="item"
|
||||
class:selected={$selected.includes(entity)}
|
||||
>
|
||||
{#if visible.has(entity)}
|
||||
{#if thumbnails}
|
||||
<UpObjectCard
|
||||
|
@ -242,6 +247,8 @@
|
|||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@use "../../styles/colors";
|
||||
|
||||
.items {
|
||||
gap: 4px;
|
||||
}
|
||||
|
@ -327,4 +334,9 @@
|
|||
.entitylist.style-grid .add {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.selected {
|
||||
margin: 0.12rem;
|
||||
box-shadow: 0 0 0.1rem 0.11rem colors.$red;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
import BrowseAdd from "../components/BrowseAdd.svelte";
|
||||
import BrowseColumn from "../components/BrowseColumn.svelte";
|
||||
import { updateTitle } from "../util/title";
|
||||
import EntitySelect, { selected } from "../components/EntitySelect.svelte";
|
||||
import SelectedColumn from "../components/SelectedColumn.svelte";
|
||||
const navigate = useNavigate();
|
||||
const params = useParams();
|
||||
|
||||
|
@ -57,6 +59,7 @@
|
|||
}
|
||||
detailMode = addresses.length === 1 && ev.detail;
|
||||
}
|
||||
$: if ($selected.length) detailMode = false;
|
||||
|
||||
$: updateTitle("Browse", identities.join(" / "));
|
||||
</script>
|
||||
|
@ -78,6 +81,9 @@
|
|||
/>
|
||||
</div>
|
||||
{/each}
|
||||
{#if $selected.length}
|
||||
<SelectedColumn />
|
||||
{/if}
|
||||
{#if !detailMode}
|
||||
{#key addresses}
|
||||
<div class="column" data-index="add">
|
||||
|
@ -90,6 +96,8 @@
|
|||
{/if}
|
||||
</div>
|
||||
|
||||
<EntitySelect />
|
||||
|
||||
<style lang="scss">
|
||||
.browser {
|
||||
height: 100%;
|
||||
|
|
Loading…
Reference in New Issue