UNPKG

4.73 kBJavaScriptView Raw
1import React, { Component } from 'react';
2import PropTypes from 'prop-types';
3import {
4 NativeModules,
5 Platform,
6 PixelRatio,
7 processColor,
8 Text,
9} from './react-native';
10
11import createIconButtonComponent from './icon-button';
12import createTabBarItemIOSComponent from './tab-bar-item-ios';
13import createToolbarAndroidComponent from './toolbar-android';
14
15const NativeIconAPI =
16 NativeModules.RNVectorIconsManager || NativeModules.RNVectorIconsModule;
17
18const DEFAULT_ICON_SIZE = 12;
19const DEFAULT_ICON_COLOR = 'black';
20
21export default function createIconSet(glyphMap, fontFamily, fontFile) {
22 let fontReference = fontFamily;
23 // Android doesn't care about actual fontFamily name, it will only look in fonts folder.
24 if (Platform.OS === 'android' && fontFile) {
25 fontReference = fontFile.replace(/\.(otf|ttf)$/, '');
26 }
27
28 if (Platform.OS === 'windows' && fontFile) {
29 fontReference = `Assets/${fontFile}#${fontFamily}`;
30 }
31
32 const IconNamePropType = PropTypes.oneOf(Object.keys(glyphMap));
33
34 class Icon extends Component {
35 static propTypes = {
36 name: IconNamePropType,
37 size: PropTypes.number,
38 color: PropTypes.string,
39 children: PropTypes.node,
40 style: PropTypes.any, // eslint-disable-line react/forbid-prop-types
41 };
42
43 static defaultProps = {
44 size: DEFAULT_ICON_SIZE,
45 allowFontScaling: false,
46 };
47
48 setNativeProps(nativeProps) {
49 if (this.root) {
50 this.root.setNativeProps(nativeProps);
51 }
52 }
53
54 root = null;
55 handleRef = ref => {
56 this.root = ref;
57 };
58
59 render() {
60 const { name, size, color, style, ...props } = this.props;
61
62 let glyph = name ? glyphMap[name] || '?' : '';
63 if (typeof glyph === 'number') {
64 glyph = String.fromCharCode(glyph);
65 }
66
67 const styleDefaults = {
68 fontSize: size,
69 color,
70 };
71
72 const styleOverrides = {
73 fontFamily: fontReference,
74 fontWeight: 'normal',
75 fontStyle: 'normal',
76 };
77
78 props.style = [styleDefaults, style, styleOverrides];
79 props.ref = this.handleRef;
80
81 return (
82 <Text {...props}>
83 {glyph}
84 {this.props.children}
85 </Text>
86 );
87 }
88 }
89
90 const imageSourceCache = {};
91
92 function ensureNativeModuleAvailable() {
93 if (!NativeIconAPI) {
94 if (Platform.OS === 'android') {
95 throw new Error(
96 'RNVectorIconsModule not available, did you properly integrate the module? Try running `react-native link react-native-vector-icons` and recompiling.'
97 );
98 }
99 throw new Error(
100 'RNVectorIconsManager not available, did you add the library to your project and link with libRNVectorIcons.a? Try running `react-native link react-native-vector-icons` and recompiling.'
101 );
102 }
103 }
104
105 function getImageSource(
106 name,
107 size = DEFAULT_ICON_SIZE,
108 color = DEFAULT_ICON_COLOR
109 ) {
110 ensureNativeModuleAvailable();
111
112 let glyph = glyphMap[name] || '?';
113 if (typeof glyph === 'number') {
114 glyph = String.fromCharCode(glyph);
115 }
116
117 const processedColor = processColor(color);
118 const cacheKey = `${glyph}:${size}:${processedColor}`;
119 const scale = PixelRatio.get();
120
121 return new Promise((resolve, reject) => {
122 const cached = imageSourceCache[cacheKey];
123 if (typeof cached !== 'undefined') {
124 if (!cached || cached instanceof Error) {
125 reject(cached);
126 } else {
127 resolve({ uri: cached, scale });
128 }
129 } else {
130 NativeIconAPI.getImageForFont(
131 fontReference,
132 glyph,
133 size,
134 processedColor,
135 (err, image) => {
136 const error = typeof err === 'string' ? new Error(err) : err;
137 imageSourceCache[cacheKey] = image || error || false;
138 if (!error && image) {
139 resolve({ uri: image, scale });
140 } else {
141 reject(error);
142 }
143 }
144 );
145 }
146 });
147 }
148
149 function loadFont(file = fontFile) {
150 if (Platform.OS === 'ios') {
151 ensureNativeModuleAvailable();
152 if (!file) {
153 return Promise.reject(
154 new Error('Unable to load font, because no file was specified. ')
155 );
156 }
157 return NativeIconAPI.loadFontWithFileName(...file.split('.'));
158 }
159 return Promise.resolve();
160 }
161
162 Icon.Button = createIconButtonComponent(Icon);
163 Icon.TabBarItem = createTabBarItemIOSComponent(
164 IconNamePropType,
165 getImageSource
166 );
167 Icon.TabBarItemIOS = Icon.TabBarItem;
168 Icon.ToolbarAndroid = createToolbarAndroidComponent(
169 IconNamePropType,
170 getImageSource
171 );
172 Icon.getImageSource = getImageSource;
173 Icon.loadFont = loadFont;
174
175 return Icon;
176}