import React, {useState, useEffect, Component, forwardRef} from 'react'; // setter使用@alifd/next,和编辑器保持一致 import {Input, Icon as NextIcon, Radio, Balloon, Search} from '@alifd/next'; import {get} from '../../_utils/utils'; import './style.less'; import {ternaryExpression, ternaryExpressionFunc} from '../../../src/utils/common'; type IconGroup = 'outlined' | 'filled' | 'two-tone' | 'iconfont' | 'extension'; const IconGroupNameMap: Record = { outlined: '线框风格', filled: '实底风格', 'two-tone': '双色风格', iconfont: 'Iconfont', extension: '扩展', }; export interface ZwIconProps { name: string, svgInfo: string, viewBox: string, } function getIconfontIconList() { const iframe = document.querySelector( 'iframe.lc-simulator-content-frame', )!; // iconfont的js会在页面中添加svg元素 const symbols = Array.prototype.slice.call( iframe.contentDocument!.querySelectorAll( 'svg[style="position: absolute; width: 0px; height: 0px; overflow: hidden;"][aria-hidden="true"] > symbol', ), ); return symbols.map(symbol => { const {id} = symbol; // @ts-ignore return { name: id, group: 'iconfont', icon: (props: any) => forwardRef((_, ref: any) => ( )), }; }); } function getAntdIconList() { const iframe = document.querySelector( 'iframe.lc-simulator-content-frame', ); const icons: Record = {}; // document.querySelectorAll('svg[style="position: absolute; width: 0px; height: 0px; overflow: hidden;"][aria-hidden="true"]') const antdIcons = get(iframe, 'contentWindow.icons', {}) as Record< string, any >; return Object.keys(antdIcons) .map(key => { const item = (antdIcons as any)[key]; if (typeof item !== 'object') { return null; } const name = item?.displayName ?? item?.render?.displayName ?? key; let group: IconGroup const lowercaseName = name.toLowerCase(); if (/outlined$/.test(lowercaseName)) { group = 'outlined'; } else if (/filled$/.test(lowercaseName)) { group = 'filled'; } else if (/twotone$/.test(lowercaseName)) { group = 'two-tone'; } else { return null; } return { name, group, icon: item, }; }) .filter(Boolean); } function extractSVGContent(str: string) { // 定义一个正则表达式来匹配SVG标签 const svgRegex = /]*>([\s\S]*?)<\/svg>/gi; // 使用正则表达式找到匹配项 const matches = str.match(svgRegex); // 如果没有匹配项,返回空数组 if (!matches) { return str; } // 提取SVG标签内的内容 const contents = matches.map((match) => { const contentRegex = /]*>([\s\S]*?)<\/svg>/i; const contentMatch = match.match(contentRegex); return contentMatch ? contentMatch[1] : null; }); return contents[0]; } function getZWIconList() { const iframe = document.querySelector('iframe.lc-simulator-content-frame'); const icons: Record = {}; const zwIcons = get(iframe, 'contentWindow.__zwicons__', {}) as Record; return Object.keys(zwIcons) .map((key) => { const item = (zwIcons as any)[key]; const name = item?.displayName ?? item?.render?.displayName ?? key; const ele = () => ( ); return { name, group: 'extension', viewBox: item.viewBox, svgInfo: extractSVGContent(item.content), icon: ele, }; }) .filter(Boolean); } function getIconList() { const iconfontIconList = getIconfontIconList(); const antdIconList = getAntdIconList(); const zwIconsList = getZWIconList(); return [...antdIconList, ...iconfontIconList, ...zwIconsList]; } const Icon = (props: any) => { const {type, icons = {}, ...rest} = props; const Comp = icons[type]; if (!Comp) return null; return ; }; interface AntdIconSetterProps { value: string; type: string; defaultValue: string; placeholder: string; hasClear: boolean; onChange: (icon: string | object) => undefined; icons: string[]; } const AntdIconSetter = (props: AntdIconSetterProps) => { const [search, setSearch] = useState(''); const [icons, setIcons] = useState>({}); const [groups, setGroups] = useState<{ group: IconGroup; list: any[] }[]>([]); const [selectedGroup, setSelectedGroup] = useState('outlined'); const [firstLoad, setFirstLoad] = useState(true); const [list, setList] = useState([]); const {value, defaultValue, type, onChange, placeholder, hasClear} = props; const _value = typeof value === 'object' ? (value as any)?.name : value; if (firstLoad && defaultValue && typeof value === 'undefined') { onChange(defaultValue); setFirstLoad(false); } const handleChange = (icon: string | ZwIconProps) => { if (type === 'string') { onChange(icon); } else if (type === 'node') { onChange({ componentName: 'Icon', props: { type: icon, }, }); } }; useEffect(() => { const iconList = getIconList(); iconList.forEach(item => { const {group} = item!; if (groups.every(groupItem => groupItem.group !== group)) { groups.push({group: group as IconGroup, list: []}); } const target = groups.find(groupItem => groupItem.group === group)!; target.list.push(item); icons[item!.name] = item?.icon; }); setIcons(icons); setGroups(groups); setSelectedGroup(groups[0]?.group); }, []); useEffect(() => { const currentGroup = groups.find(item => item.group === selectedGroup); setList( (currentGroup?.list ?? []).filter(item => { return ternaryExpression( search, item.name.toLowerCase().indexOf(search.toLowerCase()) > -1, true, ); }), ); }, [selectedGroup, search, groups]); const currentIcon = ( ); const clearIcon = hasClear && ( { e.preventDefault(); e.stopPropagation(); handleChange(''); }} /> ); const triggerNode = (
); return (
setSelectedGroup(radioValue)} > {groups.map(item => ( {IconGroupNameMap[item.group]} ))}
    {list.map(item => (
  • { ternaryExpressionFunc( item.group === 'extension', () => handleChange({ svgInfo: item.svgInfo, viewBox: item.viewBox, name: item.name, }), () => handleChange(item.name), ); }} >
    {item.name}
  • ))}
); }; AntdIconSetter.defaultProps = { value: undefined, type: 'string', defaultValue: '', hasClear: false, placeholder: '请点击选择 Icon', onChange: () => undefined, }; // 因为下面这个问题,setter必须使用class组件 // http://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/issues/109046 export default class extends Component { render() { return ; } }