Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

functions.any.ts 8.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. import Platform from '../react/Platform';
  2. import { ColorPalette } from './components/styles/ColorPalette';
  3. declare type StyleSheet = {
  4. [key: string]: string;
  5. };
  6. export type StyleType = StyleSheet | Array<StyleSheet>;
  7. /**
  8. * RegExp pattern for long HEX color format.
  9. */
  10. const HEX_LONG_COLOR_FORMAT
  11. = /^#([0-9A-F]{2,2})([0-9A-F]{2,2})([0-9A-F]{2,2})$/i;
  12. /**
  13. * RegExp pattern for short HEX color format.
  14. */
  15. const HEX_SHORT_COLOR_FORMAT
  16. = /^#([0-9A-F]{1,1})([0-9A-F]{1,1})([0-9A-F]{1,1})$/i;
  17. /**
  18. * RegExp pattern for RGB color format.
  19. */
  20. const RGB_COLOR_FORMAT = /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/i;
  21. /**
  22. * RegExp pattern for RGBA color format.
  23. */
  24. const RGBA_COLOR_FORMAT
  25. = /^rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*([0-9.]+)\)$/i;
  26. /**
  27. * The list of the well-known style properties which may not be numbers on Web
  28. * but must be numbers on React Native.
  29. *
  30. * @private
  31. */
  32. const _WELL_KNOWN_NUMBER_PROPERTIES = [ 'height', 'width' ];
  33. /**
  34. * Function to convert complex StyleType styles into a single flat object,
  35. * so then they can be deconstructed for further processing.
  36. *
  37. * @param {Styletype} st - The complex style type.
  38. * @returns {Object}
  39. */
  40. export function styleTypeToObject(st: StyleType): { [key: string]: string | number; } {
  41. if (!st) {
  42. return {};
  43. }
  44. if (Array.isArray(st)) {
  45. const flatStyle = {};
  46. for (const styleElement of st) {
  47. Object.assign(flatStyle, styleTypeToObject(styleElement));
  48. }
  49. return flatStyle;
  50. }
  51. return st;
  52. }
  53. /**
  54. * Combines the given 2 styles into a single one.
  55. *
  56. * @param {StyleType} a - An object or array of styles.
  57. * @param {StyleType} b - An object or array of styles.
  58. * @private
  59. * @returns {StyleType} - The merged styles.
  60. */
  61. export function combineStyles(a: StyleType, b: StyleType): StyleType {
  62. const result: Array<StyleSheet> = [];
  63. if (a) {
  64. if (Array.isArray(a)) {
  65. result.push(...a);
  66. } else {
  67. result.push(a);
  68. }
  69. }
  70. if (b) {
  71. if (Array.isArray(b)) {
  72. result.push(...b);
  73. } else {
  74. result.push(b);
  75. }
  76. }
  77. return result;
  78. }
  79. /**
  80. * Create a style sheet using the provided style definitions.
  81. *
  82. * @param {StyleSheet} styles - A dictionary of named style definitions.
  83. * @param {StyleSheet} [overrides={}] - Optional set of additional (often
  84. * platform-dependent/specific) style definitions that will override the base
  85. * (often platform-independent) styles.
  86. * @returns {StyleSheet}
  87. */
  88. export function createStyleSheet(
  89. styles: StyleSheet, overrides: StyleSheet = {}): StyleSheet {
  90. const combinedStyles: any = {};
  91. for (const k of Object.keys(styles)) {
  92. combinedStyles[k]
  93. = _shimStyles({
  94. // @ts-ignore
  95. ...styles[k],
  96. // @ts-ignore
  97. ...overrides[k]
  98. });
  99. }
  100. return combinedStyles;
  101. }
  102. /**
  103. * Works around a bug in react-native or react-native-webrtc on Android which
  104. * causes Views overlaying RTCView to be clipped. Even though we (may) display
  105. * multiple RTCViews, it is enough to apply the fix only to a View with a
  106. * bounding rectangle containing all RTCviews and their overlaying Views.
  107. *
  108. * @param {StyleSheet} styles - An object which represents a stylesheet.
  109. * @public
  110. * @returns {StyleSheet}
  111. */
  112. export function fixAndroidViewClipping<T extends StyleSheet>(styles: T): T {
  113. if (Platform.OS === 'android') {
  114. // @ts-ignore
  115. styles.borderColor = ColorPalette.appBackground;
  116. // @ts-ignore
  117. styles.borderWidth = 1;
  118. }
  119. return styles;
  120. }
  121. /**
  122. * Returns an rgba format of the provided color if it's in hex or rgb format.
  123. *
  124. * NOTE: The function will return the same color if it's not in one of those
  125. * two formats (e.g. 'white').
  126. *
  127. * @param {string} color - The string representation of the color in rgb or hex
  128. * format.
  129. * @param {number} alpha - The alpha value to apply.
  130. * @returns {string}
  131. */
  132. export function getRGBAFormat(color: string, alpha: number): string {
  133. let match = color.match(HEX_LONG_COLOR_FORMAT);
  134. if (match) {
  135. return `#${match[1]}${match[2]}${match[3]}${_getAlphaInHex(alpha)}`;
  136. }
  137. match = color.match(HEX_SHORT_COLOR_FORMAT);
  138. if (match) {
  139. return `#${match[1]}${match[1]}${match[2]}${match[2]}${match[3]}${
  140. match[3]}${_getAlphaInHex(alpha)}`;
  141. }
  142. match = color.match(RGB_COLOR_FORMAT);
  143. if (match) {
  144. return `rgba(${match[1]}, ${match[2]}, ${match[3]}, ${alpha})`;
  145. }
  146. return color;
  147. }
  148. /**
  149. * Decides if a color is light or dark based on the ITU-R BT.709 and W3C
  150. * recommendations.
  151. *
  152. * NOTE: Please see https://www.w3.org/TR/WCAG20/#relativeluminancedef.
  153. *
  154. * @param {string} color - The color in rgb, rgba or hex format.
  155. * @returns {boolean}
  156. */
  157. export function isDarkColor(color: string): boolean {
  158. const rgb = _getRGBObjectFormat(color);
  159. return ((_getColorLuminance(rgb.r) * 0.2126)
  160. + (_getColorLuminance(rgb.g) * 0.7152)
  161. + (_getColorLuminance(rgb.b) * 0.0722)) <= 0.179;
  162. }
  163. /**
  164. * Converts an [0..1] alpha value into HEX.
  165. *
  166. * @param {number} alpha - The alpha value to convert.
  167. * @returns {string}
  168. */
  169. function _getAlphaInHex(alpha: number): string {
  170. return Number(Math.round(255 * alpha)).toString(16)
  171. .padStart(2, '0');
  172. }
  173. /**
  174. * Calculated the color luminance component for an individual color channel.
  175. *
  176. * NOTE: Please see https://www.w3.org/TR/WCAG20/#relativeluminancedef.
  177. *
  178. * @param {number} c - The color which we need the individual luminance
  179. * for.
  180. * @returns {number}
  181. */
  182. function _getColorLuminance(c: number): number {
  183. return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
  184. }
  185. /**
  186. * Parses a color string into an object containing the RGB values as numbers.
  187. *
  188. * NOTE: Object properties are not alpha-sorted for sanity.
  189. *
  190. * @param {string} color - The color to convert.
  191. * @returns {{
  192. * r: number,
  193. * g: number,
  194. * b: number
  195. * }}
  196. */
  197. function _getRGBObjectFormat(color: string): { b: number; g: number; r: number; } {
  198. let match = color.match(HEX_LONG_COLOR_FORMAT);
  199. if (match) {
  200. return {
  201. r: parseInt(match[1], 16) / 255.0,
  202. g: parseInt(match[2], 16) / 255.0,
  203. b: parseInt(match[3], 16) / 255.0
  204. };
  205. }
  206. match = color.match(HEX_SHORT_COLOR_FORMAT);
  207. if (match) {
  208. return {
  209. r: parseInt(`${match[1]}${match[1]}`, 16) / 255.0,
  210. g: parseInt(`${match[2]}${match[2]}`, 16) / 255.0,
  211. b: parseInt(`${match[3]}${match[3]}`, 16) / 255.0
  212. };
  213. }
  214. match = color.match(RGB_COLOR_FORMAT) || color.match(RGBA_COLOR_FORMAT);
  215. if (match) {
  216. return {
  217. r: parseInt(match[1], 10) / 255.0,
  218. g: parseInt(match[2], 10) / 255.0,
  219. b: parseInt(match[3], 10) / 255.0
  220. };
  221. }
  222. return {
  223. r: 0,
  224. g: 0,
  225. b: 0
  226. };
  227. }
  228. /**
  229. * Shims style properties to work correctly on native. Allows us to minimize the
  230. * number of style declarations that need to be set or overridden for specific
  231. * platforms.
  232. *
  233. * @param {StyleSheet} styles - An object which represents a stylesheet.
  234. * @private
  235. * @returns {StyleSheet}
  236. */
  237. function _shimStyles<T extends StyleSheet>(styles: T): T {
  238. // Certain style properties may not be numbers on Web but must be numbers on
  239. // React Native. For example, height and width may be expressed in percent
  240. // on Web but React Native will not understand them and we will get errors
  241. // (at least during development). Convert such well-known properties to
  242. // numbers if possible; otherwise, remove them to avoid runtime errors.
  243. for (const k of _WELL_KNOWN_NUMBER_PROPERTIES) {
  244. const v = styles[k];
  245. const typeofV = typeof v;
  246. if (typeofV !== 'undefined' && typeofV !== 'number') {
  247. const numberV = Number(v);
  248. if (Number.isNaN(numberV)) {
  249. delete styles[k];
  250. } else {
  251. // @ts-ignore
  252. styles[k] = numberV;
  253. }
  254. }
  255. }
  256. return styles;
  257. }