feat(webui): turn groups view into a column, allow selection

also add `forceDetail` to BrowseColumn
refactor/errors
Tomáš Mládek 2023-11-02 17:03:23 +01:00
parent 52098758a1
commit 851b21ce81
8 changed files with 120 additions and 69 deletions

View File

@ -10,7 +10,6 @@
import AddModal from "./components/AddModal.svelte";
import Store from "./views/Store.svelte";
import Surface from "./views/Surface.svelte";
import Groups from "./views/Groups.svelte";
import "./styles/main.scss";
@ -40,10 +39,6 @@
<Store />
</Route>
<Route path="/groups">
<Groups />
</Route>
<Footer />
<AddModal />

View File

@ -13,10 +13,11 @@
export let index: number;
export let only: boolean;
export let background = "var(--background-lighter)";
export let forceDetail = false;
let detail = only;
let detail = only || forceDetail;
let detailChanged = false;
$: if (!detailChanged) detail = only;
$: if (!detailChanged) detail = only || forceDetail;
$: if (detailChanged) tick().then(() => dispatch("detail", detail));
let indexStore = writable(index);
@ -75,16 +76,20 @@
Detach
</IconButton>
{/if}
<IconButton
name={detail ? "zoom-out" : "zoom-in"}
on:click={() => {
detail = !detail;
detailChanged = true;
}}
active={detail}
>
Detail
</IconButton>
{#if !forceDetail}
<IconButton
name={detail ? "zoom-out" : "zoom-in"}
on:click={() => {
detail = !detail;
detailChanged = true;
}}
active={detail}
>
Detail
</IconButton>
{:else}
<div class="noop"></div>
{/if}
{#if address}
<IconButton
name="intersect"

View File

@ -2,8 +2,8 @@
import { ATTR_IN, ATTR_LABEL } from "@upnd/upend/constants";
import api from "../lib/api";
import { i18n } from "../i18n";
import Spinner from "../components/utils/Spinner.svelte";
import UpObject from "../components/display/UpObject.svelte";
import Spinner from "./utils/Spinner.svelte";
import UpObject from "./display/UpObject.svelte";
const groups = (async () => {
const data = await api.query(`(matches ? "${ATTR_IN}" ?)`);
@ -57,59 +57,80 @@
duplicateGroups: duplicates,
};
})();
let clientWidth: number;
</script>
<div class="groups">
<h1>{$i18n.t("Groups")}</h1>
{#await groups}
<Spinner centered />
{:then data}
<ul>
{#each data.groups as group}
<li class="group">
<UpObject link address={group.address} labels={group.labels} />
<div class="count">{group.count}</div>
</li>
{:else}
<li>No groups?</li>
{/each}
{#if data.groups && data.total > data.groups.length}
<li>+ {data.total - data.groups.length}...</li>
{/if}
</ul>
{#if data.duplicateGroups.length > 0}
<h2>{$i18n.t("Duplicate groups")}</h2>
<div class="groups" bind:clientWidth class:small={clientWidth < 600}>
<h2>{$i18n.t("Groups")}</h2>
<div class="main">
{#await groups}
<Spinner centered />
{:then data}
<ul>
{#each data.duplicateGroups as { label, groups }}
<li class="duplicate">
<div class="label">{label}</div>
<ul>
{#each groups as group}
<li>
<UpObject link address={group} backpath={2} />
</li>
{/each}
</ul>
{#each data.groups as group}
<li class="group" data-address={group.address}>
<UpObject link address={group.address} labels={group.labels} />
<div class="count">{group.count}</div>
</li>
{:else}
<li>No groups?</li>
{/each}
{#if data.groups && data.total > data.groups.length}
<li>+ {data.total - data.groups.length}...</li>
{/if}
</ul>
{/if}
{/await}
{#if data.duplicateGroups.length > 0}
<h3>{$i18n.t("Duplicate groups")}</h3>
<ul class="duplicate">
{#each data.duplicateGroups as { label, groups }}
<li class="duplicate-group">
<div class="label">{label}</div>
<ul>
{#each groups as group}
<li>
<UpObject link address={group} backpath={2} />
</li>
{/each}
</ul>
</li>
{/each}
</ul>
{/if}
{/await}
</div>
</div>
<style lang="scss">
@use "../styles/colors";
.groups {
text-align: center;
flex-grow: 1;
height: 0;
display: flex;
flex-direction: column;
}
.main {
overflow: hidden auto;
}
h2 {
margin-top: -0.66em;
}
ul {
list-style: none;
padding: 0;
margin: 0 2rem;
margin: 0;
display: flex;
flex-wrap: wrap;
gap: 0.5em;
justify-content: space-between;
}
.group {
@ -128,11 +149,26 @@
}
.duplicate {
margin-bottom: 1em;
display: flex;
gap: 1rem;
flex-wrap: wrap;
justify-content: center;
}
.duplicate-group {
flex-basis: 49%;
border-radius: 4px;
border: 1px solid var(--foreground);
padding: 1em;
padding: .5rem;
overflow-x: auto;
max-width: 100%;
ul {
flex-direction: column;
}
}
.groups.small {
ul {
flex-direction: column;
}

View File

@ -16,6 +16,7 @@
import { i18n } from "../../i18n";
import api from "../../lib/api";
import { ATTR_IN, ATTR_LABEL, HIER_ROOT_ADDR } from "@upnd/upend/constants";
import { selected } from "../EntitySelect.svelte";
const dispatch = createEventDispatcher();
@ -25,6 +26,7 @@
export let banner = false;
export let resolve = !(labels || []).length || banner;
export let backpath = 0;
export let select = true;
let entity: Readable<UpObject> = readable(undefined);
let entityInfo: Readable<EntityInfo> = writable(undefined);
@ -152,6 +154,7 @@
class="upobject"
class:left-active={address == $addresses[$index - 1]}
class:right-active={address == $addresses[$index + 1]}
class:selected={select && $selected.includes(address)}
>
<div
class="address"
@ -298,4 +301,15 @@
color: var(--active-color, var(--primary));
}
}
.upobject {
transition:
margin 0.2s ease,
box-shadow 0.2s ease;
}
.selected {
margin: 0.12rem;
box-shadow: 0 0 0.1rem 0.11rem colors.$red;
}
</style>

View File

@ -8,6 +8,7 @@
export let labels: string[] | undefined = undefined;
export let thumbnail = true;
export let banner = true;
export let select = true;
</script>
<div class="upobjectcard">
@ -21,7 +22,7 @@
</div>
{/if}
<div class="label">
<UpObject {address} {labels} {banner} on:resolved />
<UpObject {address} {labels} {banner} {select} on:resolved />
</div>
</div>
</UpLink>

View File

@ -175,7 +175,6 @@
data-select-mode={select}
use:observe
class="item"
class:selected={select === "add" && $selected.includes(entity)}
>
{#if visible.has(entity)}
{#if thumbnails}
@ -183,6 +182,7 @@
address={entity}
labels={sortKeys[entity]}
banner={false}
select={select === "add"}
on:resolved={(event) => {
addSortKeys(entity, event.detail, true);
}}
@ -200,6 +200,7 @@
link
address={entity}
labels={sortKeys[entity]}
select={select === "add"}
on:resolved={(event) => {
addSortKeys(entity, event.detail, true);
}}
@ -336,15 +337,4 @@
.entitylist.style-grid .add {
grid-column: 1 / -1;
}
.item {
transition:
margin 0.2s ease,
box-shadow 0.2s ease;
}
.selected {
margin: 0.12rem;
box-shadow: 0 0 0.1rem 0.11rem colors.$red;
}
</style>

View File

@ -7,6 +7,7 @@
import SelectedColumn from "../components/SelectedColumn.svelte";
import Inspect from "../components/Inspect.svelte";
import CombineColumn from "../components/CombineColumn.svelte";
import GroupColumn from "../components/GroupColumn.svelte";
const navigate = useNavigate();
const params = useParams();
@ -108,6 +109,15 @@
>
<SelectedColumn />
</BrowseColumn>
{:else if address === "groups"}
<BrowseColumn
{index}
{only}
on:close={() => close(index)}
on:detail={(ev) => onDetailChanged(index, ev)}
>
<GroupColumn />
</BrowseColumn>
{:else}
<BrowseColumn
{address}

View File

@ -178,7 +178,7 @@
</section>
<section class="groups">
<h2><Link to="/groups">{$i18n.t("Groups")}</Link></h2>
<h2><Link to="/browse/groups">{$i18n.t("Groups")}</Link></h2>
{#await groups}
<Spinner centered />
{:then data}