@apptegy/nuxt-navigation 0.1.77 → 0.1.78
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
CHANGED
|
@@ -6,6 +6,7 @@ const appConfig = useAppConfig();
|
|
|
6
6
|
const zIndex = computed(() => appConfig?.navigation?.zIndex || 100);
|
|
7
7
|
const emit = defineEmits(["update:model-value"]);
|
|
8
8
|
const dropdownTriggerRef = ref();
|
|
9
|
+
const inputRef = ref();
|
|
9
10
|
const optionRef = ref();
|
|
10
11
|
const selectedOrg = computed(() => {
|
|
11
12
|
return props.orgs.find(({ id }) => id == props.modelValue);
|
|
@@ -17,13 +18,14 @@ const props = defineProps({
|
|
|
17
18
|
const {
|
|
18
19
|
active,
|
|
19
20
|
searchQuery,
|
|
21
|
+
highlightedIndex,
|
|
20
22
|
toggleDropdown,
|
|
21
23
|
closeDropdown,
|
|
22
24
|
onClickOutsideHandler,
|
|
23
25
|
keydownActionsOnTrigger,
|
|
24
26
|
handleKeydown,
|
|
25
27
|
handleDropdownSearch
|
|
26
|
-
} = useDropdown(props.orgs, optionRef,
|
|
28
|
+
} = useDropdown(props.orgs, optionRef, inputRef, {
|
|
27
29
|
searchable: true,
|
|
28
30
|
trackBy: "name"
|
|
29
31
|
});
|
|
@@ -38,6 +40,12 @@ const sortedOrgs = computed(() => {
|
|
|
38
40
|
}
|
|
39
41
|
});
|
|
40
42
|
});
|
|
43
|
+
const highlightedOrg = computed(() => {
|
|
44
|
+
if (highlightedIndex.value >= 0 && highlightedIndex.value < props.orgs.length) {
|
|
45
|
+
return props.orgs[highlightedIndex.value];
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
});
|
|
41
49
|
function handleSelectedOrg($event, orgId) {
|
|
42
50
|
if ($event.type == "click") {
|
|
43
51
|
emit("update:model-value", orgId);
|
|
@@ -97,7 +105,10 @@ function handleSelectedOrg($event, orgId) {
|
|
|
97
105
|
v-on-click-outside="onClickOutsideHandler"
|
|
98
106
|
role="option"
|
|
99
107
|
class="org-option"
|
|
100
|
-
:class="{
|
|
108
|
+
:class="{
|
|
109
|
+
selected: option.id === modelValue,
|
|
110
|
+
highlighted: highlightedOrg && option.id === highlightedOrg.id
|
|
111
|
+
}"
|
|
101
112
|
:aria-selected="option.id === modelValue"
|
|
102
113
|
tabindex="-1"
|
|
103
114
|
@keydown="handleSelectedOrg($event, option.id)"
|
|
@@ -179,6 +190,9 @@ function handleSelectedOrg($event, orgId) {
|
|
|
179
190
|
background-position: center left 15px;
|
|
180
191
|
color: var(--purple);
|
|
181
192
|
}
|
|
193
|
+
.org-selection .dropdown .org-option.highlighted {
|
|
194
|
+
background-color: #f8f6fc;
|
|
195
|
+
}
|
|
182
196
|
.org-selection .dropdown .org-option:focus-visible, .org-selection .dropdown .org-option:hover {
|
|
183
197
|
background-color: #f8f6fc;
|
|
184
198
|
}
|
|
@@ -6,13 +6,14 @@ export declare function useDropdown(options: Array<OptionItem>, dropdownOptionsR
|
|
|
6
6
|
}): {
|
|
7
7
|
active: Ref<boolean, boolean>;
|
|
8
8
|
searchQuery: Ref<string, string>;
|
|
9
|
+
highlightedIndex: Ref<number, number>;
|
|
9
10
|
toggleDropdown: ($event: KeyboardEvent | MouseEvent) => Promise<void>;
|
|
10
11
|
openDropdown: () => void;
|
|
11
12
|
closeDropdown: () => void;
|
|
12
13
|
onClickOutsideHandler: () => void;
|
|
13
14
|
keydownActionsOnTrigger: (event: KeyboardEvent) => void;
|
|
14
15
|
handleKeydown: (event: KeyboardEvent, index: number) => number | undefined;
|
|
15
|
-
handleDropdownSearch: (inputValue:
|
|
16
|
+
handleDropdownSearch: (inputValue: Event) => void;
|
|
16
17
|
debouncedFocus: import("@vueuse/core").PromisifyFn<(idx: number) => void>;
|
|
17
18
|
};
|
|
18
19
|
export {};
|
|
@@ -3,6 +3,7 @@ import { ref, nextTick } from "#imports";
|
|
|
3
3
|
export function useDropdown(options, dropdownOptionsRef, dropdownTriggerRef, config = { searchable: false, trackBy: "name" }) {
|
|
4
4
|
const active = ref(false);
|
|
5
5
|
const searchQuery = ref("");
|
|
6
|
+
const highlightedIndex = ref(-1);
|
|
6
7
|
async function toggleDropdown($event) {
|
|
7
8
|
active.value = !active.value;
|
|
8
9
|
if ($event instanceof KeyboardEvent && active.value && $event.key === "ArrowDown") {
|
|
@@ -16,6 +17,8 @@ export function useDropdown(options, dropdownOptionsRef, dropdownTriggerRef, con
|
|
|
16
17
|
}
|
|
17
18
|
function closeDropdown() {
|
|
18
19
|
active.value = false;
|
|
20
|
+
searchQuery.value = "";
|
|
21
|
+
highlightedIndex.value = -1;
|
|
19
22
|
}
|
|
20
23
|
function onClickOutsideHandler() {
|
|
21
24
|
if (!active.value) return;
|
|
@@ -23,7 +26,8 @@ export function useDropdown(options, dropdownOptionsRef, dropdownTriggerRef, con
|
|
|
23
26
|
}
|
|
24
27
|
function keydownActionsOnTrigger(event) {
|
|
25
28
|
if (event.ctrlKey || event.metaKey || event.shiftKey) return;
|
|
26
|
-
if (event.key
|
|
29
|
+
if (config.searchable && active.value && (event.key.length === 1 || event.key === "Backspace")) {
|
|
30
|
+
} else if (event.key !== "Tab") {
|
|
27
31
|
event.preventDefault();
|
|
28
32
|
}
|
|
29
33
|
switch (event.key) {
|
|
@@ -33,17 +37,21 @@ export function useDropdown(options, dropdownOptionsRef, dropdownTriggerRef, con
|
|
|
33
37
|
break;
|
|
34
38
|
case "ArrowDown":
|
|
35
39
|
if (active.value && event.key === "ArrowDown") {
|
|
40
|
+
event.preventDefault();
|
|
36
41
|
nextTick(() => {
|
|
37
|
-
|
|
42
|
+
const targetIndex = highlightedIndex.value >= 0 ? highlightedIndex.value : 0;
|
|
43
|
+
dropdownOptionsRef.value[targetIndex]?.focus();
|
|
38
44
|
openDropdown();
|
|
39
45
|
});
|
|
40
46
|
}
|
|
41
47
|
break;
|
|
42
48
|
case "ArrowUp":
|
|
49
|
+
event.preventDefault();
|
|
43
50
|
openDropdown();
|
|
44
51
|
if (config.searchable) {
|
|
45
52
|
nextTick(() => {
|
|
46
|
-
|
|
53
|
+
const targetIndex = highlightedIndex.value >= 0 ? highlightedIndex.value : 0;
|
|
54
|
+
dropdownOptionsRef.value[targetIndex]?.focus();
|
|
47
55
|
});
|
|
48
56
|
}
|
|
49
57
|
break;
|
|
@@ -51,6 +59,9 @@ export function useDropdown(options, dropdownOptionsRef, dropdownTriggerRef, con
|
|
|
51
59
|
case "ArrowRight":
|
|
52
60
|
break;
|
|
53
61
|
case " ":
|
|
62
|
+
if (config.searchable && active.value) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
54
65
|
if (active.value) {
|
|
55
66
|
closeDropdown();
|
|
56
67
|
nextTick(() => {
|
|
@@ -62,12 +73,19 @@ export function useDropdown(options, dropdownOptionsRef, dropdownTriggerRef, con
|
|
|
62
73
|
break;
|
|
63
74
|
case "Enter":
|
|
64
75
|
if (active.value) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
76
|
+
if (highlightedIndex.value >= 0 && highlightedIndex.value < options.length) {
|
|
77
|
+
nextTick(() => {
|
|
78
|
+
dropdownOptionsRef.value[highlightedIndex.value]?.focus();
|
|
79
|
+
});
|
|
80
|
+
} else {
|
|
81
|
+
closeDropdown();
|
|
82
|
+
nextTick(() => {
|
|
83
|
+
dropdownTriggerRef.value.focus();
|
|
84
|
+
});
|
|
85
|
+
}
|
|
69
86
|
} else {
|
|
70
87
|
searchQuery.value = "";
|
|
88
|
+
highlightedIndex.value = -1;
|
|
71
89
|
openDropdown();
|
|
72
90
|
}
|
|
73
91
|
break;
|
|
@@ -76,9 +94,11 @@ export function useDropdown(options, dropdownOptionsRef, dropdownTriggerRef, con
|
|
|
76
94
|
dropdownTriggerRef.value.focus();
|
|
77
95
|
break;
|
|
78
96
|
case "Home":
|
|
97
|
+
event.preventDefault();
|
|
79
98
|
openDropdown();
|
|
80
99
|
nextTick(() => {
|
|
81
|
-
|
|
100
|
+
const targetIndex = highlightedIndex.value >= 0 ? highlightedIndex.value : 0;
|
|
101
|
+
dropdownOptionsRef.value[targetIndex]?.focus();
|
|
82
102
|
});
|
|
83
103
|
break;
|
|
84
104
|
case "End":
|
|
@@ -88,7 +108,6 @@ export function useDropdown(options, dropdownOptionsRef, dropdownTriggerRef, con
|
|
|
88
108
|
});
|
|
89
109
|
break;
|
|
90
110
|
default:
|
|
91
|
-
handleDropdownSearch(event);
|
|
92
111
|
break;
|
|
93
112
|
}
|
|
94
113
|
}
|
|
@@ -171,32 +190,40 @@ export function useDropdown(options, dropdownOptionsRef, dropdownTriggerRef, con
|
|
|
171
190
|
}, 500);
|
|
172
191
|
function handleDropdownSearch(inputValue) {
|
|
173
192
|
if (!config.searchable) return;
|
|
174
|
-
if (inputValue.
|
|
175
|
-
searchQuery.value =
|
|
176
|
-
|
|
193
|
+
if (inputValue.target && "value" in inputValue.target) {
|
|
194
|
+
searchQuery.value = inputValue.target.value;
|
|
195
|
+
updateHighlightedIndex();
|
|
177
196
|
}
|
|
178
|
-
|
|
179
|
-
|
|
197
|
+
}
|
|
198
|
+
function updateHighlightedIndex() {
|
|
180
199
|
const searchIndex = options.findIndex((option) => {
|
|
200
|
+
if (!searchQuery.value) return false;
|
|
181
201
|
if (!config.trackBy) {
|
|
182
|
-
return typeof option === "string" && option.includes(searchQuery.value);
|
|
202
|
+
return typeof option === "string" && option.toLowerCase().includes(searchQuery.value.toLowerCase());
|
|
183
203
|
} else if (typeof option === "object" && option !== null && config.trackBy in option) {
|
|
184
204
|
const value = option[config.trackBy];
|
|
185
205
|
if (typeof value === "string") {
|
|
186
206
|
return value.toLowerCase().includes(searchQuery.value.toLowerCase());
|
|
187
207
|
} else if (value !== null && value !== void 0) {
|
|
188
|
-
return String(value).includes(searchQuery.value);
|
|
208
|
+
return String(value).toLowerCase().includes(searchQuery.value.toLowerCase());
|
|
189
209
|
}
|
|
190
210
|
}
|
|
191
211
|
return false;
|
|
192
212
|
});
|
|
193
|
-
|
|
194
|
-
|
|
213
|
+
highlightedIndex.value = searchIndex;
|
|
214
|
+
if (searchIndex >= 0 && dropdownOptionsRef.value[searchIndex]) {
|
|
215
|
+
nextTick(() => {
|
|
216
|
+
dropdownOptionsRef.value[searchIndex]?.scrollIntoView({
|
|
217
|
+
block: "nearest",
|
|
218
|
+
behavior: "smooth"
|
|
219
|
+
});
|
|
220
|
+
});
|
|
195
221
|
}
|
|
196
222
|
}
|
|
197
223
|
return {
|
|
198
224
|
active,
|
|
199
225
|
searchQuery,
|
|
226
|
+
highlightedIndex,
|
|
200
227
|
toggleDropdown,
|
|
201
228
|
openDropdown,
|
|
202
229
|
closeDropdown,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apptegy/nuxt-navigation",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.78",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -46,5 +46,5 @@
|
|
|
46
46
|
"vue": "^3.5.13",
|
|
47
47
|
"vue-tsc": "^3.0.3"
|
|
48
48
|
},
|
|
49
|
-
"gitHead": "
|
|
49
|
+
"gitHead": "0270d8823bc029e30d7e32ca8b331bba224250fb"
|
|
50
50
|
}
|