-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
/
jlicon.tsx
141 lines (125 loc) · 3.42 KB
/
jlicon.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import React, {
ForwardRefExoticComponent,
PropsWithoutRef,
RefAttributes,
RefObject
} from 'react';
import { classes } from 'typestyle';
import { iconStyle, IIconStyle } from '../style/icon';
export function createIcon(
svgname: string,
svgstr: string,
debug: boolean = false
): JLIcon.IComponent {
function resolveSvg(svgstr: string, title?: string): HTMLElement | null {
const svgElement = new DOMParser().parseFromString(svgstr, 'image/svg+xml')
.documentElement;
if (svgElement.getElementsByTagName('parsererror').length > 0) {
const errmsg = `SVG HTML was malformed for icon name: ${name}`;
// parse failed, svgElement will be an error box
if (debug) {
// fail noisily, render the error box
console.error(errmsg);
return svgElement;
} else {
// bad svg is always a real error, fail silently but warn
console.warn(errmsg);
return null;
}
} else {
// parse succeeded
if (title) {
Private.setTitleSvg(svgElement, title);
}
return svgElement;
}
}
const JLIcon: JLIcon.IComponent = React.forwardRef<
HTMLDivElement,
JLIcon.IProps
>(
(props: JLIcon.IProps, ref: RefObject<HTMLDivElement>): JSX.Element => {
const { className, title, tag = 'div', ...propsStyle } = props;
const Tag = tag;
const classNames = classes(
className,
propsStyle ? iconStyle(propsStyle) : ''
);
// ensure that svg html is valid
const svgElement = resolveSvg(svgstr, title);
if (!svgElement) {
// bail if failing silently
return <></>;
}
return (
<Tag
className={classNames}
dangerouslySetInnerHTML={{ __html: svgElement.outerHTML }}
ref={ref}
/>
);
}
);
JLIcon.element = ({
className,
title,
tag = 'div',
...propsStyle
}: JLIcon.IProps): HTMLElement | null => {
const classNames = classes(
className,
propsStyle ? iconStyle(propsStyle) : ''
);
// ensure that svg html is valid
const svgElement = resolveSvg(svgstr, title);
if (!svgElement) {
// bail if failing silently
return null;
}
const container = document.createElement(tag);
container.appendChild(svgElement);
container.className = classNames;
return container;
};
// set display name of JLIcon for debugging
JLIcon.displayName = `JLIcon_${svgname}`;
return JLIcon;
}
/**
* A namespace for JLIcon statics.
*/
export namespace JLIcon {
/**
* The input props for creating a new JLIcon
*/
export interface IProps extends IIconStyle {
/**
* Extra classNames, used in addition to the typestyle className
*/
className?: string;
tag?: 'div' | 'span';
/**
* Icon title
*/
title?: string;
}
export interface IComponent
extends ForwardRefExoticComponent<
PropsWithoutRef<IProps> & RefAttributes<HTMLDivElement>
> {
element?: (props: IProps) => HTMLElement;
}
}
namespace Private {
export function setTitleSvg(svgNode: HTMLElement, title: string): void {
// add a title node to the top level svg node
let titleNodes = svgNode.getElementsByTagName('title');
if (titleNodes.length) {
titleNodes[0].textContent = title;
} else {
let titleNode = document.createElement('title');
titleNode.textContent = title;
svgNode.appendChild(titleNode);
}
}
}