feat(webui): 🚧 generic `BrowseColumn`, EntryView accepts `entities`
parent
377f0af161
commit
40b4154c3d
|
@ -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>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -281,7 +281,7 @@
|
|||
],
|
||||
},
|
||||
{
|
||||
name: "EntityList",
|
||||
name: "Entity List",
|
||||
icon: "image",
|
||||
components: ({ entries, address }) => [
|
||||
{
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
Loading…
Reference in New Issue