From 747184989ca1423c4601a4ed3d623eb72163213c Mon Sep 17 00:00:00 2001 From: Gabriel Donadel Date: Fri, 2 Feb 2024 16:44:50 -0300 Subject: [PATCH 1/2] [menu-bar] Add electron support for Checkbox component --- .../components/Checkbox/NativeCheckbox.tsx | 57 +++++++++++++++++ .../Checkbox/NativeCheckbox.web.tsx | 19 ++++++ .../src/components/Checkbox/index.tsx | 61 +++---------------- .../src/components/Checkbox/index.web.tsx | 5 -- .../src/components/Checkbox/types.tsx | 18 ++++++ 5 files changed, 101 insertions(+), 59 deletions(-) create mode 100644 apps/menu-bar/src/components/Checkbox/NativeCheckbox.tsx create mode 100644 apps/menu-bar/src/components/Checkbox/NativeCheckbox.web.tsx delete mode 100644 apps/menu-bar/src/components/Checkbox/index.web.tsx create mode 100644 apps/menu-bar/src/components/Checkbox/types.tsx diff --git a/apps/menu-bar/src/components/Checkbox/NativeCheckbox.tsx b/apps/menu-bar/src/components/Checkbox/NativeCheckbox.tsx new file mode 100644 index 00000000..fed34fc4 --- /dev/null +++ b/apps/menu-bar/src/components/Checkbox/NativeCheckbox.tsx @@ -0,0 +1,57 @@ +import React, { useImperativeHandle, useLayoutEffect, useRef, useState } from 'react'; +import { requireNativeComponent } from 'react-native'; +import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands'; + +import { CheckboxChangeEvent, NativeCheckboxProps } from './types'; + +const NativeCheckbox = requireNativeComponent('Checkbox'); + +interface NativeCommands { + setValue: (viewRef: React.ElementRef, value: boolean) => void; +} + +const Commands: NativeCommands = codegenNativeCommands({ + supportedCommands: ['setValue'], +}); + +const Checkbox = React.forwardRef( + ( + { + onChange, + ...props + }: NativeCheckboxProps & { + onChange?: (event: CheckboxChangeEvent) => void; + }, + ref: React.ForwardedRef<{ setNative: (native: { value?: boolean }) => void }> + ) => { + const nativeSwitchRef = useRef | null>(null); + const [native, setNative] = useState<{ value?: boolean }>({ value: undefined }); + + useImperativeHandle( + ref, + () => ({ + setNative, + }), + [] + ); + + useLayoutEffect(() => { + // This is necessary in case native updates the switch and JS decides + // that the update should be ignored and we should stick with the value + // that we have in JS. + const jsValue = props.value === true; + const shouldUpdateNativeSwitch = native.value != null && native.value !== jsValue; + if (shouldUpdateNativeSwitch && nativeSwitchRef.current?.setNativeProps != null) { + Commands.setValue(nativeSwitchRef.current, jsValue); + } + }, [props.value, native]); + + const handleChange = (event: CheckboxChangeEvent) => { + onChange?.(event); + setNative({ value: event.nativeEvent.value }); + }; + + return ; + } +); +export default Checkbox; diff --git a/apps/menu-bar/src/components/Checkbox/NativeCheckbox.web.tsx b/apps/menu-bar/src/components/Checkbox/NativeCheckbox.web.tsx new file mode 100644 index 00000000..25b7ef89 --- /dev/null +++ b/apps/menu-bar/src/components/Checkbox/NativeCheckbox.web.tsx @@ -0,0 +1,19 @@ +import React from 'react'; + +import { CheckboxChangeEvent, NativeCheckboxProps } from './types'; + +const Checkbox = React.forwardRef(({ value, onChange }: NativeCheckboxProps, ref) => { + return ( + + onChange?.({ + nativeEvent: { value: event.target.checked }, + } as CheckboxChangeEvent) + } + /> + ); +}); + +export default Checkbox; diff --git a/apps/menu-bar/src/components/Checkbox/index.tsx b/apps/menu-bar/src/components/Checkbox/index.tsx index 391bc807..bb687981 100644 --- a/apps/menu-bar/src/components/Checkbox/index.tsx +++ b/apps/menu-bar/src/components/Checkbox/index.tsx @@ -1,64 +1,17 @@ -import { useLayoutEffect, useRef, useState } from 'react'; -import { - NativeSyntheticEvent, - Pressable, - requireNativeComponent, - StyleSheet, - TargetedEvent, - ViewProps, -} from 'react-native'; -import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands'; +import { useRef } from 'react'; +import { Pressable, StyleSheet } from 'react-native'; +import NativeCheckbox from './NativeCheckbox'; +import { CheckboxChangeEvent, CheckboxProps } from './types'; import { Text } from '../Text'; import { Row } from '../View'; -interface CheckboxChangeEventData extends TargetedEvent { - value: boolean; -} - -interface CheckboxChangeEvent extends NativeSyntheticEvent {} - -type NativeCheckboxProps = ViewProps & { - disabled?: boolean; - onChange?: (event: CheckboxChangeEvent) => void; - value?: boolean; -}; - -const NativeCheckbox = requireNativeComponent('Checkbox'); - -type CheckboxProps = NativeCheckboxProps & { - onValueChange?: (value: boolean) => void; - label?: string; -}; - -interface NativeCommands { - setValue: (viewRef: React.ElementRef, value: boolean) => void; -} - -export const Commands: NativeCommands = codegenNativeCommands({ - supportedCommands: ['setValue'], -}); - const Checkbox = ({ onChange, onValueChange, label, ...props }: CheckboxProps) => { - const [native, setNative] = useState<{ value?: boolean }>({ value: undefined }); - - const nativeSwitchRef = useRef | null>(null); - - useLayoutEffect(() => { - // This is necessary in case native updates the switch and JS decides - // that the update should be ignored and we should stick with the value - // that we have in JS. - const jsValue = props.value === true; - const shouldUpdateNativeSwitch = native.value != null && native.value !== jsValue; - if (shouldUpdateNativeSwitch && nativeSwitchRef.current?.setNativeProps != null) { - Commands.setValue(nativeSwitchRef.current, jsValue); - } - }, [props.value, native]); + const nativeCheckboxRef = useRef>(null); const handleChange = (event: CheckboxChangeEvent) => { onChange?.(event); onValueChange?.(event.nativeEvent.value); - setNative({ value: event.nativeEvent.value }); }; return ( @@ -67,13 +20,13 @@ const Checkbox = ({ onChange, onValueChange, label, ...props }: CheckboxProps) = {...props} style={[styles.checkbox, props.style]} onChange={handleChange} - ref={nativeSwitchRef} + ref={nativeCheckboxRef} /> {label && ( { onValueChange?.(!props.value); - setNative({ value: !props.value }); + nativeCheckboxRef.current?.setNative({ value: !props.value }); }}> {label} diff --git a/apps/menu-bar/src/components/Checkbox/index.web.tsx b/apps/menu-bar/src/components/Checkbox/index.web.tsx deleted file mode 100644 index 6a276684..00000000 --- a/apps/menu-bar/src/components/Checkbox/index.web.tsx +++ /dev/null @@ -1,5 +0,0 @@ -const Checkbox = () => { - return ; -}; - -export default Checkbox; diff --git a/apps/menu-bar/src/components/Checkbox/types.tsx b/apps/menu-bar/src/components/Checkbox/types.tsx new file mode 100644 index 00000000..3ca5bcb7 --- /dev/null +++ b/apps/menu-bar/src/components/Checkbox/types.tsx @@ -0,0 +1,18 @@ +import { TargetedEvent, NativeSyntheticEvent, ViewProps } from 'react-native'; + +interface CheckboxChangeEventData extends TargetedEvent { + value: boolean; +} + +export interface CheckboxChangeEvent extends NativeSyntheticEvent {} + +export type NativeCheckboxProps = ViewProps & { + disabled?: boolean; + onChange?: (event: CheckboxChangeEvent) => void; + value?: boolean; +}; + +export type CheckboxProps = Omit & { + onValueChange?: (value: boolean) => void; + label?: string; +}; From e1082b33b7956279b3e88874a7520f3d1db8f0a1 Mon Sep 17 00:00:00 2001 From: Gabriel Donadel Date: Mon, 5 Feb 2024 20:20:54 -0300 Subject: [PATCH 2/2] Add changelog entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1e2afae..7b19021b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ - Add support for launching Expo updates. ([#134](https://github.com/expo/orbit/pull/134), [#137](https://github.com/expo/orbit/pull/137), [#138](https://github.com/expo/orbit/pull/138), [#144](https://github.com/expo/orbit/pull/144), [#148](https://github.com/expo/orbit/pull/148) by [@gabrieldonadel](https://github.com/gabrieldonadel)) - Cache builds by default. ([#156](https://github.com/expo/orbit/pull/156) by [@gabrieldonadel](https://github.com/gabrieldonadel)) -- Add experimental support for Windows and Linux. ([#152](https://github.com/expo/orbit/pull/152), [#157](https://github.com/expo/orbit/pull/157), [#158](https://github.com/expo/orbit/pull/158), [#160](https://github.com/expo/orbit/pull/160) by [@gabrieldonadel](https://github.com/gabrieldonadel)) +- Add experimental support for Windows and Linux. ([#152](https://github.com/expo/orbit/pull/152), [#157](https://github.com/expo/orbit/pull/157), [#158](https://github.com/expo/orbit/pull/158), [#160](https://github.com/expo/orbit/pull/160), [#161](https://github.com/expo/orbit/pull/161) by [@gabrieldonadel](https://github.com/gabrieldonadel)) - Improve fatal CLI error handling. ([#163](https://github.com/expo/orbit/pull/163) by [@gabrieldonadel](https://github.com/gabrieldonadel)) ### 🐛 Bug fixes