designable bar

This commit is contained in:
chenos 2021-07-01 00:16:43 +08:00
parent 75cd158a27
commit 46e4089140
12 changed files with 976 additions and 361 deletions

View File

@ -1,6 +1,6 @@
import React, { createContext, useState } from 'react';
import {
FormItem,
// FormItem,
FormLayout,
FormButtonGroup,
Submit,
@ -41,6 +41,9 @@ import { Table } from './table';
import { Tabs } from './tabs';
import { TimePicker } from './time-picker';
import { Upload } from './upload';
import { FormItem } from './form-item';
export const BlockContext = createContext({ dragRef: null });
const Div = (props) => <div {...props} />;
@ -51,7 +54,6 @@ const scope = {
};
const components = {
Div,
Space,
Card,
@ -87,13 +89,13 @@ const components = {
};
export function registerScope(scopes) {
Object.keys(scopes).forEach(key => {
Object.keys(scopes).forEach((key) => {
scope[key] = scopes[key];
});
}
export function registerComponents(values) {
Object.keys(values).forEach(key => {
Object.keys(values).forEach((key) => {
components[key] = values[key];
});
}
@ -123,6 +125,9 @@ export function DesignableProvider(props) {
);
}
export const DesignableContext =
createContext<{ schema: Schema; refresh: any }>(null);
export function DesignableSchemaField(props: { schema?: ISchema }) {
return (
<DesignableProvider schema={new Schema(props.schema)}>

View File

@ -127,8 +127,8 @@ export type ActionType = React.FC<ActionProps> & {
DesignableBar?: React.FC<any>;
};
function Blank({ children }) {
return children;
function Blank() {
return null;
}
function useDesignableBar() {
@ -171,45 +171,44 @@ export const Action: ActionType = observer((props) => {
);
}
return (
<DesignableBar>
<Button
{...others}
onClick={async () => {
if (!childSchema) {
return await run();
}
if (childSchema['x-component'] === 'Action.Drawer') {
Drawer.open({
title: childSchema.title,
...(childSchema['x-component-props'] || {}),
content: () => {
return (
<div>
<SchemaBlock schema={childSchema} onlyRenderProperties />
</div>
);
},
});
}
if (childSchema['x-component'] === 'Action.Container') {
const el = document.createElement('div');
const target = document.querySelector(
childSchema['x-component-props']?.['container'],
);
target.childNodes.forEach((child) => child.remove());
target.appendChild(el);
ReactDOM.render(
<div>
<SchemaBlock schema={childSchema} onlyRenderProperties />
</div>,
el,
);
}
}}
>
{field.title}
</Button>
</DesignableBar>
<Button
{...others}
onClick={async () => {
if (!childSchema) {
return await run();
}
if (childSchema['x-component'] === 'Action.Drawer') {
Drawer.open({
title: childSchema.title,
...(childSchema['x-component-props'] || {}),
content: () => {
return (
<div>
<SchemaBlock schema={childSchema} onlyRenderProperties />
</div>
);
},
});
}
if (childSchema['x-component'] === 'Action.Container') {
const el = document.createElement('div');
const target = document.querySelector(
childSchema['x-component-props']?.['container'],
);
target.childNodes.forEach((child) => child.remove());
target.appendChild(el);
ReactDOM.render(
<div>
<SchemaBlock schema={childSchema} onlyRenderProperties />
</div>,
el,
);
}
}}
>
{field.title}
<DesignableBar />
</Button>
);
});
@ -228,17 +227,17 @@ Action.URL = observer((props) => {
);
});
Action.DesignableBar = (props) => {
Action.DesignableBar = () => {
const field = useField();
const { schema, refresh } = useSchemaQuery();
const [visible, setVisible] = useState(false);
return (
<div className={'designable'}>
<div className={classNames('designable-bar', { active: visible })}>
<span
onClick={(e) => {
e.stopPropagation();
}}
className={classNames('designable-bar', { active: visible })}
className={classNames('designable-bar-actions', { active: visible })}
>
<Dropdown
trigger={['click']}
@ -266,7 +265,6 @@ Action.DesignableBar = (props) => {
<MenuOutlined />
</Dropdown>
</span>
{props.children}
</div>
);
};

View File

@ -1,6 +1,25 @@
.designable {
display: inline-block;
position: relative;
// .designable {
// display: inline-block;
// position: relative;
// &:hover {
// .designable-bar {
// display: block;
// }
// }
// .designable-bar {
// display: none;
// background: #fff;
// z-index: 2;
// position: absolute;
// right: 10px;
// top: 50%;
// transform: translateY(-50%);
// &.active {
// display: block;
// }
// }
// }
.ant-btn {
&:hover {
.designable-bar {
display: block;
@ -8,14 +27,27 @@
}
.designable-bar {
display: none;
background: #fff;
z-index: 2;
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
top: -1px;
right: -1px;
left: -1px;
bottom: -1px;
border-radius: 2px;
border: 2px solid #1890ff;
&.active {
display: block;
}
.designable-bar-actions {
position: absolute;
right: 0;
line-height: 1rem;
background-color: #1890ff;
color: #fff;
z-index: 10;
padding: 0 3px;
.anticon {
font-size: 10px;
}
}
}
}

View File

@ -75,10 +75,6 @@ const row = (schema) => {
[`gb_${uid()}`]: {
type: 'void',
'x-component': 'Grid.Block',
'x-component-props': {
DesignableBar: `${component}.DesignableBar`,
},
'x-designable-bar': `${component}.DesignableBar`,
properties: {
[`gbn_${uid()}`]: schema,
},
@ -394,6 +390,7 @@ AddNew.FormItem = observer((props) => {
name: `f_${uid()}`,
title: `字段${uid()}`,
'x-decorator': 'FormItem',
'x-designable-bar': 'FormItem.DesignableBar',
'x-component': 'Input',
});
console.log({ rowData });

View File

@ -41,11 +41,19 @@ const schema = {
interface: 'icon',
type: 'string',
title: `阅读模式`,
name: 'name2',
'x-read-pretty': true,
'x-decorator': 'FormItem',
'x-component': 'ColorSelect',
}
},
designable: {
interface: 'icon',
type: 'string',
title: `配置模式`,
// 'x-read-pretty': true,
'x-decorator': 'FormItem',
'x-designable-bar': 'FormItem.DesignableBar',
'x-component': 'ColorSelect',
},
}
};

View File

@ -0,0 +1,71 @@
import React, { useContext, useState } from 'react';
import { connect, mapProps, mapReadPretty, SchemaOptionsContext, useField, useFieldSchema } from '@formily/react';
import { FormItem as FormilyFormItem } from '@formily/antd';
import { Dropdown, Menu } from 'antd';
import classNames from 'classnames';
import { MenuOutlined } from '@ant-design/icons';
import './style.less';
import get from 'lodash/get';
import { BlockContext } from '../SchemaField';
function Blank() {
return null;
}
function useDesignableBar() {
const schema = useFieldSchema();
const options = useContext(SchemaOptionsContext);
const DesignableBar = get(options.components, schema['x-designable-bar']);
return {
DesignableBar: DesignableBar || Blank,
};
}
export const FormItem: any = connect((props) => {
const { DesignableBar } = useDesignableBar();
const { dragRef } = useContext(BlockContext);
return (
<div className={'designable-form-item'}>
<FormilyFormItem {...props} />
{dragRef && <div ref={dragRef}>Drag</div>}
<DesignableBar/>
</div>
);
});
FormItem.DesignableBar = () => {
const field = useField();
const [visible, setVisible] = useState(false);
return (
<div className={classNames('designable-bar', { active: visible })}>
<span
onClick={(e) => {
e.stopPropagation();
}}
className={classNames('designable-bar-actions', { active: visible })}
>
<Dropdown
trigger={['click']}
visible={visible}
onVisibleChange={(visible) => {
setVisible(visible);
}}
overlay={
<Menu>
<Menu.Item
onClick={(e) => {
setVisible(false);
}}
>
</Menu.Item>
</Menu>
}
>
<MenuOutlined />
</Dropdown>
</span>
</div>
);
};

View File

@ -0,0 +1,36 @@
.designable-form-item {
position: relative;
&:hover {
.designable-bar {
display: block;
}
}
.designable-bar {
z-index: 1;
pointer-events: none;
display: none;
position: absolute;
top: 0;
right: -0;
left: -0;
bottom: -0;
border-radius: 2px;
border: 2px solid #1890ff;
&.active {
display: block;
}
.designable-bar-actions {
pointer-events: auto;
position: absolute;
right: 0;
line-height: 1rem;
background-color: #1890ff;
color: #fff;
z-index: 10;
padding: 0 3px;
.anticon {
font-size: 10px;
}
}
}
}

View File

@ -1,4 +1,4 @@
import React, { useState, useContext } from 'react';
import React, { useState, useContext, createContext } from 'react';
import { useDrag, mergeRefs, useDrop } from './DND';
import classNames from 'classnames';
import {
@ -17,6 +17,7 @@ import {
DeleteOutlined,
} from '@ant-design/icons';
import get from 'lodash/get';
import { BlockContext } from '../SchemaField';
export const Block = observer((props: any) => {
const { children, title, DesignableBar } = props;
@ -50,6 +51,8 @@ export const Block = observer((props: any) => {
console.log({ DesignableBarComponent, DesignableBar });
const { addBlock } = useSchemaQuery();
console.log({ children });
return (
<div
data-type={'block'}
@ -57,10 +60,12 @@ export const Block = observer((props: any) => {
className={classNames('block', { 'top-half': onTopHalf, hover: isOver })}
// style={{ textAlign: 'center', lineHeight: '60px', background: '#f1f1f1' }}
>
{DesignableBarComponent && (
<BlockContext.Provider value={{ dragRef }}>
{children}
</BlockContext.Provider>
{/* {DesignableBarComponent && (
<DefaultActionBar DesignableBarComponent={DesignableBarComponent} dragRef={dragRef} />
)}
{children}
)} */}
</div>
);
});

View File

@ -30,7 +30,7 @@ const schema = {
menu1: {
type: 'void',
'x-component': 'Menu',
// 'x-decorator': 'Menu.Designable',
'x-designable-bar': 'Menu.DesignableBar',
'x-component-props': {
mode: 'horizontal',
},
@ -124,7 +124,7 @@ const schema = {
export default () => {
return (
<SchemaBlock schema={schema} />
<div style={{width: 200}}><SchemaBlock schema={schema} /></div>
);
};
```

File diff suppressed because it is too large Load Diff

View File

@ -1,34 +1,62 @@
.designable-action {
position: absolute;
right: 16px;
display: none;
z-index: 10;
}
.ant-menu-dark.ant-menu-horizontal {
width: 100%;
}
.ant-menu-dark .ant-menu-item-active .designable-action {
background-color: #001529;
.ant-menu-dark .ant-menu-item-active .designable-bar {
display: inline-block;
}
.ant-menu-dark .ant-menu-item-active:hover .designable-action,
.ant-menu-dark .ant-menu-item-active.ant-menu-item-selected .designable-action {
background-color: #1890ff;
.ant-menu-dark .ant-menu-item-active:hover .designable-bar,
.ant-menu-dark .ant-menu-item-active.ant-menu-item-selected .designable-bar {
display: inline-block;
}
.ant-menu-light .ant-menu-submenu-active > .ant-menu-submenu-title .designable-action,
.ant-menu-light .ant-menu-item-active .designable-action {
background-color: #fff;
.ant-menu-light .ant-menu-submenu-active > .ant-menu-submenu-title .designable-bar,
.ant-menu-light .ant-menu-item-active .designable-bar {
display: inline-block;
}
.ant-menu-sub.ant-menu-inline .ant-menu-submenu-active > .ant-menu-submenu-title .designable-action,
.ant-menu-sub.ant-menu-inline .ant-menu-item-active .designable-action {
background-color: #fafafa;
// .ant-menu-sub.ant-menu-inline .ant-menu-submenu-active > .ant-menu-submenu-title .designable-action,
// .ant-menu-sub.ant-menu-inline .ant-menu-item-active .designable-action {
// background-color: #fafafa;
// }
// .ant-menu-light .ant-menu-item-selected .designable-action {
// background-color: #e6f7ff !important;
// }
.ant-menu {
.designable-bar {
display: none;
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
border: 2px solid #1890ff;
&.active {
display: block;
}
.designable-bar-actions {
position: absolute;
right: 0;
line-height: 1rem;
background-color: #1890ff;
color: #fff;
font-size: 12px;
z-index: 10;
.anticon {
font-size: 10px;
}
}
}
}
.ant-menu-light .ant-menu-item-selected .designable-action {
background-color: #e6f7ff !important;
}
.ant-menu-submenu-horizontal > .ant-menu-submenu-title {
.designable-bar {
position: absolute;
left: -20px;
right: -20px;
border: 2px solid #1890ff;
}
}

View File

@ -189,11 +189,11 @@ function useMenuSchema({ schema, selectedKey }) {
// const topMenuSchema = new Schema(cloneDeep(schema.toJSON()));
let selected = find(schema).shift() as Schema;
let selected = find(topMenuSchema).shift() as Schema;
console.log({ topMenuSchema, selected, schema });
if (selected.properties) {
if (selected && selected.properties) {
const findChild = (properties) => {
const keys = Object.keys(properties || {});
const firstKey = keys.shift();
@ -420,8 +420,6 @@ function Content({ activeKey }) {
export function AdminLayout({ route, children }: any) {
const match = useRouteMatch<any>();
console.log('match.params', match.params);
const { data = {}, loading } = useRequest(
`/api/blocks:getSchema/${route.blockId}`,
{