@iconify/react
Advanced tools
Comparing version 4.1.1 to 5.0.0-beta.1
@@ -0,6 +1,6 @@ | ||
/// <reference types="react" /> | ||
import { IconifyIcon } from '@iconify/types'; | ||
import { IconifyJSON } from '@iconify/types'; | ||
import { IconifyTransformations } from '@iconify/types'; | ||
import { default as React_2 } from 'react'; | ||
import type { RefAttributes } from 'react'; | ||
import type { SVGProps } from 'react'; | ||
@@ -76,8 +76,5 @@ | ||
*/ | ||
export declare const Icon: (props: IconProps) => React_2.ReactElement<IconProps, string | React_2.JSXElementConstructor<any>>; | ||
export declare const Icon: IconComponentType; | ||
/** | ||
* Check if icon exists | ||
*/ | ||
export declare function iconExists(name: string): boolean; | ||
declare type IconComponentType = (props: IconProps) => JSX.Element; | ||
@@ -212,2 +209,3 @@ /** | ||
}; | ||
viewBox: SVGViewBox; | ||
body: string; | ||
@@ -265,2 +263,3 @@ } | ||
id?: string; | ||
ssr?: boolean; | ||
onLoad?: IconifyIconOnLoad; | ||
@@ -304,4 +303,10 @@ } | ||
/** | ||
* Check if icon exists | ||
* Check if icon data is available | ||
*/ | ||
iconLoaded: (name: string) => boolean; | ||
/** | ||
* Older, badly named, version of iconLoaded() | ||
* | ||
* @deprecated | ||
*/ | ||
iconExists: (name: string) => boolean; | ||
@@ -327,8 +332,13 @@ /** | ||
/** | ||
* Check if icon data is available | ||
*/ | ||
declare function iconLoaded(name: string): boolean; | ||
export { iconLoaded as iconExists } | ||
export { iconLoaded } | ||
/** | ||
* Mix of icon properties and SVGSVGElement properties | ||
*/ | ||
export declare type IconProps = IconifyElementProps & IconifyIconProps & ReactRefProp; | ||
export declare type IconProps = IconifyElementProps & IconifyIconProps; | ||
declare type IconRef = RefAttributes<SVGSVGElement>; | ||
/** | ||
@@ -339,3 +349,3 @@ * Inline icon (has negative verticalAlign that makes it behave like icon font) | ||
*/ | ||
export declare const InlineIcon: (props: IconProps) => React_2.ReactElement<IconProps, string | React_2.JSXElementConstructor<any>>; | ||
export declare const InlineIcon: IconComponentType; | ||
@@ -383,6 +393,2 @@ /** | ||
declare interface ReactRefProp { | ||
ref?: IconRef; | ||
} | ||
/** | ||
@@ -423,2 +429,7 @@ * Configuration object | ||
/** | ||
* SVG viewBox: x, y, width, height | ||
*/ | ||
declare type SVGViewBox = [x: number, y: number, width: number, height: number]; | ||
export { } |
'use strict'; | ||
var React = require('react'); | ||
var react = require('react'); | ||
const matchIconName = /^[a-z0-9]+(-[a-z0-9]+)*$/; | ||
const stringToIcon = (value, validate, allowSimpleName, provider = "") => { | ||
const colonSeparated = value.split(":"); | ||
if (value.slice(0, 1) === "@") { | ||
if (colonSeparated.length < 2 || colonSeparated.length > 3) { | ||
return null; | ||
} | ||
provider = colonSeparated.shift().slice(1); | ||
} | ||
if (colonSeparated.length > 3 || !colonSeparated.length) { | ||
return null; | ||
} | ||
if (colonSeparated.length > 1) { | ||
const name2 = colonSeparated.pop(); | ||
const prefix = colonSeparated.pop(); | ||
const result = { | ||
// Allow provider without '@': "provider:prefix:name" | ||
provider: colonSeparated.length > 0 ? colonSeparated[0] : provider, | ||
prefix, | ||
name: name2 | ||
}; | ||
return validate && !validateIconName(result) ? null : result; | ||
} | ||
const name = colonSeparated[0]; | ||
const dashSeparated = name.split("-"); | ||
if (dashSeparated.length > 1) { | ||
const result = { | ||
provider, | ||
prefix: dashSeparated.shift(), | ||
name: dashSeparated.join("-") | ||
}; | ||
return validate && !validateIconName(result) ? null : result; | ||
} | ||
if (allowSimpleName && provider === "") { | ||
const result = { | ||
provider, | ||
prefix: "", | ||
name | ||
}; | ||
return validate && !validateIconName(result, allowSimpleName) ? null : result; | ||
} | ||
return null; | ||
}; | ||
const validateIconName = (icon, allowSimpleName) => { | ||
if (!icon) { | ||
return false; | ||
} | ||
return !!((icon.provider === "" || icon.provider.match(matchIconName)) && (allowSimpleName && icon.prefix === "" || icon.prefix.match(matchIconName)) && icon.name.match(matchIconName)); | ||
}; | ||
const defaultIconDimensions = Object.freeze( | ||
@@ -168,2 +118,52 @@ { | ||
const matchIconName = /^[a-z0-9]+(-[a-z0-9]+)*$/; | ||
const stringToIcon = (value, validate, allowSimpleName, provider = "") => { | ||
const colonSeparated = value.split(":"); | ||
if (value.slice(0, 1) === "@") { | ||
if (colonSeparated.length < 2 || colonSeparated.length > 3) { | ||
return null; | ||
} | ||
provider = colonSeparated.shift().slice(1); | ||
} | ||
if (colonSeparated.length > 3 || !colonSeparated.length) { | ||
return null; | ||
} | ||
if (colonSeparated.length > 1) { | ||
const name2 = colonSeparated.pop(); | ||
const prefix = colonSeparated.pop(); | ||
const result = { | ||
// Allow provider without '@': "provider:prefix:name" | ||
provider: colonSeparated.length > 0 ? colonSeparated[0] : provider, | ||
prefix, | ||
name: name2 | ||
}; | ||
return validate && !validateIconName(result) ? null : result; | ||
} | ||
const name = colonSeparated[0]; | ||
const dashSeparated = name.split("-"); | ||
if (dashSeparated.length > 1) { | ||
const result = { | ||
provider, | ||
prefix: dashSeparated.shift(), | ||
name: dashSeparated.join("-") | ||
}; | ||
return validate && !validateIconName(result) ? null : result; | ||
} | ||
if (allowSimpleName && provider === "") { | ||
const result = { | ||
provider, | ||
prefix: "", | ||
name | ||
}; | ||
return validate && !validateIconName(result, allowSimpleName) ? null : result; | ||
} | ||
return null; | ||
}; | ||
const validateIconName = (icon, allowSimpleName) => { | ||
if (!icon) { | ||
return false; | ||
} | ||
return !!((icon.provider === "" || icon.provider.match(matchIconName)) && (allowSimpleName && icon.prefix === "" || icon.prefix.match(matchIconName)) && icon.name.match(matchIconName)); | ||
}; | ||
const optionalPropertyDefaults = { | ||
@@ -323,3 +323,3 @@ provider: "", | ||
} | ||
function iconExists(name) { | ||
function iconLoaded(name) { | ||
return !!getIconData(name); | ||
@@ -385,2 +385,31 @@ } | ||
function splitSVGDefs(content, tag = "defs") { | ||
let defs = ""; | ||
const index = content.indexOf("<" + tag); | ||
while (index >= 0) { | ||
const start = content.indexOf(">", index); | ||
const end = content.indexOf("</" + tag); | ||
if (start === -1 || end === -1) { | ||
break; | ||
} | ||
const endEnd = content.indexOf(">", end); | ||
if (endEnd === -1) { | ||
break; | ||
} | ||
defs += content.slice(start + 1, end).trim(); | ||
content = content.slice(0, index).trim() + content.slice(endEnd + 1); | ||
} | ||
return { | ||
defs, | ||
content | ||
}; | ||
} | ||
function mergeDefsAndContent(defs, content) { | ||
return defs ? "<defs>" + defs + "</defs>" + content : content; | ||
} | ||
function wrapSVGContent(body, start, end) { | ||
const split = splitSVGDefs(body); | ||
return mergeDefsAndContent(split.defs, start + split.content + end); | ||
} | ||
const isUnsetKeyword = (value) => value === "unset" || value === "undefined" || value === "none"; | ||
@@ -462,3 +491,7 @@ function iconToSVG(icon, customisations) { | ||
if (transformations.length) { | ||
body = '<g transform="' + transformations.join(" ") + '">' + body + "</g>"; | ||
body = wrapSVGContent( | ||
body, | ||
'<g transform="' + transformations.join(" ") + '">', | ||
"</g>" | ||
); | ||
} | ||
@@ -487,5 +520,7 @@ }); | ||
setAttr("height", height); | ||
attributes.viewBox = box.left.toString() + " " + box.top.toString() + " " + boxWidth.toString() + " " + boxHeight.toString(); | ||
const viewBox = [box.left, box.top, boxWidth, boxHeight]; | ||
attributes.viewBox = viewBox.join(" "); | ||
return { | ||
attributes, | ||
viewBox, | ||
body | ||
@@ -1136,2 +1171,3 @@ }; | ||
const browserStorageCacheExpiration = 168; | ||
const browserStorageLimit = 50; | ||
@@ -1295,3 +1331,3 @@ function getStoredItem(func, key) { | ||
index = getBrowserStorageItemsCount(func); | ||
if (!setBrowserStorageItemsCount(func, index + 1)) { | ||
if (index >= browserStorageLimit || !setBrowserStorageItemsCount(func, index + 1)) { | ||
return; | ||
@@ -1643,8 +1679,6 @@ } | ||
props, | ||
// True if icon should have vertical-align added | ||
inline, | ||
// Optional reference for SVG/SPAN, extracted by React.forwardRef() | ||
ref) => { | ||
// Icon name | ||
name) => { | ||
// Get default properties | ||
const defaultProps = inline | ||
const defaultProps = props.inline | ||
? inlineDefaults | ||
@@ -1662,4 +1696,19 @@ : defaultExtendedIconCustomisations; | ||
...(mode === 'svg' ? svgDefaults : {}), | ||
ref, | ||
}; | ||
if (name) { | ||
const iconName = stringToIcon(name, false, true); | ||
if (iconName) { | ||
const classNames = ['iconify']; | ||
const props = [ | ||
'provider', | ||
'prefix', | ||
]; | ||
for (const prop of props) { | ||
if (iconName[prop]) { | ||
classNames.push('iconify--' + iconName[prop]); | ||
} | ||
} | ||
componentProps.className = classNames.join(' '); | ||
} | ||
} | ||
// Get element properties | ||
@@ -1678,5 +1727,14 @@ for (let key in props) { | ||
case 'mode': | ||
case 'ssr': | ||
break; | ||
// Forward ref | ||
case '_ref': | ||
case '_inline': | ||
componentProps.ref = value; | ||
break; | ||
// Merge class names | ||
case 'className': | ||
componentProps[key] = | ||
(componentProps[key] ? componentProps[key] + ' ' : '') + | ||
value; | ||
break; | ||
// Boolean attributes | ||
@@ -1748,3 +1806,3 @@ case 'inline': | ||
}; | ||
return React.createElement('svg', componentProps); | ||
return react.createElement('svg', componentProps); | ||
} | ||
@@ -1771,3 +1829,3 @@ // Render <span> with style | ||
}; | ||
return React.createElement('span', componentProps); | ||
return react.createElement('span', componentProps); | ||
}; | ||
@@ -1851,148 +1909,73 @@ | ||
} | ||
class IconComponent extends React.Component { | ||
constructor(props) { | ||
super(props); | ||
this.state = { | ||
// Render placeholder before component is mounted | ||
icon: null, | ||
}; | ||
} | ||
/** | ||
* Abort loading icon | ||
*/ | ||
_abortLoading() { | ||
if (this._loading) { | ||
this._loading.abort(); | ||
this._loading = null; | ||
function IconComponent(props) { | ||
const [mounted, setMounted] = react.useState(!!props.ssr); | ||
const [abort, setAbort] = react.useState({}); | ||
const [state, setState] = react.useState({ | ||
name: '', | ||
}); | ||
// Cancel loading | ||
function cleanup() { | ||
const callback = abort.callback; | ||
if (callback) { | ||
callback(); | ||
setAbort({}); | ||
} | ||
} | ||
/** | ||
* Update state | ||
*/ | ||
_setData(icon) { | ||
if (this.state.icon !== icon) { | ||
this.setState({ | ||
icon, | ||
// Update state | ||
function updateState() { | ||
var _a; | ||
const name = props.icon; | ||
if (typeof name === 'object') { | ||
// Icon as object | ||
cleanup(); | ||
setState({ | ||
name: '', | ||
data: name, | ||
}); | ||
} | ||
} | ||
/** | ||
* Check if icon should be loaded | ||
*/ | ||
_checkIcon(changed) { | ||
const state = this.state; | ||
const icon = this.props.icon; | ||
// Icon is an object | ||
if (typeof icon === 'object' && | ||
icon !== null && | ||
typeof icon.body === 'string') { | ||
// Stop loading | ||
this._icon = ''; | ||
this._abortLoading(); | ||
if (changed || state.icon === null) { | ||
// Set data if it was changed | ||
this._setData({ | ||
data: icon, | ||
}); | ||
} | ||
return; | ||
} | ||
// Invalid icon? | ||
let iconName; | ||
if (typeof icon !== 'string' || | ||
(iconName = stringToIcon(icon, false, true)) === null) { | ||
this._abortLoading(); | ||
this._setData(null); | ||
return; | ||
} | ||
// Load icon | ||
const data = getIconData(iconName); | ||
if (!data) { | ||
// Icon data is not available | ||
if (!this._loading || this._loading.name !== icon) { | ||
// New icon to load | ||
this._abortLoading(); | ||
this._icon = ''; | ||
this._setData(null); | ||
if (data !== null) { | ||
// Icon was not loaded | ||
this._loading = { | ||
name: icon, | ||
abort: loadIcons([iconName], this._checkIcon.bind(this, false)), | ||
}; | ||
} | ||
} | ||
return; | ||
} | ||
// Icon data is available | ||
if (this._icon !== icon || state.icon === null) { | ||
// New icon or icon has been loaded | ||
this._abortLoading(); | ||
this._icon = icon; | ||
// Add classes | ||
const classes = ['iconify']; | ||
if (iconName.prefix !== '') { | ||
classes.push('iconify--' + iconName.prefix); | ||
} | ||
if (iconName.provider !== '') { | ||
classes.push('iconify--' + iconName.provider); | ||
} | ||
// Set data | ||
this._setData({ | ||
// New icon or got icon data | ||
const data = getIconData(name); | ||
if (state.name !== name || data !== state.data) { | ||
cleanup(); | ||
setState({ | ||
name, | ||
data, | ||
classes, | ||
}); | ||
if (this.props.onLoad) { | ||
this.props.onLoad(icon); | ||
if (data === undefined) { | ||
// Load icon, update state when done | ||
const callback = loadIcons([name], updateState); | ||
setAbort({ | ||
callback, | ||
}); | ||
} | ||
else if (data) { | ||
// Icon data is available: trigger onLoad callback if present | ||
(_a = props.onLoad) === null || _a === void 0 ? void 0 : _a.call(props, name); | ||
} | ||
} | ||
} | ||
/** | ||
* Component mounted | ||
*/ | ||
componentDidMount() { | ||
this._checkIcon(false); | ||
} | ||
/** | ||
* Component updated | ||
*/ | ||
componentDidUpdate(oldProps) { | ||
if (oldProps.icon !== this.props.icon) { | ||
this._checkIcon(true); | ||
// Mounted state, cleanup for loader | ||
react.useEffect(() => { | ||
setMounted(true); | ||
updateState(); | ||
return cleanup; | ||
}, []); | ||
// Icon changed | ||
react.useEffect(() => { | ||
if (mounted) { | ||
updateState(); | ||
} | ||
}, [props.icon]); | ||
// Render icon | ||
const { name, data } = state; | ||
if (!data) { | ||
return props.children | ||
? props.children | ||
: react.createElement('span', {}); | ||
} | ||
/** | ||
* Abort loading | ||
*/ | ||
componentWillUnmount() { | ||
this._abortLoading(); | ||
} | ||
/** | ||
* Render | ||
*/ | ||
render() { | ||
const props = this.props; | ||
const icon = this.state.icon; | ||
if (icon === null) { | ||
// Render placeholder | ||
return props.children | ||
? props.children | ||
: React.createElement('span', {}); | ||
} | ||
// Add classes | ||
let newProps = props; | ||
if (icon.classes) { | ||
newProps = { | ||
...props, | ||
className: (typeof props.className === 'string' | ||
? props.className + ' ' | ||
: '') + icon.classes.join(' '), | ||
}; | ||
} | ||
// Render icon | ||
return render({ | ||
...defaultIconProps, | ||
...icon.data, | ||
}, newProps, props._inline, props._ref); | ||
} | ||
return render({ | ||
...defaultIconProps, | ||
...data, | ||
}, props, name); | ||
} | ||
@@ -2004,10 +1987,6 @@ /** | ||
*/ | ||
const Icon = React.forwardRef(function Icon(props, ref) { | ||
const newProps = { | ||
...props, | ||
_ref: ref, | ||
_inline: false, | ||
}; | ||
return React.createElement(IconComponent, newProps); | ||
}); | ||
const Icon = react.forwardRef((props, ref) => IconComponent({ | ||
...props, | ||
_ref: ref, | ||
})); | ||
/** | ||
@@ -2018,10 +1997,7 @@ * Inline icon (has negative verticalAlign that makes it behave like icon font) | ||
*/ | ||
const InlineIcon = React.forwardRef(function InlineIcon(props, ref) { | ||
const newProps = { | ||
...props, | ||
_ref: ref, | ||
_inline: true, | ||
}; | ||
return React.createElement(IconComponent, newProps); | ||
}); | ||
const InlineIcon = react.forwardRef((props, ref) => IconComponent({ | ||
inline: true, | ||
...props, | ||
_ref: ref, | ||
})); | ||
/** | ||
@@ -2050,3 +2026,4 @@ * Internal API | ||
exports.getIcon = getIcon; | ||
exports.iconExists = iconExists; | ||
exports.iconExists = iconLoaded; | ||
exports.iconLoaded = iconLoaded; | ||
exports.listIcons = listIcons; | ||
@@ -2053,0 +2030,0 @@ exports.loadIcon = loadIcon; |
@@ -0,6 +1,6 @@ | ||
/// <reference types="react" /> | ||
import type { IconifyIcon } from '@iconify/types'; | ||
import type { IconifyJSON } from '@iconify/types'; | ||
import { IconifyTransformations } from '@iconify/types'; | ||
import { default as React_2 } from 'react'; | ||
import type { RefAttributes } from 'react'; | ||
import type { SVGProps } from 'react'; | ||
@@ -29,4 +29,6 @@ | ||
*/ | ||
export declare const Icon: (props: IconProps) => React_2.ReactElement<IconProps, string | React_2.JSXElementConstructor<any>>; | ||
export declare const Icon: IconComponentType; | ||
declare type IconComponentType = (props: IconProps) => JSX.Element; | ||
/** | ||
@@ -67,2 +69,3 @@ * React component properties: generic element for Icon component, SVG for generated component | ||
id?: string; | ||
ssr?: boolean; | ||
onLoad?: IconifyIconOnLoad; | ||
@@ -99,6 +102,4 @@ } | ||
*/ | ||
export declare type IconProps = IconifyElementProps & IconifyIconProps & ReactRefProp; | ||
export declare type IconProps = IconifyElementProps & IconifyIconProps; | ||
declare type IconRef = RefAttributes<SVGSVGElement>; | ||
/** | ||
@@ -109,8 +110,4 @@ * Inline icon (has negative verticalAlign that makes it behave like icon font) | ||
*/ | ||
export declare const InlineIcon: (props: IconProps) => React_2.ReactElement<IconProps, string | React_2.JSXElementConstructor<any>>; | ||
export declare const InlineIcon: IconComponentType; | ||
declare interface ReactRefProp { | ||
ref?: IconRef; | ||
} | ||
export { } |
'use strict'; | ||
var React = require('react'); | ||
var react = require('react'); | ||
@@ -119,2 +119,50 @@ const defaultIconDimensions = Object.freeze( | ||
const matchIconName = /^[a-z0-9]+(-[a-z0-9]+)*$/; | ||
const stringToIcon = (value, validate, allowSimpleName, provider = "") => { | ||
const colonSeparated = value.split(":"); | ||
if (value.slice(0, 1) === "@") { | ||
if (colonSeparated.length < 2 || colonSeparated.length > 3) { | ||
return null; | ||
} | ||
provider = colonSeparated.shift().slice(1); | ||
} | ||
if (colonSeparated.length > 3 || !colonSeparated.length) { | ||
return null; | ||
} | ||
if (colonSeparated.length > 1) { | ||
const name2 = colonSeparated.pop(); | ||
const prefix = colonSeparated.pop(); | ||
const result = { | ||
// Allow provider without '@': "provider:prefix:name" | ||
provider: colonSeparated.length > 0 ? colonSeparated[0] : provider, | ||
prefix, | ||
name: name2 | ||
}; | ||
return validate && !validateIconName(result) ? null : result; | ||
} | ||
const name = colonSeparated[0]; | ||
const dashSeparated = name.split("-"); | ||
if (dashSeparated.length > 1) { | ||
const result = { | ||
provider, | ||
prefix: dashSeparated.shift(), | ||
name: dashSeparated.join("-") | ||
}; | ||
return validate && !validateIconName(result) ? null : result; | ||
} | ||
if (allowSimpleName && provider === "") { | ||
const result = { | ||
provider, | ||
prefix: "", | ||
name | ||
}; | ||
return validate && !validateIconName(result, allowSimpleName) ? null : result; | ||
} | ||
return null; | ||
}; | ||
const validateIconName = (icon, allowSimpleName) => { | ||
if (!icon) { | ||
return false; | ||
} | ||
return !!((icon.provider === "" || icon.provider.match(matchIconName)) && (allowSimpleName && icon.prefix === "" || icon.prefix.match(matchIconName)) && icon.name.match(matchIconName)); | ||
}; | ||
@@ -285,2 +333,31 @@ const optionalPropertyDefaults = { | ||
function splitSVGDefs(content, tag = "defs") { | ||
let defs = ""; | ||
const index = content.indexOf("<" + tag); | ||
while (index >= 0) { | ||
const start = content.indexOf(">", index); | ||
const end = content.indexOf("</" + tag); | ||
if (start === -1 || end === -1) { | ||
break; | ||
} | ||
const endEnd = content.indexOf(">", end); | ||
if (endEnd === -1) { | ||
break; | ||
} | ||
defs += content.slice(start + 1, end).trim(); | ||
content = content.slice(0, index).trim() + content.slice(endEnd + 1); | ||
} | ||
return { | ||
defs, | ||
content | ||
}; | ||
} | ||
function mergeDefsAndContent(defs, content) { | ||
return defs ? "<defs>" + defs + "</defs>" + content : content; | ||
} | ||
function wrapSVGContent(body, start, end) { | ||
const split = splitSVGDefs(body); | ||
return mergeDefsAndContent(split.defs, start + split.content + end); | ||
} | ||
const isUnsetKeyword = (value) => value === "unset" || value === "undefined" || value === "none"; | ||
@@ -362,3 +439,7 @@ function iconToSVG(icon, customisations) { | ||
if (transformations.length) { | ||
body = '<g transform="' + transformations.join(" ") + '">' + body + "</g>"; | ||
body = wrapSVGContent( | ||
body, | ||
'<g transform="' + transformations.join(" ") + '">', | ||
"</g>" | ||
); | ||
} | ||
@@ -387,5 +468,7 @@ }); | ||
setAttr("height", height); | ||
attributes.viewBox = box.left.toString() + " " + box.top.toString() + " " + boxWidth.toString() + " " + boxHeight.toString(); | ||
const viewBox = [box.left, box.top, boxWidth, boxHeight]; | ||
attributes.viewBox = viewBox.join(" "); | ||
return { | ||
attributes, | ||
viewBox, | ||
body | ||
@@ -522,8 +605,6 @@ }; | ||
props, | ||
// True if icon should have vertical-align added | ||
inline, | ||
// Optional reference for SVG/SPAN, extracted by React.forwardRef() | ||
ref) => { | ||
// Icon name | ||
name) => { | ||
// Get default properties | ||
const defaultProps = inline | ||
const defaultProps = props.inline | ||
? inlineDefaults | ||
@@ -541,4 +622,19 @@ : defaultExtendedIconCustomisations; | ||
...(mode === 'svg' ? svgDefaults : {}), | ||
ref, | ||
}; | ||
if (name) { | ||
const iconName = stringToIcon(name, false, true); | ||
if (iconName) { | ||
const classNames = ['iconify']; | ||
const props = [ | ||
'provider', | ||
'prefix', | ||
]; | ||
for (const prop of props) { | ||
if (iconName[prop]) { | ||
classNames.push('iconify--' + iconName[prop]); | ||
} | ||
} | ||
componentProps.className = classNames.join(' '); | ||
} | ||
} | ||
// Get element properties | ||
@@ -557,5 +653,14 @@ for (let key in props) { | ||
case 'mode': | ||
case 'ssr': | ||
break; | ||
// Forward ref | ||
case '_ref': | ||
case '_inline': | ||
componentProps.ref = value; | ||
break; | ||
// Merge class names | ||
case 'className': | ||
componentProps[key] = | ||
(componentProps[key] ? componentProps[key] + ' ' : '') + | ||
value; | ||
break; | ||
// Boolean attributes | ||
@@ -627,3 +732,3 @@ case 'inline': | ||
}; | ||
return React.createElement('svg', componentProps); | ||
return react.createElement('svg', componentProps); | ||
} | ||
@@ -650,3 +755,3 @@ // Render <span> with style | ||
}; | ||
return React.createElement('span', componentProps); | ||
return react.createElement('span', componentProps); | ||
}; | ||
@@ -658,26 +763,14 @@ | ||
const storage = Object.create(null); | ||
/** | ||
* Generate icon | ||
*/ | ||
function component(props, inline, ref) { | ||
// Split properties | ||
const propsIcon = props.icon; | ||
const icon = typeof propsIcon === 'string' | ||
? storage[propsIcon] | ||
: typeof propsIcon === 'object' | ||
? propsIcon | ||
: null; | ||
// Validate icon object | ||
if (icon === null || | ||
typeof icon !== 'object' || | ||
typeof icon.body !== 'string') { | ||
function IconComponent(props) { | ||
const icon = props.icon; | ||
const data = typeof icon === 'string' ? storage[icon] : icon; | ||
if (!data) { | ||
return props.children | ||
? props.children | ||
: React.createElement('span', {}); | ||
: react.createElement('span', {}); | ||
} | ||
// Valid icon: render it | ||
return render({ | ||
...defaultIconProps, | ||
...icon, | ||
}, props, inline, ref); | ||
...data, | ||
}, props, typeof icon === 'string' ? icon : undefined); | ||
} | ||
@@ -689,5 +782,6 @@ /** | ||
*/ | ||
const Icon = React.forwardRef(function Icon(props, ref) { | ||
return component(props, false, ref); | ||
}); | ||
const Icon = react.forwardRef((props, ref) => IconComponent({ | ||
...props, | ||
_ref: ref, | ||
})); | ||
/** | ||
@@ -698,5 +792,7 @@ * Inline icon (has negative verticalAlign that makes it behave like icon font) | ||
*/ | ||
const InlineIcon = React.forwardRef(function InlineIcon(props, ref) { | ||
return component(props, true, ref); | ||
}); | ||
const InlineIcon = react.forwardRef((props, ref) => IconComponent({ | ||
inline: true, | ||
...props, | ||
_ref: ref, | ||
})); | ||
/** | ||
@@ -703,0 +799,0 @@ * Add icon to storage, allowing to call it by name |
@@ -5,3 +5,7 @@ { | ||
"author": "Vjacheslav Trushkin", | ||
"version": "4.1.1", | ||
"version": "5.0.0-beta.1", | ||
"publishConfig": { | ||
"access": "public", | ||
"tag": "next" | ||
}, | ||
"license": "MIT", | ||
@@ -41,16 +45,16 @@ "bugs": "https://github.com/iconify/iconify/issues", | ||
"devDependencies": { | ||
"@babel/preset-env": "^7.22.4", | ||
"@babel/preset-react": "^7.22.3", | ||
"@microsoft/api-extractor": "^7.35.1", | ||
"@rollup/plugin-node-resolve": "^15.1.0", | ||
"@types/react": "^18.2.8", | ||
"babel-jest": "^29.5.0", | ||
"jest": "^29.5.0", | ||
"@babel/preset-env": "^7.24.4", | ||
"@babel/preset-react": "^7.24.1", | ||
"@microsoft/api-extractor": "^7.43.1", | ||
"@rollup/plugin-node-resolve": "^15.2.3", | ||
"@types/react": "^18.2.78", | ||
"babel-jest": "^29.7.0", | ||
"jest": "^29.7.0", | ||
"react": "^18.2.0", | ||
"react-test-renderer": "^18.2.0", | ||
"rimraf": "^4.4.1", | ||
"rollup": "^3.23.1", | ||
"typescript": "^5.1.3", | ||
"@iconify/core": "^2.0.1", | ||
"@iconify/utils": "^2.1.6" | ||
"rollup": "^3.29.4", | ||
"typescript": "^5.4.5", | ||
"@iconify/core": "^2.0.2", | ||
"@iconify/utils": "^2.1.23" | ||
}, | ||
@@ -68,5 +72,4 @@ "peerDependencies": { | ||
"build:api": "api-extractor run --local --verbose --config api-extractor.iconify.json", | ||
"build:cleanup": "node cleanup", | ||
"test": "jest --runInBand" | ||
} | ||
} |
@@ -14,3 +14,3 @@ # Iconify for React | ||
- One syntax for over 150,000 icons from 100+ icon sets. | ||
- One syntax for over 200,000 icons from 150+ icon sets. | ||
- Renders SVG. Many components simply render icon fonts, which look ugly. Iconify renders pixel perfect SVG. | ||
@@ -51,3 +51,3 @@ - Loads icons on demand. No need to bundle icons, component will automatically load icon data for icons that you use from Iconify API. | ||
Component will automatically retrieve data for "mdi-light:home" from Iconify API and render it. There are over 150,000 icons available on Iconify API from various free and open source icon sets, including all the most popular icon sets. | ||
Component will automatically retrieve data for "mdi-light:home" from Iconify API and render it. There are over 200,000 icons available on Iconify API from various free and open source icon sets, including all the most popular icon sets. | ||
@@ -54,0 +54,0 @@ ## Offline usage |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
183572
5995
1