adminforth 2.4.0-next.31 → 2.4.0-next.310

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.
Files changed (176) hide show
  1. package/commands/callTsProxy.js +14 -4
  2. package/commands/createApp/templates/api.ts.hbs +10 -0
  3. package/commands/createApp/templates/custom/tsconfig.json.hbs +2 -3
  4. package/commands/createApp/templates/index.ts.hbs +12 -1
  5. package/commands/createApp/templates/package.json.hbs +1 -1
  6. package/commands/createApp/templates/prisma.config.ts.hbs +8 -0
  7. package/commands/createApp/templates/schema.prisma.hbs +0 -1
  8. package/commands/createApp/utils.js +10 -0
  9. package/commands/createCustomComponent/configLoader.js +17 -4
  10. package/commands/createCustomComponent/main.js +13 -7
  11. package/commands/createCustomComponent/templates/customCrud/beforeActionButtons.vue.hbs +38 -0
  12. package/commands/createCustomComponent/templates/customCrud/saveButton.vue.hbs +28 -0
  13. package/commands/createPlugin/templates/custom/tsconfig.json.hbs +2 -5
  14. package/commands/createPlugin/templates/package.json.hbs +1 -1
  15. package/commands/generateModels.js +30 -22
  16. package/dist/auth.d.ts +9 -1
  17. package/dist/auth.d.ts.map +1 -1
  18. package/dist/auth.js +21 -2
  19. package/dist/auth.js.map +1 -1
  20. package/dist/dataConnectors/baseConnector.d.ts +1 -1
  21. package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
  22. package/dist/dataConnectors/baseConnector.js +69 -17
  23. package/dist/dataConnectors/baseConnector.js.map +1 -1
  24. package/dist/dataConnectors/clickhouse.d.ts.map +1 -1
  25. package/dist/dataConnectors/clickhouse.js +15 -0
  26. package/dist/dataConnectors/clickhouse.js.map +1 -1
  27. package/dist/dataConnectors/mongo.d.ts.map +1 -1
  28. package/dist/dataConnectors/mongo.js +50 -15
  29. package/dist/dataConnectors/mongo.js.map +1 -1
  30. package/dist/dataConnectors/mysql.d.ts.map +1 -1
  31. package/dist/dataConnectors/mysql.js +11 -0
  32. package/dist/dataConnectors/mysql.js.map +1 -1
  33. package/dist/dataConnectors/postgres.d.ts.map +1 -1
  34. package/dist/dataConnectors/postgres.js +43 -14
  35. package/dist/dataConnectors/postgres.js.map +1 -1
  36. package/dist/dataConnectors/sqlite.d.ts.map +1 -1
  37. package/dist/dataConnectors/sqlite.js +11 -0
  38. package/dist/dataConnectors/sqlite.js.map +1 -1
  39. package/dist/index.d.ts +12 -2
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +45 -22
  42. package/dist/index.js.map +1 -1
  43. package/dist/modules/codeInjector.d.ts +2 -0
  44. package/dist/modules/codeInjector.d.ts.map +1 -1
  45. package/dist/modules/codeInjector.js +62 -6
  46. package/dist/modules/codeInjector.js.map +1 -1
  47. package/dist/modules/configValidator.d.ts +6 -0
  48. package/dist/modules/configValidator.d.ts.map +1 -1
  49. package/dist/modules/configValidator.js +202 -25
  50. package/dist/modules/configValidator.js.map +1 -1
  51. package/dist/modules/restApi.d.ts +1 -1
  52. package/dist/modules/restApi.d.ts.map +1 -1
  53. package/dist/modules/restApi.js +172 -31
  54. package/dist/modules/restApi.js.map +1 -1
  55. package/dist/modules/styles.d.ts +499 -13
  56. package/dist/modules/styles.d.ts.map +1 -1
  57. package/dist/modules/styles.js +555 -31
  58. package/dist/modules/styles.js.map +1 -1
  59. package/dist/modules/utils.d.ts +7 -15
  60. package/dist/modules/utils.d.ts.map +1 -1
  61. package/dist/modules/utils.js +45 -68
  62. package/dist/modules/utils.js.map +1 -1
  63. package/dist/servers/express.d.ts +5 -0
  64. package/dist/servers/express.d.ts.map +1 -1
  65. package/dist/servers/express.js +40 -1
  66. package/dist/servers/express.js.map +1 -1
  67. package/dist/spa/index.html +1 -1
  68. package/dist/spa/package-lock.json +1208 -708
  69. package/dist/spa/package.json +34 -34
  70. package/dist/spa/src/App.vue +59 -174
  71. package/dist/spa/src/adminforth.ts +42 -18
  72. package/dist/spa/src/afcl/AreaChart.vue +0 -1
  73. package/dist/spa/src/afcl/BarChart.vue +2 -2
  74. package/dist/spa/src/afcl/Button.vue +6 -6
  75. package/dist/spa/src/afcl/ButtonGroup.vue +91 -0
  76. package/dist/spa/src/afcl/Card.vue +25 -0
  77. package/dist/spa/src/afcl/Checkbox.vue +21 -13
  78. package/dist/spa/src/afcl/CountryFlag.vue +4 -1
  79. package/dist/spa/src/{components/CustomDatePicker.vue → afcl/DatePicker.vue} +95 -9
  80. package/dist/spa/src/afcl/Dialog.vue +47 -27
  81. package/dist/spa/src/afcl/Dropzone.vue +127 -48
  82. package/dist/spa/src/afcl/Input.vue +14 -6
  83. package/dist/spa/src/afcl/JsonViewer.vue +25 -0
  84. package/dist/spa/src/afcl/LinkButton.vue +3 -3
  85. package/dist/spa/src/afcl/PieChart.vue +5 -5
  86. package/dist/spa/src/afcl/ProgressBar.vue +7 -7
  87. package/dist/spa/src/afcl/Select.vue +82 -34
  88. package/dist/spa/src/afcl/Skeleton.vue +6 -6
  89. package/dist/spa/src/afcl/Table.vue +315 -73
  90. package/dist/spa/src/afcl/Textarea.vue +31 -0
  91. package/dist/spa/src/afcl/Toggle.vue +32 -0
  92. package/dist/spa/src/afcl/Tooltip.vue +28 -18
  93. package/dist/spa/src/afcl/VerticalTabs.vue +16 -7
  94. package/dist/spa/src/afcl/index.ts +6 -3
  95. package/dist/spa/src/components/AcceptModal.vue +48 -14
  96. package/dist/spa/src/components/Breadcrumbs.vue +5 -5
  97. package/dist/spa/src/components/CallActionWrapper.vue +15 -0
  98. package/dist/spa/src/components/ColumnValueInput.vue +38 -18
  99. package/dist/spa/src/components/ColumnValueInputWrapper.vue +4 -3
  100. package/dist/spa/src/components/CustomDateRangePicker.vue +9 -8
  101. package/dist/spa/src/components/CustomRangePicker.vue +37 -21
  102. package/dist/spa/src/components/ErrorMessage.vue +21 -0
  103. package/dist/spa/src/components/Filters.vue +195 -132
  104. package/dist/spa/src/components/GroupsTable.vue +9 -8
  105. package/dist/spa/src/components/MenuLink.vue +90 -23
  106. package/dist/spa/src/components/ResourceForm.vue +94 -51
  107. package/dist/spa/src/components/ResourceListTable.vue +115 -85
  108. package/dist/spa/src/components/ResourceListTableVirtual.vue +114 -80
  109. package/dist/spa/src/components/ShowTable.vue +21 -15
  110. package/dist/spa/src/components/Sidebar.vue +470 -0
  111. package/dist/spa/src/components/SingleSkeletLoader.vue +6 -6
  112. package/dist/spa/src/components/SkeleteLoader.vue +3 -3
  113. package/dist/spa/src/components/ThreeDotsMenu.vue +84 -15
  114. package/dist/spa/src/components/Toast.vue +40 -29
  115. package/dist/spa/src/components/UserMenuSettingsButton.vue +69 -0
  116. package/dist/spa/src/components/ValueRenderer.vue +44 -17
  117. package/dist/spa/src/controls/BoolToggle.vue +34 -0
  118. package/dist/spa/src/i18n.ts +5 -3
  119. package/dist/spa/src/main.ts +1 -1
  120. package/dist/spa/src/renderers/CompactField.vue +1 -1
  121. package/dist/spa/src/renderers/CompactUUID.vue +1 -1
  122. package/dist/spa/src/router/index.ts +8 -0
  123. package/dist/spa/src/shims-vue.d.ts +5 -0
  124. package/dist/spa/src/spa_types/core.ts +13 -1
  125. package/dist/spa/src/stores/core.ts +13 -1
  126. package/dist/spa/src/stores/filters.ts +33 -2
  127. package/dist/spa/src/stores/modal.ts +6 -1
  128. package/dist/spa/src/stores/toast.ts +22 -3
  129. package/dist/spa/src/types/Back.ts +163 -23
  130. package/dist/spa/src/types/Common.ts +91 -32
  131. package/dist/spa/src/types/FrontendAPI.ts +31 -5
  132. package/dist/spa/src/types/adapters/CaptchaAdapter.ts +34 -0
  133. package/dist/spa/src/types/adapters/EmailAdapter.ts +2 -2
  134. package/dist/spa/src/types/adapters/ImageVisionAdapter.ts +30 -0
  135. package/dist/spa/src/types/adapters/KeyValueAdapter.ts +16 -0
  136. package/dist/spa/src/types/adapters/index.ts +8 -0
  137. package/dist/spa/src/utils.ts +291 -11
  138. package/dist/spa/src/views/CreateView.vue +63 -21
  139. package/dist/spa/src/views/EditView.vue +55 -22
  140. package/dist/spa/src/views/ListView.vue +144 -87
  141. package/dist/spa/src/views/LoginView.vue +26 -35
  142. package/dist/spa/src/views/ResourceParent.vue +2 -2
  143. package/dist/spa/src/views/SettingsView.vue +121 -0
  144. package/dist/spa/src/views/ShowView.vue +83 -53
  145. package/dist/spa/src/websocket.ts +6 -1
  146. package/dist/spa/tsconfig.app.json +1 -1
  147. package/dist/spa/vite.config.ts +45 -2
  148. package/dist/types/Back.d.ts +146 -14
  149. package/dist/types/Back.d.ts.map +1 -1
  150. package/dist/types/Back.js +15 -0
  151. package/dist/types/Back.js.map +1 -1
  152. package/dist/types/Common.d.ts +106 -29
  153. package/dist/types/Common.d.ts.map +1 -1
  154. package/dist/types/Common.js.map +1 -1
  155. package/dist/types/FrontendAPI.d.ts +31 -3
  156. package/dist/types/FrontendAPI.d.ts.map +1 -1
  157. package/dist/types/FrontendAPI.js.map +1 -1
  158. package/dist/types/adapters/CaptchaAdapter.d.ts +30 -0
  159. package/dist/types/adapters/CaptchaAdapter.d.ts.map +1 -0
  160. package/dist/types/adapters/CaptchaAdapter.js +5 -0
  161. package/dist/types/adapters/CaptchaAdapter.js.map +1 -0
  162. package/dist/types/adapters/EmailAdapter.d.ts +1 -1
  163. package/dist/types/adapters/ImageVisionAdapter.d.ts +25 -0
  164. package/dist/types/adapters/ImageVisionAdapter.d.ts.map +1 -0
  165. package/dist/types/adapters/ImageVisionAdapter.js +2 -0
  166. package/dist/types/adapters/ImageVisionAdapter.js.map +1 -0
  167. package/dist/types/adapters/KeyValueAdapter.d.ts +10 -0
  168. package/dist/types/adapters/KeyValueAdapter.d.ts.map +1 -0
  169. package/dist/types/adapters/KeyValueAdapter.js +2 -0
  170. package/dist/types/adapters/KeyValueAdapter.js.map +1 -0
  171. package/dist/types/adapters/index.d.ts +9 -0
  172. package/dist/types/adapters/index.d.ts.map +1 -0
  173. package/dist/types/adapters/index.js +2 -0
  174. package/dist/types/adapters/index.js.map +1 -0
  175. package/package.json +4 -2
  176. package/dist/spa/src/types/adapters/index.js +0 -5
