diff --git a/change/react-native-windows-2019-12-16-18-55-30-windowsBrush.json b/change/react-native-windows-2019-12-16-18-55-30-windowsBrush.json new file mode 100644 index 00000000000..3e309afb530 --- /dev/null +++ b/change/react-native-windows-2019-12-16-18-55-30-windowsBrush.json @@ -0,0 +1,8 @@ +{ + "type": "prerelease", + "comment": "port windowsbrush changes from Microsoft fork of react-native", + "packageName": "react-native-windows", + "email": "kmelmon@microsoft.com", + "commit": "a195ce0d079f30dcfd3b376e89240916891204bd", + "date": "2019-12-17T02:55:30.505Z" +} \ No newline at end of file diff --git a/vnext/src/Libraries/Color/normalizeColor.windows.js b/vnext/src/Libraries/Color/normalizeColor.windows.js new file mode 100644 index 00000000000..8aecb3defc2 --- /dev/null +++ b/vnext/src/Libraries/Color/normalizeColor.windows.js @@ -0,0 +1,393 @@ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + * + * @format + * @flow + */ + +/* eslint no-bitwise: 0 */ +'use strict'; + +import type {NativeOrDynamicColorType} from './NativeOrDynamicColorType'; // TODO(macOS ISS#2323203) + +function normalizeColor( + color: ?( + | string + | number + | NativeOrDynamicColorType + ) /* TODO(macOS ISS#2323203) */, +): ?(number | NativeOrDynamicColorType) /* TODO(macOS ISS#2323203) */ { + const matchers = getMatchers(); + let match; + + if (typeof color === 'number') { + if (color >>> 0 === color && color >= 0 && color <= 0xffffffff) { + return color; + } + return null; + } + + // [TODO(macOS ISS#2323203) + if (typeof color === 'object' && color !== null) { + const normalizeColorObject = require('normalizeColorObject'); // TODO(macOS ISS#2323203) + + const normalizedColorObj = normalizeColorObject(color); + + if (normalizedColorObj !== null) { + return color; + } + } + + if (typeof color !== 'string') { + return null; + } // ]TODO(macOS ISS#2323203) + + // Ordered based on occurrences on Facebook codebase + if ((match = matchers.hex6.exec(color))) { + return parseInt(match[1] + 'ff', 16) >>> 0; + } + + if (names.hasOwnProperty(color)) { + return names[color]; + } + + if ((match = matchers.rgb.exec(color))) { + return ( + // b + ((parse255(match[1]) << 24) | // r + (parse255(match[2]) << 16) | // g + (parse255(match[3]) << 8) | + 0x000000ff) >>> // a + 0 + ); + } + + if ((match = matchers.rgba.exec(color))) { + return ( + // b + ((parse255(match[1]) << 24) | // r + (parse255(match[2]) << 16) | // g + (parse255(match[3]) << 8) | + parse1(match[4])) >>> // a + 0 + ); + } + + if ((match = matchers.hex3.exec(color))) { + return ( + parseInt( + match[1] + + match[1] + // r + match[2] + + match[2] + // g + match[3] + + match[3] + // b + 'ff', // a + 16, + ) >>> 0 + ); + } + + // https://drafts.csswg.org/css-color-4/#hex-notation + if ((match = matchers.hex8.exec(color))) { + return parseInt(match[1], 16) >>> 0; + } + + if ((match = matchers.hex4.exec(color))) { + return ( + parseInt( + match[1] + + match[1] + // r + match[2] + + match[2] + // g + match[3] + + match[3] + // b + match[4] + + match[4], // a + 16, + ) >>> 0 + ); + } + + if ((match = matchers.hsl.exec(color))) { + return ( + (hslToRgb( + parse360(match[1]), // h + parsePercentage(match[2]), // s + parsePercentage(match[3]), // l + ) | + 0x000000ff) >>> // a + 0 + ); + } + + if ((match = matchers.hsla.exec(color))) { + return ( + (hslToRgb( + parse360(match[1]), // h + parsePercentage(match[2]), // s + parsePercentage(match[3]), // l + ) | + parse1(match[4])) >>> // a + 0 + ); + } + + return null; +} + +function hue2rgb(p: number, q: number, t: number): number { + if (t < 0) { + t += 1; + } + if (t > 1) { + t -= 1; + } + if (t < 1 / 6) { + return p + (q - p) * 6 * t; + } + if (t < 1 / 2) { + return q; + } + if (t < 2 / 3) { + return p + (q - p) * (2 / 3 - t) * 6; + } + return p; +} + +function hslToRgb(h: number, s: number, l: number): number { + const q = l < 0.5 ? l * (1 + s) : l + s - l * s; + const p = 2 * l - q; + const r = hue2rgb(p, q, h + 1 / 3); + const g = hue2rgb(p, q, h); + const b = hue2rgb(p, q, h - 1 / 3); + + return ( + (Math.round(r * 255) << 24) | + (Math.round(g * 255) << 16) | + (Math.round(b * 255) << 8) + ); +} + +// var INTEGER = '[-+]?\\d+'; +const NUMBER = '[-+]?\\d*\\.?\\d+'; +const PERCENTAGE = NUMBER + '%'; + +function call(...args) { + return '\\(\\s*(' + args.join(')\\s*,\\s*(') + ')\\s*\\)'; +} + +let cachedMatchers; + +function getMatchers() { + if (cachedMatchers === undefined) { + cachedMatchers = { + rgb: new RegExp('rgb' + call(NUMBER, NUMBER, NUMBER)), + rgba: new RegExp('rgba' + call(NUMBER, NUMBER, NUMBER, NUMBER)), + hsl: new RegExp('hsl' + call(NUMBER, PERCENTAGE, PERCENTAGE)), + hsla: new RegExp('hsla' + call(NUMBER, PERCENTAGE, PERCENTAGE, NUMBER)), + hex3: /^#([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, + hex4: /^#([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, + hex6: /^#([0-9a-fA-F]{6})$/, + hex8: /^#([0-9a-fA-F]{8})$/, + }; + } + return cachedMatchers; +} + +function parse255(str: string): number { + const int = parseInt(str, 10); + if (int < 0) { + return 0; + } + if (int > 255) { + return 255; + } + return int; +} + +function parse360(str: string): number { + const int = parseFloat(str); + return (((int % 360) + 360) % 360) / 360; +} + +function parse1(str: string): number { + const num = parseFloat(str); + if (num < 0) { + return 0; + } + if (num > 1) { + return 255; + } + return Math.round(num * 255); +} + +function parsePercentage(str: string): number { + // parseFloat conveniently ignores the final % + const int = parseFloat(str); + if (int < 0) { + return 0; + } + if (int > 100) { + return 1; + } + return int / 100; +} + +const names = { + transparent: 0x00000000, + + // http://www.w3.org/TR/css3-color/#svg-color + aliceblue: 0xf0f8ffff, + antiquewhite: 0xfaebd7ff, + aqua: 0x00ffffff, + aquamarine: 0x7fffd4ff, + azure: 0xf0ffffff, + beige: 0xf5f5dcff, + bisque: 0xffe4c4ff, + black: 0x000000ff, + blanchedalmond: 0xffebcdff, + blue: 0x0000ffff, + blueviolet: 0x8a2be2ff, + brown: 0xa52a2aff, + burlywood: 0xdeb887ff, + burntsienna: 0xea7e5dff, + cadetblue: 0x5f9ea0ff, + chartreuse: 0x7fff00ff, + chocolate: 0xd2691eff, + coral: 0xff7f50ff, + cornflowerblue: 0x6495edff, + cornsilk: 0xfff8dcff, + crimson: 0xdc143cff, + cyan: 0x00ffffff, + darkblue: 0x00008bff, + darkcyan: 0x008b8bff, + darkgoldenrod: 0xb8860bff, + darkgray: 0xa9a9a9ff, + darkgreen: 0x006400ff, + darkgrey: 0xa9a9a9ff, + darkkhaki: 0xbdb76bff, + darkmagenta: 0x8b008bff, + darkolivegreen: 0x556b2fff, + darkorange: 0xff8c00ff, + darkorchid: 0x9932ccff, + darkred: 0x8b0000ff, + darksalmon: 0xe9967aff, + darkseagreen: 0x8fbc8fff, + darkslateblue: 0x483d8bff, + darkslategray: 0x2f4f4fff, + darkslategrey: 0x2f4f4fff, + darkturquoise: 0x00ced1ff, + darkviolet: 0x9400d3ff, + deeppink: 0xff1493ff, + deepskyblue: 0x00bfffff, + dimgray: 0x696969ff, + dimgrey: 0x696969ff, + dodgerblue: 0x1e90ffff, + firebrick: 0xb22222ff, + floralwhite: 0xfffaf0ff, + forestgreen: 0x228b22ff, + fuchsia: 0xff00ffff, + gainsboro: 0xdcdcdcff, + ghostwhite: 0xf8f8ffff, + gold: 0xffd700ff, + goldenrod: 0xdaa520ff, + gray: 0x808080ff, + green: 0x008000ff, + greenyellow: 0xadff2fff, + grey: 0x808080ff, + honeydew: 0xf0fff0ff, + hotpink: 0xff69b4ff, + indianred: 0xcd5c5cff, + indigo: 0x4b0082ff, + ivory: 0xfffff0ff, + khaki: 0xf0e68cff, + lavender: 0xe6e6faff, + lavenderblush: 0xfff0f5ff, + lawngreen: 0x7cfc00ff, + lemonchiffon: 0xfffacdff, + lightblue: 0xadd8e6ff, + lightcoral: 0xf08080ff, + lightcyan: 0xe0ffffff, + lightgoldenrodyellow: 0xfafad2ff, + lightgray: 0xd3d3d3ff, + lightgreen: 0x90ee90ff, + lightgrey: 0xd3d3d3ff, + lightpink: 0xffb6c1ff, + lightsalmon: 0xffa07aff, + lightseagreen: 0x20b2aaff, + lightskyblue: 0x87cefaff, + lightslategray: 0x778899ff, + lightslategrey: 0x778899ff, + lightsteelblue: 0xb0c4deff, + lightyellow: 0xffffe0ff, + lime: 0x00ff00ff, + limegreen: 0x32cd32ff, + linen: 0xfaf0e6ff, + magenta: 0xff00ffff, + maroon: 0x800000ff, + mediumaquamarine: 0x66cdaaff, + mediumblue: 0x0000cdff, + mediumorchid: 0xba55d3ff, + mediumpurple: 0x9370dbff, + mediumseagreen: 0x3cb371ff, + mediumslateblue: 0x7b68eeff, + mediumspringgreen: 0x00fa9aff, + mediumturquoise: 0x48d1ccff, + mediumvioletred: 0xc71585ff, + midnightblue: 0x191970ff, + mintcream: 0xf5fffaff, + mistyrose: 0xffe4e1ff, + moccasin: 0xffe4b5ff, + navajowhite: 0xffdeadff, + navy: 0x000080ff, + oldlace: 0xfdf5e6ff, + olive: 0x808000ff, + olivedrab: 0x6b8e23ff, + orange: 0xffa500ff, + orangered: 0xff4500ff, + orchid: 0xda70d6ff, + palegoldenrod: 0xeee8aaff, + palegreen: 0x98fb98ff, + paleturquoise: 0xafeeeeff, + palevioletred: 0xdb7093ff, + papayawhip: 0xffefd5ff, + peachpuff: 0xffdab9ff, + peru: 0xcd853fff, + pink: 0xffc0cbff, + plum: 0xdda0ddff, + powderblue: 0xb0e0e6ff, + purple: 0x800080ff, + rebeccapurple: 0x663399ff, + red: 0xff0000ff, + rosybrown: 0xbc8f8fff, + royalblue: 0x4169e1ff, + saddlebrown: 0x8b4513ff, + salmon: 0xfa8072ff, + sandybrown: 0xf4a460ff, + seagreen: 0x2e8b57ff, + seashell: 0xfff5eeff, + sienna: 0xa0522dff, + silver: 0xc0c0c0ff, + skyblue: 0x87ceebff, + slateblue: 0x6a5acdff, + slategray: 0x708090ff, + slategrey: 0x708090ff, + snow: 0xfffafaff, + springgreen: 0x00ff7fff, + steelblue: 0x4682b4ff, + tan: 0xd2b48cff, + teal: 0x008080ff, + thistle: 0xd8bfd8ff, + tomato: 0xff6347ff, + turquoise: 0x40e0d0ff, + violet: 0xee82eeff, + wheat: 0xf5deb3ff, + white: 0xffffffff, + whitesmoke: 0xf5f5f5ff, + yellow: 0xffff00ff, + yellowgreen: 0x9acd32ff, +}; + +module.exports = normalizeColor; diff --git a/vnext/src/Libraries/Components/ActivityIndicator/ActivityIndicator.windows.js b/vnext/src/Libraries/Components/ActivityIndicator/ActivityIndicator.windows.js new file mode 100644 index 00000000000..0cebe9f7bf6 --- /dev/null +++ b/vnext/src/Libraries/Components/ActivityIndicator/ActivityIndicator.windows.js @@ -0,0 +1,144 @@ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + * + * @format + * @flow + */ + +'use strict'; + +const Platform = require('../../Utilities/Platform'); +const React = require('react'); +const StyleSheet = require('../../StyleSheet/StyleSheet'); +const View = require('../View/View'); + +const RCTActivityIndicatorViewNativeComponent = require('./RCTActivityIndicatorViewNativeComponent'); + +import type {NativeComponent} from '../../Renderer/shims/ReactNative'; +import type {ViewProps} from '../View/ViewPropTypes'; +import type {NativeOrDynamicColorType} from '../../Color/NativeOrDynamicColorType'; // ]TODO(macOS ISS#2323203) + +const RCTActivityIndicator = + Platform.OS === 'android' + ? require('../ProgressBarAndroid/ProgressBarAndroid') + : RCTActivityIndicatorViewNativeComponent; + +const GRAY = '#999999'; + +type IndicatorSize = number | 'small' | 'large'; + +type IOSProps = $ReadOnly<{| + /** + * Whether the indicator should hide when not animating (true by default). + * + * See http://facebook.github.io/react-native/docs/activityindicator.html#hideswhenstopped + */ + hidesWhenStopped?: ?boolean, +|}>; +type Props = $ReadOnly<{| + ...ViewProps, + ...IOSProps, + + /** + * Whether to show the indicator (true, the default) or hide it (false). + * + * See http://facebook.github.io/react-native/docs/activityindicator.html#animating + */ + animating?: ?boolean, + + /** + * The foreground color of the spinner (default is gray). + * + * See http://facebook.github.io/react-native/docs/activityindicator.html#color + */ + color?: ?(string | NativeOrDynamicColorType), // TODO(macOS ISS#2323203) + + /** + * Size of the indicator (default is 'small'). + * Passing a number to the size prop is only supported on Android. + * + * See http://facebook.github.io/react-native/docs/activityindicator.html#size + */ + size?: ?IndicatorSize, +|}>; + +/** + * Displays a circular loading indicator. + * + * See http://facebook.github.io/react-native/docs/activityindicator.html + */ +const ActivityIndicator = (props: Props, forwardedRef?: any) => { + const {onLayout, style, size, ...restProps} = props; + let sizeStyle; + let sizeProp; + + switch (size) { + case 'small': + sizeStyle = styles.sizeSmall; + sizeProp = 'small'; + break; + case 'large': + sizeStyle = styles.sizeLarge; + sizeProp = 'large'; + break; + default: + sizeStyle = {height: props.size, width: props.size}; + break; + } + + const nativeProps = { + ...restProps, + ref: forwardedRef, + style: sizeStyle, + size: sizeProp, + styleAttr: 'Normal', + indeterminate: true, + }; + + return ( + + {/* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was + * found when making Flow check .android.js files. */} + + + ); +}; + +const ActivityIndicatorWithRef = React.forwardRef(ActivityIndicator); +ActivityIndicatorWithRef.displayName = 'ActivityIndicator'; + +/* $FlowFixMe(>=0.89.0 site=react_native_fb) This comment suppresses an error + * found when Flow v0.89 was deployed. To see the error, delete this comment + * and run Flow. */ +ActivityIndicatorWithRef.defaultProps = { + animating: true, + color: Platform.OS === 'ios' ? GRAY : null, + hidesWhenStopped: true, + size: 'small', +}; + +const styles = StyleSheet.create({ + container: { + alignItems: 'center', + justifyContent: 'center', + }, + sizeSmall: { + width: 20, + height: 20, + }, + sizeLarge: { + width: 36, + height: 36, + }, +}); + +/* $FlowFixMe(>=0.89.0 site=react_native_fb) This comment suppresses an error + * found when Flow v0.89 was deployed. To see the error, delete this comment + * and run Flow. */ +module.exports = (ActivityIndicatorWithRef: Class>); diff --git a/vnext/src/Libraries/Components/ActivityIndicator/RCTActivityIndicatorViewNativeComponent.windows.js b/vnext/src/Libraries/Components/ActivityIndicator/RCTActivityIndicatorViewNativeComponent.windows.js new file mode 100644 index 00000000000..6b8a581cab5 --- /dev/null +++ b/vnext/src/Libraries/Components/ActivityIndicator/RCTActivityIndicatorViewNativeComponent.windows.js @@ -0,0 +1,59 @@ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + * + * @format + * @flow + */ + +'use strict'; + +const requireNativeComponent = require('../../ReactNative/requireNativeComponent'); + +import type {ViewProps} from '../View/ViewPropTypes'; +import type {ViewStyleProp} from '../../StyleSheet/StyleSheet'; +import type {NativeComponent} from '../../Renderer/shims/ReactNative'; +import type {NativeOrDynamicColorType} from '../../Color/NativeOrDynamicColorType'; // ]TODO(macOS ISS#2323203) + +type NativeProps = $ReadOnly<{| + ...ViewProps, + + /** + * Whether the indicator should hide when not animating (true by default). + * + * See http://facebook.github.io/react-native/docs/activityindicator.html#hideswhenstopped + */ + hidesWhenStopped?: ?boolean, + + /** + * Whether to show the indicator (true, the default) or hide it (false). + * + * See http://facebook.github.io/react-native/docs/activityindicator.html#animating + */ + animating?: ?boolean, + + /** + * The foreground color of the spinner (default is gray). + * + * See http://facebook.github.io/react-native/docs/activityindicator.html#color + */ + color?: ?(string | NativeOrDynamicColorType), // TODO(macOS ISS#2323203) + + /** + * Size of the indicator (default is 'small'). + * Passing a number to the size prop is only supported on Android. + * + * See http://facebook.github.io/react-native/docs/activityindicator.html#size + */ + size?: ?('small' | 'large'), + + style?: ?ViewStyleProp, + styleAttr?: ?string, + indeterminate?: ?boolean, +|}>; + +type ActivityIndicatorNativeType = Class>; + +module.exports = ((requireNativeComponent( + 'RCTActivityIndicatorView', +): any): ActivityIndicatorNativeType); diff --git a/vnext/src/Libraries/Components/Picker/RCTPickerNativeComponent.windows.js b/vnext/src/Libraries/Components/Picker/RCTPickerNativeComponent.windows.js new file mode 100644 index 00000000000..9eb84db6493 --- /dev/null +++ b/vnext/src/Libraries/Components/Picker/RCTPickerNativeComponent.windows.js @@ -0,0 +1,46 @@ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + * + * @flow + * @format + */ +'use strict'; + +const requireNativeComponent = require('../../ReactNative/requireNativeComponent'); + +import type {SyntheticEvent} from '../../Types/CoreEventTypes'; +import type {TextStyleProp} from '../../StyleSheet/StyleSheet'; +import type {NativeComponent} from '../../Renderer/shims/ReactNative'; +import type {NativeOrDynamicColorType} from '../../Color/NativeOrDynamicColorType'; // ]TODO(macOS ISS#2323203) + +type PickerIOSChangeEvent = SyntheticEvent< + $ReadOnly<{| + newValue: number | string, + newIndex: number, + |}>, +>; + +type RCTPickerIOSItemType = $ReadOnly<{| + label: ?Label, + value: ?(number | string), + textColor: ?(number | NativeOrDynamicColorType), // TODO(macOS ISS#2323203) +|}>; + +type Label = Stringish | number; + +type RCTPickerIOSType = Class< + NativeComponent< + $ReadOnly<{| + items: $ReadOnlyArray, + onChange: (event: PickerIOSChangeEvent) => void, + onResponderTerminationRequest: () => boolean, + onStartShouldSetResponder: () => boolean, + selectedIndex: number, + style?: ?TextStyleProp, + testID?: ?string, + |}>, + >, +>; + +module.exports = ((requireNativeComponent('RCTPicker'): any): RCTPickerIOSType); diff --git a/vnext/src/Libraries/Components/Switch/SwitchNativeComponent.windows.js b/vnext/src/Libraries/Components/Switch/SwitchNativeComponent.windows.js new file mode 100644 index 00000000000..4a8fad3bd44 --- /dev/null +++ b/vnext/src/Libraries/Components/Switch/SwitchNativeComponent.windows.js @@ -0,0 +1,64 @@ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + * + * @flow + * @format + */ + +'use strict'; + +const Platform = require('../../Utilities/Platform'); +const ReactNative = require('../../Renderer/shims/ReactNative'); + +const requireNativeComponent = require('../../ReactNative/requireNativeComponent'); + +import type {SwitchChangeEvent} from '../../Types/CoreEventTypes'; +import type {ColorValue} from '../../StyleSheet/StyleSheetTypes'; +import type {ViewProps} from '../View/ViewPropTypes'; +import type {NativeOrDynamicColorType} from '../../Color/NativeOrDynamicColorType'; // TODO(macOS ISS#2323203) + +type SwitchProps = $ReadOnly<{| + ...ViewProps, + disabled?: ?boolean, + onChange?: ?(event: SwitchChangeEvent) => mixed, + thumbColor?: ?(string | NativeOrDynamicColorType), // TODO(macOS ISS#2323203) + trackColorForFalse?: ?(string | NativeOrDynamicColorType), // TODO(macOS ISS#2323203) + trackColorForTrue?: ?(string | NativeOrDynamicColorType), // TODO(macOS ISS#2323203) + value?: ?boolean, +|}>; + +// @see ReactSwitchManager.java +export type NativeAndroidProps = $ReadOnly<{| + ...SwitchProps, + + enabled?: ?boolean, + on?: ?boolean, + thumbTintColor?: ?(string | NativeOrDynamicColorType), // TODO(macOS ISS#2323203) + trackTintColor?: ?(string | NativeOrDynamicColorType), // TODO(macOS ISS#2323203) +|}>; + +// @see RCTSwitchManager.m +export type NativeIOSProps = $ReadOnly<{| + ...SwitchProps, + + onTintColor?: ?(string | NativeOrDynamicColorType), // TODO(macOS ISS#2323203) + thumbTintColor?: ?(string | NativeOrDynamicColorType), // TODO(macOS ISS#2323203) + tintColor?: ?(string | NativeOrDynamicColorType), // TODO(macOS ISS#2323203) +|}>; + +type SwitchNativeComponentType = Class< + ReactNative.NativeComponent< + $ReadOnly<{| + ...NativeAndroidProps, + ...NativeIOSProps, + |}>, + >, +>; + +const SwitchNativeComponent: SwitchNativeComponentType = + Platform.OS === 'android' + ? (requireNativeComponent('AndroidSwitch'): any) + : (requireNativeComponent('RCTSwitch'): any); + +module.exports = SwitchNativeComponent; diff --git a/vnext/src/Libraries/ReactNative/getNativeComponentAttributes.windows.js b/vnext/src/Libraries/ReactNative/getNativeComponentAttributes.windows.js new file mode 100644 index 00000000000..0ef09a566e8 --- /dev/null +++ b/vnext/src/Libraries/ReactNative/getNativeComponentAttributes.windows.js @@ -0,0 +1,190 @@ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + * + * @flow + * @format + */ + +'use strict'; + +const ReactNativeStyleAttributes = require('../Components/View/ReactNativeStyleAttributes'); +const UIManager = require('./UIManager'); + +const insetsDiffer = require('../Utilities/differ/insetsDiffer'); +const matricesDiffer = require('../Utilities/differ/matricesDiffer'); +const pointsDiffer = require('../Utilities/differ/pointsDiffer'); +const processColor = require('../StyleSheet/processColor'); +const resolveAssetSource = require('../Image/resolveAssetSource'); +const sizesDiffer = require('../Utilities/differ/sizesDiffer'); +const invariant = require('invariant'); +const warning = require('fbjs/lib/warning'); +import type {NativeOrDynamicColorType} from 'NativeOrDynamicColorType'; // TODO(macOS ISS#2323203) + +function getNativeComponentAttributes(uiViewClassName: string) { + const viewConfig = UIManager.getViewManagerConfig(uiViewClassName); + + invariant( + viewConfig != null && viewConfig.NativeProps != null, + 'requireNativeComponent: "%s" was not found in the UIManager.', + uiViewClassName, + ); + + // TODO: This seems like a whole lot of runtime initialization for every + // native component that can be either avoided or simplified. + let {baseModuleName, bubblingEventTypes, directEventTypes} = viewConfig; + let nativeProps = viewConfig.NativeProps; + while (baseModuleName) { + const baseModule = UIManager.getViewManagerConfig(baseModuleName); + if (!baseModule) { + warning(false, 'Base module "%s" does not exist', baseModuleName); + baseModuleName = null; + } else { + bubblingEventTypes = { + ...baseModule.bubblingEventTypes, + ...bubblingEventTypes, + }; + directEventTypes = { + ...baseModule.directEventTypes, + ...directEventTypes, + }; + nativeProps = { + ...baseModule.NativeProps, + ...nativeProps, + }; + baseModuleName = baseModule.baseModuleName; + } + } + + const validAttributes = {}; + + for (const key in nativeProps) { + const typeName = nativeProps[key]; + const diff = getDifferForType(typeName); + const process = getProcessorForType(typeName); + + validAttributes[key] = + diff == null && process == null ? true : {diff, process}; + } + + // Unfortunately, the current setup declares style properties as top-level + // props. This makes it so we allow style properties in the `style` prop. + // TODO: Move style properties into a `style` prop and disallow them as + // top-level props on the native side. + validAttributes.style = ReactNativeStyleAttributes; + + Object.assign(viewConfig, { + uiViewClassName, + validAttributes, + bubblingEventTypes, + directEventTypes, + }); + + if (!hasAttachedDefaultEventTypes) { + attachDefaultEventTypes(viewConfig); + hasAttachedDefaultEventTypes = true; + } + + return viewConfig; +} + +// TODO: Figure out how this makes sense. We're using a global boolean to only +// initialize this on the first eagerly initialized native component. +let hasAttachedDefaultEventTypes = false; +function attachDefaultEventTypes(viewConfig: any) { + // This is supported on UIManager platforms (ex: Android), + // as lazy view managers are not implemented for all platforms. + // See [UIManager] for details on constants and implementations. + if (UIManager.ViewManagerNames || UIManager.LazyViewManagersEnabled) { + // Lazy view managers enabled. + viewConfig = merge(viewConfig, UIManager.getDefaultEventTypes()); + } else { + viewConfig.bubblingEventTypes = merge( + viewConfig.bubblingEventTypes, + UIManager.genericBubblingEventTypes, + ); + viewConfig.directEventTypes = merge( + viewConfig.directEventTypes, + UIManager.genericDirectEventTypes, + ); + } +} + +// TODO: Figure out how to avoid all this runtime initialization cost. +function merge(destination: ?Object, source: ?Object): ?Object { + if (!source) { + return destination; + } + if (!destination) { + return source; + } + + for (const key in source) { + if (!source.hasOwnProperty(key)) { + continue; + } + + let sourceValue = source[key]; + if (destination.hasOwnProperty(key)) { + const destinationValue = destination[key]; + if ( + typeof sourceValue === 'object' && + typeof destinationValue === 'object' + ) { + sourceValue = merge(destinationValue, sourceValue); + } + } + destination[key] = sourceValue; + } + return destination; +} + +function getDifferForType( + typeName: string, +): ?(prevProp: any, nextProp: any) => boolean { + switch (typeName) { + // iOS Types + case 'CATransform3D': + return matricesDiffer; + case 'CGPoint': + return pointsDiffer; + case 'CGSize': + return sizesDiffer; + case 'UIEdgeInsets': + return insetsDiffer; + // Android Types + // (not yet implemented) + } + return null; +} + +function getProcessorForType(typeName: string): ?(nextProp: any) => any { + switch (typeName) { + // iOS Types + case 'CGColor': + case 'UIColor': + return processColor; + case 'CGColorArray': + case 'UIColorArray': + return processColorArray; + case 'CGImage': + case 'UIImage': + case 'RCTImageSource': + return resolveAssetSource; + // Android Types + case 'Color': + return processColor; + case 'ColorArray': + return processColorArray; + } + return null; +} + +function processColorArray( + colors: ?Array, +): ?Array /* TODO(macOS ISS#2323203) */ { + // ]TODO(macOS ISS#2323203) + return colors == null ? null : colors.map(processColor); +} + +module.exports = getNativeComponentAttributes; diff --git a/vnext/src/Libraries/StyleSheet/StyleSheetTypes.windows.js b/vnext/src/Libraries/StyleSheet/StyleSheetTypes.windows.js new file mode 100644 index 00000000000..3016b2facb8 --- /dev/null +++ b/vnext/src/Libraries/StyleSheet/StyleSheetTypes.windows.js @@ -0,0 +1,657 @@ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + * + * @flow strict-local + * @format + */ + +'use strict'; + +const AnimatedNode = require('../Animated/src/nodes/AnimatedNode'); +import type {NativeOrDynamicColorType} from '../Color/NativeOrDynamicColorType'; // TODO(macOS ISS#2323203) + +export type ColorValue = null | string | NativeOrDynamicColorType; // TODO(macOS ISS#2323203) +export type DimensionValue = null | number | string | AnimatedNode; + +/** + * React Native's layout system is based on Flexbox and is powered both + * on iOS and Android by an open source project called `Yoga`: + * https://github.com/facebook/yoga + * + * The implementation in Yoga is slightly different from what the + * Flexbox spec defines - for example, we chose more sensible default + * values. Since our layout docs are generated from the comments in this + * file, please keep a brief comment describing each prop type. + * + * These properties are a subset of our styles that are consumed by the layout + * algorithm and affect the positioning and sizing of views. + */ +type ____LayoutStyle_Internal = $ReadOnly<{| + /** `display` sets the display type of this component. + * + * It works similarly to `display` in CSS, but only support 'flex' and 'none'. + * 'flex' is the default. + */ + display?: 'none' | 'flex', + + /** `width` sets the width of this component. + * + * It works similarly to `width` in CSS, but in React Native you + * must use points or percentages. Ems and other units are not supported. + * See https://developer.mozilla.org/en-US/docs/Web/CSS/width for more details. + */ + width?: DimensionValue, + + /** `height` sets the height of this component. + * + * It works similarly to `height` in CSS, but in React Native you + * must use points or percentages. Ems and other units are not supported. + * See https://developer.mozilla.org/en-US/docs/Web/CSS/height for more details. + */ + height?: DimensionValue, + + /** `bottom` is the number of logical pixels to offset the bottom edge of + * this component. + * + * It works similarly to `bottom` in CSS, but in React Native you + * must use points or percentages. Ems and other units are not supported. + * + * See https://developer.mozilla.org/en-US/docs/Web/CSS/bottom + * for more details of how `bottom` affects layout. + */ + bottom?: DimensionValue, + + /** + * When the direction is `ltr`, `end` is equivalent to `right`. + * When the direction is `rtl`, `end` is equivalent to `left`. + * + * This style takes precedence over the `left` and `right` styles. + */ + end?: DimensionValue, + + /** `left` is the number of logical pixels to offset the left edge of + * this component. + * + * It works similarly to `left` in CSS, but in React Native you + * must use points or percentages. Ems and other units are not supported. + * + * See https://developer.mozilla.org/en-US/docs/Web/CSS/left + * for more details of how `left` affects layout. + */ + left?: DimensionValue, + + /** `right` is the number of logical pixels to offset the right edge of + * this component. + * + * It works similarly to `right` in CSS, but in React Native you + * must use points or percentages. Ems and other units are not supported. + * + * See https://developer.mozilla.org/en-US/docs/Web/CSS/right + * for more details of how `right` affects layout. + */ + right?: DimensionValue, + + /** + * When the direction is `ltr`, `start` is equivalent to `left`. + * When the direction is `rtl`, `start` is equivalent to `right`. + * + * This style takes precedence over the `left`, `right`, and `end` styles. + */ + start?: DimensionValue, + + /** `top` is the number of logical pixels to offset the top edge of + * this component. + * + * It works similarly to `top` in CSS, but in React Native you + * must use points or percentages. Ems and other units are not supported. + * + * See https://developer.mozilla.org/en-US/docs/Web/CSS/top + * for more details of how `top` affects layout. + */ + top?: DimensionValue, + + /** `minWidth` is the minimum width for this component, in logical pixels. + * + * It works similarly to `min-width` in CSS, but in React Native you + * must use points or percentages. Ems and other units are not supported. + * + * See https://developer.mozilla.org/en-US/docs/Web/CSS/min-width + * for more details. + */ + minWidth?: DimensionValue, + + /** `maxWidth` is the maximum width for this component, in logical pixels. + * + * It works similarly to `max-width` in CSS, but in React Native you + * must use points or percentages. Ems and other units are not supported. + * + * See https://developer.mozilla.org/en-US/docs/Web/CSS/max-width + * for more details. + */ + maxWidth?: DimensionValue, + + /** `minHeight` is the minimum height for this component, in logical pixels. + * + * It works similarly to `min-height` in CSS, but in React Native you + * must use points or percentages. Ems and other units are not supported. + * + * See https://developer.mozilla.org/en-US/docs/Web/CSS/min-height + * for more details. + */ + minHeight?: DimensionValue, + + /** `maxHeight` is the maximum height for this component, in logical pixels. + * + * It works similarly to `max-height` in CSS, but in React Native you + * must use points or percentages. Ems and other units are not supported. + * + * See https://developer.mozilla.org/en-US/docs/Web/CSS/max-height + * for more details. + */ + maxHeight?: DimensionValue, + + /** Setting `margin` has the same effect as setting each of + * `marginTop`, `marginLeft`, `marginBottom`, and `marginRight`. + * See https://developer.mozilla.org/en-US/docs/Web/CSS/margin + * for more details. + */ + margin?: DimensionValue, + + /** `marginBottom` works like `margin-bottom` in CSS. + * See https://developer.mozilla.org/en-US/docs/Web/CSS/margin-bottom + * for more details. + */ + marginBottom?: DimensionValue, + + /** + * When direction is `ltr`, `marginEnd` is equivalent to `marginRight`. + * When direction is `rtl`, `marginEnd` is equivalent to `marginLeft`. + */ + marginEnd?: DimensionValue, + + /** Setting `marginHorizontal` has the same effect as setting + * both `marginLeft` and `marginRight`. + */ + marginHorizontal?: DimensionValue, + + /** `marginLeft` works like `margin-left` in CSS. + * See https://developer.mozilla.org/en-US/docs/Web/CSS/margin-left + * for more details. + */ + marginLeft?: DimensionValue, + + /** `marginRight` works like `margin-right` in CSS. + * See https://developer.mozilla.org/en-US/docs/Web/CSS/margin-right + * for more details. + */ + marginRight?: DimensionValue, + + /** + * When direction is `ltr`, `marginStart` is equivalent to `marginLeft`. + * When direction is `rtl`, `marginStart` is equivalent to `marginRight`. + */ + marginStart?: DimensionValue, + + /** `marginTop` works like `margin-top` in CSS. + * See https://developer.mozilla.org/en-US/docs/Web/CSS/margin-top + * for more details. + */ + marginTop?: DimensionValue, + + /** Setting `marginVertical` has the same effect as setting both + * `marginTop` and `marginBottom`. + */ + marginVertical?: DimensionValue, + + /** Setting `padding` has the same effect as setting each of + * `paddingTop`, `paddingBottom`, `paddingLeft`, and `paddingRight`. + * See https://developer.mozilla.org/en-US/docs/Web/CSS/padding + * for more details. + */ + padding?: DimensionValue, + + /** `paddingBottom` works like `padding-bottom` in CSS. + * See https://developer.mozilla.org/en-US/docs/Web/CSS/padding-bottom + * for more details. + */ + paddingBottom?: DimensionValue, + + /** + * When direction is `ltr`, `paddingEnd` is equivalent to `paddingRight`. + * When direction is `rtl`, `paddingEnd` is equivalent to `paddingLeft`. + */ + paddingEnd?: DimensionValue, + + /** Setting `paddingHorizontal` is like setting both of + * `paddingLeft` and `paddingRight`. + */ + paddingHorizontal?: DimensionValue, + + /** `paddingLeft` works like `padding-left` in CSS. + * See https://developer.mozilla.org/en-US/docs/Web/CSS/padding-left + * for more details. + */ + paddingLeft?: DimensionValue, + + /** `paddingRight` works like `padding-right` in CSS. + * See https://developer.mozilla.org/en-US/docs/Web/CSS/padding-right + * for more details. + */ + paddingRight?: DimensionValue, + + /** + * When direction is `ltr`, `paddingStart` is equivalent to `paddingLeft`. + * When direction is `rtl`, `paddingStart` is equivalent to `paddingRight`. + */ + paddingStart?: DimensionValue, + + /** `paddingTop` works like `padding-top` in CSS. + * See https://developer.mozilla.org/en-US/docs/Web/CSS/padding-top + * for more details. + */ + paddingTop?: DimensionValue, + + /** Setting `paddingVertical` is like setting both of + * `paddingTop` and `paddingBottom`. + */ + paddingVertical?: DimensionValue, + + /** `borderWidth` works like `border-width` in CSS. + * See https://developer.mozilla.org/en-US/docs/Web/CSS/border-width + * for more details. + */ + borderWidth?: number, + + /** `borderBottomWidth` works like `border-bottom-width` in CSS. + * See https://developer.mozilla.org/en-US/docs/Web/CSS/border-bottom-width + * for more details. + */ + borderBottomWidth?: number, + + /** + * When direction is `ltr`, `borderEndWidth` is equivalent to `borderRightWidth`. + * When direction is `rtl`, `borderEndWidth` is equivalent to `borderLeftWidth`. + */ + borderEndWidth?: number, + + /** `borderLeftWidth` works like `border-left-width` in CSS. + * See https://developer.mozilla.org/en-US/docs/Web/CSS/border-left-width + * for more details. + */ + borderLeftWidth?: number, + + /** `borderRightWidth` works like `border-right-width` in CSS. + * See https://developer.mozilla.org/en-US/docs/Web/CSS/border-right-width + * for more details. + */ + borderRightWidth?: number, + + /** + * When direction is `ltr`, `borderStartWidth` is equivalent to `borderLeftWidth`. + * When direction is `rtl`, `borderStartWidth` is equivalent to `borderRightWidth`. + */ + borderStartWidth?: number, + + /** `borderTopWidth` works like `border-top-width` in CSS. + * See https://developer.mozilla.org/en-US/docs/Web/CSS/border-top-width + * for more details. + */ + borderTopWidth?: number, + + /** `position` in React Native is similar to regular CSS, but + * everything is set to `relative` by default, so `absolute` + * positioning is always just relative to the parent. + * + * If you want to position a child using specific numbers of logical + * pixels relative to its parent, set the child to have `absolute` + * position. + * + * If you want to position a child relative to something + * that is not its parent, just don't use styles for that. Use the + * component tree. + * + * See https://github.com/facebook/yoga + * for more details on how `position` differs between React Native + * and CSS. + */ + position?: 'absolute' | 'relative', + + /** `flexDirection` controls which directions children of a container go. + * `row` goes left to right, `column` goes top to bottom, and you may + * be able to guess what the other two do. It works like `flex-direction` + * in CSS, except the default is `column`. + * See https://developer.mozilla.org/en-US/docs/Web/CSS/flex-direction + * for more details. + */ + flexDirection?: 'row' | 'row-reverse' | 'column' | 'column-reverse', + + /** `flexWrap` controls whether children can wrap around after they + * hit the end of a flex container. + * It works like `flex-wrap` in CSS (default: nowrap). + * See https://developer.mozilla.org/en-US/docs/Web/CSS/flex-wrap + * for more details. + */ + flexWrap?: 'wrap' | 'nowrap' | 'wrap-reverse', + + /** `justifyContent` aligns children in the main direction. + * For example, if children are flowing vertically, `justifyContent` + * controls how they align vertically. + * It works like `justify-content` in CSS (default: flex-start). + * See https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content + * for more details. + */ + justifyContent?: + | 'flex-start' + | 'flex-end' + | 'center' + | 'space-between' + | 'space-around' + | 'space-evenly', + + /** `alignItems` aligns children in the cross direction. + * For example, if children are flowing vertically, `alignItems` + * controls how they align horizontally. + * It works like `align-items` in CSS (default: stretch). + * See https://developer.mozilla.org/en-US/docs/Web/CSS/align-items + * for more details. + */ + alignItems?: 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline', + + /** `alignSelf` controls how a child aligns in the cross direction, + * overriding the `alignItems` of the parent. It works like `align-self` + * in CSS (default: auto). + * See https://developer.mozilla.org/en-US/docs/Web/CSS/align-self + * for more details. + */ + alignSelf?: + | 'auto' + | 'flex-start' + | 'flex-end' + | 'center' + | 'stretch' + | 'baseline', + + /** `alignContent` controls how rows align in the cross direction, + * overriding the `alignContent` of the parent. + * See https://developer.mozilla.org/en-US/docs/Web/CSS/align-content + * for more details. + */ + alignContent?: + | 'flex-start' + | 'flex-end' + | 'center' + | 'stretch' + | 'space-between' + | 'space-around', + + /** `overflow` controls how children are measured and displayed. + * `overflow: hidden` causes views to be clipped while `overflow: scroll` + * causes views to be measured independently of their parents main axis. + * It works like `overflow` in CSS (default: visible). + * See https://developer.mozilla.org/en/docs/Web/CSS/overflow + * for more details. + * `overflow: visible` only works on iOS. On Android, all views will clip + * their children. + */ + overflow?: 'visible' | 'hidden' | 'scroll', + + /** In React Native `flex` does not work the same way that it does in CSS. + * `flex` is a number rather than a string, and it works + * according to the `Yoga` library + * at https://github.com/facebook/yoga + * + * When `flex` is a positive number, it makes the component flexible + * and it will be sized proportional to its flex value. So a + * component with `flex` set to 2 will take twice the space as a + * component with `flex` set to 1. + * + * When `flex` is 0, the component is sized according to `width` + * and `height` and it is inflexible. + * + * When `flex` is -1, the component is normally sized according + * `width` and `height`. However, if there's not enough space, + * the component will shrink to its `minWidth` and `minHeight`. + * + * flexGrow, flexShrink, and flexBasis work the same as in CSS. + */ + flex?: number, + flexGrow?: number, + flexShrink?: number, + flexBasis?: number | string, + + /** + * Aspect ratio control the size of the undefined dimension of a node. Aspect ratio is a + * non-standard property only available in react native and not CSS. + * + * - On a node with a set width/height aspect ratio control the size of the unset dimension + * - On a node with a set flex basis aspect ratio controls the size of the node in the cross axis + * if unset + * - On a node with a measure function aspect ratio works as though the measure function measures + * the flex basis + * - On a node with flex grow/shrink aspect ratio controls the size of the node in the cross axis + * if unset + * - Aspect ratio takes min/max dimensions into account + */ + aspectRatio?: number, + + /** `zIndex` controls which components display on top of others. + * Normally, you don't use `zIndex`. Components render according to + * their order in the document tree, so later components draw over + * earlier ones. `zIndex` may be useful if you have animations or custom + * modal interfaces where you don't want this behavior. + * + * It works like the CSS `z-index` property - components with a larger + * `zIndex` will render on top. Think of the z-direction like it's + * pointing from the phone into your eyeball. + * See https://developer.mozilla.org/en-US/docs/Web/CSS/z-index for + * more details. + */ + zIndex?: number, + + /** `direction` specifies the directional flow of the user interface. + * The default is `inherit`, except for root node which will have + * value based on the current locale. + * See https://facebook.github.io/yoga/docs/rtl/ + * for more details. + * @platform ios + */ + direction?: 'inherit' | 'ltr' | 'rtl', +|}>; + +type ____TransformStyle_Internal = $ReadOnly<{| + /** + * `transform` accepts an array of transformation objects. Each object specifies + * the property that will be transformed as the key, and the value to use in the + * transformation. Objects should not be combined. Use a single key/value pair + * per object. + * + * The rotate transformations require a string so that the transform may be + * expressed in degrees (deg) or radians (rad). For example: + * + * `transform([{ rotateX: '45deg' }, { rotateZ: '0.785398rad' }])` + * + * The skew transformations require a string so that the transform may be + * expressed in degrees (deg). For example: + * + * `transform([{ skewX: '45deg' }])` + */ + transform?: $ReadOnlyArray< + | {|+perspective: number | AnimatedNode|} + | {|+rotate: string | AnimatedNode|} + | {|+rotateX: string | AnimatedNode|} + | {|+rotateY: string | AnimatedNode|} + | {|+rotateZ: string | AnimatedNode|} + | {|+scale: number | AnimatedNode|} + | {|+scaleX: number | AnimatedNode|} + | {|+scaleY: number | AnimatedNode|} + | {|+translateX: number | AnimatedNode|} + | {|+translateY: number | AnimatedNode|} + | {| + +translate: + | [number | AnimatedNode, number | AnimatedNode] + | AnimatedNode, + |} + | {|+skewX: string|} + | {|+skewY: string|} + // TODO: what is the actual type it expects? + | {| + +matrix: $ReadOnlyArray | AnimatedNode, + |}, + >, +|}>; + +/** + * These props can be used to dynamically generate shadows on views, images, text, etc. + * + * Because they are dynamically generated, they may cause performance regressions. Static + * shadow image asset may be a better way to go for optimal performance. + * + * These properties are iOS only - for similar functionality on Android, use the [`elevation` + * property](docs/viewstyleproptypes.html#elevation). + */ +export type ____ShadowStyle_Internal = $ReadOnly<{| + /** + * Sets the drop shadow color + * @platform ios + */ + shadowColor?: ColorValue, + /** + * Sets the drop shadow offset + * @platform ios + */ + shadowOffset?: $ReadOnly<{| + width?: number, + height?: number, + |}>, + /** + * Sets the drop shadow opacity (multiplied by the color's alpha component) + * @platform ios + */ + shadowOpacity?: number | AnimatedNode, + /** + * Sets the drop shadow blur radius + * @platform ios + */ + shadowRadius?: number, +|}>; + +export type ____ViewStyle_Internal = $ReadOnly<{| + ...$Exact<____LayoutStyle_Internal>, + ...$Exact<____ShadowStyle_Internal>, + ...$Exact<____TransformStyle_Internal>, + backfaceVisibility?: 'visible' | 'hidden', + backgroundColor?: ColorValue, + borderColor?: ColorValue, + borderBottomColor?: ColorValue, + borderEndColor?: ColorValue, + borderLeftColor?: ColorValue, + borderRightColor?: ColorValue, + borderStartColor?: ColorValue, + borderTopColor?: ColorValue, + borderRadius?: number | AnimatedNode, + borderBottomEndRadius?: number | AnimatedNode, + borderBottomLeftRadius?: number | AnimatedNode, + borderBottomRightRadius?: number | AnimatedNode, + borderBottomStartRadius?: number | AnimatedNode, + borderTopEndRadius?: number | AnimatedNode, + borderTopLeftRadius?: number | AnimatedNode, + borderTopRightRadius?: number | AnimatedNode, + borderTopStartRadius?: number | AnimatedNode, + borderStyle?: 'solid' | 'dotted' | 'dashed', + borderWidth?: number | AnimatedNode, + borderBottomWidth?: number | AnimatedNode, + borderEndWidth?: number | AnimatedNode, + borderLeftWidth?: number | AnimatedNode, + borderRightWidth?: number | AnimatedNode, + borderStartWidth?: number | AnimatedNode, + borderTopWidth?: number | AnimatedNode, + opacity?: number | AnimatedNode, + elevation?: number, +|}>; + +export type ____TextStyle_Internal = $ReadOnly<{| + ...$Exact<____ViewStyle_Internal>, + color?: ColorValue, + fontFamily?: string, + fontSize?: number, + fontStyle?: 'normal' | 'italic', + fontWeight?: + | 'normal' + | 'bold' + | '100' + | '200' + | '300' + | '400' + | '500' + | '600' + | '700' + | '800' + | '900', + fontVariant?: $ReadOnlyArray< + | 'small-caps' + | 'oldstyle-nums' + | 'lining-nums' + | 'tabular-nums' + | 'proportional-nums', + >, + textShadowOffset?: $ReadOnly<{| + width: number, + height: number, + |}>, + textShadowRadius?: number, + textShadowColor?: ColorValue, + letterSpacing?: number, + lineHeight?: number, + textAlign?: 'auto' | 'left' | 'right' | 'center' | 'justify', + textAlignVertical?: 'auto' | 'top' | 'bottom' | 'center', + includeFontPadding?: boolean, + textDecorationLine?: + | 'none' + | 'underline' + | 'line-through' + | 'underline line-through', + textDecorationStyle?: 'solid' | 'double' | 'dotted' | 'dashed', + textDecorationColor?: ColorValue, + textTransform?: 'none' | 'capitalize' | 'uppercase' | 'lowercase', + writingDirection?: 'auto' | 'ltr' | 'rtl', +|}>; + +export type ____ImageStyle_Internal = $ReadOnly<{| + ...$Exact<____ViewStyle_Internal>, + resizeMode?: 'contain' | 'cover' | 'stretch' | 'center' | 'repeat', + tintColor?: ColorValue, + overlayColor?: string, +|}>; + +export type ____DangerouslyImpreciseStyle_Internal = { + ...$Exact<____TextStyle_Internal>, + +resizeMode?: 'contain' | 'cover' | 'stretch' | 'center' | 'repeat', + +tintColor?: ColorValue, + +overlayColor?: string, +}; + +type GenericStyleProp<+T> = + | null + | void + | T + | false + | '' + | $ReadOnlyArray>; + +export type ____DangerouslyImpreciseStyleProp_Internal = GenericStyleProp< + $Shape<____DangerouslyImpreciseStyle_Internal>, +>; +export type ____ViewStyleProp_Internal = GenericStyleProp< + $ReadOnly<$Shape<____ViewStyle_Internal>>, +>; +export type ____TextStyleProp_Internal = GenericStyleProp< + $ReadOnly<$Shape<____TextStyle_Internal>>, +>; +export type ____ImageStyleProp_Internal = GenericStyleProp< + $ReadOnly<$Shape<____ImageStyle_Internal>>, +>; + +export type ____Styles_Internal = { + +[key: string]: $Shape<____DangerouslyImpreciseStyle_Internal>, +}; diff --git a/vnext/src/Libraries/StyleSheet/processColor.windows.js b/vnext/src/Libraries/StyleSheet/processColor.windows.js new file mode 100644 index 00000000000..4589713f622 --- /dev/null +++ b/vnext/src/Libraries/StyleSheet/processColor.windows.js @@ -0,0 +1,56 @@ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + * + * @format + * @flow strict-local + */ + +'use strict'; + +const Platform = require('../Utilities/Platform'); + +const normalizeColor = require('../Color/normalizeColor'); +import type {NativeOrDynamicColorType} from '../Color/NativeOrDynamicColorType'; // ]TODO(macOS ISS#2323203) + +/* eslint no-bitwise: 0 */ +function processColor( + color?: ?(string | number | NativeOrDynamicColorType), +): ?(number | NativeOrDynamicColorType) /* TODO(macOS ISS#2323203) */ { + if (color === undefined || color === null) { + return color; + } + + let int32Color = normalizeColor(color); + if (int32Color === null || int32Color === undefined) { + return undefined; + } + + if (typeof int32Color === 'object') { + const processColorObject = require('processColorObject'); // TODO(macOS ISS#2323203) + + const processedColorObj = processColorObject(int32Color); + + if (processedColorObj !== null) { + return processedColorObj; + } + } + + if (typeof int32Color !== 'number') { + return null; + } // ]TODO(macOS ISS#2323203) + + // Converts 0xrrggbbaa into 0xaarrggbb + int32Color = ((int32Color << 24) | (int32Color >>> 8)) >>> 0; + + if (Platform.OS === 'android') { + // Android use 32 bit *signed* integer to represent the color + // We utilize the fact that bitwise operations in JS also operates on + // signed 32 bit integers, so that we can use those to convert from + // *unsigned* to *signed* 32bit int that way. + int32Color = int32Color | 0x0; + } + return int32Color; +} + +module.exports = processColor; diff --git a/vnext/src/RNTester/RNTesterList.windows.ts b/vnext/src/RNTester/RNTesterList.windows.ts index 143f0dc176a..e09814c2ccb 100644 --- a/vnext/src/RNTester/RNTesterList.windows.ts +++ b/vnext/src/RNTester/RNTesterList.windows.ts @@ -215,6 +215,10 @@ const APIExamples: Array = [ key: 'ThemingExample', module: require('./ThemingExample'), }, + { + key: 'WindowsBrushExample', + module: require('./WindowsBrushExample'), + }, { key: 'TransformExample', module: require('react-native/RNTester/js/TransformExample'), diff --git a/vnext/src/RNTester/WindowsBrushExample.windows.tsx b/vnext/src/RNTester/WindowsBrushExample.windows.tsx new file mode 100644 index 00000000000..a3b325cd245 --- /dev/null +++ b/vnext/src/RNTester/WindowsBrushExample.windows.tsx @@ -0,0 +1,42 @@ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + * @format + */ + +import React = require('react'); +import {Text, View, Button} from 'react-native'; + +class WindowsBrushExample extends React.Component { + _onPress = () => {}; + + public render() { + return ( + + + Sample Text + +