StyleSheet.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. /**
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * @flow
  8. * @format
  9. */
  10. 'use strict';
  11. const PixelRatio = require('../Utilities/PixelRatio');
  12. const ReactNativeStyleAttributes = require('../Components/View/ReactNativeStyleAttributes');
  13. const StyleSheetValidation = require('./StyleSheetValidation');
  14. const flatten = require('./flattenStyle');
  15. import type {
  16. ____Styles_Internal,
  17. ____DangerouslyImpreciseStyle_Internal,
  18. ____DangerouslyImpreciseStyleProp_Internal,
  19. ____ViewStyle_Internal,
  20. ____ViewStyleProp_Internal,
  21. ____TextStyle_Internal,
  22. ____TextStyleProp_Internal,
  23. ____ImageStyle_Internal,
  24. ____ImageStyleProp_Internal,
  25. } from './StyleSheetTypes';
  26. /**
  27. * This type should be used as the type for a prop that is passed through
  28. * to a <View>'s `style` prop. This ensures call sites of the component
  29. * can't pass styles that View doesn't support such as `fontSize`.`
  30. *
  31. * type Props = {style: ViewStyleProp}
  32. * const MyComponent = (props: Props) => <View style={props.style} />
  33. */
  34. export type ViewStyleProp = ____ViewStyleProp_Internal;
  35. /**
  36. * This type should be used as the type for a prop that is passed through
  37. * to a <Text>'s `style` prop. This ensures call sites of the component
  38. * can't pass styles that Text doesn't support such as `resizeMode`.`
  39. *
  40. * type Props = {style: TextStyleProp}
  41. * const MyComponent = (props: Props) => <Text style={props.style} />
  42. */
  43. export type TextStyleProp = ____TextStyleProp_Internal;
  44. /**
  45. * This type should be used as the type for a prop that is passed through
  46. * to an <Image>'s `style` prop. This ensures call sites of the component
  47. * can't pass styles that Image doesn't support such as `fontSize`.`
  48. *
  49. * type Props = {style: ImageStyleProp}
  50. * const MyComponent = (props: Props) => <Image style={props.style} />
  51. */
  52. export type ImageStyleProp = ____ImageStyleProp_Internal;
  53. /**
  54. * WARNING: You probably shouldn't be using this type. This type
  55. * is similar to the ones above except it allows styles that are accepted
  56. * by all of View, Text, or Image. It is therefore very unsafe to pass this
  57. * through to an underlying component. Using this is almost always a mistake
  58. * and using one of the other more restrictive types is likely the right choice.
  59. */
  60. export type DangerouslyImpreciseStyleProp = ____DangerouslyImpreciseStyleProp_Internal;
  61. /**
  62. * Utility type for getting the values for specific style keys.
  63. *
  64. * The following is bad because position is more restrictive than 'string':
  65. * ```
  66. * type Props = {position: string};
  67. * ```
  68. *
  69. * You should use the following instead:
  70. *
  71. * ```
  72. * type Props = {position: TypeForStyleKey<'position'>};
  73. * ```
  74. *
  75. * This will correctly give you the type 'absolute' | 'relative'
  76. */
  77. export type TypeForStyleKey<
  78. +key: $Keys<____DangerouslyImpreciseStyle_Internal>,
  79. > = $ElementType<____DangerouslyImpreciseStyle_Internal, key>;
  80. /**
  81. * This type is an object of the different possible style
  82. * properties that can be specified for View.
  83. *
  84. * Note that this isn't a safe way to type a style prop for a component as
  85. * results from StyleSheet.create return an internal identifier, not
  86. * an object of styles.
  87. *
  88. * If you want to type the style prop of a function,
  89. * consider using ViewStyleProp.
  90. *
  91. * A reasonable usage of this type is for helper functions that return an
  92. * object of styles to pass to a View that can't be precomputed with
  93. * StyleSheet.create.
  94. */
  95. export type ViewStyle = ____ViewStyle_Internal;
  96. /**
  97. * This type is an object of the different possible style
  98. * properties that can be specified for Text.
  99. *
  100. * Note that this isn't a safe way to type a style prop for a component as
  101. * results from StyleSheet.create return an internal identifier, not
  102. * an object of styles.
  103. *
  104. * If you want to type the style prop of a function,
  105. * consider using TextStyleProp.
  106. *
  107. * A reasonable usage of this type is for helper functions that return an
  108. * object of styles to pass to a Text that can't be precomputed with
  109. * StyleSheet.create.
  110. */
  111. export type TextStyle = ____TextStyle_Internal;
  112. /**
  113. * This type is an object of the different possible style
  114. * properties that can be specified for Image.
  115. *
  116. * Note that this isn't a safe way to type a style prop for a component as
  117. * results from StyleSheet.create return an internal identifier, not
  118. * an object of styles.
  119. *
  120. * If you want to type the style prop of a function,
  121. * consider using ImageStyleProp.
  122. *
  123. * A reasonable usage of this type is for helper functions that return an
  124. * object of styles to pass to an Image that can't be precomputed with
  125. * StyleSheet.create.
  126. */
  127. export type ImageStyle = ____ImageStyle_Internal;
  128. /**
  129. * WARNING: You probably shouldn't be using this type. This type is an object
  130. * with all possible style keys and their values. Note that this isn't
  131. * a safe way to type a style prop for a component as results from
  132. * StyleSheet.create return an internal identifier, not an object of styles.
  133. *
  134. * If you want to type the style prop of a function, consider using
  135. * ViewStyleProp, TextStyleProp, or ImageStyleProp.
  136. *
  137. * This should only be used by very core utilities that operate on an object
  138. * containing any possible style value.
  139. */
  140. export type DangerouslyImpreciseStyle = ____DangerouslyImpreciseStyle_Internal;
  141. let hairlineWidth: number = PixelRatio.roundToNearestPixel(0.4);
  142. if (hairlineWidth === 0) {
  143. hairlineWidth = 1 / PixelRatio.get();
  144. }
  145. const absoluteFill = {
  146. position: 'absolute',
  147. left: 0,
  148. right: 0,
  149. top: 0,
  150. bottom: 0,
  151. };
  152. if (__DEV__) {
  153. Object.freeze(absoluteFill);
  154. }
  155. /**
  156. * A StyleSheet is an abstraction similar to CSS StyleSheets
  157. *
  158. * Create a new StyleSheet:
  159. *
  160. * ```
  161. * const styles = StyleSheet.create({
  162. * container: {
  163. * borderRadius: 4,
  164. * borderWidth: 0.5,
  165. * borderColor: '#d6d7da',
  166. * },
  167. * title: {
  168. * fontSize: 19,
  169. * fontWeight: 'bold',
  170. * },
  171. * activeTitle: {
  172. * color: 'red',
  173. * },
  174. * });
  175. * ```
  176. *
  177. * Use a StyleSheet:
  178. *
  179. * ```
  180. * <View style={styles.container}>
  181. * <Text style={[styles.title, this.props.isActive && styles.activeTitle]} />
  182. * </View>
  183. * ```
  184. *
  185. * Code quality:
  186. *
  187. * - By moving styles away from the render function, you're making the code
  188. * easier to understand.
  189. * - Naming the styles is a good way to add meaning to the low level components
  190. * in the render function.
  191. *
  192. * Performance:
  193. *
  194. * - Making a stylesheet from a style object makes it possible to refer to it
  195. * by ID instead of creating a new style object every time.
  196. * - It also allows to send the style only once through the bridge. All
  197. * subsequent uses are going to refer an id (not implemented yet).
  198. */
  199. module.exports = {
  200. /**
  201. * This is defined as the width of a thin line on the platform. It can be
  202. * used as the thickness of a border or division between two elements.
  203. * Example:
  204. * ```
  205. * {
  206. * borderBottomColor: '#bbb',
  207. * borderBottomWidth: StyleSheet.hairlineWidth
  208. * }
  209. * ```
  210. *
  211. * This constant will always be a round number of pixels (so a line defined
  212. * by it look crisp) and will try to match the standard width of a thin line
  213. * on the underlying platform. However, you should not rely on it being a
  214. * constant size, because on different platforms and screen densities its
  215. * value may be calculated differently.
  216. *
  217. * A line with hairline width may not be visible if your simulator is downscaled.
  218. */
  219. hairlineWidth,
  220. /**
  221. * A very common pattern is to create overlays with position absolute and zero positioning,
  222. * so `absoluteFill` can be used for convenience and to reduce duplication of these repeated
  223. * styles.
  224. */
  225. absoluteFill: (absoluteFill: any), // TODO: This should be updated after we fix downstream Flow sites.
  226. /**
  227. * Sometimes you may want `absoluteFill` but with a couple tweaks - `absoluteFillObject` can be
  228. * used to create a customized entry in a `StyleSheet`, e.g.:
  229. *
  230. * const styles = StyleSheet.create({
  231. * wrapper: {
  232. * ...StyleSheet.absoluteFillObject,
  233. * top: 10,
  234. * backgroundColor: 'transparent',
  235. * },
  236. * });
  237. */
  238. absoluteFillObject: absoluteFill,
  239. /**
  240. * Combines two styles such that `style2` will override any styles in `style1`.
  241. * If either style is falsy, the other one is returned without allocating an
  242. * array, saving allocations and maintaining reference equality for
  243. * PureComponent checks.
  244. */
  245. compose<T: DangerouslyImpreciseStyleProp>(
  246. style1: ?T,
  247. style2: ?T,
  248. ): ?T | $ReadOnlyArray<T> {
  249. if (style1 != null && style2 != null) {
  250. return ([style1, style2]: $ReadOnlyArray<T>);
  251. } else {
  252. return style1 != null ? style1 : style2;
  253. }
  254. },
  255. /**
  256. * Flattens an array of style objects, into one aggregated style object.
  257. * Alternatively, this method can be used to lookup IDs, returned by
  258. * StyleSheet.register.
  259. *
  260. * > **NOTE**: Exercise caution as abusing this can tax you in terms of
  261. * > optimizations.
  262. * >
  263. * > IDs enable optimizations through the bridge and memory in general. Referring
  264. * > to style objects directly will deprive you of these optimizations.
  265. *
  266. * Example:
  267. * ```
  268. * const styles = StyleSheet.create({
  269. * listItem: {
  270. * flex: 1,
  271. * fontSize: 16,
  272. * color: 'white'
  273. * },
  274. * selectedListItem: {
  275. * color: 'green'
  276. * }
  277. * });
  278. *
  279. * StyleSheet.flatten([styles.listItem, styles.selectedListItem])
  280. * // returns { flex: 1, fontSize: 16, color: 'green' }
  281. * ```
  282. * Alternative use:
  283. * ```
  284. * StyleSheet.flatten(styles.listItem);
  285. * // return { flex: 1, fontSize: 16, color: 'white' }
  286. * // Simply styles.listItem would return its ID (number)
  287. * ```
  288. * This method internally uses `StyleSheetRegistry.getStyleByID(style)`
  289. * to resolve style objects represented by IDs. Thus, an array of style
  290. * objects (instances of StyleSheet.create), are individually resolved to,
  291. * their respective objects, merged as one and then returned. This also explains
  292. * the alternative use.
  293. */
  294. flatten,
  295. /**
  296. * WARNING: EXPERIMENTAL. Breaking changes will probably happen a lot and will
  297. * not be reliably announced. The whole thing might be deleted, who knows? Use
  298. * at your own risk.
  299. *
  300. * Sets a function to use to pre-process a style property value. This is used
  301. * internally to process color and transform values. You should not use this
  302. * unless you really know what you are doing and have exhausted other options.
  303. */
  304. setStyleAttributePreprocessor(
  305. property: string,
  306. process: (nextProp: mixed) => mixed,
  307. ) {
  308. let value;
  309. if (ReactNativeStyleAttributes[property] === true) {
  310. value = {};
  311. } else if (typeof ReactNativeStyleAttributes[property] === 'object') {
  312. value = ReactNativeStyleAttributes[property];
  313. } else {
  314. console.error(`${property} is not a valid style attribute`);
  315. return;
  316. }
  317. if (__DEV__ && typeof value.process === 'function') {
  318. console.warn(`Overwriting ${property} style attribute preprocessor`);
  319. }
  320. ReactNativeStyleAttributes[property] = {...value, process};
  321. },
  322. /**
  323. * Creates a StyleSheet style reference from the given object.
  324. */
  325. create<+S: ____Styles_Internal>(obj: S): $ReadOnly<S> {
  326. // TODO: This should return S as the return type. But first,
  327. // we need to codemod all the callsites that are typing this
  328. // return value as a number (even though it was opaque).
  329. if (__DEV__) {
  330. for (const key in obj) {
  331. StyleSheetValidation.validateStyle(key, obj);
  332. if (obj[key]) {
  333. Object.freeze(obj[key]);
  334. }
  335. }
  336. }
  337. return obj;
  338. },
  339. };