@@ -1,55 +1,58 @@
1
1
  <template>
2
- <div class="afcl-select-wrapper relative inline-block" ref="internalSelect"
2
+ <div class="afcl-select afcl-select-wrapper relative inline-block" ref="internalSelect"
3
3
  :class="{'opacity-50': readonly}"
4
4
  >
5
5
  <div class="relative">
6
6
  <input
7
7
  ref="inputEl"
8
8
  type="text"
9
- :readonly="readonly"
9
+ :readonly="readonly || searchDisabled"
10
10
  v-model="search"
11
11
  @click="inputClick"
12
12
  @input="inputInput"
13
- class="afcl-select block w-full pl-3 pr-10 py-2.5 border border-gray-300 rounded-md leading-5 bg-gray-50 placeholder-gray-500 sm:text-sm transition duration-150 ease-in-out dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white focus:ring-lightPrimary focus:border-lightPrimary dark:focus:ring-darkPrimary dark:focus:border-darkPrimary"
13
+ class="block w-full pl-3 pr-10 py-2.5 border border-lightDropownButtonsBorder rounded-md leading-5 bg-lightDropdownButtonsBackground
14
+ placeholder-lightDropdownButtonsPlaceholderText text-lightDropdownButtonsText sm:text-sm transition duration-150 ease-in-out dark:bg-darkDropdownButtonsBackground dark:border-darkDropdownButtonsBorder dark:placeholder-darkDropdownButtonsPlaceholderText
15
+ dark:text-darkDropdownButtonsText focus:ring-lightPrimary focus:border-lightPrimary dark:focus:ring-darkPrimary dark:focus:border-darkPrimary"
14
16
  autocomplete="off" data-custom="no-autofill"
