ui/components/base/AutoForm.min.js

2 lines
7.6 KiB
JavaScript

const AUTOFORM_BLUEPRINT=globalThis.Util.makeDom('\n<div class="auto-form-root">\n <form $class="${this.inline ? \'d-flex flex-wrap align-items-center gap-3\' : (this.vertical ? \'d-flex flex-column\' : (this.horizontal ? \'auto-grid-form forced-horizontal\' : \'auto-grid-form\'))}" $onsubmit="event.stopPropagation();event.preventDefault();this.submit()">\n\n <template $each="this.state._schema || []">\n <template $$if="item.if || \'true\'">\n <template $if="item.type === \'divider\'">\n <div style="grid-column:1/-1" class="pt-2 pb-1"><hr class="my-2"><div $if="item.label" $text="item.label" class="small fw-semibold text-muted text-uppercase"></div></div>\n </template>\n <template $if="item.type !== \'divider\'">\n <div style="display:contents">\n <label $name="item.name" $class="${this.inline ? \'mb-0 text-muted text-nowrap\' : \'col-form-label text-muted\'}" $text="item.label"></label>\n <div control-wrapper $class="${this.inline ? \'d-flex align-items-center\' : \'mb-3 d-flex align-items-center\'}">\n <template $if="[\'text\', \'password\', \'email\', \'number\', \'date\', \'datetime\', \'file\'].includes(item.type)"><input $name="item.name" $type="item.type" $.="item.setting || {}" $bind="this.state.data[item.name]" $class="form-control ${item.type === \'number\' ? \'text-end\' : \'\'}"></template>\n\n <template $if="item.type === \'select\'"><select $name="item.name" $.="item.setting || {}" $bind="this.state.data[item.name]" class="form-select">\n <option value="" $if="item.placeholder" $text="item.placeholder" disabled selected></option>\n <option $each="item.options" as="opt" $value="opt.value !== undefined ? opt.value : opt" $text="opt.label || opt"></option>\n </select></template>\n\n <template $if="[\'checkbox\', \'radio\'].includes(item.type)"><div class="d-flex align-items-center flex-wrap gap-3 h-100" style="padding: 0 0.75rem; min-height: calc(2.25rem + 2px);">\n <label $each="item.options || [item.text||item.label||item.name]" as="opt" class="form-check mb-0 d-flex align-items-center" style="padding-left:0; cursor:pointer;">\n <input $name="item.name" class="form-check-input m-0 me-2" style="float:none;" $type="item.type" $.="item.setting || {}" $value="item.options ? (opt.value !== undefined ? opt.value : opt) : \'on\'" $bind="this.state.data[item.name]">\n <span $if="!this.inline || (item.options && item.options.length > 0)" $text="opt.label || opt" class="form-check-label"></span>\n </label>\n </div></template>\n\n <template $if="item.type === \'switch\'"><div class="form-check form-switch fs-5 d-flex align-items-center m-0" style="padding: 0 0.75rem; min-height: calc(2.25rem + 2px); display:flex !important;">\n <input $name="item.name" class="form-check-input m-0" style="float:none; cursor:pointer" type="checkbox" $bind="this.state.data[item.name]">\n </div></template>\n\n <template $if="item.type === \'textarea\'"><textarea $name="item.name" class="form-control" $.="item.setting || {}" $bind="this.state.data[item.name]"></textarea></template>\n\n <template $if="item.type === \'label\'"><span $name="item.name" $.="item.setting || {}" $text="item.value ?? this.state.data[item.name] ?? \'\'" class="form-control-plaintext text-break py-1"></span></template>\n </div>\n </div>\n </template>\n </template>\n </template>\n\n <div $class="${this.inline ? \'\' : \'d-flex justify-content-end align-items-baseline gap-3 mt-2\'}" $style="${this.inline ? \'\' : \'grid-column:1/-1\'}">\n <div slot-id="actions"></div>\n <button $if="!this.nobutton" type="submit" class="btn btn-primary" $text="this.submitlabel || \'{#Submit#}\'"></button>\n </div>\n </form>\n</div>\n'),AUTOFORM_STYLE=globalThis.Util.makeDom("<style>\n .auto-grid-form { display: block; }\n .auto-grid-form .col-form-label { text-align: left; margin-bottom: 0.25rem; padding-top: 0; }\n\n @media (min-width: 576px) {\n .auto-grid-form { display: grid; grid-template-columns: max-content 1fr; gap: 0 1.5rem; }\n .auto-grid-form .col-form-label { text-align: right; margin-bottom: 1rem; padding-right: 0; max-width: 200px; padding-top: calc(0.375rem + 1px); }\n .auto-grid-form [control-wrapper] { min-height: calc(2.25rem + 2px); }\n }\n\n .auto-grid-form.forced-horizontal { display: grid !important; grid-template-columns: max-content 1fr !important; gap: 0 1.5rem !important; }\n .auto-grid-form.forced-horizontal .col-form-label { text-align: right !important; margin-bottom: 1rem !important; padding-right: 0 !important; max-width: 200px !important; padding-top: calc(0.375rem + 1px) !important; }\n .auto-grid-form.forced-horizontal [control-wrapper] { min-height: calc(2.25rem + 2px) !important; }\n\n .auto-form-root .form-check-input { width: 1.2em; height: 1.2em; }\n .auto-form-root .form-switch .form-check-input { width: 2em; }\n</style>"),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"},getDbFormType=t=>DB_FORM_TYPE_MAP[t]||"text",normalizeSchemaItem=t=>{if(!t?.tableID)return t;const e=t.settings||{};return{...t,name:t.name,label:e.label??t.label??t.name,type:e.type??getDbFormType(t.type),options:e.options??t.options,placeholder:e.placeholder??t.placeholder,setting:e.attrs??t.setting}};globalThis.Component.register("AutoForm",t=>{Array.isArray(t.state.schema)||(t.state.schema=[]);const e=e=>{t.state._schema=Array.isArray(e)?e.map(normalizeSchemaItem):[]};t.state.__watch("schema",e),e(t.state.schema);const a=e=>{const a=(i=e||{})&&"object"==typeof i&&!i.__isProxy?globalThis.NewState(i):i;var i;t.data=a,t.state.data!==a&&(t.state.data=a)};t.state.__watch("data",a),a(t.state.data),t.vertical=t.hasAttribute("vertical"),t.horizontal=t.hasAttribute("horizontal"),t.inline=t.hasAttribute("inline"),t.nobutton=t.hasAttribute("nobutton"),t.submitlabel=t.getAttribute("submitlabel")||"",t.request={method:"POST"},t.response={},t.result=null,t.form=globalThis.$(t,"form"),t.submit=(e={})=>{if(!t.form.reportValidity())return globalThis.Toast?.show("{#verify failed#}",{type:"danger"});if(!t.dispatchEvent(new CustomEvent("submit",{detail:t.data,cancelable:!0,bubbles:!1})))return;const a={...t.request,data:t.data,noui:!0,...e};let i=null;if(t.api)i=t.api.do(a);else{if(!t.request.url)return console.warn("{#please config .api or .request.url to auto submit#}");i=globalThis.HTTP.request(a)}i.then(e=>{if(t.response=e,t.result=e.result,"object"==typeof e.result&&e.result.error)throw new Error(e.result.error);t.dispatchEvent(new CustomEvent("response",{detail:e,bubbles:!1}))}).catch(e=>{globalThis.Toast?.show(e.message,{type:"danger"}),t.dispatchEvent(new CustomEvent("error",{detail:e,bubbles:!0}))})}},AUTOFORM_BLUEPRINT,AUTOFORM_STYLE);const findAnchorInBlueprint=t=>{let e=t.querySelector("[control-wrapper]");if(e)return e;for(const a of t.querySelectorAll("template"))if(e=findAnchorInBlueprint(a.content),e)return e;return null},AutoForm={customTypes:[],register:(t,e)=>{const a=e||t;AutoForm.customTypes.find(e=>e.name===t)||(AutoForm.customTypes.push({name:t,typeName:a}),AutoForm._addAutoFormComponent(t,a))},_addAutoFormComponent:(t,e)=>{const a=findAnchorInBlueprint(AUTOFORM_BLUEPRINT);if(a){const i=globalThis.Util.makeDom(`<template $if="item.type?.toLowerCase() === '${e.toLowerCase()}'"><${t} $name="item.name" $.="item.setting || {}" $bind="thisNode.closest('AutoForm').data[item.name]" class="w-100"></${t}></template>`);a.appendChild(i)}}};globalThis.AutoForm=AutoForm;