Skip to content

Commit

Permalink
[RNMobile] Remove third-party dependency on react-native-hsv-color-pi…
Browse files Browse the repository at this point in the history
…cker (#53329)

* Add hsv color picker components

* Update Saturation Picker from source

* Update Hue Picker from source

* Update hsv-color-picker imports

* Remove third-party dependency package react-native-hsv-color-picker

* Update hsv-color-picker styles

* Update CHANGELOG

* Add tinycolor2 package

* Remove dependency on tinycolor2 from hsv-color-picker

* Remove tinycolor package reference

* Revert empty change on initial editor html

* Update hsv-color-picker container style

* Add color-picker integration test

* Update packages/components/src/color-picker/style.native.scss

Co-authored-by: David Calhoun <github@davidcalhoun.me>

* Update packages/react-native-editor/CHANGELOG.md

Co-authored-by: David Calhoun <github@davidcalhoun.me>

* Update Color Picker code style and test formatting

* Update hsv-color-picker test ID

* Remove color picker test

Removes Color Picker test in favor of testing the color picker
in a deeper editor tree, like Cover block

* Update saturation picker imports

---------

Co-authored-by: David Calhoun <github@davidcalhoun.me>
  • Loading branch information
derekblank and dcalhoun authored Aug 28, 2023
1 parent 6c2a629 commit dc06193
Show file tree
Hide file tree
Showing 9 changed files with 474 additions and 38 deletions.
29 changes: 0 additions & 29 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 1 addition & 5 deletions packages/block-library/src/cover/test/edit.native.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { Image, Pressable } from 'react-native';
import { Image } from 'react-native';
import {
getEditorHtml,
initializeEditor,
Expand All @@ -12,7 +12,6 @@ import {
getBlock,
openBlockSettings,
} from 'test/helpers';
import HsvColorPicker from 'react-native-hsv-color-picker';

/**
* WordPress dependencies
Expand Down Expand Up @@ -541,9 +540,6 @@ describe( 'color settings', () => {
} );

it( 'displays the hex color value in the custom color picker', async () => {
HsvColorPicker.mockImplementation( ( props ) => {
return <Pressable { ...props } testID="hsv-color-picker" />;
} );
const screen = await initializeEditor( {
initialHtml: COVER_BLOCK_PLACEHOLDER_HTML,
} );
Expand Down
88 changes: 88 additions & 0 deletions packages/components/src/color-picker/hsv-color-picker.native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* External dependencies
*/
import { View, Dimensions } from 'react-native';

/**
* WordPress dependencies
*/
import { useRef } from '@wordpress/element';

/**
* Internal dependencies
*/
import HuePicker from './hue-picker';
import SaturationValuePicker from './saturation-picker';
import styles from './style.native.scss';

const HsvColorPicker = ( props ) => {
const maxWidth = Dimensions.get( 'window' ).width - 32;
const satValPickerRef = useRef( null );

const {
containerStyle = {},
currentColor,
huePickerContainerStyle = {},
huePickerBorderRadius = 0,
huePickerHue = 0,
huePickerBarWidth = maxWidth,
huePickerBarHeight = 12,
huePickerSliderSize = 24,
onHuePickerDragStart,
onHuePickerDragMove,
onHuePickerDragEnd,
onHuePickerDragTerminate,
onHuePickerPress,
satValPickerContainerStyle = {},
satValPickerBorderRadius = 0,
satValPickerSize = { width: maxWidth, height: 200 },
satValPickerSliderSize = 24,
satValPickerHue = 0,
satValPickerSaturation = 1,
satValPickerValue = 1,
onSatValPickerDragStart,
onSatValPickerDragMove,
onSatValPickerDragEnd,
onSatValPickerDragTerminate,
onSatValPickerPress,
} = props;

return (
<View
style={ [ styles[ 'hsv-container' ], containerStyle ] }
testID="hsv-color-picker"
>
<SaturationValuePicker
containerStyle={ satValPickerContainerStyle }
currentColor={ currentColor }
borderRadius={ satValPickerBorderRadius }
size={ satValPickerSize }
sliderSize={ satValPickerSliderSize }
hue={ satValPickerHue }
saturation={ satValPickerSaturation }
value={ satValPickerValue }
onDragStart={ onSatValPickerDragStart }
onDragMove={ onSatValPickerDragMove }
onDragEnd={ onSatValPickerDragEnd }
onDragTerminate={ onSatValPickerDragTerminate }
onPress={ onSatValPickerPress }
ref={ satValPickerRef }
/>
<HuePicker
containerStyle={ huePickerContainerStyle }
borderRadius={ huePickerBorderRadius }
hue={ huePickerHue }
barWidth={ huePickerBarWidth }
barHeight={ huePickerBarHeight }
sliderSize={ huePickerSliderSize }
onDragStart={ onHuePickerDragStart }
onDragMove={ onHuePickerDragMove }
onDragEnd={ onHuePickerDragEnd }
onDragTerminate={ onHuePickerDragTerminate }
onPress={ onHuePickerPress }
/>
</View>
);
};

export default HsvColorPicker;
194 changes: 194 additions & 0 deletions packages/components/src/color-picker/hue-picker.native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/**
* External dependencies
*/
import { Animated, View, PanResponder } from 'react-native';
import LinearGradient from 'react-native-linear-gradient';

/**
* WordPress dependencies
*/
import React, { Component } from '@wordpress/element';

/**
* Internal dependencies
*/
import styles from './style.scss';

export default class HuePicker extends Component {
constructor( props ) {
super( props );
this.hueColors = [
'#ff0000',
'#ffff00',
'#00ff00',
'#00ffff',
'#0000ff',
'#ff00ff',
'#ff0000',
];
this.sliderX = new Animated.Value(
( props.barHeight * props.hue ) / 360
);
this.panResponder = PanResponder.create( {
onStartShouldSetPanResponder: () => true,
onStartShouldSetPanResponderCapture: () => true,
onMoveShouldSetPanResponder: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderGrant: ( evt, gestureState ) => {
const { onPress } = this.props;
this.dragStartValue = this.computeHueValuePress( evt );

if ( onPress ) {
onPress( {
hue: this.computeHueValuePress( evt ),
nativeEvent: evt.nativeEvent,
} );
}

this.fireDragEvent( 'onDragStart', gestureState );
},
onPanResponderMove: ( evt, gestureState ) => {
this.fireDragEvent( 'onDragMove', gestureState );
},
onPanResponderTerminationRequest: () => true,
onPanResponderRelease: ( evt, gestureState ) => {
this.fireDragEvent( 'onDragEnd', gestureState );
},
onPanResponderTerminate: ( evt, gestureState ) => {
this.fireDragEvent( 'onDragTerminate', gestureState );
},
onShouldBlockNativeResponder: () => true,
} );
}

componentDidUpdate( prevProps ) {
const { hue = 0, barWidth = 200, sliderSize = 24 } = this.props;
const borderWidth = sliderSize / 10;
if ( prevProps.hue !== hue || prevProps.barWidth !== barWidth ) {
this.sliderX.setValue(
( ( barWidth - sliderSize + borderWidth ) * hue ) / 360
);
}
}

normalizeValue( value ) {
if ( value < 0 ) return 0;
if ( value > 1 ) return 1;
return value;
}

getContainerStyle() {
const {
sliderSize = 24,
barHeight = 12,
containerStyle = {},
} = this.props;
const paddingLeft = sliderSize / 2;
const paddingTop =
sliderSize - barHeight > 0 ? ( sliderSize - barHeight ) / 2 : 0;
return [
styles[ 'hsv-container' ],
containerStyle,
{
paddingTop,
paddingBottom: paddingTop,
paddingLeft,
paddingRight: paddingLeft,
},
];
}

computeHueValueDrag( gestureState ) {
const { dx } = gestureState;
const { barWidth = 200 } = this.props;
const { dragStartValue } = this;
const diff = dx / barWidth;
const updatedHue =
this.normalizeValue( dragStartValue / 360 + diff ) * 360;
return updatedHue;
}

computeHueValuePress( event ) {
const { nativeEvent } = event;
const { locationX } = nativeEvent;
const { barWidth = 200 } = this.props;
const updatedHue = this.normalizeValue( locationX / barWidth ) * 360;
return updatedHue;
}

fireDragEvent( eventName, gestureState ) {
const { [ eventName ]: event } = this.props;
if ( event ) {
event( {
hue: this.computeHueValueDrag( gestureState ),
gestureState,
} );
}
}

firePressEvent( event ) {
const { onPress } = this.props;
if ( onPress ) {
onPress( {
hue: this.computeHueValuePress( event ),
nativeEvent: event.nativeEvent,
} );
}
}

render() {
const { hueColors } = this;
const {
sliderSize = 24,
barWidth = 200,
barHeight = 12,
borderRadius = 0,
} = this.props;
const borderWidth = sliderSize / 10;
return (
<View
style={ this.getContainerStyle() }
{ ...this.panResponder.panHandlers }
hitSlop={ {
top: 10,
bottom: 10,
left: 0,
right: 0,
} }
>
<LinearGradient
colors={ hueColors }
style={ {
borderRadius,
} }
start={ { x: 0, y: 0 } }
end={ { x: 1, y: 0 } }
>
<View
style={ {
width: barWidth,
height: barHeight,
} }
/>
</LinearGradient>
<Animated.View
pointerEvents="none"
style={ [
styles[ 'hue-slider' ],
{
width: sliderSize,
height: sliderSize,
left: ( sliderSize - borderWidth ) / 2,
borderRadius: sliderSize / 2,
transform: [
{
translateX: this.sliderX,
},
],
},
] }
/>
</View>
);
}
}
Loading

0 comments on commit dc06193

Please sign in to comment.