Skip to content

Commit c42c943

Browse files
tayeinteractjs-ci
authored andcommitted
refactor(core): simplify target to interactable mapping
# Conflicts: # packages/@interactjs/arrange/componentUtils.ts # packages/@interactjs/arrange/types.ts # packages/@interactjs/multi-target/multiTarget.spec.ts # packages/@interactjs/multi-target/plugin.ts # packages/@interactjs/vue/package.json
1 parent 026fe9c commit c42c943

File tree

11 files changed

+325
-149
lines changed

11 files changed

+325
-149
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
"@babel/preset-typescript": "^7.17.12",
3131
"@babel/register": "^7.17.7",
3232
"@babel/runtime": "^7.18.3",
33+
"@testing-library/dom": "^9.3.3",
34+
"@testing-library/user-event": "^14.5.1",
3335
"@types/jest": "27",
3436
"@types/node": "^17.0.42",
3537
"@types/react": "^18.0.12",

packages/@interactjs/actions/drop/plugin.ts

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ function install (scope: Scope) {
261261
defaults.actions.drop = drop.defaults
262262
}
263263

264-
function collectDrops ({ interactables }: Scope, draggableElement: Element) {
264+
function collectDropzones ({ interactables }: Scope, draggableElement: Element) {
265265
const drops: ActiveDrop[] = []
266266

267267
// collect all dropzones and their elements which qualify for a drop
@@ -281,16 +281,7 @@ function collectDrops ({ interactables }: Scope, draggableElement: Element) {
281281
continue
282282
}
283283

284-
// query for new elements if necessary
285-
const dropElements = (
286-
is.string(dropzone.target)
287-
? dropzone._context.querySelectorAll(dropzone.target)
288-
: is.array(dropzone.target)
289-
? dropzone.target
290-
: [dropzone.target]
291-
) as Element[]
292-
293-
for (const dropzoneElement of dropElements) {
284+
for (const dropzoneElement of dropzone.getAllElements()) {
294285
if (dropzoneElement !== draggableElement) {
295286
drops.push({
296287
dropzone,
@@ -321,7 +312,7 @@ function fireActivationEvents (activeDrops: ActiveDrop[], event: DropEvent) {
321312
// dynamicDrop is true
322313
function getActiveDrops (scope: Scope, dragElement: Element) {
323314
// get dropzones and their elements that could receive the draggable
324-
const activeDrops = collectDrops(scope, dragElement)
315+
const activeDrops = collectDropzones(scope, dragElement)
325316

326317
for (const activeDrop of activeDrops) {
327318
activeDrop.rect = activeDrop.dropzone.getRect(activeDrop.element)

packages/@interactjs/core/InteractStatic.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export function createInteractStatic (scope: Scope): _InteractStatic {
7474
* @return {Interactable}
7575
*/
7676
const interact = ((target: Target, options: Options) => {
77-
let interactable = scope.interactables.get(target, options)
77+
let interactable = scope.interactables.getExisting(target, options)
7878

7979
if (!interactable) {
8080
interactable = scope.interactables.new(target, options)

packages/@interactjs/core/Interactable.spec.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,23 +37,18 @@ describe('core/Interactable', () => {
3737
})
3838

3939
test('Interactable unset correctly', () => {
40-
const scope = helpers.mockScope() as any
40+
const scope = helpers.mockScope()
4141

4242
const div = scope.document.createElement('div')
4343
const interactable = scope.interactables.new(div)
4444

45-
const mappingInfo = div[scope.id][0]
46-
47-
scope.fire('interactable:unset', { interactable })
45+
expect(div[scope.id]).toHaveLength(1)
4846

49-
// unset mappingInfo context
50-
expect(mappingInfo.context).toBeNull()
47+
interactable.unset()
5148

52-
// unset mappingInfo interactable
53-
expect(mappingInfo.interactable).toBeNull()
54-
55-
// unset target are removed
49+
// clears target mapping
5650
expect(div[scope.id]).toHaveLength(0)
51+
5752
div.remove()
5853
})
5954

packages/@interactjs/core/Interactable.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,21 @@ export class Interactable implements Partial<Eventable> {
247247
return this.options.deltaSource
248248
}
249249

250+
/** @internal */
251+
getAllElements (): Element[] {
252+
const { target } = this
253+
254+
if (is.string(target)) {
255+
return Array.from(this._context.querySelectorAll(target))
256+
}
257+
258+
if (is.func(target) && (target as any).getAllElements) {
259+
return (target as any).getAllElements()
260+
}
261+
262+
return is.element(target) ? [target] : []
263+
}
264+
250265
/**
251266
* Gets the selector context Node of the Interactable. The default is
252267
* `window.document`.

packages/@interactjs/core/InteractableSet.ts

Lines changed: 15 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { Interactable } from '@interactjs/core/Interactable'
22
import type { OptionsArg, Options } from '@interactjs/core/options'
33
import type { Scope } from '@interactjs/core/scope'
4-
import type { Target, Context } from '@interactjs/core/types'
4+
import type { Target } from '@interactjs/core/types'
55
import * as arr from '@interactjs/utils/arr'
66
import * as domUtils from '@interactjs/utils/domUtils'
77
import extend from '@interactjs/utils/extend'
@@ -18,17 +18,12 @@ declare module '@interactjs/core/scope' {
1818
}
1919
}
2020

21-
interface InteractableScopeProp {
22-
context: Context
23-
interactable: Interactable
24-
}
25-
2621
export class InteractableSet {
2722
// all set interactables
2823
list: Interactable[] = []
2924

3025
selectorMap: {
31-
[selector: string]: InteractableScopeProp[]
26+
[selector: string]: Interactable[]
3227
} = {}
3328

3429
scope: Scope
@@ -37,18 +32,13 @@ export class InteractableSet {
3732
this.scope = scope
3833
scope.addListeners({
3934
'interactable:unset': ({ interactable }) => {
40-
const { target, _context: context } = interactable
41-
const targetMappings: InteractableScopeProp[] = is.string(target)
35+
const { target } = interactable
36+
const interactablesOnTarget: Interactable[] = is.string(target)
4237
? this.selectorMap[target]
4338
: (target as any)[this.scope.id]
4439

45-
const targetIndex = arr.findIndex(targetMappings, (m) => m.context === context)
46-
if (targetMappings[targetIndex]) {
47-
// Destroying mappingInfo's context and interactable
48-
targetMappings[targetIndex].context = null as never
49-
targetMappings[targetIndex].interactable = null as never
50-
}
51-
targetMappings.splice(targetIndex, 1)
40+
const targetIndex = arr.findIndex(interactablesOnTarget, (i) => i === interactable)
41+
interactablesOnTarget.splice(targetIndex, 1)
5242
},
5343
})
5444
}
@@ -58,7 +48,6 @@ export class InteractableSet {
5848
actions: this.scope.actions,
5949
})
6050
const interactable = new this.scope.Interactable(target, options, this.scope.document, this.scope.events)
61-
const mappingInfo = { context: interactable._context, interactable }
6251

6352
this.scope.addDocument(interactable._doc)
6453
this.list.push(interactable)
@@ -67,7 +56,7 @@ export class InteractableSet {
6756
if (!this.selectorMap[target]) {
6857
this.selectorMap[target] = []
6958
}
70-
this.selectorMap[target].push(mappingInfo)
59+
this.selectorMap[target].push(interactable)
7160
} else {
7261
if (!(interactable.target as any)[this.scope.id]) {
7362
Object.defineProperty(target, this.scope.id, {
@@ -76,7 +65,7 @@ export class InteractableSet {
7665
})
7766
}
7867

79-
;(target as any)[this.scope.id].push(mappingInfo)
68+
;(target as any)[this.scope.id].push(interactable)
8069
}
8170

8271
this.scope.fire('interactable:new', {
@@ -89,23 +78,20 @@ export class InteractableSet {
8978
return interactable
9079
}
9180

92-
get (target: Target, options?: Options) {
81+
getExisting (target: Target, options?: Options) {
9382
const context = (options && options.context) || this.scope.document
9483
const isSelector = is.string(target)
95-
const targetMappings: InteractableScopeProp[] = isSelector
84+
const interactablesOnTarget: Interactable[] = isSelector
9685
? this.selectorMap[target as string]
9786
: (target as any)[this.scope.id]
9887

99-
if (!targetMappings) {
100-
return null
101-
}
88+
if (!interactablesOnTarget) return undefined
10289

103-
const found = arr.find(
104-
targetMappings,
105-
(m) => m.context === context && (isSelector || m.interactable.inContext(target as any)),
90+
return arr.find(
91+
interactablesOnTarget,
92+
(interactable) =>
93+
interactable._context === context && (isSelector || interactable.inContext(target as any)),
10694
)
107-
108-
return found && found.interactable
10995
}
11096

11197
forEachMatch<T> (node: Node, callback: (interactable: Interactable) => T): T | void {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export const NativePointerEvent = null as unknown as InstanceType<typeof PointerEvent>
22
export type NativeEventTarget = EventTarget
3+
export type NativeElement = Element

packages/@interactjs/interact/interact.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ test('interact export', () => {
4545

4646
for (const { interactable, target, context } of results) {
4747
// interactions.get returns correct result with identical targets and different contexts
48-
expect(scope.interactables.get(target, { context })).toBe(interactable)
48+
expect(scope.interactables.getExisting(target, { context })).toBe(interactable)
4949
}
5050

5151
const doc3 = makeIframeDoc()

packages/@interactjs/reflow/plugin.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import type { DoAnyPhaseArg, Interaction } from '@interactjs/core/Interaction'
33
import type { Scope, Plugin } from '@interactjs/core/scope'
44
import type { ActionName, ActionProps, Element } from '@interactjs/core/types'
55
import * as arr from '@interactjs/utils/arr'
6-
import is from '@interactjs/utils/is'
76
import { copyAction } from '@interactjs/utils/misc'
87
import * as pointerUtils from '@interactjs/utils/pointerUtils'
98
import { tlbrToXywh } from '@interactjs/utils/rect'
@@ -69,11 +68,7 @@ function doReflow<T extends ActionName> (
6968
action: ActionProps<T>,
7069
scope: Scope,
7170
): Promise<Interactable> {
72-
const elements = (
73-
is.string(interactable.target)
74-
? arr.from(interactable._context.querySelectorAll(interactable.target))
75-
: [interactable.target]
76-
) as Element[]
71+
const elements = interactable.getAllElements()
7772

7873
// tslint:disable-next-line variable-name
7974
const Promise = (scope.window as any).Promise

packages/@interactjs/reflow/reflow.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ describe('reflow', () => {
2727
interactable.fire = ((iEvent: any) => {
2828
fired.push(iEvent)
2929
}) as any
30-
;(interactable.target as any) = {}
3130
;(interactable.options as any).TEST = { enabled: true }
3231
interactable.rectChecker(() => ({ ...rect }))
3332

@@ -47,6 +46,7 @@ describe('reflow', () => {
4746
interactable.reflow(testAction)
4847

4948
const phases = ['reflow', 'start', 'move', 'end']
49+
expect(phases.map((_phase, index) => fired[index]?.type)).toEqual(phases.map((phase) => `TEST${phase}`))
5050

5151
for (const index in phases) {
5252
const phase = phases[index]
@@ -84,7 +84,7 @@ describe('reflow', () => {
8484
let reflowEvent: any
8585
let promise: Promise<Interactable>
8686

87-
const interactable = scope.interactables.new(scope.window)
87+
const interactable = scope.interactables.new(scope.document.documentElement)
8888
const rect = Object.freeze({ top: 100, left: 200, bottom: 300, right: 400 })
8989
interactable.rectChecker(() => ({ ...rect }))
9090
interactable.fire = ((iEvent: any) => {

0 commit comments

Comments
 (0)