/** * AutoForm Core Component */ const AUTOFORM_BLUEPRINT = globalThis.Util.makeDom(/*html*/`
`) const AUTOFORM_STYLE = globalThis.Util.makeDom(/*html*/``) const DB_FORM_TYPE_MAP = { i: 'number', ui: 'number', bi: 'number', ubi: 'number', ti: 'number', f: 'number', ff: 'number', b: 'switch', d: 'date', dt: 'datetime', t: 'textarea' }; const getDbFormType = type => DB_FORM_TYPE_MAP[type] || 'text'; // Database field definitions are converted only for rendering. Form data keeps // its original keys, so submitting an AutoForm still returns the API row shape. const normalizeSchemaItem = field => { if (!field?.tableID) return field; const settings = field.settings || {}; return { ...field, name: field.name, label: settings.label ?? field.label ?? field.name, type: settings.type ?? getDbFormType(field.type), options: settings.options ?? field.options, placeholder: settings.placeholder ?? field.placeholder, setting: settings.attrs ?? field.setting }; }; globalThis.Component.register('AutoForm', container => { if (!Array.isArray(container.state.schema)) container.state.schema = [] const setSchema = schema => { container.state._schema = Array.isArray(schema) ? schema.map(normalizeSchemaItem) : []; }; container.state.__watch('schema', setSchema) setSchema(container.state.schema) const ensureProxy = v => (v && typeof v === 'object' && !v.__isProxy) ? globalThis.NewState(v) : v; const setData = value => { const data = ensureProxy(value || {}) container.data = data if (container.state.data !== data) container.state.data = data } container.state.__watch('data', setData) setData(container.state.data) container.vertical = container.hasAttribute('vertical') container.horizontal = container.hasAttribute('horizontal') container.inline = container.hasAttribute('inline') container.nobutton = container.hasAttribute('nobutton') container.submitlabel = container.getAttribute('submitlabel') || '' container.request = { method: 'POST' } container.response = {} container.result = null container.form = globalThis.$(container, 'form') container.submit = (opt = {}) => { if (!container.form.reportValidity()) return globalThis.Toast?.show('{#verify failed#}', { type: 'danger' }) if (!container.dispatchEvent(new CustomEvent('submit', { detail: container.data, cancelable: true, bubbles: false }))) return const req = { ...container.request, data: container.data, noui: true, ...opt } let task = null if (container.api) task = container.api.do(req) else if (container.request.url) task = globalThis.HTTP.request(req) else return console.warn('{#please config .api or .request.url to auto submit#}') task.then(resp => { container.response = resp container.result = resp.result if (typeof resp.result === 'object' && resp.result.error) throw new Error(resp.result.error) container.dispatchEvent(new CustomEvent('response', { detail: resp, bubbles: false })) }).catch(err => { globalThis.Toast?.show(err.message, { type: 'danger' }) container.dispatchEvent(new CustomEvent('error', { detail: err, bubbles: true })) }) } }, AUTOFORM_BLUEPRINT, AUTOFORM_STYLE) const findAnchorInBlueprint = (root) => { let f = root.querySelector('[control-wrapper]'); if (f) return f; for (const t of root.querySelectorAll('template')) { f = findAnchorInBlueprint(t.content); if (f) return f; } return null; } const AutoForm = { customTypes: [], register: (name, typeName) => { const type = typeName || name if (!AutoForm.customTypes.find(t => t.name === name)) { AutoForm.customTypes.push({ name, typeName: type }) AutoForm._addAutoFormComponent(name, type) } }, _addAutoFormComponent: (name, type) => { const wrapper = findAnchorInBlueprint(AUTOFORM_BLUEPRINT) if (wrapper) { const node = globalThis.Util.makeDom(``) wrapper.appendChild(node) } } } globalThis.AutoForm = AutoForm;