@leafer/selector 1.0.0-rc.3 → 1.0.0-rc.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leafer/selector",
3
- "version": "1.0.0-rc.3",
3
+ "version": "1.0.0-rc.30",
4
4
  "description": "@leafer/selector",
5
5
  "author": "Chao (Leafer) Wan",
6
6
  "license": "MIT",
@@ -15,16 +15,16 @@
15
15
  "type": "git",
16
16
  "url": "https://github.com/leaferjs/leafer.git"
17
17
  },
18
- "homepage": "https://github.com/leaferjs/leafer/tree/main/packages/selector",
18
+ "homepage": "https://github.com/leaferjs/leafer/tree/main/packages/partner/selector",
19
19
  "bugs": "https://github.com/leaferjs/leafer/issues",
20
20
  "keywords": [
21
21
  "leafer",
22
22
  "leaferjs"
23
23
  ],
24
24
  "dependencies": {
25
- "@leafer/core": "1.0.0-rc.3"
25
+ "@leafer/core": "1.0.0-rc.30"
26
26
  },
27
27
  "devDependencies": {
28
- "@leafer/interface": "1.0.0-rc.3"
28
+ "@leafer/interface": "1.0.0-rc.30"
29
29
  }
30
30
  }
@@ -1,15 +1,15 @@
1
- import { ILeaf, ILeafList, IPointData, IRadiusPointData, ISelectPathResult, ISelectPathOptions, ISelector } from '@leafer/interface'
1
+ import { ILeaf, ILeafList, IPointData, IRadiusPointData, IPickResult, IPickOptions, ISelector, IPickBottom } from '@leafer/interface'
2
2
  import { BoundsHelper, LeafList, LeafHelper } from '@leafer/core'
3
3
 
4
4
 
5
5
  const { hitRadiusPoint } = BoundsHelper
6
6
 
