neo.mjs 5.16.3 → 5.16.5
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/apps/ServiceWorker.mjs +2 -2
- package/examples/ServiceWorker.mjs +2 -2
- package/examples/component/statusbadge/MainContainer.mjs +80 -0
- package/examples/component/statusbadge/app.mjs +6 -0
- package/examples/component/statusbadge/index.html +11 -0
- package/examples/component/statusbadge/neo-config.json +6 -0
- package/examples/form/field/fileupload/MainContainer.mjs +49 -12
- package/package.json +1 -1
- package/resources/scss/src/component/StatusBadge.scss +9 -0
- package/resources/scss/src/form/field/FileUpload.scss +3 -2
- package/src/DefaultConfig.mjs +2 -2
- package/src/component/StatusBadge.mjs +73 -0
- package/src/form/field/FileUpload.mjs +167 -15
- package/src/form/field/Text.mjs +1 -1
- package/src/main/addon/ResizeObserver.mjs +29 -4
package/apps/ServiceWorker.mjs
CHANGED
@@ -0,0 +1,80 @@
|
|
1
|
+
import CheckBox from '../../../src/form/field/CheckBox.mjs';
|
2
|
+
import ConfigurationViewport from '../../ConfigurationViewport.mjs';
|
3
|
+
import NumberField from '../../../src/form/field/Number.mjs';
|
4
|
+
import Radio from '../../../src/form/field/Radio.mjs';
|
5
|
+
import StatusBadge from '../../../src/component/StatusBadge.mjs';
|
6
|
+
import TextField from '../../../src/form/field/Text.mjs';
|
7
|
+
|
8
|
+
/**
|
9
|
+
* @class Neo.examples.component.statusbadge.MainContainer
|
10
|
+
* @extends Neo.examples.ConfigurationViewport
|
11
|
+
*/
|
12
|
+
class MainContainer extends ConfigurationViewport {
|
13
|
+
static config = {
|
14
|
+
className : 'Neo.examples.component.statusbadge.MainContainer',
|
15
|
+
autoMount : true,
|
16
|
+
configItemLabelWidth: 110,
|
17
|
+
configItemWidth : 230,
|
18
|
+
layout : {ntype: 'hbox', align: 'stretch'}
|
19
|
+
}
|
20
|
+
|
21
|
+
createConfigurationComponents() {
|
22
|
+
let me = this;
|
23
|
+
|
24
|
+
return [{
|
25
|
+
module : CheckBox,
|
26
|
+
checked : me.exampleComponent.closable,
|
27
|
+
labelText: 'closable',
|
28
|
+
listeners: {change: me.onConfigChange.bind(me, 'closable')}
|
29
|
+
}, {
|
30
|
+
module : NumberField,
|
31
|
+
clearable : true,
|
32
|
+
labelText : 'height',
|
33
|
+
listeners : {change: me.onConfigChange.bind(me, 'height')},
|
34
|
+
maxValue : 100,
|
35
|
+
minValue : 20,
|
36
|
+
stepSize : 2,
|
37
|
+
style : {marginTop: '10px'},
|
38
|
+
value : me.exampleComponent.height
|
39
|
+
}, {
|
40
|
+
module : Radio,
|
41
|
+
checked : me.exampleComponent.state === 'error',
|
42
|
+
hideValueLabel: false,
|
43
|
+
labelText : 'state',
|
44
|
+
listeners : {change: me.onRadioChange.bind(me, 'state', 'error')},
|
45
|
+
name : 'state',
|
46
|
+
style : {marginTop: '10px'},
|
47
|
+
valueLabelText: 'error'
|
48
|
+
}, {
|
49
|
+
module : Radio,
|
50
|
+
checked : me.exampleComponent.badgePosition === 'success',
|
51
|
+
hideValueLabel: false,
|
52
|
+
labelText : '',
|
53
|
+
listeners : {change: me.onRadioChange.bind(me, 'state', 'success')},
|
54
|
+
name : 'state',
|
55
|
+
valueLabelText: 'success'
|
56
|
+
}, {
|
57
|
+
module : NumberField,
|
58
|
+
clearable : true,
|
59
|
+
labelText : 'width',
|
60
|
+
listeners : {change: me.onConfigChange.bind(me, 'width')},
|
61
|
+
maxValue : 300,
|
62
|
+
minValue : 100,
|
63
|
+
stepSize : 5,
|
64
|
+
style : {marginTop: '10px'},
|
65
|
+
value : me.exampleComponent.width
|
66
|
+
}]
|
67
|
+
}
|
68
|
+
|
69
|
+
createExampleComponent() {
|
70
|
+
return Neo.create({
|
71
|
+
module: StatusBadge,
|
72
|
+
height: 30,
|
73
|
+
state : 'error'
|
74
|
+
})
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
Neo.applyClassConfig(MainContainer);
|
79
|
+
|
80
|
+
export default MainContainer;
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<!DOCTYPE HTML>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
5
|
+
<meta charset="UTF-8">
|
6
|
+
<title>Neo StatusBadge</title>
|
7
|
+
</head>
|
8
|
+
<body>
|
9
|
+
<script src="../../../src/MicroLoader.mjs" type="module"></script>
|
10
|
+
</body>
|
11
|
+
</html>
|
@@ -37,9 +37,9 @@ class MainContainer extends ConfigurationViewport {
|
|
37
37
|
module : FileUploadField,
|
38
38
|
id : 'my-downloadable-test',
|
39
39
|
uploadUrl : 'http://127.0.0.1:3000/file-upload-test',
|
40
|
-
documentStatusUrl : 'http://127.0.0.1:3000/document-status-downloadable',
|
41
|
-
documentDeleteUrl : 'http://127.0.0.1:3000/document-delete',
|
42
|
-
downloadUrl : 'http://127.0.0.1:3000/getDocument',
|
40
|
+
documentStatusUrl : 'http://127.0.0.1:3000/document-status-downloadable?documentId={documentId}',
|
41
|
+
documentDeleteUrl : 'http://127.0.0.1:3000/document-delete?documentId={documentId}',
|
42
|
+
downloadUrl : 'http://127.0.0.1:3000/getDocument?documentId={documentId}',
|
43
43
|
width : 350,
|
44
44
|
maxSize : '10mb',
|
45
45
|
types : {
|
@@ -47,14 +47,19 @@ class MainContainer extends ConfigurationViewport {
|
|
47
47
|
jpg : 1,
|
48
48
|
xls : 1,
|
49
49
|
pdf : 1
|
50
|
+
},
|
51
|
+
listeners : {
|
52
|
+
beforeRequest({ headers }) {
|
53
|
+
headers['X-XSRF-TOKEN'] = 'my-xsrf-token'
|
54
|
+
}
|
50
55
|
}
|
51
56
|
}, {
|
52
57
|
module : FileUploadField,
|
53
58
|
id : 'my-not-downloadable-test',
|
54
59
|
uploadUrl : 'http://127.0.0.1:3000/file-upload-test',
|
55
|
-
documentStatusUrl : 'http://127.0.0.1:3000/document-status-not-downloadable',
|
56
|
-
documentDeleteUrl : 'http://127.0.0.1:3000/document-delete',
|
57
|
-
downloadUrl : 'http://127.0.0.1:3000/getDocument',
|
60
|
+
documentStatusUrl : 'http://127.0.0.1:3000/document-status-not-downloadable?documentId={documentId}',
|
61
|
+
documentDeleteUrl : 'http://127.0.0.1:3000/document-delete?documentId={documentId}',
|
62
|
+
downloadUrl : 'http://127.0.0.1:3000/getDocument?documentId={documentId}',
|
58
63
|
width : 350,
|
59
64
|
maxSize : '10mb',
|
60
65
|
types : {
|
@@ -67,9 +72,9 @@ class MainContainer extends ConfigurationViewport {
|
|
67
72
|
module : FileUploadField,
|
68
73
|
id : 'my-upload-fail-test',
|
69
74
|
uploadUrl : 'http://127.0.0.1:3000/file-upload-test-fail',
|
70
|
-
documentStatusUrl : 'http://127.0.0.1:3000/document-status',
|
71
|
-
documentDeleteUrl : 'http://127.0.0.1:3000/document-delete',
|
72
|
-
downloadUrl : 'http://127.0.0.1:3000/getDocument',
|
75
|
+
documentStatusUrl : 'http://127.0.0.1:3000/document-status?documentId={documentId}',
|
76
|
+
documentDeleteUrl : 'http://127.0.0.1:3000/document-delete?documentId={documentId}',
|
77
|
+
downloadUrl : 'http://127.0.0.1:3000/getDocument?documentId={documentId}',
|
73
78
|
width : 350,
|
74
79
|
maxSize : '10mb',
|
75
80
|
types : {
|
@@ -82,9 +87,41 @@ class MainContainer extends ConfigurationViewport {
|
|
82
87
|
module : FileUploadField,
|
83
88
|
id : 'my-scan-fail-test',
|
84
89
|
uploadUrl : 'http://127.0.0.1:3000/file-upload-test',
|
85
|
-
documentStatusUrl : 'http://127.0.0.1:3000/document-status-fail',
|
86
|
-
documentDeleteUrl : 'http://127.0.0.1:3000/document-delete',
|
87
|
-
downloadUrl : 'http://127.0.0.1:3000/getDocument',
|
90
|
+
documentStatusUrl : 'http://127.0.0.1:3000/document-status-fail?documentId={documentId}',
|
91
|
+
documentDeleteUrl : 'http://127.0.0.1:3000/document-delete?documentId={documentId}',
|
92
|
+
downloadUrl : 'http://127.0.0.1:3000/getDocument?documentId={documentId}',
|
93
|
+
width : 350,
|
94
|
+
maxSize : '10mb',
|
95
|
+
types : {
|
96
|
+
png : 1,
|
97
|
+
jpg : 1,
|
98
|
+
xls : 1,
|
99
|
+
pdf : 1
|
100
|
+
}
|
101
|
+
}, {
|
102
|
+
module : FileUploadField,
|
103
|
+
id : 'my-existing-document-test',
|
104
|
+
documentId : 2,
|
105
|
+
uploadUrl : 'http://127.0.0.1:3000/file-upload-test',
|
106
|
+
documentStatusUrl : 'http://127.0.0.1:3000/document-status-not-downloadable?documentId={documentId}',
|
107
|
+
documentDeleteUrl : 'http://127.0.0.1:3000/document-delete?documentId={documentId}',
|
108
|
+
downloadUrl : 'http://127.0.0.1:3000/getDocument?documentId={documentId}',
|
109
|
+
width : 350,
|
110
|
+
maxSize : '10mb',
|
111
|
+
types : {
|
112
|
+
png : 1,
|
113
|
+
jpg : 1,
|
114
|
+
xls : 1,
|
115
|
+
pdf : 1
|
116
|
+
}
|
117
|
+
}, {
|
118
|
+
module : FileUploadField,
|
119
|
+
id : 'my-non-existing-document-test',
|
120
|
+
documentId : 2,
|
121
|
+
uploadUrl : 'http://127.0.0.1:3000/file-upload-test',
|
122
|
+
documentStatusUrl : 'http://127.0.0.1:3000/document-status-non-existent?documentId={documentId}',
|
123
|
+
documentDeleteUrl : 'http://127.0.0.1:3000/document-delete?documentId={documentId}',
|
124
|
+
downloadUrl : 'http://127.0.0.1:3000/getDocument?documentId={documentId}',
|
88
125
|
width : 350,
|
89
126
|
maxSize : '10mb',
|
90
127
|
types : {
|
package/package.json
CHANGED
@@ -125,8 +125,9 @@
|
|
125
125
|
animation: spinner-rotation 3s linear infinite;
|
126
126
|
|
127
127
|
&::after {
|
128
|
-
content
|
129
|
-
flex
|
128
|
+
content : "";
|
129
|
+
flex : 0 0 calc(100% - 6px);
|
130
|
+
background-color : var(--fileuploadfield-background-color);
|
130
131
|
}
|
131
132
|
}
|
132
133
|
}
|
package/src/DefaultConfig.mjs
CHANGED
@@ -245,12 +245,12 @@ const DefaultConfig = {
|
|
245
245
|
useVdomWorker: true,
|
246
246
|
/**
|
247
247
|
* buildScripts/injectPackageVersion.mjs will update this value
|
248
|
-
* @default '5.16.
|
248
|
+
* @default '5.16.5'
|
249
249
|
* @memberOf! module:Neo
|
250
250
|
* @name config.version
|
251
251
|
* @type String
|
252
252
|
*/
|
253
|
-
version: '5.16.
|
253
|
+
version: '5.16.5'
|
254
254
|
};
|
255
255
|
|
256
256
|
Object.assign(DefaultConfig, {
|
@@ -0,0 +1,73 @@
|
|
1
|
+
import Base from '../component/Base.mjs';
|
2
|
+
import NeoArray from '../util/Array.mjs';
|
3
|
+
|
4
|
+
/**
|
5
|
+
* @class Neo.component.StatusBadge
|
6
|
+
* @extends Neo.component.Base
|
7
|
+
*/
|
8
|
+
class StatusBadge extends Base {
|
9
|
+
/**
|
10
|
+
* Valid values for state
|
11
|
+
* @member {String[]} states=['error','neutral','success']
|
12
|
+
* @protected
|
13
|
+
* @static
|
14
|
+
*/
|
15
|
+
static states = ['error', 'neutral', 'success']
|
16
|
+
|
17
|
+
static config = {
|
18
|
+
/**
|
19
|
+
* @member {String} className='Neo.component.StatusBadge'
|
20
|
+
* @protected
|
21
|
+
*/
|
22
|
+
className: 'Neo.component.StatusBadge',
|
23
|
+
/**
|
24
|
+
* @member {String} ntype='status-badge'
|
25
|
+
* @protected
|
26
|
+
*/
|
27
|
+
ntype: 'status-badge',
|
28
|
+
/**
|
29
|
+
* @member {String[]} baseCls=['neo-status-badge']
|
30
|
+
* @protected
|
31
|
+
*/
|
32
|
+
baseCls: ['neo-status-badge'],
|
33
|
+
/**
|
34
|
+
* @member {String} state_='neutral'
|
35
|
+
*/
|
36
|
+
state_: 'neutral',
|
37
|
+
/**
|
38
|
+
* @member {Object} vdom
|
39
|
+
*/
|
40
|
+
vdom:
|
41
|
+
{}
|
42
|
+
}
|
43
|
+
|
44
|
+
/**
|
45
|
+
* Triggered after the state config got changed
|
46
|
+
* @param {String} value
|
47
|
+
* @param {String} oldValue
|
48
|
+
* @protected
|
49
|
+
*/
|
50
|
+
afterSetState(value, oldValue) {
|
51
|
+
let cls = this.cls;
|
52
|
+
|
53
|
+
NeoArray.remove(cls, 'neo-state-' + oldValue);
|
54
|
+
NeoArray.add(cls, 'neo-state-' + value);
|
55
|
+
|
56
|
+
this.cls = cls
|
57
|
+
}
|
58
|
+
|
59
|
+
/**
|
60
|
+
* Triggered before the state config gets changed
|
61
|
+
* @param {String} value
|
62
|
+
* @param {String} oldValue
|
63
|
+
* @returns {String}
|
64
|
+
* @protected
|
65
|
+
*/
|
66
|
+
beforeSetState(value, oldValue) {
|
67
|
+
return this.beforeSetEnumValue(value, oldValue, 'state')
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
Neo.applyClassConfig(StatusBadge);
|
72
|
+
|
73
|
+
export default StatusBadge;
|
@@ -14,7 +14,7 @@ const
|
|
14
14
|
* An accessible file uploading widget which automatically commences an upload as soon as
|
15
15
|
* a file is selected using the UI.
|
16
16
|
*
|
17
|
-
* The URL to which the file must be uploaded is specified in the {@link #
|
17
|
+
* The URL to which the file must be uploaded is specified in the {@link config#uploadUrl} property.
|
18
18
|
* This service must return a JSON status response in the following form for successful uploads:
|
19
19
|
*
|
20
20
|
* ```json
|
@@ -130,6 +130,35 @@ class FileUpload extends Base {
|
|
130
130
|
|
131
131
|
cls : [],
|
132
132
|
|
133
|
+
/**
|
134
|
+
* An Object containing a default set of headers to be passed to the server on every HTTP request.
|
135
|
+
* @member {Object} headers
|
136
|
+
*/
|
137
|
+
headers_ : {},
|
138
|
+
|
139
|
+
/**
|
140
|
+
* An Object which allows the status text returned from the {@link #property-documentStatusUrl} to be
|
141
|
+
* mapped to the corresponding next widget state.
|
142
|
+
* @member {Object} documentStatusMap
|
143
|
+
*/
|
144
|
+
documentStatusMap : {
|
145
|
+
SCANNING : 'scanning',
|
146
|
+
MALWARE_DETECTED : 'scan-failed',
|
147
|
+
UN_DOWNLOADABLE : 'not-downloadable',
|
148
|
+
DOWNLOADABLE : 'downloadable',
|
149
|
+
DELETED : 'deleted'
|
150
|
+
},
|
151
|
+
|
152
|
+
/**
|
153
|
+
* If this widget should reference an existing document, configure the widget with a documentId
|
154
|
+
* so that it can initialize in the correct "uploaded" state.
|
155
|
+
*
|
156
|
+
* If this is *not* configured, then this property will be set after a successful upload to
|
157
|
+
* the id returned from the {@link #property-uploadUrl}.
|
158
|
+
* @member {String|Number} documentId
|
159
|
+
*/
|
160
|
+
documentId : null,
|
161
|
+
|
133
162
|
/**
|
134
163
|
* The URL of the file upload service to which the selected file is sent.
|
135
164
|
*
|
@@ -153,8 +182,10 @@ class FileUpload extends Base {
|
|
153
182
|
|
154
183
|
/**
|
155
184
|
* The name of the JSON property in which the document id is returned in the upload response
|
156
|
-
* JSON packet and the
|
157
|
-
* deletion.
|
185
|
+
* JSON packet and the token string which is substituted for the document id when requesting
|
186
|
+
* a malware scan and a document deletion.
|
187
|
+
*
|
188
|
+
* Defaults fro `documentId`
|
158
189
|
*
|
159
190
|
* @member {String} downloadUrl
|
160
191
|
*/
|
@@ -163,6 +194,17 @@ class FileUpload extends Base {
|
|
163
194
|
/**
|
164
195
|
* The URL from which the file may be downloaded after it has finished its scan.
|
165
196
|
*
|
197
|
+
* This must contain a substitution token named the same as the {@link #property-documentIdParameter}
|
198
|
+
* which is used when creating a URL
|
199
|
+
*
|
200
|
+
* for example:
|
201
|
+
*
|
202
|
+
* ```json
|
203
|
+
* {
|
204
|
+
* downloadUrl : '/getDocument/${documentId}'
|
205
|
+
* }
|
206
|
+
* ```
|
207
|
+
*
|
166
208
|
* The document id returned from the {@link #member-uploadUrl upload} is passed in the parameter named
|
167
209
|
* by the {@link #member-documentIdParameter}. It defaults to `'documentId'`.
|
168
210
|
*
|
@@ -173,6 +215,17 @@ class FileUpload extends Base {
|
|
173
215
|
/**
|
174
216
|
* The URL of the file status reporting service.
|
175
217
|
*
|
218
|
+
* This must contain a substitution token named the same as the {@link #property-documentIdParameter}
|
219
|
+
* which is used when creating a URL
|
220
|
+
*
|
221
|
+
* for example:
|
222
|
+
*
|
223
|
+
* ```json
|
224
|
+
* {
|
225
|
+
* documentStatusUrl : '/getDocumentStatus/${documentId}'
|
226
|
+
* }
|
227
|
+
* ```
|
228
|
+
*
|
176
229
|
* This widget will use this service after a successful upload to determine its next
|
177
230
|
* state.
|
178
231
|
*
|
@@ -188,9 +241,30 @@ class FileUpload extends Base {
|
|
188
241
|
*/
|
189
242
|
documentStatusUrl : null,
|
190
243
|
|
244
|
+
/**
|
245
|
+
* The polling interval *in milliseconds* to wait between asking the server how the document scan
|
246
|
+
* is proceeding.
|
247
|
+
*
|
248
|
+
* Defaults to 2000ms
|
249
|
+
*
|
250
|
+
* @member {String} documentDeleteUrl
|
251
|
+
*/
|
252
|
+
statusScanInterval : 2000,
|
253
|
+
|
191
254
|
/**
|
192
255
|
* The URL of the file deletion service.
|
193
256
|
*
|
257
|
+
* This must contain a substitution token named the same as the {@link #property-documentIdParameter}
|
258
|
+
* which is used when creating a URL
|
259
|
+
*
|
260
|
+
* for example:
|
261
|
+
*
|
262
|
+
* ```json
|
263
|
+
* {
|
264
|
+
* documentDeleteUrl : '/deleteDocument/${documentId}'
|
265
|
+
* }
|
266
|
+
* ```
|
267
|
+
*
|
194
268
|
* This widget will use this service after a successful upload to determine its next
|
195
269
|
* state.
|
196
270
|
*
|
@@ -230,6 +304,13 @@ class FileUpload extends Base {
|
|
230
304
|
{ input : me.onInputValueChange, scope: me},
|
231
305
|
{ click : me.onActionButtonClick, delegate : '.neo-file-upload-action-button', scope : me}
|
232
306
|
]);
|
307
|
+
|
308
|
+
// If we are to reference an existing document, start by asking the server about its
|
309
|
+
// state. Widget state will proceed from there.
|
310
|
+
if (me.documentId) {
|
311
|
+
me.state = 'processing';
|
312
|
+
me.checkDocumentStatus();
|
313
|
+
}
|
233
314
|
}
|
234
315
|
|
235
316
|
async clear() {
|
@@ -288,7 +369,8 @@ class FileUpload extends Base {
|
|
288
369
|
me = this,
|
289
370
|
xhr = me.xhr = new XMLHttpRequest(),
|
290
371
|
{ upload } = xhr,
|
291
|
-
fileData = new FormData()
|
372
|
+
fileData = new FormData(),
|
373
|
+
headers = { ...me.headers };
|
292
374
|
|
293
375
|
// Show the action button
|
294
376
|
me.state = 'starting';
|
@@ -311,6 +393,23 @@ class FileUpload extends Base {
|
|
311
393
|
|
312
394
|
xhr.open("POST", me.uploadUrl, true);
|
313
395
|
|
396
|
+
/**
|
397
|
+
* This event fires before every HTTP request is sent to the server via any of the configured URLs.
|
398
|
+
*
|
399
|
+
*
|
400
|
+
* @event beforeRequest
|
401
|
+
* @param {Object} event The event
|
402
|
+
* @param {Object} event.headers An object containing the configured {@link #property-headers}
|
403
|
+
* for this widget, into which new headers may be injected.
|
404
|
+
* @returns {Object}
|
405
|
+
*/
|
406
|
+
me.fire('beforeRequest', {
|
407
|
+
headers
|
408
|
+
});
|
409
|
+
for (const header in headers) {
|
410
|
+
xhr.setRequestHeader(header, headers[header]);
|
411
|
+
}
|
412
|
+
|
314
413
|
xhr.send(fileData);
|
315
414
|
}
|
316
415
|
|
@@ -404,39 +503,77 @@ class FileUpload extends Base {
|
|
404
503
|
}
|
405
504
|
|
406
505
|
async deleteDocument() {
|
506
|
+
const
|
507
|
+
me = this,
|
508
|
+
headers = { ...me.headers },
|
509
|
+
url = me.createUrl(me.documentDeleteUrl, {
|
510
|
+
[me.documentIdParameter] : me.documentId
|
511
|
+
});
|
512
|
+
|
513
|
+
me.fire('beforeRequest', {
|
514
|
+
headers
|
515
|
+
});
|
516
|
+
|
407
517
|
// We ask the server to delete using our this.documentId
|
408
|
-
const statusResponse = await fetch(
|
518
|
+
const statusResponse = await fetch(url, {
|
519
|
+
headers
|
520
|
+
});
|
409
521
|
|
410
522
|
// Success
|
411
523
|
if (String(statusResponse.status).slice(0, 1) === '2') {
|
412
|
-
|
413
|
-
|
524
|
+
me.clear();
|
525
|
+
me.state = 'ready';
|
414
526
|
}
|
415
527
|
else {
|
416
|
-
|
528
|
+
me.error = `Document delete service error: ${statusResponse.statusText}`;
|
417
529
|
}
|
418
530
|
}
|
419
531
|
|
420
532
|
async checkDocumentStatus() {
|
421
|
-
const
|
533
|
+
const
|
534
|
+
me = this,
|
535
|
+
headers = { ...me.headers },
|
536
|
+
url = me.createUrl(me.documentStatusUrl, {
|
537
|
+
[me.documentIdParameter] : me.documentId
|
538
|
+
});
|
539
|
+
|
540
|
+
if (me.state === 'processing') {
|
541
|
+
me.fire('beforeRequest', {
|
542
|
+
headers
|
543
|
+
});
|
422
544
|
|
423
|
-
|
424
|
-
|
545
|
+
const statusResponse = await fetch(url, {
|
546
|
+
headers
|
547
|
+
});
|
425
548
|
|
426
549
|
// Success
|
427
550
|
if (String(statusResponse.status).slice(0, 1) === '2') {
|
428
|
-
const
|
551
|
+
const
|
552
|
+
serverJson = await statusResponse.json(),
|
553
|
+
serverStatus = serverJson.status,
|
554
|
+
// Map the server's states codes to our own status codes
|
555
|
+
status = me.documentStatusMap[serverStatus] || serverStatus;
|
429
556
|
|
430
557
|
switch (status) {
|
431
558
|
case 'scanning':
|
432
|
-
setTimeout(() => me.checkDocumentStatus(),
|
559
|
+
setTimeout(() => me.checkDocumentStatus(), me.statusScanInterval);
|
560
|
+
break;
|
561
|
+
case 'deleted':
|
562
|
+
me.error = `Document ${me.documentId} is no longer available`;
|
563
|
+
me.state = 'ready';
|
433
564
|
break;
|
434
565
|
default:
|
566
|
+
const { fileName, size } = serverJson;
|
567
|
+
|
568
|
+
if (fileName) {
|
569
|
+
me.vdom.cn[1].cn[0].innerHTML = fileName;
|
570
|
+
me.fileSize = me.formatSize(size);
|
571
|
+
}
|
435
572
|
me.state = status;
|
436
573
|
}
|
437
574
|
}
|
438
575
|
else {
|
439
|
-
|
576
|
+
me.error = `Document status service error: ${statusResponse.statusText}`;
|
440
577
|
}
|
441
578
|
}
|
442
579
|
}
|
@@ -473,7 +610,9 @@ class FileUpload extends Base {
|
|
473
610
|
break;
|
474
611
|
case 'downloadable':
|
475
612
|
anchor.tag = 'a';
|
476
|
-
anchor.href =
|
613
|
+
anchor.href = me.createUrl(me.downloadUrl, {
|
614
|
+
[me.documentIdParameter] : me.documentId
|
615
|
+
});
|
477
616
|
status.innerHTML = me.fileSize;
|
478
617
|
break;
|
479
618
|
case 'not-downloadable':
|
@@ -490,6 +629,19 @@ class FileUpload extends Base {
|
|
490
629
|
me.cls = cls;
|
491
630
|
}
|
492
631
|
|
632
|
+
/**
|
633
|
+
* Creates a URL substituting the passed parameter names in at the places where the name
|
634
|
+
* occurs within `{}` in the pattern.
|
635
|
+
* @param {String} urlPattern
|
636
|
+
* @param {Object} params
|
637
|
+
*/
|
638
|
+
createUrl(urlPattern, params) {
|
639
|
+
for (const paramName in params) {
|
640
|
+
urlPattern = urlPattern.replace(`{${paramName}}`, params[paramName]);
|
641
|
+
}
|
642
|
+
return urlPattern;
|
643
|
+
}
|
644
|
+
|
493
645
|
beforeGetMaxSize(maxSize) {
|
494
646
|
// Not configured means no limit
|
495
647
|
if (maxSize == null) {
|
package/src/form/field/Text.mjs
CHANGED
@@ -665,7 +665,7 @@ class Text extends Base {
|
|
665
665
|
|
666
666
|
me.silentVdomUpdate = true;
|
667
667
|
|
668
|
-
me.validate(false);
|
668
|
+
!me.clean && me.validate(false);
|
669
669
|
me.changeInputElKey('required', value ? value : null);
|
670
670
|
me.labelText = me.labelText; // apply the optional text if needed
|
671
671
|
|
@@ -49,21 +49,46 @@ class NeoResizeObserver extends Base {
|
|
49
49
|
}
|
50
50
|
|
51
51
|
/**
|
52
|
-
* Internal callback for the ResizeObserver instance
|
52
|
+
* Internal callback for the ResizeObserver instance.
|
53
|
+
* See: https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry
|
53
54
|
* @param {HTMLElement[]} entries
|
54
55
|
* @param {ResizeObserver} observer
|
55
56
|
* @protected
|
56
57
|
*/
|
57
58
|
onResize(entries, observer) {
|
58
59
|
entries.forEach(entry => {
|
60
|
+
// the content of entry is not spreadable, so we need to manually convert it
|
61
|
+
// structuredClone(entry) throws a JS error => ResizeObserverEntry object could not be cloned.
|
62
|
+
|
63
|
+
let borderBoxSize = entry.borderBoxSize[0],
|
64
|
+
contentBoxSize = entry.contentBoxSize[0],
|
65
|
+
devicePixelContentBoxSize = entry.devicePixelContentBoxSize?.[0] || {}, // Not supported in Safari yet
|
66
|
+
path = DomEvents.getPathFromElement(entry.target).map(e => DomEvents.getTargetData(e));
|
67
|
+
|
59
68
|
Neo.worker.Manager.sendMessage('app', {
|
60
69
|
action : 'domEvent',
|
61
70
|
eventName: 'resize',
|
62
71
|
|
63
72
|
data: {
|
64
|
-
|
65
|
-
|
66
|
-
|
73
|
+
contentRect: DomEvents.parseDomRect(entry.contentRect),
|
74
|
+
id : entry.target.id,
|
75
|
+
path,
|
76
|
+
rect : path[0].rect,
|
77
|
+
|
78
|
+
borderBoxSize: {
|
79
|
+
blockSize : borderBoxSize.blockSize,
|
80
|
+
inlineSize: borderBoxSize.inlineSize
|
81
|
+
},
|
82
|
+
|
83
|
+
contentBoxSize: {
|
84
|
+
blockSize : contentBoxSize.blockSize,
|
85
|
+
inlineSize: contentBoxSize.inlineSize
|
86
|
+
},
|
87
|
+
|
88
|
+
devicePixelContentBoxSize: {
|
89
|
+
blockSize : devicePixelContentBoxSize.blockSize,
|
90
|
+
inlineSize: devicePixelContentBoxSize.inlineSize
|
91
|
+
}
|
67
92
|
}
|
68
93
|
})
|
69
94
|
})
|