vue-composable-ui 0.0.1 → 1.0.1
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/LICENSE +1 -1
- package/README.md +5 -1
- package/dist/composable-ui.js +1114 -0
- package/dist/composable-ui.umd.cjs +1 -0
- package/dist/types/components/combobox/comboboxHiddenInput.vue.d.ts +8 -0
- package/dist/types/components/combobox/comboboxInput.vue.d.ts +9 -0
- package/dist/types/components/combobox/comboboxOption.vue.d.ts +22 -0
- package/dist/types/components/combobox/comboboxOptions.vue.d.ts +11 -0
- package/dist/types/components/injectionKeys.d.ts +43 -0
- package/dist/types/components/listbox/listbox.vue.d.ts +26 -0
- package/dist/types/components/listbox/listboxOption.vue.d.ts +22 -0
- package/dist/types/components/menu/menuItem.vue.d.ts +26 -0
- package/dist/types/components/menu/menuItems.vue.d.ts +11 -0
- package/dist/types/components/popover/popoverDialog.vue.d.ts +11 -0
- package/dist/types/components/tabs/tab.vue.d.ts +19 -0
- package/dist/types/components/tabs/tabContainer.vue.d.ts +18 -0
- package/dist/types/components/tabs/tabList.vue.d.ts +17 -0
- package/dist/types/components/tabs/tabPanel.vue.d.ts +15 -0
- package/dist/types/components/tooltip.vue.d.ts +36 -0
- package/dist/types/composables/formControl.d.ts +48 -0
- package/dist/types/composables/list.d.ts +103 -0
- package/dist/types/composables/listOption.d.ts +30 -0
- package/dist/types/composables/popover.d.ts +68 -0
- package/{src/main.ts → dist/types/main.d.ts} +3 -44
- package/dist/types/utils/element.d.ts +3 -0
- package/dist/types/utils/id.d.ts +1 -0
- package/package.json +19 -6
- package/.github/workflows/deploy.yml +0 -21
- package/.github/workflows/docs.yml +0 -62
- package/.vscode/extensions.json +0 -7
- package/docs/.vitepress/config.mts +0 -40
- package/docs/.vitepress/theme/index.js +0 -4
- package/docs/.vitepress/theme/style.scss +0 -155
- package/docs/components/combobox.md +0 -174
- package/docs/components/demos/combobox.vue +0 -142
- package/docs/components/demos/combobox_autocomplete.vue +0 -75
- package/docs/components/demos/combobox_tags.vue +0 -127
- package/docs/components/demos/input.vue +0 -35
- package/docs/components/demos/listbox.vue +0 -64
- package/docs/components/demos/menu.vue +0 -93
- package/docs/components/demos/popover.vue +0 -16
- package/docs/components/demos/tabs.vue +0 -59
- package/docs/components/demos/tooltip.vue +0 -27
- package/docs/components/listbox.md +0 -9
- package/docs/components/menu.md +0 -121
- package/docs/components/popover.md +0 -82
- package/docs/components/tabs.md +0 -9
- package/docs/components/tooltip.md +0 -78
- package/docs/composables/form-control.md +0 -130
- package/docs/composables/list-option.md +0 -47
- package/docs/composables/list.md +0 -192
- package/docs/composables/popover.md +0 -90
- package/docs/index.md +0 -24
- package/docs/intro.md +0 -0
- package/src/components/combobox/combobox.vue +0 -229
- package/src/components/combobox/comboboxFormInput.vue +0 -33
- package/src/components/combobox/comboboxOption.vue +0 -52
- package/src/components/combobox/comboboxOptions.vue +0 -17
- package/src/components/injectionKeys.ts +0 -50
- package/src/components/listbox/listbox.vue +0 -91
- package/src/components/listbox/listboxOption.vue +0 -37
- package/src/components/menu/menu.vue +0 -100
- package/src/components/menu/menuItem.vue +0 -86
- package/src/components/menu/menuItems.vue +0 -51
- package/src/components/popover/popover.vue +0 -68
- package/src/components/popover/popoverDialog.vue +0 -35
- package/src/components/tabs/tab.vue +0 -35
- package/src/components/tabs/tabContainer.vue +0 -50
- package/src/components/tabs/tabList.vue +0 -52
- package/src/components/tabs/tabPanel.vue +0 -36
- package/src/components/tooltip.vue +0 -115
- package/src/composables/formControl.ts +0 -144
- package/src/composables/list.ts +0 -326
- package/src/composables/listOption.ts +0 -80
- package/src/composables/popover.ts +0 -306
- package/src/utils/element.ts +0 -19
- package/src/utils/id.ts +0 -5
- package/src/vite-env.d.ts +0 -1
- package/tsconfig.json +0 -27
- package/tsconfig.node.json +0 -10
- package/vite.config.ts +0 -28
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<slot :attrs="activatorAttrs"></slot>
|
|
3
|
-
</template>
|
|
4
|
-
|
|
5
|
-
<script setup lang="ts">
|
|
6
|
-
import { computed, provide, nextTick } from "vue";
|
|
7
|
-
import { useSequentialId } from "../../utils/id";
|
|
8
|
-
import { usePopover } from "../../composables/popover";
|
|
9
|
-
import { useList } from "../../composables/list";
|
|
10
|
-
import { injectMenu } from "../injectionKeys";
|
|
11
|
-
|
|
12
|
-
const props = withDefaults(
|
|
13
|
-
defineProps<{
|
|
14
|
-
id?: string;
|
|
15
|
-
activatorId?: string;
|
|
16
|
-
direction?: "up" | "down" | "left" | "right";
|
|
17
|
-
align?: "top" | "bottom" | "left" | "right" | "center" | "stretch";
|
|
18
|
-
position?: "fixed" | "absolute";
|
|
19
|
-
}>(),
|
|
20
|
-
{
|
|
21
|
-
direction: "down",
|
|
22
|
-
align: "left",
|
|
23
|
-
position: "fixed",
|
|
24
|
-
}
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
const menuId = props.id ?? useSequentialId("menu");
|
|
28
|
-
const activatorId = props.activatorId ?? useSequentialId("menu-activator");
|
|
29
|
-
|
|
30
|
-
const model = defineModel();
|
|
31
|
-
|
|
32
|
-
const {
|
|
33
|
-
isMultiselectable,
|
|
34
|
-
activeItem,
|
|
35
|
-
activateFirstItem,
|
|
36
|
-
activateLastItem,
|
|
37
|
-
activateNextItem,
|
|
38
|
-
activatePrevItem,
|
|
39
|
-
} = useList(model);
|
|
40
|
-
|
|
41
|
-
const { isOpen, toggle, open, close } = usePopover(
|
|
42
|
-
`#${activatorId}`,
|
|
43
|
-
`#${menuId}`,
|
|
44
|
-
{
|
|
45
|
-
direction: props.direction,
|
|
46
|
-
align: props.align,
|
|
47
|
-
position: props.position,
|
|
48
|
-
closeOnOutsideClick: true,
|
|
49
|
-
closeOnInsideClick: false,
|
|
50
|
-
}
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
function onActivatorKeyDown(evt: KeyboardEvent) {
|
|
54
|
-
switch (evt.code) {
|
|
55
|
-
case "Space":
|
|
56
|
-
case "Enter":
|
|
57
|
-
toggle();
|
|
58
|
-
if (isOpen.value) {
|
|
59
|
-
nextTick(activateFirstItem);
|
|
60
|
-
}
|
|
61
|
-
break;
|
|
62
|
-
case "ArrowUp":
|
|
63
|
-
open();
|
|
64
|
-
nextTick(activateLastItem);
|
|
65
|
-
break;
|
|
66
|
-
case "ArrowDown":
|
|
67
|
-
open();
|
|
68
|
-
nextTick(activateFirstItem);
|
|
69
|
-
break;
|
|
70
|
-
default:
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
evt.preventDefault();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const activatorAttrs = computed(() => ({
|
|
77
|
-
id: activatorId,
|
|
78
|
-
"aria-controls": isOpen.value ? menuId : undefined,
|
|
79
|
-
"aria-haspopup": "menu" as "menu",
|
|
80
|
-
"aria-expanded": isOpen.value,
|
|
81
|
-
onclick: toggle,
|
|
82
|
-
onkeydown: onActivatorKeyDown,
|
|
83
|
-
tabindex: 0,
|
|
84
|
-
}));
|
|
85
|
-
|
|
86
|
-
provide(injectMenu, {
|
|
87
|
-
menuId: menuId,
|
|
88
|
-
activatorId: activatorId,
|
|
89
|
-
isOpen,
|
|
90
|
-
close,
|
|
91
|
-
isMultiselectable,
|
|
92
|
-
activeItemId: computed(() => activeItem.value?.element.value?.id),
|
|
93
|
-
activateFirstItem,
|
|
94
|
-
activateLastItem,
|
|
95
|
-
activateNextItem,
|
|
96
|
-
activatePrevItem,
|
|
97
|
-
});
|
|
98
|
-
</script>
|
|
99
|
-
|
|
100
|
-
<style scoped></style>
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div
|
|
3
|
-
ref="itemEl"
|
|
4
|
-
:id="itemId"
|
|
5
|
-
@click="onClick"
|
|
6
|
-
@keydown="onKeyDown"
|
|
7
|
-
@mouseenter="activate({ focus: false })"
|
|
8
|
-
:data-active="isActive"
|
|
9
|
-
:role="role"
|
|
10
|
-
:aria-checked="value !== undefined ? isSelected : undefined"
|
|
11
|
-
:aria-disabled="disabled || undefined"
|
|
12
|
-
tabindex="-1"
|
|
13
|
-
>
|
|
14
|
-
<slot :isSelected="isSelected" :isActive="isActive" />
|
|
15
|
-
</div>
|
|
16
|
-
</template>
|
|
17
|
-
|
|
18
|
-
<script setup lang="ts">
|
|
19
|
-
import { ref, inject, computed } from "vue";
|
|
20
|
-
import { useSequentialId } from "../../utils/id";
|
|
21
|
-
import { useListOption } from "../../composables/listOption";
|
|
22
|
-
import { injectMenu } from "../injectionKeys";
|
|
23
|
-
|
|
24
|
-
const props = defineProps<{
|
|
25
|
-
id?: string;
|
|
26
|
-
value?: any;
|
|
27
|
-
disabled?: boolean;
|
|
28
|
-
}>();
|
|
29
|
-
|
|
30
|
-
const emit = defineEmits<{
|
|
31
|
-
select: [value: any];
|
|
32
|
-
}>();
|
|
33
|
-
|
|
34
|
-
const itemId = props.id ?? useSequentialId("menu-item");
|
|
35
|
-
|
|
36
|
-
const itemEl = ref<HTMLElement>();
|
|
37
|
-
const { isSelected, isActive, select, activate } = useListOption(
|
|
38
|
-
itemEl,
|
|
39
|
-
props.value,
|
|
40
|
-
props.disabled
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
const menu = inject(injectMenu);
|
|
44
|
-
if (!menu) {
|
|
45
|
-
throw new Error("Menu injectable context is not provided");
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const role = computed(() => {
|
|
49
|
-
if (props.value !== undefined) {
|
|
50
|
-
return menu.isMultiselectable.value ? "menuitemcheckbox" : "menuitemradio";
|
|
51
|
-
}
|
|
52
|
-
return "menuitem";
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
function selectAndEmit() {
|
|
56
|
-
select();
|
|
57
|
-
emit("select", props.value);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function onClick() {
|
|
61
|
-
selectAndEmit();
|
|
62
|
-
if (props.value === undefined || !menu?.isMultiselectable.value) {
|
|
63
|
-
menu!.close();
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function onKeyDown(evt: KeyboardEvent) {
|
|
68
|
-
switch (evt.code) {
|
|
69
|
-
case "Space":
|
|
70
|
-
selectAndEmit();
|
|
71
|
-
if (props.value === undefined) {
|
|
72
|
-
menu!.close();
|
|
73
|
-
}
|
|
74
|
-
break;
|
|
75
|
-
case "Enter":
|
|
76
|
-
selectAndEmit();
|
|
77
|
-
menu!.close();
|
|
78
|
-
break;
|
|
79
|
-
default:
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
evt.preventDefault();
|
|
83
|
-
}
|
|
84
|
-
</script>
|
|
85
|
-
|
|
86
|
-
<style scoped></style>
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div
|
|
3
|
-
v-if="menu!.isOpen.value"
|
|
4
|
-
@keydown="onKeyDown"
|
|
5
|
-
:id="menu!.menuId"
|
|
6
|
-
:aria-activedescendant="menu!.activeItemId.value"
|
|
7
|
-
:aria-labelledby="menu!.activatorId"
|
|
8
|
-
role="menu"
|
|
9
|
-
tabindex="-1"
|
|
10
|
-
>
|
|
11
|
-
<slot />
|
|
12
|
-
</div>
|
|
13
|
-
</template>
|
|
14
|
-
|
|
15
|
-
<script setup lang="ts">
|
|
16
|
-
import { inject } from "vue";
|
|
17
|
-
import { injectMenu } from "../injectionKeys";
|
|
18
|
-
|
|
19
|
-
const menu = inject(injectMenu);
|
|
20
|
-
if (!menu) {
|
|
21
|
-
throw new Error("Menu injectable context is not provided");
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function onKeyDown(evt: KeyboardEvent) {
|
|
25
|
-
switch (evt.code) {
|
|
26
|
-
case "ArrowUp":
|
|
27
|
-
menu!.activatePrevItem({ focus: true, loop: false });
|
|
28
|
-
break;
|
|
29
|
-
case "ArrowDown":
|
|
30
|
-
menu!.activateNextItem({ focus: true, loop: false });
|
|
31
|
-
break;
|
|
32
|
-
case "Home":
|
|
33
|
-
menu!.activateFirstItem({ focus: true });
|
|
34
|
-
break;
|
|
35
|
-
case "End":
|
|
36
|
-
menu!.activateLastItem({ focus: true });
|
|
37
|
-
break;
|
|
38
|
-
case "Escape":
|
|
39
|
-
menu!.close();
|
|
40
|
-
break;
|
|
41
|
-
case "Tab":
|
|
42
|
-
menu!.isOpen.value = false;
|
|
43
|
-
return;
|
|
44
|
-
default:
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
evt.preventDefault();
|
|
48
|
-
}
|
|
49
|
-
</script>
|
|
50
|
-
|
|
51
|
-
<style scoped></style>
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<slot
|
|
3
|
-
:attrs="activatorAttrs"
|
|
4
|
-
:isOpen="isOpen"
|
|
5
|
-
:toggle="toggle"
|
|
6
|
-
:open="open"
|
|
7
|
-
:close="close"
|
|
8
|
-
></slot>
|
|
9
|
-
</template>
|
|
10
|
-
|
|
11
|
-
<script setup lang="ts">
|
|
12
|
-
import { provide, computed } from "vue";
|
|
13
|
-
import { useSequentialId } from "../../utils/id";
|
|
14
|
-
import { usePopover } from "../../composables/popover";
|
|
15
|
-
import { injectPopover } from "../injectionKeys";
|
|
16
|
-
|
|
17
|
-
const props = withDefaults(
|
|
18
|
-
defineProps<{
|
|
19
|
-
id?: string;
|
|
20
|
-
activatorId?: string;
|
|
21
|
-
direction?: "up" | "down" | "left" | "right";
|
|
22
|
-
align?: "top" | "bottom" | "left" | "right" | "center" | "stretch";
|
|
23
|
-
position?: "fixed" | "absolute";
|
|
24
|
-
closeOnOutsideClick?: boolean;
|
|
25
|
-
closeOnInsideClick?: boolean;
|
|
26
|
-
role?: "dialog" | "listbox" | "menu" | "tree" | "grid";
|
|
27
|
-
}>(),
|
|
28
|
-
{
|
|
29
|
-
id: useSequentialId("popover"),
|
|
30
|
-
activatorId: useSequentialId("popover-activator"),
|
|
31
|
-
direction: "down",
|
|
32
|
-
align: "left",
|
|
33
|
-
position: "fixed",
|
|
34
|
-
closeOnOutsideClick: true,
|
|
35
|
-
closeOnInsideClick: false,
|
|
36
|
-
role: "dialog",
|
|
37
|
-
}
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
const { isOpen, toggle, open, close } = usePopover(
|
|
41
|
-
`#${props.activatorId}`,
|
|
42
|
-
`#${props.id}`,
|
|
43
|
-
{
|
|
44
|
-
direction: props.direction,
|
|
45
|
-
align: props.align,
|
|
46
|
-
position: props.position,
|
|
47
|
-
closeOnOutsideClick: props.closeOnOutsideClick,
|
|
48
|
-
closeOnInsideClick: props.closeOnInsideClick,
|
|
49
|
-
}
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
const activatorAttrs = computed(() => ({
|
|
53
|
-
id: props.activatorId,
|
|
54
|
-
"aria-controls": isOpen.value ? props.id : undefined,
|
|
55
|
-
"aria-haspopup": props.role,
|
|
56
|
-
"aria-expanded": isOpen.value,
|
|
57
|
-
}));
|
|
58
|
-
|
|
59
|
-
provide(injectPopover, {
|
|
60
|
-
popoverId: props.id,
|
|
61
|
-
activatorId: props.activatorId,
|
|
62
|
-
role: props.role,
|
|
63
|
-
isOpen,
|
|
64
|
-
close,
|
|
65
|
-
});
|
|
66
|
-
</script>
|
|
67
|
-
|
|
68
|
-
<style scoped></style>
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div
|
|
3
|
-
v-if="popover!.isOpen.value"
|
|
4
|
-
@keydown="onKeyDown"
|
|
5
|
-
:id="popover!.popoverId"
|
|
6
|
-
:role="popover!.role"
|
|
7
|
-
:aria-labelledby="popover!.activatorId"
|
|
8
|
-
tabindex="-1"
|
|
9
|
-
>
|
|
10
|
-
<slot />
|
|
11
|
-
</div>
|
|
12
|
-
</template>
|
|
13
|
-
|
|
14
|
-
<script setup lang="ts">
|
|
15
|
-
import { inject } from "vue";
|
|
16
|
-
import { injectPopover } from "../injectionKeys";
|
|
17
|
-
|
|
18
|
-
const popover = inject(injectPopover);
|
|
19
|
-
if (!popover) {
|
|
20
|
-
throw new Error("Popover injectable context is not provided");
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function onKeyDown(evt: KeyboardEvent) {
|
|
24
|
-
switch (evt.code) {
|
|
25
|
-
case "Escape":
|
|
26
|
-
popover!.close();
|
|
27
|
-
break;
|
|
28
|
-
default:
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
evt.preventDefault();
|
|
32
|
-
}
|
|
33
|
-
</script>
|
|
34
|
-
|
|
35
|
-
<style scoped></style>
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<button
|
|
3
|
-
ref="tab"
|
|
4
|
-
@click="select"
|
|
5
|
-
:v="value"
|
|
6
|
-
:id="tabId"
|
|
7
|
-
:aria-selected="isSelected"
|
|
8
|
-
:aria-controls="tabPanelId"
|
|
9
|
-
:tabindex="isSelected ? 0 : -1"
|
|
10
|
-
role="tab"
|
|
11
|
-
>
|
|
12
|
-
<slot />
|
|
13
|
-
</button>
|
|
14
|
-
</template>
|
|
15
|
-
|
|
16
|
-
<script setup lang="ts">
|
|
17
|
-
import { ref } from "vue";
|
|
18
|
-
import { useSequentialId } from "../../utils/id";
|
|
19
|
-
import { useListOption } from "../../composables/listOption";
|
|
20
|
-
|
|
21
|
-
const props = defineProps<{
|
|
22
|
-
id?: string;
|
|
23
|
-
ariaControls?: string;
|
|
24
|
-
value: any;
|
|
25
|
-
}>();
|
|
26
|
-
|
|
27
|
-
const tab = ref<HTMLButtonElement>();
|
|
28
|
-
|
|
29
|
-
const { isSelected, select } = useListOption(tab, props.value);
|
|
30
|
-
|
|
31
|
-
const tabId = props.id ?? useSequentialId("tab");
|
|
32
|
-
const tabPanelId = props.ariaControls ?? useSequentialId("tab-panel");
|
|
33
|
-
</script>
|
|
34
|
-
|
|
35
|
-
<style scoped></style>
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div>
|
|
3
|
-
<slot />
|
|
4
|
-
</div>
|
|
5
|
-
</template>
|
|
6
|
-
|
|
7
|
-
<script setup lang="ts">
|
|
8
|
-
import { provide, computed } from "vue";
|
|
9
|
-
import { useList } from "../../composables/list";
|
|
10
|
-
import { injectTabs } from "../injectionKeys";
|
|
11
|
-
|
|
12
|
-
const props = withDefaults(
|
|
13
|
-
defineProps<{
|
|
14
|
-
orientation?: "horizontal" | "vertical";
|
|
15
|
-
}>(),
|
|
16
|
-
{
|
|
17
|
-
orientation: "horizontal",
|
|
18
|
-
}
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
const model = defineModel();
|
|
22
|
-
|
|
23
|
-
const {
|
|
24
|
-
items,
|
|
25
|
-
selectActiveItem,
|
|
26
|
-
activateFirstItem,
|
|
27
|
-
activateLastItem,
|
|
28
|
-
activateNextItem,
|
|
29
|
-
activatePrevItem,
|
|
30
|
-
} = useList(model);
|
|
31
|
-
|
|
32
|
-
provide(injectTabs, {
|
|
33
|
-
getIds: (value: any) =>
|
|
34
|
-
computed(() => {
|
|
35
|
-
const item = items.value.find((item) => item.value === value);
|
|
36
|
-
return {
|
|
37
|
-
tabId: item?.element.value?.getAttribute("id") || undefined,
|
|
38
|
-
panelId: item?.element.value?.getAttribute("aria-controls") || undefined,
|
|
39
|
-
};
|
|
40
|
-
}),
|
|
41
|
-
selectActiveItem,
|
|
42
|
-
activateFirstItem,
|
|
43
|
-
activateLastItem,
|
|
44
|
-
activateNextItem,
|
|
45
|
-
activatePrevItem,
|
|
46
|
-
orientation: props.orientation,
|
|
47
|
-
});
|
|
48
|
-
</script>
|
|
49
|
-
|
|
50
|
-
<style scoped></style>
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div @keydown="onKeyDown" :aria-orientation="tabs!.orientation" role="tablist">
|
|
3
|
-
<slot />
|
|
4
|
-
</div>
|
|
5
|
-
</template>
|
|
6
|
-
|
|
7
|
-
<script setup lang="ts">
|
|
8
|
-
import { inject } from "vue";
|
|
9
|
-
import { injectTabs } from "../injectionKeys";
|
|
10
|
-
|
|
11
|
-
const tabs = inject(injectTabs);
|
|
12
|
-
if (!tabs) {
|
|
13
|
-
throw new Error("Tabs injectable context is not provided");
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function onKeyDown(evt: KeyboardEvent) {
|
|
17
|
-
switch (evt.code) {
|
|
18
|
-
case "ArrowUp":
|
|
19
|
-
if (tabs!.orientation === "vertical") {
|
|
20
|
-
tabs!.activatePrevItem();
|
|
21
|
-
}
|
|
22
|
-
break;
|
|
23
|
-
case "ArrowDown":
|
|
24
|
-
if (tabs!.orientation === "vertical") {
|
|
25
|
-
tabs!.activateNextItem();
|
|
26
|
-
}
|
|
27
|
-
break;
|
|
28
|
-
case "ArrowLeft":
|
|
29
|
-
if (tabs!.orientation === "horizontal") {
|
|
30
|
-
tabs!.activatePrevItem();
|
|
31
|
-
}
|
|
32
|
-
break;
|
|
33
|
-
case "ArrowRight":
|
|
34
|
-
if (tabs!.orientation === "horizontal") {
|
|
35
|
-
tabs!.activateNextItem();
|
|
36
|
-
}
|
|
37
|
-
break;
|
|
38
|
-
case "Home":
|
|
39
|
-
tabs!.activateFirstItem();
|
|
40
|
-
break;
|
|
41
|
-
case "End":
|
|
42
|
-
tabs!.activateLastItem();
|
|
43
|
-
break;
|
|
44
|
-
default:
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
tabs!.selectActiveItem();
|
|
48
|
-
evt.preventDefault();
|
|
49
|
-
}
|
|
50
|
-
</script>
|
|
51
|
-
|
|
52
|
-
<style scoped></style>
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div
|
|
3
|
-
v-if="isSelected"
|
|
4
|
-
:id="ids.panelId"
|
|
5
|
-
:aria-labelledby="ids.tabId"
|
|
6
|
-
tabindex="0"
|
|
7
|
-
role="tabpanel"
|
|
8
|
-
>
|
|
9
|
-
<slot />
|
|
10
|
-
</div>
|
|
11
|
-
</template>
|
|
12
|
-
|
|
13
|
-
<script setup lang="ts">
|
|
14
|
-
import { inject, computed } from "vue";
|
|
15
|
-
import { injectList } from "../../composables/list";
|
|
16
|
-
import { injectTabs } from "../injectionKeys";
|
|
17
|
-
|
|
18
|
-
const props = defineProps<{
|
|
19
|
-
value: string | number;
|
|
20
|
-
}>();
|
|
21
|
-
|
|
22
|
-
const list = inject(injectList);
|
|
23
|
-
if (!list) {
|
|
24
|
-
throw new Error("List injectable context is not provided");
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const tabs = inject(injectTabs);
|
|
28
|
-
if (!tabs) {
|
|
29
|
-
throw new Error("Tabs injectable context is not provided");
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const isSelected = computed(() => props.value === list.value.value);
|
|
33
|
-
const ids = tabs.getIds(props.value);
|
|
34
|
-
</script>
|
|
35
|
-
|
|
36
|
-
<style scoped></style>
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<slot :attrs="activatorAttrs" />
|
|
3
|
-
|
|
4
|
-
<Teleport to="body">
|
|
5
|
-
<div
|
|
6
|
-
ref="popupEl"
|
|
7
|
-
v-if="isOpen"
|
|
8
|
-
:id="tooltipId"
|
|
9
|
-
v-bind="$attrs"
|
|
10
|
-
role="tooltip"
|
|
11
|
-
>
|
|
12
|
-
<slot name="content">{{ text }}</slot>
|
|
13
|
-
</div>
|
|
14
|
-
</Teleport>
|
|
15
|
-
</template>
|
|
16
|
-
|
|
17
|
-
<script setup lang="ts">
|
|
18
|
-
import { onBeforeUnmount, onMounted, ref, useSlots } from "vue";
|
|
19
|
-
import { useSequentialId } from "../utils/id";
|
|
20
|
-
import { usePopover } from "../composables/popover";
|
|
21
|
-
|
|
22
|
-
defineOptions({
|
|
23
|
-
inheritAttrs: false,
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
const props = withDefaults(
|
|
27
|
-
defineProps<{
|
|
28
|
-
id?: string;
|
|
29
|
-
text?: string;
|
|
30
|
-
delay?: string | number;
|
|
31
|
-
direction?: "up" | "down" | "left" | "right";
|
|
32
|
-
}>(),
|
|
33
|
-
{
|
|
34
|
-
direction: "up",
|
|
35
|
-
}
|
|
36
|
-
);
|
|
37
|
-
const slots = useSlots();
|
|
38
|
-
|
|
39
|
-
const popupEl = ref();
|
|
40
|
-
let timer: NodeJS.Timeout;
|
|
41
|
-
|
|
42
|
-
const tooltipId = props.id ?? useSequentialId("tooltip");
|
|
43
|
-
|
|
44
|
-
const { isOpen } = usePopover(`[aria-describedby=${tooltipId}]`, popupEl, {
|
|
45
|
-
direction: props.direction,
|
|
46
|
-
align: "center",
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
function open() {
|
|
50
|
-
timer = setTimeout(
|
|
51
|
-
() => {
|
|
52
|
-
isOpen.value = true;
|
|
53
|
-
},
|
|
54
|
-
typeof props.delay === "string" ? parseInt(props.delay) : props.delay
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function close() {
|
|
59
|
-
isOpen.value = false;
|
|
60
|
-
clearTimeout(timer);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function onKeyDown(evt: KeyboardEvent) {
|
|
64
|
-
if (evt.key === "Escape") {
|
|
65
|
-
close();
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const activatorAttrs = {
|
|
70
|
-
"aria-describedby": tooltipId,
|
|
71
|
-
onmouseenter: open,
|
|
72
|
-
onmouseleave: close,
|
|
73
|
-
onfocus: open,
|
|
74
|
-
onblur: close,
|
|
75
|
-
onkeydown: onKeyDown,
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const controller = new AbortController();
|
|
79
|
-
|
|
80
|
-
onMounted(() => {
|
|
81
|
-
if (slots.default) return;
|
|
82
|
-
const activator = document.querySelector(
|
|
83
|
-
`[aria-describedby=${tooltipId}]`
|
|
84
|
-
) as HTMLElement;
|
|
85
|
-
if (!activator) {
|
|
86
|
-
throw new Error(`Tooltip activator not found: ${tooltipId}`);
|
|
87
|
-
}
|
|
88
|
-
activator.addEventListener("mouseenter", open, {
|
|
89
|
-
signal: controller.signal,
|
|
90
|
-
passive: true,
|
|
91
|
-
});
|
|
92
|
-
activator.addEventListener("mouseleave", close, {
|
|
93
|
-
signal: controller.signal,
|
|
94
|
-
passive: true,
|
|
95
|
-
});
|
|
96
|
-
activator.addEventListener("focus", open, {
|
|
97
|
-
signal: controller.signal,
|
|
98
|
-
passive: true,
|
|
99
|
-
});
|
|
100
|
-
activator.addEventListener("blur", close, {
|
|
101
|
-
signal: controller.signal,
|
|
102
|
-
passive: true,
|
|
103
|
-
});
|
|
104
|
-
activator.addEventListener("keydown", onKeyDown, {
|
|
105
|
-
signal: controller.signal,
|
|
106
|
-
passive: true,
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
onBeforeUnmount(() => {
|
|
111
|
-
controller.abort();
|
|
112
|
-
});
|
|
113
|
-
</script>
|
|
114
|
-
|
|
115
|
-
<style scoped></style>
|