15
17
  :placeholder="
16
18
  selectedItems.length && !multiple ? '' : (showDropdown ? $t('Search') : placeholder || $t('Select...'))
17
19
  "
18
20
  />
19
21
 
20
- <div v-if="!multiple && selectedItems.length" class="absolute pointer-events-none inset-y-0 left-2 flex items-center pr-2 px-1">
22
+ <div v-if="!multiple && selectedItems.length" class="text-lightDropdownButtonsText dark:text-darkDropdownButtonsText absolute pointer-events-none inset-y-0 left-2 flex items-center pr-2 px-1">
21
23
  <slot
22
24
  name="selected-item"
23
25
  :option="selectedItems[0]"
24
26
  ></slot>
25
- <span v-if="!$slots['selected-item']" class="text-lightPrimary dark:text-white font-medium ">
27
+ <span v-if="!$slots['selected-item']" class="text-lightDropdownButtonsText dark:text-darkDropdownButtonsText font-medium ">
26
28
  {{ selectedItems[0]?.label }}
27
29
  </span>
28
30
  </div>
29
31
 
30
32
  <div class="absolute inset-y-0 right-2 flex items-center pointer-events-none">
31
33
  <!-- triangle icon -->
32
- <IconCaretDownSolid class="h-5 w-5 text-lightPrimary dark:text-gray-400 opacity-50 transition duration-150 ease-in"
34
+ <IconCaretDownSolid class="h-5 w-5 text-lightPrimary dark:text-darkPrimary opacity-50 transition duration-150 ease-in"
33
35
  :class="{ 'transform rotate-180': showDropdown }"
