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 && schema &&
remove(schema, { remove(schema, {
removeParentsIfNoChildren: true, removeParentsIfNoChildren: true,
breakSchema: (s) => { breakRemoveOn: (s) => {
return s['x-component'] === 'Grid'; return s['x-component'] === 'Grid';
}, },
}); });

View File

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

View File

@ -217,14 +217,14 @@ describe('createDesignable', () => {
}); });
}); });
describe('removeIfNoChildren', () => { describe('recursiveRemoveIfNoChildren', () => {
test('has child nodes', () => { test('has child nodes', () => {
dn.removeIfNoChildren(schema.properties.current); dn.recursiveRemoveIfNoChildren(schema.properties.current);
expect(schema.properties.current).toBeDefined(); expect(schema.properties.current).toBeDefined();
}); });
test('no child nodes', () => { 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(); expect(schema.properties.current?.properties?.child).toBeUndefined();
}); });
}); });
@ -241,20 +241,20 @@ describe('createDesignable', () => {
expect(schema.properties.current?.properties?.child).toBeUndefined(); expect(schema.properties.current?.properties?.child).toBeUndefined();
}); });
test('removeParentsIfNoChildren + breakSchema json', () => { test('removeParentsIfNoChildren + breakRemoveOn json', () => {
dn.remove(schema.properties.current.properties.child, { dn.remove(schema.properties.current.properties.child, {
removeParentsIfNoChildren: true, removeParentsIfNoChildren: true,
breakSchema: { breakRemoveOn: {
'x-uid': 'global', 'x-uid': 'global',
}, },
}); });
expect(schema?.properties?.current).toBeUndefined(); expect(schema?.properties?.current).toBeUndefined();
}); });
test('removeParentsIfNoChildren + breakSchema function', () => { test('removeParentsIfNoChildren + breakRemoveOn function', () => {
dn.remove(schema.properties.current.properties.child, { dn.remove(schema.properties.current.properties.child, {
removeParentsIfNoChildren: true, removeParentsIfNoChildren: true,
breakSchema: (s) => s['x-uid'] === 'global', breakRemoveOn: (s) => s['x-uid'] === 'global',
}); });
expect(schema?.properties?.current).toBeUndefined(); expect(schema?.properties?.current).toBeUndefined();
}); });
@ -486,6 +486,6 @@ describe('parentsIn', () => {
dn.on('error', callback); dn.on('error', callback);
dn.insertAfterBegin(schema.properties.menu); dn.insertAfterBegin(schema.properties.menu);
expect(schema.properties.menu).toBeDefined(); 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 get from 'lodash/get';
import set from 'lodash/set'; import set from 'lodash/set';
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { useAPIClient } from '../../api-client'; import { APIClient, useAPIClient } from '../../api-client';
import { SchemaComponentContext } from '../context'; import { SchemaComponentContext } from '../context';
interface CreateDesignableProps { interface CreateDesignableProps {
current: Schema; current: Schema;
api?: APIClient;
refresh?: () => void;
} }
export function createDesignable(options: CreateDesignableProps) { export function createDesignable(options: CreateDesignableProps) {
@ -22,13 +24,18 @@ type Position = 'beforeBegin' | 'afterBegin' | 'beforeEnd' | 'afterEnd';
interface InsertAdjacentOptions { interface InsertAdjacentOptions {
wrap?: (s: ISchema) => ISchema; wrap?: (s: ISchema) => ISchema;
removeParentsIfNoChildren?: boolean; removeParentsIfNoChildren?: boolean;
breakRemoveOn?: ISchema | BreakFn;
} }
type BreakFn = (s: ISchema) => boolean; type BreakFn = (s: ISchema) => boolean;
interface RemoveOptions { interface RemoveOptions {
removeParentsIfNoChildren?: boolean; removeParentsIfNoChildren?: boolean;
breakSchema?: ISchema | BreakFn; breakRemoveOn?: ISchema | BreakFn;
}
interface RecursiveRemoveOptions {
breakRemoveOn?: ISchema | BreakFn;
} }
const generateUid = (s: ISchema) => { const generateUid = (s: ISchema) => {
@ -42,13 +49,59 @@ const generateUid = (s: ISchema) => {
const defaultWrap = (s: ISchema) => s; 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 { export class Designable {
current: Schema; current: Schema;
options: CreateDesignableProps;
events = {}; events = {};
constructor({ current }) { constructor(options: CreateDesignableProps) {
this.current = current; 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) { prepareProperty(schema: ISchema) {
@ -68,18 +121,19 @@ export class Designable {
generateUid(schema); generateUid(schema);
} }
on(name: 'afterInsertAdjacent' | 'afterRemove' | 'error', listener: any) { on(name: 'insertAdjacent' | 'remove' | 'error', listener: any) {
if (!this.events[name]) { if (!this.events[name]) {
this.events[name] = []; this.events[name] = [];
} }
this.events[name].push(listener); this.events[name].push(listener);
} }
emit(name: 'afterInsertAdjacent' | 'afterRemove' | 'error', ...args) { emit(name: 'insertAdjacent' | 'remove' | 'error', ...args) {
if (!this.events[name]) { if (!this.events[name]) {
return; 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) { parentsIn(schema: Schema) {
@ -112,69 +166,46 @@ export class Designable {
} }
} }
removeIfNoChildren(schema?: Schema) { recursiveRemoveIfNoChildren(schema?: Schema, options?: RecursiveRemoveOptions) {
if (!schema) { if (!schema) {
return; return;
} }
let s = schema; let s = schema;
const count = Object.keys(s.properties || {}).length;
if (count > 0) {
return;
}
let removed: Schema; let removed: Schema;
while (s.parent) { const breakRemoveOn = options?.breakRemoveOn;
removed = s.parent.removeProperty(s.name); while (s) {
const count = Object.keys(s.parent.properties || {}).length; if (typeof breakRemoveOn === 'function') {
if (breakRemoveOn(s)) {
break;
}
} else {
if (matchSchema(s, breakRemoveOn)) {
break;
}
}
const count = Object.keys(s.properties || {}).length;
if (count > 0) { if (count > 0) {
break; break;
} }
if (s.parent) {
removed = s.parent.removeProperty(s.name);
}
s = s.parent; s = s.parent;
} }
return removed; return removed;
} }
remove(schema?: Schema, options: RemoveOptions = {}) { remove(schema?: Schema, options: RemoveOptions = {}) {
const { removeParentsIfNoChildren, breakSchema } = options; const { breakRemoveOn, removeParentsIfNoChildren } = options;
let s = schema || this.current; let s = schema || this.current;
let removed; let removed = s.parent.removeProperty(s.name);
const matchSchema = (source: ISchema, target: ISchema) => { if (removeParentsIfNoChildren) {
if (!source || !target) { const parent = this.recursiveRemoveIfNoChildren(s.parent, { breakRemoveOn });
return; 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 = {}) { insertBeforeBeginOrAfterEnd(schema: ISchema, options: InsertAdjacentOptions = {}) {
@ -208,7 +239,7 @@ export class Designable {
return; return;
} }
const opts = {}; const opts = {};
const { wrap = defaultWrap, removeParentsIfNoChildren } = options; const { wrap = defaultWrap, breakRemoveOn, removeParentsIfNoChildren } = options;
if (Schema.isSchemaInstance(schema)) { if (Schema.isSchemaInstance(schema)) {
if (this.parentsIn(schema)) { if (this.parentsIn(schema)) {
this.emit('error', { this.emit('error', {
@ -219,7 +250,7 @@ export class Designable {
} }
schema.parent.removeProperty(schema.name); schema.parent.removeProperty(schema.name);
if (removeParentsIfNoChildren) { if (removeParentsIfNoChildren) {
opts['removed'] = this.removeIfNoChildren(schema.parent); opts['removed'] = this.recursiveRemoveIfNoChildren(schema.parent, { breakRemoveOn });
} }
} }
const properties = {}; const properties = {};
@ -245,7 +276,11 @@ export class Designable {
s['x-index'] = newOrder; s['x-index'] = newOrder;
s.parent = this.current.parent; s.parent = this.current.parent;
this.current.parent.setProperties(properties); 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; return;
} }
const opts = {}; const opts = {};
const { wrap = defaultWrap, removeParentsIfNoChildren } = options; const { wrap = defaultWrap, breakRemoveOn, removeParentsIfNoChildren } = options;
if (Schema.isSchemaInstance(schema)) { if (Schema.isSchemaInstance(schema)) {
if (this.parentsIn(schema)) { if (this.parentsIn(schema)) {
this.emit('error', { this.emit('error', {
@ -270,7 +305,7 @@ export class Designable {
} }
schema.parent.removeProperty(schema.name); schema.parent.removeProperty(schema.name);
if (removeParentsIfNoChildren) { if (removeParentsIfNoChildren) {
opts['removed'] = this.removeIfNoChildren(schema.parent); opts['removed'] = this.recursiveRemoveIfNoChildren(schema.parent, { breakRemoveOn });
} }
} }
const properties = {}; const properties = {};
@ -287,7 +322,11 @@ export class Designable {
s['x-index'] = 0; s['x-index'] = 0;
s.parent = this.current; s.parent = this.current;
this.current.setProperties(properties); 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; return;
} }
const opts = {}; const opts = {};
const { wrap = defaultWrap, removeParentsIfNoChildren } = options; const { wrap = defaultWrap, breakRemoveOn, removeParentsIfNoChildren } = options;
if (Schema.isSchemaInstance(schema)) { if (Schema.isSchemaInstance(schema)) {
if (this.parentsIn(schema)) { if (this.parentsIn(schema)) {
this.emit('error', { this.emit('error', {
@ -312,14 +351,18 @@ export class Designable {
} }
schema.parent.removeProperty(schema.name); schema.parent.removeProperty(schema.name);
if (removeParentsIfNoChildren) { if (removeParentsIfNoChildren) {
opts['removed'] = this.removeIfNoChildren(schema.parent); opts['removed'] = this.recursiveRemoveIfNoChildren(schema.parent, { breakRemoveOn });
} }
} }
this.prepareProperty(schema); this.prepareProperty(schema);
const wrapped = wrap(schema); const wrapped = wrap(schema);
const s = this.current.addProperty(wrapped.name || uid(), wrapped); const s = this.current.addProperty(wrapped.name || uid(), wrapped);
s.parent = this.current; 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; return;
} }
const opts = {}; const opts = {};
const { wrap = defaultWrap, removeParentsIfNoChildren } = options; const { wrap = defaultWrap, breakRemoveOn, removeParentsIfNoChildren } = options;
if (Schema.isSchemaInstance(schema)) { if (Schema.isSchemaInstance(schema)) {
if (this.parentsIn(schema)) { if (this.parentsIn(schema)) {
this.emit('error', { this.emit('error', {
@ -341,7 +384,7 @@ export class Designable {
} }
schema.parent.removeProperty(schema.name); schema.parent.removeProperty(schema.name);
if (removeParentsIfNoChildren) { if (removeParentsIfNoChildren) {
opts['removed'] = this.removeIfNoChildren(schema.parent); opts['removed'] = this.recursiveRemoveIfNoChildren(schema.parent, { breakRemoveOn });
} }
schema.parent = null; schema.parent = null;
} }
@ -371,7 +414,11 @@ export class Designable {
s.parent = this.current.parent; s.parent = this.current.parent;
s['x-index'] = newOrder; s['x-index'] = newOrder;
this.current.parent.setProperties(properties); 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 field = useField();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const dn = createDesignable({ current: fieldSchema });
const api = useAPIClient(); const api = useAPIClient();
dn.on('afterInsertAdjacent', async (current, position, schema) => { const dn = createDesignable({ api, refresh, current: fieldSchema });
refresh(); dn.loadAPIClientEvents();
// 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);
return { return {
designable, designable,
reset, reset,