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 +63 -9
- package/README.md +70 -9
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +560 -0
- package/dist/index.js +960 -1
- package/docs/mixed-layout-code.png +0 -0
- package/docs/mixed-layout-preview.png +0 -0
- package/package.json +6 -17
package/README.en.md
CHANGED
|
@@ -2,27 +2,81 @@
|
|
|
2
2
|
|
|
3
3
|
English | [简体中文](./README.md)
|
|
4
4
|
|
|
5
|
-
> Vue 3 form
|
|
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
|
-
|
|
7
|
+
Same mixed layout: Element Row/Col/Item on the left, `<User.*>` plus nested `FormView` density on the right.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
- Written in TypeScript with full type definitions
|
|
11
|
-
- Zero runtime dependencies (aside from Vue peer)
|
|
9
|
+

|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
Playground side by side (layout match):
|
|
12
|
+
|
|
13
|
+

|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
16
|
pnpm add vue-formless
|
|
17
|
-
# or
|
|
18
|
-
|
|
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
|
|
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
|
|
5
|
+
> Vue 3 表单组合式库:控件表 + FormView,布局与输入拆开。当前 **0.1.1**,`0.x` 期间 API 仍可能调整。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
同一套混合布局:左边手写 Element Row/Col/Item,右边 `<User.*>` + 嵌套 `FormView` 换密度。
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
- TypeScript 编写:提供完整类型定义
|
|
11
|
-
- 零运行时依赖目标:不依赖第三方 npm 包(除 Vue peer)
|
|
9
|
+

|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
Playground 平铺对比(布局对齐):
|
|
12
|
+
|
|
13
|
+

|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
16
|
pnpm add vue-formless
|
|
17
|
-
#
|
|
18
|
-
|
|
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
|
|
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;
|