Skip to content

Commit

Permalink
feat(designable): add data source setter (#1746)
Browse files Browse the repository at this point in the history
  • Loading branch information
CoCoManYY authored Jul 9, 2021
1 parent 4ad1884 commit a6206e8
Show file tree
Hide file tree
Showing 16 changed files with 5,310 additions and 4,616 deletions.
7 changes: 5 additions & 2 deletions designable/antd/playground/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ import {
SchemaEditorWidget,
MarkupSchemaWidget,
} from './widgets'
import { DataSourceSetter } from '../../setters/src'
import 'antd/dist/antd.less'

GlobalRegistry.registerDesignerLocales({
'zh-CN': {
sources: {
Expand Down Expand Up @@ -110,7 +110,10 @@ const App = () => {
</WorkspacePanel>
</Workspace>
<SettingsPanel title="panels.PropertySettings">
<SettingsForm uploadAction="https://www.mocky.io/v2/5cc8019d300000980a055e76" />
<SettingsForm
components={{ DataSourceSetter }}
uploadAction="https://www.mocky.io/v2/5cc8019d300000980a055e76"
/>
</SettingsPanel>
</MainPanel>
</Designer>
Expand Down
2 changes: 1 addition & 1 deletion designable/antd/src/components/DesignableField/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ export const createDesignableField = (options: IDesignableFieldProps) => {
},
enum: {
'x-decorator': 'FormItem',
// 'x-component': 'DataSourceSetter',
'x-component': 'DataSourceSetter',
'x-index': 6,
},
'x-validator': {
Expand Down
14 changes: 13 additions & 1 deletion designable/setters/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,28 @@
"webpack": "^4.41.5",
"webpack-bundle-analyzer": "^3.9.0",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.10.1"
"webpack-dev-server": "^3.10.1",
"antd": "^4.0.0",
"@ant-design/icons": "^4.0.0"
},
"peerDependencies": {
"@types/react": ">=16.8.0 || >=17.0.0",
"@types/react-dom": ">=16.8.0 || >=17.0.0",
"antd": "^4.0.0",
"@ant-design/icons": "^4.0.0",
"react": ">=16.8.0 || >=17.0.0",
"react-dom": ">=16.8.0",
"react-is": ">=16.8.0 || >=17.0.0"
},
"dependencies": {
"@designable/core": "^0.3.24",
"@designable/formily": "^0.3.24",
"@designable/react": "^0.3.26",
"@formily/antd": "2.0.0-beta.74",
"@formily/core": "2.0.0-beta.74",
"@formily/react": "2.0.0-beta.74",
"@formily/shared": "2.0.0-beta.74"
},
"publishConfig": {
"access": "public"
},
Expand Down
116 changes: 116 additions & 0 deletions designable/setters/src/DataSourceSetter/DataSettingPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import React, { useMemo, Fragment, useEffect } from 'react'
import { Button } from 'antd'
import { PlusOutlined } from '@ant-design/icons'
import { ArrayItems, Form, Input, FormItem } from '@formily/antd'
import { createForm } from '@formily/core'
import { observer } from '@formily/reactive-react'
import { createSchemaField } from '@formily/react'
import { ValueInput } from '@designable/react-settings-form'
import { usePrefix, TextWidget } from '@designable/react'
import { Header } from './Header'
import { tranverseTree } from './shared'
import { ITreeDataSource } from './types'
import './styles.less'

const SchemaField = createSchemaField({
components: {
FormItem,
Input,
ArrayItems,
ValueInput,
},
})

export interface IDataSettingPanelProps {
treeDataSource: ITreeDataSource
}

export const DataSettingPanel: React.FC<IDataSettingPanelProps> = observer(
(props) => {
const prefix = usePrefix('data-source-setter')
const form = useMemo(() => {
let values
tranverseTree(props.treeDataSource.dataSource, (dataItem, i) => {
if (dataItem.key === props.treeDataSource.selectedkey) {
values = dataItem
}
})
return createForm({
values,
})
}, [props.treeDataSource.selectedkey])
if (!props.treeDataSource.selectedkey)
return (
<Fragment>
<Header
title={
<TextWidget token="SettingComponents.DataSourceSetter.nodeProperty" />
}
extra={null}
/>
<div className={`${prefix + '-layout-item-content'}`}>
<TextWidget token="SettingComponents.DataSourceSetter.pleaseSelectNode" />
</div>
</Fragment>
)
return (
<Fragment>
<Header
title={
<TextWidget token="SettingComponents.DataSourceSetter.nodeProperty" />
}
extra={
<Button
type="text"
onClick={() => {
form.setFieldState('map', (state) => {
state.value.push({})
})
}}
icon={<PlusOutlined />}
>
<TextWidget token="SettingComponents.DataSourceSetter.addKeyValuePair" />
</Button>
}
/>
<div className={`${prefix + '-layout-item-content'}`}>
<Form form={form}>
<SchemaField>
<SchemaField.Array name="map" x-component="ArrayItems">
<SchemaField.Object x-decorator="ArrayItems.Item">
<SchemaField.String
title={
<TextWidget token="SettingComponents.DataSourceSetter.label" />
}
x-decorator="FormItem"
name="label"
x-component="Input"
/>
<SchemaField.String
title={
<TextWidget token="SettingComponents.DataSourceSetter.value" />
}
x-decorator="FormItem"
name="value"
x-component="ValueInput"
/>
<SchemaField.Void
x-component="ArrayItems.Remove"
x-component-props={{
style: {
margin: 5,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
},
}}
/>
</SchemaField.Object>
</SchemaField.Array>
</SchemaField>
</Form>
</div>
</Fragment>
)
}
)
19 changes: 19 additions & 0 deletions designable/setters/src/DataSourceSetter/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React, { ReactNode } from 'react'
import { observer } from '@formily/reactive-react'
import { usePrefix } from '@designable/react'
import './styles.less'

export interface IHeaderProps {
extra: ReactNode | null
title: ReactNode | string
}

export const Header: React.FC<IHeaderProps> = observer(({ extra, title }) => {
const prefix = usePrefix('data-source-setter')
return (
<div className={`${prefix + '-layout-item-header'}`}>
<div className={`${prefix + '-layout-item-title'}`}>{title}</div>
{extra}
</div>
)
})
64 changes: 64 additions & 0 deletions designable/setters/src/DataSourceSetter/Title.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react'
import { clone } from '@formily/shared'
import { observer } from '@formily/reactive-react'
import { IconWidget, TextWidget } from '@designable/react'
import { INodeItem, ITreeDataSource } from './types'
import { tranverseTree } from './shared'
import './styles.less'
export interface ITitleProps extends INodeItem {
treeDataSource: ITreeDataSource
}

export const Title: React.FC<ITitleProps> = observer((props) => {
const getTitleValue = (dataSource) => {
const optionalKeys = ['label', 'title', 'header']
let nodeTitle
optionalKeys.some((key) => {
const title = (dataSource || [])?.find(
(item) => item.label === key
)?.value
if (title !== undefined) {
nodeTitle = title
return true
}
return false
})
if (nodeTitle === undefined) {
;(dataSource || [])?.some((item) => {
if (item.value && typeof item.value === 'string') {
nodeTitle = item.value
return true
}
return false
})
}
return nodeTitle
}

const renderTitle = (dataSource) => {
const nodeTitle = getTitleValue(dataSource)
if (nodeTitle === undefined)
return (
<TextWidget token="SettingComponents.DataSourceSetter.defaultTitle" />
)
else return nodeTitle + ''
}

return (
<span>
<span style={{ marginRight: '5px' }}>
{renderTitle(props?.map || [])}
</span>
<IconWidget
infer="Remove"
onClick={() => {
const newDataSource = clone(props?.treeDataSource?.dataSource)
tranverseTree(newDataSource || [], (dataItem, i, data) => {
if (data[i].key === props.duplicateKey) (data || []).splice(i, 1)
})
props.treeDataSource.dataSource = newDataSource
}}
/>
</span>
)
})
125 changes: 125 additions & 0 deletions designable/setters/src/DataSourceSetter/TreePanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import React, { Fragment } from 'react'
import { Tree, Button } from 'antd'
import { PlusOutlined } from '@ant-design/icons'
import { uid } from '@formily/shared'
import { observer } from '@formily/reactive-react'
import { usePrefix, TextWidget } from '@designable/react'
import { Title } from './Title'
import { Header } from './Header'
import { tranverseTree } from './shared'
import { ITreeDataSource, INodeItem } from './types'
import './styles.less'

export interface ITreePanelProps {
treeDataSource: ITreeDataSource
}

export const TreePanel: React.FC<ITreePanelProps> = observer((props) => {
const prefix = usePrefix('data-source-setter')
const dropHanle = (info) => {
const dropKey = info.node?.key
const dragKey = info.dragNode?.key
const dropPos = info.node.pos.split('-')
const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1])
const data = [...props.treeDataSource.dataSource]
// Find dragObject
let dragObj
tranverseTree(data, (item, index, arr) => {
if (arr[index].key === dragKey) {
arr.splice(index, 1)
dragObj = item
}
})

if (!info.dropToGap) {
// Drop on the content
tranverseTree(data, (item) => {
if (item.key === dropKey) {
item.children = item.children || []
// where to insert 示例添加到头部,可以是随意位置
item.children.unshift(dragObj)
}
})
} else if (
(info.node.props.children || []).length > 0 && // Has children
info.node.props.expanded && // Is expanded
dropPosition === 1 // On the bottom gap
) {
tranverseTree(data, (item) => {
if (item.key === dropKey) {
item.children = item.children || []
// where to insert 示例添加到头部,可以是随意位置
item.children.unshift(dragObj)
// in previous version, we use item.children.push(dragObj) to insert the
// item to the tail of the children
}
})
} else {
let ar
let i
tranverseTree(data, (item, index, arr) => {
if (item.key === dropKey) {
ar = arr
i = index
}
})
if (dropPosition === -1) {
ar.splice(i, 0, dragObj)
} else {
ar.splice(i + 1, 0, dragObj)
}
}
props.treeDataSource.dataSource = data
}
return (
<Fragment>
<Header
title={
<TextWidget token="SettingComponents.DataSourceSetter.dataSourceTree" />
}
extra={
<Button
type="text"
onClick={() => {
const uuid = uid()
props.treeDataSource.dataSource =
props.treeDataSource.dataSource.concat({
key: uuid,
duplicateKey: uuid,
map: [
{ label: 'label', value: 'Label Text' },
{ label: 'value', value: 'Actual Value' },
],
children: [],
} as INodeItem)
}}
icon={<PlusOutlined />}
>
<TextWidget token="SettingComponents.DataSourceSetter.addNode" />
</Button>
}
/>
<div className={`${prefix + '-layout-item-content'}`}>
<Tree
defaultExpandAll
draggable
showLine={{ showLeafIcon: false }}
treeData={props.treeDataSource.dataSource}
onDragEnter={() => {}}
onDrop={dropHanle}
titleRender={(titleProps: INodeItem) => (
<Title
{...titleProps}
treeDataSource={props.treeDataSource}
></Title>
)}
onSelect={(selectedKeys) => {
if (selectedKeys[0]) {
props.treeDataSource.selectedkey = selectedKeys[0].toString()
}
}}
></Tree>
</div>
</Fragment>
)
})
Loading

0 comments on commit a6206e8

Please sign in to comment.