@apptegy/nuxt-navigation 0.1.77 → 0.1.79

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
@@ -4,7 +4,7 @@
4
4
  "nuxt": ">=3.0.0"
5
5
  },
6
6
  "configKey": "@apptegy/nuxt-navigation",
7
- "version": "0.1.77",
7
+ "version": "0.1.79",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.1",
10
10
  "unbuild": "unknown"
@@ -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, dropdownTriggerRef, {
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="{ selected: option.id === modelValue }"
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
  }
@@ -42,7 +42,7 @@
42
42
  <button v-if="!lockedExpanded && allowCollapse && expanded" class="hamburger-button" @click="lockSidebar" v-tooltip.bottom="'Pin menu open'">
43
43
  <Unpinned />
44
44
  </button>
45
- <button v-if="!lockedExpanded && allowCollapse && !expanded" class="hamburger-button">
45
+ <button v-if="!lockedExpanded && allowCollapse && !expanded" class="hamburger-button" @click="onHover">
46
46
  <a-icon icon="24:hamburger" />
47
47
  </button>
48
48
  </div>
@@ -122,7 +122,7 @@ const props = defineProps({
122
122
  isAdmin: { type: Boolean, required: false, default: false },
123
123
  settingsRoute: { type: String, required: true, default: "" },
124
124
  schoolWebsite: { type: String, required: true, default: "" },
125
- showCollapseButton: { type: Boolean, required: false }
125
+ showCollapseButton: { type: Boolean, required: false, default: false }
126
126
  });
127
127
  const emit = defineEmits(["toggle-expand"]);
128
128
  const { state: expanded } = useSidebar();
@@ -50,6 +50,7 @@ declare const __VLS_component: import("vue").DefineComponent<ISidebarProps, void
50
50
  skipToContent: string;
51
51
  useApptegyLogo: boolean;
52
52
  isAdmin: boolean;
53
+ showCollapseButton: boolean;
53
54
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
54
55
  declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
55
56
  export default _default;
@@ -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: KeyboardEvent & InputEvent) => void;
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 !== "Tab") {
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
- dropdownOptionsRef.value[0].focus();
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
- dropdownOptionsRef.value[0].focus();
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
- closeDropdown();
66
- nextTick(() => {
67
- dropdownTriggerRef.value.focus();
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
- dropdownOptionsRef.value[0].focus();
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.key == "Backspace") {
175
- searchQuery.value = searchQuery.value.slice(0, -1);
176
- return;
193
+ if (inputValue.target && "value" in inputValue.target) {
194
+ searchQuery.value = inputValue.target.value;
195
+ updateHighlightedIndex();
177
196
  }
178
- const pressedKey = inputValue.key || inputValue.data;
179
- searchQuery.value = `${searchQuery.value}${pressedKey}`;
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
- if (searchIndex !== -1) {
194
- debouncedFocus(searchIndex);
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.77",
3
+ "version": "0.1.79",
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": "112c6210e954838d100c8a5bf94c9f32d91152b1"
49
+ "gitHead": "edcb211c421e908188dc18cda3ce4be1adb7cf97"
50
50
  }