From db11e8982340b35206d5c01a62ba04abfeb04a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AF=B8=E5=B2=B3?= Date: Sun, 10 Nov 2019 21:44:09 +0800 Subject: [PATCH] feat(g-base): add some member functions for Group, close #260 --- packages/g-base/src/abstract/group.ts | 81 ++++++++++++++++- packages/g-base/src/interfaces.ts | 40 +++++++++ packages/g-base/src/types.ts | 2 + packages/g-base/src/util/group.ts | 87 ------------------ packages/g-base/tests/unit/group-spec.js | 60 +++++++++++++ packages/g-base/tests/unit/group-util-spec.js | 88 ------------------- 6 files changed, 182 insertions(+), 176 deletions(-) delete mode 100644 packages/g-base/src/util/group.ts delete mode 100644 packages/g-base/tests/unit/group-util-spec.js diff --git a/packages/g-base/src/abstract/group.ts b/packages/g-base/src/abstract/group.ts index 5b5aa0eea..bff721790 100644 --- a/packages/g-base/src/abstract/group.ts +++ b/packages/g-base/src/abstract/group.ts @@ -1,4 +1,5 @@ -import { IGroup } from '../interfaces'; +import { IElement, IGroup } from '../interfaces'; +import { ElementFilterFn } from '../types'; import Container from './container'; import { each } from '../util/util'; @@ -49,6 +50,84 @@ abstract class AbstractGroup extends Container implements IGroup { } return clone; } + + /** + * 获取 Group 的第一个子元素 + * @return {IElement} 第一个元素 + */ + getFirst(): IElement { + const children = this.getChildren(); + return children[0]; + } + + /** + * 获取 Group 的最后一个子元素 + * @return {IElement} 元素 + */ + getLast(): IElement { + const children = this.getChildren(); + return children[children.length - 1]; + } + + /** + * 子元素的数量 + * @return {number} 子元素数量 + */ + getCount(): number { + const children = this.getChildren(); + return children.length; + } + + /** + * 查找所有匹配的元素 + * @param {ElementFilterFn} fn 匹配函数 + * @return {IElement[]} 元素数组 + */ + findAll(fn: ElementFilterFn): IElement[] { + let rst: IElement[] = []; + const children = this.getChildren(); + each(children, (element: IElement) => { + if (fn(element)) { + rst.push(element); + } + if (element.isGroup()) { + rst = rst.concat((element as IGroup).findAll(fn)); + } + }); + return rst; + } + + /** + * 根据 ID 查找元素 + * @param {string} id 元素 id + * @return {IElement|null} 元素 + */ + findById(id: string): IElement { + return this.find((element) => { + return element.get('id') === id; + }); + } + + /** + * 查找元素,找到第一个返回 + * @param {ElementFilterFn} fn 匹配函数 + * @return {IElement|null} 元素,可以为空 + */ + find(fn: ElementFilterFn): IElement { + let rst: IElement = null; + const children = this.getChildren(); + each(children, (element: IElement) => { + if (fn(element)) { + rst = element; + } else if (element.isGroup()) { + rst = (element as IGroup).find(fn); + } + if (rst) { + return false; + } + }); + return rst; + } } export default AbstractGroup; diff --git a/packages/g-base/src/interfaces.ts b/packages/g-base/src/interfaces.ts index 7f5538573..b1f448ff5 100644 --- a/packages/g-base/src/interfaces.ts +++ b/packages/g-base/src/interfaces.ts @@ -9,6 +9,7 @@ import { OnFrame, ShapeBase, BBox, + ElementFilterFn, } from './types'; export interface ICtor { @@ -355,6 +356,45 @@ export interface IGroup extends IElement, IContainer { * @return {boolean} 是否是实体分组 */ isEntityGroup(): boolean; + + /** + * 获取 Group 的第一个子元素 + * @return {IElement} 第一个元素 + */ + getFirst(): IElement; + + /** + * 获取 Group 的最后一个子元素 + * @return {IElement} 元素 + */ + getLast(): IElement; + + /** + * 子元素的数量 + * @return {number} 子元素数量 + */ + getCount(): number; + + /** + * 查找所有匹配的元素 + * @param {ElementFilterFn} fn 匹配函数 + * @return {IElement[]} 元素数组 + */ + findAll(fn: ElementFilterFn): IElement[]; + + /** + * 根据 ID 查找元素 + * @param {string} id 元素 id + * @return {IElement | null} 元素 + */ + findById(id: string): IElement; + + /** + * 查找元素,找到第一个返回 + * @param {ElementFilterFn} fn 匹配函数 + * @return {IElement|null} 元素,可以为空 + */ + find(fn: ElementFilterFn): IElement; } export interface IShape extends IElement { diff --git a/packages/g-base/src/types.ts b/packages/g-base/src/types.ts index b7e75f77d..70df3e1fa 100644 --- a/packages/g-base/src/types.ts +++ b/packages/g-base/src/types.ts @@ -181,6 +181,8 @@ export type ShapeBase = { [key: string]: ICtor; }; +export type ElementFilterFn = (IElement) => boolean; + type A = ['a' | 'A', number, number, number, number, number, number, number]; type C = ['c' | 'C', number, number, number, number, number, number]; type O = ['o' | 'O', number, number]; diff --git a/packages/g-base/src/util/group.ts b/packages/g-base/src/util/group.ts deleted file mode 100644 index 8d618d8d8..000000000 --- a/packages/g-base/src/util/group.ts +++ /dev/null @@ -1,87 +0,0 @@ -/** - * @fileoverview 操作 Group 的一些方法,便于进行单元测试 - * @author dxq613@gmail.com - */ -import { IGroup, IElement } from '../interfaces'; -import { isString, each } from './util'; - -const GroupUtil = { - /** - * 获取 Group 的第一个元素 - * @param {IGroup} group Group 对象 - * @return {IElement} 第一个元素 - */ - getFirst(group: IGroup): IElement { - return group.getChildren()[0]; - }, - /** - * 获取 Group 的最后一个元素 - * @param {IGroup} group Group 对象 - * @return {IElement} 元素 - */ - getLast(group: IGroup): IElement { - const children = group.getChildren(); - return children[children.length - 1]; - }, - /** - * 子元素的数量 - * @param {IGroup} group Group 对象 - * @return {number} 子元素数量 - */ - getCount(group: IGroup): number { - return group.getChildren().length; - }, - /** - * 查找所有匹配的元素 - * @param {IGroup} group Group 对象 - * @param {Function} fn 匹配函数 - * @return {IElement[]} 元素数组 - */ - findAll(group: IGroup, fn: Function): IElement[] { - let rst: IElement[] = []; - const children = group.getChildren(); - each(children, (element: IElement) => { - if (fn(element)) { - rst.push(element); - } - if (element.isGroup()) { - rst = rst.concat(GroupUtil.findAll(element as IGroup, fn)); - } - }); - return rst; - }, - /** - * 根据 ID 查找元素 - * @param {IGroup} group Group 对象 - * @param {string} id 元素 id - * @return {IElement|null} 元素 - */ - findById(group: IGroup, id: string): IElement { - return this.find(group, (element) => { - return element.get('id') === id; - }); - }, - /** - * 查找元素,找到第一个返回 - * @param {IGroup} group Group 对象 - * @param {Function} fn 匹配函数 - * @return {IElement|null} 元素,可以为空 - */ - find(group: IGroup, fn: Function): IElement { - let rst: IElement = null; - const children = group.getChildren(); - each(children, (element: IElement) => { - if (fn(element)) { - rst = element; - } else if (element.isGroup()) { - rst = GroupUtil.find(element as IGroup, fn); - } - if (rst) { - return false; - } - }); - return rst; - }, -}; - -export default GroupUtil; diff --git a/packages/g-base/tests/unit/group-spec.js b/packages/g-base/tests/unit/group-spec.js index 8b9b109ce..ebe17b8e8 100644 --- a/packages/g-base/tests/unit/group-spec.js +++ b/packages/g-base/tests/unit/group-spec.js @@ -219,3 +219,63 @@ describe('test with matrix', () => { expect(group.invertFromMatrix(v)).eqls(v); }); }); + +describe('test group member function', () => { + const group = new Group({ + children: [ + new MyShape({ id: '01', text: '01' }), + new MyShape({ id: '02', text: '02' }), + new Group({ + children: [new MyShape({ id: '04', text: '04' }), new MyShape({ id: 'test', text: '02' })], + }), + new MyShape({ id: '03', text: '03' }), + ], + }); + + it('getFirst', () => { + expect(group.getFirst().get('id')).eqls('01'); + }); + + it('getLast', () => { + expect(group.getLast().get('id')).eqls('03'); + }); + + it('getCount', () => { + expect(group.getCount()).eqls(group.getChildren().length); + }); + + it('findAll', () => { + expect( + group.findAll((item) => { + return item.get('text') === '02'; + }).length + ).eqls(2); + + expect( + group.findAll((item) => { + return item.get('text') === '05'; + }).length + ).eqls(0); + }); + + it('findById', () => { + expect(group.findById('01')).not.eqls(null); + expect(group.findById('05')).eqls(null); + expect(group.findById('04')).not.eqls(null); + }); + + it('find', () => { + expect( + group + .find((item) => { + return item.get('text') === '02'; + }) + .get('id') + ).eqls('02'); + expect( + group.find((item) => { + return item.get('text') === '05'; + }) + ).eqls(null); + }); +}); diff --git a/packages/g-base/tests/unit/group-util-spec.js b/packages/g-base/tests/unit/group-util-spec.js deleted file mode 100644 index c82341571..000000000 --- a/packages/g-base/tests/unit/group-util-spec.js +++ /dev/null @@ -1,88 +0,0 @@ -const expect = require('chai').expect; -import GroupUtil from '../../src/util/group'; -class Item { - isGroup() { - return false; - } - constructor(cfg) { - Object.assign(this, cfg); - } - get(name) { - return this[name]; - } -} - -class Group { - isGroup() { - return true; - } - constructor(cfg) { - Object.assign(this, cfg); - } - - invertFromMatrix() {} - get(name) { - return this[name]; - } - - getChildren() { - return this.children; - } -} -const group = new Group({ - children: [ - new Item({ id: '01', text: '01' }), - new Item({ id: '02', text: '02' }), - new Group({ - children: [new Item({ id: '04', text: '04' }), new Item({ id: 'test', text: '02' })], - }), - new Item({ id: '03', text: '03' }), - ], -}); - -describe('test group util', () => { - it('getFirst', () => { - expect(GroupUtil.getFirst(group).id).eqls('01'); - }); - - it('getLast', () => { - expect(GroupUtil.getLast(group).id).eqls('03'); - }); - - it('getCount', () => { - expect(GroupUtil.getCount(group)).eqls(group.children.length); - }); - - it('findAll', () => { - expect( - GroupUtil.findAll(group, function(item) { - return item.text === '02'; - }).length - ).eqls(2); - - expect( - GroupUtil.findAll(group, function(item) { - return item.text === '05'; - }).length - ).eqls(0); - }); - - it('findById', () => { - expect(GroupUtil.findById(group, '01')).not.eqls(null); - expect(GroupUtil.findById(group, '05')).eqls(null); - expect(GroupUtil.findById(group, '04')).not.eqls(null); - }); - - it('find', () => { - expect( - GroupUtil.find(group, (item) => { - return item.text === '02'; - }).id - ).eqls('02'); - expect( - GroupUtil.find(group, function(item) { - return item.text === '05'; - }) - ).eqls(null); - }); -});