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.
Files changed (81) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +5 -1
  3. package/dist/composable-ui.js +1114 -0
  4. package/dist/composable-ui.umd.cjs +1 -0
  5. package/dist/types/components/combobox/comboboxHiddenInput.vue.d.ts +8 -0
  6. package/dist/types/components/combobox/comboboxInput.vue.d.ts +9 -0
  7. package/dist/types/components/combobox/comboboxOption.vue.d.ts +22 -0
  8. package/dist/types/components/combobox/comboboxOptions.vue.d.ts +11 -0
  9. package/dist/types/components/injectionKeys.d.ts +43 -0
  10. package/dist/types/components/listbox/listbox.vue.d.ts +26 -0
  11. package/dist/types/components/listbox/listboxOption.vue.d.ts +22 -0
  12. package/dist/types/components/menu/menuItem.vue.d.ts +26 -0
  13. package/dist/types/components/menu/menuItems.vue.d.ts +11 -0
  14. package/dist/types/components/popover/popoverDialog.vue.d.ts +11 -0
  15. package/dist/types/components/tabs/tab.vue.d.ts +19 -0
  16. package/dist/types/components/tabs/tabContainer.vue.d.ts +18 -0
  17. package/dist/types/components/tabs/tabList.vue.d.ts +17 -0
  18. package/dist/types/components/tabs/tabPanel.vue.d.ts +15 -0
  19. package/dist/types/components/tooltip.vue.d.ts +36 -0
  20. package/dist/types/composables/formControl.d.ts +48 -0
  21. package/dist/types/composables/list.d.ts +103 -0
  22. package/dist/types/composables/listOption.d.ts +30 -0
  23. package/dist/types/composables/popover.d.ts +68 -0
  24. package/{src/main.ts → dist/types/main.d.ts} +3 -44
  25. package/dist/types/utils/element.d.ts +3 -0
  26. package/dist/types/utils/id.d.ts +1 -0
  27. package/package.json +19 -6
  28. package/.github/workflows/deploy.yml +0 -21
  29. package/.github/workflows/docs.yml +0 -62
  30. package/.vscode/extensions.json +0 -7
  31. package/docs/.vitepress/config.mts +0 -40
  32. package/docs/.vitepress/theme/index.js +0 -4
  33. package/docs/.vitepress/theme/style.scss +0 -155
  34. package/docs/components/combobox.md +0 -174
  35. package/docs/components/demos/combobox.vue +0 -142
  36. package/docs/components/demos/combobox_autocomplete.vue +0 -75
  37. package/docs/components/demos/combobox_tags.vue +0 -127
  38. package/docs/components/demos/input.vue +0 -35
  39. package/docs/components/demos/listbox.vue +0 -64
  40. package/docs/components/demos/menu.vue +0 -93
  41. package/docs/components/demos/popover.vue +0 -16
  42. package/docs/components/demos/tabs.vue +0 -59
  43. package/docs/components/demos/tooltip.vue +0 -27
  44. package/docs/components/listbox.md +0 -9
  45. package/docs/components/menu.md +0 -121
  46. package/docs/components/popover.md +0 -82
  47. package/docs/components/tabs.md +0 -9
  48. package/docs/components/tooltip.md +0 -78
  49. package/docs/composables/form-control.md +0 -130
  50. package/docs/composables/list-option.md +0 -47
  51. package/docs/composables/list.md +0 -192
  52. package/docs/composables/popover.md +0 -90
  53. package/docs/index.md +0 -24
  54. package/docs/intro.md +0 -0
  55. package/src/components/combobox/combobox.vue +0 -229
  56. package/src/components/combobox/comboboxFormInput.vue +0 -33
  57. package/src/components/combobox/comboboxOption.vue +0 -52
  58. package/src/components/combobox/comboboxOptions.vue +0 -17
  59. package/src/components/injectionKeys.ts +0 -50
  60. package/src/components/listbox/listbox.vue +0 -91
  61. package/src/components/listbox/listboxOption.vue +0 -37
  62. package/src/components/menu/menu.vue +0 -100
  63. package/src/components/menu/menuItem.vue +0 -86
  64. package/src/components/menu/menuItems.vue +0 -51
  65. package/src/components/popover/popover.vue +0 -68
  66. package/src/components/popover/popoverDialog.vue +0 -35
  67. package/src/components/tabs/tab.vue +0 -35
  68. package/src/components/tabs/tabContainer.vue +0 -50
  69. package/src/components/tabs/tabList.vue +0 -52
  70. package/src/components/tabs/tabPanel.vue +0 -36
  71. package/src/components/tooltip.vue +0 -115
  72. package/src/composables/formControl.ts +0 -144
  73. package/src/composables/list.ts +0 -326
  74. package/src/composables/listOption.ts +0 -80
  75. package/src/composables/popover.ts +0 -306
  76. package/src/utils/element.ts +0 -19
  77. package/src/utils/id.ts +0 -5
  78. package/src/vite-env.d.ts +0 -1
  79. package/tsconfig.json +0 -27
  80. package/tsconfig.node.json +0 -10
  81. 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>