vue-formless 0.0.1 → 0.1.1

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/README.en.md CHANGED
@@ -2,27 +2,81 @@
2
2
 
3
3
  English | [简体中文](./README.md)
4
4
 
5
- > Vue 3 form composable hooks (scaffold placeholderpublic API not published yet).
5
+ > Vue 3 form library: a page-level control table plus FormView, with layout kept off the inputs. **0.1.1** APIs may still change in 0.x.
6
6
 
7
- ## Features
7
+ Same mixed layout: Element Row/Col/Item on the left, `<User.*>` plus nested `FormView` density on the right.
8
8
 
9
- - Vue 3.3+ Composition API hooks
10
- - Written in TypeScript with full type definitions
11
- - Zero runtime dependencies (aside from Vue peer)
9
+ ![Mixed layout code](./docs/mixed-layout-code.png)
12
10
 
13
- ## Install
11
+ Playground side by side (layout match):
12
+
13
+ ![Mixed layout preview](./docs/mixed-layout-preview.png)
14
14
 
15
15
  ```bash
16
16
  pnpm add vue-formless
17
- # or
18
- npm install vue-formless
17
+ # or npm i vue-formless
18
+ ```
19
+
20
+ Peer: `vue` ^3.3.
21
+
22
+ ## Usage
23
+
24
+ Bind host Form / Item / Row / Col once in the project (no official Element adapter):
25
+
26
+ ```ts
27
+ import { ElCol, ElForm, ElFormItem, ElInput, ElRow } from 'element-plus'
28
+ import { createFormControls, createFormView, resolveFormItemProp, type ItemFl } from 'vue-formless'
29
+
30
+ declare module 'vue-formless' {
31
+ interface ControlSchema {
32
+ label?: string
33
+ }
34
+ }
35
+
36
+ export const FormView = createFormView({
37
+ layout: { Row: ElRow, Col: ElCol, column: 2, gutter: 16 },
38
+ form: {
39
+ component: ElForm,
40
+ props: (fl) => ({ model: fl.modelValue }),
41
+ },
42
+ item: {
43
+ component: ElFormItem,
44
+ props: (fl: ItemFl) => ({
45
+ label: fl.label,
46
+ prop: resolveFormItemProp(fl.binding, fl.controlKey),
47
+ }),
48
+ },
49
+ })
50
+
51
+ export const User = createFormControls({
52
+ name: { label: 'Name', component: ElInput },
53
+ })
54
+ ```
55
+
56
+ ```vue
57
+ <FormView ref="formRef" v-model="form" fl:layout label-width="96px">
58
+ <User.Name />
59
+ <User.Name col:span="max" />
60
+ </FormView>
61
+ ```
62
+
63
+ `validate()` / `resetFields()` go through the FormView ref (proxied host Form). Layout details: [docs/adr](./docs/adr/README.md).
64
+
65
+ ## Layout
66
+
67
+ ```text
68
+ packages/vue-formless # kernel (npm: vue-formless)
69
+ packages/layout # internal grid, bundled into the kernel
70
+ playground # Element Plus baseline vs Formless
71
+ docs/adr # architecture decisions
19
72
  ```
20
73
 
21
74
  ## Development
22
75
 
23
76
  ```bash
24
77
  pnpm i
25
- pnpm build
78
+ pnpm dev # playground
79
+ pnpm build # library
26
80
  pnpm test
27
81
  pnpm typecheck
28
82
  ```
package/README.md CHANGED
@@ -2,31 +2,92 @@
2
2
 
3
3
  简体中文 | [English](./README.en.md)
4
4
 
5
- > Vue 3 表单组合式 hooks(脚手架占位,尚未发布 API)。
5
+ > Vue 3 表单组合式库:控件表 + FormView,布局与输入拆开。当前 **0.1.1**,`0.x` 期间 API 仍可能调整。
6
6
 
7
- ## 特性
7
+ 同一套混合布局:左边手写 Element Row/Col/Item,右边 `<User.*>` + 嵌套 `FormView` 换密度。
8
8
 
