vue-composable-ui 0.0.1 → 1.0.2
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/combobox.vue_vue_type_script_setup_true_lang-BhVMcx67.js +140 -0
- package/dist/comboboxHiddenInput.vue_vue_type_script_setup_true_lang-Ba-UPd-M.js +29 -0
- package/dist/comboboxInput.vue_vue_type_script_setup_true_lang-DzRfJ5ke.js +33 -0
- package/dist/comboboxOption.vue_vue_type_script_setup_true_lang-OyJxB4Jk.js +42 -0
- package/dist/comboboxOptions.vue_vue_type_script_setup_true_lang-Doy6MfEQ.js +20 -0
- package/dist/composable-ui.js +49 -0
- package/dist/composable-ui10.js +4 -0
- package/dist/composable-ui11.js +4 -0
- package/dist/composable-ui12.js +4 -0
- package/dist/composable-ui13.js +4 -0
- package/dist/composable-ui14.js +4 -0
- package/dist/composable-ui15.js +4 -0
- package/dist/composable-ui16.js +4 -0
- package/dist/composable-ui17.js +31 -0
- package/dist/composable-ui18.js +4 -0
- package/dist/composable-ui19.js +4 -0
- package/dist/composable-ui2.js +55 -0
- package/dist/composable-ui20.js +4 -0
- package/dist/composable-ui21.js +100 -0
- package/dist/composable-ui22.js +144 -0
- package/dist/composable-ui23.js +7 -0
- package/dist/composable-ui24.js +7 -0
- package/dist/composable-ui3.js +4 -0
- package/dist/composable-ui4.js +4 -0
- package/dist/composable-ui5.js +4 -0
- package/dist/composable-ui6.js +4 -0
- package/dist/composable-ui7.js +4 -0
- package/dist/composable-ui8.js +4 -0
- package/dist/composable-ui9.js +4 -0
- package/dist/listbox.vue_vue_type_script_setup_true_lang-Bj1ZXiBX.js +69 -0
- package/dist/listboxOption.vue_vue_type_script_setup_true_lang-B_yRptJm.js +37 -0
- package/dist/menu.vue_vue_type_script_setup_true_lang-Ci7q2rOg.js +82 -0
- package/dist/menuItem.vue_vue_type_script_setup_true_lang-mXtMa4gO.js +63 -0
- package/dist/menuItems.vue_vue_type_script_setup_true_lang-p9vftREy.js +49 -0
- package/dist/popover.vue_vue_type_script_setup_true_lang-DhKF7_nY.js +50 -0
- package/dist/popoverDialog.vue_vue_type_script_setup_true_lang-CDXUO7wZ.js +33 -0
- package/dist/tab.vue_vue_type_script_setup_true_lang-Y5OFGqMB.js +29 -0
- package/dist/tabContainer.vue_vue_type_script_setup_true_lang-fPpLno2Z.js +40 -0
- package/dist/tabList.vue_vue_type_script_setup_true_lang-CZyqGCuj.js +48 -0
- package/dist/tabPanel.vue_vue_type_script_setup_true_lang-CGhP6SHR.js +31 -0
- package/dist/tooltip.vue_vue_type_script_setup_true_lang-DVKYJyfL.js +85 -0
- package/dist/types/components/combobox/combobox.vue.d.ts +45 -0
- package/dist/types/components/combobox/comboboxHiddenInput.vue.d.ts +6 -0
- package/dist/types/components/combobox/comboboxInput.vue.d.ts +9 -0
- package/dist/types/components/combobox/comboboxOption.vue.d.ts +20 -0
- package/dist/types/components/combobox/comboboxOptions.vue.d.ts +12 -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 +20 -0
- package/dist/types/components/menu/menu.vue.d.ts +42 -0
- package/dist/types/components/menu/menuItem.vue.d.ts +24 -0
- package/dist/types/components/menu/menuItems.vue.d.ts +12 -0
- package/dist/types/components/popover/popover.vue.d.ts +40 -0
- package/dist/types/components/popover/popoverDialog.vue.d.ts +12 -0
- package/dist/types/components/tabs/tab.vue.d.ts +17 -0
- package/dist/types/components/tabs/tabContainer.vue.d.ts +19 -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 +34 -0
- package/dist/types/composables/formControl.d.ts +48 -0
- package/dist/types/composables/list.d.ts +107 -0
- package/dist/types/composables/listOption.d.ts +34 -0
- package/dist/types/composables/popover.d.ts +68 -0
- package/dist/types/main.d.ts +22 -0
- package/dist/types/utils/element.d.ts +3 -0
- package/package.json +28 -12
- 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/main.ts +0 -65
- 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
package/docs/composables/list.md
DELETED
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
# useList
|
|
2
|
-
|
|
3
|
-
The composable implements single/multi-selection and navigation functionality for a list of HTML elements. It can be used to power a wide range of components: tabs, button groups, radio groups, comboboxes and all kinds of components where user has to select one or more values from a list.
|
|
4
|
-
|
|
5
|
-
`useList` is meant to be used in conjunction with the [`useListOption`](/composables/list-option) composable.
|
|
6
|
-
|
|
7
|
-
## Basic Usage
|
|
8
|
-
|
|
9
|
-
::: code-group
|
|
10
|
-
|
|
11
|
-
```vue [example.vue]
|
|
12
|
-
<script setup>
|
|
13
|
-
import { ref } from 'vue'
|
|
14
|
-
|
|
15
|
-
const value = ref()
|
|
16
|
-
</script>
|
|
17
|
-
|
|
18
|
-
<template>
|
|
19
|
-
Selected value: {{value}}
|
|
20
|
-
<List v-model="value">
|
|
21
|
-
<Item value="1">Item 1</Item>
|
|
22
|
-
<Item value="2">Item 2</Item>
|
|
23
|
-
<Item value="3" :disabled="true">Item 3</Item>
|
|
24
|
-
<List>
|
|
25
|
-
</template>
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
```vue [list.vue]
|
|
29
|
-
<script setup>
|
|
30
|
-
import { useList } from "vue-composable-ui";
|
|
31
|
-
|
|
32
|
-
const model = defineModel();
|
|
33
|
-
const list = useList(model);
|
|
34
|
-
</script>
|
|
35
|
-
|
|
36
|
-
<template>
|
|
37
|
-
<div>
|
|
38
|
-
<slot />
|
|
39
|
-
</div>
|
|
40
|
-
</template>
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
```vue [item.vue]
|
|
44
|
-
<script setup>
|
|
45
|
-
import { useListOption } from "vue-composable-ui";
|
|
46
|
-
import { ref } from "vue";
|
|
47
|
-
|
|
48
|
-
const props = defineProps<{
|
|
49
|
-
value: any;
|
|
50
|
-
disabled?: boolean;
|
|
51
|
-
}>()
|
|
52
|
-
|
|
53
|
-
const itemEl = ref<HTMLElement>()
|
|
54
|
-
const option = useListOption(itemEl, props.value, props.disabled);
|
|
55
|
-
|
|
56
|
-
const onClick = () => option.select();
|
|
57
|
-
</script>
|
|
58
|
-
|
|
59
|
-
<template>
|
|
60
|
-
<div ref="itemEl" @click="onClick">
|
|
61
|
-
<slot />
|
|
62
|
-
</div>
|
|
63
|
-
</template>
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
:::
|
|
67
|
-
|
|
68
|
-
## Type Declarations
|
|
69
|
-
|
|
70
|
-
### Composable
|
|
71
|
-
|
|
72
|
-
```ts
|
|
73
|
-
/**
|
|
74
|
-
* Creates a reactive list of HTML elements with assigned values.
|
|
75
|
-
*
|
|
76
|
-
* @param value A ref to store the list value. Set to an array to enable multiselection.
|
|
77
|
-
*/
|
|
78
|
-
export declare function useList(value: Ref<unknown>): UseListReturn;
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
### Return Type
|
|
82
|
-
|
|
83
|
-
```ts
|
|
84
|
-
export type ItemValue = unknown;
|
|
85
|
-
export interface Item {
|
|
86
|
-
id: number;
|
|
87
|
-
element: Ref<HTMLElement | undefined>;
|
|
88
|
-
value: ItemValue;
|
|
89
|
-
disabled: MaybeRef<boolean>;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
interface ActivationOptions {
|
|
93
|
-
/**
|
|
94
|
-
* Focus the corresponding HTML element
|
|
95
|
-
*
|
|
96
|
-
* @defaultValue false
|
|
97
|
-
*/
|
|
98
|
-
focus?: boolean;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
interface NavigationOptions {
|
|
102
|
-
/**
|
|
103
|
-
* Infinitely cycle throught the items
|
|
104
|
-
*
|
|
105
|
-
* @defaultValue false
|
|
106
|
-
*/
|
|
107
|
-
loop?: boolean;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export interface UseListReturn {
|
|
111
|
-
/**
|
|
112
|
-
* Array of items
|
|
113
|
-
*/
|
|
114
|
-
items: Readonly<ShallowRef<Item[]>>;
|
|
115
|
-
/**
|
|
116
|
-
* Multiselection flag
|
|
117
|
-
*/
|
|
118
|
-
isMultiselectable: Readonly<Ref<boolean>>;
|
|
119
|
-
/**
|
|
120
|
-
* Currently active item
|
|
121
|
-
*/
|
|
122
|
-
activeItem: ComputedRef<Item | undefined>;
|
|
123
|
-
/**
|
|
124
|
-
* Select the currently active item
|
|
125
|
-
*/
|
|
126
|
-
selectActiveItem: () => void;
|
|
127
|
-
/**
|
|
128
|
-
* Activate the currently selected item
|
|
129
|
-
*/
|
|
130
|
-
activateSelectedItem: (options?: ActivationOptions) => void;
|
|
131
|
-
/**
|
|
132
|
-
* Activate the first item
|
|
133
|
-
*/
|
|
134
|
-
activateFirstItem: (options?: ActivationOptions) => void;
|
|
135
|
-
/**
|
|
136
|
-
* Activate the last item
|
|
137
|
-
*/
|
|
138
|
-
activateLastItem: (options?: ActivationOptions) => void;
|
|
139
|
-
/**
|
|
140
|
-
* Activate next item
|
|
141
|
-
*/
|
|
142
|
-
activateNextItem: (options?: ActivationOptions & NavigationOptions) => void;
|
|
143
|
-
/**
|
|
144
|
-
* Activate previous item
|
|
145
|
-
*/
|
|
146
|
-
activatePrevItem: (options?: ActivationOptions & NavigationOptions) => void;
|
|
147
|
-
}
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
### Injection Key
|
|
151
|
-
|
|
152
|
-
> [!NOTE]
|
|
153
|
-
> List items get unique ids that are not equal to their indexes in the `items` array returned by the composable. You need to access `item.id` to get its id value.
|
|
154
|
-
|
|
155
|
-
```ts
|
|
156
|
-
interface UseListInjectionKey {
|
|
157
|
-
/**
|
|
158
|
-
* The list value
|
|
159
|
-
*/
|
|
160
|
-
value: Readonly<Ref<unknown>>;
|
|
161
|
-
/**
|
|
162
|
-
* Select an item by ID
|
|
163
|
-
*/
|
|
164
|
-
selectItem: (itemId: number) => void;
|
|
165
|
-
/**
|
|
166
|
-
* Activate an item by ID
|
|
167
|
-
*/
|
|
168
|
-
activateItem: (itemId: number, options?: ActivationOptions) => void;
|
|
169
|
-
/**
|
|
170
|
-
* Check if an item is selected
|
|
171
|
-
*/
|
|
172
|
-
isSelected: (itemId: number) => boolean;
|
|
173
|
-
/**
|
|
174
|
-
* Check if an item is active
|
|
175
|
-
*/
|
|
176
|
-
isActive: (itemId: number) => boolean;
|
|
177
|
-
/**
|
|
178
|
-
* Add an item to the list
|
|
179
|
-
*
|
|
180
|
-
* @returns New item ID
|
|
181
|
-
*/
|
|
182
|
-
addItem: (
|
|
183
|
-
itemEl: HTMLElementType,
|
|
184
|
-
itemVal: ItemValue,
|
|
185
|
-
disabled?: MaybeRef<boolean>
|
|
186
|
-
) => number;
|
|
187
|
-
/**
|
|
188
|
-
* Remove an item from the list by ID
|
|
189
|
-
*/
|
|
190
|
-
removeItem: (itemId: number) => void;
|
|
191
|
-
}
|
|
192
|
-
```
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
# usePopover
|
|
2
|
-
|
|
3
|
-
Popover is a floating container that appears next to its activator element above the main document flow. It can be used as a basic component for dropdown menus, comboboxes, tooltips, and more.
|
|
4
|
-
|
|
5
|
-
The composable abstracts the logic of positioning a popover relative to its activating element and captures clicks to automatically close the popover.
|
|
6
|
-
|
|
7
|
-
No event listeners are attached to the activating element to give the user full control over the way the popover is triggered.
|
|
8
|
-
|
|
9
|
-
## Type Declarations
|
|
10
|
-
|
|
11
|
-
### Composable
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
interface UsePopoverOptions {
|
|
15
|
-
/**
|
|
16
|
-
* The direction of the popover dialog
|
|
17
|
-
*
|
|
18
|
-
* @defaultValue `"down"`
|
|
19
|
-
*/
|
|
20
|
-
direction?: "up" | "down" | "left" | "right";
|
|
21
|
-
/**
|
|
22
|
-
* The alignment of the popover dialog
|
|
23
|
-
*
|
|
24
|
-
* @defaultValue `"left"`
|
|
25
|
-
*/
|
|
26
|
-
align?: "top" | "bottom" | "left" | "right" | "center" | "stretch";
|
|
27
|
-
/**
|
|
28
|
-
* The CSS position of the popover dialog
|
|
29
|
-
*
|
|
30
|
-
* @defaultValue `"fixed"`
|
|
31
|
-
*/
|
|
32
|
-
position?: "fixed" | "absolute";
|
|
33
|
-
/**
|
|
34
|
-
* Close the popover on a click outside of the dialog
|
|
35
|
-
*
|
|
36
|
-
* @defaultValue `true`
|
|
37
|
-
*/
|
|
38
|
-
closeOnOutsideClick?: boolean;
|
|
39
|
-
/**
|
|
40
|
-
* Close the popover on a click inside of the dialog
|
|
41
|
-
*
|
|
42
|
-
* @defaultValue `false`
|
|
43
|
-
*/
|
|
44
|
-
closeOnInsideClick?: boolean;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
*
|
|
49
|
-
* @param activatorEl A template ref or a CSS selector
|
|
50
|
-
* @param popupEl A template ref or a CSS selector
|
|
51
|
-
* @param options Popover options
|
|
52
|
-
*/
|
|
53
|
-
export declare function usePopover(
|
|
54
|
-
activatorEl: TemplateRefOrSelector,
|
|
55
|
-
popupEl: TemplateRefOrSelector,
|
|
56
|
-
options?: UsePopoverOptions
|
|
57
|
-
): UsePopoverReturn;
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### Return Type
|
|
61
|
-
|
|
62
|
-
```ts
|
|
63
|
-
interface FocusOptions {
|
|
64
|
-
/**
|
|
65
|
-
* Focus the popover/activator
|
|
66
|
-
*
|
|
67
|
-
* @defaultValue `true`
|
|
68
|
-
*/
|
|
69
|
-
focus?: boolean;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export interface UsePopoverReturn {
|
|
73
|
-
/**
|
|
74
|
-
* The popover state
|
|
75
|
-
*/
|
|
76
|
-
isOpen: Ref<boolean>;
|
|
77
|
-
/**
|
|
78
|
-
* Open the popover
|
|
79
|
-
*/
|
|
80
|
-
open: (options?: FocusOptions) => void;
|
|
81
|
-
/**
|
|
82
|
-
* Close the popover
|
|
83
|
-
*/
|
|
84
|
-
close: (options?: FocusOptions) => void;
|
|
85
|
-
/**
|
|
86
|
-
* Toggle the popover
|
|
87
|
-
*/
|
|
88
|
-
toggle: (options?: FocusOptions) => void;
|
|
89
|
-
}
|
|
90
|
-
```
|
package/docs/index.md
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
# https://vitepress.dev/reference/default-theme-home-page
|
|
3
|
-
layout: home
|
|
4
|
-
|
|
5
|
-
hero:
|
|
6
|
-
name: "Vue Composable UI"
|
|
7
|
-
text: "A truly headless UI framework"
|
|
8
|
-
tagline: My great project tagline
|
|
9
|
-
actions:
|
|
10
|
-
- theme: brand
|
|
11
|
-
text: Introduction
|
|
12
|
-
link: /intro
|
|
13
|
-
- theme: alt
|
|
14
|
-
text: API Examples
|
|
15
|
-
link: /api-examples
|
|
16
|
-
|
|
17
|
-
features:
|
|
18
|
-
- title: Feature A
|
|
19
|
-
details: Lorem ipsum dolor sit amet, consectetur adipiscing elit
|
|
20
|
-
- title: Feature B
|
|
21
|
-
details: Lorem ipsum dolor sit amet, consectetur adipiscing elit
|
|
22
|
-
- title: Feature C
|
|
23
|
-
details: Lorem ipsum dolor sit amet, consectetur adipiscing elit
|
|
24
|
-
---
|
package/docs/intro.md
DELETED
|
File without changes
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<template v-if="name">
|
|
3
|
-
<ComboboxFormInput
|
|
4
|
-
v-if="isMultiselectable"
|
|
5
|
-
v-for="(value, id) in formInputs"
|
|
6
|
-
:name="`${name}[${id}]`"
|
|
7
|
-
:value="value"
|
|
8
|
-
/>
|
|
9
|
-
<ComboboxFormInput v-else :name="name" :value="formInputs" />
|
|
10
|
-
</template>
|
|
11
|
-
|
|
12
|
-
<slot
|
|
13
|
-
name="input"
|
|
14
|
-
:popoverId="popoverId"
|
|
15
|
-
:attrs="inputAttrs"
|
|
16
|
-
:isOpen="isOpen"
|
|
17
|
-
:toggle="toggle"
|
|
18
|
-
>
|
|
19
|
-
<input v-bind="{ ...$attrs, ...inputAttrs }" autocomplete="off" />
|
|
20
|
-
</slot>
|
|
21
|
-
|
|
22
|
-
<slot />
|
|
23
|
-
</template>
|
|
24
|
-
|
|
25
|
-
<script setup lang="ts">
|
|
26
|
-
import { ref, computed, provide, watch, nextTick } from "vue";
|
|
27
|
-
import { useSequentialId } from "../../utils/id";
|
|
28
|
-
import { usePopover } from "../../composables/popover";
|
|
29
|
-
import { useList } from "../../composables/list";
|
|
30
|
-
import { useFormControl, FormValidator } from "../../composables/formControl";
|
|
31
|
-
import ComboboxFormInput from "./comboboxFormInput.vue";
|
|
32
|
-
import { injectCombobox } from "../injectionKeys";
|
|
33
|
-
|
|
34
|
-
defineOptions({ inheritAttrs: false });
|
|
35
|
-
|
|
36
|
-
const props = withDefaults(
|
|
37
|
-
defineProps<{
|
|
38
|
-
displayValue?: (value: any) => string;
|
|
39
|
-
|
|
40
|
-
id?: string;
|
|
41
|
-
activatorId?: string;
|
|
42
|
-
|
|
43
|
-
name?: string;
|
|
44
|
-
formValue?: (item: any) => Record<string, string> | string;
|
|
45
|
-
|
|
46
|
-
validators?: FormValidator[];
|
|
47
|
-
showValidationError?: boolean;
|
|
48
|
-
}>(),
|
|
49
|
-
{
|
|
50
|
-
displayValue: (value: any) =>
|
|
51
|
-
Array.isArray(value)
|
|
52
|
-
? value.map((i) => i.toString()).join(", ")
|
|
53
|
-
: value.toString(),
|
|
54
|
-
formValue: (item: any) => item,
|
|
55
|
-
showValidationError: true,
|
|
56
|
-
}
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
const formInputs = computed(() => {
|
|
60
|
-
if (isMultiselectable.value) {
|
|
61
|
-
return (model.value as any[]).map((i) => props.formValue(i));
|
|
62
|
-
} else {
|
|
63
|
-
return props.formValue(model.value);
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
const model = defineModel();
|
|
68
|
-
const modelIsValid = defineModel("isValid");
|
|
69
|
-
const modelValidationMessage = defineModel("validationMessage");
|
|
70
|
-
|
|
71
|
-
const comboboxId = props.id ?? useSequentialId("combobox");
|
|
72
|
-
const activatorId = props.activatorId ?? comboboxId;
|
|
73
|
-
const popoverId = `${comboboxId}-popover`;
|
|
74
|
-
|
|
75
|
-
// Popover
|
|
76
|
-
const { isOpen, toggle, open, close } = usePopover(
|
|
77
|
-
`#${activatorId}`,
|
|
78
|
-
`#${popoverId}`,
|
|
79
|
-
{
|
|
80
|
-
direction: "down",
|
|
81
|
-
align: "stretch",
|
|
82
|
-
position: "fixed",
|
|
83
|
-
closeOnOutsideClick: true,
|
|
84
|
-
closeOnInsideClick: false,
|
|
85
|
-
}
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
// List
|
|
89
|
-
const {
|
|
90
|
-
items,
|
|
91
|
-
activeItem,
|
|
92
|
-
isMultiselectable,
|
|
93
|
-
selectActiveItem,
|
|
94
|
-
activateSelectedItem,
|
|
95
|
-
activateFirstItem,
|
|
96
|
-
activateLastItem,
|
|
97
|
-
activateNextItem,
|
|
98
|
-
activatePrevItem,
|
|
99
|
-
} = useList(model);
|
|
100
|
-
|
|
101
|
-
// Form Control
|
|
102
|
-
const { focus, isValid, validationMessage } = useFormControl(
|
|
103
|
-
`#${comboboxId}`,
|
|
104
|
-
props.validators,
|
|
105
|
-
props.showValidationError
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
watch(isValid, () => (modelIsValid.value = isValid.value));
|
|
109
|
-
watch(
|
|
110
|
-
validationMessage,
|
|
111
|
-
() => (modelValidationMessage.value = validationMessage.value)
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
watch(items, () => {
|
|
115
|
-
if (!activeItem.value && items.value.length) {
|
|
116
|
-
if (
|
|
117
|
-
(isMultiselectable.value && (model.value as any[]).length) ||
|
|
118
|
-
(!isMultiselectable.value && model.value)
|
|
119
|
-
) {
|
|
120
|
-
activateSelectedItem({ focus: false });
|
|
121
|
-
} else {
|
|
122
|
-
activateFirstItem({ focus: false });
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
const inputValue = ref("");
|
|
128
|
-
|
|
129
|
-
function updateInputValue() {
|
|
130
|
-
inputValue.value = props.displayValue(model.value);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
watch(model, updateInputValue, { deep: true, immediate: true });
|
|
134
|
-
|
|
135
|
-
function onChange() {
|
|
136
|
-
updateInputValue();
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function onInput(evt: KeyboardEvent) {
|
|
140
|
-
inputValue.value = (evt.target as HTMLInputElement).value;
|
|
141
|
-
open({ focus: false });
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function onKeyDown(evt: KeyboardEvent) {
|
|
145
|
-
if (!isOpen.value) {
|
|
146
|
-
switch (evt.code) {
|
|
147
|
-
case "Space":
|
|
148
|
-
case "Enter":
|
|
149
|
-
open({ focus: false });
|
|
150
|
-
break;
|
|
151
|
-
|
|
152
|
-
case "End":
|
|
153
|
-
case "ArrowUp":
|
|
154
|
-
open({ focus: false });
|
|
155
|
-
nextTick(() => activateLastItem({ focus: false }));
|
|
156
|
-
break;
|
|
157
|
-
|
|
158
|
-
case "Home":
|
|
159
|
-
case "ArrowDown":
|
|
160
|
-
open({ focus: false });
|
|
161
|
-
nextTick(() => activateFirstItem({ focus: false }));
|
|
162
|
-
break;
|
|
163
|
-
|
|
164
|
-
default:
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
} else {
|
|
168
|
-
switch (evt.code) {
|
|
169
|
-
case "Space":
|
|
170
|
-
selectActiveItem();
|
|
171
|
-
if (!isMultiselectable.value) {
|
|
172
|
-
close();
|
|
173
|
-
}
|
|
174
|
-
break;
|
|
175
|
-
case "Enter":
|
|
176
|
-
selectActiveItem();
|
|
177
|
-
close();
|
|
178
|
-
break;
|
|
179
|
-
case "Tab":
|
|
180
|
-
selectActiveItem();
|
|
181
|
-
close();
|
|
182
|
-
return;
|
|
183
|
-
case "ArrowUp":
|
|
184
|
-
activatePrevItem({ focus: false, loop: false });
|
|
185
|
-
break;
|
|
186
|
-
case "ArrowDown":
|
|
187
|
-
activateNextItem({ focus: false, loop: false });
|
|
188
|
-
break;
|
|
189
|
-
case "Home":
|
|
190
|
-
activateFirstItem({ focus: false });
|
|
191
|
-
break;
|
|
192
|
-
case "End":
|
|
193
|
-
activateLastItem({ focus: false });
|
|
194
|
-
break;
|
|
195
|
-
case "Escape":
|
|
196
|
-
close();
|
|
197
|
-
break;
|
|
198
|
-
default:
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
evt.preventDefault();
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const inputAttrs = computed(() => ({
|
|
207
|
-
value: inputValue.value,
|
|
208
|
-
oninput: onInput,
|
|
209
|
-
onchange: onChange,
|
|
210
|
-
onkeydown: onKeyDown,
|
|
211
|
-
onclick: () => open({ focus: false }),
|
|
212
|
-
id: comboboxId,
|
|
213
|
-
"aria-controls": isOpen.value ? popoverId : undefined,
|
|
214
|
-
"aria-expanded": isOpen.value,
|
|
215
|
-
"aria-activedescendant": activeItem.value?.element.value?.id,
|
|
216
|
-
"aria-haspopup": "listbox" as "listbox",
|
|
217
|
-
role: "combobox",
|
|
218
|
-
}));
|
|
219
|
-
|
|
220
|
-
provide(injectCombobox, {
|
|
221
|
-
popoverId,
|
|
222
|
-
isOpen,
|
|
223
|
-
isMultiselectable,
|
|
224
|
-
close,
|
|
225
|
-
focus,
|
|
226
|
-
});
|
|
227
|
-
</script>
|
|
228
|
-
|
|
229
|
-
<style scoped></style>
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<input
|
|
3
|
-
v-for="(value, name) in inputs"
|
|
4
|
-
:name="name"
|
|
5
|
-
:value="value"
|
|
6
|
-
type="hidden"
|
|
7
|
-
/>
|
|
8
|
-
</template>
|
|
9
|
-
|
|
10
|
-
<script setup lang="ts">
|
|
11
|
-
import { computed } from "vue";
|
|
12
|
-
|
|
13
|
-
const props = defineProps<{
|
|
14
|
-
name: string;
|
|
15
|
-
value: any;
|
|
16
|
-
}>();
|
|
17
|
-
|
|
18
|
-
const inputs = computed(() => {
|
|
19
|
-
if (typeof props.value === "object") {
|
|
20
|
-
const fields: Record<string, any> = {};
|
|
21
|
-
for (const [key, value] of Object.entries(props.value)) {
|
|
22
|
-
fields[`${props.name}[${key}]`] = value;
|
|
23
|
-
}
|
|
24
|
-
return fields;
|
|
25
|
-
} else {
|
|
26
|
-
return {
|
|
27
|
-
[props.name]: props.value,
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
</script>
|
|
32
|
-
|
|
33
|
-
<style scoped></style>
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div
|
|
3
|
-
ref="itemEl"
|
|
4
|
-
:id="itemId"
|
|
5
|
-
@click="onClick"
|
|
6
|
-
@mouseenter="activate({ focus: false })"
|
|
7
|
-
:data-active="isActive"
|
|
8
|
-
:aria-selected="isSelected"
|
|
9
|
-
:aria-disabled="disabled || undefined"
|
|
10
|
-
role="option"
|
|
11
|
-
>
|
|
12
|
-
<slot :isSelected="isSelected" :isActive="isActive" />
|
|
13
|
-
</div>
|
|
14
|
-
</template>
|
|
15
|
-
|
|
16
|
-
<script setup lang="ts">
|
|
17
|
-
import { ref, inject } from "vue";
|
|
18
|
-
import { useSequentialId } from "../../utils/id";
|
|
19
|
-
import { useListOption } from "../../composables/listOption";
|
|
20
|
-
import { injectCombobox } from "../injectionKeys";
|
|
21
|
-
|
|
22
|
-
const props = defineProps<{
|
|
23
|
-
id?: string;
|
|
24
|
-
value: any;
|
|
25
|
-
disabled?: boolean;
|
|
26
|
-
}>();
|
|
27
|
-
|
|
28
|
-
const itemId = props.id ?? useSequentialId("combobox-option");
|
|
29
|
-
|
|
30
|
-
const itemEl = ref<HTMLElement>();
|
|
31
|
-
|
|
32
|
-
const { isSelected, isActive, activate, select } = useListOption(
|
|
33
|
-
itemEl,
|
|
34
|
-
props.value,
|
|
35
|
-
props.disabled
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
const combobox = inject(injectCombobox);
|
|
39
|
-
if (!combobox) {
|
|
40
|
-
throw new Error("Combobox injectable context is not provided");
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function onClick() {
|
|
44
|
-
select();
|
|
45
|
-
if (!combobox!.isMultiselectable.value) {
|
|
46
|
-
combobox!.close({ focus: false });
|
|
47
|
-
}
|
|
48
|
-
combobox!.focus();
|
|
49
|
-
}
|
|
50
|
-
</script>
|
|
51
|
-
|
|
52
|
-
<style scoped></style>
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div v-if="combobox!.isOpen.value" :id="combobox!.popoverId" role="listbox">
|
|
3
|
-
<slot />
|
|
4
|
-
</div>
|
|
5
|
-
</template>
|
|
6
|
-
|
|
7
|
-
<script setup lang="ts">
|
|
8
|
-
import { inject } from "vue";
|
|
9
|
-
import { injectCombobox } from "../injectionKeys";
|
|
10
|
-
|
|
11
|
-
const combobox = inject(injectCombobox);
|
|
12
|
-
if (!combobox) {
|
|
13
|
-
throw new Error("Combobox injectable context is not provided");
|
|
14
|
-
}
|
|
15
|
-
</script>
|
|
16
|
-
|
|
17
|
-
<style scoped></style>
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import type { ComputedRef, InjectionKey } from "vue";
|
|
2
|
-
|
|
3
|
-
import type { UseListReturn } from "../composables/list";
|
|
4
|
-
import type { UsePopoverReturn } from "../composables/popover";
|
|
5
|
-
import type { useFormControlReturn } from "../composables/formControl";
|
|
6
|
-
|
|
7
|
-
export const injectTabs: InjectionKey<{
|
|
8
|
-
getIds: (value: any) => ComputedRef<{
|
|
9
|
-
tabId: string | undefined;
|
|
10
|
-
panelId: string | undefined;
|
|
11
|
-
}>;
|
|
12
|
-
selectActiveItem: UseListReturn["selectActiveItem"];
|
|
13
|
-
activateFirstItem: UseListReturn["activateFirstItem"];
|
|
14
|
-
activateLastItem: UseListReturn["activateLastItem"];
|
|
15
|
-
activateNextItem: UseListReturn["activateNextItem"];
|
|
16
|
-
activatePrevItem: UseListReturn["activatePrevItem"];
|
|
17
|
-
orientation: "horizontal" | "vertical";
|
|
18
|
-
}> = Symbol();
|
|
19
|
-
|
|
20
|
-
export const injectPopover: InjectionKey<{
|
|
21
|
-
popoverId: string;
|
|
22
|
-
activatorId: string;
|
|
23
|
-
role: "dialog" | "listbox" | "menu" | "tree" | "grid";
|
|
24
|
-
isOpen: UsePopoverReturn["isOpen"];
|
|
25
|
-
close: UsePopoverReturn["close"];
|
|
26
|
-
}> = Symbol();
|
|
27
|
-
|
|
28
|
-
export const injectMenu: InjectionKey<{
|
|
29
|
-
menuId: string;
|
|
30
|
-
activatorId: string;
|
|
31
|
-
isOpen: UsePopoverReturn["isOpen"];
|
|
32
|
-
close: UsePopoverReturn["close"];
|
|
33
|
-
|
|
34
|
-
activeItemId: ComputedRef<string | undefined>;
|
|
35
|
-
isMultiselectable: UseListReturn["isMultiselectable"];
|
|
36
|
-
activateFirstItem: UseListReturn["activateFirstItem"];
|
|
37
|
-
activateLastItem: UseListReturn["activateLastItem"];
|
|
38
|
-
activateNextItem: UseListReturn["activateNextItem"];
|
|
39
|
-
activatePrevItem: UseListReturn["activatePrevItem"];
|
|
40
|
-
}> = Symbol();
|
|
41
|
-
|
|
42
|
-
export const injectCombobox: InjectionKey<{
|
|
43
|
-
popoverId: string;
|
|
44
|
-
isOpen: UsePopoverReturn["isOpen"];
|
|
45
|
-
close: UsePopoverReturn["close"];
|
|
46
|
-
|
|
47
|
-
isMultiselectable: UseListReturn["isMultiselectable"];
|
|
48
|
-
|
|
49
|
-
focus: useFormControlReturn["focus"];
|
|
50
|
-
}> = Symbol();
|