Skip to content

Commit cfa3e3f

Browse files
committed
feat: add teleport option and dynamic dropdown positioning for Select component
1 parent 4f7372c commit cfa3e3f

File tree

1 file changed

+46
-2
lines changed

1 file changed

+46
-2
lines changed

adminforth/spa/src/afcl/Select.vue

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,32 @@
3232
/>
3333
</div>
3434
</div>
35-
<div v-if="showDropdown" ref="dropdownEl" :style="dropdownStyle" :class="{'shadow-none': isTop}"
36-
class="absolute z-10 mt-1 w-full bg-white shadow-lg dark:shadow-black dark:bg-gray-700
35+
<teleport to="body" v-if="teleportToBody && showDropdown">
36+
<div ref="dropdownEl" :style="getDropdownPosition" :class="{'shadow-none': isTop}"
37+
class="fixed z-50 w-full bg-white shadow-lg dark:shadow-black dark:bg-gray-700
38+
dark:border-gray-600 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm max-h-48">
39+
<div
40+
v-for="item in filteredItems"
41+
:key="item.value"
42+
class="px-4 py-2 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-400"
43+
:class="{ 'bg-lightPrimaryOpacity dark:bg-darkPrimaryOpacity': selectedItems.includes(item) }"
44+
@click="toogleItem(item)"
45+
>
46+
<slot name="item" :option="item"></slot>
47+
<label v-if="!$slots.item" :for="item.value">{{ item.label }}</label>
48+
</div>
49+
<div v-if="!filteredItems.length" class="px-4 py-2 cursor-pointer text-gray-400 dark:text-gray-300">
50+
{{ options.length ? $t('No results found') : $t('No items here') }}
51+
</div>
52+
53+
<div v-if="$slots['extra-item']" class="px-4 py-2 dark:text-gray-400">
54+
<slot name="extra-item"></slot>
55+
</div>
56+
</div>
57+
</teleport>
58+
59+
<div v-if="!teleportToBody && showDropdown" ref="dropdownEl" :style="dropdownStyle" :class="{'shadow-none': isTop}"
60+
class="absolute z-10 mt-1 w-full bg-white shadow-lg dark:shadow-black dark:bg-gray-700
3761
dark:border-gray-600 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm max-h-48">
3862
<div
3963
v-for="item in filteredItems"
@@ -106,6 +130,10 @@ const props = defineProps({
106130
type: Boolean,
107131
default: false,
108132
},
133+
teleportToBody: {
134+
type: Boolean,
135+
default: true,
136+
},
109137
});
110138
111139
const emit = defineEmits(['update:modelValue']);
@@ -242,4 +270,20 @@ onUnmounted(() => {
242270
removeClickListener();
243271
});
244272
273+
const getDropdownPosition = computed(() => {
274+
if (!inputEl.value) return {};
275+
const rect = inputEl.value.getBoundingClientRect();
276+
const style: { left: string; top: string; width: string } = {
277+
left: `${rect.left}px`,
278+
top: `${rect.bottom + 8}px`,
279+
width: `${rect.width}px`
280+
};
281+
282+
if (isTop.value && dropdownHeight.value) {
283+
style.top = `${rect.top - dropdownHeight.value - 8}px`;
284+
}
285+
286+
return style;
287+
});
288+
245289
</script>

0 commit comments

Comments
 (0)