summaryrefslogtreecommitdiffhomepage
path: root/src/features/views/ui/ViewSidebar.svelte
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-27 01:13:28 +0900
committerAdam Malczewski <[email protected]>2026-06-27 01:13:31 +0900
commit2fa03f8d7410c2b8d6be8e10ad088863e83d7177 (patch)
tree94e1923180ae38d571d34b578afecb0a18913c24 /src/features/views/ui/ViewSidebar.svelte
parent80f99665034a0e510300793205c162fc7a46769f (diff)
parent08b12478636f4a5c86a1f3c40a843f2906b7c82f (diff)
downloaddispatch-web-2fa03f8d7410c2b8d6be8e10ad088863e83d7177.tar.gz
dispatch-web-2fa03f8d7410c2b8d6be8e10ad088863e83d7177.zip
Merge branch 'dev' into feature/heartbeat
# Conflicts: # src/app/App.svelte # src/app/store.svelte.ts # src/app/store.test.ts # src/features/workspaces/ui/WorkspaceCard.test.ts
Diffstat (limited to 'src/features/views/ui/ViewSidebar.svelte')
-rw-r--r--src/features/views/ui/ViewSidebar.svelte170
1 files changed, 84 insertions, 86 deletions
diff --git a/src/features/views/ui/ViewSidebar.svelte b/src/features/views/ui/ViewSidebar.svelte
index e4a3ee6..e9e8682 100644
--- a/src/features/views/ui/ViewSidebar.svelte
+++ b/src/features/views/ui/ViewSidebar.svelte
@@ -1,97 +1,95 @@
<script lang="ts">
- import { type Snippet, untrack } from "svelte";
- import {
- addPanel,
- initialPanels,
- type PanelsState,
- removePanel,
- selectKind,
- } from "../logic/panels";
+ import { type Snippet, untrack } from "svelte";
+ import {
+ addPanel,
+ initialPanels,
+ type PanelsState,
+ removePanel,
+ selectKind,
+ } from "../logic/panels";
- interface ViewKind {
- readonly id: string;
- readonly label: string;
- }
+ interface ViewKind {
+ readonly id: string;
+ readonly label: string;
+ }
- let {
- kinds,
- content,
- initial,
- onChange,
- }: {
- /** The view kinds offered in every panel's dropdown. */
- kinds: readonly ViewKind[];
- /** Renders a panel body for the given (non-null) view-kind id. */
- content: Snippet<[string]>;
- /** Optional seed of panel kinds; defaults to one panel of the first kind. */
- initial?: readonly (string | null)[];
- /** Called whenever the panel layout changes (add/remove/select). */
- onChange?: (kinds: readonly (string | null)[]) => void;
- } = $props();
+ let {
+ kinds,
+ content,
+ initial,
+ onChange,
+ }: {
+ /** The view kinds offered in every panel's dropdown. */
+ kinds: readonly ViewKind[];
+ /** Renders a panel body for the given (non-null) view-kind id. */
+ content: Snippet<[string]>;
+ /** Optional seed of panel kinds; defaults to one panel of the first kind. */
+ initial?: readonly (string | null)[];
+ /** Called whenever the panel layout changes (add/remove/select). */
+ onChange?: (kinds: readonly (string | null)[]) => void;
+ } = $props();
- // Local UI composition state, owned by this unit and folded through the pure
- // reducer — never reached from elsewhere (no ambient store). Seeded ONCE from
- // the props (untrack makes that one-time read explicit, not reactive).
- let state = $state<PanelsState>(
- untrack(() => initialPanels(initial ?? [kinds[0]?.id ?? null])),
- );
+ // Local UI composition state, owned by this unit and folded through the pure
+ // reducer — never reached from elsewhere (no ambient store). Seeded ONCE from
+ // the props (untrack makes that one-time read explicit, not reactive).
+ let state = $state<PanelsState>(untrack(() => initialPanels(initial ?? [kinds[0]?.id ?? null])));
- function notify(): void {
- onChange?.(state.panels.map((p) => p.kind));
- }
+ function notify(): void {
+ onChange?.(state.panels.map((p) => p.kind));
+ }
</script>
<div class="flex min-h-0 flex-col gap-2">
- {#each state.panels as panel, idx (panel.id)}
- <div class="flex flex-col rounded-lg bg-base-200 p-3">
- <div class="flex items-center gap-1">
- <select
- class="select select-bordered select-sm flex-1"
- aria-label="Select a view"
- value={panel.kind ?? ""}
- onchange={(e) => {
- const v = e.currentTarget.value;
- state = selectKind(state, panel.id, v === "" ? null : v);
- notify();
- }}
- >
- <option value="" disabled>Select a view</option>
- {#each kinds as kind (kind.id)}
- <option value={kind.id}>{kind.label}</option>
- {/each}
- </select>
- {#if idx > 0}
- <button
- type="button"
- class="btn btn-square btn-ghost btn-sm shrink-0"
- aria-label="Remove view"
- onclick={() => {
- state = removePanel(state, panel.id);
- notify();
- }}
- >
- ✕
- </button>
- {/if}
- </div>
+ {#each state.panels as panel, idx (panel.id)}
+ <div class="flex flex-col rounded-lg bg-base-200 p-3">
+ <div class="flex items-center gap-1">
+ <select
+ class="select select-bordered select-sm flex-1"
+ aria-label="Select a view"
+ value={panel.kind ?? ""}
+ onchange={(e) => {
+ const v = e.currentTarget.value;
+ state = selectKind(state, panel.id, v === "" ? null : v);
+ notify();
+ }}
+ >
+ <option value="" disabled>Select a view</option>
+ {#each kinds as kind (kind.id)}
+ <option value={kind.id}>{kind.label}</option>
+ {/each}
+ </select>
+ {#if idx > 0}
+ <button
+ type="button"
+ class="btn btn-square btn-ghost btn-sm shrink-0"
+ aria-label="Remove view"
+ onclick={() => {
+ state = removePanel(state, panel.id);
+ notify();
+ }}
+ >
+ ✕
+ </button>
+ {/if}
+ </div>
- {#if panel.kind !== null}
- <div class="mt-2">
- {@render content(panel.kind)}
- </div>
- {/if}
- </div>
- {/each}
+ {#if panel.kind !== null}
+ <div class="mt-2">
+ {@render content(panel.kind)}
+ </div>
+ {/if}
+ </div>
+ {/each}
- <button
- type="button"
- class="btn w-full border-none bg-base-200 text-lg hover:bg-base-300"
- aria-label="Add view"
- onclick={() => {
- state = addPanel(state);
- notify();
- }}
- >
- +
- </button>
+ <button
+ type="button"
+ class="btn w-full border-none bg-base-200 text-lg hover:bg-base-300"
+ aria-label="Add view"
+ onclick={() => {
+ state = addPanel(state);
+ notify();
+ }}
+ >
+ +
+ </button>
</div>