You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
201 lines
4.9 KiB
201 lines
4.9 KiB
import React, { PureComponent } from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import {
|
|
NativeModules,
|
|
Platform,
|
|
PixelRatio,
|
|
processColor,
|
|
Text,
|
|
} from 'react-native';
|
|
|
|
import ensureNativeModuleAvailable from './ensure-native-module-available';
|
|
import createIconSourceCache from './create-icon-source-cache';
|
|
import createIconButtonComponent from './icon-button';
|
|
|
|
export const NativeIconAPI =
|
|
NativeModules.RNVectorIconsManager || NativeModules.RNVectorIconsModule;
|
|
|
|
export const DEFAULT_ICON_SIZE = 12;
|
|
export const DEFAULT_ICON_COLOR = 'black';
|
|
|
|
export default function createIconSet(
|
|
glyphMap,
|
|
fontFamily,
|
|
fontFile,
|
|
fontStyle
|
|
) {
|
|
// Android doesn't care about actual fontFamily name, it will only look in fonts folder.
|
|
const fontBasename = fontFile
|
|
? fontFile.replace(/\.(otf|ttf)$/, '')
|
|
: fontFamily;
|
|
|
|
const fontReference = Platform.select({
|
|
windows: `/Assets/${fontFile}#${fontFamily}`,
|
|
android: fontBasename,
|
|
web: fontBasename,
|
|
default: fontFamily,
|
|
});
|
|
|
|
const IconNamePropType = PropTypes.oneOf(Object.keys(glyphMap));
|
|
|
|
class Icon extends PureComponent {
|
|
root = null;
|
|
|
|
static propTypes = {
|
|
allowFontScaling: PropTypes.bool,
|
|
name: IconNamePropType,
|
|
size: PropTypes.number,
|
|
color: PropTypes.any, // eslint-disable-line react/forbid-prop-types
|
|
children: PropTypes.node,
|
|
style: PropTypes.any, // eslint-disable-line react/forbid-prop-types
|
|
};
|
|
|
|
static defaultProps = {
|
|
size: DEFAULT_ICON_SIZE,
|
|
allowFontScaling: false,
|
|
};
|
|
|
|
setNativeProps(nativeProps) {
|
|
if (this.root) {
|
|
this.root.setNativeProps(nativeProps);
|
|
}
|
|
}
|
|
|
|
handleRef = ref => {
|
|
this.root = ref;
|
|
};
|
|
|
|
render() {
|
|
const { name, size, color, style, children, ...props } = this.props;
|
|
|
|
let glyph = name ? glyphMap[name] || '?' : '';
|
|
if (typeof glyph === 'number') {
|
|
glyph = String.fromCodePoint(glyph);
|
|
}
|
|
|
|
const styleDefaults = {
|
|
fontSize: size,
|
|
color,
|
|
};
|
|
|
|
const styleOverrides = {
|
|
fontFamily: fontReference,
|
|
fontWeight: 'normal',
|
|
fontStyle: 'normal',
|
|
};
|
|
|
|
props.style = [styleDefaults, style, styleOverrides, fontStyle || {}];
|
|
props.ref = this.handleRef;
|
|
|
|
return (
|
|
<Text selectable={false} {...props}>
|
|
{glyph}
|
|
{children}
|
|
</Text>
|
|
);
|
|
}
|
|
}
|
|
|
|
const imageSourceCache = createIconSourceCache();
|
|
|
|
function resolveGlyph(name) {
|
|
const glyph = glyphMap[name] || '?';
|
|
if (typeof glyph === 'number') {
|
|
return String.fromCodePoint(glyph);
|
|
}
|
|
return glyph;
|
|
}
|
|
|
|
function getImageSourceSync(
|
|
name,
|
|
size = DEFAULT_ICON_SIZE,
|
|
color = DEFAULT_ICON_COLOR
|
|
) {
|
|
ensureNativeModuleAvailable();
|
|
|
|
const glyph = resolveGlyph(name);
|
|
const processedColor = processColor(color);
|
|
const cacheKey = `${glyph}:${size}:${processedColor}`;
|
|
|
|
if (imageSourceCache.has(cacheKey)) {
|
|
return imageSourceCache.get(cacheKey);
|
|
}
|
|
try {
|
|
const imagePath = NativeIconAPI.getImageForFontSync(
|
|
fontReference,
|
|
glyph,
|
|
size,
|
|
processedColor
|
|
);
|
|
const value = { uri: imagePath, scale: PixelRatio.get() };
|
|
imageSourceCache.setValue(cacheKey, value);
|
|
return value;
|
|
} catch (error) {
|
|
imageSourceCache.setError(cacheKey, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async function getImageSource(
|
|
name,
|
|
size = DEFAULT_ICON_SIZE,
|
|
color = DEFAULT_ICON_COLOR
|
|
) {
|
|
ensureNativeModuleAvailable();
|
|
|
|
const glyph = resolveGlyph(name);
|
|
const processedColor = processColor(color);
|
|
const cacheKey = `${glyph}:${size}:${processedColor}`;
|
|
|
|
if (imageSourceCache.has(cacheKey)) {
|
|
return imageSourceCache.get(cacheKey);
|
|
}
|
|
try {
|
|
const imagePath = await NativeIconAPI.getImageForFont(
|
|
fontReference,
|
|
glyph,
|
|
size,
|
|
processedColor
|
|
);
|
|
const value = { uri: imagePath, scale: PixelRatio.get() };
|
|
imageSourceCache.setValue(cacheKey, value);
|
|
return value;
|
|
} catch (error) {
|
|
imageSourceCache.setError(cacheKey, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async function loadFont(file = fontFile) {
|
|
if (Platform.OS === 'ios') {
|
|
ensureNativeModuleAvailable();
|
|
if (!file) {
|
|
throw new Error('Unable to load font, because no file was specified. ');
|
|
}
|
|
await NativeIconAPI.loadFontWithFileName(...file.split('.'));
|
|
}
|
|
}
|
|
|
|
function hasIcon(name) {
|
|
return Object.prototype.hasOwnProperty.call(glyphMap, name);
|
|
}
|
|
|
|
function getRawGlyphMap() {
|
|
return glyphMap;
|
|
}
|
|
|
|
function getFontFamily() {
|
|
return fontReference;
|
|
}
|
|
|
|
Icon.Button = createIconButtonComponent(Icon);
|
|
Icon.getImageSource = getImageSource;
|
|
Icon.getImageSourceSync = getImageSourceSync;
|
|
Icon.loadFont = loadFont;
|
|
Icon.hasIcon = hasIcon;
|
|
Icon.getRawGlyphMap = getRawGlyphMap;
|
|
Icon.getFontFamily = getFontFamily;
|
|
|
|
return Icon;
|
|
}
|