UNPKG

6.16 kBTypeScriptView Raw
1/*
2 * Copyright 2017 Palantir Technologies, Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import classNames from "classnames";
18import * as React from "react";
19import { polyfill } from "react-lifecycles-compat";
20
21import { IconName, IconSvgPaths16, IconSvgPaths20 } from "@blueprintjs/icons";
22
23import { AbstractPureComponent2, Classes, DISPLAYNAME_PREFIX, IntentProps, Props, MaybeElement } from "../../common";
24
25export { IconName };
26
27export enum IconSize {
28 STANDARD = 16,
29 LARGE = 20,
30}
31
32// eslint-disable-next-line deprecation/deprecation
33export type IconProps = IIconProps;
34/** @deprecated use IconProps */
35export interface IIconProps extends IntentProps, Props {
36 /** This component does not support custom children. Use the `icon` prop. */
37 children?: never;
38
39 /**
40 * Color of icon. This is used as the `fill` attribute on the `<svg>` image
41 * so it will override any CSS `color` property, including that set by
42 * `intent`. If this prop is omitted, icon color is inherited from
43 * surrounding text.
44 */
45 color?: string;
46
47 /**
48 * String for the `title` attribute on the rendered element, which will appear
49 * on hover as a native browser tooltip.
50 */
51 htmlTitle?: string;
52
53 /**
54 * Name of a Blueprint UI icon, or an icon element, to render. This prop is
55 * required because it determines the content of the component, but it can
56 * be explicitly set to falsy values to render nothing.
57 *
58 * - If `null` or `undefined` or `false`, this component will render nothing.
59 * - If given an `IconName` (a string literal union of all icon names), that
60 * icon will be rendered as an `<svg>` with `<path>` tags. Unknown strings
61 * will render a blank icon to occupy space.
62 * - If given a `JSX.Element`, that element will be rendered and _all other
63 * props on this component are ignored._ This type is supported to
64 * simplify icon support in other Blueprint components. As a consumer, you
65 * should avoid using `<Icon icon={<Element />}` directly; simply render
66 * `<Element />` instead.
67 */
68 icon: IconName | MaybeElement;
69
70 /**
71 * @deprecated use size prop instead
72 */
73 iconSize?: number;
74
75 /**
76 * Size of the icon, in pixels. Blueprint contains 16px and 20px SVG icon
77 * images, and chooses the appropriate resolution based on this prop.
78 *
79 * @default IconSize.STANDARD = 16
80 */
81 size?: number;
82
83 /** CSS style properties. */
84 style?: React.CSSProperties;
85
86 /**
87 * HTML tag to use for the rendered element.
88 *
89 * @default "span"
90 */
91 tagName?: keyof JSX.IntrinsicElements;
92
93 /**
94 * Description string. This string does not appear in normal browsers, but
95 * it increases accessibility. For instance, screen readers will use it for
96 * aural feedback.
97 *
98 * If this value is nullish, `false`, or an empty string, the component will assume
99 * that the icon is decorative and `aria-hidden="true"` will be applied.
100 *
101 * @see https://www.w3.org/WAI/tutorials/images/decorative/
102 */
103 title?: string | false | null;
104}
105
106@polyfill
107export class Icon extends AbstractPureComponent2<IconProps & Omit<React.HTMLAttributes<HTMLElement>, "title">> {
108 public static displayName = `${DISPLAYNAME_PREFIX}.Icon`;
109
110 /** @deprecated use IconSize.STANDARD */
111 public static readonly SIZE_STANDARD = IconSize.STANDARD;
112
113 /** @deprecated use IconSize.LARGE */
114 public static readonly SIZE_LARGE = IconSize.LARGE;
115
116 public render(): JSX.Element | null {
117 const { icon } = this.props;
118 if (icon == null || typeof icon === "boolean") {
119 return null;
120 } else if (typeof icon !== "string") {
121 return icon;
122 }
123
124 const {
125 className,
126 color,
127 htmlTitle,
128 // eslint-disable-next-line deprecation/deprecation
129 iconSize,
130 intent,
131 size = iconSize ?? IconSize.STANDARD,
132 title,
133 tagName = "span",
134 ...htmlprops
135 } = this.props;
136
137 // choose which pixel grid is most appropriate for given icon size
138 const pixelGridSize = size >= IconSize.LARGE ? IconSize.LARGE : IconSize.STANDARD;
139 // render path elements, or nothing if icon name is unknown.
140 const paths = this.renderSvgPaths(pixelGridSize, icon);
141
142 // eslint-disable-next-line deprecation/deprecation
143 const classes = classNames(Classes.ICON, Classes.iconClass(icon), Classes.intentClass(intent), className);
144 const viewBox = `0 0 ${pixelGridSize} ${pixelGridSize}`;
145
146 return React.createElement(
147 tagName,
148 {
149 ...htmlprops,
150 "aria-hidden": title ? undefined : true,
151 className: classes,
152 title: htmlTitle,
153 },
154 <svg fill={color} data-icon={icon} width={size} height={size} viewBox={viewBox}>
155 {title && <desc>{title}</desc>}
156 {paths}
157 </svg>,
158 );
159 }
160
161 /** Render `<path>` elements for the given icon name. Returns `null` if name is unknown. */
162 private renderSvgPaths(pathsSize: number, iconName: IconName): JSX.Element[] | null {
163 const svgPathsRecord = pathsSize === IconSize.STANDARD ? IconSvgPaths16 : IconSvgPaths20;
164 const pathStrings = svgPathsRecord[iconName];
165 if (pathStrings == null) {
166 return null;
167 }
168 return pathStrings.map((d, i) => <path key={i} d={d} fillRule="evenodd" />);
169 }
170}