Skip to content

Commit

Permalink
Map propertion when computing tree and then access properties directly
Browse files Browse the repository at this point in the history
  • Loading branch information
ZelvaMan committed May 8, 2024
1 parent 00656ae commit 86e191a
Show file tree
Hide file tree
Showing 15 changed files with 480 additions and 787 deletions.
29 changes: 12 additions & 17 deletions src/lib/Branch.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@
export let validTarget: boolean;
export let insPos: InsertionType;
const getNodeId = (node: Node) => `${treeId}-${helper.path(node)}`;
const getNodeId = (node: Node) => `${treeId}-${node.path}`;
function setExpansion(node: Node, changeTo: boolean) {
dispatch('internal-expand', { node: node, changeTo });
}
function isExpanded(node: Node, depth: number, expandToDepth: number) {
const nodeExpanded = helper.props.expanded(node);
const nodeExpanded = node.expanded;
//if expanded prop is defined it has priority over expand to
if (nodeExpanded === null) {
Expand Down Expand Up @@ -77,7 +77,7 @@
*check if this node is one being hovered over (highlited) and is valid target
*/
function highlighThisNode(node: Node, highlitedNode: Node, validTarget: boolean) {
return validTarget && helper.path(highlitedNode) == helper.path(node);
return validTarget && highlitedNode.path == node.path;
}
/**
* returns true, it should highlight nesting on this node
Expand All @@ -95,9 +95,7 @@
if (!highlitedNode) return false;
return (
canNest &&
highlighThisNode(node, highlitedNode, validTarget) &&
helper.props.nestDisabled(node) !== true
canNest && highlighThisNode(node, highlitedNode, validTarget) && node.nestDisabled !== true
);
}
/**
Expand All @@ -116,9 +114,7 @@
if (!highlitedNode) return false;
return (
!canNest &&
highlighThisNode(node, highlitedNode, validTarget) &&
helper.props.nestDisabled(node) !== true
!canNest && highlighThisNode(node, highlitedNode, validTarget) && node.nestDisabled !== true
);
}
Expand All @@ -130,18 +126,17 @@
class:child-menu={childDepth > 0}
class={childDepth === 0 ? classes.treeClass : ''}
>
{#each helper.getDirectChildren(tree, helper.path(branchRootNode)) as node (getNodeId(node))}
{#each helper.getDirectChildren(tree, branchRootNode?.path ?? null) as node (getNodeId(node))}
{@const nesthighlighed = highlightNesting(node, highlightedNode, validTarget, canNest)}
{@const insertHighlighted = highlightInsert(node, highlightedNode, validTarget, canNest)}
{@const expanded = isExpanded(node, childDepth, expandTo)}
{@const hasChildren = helper.props.hasChildren(node)}
{@const draggable = !readonly && dragAndDrop && helper.props.isDraggable(node)}
{@const hasChildren = node.hasChildren}
{@const draggable = !readonly && dragAndDrop && node.isDraggable}
{@const isCurrentlyDragged =
draggedPath == helper.path(node) ||
(draggedPath && helper.path(node)?.startsWith(draggedPath))}
draggedPath == node.path || (draggedPath && node.path?.startsWith(draggedPath))}

<li
class:is-child={helper.nodePathIsChild(helper.path(node))}
class:is-child={helper.nodePathIsChild(node.path)}
class:has-children={hasChildren}
on:contextmenu|stopPropagation={(e) => {
dispatch('open-ctxmenu', { e: e, node: Node });
Expand Down Expand Up @@ -176,7 +171,7 @@
<i
class="far {expanded ? classes.expandedToggleClass : classes.collapsedToggleClass}"
class:fa-minus-square={expanded}
class:fa-plus-square={!expanded || helper.props.useCallback(node)}
class:fa-plus-square={!expanded || node.useCallback}
/>
</span>
{:else}
Expand All @@ -194,7 +189,7 @@
on:select={({ detail: { node } }) => selectionChanged(node)}
/>
<span class:pointer-cursor={draggable}>
<slot {node} />
<slot node={node.originalNode} />
</span>
</div>

Expand Down
34 changes: 6 additions & 28 deletions src/lib/Checkbox.svelte
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import { createEventDispatcher, hasContext } from 'svelte';
import { SelectionModes, VisualState, type Node } from './types.js';
import type { TreeHelper } from '$lib/index.js';
import { SelectionProvider } from '$lib/providers/selection-provider.js';
import { SelectionProvider, isSelectable } from '$lib/providers/selection-provider.js';
export let checkboxes: SelectionModes;
export let helper: TreeHelper;
Expand All @@ -12,18 +12,6 @@
export let hideDisabledCheckboxes: boolean;
export let readonly = false;
let indeterminate: boolean;
$: {
if (helper.props.visualState(node) == 'indeterminate') {
indeterminate = true;
} else {
indeterminate = false;
}
}
// TODO pass from root
$: selectionProvider = new SelectionProvider(helper, recursive);
const dispatch = createEventDispatcher();
function onSelect(node: Node) {
Expand All @@ -32,23 +20,13 @@
</script>

{#if checkboxes == SelectionModes.perNode || checkboxes == SelectionModes.all}
{#if selectionProvider.isSelectable(node, checkboxes)}
<!-- select node -->
{#if !recursive || (recursive && !helper.props.hasChildren(node))}
{#if isSelectable(node, checkboxes)}
{#if !recursive || (recursive && !node.hasChildren) || !onlyLeafCheckboxes}
<input
type="checkbox"
on:change={() => onSelect(node)}
checked={helper.props.selected(node)}
disabled={readonly}
/>
<!-- select children-->
{:else if !onlyLeafCheckboxes}
<!-- @ts-ingore -->
<input
type="checkbox"
on:click={() => onSelect(node)}
checked={helper.props.visualState(node) === VisualState.selected}
{indeterminate}
checked={node.visualState === VisualState.selected}
indeterminate={node.visualState === VisualState.indeterminate}
disabled={readonly}
/>
{:else}
Expand Down
85 changes: 45 additions & 40 deletions src/lib/TreeView.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<script lang="ts">
<script lang="ts" generics="T">
import ContextMenu from './menu/ContextMenu.svelte';
import { createEventDispatcher, onMount } from 'svelte';
import { defaultClasses, defaultPixelTreshold, defaultPropNames } from './constants.js';
Expand All @@ -14,11 +14,12 @@
type BeforeMovedCallback,
type ExpandedCallback,
VisualState,
type NodeId
type NodeId,
type ProvidedTree,
type FilterFunction
} from '$lib/types.js';
import { TreeHelper } from '$lib/index.js';
import Branch from './Branch.svelte';
import { PropertyHelper } from '$lib/helpers/property-helper.js';
import { SelectionProvider } from '$lib/providers/selection-provider.js';
const dispatch = createEventDispatcher();
Expand All @@ -29,7 +30,7 @@
* Each node should have unique path
* All tree modifications are made by modifying this array, so you need to bind it to parent component
*/
export let tree: Node[];
export let tree: ProvidedTree;
/**
* Node paths of selected nodes
Expand Down Expand Up @@ -106,7 +107,7 @@
* If you want to only search leaf nodes,
* its your responsibility to check if its hasChildren property is false
*/
export let filter: (node: any) => boolean = (_) => true;
export let filter: FilterFunction | null = null;
/**
* Log function that will be called when something happens in tree.
Expand All @@ -129,48 +130,59 @@
// OLD variables, will be removed/changed in future
let draggedPath: string | null = null;
let highlightedNode: Node | null = null;
let dragenterTimestamp: Date | null = null;
let canNestPos = false;
let canNestTime = false;
let canNest: boolean;
let dragTimeout: NodeJS.Timeout;
let canNest: boolean = false;
let validTarget = false;
let insPos: InsertionType;
$: dragAndDrop && console.warn('Drag and drop is not supported in this version');
// ensure tree is never null
$: tree, tree == null || tree == undefined ? (tree = []) : '';
$: propHelper = new PropertyHelper({ ...defaultPropNames, ...props });
$: helper = new TreeHelper(propHelper, {
$: helper = new TreeHelper({
recursive: recursiveSelection,
recalculateNodePath,
checkboxes: selectionMode,
separator
});
$: selectionProvider = new SelectionProvider(helper, recursiveSelection);
$: computedTree = computeTree(helper, selectionProvider, tree, filter, props, expandedIds, value);
$: debugLog('computedTree', computedTree);
export function changeAllExpansion(changeTo: boolean) {
debugLog('chaning expantion of every node to ', changeTo ? 'expanded' : 'collapsed');
expandedIds = computedTree.map((node) => node.id);
}
function computeTree(
helper: TreeHelper,
selectionProvider: SelectionProvider,
userProvidedTree: any[],
filter: FilterFunction | null,
props: Partial<Props>,
expandedIds: NodeId[],
value: NodeId[]
): Tree {
if (!Array.isArray(userProvidedTree) || !Array.isArray(value)) {
console.error('value and tree must be arrays!!');
return [];
}
$: computedTree = helper.computeTree(
tree,
filter,
expandedIds,
value,
selectionProvider.computeVisualStates(tree, value)
);
$: console.log('computedTree', computedTree);
let mappedTree = helper.mapTree(userProvidedTree, filter, { ...defaultPropNames, ...props });
//if insert is disabled => nest right away and never nest if its disabled
$: canNest =
(propHelper.insertDisabled(highlightedNode) || canNestPos || canNestTime) &&
propHelper.nestDisabled(highlightedNode) !== true;
helper.markExpanded(mappedTree, expandedIds);
// TODO here we could save last value and only recompute visual state if value changed
// or use diff to only update affected nodes
selectionProvider.markSelected(mappedTree, value);
return mappedTree;
}
function onExpand(event: CustomEvent<{ node: Node; changeTo: boolean }>) {
const { node, changeTo } = event.detail;
expandedIds = helper.changeExpansion(node, changeTo, expandedIds);
debugLog("changed expansion of node '", helper.path(node), "' to ", changeTo);
debugLog("changed expansion of node '", node.id, "' to ", changeTo);
//trigger callback if it is present and node has useCallback property set to true
if (changeTo) {
Expand All @@ -192,14 +204,14 @@
function handleCallback(node: Node) {
// only call on nodes with children
if (propHelper.hasChildren(node) !== true) {
if (node.hasChildren !== true) {
return;
}
if (loadChildrenAsync == null) {
console.warn(
'loadChildrenAsync is not set, but useCallback is set to true on node with path',
helper.path(node)
node.path
);
return;
}
Expand All @@ -210,27 +222,20 @@
loadChildrenAsync(node);
}
// TODO remove and expose function from package
export function changeAllExpansion(changeTo: boolean) {
debugLog('chaning expantion of every node to ', changeTo ? 'expanded' : 'collapsed');
tree = helper.changeEveryExpansion(tree, changeTo);
}
function onSelectionChanged(event: CustomEvent<{ node: Node }>) {
const { node } = event.detail;
const nodePath = helper.path(node);
const nodePath = node.path;
const changeTo = !selectionProvider.isNodeSelected(node);
const newValue = selectionProvider.setSelection(tree, nodePath, changeTo, value);
const newValue = selectionProvider.setSelection(computedTree, nodePath, changeTo, value);
debugLog(
"changing selection of node '",
nodePath,
"' to ",
!propHelper.selected(node),
changeTo,
' returing value ',
newValue
);
Expand Down
5 changes: 1 addition & 4 deletions src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,12 @@ export const defaultPropNames: Props = {
nodePath: 'nodePath',
nodeId: 'nodePath',
hasChildren: 'hasChildren',
expanded: '__expanded',
selected: '__selected',
useCallback: '__useCallback',
priority: 'priority',
isDraggable: 'isDraggable',
insertDisabled: 'insertDisabled',
nestDisabled: 'nestDisabled',
checkbox: 'checkbox',
visualState: '__visual_state'
checkbox: 'checkbox'
};

export const defaultPixelTreshold = 50;
Expand Down
Loading

0 comments on commit 86e191a

Please sign in to comment.