9
- - Vue 3.3+ Composition API hooks
10
- - TypeScript 编写:提供完整类型定义
11
- - 零运行时依赖目标:不依赖第三方 npm 包(除 Vue peer)
9
+ ![混合布局代码对照](./docs/mixed-layout-code.png)
12
10
 
13
- ## 安装
11
+ Playground 平铺对比(布局对齐):
12
+
13
+ ![混合布局平铺对比](./docs/mixed-layout-preview.png)
14
14
 
15
15
  ```bash
16
16
  pnpm add vue-formless
17
- # or
18
- npm install vue-formless
17
+ # 或 npm i vue-formless
18
+ ```
19
+
20
+ peer:`vue` ^3.3。
21
+
22
+ ## 用法
23
+
24
+ 项目里绑一次宿主(无官方 Element 适配包):
25
+
26
+ ```ts
27
+ import { ElCol, ElForm, ElFormItem, ElInput, ElRow } from 'element-plus'
28
+ import { createFormControls, createFormView, resolveFormItemProp, type ItemFl } from 'vue-formless'
29
+
30
+ declare module 'vue-formless' {
31
+ interface ControlSchema {
32
+ label?: string
33
+ }
34
+ }
35
+
36
+ export const FormView = createFormView({
37
+ layout: { Row: ElRow, Col: ElCol, column: 2, gutter: 16 },
38
+ form: {
39
+ component: ElForm,
40
+ props: (fl) => ({ model: fl.modelValue }),
41
+ },
42
+ item: {
43
+ component: ElFormItem,
44
+ props: (fl: ItemFl) => ({
45
+ label: fl.label,
46
+ prop: resolveFormItemProp(fl.binding, fl.controlKey),
47
+ }),
48
+ },
49
+ })
50
+
51
+ export const User = createFormControls({
52
+ name: { label: '姓名', component: ElInput },
53
+ })
54
+ ```
55
+
56
+ ```vue
57
+ <FormView ref="formRef" v-model="form" fl:layout label-width="96px">
58
+ <User.Name />
59
+ <User.Name col:span="max" />
60
+ </FormView>
61
+ ```
62
+
63
+ `validate()` / `resetFields()` 走 FormView 的 ref(代理内层 Form)。栅格细节见 [docs/adr](./docs/adr/README.md)。
64
+
65
+ ## 仓库结构
66
+
67
+ ```text
68
+ packages/vue-formless # 内核(npm:vue-formless)
69
+ packages/layout # 内部栅格,打进内核 dist
70
+ playground # Element Plus 基线 vs Formless
71
+ docs/adr # 架构决策
19
72
  ```
20
73
 
21
74
  ## 开发
22
75
 
23
76
  ```bash
24
77
  pnpm i
25
- pnpm build
78
+ pnpm dev # 启动 playground
79
+ pnpm build # 构建库
26
80
  pnpm test
27
81
  pnpm typecheck
28
82
  ```
29
83
 
84
+ Playground 对照:基础表单、筛选条、只读详情、混合布局、DateRange。
85
+
86
+ ```bash
87
+ pnpm playground
88
+ # http://localhost:5173
89
+ ```
90
+
30
91
  ## License
31
92
 
