2018-07-18 13:38:19 +08:00
|
|
|
import * as React from 'react';
|
|
|
|
import classNames from 'classnames';
|
|
|
|
import omit from 'omit.js';
|
2018-07-19 12:23:33 +08:00
|
|
|
import { Omit } from '../_util/type';
|
|
|
|
import { IconProps } from './index';
|
2018-07-18 13:38:19 +08:00
|
|
|
|
|
|
|
export interface CustomIconProps extends Omit<IconProps, 'type'> {
|
2018-07-19 19:23:33 +08:00
|
|
|
viewBox?: string;
|
|
|
|
component?: React.ComponentType<CustomIconComponentProps>;
|
|
|
|
}
|
|
|
|
|
2018-07-20 10:32:00 +08:00
|
|
|
export interface CustomIconComponentProps {
|
2018-07-20 16:55:45 +08:00
|
|
|
width: Readonly<string | number>;
|
|
|
|
height: Readonly<string | number>;
|
|
|
|
fill: Readonly<string>;
|
|
|
|
viewBox: Readonly<string>;
|
2018-07-18 13:38:19 +08:00
|
|
|
}
|
|
|
|
|
2018-07-20 10:32:00 +08:00
|
|
|
// These props make sure that the SVG behaviours like general text.
|
|
|
|
// Reference: https://blog.prototypr.io/align-svg-icons-to-text-and-say-goodbye-to-font-icons-d44b3d7b26b4
|
|
|
|
const svgBaseProps = {
|
|
|
|
width: '1em',
|
|
|
|
height: '1em',
|
|
|
|
fill: 'currentColor',
|
|
|
|
['aria-hidden']: 'true',
|
|
|
|
};
|
|
|
|
|
2018-07-18 13:38:19 +08:00
|
|
|
const CustomIcon: React.SFC<CustomIconProps> = (props) => {
|
2018-07-19 19:23:33 +08:00
|
|
|
const {
|
|
|
|
className = '',
|
|
|
|
spin,
|
2018-07-20 10:32:00 +08:00
|
|
|
// ⬇️ Todo, what's the best default value?
|
|
|
|
// ⬇️ "0 0 24 24" for material-ui or "0 0 1024 1024" for ant-design
|
|
|
|
viewBox = '0 0 1024 1024',
|
2018-07-19 19:23:33 +08:00
|
|
|
children,
|
|
|
|
component: Component,
|
|
|
|
} = props;
|
|
|
|
|
2018-07-18 13:38:19 +08:00
|
|
|
const classString = classNames({
|
|
|
|
anticon: true,
|
|
|
|
'anticon-spin': !!spin,
|
|
|
|
}, className);
|
|
|
|
|
2018-07-19 20:08:04 +08:00
|
|
|
let content = (
|
2018-07-23 20:12:59 +08:00
|
|
|
<svg {...omit(props, ['type', 'spin', 'className'])} {...svgBaseProps} viewBox={viewBox}>
|
2018-07-19 20:08:04 +08:00
|
|
|
{children}
|
|
|
|
</svg>
|
|
|
|
);
|
|
|
|
|
2018-07-19 19:23:33 +08:00
|
|
|
if (Component) {
|
2018-07-20 10:32:00 +08:00
|
|
|
content = <Component {...svgBaseProps} viewBox={viewBox}>{children}</Component>;
|
2018-07-18 13:38:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
2018-07-19 20:08:04 +08:00
|
|
|
<i className={classString}>
|
|
|
|
{content}
|
|
|
|
</i>
|
2018-07-18 13:38:19 +08:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2018-07-18 17:35:39 +08:00
|
|
|
const customCache = new Set<string>();
|
|
|
|
|
|
|
|
export interface CustomIconOptions {
|
|
|
|
namespace?: string;
|
|
|
|
prefix?: string;
|
2018-07-20 10:32:00 +08:00
|
|
|
scriptUrl?: string;
|
2018-07-20 16:55:45 +08:00
|
|
|
extraCommonProps?: { [key: string]: any };
|
2018-07-18 17:35:39 +08:00
|
|
|
}
|
|
|
|
|
2018-07-19 12:23:33 +08:00
|
|
|
export function create(options: CustomIconOptions = {}): React.ComponentClass<IconProps> {
|
2018-07-20 16:55:45 +08:00
|
|
|
const { namespace, prefix = '', scriptUrl, extraCommonProps = {} } = options;
|
2018-07-18 17:35:39 +08:00
|
|
|
|
|
|
|
class Custom extends React.Component<IconProps> {
|
|
|
|
render() {
|
|
|
|
const { type, className = '', spin } = this.props;
|
|
|
|
const classString = classNames({
|
|
|
|
anticon: true,
|
|
|
|
'anticon-spin': !!spin || type === 'loading',
|
|
|
|
}, className);
|
|
|
|
return (
|
2018-07-19 20:08:04 +08:00
|
|
|
<i className={classString}>
|
|
|
|
<svg
|
|
|
|
{...extraCommonProps}
|
2018-07-23 20:12:59 +08:00
|
|
|
{...omit(this.props, ['type', 'spin', 'className'])}
|
2018-07-20 10:32:00 +08:00
|
|
|
{...svgBaseProps}
|
2018-07-19 20:08:04 +08:00
|
|
|
>
|
2018-07-20 16:55:45 +08:00
|
|
|
<use xlinkHref={`#${prefix}${type}`}/>
|
2018-07-19 20:08:04 +08:00
|
|
|
</svg>
|
|
|
|
</i>
|
2018-07-18 17:35:39 +08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
/**
|
2018-07-19 12:23:33 +08:00
|
|
|
* DOM API required.
|
2018-07-19 20:32:34 +08:00
|
|
|
* Make sure in browser environment.
|
2018-07-18 17:35:39 +08:00
|
|
|
* The Custom Icon will create a <script/>
|
|
|
|
* that loads SVG symbols and insert the SVG Element into the document body.
|
|
|
|
*/
|
2018-07-20 10:32:00 +08:00
|
|
|
if (typeof document !== 'undefined' && typeof window !== 'undefined'
|
2018-07-19 20:32:34 +08:00
|
|
|
&& typeof document.createElement === 'function'
|
2018-07-20 10:32:00 +08:00
|
|
|
&& typeof scriptUrl === 'string' && scriptUrl.length
|
2018-07-18 17:35:39 +08:00
|
|
|
&& typeof namespace === 'string' && namespace.length
|
|
|
|
&& !customCache.has(namespace)
|
|
|
|
) {
|
|
|
|
const script = document.createElement('script');
|
2018-07-20 10:32:00 +08:00
|
|
|
script.setAttribute('src', scriptUrl);
|
2018-07-18 17:35:39 +08:00
|
|
|
script.setAttribute('data-namespace', namespace);
|
|
|
|
customCache.add(namespace);
|
|
|
|
document.body.appendChild(script);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Custom;
|
|
|
|
}
|
|
|
|
|
2018-07-19 12:23:33 +08:00
|
|
|
export default CustomIcon;
|