nuance-ui 0.1.6 → 0.1.8
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.
- package/dist/module.json +1 -1
- package/dist/module.mjs +1 -20
- package/dist/runtime/components/box.d.vue.ts +1 -1
- package/dist/runtime/components/box.vue +10 -2
- package/dist/runtime/components/box.vue.d.ts +1 -1
- package/dist/runtime/components/button/button.vue +3 -3
- package/dist/runtime/components/checkbox/checkbox-card.d.vue.ts +1 -1
- package/dist/runtime/components/checkbox/checkbox-card.vue.d.ts +1 -1
- package/dist/runtime/components/index.d.ts +2 -0
- package/dist/runtime/components/index.js +2 -0
- package/dist/runtime/components/input/email-input.d.vue.ts +22 -0
- package/dist/runtime/components/input/email-input.vue +41 -0
- package/dist/runtime/components/input/email-input.vue.d.ts +22 -0
- package/dist/runtime/components/input/input.d.vue.ts +4 -4
- package/dist/runtime/components/input/input.vue +5 -7
- package/dist/runtime/components/input/input.vue.d.ts +4 -4
- package/dist/runtime/components/input/number-input.d.vue.ts +7 -1
- package/dist/runtime/components/input/number-input.vue +10 -0
- package/dist/runtime/components/input/number-input.vue.d.ts +7 -1
- package/dist/runtime/components/input/password-input.d.vue.ts +19 -1
- package/dist/runtime/components/input/password-input.vue +20 -4
- package/dist/runtime/components/input/password-input.vue.d.ts +19 -1
- package/dist/runtime/components/input/text-input.d.vue.ts +4 -4
- package/dist/runtime/components/input/text-input.vue +9 -3
- package/dist/runtime/components/input/text-input.vue.d.ts +4 -4
- package/dist/runtime/components/input/ui/input-base.vue +2 -2
- package/dist/runtime/components/input/ui/input-wrapper.vue +1 -1
- package/dist/runtime/components/link/lib.d.ts +2 -2
- package/dist/runtime/components/roving-focus/_lib/context.d.ts +51 -0
- package/dist/runtime/components/roving-focus/_lib/context.js +90 -0
- package/dist/runtime/components/roving-focus/index.d.ts +1 -0
- package/dist/runtime/components/roving-focus/index.js +1 -0
- package/dist/runtime/components/roving-focus/roving-focus-item.d.vue.ts +13 -0
- package/dist/runtime/components/roving-focus/roving-focus-item.vue +32 -0
- package/dist/runtime/components/roving-focus/roving-focus-item.vue.d.ts +13 -0
- package/dist/runtime/components/roving-focus/roving-focus.d.vue.ts +21 -0
- package/dist/runtime/components/roving-focus/roving-focus.vue +23 -0
- package/dist/runtime/components/roving-focus/roving-focus.vue.d.ts +21 -0
- package/dist/runtime/components/select/select.d.vue.ts +2 -2
- package/dist/runtime/components/select/select.vue.d.ts +2 -2
- package/dist/runtime/components/tabs/tabs-tab.d.vue.ts +1 -1
- package/dist/runtime/components/tabs/tabs-tab.vue.d.ts +1 -1
- package/dist/runtime/components/tree/_ui/tree-item.d.vue.ts +0 -0
- package/dist/runtime/components/tree/_ui/tree-item.vue +171 -0
- package/dist/runtime/components/tree/_ui/tree-item.vue.d.ts +0 -0
- package/dist/runtime/components/tree/_ui/tree-root.d.vue.ts +33 -0
- package/dist/runtime/components/tree/_ui/tree-root.vue +77 -0
- package/dist/runtime/components/tree/_ui/tree-root.vue.d.ts +33 -0
- package/dist/runtime/components/tree/index.d.ts +1 -0
- package/dist/runtime/components/tree/index.js +1 -0
- package/dist/runtime/components/tree/lib/context.d.ts +24 -0
- package/dist/runtime/components/tree/lib/context.js +102 -0
- package/dist/runtime/components/tree/lib/get-default.d.ts +7 -0
- package/dist/runtime/components/tree/lib/get-default.js +10 -0
- package/dist/runtime/components/tree/model.d.ts +33 -0
- package/dist/runtime/components/tree/model.js +0 -0
- package/dist/runtime/components/tree/tree.d.vue.ts +23 -0
- package/dist/runtime/components/tree/tree.vue +36 -0
- package/dist/runtime/components/tree/tree.vue.d.ts +23 -0
- package/dist/runtime/composals/index.d.ts +0 -1
- package/dist/runtime/composals/index.js +0 -1
- package/dist/runtime/utils/get-mod.d.ts +2 -0
- package/dist/runtime/{composals/use-mod.js → utils/get-mod.js} +2 -13
- package/dist/runtime/utils/index.d.ts +2 -0
- package/dist/runtime/utils/index.js +2 -0
- package/dist/runtime/utils/tree.d.ts +88 -0
- package/dist/runtime/utils/tree.js +85 -0
- package/package.json +1 -1
- package/dist/runtime/composals/use-mod.d.ts +0 -2
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { createStrictInjection } from "@nui/helpers";
|
|
2
|
+
import { getBranchChildren, getTreeItemsBetween } from "@nui/utils";
|
|
3
|
+
const injectionKey = Symbol("nui-tree");
|
|
4
|
+
const [useProvide, useState] = createStrictInjection(({
|
|
5
|
+
tree,
|
|
6
|
+
active,
|
|
7
|
+
selected,
|
|
8
|
+
expanded,
|
|
9
|
+
iconResolver
|
|
10
|
+
}) => {
|
|
11
|
+
const setActive = (value) => active.value = value;
|
|
12
|
+
function on(type, value, mode) {
|
|
13
|
+
if (type === "select") {
|
|
14
|
+
setActive(value);
|
|
15
|
+
switch (mode) {
|
|
16
|
+
case "single":
|
|
17
|
+
return selected.value = [value];
|
|
18
|
+
case "multiple": {
|
|
19
|
+
const exist = selected.value.includes(value);
|
|
20
|
+
if (!exist)
|
|
21
|
+
selected.value = [...selected.value, value];
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
case "range": {
|
|
25
|
+
if (!active.value || active.value === value)
|
|
26
|
+
return;
|
|
27
|
+
const range = getTreeItemsBetween(tree.value, active.value, value);
|
|
28
|
+
return selected.value = [.../* @__PURE__ */ new Set([...selected.value, ...range])];
|
|
29
|
+
}
|
|
30
|
+
default:
|
|
31
|
+
return selected.value = [value];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (type === "expand") {
|
|
35
|
+
const exist = expanded.value.includes(value);
|
|
36
|
+
if (!exist)
|
|
37
|
+
expanded.value = [...expanded.value, value];
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
return console.warn(`Unknown target type in NuiTree: ${type}`);
|
|
41
|
+
}
|
|
42
|
+
const off = (type, value) => {
|
|
43
|
+
switch (type) {
|
|
44
|
+
case "select": {
|
|
45
|
+
selected.value = selected.value.filter((i) => i !== value);
|
|
46
|
+
if (active.value === value)
|
|
47
|
+
active.value = selected.value[0] ?? null;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
case "expand": {
|
|
51
|
+
const children = getBranchChildren(tree.value, value);
|
|
52
|
+
return expanded.value = expanded.value.filter((v) => !children.includes(v)).filter((v) => v !== value);
|
|
53
|
+
}
|
|
54
|
+
default:
|
|
55
|
+
return console.warn(`Unknown target type in NuiTree: ${type}`);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const toggle = (type, value, mode = "single") => {
|
|
59
|
+
switch (type) {
|
|
60
|
+
case "select": {
|
|
61
|
+
const isSelected = selected.value.includes(value);
|
|
62
|
+
if (mode === "single")
|
|
63
|
+
return on(type, value, mode);
|
|
64
|
+
if (mode === "multiple") {
|
|
65
|
+
if (isSelected)
|
|
66
|
+
return off(type, value);
|
|
67
|
+
return on(type, value, mode);
|
|
68
|
+
}
|
|
69
|
+
if (mode === "range")
|
|
70
|
+
return on(type, value, mode);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
case "expand": {
|
|
74
|
+
const isExpanded = expanded.value.includes(value);
|
|
75
|
+
if (isExpanded)
|
|
76
|
+
return off(type, value);
|
|
77
|
+
return on(type, value);
|
|
78
|
+
}
|
|
79
|
+
default:
|
|
80
|
+
console.warn(`Unknown target type in NuiTree: ${type}`);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
return {
|
|
84
|
+
active,
|
|
85
|
+
selected,
|
|
86
|
+
expanded,
|
|
87
|
+
iconResolver,
|
|
88
|
+
toggle,
|
|
89
|
+
on,
|
|
90
|
+
off,
|
|
91
|
+
setActive
|
|
92
|
+
};
|
|
93
|
+
}, {
|
|
94
|
+
injectionKey,
|
|
95
|
+
name: "TreeState"
|
|
96
|
+
});
|
|
97
|
+
export function useProvideTreeState(ctx) {
|
|
98
|
+
return useProvide(ctx);
|
|
99
|
+
}
|
|
100
|
+
export function useTreeState() {
|
|
101
|
+
return useState();
|
|
102
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { TreeItem } from '../model.js';
|
|
2
|
+
/**
|
|
3
|
+
* Получает все элементы дерева с expanded: true
|
|
4
|
+
* @param items - массив элементов дерева
|
|
5
|
+
* @returns массив value элементов с expanded: true
|
|
6
|
+
*/
|
|
7
|
+
export declare function getExpandedItems<T extends string = string>(items: TreeItem<T>[]): T[];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function getExpandedItems(items) {
|
|
2
|
+
const result = [];
|
|
3
|
+
for (const item of items) {
|
|
4
|
+
if (item.expanded === true)
|
|
5
|
+
result.push(item.value);
|
|
6
|
+
if (item.children && item.children.length > 0)
|
|
7
|
+
result.push(...getExpandedItems(item.children));
|
|
8
|
+
}
|
|
9
|
+
return result;
|
|
10
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { NuanceColor } from '@nui/types';
|
|
2
|
+
export interface TreeModels<T extends string = string> {
|
|
3
|
+
active: T | null;
|
|
4
|
+
tree: TreeItem<T>[];
|
|
5
|
+
selected: T[];
|
|
6
|
+
expanded: T[];
|
|
7
|
+
}
|
|
8
|
+
export interface TreeItem<T extends string = string> {
|
|
9
|
+
/** @IconifyIcon */
|
|
10
|
+
icon?: string;
|
|
11
|
+
/** Value given to this item */
|
|
12
|
+
value: T;
|
|
13
|
+
/** Item label */
|
|
14
|
+
label?: string;
|
|
15
|
+
/** @IconifyIcon */
|
|
16
|
+
trailingIcon?: string;
|
|
17
|
+
/** Item default expanded */
|
|
18
|
+
expanded?: boolean;
|
|
19
|
+
/** Item disabled state */
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
/** Slot name */
|
|
22
|
+
slot?: string;
|
|
23
|
+
/** Item children */
|
|
24
|
+
children?: TreeItem<T>[];
|
|
25
|
+
/** Fires when item is selected */
|
|
26
|
+
onSelect?: (item: TreeItem<T>) => void;
|
|
27
|
+
/** Fires when item is toggled */
|
|
28
|
+
onToggle?: (item: TreeItem<T>) => void;
|
|
29
|
+
}
|
|
30
|
+
export type TreeIconResolver<T extends string = string> = (item: TreeItem<T>) => {
|
|
31
|
+
icon: string;
|
|
32
|
+
color?: NuanceColor;
|
|
33
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { TreeRootProps } from './_ui/tree-root.vue.js';
|
|
2
|
+
import type { TreeModels } from './model.js';
|
|
3
|
+
export interface TreeProps<T extends string = string> extends TreeRootProps<T> {
|
|
4
|
+
}
|
|
5
|
+
declare const __VLS_export: <T extends string = string>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
6
|
+
props: __VLS_PrettifyLocal<(TreeProps<T> & {
|
|
7
|
+
tree: TreeModels<T>["tree"];
|
|
8
|
+
active?: TreeModels<T>["active"];
|
|
9
|
+
selected?: TreeModels<T>["selected"];
|
|
10
|
+
expanded?: TreeModels<T>["expanded"];
|
|
11
|
+
}) & __VLS_EmitsToProps<__VLS_NormalizeEmits<((evt: "update:tree", value: import("./model.js").TreeItem<T>[]) => void) & ((evt: "update:active", value: T | null) => void) & ((evt: "update:selected", value: T[]) => void) & ((evt: "update:expanded", value: T[]) => void)>>> & import("vue").PublicProps;
|
|
12
|
+
expose: (exposed: {}) => void;
|
|
13
|
+
attrs: any;
|
|
14
|
+
slots: {};
|
|
15
|
+
emit: ((evt: "update:tree", value: import("./model.js").TreeItem<T>[]) => void) & ((evt: "update:active", value: T | null) => void) & ((evt: "update:selected", value: T[]) => void) & ((evt: "update:expanded", value: T[]) => void);
|
|
16
|
+
}>) => import("vue").VNode & {
|
|
17
|
+
__ctx?: Awaited<typeof __VLS_setup>;
|
|
18
|
+
};
|
|
19
|
+
declare const _default: typeof __VLS_export;
|
|
20
|
+
export default _default;
|
|
21
|
+
type __VLS_PrettifyLocal<T> = {
|
|
22
|
+
[K in keyof T as K]: T[K];
|
|
23
|
+
} & {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { unref } from "vue";
|
|
3
|
+
import UTreeItem from "./_ui/tree-item.vue";
|
|
4
|
+
import TreeRoot from "./_ui/tree-root.vue";
|
|
5
|
+
import { getExpandedItems } from "./lib/get-default";
|
|
6
|
+
const props = defineProps({
|
|
7
|
+
loop: { type: Boolean, required: false },
|
|
8
|
+
orientation: { type: String, required: false },
|
|
9
|
+
attr: { type: String, required: false },
|
|
10
|
+
size: { type: String, required: false },
|
|
11
|
+
iconResolver: { type: Function, required: false },
|
|
12
|
+
removable: { type: Boolean, required: false }
|
|
13
|
+
});
|
|
14
|
+
const tree = defineModel("tree", { type: Array, ...{ required: true } });
|
|
15
|
+
const active = defineModel("active", { type: null, ...{ default: null } });
|
|
16
|
+
const selected = defineModel("selected", { type: Array, ...{ default: [] } });
|
|
17
|
+
const expanded = defineModel("expanded", { type: Array, ...{ default: [] } });
|
|
18
|
+
expanded.value = getExpandedItems(tree.value);
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<template>
|
|
22
|
+
<TreeRoot
|
|
23
|
+
v-bind='props'
|
|
24
|
+
v-model:tree='tree'
|
|
25
|
+
v-model:active='active'
|
|
26
|
+
v-model:selected='selected'
|
|
27
|
+
v-model:expanded='expanded'
|
|
28
|
+
>
|
|
29
|
+
<UTreeItem
|
|
30
|
+
v-for='item in unref(tree)'
|
|
31
|
+
:key='item.value'
|
|
32
|
+
:item
|
|
33
|
+
:level='1'
|
|
34
|
+
/>
|
|
35
|
+
</TreeRoot>
|
|
36
|
+
</template>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { TreeRootProps } from './_ui/tree-root.vue.js';
|
|
2
|
+
import type { TreeModels } from './model.js';
|
|
3
|
+
export interface TreeProps<T extends string = string> extends TreeRootProps<T> {
|
|
4
|
+
}
|
|
5
|
+
declare const __VLS_export: <T extends string = string>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
6
|
+
props: __VLS_PrettifyLocal<(TreeProps<T> & {
|
|
7
|
+
tree: TreeModels<T>["tree"];
|
|
8
|
+
active?: TreeModels<T>["active"];
|
|
9
|
+
selected?: TreeModels<T>["selected"];
|
|
10
|
+
expanded?: TreeModels<T>["expanded"];
|
|
11
|
+
}) & __VLS_EmitsToProps<__VLS_NormalizeEmits<((evt: "update:tree", value: import("./model.js").TreeItem<T>[]) => void) & ((evt: "update:active", value: T | null) => void) & ((evt: "update:selected", value: T[]) => void) & ((evt: "update:expanded", value: T[]) => void)>>> & import("vue").PublicProps;
|
|
12
|
+
expose: (exposed: {}) => void;
|
|
13
|
+
attrs: any;
|
|
14
|
+
slots: {};
|
|
15
|
+
emit: ((evt: "update:tree", value: import("./model.js").TreeItem<T>[]) => void) & ((evt: "update:active", value: T | null) => void) & ((evt: "update:selected", value: T[]) => void) & ((evt: "update:expanded", value: T[]) => void);
|
|
16
|
+
}>) => import("vue").VNode & {
|
|
17
|
+
__ctx?: Awaited<typeof __VLS_setup>;
|
|
18
|
+
};
|
|
19
|
+
declare const _default: typeof __VLS_export;
|
|
20
|
+
export default _default;
|
|
21
|
+
type __VLS_PrettifyLocal<T> = {
|
|
22
|
+
[K in keyof T as K]: T[K];
|
|
23
|
+
} & {};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { isFalsy } from "
|
|
2
|
-
import { computed } from "vue";
|
|
1
|
+
import { isFalsy } from "./boolean/is-falsy.js";
|
|
3
2
|
const transformKey = (key) => key.startsWith("data-") ? key : `data-${key}`;
|
|
4
|
-
function getMod(props) {
|
|
3
|
+
export function getMod(props) {
|
|
5
4
|
if (Array.isArray(props)) {
|
|
6
5
|
return props.filter((i) => !isFalsy(i)).reduce((acc, item) => ({ ...acc, ...getMod(item) }), {});
|
|
7
6
|
}
|
|
@@ -13,13 +12,3 @@ function getMod(props) {
|
|
|
13
12
|
return acc;
|
|
14
13
|
}, {});
|
|
15
14
|
}
|
|
16
|
-
export function useMod(mod) {
|
|
17
|
-
return computed(() => {
|
|
18
|
-
if (!mod)
|
|
19
|
-
return null;
|
|
20
|
-
if (Array.isArray(mod)) {
|
|
21
|
-
return mod.filter((i) => !isFalsy(i)).reduce((acc, value) => ({ ...acc, ...getMod(value) }), {});
|
|
22
|
-
}
|
|
23
|
-
return getMod(mod);
|
|
24
|
-
});
|
|
25
|
-
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export { isFalsy } from './boolean/is-falsy.js';
|
|
2
2
|
export { getGradient } from './color-functions/get-gradient.js';
|
|
3
3
|
export { em, rem } from './converters/rem.js';
|
|
4
|
+
export * from './get-mod.js';
|
|
4
5
|
export { getSafeId } from './get-safe-id.js';
|
|
5
6
|
export * from './get-size/get-size.js';
|
|
6
7
|
export { getThemeColor, parseThemeColor } from './parse-theme-color/parse-theme-color.js';
|
|
8
|
+
export * from './tree.js';
|
|
7
9
|
export { createVariantColorResolver } from './vars-resolver/create-variant-color-resolver.js';
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export { isFalsy } from "./boolean/is-falsy.js";
|
|
2
2
|
export { getGradient } from "./color-functions/get-gradient.js";
|
|
3
3
|
export { em, rem } from "./converters/rem.js";
|
|
4
|
+
export * from "./get-mod.js";
|
|
4
5
|
export { getSafeId } from "./get-safe-id.js";
|
|
5
6
|
export * from "./get-size/get-size.js";
|
|
6
7
|
export { getThemeColor, parseThemeColor } from "./parse-theme-color/parse-theme-color.js";
|
|
8
|
+
export * from "./tree.js";
|
|
7
9
|
export { createVariantColorResolver } from "./vars-resolver/create-variant-color-resolver.js";
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/** Represents a tree node with a value and optional children. */
|
|
2
|
+
export interface TreeNode<T extends string = string> {
|
|
3
|
+
/** The value stored in this tree node */
|
|
4
|
+
value: string;
|
|
5
|
+
/** Optional array of child nodes */
|
|
6
|
+
children?: TreeNode<T>[];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Searches for a tree item by its value using depth-first search.
|
|
10
|
+
* @template T - The type of node values
|
|
11
|
+
* @param {TreeNode<T>[]} items - The array of tree nodes to search in
|
|
12
|
+
* @param {T} value - The value to search for
|
|
13
|
+
* @returns {TreeNode<T> | null} The found node or null if not found
|
|
14
|
+
* @example
|
|
15
|
+
* const tree = [{ value: 'folder', children: [{ value: 'file' }] }]
|
|
16
|
+
* findTreeItem(tree, 'file') // { value: 'file' }
|
|
17
|
+
* findTreeItem(tree, 'missing') // null
|
|
18
|
+
*/
|
|
19
|
+
export declare function findTreeItem<T extends string = string>(items: TreeNode<T>[], value: T): TreeNode<T> | null;
|
|
20
|
+
/**
|
|
21
|
+
* Flattens a tree structure into a single-level array in depth-first order.
|
|
22
|
+
* @template T - The type of node values
|
|
23
|
+
* @param {TreeNode<T>[]} tree - The tree to flatten
|
|
24
|
+
* @returns {TreeNode<T>[]} Flattened array containing all nodes
|
|
25
|
+
* @example
|
|
26
|
+
* const tree = [
|
|
27
|
+
* { value: 'root', children: [
|
|
28
|
+
* { value: 'child1' },
|
|
29
|
+
* { value: 'child2' }
|
|
30
|
+
* ]}
|
|
31
|
+
* ]
|
|
32
|
+
* flatTree(tree) // [{ value: 'root', ... }, { value: 'child1' }, { value: 'child2' }]
|
|
33
|
+
*/
|
|
34
|
+
export declare function flatTree<T extends string = string>(tree: TreeNode<T>[]): TreeNode<T>[];
|
|
35
|
+
/**
|
|
36
|
+
* Gets all descendant values of a specific node in the tree.
|
|
37
|
+
*
|
|
38
|
+
* @template T - The type of node values
|
|
39
|
+
* @param {TreeNode<T>[]} tree - The tree to search in
|
|
40
|
+
* @param {T} value - The value of the parent node
|
|
41
|
+
* @returns {T[]} Array of all descendant values (empty if node not found or has no children)
|
|
42
|
+
* @example
|
|
43
|
+
* const tree = [
|
|
44
|
+
* { value: 'folder', children: [
|
|
45
|
+
* { value: 'file1' },
|
|
46
|
+
* { value: 'subfolder', children: [{ value: 'file2' }] }
|
|
47
|
+
* ]}
|
|
48
|
+
* ]
|
|
49
|
+
* getBranchChildren(tree, 'folder') // ['file1', 'subfolder', 'file2']
|
|
50
|
+
* getBranchChildren(tree, 'file1') // []
|
|
51
|
+
*/
|
|
52
|
+
export declare function getBranchChildren<T extends string = string>(tree: TreeNode<T>[], value: T): T[];
|
|
53
|
+
/**
|
|
54
|
+
* Gets all node values between two specified values (inclusive) in tree traversal order.
|
|
55
|
+
* If start comes after end in traversal order, the range is automatically reversed.
|
|
56
|
+
* Uses a single-pass algorithm that flattens and searches simultaneously.
|
|
57
|
+
* @template T - The type of node values
|
|
58
|
+
* @param {TreeNode<T>[]} tree - The tree to search in
|
|
59
|
+
* @param {T} start - The starting value
|
|
60
|
+
* @param {T} end - The ending value
|
|
61
|
+
* @returns {T[]} Array of values between start and end (inclusive), empty if either value not found
|
|
62
|
+
* @example
|
|
63
|
+
* const tree = [
|
|
64
|
+
* { value: 'a', children: [{ value: 'b' }, { value: 'c' }] },
|
|
65
|
+
* { value: 'd' }
|
|
66
|
+
* ]
|
|
67
|
+
* getItemsBetween(tree, 'b', 'd') // ['b', 'c', 'd']
|
|
68
|
+
* getItemsBetween(tree, 'd', 'b') // ['b', 'c', 'd'] (auto-reversed)
|
|
69
|
+
* getItemsBetween(tree, 'a', 'missing') // []
|
|
70
|
+
*/
|
|
71
|
+
export declare function getTreeItemsBetween<T extends string = string>(tree: TreeNode<T>[], start: T, end: T): T[];
|
|
72
|
+
/**
|
|
73
|
+
* Removes nodes with specified values from the tree, including their subtrees.
|
|
74
|
+
* Returns a new tree without modifying the original.
|
|
75
|
+
*
|
|
76
|
+
* @template T - The type of node values
|
|
77
|
+
* @param {TreeNode<T>[]} tree - The original tree (array of root nodes)
|
|
78
|
+
* @param {T[]} valuesToRemove - Array of values to remove
|
|
79
|
+
* @returns {TreeNode<T>[]} New tree with specified nodes and their subtrees removed
|
|
80
|
+
* @example
|
|
81
|
+
* const tree = [
|
|
82
|
+
* { value: 'a', children: [{ value: 'b' }, { value: 'c' }] },
|
|
83
|
+
* { value: 'd' }
|
|
84
|
+
* ]
|
|
85
|
+
* removeTreeNodes(tree, ['b', 'd']) // [{ value: 'a', children: [{ value: 'c' }] }]
|
|
86
|
+
* removeTreeNodes(tree, ['a']) // [{ value: 'd' }]
|
|
87
|
+
*/
|
|
88
|
+
export declare function removeTreeNodes<Node extends TreeNode = TreeNode, Value extends string = string>(tree: Node[], valuesToRemove: Value[]): Node[];
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
function traverse(nodes) {
|
|
2
|
+
const result = [];
|
|
3
|
+
for (const node of nodes) {
|
|
4
|
+
result.push(node);
|
|
5
|
+
if (node.children?.length)
|
|
6
|
+
result.push(...traverse(node.children));
|
|
7
|
+
}
|
|
8
|
+
return result;
|
|
9
|
+
}
|
|
10
|
+
export function findTreeItem(items, value) {
|
|
11
|
+
for (const item of items) {
|
|
12
|
+
if (item.value === value)
|
|
13
|
+
return item;
|
|
14
|
+
if (item.children?.length) {
|
|
15
|
+
const found = findTreeItem(item.children, value);
|
|
16
|
+
if (found)
|
|
17
|
+
return found;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
export function flatTree(tree) {
|
|
23
|
+
return traverse(tree);
|
|
24
|
+
}
|
|
25
|
+
export function getBranchChildren(tree, value) {
|
|
26
|
+
const children = [];
|
|
27
|
+
function findAndExtract(nodes) {
|
|
28
|
+
for (const node of nodes) {
|
|
29
|
+
if (node.value === value) {
|
|
30
|
+
if (node.children?.length) {
|
|
31
|
+
const extracted = traverse(node.children);
|
|
32
|
+
children.push(...extracted.map((i) => i.value));
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
if (node.children?.length && findAndExtract(node.children))
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
findAndExtract(tree);
|
|
42
|
+
return children;
|
|
43
|
+
}
|
|
44
|
+
export function getTreeItemsBetween(tree, start, end) {
|
|
45
|
+
const result = [];
|
|
46
|
+
let startIdx = -1;
|
|
47
|
+
let endIdx = -1;
|
|
48
|
+
let currentIdx = 0;
|
|
49
|
+
function traverseAndFind(nodes) {
|
|
50
|
+
for (const node of nodes) {
|
|
51
|
+
if (node.value === start)
|
|
52
|
+
startIdx = currentIdx;
|
|
53
|
+
if (node.value === end)
|
|
54
|
+
endIdx = currentIdx;
|
|
55
|
+
result.push(node.value);
|
|
56
|
+
currentIdx++;
|
|
57
|
+
if (node.children?.length)
|
|
58
|
+
traverseAndFind(node.children);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
traverseAndFind(tree);
|
|
62
|
+
if (startIdx === -1 || endIdx === -1)
|
|
63
|
+
return [];
|
|
64
|
+
const [from, to] = startIdx < endIdx ? [startIdx, endIdx] : [endIdx, startIdx];
|
|
65
|
+
return result.slice(from, to + 1);
|
|
66
|
+
}
|
|
67
|
+
export function removeTreeNodes(tree, valuesToRemove) {
|
|
68
|
+
const removeSet = new Set(valuesToRemove);
|
|
69
|
+
function recurse(nodes) {
|
|
70
|
+
const result = [];
|
|
71
|
+
for (const node of nodes) {
|
|
72
|
+
if (removeSet.has(node.value))
|
|
73
|
+
continue;
|
|
74
|
+
const newNode = { ...node };
|
|
75
|
+
if (node.children?.length) {
|
|
76
|
+
const filteredChildren = recurse(node.children);
|
|
77
|
+
if (filteredChildren.length > 0)
|
|
78
|
+
newNode.children = filteredChildren;
|
|
79
|
+
}
|
|
80
|
+
result.push(newNode);
|
|
81
|
+
}
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
return recurse(tree);
|
|
85
|
+
}
|
package/package.json
CHANGED