UNPKG

@formschema/native

Version:

Vue component form based on JSON Schema

375 lines (336 loc) 9.29 kB
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable no-use-before-define */ /** * VueMaterial Elements sample for FormSchema */ export default function createComponents(NativeComponents) { const components = new NativeComponents(); components.set('boolean', CheckboxElement); components.set('string', InputElement); components.set('password', InputElement); components.set('file', FileElement); components.set('image', FileElement); components.set('radio', RadioElement); components.set('checkbox', CheckboxElement); components.set('number', InputElement); components.set('integer', InputElement); components.set('list', ListElement); components.set('textarea', TextareaElement); components.set('button', ButtonElement); return components; } function computeEvents(field, listeners, type) { const events = { ...listeners, [type]: [ (value) => { if (!(value instanceof Array)) { field.setValue(value); } } ] }; if (listeners[type]) { if (listeners[type] instanceof Array) { events[type].unshift(...listeners[type]); } else { events[type].unshift(listeners[type]); } } return events; } const Helper = { functional: true, render(h, { props: { field } }) { if (field.descriptor.helper) { return h('span', { attrs: field.descriptor.helperAttrs, staticClass: 'md-helper-text' }, field.descriptor.helper); } return []; } }; const FieldElement = { name: 'FieldElement', functional: true, render(h, { data, props: { field }, children }) { if (field.kind === 'hidden' || field.descriptor.definition.kind === 'hidden') { return children; } const props = { mdInline: false, mdClearable: false, mdCounter: true, mdTogglePassword: true, ...field.descriptor.props }; const nodes = []; if (field.descriptor.label) { nodes.push(h('label', { attrs: field.descriptor.labelAttrs }, field.descriptor.label)); } nodes.push(h(Helper, data)); nodes.push(...children); return h('MdField', { props }, nodes); } }; const InputElement = { name: 'InputElement', functional: true, render(h, { props: { field }, data, listeners }) { if (field.descriptor.definition.kind === 'hidden') { return h('input', { attrs: { ...field.descriptor.attrs, type: 'hidden' } }); } switch (field.schema.format) { case 'date': case 'date-time': return h('MdDatepicker', { attrs: field.descriptor.attrs, props: { id: field.descriptor.attrs.id, value: field.value, mdModelType: String, mdImmediately: true }, on: computeEvents(field, listeners, 'input'), staticStyle: { zIndex: 15 } }, [ h('label', { attrs: field.descriptor.labelAttrs }, field.descriptor.label) ]); } return h(FieldElement, data, [ h('MdInput', { attrs: field.descriptor.attrs, props: { ...field.descriptor.props, id: field.descriptor.attrs.id, type: field.descriptor.attrs.type, value: field.value, mdCounter: field.schema.maxLength }, on: computeEvents(field, listeners, 'input') }) ]); } }; const TextareaElement = { name: 'TextareaElement', functional: true, render(h, { props: { field }, data, listeners }) { const autogrow = 'autogrow' in field.descriptor.props ? field.descriptor.props.autogrow : field.schema.contentMediaType === 'text/plain'; return h(FieldElement, data, [ h('MdTextarea', { attrs: field.descriptor.attrs, props: { id: field.descriptor.attrs.id, value: field.value, mdAutogrow: autogrow, mdCounter: field.schema.maxLength }, on: computeEvents(field, listeners, 'input') }) ]); } }; const StateElement = (tag) => ({ name: 'StateElement', functional: true, render(h, { props: { field }, listeners, children = [] }) { const nodeTag = field.descriptor.props.tag || tag; const options = { disabled: field.root && field.root.disabled, required: field.required }; switch (nodeTag) { case 'MdRadio': options.model = field.parent.value; options.value = field.attrs.value || field.value; break; case 'MdSwitch': if (field.parent && field.parent.kind === 'array') { options.model = field.parent.value; options.value = field.attrs.value || field.value; } else { options.model = field.value; } return h('div', { key: field.key, staticStyle: { display: 'flex', flexDirection: 'row' } }, [ h('div', { staticStyle: { display: 'flex', flexDirection: 'column', flexGrow: 1, margin: '5px 0' } }, [ h('label', { attrs: { for: field.id } }, field.descriptor.label), field.descriptor.helper ? h('span', field.descriptor.helper) : undefined ]), h(nodeTag, { staticClass: 'md-primary', staticStyle: { margin: 0 }, props: options, attrs: field.attrs, on: computeEvents(field, listeners, 'change') }) ]); default: options.model = field.value; options.indeterminate = false; } if (field.descriptor.helper) { return h('div', { key: field.key, staticStyle: { display: 'flex', flexDirection: 'row' } }, [ h(nodeTag, { staticClass: 'md-primary', props: options, attrs: field.attrs, on: computeEvents(field, listeners, 'change') }), h('div', { staticStyle: { display: 'flex', flexDirection: 'column' } }, [ h('label', { attrs: { for: field.id }, staticStyle: { cursor: 'pointer' } }, field.descriptor.label), h('span', field.descriptor.helper) ]) ]); } return [ h(nodeTag, { key: field.key, staticClass: 'md-primary', props: options, attrs: field.attrs, on: computeEvents(field, listeners, 'change') }, field.descriptor.label), ...children ]; } }); const RadioElement = StateElement('MdRadio'); const SwitchElement = StateElement('MdSwitch'); const CheckboxElement = StateElement('MdCheckbox'); const ButtonType = { push: 'add', moveUp: 'keyboard_arrow_up', moveDown: 'keyboard_arrow_down', delete: 'delete' }; const ButtonElement = { name: 'ButtonElement', functional: true, render(h, { props: { button } }) { const children = [ h('MdIcon', ButtonType[button.type] || button.type) ]; const klass = button.type === 'push' ? 'md-icon-button md-raised' : 'md-icon-button md-dense'; return h('MdButton', { staticClass: klass, props: { disabled: button.disabled }, attrs: { title: button.tooltip }, on: { click: () => button.trigger() }, style: { margin: button.type === 'push' ? '0 0 10px' : '0' } }, children); } }; const ListElement = { name: 'ListElement', functional: true, render(h, { data, props: { field }, listeners }) { const children = field.descriptor.options .filter(({ value }) => value !== '') .map(({ label, value, selected }) => h('MdOption', { attrs: { value, selected } }, label)); return h(FieldElement, data, [ h('MdSelect', { attrs: field.descriptor.attrs, props: { ...field.descriptor.props, value: field.value }, on: computeEvents(field, listeners, 'md-selected') }, children) ]); } }; const FileElement = { name: 'FileElement', functional: true, render(h, { props: { field }, data, listeners }) { const nodes = []; if (nodes.length) { if (field.descriptor.label) { nodes.unshift(h('label', { attrs: { for: field.descriptor.attrs.id } }, field.descriptor.label)); } if (field.descriptor.helper) { nodes.push(h('p', { staticStyle: { color: 'rgba(0, 0, 0, .54)', fontSize: '12px' } }, field.descriptor.helper)); } return h('div', { staticStyle: { marginBottom: '20px' } }, nodes); } return h(FieldElement, data, [ h('MdFile', { staticClass: data.staticClass, attrs: field.descriptor.attrs, props: { ...field.descriptor.props, id: field.descriptor.attrs.id, value: field.value }, on: computeEvents(field, listeners, 'change') }) ]); } };