Merge pull request #122 from agnosticeng/feat/expand-dataset

This commit is contained in:
Yann Amsellem
2025-06-09 15:17:46 +02:00
committed by GitHub
5 changed files with 132 additions and 15 deletions

2
src-tauri/Cargo.lock generated
View File

@@ -19,7 +19,7 @@ checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "agx"
version = "0.3.0"
version = "0.3.1"
dependencies = [
"bindgen",
"serde_json",

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,77 @@
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;
}
.datasets::-webkit-scrollbar {
display: none;
}
.datasets {
-ms-overflow-style: none;
scrollbar-width: none;
}
.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

@@ -2,35 +2,45 @@
import Folder from '$lib/icons/Folder.svelte';
import FolderOpen from '$lib/icons/FolderOpen.svelte';
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;
}
$effect(() =>
onExpand((value) => {
if (node.type === 'dataset' && node.value === value) expanded = true;
else if (node.type === 'group' && findNodeInTree(node.children, value)) expanded = true;
onExpand(async (value) => {
if (node.type === 'dataset' && node.value === value) {
expanded = true;
await tick();
animateExpand(value);
} else if (node.type === 'group' && findNodeInTree(node.children, value)) expanded = true;
})
);
function animateExpand(id: string) {
const element = document.getElementById(id);
if (element) {
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
element.style.backgroundColor = 'hsl(60 40% 34% / 1)';
}
}
</script>
<div class="node" style:opacity={expanded ? 1 : 0.7 + level}>
@@ -53,7 +63,11 @@
{/if}
{#if node.type == 'dataset'}
<span class="name">
<span
class="name"
id={node.value}
ontransitionend={(e) => (e.currentTarget.style.backgroundColor = '')}
>
<Table size={10} />
<span>{node.name}</span>
</span>
@@ -62,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}
@@ -94,6 +108,9 @@
display: flex;
align-items: center;
margin-top: 3px;
transition: background-color linear 0.25s;
scroll-margin-top: 30px;
}
.name span {

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>