Skip to content

Commit

Permalink
chore: add relation demo
Browse files Browse the repository at this point in the history
  • Loading branch information
CCherry07 committed Feb 10, 2025
1 parent 0b2daf5 commit 9ca123b
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 2 deletions.
5 changes: 3 additions & 2 deletions examples/react/src/components/InputNumber.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ interface Props {
label: string
isDisabled: boolean
required: boolean
prefix: React.ReactNode
}
export default function (props: Props) {
const { onChange, value, errors, label, required, isDisabled } = props;
const { onChange, value, errors, label, required, isDisabled, prefix } = props;
return <Form.Item label={label} required={required}>
<InputNumber style={{ width: "100%" }} disabled={isDisabled} value={value} onChange={onChange} />
<InputNumber prefix={prefix} style={{ width: "100%" }} disabled={isDisabled} value={value} onChange={onChange} />
<FieldError errors={errors} />
</Form.Item>
}
40 changes: 40 additions & 0 deletions examples/react/src/demos/relation/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React, { useEffect, useState } from "react";
import { ReactNode } from "react";
import { Button, Spin } from "antd";
import { effect, watch } from "alien-deepsignals";
import { AbstractModel } from "@formula/core/model/abstract_model";
interface Props {
app: ReactNode,
form: AbstractModel<Record<string, any>>
}

export function App(props: Props) {
const [state, setState] = useState(false)
const [submitting, setSubmitted] = useState(false)
const [model, setModel] = useState({} as any)

useEffect(() => {
effect(() => {
setState(props.form.isUpdating.value)
setSubmitted(props.form.submiting.value)
})
watch(props.form.model, (model) => {
setModel({ ...model })
}, {
immediate: true,
deep: true
});
}, [])
return <div>
<div style={{ marginBottom: 20 }}>
model: {JSON.stringify(model, null, 2)}
</div>
<Spin spinning={state || submitting}>
{props.app}
<Button style={{ width: "400px" }} type="primary" onClick={async () => {
const model = await props.form.submit()
alert(JSON.stringify(model, null, 2))
}}>登录</Button>
</Spin>
</div>
}
188 changes: 188 additions & 0 deletions examples/react/src/demos/relation/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import React from "react";
import { ReactNode } from "react";
import { createForm } from "@formula/react"
import { createDecision, defineField, match } from "@formula/core";
import { z } from "zod";
import { zodResolver } from "@formula/resolvers";
import { defineRelation } from "@formula/core"
import Form from "../../components/Form";
import Input from "../../components/Input";
import { App } from "./app"
import InputNumber from "../../components/InputNumber";
import { Info } from "./info"
interface Props {
label: string
type?: "Group" | "Search" | "TextArea" | "Password" | "OTP";
prefix?: ReactNode
required?: boolean
}
interface Model {
account: {
username: string
password: string
age: number,
address: string
}
}

const boolsConfig = {
isTom: (model: Model) => model.account.username === "tom",
isJerry: (model: Model) => model.account.username === "jerry",
is18: (model: Model) => model.account.age >= 18,
isChongqing: (model: Model) => model.account.address === "重庆"
}

const D = createDecision(boolsConfig)

const nameRelaition = defineRelation([
[
"account.age",
(field, age) => {
console.log(`${field.value} is ${age} years old`);
}
],
[
["account.address", "account.age"],
(field, [address, age]) => {
if (address && age) {
console.log(`${field.value} is ${age} years old, and he lives in ${address}`);
}
}
],
function (field) {
if (field.execDecision(D.and("is18", "isTom"))) {
console.log("Tom is 18 years old");
}
}
])

const infoRelaition = defineRelation([
[
["account.username", "account.age"],
(field, [username, age]) => {
if (field.value.length < 2 && username && age) {
field.value = [`${username} is ${age} years old`]
}
}
],
[
["account.username", "account.address", "account.age"],
(field, [username, address, age]) => {
if (username && address && age ) {
field.value= [
field.value[0],
`${username} is ${age} years old, and he lives in ${address}`
]
}
}
],
])

const username = defineField<string, Props>()
.component({
id: "username",
component: Input
})
.props({
label: "用户名",
prefix: "👤",
required: true
})
.relation(nameRelaition)
.validator(z.string({ message: "用户名为必填项" }).min(2, "用户名长度必须在2-10").max(10, "用户名长度必须在2-10").regex(/^[a-zA-Z]+$/, { message: "用户名必须是英文" }))
.build()

const password = defineField<string, Props>()
.component({
id: "password",
component: Input,
})
.props({ label: "密码", type: "Password", prefix: "🔒", required: true })
.validator(
z.string({ message: "密码必须包含大小写字母、数字和特殊字符" })
.min(6, "密码长度必须在6-16").max(16, "密码长度必须在6-16")
.regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\da-zA-Z]).{6,16}$/, { message: "密码必须包含大小写字母、数字和特殊字符" })
)
.events({
onChange: function (value) {
const res = match(this.execDecision(D.and("is18", "isTom")),
[
[true, () => "Tom is 18"],
[false, () => "Tom is not 18"]
]
)
console.log(res)
this.value = value
}
})
.build()

const age = defineField<number, Props>()
.component({
id: "age",
component: InputNumber,
hidden: D.use('isJerry')
}).props({
label: "年龄",
prefix: "🎂",
required: true
})

const address = defineField<string, Props>()
.component({
id: "address",
component: Input,
})
.props({
label: "地址",
prefix: "🏠",
required: true
})
.validator(z.string({ message: "地址为必填项" }).min(2, "地址长度必须在2-10").max(10, "地址长度必须在2-10"))
.build()

const info = defineField<any, any>().component({
id: "info",
component: Info,
}).actions({
setDefaultValue() {
return ["请填写信息"]
},
}).relation(infoRelaition).build()

const useraccount = defineField<Model['account'], any>()
.component({
id: "account",
component: Form,
})
.properties([
username,
password,
age,
address,
info
])
.props({
style: {
width: "400px"
}
})
.build()


const { app, form } = createForm({
id: "boolless",
defaultValidatorEngine: "zod",
graph: [
useraccount
],
boolsConfig,
resolvers: {
validator: {
zod: zodResolver
}
}
})
export default function () {
return <App app={app} form={form} />
}
11 changes: 11 additions & 0 deletions examples/react/src/demos/relation/info.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from "react"
import { Space, Typography } from 'antd';
export function Info({
value
}: { value: string[] }) {
return <Space direction="vertical">
{
value.map((v, i) => <Typography.Text key={i}>{v}</Typography.Text>)
}
</Space>
}
2 changes: 2 additions & 0 deletions examples/react/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Login from './demos/login';
import Edit from "./demos/edit";
import Validator from "./demos/validator";
import Boolless from './demos/boolless';
import Relation from './demos/relation';
function createElement(node: ReactNode){
const el = document.createElement('div')
el.style.marginBottom = '20px';
Expand All @@ -15,3 +16,4 @@ createElement(<Login />);
createElement(<Edit />);
createElement(<Validator />);
createElement(<Boolless />);
createElement(<Relation />);

0 comments on commit 9ca123b

Please sign in to comment.