wip: get rid of types, new EntryVIew
This commit is contained in:
parent
e428ef188f
commit
e496710e20
6 changed files with 211 additions and 345 deletions
|
@ -1,22 +1,31 @@
|
||||||
|
<script lang="ts" context="module">
|
||||||
|
export interface WidgetComponent {
|
||||||
|
component: ComponentType;
|
||||||
|
props: { [key: string]: unknown };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Widget {
|
||||||
|
name: string;
|
||||||
|
icon?: string;
|
||||||
|
components: (entries: UpEntry[]) => Array<WidgetComponent>;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import UpLink from "./display/UpLink.svelte";
|
|
||||||
import type { Component, UpType, Widget } from "../lib/types";
|
|
||||||
import EntryList from "./widgets/EntryList.svelte";
|
import EntryList from "./widgets/EntryList.svelte";
|
||||||
import type { UpEntry } from "upend";
|
import type { UpEntry } from "upend";
|
||||||
import Icon from "./utils/Icon.svelte";
|
import Icon from "./utils/Icon.svelte";
|
||||||
import IconButton from "./utils/IconButton.svelte";
|
import IconButton from "./utils/IconButton.svelte";
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher, type ComponentType } from "svelte";
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
import { i18n } from "../i18n";
|
|
||||||
|
|
||||||
export let entries: UpEntry[];
|
export let entries: UpEntry[];
|
||||||
export let type: UpType | undefined = undefined;
|
|
||||||
export let widgets: Widget[] | undefined = undefined;
|
export let widgets: Widget[] | undefined = undefined;
|
||||||
export let title: string | undefined = undefined;
|
|
||||||
export let editable = false;
|
|
||||||
export let reverse = false;
|
|
||||||
export let initialWidget: string | undefined = undefined;
|
export let initialWidget: string | undefined = undefined;
|
||||||
|
export let title: string | undefined = undefined;
|
||||||
|
export let icon: string | undefined = undefined;
|
||||||
export let highlighted = false;
|
export let highlighted = false;
|
||||||
|
export let editable = false;
|
||||||
|
|
||||||
let currentWidget: string | undefined;
|
let currentWidget: string | undefined;
|
||||||
|
|
||||||
|
@ -29,20 +38,17 @@
|
||||||
$: {
|
$: {
|
||||||
availableWidgets = [
|
availableWidgets = [
|
||||||
{
|
{
|
||||||
name: "entrylist",
|
name: "Entry List",
|
||||||
icon: "table",
|
icon: "table",
|
||||||
components: [
|
components: (entries) => [
|
||||||
{
|
{
|
||||||
component: EntryList,
|
component: EntryList,
|
||||||
|
props: { entries, columns: "entity, attribute, value" },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (type?.widgetInfo.length > 0) {
|
|
||||||
availableWidgets = [...type.widgetInfo, ...availableWidgets];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (widgets?.length) {
|
if (widgets?.length) {
|
||||||
availableWidgets = [...widgets, ...availableWidgets];
|
availableWidgets = [...widgets, ...availableWidgets];
|
||||||
}
|
}
|
||||||
|
@ -54,95 +60,80 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let components: Component[] = [];
|
let components: WidgetComponent[] = [];
|
||||||
$: {
|
$: {
|
||||||
components = availableWidgets.find(
|
components = availableWidgets
|
||||||
(w) => w.name === currentWidget
|
.find((w) => w.name === currentWidget)
|
||||||
).components;
|
.components(entries);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section class="attribute-view labelborder" class:highlighted>
|
<section class="entry-view labelborder" class:highlighted>
|
||||||
<header>
|
<header>
|
||||||
<h3>
|
<h3>
|
||||||
{#if !title && type?.address}
|
{#if icon}
|
||||||
<UpLink to={{ entity: type.address }}>
|
<div class="icon">
|
||||||
{#if type.icon}
|
<Icon name={icon} />
|
||||||
<div class="icon">
|
</div>
|
||||||
<Icon name={type.icon} />
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{#if type.name != "HIER"}
|
|
||||||
{type.label || type.name || "???"}
|
|
||||||
{:else}
|
|
||||||
{$i18n.t("Members")}
|
|
||||||
{/if}
|
|
||||||
</UpLink>
|
|
||||||
{:else}
|
|
||||||
{title || ""}
|
|
||||||
{/if}
|
{/if}
|
||||||
|
{title || ""}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
{#if currentWidget && (availableWidgets.length > 1 || editable)}
|
{#if currentWidget && (availableWidgets.length > 1 || editable)}
|
||||||
<div class="views">
|
<div class="views">
|
||||||
{#each availableWidgets as widget (widget.name)}
|
{#each availableWidgets as widget (widget.name)}
|
||||||
<IconButton
|
<IconButton
|
||||||
name={widget.icon || "question-diamond"}
|
name={widget.icon || "cube"}
|
||||||
|
title={widget.name}
|
||||||
active={widget.name === currentWidget}
|
active={widget.name === currentWidget}
|
||||||
--active-color="var(--foreground)"
|
--active-color="var(--foreground)"
|
||||||
on:click={() => switchWidget(widget.name)}
|
on:click={() => switchWidget(widget.name)}>{widget.name}</IconButton
|
||||||
/>
|
>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</header>
|
</header>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
{#if !reverse}
|
{#each components as component}
|
||||||
{#each components as component}
|
|
||||||
<svelte:component
|
|
||||||
this={component.component}
|
|
||||||
{...(typeof component.props === "function"
|
|
||||||
? component.props(entries)
|
|
||||||
: component.props) || {}}
|
|
||||||
{entries}
|
|
||||||
{editable}
|
|
||||||
{type}
|
|
||||||
on:change
|
|
||||||
/>
|
|
||||||
{/each}
|
|
||||||
{:else}
|
|
||||||
<!-- shut up svelte check -->
|
|
||||||
<svelte:component
|
<svelte:component
|
||||||
this={EntryList}
|
this={component.component}
|
||||||
columns="entity, attribute"
|
{...component.props || {}}
|
||||||
{entries}
|
{editable}
|
||||||
|
on:change
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@use "./util";
|
@use "./util";
|
||||||
|
|
||||||
section h3 {
|
section.entry-view {
|
||||||
transition: text-shadow 0.2s;
|
header {
|
||||||
}
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
section.highlighted h3 {
|
.icon {
|
||||||
text-shadow: #cb4b16 0 0 0.5em;
|
display: inline-block;
|
||||||
}
|
font-size: 1.25em;
|
||||||
|
margin-top: -0.3em;
|
||||||
|
position: relative;
|
||||||
|
bottom: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
.icon {
|
h3 {
|
||||||
display: inline-block;
|
transition: text-shadow 0.2s;
|
||||||
font-size: 1.25em;
|
}
|
||||||
margin-top: -0.3em;
|
|
||||||
position: relative;
|
&.highlighted h3 {
|
||||||
bottom: -2px;
|
text-shadow: #cb4b16 0 0 0.5em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.views {
|
.views {
|
||||||
display: flex;
|
display: flex;
|
||||||
right: 1ex;
|
right: 1ex;
|
||||||
|
transform: translateY(-25%);
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import AttributeView from "./AttributeView.svelte";
|
import EntryView from "./EntryView.svelte";
|
||||||
import { query, useEntity } from "../lib/entity";
|
import { query, useEntity } from "../lib/entity";
|
||||||
import UpObject from "./display/UpObject.svelte";
|
import UpObject from "./display/UpObject.svelte";
|
||||||
import { UpType } from "../lib/types";
|
|
||||||
import { createEventDispatcher, setContext } from "svelte";
|
import { createEventDispatcher, setContext } from "svelte";
|
||||||
import { writable } from "svelte/store";
|
import { writable } from "svelte/store";
|
||||||
import type { UpEntry } from "upend";
|
import type { UpEntry } from "upend";
|
||||||
|
@ -19,6 +18,7 @@
|
||||||
import { i18n } from "../i18n";
|
import { i18n } from "../i18n";
|
||||||
import EntryList from "./widgets/EntryList.svelte";
|
import EntryList from "./widgets/EntryList.svelte";
|
||||||
import api from "../lib/api";
|
import api from "../lib/api";
|
||||||
|
import Gallery from "./widgets/Gallery.svelte";
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
|
||||||
|
@ -44,38 +44,6 @@
|
||||||
|
|
||||||
$: ({ entity, entityInfo, error, revalidate } = useEntity(address));
|
$: ({ entity, entityInfo, error, revalidate } = useEntity(address));
|
||||||
|
|
||||||
$: allTypeAddresses = ($entity?.attr["OF"] || [])
|
|
||||||
.filter((attr) => attr.value.t == "Address")
|
|
||||||
.map((attr) => attr.value.c);
|
|
||||||
|
|
||||||
$: allTypeEntries = query(
|
|
||||||
`(matches (in ${allTypeAddresses.map((addr) => `@${addr}`).join(" ")}) ? ?)`
|
|
||||||
).result;
|
|
||||||
|
|
||||||
let allTypes: { [key: string]: UpType } = {};
|
|
||||||
$: {
|
|
||||||
allTypes = {};
|
|
||||||
($allTypeEntries?.entries || []).forEach((entry) => {
|
|
||||||
if (allTypes[entry.entity] === undefined) {
|
|
||||||
allTypes[entry.entity] = new UpType(entry.entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (entry.attribute) {
|
|
||||||
case "TYPE":
|
|
||||||
allTypes[entry.entity].name = String(entry.value.c);
|
|
||||||
break;
|
|
||||||
case "LBL":
|
|
||||||
allTypes[entry.entity].label = String(entry.value.c);
|
|
||||||
break;
|
|
||||||
case "TYPE_HAS":
|
|
||||||
allTypes[entry.entity].attributes.push(String(entry.value.c));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
allTypes = allTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
let typedAttributes = {} as { [key: string]: UpEntry[] };
|
let typedAttributes = {} as { [key: string]: UpEntry[] };
|
||||||
let untypedAttributes = [] as UpEntry[];
|
let untypedAttributes = [] as UpEntry[];
|
||||||
|
|
||||||
|
@ -84,8 +52,10 @@
|
||||||
untypedAttributes = [];
|
untypedAttributes = [];
|
||||||
|
|
||||||
($entity?.attributes || []).forEach((entry) => {
|
($entity?.attributes || []).forEach((entry) => {
|
||||||
const entryTypes = Object.entries(allTypes).filter(([_, t]) =>
|
const entryTypes = Object.entries(/*allTypes*/ {}).filter(
|
||||||
t.attributes.includes(entry.attribute)
|
([_, t]) =>
|
||||||
|
// t.attributes.includes(entry.attribute)
|
||||||
|
false
|
||||||
);
|
);
|
||||||
if (entryTypes.length > 0) {
|
if (entryTypes.length > 0) {
|
||||||
entryTypes.forEach(([addr, _]) => {
|
entryTypes.forEach(([addr, _]) => {
|
||||||
|
@ -106,7 +76,6 @@
|
||||||
$: filteredUntypedAttributes = untypedAttributes.filter(
|
$: filteredUntypedAttributes = untypedAttributes.filter(
|
||||||
(entry) =>
|
(entry) =>
|
||||||
![
|
![
|
||||||
"IS",
|
|
||||||
"LBL",
|
"LBL",
|
||||||
"OF",
|
"OF",
|
||||||
"NOTE",
|
"NOTE",
|
||||||
|
@ -120,9 +89,15 @@
|
||||||
? untypedAttributes
|
? untypedAttributes
|
||||||
: filteredUntypedAttributes;
|
: filteredUntypedAttributes;
|
||||||
|
|
||||||
$: currentBacklinks = $entity?.backlinks || [];
|
$: currentBacklinks =
|
||||||
|
(editable
|
||||||
|
? $entity?.backlinks
|
||||||
|
: $entity?.backlinks.filter(
|
||||||
|
(entry) => !["OF"].includes(entry.attribute)
|
||||||
|
)) || [];
|
||||||
|
|
||||||
$: groups = [];
|
$: groups = ($entity?.attr["OF"] || []).map((e) => e.value.c as string);
|
||||||
|
$: tagged = $entity?.attr["~OF"] || [];
|
||||||
|
|
||||||
let attributesUsed: UpEntry[] = [];
|
let attributesUsed: UpEntry[] = [];
|
||||||
$: {
|
$: {
|
||||||
|
@ -238,33 +213,22 @@
|
||||||
</div>
|
</div>
|
||||||
<NotesEditor {address} {editable} on:change={onChange} />
|
<NotesEditor {address} {editable} on:change={onChange} />
|
||||||
{#if !$error}
|
{#if !$error}
|
||||||
{#if Object.keys(allTypes).length || groups.length}
|
{#if groups.length}
|
||||||
<section class="tags labelborder">
|
<section class="tags labelborder">
|
||||||
<header><h3>{$i18n.t("Tags")}</h3></header>
|
<header><h3>{$i18n.t("Tags")}</h3></header>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
{#each Object.values(allTypes) as type}
|
{#each groups as group}
|
||||||
<div
|
<div
|
||||||
class="tag type"
|
class="tag"
|
||||||
on:mouseenter={() => (highlightedType = type.address)}
|
on:mouseenter={() => (highlightedType = group)}
|
||||||
on:mouseleave={() => (highlightedType = undefined)}
|
on:mouseleave={() => (highlightedType = undefined)}
|
||||||
>
|
>
|
||||||
<UpObject address={type.address} link />
|
<UpObject address={group} link />
|
||||||
{#if editable}
|
{#if editable}
|
||||||
<IconButton name="x-circle" />
|
<IconButton name="x-circle" />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
{#each groups as [entryAddress, address]}
|
|
||||||
<div class="tag group">
|
|
||||||
<UpObject {address} link />
|
|
||||||
{#if editable}
|
|
||||||
<IconButton
|
|
||||||
name="x-circle"
|
|
||||||
on:click={() => removeGroup(entryAddress)}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
{#if editable}
|
{#if editable}
|
||||||
<div class="selector">
|
<div class="selector">
|
||||||
<Selector
|
<Selector
|
||||||
|
@ -279,70 +243,97 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{/if}
|
{/if}
|
||||||
{#if Boolean($allTypeEntries)}
|
<div class="attributes">
|
||||||
<div class="attributes">
|
{#each Object.entries(typedAttributes) as [typeAddr, entries] (typeAddr)}
|
||||||
{#each Object.entries(typedAttributes) as [typeAddr, entries] (typeAddr)}
|
<EntryView
|
||||||
<AttributeView
|
{entries}
|
||||||
{entries}
|
{editable}
|
||||||
type={allTypes[typeAddr]}
|
on:change={onChange}
|
||||||
{editable}
|
initialWidget={String($entity.get("LAST_ATTRIBUTE_WIDGET"))}
|
||||||
on:change={onChange}
|
on:widgetSwitched={onAttributeWidgetSwitch}
|
||||||
initialWidget={String($entity.get("LAST_ATTRIBUTE_WIDGET"))}
|
highlighted={highlightedType == typeAddr}
|
||||||
on:widgetSwitched={onAttributeWidgetSwitch}
|
/>
|
||||||
highlighted={highlightedType == typeAddr}
|
{/each}
|
||||||
/>
|
|
||||||
{/each}
|
|
||||||
|
|
||||||
{#if currentUntypedAttributes.length > 0 || editable}
|
{#if currentUntypedAttributes.length > 0 || editable}
|
||||||
<AttributeView
|
<EntryView
|
||||||
title={$i18n.t("Attributes")}
|
title={$i18n.t("Attributes")}
|
||||||
{editable}
|
{editable}
|
||||||
entries={currentUntypedAttributes}
|
entries={currentUntypedAttributes}
|
||||||
on:change={onChange}
|
on:change={onChange}
|
||||||
/>
|
/>
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if currentBacklinks.length > 0}
|
|
||||||
<AttributeView
|
|
||||||
title={`${$i18n.t("Referred to")} (${
|
|
||||||
$entity.backlinks.length
|
|
||||||
})`}
|
|
||||||
entries={currentBacklinks}
|
|
||||||
reverse
|
|
||||||
on:change={onChange}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if $entityInfo?.t === "Attribute"}
|
|
||||||
<div class="buttons">
|
|
||||||
<div class="button">
|
|
||||||
<Link to="/surface?x={$entityInfo.c}">
|
|
||||||
{$i18n.t("Surface view")}
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<section class="labelborder">
|
|
||||||
<header>
|
|
||||||
<h3>{$i18n.t("Used")} ({attributesUsed.length})</h3>
|
|
||||||
</header>
|
|
||||||
<EntryList
|
|
||||||
columns="entity,value"
|
|
||||||
columnWidths={["auto", "33%"]}
|
|
||||||
entries={attributesUsed}
|
|
||||||
orderByValue
|
|
||||||
/>
|
|
||||||
</section>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if editable}
|
|
||||||
<div class="button" on:click={deleteObject}>
|
|
||||||
<Icon name="trash" />
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
|
||||||
<Spinner centered />
|
{#if currentBacklinks.length > 0}
|
||||||
|
<EntryView
|
||||||
|
title={`${$i18n.t("Referred to")} (${currentBacklinks.length})`}
|
||||||
|
entries={currentBacklinks}
|
||||||
|
on:change={onChange}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if tagged.length > 0}
|
||||||
|
<EntryView
|
||||||
|
title={`${$i18n.t("Links")}`}
|
||||||
|
widgets={[
|
||||||
|
{
|
||||||
|
name: "List",
|
||||||
|
components: (entries) => [
|
||||||
|
{
|
||||||
|
component: Gallery,
|
||||||
|
props: {
|
||||||
|
entities: entries.map((e) => e.entity),
|
||||||
|
thumbnails: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Gallery",
|
||||||
|
components: (entries) => [
|
||||||
|
{
|
||||||
|
component: Gallery,
|
||||||
|
props: {
|
||||||
|
entities: entries.map((e) => e.entity),
|
||||||
|
thumbnails: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
entries={tagged}
|
||||||
|
on:change={onChange}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if $entityInfo?.t === "Attribute"}
|
||||||
|
<div class="buttons">
|
||||||
|
<div class="button">
|
||||||
|
<Link to="/surface?x={$entityInfo.c}">
|
||||||
|
{$i18n.t("Surface view")}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="labelborder">
|
||||||
|
<header>
|
||||||
|
<h3>{$i18n.t("Used")} ({attributesUsed.length})</h3>
|
||||||
|
</header>
|
||||||
|
<EntryList
|
||||||
|
columns="entity,value"
|
||||||
|
columnWidths={["auto", "33%"]}
|
||||||
|
entries={attributesUsed}
|
||||||
|
orderByValue
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if editable}
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<div class="button" on:click={deleteObject}>
|
||||||
|
<Icon name="trash" />
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
<div class="error">
|
<div class="error">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import filesize from "filesize";
|
import filesize from "filesize";
|
||||||
import { format, formatRelative, fromUnixTime, parseISO } from "date-fns";
|
import { formatRelative, fromUnixTime, parseISO } from "date-fns";
|
||||||
import Ellipsis from "../utils/Ellipsis.svelte";
|
import Ellipsis from "../utils/Ellipsis.svelte";
|
||||||
import UpObject from "../display/UpObject.svelte";
|
import UpObject from "../display/UpObject.svelte";
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
|
@ -17,18 +17,17 @@
|
||||||
import { formatDuration } from "../../util/fragments/time";
|
import { formatDuration } from "../../util/fragments/time";
|
||||||
import { i18n } from "../../i18n";
|
import { i18n } from "../../i18n";
|
||||||
import UpLink from "../display/UpLink.svelte";
|
import UpLink from "../display/UpLink.svelte";
|
||||||
import type { UpType } from "src/lib/types";
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
export let columns: string | undefined = undefined;
|
export let columns: string | undefined = undefined;
|
||||||
export let header = true;
|
export let header = true;
|
||||||
|
|
||||||
export let orderByValue = false;
|
export let orderByValue = false;
|
||||||
export let columnWidths: string[] = [];
|
export let columnWidths: string[] | undefined = undefined;
|
||||||
|
|
||||||
export let entries: UpEntry[];
|
export let entries: UpEntry[];
|
||||||
export let type: UpType | undefined = undefined;
|
|
||||||
export let editable = false;
|
export let editable = false;
|
||||||
|
export let attributeOptions: string[] | undefined = undefined;
|
||||||
|
|
||||||
// Display
|
// Display
|
||||||
$: displayColumns = (columns || "attribute, value")
|
$: displayColumns = (columns || "attribute, value")
|
||||||
|
@ -182,7 +181,7 @@
|
||||||
<col class="action-col" />
|
<col class="action-col" />
|
||||||
{/if}
|
{/if}
|
||||||
{#each displayColumns as column, idx}
|
{#each displayColumns as column, idx}
|
||||||
{#if columnWidths.length}
|
{#if columnWidths?.length}
|
||||||
<col
|
<col
|
||||||
class="{column}-col"
|
class="{column}-col"
|
||||||
style="width: {columnWidths[idx] || 'unset'}"
|
style="width: {columnWidths[idx] || 'unset'}"
|
||||||
|
@ -300,7 +299,7 @@
|
||||||
<Selector
|
<Selector
|
||||||
type="attribute"
|
type="attribute"
|
||||||
bind:attribute={newEntryAttribute}
|
bind:attribute={newEntryAttribute}
|
||||||
attributeOptions={type?.attributes}
|
attributeOptions={attributeOptions || []}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -1,108 +0,0 @@
|
||||||
import type { UpEntry } from "upend";
|
|
||||||
import Gallery from "../components/widgets/Gallery.svelte";
|
|
||||||
|
|
||||||
export class UpType {
|
|
||||||
address: string | undefined;
|
|
||||||
name: string | null = null;
|
|
||||||
label: string | null = null;
|
|
||||||
attributes: string[] = [];
|
|
||||||
|
|
||||||
constructor(address?: string) {
|
|
||||||
this.address = address;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get icon(): string | undefined {
|
|
||||||
return this.name ? TYPE_ICONS[this.name] : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get widgetInfo(): Widget[] {
|
|
||||||
return TYPE_WIDGETS[this.name] || [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const UNTYPED = new UpType();
|
|
||||||
|
|
||||||
export interface Component {
|
|
||||||
component: any; // TODO
|
|
||||||
props?:
|
|
||||||
| { [key: string]: unknown }
|
|
||||||
| ((entries: UpEntry[]) => { [key: string]: unknown });
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Widget {
|
|
||||||
name: string;
|
|
||||||
icon?: string;
|
|
||||||
components: Array<Component>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TYPE_ICONS: { [key: string]: string } = {
|
|
||||||
BLOB: "package",
|
|
||||||
HIER: "folder",
|
|
||||||
};
|
|
||||||
|
|
||||||
const TYPE_WIDGETS: { [key: string]: Widget[] } = {
|
|
||||||
HIER: [
|
|
||||||
{
|
|
||||||
name: "hierarchical-listing",
|
|
||||||
icon: "folder",
|
|
||||||
components: [
|
|
||||||
{
|
|
||||||
component: Gallery,
|
|
||||||
props: (entries) => {
|
|
||||||
return {
|
|
||||||
thumbnails: false,
|
|
||||||
entities: entries
|
|
||||||
.filter((e) => e.attribute == "HAS")
|
|
||||||
.map((e) => String(e.value.c)),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "hierarchical-listing-gallery",
|
|
||||||
icon: "image",
|
|
||||||
components: [
|
|
||||||
{
|
|
||||||
component: Gallery,
|
|
||||||
props: (entries) => {
|
|
||||||
return {
|
|
||||||
thumbnails: true,
|
|
||||||
entities: entries
|
|
||||||
.filter((e) => e.attribute == "HAS")
|
|
||||||
.map((e) => String(e.value.c)),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
KSX_TRACK_MOODS: [
|
|
||||||
{
|
|
||||||
name: "ksx-track-compass",
|
|
||||||
icon: "plus-square",
|
|
||||||
components: [
|
|
||||||
// {
|
|
||||||
// name: "Compass",
|
|
||||||
// id: "compass_tint_energy",
|
|
||||||
// props: {
|
|
||||||
// xAttrName: "KSX_TINT",
|
|
||||||
// yAttrName: "KSX_ENERGY",
|
|
||||||
// xLabel: "Lightsoft // Heavydark",
|
|
||||||
// yLabel: "Chill // Extreme",
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// name: "Compass",
|
|
||||||
// id: "compass_seriousness_materials",
|
|
||||||
// props: {
|
|
||||||
// xAttrName: "KSX_SERIOUSNESS",
|
|
||||||
// yAttrName: "KSX_MATERIALS",
|
|
||||||
// xLabel: "Dionysia // Apollonia",
|
|
||||||
// yLabel: "Natural // Reinforced",
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
|
@ -1,10 +1,10 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import EntryList from "../components/widgets/EntryList.svelte";
|
import EntryList from "../components/widgets/EntryList.svelte";
|
||||||
import Gallery from "../components/widgets/Gallery.svelte";
|
import Gallery from "../components/widgets/Gallery.svelte";
|
||||||
import type { Widget } from "src/lib/types";
|
import type { Widget } from "../components/EntryView.svelte";
|
||||||
import { Link } from "svelte-navigator";
|
import { Link } from "svelte-navigator";
|
||||||
import { UpListing } from "upend";
|
import { UpListing } from "upend";
|
||||||
import AttributeView from "../components/AttributeView.svelte";
|
import EntryView from "../components/EntryView.svelte";
|
||||||
import UpObjectCard from "../components/display/UpObjectCard.svelte";
|
import UpObjectCard from "../components/display/UpObjectCard.svelte";
|
||||||
import Spinner from "../components/utils/Spinner.svelte";
|
import Spinner from "../components/utils/Spinner.svelte";
|
||||||
import api from "../lib/api";
|
import api from "../lib/api";
|
||||||
|
@ -42,9 +42,9 @@
|
||||||
|
|
||||||
const shortWidgets: Widget[] = [
|
const shortWidgets: Widget[] = [
|
||||||
{
|
{
|
||||||
name: "list-table",
|
name: "List",
|
||||||
icon: "list-ul",
|
icon: "list-ul",
|
||||||
components: [
|
components: (entries) => [
|
||||||
{
|
{
|
||||||
component: EntryList,
|
component: EntryList,
|
||||||
props: {
|
props: {
|
||||||
|
@ -52,21 +52,20 @@
|
||||||
columnWidths: ["6em"],
|
columnWidths: ["6em"],
|
||||||
orderByValue: true,
|
orderByValue: true,
|
||||||
header: false,
|
header: false,
|
||||||
|
entries,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "gallery-view",
|
name: "Gallery",
|
||||||
icon: "image",
|
icon: "image",
|
||||||
components: [
|
components: (entries) => [
|
||||||
{
|
{
|
||||||
component: Gallery,
|
component: Gallery,
|
||||||
props: (entries) => {
|
props: {
|
||||||
return {
|
entities: entries.map((e) => e.entity),
|
||||||
entities: entries.map((e) => e.entity),
|
sort: false,
|
||||||
sort: false,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -75,9 +74,9 @@
|
||||||
|
|
||||||
const longWidgets: Widget[] = [
|
const longWidgets: Widget[] = [
|
||||||
{
|
{
|
||||||
name: "list-table",
|
name: "List",
|
||||||
icon: "list-ul",
|
icon: "list-ul",
|
||||||
components: [
|
components: (entries) => [
|
||||||
{
|
{
|
||||||
component: EntryList,
|
component: EntryList,
|
||||||
props: {
|
props: {
|
||||||
|
@ -85,21 +84,20 @@
|
||||||
columnWidths: ["13em"],
|
columnWidths: ["13em"],
|
||||||
orderByValue: true,
|
orderByValue: true,
|
||||||
header: false,
|
header: false,
|
||||||
|
entries,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "gallery-view",
|
name: "Gallery",
|
||||||
icon: "image",
|
icon: "image",
|
||||||
components: [
|
components: (entries) => [
|
||||||
{
|
{
|
||||||
component: Gallery,
|
component: Gallery,
|
||||||
props: (entries) => {
|
props: {
|
||||||
return {
|
entities: entries.map((e) => e.entity),
|
||||||
entities: entries.map((e) => e.entity),
|
sort: false,
|
||||||
sort: false,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -138,7 +136,7 @@
|
||||||
{#if $frequentQuery == undefined}
|
{#if $frequentQuery == undefined}
|
||||||
<Spinner centered />
|
<Spinner centered />
|
||||||
{:else}
|
{:else}
|
||||||
<AttributeView
|
<EntryView
|
||||||
--current-background="var(--background)"
|
--current-background="var(--background)"
|
||||||
entries={frequent}
|
entries={frequent}
|
||||||
widgets={shortWidgets}
|
widgets={shortWidgets}
|
||||||
|
@ -152,7 +150,7 @@
|
||||||
{#if $recentQuery == undefined}
|
{#if $recentQuery == undefined}
|
||||||
<Spinner centered />
|
<Spinner centered />
|
||||||
{:else}
|
{:else}
|
||||||
<AttributeView
|
<EntryView
|
||||||
--current-background="var(--background)"
|
--current-background="var(--background)"
|
||||||
entries={recent}
|
entries={recent}
|
||||||
widgets={longWidgets}
|
widgets={longWidgets}
|
||||||
|
@ -168,7 +166,7 @@
|
||||||
{#if $latestQuery == undefined}
|
{#if $latestQuery == undefined}
|
||||||
<Spinner centered />
|
<Spinner centered />
|
||||||
{:else}
|
{:else}
|
||||||
<AttributeView
|
<EntryView
|
||||||
--current-background="var(--background)"
|
--current-background="var(--background)"
|
||||||
entries={latest}
|
entries={latest}
|
||||||
widgets={longWidgets}
|
widgets={longWidgets}
|
||||||
|
|
|
@ -9,10 +9,9 @@
|
||||||
import { baseSearch, createLabelled } from "../util/search";
|
import { baseSearch, createLabelled } from "../util/search";
|
||||||
import { updateTitle } from "../util/title";
|
import { updateTitle } from "../util/title";
|
||||||
import { query as queryFn } from "../lib/entity";
|
import { query as queryFn } from "../lib/entity";
|
||||||
import AttributeView from "../components/AttributeView.svelte";
|
import EntryView, { type Widget } from "../components/EntryView.svelte";
|
||||||
import api from "../lib/api";
|
import api from "../lib/api";
|
||||||
import Gallery from "../components/widgets/Gallery.svelte";
|
import Gallery from "../components/widgets/Gallery.svelte";
|
||||||
import type { Widget } from "src/lib/types";
|
|
||||||
import { matchSorter } from "match-sorter";
|
import { matchSorter } from "match-sorter";
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
@ -44,15 +43,15 @@
|
||||||
let exactHits: string[] = [];
|
let exactHits: string[] = [];
|
||||||
$: {
|
$: {
|
||||||
const addressesString = objects.map((e) => `@${e.entity}`).join(" ");
|
const addressesString = objects.map((e) => `@${e.entity}`).join(" ");
|
||||||
api.query(`(matches (in ${addressesString}) "LBL" ? )`).then(
|
api
|
||||||
(labelListing) => {
|
.query(`(matches (in ${addressesString}) "LBL" ? )`)
|
||||||
|
.then((labelListing) => {
|
||||||
exactHits = labelListing.entries
|
exactHits = labelListing.entries
|
||||||
.filter(
|
.filter(
|
||||||
(e) => String(e.value.c).toLowerCase() === query.toLowerCase()
|
(e) => String(e.value.c).toLowerCase() === query.toLowerCase()
|
||||||
)
|
)
|
||||||
.map((e) => e.entity);
|
.map((e) => e.entity);
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function create() {
|
async function create() {
|
||||||
|
@ -64,33 +63,29 @@
|
||||||
|
|
||||||
const searchWidgets: Widget[] = [
|
const searchWidgets: Widget[] = [
|
||||||
{
|
{
|
||||||
name: "list-table",
|
name: "List",
|
||||||
icon: "list-ul",
|
icon: "list-ul",
|
||||||
components: [
|
components: (entries) => [
|
||||||
{
|
{
|
||||||
component: Gallery,
|
component: Gallery,
|
||||||
props: (entries) => {
|
props: {
|
||||||
return {
|
entities: entries.map((e) => e.entity),
|
||||||
entities: entries.map((e) => e.entity),
|
sort: false,
|
||||||
sort: false,
|
thumbnails: false,
|
||||||
thumbnails: false,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "gallery-view",
|
name: "Gallery",
|
||||||
icon: "image",
|
icon: "image",
|
||||||
components: [
|
components: (entries) => [
|
||||||
{
|
{
|
||||||
component: Gallery,
|
component: Gallery,
|
||||||
props: (entries) => {
|
props: {
|
||||||
return {
|
entities: entries.map((e) => e.entity),
|
||||||
entities: entries.map((e) => e.entity),
|
sort: false,
|
||||||
sort: false,
|
thumbnails: true,
|
||||||
thumbnails: true,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -123,7 +118,7 @@
|
||||||
<section class="objects">
|
<section class="objects">
|
||||||
{#if sortedObjects.length}
|
{#if sortedObjects.length}
|
||||||
<h2>Objects</h2>
|
<h2>Objects</h2>
|
||||||
<AttributeView
|
<EntryView
|
||||||
--current-background="var(--background)"
|
--current-background="var(--background)"
|
||||||
entries={sortedObjects}
|
entries={sortedObjects}
|
||||||
widgets={searchWidgets}
|
widgets={searchWidgets}
|
||||||
|
|
Loading…
Reference in a new issue