7
- export class FindPath {
7
+ export class Picker {
8
8
 
9
9
  protected target: ILeaf
10
10
  protected selector: ISelector
11
11
 
12
- protected findList: ILeaf[]
12
+ protected findList: ILeafList
13
13
  protected exclude: ILeafList
14
14
 
15
15
  protected point: IRadiusPointData
@@ -19,64 +19,72 @@ export class FindPath {
19
19
  this.selector = selector
20
20
  }
21
21
 
22
- public getByPoint(hitPoint: IPointData, hitRadius: number, options?: ISelectPathOptions): ISelectPathResult {
22
+ public getByPoint(hitPoint: IPointData, hitRadius: number, options?: IPickOptions): IPickResult {
23
23
  if (!hitRadius) hitRadius = 0
24
24
  if (!options) options = {}
25
25
 
26
26
  const through = options.through || false
27
27
  const ignoreHittable = options.ignoreHittable || false
28
+ const target = options.target || this.target
28
29
  this.exclude = options.exclude || null
29
30
 
30
31
  this.point = { x: hitPoint.x, y: hitPoint.y, radiusX: hitRadius, radiusY: hitRadius }
31
- this.findList = []
32
+ this.findList = new LeafList(options.findList)
32
33
 
33
34
  // path
34
- this.eachFind(this.target.children, this.target.__onlyHitMask)
35
+ if (!options.findList) this.hitBranch(target) // 包含through元素
35
36
 
36
- const list = this.findList
37
- const leaf = this.getBestMatchLeaf()
37
+ const { list } = this.findList
38
+ const leaf = this.getBestMatchLeaf(list, options.bottomList, ignoreHittable)
38
39
  const path = ignoreHittable ? this.getPath(leaf) : this.getHitablePath(leaf)
39
40
 
40
41
  this.clear()
41
42
 
42
- return through ? { path, leaf, throughPath: list.length ? this.getThroughPath(list) : path } : { path, leaf }
43
+ return through ? { path, target: leaf, throughPath: list.length ? this.getThroughPath(list) : path } : { path, target: leaf }
43
44
  }
44
45
 
45
- public getBestMatchLeaf(): ILeaf {
46
- const { findList: targets } = this
47
- if (targets.length > 1) {
46
+ public getBestMatchLeaf(list: ILeaf[], bottomList: IPickBottom[], ignoreHittable: boolean): ILeaf {
47
+ if (list.length) {
48
48
  let find: ILeaf
49
- this.findList = []
49
+ this.findList = new LeafList()
50
50
  const { x, y } = this.point
51
51
  const point = { x, y, radiusX: 0, radiusY: 0 }
52
- for (let i = 0, len = targets.length; i < len; i++) {
53
- find = targets[i]
54
- if (LeafHelper.worldHittable(find)) {
52
+ for (let i = 0, len = list.length; i < len; i++) {
53
+ find = list[i]
54
+ if (ignoreHittable || LeafHelper.worldHittable(find)) {
55
55
  this.hitChild(find, point)
56
- if (this.findList.length) return this.findList[0]
56
+ if (this.findList.length) return this.findList.list[0]
57
57
  }
58
58
  }
59
59
  }
60
- return targets[0]
60
+
61
+ if (bottomList) { // 底部虚拟元素
62
+ for (let i = 0, len = bottomList.length; i < len; i++) {
63
+ this.hitChild(bottomList[i].target, this.point, bottomList[i].proxy)
64
+ if (this.findList.length) return this.findList.list[0]
65
+ }
66
+ }
67
+
68
+ return list[0]
61
69
  }
62
70
 
63
71
  public getPath(leaf: ILeaf): LeafList {
64
72
  const path = new LeafList()
65
73
  while (leaf) {
66
- path.push(leaf)
74
+ path.add(leaf)
67
75
  leaf = leaf.parent
68
76
  }
69
- path.push(this.target)
77
+ path.add(this.target)
70
78
  return path
71
79
  }
72
80
 
73
81
  public getHitablePath(leaf: ILeaf): LeafList {
74
- const path = this.getPath(leaf)
82
+ const path = this.getPath(leaf && leaf.hittable ? leaf : null)
75
83
  let item: ILeaf, hittablePath = new LeafList()
76
84
  for (let i = path.list.length - 1; i > -1; i--) {
77
85
  item = path.list[i]
78
86
  if (!item.__.hittable) break
79
- hittablePath.unshift(item)
87
+ hittablePath.addAt(item, 0)
80
88
  if (!item.__.hitChildren) break
81
89
  }
82
90
  return hittablePath
@@ -96,20 +104,23 @@ export class FindPath {
96
104
  for (let j = 0, jLen = path.length; j < jLen; j++) {
97
105
  leaf = path.list[j]
98
106
  if (nextPath && nextPath.has(leaf)) break
99
- throughPath.push(leaf)
107
+ throughPath.add(leaf)
100
108
  }
101
109
  }
102
110
 
103
111
  return throughPath
104
112
  }
105
113
 
114
+ protected hitBranch(branch: ILeaf): void {
115
+ this.eachFind(branch.children, branch.__onlyHitMask)
116
+ }
106
117
 
107
- protected eachFind(children: Array<ILeaf>, hitMask: boolean): void {
118
+ protected eachFind(children: ILeaf[], hitMask: boolean): void {
108
119
  let child: ILeaf, hit: boolean
109
120
  const { point } = this, len = children.length
110
121
  for (let i = len - 1; i > -1; i--) {
111
122
  child = children[i]
112
- if (!child.__.visible || (hitMask && !child.__.isMask)) continue
123
+ if (!child.__.visible || (hitMask && !child.__.mask)) continue
113
124
  hit = child.__.hitRadius ? true : hitRadiusPoint(child.__world, point)
114
125
 
115
126
  if (child.isBranch) {
@@ -123,9 +134,13 @@ export class FindPath {
123
134
  }
124
135
  }
125
136
 
126
- protected hitChild(child: ILeaf, point: IRadiusPointData): void {
137
+ protected hitChild(child: ILeaf, point: IRadiusPointData, proxy?: ILeaf): void {
127
138
  if (this.exclude && this.exclude.has(child)) return
128
- if (child.__hitWorld(point)) this.findList.push(child)
139
+ if (child.__hitWorld(point)) {
140
+ const { parent } = child
141
+ if (parent && parent.__hasMask && !child.__.mask && !parent.children.some(item => item.__.mask && item.__hitWorld(point))) return
142
+ this.findList.add(proxy || child)
143
+ }
129
144
  }
130
145
 
131
146
  protected clear(): void {
package/src/Selector.ts CHANGED
@@ -1,127 +1,148 @@
1
- import { ILeaf, ILeafArrayMap, ILeafMap, ISelector, ISelectPathResult, ISelectPathOptions, IPointData, IEventListenerId, ISelectorConfig } from '@leafer/interface'
2
- import { ChildEvent, LayoutEvent, DataHelper, Platform } from '@leafer/core'
1
+ import { ILeaf, ILeafMap, ISelector, ISelectorProxy, IPickResult, IPickOptions, IPointData, IEventListenerId, ISelectorConfig, IFindMethod, IAnswer, IFindCondition, IBooleanMap } from '@leafer/interface'
2
+ import { ChildEvent, LayoutEvent, DataHelper, Answer, Platform, PropertyEvent, LeafHelper } from '@leafer/core'
3
3
 
4
- import { FindPath } from './FindPath'
5
-
6
-
7
- interface IFind {
8
- (leaf: ILeaf): boolean
9
- }
4
+ import { Picker } from './Picker'
10
5
 
11
6
 
7
+ const { Yes, NoAndSkip, YesAndSkip } = Answer
8
+ const idCondition = {} as IFindCondition, classNameCondition = {} as IFindCondition, tagCondition = {} as IFindCondition
12
9
  export class Selector implements ISelector {
13
10
 
14
11
  public target: ILeaf
15
12
 
13
+ public proxy?: ISelectorProxy // editor
14
+
16
15
  public config: ISelectorConfig = {}
17
16
 
18
- protected findPath: FindPath
17
+ protected picker: Picker
19
18
 
20
- protected innerIdList: ILeafMap = {}
21
- protected idList: ILeafMap = {}
22
- protected classNameList: ILeafArrayMap = {}
23
- protected tagNameList: ILeafArrayMap = {}
19
+ protected innerIdMap: ILeafMap = {}
20
+ protected idMap: ILeafMap = {}
21
+
22
+ protected findLeaf: ILeaf
23
+
24
+ protected methods = {
25
+ id: (leaf: ILeaf, name: string) => leaf.id === name ? (this.idMap[name] = leaf, 1) : 0,
26
+ innerId: (leaf: ILeaf, innerId: number) => leaf.innerId === innerId ? (this.innerIdMap[innerId] = leaf, 1) : 0,
27
+ className: (leaf: ILeaf, name: string) => leaf.className === name ? 1 : 0,
28
+ tag: (leaf: ILeaf, name: string) => leaf.__tag === name ? 1 : 0,
29
+ tags: (leaf: ILeaf, nameMap: IBooleanMap) => nameMap[leaf.__tag] ? 1 : 0
30
+ }
24
31
 
25
32
  protected __eventIds: IEventListenerId[]
26
33
 
34
+
27
35
  constructor(target: ILeaf, userConfig?: ISelectorConfig) {
28
36
  this.target = target
29
37
  if (userConfig) this.config = DataHelper.default(userConfig, this.config)
30
- this.findPath = new FindPath(target, this)
38
+ this.picker = new Picker(target, this)
31
39
  this.__listenEvents()
32
40
  }
33
41
 
34
- public getByPoint(hitPoint: IPointData, hitRadius: number, options?: ISelectPathOptions): ISelectPathResult {
35
- if (Platform.name === 'node') this.target.emit(LayoutEvent.CHECK_UPDATE)
36
- return this.findPath.getByPoint(hitPoint, hitRadius, options)
37
- }
38
-
39
- public find(name: number | string, branch?: ILeaf): ILeaf | ILeaf[] {
40
- if (typeof name === 'number') {
41
- return this.getByInnerId(name, branch)
42
- } else if (name.startsWith('#')) {
43
- return this.getById(name.substring(1), branch)
44
- } else if (name.startsWith('.')) {
45
- return this.getByClassName(name.substring(1), branch)
46
- } else {
47
- return this.getByTagName(name, branch)
42
+ public getBy(condition: number | string | IFindCondition | IFindMethod, branch?: ILeaf, one?: boolean, options?: any): ILeaf | ILeaf[] {
43
+ switch (typeof condition) {
44
+ case 'number':
45
+ const leaf = this.getByInnerId(condition, branch)
46
+ return one ? leaf : (leaf ? [leaf] : [])
47
+ case 'string':
48
+ switch (condition[0]) {
49
+ case '#':
50
+ idCondition.id = condition.substring(1), condition = idCondition; break
51
+ case '.':
52
+ classNameCondition.className = condition.substring(1), condition = classNameCondition; break
53
+ default:
54
+ tagCondition.tag = condition, condition = tagCondition
55
+ }
56
+ case 'object':
57
+ if (condition.id !== undefined) {
58
+ const leaf = this.getById(condition.id as string, branch)
59
+ return one ? leaf : (leaf ? [leaf] : [])
60
+ } else if (condition.tag) {
61
+ const { tag } = condition, isArray = tag instanceof Array
62
+ return this.getByMethod(isArray ? this.methods.tags : this.methods.tag, branch, one, isArray ? DataHelper.toMap(tag) : tag)
63
+ } else {
64
+ return this.getByMethod(this.methods.className, branch, one, condition.className)
65
+ }
66
+ case 'function':
67
+ return this.getByMethod(condition as IFindMethod, branch, one, options)
48
68
  }
49
69
  }
50
70
 
51
- public getByInnerId(name: number, branch?: ILeaf): ILeaf {
52
- let cache = this.innerIdList[name]
53
- if (cache) return cache
54
- if (!branch) branch = this.target
55
- let find: ILeaf
56
- this.loopFind(branch, (leaf) => {
57
- if (leaf.innerId === name) {
58
- find = leaf
59
- this.innerIdList[name] = find
60
- return true
61
- } else {
62
- return false
63
- }
64
- })
65
- return find
71
+ public getByPoint(hitPoint: IPointData, hitRadius: number, options?: IPickOptions): IPickResult {
72
+ if (Platform.name === 'node') this.target.emit(LayoutEvent.CHECK_UPDATE)
73
+ return this.picker.getByPoint(hitPoint, hitRadius, options)
66
74
  }
67
75
 
68
- public getById(name: string, branch?: ILeaf): ILeaf {
69
- let cache = this.idList[name]
76
+ public getByInnerId(innerId: number, branch?: ILeaf): ILeaf {
77
+ const cache = this.innerIdMap[innerId]
70
78
  if (cache) return cache
71
- if (!branch) branch = this.target
72
- let find: ILeaf
73
- this.loopFind(branch, (leaf) => {
74
- if (leaf.id === name) {
75
- find = leaf
76
- this.idList[name] = find
77
- return true
78
- } else {
79
- return false
80
- }
81
- })
82
- return find
79
+ this.eachFind(this.toChildren(branch), this.methods.innerId, null, innerId)
80
+ return this.findLeaf
83
81
  }
84
82
 
85
- public getByClassName(name: string, branch?: ILeaf): ILeaf[] {
86
- if (!branch) branch = this.target
87
- let find: Array<ILeaf | ILeaf> = []
88
- this.loopFind(branch, (leaf) => {
89
- if (leaf.className === name) find.push(leaf)
90
- return false
91
- })
92
- return find
83
+ public getById(id: string, branch?: ILeaf): ILeaf {
84
+ const cache = this.idMap[id]
85
+ if (cache && LeafHelper.hasParent(cache, branch || this.target)) return cache
86
+ this.eachFind(this.toChildren(branch), this.methods.id, null, id)
87
+ return this.findLeaf
93
88
  }
94
89
 
95
- public getByTagName(name: string, branch?: ILeaf): ILeaf[] {
96
- if (!branch) branch = this.target
97
- let find: Array<ILeaf | ILeaf> = []
98
- this.loopFind(branch, (leaf) => {
99
- if (leaf.__tag === name) find.push(leaf)
100
- return false
101
- })
102
- return find
90
+ public getByClassName(className: string, branch?: ILeaf): ILeaf[] {
91
+ return this.getByMethod(this.methods.className, branch, false, className) as ILeaf[]
103
92
  }
104
93
 
105
- protected loopFind(branch: ILeaf, find: IFind): void {
106
- if (find(branch)) return
107
- const { children } = branch
94
+ public getByTag(tag: string, branch?: ILeaf): ILeaf[] {
95
+ return this.getByMethod(this.methods.tag, branch, false, tag) as ILeaf[]
96
+ }
97
+
98
+ public getByMethod(method: IFindMethod, branch?: ILeaf, one?: boolean, options?: any): ILeaf[] | ILeaf {
99
+ const list: ILeaf[] = one ? null : []
100
+ this.eachFind(this.toChildren(branch), method, list, options)
101
+ return list || this.findLeaf
102
+ }
103
+
104
+
105
+ protected eachFind(children: ILeaf[], method: IFindMethod, list?: ILeaf[], options?: any): void {
106
+ let child: ILeaf, result: IAnswer
108
107
  for (let i = 0, len = children.length; i < len; i++) {
109
- branch = children[i] as ILeaf
110
- if (find(branch)) return
111
- if (branch.isBranch) this.loopFind(branch, find)
108
+ child = children[i]
109
+ result = method(child, options)
110
+ if (result === Yes || result === YesAndSkip) {
111
+ if (list) {
112
+ list.push(child)
113
+ } else {
114
+ this.findLeaf = child
115
+ return
116
+ }
117
+ }
118
+ if (child.isBranch && result < NoAndSkip) this.eachFind(child.children, method, list, options)
112
119
  }
113
120
  }
114
121
 
122
+ protected toChildren(branch: ILeaf): ILeaf[] {
123
+ this.findLeaf = null
124
+ return [branch || this.target]
125
+ }
126
+
127
+
115
128
  protected __onRemoveChild(event: ChildEvent): void {
116
- const target = event.target as ILeaf
117
- if (this.idList[target.id]) this.idList[target.id] = null
118
- if (this.innerIdList[target.id]) this.innerIdList[target.innerId] = null
129
+ const { id, innerId } = event.child
130
+ if (this.idMap[id]) delete this.idMap[id]
131
+ if (this.innerIdMap[innerId]) delete this.innerIdMap[innerId]
132
+ }
133
+
134
+ protected __checkIdChange(event: PropertyEvent): void {
135
+ if (event.attrName === 'id') {
136
+ const id = event.oldValue as string
137
+ if (this.idMap[id]) delete this.idMap[id]
138
+ }
119
139
  }
120
140
 
121
141
 
122
142
  protected __listenEvents(): void {
123
143
  this.__eventIds = [
124
- this.target.on_(ChildEvent.REMOVE, this.__onRemoveChild, this)
144
+ this.target.on_(ChildEvent.REMOVE, this.__onRemoveChild, this),
145
+ this.target.on_(PropertyEvent.CHANGE, this.__checkIdChange, this)
125
146
  ]
126
147
  }
127
148
 
@@ -133,11 +154,10 @@ export class Selector implements ISelector {
133
154
  public destroy(): void {
134
155
  if (this.__eventIds.length) {
135
156
  this.__removeListenEvents()
136
- this.findPath.destroy()
137
- this.innerIdList = {}
138
- this.idList = {}
139
- this.classNameList = {}
140
- this.tagNameList = {}
157
+ this.picker.destroy()
158
+ this.findLeaf = null
159
+ this.innerIdMap = {}
160
+ this.idMap = {}
141
161
  }
142
162
  }
143
163
 
package/types/index.d.ts CHANGED
@@ -1,45 +1,53 @@
1
- import { ILeaf, ISelector, ILeafList, IRadiusPointData, IPointData, ISelectPathOptions, ISelectPathResult, ISelectorConfig, ILeafMap, ILeafArrayMap, IEventListenerId } from '@leafer/interface';
2
- import { LeafList, ChildEvent } from '@leafer/core';
1
+ import { ILeaf, ISelector, ILeafList, IRadiusPointData, IPointData, IPickOptions, IPickResult, IPickBottom, ISelectorProxy, ISelectorConfig, ILeafMap, IBooleanMap, IEventListenerId, IFindCondition, IFindMethod } from '@leafer/interface';
2
+ import { LeafList, ChildEvent, PropertyEvent } from '@leafer/core';
3
3
 
4
- declare class FindPath {
4
+ declare class Picker {
5
5
  protected target: ILeaf;
6
6
  protected selector: ISelector;
7
- protected findList: ILeaf[];
7
+ protected findList: ILeafList;
8
8
  protected exclude: ILeafList;
9
9
  protected point: IRadiusPointData;
10
10
  constructor(target: ILeaf, selector: ISelector);
11
- getByPoint(hitPoint: IPointData, hitRadius: number, options?: ISelectPathOptions): ISelectPathResult;
12
- getBestMatchLeaf(): ILeaf;
11
+ getByPoint(hitPoint: IPointData, hitRadius: number, options?: IPickOptions): IPickResult;
12
+ getBestMatchLeaf(list: ILeaf[], bottomList: IPickBottom[], ignoreHittable: boolean): ILeaf;
13
13
  getPath(leaf: ILeaf): LeafList;
14
14
  getHitablePath(leaf: ILeaf): LeafList;
15
15
  getThroughPath(list: ILeaf[]): LeafList;
16
- protected eachFind(children: Array<ILeaf>, hitMask: boolean): void;
17
- protected hitChild(child: ILeaf, point: IRadiusPointData): void;
16
+ protected hitBranch(branch: ILeaf): void;
17
+ protected eachFind(children: ILeaf[], hitMask: boolean): void;
18
+ protected hitChild(child: ILeaf, point: IRadiusPointData, proxy?: ILeaf): void;
18
19
  protected clear(): void;
19
20
  destroy(): void;
20
21
  }
21
22
 
22
- interface IFind {
23
- (leaf: ILeaf): boolean;
24
- }
25
23
  declare class Selector implements ISelector {
26
24
  target: ILeaf;
25
+ proxy?: ISelectorProxy;
27
26
  config: ISelectorConfig;
28
- protected findPath: FindPath;
29
- protected innerIdList: ILeafMap;
30
- protected idList: ILeafMap;
31
- protected classNameList: ILeafArrayMap;
32
- protected tagNameList: ILeafArrayMap;
27
+ protected picker: Picker;
28
+ protected innerIdMap: ILeafMap;
29
+ protected idMap: ILeafMap;
30
+ protected findLeaf: ILeaf;
31
+ protected methods: {
32
+ id: (leaf: ILeaf, name: string) => 1 | 0;
33
+ innerId: (leaf: ILeaf, innerId: number) => 1 | 0;
34
+ className: (leaf: ILeaf, name: string) => 1 | 0;
35
+ tag: (leaf: ILeaf, name: string) => 1 | 0;
36
+ tags: (leaf: ILeaf, nameMap: IBooleanMap) => 1 | 0;
37
+ };
33
38
  protected __eventIds: IEventListenerId[];
34
39
  constructor(target: ILeaf, userConfig?: ISelectorConfig);
35
- getByPoint(hitPoint: IPointData, hitRadius: number, options?: ISelectPathOptions): ISelectPathResult;
36
- find(name: number | string, branch?: ILeaf): ILeaf | ILeaf[];
37
- getByInnerId(name: number, branch?: ILeaf): ILeaf;
38
- getById(name: string, branch?: ILeaf): ILeaf;
39
- getByClassName(name: string, branch?: ILeaf): ILeaf[];
40
- getByTagName(name: string, branch?: ILeaf): ILeaf[];
41
- protected loopFind(branch: ILeaf, find: IFind): void;
40
+ getBy(condition: number | string | IFindCondition | IFindMethod, branch?: ILeaf, one?: boolean, options?: any): ILeaf | ILeaf[];
41
+ getByPoint(hitPoint: IPointData, hitRadius: number, options?: IPickOptions): IPickResult;
42
+ getByInnerId(innerId: number, branch?: ILeaf): ILeaf;
43
+ getById(id: string, branch?: ILeaf): ILeaf;
44
+ getByClassName(className: string, branch?: ILeaf): ILeaf[];
45
+ getByTag(tag: string, branch?: ILeaf): ILeaf[];
46
+ getByMethod(method: IFindMethod, branch?: ILeaf, one?: boolean, options?: any): ILeaf[] | ILeaf;
47
+ protected eachFind(children: ILeaf[], method: IFindMethod, list?: ILeaf[], options?: any): void;
48
+ protected toChildren(branch: ILeaf): ILeaf[];
42
49
  protected __onRemoveChild(event: ChildEvent): void;
50
+ protected __checkIdChange(event: PropertyEvent): void;
43
51
  protected __listenEvents(): void;
44
52
  protected __removeListenEvents(): void;
45
53
  destroy(): void;