From e56b91a3bef0578565848fbb7dcbdae3fc9f6386 Mon Sep 17 00:00:00 2001 From: Nicolas Gallagher Date: Fri, 5 Aug 2022 12:05:56 -0700 Subject: [PATCH] RFC: Reduce API fragmentation across platforms --- proposals/0000-reduce-fragmentation.md | 340 +++++++++++++++++++++++++ 1 file changed, 340 insertions(+) create mode 100644 proposals/0000-reduce-fragmentation.md diff --git a/proposals/0000-reduce-fragmentation.md b/proposals/0000-reduce-fragmentation.md new file mode 100644 index 00000000..bf449a51 --- /dev/null +++ b/proposals/0000-reduce-fragmentation.md @@ -0,0 +1,340 @@ +--- +title: Reduce API fragmentation across platforms +author: +- Nicolas Gallagher +date: 2021-08-03 +--- + +# RFC: Reduce API fragmentation across platforms + +## Summary + +This is a proposal to incrementally reduce the API fragmentation faced by developers using React Native to target multiple platforms, and sharing code between native and web. Each proposed change is additive and can be tackled relatively independently. The aim is to avoid needing to migrate existing React Native code, and to make deprecations an optional and future part of this work. + +## Motivation + +React Native currently includes many APIs that are modelled on Web APIs but do not conform to the standards of those Web APIs. React Native also includes many APIs that achieve the same results on Android and iOS but are exposed as 2 different props. And React Native includes several APIs that have known performance (network and runtime) drawbacks on Web. + +This proposal aims to allow developers to target more platforms with cross-platform APIs, and deliver better performance when targeting browsers. Features for Android, iOS, and Web are unified by aligning with the Web standard to a) minimize the overhead when running in browsers, and b) reduce developer education required to learn these features. + +## Detailed design + +Many of these additions are already in progress and have been proven in a user-space shim at Meta. The features that cannot be shimmed are explicitly called out below. Those features require core changes to React Native. Other features can be shimmed in user-space JavaScript if desired (with any associated performance cost of doing so). + +### New common props + +Certain props common to all core components have direct equivalents in React DOM. We should support those props where possible. For example, many of the existing `accessibility*` props in React Native are currently mapped to `aria-*` equivalents by React Native for Web. Supporting the ARIA interface would allow more ARIA-targeting React DOM libraries to work on React Native. + +* ARIA props + * `aria-busy` is the same as `accessibilityState.busy`. + * `aria-checked` is the same as `accessibilityState.checked`. + * `aria-disabled` is the same as `accessibilityState.disabled`. + * `aria-expanded` is the same as `accessibilityState.expanded`. + * `aria-hidden` is the same as `accessibilityElementsHidden` (iOS) / `importantforAccessibility` (Android). + * `aria-label` is the same as `accessibilityLabel`. + * `aria-live` is the same as `accessibilityLiveRegion` (with `'off'` value changed to `'none'`). + * `aria-modal` is the same as `accessibilityViewIsModal`. + * `aria-selected` is the same as `accessibilityState.selected`. + * `aria-valuemax` is the same as `accessibilityValue.max`. + * `aria-valuemin` is the same as `accessibilityValue.min`. + * `aria-valuenow` is the same as `accessibilityValue.now`. + * `aria-valuetext` is the same as `accessibilityValue.text`. + * `role` is the same as `accessibilityRole` (with support for web values). + * See prior work: [RFC Accessibility APIs](https://github.com/react-native-community/discussions-and-proposals/pull/410/files). +* Add `dir` prop to configure element's layout / writing direction. +* Make `id` the same as `nativeID`. +* Make `onAuxClick` the same as React DOM `onAuxClick` PointerEvent (can't be shimmed). +* Make `onClick` the same as React DOM `onClick` as PointerEvent (can't be shimmed). +* Make `onPointer*` props that same as React DOM PointerEvent props (can't be shimmed). + * The Android and iOS implementation is currently being developed by Meta. +* Make `tabIndex` the same as React Native `focusable` prop. + * Add support for `0` and `-1` values only. + +Example: + +```jsx + +``` + +### New props for `` + +Various React DOM `img` props also have direct equivalents in React Native. For example, `srcSet` is a subset of the `source` prop. Features like alternative text content are also required to target web. + +* Add `alt` prop for alternative text support. +* Add `crossOrigin` prop to declaratively configure cross-origin permissions for loaded images. Equivalent to setting the relevant `Header` in the `source` object. +* Add `referrerPolicy` to declaratively configure referrer policy. Equivalent to setting the relevant `Header` in the `source` object. +* Make `src` that same as setting `source.uri`. +* Make `srcSet` the same as setting a `source` array with `uri` and `scale` properties defined. +* Add `tintColor` prop. + * Image currently sets `tintColor` via the inclusion of a non-standard style property. + * Recommend adding `tintColor` prop to replace the React Native style property, to avoid the need to parse out `tintColor` from style for platforms like web. + +`srcSet` to `source` mapping: + +```js +const { crossOrigin, referrerPolicy, srcSet } = props; +const source = []; +const srcList = srcSet.split(', '); +srcList.forEach((src) => { + const [uri, xscale] = src.split(' '); + const scale = parseInt(xscale.split('x')[0], 10); + const headers = {}; + if (crossOrigin === 'use-credentials') { + headers['Access-Control-Allow-Credentials'] = true; + } + if (referrerPolicy != null) { + headers['Referrer-Policy'] = referrerPolicy; + } + source.push({ headers, height, scale, uri, width }); +}); +``` + +Example: + +```jsx +Alternative text +``` + +### New props for `` + +* Redefine `autoComplete` prop. + * Unify values for Android (`autoComplete`) and iOS (`textContentType`) with Web (`autoComplete`). + * See below for example mapping +* Add `enterKeyHint` prop and map to equivalent `returnKeyType` values. +* Add `inputMode` prop and map to equivalent `keyboardType` values. + * `inputMode === 'decimal'` is the same as `keyboardType = 'decimal-pad'` + * `inputMode === 'email'` is the same as `keyboardType = 'email-address'` + * `inputMode === 'numeric'` is the same as `keyboardType = 'numeric'` + * `inputMode === 'search'` is the same as `keyboardType = 'search'` + * `inputMode === 'tel'` is the same as `keyboardType = 'phone-pad'` + * `inputMode === 'url'` is the same as `keyboardType = 'url'` +* Make `readOnly` the same as `editable` (inverse). +* Make `rows` the same as `numberOfLines`. + * Support auto-expanding textarea on native platforms. + +`autoComplete` mapping: + +```js +// https://reactnative.dev/docs/textinput#autocomplete-android +const autoCompleteAndroid = { + 'address-line1': 'postal-address-region', + 'address-line2': 'postal-address-locality', + bday: 'birthdate-full', + 'bday-day': 'birthdate-day', + 'bday-month': 'birthdate-month', + 'bday-year': 'birthdate-year', + 'cc-csc': 'cc-csc', + 'cc-exp': 'cc-exp', + 'cc-exp-month': 'cc-exp-month', + 'cc-exp-year': 'cc-exp-year', + 'cc-number': 'cc-number', + country: 'postal-address-country', + 'current-password': 'password', + email: 'email', + name: 'name', + 'additional-name': 'name-middle', + 'family-name': 'name-family', + 'given-name': 'name-given', + 'honorific-prefix': 'namePrefix', + 'honorific-suffix': 'nameSuffix', + 'new-password': 'password-new', + off: 'off', + 'one-time-code': 'sms-otp', + 'postal-code': 'postal-code', + sex: 'gender', + 'street-address': 'street-address', + tel: 'tel', + 'tel-country-code': 'tel-country-code', + 'tel-national': 'tel-national', + username: 'username' +}; + +// https://reactnative.dev/docs/textinput#textcontenttype-ios +const autoCompleteIOS = { + 'address-line1': 'streetAddressLine1', + 'address-line2': 'streetAddressLine2', + 'cc-number': 'creditCardNumber', + 'current-password': 'password', + country: 'countryName', + email: 'emailAddress', + name: 'name', + 'additional-name': 'middleName', + 'family-name': 'familyName', + 'given-name': 'givenName', + nickname: 'nickname', + 'honorific-prefix': 'name-prefix', + 'honorific-suffix': 'name-suffix', + 'new-password': 'newPassword', + off: 'none', + 'one-time-code': 'oneTimeCode', + organization: 'organizationName', + 'organization-title': 'jobTitle', + 'postal-code': 'postalCode', + 'street-address': 'fullStreetAddress', + tel: 'telephoneNumber', + url: 'URL', + username: 'username' +}; +``` + +Example: + +```jsx +