34
36
  />
35
37
  </div>
36
38
  </div>
37
- <teleport to="body" v-if="teleportToBody && showDropdown">
38
- <div ref="dropdownEl" :style="getDropdownPosition" :class="{'shadow-none': isTop}"
39
- class="fixed z-[5] w-full bg-white shadow-lg dark:shadow-black dark:bg-gray-700
40
- 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
+ <teleport to="body" v-if="(teleportToBody || teleportToTop) && showDropdown">
40
+ <div ref="dropdownEl" :style="getDropdownPosition" :class="{'shadow-none': isTop, 'z-10': teleportToBody, 'z-[1000]': teleportToTop}"
41
+ class="fixed w-full bg-lightDropdownOptionsBackground shadow-lg dark:shadow-black dark:bg-darkDropdownOptionsBackground
42
+ 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"
43
+ @scroll="handleDropdownScroll">
41
44
  <div
42
45
  v-for="item in filteredItems"
43
46
  :key="item.value"
44
- class="px-4 py-2 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-400"
45
- :class="{ 'bg-lightPrimaryOpacity dark:bg-darkPrimaryOpacity': selectedItems.includes(item) }"
47
+ class="px-4 py-2 cursor-pointer hover:bg-lightDropdownOptionsHoverBackground dark:hover:bg-darkDropdownOptionsHoverBackground text-lightDropdownOptionsText dark:text-darkDropdownOptionsText"
48
+ :class="{ 'bg-lightDropdownPicked dark:bg-darkDropdownPicked': selectedItems.includes(item) }"
46
49
  @click="toogleItem(item)"
