@onehat/ui 0.3.8 → 0.3.9
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 +9 -6
- package/src/Components/Editor/Viewer.js +4 -1
- package/src/Components/Form/Form.js +14 -9
- package/src/Components/Hoc/withEditor.js +1 -1
- package/src/Components/Hoc/withPresetButtons.js +1 -1
- package/src/Constants/Styles.js +1 -0
- package/src/PlatformImports/Web/Attachments.js +198 -83
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onehat/ui",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9",
|
|
4
4
|
"description": "Base UI for OneHat apps",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -25,14 +25,17 @@
|
|
|
25
25
|
},
|
|
26
26
|
"license": "UNLICENSED",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@
|
|
28
|
+
"@gluestack-style/react": "^0.2.38",
|
|
29
|
+
"@gluestack-ui/themed": "^0.1.21",
|
|
29
30
|
"@hookform/resolvers": "^3.3.1",
|
|
30
31
|
"@k-renwick/colour-mixer": "^1.2.1",
|
|
32
|
+
"@onehat/data": "^1.19.0",
|
|
31
33
|
"@reduxjs/toolkit": "^1.9.5",
|
|
32
|
-
"js-cookie": "^3.0.5",
|
|
33
34
|
"inflector-js": "^1.0.1",
|
|
35
|
+
"js-cookie": "^3.0.5",
|
|
34
36
|
"native-base": "^3.4.28",
|
|
35
37
|
"react-hook-form": "^7.45.0",
|
|
38
|
+
"react-native-svg": "^13.13.0",
|
|
36
39
|
"react-redux": "^8.1.2",
|
|
37
40
|
"yup": "^1.2.0"
|
|
38
41
|
},
|
|
@@ -42,10 +45,10 @@
|
|
|
42
45
|
"react": "*",
|
|
43
46
|
"react-color": "^2.19.3",
|
|
44
47
|
"react-datetime": "^3.2.0",
|
|
45
|
-
"react-draggable": "^4.4.5",
|
|
46
|
-
"react-native-draggable": "^3.3.0",
|
|
47
48
|
"react-dom": "*",
|
|
48
|
-
"react-
|
|
49
|
+
"react-draggable": "^4.4.5",
|
|
50
|
+
"react-native": "*",
|
|
51
|
+
"react-native-draggable": "^3.3.0"
|
|
49
52
|
},
|
|
50
53
|
"devDependencies": {
|
|
51
54
|
"@babel/core": "^7.22.1",
|
|
@@ -25,6 +25,9 @@ export default function Viewer(props) {
|
|
|
25
25
|
// withData
|
|
26
26
|
record,
|
|
27
27
|
|
|
28
|
+
// parent container
|
|
29
|
+
selectorSelected,
|
|
30
|
+
|
|
28
31
|
// withEditor
|
|
29
32
|
onEditMode,
|
|
30
33
|
onClose,
|
|
@@ -39,7 +42,7 @@ export default function Viewer(props) {
|
|
|
39
42
|
let {
|
|
40
43
|
type,
|
|
41
44
|
title = null,
|
|
42
|
-
selectorId,
|
|
45
|
+
selectorId = null,
|
|
43
46
|
...propsToPass
|
|
44
47
|
} = item;
|
|
45
48
|
const
|
|
@@ -89,7 +89,7 @@ function Form(props) {
|
|
|
89
89
|
onDelete,
|
|
90
90
|
editorStateRef,
|
|
91
91
|
|
|
92
|
-
//
|
|
92
|
+
// parent container
|
|
93
93
|
selectorId,
|
|
94
94
|
selectorSelected,
|
|
95
95
|
|
|
@@ -115,10 +115,10 @@ function Form(props) {
|
|
|
115
115
|
// resetField,
|
|
116
116
|
// setError,
|
|
117
117
|
// clearErrors,
|
|
118
|
-
|
|
118
|
+
setValue: formSetValue,
|
|
119
119
|
// setFocus,
|
|
120
|
-
getValues,
|
|
121
|
-
getFieldState,
|
|
120
|
+
getValues: formGetValues,
|
|
121
|
+
// getFieldState,
|
|
122
122
|
// trigger,
|
|
123
123
|
} = useForm({
|
|
124
124
|
mode: 'onChange', // onChange | onBlur | onSubmit | onTouched | all
|
|
@@ -204,6 +204,7 @@ function Form(props) {
|
|
|
204
204
|
if (_.isPlainObject(editor)) {
|
|
205
205
|
const {
|
|
206
206
|
type,
|
|
207
|
+
onChange: onEditorChange,
|
|
207
208
|
...p
|
|
208
209
|
} = editor;
|
|
209
210
|
editorProps = p;
|
|
@@ -217,6 +218,9 @@ function Form(props) {
|
|
|
217
218
|
value={value}
|
|
218
219
|
setValue={(newValue) => {
|
|
219
220
|
onChange(newValue);
|
|
221
|
+
if (onEditorChange) {
|
|
222
|
+
onEditorChange(newValue, formSetValue, formGetValues, formState);
|
|
223
|
+
}
|
|
220
224
|
}}
|
|
221
225
|
onBlur={onBlur}
|
|
222
226
|
selectorId={selectorId}
|
|
@@ -256,6 +260,7 @@ function Form(props) {
|
|
|
256
260
|
isEditable = true,
|
|
257
261
|
label,
|
|
258
262
|
items,
|
|
263
|
+
onChange: onEditorChange,
|
|
259
264
|
...propsToPass
|
|
260
265
|
} = item;
|
|
261
266
|
let editorTypeProps = {};
|
|
@@ -363,10 +368,10 @@ function Form(props) {
|
|
|
363
368
|
let element = <Element
|
|
364
369
|
name={name}
|
|
365
370
|
value={value}
|
|
366
|
-
onChangeValue={(
|
|
367
|
-
onChange(
|
|
368
|
-
if (
|
|
369
|
-
|
|
371
|
+
onChangeValue={(newValue) => {
|
|
372
|
+
onChange(newValue);
|
|
373
|
+
if (onEditorChange) {
|
|
374
|
+
onEditorChange(newValue, formSetValue, formGetValues, formState);
|
|
370
375
|
}
|
|
371
376
|
}}
|
|
372
377
|
onBlur={onBlur}
|
|
@@ -419,7 +424,7 @@ function Form(props) {
|
|
|
419
424
|
Element = getComponentFromType(type),
|
|
420
425
|
element = <Element
|
|
421
426
|
selectorId={selectorId}
|
|
422
|
-
selectorSelected={
|
|
427
|
+
selectorSelected={selectorSelected || record}
|
|
423
428
|
flex={1}
|
|
424
429
|
{...propsToPass}
|
|
425
430
|
/>;
|
package/src/Constants/Styles.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useState, useEffect, } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Box,
|
|
4
|
-
|
|
4
|
+
Button,
|
|
5
|
+
Column,
|
|
5
6
|
Row,
|
|
6
|
-
Text,
|
|
7
|
-
Tooltip,
|
|
8
7
|
} from 'native-base';
|
|
9
8
|
import {
|
|
10
9
|
CURRENT_MODE,
|
|
@@ -20,6 +19,13 @@ import { Avatar, Dropzone, FileMosaic, FileCard, FileInputButton, } from "@files
|
|
|
20
19
|
import withData from '../../Components/Hoc/withData.js';
|
|
21
20
|
import _ from 'lodash';
|
|
22
21
|
|
|
22
|
+
const
|
|
23
|
+
EXPANDED_MAX = 100,
|
|
24
|
+
COLLAPSED_MAX = 2;
|
|
25
|
+
|
|
26
|
+
// Note this component uploads only one file per server request---
|
|
27
|
+
// it doesn't upload multiple files simultaneously.
|
|
28
|
+
|
|
23
29
|
function AttachmentsElement(props) {
|
|
24
30
|
|
|
25
31
|
if (CURRENT_MODE !== UI_MODE_WEB) {
|
|
@@ -30,105 +36,214 @@ function AttachmentsElement(props) {
|
|
|
30
36
|
canCrud = true,
|
|
31
37
|
_dropZone = {},
|
|
32
38
|
_fileMosaic = {},
|
|
33
|
-
accept
|
|
39
|
+
accept, // 'image/*'
|
|
34
40
|
maxFiles = null,
|
|
35
|
-
maxFileSize = 28 * 1024,
|
|
36
41
|
disabled = false,
|
|
37
42
|
clickable = true,
|
|
38
|
-
|
|
43
|
+
|
|
44
|
+
// parentContainer
|
|
45
|
+
selectorSelected,
|
|
39
46
|
|
|
40
47
|
// withData
|
|
41
48
|
Repository,
|
|
42
49
|
|
|
43
50
|
} = props,
|
|
44
51
|
styles = UiGlobals.styles,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
52
|
+
model = selectorSelected?.repository.name,
|
|
53
|
+
modelid = selectorSelected?.id,
|
|
54
|
+
[isReady, setIsReady] = useState(false),
|
|
55
|
+
[isUploading, setIsUploading] = useState(false),
|
|
56
|
+
[showAll, setShowAll] = useState(false),
|
|
57
|
+
[files, setFiles] = useState([]),
|
|
58
|
+
buildFiles = () => {
|
|
59
|
+
const files = _.map(Repository.entities, (entity) => {
|
|
60
|
+
return {
|
|
61
|
+
id: entity.id, // string | number The identifier of the file
|
|
62
|
+
// file: null, // File The file object obtained from client drop or selection
|
|
63
|
+
name: entity.attachments__filename, // string The name of the file
|
|
64
|
+
type: entity.attachments__mimetype, // string The file mime type.
|
|
65
|
+
size: entity.attachments__size, // number The size of the file in bytes.
|
|
66
|
+
// valid: null, // boolean If present, it will show a valid or rejected message ("valid", "denied"). By default valid is undefined.
|
|
67
|
+
// errors: null, // string[] The list of errors according to the validation criteria or the result of the given custom validation function.
|
|
68
|
+
// uploadStatus: null, // UPLOADSTATUS The current upload status. (e.g. "uploading").
|
|
69
|
+
// uploadMessage: null, // string A message that shows the result of the upload process.
|
|
70
|
+
imageUrl: entity.attachments__uri, // string A string representation or web url of the image that will be set to the "src" prop of an <img/> tag. If given, the component will use this image source instead of reading the image file.
|
|
71
|
+
downloadUrl: entity.attachments__uri, // string The url to be used to perform a GET request in order to download the file. If defined, the download icon will be shown.
|
|
72
|
+
// progress: null, // number The current percentage of upload progress. This value will have a higher priority over the upload progress value calculated inside the component.
|
|
73
|
+
// extraUploadData: null, // Record<string, any> The additional data that will be sent to the server when files are uploaded individually
|
|
74
|
+
// extraData: null, // Object Any kind of extra data that could be needed.
|
|
75
|
+
// serverResponse: null, // ServerResponse The upload response from server.
|
|
76
|
+
// xhr: null, // XMLHttpRequest A reference to the XHR object that allows the upload, progress and abort events.
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
setFiles(files);
|
|
80
|
+
},
|
|
81
|
+
toggleShowAll = () => {
|
|
82
|
+
setShowAll(!showAll);
|
|
83
|
+
},
|
|
84
|
+
onDropzoneChange = (files) => {
|
|
85
|
+
setFiles(files);
|
|
86
|
+
_.each(files, (file) => {
|
|
87
|
+
file.extraUploadData = {
|
|
88
|
+
model,
|
|
89
|
+
modelid,
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
onUploadStart = (files) => {
|
|
94
|
+
setIsUploading(true);
|
|
95
|
+
},
|
|
96
|
+
onUploadFinish = (files) => {
|
|
97
|
+
let isDoneUploading = true;
|
|
98
|
+
|
|
99
|
+
_.each(files, (file) => {
|
|
100
|
+
if (!file.xhr || file.xhr.status !== 200) {
|
|
101
|
+
isDoneUploading = false;
|
|
102
|
+
return false; // break
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
if (isDoneUploading) {
|
|
107
|
+
setIsUploading(false);
|
|
108
|
+
Repository.reload();
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
onFileDelete = (id) => {
|
|
112
|
+
Repository.deleteById(id);
|
|
72
113
|
};
|
|
73
114
|
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
|
|
117
|
+
if (!model) {
|
|
118
|
+
return () => {};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
(async () => {
|
|
122
|
+
|
|
123
|
+
// Load Repository
|
|
124
|
+
const filters = [
|
|
125
|
+
{
|
|
126
|
+
name: 'model',
|
|
127
|
+
value: model,
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: 'modelid',
|
|
131
|
+
value: modelid,
|
|
132
|
+
},
|
|
133
|
+
];
|
|
134
|
+
if (accept) {
|
|
135
|
+
let name,
|
|
136
|
+
mimetypes;
|
|
137
|
+
if (_.isString(accept)) {
|
|
138
|
+
name = 'mimetype LIKE';
|
|
139
|
+
mimetypes = accept.replace('*', '%');
|
|
140
|
+
} else if (_.isArray(accept)) {
|
|
141
|
+
name = 'mimetype IN';
|
|
142
|
+
mimetypes = accept;
|
|
143
|
+
}
|
|
144
|
+
filters.push({
|
|
145
|
+
name,
|
|
146
|
+
value: mimetypes,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
Repository.filter(filters);
|
|
150
|
+
Repository.setPageSize(showAll ? EXPANDED_MAX : COLLAPSED_MAX);
|
|
151
|
+
await Repository.load();
|
|
152
|
+
|
|
153
|
+
buildFiles();
|
|
154
|
+
|
|
155
|
+
if (!isReady) {
|
|
156
|
+
setIsReady(true);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
})();
|
|
160
|
+
|
|
161
|
+
Repository.on('load', buildFiles);
|
|
162
|
+
return () => {
|
|
163
|
+
Repository.off('load', buildFiles);
|
|
164
|
+
};
|
|
165
|
+
}, [model, modelid, showAll]);
|
|
166
|
+
|
|
167
|
+
if (!isReady) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
if (canCrud) {
|
|
173
|
+
_fileMosaic.onDelete = onFileDelete;
|
|
174
|
+
}
|
|
175
|
+
let content = <Column
|
|
176
|
+
w="100%"
|
|
177
|
+
// minHeight={50}
|
|
178
|
+
p={2}
|
|
179
|
+
background={styles.ATTACHMENTS_BG}
|
|
180
|
+
>
|
|
181
|
+
<Row flexWrap="wrap">
|
|
182
|
+
{files.map((file) => {
|
|
183
|
+
return <Box
|
|
184
|
+
key={file.id}
|
|
185
|
+
marginRight={4}
|
|
186
|
+
>
|
|
187
|
+
<FileMosaic
|
|
188
|
+
{...file}
|
|
189
|
+
backgroundBlurImage={false}
|
|
190
|
+
{..._fileMosaic}
|
|
191
|
+
/>
|
|
192
|
+
</Box>;
|
|
193
|
+
})}
|
|
194
|
+
</Row>
|
|
195
|
+
{Repository.total <= COLLAPSED_MAX ? null :
|
|
196
|
+
<Button
|
|
197
|
+
onPress={toggleShowAll}
|
|
198
|
+
marginTop={4}
|
|
199
|
+
_text={{
|
|
200
|
+
color: 'trueGray.600',
|
|
201
|
+
fontStyle: 'italic',
|
|
202
|
+
textAlign: 'left',
|
|
203
|
+
width: '100%',
|
|
204
|
+
}}
|
|
205
|
+
variant="ghost"
|
|
206
|
+
>{'Show ' + (showAll ? ' Less' : ' All ' + Repository.total)}</Button>}
|
|
207
|
+
</Column>;
|
|
208
|
+
|
|
74
209
|
if (canCrud) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
info
|
|
103
|
-
{..._fileMosaic}
|
|
104
|
-
/>;
|
|
105
|
-
})}
|
|
106
|
-
</Dropzone>;
|
|
210
|
+
content = <Dropzone
|
|
211
|
+
value={files}
|
|
212
|
+
onChange={onDropzoneChange}
|
|
213
|
+
accept={accept}
|
|
214
|
+
maxFiles={maxFiles}
|
|
215
|
+
maxFileSize={styles.ATTACHMENTS_MAX_FILESIZE}
|
|
216
|
+
autoClean={true}
|
|
217
|
+
uploadConfig={{
|
|
218
|
+
url: Repository.api.baseURL + Repository.name + '/uploadAttachment',
|
|
219
|
+
method: 'POST',
|
|
220
|
+
headers: Repository.headers,
|
|
221
|
+
autoUpload: true,
|
|
222
|
+
}}
|
|
223
|
+
headerConfig={{
|
|
224
|
+
deleteFiles: false,
|
|
225
|
+
}}
|
|
226
|
+
onUploadStart={onUploadStart}
|
|
227
|
+
onUploadFinish={onUploadFinish}
|
|
228
|
+
background={styles.ATTACHMENTS_BG}
|
|
229
|
+
color={styles.ATTACHMENTS_COLOR}
|
|
230
|
+
minHeight={150}
|
|
231
|
+
footer={false}
|
|
232
|
+
clickable={clickable}
|
|
233
|
+
{..._dropZone}
|
|
234
|
+
>
|
|
235
|
+
{content}
|
|
236
|
+
</Dropzone>;
|
|
107
237
|
|
|
108
238
|
}
|
|
109
|
-
|
|
110
|
-
return <Row
|
|
111
|
-
flex={1}
|
|
112
|
-
minHeight={150}
|
|
113
|
-
background={styles.ATTACHMENTS_BG}
|
|
114
|
-
color={styles.ATTACHMENTS_COLOR}
|
|
115
|
-
>
|
|
116
|
-
{files.map((file) => {
|
|
117
|
-
return <WhichFile
|
|
118
|
-
key={file.id}
|
|
119
|
-
{...file}
|
|
120
|
-
onDelete={removeFile}
|
|
121
|
-
info
|
|
122
|
-
{..._fileMosaic}
|
|
123
|
-
/>;
|
|
124
|
-
})}
|
|
125
|
-
</Row>;
|
|
239
|
+
return content;
|
|
126
240
|
}
|
|
127
241
|
|
|
128
242
|
function withAdditionalProps(WrappedComponent) {
|
|
129
243
|
return (props) => {
|
|
130
244
|
return <WrappedComponent
|
|
131
245
|
model="Attachments"
|
|
246
|
+
uniqueRepository={true}
|
|
132
247
|
{...props}
|
|
133
248
|
/>;
|
|
134
249
|
};
|