UNPKG

@atlaskit/icon

Version:

A React package that contains icons

126 lines (114 loc) 3.82 kB
// @flow import React, { Component, type Node } from 'react'; import styled from 'styled-components'; import uuid from 'uuid'; import { colors } from '@atlaskit/theme'; import { sizes } from '../constants'; const getSize = props => { if (props.size) { return `height: ${sizes[props.size]}; width: ${sizes[props.size]};`; } return null; }; export const IconWrapper = styled.span` ${getSize} color: ${p => p.primaryColor || 'currentColor'}; display: inline-block; fill: ${p => p.secondaryColor || colors.background}; flex-shrink: 0; line-height: 1; > svg { ${getSize} max-height: 100%; max-width: 100%; overflow: hidden; pointer-events: none; vertical-align: bottom; } /* Stop-color doesn't properly apply in chrome when the inherited/current color changes. * We have to initially set stop-color to inherit (either via DOM attribute or an initial CSS * rule) and then override it with currentColor for the color changes to be picked up. */ stop { stop-color: currentColor; } `; type Props = { /** Glyph to show by Icon component (not required when you import a glyph directly) */ glyph?: (props?: {}) => Node, /** More performant than the glyph prop, but potentially dangerous if the SVG string hasn't been "sanitised" */ dangerouslySetGlyph?: string, /** String to use as the aria-label for the icon. Set to an empty string if you are rendering the icon with visible text to prevent accessibility label duplication. */ label: string, /** onClick handler for the icon element */ onClick?: () => mixed, /** For primary colour for icons */ primaryColor?: string, /** For secondary colour for 2-color icons. Set to inherit to control this via "fill" in CSS */ secondaryColor?: string, /** Control the size of the icon */ size?: 'small' | 'medium' | 'large' | 'xlarge', }; export default class Icon extends Component<Props, {}> { static defaultProps = { onClick: () => {}, }; /* Icons need unique gradient IDs across instances for different gradient definitions to work * correctly. * A step in the icon build process replaces linear gradient IDs and their references in paths * to a placeholder string so we can replace them with a dynamic ID here. * Replacing the original IDs with placeholders in the build process is more robust than not * using placeholders as we do not have to rely on regular expressions to find specific element * to replace. */ static insertDynamicGradientID(svgStr: string) { const id = uuid(); const replacedSvgStr = svgStr .replace(/id="([^"]+)-idPlaceholder"/g, `id=$1-${id}`) .replace( /fill="url\(#([^"]+)-idPlaceholder\)"/g, `fill="url(#$1-${id})"`, ); return replacedSvgStr; } render() { const { glyph: Glyph, dangerouslySetGlyph, onClick, primaryColor, secondaryColor, size, } = this.props; // handling the glyphs as strings if (dangerouslySetGlyph) { return ( <IconWrapper onClick={onClick} primaryColor={primaryColor} secondaryColor={secondaryColor} size={size} aria-label={this.props.label} dangerouslySetInnerHTML={{ __html: Icon.insertDynamicGradientID(dangerouslySetGlyph), }} /> ); } // handling the glyphs when passed through as functions return ( <IconWrapper onClick={onClick} primaryColor={primaryColor} secondaryColor={secondaryColor} size={size} aria-label={this.props.label} > {Glyph ? <Glyph role="presentation" /> : null} </IconWrapper> ); } } export const size = Object.keys(sizes).reduce( (p, c) => Object.assign(p, { [c]: c }), {}, );