UNPKG

formio-sfds

Version:
226 lines (202 loc) • 6.49 kB
const dot = require('dotmap') class UIString { static key (component, path) { return `${component.key}.${path}` } constructor (value, component, path, context) { if (!value) { throw new Error('UIString() requires a value as its first argument') } else if (!(component instanceof Object)) { throw new Error('UIString() requires a component as its second argument') } else if (!component.key) { throw new Error('UIString() requires a component object with a "key" property') } Object.assign(this, { value, component, path, _context: context }) } get type () { return 'string' } get key () { return this.path ? UIString.key(this.component, this.path) : this.value } get context () { return this._context && UIString.key(this.component, this._context) } get label () { const componentLabel = this.component ? this.component.label : undefined const context = this.component ? `${this.component.type} ${this.path}` : this.path if (this.path && this.path.startsWith('values.')) { return `${this.value} (in "${componentLabel || 'unknown'}" ${context})` } else if (componentLabel) { return `${componentLabel} (${context})` } return this.key } get isPageComponent () { return this.component && this.component.type === 'panel' } get pageComponent () { return this.isPageComponent ? this.component : [].concat(this.parents).find(c => c && c.type === 'panel') } get page () { const { pageComponent } = this return pageComponent ? pluck(pageComponent, ['key', 'title']) : undefined } } class StringInterpolation extends UIString { constructor (value, str) { const { component, path, context } = str super(value, component, path, context) this.string = str } get type () { return 'interpolation' } get key () { return this.value } get context () { return super.key } } module.exports = { getCondition, getStrings, UIString, StringInterpolation } function getStrings (form) { const fields = [ fieldGetter('label', (label, component, path) => { switch (component.type) { case 'content': // we don't show labels for content components case 'htmlelement': // we don't show labels for HTML elements case 'panel': // panels use the title, not the label return [] default: if (label && !component.hideLabel) { return [new UIString(label, component, path)] } } }), fieldGetter('title', (value, component, path) => { if (component.type === 'panel' && value) { return [new UIString(value, component, path)] } }), fieldGetter('description'), fieldGetter('html'), fieldGetter('content'), fieldGetter('suffix'), fieldGetter('values', (values, component, path) => { return Array.isArray(values) && values.map(({ label, value }) => { return label && new UIString(label, component, `values.${value}`) }).filter(Boolean) }), fieldGetter('data', (data, component, path) => { const { dataSrc } = component const { values } = data if (Array.isArray(values) && (!dataSrc || dataSrc === 'values')) { return values.map(({ label, value }) => { return label && new UIString(label, component, `values.${value}`) }).filter(Boolean) } else if (values) { console.info('Skipping data for component "%s" (dataSrc: "%s"), length: %d', component.key, dataSrc, values.length) } else { console.info('No values for data component "%s" (dataSrc: "%s")', component.key, dataSrc, values) } }), fieldGetter('validate.customMessage'), fieldGetter('errorLabel'), fieldGetter('errors', (errors, component, path) => { const strings = [] for (const [type, value] of Object.entries(errors)) { if (value) { strings.push(new UIString(value, component, `errors.${type}`)) } } return strings }), fieldGetter('customError', (customError, component, path) => { return customError ? [new UIString(customError, component, path)] : [] }), fieldGetterAlias('properties.displayTitle', 'displayTitle', 'panel'), fieldGetterAlias('properties.backTitle', 'form.backTitle') ] const all = [] eachComponent(form.components, (component, index, parents) => { for (const getFieldStrings of fields) { const strings = getFieldStrings(component) .filter(str => str && str.value) for (const str of strings) { Object.assign(str, { parents }) } all.push(...strings) } }) for (const str of all) { str.value.replace(/\$t\((.*?)\)/g, (_, key) => { all.push(new StringInterpolation(key, str)) }) } return all function fieldGetter (path, get) { if (get) { return component => { const value = dot.get(component, path) return value ? get(value, component, path) || [] : [] } } else { return component => { const value = dot.get(component, path) return value ? [new UIString(value, component, path)] : [] } } } function fieldGetterAlias (path, key, filter = () => true) { if (typeof filter === 'string') { const type = filter filter = component => component.type === type } return component => { const value = dot.get(component, path) return value && filter(component, value) ? [new UIString(value, component, key)] : [] } } } function eachComponent (components, iter, parents = []) { for (const [index, component] of Object.entries(components)) { const next = iter(component, index, parents || []) if (next === true) return component else if (next === false) return let children = component.components if (!children && component.columns) { children = component.columns.reduce((list, column) => list.concat(column.components), []) } if (children && children.length) { eachComponent(children, iter, parents.concat(component)) } } } function getCondition (component) { const { customConditional, conditional } = component return customConditional || (conditional && conditional.show ? conditional : null) } function pluck (obj, keys) { return Object.fromEntries( keys.map(key => [key, obj[key]]) ) }