47
50
  >
48
51
  <slot name="item" :option="item"></slot>
49
52
  <label v-if="!$slots.item" :for="item.value">{{ item.label }}</label>
50
53
  </div>
51
- <div v-if="!filteredItems.length" class="px-4 py-2 cursor-pointer text-gray-400 dark:text-gray-300">
52
- {{ options.length ? $t('No results found') : $t('No items here') }}
54
+ <div v-if="!filteredItems.length" class="px-4 py-2 cursor-pointer text-lightDropdownOptionsText dark:text-darkDropdownOptionsText">
55
+ {{ $t('No results found') }}
53
56
  </div>
54
57
 
55
58
  <div v-if="$slots['extra-item']" class="px-4 py-2 dark:text-gray-400">
@@ -58,24 +61,24 @@
58
61
  </div>
59
62
  </teleport>
60
63
 
61
- <div v-if="!teleportToBody && showDropdown" ref="dropdownEl" :style="dropdownStyle" :class="{'shadow-none': isTop}"
62
- class="absolute z-10 mt-1 w-full bg-white shadow-lg dark:shadow-black dark:bg-gray-700
63
- 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">
64
+ <div v-if="!teleportToBody && !teleportToTop && showDropdown" ref="dropdownEl" :style="dropdownStyle" :class="{'shadow-none': isTop}"
65
+ class="absolute z-10 mt-1 w-full bg-lightDropdownOptionsBackground shadow-lg text-lightDropdownButtonsText dark:shadow-black dark:bg-darkDropdownOptionsBackground
66
+ 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"
67
+ @scroll="handleDropdownScroll">
64
68
  <div
65
69
  v-for="item in filteredItems"
66
70
  :key="item.value"
67
- class="px-4 py-2 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-400"
68
- :class="{ 'bg-lightPrimaryOpacity dark:bg-darkPrimaryOpacity': selectedItems.includes(item) }"
71
+ class="px-4 py-2 cursor-pointer text-lightDropdownOptionsText hover:bg-lightDropdownOptionsHoverBackground dark:hover:bg-darkDropdownOptionsHoverBackground dark:text-darkDropdownOptionsText"
72
+ :class="{ 'bg-lightDropdownPicked dark:bg-darkDropdownPicked': selectedItems.includes(item) }"
69
73
  @click="toogleItem(item)"
70
74
  >
71
75
  <slot name="item" :option="item"></slot>
72
76
  <label v-if="!$slots.item" :for="item.value">{{ item.label }}</label>
73
77
  </div>
