-
Notifications
You must be signed in to change notification settings - Fork 1
/
event-delegation.d.ts
407 lines (378 loc) · 15.2 KB
/
event-delegation.d.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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
import type { ParseSelector } from 'typed-query-selector/parser'
/**
* The listener callback to invoke whenever an event occurs. It is provided 2 ways to get the
* delegating element -- the 'delegator':
*
* - Through `this` binding
* - Through the property {@link DelegationEvent.delegator `delegator`} on the event argument.
*
* @typeParam D The element type for the delegation selector
* @typeParam E The event instance type
* @typeParam R The type of the root element (or `event.currentTarget`)
*/
export type DelegationListener<
D extends Element,
E extends Event,
R extends Element
> = (this: D, event: DelegationEvent<D, E, R>) => void
/**
* A delegation event. The inferred event instance type with an additional property `delegator` referencing the
* descendant element that matched the delegation selector.
*
* @typeParam D The element type for the delegation selector
* @typeParam E The event instance type
* @typeParam R The type of the root element (or `event.currentTarget`)
*/
export type DelegationEvent<D extends Element, E extends Event, R extends Element> = E & {
readonly delegator: D
readonly currentTarget: R
}
/**
* The event handler instance interface.
*
* {@link AskRoot `AskRoot`} => {@link AskEvent `AskEvent`} => {@link AskSelector `AskSelector`} => {@link AskListener `AskListener`} builds **`EventHandler`**
*
* @typeParam R The type of the root element to which the event listener is attached.
*/
export interface EventHandler<R extends Element> {
/** Tells whether this handler is currently attached, ie. the listener is active. */
isAttached(): boolean
/** Tells whether the listener for this instance has been removed previously. */
isDestroyed(): boolean
/** Returns the root element - the element to which the event listener is attached. */
root(): R
/** Tells what events this instance is listening for. */
eventType(): string
/** Returns the CSS selector that is used to match against descendant elements. */
selector(): string
/** Removes the event listener for this instance. */
remove(): void
}
/**
* Internally used configuration type for EventHandler construction.
*/
export interface DelegationConfig<R extends Element, E extends Event = Event, D extends Element = Element> {
readonly root: R
readonly eventType: string
readonly selector: string
readonly listener: DelegationListener<D, E, R>
readonly listenerOptions?: boolean | AddEventListenerOptions
}
export type TagNameMap = HTMLElementTagNameMap & SVGElementTagNameMap
export type EventMap = GlobalEventHandlersEventMap
export type BuildMode = 'SINGLE' | 'MANY'
export type Build<M extends BuildMode, R extends Element> = M extends 'MANY' ? EventHandler<R>[] :
M extends 'SINGLE' ? EventHandler<R> :
never
/**
* Represents the package's main namespace `EventDelegation`. Provides methods for setting the
* event-delegation root element(s)
*
* **`AskRoot`** => {@link AskEvent `AskEvent`} => {@link AskSelector `AskSelector`} => {@link AskListener `AskListener`} builds {@link EventHandler `EventHandler`}
* > All methods / overloads return the {@link AskEvent `AskEvent`} interface.
*/
export interface AskRoot {
// ------------------------------------------------------------------------------
// EventDelegation.global()
// ------------------------------------------------------------------------------
/**
* Start building an event-delegation handler for the (global) body element.
*
* @example
* ```typescript
* EventDelegation
* .global()
* ```
*/
global(): AskEvent<HTMLElement, 'SINGLE'>
// ------------------------------------------------------------------------------
// EventDelegation.within()
// ------------------------------------------------------------------------------
/**
* Start building an event-delegation handler for a specified root.
*
* Method overload that takes an Element instance as the root.
*
* @param root A root element reference.
* @typeParam R The element type for the root element.
* This param is inferred from the selector argument.
*
* @example
* ```typescript
* declare const myRoot: HTMLElement
*
* EventDelegation
* .within(myRoot)
* ```
*/
within<R extends Element>(root: R): AskEvent<R, 'SINGLE'>
/**
* Start building an event-delegation handler for a specified root.
*
* Method overload that takes a CSS tag selector for the root. This provides autocompletion
* features when starting to type tag-qualified CSS selectors.
*
* @param selector An HTML (or SVG) tag selector.
* @typeParam K the HTML tag name literal type for the selector argument.
* This param is inferred from the selector argument.
*
* @example
* ```typescript
* EventDelegation
* .within('form')
* ```
*/
within<K extends keyof TagNameMap>(selector: K): AskEvent<TagNameMap[K], 'SINGLE'> | never
/**
* Start building an event-delegation handler for a specified root.
*
* Method overload that takes a CSS selector for the root. It wil attempt to parse the selector
* and infer the root element type from it.
*
* @param selector A tag-qualified CSS-style selector to parse.
* @typeParam S The CSS-style selector literal type for the selector argument.
* This param is inferred from the selector argument.
*
* @example
* ```typescript
* EventDelegation
* .within('#main form.my-form')
* ```
*/
within<S extends string>(selector: S): AskEvent<ParseSelector<S>, 'SINGLE'> | never
/**
* Start building an event-delegation handler for a specified root.
*
* Method overload that can be used when all else fails. If previous overloads
* failed to match, this one allows you to explicitly specify the expected root
* type for _any_ selector string.
*
* @param root Any CSS selector that is expected to select the root element type.
* @typeParam R The element type for the root element.
* This param can be explicitly given to override the default `Element` type.
*
* @example
* ```typescript
* EventDelegation
* .within<CustomComponent>('custom-component')
* ```
*/
within<R extends Element>(root: string): AskEvent<R, 'SINGLE'> | never
// ------------------------------------------------------------------------------
// EventDelegation.withinMany()
// ------------------------------------------------------------------------------
/**
* Start building an event-delegation handler for multiple specified roots.
*
* Method overload that takes Element instances as the roots.
*
* @param roots An array of root element references.
* @typeParam R The element type for the root elements. Note that this may very well be a union type
* when an array with multiple root types is given. This param is inferred from the selector
* argument.
*
* @example
* ```typescript
* declare const rootA: HTMLElement
* declare const rootB: HTMLFormElement
*
* EventDelegation
* .withinMany([rootA, rootB])
* ```
*/
withinMany<R extends Element>(roots: R[]): AskEvent<R, 'MANY'>
/**
* Start building an event-delegation handler for multiple specified roots.
*
* Method overload that takes a CSS tag selector for the roots. This provides autocompletion
* features when starting to type tag-qualified CSS selectors.
*
* @param selector An HTML (or SVG) tag selector.
* @typeParam K the HTML tag name literal type for the selector argument.
* This param is inferred from the selector argument.
*
* @example
* ```typescript
* EventDelegation
* .withinMany('form')
* ```
*/
withinMany<K extends keyof TagNameMap>(selector: K): AskEvent<TagNameMap[K], 'MANY'>
/**
* Start building an event-delegation handler for multiple specified roots.
*
* Method overload that takes a CSS selector for the roots. It wil attempt to parse the selector
* and infer the root elements' type from it.
*
* @param selector A tag-qualified CSS-style selector to parse.
* @typeParam S The CSS-style selector literal type for the selector argument.
* This param is inferred from the selector argument.
*
* @example
* ```typescript
* EventDelegation
* .withinMany('#main form.my-form')
* ```
*/
withinMany<S extends string>(selector: S): AskEvent<ParseSelector<S>, 'MANY'>
/**
* Start building an event-delegation handler for multiple specified roots.
*
* Method overload that can be used when all else fails. If previous overloads
* failed to match, this one allows you to explicitly specify the expected
* type for _any_ selector string.
*
* @param root Any CSS selector that is expected to select the root element type.
* @typeParam R The element type for the root element.
* This param can be explicitly given to override the default `Element` type.
*
* @example
* ```typescript
* EventDelegation
* .withinMany<CustomComponent>('custom-component')
* ```
*/
withinMany<R extends Element>(roots: string): AskEvent<R, 'MANY'>
}
/**
* Method signatures that set the event name and instance type.
*
* {@link AskRoot `AskRoot`} => **`AskEvent`** => {@link AskSelector `AskSelector`} => {@link AskListener `AskListener`} builds {@link EventHandler `EventHandler`}
* > All overloads return the {@link AskSelector `AskSelector`} interface.
*/
export interface AskEvent<R extends Element, Mode extends BuildMode> {
/**
* Method overload that infers the event instance type from the given event name.
*
* @param eventType The event name of events to listen to.
* @typeParam EKey The event name literal type that's used to infer the Event instance type.
* This param is inferred from the `eventType` argument.
*
* @example
* ```typescript
* EventDelegation
* .global()
* .events('click')
* ```
*/
events<EKey extends keyof EventMap>(eventType: EKey): AskSelector<R, EventMap[EKey], Mode>
/**
* Method overload that allows to explicitly specify the event instance type.
*
* @param eventType The event name of events to listen to.
* @typeParam E The Event instance type. This param can be explicitly given to override
* the default `Event` type.
*
* @example
* ```typescript
* EventDelegation
* .global()
* .events<CustomEvent>('my:event')
* ```
*/
events<E extends Event>(eventType: string): AskSelector<R, E, Mode>
}
/**
* Method signatures that set the delegator selector and instance type of delegating elements.
*
* {@link AskRoot `AskRoot`} => {@link AskEvent `AskEvent`} => **`AskSelector`** => {@link AskListener `AskListener`} builds {@link EventHandler `EventHandler`}
* > All overloads return the {@link AskListener `AskListener`} interface
*/
export interface AskSelector<R extends Element, E extends Event, Mode extends BuildMode> {
/**
* Takes the delegation selector.
*
* Method overload that takes a CSS tag selector for the delegator elements. This provides autocompletion
* features when starting to type tag-qualified CSS selectors.
*
* @param selector An HTML (or SVG) tag selector.
* @typeParam K the HTML tag name literal type for the selector argument.
* This param is inferred from the selector argument.
*
* @example
* ```typescript
* EventDelegation
* .global()
* .events('click')
* .select('button')
* ```
*/
select<K extends keyof TagNameMap>(selector: K): AskListener<R, E, TagNameMap[K], Mode>
/**
* Takes the delegation selector.
*
* The method wil attempt to parse the selector and infer the root element type from it.
*
* @param selector A tag-qualified CSS-style selector to parse.
* @typeParam S The CSS-style selector literal type for the selector argument.
* This param is inferred from the selector argument.
*
* @example
* ```typescript
* EventDelegation
* .global()
* .events('click')
* .select('form button.my-button[aria-disabled="false"]')
* ```
*/
select<S extends string>(selector: S): AskListener<R, E, ParseSelector<S>, Mode>
/**
* Takes the delegation selector.
*
* Method overload that can be used when all else fails. If previous overloads
* failed to match, this one allows you to explicitly specify the expected element
* type for _any_ selector string.
*
* @param selector Any CSS selector that is expected to select the delegator element type.
* @typeParam D The element type for the delegator elements.
* This param can be explicitly given to override the default `Element` type.
*
* @example
* ```typescript
* EventDelegation
* .global()
* .events('click')
* .select<CustomButton>('custom-button')
* ```
*/
select<D extends Element>(selector: string): AskListener<R, E, D, Mode>
}
/**
* Method signatures that set the event listener function and return event handlers.
*
* {@link AskRoot `AskRoot`} => {@link AskEvent `AskEvent`} => {@link AskSelector `AskSelector`} => **`AskListener`** builds {@link EventHandler `EventHandler`}
* > Returns one or many {@link EventHandler `EventHandler`} instances.
*/
export interface AskListener<R extends Element, E extends Event, D extends Element, Mode extends BuildMode> {
/**
* Takes the event listener function. Uses the previously constructed types for delegator elements
* and event instances to provide full type inference inside listener callbacks.
*
* returns one {@link EventHandler `EventHandler`} instance when the build started with either
* {@link AskRoot.global `global()`} or {@link AskRoot.within `within()`},
* or an array of EventHandler instances when the build started with
* {@link AskRoot.withinMany `withinMany()`}.
*
* @param listener
* @param listenerOptions
* @example
* ```typescript
* // Returns either one handler
* const oneHandler: EventHandler<HTMLElement> = EventDelegation
* .global()
* .events('click')
* .select('button')
* .listen((e) => e.delegator.disabled = true)
*
* // or many handlers
* const manyHandlers: EventHandler<HTMLFormElement>[] = EventDelegation
* .withinMany('form')
* .events('click')
* .select('button')
* .listen((e) => e.delegator.disabled = true)
* ```
*/
listen(listener: DelegationListener<D, E, R>, listenerOptions?: boolean | AddEventListenerOptions): Build<Mode, R>
}
declare const EventDelegation: AskRoot
export default EventDelegation