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,90 @@
|
|
|
1
|
+
import { createStrictInjection } from "@nui/helpers";
|
|
2
|
+
import { unrefElement } from "@vueuse/core";
|
|
3
|
+
import { nextTick, ref, watch } from "vue";
|
|
4
|
+
const SELECT_ATTR = "data-roving-item";
|
|
5
|
+
const injectionKey = Symbol("roving-focus");
|
|
6
|
+
const [useProvide, useState] = createStrictInjection(({ list, loop, orientation, attr = SELECT_ATTR }) => {
|
|
7
|
+
const activeIx = ref(-1);
|
|
8
|
+
const init = () => activeIx.value = 0;
|
|
9
|
+
const getItems = () => Array.from(
|
|
10
|
+
unrefElement(list)?.querySelectorAll(`[${attr}]:not(:disabled)`) ?? []
|
|
11
|
+
);
|
|
12
|
+
watch(activeIx, (ix, oldIx) => {
|
|
13
|
+
const items = getItems();
|
|
14
|
+
if (ix !== oldIx) {
|
|
15
|
+
items[oldIx]?.setAttribute("tabindex", "-1");
|
|
16
|
+
items[ix]?.setAttribute("tabindex", "0");
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
const focusElement = (element) => {
|
|
20
|
+
if (!element)
|
|
21
|
+
return;
|
|
22
|
+
activeIx.value = getItems().indexOf(element);
|
|
23
|
+
nextTick(() => element.focus());
|
|
24
|
+
};
|
|
25
|
+
function focus(dir, element) {
|
|
26
|
+
if (!element && dir !== "first" && dir !== "last")
|
|
27
|
+
return;
|
|
28
|
+
const items = getItems();
|
|
29
|
+
const ix = element ? items.indexOf(element) : -1;
|
|
30
|
+
let nextIndex = ix;
|
|
31
|
+
switch (dir) {
|
|
32
|
+
case "first":
|
|
33
|
+
nextIndex = 0;
|
|
34
|
+
break;
|
|
35
|
+
case "last":
|
|
36
|
+
nextIndex = items.length - 1;
|
|
37
|
+
break;
|
|
38
|
+
case "next": {
|
|
39
|
+
nextIndex = ix + 1;
|
|
40
|
+
if (nextIndex >= items.length)
|
|
41
|
+
nextIndex = loop ? 0 : items.length - 1;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
case "prev": {
|
|
45
|
+
nextIndex = ix - 1;
|
|
46
|
+
if (nextIndex < 0)
|
|
47
|
+
nextIndex = loop ? items.length - 1 : 0;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (nextIndex !== ix && items[nextIndex] !== void 0) {
|
|
52
|
+
activeIx.value = nextIndex;
|
|
53
|
+
focusElement(items[nextIndex]);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const onItemUnmount = (element) => {
|
|
57
|
+
if (!element)
|
|
58
|
+
return;
|
|
59
|
+
const items = getItems();
|
|
60
|
+
const currentIndex = items.indexOf(element);
|
|
61
|
+
if (currentIndex === activeIx.value) {
|
|
62
|
+
nextTick(() => {
|
|
63
|
+
const newItems = getItems();
|
|
64
|
+
if (newItems.length === 0)
|
|
65
|
+
return activeIx.value = -1;
|
|
66
|
+
const nextIndex = currentIndex < newItems.length ? currentIndex : newItems.length - 1;
|
|
67
|
+
activeIx.value = nextIndex;
|
|
68
|
+
newItems[nextIndex]?.focus();
|
|
69
|
+
});
|
|
70
|
+
} else if (currentIndex < activeIx.value) {
|
|
71
|
+
activeIx.value--;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
return {
|
|
75
|
+
attr,
|
|
76
|
+
list,
|
|
77
|
+
loop,
|
|
78
|
+
orientation,
|
|
79
|
+
init,
|
|
80
|
+
focus,
|
|
81
|
+
getItems,
|
|
82
|
+
focusElement,
|
|
83
|
+
onItemUnmount
|
|
84
|
+
};
|
|
85
|
+
}, {
|
|
86
|
+
injectionKey,
|
|
87
|
+
name: "RovingFocus"
|
|
88
|
+
});
|
|
89
|
+
export const useProvideRovingFocus = useProvide;
|
|
90
|
+
export const useRovingFocus = useState;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useRovingFocus } from './_lib/context.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useRovingFocus } from "./_lib/context.js";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
declare var __VLS_16: {};
|
|
2
|
+
type __VLS_Slots = {} & {
|
|
3
|
+
default?: (props: typeof __VLS_16) => any;
|
|
4
|
+
};
|
|
5
|
+
declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
6
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
7
|
+
declare const _default: typeof __VLS_export;
|
|
8
|
+
export default _default;
|
|
9
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
10
|
+
new (): {
|
|
11
|
+
$slots: S;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { unrefElement } from "@vueuse/core";
|
|
3
|
+
import { computed, onBeforeUnmount, useTemplateRef } from "vue";
|
|
4
|
+
import Renderless from "../renderless/renderless.vue";
|
|
5
|
+
import { useRovingFocus } from "./_lib/context";
|
|
6
|
+
const { focus, focusElement, orientation, attr, onItemUnmount } = useRovingFocus();
|
|
7
|
+
const isVertical = computed(() => orientation === "vertical" || orientation === "both");
|
|
8
|
+
const isHorizontal = computed(() => orientation === "horizontal" || orientation === "both");
|
|
9
|
+
const attrs = computed(() => ({ [attr]: "" }));
|
|
10
|
+
const ref = useTemplateRef("item");
|
|
11
|
+
onBeforeUnmount(() => {
|
|
12
|
+
const el = unrefElement(ref);
|
|
13
|
+
onItemUnmount(el);
|
|
14
|
+
});
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<Renderless
|
|
19
|
+
v-bind='attrs'
|
|
20
|
+
ref='item'
|
|
21
|
+
:tabindex='-1'
|
|
22
|
+
@keydown.up.prevent='isVertical && focus("prev", $event.currentTarget)'
|
|
23
|
+
@keydown.down.prevent='isVertical && focus("next", $event.currentTarget)'
|
|
24
|
+
@keydown.left.prevent='isHorizontal && focus("prev", $event.currentTarget)'
|
|
25
|
+
@keydown.right.prevent='isHorizontal && focus("next", $event.currentTarget)'
|
|
26
|
+
@keydown.home.prevent='focus("first")'
|
|
27
|
+
@keydown.end.prevent='focus("last")'
|
|
28
|
+
@click.prevent='focusElement($event.currentTarget)'
|
|
29
|
+
>
|
|
30
|
+
<slot />
|
|
31
|
+
</Renderless>
|
|
32
|
+
</template>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
declare var __VLS_16: {};
|
|
2
|
+
type __VLS_Slots = {} & {
|
|
3
|
+
default?: (props: typeof __VLS_16) => any;
|
|
4
|
+
};
|
|
5
|
+
declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
6
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
7
|
+
declare const _default: typeof __VLS_export;
|
|
8
|
+
export default _default;
|
|
9
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
10
|
+
new (): {
|
|
11
|
+
$slots: S;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface RovingFocusProps {
|
|
2
|
+
/** Loop focus from last to first and vice versa */
|
|
3
|
+
loop?: boolean;
|
|
4
|
+
/** Allowed arrow key directions */
|
|
5
|
+
orientation?: 'vertical' | 'horizontal' | 'both';
|
|
6
|
+
/** Custom attribute instead of data-roving-item */
|
|
7
|
+
attr?: string;
|
|
8
|
+
}
|
|
9
|
+
declare var __VLS_7: {};
|
|
10
|
+
type __VLS_Slots = {} & {
|
|
11
|
+
default?: (props: typeof __VLS_7) => any;
|
|
12
|
+
};
|
|
13
|
+
declare const __VLS_base: import("vue").DefineComponent<RovingFocusProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<RovingFocusProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
14
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
15
|
+
declare const _default: typeof __VLS_export;
|
|
16
|
+
export default _default;
|
|
17
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
18
|
+
new (): {
|
|
19
|
+
$slots: S;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { nextTick, onMounted, useTemplateRef } from "vue";
|
|
3
|
+
import Renderless from "../renderless/renderless.vue";
|
|
4
|
+
import { useProvideRovingFocus } from "./_lib/context";
|
|
5
|
+
const { loop = false, orientation = "vertical" } = defineProps({
|
|
6
|
+
loop: { type: Boolean, required: false },
|
|
7
|
+
orientation: { type: String, required: false },
|
|
8
|
+
attr: { type: String, required: false }
|
|
9
|
+
});
|
|
10
|
+
const list = useTemplateRef("list");
|
|
11
|
+
const { init } = useProvideRovingFocus({
|
|
12
|
+
list,
|
|
13
|
+
loop,
|
|
14
|
+
orientation
|
|
15
|
+
});
|
|
16
|
+
onMounted(() => nextTick(() => init()));
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<Renderless ref='list'>
|
|
21
|
+
<slot />
|
|
22
|
+
</Renderless>
|
|
23
|
+
</template>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface RovingFocusProps {
|
|
2
|
+
/** Loop focus from last to first and vice versa */
|
|
3
|
+
loop?: boolean;
|
|
4
|
+
/** Allowed arrow key directions */
|
|
5
|
+
orientation?: 'vertical' | 'horizontal' | 'both';
|
|
6
|
+
/** Custom attribute instead of data-roving-item */
|
|
7
|
+
attr?: string;
|
|
8
|
+
}
|
|
9
|
+
declare var __VLS_7: {};
|
|
10
|
+
type __VLS_Slots = {} & {
|
|
11
|
+
default?: (props: typeof __VLS_7) => any;
|
|
12
|
+
};
|
|
13
|
+
declare const __VLS_base: import("vue").DefineComponent<RovingFocusProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<RovingFocusProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
14
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
15
|
+
declare const _default: typeof __VLS_export;
|
|
16
|
+
export default _default;
|
|
17
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
18
|
+
new (): {
|
|
19
|
+
$slots: S;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
@@ -23,7 +23,7 @@ declare const __VLS_export: <Value extends string = string, Ext extends Combobox
|
|
|
23
23
|
open?: boolean;
|
|
24
24
|
modelValue?: ComboboxItem<Value, Ext> | null;
|
|
25
25
|
search?: string;
|
|
26
|
-
}) & __VLS_EmitsToProps<__VLS_NormalizeEmits<((evt: "select", args_0: number) => void) & ((evt: "clear") => void) & ((evt: "close", args_0: import("
|
|
26
|
+
}) & __VLS_EmitsToProps<__VLS_NormalizeEmits<((evt: "select", args_0: number) => void) & ((evt: "clear") => void) & ((evt: "close", args_0: import("..").ComboboxDropdownEventSource) => void) & ((evt: "submit", args_0: string, args_1: ComboboxItem) => void) & ((evt: "open", args_0: import("..").ComboboxDropdownEventSource) => void) & ((evt: "update:modelValue", value: ComboboxItem<Value, Ext> | null) => void) & ((evt: "update:search", value: string) => void) & ((evt: "update:open", value: boolean) => void)>>> & import("vue").PublicProps;
|
|
27
27
|
expose: (exposed: {}) => void;
|
|
28
28
|
attrs: any;
|
|
29
29
|
slots: {
|
|
@@ -37,7 +37,7 @@ declare const __VLS_export: <Value extends string = string, Ext extends Combobox
|
|
|
37
37
|
} & {
|
|
38
38
|
rightSection?: (props: {}) => any;
|
|
39
39
|
};
|
|
40
|
-
emit: (((evt: "select", args_0: number) => void) & ((evt: "clear") => void) & ((evt: "close", args_0: import("
|
|
40
|
+
emit: (((evt: "select", args_0: number) => void) & ((evt: "clear") => void) & ((evt: "close", args_0: import("..").ComboboxDropdownEventSource) => void) & ((evt: "submit", args_0: string, args_1: ComboboxItem) => void) & ((evt: "open", args_0: import("..").ComboboxDropdownEventSource) => void)) & (((evt: "update:modelValue", value: ComboboxItem<Value, Ext> | null) => void) & ((evt: "update:search", value: string) => void) & ((evt: "update:open", value: boolean) => void));
|
|
41
41
|
}>) => import("vue").VNode & {
|
|
42
42
|
__ctx?: Awaited<typeof __VLS_setup>;
|
|
43
43
|
};
|
|
@@ -23,7 +23,7 @@ declare const __VLS_export: <Value extends string = string, Ext extends Combobox
|
|
|
23
23
|
open?: boolean;
|
|
24
24
|
modelValue?: ComboboxItem<Value, Ext> | null;
|
|
25
25
|
search?: string;
|
|
26
|
-
}) & __VLS_EmitsToProps<__VLS_NormalizeEmits<((evt: "select", args_0: number) => void) & ((evt: "clear") => void) & ((evt: "close", args_0: import("
|
|
26
|
+
}) & __VLS_EmitsToProps<__VLS_NormalizeEmits<((evt: "select", args_0: number) => void) & ((evt: "clear") => void) & ((evt: "close", args_0: import("..").ComboboxDropdownEventSource) => void) & ((evt: "submit", args_0: string, args_1: ComboboxItem) => void) & ((evt: "open", args_0: import("..").ComboboxDropdownEventSource) => void) & ((evt: "update:modelValue", value: ComboboxItem<Value, Ext> | null) => void) & ((evt: "update:search", value: string) => void) & ((evt: "update:open", value: boolean) => void)>>> & import("vue").PublicProps;
|
|
27
27
|
expose: (exposed: {}) => void;
|
|
28
28
|
attrs: any;
|
|
29
29
|
slots: {
|
|
@@ -37,7 +37,7 @@ declare const __VLS_export: <Value extends string = string, Ext extends Combobox
|
|
|
37
37
|
} & {
|
|
38
38
|
rightSection?: (props: {}) => any;
|
|
39
39
|
};
|
|
40
|
-
emit: (((evt: "select", args_0: number) => void) & ((evt: "clear") => void) & ((evt: "close", args_0: import("
|
|
40
|
+
emit: (((evt: "select", args_0: number) => void) & ((evt: "clear") => void) & ((evt: "close", args_0: import("..").ComboboxDropdownEventSource) => void) & ((evt: "submit", args_0: string, args_1: ComboboxItem) => void) & ((evt: "open", args_0: import("..").ComboboxDropdownEventSource) => void)) & (((evt: "update:modelValue", value: ComboboxItem<Value, Ext> | null) => void) & ((evt: "update:search", value: string) => void) & ((evt: "update:open", value: boolean) => void));
|
|
41
41
|
}>) => import("vue").VNode & {
|
|
42
42
|
__ctx?: Awaited<typeof __VLS_setup>;
|
|
43
43
|
};
|
|
File without changes
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { useTheme } from "@nui/composals";
|
|
3
|
+
import { getThemeColor, isFalsy } from "@nui/utils";
|
|
4
|
+
import { computed } from "vue";
|
|
5
|
+
import Button from "../../button/button.vue";
|
|
6
|
+
import { useRovingFocus } from "../../roving-focus";
|
|
7
|
+
import RovingFocusItem from "../../roving-focus/roving-focus-item.vue";
|
|
8
|
+
import { useTreeState } from "../lib/context";
|
|
9
|
+
const { item, level } = defineProps({
|
|
10
|
+
item: { type: Object, required: true },
|
|
11
|
+
level: { type: Number, required: true }
|
|
12
|
+
});
|
|
13
|
+
const {
|
|
14
|
+
icon = "gravity-ui:folder",
|
|
15
|
+
trailingIcon = "gravity-ui:folder-open",
|
|
16
|
+
value,
|
|
17
|
+
label,
|
|
18
|
+
children,
|
|
19
|
+
disabled
|
|
20
|
+
} = item;
|
|
21
|
+
const { value: theme } = useTheme();
|
|
22
|
+
const isFolder = computed(() => !isFalsy(children));
|
|
23
|
+
const ctx = useTreeState();
|
|
24
|
+
const selected = computed(() => ctx.selected.value.includes(value));
|
|
25
|
+
const expanded = computed(() => ctx.expanded.value.includes(value));
|
|
26
|
+
const active = computed(() => ctx.active.value === value);
|
|
27
|
+
const { icon: fileIcon, color } = ctx.iconResolver(item);
|
|
28
|
+
const { focus } = useRovingFocus();
|
|
29
|
+
function handleClick(event) {
|
|
30
|
+
if (event.shiftKey)
|
|
31
|
+
ctx.toggle("select", value, "range");
|
|
32
|
+
else if (event.ctrlKey || event.metaKey)
|
|
33
|
+
ctx.toggle("select", value, "multiple");
|
|
34
|
+
else
|
|
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
|
+
case " ": {
|
|
63
|
+
event.preventDefault();
|
|
64
|
+
if (event.shiftKey)
|
|
65
|
+
return ctx.toggle("select", value, "range");
|
|
66
|
+
else if (event.ctrlKey || event.metaKey)
|
|
67
|
+
return ctx.toggle("select", value, "multiple");
|
|
68
|
+
return ctx.toggle("select", value, "single");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<template>
|
|
75
|
+
<li :class='$style.root' role='presentation'>
|
|
76
|
+
<RovingFocusItem>
|
|
77
|
+
<Button
|
|
78
|
+
variant='subtle'
|
|
79
|
+
size='compact-md'
|
|
80
|
+
role='treeitem'
|
|
81
|
+
:classes='{
|
|
82
|
+
root: $style.button,
|
|
83
|
+
label: $style.label,
|
|
84
|
+
inner: $style.inner,
|
|
85
|
+
section: $style.section
|
|
86
|
+
}'
|
|
87
|
+
:mod='{ active, selected, level }'
|
|
88
|
+
:aria-selected='selected'
|
|
89
|
+
:aria-level='level'
|
|
90
|
+
:disabled
|
|
91
|
+
@click.prevent='handleClick'
|
|
92
|
+
@keydown.prevent='handleKeyDown'
|
|
93
|
+
>
|
|
94
|
+
<template v-if='isFolder' #leftSection>
|
|
95
|
+
<Icon v-if='expanded' :class='$style.icon' :name='trailingIcon' />
|
|
96
|
+
<Icon v-else :class='$style.icon' :name='icon' />
|
|
97
|
+
</template>
|
|
98
|
+
<template v-else #leftSection>
|
|
99
|
+
<Icon
|
|
100
|
+
:class='$style.icon'
|
|
101
|
+
:name='fileIcon'
|
|
102
|
+
:style='{ color: color && getThemeColor(color, theme) }'
|
|
103
|
+
/>
|
|
104
|
+
</template>
|
|
105
|
+
{{ label ?? value }}
|
|
106
|
+
</Button>
|
|
107
|
+
</RovingFocusItem>
|
|
108
|
+
|
|
109
|
+
<ul
|
|
110
|
+
v-if='expanded && item.children && item.children.length > 0'
|
|
111
|
+
:class='$style.list'
|
|
112
|
+
role='group'
|
|
113
|
+
>
|
|
114
|
+
<TreeItem
|
|
115
|
+
v-for='child in item.children'
|
|
116
|
+
:key='child.value'
|
|
117
|
+
:item='child'
|
|
118
|
+
:level='level + 1'
|
|
119
|
+
/>
|
|
120
|
+
</ul>
|
|
121
|
+
</li>
|
|
122
|
+
</template>
|
|
123
|
+
|
|
124
|
+
<style module lang="postcss">
|
|
125
|
+
.root {
|
|
126
|
+
display: grid;
|
|
127
|
+
gap: .25rem;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.button {
|
|
131
|
+
.inner {
|
|
132
|
+
display: grid;
|
|
133
|
+
grid-template-columns: auto 1fr auto;
|
|
134
|
+
|
|
135
|
+
color: var(--color-text);
|
|
136
|
+
|
|
137
|
+
.label {
|
|
138
|
+
font-size: var(--font-size-sm);
|
|
139
|
+
font-weight: 500;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
&[data-active],
|
|
144
|
+
&[data-selected] {
|
|
145
|
+
.inner {
|
|
146
|
+
color: var(--button-color);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
&[data-selected] {
|
|
151
|
+
background: alpha(var(--button-color), .1);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.icon {
|
|
156
|
+
width: var(--tree-icon-size);
|
|
157
|
+
height: var(--tree-icon-size);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.list {
|
|
161
|
+
display: grid;
|
|
162
|
+
gap: .25rem;
|
|
163
|
+
|
|
164
|
+
margin-inline-start: 1rem;
|
|
165
|
+
|
|
166
|
+
padding-inline-start: .75rem;
|
|
167
|
+
border-left: 1px solid var(--color-gray-4);
|
|
168
|
+
|
|
169
|
+
list-style: none;
|
|
170
|
+
}
|
|
171
|
+
</style>
|
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { NuanceSize } from '@nui/types';
|
|
2
|
+
import type { RovingFocusProps } from '../../roving-focus/roving-focus.vue.js';
|
|
3
|
+
import type { TreeIconResolver, TreeModels } from '../model.js';
|
|
4
|
+
export type TreeRootProps<T extends string = string> = RovingFocusProps & {
|
|
5
|
+
size?: NuanceSize | string;
|
|
6
|
+
iconResolver?: TreeIconResolver<T>;
|
|
7
|
+
removable?: boolean;
|
|
8
|
+
};
|
|
9
|
+
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<{
|
|
10
|
+
props: __VLS_PrettifyLocal<(RovingFocusProps & {
|
|
11
|
+
size?: NuanceSize | string;
|
|
12
|
+
iconResolver?: TreeIconResolver<T> | undefined;
|
|
13
|
+
removable?: boolean;
|
|
14
|
+
} & {
|
|
15
|
+
tree: TreeModels<T>["tree"];
|
|
16
|
+
active?: TreeModels<T>["active"];
|
|
17
|
+
selected?: TreeModels<T>["selected"];
|
|
18
|
+
expanded?: TreeModels<T>["expanded"];
|
|
19
|
+
}) & __VLS_EmitsToProps<__VLS_NormalizeEmits<((evt: "update:tree", value: import("..").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;
|
|
20
|
+
expose: (exposed: {}) => void;
|
|
21
|
+
attrs: any;
|
|
22
|
+
slots: {
|
|
23
|
+
default?: (props: {}) => any;
|
|
24
|
+
};
|
|
25
|
+
emit: ((evt: "update:tree", value: import("..").TreeItem<T>[]) => void) & ((evt: "update:active", value: T | null) => void) & ((evt: "update:selected", value: T[]) => void) & ((evt: "update:expanded", value: T[]) => void);
|
|
26
|
+
}>) => import("vue").VNode & {
|
|
27
|
+
__ctx?: Awaited<typeof __VLS_setup>;
|
|
28
|
+
};
|
|
29
|
+
declare const _default: typeof __VLS_export;
|
|
30
|
+
export default _default;
|
|
31
|
+
type __VLS_PrettifyLocal<T> = {
|
|
32
|
+
[K in keyof T as K]: T[K];
|
|
33
|
+
} & {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { useStyleResolver } from "@nui/composals";
|
|
3
|
+
import { getSize, removeTreeNodes } from "@nui/utils";
|
|
4
|
+
import { onClickOutside, useEventListener } from "@vueuse/core";
|
|
5
|
+
import { useTemplateRef } from "vue";
|
|
6
|
+
import Box from "../../box.vue";
|
|
7
|
+
import RovingFocus from "../../roving-focus/roving-focus.vue";
|
|
8
|
+
import { useProvideTreeState } from "../lib/context";
|
|
9
|
+
const {
|
|
10
|
+
size,
|
|
11
|
+
attr,
|
|
12
|
+
loop,
|
|
13
|
+
orientation,
|
|
14
|
+
iconResolver = () => ({ icon: "gravity-ui:file" }),
|
|
15
|
+
removable = false
|
|
16
|
+
} = defineProps({
|
|
17
|
+
loop: { type: Boolean, required: false },
|
|
18
|
+
orientation: { type: String, required: false },
|
|
19
|
+
attr: { type: String, required: false },
|
|
20
|
+
size: { type: String, required: false },
|
|
21
|
+
iconResolver: { type: Function, required: false },
|
|
22
|
+
removable: { type: Boolean, required: false }
|
|
23
|
+
});
|
|
24
|
+
const tree = defineModel("tree", { type: Array, ...{ required: true } });
|
|
25
|
+
const active = defineModel("active", { type: null, ...{ default: null } });
|
|
26
|
+
const selected = defineModel("selected", { type: Array, ...{ default: [] } });
|
|
27
|
+
const expanded = defineModel("expanded", { type: Array, ...{ default: [] } });
|
|
28
|
+
const style = useStyleResolver(() => ({
|
|
29
|
+
"--icon-size": getSize(size)
|
|
30
|
+
}));
|
|
31
|
+
const root = useTemplateRef("parent");
|
|
32
|
+
onClickOutside(root, () => selected.value = []);
|
|
33
|
+
useProvideTreeState({
|
|
34
|
+
tree,
|
|
35
|
+
active,
|
|
36
|
+
selected,
|
|
37
|
+
expanded,
|
|
38
|
+
iconResolver
|
|
39
|
+
});
|
|
40
|
+
if (removable) {
|
|
41
|
+
useEventListener(root, "keydown", (event) => {
|
|
42
|
+
if (event.key === "Delete") {
|
|
43
|
+
event.preventDefault();
|
|
44
|
+
return tree.value = removeTreeNodes(tree.value, selected.value);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<template>
|
|
51
|
+
<RovingFocus :attr :loop :orientation>
|
|
52
|
+
<Box
|
|
53
|
+
is='ul'
|
|
54
|
+
ref='parent'
|
|
55
|
+
role='tree'
|
|
56
|
+
:style
|
|
57
|
+
:class='$style.root'
|
|
58
|
+
@keydown.esc.prevent='selected.value = []'
|
|
59
|
+
>
|
|
60
|
+
<slot />
|
|
61
|
+
</Box>
|
|
62
|
+
</RovingFocus>
|
|
63
|
+
</template>
|
|
64
|
+
|
|
65
|
+
<style module lang="postcss">
|
|
66
|
+
.root {
|
|
67
|
+
--tree-icon-size: var(--spacing-md);
|
|
68
|
+
|
|
69
|
+
display: flex;
|
|
70
|
+
flex-direction: column;
|
|
71
|
+
gap: .25rem;
|
|
72
|
+
|
|
73
|
+
padding-inline-start: 0;
|
|
74
|
+
|
|
75
|
+
list-style: none;
|
|
76
|
+
}
|
|
77
|
+
</style>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { NuanceSize } from '@nui/types';
|
|
2
|
+
import type { RovingFocusProps } from '../../roving-focus/roving-focus.vue.js';
|
|
3
|
+
import type { TreeIconResolver, TreeModels } from '../model.js';
|
|
4
|
+
export type TreeRootProps<T extends string = string> = RovingFocusProps & {
|
|
5
|
+
size?: NuanceSize | string;
|
|
6
|
+
iconResolver?: TreeIconResolver<T>;
|
|
7
|
+
removable?: boolean;
|
|
8
|
+
};
|
|
9
|
+
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<{
|
|
10
|
+
props: __VLS_PrettifyLocal<(RovingFocusProps & {
|
|
11
|
+
size?: NuanceSize | string;
|
|
12
|
+
iconResolver?: TreeIconResolver<T> | undefined;
|
|
13
|
+
removable?: boolean;
|
|
14
|
+
} & {
|
|
15
|
+
tree: TreeModels<T>["tree"];
|
|
16
|
+
active?: TreeModels<T>["active"];
|
|
17
|
+
selected?: TreeModels<T>["selected"];
|
|
18
|
+
expanded?: TreeModels<T>["expanded"];
|
|
19
|
+
}) & __VLS_EmitsToProps<__VLS_NormalizeEmits<((evt: "update:tree", value: import("..").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;
|
|
20
|
+
expose: (exposed: {}) => void;
|
|
21
|
+
attrs: any;
|
|
22
|
+
slots: {
|
|
23
|
+
default?: (props: {}) => any;
|
|
24
|
+
};
|
|
25
|
+
emit: ((evt: "update:tree", value: import("..").TreeItem<T>[]) => void) & ((evt: "update:active", value: T | null) => void) & ((evt: "update:selected", value: T[]) => void) & ((evt: "update:expanded", value: T[]) => void);
|
|
26
|
+
}>) => import("vue").VNode & {
|
|
27
|
+
__ctx?: Awaited<typeof __VLS_setup>;
|
|
28
|
+
};
|
|
29
|
+
declare const _default: typeof __VLS_export;
|
|
30
|
+
export default _default;
|
|
31
|
+
type __VLS_PrettifyLocal<T> = {
|
|
32
|
+
[K in keyof T as K]: T[K];
|
|
33
|
+
} & {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './model.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./model.js";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { TreeIconResolver, TreeItem } from '@nui/components';
|
|
2
|
+
import type { ModelRef } from 'vue';
|
|
3
|
+
type EventType = 'select' | 'expand';
|
|
4
|
+
type SelectMode = 'single' | 'multiple' | 'range';
|
|
5
|
+
export interface TreeContext<T extends string = string> {
|
|
6
|
+
tree: ModelRef<TreeItem<T>[]>;
|
|
7
|
+
active: ModelRef<T | null>;
|
|
8
|
+
selected: ModelRef<T[]>;
|
|
9
|
+
expanded: ModelRef<T[]>;
|
|
10
|
+
iconResolver: TreeIconResolver<T>;
|
|
11
|
+
}
|
|
12
|
+
export interface TreeState<T extends string = string> {
|
|
13
|
+
active: ModelRef<T | null>;
|
|
14
|
+
selected: ModelRef<T[]>;
|
|
15
|
+
expanded: ModelRef<T[]>;
|
|
16
|
+
iconResolver: TreeIconResolver<T>;
|
|
17
|
+
toggle: (type: EventType, value: T, mode?: SelectMode) => void;
|
|
18
|
+
on: ((type: 'expand', value: T) => void) & ((type: 'select', value: T, mode?: SelectMode) => void);
|
|
19
|
+
off: (type: EventType, value: T) => void;
|
|
20
|
+
setActive: (value: T | null) => void;
|
|
21
|
+
}
|
|
22
|
+
export declare function useProvideTreeState<T extends string = string>(ctx: TreeContext<T>): TreeState<T>;
|
|
23
|
+
export declare function useTreeState<T extends string = string>(): TreeState<T>;
|
|
24
|
+
export {};
|