74
- <div v-if="!filteredItems.length" class="px-4 py-2 cursor-pointer text-gray-400 dark:text-gray-300">
75
- {{ options.length ? $t('No results found') : $t('No items here') }}
78
+ <div v-if="!filteredItems.length" class="px-4 py-2 cursor-pointer text-lightDropdownOptionsText dark:text-darkDropdownOptionsText">
79
+ {{ options?.length ? $t('No results found') : $t('No items here') }}
76
80
  </div>
77
-
78
- <div v-if="$slots['extra-item']" class="px-4 py-2 dark:text-gray-400">
81
+ <div v-if="$slots['extra-item']" class="px-4 py-2 dark:text-darkDropdownOptionsText">
79
82
  <slot name="extra-item"></slot>
80
83
  </div>
81
84
 
@@ -87,12 +90,12 @@
87
90
  <template v-for="item in selectedItems" :key="`afcl-select-${item.value}`">
88
91
  <slot name="selected-item" :item="item"></slot>
89
92
  <div v-if="!$slots['selected-item']"
90
- class="bg-lightPrimaryOpacity text-lightPrimary text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-darkPrimaryOpacity dark:text-darkPrimary">
93
+ class="bg-lightDropdownMultipleSelectBackground text-lightDropdownMultipleSelectText text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-darkDropdownMultipleSelectBackground dark:text-darkDropdownMultipleSelectText">
91
94
  <span>{{ item.label }}</span>
92
95
  <button
93
96
  type="button"
94
97
  @click="toogleItem(item)"
95
- class="z-index-100 flex-shrink-0 ml-1 h-4 w-4 -mr-1 rounded-full inline-flex items-center justify-center text-gray-400 hover:text-gray-500 focus:outline-none focus:text-gray-500 focus:bg-gray-100"
98
+ class="z-index-100 flex-shrink-0 ml-1 h-4 w-4 -mr-1 rounded-full inline-flex items-center justify-center text-lightDropdownMultipleSelectIcon hover:text-lightDropdownMultipleSelectIconHover dark:text-darkDropdownMultipleSelectIcon dark:hover:text-darkDropdownMultipleSelectIconHover focus:outline-none focus:text-lightDropdownMultipleSelectIconFocus focus:bg-lightDropdownMultipleSelectIconFocusBackground dark:focus:text-darkDropdownMultipleSelectIconFocus dark:focus:bg-darkDropdownMultipleSelectIconFocusBackground"
96
99
  >
97
100
  <span class="sr-only">{{ $t('Remove item') }}</span>
98
101
  <svg class="h-2 w-2" stroke="currentColor" fill="none" viewBox="0 0 8 8">
@@ -111,14 +114,15 @@
111
114
  </template>
112
115
 
113
116
  <script setup lang="ts">
114
- import { ref, computed, onMounted, onUnmounted, watch, type Ref } from 'vue';
117
+ import { ref, computed, onMounted, onUnmounted, watch, nextTick,type PropType, type Ref } from 'vue';
115
118
  import { IconCaretDownSolid } from '@iconify-prerendered/vue-flowbite';
116
119
  import { useElementSize } from '@vueuse/core'
117
120
 
