@xeplr/ui-factory 1.8.5 → 1.8.7
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/AUTHORING.md +5 -1
- package/README.md +97 -13
- package/package.json +1 -1
- package/src/buttons.js +105 -3
- package/src/controls.js +52 -4
- package/src/designs/ButtonsView.jsx +69 -6
- package/src/designs/ControlView.jsx +26 -44
- package/src/designs/FedList.jsx +70 -4
- package/src/designs/FileField.jsx +99 -0
- package/src/designs/PropertyPanel.jsx +127 -3
- package/src/designs/ScreenSample.jsx +1 -0
- package/src/designs/factory.css +81 -10
- package/src/listRange.js +85 -0
- package/src/model.js +6 -4
- package/src/pages.jsx +7 -0
- package/src/remote.js +22 -7
- package/src/tableSchema.js +4 -2
- package/src/urlState.js +2 -2
- package/src/useFactoryScreen.js +47 -12
- package/src/useFilePreview.js +40 -0
- package/src/validateDocument.js +112 -12
- package/src/values.js +27 -8
package/src/values.js
CHANGED
|
@@ -20,7 +20,7 @@ export function formSchema(doc, controls = CONTROLS) {
|
|
|
20
20
|
const def = controls[node.type]
|
|
21
21
|
const p = node.props
|
|
22
22
|
const field = { name: p.name }
|
|
23
|
-
if (def.valueType) field.type = def.valueType
|
|
23
|
+
if (def.valueType) field.type = isMultiFile(node) ? 'array' : def.valueType
|
|
24
24
|
if (p.label) field.description = p.label
|
|
25
25
|
if (p.required) field.required = true
|
|
26
26
|
if (p.default !== undefined) field.default = p.default
|
|
@@ -41,6 +41,8 @@ export function formSchema(doc, controls = CONTROLS) {
|
|
|
41
41
|
delete v.notFuture
|
|
42
42
|
if (Object.keys(v).length) field.validation = v
|
|
43
43
|
}
|
|
44
|
+
// Several files: a list of stored paths, no longer than the field holds.
|
|
45
|
+
if (isMultiFile(node)) field.validation = { ...(field.validation || {}), itemType: 'string', ...(p.maxFiles ? { maxItems: p.maxFiles } : {}) }
|
|
44
46
|
return field
|
|
45
47
|
})
|
|
46
48
|
}
|
|
@@ -101,10 +103,10 @@ export function initialValues(doc, provided = {}, controls = CONTROLS, previous
|
|
|
101
103
|
if (resolved !== undefined) out[name] = resolved
|
|
102
104
|
else if (!isBinding(node.props.default)) out[name] = node.props.default
|
|
103
105
|
else if (node.type === 'checkbox') out[name] = false
|
|
104
|
-
else if (node
|
|
106
|
+
else if (listValued(node)) out[name] = []
|
|
105
107
|
}
|
|
106
108
|
else if (node.type === 'checkbox') out[name] = false
|
|
107
|
-
else if (node
|
|
109
|
+
else if (listValued(node)) out[name] = []
|
|
108
110
|
})
|
|
109
111
|
return out
|
|
110
112
|
}
|
|
@@ -116,7 +118,7 @@ export function initialValues(doc, provided = {}, controls = CONTROLS, previous
|
|
|
116
118
|
*/
|
|
117
119
|
export function parseInput(node, raw) {
|
|
118
120
|
if (node.type === 'checkbox') return Boolean(raw)
|
|
119
|
-
if (node.type === 'multiselect') return Array.isArray(raw) ? raw : fromDbValue(node, raw) || []
|
|
121
|
+
if (node.type === 'multiselect' || isMultiFile(node)) return Array.isArray(raw) ? raw : fromDbValue(node, raw) || []
|
|
120
122
|
if (node.type === 'table') return Array.isArray(raw) ? raw : []
|
|
121
123
|
if (raw === undefined || raw === null || raw === '') return undefined
|
|
122
124
|
if (node.type === 'number') {
|
|
@@ -166,6 +168,13 @@ export function fieldError(node, value, loadedOptions) {
|
|
|
166
168
|
if (v.maxItems != null && ticked.length > v.maxItems) return `${label}: tick at most ${v.maxItems}`
|
|
167
169
|
return null
|
|
168
170
|
}
|
|
171
|
+
// SEVERAL FILES: required is at least one, and no more than it holds.
|
|
172
|
+
if (isMultiFile(node)) {
|
|
173
|
+
const files = Array.isArray(value) ? value : []
|
|
174
|
+
if (!files.length) return p.required ? `${label} is required` : null
|
|
175
|
+
if (p.maxFiles && files.length > p.maxFiles) return `${label}: at most ${p.maxFiles} files`
|
|
176
|
+
return null
|
|
177
|
+
}
|
|
169
178
|
if (node.type === 'multiselect') {
|
|
170
179
|
const chosen = Array.isArray(value) ? value : []
|
|
171
180
|
if (!chosen.length) return p.required ? `${label} is required` : null
|
|
@@ -286,7 +295,7 @@ export function recordValues(doc, record, controls = CONTROLS) {
|
|
|
286
295
|
const v = record ? record[name] : undefined
|
|
287
296
|
if (v !== undefined && v !== null) out[name] = fromDbValue(node, v)
|
|
288
297
|
else if (node.type === 'checkbox') out[name] = false
|
|
289
|
-
else if (node
|
|
298
|
+
else if (listValued(node)) out[name] = []
|
|
290
299
|
})
|
|
291
300
|
return out
|
|
292
301
|
}
|
|
@@ -329,7 +338,7 @@ export function displayValue(node, value, options) {
|
|
|
329
338
|
if (value === undefined || value === null || value === '') return ''
|
|
330
339
|
if (!node) return typeof value === 'object' ? JSON.stringify(value) : value
|
|
331
340
|
if (node.type === 'checkbox') return value ? 'Yes' : 'No'
|
|
332
|
-
if (node.type === 'file') return
|
|
341
|
+
if (node.type === 'file') return (isMultiFile(node) ? fromDbValue(node, value) || [] : [value]).map(fileLabel).join(', ')
|
|
333
342
|
if (node.type === 'datetime') return String(value).replace('T', ' ').slice(0, 16)
|
|
334
343
|
if (node.type === 'dropdown' || node.type === 'radio') return optionName(node, value, options)
|
|
335
344
|
if (node.type === 'multiselect') {
|
|
@@ -344,6 +353,16 @@ function optionName(node, value, options) {
|
|
|
344
353
|
return hit ? hit.name : String(value)
|
|
345
354
|
}
|
|
346
355
|
|
|
356
|
+
/** A file field that holds several files: its value is a list of stored paths. */
|
|
357
|
+
export function isMultiFile(node) {
|
|
358
|
+
return Boolean(node && node.type === 'file' && node.props && node.props.multiple)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/** A field whose value is a list: a multi-select, a table's ticked rows, several files. */
|
|
362
|
+
function listValued(node) {
|
|
363
|
+
return node.type === 'multiselect' || node.type === 'table' || isMultiFile(node)
|
|
364
|
+
}
|
|
365
|
+
|
|
347
366
|
/** The name at the end of a stored file path, without the id that keeps it apart. */
|
|
348
367
|
export function fileLabel(path) {
|
|
349
368
|
const s = String(path)
|
|
@@ -361,7 +380,7 @@ export function fileLabel(path) {
|
|
|
361
380
|
/** A screen value → what its column stores. */
|
|
362
381
|
export function toDbValue(node, value) {
|
|
363
382
|
if (value === undefined || value === null) return value
|
|
364
|
-
if (node.type === 'multiselect') {
|
|
383
|
+
if (node.type === 'multiselect' || isMultiFile(node)) {
|
|
365
384
|
const list = Array.isArray(value) ? value : [value]
|
|
366
385
|
return list.length ? list.map((v) => String(v)).join(MULTI_SEPARATOR) : null
|
|
367
386
|
}
|
|
@@ -371,7 +390,7 @@ export function toDbValue(node, value) {
|
|
|
371
390
|
/** What a column holds → the screen's value. */
|
|
372
391
|
export function fromDbValue(node, value) {
|
|
373
392
|
if (value === undefined || value === null) return value
|
|
374
|
-
if (node.type === 'multiselect') {
|
|
393
|
+
if (node.type === 'multiselect' || isMultiFile(node)) {
|
|
375
394
|
if (Array.isArray(value)) return value
|
|
376
395
|
const s = String(value)
|
|
377
396
|
return s === '' ? [] : s.split(MULTI_SEPARATOR).filter((x) => x !== '')
|