nuance-ui 0.1.18 → 0.1.19
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/runtime/components/loader/_loaders/oval-loader.vue +1 -1
- package/dist/runtime/components/loader/loader.d.vue.ts +1 -1
- package/dist/runtime/components/loader/loader.vue +5 -8
- package/dist/runtime/components/loader/loader.vue.d.ts +1 -1
- package/dist/runtime/components/tree/_ui/tree-item.d.vue.ts +2 -14
- package/dist/runtime/components/tree/_ui/tree-item.vue +92 -109
- package/dist/runtime/components/tree/_ui/tree-item.vue.d.ts +2 -14
- package/dist/runtime/components/tree/_ui/tree-root.d.vue.ts +31 -33
- package/dist/runtime/components/tree/_ui/tree-root.vue +11 -8
- package/dist/runtime/components/tree/_ui/tree-root.vue.d.ts +31 -33
- package/dist/runtime/components/tree/lib/context.d.ts +17 -23
- package/dist/runtime/components/tree/lib/context.js +44 -36
- package/dist/runtime/components/tree/lib/item-handlers.d.ts +5 -0
- package/dist/runtime/components/tree/lib/item-handlers.js +54 -0
- package/dist/runtime/components/tree/model.d.ts +27 -21
- package/dist/runtime/components/tree/tree.d.vue.ts +18 -22
- package/dist/runtime/components/tree/tree.vue +13 -8
- package/dist/runtime/components/tree/tree.vue.d.ts +18 -22
- package/dist/runtime/utils/tree.d.ts +6 -6
- package/dist/runtime/utils/tree.js +10 -10
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
</template>
|
|
4
4
|
|
|
5
5
|
<style module>
|
|
6
|
-
.root{display:inline-block}.root,.root:after{height:var(--loader-size);width:var(--loader-size)}.root:after{animation:oval-loader-animation 1.2s linear infinite;border-color:var(--loader-color) var(--loader-color) var(--loader-color) transparent;border-radius:10000px;border-style:solid;border-width:calc(var(--loader-size)/
|
|
6
|
+
.root{display:inline-block}.root,.root:after{height:var(--loader-size);width:var(--loader-size)}.root:after{animation:oval-loader-animation 1.2s linear infinite;border-color:var(--loader-color) var(--loader-color) var(--loader-color) transparent;border-radius:10000px;border-style:solid;border-width:calc(var(--loader-size)/6);content:"";display:block}@keyframes oval-loader-animation{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}
|
|
7
7
|
</style>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { NuanceColor, NuanceSize } from '@nui/types';
|
|
2
2
|
export type LoaderType = 'bars' | 'dots' | 'oval';
|
|
3
3
|
export interface LoaderProps {
|
|
4
|
-
size?: NuanceSize | string;
|
|
4
|
+
size?: NuanceSize | `compact-${NuanceSize}` | string;
|
|
5
5
|
color?: NuanceColor | string;
|
|
6
6
|
type?: LoaderType;
|
|
7
7
|
}
|
|
@@ -5,12 +5,8 @@ import { computed } from "vue";
|
|
|
5
5
|
import BarsLoader from "./_loaders/bars-loader.vue";
|
|
6
6
|
import DotsLoader from "./_loaders/dots-loader.vue";
|
|
7
7
|
import OvalLoader from "./_loaders/oval-loader.vue";
|
|
8
|
-
const {
|
|
9
|
-
size
|
|
10
|
-
color,
|
|
11
|
-
type = "oval"
|
|
12
|
-
} = defineProps({
|
|
13
|
-
size: { type: String, required: false },
|
|
8
|
+
const { size, color, type = "oval" } = defineProps({
|
|
9
|
+
size: { type: null, required: false },
|
|
14
10
|
color: { type: null, required: false },
|
|
15
11
|
type: { type: String, required: false }
|
|
16
12
|
});
|
|
@@ -21,8 +17,9 @@ const loaders = {
|
|
|
21
17
|
};
|
|
22
18
|
const style = computed(() => useStyleResolver((theme) => {
|
|
23
19
|
const _color = parseThemeColor({ color, theme });
|
|
20
|
+
const _size = size?.includes("compact") ? size.replace("compact-", "") : size;
|
|
24
21
|
return {
|
|
25
|
-
"--loader-size": getSize(
|
|
22
|
+
"--loader-size": getSize(_size, "loader-size"),
|
|
26
23
|
"--loader-color": _color.value
|
|
27
24
|
};
|
|
28
25
|
}));
|
|
@@ -33,5 +30,5 @@ const style = computed(() => useStyleResolver((theme) => {
|
|
|
33
30
|
</template>
|
|
34
31
|
|
|
35
32
|
<style module>
|
|
36
|
-
.root{--loader-size-xs:rem(18px);--loader-size-sm:rem(22px);--loader-size-md:rem(36px);--loader-size-lg:rem(44px);--loader-size-xl:rem(58px);--loader-size:var(--loader-size-
|
|
33
|
+
.root{--loader-size-xs:rem(18px);--loader-size-sm:rem(22px);--loader-size-md:rem(36px);--loader-size-lg:rem(44px);--loader-size-xl:rem(58px);--loader-size:var(--loader-size-sm);--loader-color:var(--color-primary-filled)}
|
|
37
34
|
</style>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { NuanceColor, NuanceSize } from '@nui/types';
|
|
2
2
|
export type LoaderType = 'bars' | 'dots' | 'oval';
|
|
3
3
|
export interface LoaderProps {
|
|
4
|
-
size?: NuanceSize | string;
|
|
4
|
+
size?: NuanceSize | `compact-${NuanceSize}` | string;
|
|
5
5
|
color?: NuanceColor | string;
|
|
6
6
|
type?: LoaderType;
|
|
7
7
|
}
|
|
@@ -1,19 +1,7 @@
|
|
|
1
1
|
import type { TreeItem } from '../model.js';
|
|
2
|
-
export interface TreeItemProps
|
|
3
|
-
item: TreeItem<T>;
|
|
2
|
+
export interface TreeItemProps extends TreeItem {
|
|
4
3
|
level: number;
|
|
5
4
|
}
|
|
6
|
-
declare const __VLS_export: <
|
|
7
|
-
props: __VLS_PrettifyLocal<TreeItemProps<T>> & import("vue").PublicProps;
|
|
8
|
-
expose: (exposed: {}) => void;
|
|
9
|
-
attrs: any;
|
|
10
|
-
slots: {};
|
|
11
|
-
emit: {};
|
|
12
|
-
}>) => import("vue").VNode & {
|
|
13
|
-
__ctx?: Awaited<typeof __VLS_setup>;
|
|
14
|
-
};
|
|
5
|
+
declare const __VLS_export: import("vue").DefineComponent<TreeItemProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<TreeItemProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
15
6
|
declare const _default: typeof __VLS_export;
|
|
16
7
|
export default _default;
|
|
17
|
-
type __VLS_PrettifyLocal<T> = {
|
|
18
|
-
[K in keyof T as K]: T[K];
|
|
19
|
-
} & {};
|
|
@@ -1,79 +1,54 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { useTheme } from "@nui/composals";
|
|
3
|
-
import { getThemeColor
|
|
4
|
-
import { computed } from "vue";
|
|
3
|
+
import { getThemeColor } from "@nui/utils";
|
|
4
|
+
import { computed, watch } from "vue";
|
|
5
5
|
import Button from "../../button/button.vue";
|
|
6
|
-
import
|
|
6
|
+
import Loader from "../../loader/loader.vue";
|
|
7
7
|
import RovingFocusItem from "../../roving-focus/roving-focus-item.vue";
|
|
8
|
+
import UTransition from "../../transition/transition.vue";
|
|
8
9
|
import { useTreeState } from "../lib/context";
|
|
9
|
-
|
|
10
|
-
item: { type: Object, required: true },
|
|
11
|
-
level: { type: Number, required: true }
|
|
12
|
-
});
|
|
10
|
+
import { useTreeItemHandlers } from "../lib/item-handlers";
|
|
13
11
|
const {
|
|
12
|
+
type = "file",
|
|
14
13
|
icon = "gravity-ui:folder",
|
|
15
14
|
trailingIcon = "gravity-ui:folder-open",
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
path,
|
|
16
|
+
name,
|
|
17
|
+
level,
|
|
19
18
|
disabled
|
|
20
|
-
} =
|
|
19
|
+
} = defineProps({
|
|
20
|
+
level: { type: Number, required: true },
|
|
21
|
+
icon: { type: String, required: false },
|
|
22
|
+
path: { type: String, required: true },
|
|
23
|
+
name: { type: String, required: false },
|
|
24
|
+
trailingIcon: { type: String, required: false },
|
|
25
|
+
disabled: { type: Boolean, required: false },
|
|
26
|
+
type: { type: String, required: false },
|
|
27
|
+
children: { type: Array, required: false }
|
|
28
|
+
});
|
|
21
29
|
const { value: theme } = useTheme();
|
|
22
|
-
const isFolder = computed(() =>
|
|
30
|
+
const isFolder = computed(() => type === "directory");
|
|
23
31
|
const ctx = useTreeState();
|
|
24
|
-
const selected = computed(() => ctx.selected.value.includes(
|
|
25
|
-
const expanded = computed(() => ctx.expanded.value.includes(
|
|
26
|
-
const active = computed(() => ctx.active.value ===
|
|
27
|
-
const {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
ctx.toggle("select", value, "single");
|
|
36
|
-
if (isFolder.value && !event.ctrlKey && !event.metaKey && !event.shiftKey)
|
|
37
|
-
ctx.toggle("expand", value);
|
|
38
|
-
}
|
|
39
|
-
function handleKeyDown(event) {
|
|
40
|
-
switch (event.key) {
|
|
41
|
-
// Arrow Left - закрыть папку или перейти к родителю
|
|
42
|
-
case "ArrowLeft": {
|
|
43
|
-
event.preventDefault();
|
|
44
|
-
if (isFolder.value && expanded.value)
|
|
45
|
-
return ctx.off("expand", value);
|
|
46
|
-
else
|
|
47
|
-
return focus("prev", event.currentTarget);
|
|
48
|
-
}
|
|
49
|
-
// Arrow Right - открыть папку или перейти к первому ребенку
|
|
50
|
-
case "ArrowRight": {
|
|
51
|
-
event.preventDefault();
|
|
52
|
-
if (isFolder.value && !expanded.value)
|
|
53
|
-
return ctx.on("expand", value);
|
|
54
|
-
else if (expanded.value)
|
|
55
|
-
return focus("next", event.currentTarget);
|
|
56
|
-
break;
|
|
57
|
-
}
|
|
58
|
-
// Enter/Space - выбор элемента
|
|
59
|
-
case "Enter": {
|
|
60
|
-
ctx.on("select", value);
|
|
61
|
-
return ctx.setActive(value);
|
|
62
|
-
}
|
|
63
|
-
case " ": {
|
|
64
|
-
event.preventDefault();
|
|
65
|
-
if (event.shiftKey)
|
|
66
|
-
return ctx.toggle("select", value, "range");
|
|
67
|
-
else if (event.ctrlKey || event.metaKey)
|
|
68
|
-
return ctx.toggle("select", value, "multiple");
|
|
69
|
-
return ctx.toggle("select", value, "single");
|
|
32
|
+
const selected = computed(() => ctx.selected.value.includes(path));
|
|
33
|
+
const expanded = computed(() => ctx.expanded.value.includes(path));
|
|
34
|
+
const active = computed(() => ctx.active.value === path);
|
|
35
|
+
const { data, pending, execute } = ctx.loadBranch(path);
|
|
36
|
+
watch(expanded, (expanded2) => {
|
|
37
|
+
if (expanded2) {
|
|
38
|
+
try {
|
|
39
|
+
execute();
|
|
40
|
+
} catch (e) {
|
|
41
|
+
console.error(e);
|
|
42
|
+
ctx.off("expand", path);
|
|
70
43
|
}
|
|
71
44
|
}
|
|
72
|
-
}
|
|
45
|
+
}, { immediate: true });
|
|
46
|
+
const { icon: fileIcon, color } = ctx.iconResolver(type, name, path, disabled);
|
|
47
|
+
const { handleClick, handleKeyDown } = useTreeItemHandlers(path, isFolder, expanded);
|
|
73
48
|
</script>
|
|
74
49
|
|
|
75
50
|
<template>
|
|
76
|
-
<li :class='$style.
|
|
51
|
+
<li :class='$style.item' role='presentation'>
|
|
77
52
|
<RovingFocusItem>
|
|
78
53
|
<Button
|
|
79
54
|
:size='ctx.size'
|
|
@@ -81,55 +56,80 @@ function handleKeyDown(event) {
|
|
|
81
56
|
:variant='ctx.variant'
|
|
82
57
|
:disabled
|
|
83
58
|
role='treeitem'
|
|
84
|
-
:classes='
|
|
85
|
-
root: $style.button,
|
|
86
|
-
label: $style.label,
|
|
87
|
-
inner: $style.inner,
|
|
88
|
-
section: $style.section
|
|
89
|
-
}'
|
|
59
|
+
:classes='$style'
|
|
90
60
|
:aria-level='level'
|
|
91
61
|
:aria-selected='selected'
|
|
92
|
-
:mod='{ active, selected,
|
|
62
|
+
:mod='{ active, selected, "tree-item": path }'
|
|
93
63
|
@click.prevent='handleClick'
|
|
94
64
|
@keydown.prevent='handleKeyDown'
|
|
95
65
|
>
|
|
96
|
-
<template
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
<
|
|
102
|
-
:class='$style.icon'
|
|
103
|
-
:name='
|
|
104
|
-
|
|
105
|
-
|
|
66
|
+
<template #leftSection>
|
|
67
|
+
<template v-if='pending'>
|
|
68
|
+
<Loader :class='$style.icon' />
|
|
69
|
+
</template>
|
|
70
|
+
|
|
71
|
+
<template v-else-if='isFolder'>
|
|
72
|
+
<Icon v-if='expanded' :class='$style.icon' :name='trailingIcon' />
|
|
73
|
+
<Icon v-else :class='$style.icon' :name='icon' />
|
|
74
|
+
</template>
|
|
75
|
+
|
|
76
|
+
<template v-else>
|
|
77
|
+
<Icon
|
|
78
|
+
:class='$style.icon'
|
|
79
|
+
:name='fileIcon'
|
|
80
|
+
:style='{ color: color && getThemeColor(color, theme) }'
|
|
81
|
+
/>
|
|
82
|
+
</template>
|
|
106
83
|
</template>
|
|
107
|
-
|
|
84
|
+
|
|
85
|
+
{{ name ?? path }}
|
|
108
86
|
</Button>
|
|
109
87
|
</RovingFocusItem>
|
|
110
88
|
|
|
111
|
-
<
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
89
|
+
<UTransition name='scale-y'>
|
|
90
|
+
<ul
|
|
91
|
+
v-if='expanded && data && data.length > 0'
|
|
92
|
+
:class='$style.list'
|
|
93
|
+
role='group'
|
|
94
|
+
>
|
|
95
|
+
<TreeItem
|
|
96
|
+
v-for='child in data'
|
|
97
|
+
:key='child.path'
|
|
98
|
+
v-bind='child'
|
|
99
|
+
:path='child.path'
|
|
100
|
+
:level='level + 1'
|
|
101
|
+
/>
|
|
102
|
+
</ul>
|
|
103
|
+
</UTransition>
|
|
123
104
|
</li>
|
|
124
105
|
</template>
|
|
125
106
|
|
|
126
107
|
<style module lang="postcss">
|
|
127
|
-
.
|
|
108
|
+
.item {
|
|
128
109
|
display: grid;
|
|
129
110
|
gap: .25rem;
|
|
130
111
|
}
|
|
131
112
|
|
|
132
|
-
.
|
|
113
|
+
.icon {
|
|
114
|
+
--loader-size: var(--tree-icon-size);
|
|
115
|
+
|
|
116
|
+
width: var(--tree-icon-size);
|
|
117
|
+
height: var(--tree-icon-size);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.list {
|
|
121
|
+
display: grid;
|
|
122
|
+
gap: .25rem;
|
|
123
|
+
|
|
124
|
+
margin-inline-start: 1rem;
|
|
125
|
+
|
|
126
|
+
padding-inline-start: .75rem;
|
|
127
|
+
border-left: 1px solid var(--color-gray-4);
|
|
128
|
+
|
|
129
|
+
list-style: none;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.root {
|
|
133
133
|
.inner {
|
|
134
134
|
display: grid;
|
|
135
135
|
grid-template-columns: auto 1fr auto;
|
|
@@ -152,21 +152,4 @@ function handleKeyDown(event) {
|
|
|
152
152
|
background: alpha(var(--button-color), .1);
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
|
-
|
|
156
|
-
.icon {
|
|
157
|
-
width: var(--tree-icon-size);
|
|
158
|
-
height: var(--tree-icon-size);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
.list {
|
|
162
|
-
display: grid;
|
|
163
|
-
gap: .25rem;
|
|
164
|
-
|
|
165
|
-
margin-inline-start: 1rem;
|
|
166
|
-
|
|
167
|
-
padding-inline-start: .75rem;
|
|
168
|
-
border-left: 1px solid var(--color-gray-4);
|
|
169
|
-
|
|
170
|
-
list-style: none;
|
|
171
|
-
}
|
|
172
155
|
</style>
|
|
@@ -1,19 +1,7 @@
|
|
|
1
1
|
import type { TreeItem } from '../model.js';
|
|
2
|
-
export interface TreeItemProps
|
|
3
|
-
item: TreeItem<T>;
|
|
2
|
+
export interface TreeItemProps extends TreeItem {
|
|
4
3
|
level: number;
|
|
5
4
|
}
|
|
6
|
-
declare const __VLS_export: <
|
|
7
|
-
props: __VLS_PrettifyLocal<TreeItemProps<T>> & import("vue").PublicProps;
|
|
8
|
-
expose: (exposed: {}) => void;
|
|
9
|
-
attrs: any;
|
|
10
|
-
slots: {};
|
|
11
|
-
emit: {};
|
|
12
|
-
}>) => import("vue").VNode & {
|
|
13
|
-
__ctx?: Awaited<typeof __VLS_setup>;
|
|
14
|
-
};
|
|
5
|
+
declare const __VLS_export: import("vue").DefineComponent<TreeItemProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<TreeItemProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
15
6
|
declare const _default: typeof __VLS_export;
|
|
16
7
|
export default _default;
|
|
17
|
-
type __VLS_PrettifyLocal<T> = {
|
|
18
|
-
[K in keyof T as K]: T[K];
|
|
19
|
-
} & {};
|
|
@@ -1,44 +1,42 @@
|
|
|
1
1
|
import type { ButtonProps } from '@nui/components';
|
|
2
2
|
import type { RovingFocusProps } from '../../roving-focus/roving-focus.vue.js';
|
|
3
|
-
import type { TreeIconResolver, TreeModels } from '../model.js';
|
|
4
|
-
export type TreeRootProps
|
|
5
|
-
iconResolver?: TreeIconResolver
|
|
3
|
+
import type { TreeIconResolver, TreeLoader, TreeModels } from '../model.js';
|
|
4
|
+
export type TreeRootProps = RovingFocusProps & {
|
|
5
|
+
iconResolver?: TreeIconResolver;
|
|
6
6
|
removable?: boolean;
|
|
7
7
|
selectable?: boolean;
|
|
8
|
+
loadBranch: TreeLoader;
|
|
8
9
|
variant?: ButtonProps['variant'];
|
|
9
10
|
color?: ButtonProps['color'];
|
|
10
11
|
size?: ButtonProps['size'];
|
|
11
12
|
};
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
active?: TreeModels<T>["active"];
|
|
23
|
-
selected?: TreeModels<T>["selected"];
|
|
24
|
-
expanded?: TreeModels<T>["expanded"];
|
|
25
|
-
}) & {
|
|
26
|
-
"onUpdate:tree"?: ((value: import("@nui/components").TreeItem<T>[]) => any) | undefined;
|
|
27
|
-
"onUpdate:active"?: ((value: T | null) => any) | undefined;
|
|
28
|
-
"onUpdate:selected"?: ((value: T[]) => any) | undefined;
|
|
29
|
-
"onUpdate:expanded"?: ((value: T[]) => any) | undefined;
|
|
30
|
-
}> & import("vue").PublicProps;
|
|
31
|
-
expose: (exposed: {}) => void;
|
|
32
|
-
attrs: any;
|
|
33
|
-
slots: {
|
|
34
|
-
default?: (props: {}) => any;
|
|
35
|
-
};
|
|
36
|
-
emit: ((evt: "update:tree", value: import("@nui/components").TreeItem<T>[]) => void) & ((evt: "update:active", value: T | null) => void) & ((evt: "update:selected", value: T[]) => void) & ((evt: "update:expanded", value: T[]) => void);
|
|
37
|
-
}>) => import("vue").VNode & {
|
|
38
|
-
__ctx?: Awaited<typeof __VLS_setup>;
|
|
13
|
+
type __VLS_Props = TreeRootProps;
|
|
14
|
+
type __VLS_ModelProps = {
|
|
15
|
+
'active'?: TreeModels['active'];
|
|
16
|
+
'selected'?: TreeModels['selected'];
|
|
17
|
+
'expanded'?: TreeModels['expanded'];
|
|
18
|
+
};
|
|
19
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
20
|
+
declare var __VLS_15: {};
|
|
21
|
+
type __VLS_Slots = {} & {
|
|
22
|
+
default?: (props: typeof __VLS_15) => any;
|
|
39
23
|
};
|
|
24
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
25
|
+
delete: (path: string[]) => any;
|
|
26
|
+
"update:active": (value: string | null) => any;
|
|
27
|
+
"update:selected": (value: string[]) => any;
|
|
28
|
+
"update:expanded": (value: string[]) => any;
|
|
29
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
30
|
+
onDelete?: ((path: string[]) => any) | undefined;
|
|
31
|
+
"onUpdate:active"?: ((value: string | null) => any) | undefined;
|
|
32
|
+
"onUpdate:selected"?: ((value: string[]) => any) | undefined;
|
|
33
|
+
"onUpdate:expanded"?: ((value: string[]) => any) | undefined;
|
|
34
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
35
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
40
36
|
declare const _default: typeof __VLS_export;
|
|
41
37
|
export default _default;
|
|
42
|
-
type
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
39
|
+
new (): {
|
|
40
|
+
$slots: S;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { useStyleResolver } from "@nui/composals";
|
|
3
|
-
import { getSize
|
|
3
|
+
import { getSize } from "@nui/utils";
|
|
4
4
|
import { onClickOutside, useEventListener } from "@vueuse/core";
|
|
5
5
|
import { useTemplateRef } from "vue";
|
|
6
6
|
import Box from "../../box.vue";
|
|
@@ -15,7 +15,8 @@ const {
|
|
|
15
15
|
size = "compact-sm",
|
|
16
16
|
iconResolver = () => ({ icon: "gravity-ui:file" }),
|
|
17
17
|
removable = false,
|
|
18
|
-
selectable = false
|
|
18
|
+
selectable = false,
|
|
19
|
+
loadBranch
|
|
19
20
|
} = defineProps({
|
|
20
21
|
loop: { type: Boolean, required: false },
|
|
21
22
|
orientation: { type: String, required: false },
|
|
@@ -23,12 +24,13 @@ const {
|
|
|
23
24
|
iconResolver: { type: Function, required: false },
|
|
24
25
|
removable: { type: Boolean, required: false },
|
|
25
26
|
selectable: { type: Boolean, required: false },
|
|
27
|
+
loadBranch: { type: Function, required: true },
|
|
26
28
|
variant: { type: String, required: false },
|
|
27
29
|
color: { type: null, required: false },
|
|
28
30
|
size: { type: null, required: false }
|
|
29
31
|
});
|
|
30
|
-
const
|
|
31
|
-
const active = defineModel("active", { type: null, ...{ default: null } });
|
|
32
|
+
const emit = defineEmits(["delete"]);
|
|
33
|
+
const active = defineModel("active", { type: [String, null], ...{ default: null } });
|
|
32
34
|
const selected = defineModel("selected", { type: Array, ...{ default: [] } });
|
|
33
35
|
const expanded = defineModel("expanded", { type: Array, ...{ default: [] } });
|
|
34
36
|
const style = useStyleResolver(() => ({
|
|
@@ -37,7 +39,7 @@ const style = useStyleResolver(() => ({
|
|
|
37
39
|
const root = useTemplateRef("parent");
|
|
38
40
|
onClickOutside(root, () => selected.value = []);
|
|
39
41
|
useProvideTreeState({
|
|
40
|
-
|
|
42
|
+
root,
|
|
41
43
|
active,
|
|
42
44
|
selected,
|
|
43
45
|
expanded,
|
|
@@ -45,13 +47,14 @@ useProvideTreeState({
|
|
|
45
47
|
size,
|
|
46
48
|
color,
|
|
47
49
|
variant,
|
|
48
|
-
selectable
|
|
50
|
+
selectable,
|
|
51
|
+
loadBranch
|
|
49
52
|
});
|
|
50
53
|
if (removable) {
|
|
51
54
|
useEventListener(root, "keydown", (event) => {
|
|
52
55
|
if (event.key === "Delete") {
|
|
53
56
|
event.preventDefault();
|
|
54
|
-
|
|
57
|
+
emit("delete", selected.value);
|
|
55
58
|
}
|
|
56
59
|
});
|
|
57
60
|
}
|
|
@@ -65,7 +68,7 @@ if (removable) {
|
|
|
65
68
|
role='tree'
|
|
66
69
|
:style
|
|
67
70
|
:class='$style.root'
|
|
68
|
-
@keydown.esc.prevent='selected
|
|
71
|
+
@keydown.esc.prevent='selected = []'
|
|
69
72
|
>
|
|
70
73
|
<slot />
|
|
71
74
|
</Box>
|
|
@@ -1,44 +1,42 @@
|
|
|
1
1
|
import type { ButtonProps } from '@nui/components';
|
|
2
2
|
import type { RovingFocusProps } from '../../roving-focus/roving-focus.vue.js';
|
|
3
|
-
import type { TreeIconResolver, TreeModels } from '../model.js';
|
|
4
|
-
export type TreeRootProps
|
|
5
|
-
iconResolver?: TreeIconResolver
|
|
3
|
+
import type { TreeIconResolver, TreeLoader, TreeModels } from '../model.js';
|
|
4
|
+
export type TreeRootProps = RovingFocusProps & {
|
|
5
|
+
iconResolver?: TreeIconResolver;
|
|
6
6
|
removable?: boolean;
|
|
7
7
|
selectable?: boolean;
|
|
8
|
+
loadBranch: TreeLoader;
|
|
8
9
|
variant?: ButtonProps['variant'];
|
|
9
10
|
color?: ButtonProps['color'];
|
|
10
11
|
size?: ButtonProps['size'];
|
|
11
12
|
};
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
active?: TreeModels<T>["active"];
|
|
23
|
-
selected?: TreeModels<T>["selected"];
|
|
24
|
-
expanded?: TreeModels<T>["expanded"];
|
|
25
|
-
}) & {
|
|
26
|
-
"onUpdate:tree"?: ((value: import("@nui/components").TreeItem<T>[]) => any) | undefined;
|
|
27
|
-
"onUpdate:active"?: ((value: T | null) => any) | undefined;
|
|
28
|
-
"onUpdate:selected"?: ((value: T[]) => any) | undefined;
|
|
29
|
-
"onUpdate:expanded"?: ((value: T[]) => any) | undefined;
|
|
30
|
-
}> & import("vue").PublicProps;
|
|
31
|
-
expose: (exposed: {}) => void;
|
|
32
|
-
attrs: any;
|
|
33
|
-
slots: {
|
|
34
|
-
default?: (props: {}) => any;
|
|
35
|
-
};
|
|
36
|
-
emit: ((evt: "update:tree", value: import("@nui/components").TreeItem<T>[]) => void) & ((evt: "update:active", value: T | null) => void) & ((evt: "update:selected", value: T[]) => void) & ((evt: "update:expanded", value: T[]) => void);
|
|
37
|
-
}>) => import("vue").VNode & {
|
|
38
|
-
__ctx?: Awaited<typeof __VLS_setup>;
|
|
13
|
+
type __VLS_Props = TreeRootProps;
|
|
14
|
+
type __VLS_ModelProps = {
|
|
15
|
+
'active'?: TreeModels['active'];
|
|
16
|
+
'selected'?: TreeModels['selected'];
|
|
17
|
+
'expanded'?: TreeModels['expanded'];
|
|
18
|
+
};
|
|
19
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
20
|
+
declare var __VLS_15: {};
|
|
21
|
+
type __VLS_Slots = {} & {
|
|
22
|
+
default?: (props: typeof __VLS_15) => any;
|
|
39
23
|
};
|
|
24
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
25
|
+
delete: (path: string[]) => any;
|
|
26
|
+
"update:active": (value: string | null) => any;
|
|
27
|
+
"update:selected": (value: string[]) => any;
|
|
28
|
+
"update:expanded": (value: string[]) => any;
|
|
29
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
30
|
+
onDelete?: ((path: string[]) => any) | undefined;
|
|
31
|
+
"onUpdate:active"?: ((value: string | null) => any) | undefined;
|
|
32
|
+
"onUpdate:selected"?: ((value: string[]) => any) | undefined;
|
|
33
|
+
"onUpdate:expanded"?: ((value: string[]) => any) | undefined;
|
|
34
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
35
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
40
36
|
declare const _default: typeof __VLS_export;
|
|
41
37
|
export default _default;
|
|
42
|
-
type
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
39
|
+
new (): {
|
|
40
|
+
$slots: S;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
@@ -1,32 +1,26 @@
|
|
|
1
|
-
import type { ButtonProps
|
|
2
|
-
import type { ModelRef } from 'vue';
|
|
1
|
+
import type { ButtonProps } from '@nui/components';
|
|
2
|
+
import type { ModelRef, ShallowRef } from 'vue';
|
|
3
|
+
import type { TreeIconResolver, TreeLoader } from '../model.js';
|
|
3
4
|
type EventType = 'select' | 'expand';
|
|
4
5
|
type SelectMode = 'single' | 'multiple' | 'range';
|
|
5
|
-
export interface TreeContext
|
|
6
|
-
|
|
7
|
-
active: ModelRef<
|
|
8
|
-
selected: ModelRef<
|
|
9
|
-
expanded: ModelRef<
|
|
10
|
-
iconResolver: TreeIconResolver
|
|
6
|
+
export interface TreeContext {
|
|
7
|
+
root: ShallowRef<HTMLUListElement | null>;
|
|
8
|
+
active: ModelRef<string | null>;
|
|
9
|
+
selected: ModelRef<string[]>;
|
|
10
|
+
expanded: ModelRef<string[]>;
|
|
11
|
+
iconResolver: TreeIconResolver;
|
|
12
|
+
loadBranch: TreeLoader;
|
|
11
13
|
selectable: boolean;
|
|
12
14
|
variant: ButtonProps['variant'];
|
|
13
15
|
color: ButtonProps['color'];
|
|
14
16
|
size: ButtonProps['size'];
|
|
15
17
|
}
|
|
16
|
-
export interface TreeState
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
toggle: (type: EventType, value: T, mode?: SelectMode) => void;
|
|
22
|
-
on: ((type: 'expand', value: T) => void) & ((type: 'select', value: T, mode?: SelectMode) => void);
|
|
23
|
-
off: (type: EventType, value: T) => void;
|
|
24
|
-
setActive: (value: T | null) => void;
|
|
25
|
-
selectable: boolean;
|
|
26
|
-
variant: ButtonProps['variant'];
|
|
27
|
-
color: ButtonProps['color'];
|
|
28
|
-
size: ButtonProps['size'];
|
|
18
|
+
export interface TreeState extends Omit<TreeContext, 'root'> {
|
|
19
|
+
toggle: (type: EventType, path: string, mode?: SelectMode) => void;
|
|
20
|
+
on: ((type: 'expand', path: string) => void) & ((type: 'select', path: string, mode?: SelectMode) => void);
|
|
21
|
+
off: (type: EventType, path: string) => void;
|
|
22
|
+
setActive: (path: string | null) => void;
|
|
29
23
|
}
|
|
30
|
-
export declare
|
|
31
|
-
export declare
|
|
24
|
+
export declare const useProvideTreeState: (args_0: TreeContext) => TreeState;
|
|
25
|
+
export declare const useTreeState: () => TreeState;
|
|
32
26
|
export {};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createStrictInjection } from "@nui/helpers";
|
|
2
|
-
import {
|
|
2
|
+
import { unrefElement } from "@vueuse/core";
|
|
3
3
|
const injectionKey = Symbol("nui-tree");
|
|
4
4
|
const [useProvide, useState] = createStrictInjection(({
|
|
5
|
-
|
|
5
|
+
root,
|
|
6
6
|
active,
|
|
7
7
|
selected,
|
|
8
8
|
expanded,
|
|
@@ -11,70 +11,82 @@ const [useProvide, useState] = createStrictInjection(({
|
|
|
11
11
|
...rest
|
|
12
12
|
}) => {
|
|
13
13
|
const setActive = (value) => active.value = value;
|
|
14
|
-
function on(type,
|
|
14
|
+
function on(type, path, mode) {
|
|
15
15
|
if (type === "select") {
|
|
16
|
-
setActive(
|
|
16
|
+
setActive(path);
|
|
17
17
|
if (!selectable)
|
|
18
18
|
return;
|
|
19
19
|
switch (mode) {
|
|
20
20
|
case "single":
|
|
21
|
-
return selected.value = [
|
|
21
|
+
return selected.value = [path];
|
|
22
22
|
case "multiple": {
|
|
23
|
-
const exist = selected.value.includes(
|
|
23
|
+
const exist = selected.value.includes(path);
|
|
24
24
|
if (!exist)
|
|
25
|
-
selected.value = [...selected.value,
|
|
25
|
+
selected.value = [...selected.value, path];
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
28
|
case "range": {
|
|
29
|
-
if (!active.value || active.value === value)
|
|
29
|
+
if (!active.value || active.value === path || !root.value)
|
|
30
30
|
return;
|
|
31
|
-
const
|
|
32
|
-
|
|
31
|
+
const elements = unrefElement(root)?.querySelectorAll("[data-tree-item]");
|
|
32
|
+
if (!elements)
|
|
33
|
+
return;
|
|
34
|
+
const newSelected = /* @__PURE__ */ new Set();
|
|
35
|
+
let startIx = -1;
|
|
36
|
+
for (const [ix, elem] of elements.entries()) {
|
|
37
|
+
const elemPath = elem.getAttribute("data-tree-item");
|
|
38
|
+
if (!elemPath)
|
|
39
|
+
continue;
|
|
40
|
+
const isBoundary = elemPath === active.value || elemPath === path;
|
|
41
|
+
if (isBoundary || startIx >= 0)
|
|
42
|
+
newSelected.add(elemPath);
|
|
43
|
+
if (isBoundary) {
|
|
44
|
+
if (startIx >= 0)
|
|
45
|
+
break;
|
|
46
|
+
startIx = ix;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return selected.value = [...newSelected];
|
|
33
50
|
}
|
|
34
51
|
default:
|
|
35
|
-
return selected.value = [
|
|
52
|
+
return selected.value = [path];
|
|
36
53
|
}
|
|
37
54
|
}
|
|
38
55
|
if (type === "expand") {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
expanded.value = [...expanded.value, value];
|
|
56
|
+
if (!expanded.value.includes(path))
|
|
57
|
+
expanded.value = [...expanded.value, path];
|
|
42
58
|
return;
|
|
43
59
|
}
|
|
44
60
|
return console.warn(`Unknown target type in NuiTree: ${type}`);
|
|
45
61
|
}
|
|
46
|
-
const off = (type,
|
|
62
|
+
const off = (type, path) => {
|
|
47
63
|
switch (type) {
|
|
48
64
|
case "select": {
|
|
49
|
-
selected.value = selected.value.filter((i) => i !==
|
|
50
|
-
if (active.value ===
|
|
65
|
+
selected.value = selected.value.filter((i) => i !== path);
|
|
66
|
+
if (active.value === path)
|
|
51
67
|
active.value = selected.value[0] ?? null;
|
|
52
68
|
return;
|
|
53
69
|
}
|
|
54
|
-
case "expand":
|
|
55
|
-
|
|
56
|
-
return expanded.value = expanded.value.filter((v) => v !== value && !children.has(v));
|
|
57
|
-
}
|
|
70
|
+
case "expand":
|
|
71
|
+
return expanded.value = expanded.value.filter((v) => !v.startsWith(path));
|
|
58
72
|
default:
|
|
59
73
|
return console.warn(`Unknown target type in NuiTree: ${type}`);
|
|
60
74
|
}
|
|
61
75
|
};
|
|
62
|
-
const toggle = (type,
|
|
76
|
+
const toggle = (type, path, mode = "single") => {
|
|
63
77
|
switch (type) {
|
|
64
78
|
case "select": {
|
|
65
|
-
const isSelected = selected.value.includes(
|
|
79
|
+
const isSelected = selected.value.includes(path);
|
|
66
80
|
if (mode === "single")
|
|
67
|
-
return on(type,
|
|
81
|
+
return on(type, path, mode);
|
|
68
82
|
if (mode === "multiple")
|
|
69
|
-
return isSelected ? off(type,
|
|
83
|
+
return isSelected ? off(type, path) : on(type, path, mode);
|
|
70
84
|
if (mode === "range")
|
|
71
|
-
return on(type,
|
|
85
|
+
return on(type, path, mode);
|
|
72
86
|
return;
|
|
73
87
|
}
|
|
74
|
-
case "expand":
|
|
75
|
-
|
|
76
|
-
return isExpanded ? off(type, value) : on(type, value);
|
|
77
|
-
}
|
|
88
|
+
case "expand":
|
|
89
|
+
return expanded.value.includes(path) ? off(type, path) : on(type, path);
|
|
78
90
|
default:
|
|
79
91
|
console.warn(`Unknown target type in NuiTree: ${type}`);
|
|
80
92
|
}
|
|
@@ -95,9 +107,5 @@ const [useProvide, useState] = createStrictInjection(({
|
|
|
95
107
|
injectionKey,
|
|
96
108
|
name: "TreeState"
|
|
97
109
|
});
|
|
98
|
-
export
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
export function useTreeState() {
|
|
102
|
-
return useState();
|
|
103
|
-
}
|
|
110
|
+
export const useProvideTreeState = useProvide;
|
|
111
|
+
export const useTreeState = useState;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useRovingFocus } from "@nui/components";
|
|
2
|
+
import { useTreeState } from "./context.js";
|
|
3
|
+
export function useTreeItemHandlers(path, isFolder, expanded) {
|
|
4
|
+
const ctx = useTreeState();
|
|
5
|
+
const { focus } = useRovingFocus();
|
|
6
|
+
function handleClick(event) {
|
|
7
|
+
if (event.shiftKey)
|
|
8
|
+
ctx.toggle("select", path, "range");
|
|
9
|
+
else if (event.ctrlKey || event.metaKey)
|
|
10
|
+
ctx.toggle("select", path, "multiple");
|
|
11
|
+
else
|
|
12
|
+
ctx.toggle("select", path, "single");
|
|
13
|
+
if (isFolder.value && !event.ctrlKey && !event.metaKey && !event.shiftKey)
|
|
14
|
+
ctx.toggle("expand", path);
|
|
15
|
+
}
|
|
16
|
+
function handleKeyDown(event) {
|
|
17
|
+
switch (event.key) {
|
|
18
|
+
// Arrow Left - закрыть папку или перейти к родителю
|
|
19
|
+
case "ArrowLeft": {
|
|
20
|
+
event.preventDefault();
|
|
21
|
+
if (isFolder.value && expanded.value)
|
|
22
|
+
return ctx.off("expand", path);
|
|
23
|
+
else
|
|
24
|
+
return focus("prev", event.currentTarget);
|
|
25
|
+
}
|
|
26
|
+
// Arrow Right - открыть папку или перейти к первому ребенку
|
|
27
|
+
case "ArrowRight": {
|
|
28
|
+
event.preventDefault();
|
|
29
|
+
if (isFolder.value && !expanded.value)
|
|
30
|
+
return ctx.on("expand", path);
|
|
31
|
+
else if (expanded.value)
|
|
32
|
+
return focus("next", event.currentTarget);
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
// Enter/Space - выбор элемента
|
|
36
|
+
case "Enter": {
|
|
37
|
+
ctx.on("select", path);
|
|
38
|
+
return ctx.setActive(path);
|
|
39
|
+
}
|
|
40
|
+
case " ": {
|
|
41
|
+
event.preventDefault();
|
|
42
|
+
if (event.shiftKey)
|
|
43
|
+
return ctx.toggle("select", path, "range");
|
|
44
|
+
else if (event.ctrlKey || event.metaKey)
|
|
45
|
+
return ctx.toggle("select", path, "multiple");
|
|
46
|
+
return ctx.toggle("select", path, "single");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
handleClick,
|
|
52
|
+
handleKeyDown
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -1,33 +1,39 @@
|
|
|
1
1
|
import type { NuanceColor } from '@nui/types';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
import type { AsyncData } from '#app';
|
|
3
|
+
export interface TreeModels {
|
|
4
|
+
active: string | null;
|
|
5
|
+
tree: TreeItem[];
|
|
6
|
+
selected: string[];
|
|
7
|
+
expanded: string[];
|
|
7
8
|
}
|
|
8
|
-
export interface
|
|
9
|
+
export interface TreeEmits {
|
|
10
|
+
delete: [path: string[]];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Required pass instance of `useFetch`/`useAsyncData` with `{ immediate: false }`
|
|
14
|
+
* Root path will be queried immediate with path `/`
|
|
15
|
+
*/
|
|
16
|
+
export type TreeLoader = (path: string) => AsyncData<TreeItem[], unknown>;
|
|
17
|
+
export type TreeItemType = 'file' | 'directory';
|
|
18
|
+
export interface TreeItem {
|
|
9
19
|
/** @IconifyIcon */
|
|
10
20
|
icon?: string;
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
/** Item
|
|
14
|
-
|
|
21
|
+
/** Path given to this item */
|
|
22
|
+
path: string;
|
|
23
|
+
/** Item name */
|
|
24
|
+
name?: string;
|
|
15
25
|
/** @IconifyIcon */
|
|
16
26
|
trailingIcon?: string;
|
|
17
|
-
/** Item default expanded */
|
|
18
|
-
expanded?: boolean;
|
|
19
27
|
/** Item disabled state */
|
|
20
28
|
disabled?: boolean;
|
|
21
|
-
/**
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
/** Fires when item is toggled */
|
|
28
|
-
onToggle?: (item: TreeItem<T>) => void;
|
|
29
|
+
/**
|
|
30
|
+
* Type of item
|
|
31
|
+
* @default `file`
|
|
32
|
+
*/
|
|
33
|
+
type?: TreeItemType;
|
|
34
|
+
children?: TreeItem[];
|
|
29
35
|
}
|
|
30
|
-
export type TreeIconResolver
|
|
36
|
+
export type TreeIconResolver = (type: TreeItemType, name?: string, path?: string, disabled?: boolean) => {
|
|
31
37
|
icon: string;
|
|
32
38
|
color?: NuanceColor;
|
|
33
39
|
};
|
|
@@ -1,28 +1,24 @@
|
|
|
1
1
|
import type { TreeRootProps } from './_ui/tree-root.vue.js';
|
|
2
2
|
import type { TreeModels } from './model.js';
|
|
3
|
-
export interface TreeProps
|
|
3
|
+
export interface TreeProps extends TreeRootProps {
|
|
4
4
|
}
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
expanded?: TreeModels<T>["expanded"];
|
|
11
|
-
}) & {
|
|
12
|
-
"onUpdate:tree"?: ((value: import("./model.js").TreeItem<T>[]) => any) | undefined;
|
|
13
|
-
"onUpdate:active"?: ((value: T | null) => any) | undefined;
|
|
14
|
-
"onUpdate:selected"?: ((value: T[]) => any) | undefined;
|
|
15
|
-
"onUpdate:expanded"?: ((value: T[]) => any) | undefined;
|
|
16
|
-
}> & import("vue").PublicProps;
|
|
17
|
-
expose: (exposed: {}) => void;
|
|
18
|
-
attrs: any;
|
|
19
|
-
slots: {};
|
|
20
|
-
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);
|
|
21
|
-
}>) => import("vue").VNode & {
|
|
22
|
-
__ctx?: Awaited<typeof __VLS_setup>;
|
|
5
|
+
type __VLS_Props = TreeProps;
|
|
6
|
+
type __VLS_ModelProps = {
|
|
7
|
+
'active'?: TreeModels['active'];
|
|
8
|
+
'selected'?: TreeModels['selected'];
|
|
9
|
+
'expanded'?: TreeModels['expanded'];
|
|
23
10
|
};
|
|
11
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
12
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
13
|
+
delete: (path: string[]) => any;
|
|
14
|
+
"update:active": (value: string | null) => any;
|
|
15
|
+
"update:selected": (value: string[]) => any;
|
|
16
|
+
"update:expanded": (value: string[]) => any;
|
|
17
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
18
|
+
onDelete?: ((path: string[]) => any) | undefined;
|
|
19
|
+
"onUpdate:active"?: ((value: string | null) => any) | undefined;
|
|
20
|
+
"onUpdate:selected"?: ((value: string[]) => any) | undefined;
|
|
21
|
+
"onUpdate:expanded"?: ((value: string[]) => any) | undefined;
|
|
22
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
24
23
|
declare const _default: typeof __VLS_export;
|
|
25
24
|
export default _default;
|
|
26
|
-
type __VLS_PrettifyLocal<T> = {
|
|
27
|
-
[K in keyof T as K]: T[K];
|
|
28
|
-
} & {};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
+
import { onMounted } from "vue";
|
|
2
3
|
import UTreeItem from "./_ui/tree-item.vue";
|
|
3
4
|
import TreeRoot from "./_ui/tree-root.vue";
|
|
4
|
-
import { getExpandedItems } from "./lib/get-default";
|
|
5
5
|
const {
|
|
6
6
|
color,
|
|
7
7
|
variant = "subtle",
|
|
8
8
|
size = "compact-sm",
|
|
9
|
+
loadBranch,
|
|
9
10
|
...props
|
|
10
11
|
} = defineProps({
|
|
11
12
|
loop: { type: Boolean, required: false },
|
|
@@ -14,32 +15,36 @@ const {
|
|
|
14
15
|
iconResolver: { type: Function, required: false },
|
|
15
16
|
removable: { type: Boolean, required: false },
|
|
16
17
|
selectable: { type: Boolean, required: false },
|
|
18
|
+
loadBranch: { type: Function, required: true },
|
|
17
19
|
variant: { type: String, required: false },
|
|
18
20
|
color: { type: null, required: false },
|
|
19
21
|
size: { type: null, required: false }
|
|
20
22
|
});
|
|
21
|
-
|
|
22
|
-
const active = defineModel("active", { type: null, ...{ default: null } });
|
|
23
|
+
defineEmits(["delete"]);
|
|
24
|
+
const active = defineModel("active", { type: [String, null], ...{ default: null } });
|
|
23
25
|
const selected = defineModel("selected", { type: Array, ...{ default: [] } });
|
|
24
26
|
const expanded = defineModel("expanded", { type: Array, ...{ default: [] } });
|
|
25
|
-
|
|
27
|
+
const { data: root, execute } = loadBranch("/");
|
|
28
|
+
onMounted(execute);
|
|
26
29
|
</script>
|
|
27
30
|
|
|
28
31
|
<template>
|
|
29
32
|
<TreeRoot
|
|
30
33
|
v-bind='props'
|
|
31
|
-
v-model:tree='tree'
|
|
32
34
|
v-model:active='active'
|
|
33
35
|
v-model:selected='selected'
|
|
34
36
|
v-model:expanded='expanded'
|
|
37
|
+
:load-branch
|
|
35
38
|
:size
|
|
36
39
|
:color
|
|
37
40
|
:variant
|
|
41
|
+
@delete='(path) => $emit("delete", path)'
|
|
38
42
|
>
|
|
39
43
|
<UTreeItem
|
|
40
|
-
v-for='item in
|
|
41
|
-
:key='item.
|
|
42
|
-
|
|
44
|
+
v-for='item in root'
|
|
45
|
+
:key='item.path'
|
|
46
|
+
v-bind='item'
|
|
47
|
+
:path='item.path'
|
|
43
48
|
:level='1'
|
|
44
49
|
/>
|
|
45
50
|
</TreeRoot>
|
|
@@ -1,28 +1,24 @@
|
|
|
1
1
|
import type { TreeRootProps } from './_ui/tree-root.vue.js';
|
|
2
2
|
import type { TreeModels } from './model.js';
|
|
3
|
-
export interface TreeProps
|
|
3
|
+
export interface TreeProps extends TreeRootProps {
|
|
4
4
|
}
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
expanded?: TreeModels<T>["expanded"];
|
|
11
|
-
}) & {
|
|
12
|
-
"onUpdate:tree"?: ((value: import("./model.js").TreeItem<T>[]) => any) | undefined;
|
|
13
|
-
"onUpdate:active"?: ((value: T | null) => any) | undefined;
|
|
14
|
-
"onUpdate:selected"?: ((value: T[]) => any) | undefined;
|
|
15
|
-
"onUpdate:expanded"?: ((value: T[]) => any) | undefined;
|
|
16
|
-
}> & import("vue").PublicProps;
|
|
17
|
-
expose: (exposed: {}) => void;
|
|
18
|
-
attrs: any;
|
|
19
|
-
slots: {};
|
|
20
|
-
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);
|
|
21
|
-
}>) => import("vue").VNode & {
|
|
22
|
-
__ctx?: Awaited<typeof __VLS_setup>;
|
|
5
|
+
type __VLS_Props = TreeProps;
|
|
6
|
+
type __VLS_ModelProps = {
|
|
7
|
+
'active'?: TreeModels['active'];
|
|
8
|
+
'selected'?: TreeModels['selected'];
|
|
9
|
+
'expanded'?: TreeModels['expanded'];
|
|
23
10
|
};
|
|
11
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
12
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
13
|
+
delete: (path: string[]) => any;
|
|
14
|
+
"update:active": (value: string | null) => any;
|
|
15
|
+
"update:selected": (value: string[]) => any;
|
|
16
|
+
"update:expanded": (value: string[]) => any;
|
|
17
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
18
|
+
onDelete?: ((path: string[]) => any) | undefined;
|
|
19
|
+
"onUpdate:active"?: ((value: string | null) => any) | undefined;
|
|
20
|
+
"onUpdate:selected"?: ((value: string[]) => any) | undefined;
|
|
21
|
+
"onUpdate:expanded"?: ((value: string[]) => any) | undefined;
|
|
22
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
24
23
|
declare const _default: typeof __VLS_export;
|
|
25
24
|
export default _default;
|
|
26
|
-
type __VLS_PrettifyLocal<T> = {
|
|
27
|
-
[K in keyof T as K]: T[K];
|
|
28
|
-
} & {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Represents a tree node with a value and optional children. */
|
|
2
2
|
export interface TreeNode<T extends string = string> {
|
|
3
3
|
/** The value stored in this tree node */
|
|
4
|
-
|
|
4
|
+
path: string;
|
|
5
5
|
/** Optional array of child nodes */
|
|
6
6
|
children?: TreeNode<T>[];
|
|
7
7
|
}
|
|
@@ -28,14 +28,14 @@ export declare function filterTree<Node extends TreeNode = TreeNode>(tree: Node[
|
|
|
28
28
|
* Searches for a tree item by its value using depth-first search.
|
|
29
29
|
* @template T - The type of node values
|
|
30
30
|
* @param {TreeNode<T>[]} items - The array of tree nodes to search in
|
|
31
|
-
* @param {T}
|
|
31
|
+
* @param {T} path - The value to search for
|
|
32
32
|
* @returns {TreeNode<T> | null} The found node or null if not found
|
|
33
33
|
* @example
|
|
34
34
|
* const tree = [{ value: 'folder', children: [{ value: 'file' }] }]
|
|
35
35
|
* findTreeItem(tree, 'file') // { value: 'file' }
|
|
36
36
|
* findTreeItem(tree, 'missing') // null
|
|
37
37
|
*/
|
|
38
|
-
export declare function findTreeItem<T extends string = string>(items: TreeNode<T>[],
|
|
38
|
+
export declare function findTreeItem<T extends string = string>(items: TreeNode<T>[], path: T): TreeNode<T> | null;
|
|
39
39
|
/**
|
|
40
40
|
* Flattens a tree structure into a single-level array in depth-first order.
|
|
41
41
|
* @template T - The type of node values
|
|
@@ -68,7 +68,7 @@ export declare function flatTree<T extends string = string>(tree: TreeNode<T>[])
|
|
|
68
68
|
* getBranchChildren(tree, 'folder') // ['file1', 'subfolder', 'file2']
|
|
69
69
|
* getBranchChildren(tree, 'file1') // []
|
|
70
70
|
*/
|
|
71
|
-
export declare function getBranchChildren<T extends string = string>(tree: TreeNode<T>[],
|
|
71
|
+
export declare function getBranchChildren<T extends string = string>(tree: TreeNode<T>[], path: T): T[];
|
|
72
72
|
/**
|
|
73
73
|
* Gets all node values between two specified values (inclusive) in tree traversal order.
|
|
74
74
|
* If start comes after end in traversal order, the range is automatically reversed.
|
|
@@ -92,7 +92,7 @@ export declare function getTreeItemsBetween<T extends string = string>(tree: Tre
|
|
|
92
92
|
* Removes nodes with specified values from the tree, including their subtrees.
|
|
93
93
|
* Returns a new tree without modifying the original.
|
|
94
94
|
*
|
|
95
|
-
* @template T - The type of node
|
|
95
|
+
* @template T - The type of node path
|
|
96
96
|
* @param {TreeNode<T>[]} tree - The original tree (array of root nodes)
|
|
97
97
|
* @param {T[]} valuesToRemove - Array of values to remove
|
|
98
98
|
* @returns {TreeNode<T>[]} New tree with specified nodes and their subtrees removed
|
|
@@ -104,4 +104,4 @@ export declare function getTreeItemsBetween<T extends string = string>(tree: Tre
|
|
|
104
104
|
* removeTreeNodes(tree, ['b', 'd']) // [{ value: 'a', children: [{ value: 'c' }] }]
|
|
105
105
|
* removeTreeNodes(tree, ['a']) // [{ value: 'd' }]
|
|
106
106
|
*/
|
|
107
|
-
export declare function removeTreeNodes<Node extends TreeNode = TreeNode,
|
|
107
|
+
export declare function removeTreeNodes<Node extends TreeNode = TreeNode, Path extends string = string>(tree: Node[], valuesToRemove: Path[]): Node[];
|
|
@@ -22,12 +22,12 @@ export function filterTree(tree, predicate) {
|
|
|
22
22
|
}
|
|
23
23
|
return result;
|
|
24
24
|
}
|
|
25
|
-
export function findTreeItem(items,
|
|
25
|
+
export function findTreeItem(items, path) {
|
|
26
26
|
for (const item of items) {
|
|
27
|
-
if (item.
|
|
27
|
+
if (item.path === path)
|
|
28
28
|
return item;
|
|
29
29
|
if (item.children?.length) {
|
|
30
|
-
const found = findTreeItem(item.children,
|
|
30
|
+
const found = findTreeItem(item.children, path);
|
|
31
31
|
if (found)
|
|
32
32
|
return found;
|
|
33
33
|
}
|
|
@@ -37,14 +37,14 @@ export function findTreeItem(items, value) {
|
|
|
37
37
|
export function flatTree(tree) {
|
|
38
38
|
return traverse(tree);
|
|
39
39
|
}
|
|
40
|
-
export function getBranchChildren(tree,
|
|
40
|
+
export function getBranchChildren(tree, path) {
|
|
41
41
|
const children = [];
|
|
42
42
|
function findAndExtract(nodes) {
|
|
43
43
|
for (const node of nodes) {
|
|
44
|
-
if (node.
|
|
44
|
+
if (node.path === path) {
|
|
45
45
|
if (node.children?.length) {
|
|
46
46
|
const extracted = traverse(node.children);
|
|
47
|
-
children.push(...extracted.map((i) => i.
|
|
47
|
+
children.push(...extracted.map((i) => i.path));
|
|
48
48
|
}
|
|
49
49
|
return true;
|
|
50
50
|
}
|
|
@@ -63,11 +63,11 @@ export function getTreeItemsBetween(tree, start, end) {
|
|
|
63
63
|
let currentIdx = 0;
|
|
64
64
|
function traverseAndFind(nodes) {
|
|
65
65
|
for (const node of nodes) {
|
|
66
|
-
if (node.
|
|
66
|
+
if (node.path === start)
|
|
67
67
|
startIdx = currentIdx;
|
|
68
|
-
if (node.
|
|
68
|
+
if (node.path === end)
|
|
69
69
|
endIdx = currentIdx;
|
|
70
|
-
result.push(node.
|
|
70
|
+
result.push(node.path);
|
|
71
71
|
currentIdx++;
|
|
72
72
|
if (node.children?.length)
|
|
73
73
|
traverseAndFind(node.children);
|
|
@@ -81,5 +81,5 @@ export function getTreeItemsBetween(tree, start, end) {
|
|
|
81
81
|
}
|
|
82
82
|
export function removeTreeNodes(tree, valuesToRemove) {
|
|
83
83
|
const removeSet = new Set(valuesToRemove);
|
|
84
|
-
return filterTree(tree, (n) => !removeSet.has(n.
|
|
84
|
+
return filterTree(tree, (n) => !removeSet.has(n.path));
|
|
85
85
|
}
|