edvoyui-component-library-test-flight 0.0.224 → 0.0.225

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.
@@ -34,7 +34,9 @@
34
34
  :key="item.text"
35
35
  class="relative flex items-center px-4 py-2 rounded-lg cursor-pointer hover:bg-gray-100"
36
36
  @mouseenter="setActiveMenuItem(item.text)"
37
- @mouseleave="clearActiveMenuItem"
37
+ @mouseleave="scheduleClearActiveMenuItem(item.text)"
38
+ @focusin="setActiveMenuItem(item.text)"
39
+ @focusout="scheduleClearActiveMenuItem(item.text)"
38
40
  @click.stop="selectMenuItem(item)"
39
41
  >
40
42
  <div
@@ -59,6 +61,7 @@
59
61
  <div
60
62
  v-if="item.subMenu && activeMenuItem === item.text"
61
63
  :class="['absolute top-0 z-10 transition-all duration-300 ease-in-out min-w-32 max-w-64 w-max', placement === 'right' ? 'right-full' : 'left-full']"
64
+ @mouseenter="setActiveMenuItem(item.text)"
62
65
  >
63
66
  <div
64
67
  class="bg-white border border-gray-200 border-solid rounded-lg shadow-2xl ms-2 shadow-gray-300"
@@ -175,24 +178,37 @@ const dropdownMenu = ref<HTMLElement | null>(null);
175
178
  const resolvedPlacement = ref<"top" | "bottom" | "left" | "right">(
176
179
  props.placement
177
180
  );
181
+ let activeMenuCloseTimer: ReturnType<typeof setTimeout> | undefined;
178
182
 
179
183
  const emit = defineEmits(["subMenuItem", "menuItem", "actionItem"]);
180
184
 
181
185
  const toggleDropdown = () => {
182
- isOpen.value = !isOpen.value;
186
+ if (isOpen.value) {
187
+ closeDropdown();
188
+ return;
189
+ }
190
+
191
+ isOpen.value = true;
183
192
  };
184
193
 
185
194
  const closeDropdown = () => {
195
+ clearTimeout(activeMenuCloseTimer);
186
196
  isOpen.value = false;
187
197
  activeMenuItem.value = null;
188
198
  };
189
199
 
190
200
  const setActiveMenuItem = (text: string) => {
201
+ clearTimeout(activeMenuCloseTimer);
191
202
  activeMenuItem.value = text;
192
203
  };
193
204
 
194
- const clearActiveMenuItem = () => {
195
- activeMenuItem.value = null;
205
+ const scheduleClearActiveMenuItem = (text: string) => {
206
+ clearTimeout(activeMenuCloseTimer);
207
+ activeMenuCloseTimer = setTimeout(() => {
208
+ // Interactive submenu content (for example, search) must stay mounted while focused.
209
+ if (dropdownMenu.value?.contains(document.activeElement)) return;
210
+ if (activeMenuItem.value === text) activeMenuItem.value = null;
211
+ }, 150);
196
212
  };
197
213
 
198
214
  const selectMenuItem = (item: MenuItem) => {
@@ -272,6 +288,7 @@ onMounted(() => {
272
288
  });
273
289
 
274
290
  onBeforeUnmount(() => {
291
+ clearTimeout(activeMenuCloseTimer);
275
292
  window.removeEventListener("resize", updateDropdownPlacement);
276
293
  window.removeEventListener("scroll", updateDropdownPlacement, true);
277
294
  });
@@ -63,7 +63,16 @@
63
63
  </template>
64
64
 
65
65
  <script lang="ts" setup>
66
- import { computed, onBeforeUnmount, onMounted, PropType, ref, toRefs, watch } from "vue";
66
+ import {
67
+ computed,
68
+ nextTick,
69
+ onBeforeUnmount,
70
+ onMounted,
71
+ PropType,
72
+ ref,
73
+ toRefs,
74
+ watch,
75
+ } from "vue";
67
76
  import SearchBigZoomIn from "../../assets/svg/SearchBigZoomIn.vue";
68
77
 
69
78
  const props = defineProps({
@@ -87,12 +96,13 @@ const emit = defineEmits(["update:modelValue"]);
87
96
 
88
97
  const input = ref<HTMLInputElement>();
89
98
  const isExpanded = ref(true);
90
- const searchQuery = ref();
99
+ const searchQuery = ref<string | number>(props.modelValue ?? "");
91
100
  const componentWrapper = ref<HTMLElement | null>(null);
92
101
 
93
- function expandInput() {
102
+ async function expandInput() {
94
103
  isExpanded.value = true;
95
- input?.value?.focus();
104
+ await nextTick();
105
+ input.value?.focus();
96
106
  }
97
107
 
98
108
  const inputValue = computed(() => {
@@ -124,10 +134,19 @@ watch(
124
134
  { immediate: true, deep: true }
125
135
  );
126
136
 
137
+ watch(
138
+ () => props.modelValue,
139
+ (newValue) => {
140
+ if (newValue !== searchQuery.value) {
141
+ searchQuery.value = newValue ?? "";
142
+ }
143
+ }
144
+ );
145
+
127
146
  onMounted(() => {
128
147
  document.addEventListener("click", handleOutsideClick);
129
- if (searchQuery.value) {
130
- searchQuery.value?.focus();
148
+ if (isExpanded.value) {
149
+ nextTick(() => input.value?.focus());
131
150
  }
132
151
  });
133
152