feat(client): give more reasonable names

This commit is contained in:
chenos 2022-02-14 18:26:16 +08:00
parent 441a7aecf2
commit 620b2b463e
4 changed files with 131 additions and 88 deletions

View File

@ -75,7 +75,7 @@ const useCurrentFieldSchema = (path: string) => {
schema &&
remove(schema, {
removeParentsIfNoChildren: true,
breakSchema: (s) => {
breakRemoveOn: (s) => {
return s['x-component'] === 'Grid';
},
});

View File

@ -1,15 +1,18 @@
import { DndContext as DndKitContext, DragEndEvent, rectIntersection } from '@dnd-kit/core';
import { observer } from '@formily/react';
import React from 'react';
import { useAPIClient } from '../../../';
import { createDesignable, useDesignable } from '../../hooks';
const useDragEnd = () => {
const { refresh } = useDesignable();
const api = useAPIClient();
return ({ active, over }: DragEndEvent) => {
const activeSchema = active?.data?.current?.schema;
const overSchema = over?.data?.current?.schema;
const insertAdjacent = over?.data?.current?.insertAdjacent;
const breakRemoveOn = over?.data?.current?.breakRemoveOn;
const wrapSchema = over?.data?.current?.wrapSchema;
if (!activeSchema || !overSchema) {
@ -21,11 +24,12 @@ const useDragEnd = () => {
}
const dn = createDesignable({
api,
refresh,
current: overSchema,
});
dn.on('afterInsertAdjacent', refresh);
dn.on('afterRemove', refresh);
dn.loadAPIClientEvents();
if (activeSchema.parent === overSchema.parent) {
return dn.insertBeforeBeginOrAfterEnd(activeSchema);
@ -34,6 +38,7 @@ const useDragEnd = () => {
if (insertAdjacent) {
dn.insertAdjacent(insertAdjacent, activeSchema, {
wrap: wrapSchema,
breakRemoveOn,
removeParentsIfNoChildren: true,
});
return;

View File

@ -217,14 +217,14 @@ describe('createDesignable', () => {
});
});
describe('removeIfNoChildren', () => {
describe('recursiveRemoveIfNoChildren', () => {
test('has child nodes', () => {
dn.removeIfNoChildren(schema.properties.current);
dn.recursiveRemoveIfNoChildren(schema.properties.current);
expect(schema.properties.current).toBeDefined();
});
test('no child nodes', () => {
dn.removeIfNoChildren(schema.properties.current.properties.child);
dn.recursiveRemoveIfNoChildren(schema.properties.current.properties.child);
expect(schema.properties.current?.properties?.child).toBeUndefined();
});
});
@ -241,20 +241,20 @@ describe('createDesignable', () => {
expect(schema.properties.current?.properties?.child).toBeUndefined();
});
test('removeParentsIfNoChildren + breakSchema json', () => {
test('removeParentsIfNoChildren + breakRemoveOn json', () => {
dn.remove(schema.properties.current.properties.child, {
removeParentsIfNoChildren: true,
breakSchema: {
breakRemoveOn: {
'x-uid': 'global',
},
});
expect(schema?.properties?.current).toBeUndefined();
});
test('removeParentsIfNoChildren + breakSchema function', () => {
test('removeParentsIfNoChildren + breakRemoveOn function', () => {
dn.remove(schema.properties.current.properties.child, {
removeParentsIfNoChildren: true,
breakSchema: (s) => s['x-uid'] === 'global',
breakRemoveOn: (s) => s['x-uid'] === 'global',
});
expect(schema?.properties?.current).toBeUndefined();
});
@ -486,6 +486,6 @@ describe('parentsIn', () => {
dn.on('error', callback);
dn.insertAfterBegin(schema.properties.menu);
expect(schema.properties.menu).toBeDefined();
expect(callback.mock.calls[0][1].code).toBe('parent_is_not_allowed');
expect(callback.mock.calls[0][0].code).toBe('parent_is_not_allowed');
});
});

View File

@ -3,11 +3,13 @@ import { uid } from '@formily/shared';
import get from 'lodash/get';
import set from 'lodash/set';
import React, { useContext } from 'react';
import { useAPIClient } from '../../api-client';
import { APIClient, useAPIClient } from '../../api-client';
import { SchemaComponentContext } from '../context';
interface CreateDesignableProps {
current: Schema;
api?: APIClient;
refresh?: () => void;
}
export function createDesignable(options: CreateDesignableProps) {
@ -22,13 +24,18 @@ type Position = 'beforeBegin' | 'afterBegin' | 'beforeEnd' | 'afterEnd';
interface InsertAdjacentOptions {
wrap?: (s: ISchema) => ISchema;
removeParentsIfNoChildren?: boolean;
breakRemoveOn?: ISchema | BreakFn;
}
type BreakFn = (s: ISchema) => boolean;
interface RemoveOptions {
removeParentsIfNoChildren?: boolean;
breakSchema?: ISchema | BreakFn;
breakRemoveOn?: ISchema | BreakFn;
}
interface RecursiveRemoveOptions {
breakRemoveOn?: ISchema | BreakFn;
}
const generateUid = (s: ISchema) => {
@ -42,13 +49,59 @@ const generateUid = (s: ISchema) => {
const defaultWrap = (s: ISchema) => s;
const matchSchema = (source: ISchema, target: ISchema) => {
if (!source || !target) {
return;
}
for (const key in target) {
if (Object.prototype.hasOwnProperty.call(target, key)) {
const value = target[key];
if (value !== source?.[key]) {
return false;
}
}
}
return true;
};
export class Designable {
current: Schema;
options: CreateDesignableProps;
events = {};
constructor({ current }) {
this.current = current;
constructor(options: CreateDesignableProps) {
this.options = options;
this.current = options.current;
}
loadAPIClientEvents() {
const { refresh, api } = this.options;
if (!api) {
return;
}
this.on('insertAdjacent', async ({ current, position, schema, removed }) => {
refresh();
await api.request({
url: `/ui_schemas:insertAdjacent/${current['x-uid']}?position=${position}`,
method: 'post',
data: schema.toJSON(),
});
if (removed?.['x-uid']) {
await api.request({
url: `/ui_schemas:remove/${removed['x-uid']}`,
method: 'post',
});
}
});
this.on('remove', async ({ removed }) => {
refresh();
if (removed?.['x-uid']) {
await api.request({
url: `/ui_schemas:remove/${removed['x-uid']}`,
method: 'post',
});
}
});
}
prepareProperty(schema: ISchema) {
@ -68,18 +121,19 @@ export class Designable {
generateUid(schema);
}
on(name: 'afterInsertAdjacent' | 'afterRemove' | 'error', listener: any) {
on(name: 'insertAdjacent' | 'remove' | 'error', listener: any) {
if (!this.events[name]) {
this.events[name] = [];
}
this.events[name].push(listener);
}
emit(name: 'afterInsertAdjacent' | 'afterRemove' | 'error', ...args) {
emit(name: 'insertAdjacent' | 'remove' | 'error', ...args) {
if (!this.events[name]) {
return;
}
this.events[name].forEach((fn) => fn.bind(this)(this.current, ...args));
const [opts, ...others] = args;
this.events[name].forEach((fn) => fn.bind(this)({ current: this.current, ...opts }, ...others));
}
parentsIn(schema: Schema) {
@ -112,69 +166,46 @@ export class Designable {
}
}
removeIfNoChildren(schema?: Schema) {
recursiveRemoveIfNoChildren(schema?: Schema, options?: RecursiveRemoveOptions) {
if (!schema) {
return;
}
let s = schema;
const count = Object.keys(s.properties || {}).length;
if (count > 0) {
return;
}
let removed: Schema;
while (s.parent) {
removed = s.parent.removeProperty(s.name);
const count = Object.keys(s.parent.properties || {}).length;
const breakRemoveOn = options?.breakRemoveOn;
while (s) {
if (typeof breakRemoveOn === 'function') {
if (breakRemoveOn(s)) {
break;
}
} else {
if (matchSchema(s, breakRemoveOn)) {
break;
}
}
const count = Object.keys(s.properties || {}).length;
if (count > 0) {
break;
}
if (s.parent) {
removed = s.parent.removeProperty(s.name);
}
s = s.parent;
}
return removed;
}
remove(schema?: Schema, options: RemoveOptions = {}) {
const { removeParentsIfNoChildren, breakSchema } = options;
const { breakRemoveOn, removeParentsIfNoChildren } = options;
let s = schema || this.current;
let removed;
const matchSchema = (source: ISchema, target: ISchema) => {
if (!source || !target) {
return;
let removed = s.parent.removeProperty(s.name);
if (removeParentsIfNoChildren) {
const parent = this.recursiveRemoveIfNoChildren(s.parent, { breakRemoveOn });
if (parent) {
removed = parent;
}
for (const key in target) {
if (Object.prototype.hasOwnProperty.call(target, key)) {
const value = target[key];
if (value !== source?.[key]) {
return false;
}
}
}
return true;
};
while (s.parent) {
removed = s.parent.removeProperty(s.name);
if (!removeParentsIfNoChildren) {
break;
}
if (typeof breakSchema === 'function') {
if (breakSchema(s?.parent)) {
break;
}
} else {
if (matchSchema(s?.parent, breakSchema)) {
break;
}
}
if (s?.parent?.['x-component'] === breakSchema) {
break;
}
const count = Object.keys(s.parent.properties || {}).length;
if (count > 0) {
break;
}
s = s.parent;
}
this.emit('afterRemove', removed, options);
this.emit('remove', { removed });
}
insertBeforeBeginOrAfterEnd(schema: ISchema, options: InsertAdjacentOptions = {}) {
@ -208,7 +239,7 @@ export class Designable {
return;
}
const opts = {};
const { wrap = defaultWrap, removeParentsIfNoChildren } = options;
const { wrap = defaultWrap, breakRemoveOn, removeParentsIfNoChildren } = options;
if (Schema.isSchemaInstance(schema)) {
if (this.parentsIn(schema)) {
this.emit('error', {
@ -219,7 +250,7 @@ export class Designable {
}
schema.parent.removeProperty(schema.name);
if (removeParentsIfNoChildren) {
opts['removed'] = this.removeIfNoChildren(schema.parent);
opts['removed'] = this.recursiveRemoveIfNoChildren(schema.parent, { breakRemoveOn });
}
}
const properties = {};
@ -245,7 +276,11 @@ export class Designable {
s['x-index'] = newOrder;
s.parent = this.current.parent;
this.current.parent.setProperties(properties);
this.emit('afterInsertAdjacent', 'beforeBegin', s, opts);
this.emit('insertAdjacent', {
position: 'beforeBegin',
schema: s,
...opts,
});
}
/**
@ -259,7 +294,7 @@ export class Designable {
return;
}
const opts = {};
const { wrap = defaultWrap, removeParentsIfNoChildren } = options;
const { wrap = defaultWrap, breakRemoveOn, removeParentsIfNoChildren } = options;
if (Schema.isSchemaInstance(schema)) {
if (this.parentsIn(schema)) {
this.emit('error', {
@ -270,7 +305,7 @@ export class Designable {
}
schema.parent.removeProperty(schema.name);
if (removeParentsIfNoChildren) {
opts['removed'] = this.removeIfNoChildren(schema.parent);
opts['removed'] = this.recursiveRemoveIfNoChildren(schema.parent, { breakRemoveOn });
}
}
const properties = {};
@ -287,7 +322,11 @@ export class Designable {
s['x-index'] = 0;
s.parent = this.current;
this.current.setProperties(properties);
this.emit('afterInsertAdjacent', 'afterBegin', s, opts);
this.emit('insertAdjacent', {
position: 'afterBegin',
schema: s,
...opts,
});
}
/**
@ -301,7 +340,7 @@ export class Designable {
return;
}
const opts = {};
const { wrap = defaultWrap, removeParentsIfNoChildren } = options;
const { wrap = defaultWrap, breakRemoveOn, removeParentsIfNoChildren } = options;
if (Schema.isSchemaInstance(schema)) {
if (this.parentsIn(schema)) {
this.emit('error', {
@ -312,14 +351,18 @@ export class Designable {
}
schema.parent.removeProperty(schema.name);
if (removeParentsIfNoChildren) {
opts['removed'] = this.removeIfNoChildren(schema.parent);
opts['removed'] = this.recursiveRemoveIfNoChildren(schema.parent, { breakRemoveOn });
}
}
this.prepareProperty(schema);
const wrapped = wrap(schema);
const s = this.current.addProperty(wrapped.name || uid(), wrapped);
s.parent = this.current;
this.emit('afterInsertAdjacent', 'beforeEnd', s, opts);
this.emit('insertAdjacent', {
position: 'beforeEnd',
schema: s,
...opts,
});
}
/**
@ -330,7 +373,7 @@ export class Designable {
return;
}
const opts = {};
const { wrap = defaultWrap, removeParentsIfNoChildren } = options;
const { wrap = defaultWrap, breakRemoveOn, removeParentsIfNoChildren } = options;
if (Schema.isSchemaInstance(schema)) {
if (this.parentsIn(schema)) {
this.emit('error', {
@ -341,7 +384,7 @@ export class Designable {
}
schema.parent.removeProperty(schema.name);
if (removeParentsIfNoChildren) {
opts['removed'] = this.removeIfNoChildren(schema.parent);
opts['removed'] = this.recursiveRemoveIfNoChildren(schema.parent, { breakRemoveOn });
}
schema.parent = null;
}
@ -371,7 +414,11 @@ export class Designable {
s.parent = this.current.parent;
s['x-index'] = newOrder;
this.current.parent.setProperties(properties);
this.emit('afterInsertAdjacent', 'afterEnd', s, opts);
this.emit('insertAdjacent', {
position: 'afterEnd',
schema: s,
...opts,
});
}
}
@ -384,18 +431,9 @@ export function useDesignable() {
};
const field = useField();
const fieldSchema = useFieldSchema();
const dn = createDesignable({ current: fieldSchema });
const api = useAPIClient();
dn.on('afterInsertAdjacent', async (current, position, schema) => {
refresh();
// await api.request({
// url: `/ui_schemas:insertAdjacent/${current['x-uid']}?position=${position}`,
// method: 'post',
// data: schema.toJSON(),
// });
// console.log(current, position, schema);
});
dn.on('afterRemove', refresh);
const dn = createDesignable({ api, refresh, current: fieldSchema });
dn.loadAPIClientEvents();
return {
designable,
reset,