Skip to content

Commit

Permalink
feat: slot
Browse files Browse the repository at this point in the history
  • Loading branch information
nicetooo committed Jan 22, 2025
1 parent 5472532 commit c8d8312
Show file tree
Hide file tree
Showing 8 changed files with 548 additions and 5 deletions.
3 changes: 3 additions & 0 deletions packages/json-schema/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
SchemaTypes,
SchemaKey,
ISchemaTransformerOptions,
Slot,
} from './types'
import { IFieldFactoryProps } from '@formily/core'
import { map, each, isFn, instOf, FormPath, isStr } from '@formily/shared'
Expand Down Expand Up @@ -180,6 +181,8 @@ export class Schema<

['x-compile-omitted']?: string[];

['x-slot-node']?: Slot;

[key: `x-${string | number}` | symbol]: any

_isJSONSchemaObject = true
Expand Down
5 changes: 5 additions & 0 deletions packages/json-schema/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,11 @@ export interface ISchemaTransformerOptions {
scope?: IScopeContext
}

export type Slot = {
target: string
isRenderProp?: boolean
}

export type Stringify<P extends { [key: string]: any }> = {
/**
* Use `string & {}` instead of string to keep Literal Type for ISchema#component and ISchema#decorator
Expand Down
119 changes: 119 additions & 0 deletions packages/react/docs/api/components/SchemaField.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,122 @@ export default () => (
</FormProvider>
)
```

## JSON Schema ReactNode Prop Use Case (x-slot-node)

Reference [Slot](https://react.formilyjs.org/api/shared/schema#slot)

```tsx
import React from 'react'
import { createForm } from '@formily/core'
import { FormProvider, createSchemaField } from '@formily/react'
import { Input, Tag } from 'antd'
import { CheckCircleTwoTone } from '@ant-design/icons'

const form = createForm()

const SchemaField = createSchemaField({
components: {
Input,
CheckCircleTwoTone,
},
})

export default () => (
<FormProvider form={form}>
<SchemaField
components={{
Tag,
}}
schema={{
type: 'object',
properties: {
tag: {
'x-slot-node': {
target: 'input.x-component-props.prefix',
},
'x-component': 'Tag',
'x-component-props': {
children: 'www.',
},
},
tag2: {
'x-slot-node': {
target: 'input.x-component-props.suffix',
},
'x-component': 'Tag',
'x-component-props': {
children: '.com',
},
},
icon: {
'x-slot-node': {
target: 'input.x-component-props.addonAfter',
},
'x-component': 'CheckCircleTwoTone',
},
input: {
type: 'string',
'x-component': 'Input',
'x-component-props': {},
},
},
}}
></SchemaField>
</FormProvider>
)
```

## JSON Schema Render Prop Use Case (x-slot-node & isRenderProp)

Reference [Slot](https://react.formilyjs.org/api/shared/schema#slot)

```tsx
import React from 'react'
import { createForm } from '@formily/core'
import { FormProvider, createSchemaField } from '@formily/react'
import { Rate } from 'antd'
import { DollarOutlined } from '@ant-design/icons'

const form = createForm()

const SchemaField = createSchemaField({
components: {
DollarOutlined,
},
})

export default () => (
<FormProvider form={form}>
<SchemaField
components={{
Rate,
}}
schema={{
type: 'object',
properties: {
icon: {
'x-slot-node': {
target: 'rate.x-component-props.character',
isRenderProp: true,
},
'x-component': 'DollarOutlined',
'x-component-props': {
rotate: '{{ $slotArgs[0].value * 45 }}',
style: {
fontSize: '50px',
},
},
},
rate: {
'x-component': 'Rate',
'x-component-props': {
defaultValue: 3,
},
},
},
}}
></SchemaField>
</FormProvider>
)
```
119 changes: 119 additions & 0 deletions packages/react/docs/api/components/SchemaField.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,122 @@ export default () => (
</FormProvider>
)
```

## JSON Schema ReactNode Prop 用例 (x-slot-node)

参考[Slot](https://react.formilyjs.org/zh-CN/api/shared/schema#slot)

```tsx
import React from 'react'
import { createForm } from '@formily/core'
import { FormProvider, createSchemaField } from '@formily/react'
import { Input, Tag } from 'antd'
import { CheckCircleTwoTone } from '@ant-design/icons'

const form = createForm()

const SchemaField = createSchemaField({
components: {
Input,
CheckCircleTwoTone,
},
})

export default () => (
<FormProvider form={form}>
<SchemaField
components={{
Tag,
}}
schema={{
type: 'object',
properties: {
tag: {
'x-slot-node': {
target: 'input.x-component-props.prefix',
},
'x-component': 'Tag',
'x-component-props': {
children: 'www.',
},
},
tag2: {
'x-slot-node': {
target: 'input.x-component-props.suffix',
},
'x-component': 'Tag',
'x-component-props': {
children: '.com',
},
},
icon: {
'x-slot-node': {
target: 'input.x-component-props.addonAfter',
},
'x-component': 'CheckCircleTwoTone',
},
input: {
type: 'string',
'x-component': 'Input',
'x-component-props': {},
},
},
}}
></SchemaField>
</FormProvider>
)
```

## JSON Schema Render Prop 用例 (x-slot-node & isRenderProp)

参考[Slot](https://react.formilyjs.org/zh-CN/api/shared/schema#slot)

```tsx
import React from 'react'
import { createForm } from '@formily/core'
import { FormProvider, createSchemaField } from '@formily/react'
import { Rate } from 'antd'
import { DollarOutlined } from '@ant-design/icons'

const form = createForm()

const SchemaField = createSchemaField({
components: {
DollarOutlined,
},
})

export default () => (
<FormProvider form={form}>
<SchemaField
components={{
Rate,
}}
schema={{
type: 'object',
properties: {
icon: {
'x-slot-node': {
target: 'rate.x-component-props.character',
isRenderProp: true,
},
'x-component': 'DollarOutlined',
'x-component-props': {
rotate: '{{ $slotArgs[0].value * 45 }}',
style: {
fontSize: '50px',
},
},
},
rate: {
'x-component': 'Rate',
'x-component-props': {
defaultValue: 3,
},
},
},
}}
></SchemaField>
</FormProvider>
)
```
82 changes: 82 additions & 0 deletions packages/react/docs/api/shared/Schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ Create a Schema Tree based on a piece of json schema data to ensure that each sc
| $ref | Read the Schema from the Schema predefined and merge it into the current Schema | String | - |
| x-data | Extends Data | Object | `data` |
| x-compile-omitted | list of attributes to ignore compiled expressions | string[] | `[]` |
| x-slot-node | Slot node mark | [Slot](#slot) | - |

#### Detailed description

Expand Down Expand Up @@ -957,6 +958,83 @@ Writing method two, operating the Schema protocol
}
```
### Slot
#### Description
Mark this node as a Slot node, which will be skipped in the normal rendering process.
Use `target` to specify the target property for rendering this node, which must be a sibling property at the same level.
You can use `isRenderProp` to specify that this node is passed in the form of the renderProp function.
When `isRenderProp` is `true`, the renderProp function’s argument list can be accessed within the Slot through `$slotArgs`.
#### Signature
```ts
type Slot = {
//Slot target: Specify the target property for rendering this node, which must be a sibling property at the same level.
target: string // 'some-sibling-node.x-component-props.xxx' or 'some-sibling-node.x-decorator-props.xxx'
//whether it is a renderProp Slot
isRenderProp?: boolean
}
```
#### Example
**ReactNode Prop**
Reference [SchemaField](https://react.formilyjs.org/api/components/schema-field#json-schema-reactnode-prop-use-case-x-slot-node)
```json
{
"type": "object",
"properties": {
"search_icon": {
"x-slot-node": {
"target": "button.x-component-props.icon" //Specify to render the search_icon node as a slot into the icon prop of the Button component.
},
"x-component": "SearchOutlined",
"x-component-props": {
"data-testid": "icon"
}
},
"button": {
"type": "string",
"x-component": "Button",
"x-component-props": {
"data-testid": "button"
}
}
}
}
```
**RenderProp**
Reference [SchemaField](https://react.formilyjs.org/api/components/schema-field#json-schema-render-prop-use-case-x-slot-node--isrenderprop)
```json
{
"type": "object",
"properties": {
"dollar_icon": {
"x-slot-node": {
"target": "rate.x-component-props.character", //Specify to render the dollar_icon node as a slot into the character prop of the Rate component.
"isRenderProp": true //The character prop accepts a renderProp function. Specify the Slot as a renderProp to take control of the rendering of the rating icons.
},
"x-component": "DollarOutlined",
"x-component-props": {
"data-testid": "icon",
"rotate": "{{$slotArgs[0].value * 45}}", //When isRenderProp is true, the renderProp function’s argument list can be accessed within the Slot through $slotArgs.
"style": {
"fontSize": "50px"
}
}
},
"rate": {
"x-component": "Rate"
}
}
}
```
## Built-in expression scope
Built-in expression scope is mainly used to realize various linkage relationships in expressions
Expand Down Expand Up @@ -996,3 +1074,7 @@ It can only be consumed by expressions in x-reactions, corresponding to the depe
### $target
Can only be consumed in expressions in x-reactions, representing the target field of active mode
### $slotArgs
Can only be consumed in slot node. When slot used as render prop, $slotArgs can access render function arguments array
Loading

0 comments on commit c8d8312

Please sign in to comment.