1 | import React, { Component } from 'react';
|
2 | import PropTypes from 'prop-types';
|
3 | import {
|
4 | NativeModules,
|
5 | Platform,
|
6 | PixelRatio,
|
7 | processColor,
|
8 | Text,
|
9 | } from './react-native';
|
10 |
|
11 | import createIconButtonComponent from './icon-button';
|
12 | import createTabBarItemIOSComponent from './tab-bar-item-ios';
|
13 | import createToolbarAndroidComponent from './toolbar-android';
|
14 |
|
15 | const NativeIconAPI =
|
16 | NativeModules.RNVectorIconsManager || NativeModules.RNVectorIconsModule;
|
17 |
|
18 | const DEFAULT_ICON_SIZE = 12;
|
19 | const DEFAULT_ICON_COLOR = 'black';
|
20 |
|
21 | export default function createIconSet(glyphMap, fontFamily, fontFile) {
|
22 | let fontReference = fontFamily;
|
23 |
|
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,
|
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 | }
|