Skip to content

Commit 0f84a81

Browse files
authored
Mobile: BottomSheet design tweaks v2 (#13855)
* Mobile BottomSheet: Added the posibility of selecting the cell separator style. * Mobile BottomSheet: Increased space between top and table * Mobile BottomSheet: Truncating long values at the middle of the string. * Fix lint issues * Fix syntax error to pass CI * Fix lint issue * Mobile Picker: Tweak Android styles. * Mobile BottomSheet: Simplified Android cell styling. * Mobile: Fix swipe to dismiss on BottomSheet * Fix lint issues * Mobile BottomSheet: Fixes sensibility of pan vs tap gestures on swipe to dismiss. * Fix lint issues * Fixed failed tests * Mobile BottomSheet cell: Removed unnecessary variable init. * Fixed set state after component is unmounted * Revert "Fixed set state after component is unmounted" This reverts commit 787df73.
1 parent c17239f commit 0f84a81

File tree

8 files changed

+165
-89
lines changed

8 files changed

+165
-89
lines changed

packages/block-library/src/image/edit.native.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,9 @@ class ImageEdit extends React.Component {
193193

194194
getMediaOptionsItems() {
195195
return [
196-
{ icon: 'wordpress-alt', value: MEDIA_UPLOAD_BOTTOM_SHEET_VALUE_CHOOSE_FROM_DEVICE, label: __( 'Choose from device' ) },
196+
{ icon: 'format-image', value: MEDIA_UPLOAD_BOTTOM_SHEET_VALUE_CHOOSE_FROM_DEVICE, label: __( 'Choose from device' ) },
197197
{ icon: 'camera', value: MEDIA_UPLOAD_BOTTOM_SHEET_VALUE_TAKE_PHOTO, label: __( 'Take a Photo' ) },
198-
{ icon: 'format-image', value: MEDIA_UPLOAD_BOTTOM_SHEET_VALUE_WORD_PRESS_LIBRARY, label: __( 'WordPress Media Library' ) },
198+
{ icon: 'wordpress-alt', value: MEDIA_UPLOAD_BOTTOM_SHEET_VALUE_WORD_PRESS_LIBRARY, label: __( 'WordPress Media Library' ) },
199199
];
200200
}
201201

@@ -273,12 +273,13 @@ class ImageEdit extends React.Component {
273273
label={ __( 'Alt Text' ) }
274274
value={ alt || '' }
275275
valuePlaceholder={ __( 'None' ) }
276+
separatorType={ 'fullWidth' }
276277
onChangeValue={ this.updateAlt }
277278
/>
278279
<BottomSheet.Cell
279280
label={ __( 'Clear All Settings' ) }
280281
labelStyle={ styles.clearSettingsButton }
281-
drawSeparator={ false }
282+
separatorType={ 'none' }
282283
onPress={ this.onClearSettings }
283284
/>
284285
</BottomSheet>

packages/editor/src/components/mobile/bottom-sheet/cell.native.js

Lines changed: 115 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -7,85 +7,131 @@ import { TouchableOpacity, Text, View, TextInput, I18nManager } from 'react-nati
77
* WordPress dependencies
88
*/
99
import { Dashicon } from '@wordpress/components';
10+
import { Component } from '@wordpress/element';
1011

1112
/**
1213
* Internal dependencies
1314
*/
1415
import styles from './styles.scss';
16+
import platformStyles from './cellStyles.scss';
1517

16-
export default function Cell( props ) {
17-
const {
18-
onPress,
19-
label,
20-
value,
21-
valuePlaceholder = '',
22-
drawSeparator = true,
23-
icon,
24-
labelStyle = {},
25-
valueStyle = {},
26-
onChangeValue,
27-
children,
28-
editable = true,
29-
...valueProps
30-
} = props;
18+
export default class Cell extends Component {
19+
constructor() {
20+
super( ...arguments );
21+
this.state = {
22+
isEditingValue: false,
23+
};
24+
}
3125

32-
const showValue = value !== undefined;
33-
const isValueEditable = editable && onChangeValue !== undefined;
34-
const defaultLabelStyle = showValue ? styles.cellLabel : styles.cellLabelCentered;
35-
const separatorStyle = showValue ? styles.cellSeparator : styles.separator;
36-
let valueTextInput;
37-
38-
const onCellPress = () => {
39-
if ( isValueEditable ) {
40-
valueTextInput.focus();
41-
} else if ( onPress !== undefined ) {
42-
onPress();
26+
componentDidUpdate() {
27+
if ( this.state.isEditingValue ) {
28+
this._valueTextInput.focus();
4329
}
44-
};
30+
}
4531

46-
const getValueComponent = () => {
47-
const styleRTL = I18nManager.isRTL && styles.cellValueRTL;
48-
const style = { ...styles.cellValue, ...valueStyle, ...styleRTL };
32+
render() {
33+
const {
34+
onPress,
35+
label,
36+
value,
37+
valuePlaceholder = '',
38+
icon,
39+
labelStyle = {},
40+
valueStyle = {},
41+
onChangeValue,
42+
children,
43+
editable = true,
44+
separatorType,
45+
style = {},
46+
...valueProps
47+
} = this.props;
4948

50-
return isValueEditable ? (
51-
<TextInput
52-
ref={ ( c ) => valueTextInput = c }
53-
numberOfLines={ 1 }
54-
style={ style }
55-
value={ value }
56-
placeholder={ valuePlaceholder }
57-
placeholderTextColor={ '#87a6bc' }
58-
onChangeText={ onChangeValue }
59-
editable={ isValueEditable }
60-
{ ...valueProps }
61-
/>
62-
) : (
63-
<Text style={ { ...styles.cellValue, ...valueStyle } }>
64-
{ value }
65-
</Text>
66-
);
67-
};
49+
const showValue = value !== undefined;
50+
const isValueEditable = editable && onChangeValue !== undefined;
51+
const defaultLabelStyle = showValue || icon !== undefined ? styles.cellLabel : styles.cellLabelCentered;
52+
const drawSeparator = ( separatorType && separatorType !== 'none' ) || separatorStyle === undefined;
53+
54+
const onCellPress = () => {
55+
if ( isValueEditable ) {
56+
this.setState( { isEditingValue: true } );
57+
} else if ( onPress !== undefined ) {
58+
onPress();
59+
}
60+
};
61+
62+
const finishEditing = () => {
63+
this.setState( { isEditingValue: false } );
64+
};
6865

69-
return (
70-
<TouchableOpacity onPress={ onCellPress } >
71-
<View style={ styles.cellContainer }>
72-
<View style={ styles.cellRowContainer }>
73-
{ icon && (
74-
<View style={ styles.cellRowContainer }>
75-
<Dashicon icon={ icon } size={ 24 } />
76-
<View style={ { width: 12 } } />
77-
</View>
78-
) }
79-
<Text numberOfLines={ 1 } style={ { ...defaultLabelStyle, ...labelStyle } }>
80-
{ label }
81-
</Text>
66+
const separatorStyle = () => {
67+
const leftMarginStyle = { ...styles.cellSeparator, ...platformStyles.separatorMarginLeft };
68+
switch ( separatorType ) {
69+
case 'leftMargin':
70+
return leftMarginStyle;
71+
case 'fullWidth':
72+
return styles.separator;
73+
case 'none':
74+
return undefined;
75+
case undefined:
76+
return showValue ? leftMarginStyle : styles.separator;
77+
}
78+
};
79+
80+
const getValueComponent = () => {
81+
const styleRTL = I18nManager.isRTL && styles.cellValueRTL;
82+
const finalStyle = { ...styles.cellValue, ...valueStyle, ...styleRTL };
83+
84+
// To be able to show the `middle` ellipsizeMode on editable cells
85+
// we show the TextInput just when the user wants to edit the value,
86+
// and the Text component to display it.
87+
// We also show the TextInput to display placeholder.
88+
const shouldShowPlaceholder = isValueEditable && value === '';
89+
return this.state.isEditingValue || shouldShowPlaceholder ? (
90+
<TextInput
91+
ref={ ( c ) => this._valueTextInput = c }
92+
numberOfLines={ 1 }
93+
style={ finalStyle }
94+
value={ value }
95+
placeholder={ valuePlaceholder }
96+
placeholderTextColor={ '#87a6bc' }
97+
onChangeText={ onChangeValue }
98+
editable={ isValueEditable }
99+
pointerEvents={ this.state.isEditingValue ? 'auto' : 'none' }
100+
onBlur={ finishEditing }
101+
{ ...valueProps }
102+
/>
103+
) : (
104+
<Text
105+
style={ { ...styles.cellValue, ...valueStyle } }
106+
numberOfLines={ 1 }
107+
ellipsizeMode={ 'middle' }
108+
>
109+
{ value }
110+
</Text>
111+
);
112+
};
113+
114+
return (
115+
<TouchableOpacity onPress={ onCellPress } style={ { ...styles.clipToBounds, ...style } } >
116+
<View style={ styles.cellContainer }>
117+
<View style={ styles.cellRowContainer }>
118+
{ icon && (
119+
<View style={ styles.cellRowContainer }>
120+
<Dashicon icon={ icon } size={ 24 } />
121+
<View style={ platformStyles.labelIconSeparator } />
122+
</View>
123+
) }
124+
<Text numberOfLines={ 1 } style={ { ...defaultLabelStyle, ...labelStyle } }>
125+
{ label }
126+
</Text>
127+
</View>
128+
{ showValue && getValueComponent() }
129+
{ children }
82130
</View>
83-
{ showValue && getValueComponent() }
84-
{ children }
85-
</View>
86-
{ drawSeparator && (
87-
<View style={ separatorStyle } />
88-
) }
89-
</TouchableOpacity>
90-
);
131+
{ drawSeparator && (
132+
<View style={ separatorStyle() } />
133+
) }
134+
</TouchableOpacity>
135+
);
136+
}
91137
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.labelIconSeparator {
2+
width: 32px;
3+
}
4+
5+
.separatorMarginLeft {
6+
margin-left: 56px;
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.labelIconSeparator {
2+
width: 12px;
3+
}
4+
5+
.separatorMarginLeft {
6+
margin-left: 36px;
7+
}

packages/editor/src/components/mobile/bottom-sheet/index.native.js

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* External dependencies
33
*/
4-
import { Text, View, KeyboardAvoidingView, Platform } from 'react-native';
4+
import { Text, View, KeyboardAvoidingView, Platform, PanResponder } from 'react-native';
55
import Modal from 'react-native-modal';
66
import SafeArea from 'react-native-safe-area';
77

@@ -50,7 +50,18 @@ class BottomSheet extends Component {
5050
}
5151

5252
render() {
53-
const { title = '', isVisible, leftButton, rightButton, hideHeader } = this.props;
53+
const { title = '', isVisible, leftButton, rightButton, hideHeader, style = {} } = this.props;
54+
55+
const panResponder = PanResponder.create( {
56+
onMoveShouldSetPanResponder: ( evt, gestureState ) => {
57+
// Activates swipe down over child Touchables if the swipe is long enough.
58+
// With this we can adjust sensibility on the swipe vs tap gestures.
59+
if ( gestureState.dy > 3 ) {
60+
gestureState.dy = 0;
61+
return true;
62+
}
63+
},
64+
} );
5465

5566
return (
5667
<Modal
@@ -65,14 +76,18 @@ class BottomSheet extends Component {
6576
onBackButtonPress={ this.props.onClose }
6677
onSwipe={ this.props.onClose }
6778
swipeDirection="down"
79+
onMoveShouldSetResponder={ panResponder.panHandlers.onMoveShouldSetResponder }
80+
onMoveShouldSetResponderCapture={ panResponder.panHandlers.onMoveShouldSetResponderCapture }
6881
>
6982
<KeyboardAvoidingView
7083
behavior={ Platform.OS === 'ios' && 'padding' }
71-
style={ { ...styles.content, borderColor: 'rgba(0, 0, 0, 0.1)' } }
84+
style={ { ...styles.content, borderColor: 'rgba(0, 0, 0, 0.1)', ...style } }
7285
keyboardVerticalOffset={ -this.state.safeAreaBottomInset }
7386
>
7487
<View style={ styles.dragIndicator } />
75-
{ hideHeader || (
88+
{ hideHeader ? (
89+
<View style={ styles.emptyHeaderSpace } />
90+
) : (
7691
<View>
7792
<View style={ styles.head }>
7893
<View style={ { flex: 1 } }>

packages/editor/src/components/mobile/bottom-sheet/styles.native.scss

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
.dragIndicator {
99
background-color: $light-gray-400;
1010
height: 4px;
11-
width: 10%;
11+
width: 36px;
1212
margin: auto;
1313
border-radius: 2px;
1414
}
@@ -19,6 +19,10 @@
1919
width: 100%;
2020
}
2121

22+
.emptyHeaderSpace {
23+
height: 14;
24+
}
25+
2226
.content {
2327
padding: 6px 16px 0 16px;
2428
background-color: $white;
@@ -33,6 +37,7 @@
3337
justify-content: space-between;
3438
align-items: center;
3539
align-content: center;
40+
min-height: 48;
3641
}
3742

3843
.title {
@@ -63,11 +68,14 @@
6368
align-items: center;
6469
}
6570

71+
.clipToBounds {
72+
overflow: hidden;
73+
}
74+
6675
.cellSeparator {
6776
background-color: $light-gray-400;
6877
height: 1px;
6978
width: 100%;
70-
margin-left: 36px;
7179
}
7280

7381
.cellRowContainer {

packages/editor/src/components/mobile/picker/index.android.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export default class Picker extends Component {
4040
<BottomSheet
4141
isVisible={ this.state.isVisible }
4242
onClose={ this.onClose }
43+
style={ { paddingBottom: 20 } }
4344
hideHeader
4445
>
4546
<View>
@@ -48,13 +49,14 @@ export default class Picker extends Component {
4849
icon={ option.icon }
4950
key={ index }
5051
label={ option.label }
52+
separatorType={ 'none' }
5153
onPress={ () => this.onCellPress( option.value ) }
5254
/>
5355
) }
5456
{ ! this.props.hideCancelButton && <BottomSheet.Cell
5557
label={ __( 'Cancel' ) }
5658
onPress={ this.onClose }
57-
drawSeparator={ false }
59+
separatorType={ 'none' }
5860
/> }
5961
</View>
6062
</BottomSheet>

packages/editor/src/components/mobile/picker/styles.android.scss

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)