feat(webui): 🚧 generic `BrowseColumn`, EntryView accepts `entities`

pull/79/head
Tomáš Mládek 2023-10-22 13:38:52 +02:00
parent 377f0af161
commit 40b4154c3d
6 changed files with 107 additions and 39 deletions

View File

@ -2,12 +2,11 @@
import { createEventDispatcher, onMount, tick } from "svelte";
import { normUrl } from "../util/history";
import Inspect from "./Inspect.svelte";
import IconButton from "./utils/IconButton.svelte";
import { selected } from "./EntitySelect.svelte";
const dispatch = createEventDispatcher();
export let address: string;
export let index: number;
export let detachUrl: string | undefined = undefined;
export let only: boolean;
let detail = only;
@ -19,9 +18,12 @@
// Required to make detail mode detection work in Browse
dispatch("detail", detail);
});
$: if ($selected.length) {
detail = false;
}
function visit() {
window.open(normUrl(`/browse/${address}`), "_blank");
window.open(normUrl(detachUrl), "_blank");
}
let width = 460;
@ -48,9 +50,11 @@
<div class="browse-column" class:detail>
<div class="view" style="--width: {width}px">
<header>
<IconButton name="link" on:click={() => visit()} disabled={only}>
Detach
</IconButton>
{#if detachUrl}
<IconButton name="link" on:click={() => visit()} disabled={only}>
Detach
</IconButton>
{/if}
<IconButton
name={detail ? "zoom-out" : "zoom-in"}
on:click={() => {
@ -69,7 +73,7 @@
Close
</IconButton>
</header>
<Inspect {address} {index} {detail} on:resolved on:close />
<slot {detail} />
</div>
<div class="resizeHandle" on:mousedown|preventDefault={drag} />
</div>

View File

@ -9,6 +9,7 @@
icon?: string;
components: (input: {
entries: UpEntry[];
entities: string[];
group?: string;
address?: string;
}) => Array<WidgetComponent>;
@ -25,7 +26,8 @@
import LabelBorder from "./utils/LabelBorder.svelte";
const dispatch = createEventDispatcher();
export let entries: UpEntry[];
export let entries: UpEntry[] = [];
export let entities: string[] = [];
export let widgets: Widget[] | undefined = undefined;
export let initialWidget: string | undefined = undefined;
export let title: string | undefined = undefined;
@ -43,18 +45,23 @@
let availableWidgets: Widget[] = [];
$: {
availableWidgets = [
{
name: "Entry List",
icon: "table",
components: ({ entries }) => [
{
component: EntryList,
props: { entries, columns: "entity, attribute, value" },
},
],
},
];
availableWidgets = [];
if (entries.length) {
availableWidgets = [
...availableWidgets,
{
name: "Entry List",
icon: "table",
components: ({ entries }) => [
{
component: EntryList,
props: { entries, columns: "entity, attribute, value" },
},
],
},
];
}
if (widgets?.length) {
availableWidgets = [...widgets, ...availableWidgets];
@ -71,11 +78,11 @@
$: {
components = availableWidgets
.find((w) => w.name === currentWidget)
.components({ entries, group, address });
.components({ entries, entities, group, address });
}
</script>
<LabelBorder hide={entries.length === 0}>
<LabelBorder hide={entries.length === 0 && entities.length === 0}>
<svelte:fragment slot="header-full">
<h3 class:highlighted>
{#if group}

View File

@ -281,7 +281,7 @@
],
},
{
name: "EntityList",
name: "Entity List",
icon: "image",
components: ({ entries, address }) => [
{

View File

@ -1,20 +1,62 @@
<script lang="ts">
import { i18n } from "../i18n";
import { selected } from "./EntitySelect.svelte";
import EntryView from "./EntryView.svelte";
import EntityList from "./widgets/EntityList.svelte";
const selectedWidgets = [
{
name: "List",
icon: "list-check",
components: ({ entities }) => [
{
component: EntityList,
props: {
entities,
thumbnails: false,
selectable: false,
},
},
],
},
{
name: "EntityList",
icon: "image",
components: ({ entities }) => [
{
component: EntityList,
props: {
entities,
thumbnails: true,
selectable: false,
},
},
],
},
];
</script>
<div class="view">
<h1>{$i18n.t("Selected entities")}</h1>
<EntityList entities={$selected} />
<h2>{$i18n.t("Selected")}</h2>
<div class="entities">
<EntryView entities={$selected} widgets={selectedWidgets} />
</div>
</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;
display: flex;
flex-direction: column;
height: 100%;
}
h2 {
text-align: center;
margin: 0;
}
.entities {
flex-grow: 1;
overflow-y: auto;
}
</style>

View File

@ -18,6 +18,7 @@
export let entities: Address[];
export let thumbnails = true;
export let selectable = true;
export let sort = true;
export let address: Address | undefined = undefined;
@ -173,7 +174,7 @@
data-address={entity}
use:observe
class="item"
class:selected={$selected.includes(entity)}
class:selected={selectable && $selected.includes(entity)}
>
{#if visible.has(entity)}
{#if thumbnails}

View File

@ -5,6 +5,7 @@
import { updateTitle } from "../util/title";
import EntitySelect, { selected } from "../components/EntitySelect.svelte";
import SelectedColumn from "../components/SelectedColumn.svelte";
import Inspect from "../components/Inspect.svelte";
const navigate = useNavigate();
const params = useParams();
@ -53,13 +54,16 @@
}
let detailMode = false;
function onDetailChanged(index: number, ev: CustomEvent<boolean>) {
function onDetailChanged(
index: number | "selected",
ev: CustomEvent<boolean>,
) {
if (ev.detail) {
scrollToVisible(index);
}
detailMode = addresses.length === 1 && ev.detail;
}
$: if ($selected.length) detailMode = false;
$: only = addresses.length === 1 && !$selected.length;
$: updateTitle("Browse", identities.join(" / "));
</script>
@ -72,17 +76,27 @@
{#each addresses as address, index}
<div class="column" data-index={index}>
<BrowseColumn
{address}
{index}
only={addresses.length === 1}
detachUrl="/browse/{address}"
{only}
on:close={() => close(index)}
on:resolved={(ev) => onIdentified(index, ev)}
on:detail={(ev) => onDetailChanged(index, ev)}
/>
let:detail
>
<Inspect {address} {index} {detail} on:resolved on:close />
</BrowseColumn>
</div>
{/each}
{#if $selected.length}
<SelectedColumn />
<div class="column" data-index="selected">
<BrowseColumn
{only}
on:close={() => selected.set([])}
on:detail={(ev) => onDetailChanged("selected", ev)}
>
<SelectedColumn />
</BrowseColumn>
</div>
{/if}
{#if !detailMode}
{#key addresses}