upend/webui/src/views/Browse.svelte

193 lines
5.1 KiB
Svelte

<script lang="ts">
import { useNavigate, useParams } from "svelte-navigator";
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";
import Inspect from "../components/Inspect.svelte";
import CombineColumn from "../components/CombineColumn.svelte";
import GroupColumn from "../components/GroupColumn.svelte";
import SurfaceColumn from "../components/SurfaceColumn.svelte";
import type { SelectorValue } from "src/components/utils/Selector.svelte";
const navigate = useNavigate();
const params = useParams();
let root: HTMLDivElement;
let identities: string[] = [];
$: addresses = $params.addresses.split(",");
function add(value: SelectorValue) {
if (value.t !== "Address") return;
const address = value.c;
let _addresses = addresses.concat();
_addresses.push(address);
navigate(`/browse/${_addresses.join(",")}`);
}
function close(idx: number) {
let _addresses = addresses.concat();
_addresses.splice(idx, 1);
if (_addresses.length) {
navigate(`/browse/${_addresses.join(",")}`);
} else {
navigate("/");
}
}
$: scrollToVisible(addresses.length - 1);
function scrollToVisible(idx: string | number) {
const target = root?.querySelector(`[data-index='${idx}']`)
?.firstElementChild as HTMLElement;
if (target) {
root?.scrollTo({
left: target.offsetLeft,
behavior: "smooth",
});
}
}
function onIdentified(index: number, ev: CustomEvent<string[]>) {
for (let i = 0; i < addresses.length; i++) {
if (!identities[i]) {
identities[i] = "?";
}
identities[index] = ev.detail.join("/");
}
identities = identities;
}
let detailMode = false;
function onDetailChanged(index: number, ev: CustomEvent<boolean>) {
if (ev.detail) {
scrollToVisible(index);
}
detailMode = addresses.length === 1 && ev.detail;
}
$: only = addresses.length === 1 && !$selected.length;
$: if ($selected.length && !addresses.includes("selected")) {
let _addresses = addresses.concat();
_addresses.push("selected");
navigate(`/browse/${_addresses.join(",")}`);
}
function addCombine(address: string) {
let _addresses = addresses.concat();
_addresses.push(`+${address}`);
navigate(`/browse/${_addresses.join(",")}`);
}
$: updateTitle("Browse", identities.join(" / "));
</script>
<div
class="browser"
bind:this={root}
class:single={addresses.length === 1 && detailMode}
>
{#each addresses as address, index}
<div class="column" data-index={index}>
{#if ["+", "-"].some((c) => address.includes(c))}
<BrowseColumn
{index}
{only}
on:close={() => close(index)}
on:detail={(ev) => onDetailChanged(index, ev)}
background="var(--background-lightest)"
>
<CombineColumn spec={address} on:close={() => close(index)} />
</BrowseColumn>
{:else if address === "selected"}
<BrowseColumn
{index}
{only}
on:close={() => {
selected.set([]);
close(index);
}}
on:detail={(ev) => onDetailChanged(index, ev)}
background="var(--background-lightest)"
>
<SelectedColumn />
</BrowseColumn>
{:else if address === "groups"}
<BrowseColumn
{index}
{only}
on:close={() => close(index)}
on:detail={(ev) => onDetailChanged(index, ev)}
>
<GroupColumn />
</BrowseColumn>
{:else if address.startsWith("surface")}
<BrowseColumn
{index}
{only}
on:close={() => close(index)}
on:detail={(ev) => onDetailChanged(index, ev)}
>
<SurfaceColumn x={address.split(":")[1]} />
</BrowseColumn>
{:else}
<BrowseColumn
{address}
{index}
{only}
on:close={() => close(index)}
on:resolved={(ev) => onIdentified(index, ev)}
on:detail={(ev) => onDetailChanged(index, ev)}
on:combine={() => addCombine(address)}
let:detail
>
<Inspect {address} {detail} on:resolved on:close />
</BrowseColumn>
{/if}
</div>
{/each}
{#if !detailMode}
{#key addresses}
<div class="column" data-index="add">
<BrowseAdd
on:input={(ev) => add(ev.detail)}
on:editable={() => scrollToVisible("add")}
/>
</div>
{/key}
{/if}
</div>
<EntitySelect />
<style lang="scss">
.browser {
height: 100%;
padding: 1rem;
display: flex;
overflow-x: auto;
gap: 1rem;
@media screen and (max-width: 600px) {
scroll-snap-type: x mandatory;
}
}
.column {
display: flex;
justify-content: center;
scroll-snap-align: center;
}
.browser.single {
justify-content: center;
.column {
flex-grow: 1;
}
}
</style>