发布 v1.10 #5
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
import { createIconifyIcon } from '@easyflow/icons';
|
import { EasyFlowIcon } from '@easyflow-core/shadcn-ui';
|
||||||
|
|
||||||
import { Delete, MoreFilled, Plus } from '@element-plus/icons-vue';
|
import { Delete, MoreFilled, Plus } from '@element-plus/icons-vue';
|
||||||
import {
|
import {
|
||||||
@@ -123,7 +123,7 @@ function getTree() {
|
|||||||
</ElTableColumn>
|
</ElTableColumn>
|
||||||
<ElTableColumn prop="menuIcon" :label="$t('sysMenu.menuIcon')">
|
<ElTableColumn prop="menuIcon" :label="$t('sysMenu.menuIcon')">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<component class="size-5" :is="createIconifyIcon(row.menuIcon)" />
|
<EasyFlowIcon class="size-5" :icon="row.menuIcon" fallback />
|
||||||
</template>
|
</template>
|
||||||
</ElTableColumn>
|
</ElTableColumn>
|
||||||
<ElTableColumn prop="isShow" :label="$t('sysMenu.isShow')">
|
<ElTableColumn prop="isShow" :label="$t('sysMenu.isShow')">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { defineComponent, h } from 'vue';
|
import { defineComponent, h } from 'vue';
|
||||||
|
|
||||||
import { Icon } from '@iconify/vue';
|
import { Icon } from '@iconify/vue/offline';
|
||||||
|
|
||||||
function createIconifyIcon(icon: string) {
|
function createIconifyIcon(icon: string) {
|
||||||
return defineComponent({
|
return defineComponent({
|
||||||
|
|||||||
@@ -2,19 +2,82 @@ import antDesign from '@iconify/json/json/ant-design.json';
|
|||||||
import ep from '@iconify/json/json/ep.json';
|
import ep from '@iconify/json/json/ep.json';
|
||||||
import mdi from '@iconify/json/json/mdi.json';
|
import mdi from '@iconify/json/json/mdi.json';
|
||||||
import {
|
import {
|
||||||
addCollection,
|
addCollection as addOfflineCollection,
|
||||||
addIcon,
|
addIcon as addOfflineIcon,
|
||||||
Icon as IconifyIcon,
|
Icon as IconifyIcon,
|
||||||
listIcons,
|
} from '@iconify/vue/offline';
|
||||||
} from '@iconify/vue';
|
import type {
|
||||||
|
IconifyIcon as IconifyIconData,
|
||||||
|
IconifyJSON,
|
||||||
|
} from '@iconify/vue/offline';
|
||||||
|
|
||||||
addCollection(antDesign);
|
import { LOCAL_ICON_DATA } from './local-icons';
|
||||||
addCollection(ep);
|
|
||||||
addCollection(mdi);
|
const LOCAL_ICON_NAMES = new Set<string>();
|
||||||
|
|
||||||
|
function getCollectionPrefix(data: IconifyJSON, prefix?: string | boolean) {
|
||||||
|
if (prefix === false) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return `${typeof prefix === 'string' ? prefix : data.prefix}:`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an icon collection in the offline Iconify store and local name index.
|
||||||
|
*/
|
||||||
|
function registerCollection(data: IconifyJSON, prefix?: string | boolean) {
|
||||||
|
addOfflineCollection(data, prefix);
|
||||||
|
const iconPrefix = getCollectionPrefix(data, prefix);
|
||||||
|
Object.keys(data.icons).forEach((name) => {
|
||||||
|
LOCAL_ICON_NAMES.add(`${iconPrefix}${name}`);
|
||||||
|
});
|
||||||
|
Object.keys(data.aliases ?? {}).forEach((name) => {
|
||||||
|
LOCAL_ICON_NAMES.add(`${iconPrefix}${name}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a single icon in the offline Iconify store.
|
||||||
|
*/
|
||||||
|
function registerIcon(name: string, data: IconifyIconData) {
|
||||||
|
addOfflineIcon(name, data);
|
||||||
|
LOCAL_ICON_NAMES.add(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return locally registered icon names for the requested collection.
|
||||||
|
*/
|
||||||
|
function listIcons(provider = '', prefix = ''): string[] {
|
||||||
|
const providerPrefix = provider ? `@${provider}:` : '';
|
||||||
|
const iconPrefix = prefix ? `${prefix}:` : '';
|
||||||
|
return [...LOCAL_ICON_NAMES].filter((name) =>
|
||||||
|
name.startsWith(`${providerPrefix}${iconPrefix}`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether an icon name is available without a network request.
|
||||||
|
*/
|
||||||
|
function isLocalIcon(name: string) {
|
||||||
|
return LOCAL_ICON_NAMES.has(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
registerCollection(antDesign);
|
||||||
|
registerCollection(ep);
|
||||||
|
registerCollection(mdi);
|
||||||
|
Object.entries(LOCAL_ICON_DATA).forEach(([name, data]) => {
|
||||||
|
registerIcon(name, data);
|
||||||
|
});
|
||||||
|
|
||||||
export * from './create-icon';
|
export * from './create-icon';
|
||||||
|
|
||||||
export * from './lucide';
|
export * from './lucide';
|
||||||
|
|
||||||
export type { IconifyIcon as IconifyIconStructure } from '@iconify/vue';
|
export type { IconifyIcon as IconifyIconStructure } from '@iconify/vue/offline';
|
||||||
export { addCollection, addIcon, IconifyIcon, listIcons };
|
export {
|
||||||
|
IconifyIcon,
|
||||||
|
isLocalIcon,
|
||||||
|
listIcons,
|
||||||
|
registerCollection as addCollection,
|
||||||
|
registerIcon as addIcon,
|
||||||
|
};
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import type { IconifyIcon } from '@iconify/vue/offline';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Icon names persisted in routes or database migrations that are not part of
|
||||||
|
* the small set of full local collections loaded by the application.
|
||||||
|
*/
|
||||||
|
export const LOCAL_ICON_DATA: Record<string, IconifyIcon> = {
|
||||||
|
'carbon:workspace': {
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
body: '<path fill="currentColor" d="M16 17v8H6v-8zm0-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-8a2 2 0 0 0-2-2m11-9v5H17V6zm0-2H17a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2m0 13v5h-5v-5zm0-2h-5a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h5a2 2 0 0 0 2-2v-5a2 2 0 0 0-2-2M11 6v5H6V6zm0-2H6a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h5a2 2 0 0 0 2-2V6z"/>',
|
||||||
|
},
|
||||||
|
'lucide:bot': {
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
body: '<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2m16 0h2m-7-1v2m-6-2v2"/></g>',
|
||||||
|
},
|
||||||
|
'lucide:message-square': {
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
body: '<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"/>',
|
||||||
|
},
|
||||||
|
'lucide:badge-check': {
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
body: '<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M3.85 8.62a4 4 0 0 1 4.78-4.77a4 4 0 0 1 6.74 0a4 4 0 0 1 4.78 4.78a4 4 0 0 1 0 6.74a4 4 0 0 1-4.77 4.78a4 4 0 0 1-6.75 0a4 4 0 0 1-4.78-4.77a4 4 0 0 1 0-6.76"/><path d="m9 12l2 2l4-4"/></g>',
|
||||||
|
},
|
||||||
|
'lucide:user': {
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
body: '<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></g>',
|
||||||
|
},
|
||||||
|
'lucide:lock': {
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
body: '<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></g>',
|
||||||
|
},
|
||||||
|
'lucide:area-chart': {
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
body: '<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M3 3v18h18"/><path d="M7 12v5h12V8l-5 5l-4-4Z"/></g>',
|
||||||
|
},
|
||||||
|
'lucide:layout-dashboard': {
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
body: '<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><rect width="7" height="9" x="3" y="3" rx="1"/><rect width="7" height="5" x="14" y="3" rx="1"/><rect width="7" height="9" x="14" y="12" rx="1"/><rect width="7" height="5" x="3" y="16" rx="1"/></g>',
|
||||||
|
},
|
||||||
|
'lucide:book-open-text': {
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
body: '<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 7v14m4-9h2m-2-4h2M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4a4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3a3 3 0 0 0-3-3zm3-6h2M6 8h2"/>',
|
||||||
|
},
|
||||||
|
'lucide:copyright': {
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
body: '<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M14.83 14.83a4 4 0 1 1 0-5.66"/></g>',
|
||||||
|
},
|
||||||
|
'clarity:database': {
|
||||||
|
width: 36,
|
||||||
|
height: 36,
|
||||||
|
body: '<path fill="currentColor" d="M33 6.69c-.18-3.41-9.47-4.33-15-4.33S3 3.29 3 6.78v22.59c0 3.49 9.43 4.43 15 4.43s15-.93 15-4.43zm-2 7.56c-.33.86-5.06 2.45-13 2.45a37.5 37.5 0 0 1-11-1.36v2.08a43.3 43.3 0 0 0 11 1.28c4 0 9.93-.48 13-2v5.17c-.33.86-5.06 2.45-13 2.45a37.5 37.5 0 0 1-11-1.4V25a43.3 43.3 0 0 0 11 1.28c4 0 9.93-.48 13-2v5.1c-.35.86-5.08 2.45-13 2.45S5.3 30.2 5 29.37V6.82c.3-.82 5-2.46 13-2.46c7.77 0 12.46 1.53 13 2.37c-.52.87-5.21 2.39-13 2.39A37.6 37.6 0 0 1 7 7.76v2.09a43.5 43.5 0 0 0 11 1.27c4 0 9.93-.48 13-2Z"/>',
|
||||||
|
},
|
||||||
|
'clarity:time-line': {
|
||||||
|
width: 36,
|
||||||
|
height: 36,
|
||||||
|
body: '<path fill="currentColor" d="M10 18c0-1.3-.8-2.4-2-2.8v-3.4c1.2-.4 2-1.5 2-2.8c0-1.7-1.3-3-3-3S4 7.3 4 9c0 1.3.8 2.4 2 2.8v3.4c-1.2.4-2 1.5-2 2.8s.8 2.4 2 2.8v3.4c-1.2.4-2 1.5-2 2.8c0 1.7 1.3 3 3 3s3-1.3 3-3c0-1.3-.8-2.4-2-2.8v-3.4c1.2-.4 2-1.5 2-2.8m21-8H15c-.6 0-1-.4-1-1s.4-1 1-1h16c.6 0 1 .4 1 1s-.4 1-1 1m0 9H15c-.6 0-1-.4-1-1s.4-1 1-1h16c.6 0 1 .4 1 1s-.4 1-1 1m0 9H15c-.6 0-1-.4-1-1s.4-1 1-1h16c.6 0 1 .4 1 1s-.4 1-1 1"/><path fill="none" d="M0 0h36v36H0z"/>',
|
||||||
|
},
|
||||||
|
'solar:widget-5-outline': {
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
body: '<g fill="none" stroke="currentColor" stroke-width="1.5"><rect width="8" height="8" x="2.75" y="2.75" rx="1.5"/><rect width="8" height="8" x="13.25" y="13.25" rx="1.5"/><rect width="8" height="5" x="13.25" y="2.75" rx="1.5"/><rect width="5" height="8" x="2.75" y="13.25" rx="1.5"/></g>',
|
||||||
|
},
|
||||||
|
'solar:stop-circle-outline': {
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
body: '<circle cx="12" cy="12" r="9.25" fill="none" stroke="currentColor" stroke-width="1.5"/><rect width="6" height="6" x="9" y="9" fill="currentColor" rx="1"/>',
|
||||||
|
},
|
||||||
|
'solar:plain-linear': {
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
body: '<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="m21.4 3.7l-6.1 16.8l-3.6-6.2l-6.2-3.6z"/><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="1.5" d="m21.4 3.7l-9.7 10.6"/>',
|
||||||
|
},
|
||||||
|
'fluent-mdl2:world-clock': {
|
||||||
|
width: 2048,
|
||||||
|
height: 2048,
|
||||||
|
body: '<path fill="currentColor" d="M896 768H512V256h128v384h256zm1152 640q0 87-22 168t-64 152t-100 130t-128 101t-152 66t-168 23q-134 0-251-49t-205-136t-139-204t-51-251q0-132 50-248t138-204t203-137t249-51q132 0 248 50t204 138t137 203t51 249m-640 512q21 0 37-15t29-40t21-53t15-58t9-53t5-37h-230q1 13 5 37t10 52t15 58t21 54t27 39t36 16m125-384q3-64 3-128q0-63-3-128h-250q-3 65-3 128q0 64 3 128zm-637-128q0 32 4 64t12 64h243q-6-128 0-256H912q-8 32-12 64t-4 64m512-512q-19 0-34 15t-27 40t-21 54t-15 58t-11 53t-5 36h225q-1-11-5-34t-11-52t-16-59t-21-54t-27-41t-32-16m253 384q3 64 3 128t-2 128h242q8-32 12-64t4-64t-4-64t-12-64zm190-128q-43-75-108-131t-145-89q20 53 32 108t20 112zm-637-218q-78 32-142 88t-107 130h200q7-56 18-110t31-108m-249 730q42 73 105 129t142 88q-20-52-30-107t-17-110zm643 215q77-32 139-87t104-128h-198q-5 55-15 109t-30 106M640 0q88 0 170 23t153 64t129 100t100 130t65 153t23 170h-128q0-106-40-199t-110-162t-163-110t-199-41t-199 40t-162 110t-110 163t-41 199t40 199t110 162t163 110t199 41v128q-88 0-170-23t-153-64t-129-100T88 963T23 810T0 640q0-132 50-248t138-204T391 51T640 0"/>',
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -3,7 +3,7 @@ import type { Component } from 'vue';
|
|||||||
|
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
|
||||||
import { IconDefault, IconifyIcon } from '@easyflow-core/icons';
|
import { IconDefault, IconifyIcon, isLocalIcon } from '@easyflow-core/icons';
|
||||||
import {
|
import {
|
||||||
isFunction,
|
isFunction,
|
||||||
isHttpUrl,
|
isHttpUrl,
|
||||||
@@ -21,6 +21,12 @@ const isRemoteIcon = computed(() => {
|
|||||||
return isString(props.icon) && isHttpUrl(props.icon);
|
return isString(props.icon) && isHttpUrl(props.icon);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isUnavailableIcon = computed(() => {
|
||||||
|
return (
|
||||||
|
isString(props.icon) && !isRemoteIcon.value && !isLocalIcon(props.icon)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const isComponent = computed(() => {
|
const isComponent = computed(() => {
|
||||||
const { icon } = props;
|
const { icon } = props;
|
||||||
return !isString(icon) && (isObject(icon) || isFunction(icon));
|
return !isString(icon) && (isObject(icon) || isFunction(icon));
|
||||||
@@ -30,6 +36,10 @@ const isComponent = computed(() => {
|
|||||||
<template>
|
<template>
|
||||||
<component :is="icon as Component" v-if="isComponent" v-bind="$attrs" />
|
<component :is="icon as Component" v-if="isComponent" v-bind="$attrs" />
|
||||||
<img v-else-if="isRemoteIcon" :src="icon as string" v-bind="$attrs" />
|
<img v-else-if="isRemoteIcon" :src="icon as string" v-bind="$attrs" />
|
||||||
<IconifyIcon v-else-if="icon" v-bind="$attrs" :icon="icon as string" />
|
<IconifyIcon
|
||||||
|
v-else-if="icon && !isUnavailableIcon"
|
||||||
|
v-bind="$attrs"
|
||||||
|
:icon="icon as string"
|
||||||
|
/>
|
||||||
<IconDefault v-else-if="fallback" v-bind="$attrs" />
|
<IconDefault v-else-if="fallback" v-bind="$attrs" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ interface Props extends PopoverRootProps {
|
|||||||
contentClass?: ClassType;
|
contentClass?: ClassType;
|
||||||
contentProps?: PopoverContentProps;
|
contentProps?: PopoverContentProps;
|
||||||
triggerClass?: ClassType;
|
triggerClass?: ClassType;
|
||||||
|
triggerAsChild?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {});
|
const props = withDefaults(defineProps<Props>(), {});
|
||||||
@@ -34,6 +35,7 @@ const delegatedProps = computed(() => {
|
|||||||
contentClass: _,
|
contentClass: _,
|
||||||
contentProps: _cProps,
|
contentProps: _cProps,
|
||||||
triggerClass: _tClass,
|
triggerClass: _tClass,
|
||||||
|
triggerAsChild: _tAsChild,
|
||||||
...delegated
|
...delegated
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@@ -45,16 +47,15 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<PopoverRoot v-bind="forwarded">
|
<PopoverRoot v-bind="forwarded">
|
||||||
<PopoverTrigger :class="triggerClass">
|
<PopoverTrigger :class="triggerClass" :as-child="triggerAsChild">
|
||||||
<slot name="trigger"></slot>
|
<slot name="trigger"></slot>
|
||||||
|
|
||||||
<PopoverContent
|
|
||||||
:class="contentClass"
|
|
||||||
class="side-content z-popup"
|
|
||||||
v-bind="contentProps"
|
|
||||||
>
|
|
||||||
<slot></slot>
|
|
||||||
</PopoverContent>
|
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
|
<PopoverContent
|
||||||
|
:class="contentClass"
|
||||||
|
class="side-content z-popup"
|
||||||
|
v-bind="contentProps"
|
||||||
|
>
|
||||||
|
<slot></slot>
|
||||||
|
</PopoverContent>
|
||||||
</PopoverRoot>
|
</PopoverRoot>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import { isFunction } from '@easyflow-core/shared/utils';
|
|||||||
|
|
||||||
import { objectOmit, refDebounced, watchDebounced } from '@vueuse/core';
|
import { objectOmit, refDebounced, watchDebounced } from '@vueuse/core';
|
||||||
|
|
||||||
import { fetchIconsData } from './icons';
|
import { getLocalIconsData } from './icons';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
@@ -107,7 +107,7 @@ watchDebounced(
|
|||||||
'svg:user-feedback',
|
'svg:user-feedback',
|
||||||
'svg:oauth',
|
'svg:oauth',
|
||||||
'svg:mcp',
|
'svg:mcp',
|
||||||
...(await fetchIconsData(prefix)),
|
...getLocalIconsData(prefix),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -215,6 +215,7 @@ defineExpose({ toggleOpenState, open, close });
|
|||||||
:content-props="{ align: 'end', alignOffset: -11, sideOffset: 8 }"
|
:content-props="{ align: 'end', alignOffset: -11, sideOffset: 8 }"
|
||||||
content-class="p-0 pt-3 w-full"
|
content-class="p-0 pt-3 w-full"
|
||||||
trigger-class="w-full"
|
trigger-class="w-full"
|
||||||
|
trigger-as-child
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<template v-if="props.type === 'input'">
|
<template v-if="props.type === 'input'">
|
||||||
@@ -233,6 +234,7 @@ defineExpose({ toggleOpenState, open, close });
|
|||||||
<EasyFlowIcon
|
<EasyFlowIcon
|
||||||
:icon="currentSelect || Grip"
|
:icon="currentSelect || Grip"
|
||||||
class="size-4"
|
class="size-4"
|
||||||
|
fallback
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
@@ -250,6 +252,7 @@ defineExpose({ toggleOpenState, open, close });
|
|||||||
<EasyFlowIcon
|
<EasyFlowIcon
|
||||||
:icon="currentSelect || Grip"
|
:icon="currentSelect || Grip"
|
||||||
class="absolute right-1 top-1 size-6"
|
class="absolute right-1 top-1 size-6"
|
||||||
|
fallback
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -258,6 +261,7 @@ defineExpose({ toggleOpenState, open, close });
|
|||||||
:icon="currentSelect || Grip"
|
:icon="currentSelect || Grip"
|
||||||
v-else
|
v-else
|
||||||
class="size-4"
|
class="size-4"
|
||||||
|
fallback
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
@@ -289,6 +293,7 @@ defineExpose({ toggleOpenState, open, close });
|
|||||||
'text-primary transition-all': currentSelect === item,
|
'text-primary transition-all': currentSelect === item,
|
||||||
}"
|
}"
|
||||||
:icon="item"
|
:icon="item"
|
||||||
|
fallback
|
||||||
/>
|
/>
|
||||||
</EasyFlowIconButton>
|
</EasyFlowIconButton>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,59 +1,21 @@
|
|||||||
import type { Recordable } from '@easyflow/types';
|
import type { Recordable } from '@easyflow/types';
|
||||||
|
|
||||||
import uncategorized from './uncategorized';
|
import { listIcons } from '@easyflow/icons';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 一个缓存对象,在不刷新页面时,无需重复请求远程接口
|
* 本地可用图标的缓存对象。
|
||||||
*/
|
*/
|
||||||
export const ICONS_MAP: Recordable<string[]> = {};
|
export const ICONS_MAP: Recordable<string[]> = {};
|
||||||
|
|
||||||
// interface IconifyResponse {
|
|
||||||
// prefix: string;
|
|
||||||
// total: number;
|
|
||||||
// title: string;
|
|
||||||
// uncategorized?: string[];
|
|
||||||
// categories?: Recordable<string[]>;
|
|
||||||
// aliases?: Recordable<string>;
|
|
||||||
// }
|
|
||||||
|
|
||||||
const PENDING_REQUESTS: Recordable<Promise<string[]>> = {};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过Iconify接口获取图标集数据。
|
* 获取本地已注册的图标集数据。
|
||||||
* 同一时间多个图标选择器同时请求同一个图标集时,实际上只会发起一次请求(所有请求共享同一份结果)。
|
|
||||||
* 请求结果会被缓存,刷新页面前同一个图标集不会再次请求
|
|
||||||
* @param prefix 图标集名称
|
* @param prefix 图标集名称
|
||||||
* @returns 图标集中包含的所有图标名称
|
* @returns 图标集中包含的所有图标名称
|
||||||
*/
|
*/
|
||||||
export async function fetchIconsData(prefix: string): Promise<string[]> {
|
export function getLocalIconsData(prefix: string): string[] {
|
||||||
if (Reflect.has(ICONS_MAP, prefix) && ICONS_MAP[prefix]) {
|
if (Reflect.has(ICONS_MAP, prefix) && ICONS_MAP[prefix]) {
|
||||||
return ICONS_MAP[prefix];
|
return ICONS_MAP[prefix];
|
||||||
}
|
}
|
||||||
if (Reflect.has(PENDING_REQUESTS, prefix) && PENDING_REQUESTS[prefix]) {
|
ICONS_MAP[prefix] = listIcons('', prefix);
|
||||||
return PENDING_REQUESTS[prefix];
|
return ICONS_MAP[prefix];
|
||||||
}
|
|
||||||
PENDING_REQUESTS[prefix] = (async () => {
|
|
||||||
try {
|
|
||||||
// const controller = new AbortController();
|
|
||||||
// const timeoutId = setTimeout(() => controller.abort(), 1000 * 10);
|
|
||||||
// const response: IconifyResponse = await fetch(
|
|
||||||
// `https://api.iconify.design/collection?prefix=${prefix}`,
|
|
||||||
// { signal: controller.signal },
|
|
||||||
// ).then((res) => res.json());
|
|
||||||
// clearTimeout(timeoutId);
|
|
||||||
// const list = response.uncategorized || [];
|
|
||||||
// if (response.categories) {
|
|
||||||
// for (const category in response.categories) {
|
|
||||||
// list.push(...(response.categories[category] || []));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// ICONS_MAP[prefix] = list.map((v) => `${prefix}:${v}`);
|
|
||||||
ICONS_MAP[prefix] = uncategorized.map((v) => `${prefix}:${v}`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Failed to fetch icons for prefix ${prefix}:`, error);
|
|
||||||
return [] as string[];
|
|
||||||
}
|
|
||||||
return ICONS_MAP[prefix];
|
|
||||||
})();
|
|
||||||
return PENDING_REQUESTS[prefix];
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user