tachybase_todo/packages/client/src/schema-component/hooks/useDesignable.tsx
chenos a87a089acf
feat: client v0.6 (#150)
* v0.6

* update...

* feat: improve code

* improve code

* action & form

* update...

* improve code

* improve code

* improve code

* designable

* update

* update...

* api client

* RecordProvider

* collection manager

* update...

* update api client

* update use request

* update

* update doc

* test cases for compose

* docs: improve documentation
2022-01-10 19:22:21 +08:00

279 lines
7.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useContext } from 'react';
import { ISchema, Schema, useField, useFieldSchema } from '@formily/react';
import { SchemaComponentContext } from '../context';
import set from 'lodash/set';
import { uid } from '@formily/shared';
interface CreateDesignableProps {
current: Schema;
}
export function createDesignable(options: CreateDesignableProps) {
return new Designable(options);
}
/**
*
*/
type Position = 'beforeBegin' | 'afterBegin' | 'beforeEnd' | 'afterEnd';
const generateUid = (s: ISchema) => {
if (!s['x-uid']) {
s['x-uid'] = uid();
}
Object.keys(s.properties || {}).forEach((key) => {
generateUid(s.properties[key]);
});
};
export class Designable {
current: Schema;
events = {};
constructor({ current }) {
this.current = current;
}
prepareProperty(schema: ISchema) {
if (!schema.type) {
schema.type = 'void';
}
if (!schema.name) {
schema.name = uid();
}
// x-uid 仅用于后端查询 schema如果 current 没有 x-uid 不处理
if (!this.current['x-uid']) {
return;
}
// if (Schema.isSchemaInstance(schema)) {
// return;
// }
generateUid(schema);
}
on(name: 'afterInsertAdjacent' | 'afterRemove', listener: any) {
if (!this.events[name]) {
this.events[name] = [];
}
this.events[name].push(listener);
}
emit(name: 'afterInsertAdjacent' | 'afterRemove', ...args) {
if (!this.events[name]) {
return;
}
this.events[name].forEach((fn) => fn.bind(this)(...args));
}
insertAdjacent(position: Position, schema: ISchema) {
switch (position) {
case 'beforeBegin':
return this.insertBeforeBegin(schema);
case 'afterBegin':
return this.insertAfterBegin(schema);
case 'beforeEnd':
return this.insertBeforeEnd(schema);
case 'afterEnd':
return this.insertAfterEnd(schema);
}
}
remove() {
const s = this.current.parent.removeProperty(this.current.name);
this.emit('afterRemove', s);
}
insertBeforeBeginOrAfterEnd(schema: ISchema) {
if (!Schema.isSchemaInstance(this.current)) {
return;
}
if (!Schema.isSchemaInstance(schema)) {
return;
}
if (this.current.parent !== schema.parent) {
return;
}
let fromIndex = 0;
let toIndex = 0;
this.current.parent.mapProperties((property, key, index) => {
if (this.current.name === key) {
toIndex = index;
}
if (schema.name === key) {
fromIndex = index;
}
});
return fromIndex > toIndex ? this.insertBeforeBegin(schema) : this.insertAfterEnd(schema);
}
/**
* Before the current schema itself.
*/
insertBeforeBegin(schema: ISchema) {
if (!Schema.isSchemaInstance(this.current)) {
return;
}
const properties = {};
let start = false;
if (Schema.isSchemaInstance(schema)) {
schema.parent.removeProperty(schema.name);
schema.parent = this.current.parent;
}
this.current.parent.mapProperties((property, key) => {
if (key === this.current.name) {
start = true;
}
if (start) {
properties[key] = property;
this.current.parent.removeProperty(key);
}
});
this.prepareProperty(schema);
const s = this.current.parent.addProperty(schema.name, schema);
this.current.parent.setProperties(properties);
this.emit('afterInsertAdjacent', 'beforeBegin', s);
}
/**
* Just inside the current schema, before its first child.
*
* @param schema
* @returns
*/
insertAfterBegin(schema: ISchema) {
if (!Schema.isSchemaInstance(this.current)) {
return;
}
if (Schema.isSchemaInstance(schema)) {
schema.parent.removeProperty(schema.name);
schema.parent = this.current;
}
const properties = {};
this.current.mapProperties((schema, key) => {
properties[key] = schema;
});
this.current.properties = {};
this.prepareProperty(schema);
const s = this.current.addProperty(schema.name, schema);
this.current.setProperties(properties);
this.emit('afterInsertAdjacent', 'afterBegin', s);
}
/**
* Just inside the targetElement, after its last child.
*
* @param schema
* @returns
*/
insertBeforeEnd(schema: ISchema) {
if (!Schema.isSchemaInstance(this.current)) {
return;
}
if (Schema.isSchemaInstance(schema)) {
schema.parent.removeProperty(schema.name);
schema.parent = this.current;
}
this.prepareProperty(schema);
const s = this.current.addProperty(schema.name || uid(), schema);
this.emit('afterInsertAdjacent', 'beforeEnd', s);
}
/**
* After the current schema itself.
*/
insertAfterEnd(schema: ISchema) {
if (!Schema.isSchemaInstance(this.current)) {
return;
}
const properties = {};
let start = false;
if (Schema.isSchemaInstance(schema)) {
schema.parent.removeProperty(schema.name);
schema.parent = this.current.parent;
}
this.current.parent.mapProperties((property, key) => {
if (key === this.current.name) {
start = true;
}
if (start && key !== this.current.name) {
properties[key] = property;
this.current.parent.removeProperty(key);
}
});
this.prepareProperty(schema);
const s = this.current.parent.addProperty(schema.name || uid(), schema);
this.current.parent.setProperties(properties);
this.emit('afterInsertAdjacent', 'afterEnd', s);
}
}
// TODO
export function useDesignable() {
const { designable, refresh, reset } = useContext(SchemaComponentContext);
const DesignableBar = () => {
return <></>;
};
const field = useField();
const fieldSchema = useFieldSchema();
const dn = createDesignable({ current: fieldSchema });
dn.on('afterInsertAdjacent', refresh);
dn.on('afterRemove', refresh);
return {
designable,
reset,
refresh,
DesignableBar,
on: dn.on.bind(dn),
// TODO
patch: (key: ISchema | string, value?: any) => {
const update = (obj: any) => {
Object.keys(obj).forEach((k) => {
const val = obj[k];
if (k === 'title') {
field.title = val;
fieldSchema['title'] = val;
}
if (k === 'x-component-props') {
Object.keys(val).forEach((i) => {
field.componentProps[i] = val[i];
fieldSchema['x-component-props'][i] = val[i];
});
}
});
};
if (typeof key === 'string') {
const obj = {};
set(obj, key, value);
return update(obj);
}
update(key);
refresh();
},
remove() {
dn.remove();
},
insertAdjacent(position: Position, schema: ISchema) {
dn.insertAdjacent(position, schema);
},
insertBeforeBegin(schema: ISchema) {
dn.insertBeforeBegin(schema);
},
insertAfterBegin(schema: ISchema) {
dn.insertAfterBegin(schema);
},
insertBeforeEnd(schema: ISchema) {
dn.insertBeforeEnd(schema);
},
insertAfterEnd(schema: ISchema) {
dn.insertAfterEnd(schema);
},
};
}