forked from hotwired/stimulus
-
Notifications
You must be signed in to change notification settings - Fork 0
/
binding.ts
113 lines (92 loc) · 2.95 KB
/
binding.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import { Action } from "./action"
import { ActionEvent } from "./action_event"
import { Context } from "./context"
import { Controller } from "./controller"
import { Scope } from "./scope"
export class Binding {
readonly context: Context
readonly action: Action
constructor(context: Context, action: Action) {
this.context = context
this.action = action
}
get index(): number {
return this.action.index
}
get eventTarget(): EventTarget {
return this.action.eventTarget
}
get eventOptions(): AddEventListenerOptions {
return this.action.eventOptions
}
get identifier(): string {
return this.context.identifier
}
handleEvent(event: Event) {
if (this.willBeInvokedByEvent(event) && this.applyEventModifiers(event)) {
this.invokeWithEvent(event)
}
}
get eventName(): string {
return this.action.eventName
}
get method(): Function {
const method = (this.controller as any)[this.methodName]
if (typeof method == "function") {
return method
}
throw new Error(`Action "${this.action}" references undefined method "${this.methodName}"`)
}
private applyEventModifiers(event: Event): boolean {
const { element } = this.action
const { actionDescriptorFilters } = this.context.application
let passes = true
for (const [name, value] of Object.entries(this.eventOptions)) {
if (name in actionDescriptorFilters) {
const filter = actionDescriptorFilters[name]
passes = passes && filter({ name, value, event, element })
} else {
continue
}
}
return passes
}
private invokeWithEvent(event: Event) {
const { target, currentTarget } = event
try {
const { params } = this.action
const actionEvent: ActionEvent = Object.assign(event, { params })
this.method.call(this.controller, actionEvent)
this.context.logDebugActivity(this.methodName, { event, target, currentTarget, action: this.methodName })
} catch (error: any) {
const { identifier, controller, element, index } = this
const detail = { identifier, controller, element, index, event }
this.context.handleError(error, `invoking action "${this.action}"`, detail)
}
}
private willBeInvokedByEvent(event: Event): boolean {
const eventTarget = event.target
if (event instanceof KeyboardEvent && this.action.isFilterTarget(event)) {
return false
}
if (this.element === eventTarget) {
return true
} else if (eventTarget instanceof Element && this.element.contains(eventTarget)) {
return this.scope.containsElement(eventTarget)
} else {
return this.scope.containsElement(this.action.element)
}
}
private get controller(): Controller {
return this.context.controller
}
private get methodName(): string {
return this.action.methodName
}
private get element(): Element {
return this.scope.element
}
private get scope(): Scope {
return this.context.scope
}
}