@onehat/ui 0.3.212 → 0.3.215

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": "@onehat/ui",
3
- "version": "0.3.212",
3
+ "version": "0.3.215",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -46,6 +46,9 @@
46
46
  "react-color": "^2.19.3",
47
47
  "react-datetime": "^3.2.0",
48
48
  "react-dom": "*",
49
+ "react-dnd": "^16.0.1",
50
+ "react-dnd-html5-backend": "^16.0.1",
51
+ "react-dnd-touch-backend":"16.0.1",
49
52
  "react-draggable": "^4.4.5",
50
53
  "react-native": "*",
51
54
  "react-native-draggable": "^3.3.0",
@@ -1,5 +1,6 @@
1
1
  import React, { useState, useEffect, useRef, useMemo, useCallback, } from 'react';
2
2
  import {
3
+ Box,
3
4
  Column,
4
5
  FlatList,
5
6
  Pressable,
@@ -34,6 +35,7 @@ import withContextMenu from '../Hoc/withContextMenu.js';
34
35
  import withAlert from '../Hoc/withAlert.js';
35
36
  import withComponent from '../Hoc/withComponent.js';
36
37
  import withData from '../Hoc/withData.js';
38
+ import { withDropTarget } from '../Hoc/withDnd.js';
37
39
  import withEvents from '../Hoc/withEvents.js';
38
40
  import withSideEditor from '../Hoc/withSideEditor.js';
39
41
  import withFilters from '../Hoc/withFilters.js';
@@ -49,7 +51,7 @@ import testProps from '../../Functions/testProps.js';
49
51
  import nbToRgb from '../../Functions/nbToRgb.js';
50
52
  import Loading from '../Messages/Loading.js';
51
53
  import GridHeaderRow from './GridHeaderRow.js';
52
- import GridRow, { ReorderableGridRow } from './GridRow.js';
54
+ import GridRow from './GridRow.js';
53
55
  import IconButton from '../Buttons/IconButton.js';
54
56
  import PaginationToolbar from '../Toolbar/PaginationToolbar.js';
55
57
  import NoRecordsFound from './NoRecordsFound.js';
@@ -111,6 +113,8 @@ function GridComponent(props) {
111
113
  canColumnsReorder = true,
112
114
  canColumnsResize = true,
113
115
  canRowsReorder = false,
116
+ areRowsDragSource = false,
117
+ rowDragSourceType,
114
118
  allowToggleSelection = false, // i.e. single click with no shift key toggles the selection of the item clicked on
115
119
  disableBottomToolbar = false,
116
120
  disablePagination = false,
@@ -123,6 +127,7 @@ function GridComponent(props) {
123
127
  verifyCanEdit,
124
128
  alternateRowBackgrounds = true,
125
129
  alternatingInterval = 2,
130
+ defaultRowHeight = 48,
126
131
 
127
132
  // withComponent
128
133
  self,
@@ -146,6 +151,12 @@ function GridComponent(props) {
146
151
  idIx,
147
152
  displayIx,
148
153
 
154
+ // withDnd
155
+ isDropTarget,
156
+ canDrop,
157
+ isOver,
158
+ dropTargetRef,
159
+
149
160
  // withPresetButtons
150
161
  onChangeColumnsConfig,
151
162
 
@@ -420,11 +431,12 @@ function GridComponent(props) {
420
431
  ratio = mixWithObj.alpha ? 1 - mixWithObj.alpha : 0.5;
421
432
  bg = colourMixer.blend(bg, ratio, mixWithObj.color);
422
433
  }
423
- let WhichGridRow = GridRow,
424
- rowReorderProps = {};
434
+ const
435
+ rowReorderProps = {},
436
+ rowDragProps = {};
425
437
  if (canRowsReorder && isDragMode) {
426
- WhichGridRow = ReorderableGridRow;
427
438
  rowReorderProps = {
439
+ isDraggable: true,
428
440
  mode: VERTICAL,
429
441
  onDragStart: onRowReorderDragStart,
430
442
  onDrag: onRowReorderDrag,
@@ -435,8 +447,12 @@ function GridComponent(props) {
435
447
  getProxy: getReorderProxy,
436
448
  };
437
449
  }
438
-
439
- return <WhichGridRow
450
+ if (areRowsDragSource) {
451
+ rowDragProps.isDragSource = true;
452
+ rowDragProps.dragSourceType = rowDragSourceType;
453
+ rowDragProps.dragSourceItem = { id: item.id };
454
+ }
455
+ return <GridRow
440
456
  columnsConfig={localColumnsConfig}
441
457
  columnProps={columnProps}
442
458
  fields={fields}
@@ -446,6 +462,7 @@ function GridComponent(props) {
446
462
  item={item}
447
463
  isInlineEditorShown={isInlineEditorShown}
448
464
  {...rowReorderProps}
465
+ {...rowDragProps}
449
466
  />;
450
467
  }}
451
468
  </Pressable>;
@@ -698,8 +715,7 @@ function GridComponent(props) {
698
715
  headerHeight = showHeaders ? 50 : 0,
699
716
  footerHeight = !disablePagination ? 50 : 0,
700
717
  height = containerHeight - headerHeight - footerHeight,
701
- rowHeight = 48,
702
- rowsPerContainer = Math.floor(height / rowHeight);
718
+ rowsPerContainer = Math.floor(height / defaultRowHeight);
703
719
  let pageSize = rowsPerContainer;
704
720
  if (showHeaders) {
705
721
  pageSize--;
@@ -1006,7 +1022,7 @@ function GridComponent(props) {
1006
1022
  }
1007
1023
  }
1008
1024
 
1009
- return <Column
1025
+ grid = <Column
1010
1026
  {...testProps('Grid')}
1011
1027
  ref={containerRef}
1012
1028
  w="100%"
@@ -1028,7 +1044,19 @@ function GridComponent(props) {
1028
1044
 
1029
1045
  {listFooterComponent}
1030
1046
 
1031
- </Column>;
1047
+ </Column>
1048
+
1049
+ if (isDropTarget) {
1050
+ grid = <Box
1051
+ ref={dropTargetRef}
1052
+ borderWidth={canDrop && isOver ? 4 : 0}
1053
+ borderColor="#0ff"
1054
+ w="100%"
1055
+ {...sizeProps}
1056
+ >{grid}</Box>
1057
+ }
1058
+
1059
+ return grid;
1032
1060
 
1033
1061
  }
1034
1062
 
@@ -1036,12 +1064,14 @@ export const Grid = withComponent(
1036
1064
  withAlert(
1037
1065
  withEvents(
1038
1066
  withData(
1039
- withMultiSelection(
1040
- withSelection(
1041
- withFilters(
1042
- withPresetButtons(
1043
- withContextMenu(
1044
- GridComponent
1067
+ withDropTarget(
1068
+ withMultiSelection(
1069
+ withSelection(
1070
+ withFilters(
1071
+ withPresetButtons(
1072
+ withContextMenu(
1073
+ GridComponent
1074
+ )
1045
1075
  ),
1046
1076
  true // isGrid
1047
1077
  )
@@ -1057,13 +1087,15 @@ export const SideGridEditor = withComponent(
1057
1087
  withAlert(
1058
1088
  withEvents(
1059
1089
  withData(
1060
- withMultiSelection(
1061
- withSelection(
1062
- withSideEditor(
1063
- withFilters(
1064
- withPresetButtons(
1065
- withContextMenu(
1066
- GridComponent
1090
+ withDropTarget(
1091
+ withMultiSelection(
1092
+ withSelection(
1093
+ withSideEditor(
1094
+ withFilters(
1095
+ withPresetButtons(
1096
+ withContextMenu(
1097
+ GridComponent
1098
+ )
1067
1099
  ),
1068
1100
  true // isGrid
1069
1101
  )
@@ -1080,15 +1112,17 @@ export const WindowedGridEditor = withComponent(
1080
1112
  withAlert(
1081
1113
  withEvents(
1082
1114
  withData(
1083
- withMultiSelection(
1084
- withSelection(
1085
- withWindowedEditor(
1086
- withFilters(
1087
- withPresetButtons(
1088
- withContextMenu(
1089
- GridComponent
1090
- ),
1091
- true // isGrid
1115
+ withDropTarget(
1116
+ withMultiSelection(
1117
+ withSelection(
1118
+ withWindowedEditor(
1119
+ withFilters(
1120
+ withPresetButtons(
1121
+ withContextMenu(
1122
+ GridComponent
1123
+ ),
1124
+ true // isGrid
1125
+ )
1092
1126
  )
1093
1127
  )
1094
1128
  )
@@ -1103,16 +1137,18 @@ export const InlineGridEditor = withComponent(
1103
1137
  withAlert(
1104
1138
  withEvents(
1105
1139
  withData(
1106
- withMultiSelection(
1107
- withSelection(
1108
- withInlineEditor(
1109
- withFilters(
1110
- withPresetButtons(
1111
- withContextMenu(
1112
- GridComponent
1113
- )
1114
- ),
1115
- true // isGrid
1140
+ withDropTarget(
1141
+ withMultiSelection(
1142
+ withSelection(
1143
+ withInlineEditor(
1144
+ withFilters(
1145
+ withPresetButtons(
1146
+ withContextMenu(
1147
+ GridComponent
1148
+ )
1149
+ ),
1150
+ true // isGrid
1151
+ )
1116
1152
  )
1117
1153
  )
1118
1154
  )
@@ -1,24 +1,22 @@
1
- import { useState, useMemo, } from 'react';
1
+ import { useMemo, } from 'react';
2
2
  import {
3
3
  Box,
4
4
  Row,
5
5
  Text,
6
6
  } from 'native-base';
7
- import {
8
- VERTICAL,
9
- } from '../../Constants/Directions.js';
10
7
  import {
11
8
  UI_MODE_WEB,
12
9
  } from '../../Constants/UiModes.js';
13
10
  import getComponentFromType from '../../Functions/getComponentFromType.js';
14
11
  import UiGlobals from '../../UiGlobals.js';
12
+ import { withDragSource } from '../Hoc/withDnd.js';
15
13
  import withDraggable from '../Hoc/withDraggable.js';
16
14
  import AngleRight from '../Icons/AngleRight.js';
17
15
  import _ from 'lodash';
18
16
 
19
17
  // This was broken out from Grid simply so we can memoize it
20
18
 
21
- export default function GridRow(props) {
19
+ function GridRow(props) {
22
20
  const {
23
21
  columnsConfig,
24
22
  columnProps,
@@ -39,182 +37,176 @@ export default function GridRow(props) {
39
37
  isPhantom = item.isPhantom,
40
38
  hash = item?.hash || item;
41
39
 
40
+ if (props.dragSourceRef) {
41
+ rowProps.ref = props.dragSourceRef;
42
+ }
42
43
 
43
- return useMemo(() => {
44
- const renderColumns = (item) => {
45
- if (_.isArray(columnsConfig)) {
46
- return _.map(columnsConfig, (config, key, all) => {
47
- const propsToPass = columnProps[key] || {};
48
- if (all.length === 1) {
49
- propsToPass.w = '100%';
44
+ return useMemo(() => {
45
+ const renderColumns = (item) => {
46
+ if (_.isArray(columnsConfig)) {
47
+ return _.map(columnsConfig, (config, key, all) => {
48
+ const propsToPass = columnProps[key] || {};
49
+ if (all.length === 1) {
50
+ propsToPass.w = '100%';
51
+ } else {
52
+ if (config.w) {
53
+ propsToPass.w = config.w;
54
+ } else if (config.flex) {
55
+ propsToPass.flex = config.flex;
56
+ propsToPass.minWidth = 100;
50
57
  } else {
51
- if (config.w) {
52
- propsToPass.w = config.w;
53
- } else if (config.flex) {
54
- propsToPass.flex = config.flex;
55
- propsToPass.minWidth = 100;
56
- } else {
57
- propsToPass.flex = 1;
58
- }
58
+ propsToPass.flex = 1;
59
59
  }
60
- propsToPass.p = 1;
61
- propsToPass.justifyContent = 'center';
60
+ }
61
+ propsToPass.p = 1;
62
+ propsToPass.justifyContent = 'center';
62
63
 
63
- if (isInlineEditorShown) {
64
- propsToPass.minWidth = styles.INLINE_EDITOR_MIN_WIDTH;
65
- }
64
+ if (isInlineEditorShown) {
65
+ propsToPass.minWidth = styles.INLINE_EDITOR_MIN_WIDTH;
66
+ }
66
67
 
67
- let value;
68
- if (_.isFunction(config)) {
69
- return config(item, key);
70
- }
71
- if (_.isPlainObject(config)) {
72
- if (config.renderer) {
73
- const extraProps = _.omit(config, [
74
- 'columnId',
75
- 'header',
76
- 'fieldName',
77
- 'type',
78
- 'isEditable',
79
- 'editor',
80
- 'format',
81
- 'renderer',
82
- 'reorderable',
83
- 'resizable',
84
- 'sortable',
85
- 'w',
86
- 'flex',
87
- 'showDragHandles',
88
- ]);
68
+ let value;
69
+ if (_.isFunction(config)) {
70
+ return config(item, key);
71
+ }
72
+ if (_.isPlainObject(config)) {
73
+ if (config.renderer) {
74
+ const extraProps = _.omit(config, [
75
+ 'columnId',
76
+ 'header',
77
+ 'fieldName',
78
+ 'type',
79
+ 'isEditable',
80
+ 'editor',
81
+ 'format',
82
+ 'renderer',
83
+ 'reorderable',
84
+ 'resizable',
85
+ 'sortable',
86
+ 'w',
87
+ 'flex',
88
+ 'showDragHandles',
89
+ ]);
89
90
 
90
- if (!extraProps._web) {
91
- extraProps._web = {};
92
- }
93
- if (!extraProps._web.style) {
94
- extraProps._web.style = {};
95
- }
96
- extraProps._web.style = {
97
- userSelect: 'none',
98
- };
99
-
100
- return <Row key={key} {...propsToPass} {...extraProps}>{config.renderer(item)}</Row>;
91
+ if (!extraProps._web) {
92
+ extraProps._web = {};
93
+ }
94
+ if (!extraProps._web.style) {
95
+ extraProps._web.style = {};
101
96
  }
102
- if (config.fieldName) {
103
- if (item?.properties && item.properties[config.fieldName]) {
104
- const property = item.properties[config.fieldName];
105
- value = property.displayValue;
106
- const type = property?.viewerType?.type;
97
+ extraProps._web.style = {
98
+ userSelect: 'none',
99
+ };
100
+
101
+ return <Row key={key} {...propsToPass} {...extraProps}>{config.renderer(item)}</Row>;
102
+ }
103
+ if (config.fieldName) {
104
+ if (item?.properties && item.properties[config.fieldName]) {
105
+ const property = item.properties[config.fieldName];
106
+ value = property.displayValue;
107
+ const type = property?.viewerType?.type;
107
108
 
108
- if (type) {
109
- const Element = getComponentFromType(type);
110
- const elementProps = {};
111
- if (UiGlobals.mode === UI_MODE_WEB) {
112
- elementProps.textOverflow = 'ellipsis';
113
- }
114
- if (type.match(/(Tag|TagEditor)$/)) {
115
- elementProps.isViewOnly = true; // TODO: this won't work for InlineGridEditor, bc that Grid can't use isViewOnly when actually editing
116
- }
117
- return <Element
118
- value={value}
119
- key={key}
120
- overflow="hidden"
121
- alignSelf="center"
122
- style={{
123
- userSelect: 'none',
124
- }}
125
- fontSize={styles.GRID_CELL_FONTSIZE}
126
- px={styles.GRID_CELL_PX}
127
- py={styles.GRID_CELL_PY}
128
- numberOfLines={1}
129
- ellipsizeMode="head"
130
- {...propsToPass}
131
- {...elementProps}
132
- />;
109
+ if (type) {
110
+ const Element = getComponentFromType(type);
111
+ const elementProps = {};
112
+ if (UiGlobals.mode === UI_MODE_WEB) {
113
+ elementProps.textOverflow = 'ellipsis';
133
114
  }
134
- } else if (item[config.fieldName]) {
135
- value = item[config.fieldName];
136
- } else if (fields) {
137
- const ix = fields.indexOf(config.fieldName);
138
- value = item[ix];
115
+ if (type.match(/(Tag|TagEditor)$/)) {
116
+ elementProps.isViewOnly = true; // TODO: this won't work for InlineGridEditor, bc that Grid can't use isViewOnly when actually editing
117
+ }
118
+ return <Element
119
+ value={value}
120
+ key={key}
121
+ overflow="hidden"
122
+ alignSelf="center"
123
+ style={{
124
+ userSelect: 'none',
125
+ }}
126
+ fontSize={styles.GRID_CELL_FONTSIZE}
127
+ px={styles.GRID_CELL_PX}
128
+ py={styles.GRID_CELL_PY}
129
+ numberOfLines={1}
130
+ ellipsizeMode="head"
131
+ {...propsToPass}
132
+ {...elementProps}
133
+ />;
139
134
  }
140
- }
141
- }
142
- if (_.isString(config)) {
143
- if (fields) {
144
- const ix = fields.indexOf(config);
135
+ } else if (item[config.fieldName]) {
136
+ value = item[config.fieldName];
137
+ } else if (fields) {
138
+ const ix = fields.indexOf(config.fieldName);
145
139
  value = item[ix];
146
- } else {
147
- value = item[config];
148
140
  }
149
141
  }
150
- if (_.isFunction(value)) {
151
- return value(key);
152
- }
153
- const elementProps = {};
154
- if (UiGlobals.mode === UI_MODE_WEB) {
155
- elementProps.textOverflow = 'ellipsis';
142
+ }
143
+ if (_.isString(config)) {
144
+ if (fields) {
145
+ const ix = fields.indexOf(config);
146
+ value = item[ix];
147
+ } else {
148
+ value = item[config];
156
149
  }
157
- return <Text
158
- key={key}
159
- overflow="hidden"
160
- alignSelf="center"
161
- style={{
162
- userSelect: 'none',
163
- }}
164
- fontSize={styles.GRID_CELL_FONTSIZE}
165
- px={styles.GRID_CELL_PX}
166
- py={styles.GRID_CELL_PY}
167
- numberOfLines={1}
168
- ellipsizeMode="head"
169
- {...elementProps}
170
- {...propsToPass}
171
- >{value}</Text>;
172
- });
173
- } else {
174
- // TODO: if 'columnsConfig' is an object, parse its contents
175
- throw new Error('Non-array columnsConfig not yet supported');
176
- }
177
- };
178
- return <Row
179
- alignItems="center"
180
- flexGrow={1}
181
- {...rowProps}
182
- bg={bg}
183
- key={hash}
184
- >
185
- {isPhantom && <Box position="absolute" bg="#f00" h={2} w={2} t={0} l={0} />}
186
-
187
- {renderColumns(item)}
188
-
189
- {!hideNavColumn && <AngleRight
190
- color={styles.GRID_NAV_COLUMN_COLOR}
191
- variant="ghost"
192
- w={30}
193
- alignSelf="center"
194
- mx={3}
195
- />}
196
- </Row>;
197
- }, [
198
- columnsConfig,
199
- columnProps,
200
- fields,
201
- rowProps,
202
- hideNavColumn,
203
- bg,
204
- item,
205
- isPhantom,
206
- hash, // this is an easy way to determine if the data has changed and the item needs to be rerendered
207
- isInlineEditorShown,
208
- ]);
209
- }
150
+ }
151
+ if (_.isFunction(value)) {
152
+ return value(key);
153
+ }
154
+ const elementProps = {};
155
+ if (UiGlobals.mode === UI_MODE_WEB) {
156
+ elementProps.textOverflow = 'ellipsis';
157
+ }
158
+ return <Text
159
+ key={key}
160
+ overflow="hidden"
161
+ alignSelf="center"
162
+ style={{
163
+ userSelect: 'none',
164
+ }}
165
+ fontSize={styles.GRID_CELL_FONTSIZE}
166
+ px={styles.GRID_CELL_PX}
167
+ py={styles.GRID_CELL_PY}
168
+ numberOfLines={1}
169
+ ellipsizeMode="head"
170
+ {...elementProps}
171
+ {...propsToPass}
172
+ >{value}</Text>;
173
+ });
174
+ } else {
175
+ // TODO: if 'columnsConfig' is an object, parse its contents
176
+ throw new Error('Non-array columnsConfig not yet supported');
177
+ }
178
+ };
179
+ return <Row
180
+ alignItems="center"
181
+ flexGrow={1}
182
+ {...rowProps}
183
+ bg={bg}
184
+ key={hash}
185
+ >
186
+ {isPhantom && <Box position="absolute" bg="#f00" h={2} w={2} t={0} l={0} />}
187
+
188
+ {renderColumns(item)}
210
189
 
211
- function withAdditionalProps(WrappedComponent) {
212
- return (props) => {
213
- return <WrappedComponent
214
- mode={VERTICAL}
215
- {...props}
216
- />;
217
- };
190
+ {!hideNavColumn && <AngleRight
191
+ color={styles.GRID_NAV_COLUMN_COLOR}
192
+ variant="ghost"
193
+ w={30}
194
+ alignSelf="center"
195
+ mx={3}
196
+ />}
197
+ </Row>;
198
+ }, [
199
+ columnsConfig,
200
+ columnProps,
201
+ fields,
202
+ rowProps,
203
+ hideNavColumn,
204
+ bg,
205
+ item,
206
+ isPhantom,
207
+ hash, // this is an easy way to determine if the data has changed and the item needs to be rerendered
208
+ isInlineEditorShown,
209
+ ]);
218
210
  }
219
211
 
220
- export const ReorderableGridRow = withAdditionalProps(withDraggable(GridRow));
212
+ export default withDraggable(withDragSource(GridRow));
@@ -91,7 +91,11 @@ export default function withContextMenu(WrappedComponent) {
91
91
  w: 20,
92
92
  mr: 2,
93
93
  };
94
- icon = React.cloneElement(icon, {...iconProps});
94
+ if (React.isValidElement(icon)) {
95
+ icon = React.cloneElement(icon, {...iconProps});
96
+ } else {
97
+ icon = <Icon as={icon} {...iconProps} />;
98
+ }
95
99
  }
96
100
 
97
101
  // <div style={{
@@ -0,0 +1,137 @@
1
+ import { useDrag, useDrop } from 'react-dnd'; // https://react-dnd.github.io/react-dnd/about don't forget the wrapping <DndProvider /> as shown here: https://react-dnd.github.io/react-dnd/docs/api/dnd-provider
2
+
3
+ // This HOC allows components to be dragged and dropped onto another component.
4
+ // It doesn't contraint the moment of the preview item.
5
+
6
+ export function withDragSource(WrappedComponent) {
7
+ return (props) => {
8
+
9
+ if (!props.isDragSource) {
10
+ return <WrappedComponent {...props} />;
11
+ }
12
+
13
+ if (!props.dragSourceType) {
14
+ throw Error('dragSourceType not defined');
15
+ }
16
+ if (!props.dragSourceItem) {
17
+ throw Error('dragSourceItem not defined');
18
+ }
19
+
20
+ const {
21
+ dragSourceType,
22
+ dragSourceItem,
23
+ } = props,
24
+ [dragState, dragSourceRef, dragPreviewRef] = useDrag(() => { // A specification object or a function that creates a specification object.
25
+ // The useDrag hook provides a way to wire your component into the DnD system as a drag source. By passing in a specification into useDrag, you declaratively describe the typeof draggable being generated, the itemobject representing the drag source, what props to collect, and more. The useDraghooks returns a few key items: a set of collected props, and refs that may be attached to drag source and drag preview elements
26
+ return {
27
+ type: dragSourceType, // Required. This must be either a string or a symbol. Only the drop targets registered for the same type will react to this item.
28
+ item: dragSourceItem, // Required (object or function).
29
+ // When an object, it is a plain JavaScript object describing the data being dragged. This is the only information available to the drop targets about the drag source so it's important to pick the minimal data they need to know. You may be tempted to put a complex reference here, but you should try very hard to avoid doing this because it couples the drag sources and drop targets. It's a good idea to use something like { id }.
30
+ // When a function, it is fired at the beginning of the drag operation and returns an object representing the drag operation (see first bullet). If null is returned, the drag operation is cancelled.
31
+ previewOptions: null, // Optional. A plain JavaScript object describing drag preview options.
32
+ options: null, // Optional. A plain object optionally containing any of the following properties:
33
+ // dropEffect: Optional: The type of drop effect to use on this drag. ("move" or "copy" are valid values.)
34
+ end: null, // (item, monitor) Optional. When the dragging stops, endis called. For every begin call, a corresponding end call is guaranteed. You may call monitor.didDrop() to check whether or not the drop was handled by a compatible drop target. If it was handled, and the drop target specified a drop result by returning a plain object from its drop()method, it will be available as monitor.getDropResult(). This method is a good place to fire a Flux action. Note: If the component is unmounted while dragging, componentparameter is set to be null.
35
+ canDrag: null, // (monitor): Optional. Use it to specify whether the dragging is currently allowed. If you want to always allow it, just omit this method. Specifying it is handy if you'd like to disable dragging based on some predicate over props. Note: You may not call monitor.canDrag()inside this method.
36
+ isDragging: null, // (monitor): Optional. By default, only the drag source that initiated the drag operation is considered to be dragging. You can override this behavior by defining a custom isDraggingmethod. It might return something like props.id === monitor.getItem().id. Do this if the original component may be unmounted during the dragging and later “resurrected” with a different parent. For example, when moving a card across the lists in a Kanban board, you want it to retain the dragged appearance—even though technically, the component gets unmounted and a different one gets mounted every time you move it to another list. Note: You may not call monitor.isDragging()inside this method.
37
+ collect: (monitor, props) => { // Optional. The collecting function. It should return a plain object of the props to return for injection into your component. It receives two parameters, monitor and props. Read the overview for an introduction to the monitors and the collecting function. See the collecting function described in detail in the next section.
38
+ // monitor fn determines which props from dnd state get passed
39
+ return {
40
+ canDrag: !!monitor.canDrag(), // Returns trueif no drag operation is in progress, and the owner's canDrag() returns true or is not defined.
41
+ isDragging: !!monitor.isDragging(), // Returns trueif a drag operation is in progress, and either the owner initiated the drag, or its isDragging() is defined and returns true.
42
+ // type: monitor.getItemType(), // Returns a string or a symbol identifying the type of the current dragged item. Returns null if no item is being dragged.
43
+ // item: monitor.getItem(), // Returns a plain object representing the currently dragged item. Every drag source must specify it by returning an object from its beginDrag()method. Returns nullif no item is being dragged.
44
+ // dropResult: monitor.getDropResult(), // Returns a plain object representing the last recorded drop result. The drop targets may optionally specify it by returning an object from their drop()methods. When a chain of drop()is dispatched for the nested targets, bottom up, any parent that explicitly returns its own result from drop()overrides the child drop result previously set by the child. Returns nullif called outside endDrag().
45
+ // didDrop: !!monitor.didDrop(), // Returns trueif some drop target has handled the drop event, falseotherwise. Even if a target did not return a drop result, didDrop()returns true. Use it inside endDrag()to test whether any drop target has handled the drop. Returns falseif called outside endDrag().
46
+ // initialClientOffset: monitor.getInitialClientOffset(), // Returns the { x, y }client offset of the pointer at the time when the current drag operation has started. Returns nullif no item is being dragged.
47
+ // initialSourceClientOffset: monitor.getInitialSourceClientOffset(), // Returns the { x, y }client offset of the drag source component's root DOM node at the time when the current drag operation has started. Returns nullif no item is being dragged.
48
+ // clientOffset: monitor.getClientOffset(), // Returns the last recorded { x, y }client offset of the pointer while a drag operation is in progress. Returns nullif no item is being dragged.
49
+ // differenceFromInitialOffset: monitor.getDifferenceFromInitialOffset(), // Returns the { x, y }difference between the last recorded client offset of the pointer and the client offset when the current drag operation has started. Returns nullif no item is being dragged.
50
+ // sourceClientOffset: monitor.getSourceClientOffset(), // Returns the projected { x, y }client offset of the drag source component's root DOM node, based on its position at the time when the current drag operation has started, and the movement difference. Returns nullif no item is being dragged.
51
+ };
52
+ },
53
+ };
54
+ }),
55
+ {
56
+ canDrag,
57
+ isDragging,
58
+ // type,
59
+ // item,
60
+ // dropResult,
61
+ // didDrop,
62
+ // initialClientOffset,
63
+ // initialSourceClientOffset,
64
+ // clientOffset,
65
+ // differenceFromInitialOffset,
66
+ // sourceClientOffset,
67
+ } = dragState;
68
+
69
+ return <WrappedComponent
70
+ canDrag={canDrag}
71
+ isDragging={isDragging}
72
+ dragSourceRef={dragSourceRef}
73
+ {...props}
74
+ />;
75
+ };
76
+ }
77
+
78
+
79
+ export function withDropTarget(WrappedComponent) {
80
+ return (props) => {
81
+ if (!props.isDropTarget) {
82
+ return <WrappedComponent {...props} />;
83
+ }
84
+
85
+ if (!props.dropTargetAccept) {
86
+ throw Error('dropTargetAccept not defined');
87
+ }
88
+
89
+ const {
90
+ dropTargetAccept,
91
+ onDrop = null,
92
+ } = props,
93
+ [dropState, dropTargetRef] = useDrop(() => { // A specification object or a function that creates a specification object.
94
+ // The useDrophook provides a way for you to wire in your component into the DnD system as a drop target. By passing in a specification into the useDrophook, you can specify including what types of data items the drop-target will accept, what props to collect, and more. This function returns an array containing a ref to attach to the Drop Target node and the collected props.
95
+ return {
96
+ accept: dropTargetAccept, // Required. A string, a symbol, or an array of either. This drop target will only react to the items produced by the drag sources of the specified type or types. Read the overview to learn more about the items and types.
97
+ // options: null, // Optional. A plain object. If some of the props to your component are not scalar (that is, are not primitive values or functions), specifying a custom arePropsEqual(props, otherProps) function inside the options object can improve the performance. Unless you have performance problems, don't worry about it.
98
+ drop: onDrop, // (item, monitor): Optional. Called when a compatible item is dropped on the target. You may either return undefined, or a plain object. If you return an object, it is going to become the drop result and will be available to the drag source in its endDragmethod as monitor.getDropResult(). This is useful in case you want to perform different actions depending on which target received the drop. If you have nested drop targets, you can test whether a nested target has already handled dropby checking monitor.didDrop()and monitor.getDropResult(). Both this method and the source's endDragmethod are good places to fire Flux actions. This method will not be called if canDrop()is defined and returns false.
99
+ // hover: null, // (item, monitor): Optional. Called when an item is hovered over the component. You can check monitor.isOver({ shallow: true })to test whether the hover happens over only the current target, or over a nested one. Unlike drop(), this method will be called even if canDrop()is defined and returns false. You can check monitor.canDrop()to test whether this is the case.
100
+ // canDrop: null, // (item, monitor): Optional. Use it to specify whether the drop target is able to accept the item. If you want to always allow it, omit this method. Specifying it is handy if you'd like to disable dropping based on some predicate over props or monitor.getItem(). Note: You may not call monitor.canDrop() inside this method.
101
+ collect: (monitor, props) => { // Optional. The collecting function. It should return a plain object of the props to return for injection into your component. It receives two parameters, monitorand props. Read the overview for an introduction to the monitors and the collecting function. See the collecting function described in detail in the next section.
102
+ return {
103
+ canDrop: !!monitor.canDrop(),
104
+ isOver: !!monitor.isOver(),
105
+ // didDrop: !!monitor.didDrop(),
106
+ // clientOffset: monitor.getClientOffset(),
107
+ // differenceFromInitialOffset: monitor.getDifferenceFromInitialOffset(),
108
+ // dropResult: monitor.getDropResult(),
109
+ // handlerId: monitor.getHandlerId(),
110
+ // initialClientOffset: monitor.getInitialClientOffset(),
111
+ // initialSourceClientOffset: monitor.getInitialSourceClientOffset(),
112
+ // receiveHandlerId
113
+ // subscribeToStateChange
114
+ };
115
+ },
116
+ };
117
+ }),
118
+ {
119
+ canDrop,
120
+ isOver,
121
+ // didDrop,
122
+ // clientOffset,
123
+ // differenceFromInitialOffset,
124
+ // dropResult,
125
+ // handlerId,
126
+ // initialClientOffset,
127
+ // initialSourceClientOffset,
128
+ } = dropState;
129
+
130
+ return <WrappedComponent
131
+ canDrop={canDrop}
132
+ isOver={isOver}
133
+ dropTargetRef={dropTargetRef}
134
+ {...props}
135
+ />;
136
+ };
137
+ }
@@ -23,6 +23,10 @@ import getComponentFromType from '../../Functions/getComponentFromType.js';
23
23
  export default function withDraggable(WrappedComponent) {
24
24
  return (props) => {
25
25
 
26
+ if (!props.isDraggable) {
27
+ return <WrappedComponent {...props} />;
28
+ }
29
+
26
30
  const {
27
31
  // extract and pass
28
32
  onDragStart,
@@ -327,12 +327,12 @@ export default function withPresetButtons(WrappedComponent, isGrid = false) {
327
327
  {...propsToPass}
328
328
  disablePresetButtons={false}
329
329
  contextMenuItems={[
330
- ...contextMenuItems,
331
330
  ...localContextMenuItems,
331
+ ...contextMenuItems,
332
332
  ]}
333
333
  additionalToolbarButtons={[
334
- ...additionalToolbarButtons,
335
334
  ...localAdditionalToolbarButtons,
335
+ ...additionalToolbarButtons,
336
336
  ]}
337
337
  onChangeColumnsConfig={onChangeColumnsConfigDecorator}
338
338
  />