32
93
  [MIT](./LICENSE)
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=require("vue"),N=Symbol("vue-formless.formContext");function Y(){const e=l.inject(N,null);if(!e)throw new Error("[vue-formless] useFormContext() must be used inside <FormView>.");return e}function D(e){if(!e)return[];const t=[];let o=0;for(;o<e.length;){if(e[o]==="."){o++;continue}if(e[o]==="["){const n=e.indexOf("]",o+1);if(n===-1)throw new Error(`Invalid path "${e}": unclosed bracket`);const r=e.slice(o+1,n),s=Number(r);if(!Number.isInteger(s)||String(s)!==r||s<0)throw new Error(`Invalid path "${e}": index must be a non-negative integer`);t.push({type:"index",index:s}),o=n+1}else{const n=/^[a-zA-Z_$][a-zA-Z0-9_$]*/.exec(e.slice(o));if(!n)throw new Error(`Invalid path "${e}" at position ${o}`);t.push({type:"key",key:n[0]}),o+=n[0].length}}return t}function Ne(e,t){let o=e;for(const n of t)n.type==="key"?o=o[n.key]:o=o[n.index];return o}function _e(e,t){if(t.type==="key")return e==null||typeof e!="object"||Array.isArray(e)?void 0:e[t.key];if(Array.isArray(e))return e[t.index]}function k(e,t){const o=D(t);if(o.length===0)return;const n=o[o.length-1],r=Ne(e,o.slice(0,-1));return _e(r,n)}function J(e,t,o,n){const r=t[o],s=o===t.length-1;if(r.type==="key"){if(Array.isArray(e))throw new Error(`Cannot set "${r.key}" on an array node`);const i=e!=null&&typeof e=="object"?e:{};return s?{...i,[r.key]:n}:{...i,[r.key]:J(i[r.key],t,o+1,n)}}const u=Array.isArray(e)?[...e]:[];return s?(u[r.index]=n,u):(u[r.index]=J(u[r.index],t,o+1,n),u)}function we(e,t,o){const n=D(t);if(n.length===0)throw new Error("Cannot set an empty prop");return J(e,n,0,o)}function ye(e){return D(e).map(t=>t.type==="key"?t.key:String(t.index)).join(".")}function Me(e,t){let o=null;function n(r,s){o||(o=[],l.nextTick(()=>{if(!o?.length){o=null;return}let u=e();for(const i of o)u=we(u,i.prop,i.value);o=null,t(u)})),o.push({prop:r,value:s})}return{update:n}}const p=24,V={column:1};function Te(e){const t=Math.floor(Number(e));return t===1/0?p:Number.isFinite(t)?Math.min(p,Math.max(1,t)):V.column}function Be(...e){return Te(e.find(t=>t!=null)??V.column)}function Ue(e,t){const o=t>0?t:V.column,n=Math.max(1,Math.floor(p/o));if(e==null||e==="")return n;if(typeof e=="number")return ce(e),e;const r=e.trim();if(r==="max")return p;const s=/^(\d+)x$/.exec(r);if(s){const u=Number(s[1]);if(u<1)throw new Error(`[vue-formless] col:span "${e}" is invalid`);return Math.min(p,u*n)}if(/^\d+$/.test(r)){const u=Number(r);return ce(u),u}throw new Error(`[vue-formless] col:span "${e}" is invalid`)}function ce(e){if(!Number.isInteger(e)||e<1||e>p)throw new Error(`[vue-formless] col:span ${e} must be an integer 1–${p}`)}function We(e){if(e==null||e===""||e==="auto")return"auto";if(e==="start"||e==="end")return e;throw new Error(`[vue-formless] col:place "${String(e)}" is invalid`)}const W=Symbol("vue-formless:layout-view");function Ge(e,t,o={childList:!0}){if(typeof MutationObserver>"u")return;let n=null;function r(u){n?.disconnect(),n=null,u&&(n=new MutationObserver(t),n.observe(u,o))}l.onMounted(()=>r(l.toValue(e)));const s=l.watch(()=>l.toValue(e),r,{flush:"post"});l.onBeforeUnmount(()=>{s(),n?.disconnect(),n=null})}function Ye(e,t=0){const o=l.ref([]);function n(){const r=l.toValue(e);o.value=r?Array.from(r.children):[]}return l.watch(()=>[l.toValue(e),l.toValue(t)],n,{immediate:!0}),Ge(e,n),o}function De(e,t,o){const n=[];if(o==="auto")return e.n+t>p&&(e.n=0),n;if(o==="start")return e.n>0&&(n.push(p-e.n),e.n=0),n;e.n+t>p&&e.n>0&&(n.push(p-e.n),e.n=0);const r=p-e.n-t;return r>0&&(n.push(r),e.n+=r),n}function ze(e){return l.computed(()=>{const t={n:0},o=new Map;for(const n of l.toValue(e)){const r=De(t,n.span,n.place);o.set(n.id,r),t.n+=n.span}return o})}function ge(e){if(e==null||typeof e!="object")return null;if(typeof Element<"u"&&e instanceof Element)return e;const t=e;return U(t.vnode?.el)??U(t.subTree?.el)??U(t.subTree?.component?.vnode?.el)??U(t.$el)}function U(e){if(typeof Element<"u"&&e instanceof Element)return e;if(e&&e.nodeType===8){for(let t=e.nextSibling;t;t=t.nextSibling)if(typeof Element<"u"&&t instanceof Element)return t}return null}const Ke=l.defineComponent({name:"LayoutBlanks",inheritAttrs:!1,props:{is:{type:Object,default:void 0},spans:{type:Array,required:!0}},setup(e){return()=>{if(e.spans.length===0||e.is==null)return null;const t=e.is;return l.createVNode(l.Fragment,null,[e.spans.map((o,n)=>l.createVNode(t,{key:n,span:o},null))])}}}),qe=l.defineComponent({name:"LayoutItem",inheritAttrs:!1,props:{span:{type:[String,Number],default:void 0},place:{type:String,default:void 0}},setup(e,{slots:t}){const o=l.inject(W,null);if(!o)return()=>t.default?.()??null;const{span:n,blanks:r,itemRef:s,Col:u,disabled:i}=o(()=>e.span,()=>e.place);return()=>{if(i.value)return t.default?.()??null;const f=u;return l.createVNode(l.Fragment,null,[l.createVNode(Ke,{is:u,spans:r.value},null),l.createVNode(f,{ref:s,span:n.value},{default:()=>[t.default?.()??null]})])}}}),He=l.defineComponent({name:"LayoutItemPassThrough",inheritAttrs:!1,setup(e,{slots:t}){return()=>t.default?.()??null}});function he(){return l.inject(W,null)?qe:He}function Xe(e){const t=l.ref({});let o=0;return{get value(){return t.value},setup(n,r){const s=String(++o),u=l.computed(()=>Ue(l.toValue(n),l.toValue(e))),i=l.computed(()=>We(l.toValue(r)));return t.value[s]={span:u,place:i,el:null,mounted:!1},l.onMounted(()=>{const f=t.value[s];f&&(f.mounted=!0)}),l.onBeforeUnmount(()=>{delete t.value[s]}),s},span(n){return t.value[n]?.span??0},ref(n,r){const s=t.value[n];if(!s)return;const u=ge(r);s.el!==u&&(s.el=u)}}}function Ze(e,t){const o=new Map(Object.entries(e).filter(([,n])=>n.el).map(([n,r])=>[r.el,{id:n,span:r.span,place:r.place}]));return t.filter(n=>o.has(n)).map(n=>o.get(n))}function Je(e,t){const o=Ye(()=>ge(t.value),()=>Object.keys(e.value).filter(n=>e.value[n].mounted).join(","));return ze(()=>Ze(e.value,o.value))}function ne(e={}){const{Row:t,Col:o}=e;return l.defineComponent({name:"LayoutView",inheritAttrs:!1,props:{disabled:{type:Boolean,default:!1},column:{type:Number,default:void 0}},setup(n,{slots:r,attrs:s}){const u=l.computed(()=>!t||!o||n.disabled),i=Xe(()=>Be(n.column,e.column)),f=l.ref(null),a=Je(i,f);return l.provide(W,(m,g)=>{l.provide(W,null);const w=i.setup(m,g);return{span:l.computed(()=>i.span(w)),blanks:l.computed(()=>a.value.get(w)??[]),itemRef:b=>i.ref(w,b),Col:o,disabled:u}}),()=>{if(u.value)return r.default?.()??null;const m=t;return l.createVNode(m,l.mergeProps({ref:f},s),{default:()=>[r.default?.()??null]})}}})}const Qe=new Set(["item","layout","model","span"]),et=new Set(["component","props","model","prop","item","layout"]);function Q(e){const t={};for(const[o,n]of Object.entries(e))n!==void 0&&(t[o]=n);return t}function oe(e){const t={};for(const[o,n]of Object.entries(e))o==="span"&&n!==void 0&&console.warn("[vue-formless] fl:span is removed; use col:span"),Qe.has(o)||(t[o]=n);return t}function tt(e){const t={};for(const[o,n]of Object.entries(e))!et.has(o)&&n!==void 0&&(t[o]=n);return t}function nt(e){if(e==null||typeof e!="object")return{};const t=e.formless;if(t==null||typeof t!="object")return{};const{model:o,item:n}=t;return Q({model:o,item:n})}function ve(e){const t={};for(const[o,n]of Object.entries(e))!o.startsWith("fl:")||o.length<=3||n===void 0||(t[o.slice(3)]=n);return t}function ot(e,t){const o=new Set;for(const r of t)o.add(r),o.add(`onUpdate:${r}`);const n={};for(const[r,s]of Object.entries(e))o.has(r)||(n[r]=s);return n}function $(e,t){return e==null?{}:Q(typeof e=="function"?e(t)??{}:e)}function z(...e){const t={};for(const o of e)if(o!=null)for(const[n,r]of Object.entries(o))r!==void 0&&(t[n]=r);return t}function rt(e){const t=e.Item?l.markRaw(e.Item):void 0;return(o,n)=>t&&n.item!==!1&&(n.item===!0||(e.isItemEnabled?.()??!0))?l.h(t,z($(e.itemProps,n.fl),n.itemAttrs,n.itemOn),{...n.itemSlots,default:()=>o}):o}function ee(e){return typeof e=="string"?[e]:[...e]}function re(e,t={},o={}){if(o.prop==="")throw new Error(`[vue-formless] fl:prop cannot be an empty string (control "${e}")`);if(t.prop==="")throw new Error(`[vue-formless] prop cannot be an empty string (control "${e}")`);const n=ee(t.model??"modelValue"),r=ee(o.prop!==void 0?o.prop:t.prop??e);if(r.length>n.length)throw new Error(`createFormControls: prop cannot be longer than model (control "${e}": model has ${n.length}, prop has ${r.length})`);if(r.some(s=>s===""))throw new Error(`[vue-formless] prop cannot be an empty string (control "${e}")`);return{models:n,props:r}}function lt(e,t){return e.props.length===1?ye(e.props[0]):t}function be(e,t){const o=e.models.indexOf(t);if(o===-1)throw new Error(`[vue-formless] useFormItem("${t}"): not a v-model port of this control`);const n=e.props[o];if(n===void 0)throw new Error(`[vue-formless] useFormItem("${t}"): port is not bound to a prop`);return{models:[t],props:[n]}}function le(e,t,o){const n={};for(let r=0;r<t.props.length;r++){const s=t.models[r],u=t.props[r];n[s]=k(e,u),n[`onUpdate:${s}`]=i=>{o(u,i)}}return n}const O="item:",q="onItem:",H="fl:",X="row:",Z="col:",st=new Set(["column","gutter"]),ut=new Set(["span","place"]);function fe(e){console.warn(e)}function Ie(e){const t={},o={};for(const n of Object.keys(e)){const r=e[n];r&&(n.startsWith(O)&&n.length>O.length?t[n.slice(O.length)]=r:o[n]=r)}return{itemSlots:t,inputSlots:o}}function Ce(e){const t={},o={};for(const[n,r]of Object.entries(e))n.startsWith(H)&&n.length>H.length?t[n.slice(H.length)]=r:o[n]=r;return{fl:t,rest:o}}function Fe(e){const t={},o={},n={};for(const[r,s]of Object.entries(e))if(r.startsWith(X)&&r.length>X.length){const u=r.slice(X.length);st.has(u)?t[u]=s:fe(`[vue-formless] unknown row:${u} is ignored`)}else if(r.startsWith(Z)&&r.length>Z.length){const u=r.slice(Z.length);ut.has(u)?o[u]=s:fe(`[vue-formless] unknown col:${u} is ignored`)}else n[r]=s;return{row:t,col:o,rest:n}}function me(e){if(e==null||e==="")return;const t=typeof e=="number"?e:Number(e);if(Number.isFinite(t))return t}function Ee(e){const t={},o={},n={};for(const[r,s]of Object.entries(e))r.startsWith(q)&&r.length>q.length?o[it(r.slice(q.length))]=s:r.startsWith(O)&&r.length>O.length?t[r.slice(O.length)]=s:n[r]=s;return{itemAttrs:t,itemOn:o,inputAttrs:n}}function it(e){const t=e.indexOf(":");return t===-1?`on${de(e)}`:`on${de(e.slice(0,t))}:${e.slice(t+1)}`}function de(e){return e&&e.charAt(0).toUpperCase()+e.slice(1)}const Ve=Symbol("vue-formless.controlRuntime");function at(e){l.provide(Ve,e)}const ct={"fl:prop":{type:[String,Array],default:void 0},"col:span":{type:[String,Number],default:void 0},"col:place":{type:String,default:void 0}};function Se(e){return l.defineComponent({name:e?`FormViewItem_${e}`:"FormViewItem",inheritAttrs:!1,props:ct,setup(t,{slots:o,attrs:n}){const r=Y(),s=he(),u=l.inject(Ve,null);if(e!=null&&!u)throw new Error("[vue-formless] useFormItem(port) must be used inside a namespaced control.");return()=>ft(r,s,u,e,o,n,t)}})}const se=Se();function Ae(e){return Y(),e===void 0?se:Se(e)}function ft(e,t,o,n,r,s,u){const{fl:i,rest:f}=Ce(s),{row:a,col:m,rest:g}=Fe(f),w={...i,...ve(u)},{itemAttrs:b,itemOn:v,inputAttrs:S}=Ee(g),I={...b,...S},{itemSlots:y}=Ie(r),d=n==null&&o!=null,C=u["col:span"]??m.span,A=u["col:place"]??m.place,c=o?.getFrame(),L=C??(d?c?.colSpan:void 0),x=A??(d?c?.colPlace:void 0),_=mt(e,o,n,w),M=le(e.model,_.binding,e.update),K=r.default?.({field:M})??null;!d&&(a.column!=null||a.gutter!=null)&&console.warn("[vue-formless] :row:* is ignored on a leaf cell");let F=I,E=v,T=y;d&&c&&(F={...c.itemAttrs,...I},E={...c.itemOn,...v},T={...c.itemSlots,...y});let R,B,P=!1;d&&c&&(R=c.wrapItem,B=c.wrapCol,P=c.extraRow);let h=K;if(P&&c){const j=h;h=l.h(e.LayoutView,{disabled:!e.isLayoutEnabled(),column:c.rowColumn??e.factoryColumn,gutter:c.rowGutter??e.factoryGutter},()=>j)}if(h=e.wrap(h,{item:R===!1?!1:R===!0?!0:void 0,fl:_.fl,itemAttrs:F,itemOn:E,itemSlots:T}),B!==!1){const j=h;h=l.h(t,{span:L,place:x},()=>j)}return h}function mt(e,t,o,n){const r=oe(n);if(t){const a=t.getFrame(),m=o!=null?be(a.binding,o):a.binding,g={...a.fl,...r,controlKey:t.controlKey,binding:m,getValues:()=>m.props.map(w=>k(e.model,w))};return{binding:m,fl:g}}const s=n.prop;if(s==="")throw new Error("[vue-formless] fl:prop cannot be an empty string");const u=typeof s=="string"?s:Array.isArray(s)&&s[0]?String(s[0]):"",i=s===void 0?{models:["modelValue"],props:[]}:re(u||"field",{prop:s}),f={...r,controlKey:u,binding:i,getValues:()=>i.props.map(a=>k(e.model,a))};return{binding:i,fl:f}}function Le(e){const t=e;return t.Item=se,t}const te=0;function dt(e){return new Proxy({},{get(t,o){const n=e.value;if(n==null)return;const r=Reflect.get(n,o,n);return typeof r=="function"?r.bind(n):r},has(t,o){return e.value!=null&&o in e.value}})}const Oe={modelValue:{type:[Object,Array],default:void 0},"fl:layout":{type:Boolean,default:!1},"row:column":{type:Number,default:void 0},"row:gutter":{type:Number,default:void 0},"fl:form":{type:[Boolean,String],default:"auto"},"fl:item":{type:Boolean,default:!0}},ke={"update:modelValue":e=>!0};function G(e){if(e!=null&&typeof e=="object")throw new Error("[vue-formless] fl:layout is boolean only; use :row:column / :row:gutter");return e===!0||e===""}function pt(e,t){return e===!0||e===!1?e:!t}function wt(e){return e==null?!1:"modelValue"in e||"model-value"in e||"onUpdate:modelValue"in e||"onUpdate:model-value"in e}function xe(e){const t=rt({Item:e.Item,itemProps:e.itemProps,isItemEnabled:e.isItemEnabled}),o=e.isItemEnabled??(()=>!0);l.provide(N,l.reactive({get model(){return e.getModel()},update:e.update,wrap:t,isItemEnabled:o,isLayoutEnabled:e.isLayoutEnabled,LayoutView:l.markRaw(e.LayoutView),factoryColumn:e.factoryColumn,factoryGutter:e.factoryGutter}))}function Re(e,t){const o=l.inject(N,null);if(wt(l.getCurrentInstance()?.vnode.props)){const r=Me(e,t);return{getModel:e,update:r.update}}if(o)return{getModel:()=>o.model,update:o.update};throw new Error("[vue-formless] FormView requires v-model unless nested inside another FormView.")}function yt(e){if(e!=null){if(e.Row==null||e.Col==null)throw new Error("[vue-formless] createFormView layout requires both Row and Col.");return{Row:l.markRaw(e.Row),Col:l.markRaw(e.Col),column:e.column,gutter:e.gutter}}}function gt(e={}){const t=yt(e.layout),o=e.form?.component?l.markRaw(e.form.component):void 0,n=e.form?.props,r=e.item?.component?l.markRaw(e.item.component):void 0,s=e.item?.props,u=ne(t?{Row:t.Row,Col:t.Col}:{}),i=t?.column??V.column,f=t?.gutter??te;return Le(l.defineComponent({name:"FormView",inheritAttrs:!1,props:Oe,emits:ke,setup(a,{slots:m,emit:g,attrs:w,expose:b}){const v=l.ref(null);b(dt(v));const S=l.inject(N,null)!=null,{getModel:I,update:y}=Re(()=>a.modelValue,d=>g("update:modelValue",d));return xe({getModel:I,update:y,Item:r,itemProps:s,isItemEnabled:()=>a["fl:item"]!==!1,isLayoutEnabled:()=>G(a["fl:layout"]),LayoutView:u,factoryColumn:i,factoryGutter:f}),()=>{const d=m.default?.()??null,C=G(a["fl:layout"]),A={column:a["row:column"]??i,gutter:a["row:gutter"]??f},c=l.h(u,{disabled:!C,column:A.column,gutter:A.gutter},()=>d),L=o?pt(a["fl:form"],S):!1;if(!o||!L)return c;const x={layout:C,form:L,item:a["fl:item"]!==!1,modelValue:I()};return l.h(o,{ref:v,...z($(n,x),w)},{default:()=>c})}}}))}const pe=ne(),ht=Le(l.defineComponent({name:"FormView",inheritAttrs:!1,props:Oe,emits:ke,setup(e,{slots:t,emit:o}){const{getModel:n,update:r}=Re(()=>e.modelValue,s=>o("update:modelValue",s));return xe({getModel:n,update:r,isLayoutEnabled:()=>G(e["fl:layout"]),LayoutView:pe,factoryColumn:V.column,factoryGutter:te}),()=>l.h(pe,{disabled:!G(e["fl:layout"]),column:e["row:column"]??V.column,gutter:e["row:gutter"]??te},()=>t.default?.()??null)}}));function ue(e){return e&&e.charAt(0).toUpperCase()+e.slice(1)}function vt(e){return e&&e.charAt(0).toLowerCase()+e.slice(1)}function Pe(e,t){if(e==="self"&&t===!1||e===!1&&t==="self")throw new Error('[vue-formless] formless.item "self" conflicts with schema item: false');return e??t}function je(e){const t=e.internalItem==="self";let o;e.tagItem===!0?o=!0:e.tagItem===!1||t||e.internalItem===!1?o=!1:e.internalItem===!0?o=!0:o=e.pageItem;let n=e.pageLayoutOn;t&&e.tagItem!==!0&&(o=!1,n=!1);const r=t&&e.tagItem===!0&&e.pageLayoutOn;return{wrapItem:o,wrapCol:n,extraRow:r,self:t}}const bt={"fl:prop":{type:[String,Array],default:void 0},"fl:item":{type:Boolean,default:void 0},"col:span":{type:[String,Number],default:void 0},"col:place":{type:String,default:void 0},"row:column":{type:Number,default:void 0},"row:gutter":{type:Number,default:void 0}};function It(e,t){const o=Et(e),n={};for(const r of Object.keys(o)){const s=o[r];if(!s)continue;const u=ue(r);n[u]=Ct(r,s,t)}return n}function Ct(e,t,o){const n=nt(t.component),r=n.model??t.model,s=Pe(n.item,t.item);return l.defineComponent({name:`Control_${ue(e)}`,inheritAttrs:!1,props:bt,setup(u,{attrs:i,slots:f}){const a=Y();let m;at({controlKey:e,getFrame:()=>m});const g=Ae();return()=>{const{fl:w,rest:b}=Ce(i),{row:v,col:S,rest:I}=Fe(b),y={...w,...ve(u)},d=re(e,{model:r,prop:t.prop},{prop:y.prop}),C=t.component,{itemSlots:A,inputSlots:c}=Ie(f),{itemAttrs:L,itemOn:x,inputAttrs:_}=Ee(I),M=tt(t),K=y.item===!0?!0:y.item===!1?!1:void 0,F=je({pageItem:a.isItemEnabled(),pageLayoutOn:a.isLayoutEnabled(),internalItem:s,tagItem:K}),E=Ft(a,e,d,M,y),T=z($(o?.props,E),$(t.props,E),ot(_,d.models)),R=le(a.model,d,a.update),B=d.props[0]??e,P=typeof E.label=="string"?E.label:void 0,h=u["col:span"]??S.span,j=u["col:place"]??S.place,ie=me(u["row:column"]??v.column),ae=me(u["row:gutter"]??v.gutter);!F.extraRow&&(ie!=null||ae!=null)&&console.warn("[vue-formless] :row:* is ignored on a leaf control");const $e=C?l.h(C,{...T,...R},c):l.h("div",{class:"vue-formless-control","data-control":e},[P?l.h("label",{class:"vue-formless-control__label"},String(P)):null,String(k(a.model,B)??"")]);return m={fl:{...M,...oe(y)},binding:d,wrapItem:F.wrapItem,wrapCol:F.wrapCol,extraRow:F.extraRow,colSpan:h,colPlace:j,rowColumn:ie,rowGutter:ae,itemAttrs:L,itemOn:x,itemSlots:A},l.h(g,null,()=>$e)}}})}function Ft(e,t,o,n,r){return{...n,...oe(r),controlKey:t,binding:o,getValues:()=>o.props.map(s=>k(e.model,s))}}function Et(e){const t={...e};for(const o of Object.keys(t)){const n=t[o];n&&(t[o]={...n,component:n.component&&typeof n.component=="object"?l.markRaw(n.component):n.component})}return t}exports.DEFAULT_LAYOUT=V;exports.FormView=ht;exports.FormViewItem=se;exports.GRID_TOTAL=p;exports.applyControlBinding=le;exports.bindingForPort=be;exports.camelToPascal=ue;exports.createFormControls=It;exports.createFormView=gt;exports.createLayoutView=ne;exports.formContextKey=N;exports.formItemProp=ye;exports.getIn=k;exports.mergeInternalItem=Pe;exports.overlayProps=z;exports.parsePath=D;exports.pascalToCamel=vt;exports.resolveControlBinding=re;exports.resolveControlShell=je;exports.resolveFormItemProp=lt;exports.resolveProps=$;exports.setIn=we;exports.toBindingList=ee;exports.useFormContext=Y;exports.useFormItem=Ae;exports.useLayoutItem=he;