@onehat/ui 0.2.42 → 0.2.44
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 +1 -1
- package/src/Components/Editor/Editor.js +58 -0
- package/src/Components/Form/Field/Input.js +1 -1
- package/src/Components/Form/Form.js +1 -1
- package/src/Components/Grid/Grid.js +5 -5
- package/src/Components/Grid/GridRow.js +1 -1
- package/src/Components/Hoc/withEditor.js +21 -7
- package/src/Components/Hoc/withPresetButtons.js +45 -7
- package/src/Components/Screens/DataMgt.js +5 -5
- package/src/Hooks/useAdjustedWindowSize.js +18 -0
- package/src/Hooks/useWindowSize.js +25 -0
package/package.json
CHANGED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EDITOR_MODE__VIEW,
|
|
3
|
+
EDITOR_MODE__ADD,
|
|
4
|
+
EDITOR_MODE__EDIT,
|
|
5
|
+
} from '../../Constants/Editor.js';
|
|
6
|
+
import _ from 'lodash';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
export default function Editor(props) {
|
|
10
|
+
const {
|
|
11
|
+
Form,
|
|
12
|
+
Viewer,
|
|
13
|
+
isEditorViewOnly: isViewOnly,
|
|
14
|
+
onEditorCancel: onCancel,
|
|
15
|
+
onEditorSave: onSave,
|
|
16
|
+
onEditorClose: onClose,
|
|
17
|
+
editorMode,
|
|
18
|
+
setEditorMode,
|
|
19
|
+
|
|
20
|
+
// withData
|
|
21
|
+
Repository,
|
|
22
|
+
|
|
23
|
+
// withSelection
|
|
24
|
+
selection,
|
|
25
|
+
|
|
26
|
+
} = props,
|
|
27
|
+
onEditMode = () => {
|
|
28
|
+
setEditorMode(EDITOR_MODE__EDIT);
|
|
29
|
+
},
|
|
30
|
+
onBack = () => {
|
|
31
|
+
setEditorMode(EDITOR_MODE__VIEW);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
if (_.isEmpty(selection)) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (Repository.isRemotePhantomMode && selection.length === 1 && editorMode === EDITOR_MODE__VIEW) {
|
|
39
|
+
return <Viewer
|
|
40
|
+
record={selection[0]}
|
|
41
|
+
Repository={Repository}
|
|
42
|
+
onEditMode={isViewOnly ? null : onEditMode}
|
|
43
|
+
{...props}
|
|
44
|
+
/>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// NOTE: Ideally, this form should use multiple columns when screen is wide enough,
|
|
48
|
+
// and only show in one column when it's not.
|
|
49
|
+
|
|
50
|
+
return <Form
|
|
51
|
+
record={selection}
|
|
52
|
+
onBack={onBack}
|
|
53
|
+
onCancel={onCancel}
|
|
54
|
+
onSave={onSave}
|
|
55
|
+
onClose={onClose}
|
|
56
|
+
{...props}
|
|
57
|
+
/>;
|
|
58
|
+
}
|
|
@@ -14,7 +14,7 @@ function InputElement(props) {
|
|
|
14
14
|
let {
|
|
15
15
|
value,
|
|
16
16
|
setValue,
|
|
17
|
-
autoSubmit =
|
|
17
|
+
autoSubmit = true, // automatically setValue after user stops typing for autoSubmitDelay
|
|
18
18
|
autoSubmitDelay = AUTO_SUBMIT_DELAY,
|
|
19
19
|
maxLength,
|
|
20
20
|
onKeyPress,
|
|
@@ -82,6 +82,7 @@ export function Grid(props) {
|
|
|
82
82
|
hideNavColumn = true,
|
|
83
83
|
noneFoundText,
|
|
84
84
|
disableLoadingIndicator = false,
|
|
85
|
+
disableSelectorSelected = false,
|
|
85
86
|
showRowExpander = false,
|
|
86
87
|
rowExpanderTpl = '',
|
|
87
88
|
showHeaders = true,
|
|
@@ -129,10 +130,9 @@ export function Grid(props) {
|
|
|
129
130
|
isInSelection,
|
|
130
131
|
noSelectorMeansNoResults = false,
|
|
131
132
|
|
|
132
|
-
//
|
|
133
|
+
// DataMgt
|
|
133
134
|
selectorId,
|
|
134
135
|
selectorSelected,
|
|
135
|
-
disableSelectorSelected = false,
|
|
136
136
|
|
|
137
137
|
// withInlineEditor
|
|
138
138
|
inlineEditorRef,
|
|
@@ -751,11 +751,11 @@ export function Grid(props) {
|
|
|
751
751
|
return () => {};
|
|
752
752
|
}
|
|
753
753
|
if (!disableSelectorSelected) {
|
|
754
|
-
let
|
|
754
|
+
let id = selectorSelected?.id;
|
|
755
755
|
if (_.isEmpty(selectorSelected)) {
|
|
756
|
-
|
|
756
|
+
id = noSelectorMeansNoResults ? 'NO_MATCHES' : null;
|
|
757
757
|
}
|
|
758
|
-
Repository.filter(selectorId,
|
|
758
|
+
Repository.filter(selectorId, id, false); // so it doesn't clear existing filters
|
|
759
759
|
}
|
|
760
760
|
|
|
761
761
|
}, [selectorId, selectorSelected]);
|
|
@@ -27,6 +27,10 @@ export default function withEditor(WrappedComponent) {
|
|
|
27
27
|
},
|
|
28
28
|
record,
|
|
29
29
|
|
|
30
|
+
// DataMgt
|
|
31
|
+
selectorId,
|
|
32
|
+
selectorSelected,
|
|
33
|
+
|
|
30
34
|
// withData
|
|
31
35
|
Repository,
|
|
32
36
|
|
|
@@ -47,7 +51,13 @@ export default function withEditor(WrappedComponent) {
|
|
|
47
51
|
}
|
|
48
52
|
const
|
|
49
53
|
defaultValues = Repository.getSchema().model.defaultValues,
|
|
50
|
-
|
|
54
|
+
addValues = _.clone(defaultValues);
|
|
55
|
+
|
|
56
|
+
if (selectorId && !_.isEmpty(selectorSelected)) {
|
|
57
|
+
addValues[selectorId] = selectorSelected.id;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const entity = await Repository.add(addValues, false, true, true);
|
|
51
61
|
setSelection([entity]);
|
|
52
62
|
setIsEditorViewOnly(false);
|
|
53
63
|
setEditorMode(EDITOR_MODE__ADD);
|
|
@@ -118,11 +128,15 @@ export default function withEditor(WrappedComponent) {
|
|
|
118
128
|
} else if (selection.length > 1) {
|
|
119
129
|
// Edit multiple entities
|
|
120
130
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
131
|
+
// Loop through all entities and change fields that are not null
|
|
132
|
+
const propertyNames = Object.getOwnPropertyNames(data);
|
|
133
|
+
_.each(propertyNames, (propertyName) => {
|
|
134
|
+
if (!_.isNil(data[propertyName])) {
|
|
135
|
+
_.each(what, (rec) => {
|
|
136
|
+
rec[propertyName] = data[propertyName]
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
});
|
|
126
140
|
}
|
|
127
141
|
await Repository.save();
|
|
128
142
|
setIsEditorShown(false);
|
|
@@ -148,7 +162,7 @@ export default function withEditor(WrappedComponent) {
|
|
|
148
162
|
// For multiple entities selected, change it to edit multiple mode
|
|
149
163
|
mode = EDITOR_MODE__EDIT;
|
|
150
164
|
}
|
|
151
|
-
} else if (selection.length === 1 && selection.isPhantom) {
|
|
165
|
+
} else if (selection.length === 1 && selection[0].isPhantom) {
|
|
152
166
|
if (!disableAdd) {
|
|
153
167
|
// When a phantom entity is selected, change it to add mode.
|
|
154
168
|
mode = EDITOR_MODE__ADD;
|
|
@@ -33,12 +33,6 @@ export default function withPresetButtons(WrappedComponent) {
|
|
|
33
33
|
} = props,
|
|
34
34
|
{
|
|
35
35
|
// for local use
|
|
36
|
-
selection,
|
|
37
|
-
onAdd,
|
|
38
|
-
onEdit,
|
|
39
|
-
onDelete,
|
|
40
|
-
onView,
|
|
41
|
-
onDuplicate,
|
|
42
36
|
useEditor = true,
|
|
43
37
|
disableAdd = false,
|
|
44
38
|
disableEdit = false,
|
|
@@ -51,6 +45,19 @@ export default function withPresetButtons(WrappedComponent) {
|
|
|
51
45
|
// withEditor
|
|
52
46
|
userCanEdit = true,
|
|
53
47
|
userCanView = true,
|
|
48
|
+
onAdd,
|
|
49
|
+
onEdit,
|
|
50
|
+
onDelete,
|
|
51
|
+
onView,
|
|
52
|
+
onDuplicate,
|
|
53
|
+
|
|
54
|
+
// withSelection
|
|
55
|
+
selection,
|
|
56
|
+
setSelection,
|
|
57
|
+
|
|
58
|
+
// DataMgt
|
|
59
|
+
selectorId,
|
|
60
|
+
selectorSelected,
|
|
54
61
|
} = props,
|
|
55
62
|
[isReady, setIsReady] = useState(false),
|
|
56
63
|
[localContextMenuItems, setLocalContextMenuItems] = useState([]),
|
|
@@ -119,29 +126,59 @@ export default function withPresetButtons(WrappedComponent) {
|
|
|
119
126
|
text = 'Edit';
|
|
120
127
|
handler = onEdit;
|
|
121
128
|
icon = <Edit />;
|
|
129
|
+
if (selectorId && !selectorSelected) {
|
|
130
|
+
isDisabled = true;
|
|
131
|
+
}
|
|
132
|
+
if (_.isEmpty(selection)) {
|
|
133
|
+
isDisabled = true;
|
|
134
|
+
}
|
|
122
135
|
break;
|
|
123
136
|
case 'delete':
|
|
124
137
|
text = 'Delete';
|
|
125
138
|
handler = onDelete;
|
|
126
139
|
icon = <Trash />;
|
|
140
|
+
if (selectorId && !selectorSelected) {
|
|
141
|
+
isDisabled = true;
|
|
142
|
+
}
|
|
143
|
+
if (_.isEmpty(selection) || selection.length > 1) {
|
|
144
|
+
isDisabled = true;
|
|
145
|
+
}
|
|
127
146
|
break;
|
|
128
147
|
case 'view':
|
|
129
148
|
text = 'View';
|
|
130
149
|
handler = onView;
|
|
131
150
|
icon = <Eye />;
|
|
132
151
|
isDisabled = !selection.length || selection.length !== 1;
|
|
152
|
+
if (selectorId && !selectorSelected) {
|
|
153
|
+
isDisabled = true;
|
|
154
|
+
}
|
|
155
|
+
if (_.isEmpty(selection) || selection.length > 1) {
|
|
156
|
+
isDisabled = true;
|
|
157
|
+
}
|
|
133
158
|
break;
|
|
134
159
|
case 'copy':
|
|
135
160
|
text = 'Copy to Clipboard';
|
|
136
161
|
handler = onCopyToClipboard;
|
|
137
162
|
icon = <Clipboard />;
|
|
138
163
|
isDisabled = !selection.length;
|
|
164
|
+
if (selectorId && !selectorSelected) {
|
|
165
|
+
isDisabled = true;
|
|
166
|
+
}
|
|
167
|
+
if (_.isEmpty(selection)) {
|
|
168
|
+
isDisabled = true;
|
|
169
|
+
}
|
|
139
170
|
break;
|
|
140
171
|
case 'duplicate':
|
|
141
172
|
text = 'Duplicate';
|
|
142
173
|
handler = onDuplicate;
|
|
143
174
|
icon = <Duplicate />;
|
|
144
175
|
isDisabled = !selection.length || selection.length !== 1;
|
|
176
|
+
if (selectorId && !selectorSelected) {
|
|
177
|
+
isDisabled = true;
|
|
178
|
+
}
|
|
179
|
+
if (_.isEmpty(selection) || selection.length > 1) {
|
|
180
|
+
isDisabled = true;
|
|
181
|
+
}
|
|
145
182
|
break;
|
|
146
183
|
// case 'print':
|
|
147
184
|
// text = 'Print';
|
|
@@ -210,7 +247,7 @@ export default function withPresetButtons(WrappedComponent) {
|
|
|
210
247
|
if (!isReady) {
|
|
211
248
|
setIsReady(true);
|
|
212
249
|
}
|
|
213
|
-
}, [selection, localColumnsConfig]);
|
|
250
|
+
}, [selection, selectorSelected, localColumnsConfig]);
|
|
214
251
|
|
|
215
252
|
if (!isReady) {
|
|
216
253
|
return null;
|
|
@@ -229,6 +266,7 @@ export default function withPresetButtons(WrappedComponent) {
|
|
|
229
266
|
if (additionalToolbarButtons) {
|
|
230
267
|
additionalToolbarButtonsToPass.concat(additionalToolbarButtons);
|
|
231
268
|
}
|
|
269
|
+
|
|
232
270
|
return <WrappedComponent
|
|
233
271
|
{...propsToPass}
|
|
234
272
|
contextMenuItems={contextMenuItemsToPass}
|
|
@@ -44,8 +44,8 @@ export default function DataMgt(props) {
|
|
|
44
44
|
[isWestCollapsed, setIsWestCollapsed] = useState(westStartsCollapsed),
|
|
45
45
|
[isEastCollapsed, setIsEastCollapsed] = useState(eastStartsCollapsed),
|
|
46
46
|
[isFullscreen, setIsFullscreen] = useState(false),
|
|
47
|
-
[westSelected, setWestSelectedRaw] = useState(
|
|
48
|
-
[centerSelected, setCenterSelected] = useState(
|
|
47
|
+
[westSelected, setWestSelectedRaw] = useState(),
|
|
48
|
+
[centerSelected, setCenterSelected] = useState(),
|
|
49
49
|
setWestSelected = (selected) => {
|
|
50
50
|
setWestSelectedRaw(selected);
|
|
51
51
|
setCenterSelected(); // clear selection in center
|
|
@@ -156,7 +156,7 @@ export default function DataMgt(props) {
|
|
|
156
156
|
autoLoad={!showSelector}
|
|
157
157
|
uniqueRepository={true}
|
|
158
158
|
selectorId={showSelector ? westSelector_id : null}
|
|
159
|
-
selectorSelected={westSelected}
|
|
159
|
+
selectorSelected={westSelected?.[0]}
|
|
160
160
|
noSelectorMeansNoResults={centerNoSelectorMeansNoResults}
|
|
161
161
|
onChangeSelection={setCenterSelected}
|
|
162
162
|
onEvent={onEvent}
|
|
@@ -167,7 +167,7 @@ export default function DataMgt(props) {
|
|
|
167
167
|
isFullscreen,
|
|
168
168
|
showSelector,
|
|
169
169
|
westSelected,
|
|
170
|
-
westSelected?.hash,
|
|
170
|
+
westSelected?.[0]?.hash,
|
|
171
171
|
centerNoSelectorMeansNoResults,
|
|
172
172
|
// {...centerProps}
|
|
173
173
|
])
|
|
@@ -191,7 +191,7 @@ export default function DataMgt(props) {
|
|
|
191
191
|
controlledByCenter = typeof associatedPanel.props.controlledByCenter === 'undefined' ? true : associatedPanel.props.controlledByCenter,
|
|
192
192
|
thisAssociatedPanelProps = {
|
|
193
193
|
selectorId: controlledByCenter ? centerSelector_id : westSelector_id,
|
|
194
|
-
selectorSelected: controlledByCenter ? centerSelected : westSelected,
|
|
194
|
+
selectorSelected: controlledByCenter ? centerSelected?.[0] : westSelected?.[0],
|
|
195
195
|
...associatedPanel.props,
|
|
196
196
|
};
|
|
197
197
|
return React.cloneElement(associatedPanel, { key: ix, reference: 'associatedPanel' + ix, ...allAssociatedPanelProps, ...thisAssociatedPanelProps, });
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import useWindowSize from './useWindowSize.js';
|
|
2
|
+
|
|
3
|
+
// This hook takes the submitted window size and adjusts it
|
|
4
|
+
// to fit the actual screen size
|
|
5
|
+
|
|
6
|
+
export default function(width, height, percentage = 0.9) {
|
|
7
|
+
|
|
8
|
+
const windowSize = useWindowSize();
|
|
9
|
+
|
|
10
|
+
if (width > windowSize.width) {
|
|
11
|
+
width = windowSize.width * percentage;
|
|
12
|
+
}
|
|
13
|
+
if (height > windowSize.height) {
|
|
14
|
+
height = windowSize.height * percentage;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return [ width, height, ];
|
|
18
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// from https://designcode.io/react-hooks-usewindowsize-hook
|
|
2
|
+
|
|
3
|
+
import { useLayoutEffect, useState } from 'react';
|
|
4
|
+
|
|
5
|
+
// For web only!
|
|
6
|
+
export default function useWindowSize() {
|
|
7
|
+
const [windowSize, setWindowSize] = useState({ width: 0, height: 0 });
|
|
8
|
+
|
|
9
|
+
const handleSize = () => {
|
|
10
|
+
setWindowSize({
|
|
11
|
+
width: window.innerWidth,
|
|
12
|
+
height: window.innerHeight
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
useLayoutEffect(() => {
|
|
17
|
+
handleSize();
|
|
18
|
+
|
|
19
|
+
window.addEventListener('resize', handleSize);
|
|
20
|
+
|
|
21
|
+
return () => window.removeEventListener('resize', handleSize);
|
|
22
|
+
}, []);
|
|
23
|
+
|
|
24
|
+
return windowSize;
|
|
25
|
+
};
|