123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- /* @flow */
- import Platform from '../react/Platform';
-
- import { ColorPalette } from './components';
-
- declare type StyleSheet = Object;
- export type StyleType = StyleSheet | Array<StyleSheet>;
-
- /**
- * RegExp pattern for long HEX color format.
- */
- const HEX_LONG_COLOR_FORMAT
- = /^#([0-9A-F]{2,2})([0-9A-F]{2,2})([0-9A-F]{2,2})$/i;
-
- /**
- * RegExp pattern for short HEX color format.
- */
- const HEX_SHORT_COLOR_FORMAT
- = /^#([0-9A-F]{1,1})([0-9A-F]{1,1})([0-9A-F]{1,1})$/i;
-
- /**
- * RegExp pattern for RGB color format.
- */
- const RGB_COLOR_FORMAT = /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/i;
-
- /**
- * RegExp pattern for RGBA color format.
- */
- const RGBA_COLOR_FORMAT
- = /^rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*([0-9.]+)\)$/i;
-
- /**
- * The list of the well-known style properties which may not be numbers on Web
- * but must be numbers on React Native.
- *
- * @private
- */
- const _WELL_KNOWN_NUMBER_PROPERTIES = [ 'height', 'width' ];
-
- /**
- * Function to convert complex StyleType styles into a single flat object,
- * so then they can be deconstructed for further processing.
- *
- * @param {Styletype} st - The complex style type.
- * @returns {Object}
- */
- export function styleTypeToObject(st: StyleType): Object {
- if (!st) {
- return {};
- }
-
- if (Array.isArray(st)) {
- const flatStyle = {};
-
- for (const styleElement of st) {
- Object.assign(flatStyle, styleTypeToObject(styleElement));
- }
-
- return flatStyle;
- }
-
- return st;
- }
-
- /**
- * Combines the given 2 styles into a single one.
- *
- * @param {StyleType} a - An object or array of styles.
- * @param {StyleType} b - An object or array of styles.
- * @private
- * @returns {StyleType} - The merged styles.
- */
- export function combineStyles(a: StyleType, b: StyleType): StyleType {
- const result: Array<StyleSheet> = [];
-
- if (a) {
- if (Array.isArray(a)) {
- result.push(...a);
- } else {
- result.push(a);
- }
- }
-
- if (b) {
- if (Array.isArray(b)) {
- result.push(...b);
- } else {
- result.push(b);
- }
- }
-
- return result;
- }
-
- /**
- * Create a style sheet using the provided style definitions.
- *
- * @param {StyleSheet} styles - A dictionary of named style definitions.
- * @param {StyleSheet} [overrides={}] - Optional set of additional (often
- * platform-dependent/specific) style definitions that will override the base
- * (often platform-independent) styles.
- * @returns {StyleSheet}
- */
- export function createStyleSheet(
- styles: StyleSheet, overrides: StyleSheet = {}): StyleSheet {
- const combinedStyles = {};
-
- for (const k of Object.keys(styles)) {
- combinedStyles[k]
- = _shimStyles({
- ...styles[k],
- ...overrides[k]
- });
- }
-
- return combinedStyles;
- }
-
- /**
- * Works around a bug in react-native or react-native-webrtc on Android which
- * causes Views overlaying RTCView to be clipped. Even though we (may) display
- * multiple RTCViews, it is enough to apply the fix only to a View with a
- * bounding rectangle containing all RTCviews and their overlaying Views.
- *
- * @param {StyleSheet} styles - An object which represents a stylesheet.
- * @public
- * @returns {StyleSheet}
- */
- export function fixAndroidViewClipping<T: StyleSheet>(styles: T): T {
- if (Platform.OS === 'android') {
- styles.borderColor = ColorPalette.appBackground;
- styles.borderWidth = 1;
- }
-
- return styles;
- }
-
- /**
- * Returns an rgba format of the provided color if it's in hex or rgb format.
- *
- * NOTE: The function will return the same color if it's not in one of those
- * two formats (e.g. 'white').
- *
- * @param {string} color - The string representation of the color in rgb or hex
- * format.
- * @param {number} alpha - The alpha value to apply.
- * @returns {string}
- */
- export function getRGBAFormat(color: string, alpha: number): string {
- let match = color.match(HEX_LONG_COLOR_FORMAT);
-
- if (match) {
- return `#${match[1]}${match[2]}${match[3]}${_getAlphaInHex(alpha)}`;
- }
-
- match = color.match(HEX_SHORT_COLOR_FORMAT);
- if (match) {
- return `#${match[1]}${match[1]}${match[2]}${match[2]}${match[3]}${
- match[3]}${_getAlphaInHex(alpha)}`;
- }
-
- match = color.match(RGB_COLOR_FORMAT);
- if (match) {
- return `rgba(${match[1]}, ${match[2]}, ${match[3]}, ${alpha})`;
- }
-
- return color;
- }
-
- /**
- * Decides if a color is light or dark based on the ITU-R BT.709 and W3C
- * recommendations.
- *
- * NOTE: Please see https://www.w3.org/TR/WCAG20/#relativeluminancedef.
- *
- * @param {string} color - The color in rgb, rgba or hex format.
- * @returns {boolean}
- */
- export function isDarkColor(color: string): boolean {
- const rgb = _getRGBObjectFormat(color);
-
- return ((_getColorLuminance(rgb.r) * 0.2126)
- + (_getColorLuminance(rgb.g) * 0.7152)
- + (_getColorLuminance(rgb.b) * 0.0722)) <= 0.179;
- }
-
- /**
- * Converts an [0..1] alpha value into HEX.
- *
- * @param {number} alpha - The alpha value to convert.
- * @returns {string}
- */
- function _getAlphaInHex(alpha: number): string {
- return Number(Math.round(255 * alpha)).toString(16)
- .padStart(2, '0');
- }
-
- /**
- * Calculated the color luminance component for an individual color channel.
- *
- * NOTE: Please see https://www.w3.org/TR/WCAG20/#relativeluminancedef.
- *
- * @param {number} c - The color which we need the individual luminance
- * for.
- * @returns {number}
- */
- function _getColorLuminance(c: number): number {
- return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
- }
-
- /**
- * Parses a color string into an object containing the RGB values as numbers.
- *
- * NOTE: Object properties are not alpha-sorted for sanity.
- *
- * @param {string} color - The color to convert.
- * @returns {{
- * r: number,
- * g: number,
- * b: number
- * }}
- */
- function _getRGBObjectFormat(color: string): {r: number, g: number, b: number} {
- let match = color.match(HEX_LONG_COLOR_FORMAT);
-
- if (match) {
- return {
- r: parseInt(match[1], 16) / 255.0,
- g: parseInt(match[2], 16) / 255.0,
- b: parseInt(match[3], 16) / 255.0
- };
- }
-
- match = color.match(HEX_SHORT_COLOR_FORMAT);
- if (match) {
- return {
- r: parseInt(`${match[1]}${match[1]}`, 16) / 255.0,
- g: parseInt(`${match[2]}${match[2]}`, 16) / 255.0,
- b: parseInt(`${match[3]}${match[3]}`, 16) / 255.0
- };
- }
-
- match = color.match(RGB_COLOR_FORMAT) || color.match(RGBA_COLOR_FORMAT);
- if (match) {
- return {
- r: parseInt(match[1], 10) / 255.0,
- g: parseInt(match[2], 10) / 255.0,
- b: parseInt(match[3], 10) / 255.0
- };
- }
-
- return {
- r: 0,
- g: 0,
- b: 0
- };
- }
-
- /**
- * Shims style properties to work correctly on native. Allows us to minimize the
- * number of style declarations that need to be set or overridden for specific
- * platforms.
- *
- * @param {StyleSheet} styles - An object which represents a stylesheet.
- * @private
- * @returns {StyleSheet}
- */
- function _shimStyles<T: StyleSheet>(styles: T): T {
- // Certain style properties may not be numbers on Web but must be numbers on
- // React Native. For example, height and width may be expressed in percent
- // on Web but React Native will not understand them and we will get errors
- // (at least during development). Convert such well-known properties to
- // numbers if possible; otherwise, remove them to avoid runtime errors.
- for (const k of _WELL_KNOWN_NUMBER_PROPERTIES) {
- const v = styles[k];
- const typeofV = typeof v;
-
- if (typeofV !== 'undefined' && typeofV !== 'number') {
- const numberV = Number(v);
-
- if (Number.isNaN(numberV)) {
- delete styles[k];
- } else {
- styles[k] = numberV;
- }
- }
- }
-
- return styles;
- }
|