adminforth 2.45.0 → 2.46.0

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.
@@ -0,0 +1,148 @@
1
+ <template>
2
+ <div class="afcl-select afcl-select-wrapper relative inline-block af-button-shadow rounded" ref="internalSelect"
3
+ :class="{'opacity-50': readonly}"
4
+ >
5
+ <div class="relative w-fit">
6
+ <button
7
+ ref="dropdownFilterEl"
8
+ type="button"
9
+ @click="dropdownClick"
10
+ class="group h-[34px] inline-flex items-center justify-between min-w-max px-3 py-2 text-left cursor-pointer
11
+ text-sm font-medium transition-all rounded border outline-none gap-x-2
12
+ bg-lightListViewButtonBackground text-lightListViewButtonText border-lightListViewButtonBorder
13
+ dark:bg-darkListViewButtonBackground dark:text-darkListViewButtonText dark:border-darkListViewButtonBorder
14
+ hover:bg-lightListViewButtonBackgroundHover hover:text-lightListViewButtonTextHover"
15
+ >
16
+ <span v-if="displayLabel" class="whitespace-nowrap">
17
+ {{ displayLabel }}
18
+ </span>
19
+ <span
20
+ v-else
21
+ class="opacity-100 transition-colors whitespace-nowrap"
22
+ >
23
+ {{ filter?.name || placeholder || $t('Select...') }}
24
+ </span>
25
+
26
+ <IconCaretDownSolid class="h-4 w-4 text-lightPrimary dark:text-darkPrimary opacity-50 transition duration-150 ease-in flex-shrink-0"
27
+ :class="{ 'transform rotate-180': showDropdown }"
28
+ />
29
+ </button>
30
+ </div>
31
+
32
+ <teleport to="body" v-if="teleportToBody && showDropdown">
33
+ <div
34
+ ref="dropdownEl"
35
+ :style="getDropdownPosition"
36
+ class="fixed z-[1000] w-max min-w-fit bg-lightDropdownOptionsBackground shadow-lg dark:shadow-black dark:bg-darkDropdownOptionsBackground
37
+ dark:border-gray-600 rounded-md text-base ring-1 ring-black ring-opacity-5 overflow-hidden focus:outline-none sm:text-sm max-h-64 flex flex-col"
38
+ >
39
+ <div class="py-1 overflow-y-auto grow" @scroll="handleDropdownScroll">
40
+ <div
41
+ v-for="item in options"
42
+ :key="item.value"
43
+ class="px-4 py-2 cursor-pointer hover:bg-lightDropdownOptionsHoverBackground dark:hover:bg-darkDropdownOptionsHoverBackground text-lightDropdownOptionsText dark:text-darkDropdownOptionsText"
44
+ :class="{ 'bg-lightDropdownPicked dark:bg-darkDropdownPicked': isItemSelected(item) }"
45
+ @click="toggleItem(item)"
46
+ >
47
+ <slot name="item" :option="item">
48
+ {{ item.label }}
49
+ </slot>
50
+ </div>
51
+
52
+ <div v-if="!options?.length" class="px-4 py-2 text-gray-500 italic text-center">
53
+ {{ $t('No items here') }}
54
+ </div>
55
+
56
+ <div
57
+ v-if="modelValue !== null && modelValue !== undefined && modelValue !== ''"
58
+ class="px-4 py-2 cursor-pointer hover:bg-lightDropdownOptionsHoverBackground dark:hover:bg-darkDropdownOptionsHoverBackground text-lightDropdownOptionsText dark:text-darkDropdownOptionsText"
59
+ @click="clearSelection"
60
+ >
61
+ {{ $t('Clear selection') }}
62
+ </div>
63
+ </div>
64
+ </div>
65
+ </teleport>
66
+ </div>
67
+ </template>
68
+
69
+ <script setup lang="ts">
70
+ import { ref, computed, onMounted, onUnmounted, type PropType } from 'vue';
71
+ import { IconCaretDownSolid } from '@iconify-prerendered/vue-flowbite';
72
+
73
+ const props = defineProps({
74
+ filter: {
75
+ type: Object as PropType<{ name: string; enum: any[] }>,
76
+ default: null,
77
+ },
78
+ options: {
79
+ type: Array as PropType<{label: string, value: any}[]>,
80
+ default: () => [],
81
+ },
82
+ modelValue: [String, Number, Boolean, Array] as PropType<any>,
83
+ placeholder: String,
84
+ readonly: Boolean,
85
+ teleportToBody: Boolean,
86
+ });
87
+
88
+ const emit = defineEmits(['update:modelValue', 'scroll-near-end']);
89
+
90
+ const showDropdown = ref(false);
91
+ const dropdownFilterEl = ref<HTMLElement | null>(null);
92
+ const dropdownEl = ref<HTMLElement | null>(null);
93
+ const internalSelect = ref<HTMLElement | null>(null);
94
+
95
+ const displayLabel = computed(() => {
96
+ const selected = props.options.find(o => o.value === props.modelValue);
97
+ return selected ? selected.label : '';
98
+ });
99
+
100
+ const isItemSelected = (item: any) => props.modelValue === item.value;
101
+
102
+ const toggleItem = (item: any) => {
103
+ emit('update:modelValue', item.value);
104
+ showDropdown.value = false;
105
+ };
106
+
107
+ const clearSelection = () => {
108
+ emit('update:modelValue', null);
109
+ showDropdown.value = false;
110
+ };
111
+
112
+ const dropdownClick = () => {
113
+ if (!props.readonly) showDropdown.value = !showDropdown.value;
114
+ };
115
+
116
+ const getDropdownPosition = computed(() => {
117
+ if (!dropdownFilterEl.value) return {};
118
+ const rect = dropdownFilterEl.value.getBoundingClientRect();
119
+ return {
120
+ top: `${rect.bottom + window.scrollY + 4}px`,
121
+ left: `${rect.right + window.scrollX - (dropdownEl.value?.offsetWidth || rect.width)}px`,
122
+ minWidth: `${rect.width}px`
123
+ };
124
+ });
125
+
126
+ const handleDropdownScroll = (event: Event) => {
127
+ const target = event.target as HTMLElement;
128
+ if (target.scrollTop + target.clientHeight >= target.scrollHeight - 10) {
129
+ emit('scroll-near-end');
130
+ }
131
+ };
132
+
133
+ const handleClickOutside = (event: MouseEvent) => {
134
+ const target = event.target as HTMLElement;
135
+ if (internalSelect.value?.contains(target)) return;
136
+ if (dropdownEl.value?.contains(target)) return;
137
+
138
+ showDropdown.value = false;
139
+ };
140
+
141
+ onMounted(() => {
142
+ document.addEventListener('click', handleClickOutside);
143
+ });
144
+
145
+ onUnmounted(() => {
146
+ document.removeEventListener('click', handleClickOutside);
147
+ });
148
+ </script>
@@ -26,4 +26,5 @@ export { default as DatePicker } from './DatePicker.vue';
26
26
  export { default as Textarea } from './Textarea.vue';
27
27
  export { default as ButtonGroup } from './ButtonGroup.vue';
28
28
  export { default as Card } from './Card.vue';
29
- export { default as Modal } from './Modal.vue';
29
+ export { default as Modal } from './Modal.vue';
30
+ export { default as FilterDropdown } from './FilterDropdown.vue';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adminforth",
3
- "version": "2.45.0",
3
+ "version": "2.46.0",
4
4
  "description": "OpenSource Agent-Native forth-generation admin panel",
5
5
  "keywords": [
6
6
  "adminforth",