feat(datasets): add collapse all button

This commit is contained in:
Yann Amsellem
2025-06-06 15:53:44 +02:00
parent ffd2e2d77e
commit b094207455
4 changed files with 99 additions and 10 deletions

View File

@@ -2,6 +2,8 @@
import type { Table } from '$lib/olap-engine';
import SearchBar from '$lib/components/SearchBar.svelte';
import CollapseAll from '$lib/icons/CollapseAll.svelte';
import { collapseAll, expandAll } from './emitter';
import Tree from './Tree.svelte';
import { buildTree, filter } from './utils';
@@ -14,19 +16,68 @@
let search = $state<string>('');
const filtered = $derived(filter(tables, search));
const tree = $derived(buildTree(filtered));
$effect(() => {
if (search) expandAll();
else collapseAll();
});
</script>
<SearchBar bind:value={search} />
<div>
<div class="datasets">
<div class="toolbar">
<button onclick={() => collapseAll()}><CollapseAll size="12" /></button>
</div>
{#each tree as node}
<Tree {node} expanded={!!search} />
<Tree {node} />
{/each}
</div>
<style>
div {
.datasets {
position: relative;
flex: 1;
overflow-y: scroll;
}
.toolbar {
position: sticky;
top: 0px;
height: 16px;
width: 100%;
display: flex;
align-items: center;
justify-content: end;
background-color: hsl(0deg 0% 5%);
}
.toolbar button {
height: 100%;
aspect-ratio: 1;
background-color: transparent;
border-radius: 0;
display: flex;
justify-content: center;
align-items: center;
border-radius: 4px;
color: hsl(0deg 0% 83%);
&:hover:not(:disabled) {
background-color: hsl(0deg 0% 10%);
}
}
button {
appearance: none;
outline: none;
border: none;
font-size: 10px;
font-weight: 500;
padding: 0;
&:is(:hover, :focus-within):not(:disabled) {
cursor: pointer;
}
}
</style>

View File

@@ -4,23 +4,21 @@
import Table from '$lib/icons/Table.svelte';
import { tick } from 'svelte';
import Columns from './Columns.svelte';
import { onExpand } from './emitter';
import { onCollapseAll, onExpand, onExpandAll } from './emitter';
import Tree from './Tree.svelte';
import { findNodeInTree, type TreeNode } from './utils';
interface Props {
node: TreeNode;
level?: number;
expanded?: boolean;
}
let { node, level = 0, expanded: forceExpanded }: Props = $props();
let { node, level = 0 }: Props = $props();
let expanded = $state(false);
$effect(() => {
if (typeof forceExpanded === 'boolean') expanded = forceExpanded;
});
$effect(() => onCollapseAll(() => (expanded = false)));
$effect(() => onExpandAll(() => (expanded = true)));
function toggleExpanded() {
expanded = !expanded;
@@ -78,7 +76,7 @@
{#if node.type === 'group'}
<div style:display={expanded ? 'contents' : 'none'}>
{#each node.children as child}
<Tree node={child} level={level + 1} expanded={forceExpanded} />
<Tree node={child} level={level + 1} />
{/each}
</div>
{/if}

View File

@@ -3,6 +3,8 @@ import mitt from 'mitt';
type Events = {
expand: Table['name'];
'expand-all': void;
'collapse-all': void;
};
const emitter = mitt<Events>();
@@ -16,3 +18,23 @@ export function onExpand(handler: (tableName: string) => void) {
return () => emitter.off('expand', handler);
}
export function expandAll() {
emitter.emit('expand-all');
}
export function onExpandAll(handler: () => void) {
emitter.on('expand-all', handler);
return () => emitter.off('expand-all', handler);
}
export function collapseAll() {
emitter.emit('collapse-all');
}
export function onCollapseAll(handler: () => void) {
emitter.on('collapse-all', handler);
return () => emitter.off('collapse-all', handler);
}

View File

@@ -0,0 +1,18 @@
<script lang="ts">
import type { SvelteHTMLElements } from 'svelte/elements';
interface Props extends Omit<SvelteHTMLElements['svg'], 'width' | 'height' | 'fill'> {
size?: string | number | null;
}
let { size = 24, color = 'currentColor', ...rest }: Props = $props();
</script>
<svg width={size} height={size} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill={color}>
<path d="M9 9H4v1h5V9z" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M5 3l1-1h7l1 1v7l-1 1h-2v2l-1 1H3l-1-1V6l1-1h2V3zm1 2h4l1 1v4h2V3H6v2zm4 1H3v7h7V6z"
/>
</svg>