11<script setup lang="ts">
22import type { VNode } from ' vue' ;
33
4- import { computed , h , ref , watch , watchEffect } from ' vue' ;
4+ import { computed , ref , watch , watchEffect } from ' vue' ;
55
66import { usePagination } from ' @vben/hooks' ;
77import { EmptyIcon , Grip , listIcons } from ' @vben/icons' ;
88import { $t } from ' @vben/locales' ;
99
1010import {
1111 Button ,
12+ Input ,
1213 Pagination ,
1314 PaginationEllipsis ,
1415 PaginationFirst ,
@@ -22,11 +23,16 @@ import {
2223 VbenPopover ,
2324} from ' @vben-core/shadcn-ui' ;
2425
25- import { refDebounced } from ' @vueuse/core' ;
26+ import { refDebounced , watchDebounced } from ' @vueuse/core' ;
27+
28+ import { fetchIconsData } from ' ./icons' ;
2629
2730interface Props {
2831 pageSize? : number ;
32+ /** 图标集的名字 */
2933 prefix? : string ;
34+ /** 是否自动请求API以获得图标集的数据.提供prefix时有效 */
35+ autoFetchApi? : boolean ;
3036 /**
3137 * 图标列表
3238 */
@@ -39,16 +45,19 @@ interface Props {
3945 modelValueProp? : string ;
4046 /** 图标样式 */
4147 iconClass? : string ;
48+ type? : ' icon' | ' input' ;
4249}
4350
4451const props = withDefaults (defineProps <Props >(), {
4552 prefix: ' ant-design' ,
4653 pageSize: 36 ,
4754 icons : () => [],
48- inputComponent : () => h (' div' ),
4955 iconSlot: ' default' ,
5056 iconClass: ' size-4' ,
51- modelValueProp: ' value' ,
57+ autoFetchApi: true ,
58+ modelValueProp: ' modelValue' ,
59+ inputComponent: undefined ,
60+ type: ' input' ,
5261});
5362
5463const emit = defineEmits <{
@@ -62,9 +71,28 @@ const currentSelect = ref('');
6271const currentPage = ref (1 );
6372const keyword = ref (' ' );
6473const keywordDebounce = refDebounced (keyword , 300 );
74+ const innerIcons = ref <string []>([]);
75+
76+ watchDebounced (
77+ () => props .prefix ,
78+ async (prefix ) => {
79+ if (prefix && prefix !== ' svg' && props .autoFetchApi ) {
80+ innerIcons .value = await fetchIconsData (prefix );
81+ }
82+ },
83+ { immediate: true , debounce: 500 , maxWait: 1000 },
84+ );
85+
6586const currentList = computed (() => {
6687 try {
6788 if (props .prefix ) {
89+ if (
90+ props .prefix !== ' svg' &&
91+ props .autoFetchApi &&
92+ props .icons .length === 0
93+ ) {
94+ return innerIcons .value ;
95+ }
6896 const icons = listIcons (' ' , props .prefix );
6997 if (icons .length === 0 ) {
7098 console .warn (` No icons found for prefix: ${props .prefix } ` );
@@ -146,18 +174,61 @@ defineExpose({ toggleOpenState, open, close });
146174 content-class =" p-0 pt-3"
147175 >
148176 <template #trigger >
149- <component
150- :is =" inputComponent"
151- :[modelValueProp] =" currentSelect"
152- :placeholder =" $t('ui.iconPicker.placeholder')"
153- >
154- <template #[iconSlot ]>
155- <VbenIcon :icon =" currentSelect || Grip" class =" size-4" />
156- </template >
157- </component >
177+ <template v-if =" props .type === ' input' " >
178+ <component
179+ v-if =" props.inputComponent"
180+ :is =" inputComponent"
181+ :[modelValueProp] =" currentSelect"
182+ :placeholder =" $t('ui.iconPicker.placeholder')"
183+ role =" combobox"
184+ :aria-label =" $t('ui.iconPicker.placeholder')"
185+ aria-expanded =" visible"
186+ v-bind =" $attrs"
187+ >
188+ <template #[iconSlot ]>
189+ <VbenIcon
190+ :icon =" currentSelect || Grip"
191+ class =" size-4"
192+ aria-hidden =" true"
193+ />
194+ </template >
195+ </component >
196+ <div class =" relative w-full" v-else >
197+ <Input
198+ v-bind =" $attrs"
199+ v-model =" currentSelect"
200+ :placeholder =" $t('ui.iconPicker.placeholder')"
201+ class =" h-8 w-full pr-8"
202+ role =" combobox"
203+ :aria-label =" $t('ui.iconPicker.placeholder')"
204+ aria-expanded =" visible"
205+ />
206+ <VbenIcon
207+ :icon =" currentSelect || Grip"
208+ class =" absolute right-1 top-1 size-6"
209+ aria-hidden =" true"
210+ />
211+ </div >
212+ </template >
213+ <VbenIcon
214+ :icon =" currentSelect || Grip"
215+ v-else
216+ class =" size-4"
217+ v-bind =" $attrs"
218+ />
158219 </template >
159220 <div class =" mb-2 flex w-full" >
160- <component :is =" inputComponent" v-bind =" searchInputProps" />
221+ <component
222+ v-if =" inputComponent"
223+ :is =" inputComponent"
224+ v-bind =" searchInputProps"
225+ />
226+ <Input
227+ v-else
228+ class =" mx-2 h-8 w-full"
229+ :placeholder =" $t('ui.iconPicker.search')"
230+ v-model =" keyword"
231+ />
161232 </div >
162233
163234 <template v-if =" paginationList .length > 0 " >
0 commit comments