@fto-consult/expo-ui 2.5.0 → 2.5.2

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fto-consult/expo-ui",
3
- "version": "2.5.0",
3
+ "version": "2.5.2",
4
4
  "description": "Bibliothèque de composants UI Expo,react-native",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -8,7 +8,7 @@ import PropTypes from "prop-types";
8
8
  import {getContentHeight} from "./utils";
9
9
 
10
10
  const BottomSheetMenuComponent = React.forwardRef((props,ref)=>{
11
- let {anchor,screenIndent,height:customHeight,bindResizeEvent,onDismiss,testID,visible:customVisible,controlled,mobile,animateOnClose,renderMenuContent,sheet,children,...rest} = props;
11
+ let {anchor,anchorProps,screenIndent,height:customHeight,bindResizeEvent,onDismiss,testID,visible:customVisible,controlled,mobile,animateOnClose,renderMenuContent,sheet,children,...rest} = props;
12
12
  rest = defaultObj(rest);
13
13
  const isControlled = controlled ? true : false;
14
14
  const visibleRef = React.useRef(null);
@@ -44,12 +44,13 @@ const BottomSheetMenuComponent = React.forwardRef((props,ref)=>{
44
44
  setState({...state,visible:false})
45
45
  }
46
46
  const Component = isMob ? BottomSheet : Menu;
47
+ anchorProps = defaultObj(anchorProps);
47
48
  if(isMob){
48
49
  if(typeof anchor ==='function'){
49
50
  anchor = anchor(!isControlled?{onPress:open}:{});
50
51
  }
51
52
  if(React.isValidElement(anchor)){
52
- anchor = <View testID={testID+"_Anchor"} ref={anchorRef} collapsable={false}>{anchor}</View>
53
+ anchor = <View testID={testID+"_Anchor"} ref={anchorRef} {...anchorProps} collapsable={false}>{anchor}</View>
53
54
  }
54
55
  if(renderMenuContent !== false){
55
56
  children = renderItems({testID:testID+".Items",...props,closeMenu:close,openMenu:open,isBottomSheetItem:true});
@@ -101,6 +102,7 @@ const BottomSheetMenuComponent = React.forwardRef((props,ref)=>{
101
102
  height = {controlled?customHeight:height}
102
103
  ref = {React.useMergeRefs(innerRef,ref)}
103
104
  anchor = {anchor}
105
+ anchorProps = {anchorProps}
104
106
  children = {visible && (React.isValidElement(children) || Array.isArray(children))?children:undefined}
105
107
  />
106
108
  </>
@@ -385,7 +385,7 @@ const DatagridFactory = (Factory)=>{
385
385
  if(toggleFilters === false){
386
386
  filters = false;
387
387
  }
388
- let {showFilters,showFooter} = this.state;
388
+ let {showFilters,showFooters} = this.state;
389
389
  let max = this.getMaxSelectableRows();
390
390
  let restItems = [];
391
391
 
@@ -499,9 +499,9 @@ const DatagridFactory = (Factory)=>{
499
499
  closeOnPress : false,
500
500
  } : null,
501
501
  hasFooterFields ? {
502
- onPress : ()=>{showFooter?this.hideFooter():this.showFooter()}
503
- ,icon : showFooter?'view-column':'view-module'
504
- ,text : (showFooter?'Masquer/Ligne des totaux':'Afficher/Ligne des totaux')
502
+ onPress : ()=>{showFooters?this.hideFooter():this.showFooters()}
503
+ ,icon : showFooters?'view-column':'view-module'
504
+ ,text : (showFooters?'Masquer/Ligne des totaux':'Afficher/Ligne des totaux')
505
505
  }:null,
506
506
  ...restItems,
507
507
  this.canScrollTo() && {
@@ -557,7 +557,7 @@ const DatagridFactory = (Factory)=>{
557
557
  />
558
558
  {datagridHeader}
559
559
  {_progressBar}
560
- {showFooter ? (
560
+ {showFooters ? (
561
561
  <View testID={testID+"_FooterContainer"} pointerEvents={pointerEvents} style={[{justifyContent:'center'}]}>
562
562
  <View testID={testID+"_FooterContentContainer"} style={[styles.footersContainer]}>
563
563
  <ScrollView testID={testID+"_FooterScrollView"} horizontal contentContainerStyle={[styles.contentContainerStyle]}>
@@ -567,6 +567,7 @@ const DatagridFactory = (Factory)=>{
567
567
  key = {field}
568
568
  testID={testID+"_FooterItem_"+field}
569
569
  {...footer}
570
+ anchorProps = {{style:[theme.styles.ph1,theme.styles.mh05]}}
570
571
  />
571
572
  })}
572
573
  </View>
@@ -71,13 +71,13 @@ export default class CommonDatagridComponent extends AppComponent {
71
71
  });
72
72
  selectedRows = sRows;
73
73
  let sData = this.getSessionData()
74
- sData.showFooter = defaultVal(sData.showFooter,true);
74
+ sData.showFooters = defaultVal(sData.showFooters,true);
75
75
  sData.fixedTable = defaultBool(sData.fixedTable,false);
76
76
  extendObj(this.state, {
77
77
  data,
78
78
  sort :defaultObj(props.sort),
79
79
  showFilters : defaultBool(props.showFilters,(sData.showFilter? true : this.isPivotDatagrid())),
80
- showFooter : defaultBool(props.showFooter,(sData.showFooter? true : false)),
80
+ showFooters : defaultBool(props.showFooters,(sData.showFooters? true : false)),
81
81
  fixedTable : sData.fixedTable
82
82
  });
83
83
  Object.defineProperties(this,{
@@ -784,16 +784,16 @@ export default class CommonDatagridComponent extends AppComponent {
784
784
  }
785
785
 
786
786
 
787
- showFooter(){
787
+ showFooters(){
788
788
  if(!this._isMounted()) {
789
789
  this.isUpdating = false;
790
790
  return;
791
791
  }
792
792
  if(this.isUpdating) return false;
793
793
  this.isUpdating = true;
794
- this.setState( {showFooter:true},()=>{
794
+ this.setState( {showFooters:true},()=>{
795
795
  this.isUpdating = false;
796
- this.setSessionData({showFooter:true})
796
+ this.setSessionData({showFooters:true})
797
797
  })
798
798
  }
799
799
  hideFooter (){
@@ -802,9 +802,9 @@ export default class CommonDatagridComponent extends AppComponent {
802
802
  return;
803
803
  }
804
804
  if(this.isUpdating) return false;
805
- this.setState({showFooter:false},()=>{
805
+ this.setState({showFooters:false},()=>{
806
806
  this.isUpdating = false;
807
- this.setSessionData({showFooter:false});
807
+ this.setSessionData({showFooters:false});
808
808
  })
809
809
  }
810
810
 
@@ -899,14 +899,18 @@ export default class CommonDatagridComponent extends AppComponent {
899
899
  let colFilter = defaultVal(restCol.filter,true);
900
900
  field = header.field = defaultStr(header.field,field,headerIndex);
901
901
  delete restCol.filter;
902
+
903
+ const type = defaultStr(header.type).toLowerCase();
904
+ sortType = defaultStr(sortType,type).toLowerCase();
902
905
  width = defaultDecimal(width);
903
906
  if(width <COLUMN_WIDTH/2){
904
907
  width = COLUMN_WIDTH;
905
908
  }
906
- const type = defaultStr(header.type).toLowerCase();
907
- sortType = defaultStr(sortType,type).toLowerCase();
908
909
  if(type.contains("date")|| type.contains("time")){
909
- width = Math.max(width,DATE_COLUMN_WIDTH);
910
+ const mWidth = type.toLowerCase().contains('datetime')? (DATE_COLUMN_WIDTH+30) : DATE_COLUMN_WIDTH;
911
+ width = Math.max(width,mWidth);
912
+ } else if((type.contains("number") || type.contains("decimal") && this.props.format)){
913
+ width = Math.max(width,DATE_COLUMN_WIDTH-30);
910
914
  }
911
915
  totalWidths +=width;
912
916
  widths[header.field] = width;
@@ -953,6 +957,17 @@ export default class CommonDatagridComponent extends AppComponent {
953
957
  delete restCol.sortable;
954
958
  filterProps = {
955
959
  ...restCol,
960
+ columnIndex,
961
+ visibleColumnIndex,
962
+ sortable:isColumnSortable,
963
+ sorted:isColumnSorted,
964
+ sortedColumn :sortedProps,///les props de la columns triée
965
+ sortedProps,
966
+ width,
967
+ columnField : field,
968
+ columnDef : header,
969
+ index : headerIndex,
970
+ visible,
956
971
  key : header.field,
957
972
  label : defaultStr(header.label,header.text),
958
973
  orOperator : filterOrOperator,
@@ -1710,7 +1725,7 @@ CommonDatagridComponent.propTypes = {
1710
1725
  /*** affiche ou masque les filtres */
1711
1726
  showFilters : PropTypes.bool,
1712
1727
  /*** si le pied de page sera affiché */
1713
- showFooter : PropTypes.bool,
1728
+ showFooters : PropTypes.bool,
1714
1729
  /*** les donnnées peuvent être soient retournées par une fonction, soit par un tableau soit une promesse */
1715
1730
  data : PropTypes.oneOfType([PropTypes.array, PropTypes.func,PropTypes.object]),//.isRequired,
1716
1731
  columns:PropTypes.oneOfType([PropTypes.array,PropTypes.object]),//.isRequired,
@@ -3,7 +3,7 @@ import Menu from "$ecomponents/BottomSheet/Menu";
3
3
  import View from "$ecomponents/View";
4
4
  import {Pressable,StyleSheet} from "react-native";
5
5
  import Label from "$ecomponents/Label";
6
- import {defaultVal} from "$utils";
6
+ import {defaultVal,defaultObj} from "$utils";
7
7
  import React from "$react";
8
8
  import theme from "$theme"
9
9
 
@@ -18,7 +18,9 @@ const formatValue = ({value,format,method})=>{
18
18
  return (format === 'money' && method != 'count')? value.formatMoney():value.formatNumber();
19
19
  }
20
20
  export default function DGGridFooterValue (props){
21
- let {label,text,displayLabel,style,format} = props;
21
+ let {label,text,displayLabel,style,format,testID,anchorProps} = props;
22
+ anchorProps = defaultObj(anchorProps);
23
+ testID = defaultStr(testID,"RN_DatagridFooterComponent");
22
24
  label = defaultVal(label,text);
23
25
  const defLabel = label;
24
26
  if(displayLabel !== false){
@@ -50,19 +52,19 @@ export default function DGGridFooterValue (props){
50
52
  }
51
53
  }
52
54
  return <Menu
55
+ testID = {testID+"_Menu"}
53
56
  items = {menuItems}
54
- style = {{minWidth:220}}
55
57
  title = {'Totaux de la colonne '+(defLabel?("[ "+defLabel+"]"):'')}
56
58
  animateOnClose
57
59
  anchor = {(p)=>{
58
- return <Pressable {...p} style={[styles.anchor,style,label?styles.row:null]} title={title}>
60
+ return <Pressable {...anchorProps} {...p} testID={testID} style={[styles.anchor,anchorProps.style,label?styles.row:null]} title={title}>
59
61
  {label ?
60
62
  <>
61
- <View><Label style={[styles.label]}>{label}</Label></View>
62
- <View><Label style = {styles.label}> : </Label></View>
63
+ <View testID={testID+"_Label"}><Label style={[styles.label]}>{label}</Label></View>
64
+ <View testID={testID+"_LabelPoint"}><Label style = {styles.label}> : </Label></View>
63
65
  </>
64
66
  : null}
65
- <Label primary style={[styles.value]}>
67
+ <Label testID={testID+"_LabelContent"} primary style={[styles.value]}>
66
68
  {formatValue({value:defaultDecimal(props[active]),method:active,format})}
67
69
  </Label>
68
70
  </Pressable>
@@ -75,11 +77,7 @@ const styles = StyleSheet.create({
75
77
  flexDirection : 'row',
76
78
  alignItems : 'center',
77
79
  },
78
- anchor : {
79
- paddingVertical : 10,
80
- marginHorizontal : 10,
81
- paddingHorizontal : 5,
82
- },
80
+ anchor : {},
83
81
  value : {
84
82
  fontWeight : 'bold'
85
83
  }
@@ -13,6 +13,7 @@ import React from "$react";
13
13
  import {Menu as BottomSheetMenu} from "$ecomponents/BottomSheet"
14
14
  import RenderType from "../RenderType";
15
15
  import Footer from "../Footer/Footer";
16
+ import theme from "$theme";
16
17
  import Table from "$ecomponents/Table";
17
18
 
18
19
 
@@ -62,41 +63,23 @@ const DatagridFactory = (Factory)=>{
62
63
  }
63
64
  return null;
64
65
  }
65
- renderHeaderCell(props){
66
- let ret = super.renderHeaderCell(props);
67
- const {columnField,columnDef,style} = props;
68
- if(this.state.showFilters){
69
- const filterC = this.customFilteredColumns[columnField];
70
- if(isObj(filterC)){
71
- return <View testID={"RN_DatagridTableComponent_HeaderField_"+columnField} style={[style,{minHeight:100}]}>
72
- {ret}
73
- <Filter
74
- {...filterC}
75
- withLabel = {false}
76
- style = {[styles.filter,style]}
77
- anchorProps ={{size:20}}
78
- inputProps = {{
79
- style : [styles.filter,style]
80
- }}
81
- />
82
- </View>
83
- }
84
- }
85
- return ret;
86
- }
87
- beforePrepareColumns(){
88
- this.customFilteredColumns = {};
89
- }
90
- prepareColumn({filterProps,columnField,columnDef,key,field,visible,filter},filters){
91
- this.customFilteredColumns = defaultObj(this.customFilteredColumns);
92
- if(visible){
93
- if(filter){
94
- this.customFilteredColumns[columnField] = filterProps;
95
- } else {
96
- this.customFilteredColumns[columnField] = null;
97
- }
66
+ renderFilterCell(props){
67
+ const {columnField,style} = props;
68
+ const filterC = this.currentFilteringColumns[columnField];
69
+ if(isObj(filterC)){
70
+ return <Filter
71
+ {...filterC}
72
+ withLabel = {false}
73
+ style = {[styles.filter,theme.styles.pv0,theme.styles.mv0]}
74
+ anchorProps ={{size:20}}
75
+ mode = "flat"
76
+ inputProps = {{
77
+ style : [styles.filter],
78
+ mode : "flat",
79
+ }}
80
+ />
98
81
  }
99
- return filters;
82
+ return null;
100
83
  }
101
84
  updateLayout(e){
102
85
  if(this.state.fixedTable === false) return;
@@ -176,7 +159,7 @@ const DatagridFactory = (Factory)=>{
176
159
  })*/
177
160
  const {visibleColumns} = this.preparedColumns;
178
161
  const hasFooterFields = this.hasFooterFields();
179
- const {columnsWidths:widths,showFilters,showFooter} = this.state;
162
+ const {columnsWidths:widths,showFilters,showFooters} = this.state;
180
163
  let isAllRowsSelected = this.isAllRowsSelected();
181
164
  const isLoading = this.isLoading();
182
165
  let _progressBar = this.getProgressBar();
@@ -227,10 +210,10 @@ const DatagridFactory = (Factory)=>{
227
210
  {hasFooterFields ? <Button
228
211
  normal
229
212
  style={styles.paginationItem}
230
- onPress = {()=>{showFooter?this.hideFooter():this.showFooter()} }
231
- icon = {showFooter?'view-column':'view-module'}
213
+ onPress = {()=>{showFooters?this.hideFooter():this.showFooters()} }
214
+ icon = {showFooters?'view-column':'view-module'}
232
215
  >
233
- {showFooter?'Masquer/Ligne des totaux':'Afficher/Ligne des totaux'}
216
+ {showFooters?'Masquer/Ligne des totaux':'Afficher/Ligne des totaux'}
234
217
  </Button>:null}
235
218
  {selectableMultiple && (<>
236
219
  {restItems.map((item,index)=>{
@@ -287,9 +270,9 @@ const DatagridFactory = (Factory)=>{
287
270
  ,text : (showFilters?'Masquer/Filtres':'Afficher/Filtres')
288
271
  } : null,
289
272
  isMobile && hasFooterFields?{
290
- onPress : ()=>{showFooter?this.hideFooter():this.showFooter()}
291
- ,icon : showFooter?'view-column':'view-module'
292
- ,text : (showFooter?'Masquer/Ligne des totaux':'Afficher/Ligne des totaux')
273
+ onPress : ()=>{showFooters?this.hideFooter():this.showFooters()}
274
+ ,icon : showFooters?'view-column':'view-module'
275
+ ,text : (showFooters?'Masquer/Ligne des totaux':'Afficher/Ligne des totaux')
293
276
  } : null,
294
277
  ...(selectableMultiple ? restItems : [])
295
278
  ] : visibleColumns}
@@ -340,7 +323,7 @@ const DatagridFactory = (Factory)=>{
340
323
  ref = {this.listRef}
341
324
  hasFooters = {hasFooterFields}
342
325
  showFilters = {showFilters}
343
- showFooters = {showFooter}
326
+ showFooters = {showFooters}
344
327
  headerContainerProps = {{}}
345
328
  headerCellContainerProps = {{
346
329
  style : showFilters?{justifyContent:'flex-start'}:null
@@ -358,7 +341,9 @@ const DatagridFactory = (Factory)=>{
358
341
  }}
359
342
  data = {this.state.data}
360
343
  renderHeaderCell={this.renderHeaderCell.bind(this)}
344
+ renderFilterCell={this.renderFilterCell.bind(this)}
361
345
  renderFooterCell={this.renderFooterCell.bind(this)}
346
+ renderEmpty = {this.renderEmpty.bind(this)}
362
347
  />
363
348
  </View>
364
349
  }
@@ -440,8 +425,10 @@ const styles = StyleSheet.create({
440
425
  maxHeight : 40,
441
426
  height : 40,
442
427
  width : "100%",
428
+ alignSelf : 'flex-start',
443
429
  flexGrow : 1,
444
430
  minHeight : 40,
431
+ backgroundColor : 'transparent'
445
432
  },
446
433
  layoutContent : {
447
434
  maxWidth : '100%',
@@ -166,7 +166,7 @@ const DatePickerInput = React.forwardRef(({
166
166
  onChange({dateObject:state.inputDate,date:state.inputDate,sqlDate:date,value:date})
167
167
  }
168
168
  },[state])
169
- const labelText = render_filter ? label : withLabel!==false ? getLabel({ label, inputFormat:inputFormatLabel, withDateFormatInLabel }):"";
169
+ const labelText = withLabel === false ? null: (render_filter ? label : getLabel({ label, inputFormat:inputFormatLabel, withDateFormatInLabel }));
170
170
  return (
171
171
  <>
172
172
  <TextField
@@ -13,7 +13,7 @@ import { toDateObj } from "./utils";
13
13
  import TextField from "$components/TextField";
14
14
  import PeriodActionComponent from "./PeriodAction";
15
15
 
16
- export default function DateTimePickerComponent({left,isPeriodAction,withSeconds,right,format,dateFormat,timeFormat,defaultValue,onChange,testID,dateProps,disabled,readOnly,timeProps,...rest}){
16
+ export default function DateTimePickerComponent({left,isPeriodAction,contentProps,withSeconds,right,format,dateFormat,timeFormat,defaultValue,onChange,testID,dateProps,disabled,readOnly,timeProps,...rest}){
17
17
  if(!isPeriodAction){
18
18
  isPeriodAction = isNonNullString(defaultValue) && defaultValue.contains("=>");
19
19
  }
@@ -28,6 +28,7 @@ export default function DateTimePickerComponent({left,isPeriodAction,withSeconds
28
28
  }
29
29
  dateProps = defaultObj(dateProps);
30
30
  timeProps = defaultObj(timeProps);
31
+ contentProps = defaultObj(contentProps);
31
32
  testID = defaultStr(testID,"RN_DateTimeComponent")
32
33
  const anchorTimeProps = defaultObj(timeProps.anchorProps);
33
34
  const timePropsContainerProps = defaultObj(timeProps.containerProps);
@@ -52,8 +53,8 @@ export default function DateTimePickerComponent({left,isPeriodAction,withSeconds
52
53
  const timeDefaultValue = getTimeValue(dateObj);
53
54
  const changedTimeArgsRef = {current:{...defaultObj(parseTime(timeDefaultValue,withSeconds))}};
54
55
  withSeconds = defaultBool(timeProps.withSeconds,withSeconds,true);
55
-
56
- const maxWidth = 120;//withSeconds ? 120 : 120;
56
+ const cStyle = [theme.styles.noPadding,theme.styles.noMargin];
57
+ const maxWidth = 110;
57
58
  const callOnChange = ()=>{
58
59
  if(onChange){
59
60
  const dObj = changeDateArgsRef.current;
@@ -71,6 +72,8 @@ export default function DateTimePickerComponent({left,isPeriodAction,withSeconds
71
72
  onChange(args);
72
73
  }
73
74
  }
75
+ const tInputProps = defaultObj(timeProps.inputProps);
76
+ const dStyle = flattenStyle([rest.style,dateProps.style]);
74
77
  return <DateComponent
75
78
  defaultValue = {dateObj}
76
79
  disabled = {disabled}
@@ -79,7 +82,7 @@ export default function DateTimePickerComponent({left,isPeriodAction,withSeconds
79
82
  {...rest}
80
83
  format = {dateFormat}
81
84
  {...dateProps}
82
- style = {[rest.style,dateProps.style]}
85
+ style = {dStyle}
83
86
  calendarIconBefore = {true}
84
87
  onChange = {(args)=>{
85
88
  changeDateArgsRef.current = args;
@@ -102,15 +105,17 @@ export default function DateTimePickerComponent({left,isPeriodAction,withSeconds
102
105
  }}
103
106
  withLabel = {false}
104
107
  mode = {"flat"}
105
- containerProps = {{...timePropsContainerProps,style:[{maxWidth},timePropsContainerProps.style]}}
108
+ contentProps = {{style:cStyle}}
109
+ containerProps = {{...timePropsContainerProps,style:[{maxWidth},theme.styles.noPadding,theme.styles.noMargin,timePropsContainerProps.style]}}
106
110
  divider = {false}
111
+ style = {[theme.styles.noPadding,{maxHeight:40},theme.styles.noMargin,timeProps.style,dStyle.backgroundColor && {backgroundColor:dStyle.backgroundColor}]}
107
112
  anchorProps = {{
108
113
  ...anchorTimeProps,
109
114
  testID:testID+"_TimeAnchor",
110
115
  style : [theme.styles.noPadding,{borderRadius:0},theme.styles.noMargin,anchorTimeProps.style]
111
116
  }}
112
117
  inputProps = {{
113
- ...defaultObj(timeProps.inputProps),
118
+ ...tInputProps,
114
119
  mode : "flat",
115
120
  }}
116
121
  />
@@ -44,8 +44,9 @@ export const timeToString = (value,withSeconds)=>{
44
44
  return value.substring(0,5);
45
45
  }
46
46
  export default function TimePickerComponent (props){
47
- let {right:customRight,upper,anchorProps,dialogProps,withLabel,containerProps,mode,onChange,withSeconds,cancelLabel,confirmLabel,label,text,upperCase,defaultValue,disabled,editable,withModal,readOnly,...rest} = props;
47
+ let {right:customRight,upper,anchorProps,dialogProps,withLabel,inputProps,containerProps,mode,onChange,withSeconds,cancelLabel,confirmLabel,label,text,upperCase,defaultValue,disabled,editable,withModal,readOnly,...rest} = props;
48
48
  rest = defaultObj(rest);
49
+ inputProps = defaultObj(inputProps);
49
50
  const isEditable = disabled !== true && readOnly !== true && editable !== false?true : false;
50
51
  withModal = defaultBool(withModal,true);
51
52
  if(!isEditable){
@@ -110,6 +111,7 @@ export default function TimePickerComponent (props){
110
111
  if(!disabled){
111
112
  containerProps.style = [containerProps.style,{opacity:1}]
112
113
  }
114
+ const iContainerProps = defaultObj(inputProps.containerProps);
113
115
  return <>
114
116
  <TouchableRipple {...containerProps}
115
117
  disabled = {!isEditable}
@@ -120,6 +122,8 @@ export default function TimePickerComponent (props){
120
122
  <TextField
121
123
  mode = {mode||theme.textFieldMode}
122
124
  {...rest}
125
+ {...inputProps}
126
+ containerProps = {{...iContainerProps,style:[containerProps.style,iContainerProps.style]}}
123
127
  label = {label}
124
128
  right = {right}
125
129
  disabled = {disabled}
@@ -0,0 +1,132 @@
1
+ // Copyright 2022 @fto-consult/Boris Fouomene. All rights reserved.
2
+ // Use of this source code is governed by a BSD-style
3
+ // license that can be found in the LICENSE file.
4
+
5
+ import React, {Component} from 'react';
6
+ import {
7
+ PanResponder,
8
+ View,
9
+ } from 'react-native';
10
+ import PropTypes from 'prop-types';
11
+
12
+ export const CONNECTOR_TOP_LEFT = 'tl';
13
+ export const CONNECTOR_TOP_MIDDLE = 'tm';
14
+ export const CONNECTOR_TOP_RIGHT = 'tr';
15
+ export const CONNECTOR_MIDDLE_RIGHT = 'mr';
16
+ export const CONNECTOR_BOTTOM_RIGHT = 'br';
17
+ export const CONNECTOR_BOTTOM_MIDDLE = 'bm';
18
+ export const CONNECTOR_BOTTOM_LEFT = 'bl';
19
+ export const CONNECTOR_MIDDLE_LEFT = 'ml';
20
+ export const CONNECTOR_CENTER = 'c';
21
+
22
+ /**
23
+ * Connector component for handle touch events.
24
+ */
25
+ export class Connector extends Component {
26
+
27
+ constructor(props) {
28
+ super(props);
29
+
30
+ this.position = {
31
+ x: 0,
32
+ y: 0,
33
+ };
34
+
35
+ this._panResponder = PanResponder.create({
36
+ // Ask to be the responder:
37
+ onStartShouldSetPanResponder: (event, gestureState) => true,
38
+ onStartShouldSetPanResponderCapture: (event, gestureState) => true,
39
+ onMoveShouldSetPanResponder: (event, gestureState) => true,
40
+ onMoveShouldSetPanResponderCapture: (event, gestureState) => true,
41
+
42
+ onPanResponderGrant: (event, gestureState) => {
43
+ // The gesture has started. Show visual feedback so the user knows
44
+ // what is happening!
45
+ // gestureState.d{x,y} will be set to zero now
46
+ const {
47
+ onStart
48
+ } = this.props;
49
+
50
+ this.position = {
51
+ x: 0,
52
+ y: 0,
53
+ };
54
+
55
+ onStart([
56
+ 0,
57
+ 0,
58
+ ]);
59
+ },
60
+ onPanResponderMove: (event, gestureState) => {
61
+ const {
62
+ onMove
63
+ } = this.props;
64
+
65
+ onMove([
66
+ gestureState.dx - this.position.x,
67
+ gestureState.dy - this.position.y,
68
+ ]);
69
+
70
+ this.position = {
71
+ x: gestureState.dx,
72
+ y: gestureState.dy,
73
+ };
74
+ },
75
+ onPanResponderTerminationRequest: (event, gestureState) => true,
76
+ onPanResponderRelease: (event, gestureState) => {
77
+ // The user has released all touches while this view is the
78
+ // responder. This typically means a gesture has succeeded
79
+ const {
80
+ onEnd
81
+ } = this.props;
82
+
83
+ onEnd([
84
+ gestureState.moveX,
85
+ gestureState.moveY,
86
+ ]);
87
+ },
88
+ onPanResponderTerminate: (event, gestureState) => {
89
+ // Another component has become the responder, so this gesture
90
+ // should be cancelled
91
+ },
92
+ onShouldBlockNativeResponder: (event, gestureState) => {
93
+ // Returns whether this component should block native components from becoming the JS
94
+ // responder. Returns true by default. Is currently only supported on android.
95
+ return true;
96
+ },
97
+ });
98
+ }
99
+
100
+ render() {
101
+ const {
102
+ x,
103
+ y,
104
+ size,
105
+ } = this.props;
106
+
107
+ return (
108
+ <View
109
+ style={{
110
+ position: 'absolute',
111
+ left: x,
112
+ top: y,
113
+ width: size,
114
+ height: size,
115
+ borderWidth: 2,
116
+ borderColor: 'black',
117
+ backgroundColor: 'white'
118
+ }}
119
+ {...this._panResponder.panHandlers}
120
+ />
121
+ );
122
+ }
123
+ }
124
+
125
+ Connector.propTypes = {
126
+ x: PropTypes.number,
127
+ y: PropTypes.number,
128
+ size: PropTypes.number,
129
+ onStart: PropTypes.func.isRequired,
130
+ onMove: PropTypes.func.isRequired,
131
+ onEnd: PropTypes.func.isRequired,
132
+ };