@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/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.type === 'multiselect' || node.type === 'table') out[name] = []
106
+ else if (listValued(node)) out[name] = []
105
107
  }
106
108
  else if (node.type === 'checkbox') out[name] = false
107
- else if (node.type === 'multiselect' || node.type === 'table') out[name] = []
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.type === 'multiselect' || node.type === 'table') out[name] = []
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 fileLabel(value)
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 !== '')