|
|
|
|
@ -1,11 +1,15 @@
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import type { VNode } from 'vue';
|
|
|
|
|
|
|
|
|
|
import { computed, ref, watch, watchEffect } from 'vue';
|
|
|
|
|
|
|
|
|
|
import { usePagination } from '@vben/hooks';
|
|
|
|
|
import { EmptyIcon, Grip, listIcons } from '@vben/icons';
|
|
|
|
|
import { $t } from '@vben/locales';
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
Button,
|
|
|
|
|
Input,
|
|
|
|
|
Pagination,
|
|
|
|
|
PaginationEllipsis,
|
|
|
|
|
PaginationFirst,
|
|
|
|
|
@ -18,12 +22,17 @@ import {
|
|
|
|
|
VbenIconButton,
|
|
|
|
|
VbenPopover,
|
|
|
|
|
} from '@vben-core/shadcn-ui';
|
|
|
|
|
import { refDebounced } from '@vueuse/core';
|
|
|
|
|
import { computed, h, ref, watch, watchEffect } from 'vue';
|
|
|
|
|
|
|
|
|
|
import { refDebounced, watchDebounced } from '@vueuse/core';
|
|
|
|
|
|
|
|
|
|
import { fetchIconsData } from './icons';
|
|
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
|
pageSize?: number;
|
|
|
|
|
/** 图标集的名字 */
|
|
|
|
|
prefix?: string;
|
|
|
|
|
/** 是否自动请求API以获得图标集的数据.提供prefix时有效 */
|
|
|
|
|
autoFetchApi?: boolean;
|
|
|
|
|
/**
|
|
|
|
|
* 图标列表
|
|
|
|
|
*/
|
|
|
|
|
@ -36,16 +45,19 @@ interface Props {
|
|
|
|
|
modelValueProp?: string;
|
|
|
|
|
/** 图标样式 */
|
|
|
|
|
iconClass?: string;
|
|
|
|
|
type?: 'icon' | 'input';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
|
|
|
prefix: 'ant-design',
|
|
|
|
|
pageSize: 36,
|
|
|
|
|
icons: () => [],
|
|
|
|
|
inputComponent: () => h('div'),
|
|
|
|
|
iconSlot: 'default',
|
|
|
|
|
iconClass: 'size-4',
|
|
|
|
|
modelValueProp: 'value',
|
|
|
|
|
autoFetchApi: true,
|
|
|
|
|
modelValueProp: 'modelValue',
|
|
|
|
|
inputComponent: undefined,
|
|
|
|
|
type: 'input',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const emit = defineEmits<{
|
|
|
|
|
@ -59,9 +71,28 @@ const currentSelect = ref('');
|
|
|
|
|
const currentPage = ref(1);
|
|
|
|
|
const keyword = ref('');
|
|
|
|
|
const keywordDebounce = refDebounced(keyword, 300);
|
|
|
|
|
const innerIcons = ref<string[]>([]);
|
|
|
|
|
|
|
|
|
|
watchDebounced(
|
|
|
|
|
() => props.prefix,
|
|
|
|
|
async (prefix) => {
|
|
|
|
|
if (prefix && prefix !== 'svg' && props.autoFetchApi) {
|
|
|
|
|
innerIcons.value = await fetchIconsData(prefix);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{ immediate: true, debounce: 500, maxWait: 1000 },
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const currentList = computed(() => {
|
|
|
|
|
try {
|
|
|
|
|
if (props.prefix) {
|
|
|
|
|
if (
|
|
|
|
|
props.prefix !== 'svg' &&
|
|
|
|
|
props.autoFetchApi &&
|
|
|
|
|
props.icons.length === 0
|
|
|
|
|
) {
|
|
|
|
|
return innerIcons.value;
|
|
|
|
|
}
|
|
|
|
|
const icons = listIcons('', props.prefix);
|
|
|
|
|
if (icons.length === 0) {
|
|
|
|
|
console.warn(`No icons found for prefix: ${props.prefix}`);
|
|
|
|
|
@ -143,18 +174,61 @@ defineExpose({ toggleOpenState, open, close });
|
|
|
|
|
content-class="p-0 pt-3"
|
|
|
|
|
>
|
|
|
|
|
<template #trigger>
|
|
|
|
|
<component
|
|
|
|
|
:is="inputComponent"
|
|
|
|
|
:[modelValueProp]="currentSelect"
|
|
|
|
|
:placeholder="$t('ui.iconPicker.placeholder')"
|
|
|
|
|
>
|
|
|
|
|
<template #[iconSlot]>
|
|
|
|
|
<VbenIcon :icon="currentSelect || Grip" class="size-4" />
|
|
|
|
|
</template>
|
|
|
|
|
</component>
|
|
|
|
|
<template v-if="props.type === 'input'">
|
|
|
|
|
<component
|
|
|
|
|
v-if="props.inputComponent"
|
|
|
|
|
:is="inputComponent"
|
|
|
|
|
:[modelValueProp]="currentSelect"
|
|
|
|
|
:placeholder="$t('ui.iconPicker.placeholder')"
|
|
|
|
|
role="combobox"
|
|
|
|
|
:aria-label="$t('ui.iconPicker.placeholder')"
|
|
|
|
|
aria-expanded="visible"
|
|
|
|
|
v-bind="$attrs"
|
|
|
|
|
>
|
|
|
|
|
<template #[iconSlot]>
|
|
|
|
|
<VbenIcon
|
|
|
|
|
:icon="currentSelect || Grip"
|
|
|
|
|
class="size-4"
|
|
|
|
|
aria-hidden="true"
|
|
|
|
|
/>
|
|
|
|
|
</template>
|
|
|
|
|
</component>
|
|
|
|
|
<div class="relative w-full" v-else>
|
|
|
|
|
<Input
|
|
|
|
|
v-bind="$attrs"
|
|
|
|
|
v-model="currentSelect"
|
|
|
|
|
:placeholder="$t('ui.iconPicker.placeholder')"
|
|
|
|
|
class="h-8 w-full pr-8"
|
|
|
|
|
role="combobox"
|
|
|
|
|
:aria-label="$t('ui.iconPicker.placeholder')"
|
|
|
|
|
aria-expanded="visible"
|
|
|
|
|
/>
|
|
|
|
|
<VbenIcon
|
|
|
|
|
:icon="currentSelect || Grip"
|
|
|
|
|
class="absolute right-1 top-1 size-6"
|
|
|
|
|
aria-hidden="true"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<VbenIcon
|
|
|
|
|
:icon="currentSelect || Grip"
|
|
|
|
|
v-else
|
|
|
|
|
class="size-4"
|
|
|
|
|
v-bind="$attrs"
|
|
|
|
|
/>
|
|
|
|
|
</template>
|
|
|
|
|
<div class="mb-2 flex w-full">
|
|
|
|
|
<component :is="inputComponent" v-bind="searchInputProps" />
|
|
|
|
|
<component
|
|
|
|
|
v-if="inputComponent"
|
|
|
|
|
:is="inputComponent"
|
|
|
|
|
v-bind="searchInputProps"
|
|
|
|
|
/>
|
|
|
|
|
<Input
|
|
|
|
|
v-else
|
|
|
|
|
class="mx-2 h-8 w-full"
|
|
|
|
|
:placeholder="$t('ui.iconPicker.search')"
|
|
|
|
|
v-model="keyword"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<template v-if="paginationList.length > 0">
|
|
|
|
|
|