213 lines
6.9 KiB
Svelte
213 lines
6.9 KiB
Svelte
<script lang="ts">
|
|
import { state } from "./state";
|
|
import SettingsIc from "carbon-icons-svelte/lib/Settings.svelte";
|
|
import HomeIc from "carbon-icons-svelte/lib/Home.svelte";
|
|
import ForumIc from "carbon-icons-svelte/lib/Forum.svelte";
|
|
import FolderIc from "carbon-icons-svelte/lib/Folder.svelte";
|
|
import BookmarkIc from "carbon-icons-svelte/lib/Bookmark.svelte";
|
|
import DocumentIc from "carbon-icons-svelte/lib/Document.svelte";
|
|
import MusicIc from "carbon-icons-svelte/lib/Music.svelte";
|
|
import ImageIc from "carbon-icons-svelte/lib/Image.svelte";
|
|
import VideoIc from "carbon-icons-svelte/lib/Video.svelte";
|
|
import ChatIc from "carbon-icons-svelte/lib/Chat.svelte";
|
|
import UnknownIc from "carbon-icons-svelte/lib/Unknown.svelte";
|
|
import { Event } from "./lib/api";
|
|
import { filterRels } from "./lib/util";
|
|
|
|
export let items: Array<Event>;
|
|
export let selected: Promise<Event | null> = Promise.resolve(null);
|
|
let selectedRes: Event | null = null;
|
|
$: selected.then(s => selectedRes = s);
|
|
|
|
function getIcon(event: Event) {
|
|
switch (event.type) {
|
|
case "home": return HomeIc;
|
|
case "settings": return SettingsIc;
|
|
case "x.file": {
|
|
switch (event.derived?.file?.mime.split("/")[0]) {
|
|
case "image": return ImageIc;
|
|
case "video": return VideoIc;
|
|
case "audio": return MusicIc;
|
|
default: return DocumentIc;
|
|
}
|
|
}
|
|
case "l.files": return FolderIc;
|
|
case "l.notes": return BookmarkIc;
|
|
case "l.forum": return ForumIc;
|
|
case "l.forum.post": return ChatIc;
|
|
case "l.docs": return DocumentIc;
|
|
case "l.doc": return DocumentIc;
|
|
default: return UnknownIc;
|
|
}
|
|
}
|
|
|
|
function getName(event: Event) {
|
|
const cont = event.getContent();
|
|
if (!cont) return "---";
|
|
switch (event.type) {
|
|
case "home": return "home";
|
|
case "settings": return "settings";
|
|
case "x.file": return cont.name || "file";
|
|
case "l.files": return cont.name;
|
|
case "l.notes": return cont.name;
|
|
case "l.forum": return cont.name;
|
|
case "l.forum.post": return cont.title;
|
|
case "l.docs": return cont.name;
|
|
case "l.doc": return cont.name;
|
|
default: return null;
|
|
}
|
|
}
|
|
|
|
const openContext = (event: Event) => (e) => {
|
|
const items = [
|
|
{ type: "submenu", text: "edit", clicked: state.curry("popup/open", { type: "edit", event, panel: "general" }), items: [
|
|
{ type: "item", text: "general", clicked: state.curry("popup/open", { type: "edit", event, panel: "general" }) },
|
|
{ type: "item", text: "access", clicked: state.curry("popup/open", { type: "edit", event, panel: "access" }) },
|
|
{ type: "item", text: "members", clicked: state.curry("popup/open", { type: "edit", event, panel: "members" }) },
|
|
{ type: "item", text: "debug", clicked: state.curry("popup/open", { type: "edit", event, panel: "debug" }) },
|
|
] },
|
|
{ type: "item", text: "pin", clicked: state.curry("popup/open", { type: "text", text: "not implemented yet!" }) },
|
|
{ type: "item", text: "copy id", clicked: navigator.clipboard.writeText(event.id) },
|
|
];
|
|
state.do("menu/open", { x: e.clientX, y: e.clientY, items });
|
|
};
|
|
|
|
$: selectedResParentId = selectedRes ? filterRels(selectedRes, "in")[0] : null;
|
|
</script>
|
|
<nav id="nav">
|
|
{#if selectedRes && !selectedResParentId && items.findIndex(ev => ev.id === selectedRes.id) === -1}
|
|
{@const name = getName(selectedRes) || "unnamed"}
|
|
<a
|
|
href="#/{selectedRes.id}"
|
|
class="wrapper selected temp"
|
|
on:contextmenu|preventDefault|stopPropagation={openContext(selectedRes)}
|
|
>
|
|
<div class="highlight">
|
|
<div class="icon"><svelte:component this={getIcon(selectedRes)} /></div>
|
|
<span class="name" title={name}>{name}</span>
|
|
<button on:click|preventDefault={state.curry("popup/open", { type: "edit", event: selectedRes, panel: "general" })}>+</button>
|
|
</div>
|
|
</a>
|
|
{/if}
|
|
{#each items as item (item.id)}
|
|
{@const name = getName(item) || "unnamed"}
|
|
<a
|
|
href="#/{item.id}"
|
|
class="wrapper"
|
|
class:selected={item.id === selectedRes?.id}
|
|
on:contextmenu|preventDefault|stopPropagation={openContext(item)}
|
|
>
|
|
<div class="highlight">
|
|
<div class="icon"><svelte:component this={getIcon(item)} /></div>
|
|
<span class="name" title={name}>{name}</span>
|
|
{#if !["home", "settings"].includes(item.id)}
|
|
<button on:click|preventDefault={state.curry("popup/open", { type: "edit", event: item, panel: "general" })}>+</button>
|
|
{/if}
|
|
</div>
|
|
</a>
|
|
{#if item.id === selectedResParentId}
|
|
{@const name = getName(selectedRes) || "unnamed"}
|
|
<a
|
|
href="#/{selectedRes.id}"
|
|
class="wrapper selected child"
|
|
on:contextmenu|preventDefault|stopPropagation={openContext(selectedRes)}
|
|
>
|
|
<div class="highlight">
|
|
<span class="name" title={name}>{name}</span>
|
|
<button on:click|preventDefault={state.curry("popup/open", { type: "edit", event: selectedRes, panel: "general" })}>+</button>
|
|
</div>
|
|
</a>
|
|
{/if}
|
|
{/each}
|
|
</nav>
|
|
<style lang="scss">
|
|
#nav {
|
|
grid-area: nav;
|
|
display: flex;
|
|
flex-direction: column;
|
|
background: var(--bg-secondary);
|
|
padding: 2px 0;
|
|
overflow-y: auto;
|
|
contain: strict;
|
|
padding-bottom: 64px;
|
|
|
|
& > .wrapper {
|
|
padding: 2px 4px;
|
|
text-decoration: none;
|
|
|
|
&.selected > .highlight {
|
|
background: #ffffff33;
|
|
}
|
|
|
|
&.temp > .highlight {
|
|
border-left: solid var(--fg-dimmed) 4px;
|
|
border-top-left-radius: 0;
|
|
border-bottom-left-radius: 0;
|
|
margin-left: -4px;
|
|
}
|
|
|
|
&.child {
|
|
position: relative;
|
|
padding-left: 32px;
|
|
|
|
&::before {
|
|
content: "";
|
|
display: block;
|
|
position: absolute;
|
|
|
|
--size: 12px;
|
|
left: calc(4px + var(--size));
|
|
top: calc(50% - var(--size));
|
|
height: var(--size);
|
|
width: var(--size);
|
|
|
|
--color: #44535f;
|
|
border-bottom: solid var(--color) 1px;
|
|
border-left: solid var(--color) 1px;
|
|
}
|
|
}
|
|
|
|
& > .highlight {
|
|
display: flex;
|
|
border-radius: 2px;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
|
|
& > .icon {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 4px;
|
|
}
|
|
|
|
& > .name {
|
|
flex: 1;
|
|
padding: 4px;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
overflow: hidden;
|
|
}
|
|
|
|
& > button {
|
|
padding: 4px;
|
|
border: none;
|
|
background: none;
|
|
color: inherit;
|
|
display: none;
|
|
text-decoration: none;
|
|
}
|
|
}
|
|
|
|
&:is(:hover, :focus-visible) > .highlight {
|
|
background: #ffffff22;
|
|
|
|
& > .name {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
& > button {
|
|
display: block;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|