feat(webui): allow search / selection of entries via their attributes
ci/woodpecker/push/woodpecker Pipeline failed Details

refactor/sveltekit
Tomáš Mládek 2024-01-17 23:06:00 +01:00
parent 8a32b583d1
commit bfce05600b
3 changed files with 68 additions and 30 deletions

View File

@ -9,7 +9,12 @@
<div class="entry">
<div class="entity">
<UpObject link address={entry.entity} labels={resolve ? undefined : []} />
<UpObject
plain
link
address={entry.entity}
labels={resolve ? undefined : []}
/>
</div>
<div class="attribute" title={entry.attribute}>
{$attributeLabels[entry.attribute] || entry.attribute}
@ -30,24 +35,30 @@
<style lang="scss">
.entry {
border: 1px solid var(--foreground);
background: var(--background-lighter);
border-radius: 4px;
padding: 0.5em;
padding: 0.1em 0.5em 0.1em 0.25em;
display: flex;
align-content: center;
align-items: center;
justify-content: space-between;
gap: 1em;
& > * {
flex: 33%;
min-width: 0;
word-break: break-all;
}
}
.attribute {
font-weight: bold;
flex-grow: 1;
text-align: center;
font-weight: 300;
&::before {
content: "→\00a0";
}
&::after {
content: "\00a0→";
}
}
:global(.value-value) {

View File

@ -28,6 +28,7 @@
export let resolve = !(labels || []).length || banner;
export let backpath = 0;
export let select = true;
export let plain = false;
let entity: Readable<UpObject> = readable(undefined);
let entityInfo: Readable<EntityInfo> = writable(undefined);
@ -164,6 +165,7 @@
class:left-active={address == $addresses[$index - 1]}
class:right-active={address == $addresses[$index + 1]}
class:selected={select && $selected.includes(address)}
class:plain
>
<div
class="address"
@ -248,6 +250,12 @@
background: linear-gradient(90deg, transparent 0%, colors.$orange 100%);
padding: 2px 2px 2px 0;
}
&.plain .address {
border: none;
background: none;
padding: 0;
}
}
.address {

View File

@ -1,8 +1,11 @@
<script lang="ts" context="module">
import type { IValue } from "@upnd/upend/types";
import UpEntry from "@upnd/upend";
import UpEntryComponent from "../display/UpEntry.svelte";
export type SELECTOR_TYPE =
| "Address"
| "LabelledAddress"
| "NewAddress"
| "Attribute"
| "NewAttribute"
@ -16,6 +19,7 @@
| {
t: "Address";
c: Address;
entry?: UpEntry;
labels?: string[];
}
| {
@ -146,7 +150,7 @@
let searchResult: UpListing | undefined = undefined;
const updateOptions = debounce(async (query: string, doSearch: boolean) => {
updating = true;
let result = [];
let result: SelectorOption[] = [];
if (query.length === 0 && emptyOptions !== undefined) {
options = emptyOptions;
@ -173,7 +177,7 @@
options = result;
if (types.includes("Address")) {
if (types.includes("Address") || types.includes("LabelledAddress")) {
if (doSearch) {
if (emptyOptions === undefined || query.length > 0) {
searchResult = await baseSearchOnce(query);
@ -194,6 +198,7 @@
t: "Address",
c: addr,
labels: addressToLabels[addr],
entry: null,
}),
);
} else if (query.length && types.includes("NewAddress")) {
@ -203,23 +208,32 @@
});
}
const validOptions = (searchResult?.entries || [])
.filter((e) => e.attribute === ATTR_LABEL)
.filter((e) => !exactHits.includes(e.entity));
let validOptions = (searchResult?.entries || []).filter(
(e) => !exactHits.includes(e.entity),
);
// only includes LabelledAddress
if (!types.includes("Address")) {
validOptions = validOptions.filter((e) => e.attribute == ATTR_LABEL);
}
const sortedOptions = matchSorter(validOptions, inputValue, {
keys: ["value.c"],
keys: ["value.c", (i) => addressToLabels[i.entity]?.join(" ")],
});
result.push(
...sortedOptions.map((e) => {
return {
t: "Address",
c: e.entity,
labels: [e.value.c],
} as SelectorOption;
}),
);
for (const entry of sortedOptions) {
const common = {
t: "Address" as const,
c: entry.entity,
};
if (entry.attribute == ATTR_LABEL) {
result.push({
...common,
labels: [entry.value.c.toString()],
});
} else {
result.push({ ...common, entry });
}
}
}
if (types.includes("Attribute")) {
@ -463,13 +477,16 @@
>
{#if option.t === "Address"}
{@const address = option.c}
<UpObject
{address}
labels={option.labels}
on:resolved={(ev) => onAddressResolved(address, ev)}
/>
{#if option.entry}
<UpEntryComponent entry={option.entry} />
{:else}
<UpObject
{address}
labels={option.labels}
on:resolved={(ev) => onAddressResolved(address, ev)}
/>{/if}
{:else if option.t === "NewAddress"}
<div class="content">{option.c}</div>
<div class="content new">{option.c}</div>
<div class="type">{$i18n.t("Create object")}</div>
{:else if option.t === "Attribute"}
{#if option.labels.length}
@ -513,14 +530,12 @@
.options {
position: absolute;
list-style: none;
margin: 0;
margin: 2px 0 0;
padding: 0;
border: 1px solid var(--foreground-lighter);
width: 100%;
border-radius: 4px;
margin-top: 2px;
background: var(--background);
font-size: smaller;
visibility: hidden;
opacity: 0;
@ -536,7 +551,7 @@
li {
cursor: pointer;
padding: 0.5em;
padding: 0.25em;
transition: background-color 0.1s;
&:hover {
@ -562,5 +577,9 @@
display: inline-block;
}
}
.content.new {
padding: 0.25em;
}
}
</style>