118
121
  const props = defineProps({
119
122
  options: Array,
120
123
  modelValue: {
121
- default: undefined,
124
+ type: Array as PropType<(string | number)[]>,
125
+ default: () => [],
122
126
  },
123
127
  multiple: {
124
128
  type: Boolean,
@@ -132,13 +136,25 @@ const props = defineProps({
132
136
  type: Boolean,
133
137
  default: false,
134
138
  },
139
+ searchDisabled: {
140
+ type: Boolean,
141
+ default: false,
142
+ },
135
143
  teleportToBody: {
136
144
  type: Boolean,
137
145
  default: false,
138
146
  },
147
+ teleportToTop: {
148
+ type: Boolean,
149
+ default: false,
150
+ },
151
+ searchDebounceMs: {
152
+ type: Number,
153
+ default: 300,
154
+ },
139
155
  });
140
156
 
141
- const emit = defineEmits(['update:modelValue']);
157
+ const emit = defineEmits(['update:modelValue', 'scroll-near-end', 'search']);
142
158
 
143
159
  const search = ref('');
144
160
  const showDropdown = ref(false);
@@ -153,30 +169,39 @@ const dropdownStyle = ref<{ top?: string; }>({
153
169
 
154
170
  const selectedItems: Ref<any[]> = ref([]);
155
171
  const internalSelect = ref<HTMLElement | null>(null);
172
+ let searchDebounceHandle: ReturnType<typeof setTimeout> | null = null;
156
173
 
157
174
  function inputInput() {
158
175
  if (!props.multiple && selectedItems.value.length) {
159
176
  selectedItems.value = [];
160
177
  emit('update:modelValue', null);
161
178
  }
179
+ if (!props.searchDisabled) {
180
+ if (searchDebounceHandle) {
181
+ clearTimeout(searchDebounceHandle);
182
+ }
183
+ searchDebounceHandle = setTimeout(() => {
184
+ emit('search', search.value);
185
+ }, props.searchDebounceMs);
186
+ }
162
187
  }
163
188
 
164
189
  function updateFromProps() {
165
190
  if (props.modelValue !== undefined) {
166
191
  if (!props.multiple) {
167
- const el = props.options.find(item => item.value === props.modelValue);
192
+ const el = props.options?.find((item: any) => item.value === props.modelValue);
168
193
  if (el) {
169
194
  selectedItems.value = [el];
170
195
  } else {
171
196
  selectedItems.value = [];
172
197
  }
173
198
  } else {
174
- selectedItems.value = props.options.filter(item => props.modelValue.includes(item.value));
199
+ selectedItems.value = props.options?.filter((item: any) => props.modelValue?.includes(item.value)) || [];
175
200
  }
176
201
  }
177
202
  }
178
203
 
179
- function inputClick() {
204
+ async function inputClick() {
180
205
  if (props.readonly) return;
181
206
  // Toggle local dropdown
182
207
  showDropdown.value = !showDropdown.value;
@@ -184,6 +209,11 @@ function inputClick() {
184
209
  if (!showDropdown.value && !search.value) {
185
210
  search.value = '';
186
211
  }
212
+
213
+ if(props.teleportToBody){
214
+ await nextTick();
215
+ handleScroll();
216
+ }
187
217
  }
188
218
 
189
219
  watch(
@@ -221,6 +251,15 @@ const handleScroll = () => {
221
251
  }
222
252
  };
223
253
 
254
+ const handleDropdownScroll = (event: Event) => {
255
+ const target = event.target as HTMLElement;
256
+ const threshold = 10; // pixels from bottom
257
+
258
+ if (target.scrollTop + target.clientHeight >= target.scrollHeight - threshold) {
259
+ emit('scroll-near-end');
260
+ }
261
+ };
262
+
224
263
  onMounted(() => {
225
264
  updateFromProps();
226
265
 
@@ -240,8 +279,13 @@ onMounted(() => {
240
279
  }
241
280
  });
242
281
 
243
- const filteredItems = computed(() => {
244
- return props.options.filter(item =>
282
+ const filteredItems: Ref<any[]> = computed(() => {
283
+
284
+ if (props.searchDisabled) {
285
+ return props.options || [];
286
+ }
287
+
288
+ return (props.options || []).filter((item: any) =>
245
289
  item.label.toLowerCase().includes(search.value.toLowerCase())
246
290
  );
247
291
  });
@@ -262,7 +306,7 @@ const removeClickListener = () => {
262
306
  document.removeEventListener('click', handleClickOutside);
263
307
  };
264
308
 
265
- const toogleItem = (item) => {
309
+ const toogleItem = (item: any) => {
266
310
  if (selectedItems.value.includes(item)) {
267
311
  selectedItems.value = selectedItems.value.filter(i => i.value !== item.value);
268
312
  } else {
@@ -295,6 +339,10 @@ onUnmounted(() => {
295
339
  if (props.teleportToBody) {
296
340
  window.removeEventListener('scroll', handleScroll, true);
297
341
  }
342
+ if (searchDebounceHandle) {
343
+ clearTimeout(searchDebounceHandle);
344
+ searchDebounceHandle = null;
345
+ }
298
346
  });
299
347
 
300
348
  const getDropdownPosition = computed(() => {
@@ -1,20 +1,20 @@
1
1
  <template>
2
- <div v-if="type === 'image'" role="status" class="flex animate-pulse items-center justify-center bg-gray-300 rounded dark:bg-gray-700">
3
- <svg class="w-10 h-10 text-gray-200 dark:text-gray-600" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 18">
2
+ <div v-if="type === 'image'" role="status" class="flex animate-pulse items-center justify-center bg-lightSkeletonBackgroundColor rounded dark:bg-darkSkeletonBackgroundColor">
3
+ <svg class="w-10 h-10 text-lightSkeletonIconColor dark:text-darkSkeletonIconColor" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 18">
4
4
  <path d="M18 0H2a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2Zm-5.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Zm4.376 10.481A1 1 0 0 1 16 15H4a1 1 0 0 1-.895-1.447l3.5-7A1 1 0 0 1 7.468 6a.965.965 0 0 1 .9.5l2.775 4.757 1.546-1.887a1 1 0 0 1 1.618.1l2.541 4a1 1 0 0 1 .028 1.011Z"/>
5
5
  </svg>
6
6
  </div>
7
- <div v-else-if="type === 'video'" role="status" class="flex items-center justify-center max-w-sm bg-gray-300 rounded-lg animate-pulse dark:bg-gray-700">
8
- <svg class="w-10 h-10 text-gray-200 dark:text-gray-600" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 20">
7
+ <div v-else-if="type === 'video'" role="status" class="flex items-center justify-center max-w-sm bg-lightSkeletonBackgroundColor rounded-lg animate-pulse dark:bg-darkSkeletonBackgroundColor">
8
+ <svg class="w-10 h-10 text-lightSkeletonIconColor dark:text-darkSkeletonIconColor" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 20">
9
9
  <path d="M5 5V.13a2.96 2.96 0 0 0-1.293.749L.879 3.707A2.98 2.98 0 0 0 .13 5H5Z"/>
10
10
  <path d="M14.066 0H7v5a2 2 0 0 1-2 2H0v11a1.97 1.97 0 0 0 1.934 2h12.132A1.97 1.97 0 0 0 16 18V2a1.97 1.97 0 0 0-1.934-2ZM9 13a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-2a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2Zm4 .382a1 1 0 0 1-1.447.894L10 13v-2l1.553-1.276a1 1 0 0 1 1.447.894v2.764Z"/>
11
11
  </svg>
12
12
  <span class="sr-only">Loading...</span>
13
13
  </div>
14
- <svg v-else-if="type === 'avatar'" class="me-3 animate-pulse text-gray-200 dark:text-gray-700" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
14
+ <svg v-else-if="type === 'avatar'" class="me-3 animate-pulse text-lightSkeletonIconColor dark:text-darkSkeletonBackgroundColor" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
15
15
  <path d="M10 0a10 10 0 1 0 10 10A10.011 10.011 0 0 0 10 0Zm0 5a3 3 0 1 1 0 6 3 3 0 0 1 0-6Zm0 13a8.949 8.949 0 0 1-4.951-1.488A3.987 3.987 0 0 1 9 13h2a3.987 3.987 0 0 1 3.951 3.512A8.949 8.949 0 0 1 10 18Z"/>
16
16
  </svg>
17
- <div v-else role="status" class="flex items-center justify-center animate-pulse bg-gray-200 rounded-full dark:bg-gray-700">
17
+ <div v-else role="status" class="flex items-center justify-center animate-pulse bg-lightSkeletonIconColor rounded-full dark:bg-darkSkeletonBackgroundColor">
18
18
  <span class="sr-only">Loading...</span>
19
19
  </div>
20
20
  </template>