@fto-consult/expo-ui 8.80.0 → 8.81.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,13 +8,18 @@ import {
8
8
  TouchableWithoutFeedback,
9
9
  I18nManager,
10
10
  } from 'react-native';
11
+ import { Portal } from 'react-native-paper';
11
12
  import PropTypes from "prop-types";
12
13
  import View from "$ecomponents/View";
13
- import {defaultStr} from "$cutils";
14
+ import {defaultStr,defaultObj} from "$cutils";
14
15
  import theme,{Colors} from "$theme";
15
16
  import {isMobileMedia} from "$cplatform/dimensions";
16
17
  import Preloader from "$epreloader";
17
18
  import {Elevations} from "$ecomponents/Surface";
19
+ import {HStack} from "$ecomponents/Stack";
20
+ import Divider from "$ecomponents/Divider";
21
+ import Label from "$ecomponents/Label";
22
+ import Icon from "$ecomponents/Icon";
18
23
 
19
24
  const MIN_SWIPE_DISTANCE = 3;
20
25
  const DEVICE_WIDTH = Math.max(Dimensions.get('window').width,280);
@@ -37,6 +42,7 @@ export default class DrawerLayout extends React.PureComponent {
37
42
 
38
43
  constructor(props) {
39
44
  super(props);
45
+ const isPortal = !! props.isPortal;
40
46
  this._panResponder = PanResponder.create({
41
47
  onMoveShouldSetPanResponder: this._shouldSetPanResponder,
42
48
  onPanResponderGrant: this._panResponderGrant,
@@ -45,20 +51,31 @@ export default class DrawerLayout extends React.PureComponent {
45
51
  onPanResponderRelease: this._panResponderRelease,
46
52
  onPanResponderTerminate: () => {},
47
53
  });
48
- const drawerShown = props.permanent? true : false;
54
+ const drawerShown = !isPortal && props.permanent? true : false;
49
55
  this.state = {
50
56
  accessibilityViewIsModal: false,
51
57
  drawerShown,
58
+ isPortal,
59
+ portalProps : {},
52
60
  openValue: new Animated.Value(drawerShown?1:0),
53
61
  };
54
62
  }
55
-
63
+ isPortal(){
64
+ return !!this.state.isPortal;
65
+ }
56
66
  getDrawerPosition() {
57
- const { drawerPosition } = this.props;
58
67
  const rtl = I18nManager.isRTL;
68
+ const p = this.isPortal()? this.state.portalProps : this.props;
69
+ let position = defaultStr(p?.drawerPosition,p?.position,this.props.drawerPosition).toLowerCase();
70
+ if(position !=='left' && position !=='right'){
71
+ position = 'left';
72
+ }
59
73
  return rtl
60
- ? drawerPosition === 'left' ? 'right' : 'left' // invert it
61
- : drawerPosition;
74
+ ? position === 'left' ? 'right' : 'left' // invert it
75
+ : position;
76
+ }
77
+ isPositionRight(){
78
+ return this.getDrawerPosition() === 'right';
62
79
  }
63
80
  isOpen(){
64
81
  return this.state.drawerShown;
@@ -106,34 +123,67 @@ export default class DrawerLayout extends React.PureComponent {
106
123
  getBackgroundColor(){
107
124
  return Colors.isValid(this.props.drawerBackgroundColor)? this.props.backgroundColor : theme.colors.surface;
108
125
  }
126
+ getPortalTestID(){
127
+ return defaultStr(this.state.portalProps.testID,"RN_DrawerLayoutPortal");
128
+ }
129
+ renderPortalTitle(){
130
+ if(React.isValidElement(this.state.portalProps?.title)) return this.state.portalProps?.title;
131
+ const title = this.state.portalProps?.title;
132
+ const isPositionRight = this.isPositionRight();
133
+ if(typeof title == 'string' && title || typeof title =="number"){
134
+ const titleProps = defaultObj(this.state.portalProps.titleProps);
135
+ const testID = this.getPortalTestID();
136
+ const icon = <Icon
137
+ onPress = {this.closeDrawer.bind(this)}
138
+ icon = {this.state.portalProps?.closeIcon || !this.isPositionRight() == 'left'? 'chevron-left' : 'chevron-right'}
139
+ {...defaultObj(this.state.portalProps.closeIconProps)}
140
+ />;
141
+ return <>
142
+ <HStack style={[styles.portalTitle]} testID={testID+"_TitleContainer"}>
143
+ {isPositionRight? icon : null}
144
+ <Label {...titleProps} style={[styles.portalTitleText]} testID={testID+"_DrawerLayoutTitle"}>
145
+ {title}
146
+ </Label>
147
+ {!isPositionRight ? icon : null}
148
+ </HStack>
149
+ <Divider/>
150
+ </>;
151
+ }
152
+ return null;
153
+ }
154
+ renderPortalChildren(){
155
+ return <>
156
+ {this.renderPortalTitle()}
157
+ {React.isValidElement(this.state.portalProps?.children) ? this.state.portalProps?.children : null}
158
+ </>
159
+ }
109
160
  renderContent({testID}){
110
161
  return <View style={[styles.main]} testID={testID+"_DrawerLayoutContent"}>
111
162
  {this.props.children}
112
163
  </View>;
113
164
  }
165
+ getDrawerWidth() {
166
+ return defaultNumber(this.isPortal()? this.state.portalProps?.drawerWidth : 0,this.props.drawerWidth);
167
+ }
114
168
  render() {
115
169
  const { accessibilityViewIsModal, drawerShown, openValue } = this.state;
116
170
  const elevation = typeof this.props.elevation =='number'? this.props.elevation : 5;
117
171
  const elev = this.props.permanent && Elevations[elevation]? Elevations[elevation] : null;
118
172
  const testID = defaultStr(this.props.testID,"RN_DrawerLayoutComponent")
119
- let {
120
- drawerBackgroundColor,
121
- drawerWidth,
122
- drawerPosition,
123
- permanent,
124
- position,
173
+ let { permanent,
125
174
  navigationViewRef,
126
175
  } = this.props;
127
-
176
+ let drawerWidth = this.getDrawerWidth();
128
177
  /**
129
178
  * We need to use the "original" drawer position here
130
179
  * as RTL turns position left and right on its own
131
180
  **/
181
+ const posRight = this.isPositionRight();
132
182
  const dynamicDrawerStyles = {
133
183
  backgroundColor: this.getBackgroundColor(),
134
184
  width: drawerWidth,
135
- left: drawerPosition === 'left' ? 0 : null,
136
- right: drawerPosition === 'right' ? 0 : null,
185
+ left: !posRight ? 0 : null,
186
+ right: posRight? 0 : null,
137
187
  };
138
188
 
139
189
  /* Drawer styles */
@@ -162,47 +212,48 @@ export default class DrawerLayout extends React.PureComponent {
162
212
  });
163
213
  const animatedOverlayStyles = { opacity: overlayOpacity };
164
214
  const pointerEvents = drawerShown || permanent ? 'auto' : 'none';
165
- position = defaultStr(position).toLowerCase();
166
- if(position !=='left' && position !=='right'){
167
- position = 'left';
168
- }
169
- const posRight = position =="right"? true : false;
215
+
216
+
170
217
  if(permanent){
171
218
  dynamicDrawerStyles.position = "relative";
172
219
  }
220
+ const Wrapper = this.isPortal()? Portal : React.Fragment;
221
+ const canRender = this.isPortal()? this.state.drawerShown : true;
173
222
  return (
174
- <View
175
- testID = {testID}
176
- style={{ flex: 1, backgroundColor: 'transparent',flexDirection:permanent?'row':'column'}}
177
- {...this._panResponder.panHandlers}
178
- >
179
- {!permanent && <TouchableWithoutFeedback
180
- style={{pointerEvents}}
181
- testID = {testID+"_TouchableWithoutFeedBack"}
182
- onPress={this._onOverlayClick}
223
+ <Wrapper>
224
+ <View
225
+ testID = {testID}
226
+ style={[{ flex: canRender && 1 || 0, backgroundColor: 'transparent',flexDirection:permanent?'row':'column'},canRender?styles.portalVisibleContainer:styles.portalNotVisibleContainer]}
227
+ {...this._panResponder.panHandlers}
183
228
  >
229
+ {!permanent && <TouchableWithoutFeedback
230
+ style={{pointerEvents}}
231
+ testID = {testID+"_TouchableWithoutFeedBack"}
232
+ onPress={this._onOverlayClick}
233
+ >
234
+ <Animated.View
235
+ testID={testID+"_Backdrow"}
236
+ ref = {this._backdropRef}
237
+ style={[styles.overlay,{backgroundColor:theme.colors.backdrop},{pointerEvents}, animatedOverlayStyles]}
238
+ />
239
+ </TouchableWithoutFeedback>}
240
+ {posRight && this.renderContent({testID})}
184
241
  <Animated.View
185
- testID={testID+"_Backdrow"}
186
- ref = {this._backdropRef}
187
- style={[styles.overlay,{backgroundColor:theme.colors.backdrop},{pointerEvents}, animatedOverlayStyles]}
188
- />
189
- </TouchableWithoutFeedback>}
190
- {posRight && this.renderContent({testID})}
191
- <Animated.View
192
- testID={testID+"_NavigationViewContainer"}
193
- ref={React.mergeRefs(navigationViewRef,this._navigationViewRef)}
194
- accessibilityViewIsModal={accessibilityViewIsModal}
195
- style={[
196
- styles.drawer,
197
- dynamicDrawerStyles,
198
- elev,
199
- animatedDrawerStyles,
200
- ]}
201
- >
202
- {this.props.renderNavigationView()}
203
- </Animated.View>
204
- {!posRight && this.renderContent({testID})}
205
- </View>
242
+ testID={testID+"_NavigationViewContainer"}
243
+ ref={React.mergeRefs(navigationViewRef,this._navigationViewRef)}
244
+ accessibilityViewIsModal={accessibilityViewIsModal}
245
+ style={[
246
+ styles.drawer,
247
+ dynamicDrawerStyles,
248
+ elev,
249
+ animatedDrawerStyles,
250
+ ]}
251
+ >
252
+ {this.isPortal()? this.renderPortalChildren() : this.props.renderNavigationView()}
253
+ </Animated.View>
254
+ {!posRight && this.renderContent({testID})}
255
+ </View>
256
+ </Wrapper>
206
257
  );
207
258
  }
208
259
 
@@ -219,27 +270,35 @@ export default class DrawerLayout extends React.PureComponent {
219
270
  }
220
271
  };
221
272
 
222
- openDrawer = (options = {}) => {
223
- this._emitStateChanged(SETTLING);
224
- Animated.spring(this.state.openValue, {
225
- toValue: 1,
226
- bounciness: 0,
227
- restSpeedThreshold: 0.1,
228
- useNativeDriver: this.props.useNativeAnimations,
229
- ...options,
230
- }).start(() => {
231
- if (this.props.onDrawerOpen) {
232
- this.props.onDrawerOpen();
233
- }
234
- this._emitStateChanged(IDLE);
235
- });
273
+ openDrawer = (options) => {
274
+ options = Object.assign({}, options);
275
+ const cb = ()=>{
276
+ this._emitStateChanged(SETTLING);
277
+ Animated.spring(this.state.openValue, {
278
+ toValue: 1,
279
+ bounciness: 0,
280
+ restSpeedThreshold: 0.1,
281
+ useNativeDriver: this.props.useNativeAnimations,
282
+ ...options,
283
+ }).start(() => {
284
+ if (this.props.onDrawerOpen) {
285
+ this.props.onDrawerOpen();
286
+ }
287
+ this._emitStateChanged(IDLE);
288
+ });
289
+ }
290
+ if(this.isPortal()){
291
+ this.setState({portalProps:options},cb)
292
+ } else {
293
+ cb();
294
+ }
236
295
  };
237
296
 
238
297
  closeDrawer = (options = {},showPreloader) => {
239
298
  if(typeof options ==='boolean'){
240
299
  showPreloader = options;
241
300
  }
242
- options = typeof options =='object' && options ? options : {};
301
+ options = Object.assign({}, options);
243
302
  if(typeof showPreloader !== 'boolean'){
244
303
  showPreloader = options.showPreloader || options.preloader;
245
304
  }
@@ -291,10 +350,9 @@ export default class DrawerLayout extends React.PureComponent {
291
350
  if (this._isLockedClosed() || this._isLockedOpen()) {
292
351
  return false;
293
352
  }
294
-
295
353
  if (this.getDrawerPosition() === 'left') {
296
354
  const overlayArea = DEVICE_WIDTH -
297
- (DEVICE_WIDTH - this.props.drawerWidth);
355
+ (DEVICE_WIDTH - this.getDrawerWidth());
298
356
 
299
357
  if (this._lastOpenValue === 1) {
300
358
  if (
@@ -314,8 +372,7 @@ export default class DrawerLayout extends React.PureComponent {
314
372
  return false;
315
373
  }
316
374
  } else {
317
- const overlayArea = DEVICE_WIDTH - this.props.drawerWidth;
318
-
375
+ const overlayArea = DEVICE_WIDTH - this.getDrawerWidth();
319
376
  if (this._lastOpenValue === 1) {
320
377
  if (
321
378
  (dx > 0 && Math.abs(dx) > Math.abs(dy) * 3) ||
@@ -417,7 +474,7 @@ export default class DrawerLayout extends React.PureComponent {
417
474
  };
418
475
 
419
476
  _getOpenValueForX(x) {
420
- const { drawerWidth } = this.props;
477
+ const drawerWidth = this.getDrawerWidth();
421
478
 
422
479
  if (this.getDrawerPosition() === 'left') {
423
480
  return x / drawerWidth;
@@ -449,13 +506,25 @@ const styles = StyleSheet.create({
449
506
  right: 0,
450
507
  zIndex: 1000,
451
508
  },
509
+ portalVisibleContainer : {
510
+ ...StyleSheet.absoluteFill,
511
+ },
512
+ portalNotVisibleContainer : {
513
+ opacity : 0,
514
+ },
515
+ portalTitle : {
516
+ justifyContent : 'space-between',
517
+ alignItems : 'center',
518
+ paddingHorizontal : 10,
519
+ }
452
520
  });
453
-
521
+ const posPropType = PropTypes.oneOf(['left', 'right']);
454
522
  DrawerLayout.propTypes = {
455
- children: PropTypes.any.isRequired,
523
+ isPortal : PropTypes.bool,
524
+ children: PropTypes.any,
456
525
  drawerBackgroundColor : PropTypes.string,
457
526
  drawerLockMode: PropTypes.oneOf(['unlocked','locked-closed', 'locked-open']),
458
- drawerPosition: PropTypes.oneOf(['left', 'right']),
527
+ drawerPosition: posPropType,
459
528
  drawerWidth: PropTypes.number,
460
529
  keyboardDismissMode: PropTypes.oneOf(['none' , 'on-drag']),
461
530
  onDrawerClose: PropTypes.func,
@@ -465,6 +534,36 @@ DrawerLayout.propTypes = {
465
534
  renderNavigationView: PropTypes.any,
466
535
  statusBarBackgroundColor : PropTypes.string,
467
536
  useNativeAnimations: PropTypes.bool,
537
+ /****
538
+ * les props à passer à la fonction open du drawer, lorsqu'il s'agit du portal
539
+ *
540
+ */
541
+ portalProps : PropTypes.shape({
542
+ title : PropTypes.oneOfType([
543
+ PropTypes.string,//si title est une chaine de caractère alors il sera rendu avec le bouton close permettant de fermer le Drawer
544
+ PropTypes.node,
545
+ PropTypes.element,
546
+ PropTypes.elementType,
547
+ ]),
548
+ titleProps : PropTypes.shape({
549
+ ...defaultObj(Label.propTypes),
550
+ }),
551
+ closeIconProps : PropTypes.shape({
552
+ ...defaultObj(Icon.propTypes),
553
+ }),
554
+ icon : PropTypes.oneOfType([
555
+ PropTypes.string,
556
+ PropTypes.node,
557
+ PropTypes.element,
558
+ ]),
559
+ children : PropTypes.oneOfType([
560
+ PropTypes.node,
561
+ PropTypes.element,
562
+ ]),
563
+ drawerPosition : posPropType,
564
+ position : posPropType,
565
+ drawerWidth : PropTypes.number,
566
+ }),
468
567
  }
469
568
 
470
569
  DrawerLayout.defaultProps = {
@@ -71,7 +71,7 @@ const DrawerNavigationViewComponent = React.forwardRef((props,ref)=>{
71
71
  <DrawerItems
72
72
  drawerRef = {drawerRef}
73
73
  {...defaultObj(drawerItemsProps)}
74
- items = {drawerItems}
74
+ items = {drawerItems || []}
75
75
  drawerType = {drawerType}
76
76
  isDrawerOpen = {drawerRef.current?.isOpen() || false}
77
77
  minimized = {isMinimized}
@@ -1,6 +1,73 @@
1
+ import Drawer from "./Drawer";
1
2
  import React from "$react";
2
- export const DrawerContext = React.createContext(null);
3
+ import {View,StyleSheet} from "react-native";
4
+ let drawerRef = null;
3
5
 
4
- export const useDrawer = ()=>{
5
- return React.useContext(DrawerContext) || {hasContext:x=>false};
6
- }
6
+ export const createProviderRef = (cb)=>{
7
+ const ref = React.useRef(null);
8
+ React.useEffect(()=>{
9
+ if(typeof cb =='function'){
10
+ cb(ref.current);
11
+ } else if(cb !== false) {
12
+ drawerRef = ref.current;
13
+ }
14
+ },[ref.current])
15
+ return ref;
16
+ };
17
+
18
+ export const getProviderRef = ()=> drawerRef;
19
+
20
+ export const open = (props,innerProviderRef)=>{
21
+ innerProviderRef = innerProviderRef || getProviderRef();
22
+ if(!innerProviderRef) return false;
23
+ if(innerProviderRef.current && innerProviderRef.current.open){
24
+ innerProviderRef = innerProviderRef.current;
25
+ }
26
+ if(innerProviderRef && typeof innerProviderRef.open =='function') {
27
+ return innerProviderRef.open(props);
28
+ }
29
+ return false;
30
+ }
31
+
32
+ export const close = (props,innerProviderRef)=>{
33
+ innerProviderRef = innerProviderRef || getProviderRef();
34
+ if(!innerProviderRef) return false;
35
+ if(innerProviderRef.current && innerProviderRef.current.open){
36
+ innerProviderRef = innerProviderRef.current;
37
+ }
38
+ if(innerProviderRef && typeof innerProviderRef.close =='function') return innerProviderRef.close(props);
39
+ return false;
40
+ }
41
+
42
+ const Provider = React.forwardRef((props,innerRef)=>{
43
+ return <Drawer
44
+ ref = {innerRef || createProviderRef()}
45
+ position="right"
46
+ sessionName = {sessionName}
47
+ {...props}
48
+ permanent={false}
49
+ isPortal
50
+ />
51
+ });
52
+
53
+ const sessionName = "custom-drawer-provider-right";
54
+
55
+ Provider.displayName = "DrawerProviderComponent";
56
+
57
+ export default Provider;
58
+
59
+ Provider.open = open;
60
+
61
+ Provider.close = close;
62
+
63
+ Provider.sessionName = sessionName;
64
+
65
+ const styles = StyleSheet.create({
66
+ visible : {
67
+ opacity : 1,
68
+ ...StyleSheet.absoluteFill,
69
+ },
70
+ notVisible : {
71
+ opacity : 0,
72
+ }
73
+ })
@@ -0,0 +1,6 @@
1
+ import React from "$react";
2
+ export const DrawerContext = React.createContext(null);
3
+
4
+ export const useDrawer = ()=>{
5
+ return React.useContext(DrawerContext) || {hasContext:x=>false};
6
+ }