@warkypublic/svelix 0.2.4 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
<script lang="ts">import { onMount } from "svelte";
|
|
1
|
+
<script lang="ts">import { onMount, untrack } from "svelte";
|
|
2
2
|
import VTreeSearch from "./VTreeSearch.svelte";
|
|
3
3
|
import VTreeVirtualViewport from "./VTreeVirtualViewport.svelte";
|
|
4
4
|
import VTreeContextMenu from "./VTreeContextMenu.svelte";
|
|
5
5
|
import { buildNodeIndex, computeMultiSelectClick, findAncestorIds } from "./VTree.utils.js";
|
|
6
|
-
const { data = [], adapter, idField = "id", labelField = "label", descriptionField = "description", childrenField = "children", hasChildrenField = "hasChildren", height =
|
|
6
|
+
const { data = [], adapter, idField = "id", labelField = "label", descriptionField = "description", childrenField = "children", hasChildrenField = "hasChildren", height = "100%", rowHeight = 36, indent = 18, overscan = 8, searchable = true, searchPlaceholder = "Search tree...", searchValue = "", serverSideSearch = false, autoExpandSearchResults = true, loadOnMount = true, emptyMessage = "Nothing here...", loadingMessage = "Loading...", leftSection, rightSection, contextMenuItems, onselect, onclick, ondoubleclick, oncontext, ontoggle, onsearch, onloaderror, selected, onSelectedChange, multiple = false, selectedItems, onSelectedItemsChange, selectionMode = "modifier", autoMeasure = false, iconField, icon, classes, showRowSeparator = true, hoverEffect = true, onsingleclick, singleClickDelay = 250, onlongpress, longPressDelay = 500, longPressMoveTolerance = 10, onrowhover, onrowhoverend } = $props();
|
|
7
7
|
let viewportRef = $state(undefined);
|
|
8
8
|
let focusedIndex = $state(0);
|
|
9
9
|
let selectedId = $state(null);
|
|
10
10
|
let searchQuery = $state("");
|
|
11
11
|
let loadingRoot = $state(false);
|
|
12
|
+
let treeElement = $state(null);
|
|
13
|
+
let searchElement = $state(null);
|
|
14
|
+
let viewportHeight = $state("0px");
|
|
12
15
|
let rootNodes = $state([]);
|
|
13
16
|
let searchNodes = $state([]);
|
|
14
17
|
let childrenByParent = $state({});
|
|
@@ -93,6 +96,18 @@ function nodeHasChildren(node) {
|
|
|
93
96
|
function normalizeRows(rows) {
|
|
94
97
|
return Array.isArray(rows) ? rows : [];
|
|
95
98
|
}
|
|
99
|
+
function toCssSize(value) {
|
|
100
|
+
return typeof value === "number" ? `${value}px` : value;
|
|
101
|
+
}
|
|
102
|
+
function updateViewportHeight() {
|
|
103
|
+
if (!treeElement) return;
|
|
104
|
+
const styles = getComputedStyle(treeElement);
|
|
105
|
+
const verticalPadding = parseFloat(styles.paddingTop) + parseFloat(styles.paddingBottom);
|
|
106
|
+
const rowGap = parseFloat(styles.rowGap) || 0;
|
|
107
|
+
const searchHeight = searchElement?.offsetHeight ?? 0;
|
|
108
|
+
const nextHeight = `${Math.max(0, treeElement.clientHeight - verticalPadding - searchHeight - (searchElement ? rowGap : 0))}px`;
|
|
109
|
+
if (untrack(() => viewportHeight) !== nextHeight) viewportHeight = nextHeight;
|
|
110
|
+
}
|
|
96
111
|
async function loadRoot() {
|
|
97
112
|
if (!adapter) {
|
|
98
113
|
rootNodes = normalizeRows(data ?? []);
|
|
@@ -411,6 +426,14 @@ function handleContextMenuItem(item) {
|
|
|
411
426
|
$effect(() => {
|
|
412
427
|
if (!adapter) rootNodes = normalizeRows(data ?? []);
|
|
413
428
|
});
|
|
429
|
+
$effect(() => {
|
|
430
|
+
if (!treeElement) return;
|
|
431
|
+
const observer = new ResizeObserver(updateViewportHeight);
|
|
432
|
+
observer.observe(treeElement);
|
|
433
|
+
if (searchElement) observer.observe(searchElement);
|
|
434
|
+
updateViewportHeight();
|
|
435
|
+
return () => observer.disconnect();
|
|
436
|
+
});
|
|
414
437
|
$effect(() => {
|
|
415
438
|
if (!adapter || !serverSideSearch) return;
|
|
416
439
|
clearTimeout(lastSearchHandle);
|
|
@@ -438,9 +461,11 @@ onMount(() => {
|
|
|
438
461
|
});
|
|
439
462
|
</script>
|
|
440
463
|
|
|
441
|
-
<div class="card
|
|
464
|
+
<div class="card flex h-full min-h-0 flex-col gap-2 border border-surface-300-700 bg-surface-100-900 p-2" bind:this={treeElement} style={`height: ${toCssSize(height)};`}>
|
|
442
465
|
{#if searchable}
|
|
443
|
-
<
|
|
466
|
+
<div bind:this={searchElement}>
|
|
467
|
+
<VTreeSearch value={searchQuery} placeholder={searchPlaceholder} oninputvalue={handleSearchInput} />
|
|
468
|
+
</div>
|
|
444
469
|
{/if}
|
|
445
470
|
|
|
446
471
|
<VTreeVirtualViewport
|
|
@@ -448,7 +473,7 @@ onMount(() => {
|
|
|
448
473
|
rows={flattenedRows}
|
|
449
474
|
{rowHeight}
|
|
450
475
|
{autoMeasure}
|
|
451
|
-
{
|
|
476
|
+
height={viewportHeight}
|
|
452
477
|
{indent}
|
|
453
478
|
{overscan}
|
|
454
479
|
{focusedIndex}
|
|
@@ -51,7 +51,7 @@ $effect(() => {
|
|
|
51
51
|
});
|
|
52
52
|
</script>
|
|
53
53
|
|
|
54
|
-
<div class="card overflow-auto border border-surface-300-700 bg-surface-50-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500" bind:this={viewportRef} style={`height: ${toCssSize(height)};`} onkeydown={(event) => ontreekeydown?.(event)} oncontextmenu={(event) => onviewportcontext?.(event)} tabindex="0" role="tree" aria-label="Virtual tree">
|
|
54
|
+
<div class="card min-h-0 flex-1 basis-0 overflow-auto border border-surface-300-700 bg-surface-50-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500" bind:this={viewportRef} style={`height: ${toCssSize(height)};`} onkeydown={(event) => ontreekeydown?.(event)} oncontextmenu={(event) => onviewportcontext?.(event)} tabindex="0" role="tree" aria-label="Virtual tree">
|
|
55
55
|
{#if loadingRoot}
|
|
56
56
|
<div class="grid min-h-24 place-items-center px-3 text-sm text-surface-500-400-token">{loadingMessage}</div>
|
|
57
57
|
{:else if rows.length === 0}
|