UNPKG

@pmwcs/icon

Version:
160 lines (133 loc) 4.66 kB
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } import { h, cloneElement, isValidElement } from 'preact'; import { useProviderContext } from '@pmwcs/provider'; import { Tag, classNames, getDisplayName } from '@pmwcs/base'; /** * Given content, tries to figure out an appropriate strategy for it */ const processAutoStrategy = function (content, prefix) { // check for URLS if (typeof content === 'string' && content.includes('/')) { return 'url'; } // handle prefixed if (typeof content === 'string' && prefix) { return 'className'; } // handle JSX components if (isValidElement(content)) { return 'component'; } // we don't know what it is, default to ligature for compat with material icons return 'ligature'; }; /** * Get the actual icon strategy to use */ export const getIconStrategy = function (content, strategy, providerStrategy, prefix) { strategy = strategy || providerStrategy || 'auto'; if (strategy === 'auto') { return processAutoStrategy(content, prefix); } return strategy; }; const renderLigature = ({ content, ...rest }) => h(Tag, _extends({ tag: "i" }, rest), content); const renderClassName = ({ content, ...rest }) => h(Tag, _extends({ tag: "i" }, rest)); const renderUrl = ({ content, ...rest }) => h(Tag, _extends({ tag: "i" }, rest, { style: { ...rest.style, backgroundImage: `url(${content})` } })); const renderComponent = ({ content, ...rest }) => { return h(Tag, _extends({ tag: "i" }, rest), content); }; const iconRenderMap = { ligature: renderLigature, className: renderClassName, url: renderUrl, component: renderComponent, auto: undefined }; const buildIconOptions = icon => isValidElement(icon) || icon && typeof icon !== 'object' ? { icon } : icon; /** An Icon component. Most of these options can be set once globally, read the documentation on Provider for more info. */ export function Icon({ icon, size: _size, prefix: _prefix, basename: _basename, children, outlined, rounded, twoTone, sharp, ...rest }) { const providerContext = useProviderContext(); // Build icon options object const { ref, icon: content, strategy, prefix = _prefix, basename = _basename, size = _size, render, ...optionsRest } = { ...buildIconOptions(icon || children) }; // Get provider options const { basename: providerBasename = null, prefix: providerPrefix = null, strategy: providerStrategy = null, render: providerRender = null } = providerContext.icon || {}; const contentToUse = content; const strategyToUse = getIconStrategy(contentToUse, strategy || null, providerStrategy || null, prefix); const prefixToUse = prefix || providerPrefix; const materialVariant = outlined ? 'material-icons-outlined' : rounded ? 'material-icons-round' : sharp ? 'material-icons-sharp' : twoTone ? 'material-icons-two-tone' : providerBasename; const basenameToUse = basename !== undefined ? basename : providerBasename === 'material-icons' ? materialVariant : providerBasename; const iconClassName = strategyToUse === 'className' && typeof content === 'string' ? `${String(prefixToUse)}${content}` : null; const rendererFromMap = !!strategyToUse && iconRenderMap[strategyToUse]; // For some reason TS thinks the render method will return undefined... const renderToUse = strategyToUse === 'custom' ? render || providerRender : rendererFromMap || null; if (!renderToUse) { console.error(`Icon: rendering not implemented for ${String(strategyToUse)}.`); return null; } const rendered = renderToUse({ ...rest, ...optionsRest, ref, content: contentToUse, className: classNames('pmwc-icon', `pmwc-icon--${strategyToUse}`, basenameToUse, rest.className, optionsRest.className, iconClassName, { [`pmwc-icon--size-${size || ''}`]: !!size }) }); const childDisplayName = getDisplayName(rendered.props.children); if (childDisplayName.includes('Avatar') || childDisplayName.includes('Icon')) { return cloneElement(rendered.props.children, { ...rendered.props.children.props, ...rendered.props, ref, // prevents an infinite loop children: rendered.props.children.props.children, className: classNames(rendered.props.className, rendered.props.children.props.className) }); } return rendered; }