upend/webui/src/components/CombineColumn.svelte

179 lines
4.3 KiB
Svelte

<script lang="ts">
import { i18n } from "../i18n";
import EntitySetEditor from "./EntitySetEditor.svelte";
import EntryView from "./EntryView.svelte";
import Icon from "./utils/Icon.svelte";
import EntityList from "./widgets/EntityList.svelte";
import api from "../lib/api";
import { Query } from "@upnd/upend";
import { ATTR_IN } from "@upnd/upend/constants";
import { createEventDispatcher } from "svelte";
import { Any } from "@upnd/upend/query";
const dispatch = createEventDispatcher();
export let spec: string;
const individualSpecs = spec.split(/(?=[+=-])/);
let includedGroups = individualSpecs
.filter((s) => s.startsWith("+"))
.map((s) => s.slice(1));
let requiredGroups = individualSpecs
.filter((s) => s.startsWith("="))
.map((s) => s.slice(1));
let excludedGroups = individualSpecs
.filter((s) => s.startsWith("-"))
.map((s) => s.slice(1));
$: if (
includedGroups.length === 0 &&
requiredGroups.length === 0 &&
excludedGroups.length === 0
) {
dispatch("close");
}
const combinedWidgets = [
{
name: "List",
icon: "list-check",
components: ({ entities }) => [
{
component: EntityList,
props: {
entities,
thumbnails: false,
},
},
],
},
{
name: "EntityList",
icon: "image",
components: ({ entities }) => [
{
component: EntityList,
props: {
entities,
thumbnails: true,
},
},
],
},
];
let resultEntities = [];
async function updateResultEntities(
includedGroups: string[],
requiredGroups: string[],
excludedGroups: string[],
) {
const included = includedGroups.length
? (
await api.query(
Query.matches(
Any,
ATTR_IN,
includedGroups.map((g) => `@${g}`),
),
)
).objects
: [];
const required = requiredGroups.length
? (
await api.query(
Query.matches(
Any,
ATTR_IN,
requiredGroups.map((g) => `@${g}`),
),
)
).objects
: [];
const excluded = excludedGroups.length
? (
await api.query(
Query.matches(
Any,
ATTR_IN,
excludedGroups.map((g) => `@${g}`),
),
)
).objects
: [];
resultEntities = (
Object.keys(included).length
? Object.keys(included)
: Object.keys(required)
)
.filter(
(e) => !requiredGroups.length || Object.keys(required).includes(e),
)
.filter((e) => !Object.keys(excluded).includes(e));
}
$: updateResultEntities(includedGroups, requiredGroups, excludedGroups);
</script>
<div class="view" data-address-multi={resultEntities}>
<h2>
<Icon plain name="intersect" />
{$i18n.t("Combine")}
</h2>
<div class="controls">
<EntitySetEditor
entities={includedGroups}
header={$i18n.t("Include")}
confirmRemoveMessage={null}
on:add={(ev) => (includedGroups = [...includedGroups, ev.detail])}
on:remove={(ev) =>
(includedGroups = includedGroups.filter((e) => e !== ev.detail))}
/>
<EntitySetEditor
entities={requiredGroups}
header={$i18n.t("Require")}
confirmRemoveMessage={null}
on:add={(ev) => (requiredGroups = [...requiredGroups, ev.detail])}
on:remove={(ev) =>
(requiredGroups = requiredGroups.filter((e) => e !== ev.detail))}
/>
<EntitySetEditor
entities={excludedGroups}
header={$i18n.t("Exclude")}
confirmRemoveMessage={null}
on:add={(ev) => (excludedGroups = [...excludedGroups, ev.detail])}
on:remove={(ev) =>
(excludedGroups = excludedGroups.filter((e) => e !== ev.detail))}
/>
</div>
<div class="entities">
<EntryView
title={$i18n.t("Matching entities")}
entities={resultEntities}
widgets={combinedWidgets}
/>
</div>
</div>
<style lang="scss">
.view {
display: flex;
flex-direction: column;
height: 100%;
}
h2 {
text-align: center;
margin: 0;
margin-top: -0.66em;
}
.controls {
margin-bottom: 1rem;
}
.entities {
flex-grow: 1;
overflow-y: auto;
height: 0;
}
</style>