Skip to content

Commit 11b68e7

Browse files
committed
fix: use svelte event on instead of addEventListener
1 parent 0c90d40 commit 11b68e7

File tree

18 files changed

+289
-157
lines changed

18 files changed

+289
-157
lines changed

packages/accordion/src/Panel.svelte

+3-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
<script lang="ts">
3030
import type { ComponentProps, Snippet } from 'svelte';
3131
import { onMount, setContext, getContext } from 'svelte';
32+
import { on } from 'svelte/events';
3233
import { writable } from 'svelte/store';
3334
import type { ActionArray } from '@smui/common/internal';
3435
import { classMap, dispatch } from '@smui/common/internal';
@@ -139,7 +140,8 @@
139140
content.getBoundingClientRect();
140141
content.classList.remove('smui-accordion__content--no-transition');
141142
content.style.height = height + 'px';
142-
content.addEventListener(
143+
on(
144+
content,
143145
'transitionend',
144146
() => {
145147
if (content) {

packages/autocomplete/src/Autocomplete.svelte

+3-1
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@
118118
<script lang="ts">
119119
import type { ComponentProps, Snippet } from 'svelte';
120120
import { setContext } from 'svelte';
121+
import { on } from 'svelte/events';
121122
import type { SmuiAttrs } from '@smui/common';
122123
import type { ActionArray } from '@smui/common/internal';
123124
import {
@@ -546,7 +547,8 @@
546547
) {
547548
if (!document.hasFocus()) {
548549
// Document lost focus.
549-
window.addEventListener(
550+
on(
551+
window,
550552
'focus',
551553
() => {
552554
if (!getElement()?.contains(document.activeElement)) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { on } from 'svelte/events';
2+
3+
/**
4+
* A way to keep track of things you've "on"ed so you can "off" them too.
5+
*
6+
* This is needed because Svelte doesn't have an "off" function, it returns
7+
* unlisten functions from its "on" function, and MDC-Web expects register and
8+
* deregister functions in the adapters.
9+
*/
10+
export class SvelteEventManager {
11+
elementMap = new Map<
12+
Element | Window | Document,
13+
{ [k: string]: Map<Function, Function> }
14+
>();
15+
16+
/**
17+
* Listen to an event on an element.
18+
*/
19+
on<T extends Event>(
20+
element: Element | Window | Document,
21+
event: string,
22+
handler: (evt: T) => void,
23+
options?: AddEventListenerOptions,
24+
) {
25+
if (!this.elementMap.has(element)) {
26+
this.elementMap.set(element, {});
27+
}
28+
29+
const eventMap = this.elementMap.get(element);
30+
31+
if (eventMap == null) {
32+
throw new Error("Event map couldn't be created.");
33+
}
34+
35+
if (!(event in eventMap)) {
36+
eventMap[event] = new Map<Function, Function>();
37+
}
38+
39+
const handlerMap = eventMap[event];
40+
41+
handlerMap.set(
42+
handler,
43+
on(element, event, handler as EventListener, options),
44+
);
45+
}
46+
47+
/**
48+
* Unlisten to an event on an element.
49+
*/
50+
off<T extends Event>(
51+
element: Element | Window | Document,
52+
event: string,
53+
handler: (evt: T) => void,
54+
) {
55+
const eventMap = this.elementMap.get(element);
56+
57+
if (eventMap == null || !(event in eventMap)) {
58+
return;
59+
}
60+
61+
const handlerMap = eventMap[event];
62+
const unlisten = handlerMap.get(handler);
63+
64+
if (unlisten != null) {
65+
unlisten();
66+
handlerMap.delete(handler);
67+
if (handlerMap.size === 0) {
68+
delete eventMap[event];
69+
70+
if (Object.keys(eventMap).length === 0) {
71+
this.elementMap.delete(element);
72+
}
73+
}
74+
}
75+
}
76+
77+
/**
78+
* Unlisten to all events managed by this instance.
79+
*/
80+
clear() {
81+
this.elementMap.forEach((eventMaps, _element) => {
82+
for (let [_event, eventMap] of Object.entries(eventMaps)) {
83+
eventMap.forEach((unlisten, _handler) => {
84+
unlisten();
85+
});
86+
}
87+
});
88+
this.elementMap.clear();
89+
}
90+
}

packages/common/src/internal/index.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ export * from './classMap.js';
33
export * from './dispatch.js';
44
export * from './exclude.js';
55
export * from './prefixFilter.js';
6+
export * from './SvelteEventManager.js';
67
export * from './useActions.js';

packages/common/src/internal/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ export * from './classMap.js';
33
export * from './dispatch.js';
44
export * from './exclude.js';
55
export * from './prefixFilter.js';
6+
export * from './SvelteEventManager.js';
67
export * from './useActions.js';

packages/dialog/src/Dialog.svelte

+9-8
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
prefixFilter,
9999
useActions,
100100
dispatch,
101+
SvelteEventManager,
101102
} from '@smui/common/internal';
102103
103104
const { FocusTrap } = domFocusTrap;
@@ -196,6 +197,7 @@
196197
197198
let element: HTMLDivElement;
198199
let instance: MDCDialogFoundation | undefined = $state();
200+
let eventManager = new SvelteEventManager();
199201
let internalClasses: { [k: string]: boolean } = $state({});
200202
let focusTrap: domFocusTrap.FocusTrap;
201203
let actionButtonsReversed = writable(false);
@@ -335,13 +337,13 @@
335337
registerContentEventHandler: (evt, handler) => {
336338
const content = getContentEl();
337339
if (content instanceof HTMLElement) {
338-
content.addEventListener(evt, handler);
340+
eventManager.on(content, evt, handler);
339341
}
340342
},
341343
deregisterContentEventHandler: (evt, handler) => {
342344
const content = getContentEl();
343345
if (content instanceof HTMLElement) {
344-
content.removeEventListener(evt, handler);
346+
eventManager.off(content, evt, handler);
345347
}
346348
},
347349
isScrollableContentAtTop: () => {
@@ -350,18 +352,17 @@
350352
isScrollableContentAtBottom: () => {
351353
return util.isScrollAtBottom(getContentEl());
352354
},
353-
registerWindowEventHandler: (evt, handler) => {
354-
window.addEventListener(evt, handler);
355-
},
356-
deregisterWindowEventHandler: (evt, handler) => {
357-
window.removeEventListener(evt, handler);
358-
},
355+
registerWindowEventHandler: (evt, handler) =>
356+
eventManager.on(window, evt, handler),
357+
deregisterWindowEventHandler: (evt, handler) =>
358+
eventManager.off(window, evt, handler),
359359
});
360360
361361
instance.init();
362362
363363
return () => {
364364
instance?.destroy();
365+
eventManager.clear();
365366
};
366367
});
367368

packages/drawer/src/Drawer.svelte

+11-5
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,12 @@
3838
import { onMount, onDestroy, setContext } from 'svelte';
3939
import type { SmuiAttrs } from '@smui/common';
4040
import type { ActionArray } from '@smui/common/internal';
41-
import { classMap, useActions, dispatch } from '@smui/common/internal';
41+
import {
42+
classMap,
43+
useActions,
44+
dispatch,
45+
SvelteEventManager,
46+
} from '@smui/common/internal';
4247
4348
const { FocusTrap } = domFocusTrap;
4449
@@ -89,6 +94,7 @@
8994
| MDCDismissibleDrawerFoundation
9095
| MDCModalDrawerFoundation
9196
| undefined = $state(undefined);
97+
let eventManager = new SvelteEventManager();
9298
let internalClasses: { [k: string]: boolean } = $state({});
9399
let previousFocus: Element | null = $state(null);
94100
let focusTrap: domFocusTrap.FocusTrap;
@@ -131,20 +137,20 @@
131137
132138
onDestroy(() => {
133139
instance && instance.destroy();
134-
scrim &&
135-
scrim.removeEventListener('SMUIDrawerScrimClick', handleScrimClick);
140+
scrim && eventManager.off(scrim, 'SMUIDrawerScrimClick', handleScrimClick);
141+
eventManager.clear();
136142
});
137143
138144
function getInstance() {
139145
if (scrim) {
140-
scrim.removeEventListener('SMUIDrawerScrimClick', handleScrimClick);
146+
eventManager.off(scrim, 'SMUIDrawerScrimClick', handleScrimClick);
141147
}
142148
143149
if (variant === 'modal') {
144150
scrim =
145151
getElement().parentNode?.querySelector('.mdc-drawer-scrim') ?? false;
146152
if (scrim) {
147-
scrim.addEventListener('SMUIDrawerScrimClick', handleScrimClick);
153+
eventManager.on(scrim, 'SMUIDrawerScrimClick', handleScrimClick);
148154
}
149155
}
150156

packages/floating-label/src/FloatingLabel.svelte

+9-3
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@
4343
import { onMount, getContext } from 'svelte';
4444
import type { SmuiAttrs } from '@smui/common';
4545
import type { ActionArray } from '@smui/common/internal';
46-
import { classMap, useActions } from '@smui/common/internal';
46+
import {
47+
classMap,
48+
useActions,
49+
SvelteEventManager,
50+
} from '@smui/common/internal';
4751
4852
import type { SMUIFloatingLabelAccessor } from './FloatingLabel.types.js';
4953
@@ -97,6 +101,7 @@
97101
98102
let element: HTMLSpanElement | HTMLLabelElement;
99103
let instance: MDCFloatingLabelFoundation | undefined = $state();
104+
let eventManager = new SvelteEventManager();
100105
let internalClasses: { [k: string]: boolean } = $state({});
101106
let internalStyles: { [k: string]: string } = $state({});
102107
let inputProps =
@@ -141,9 +146,9 @@
141146
return scrollWidth;
142147
},
143148
registerInteractionHandler: (evtType, handler) =>
144-
getElement().addEventListener(evtType, handler as EventListener),
149+
eventManager.on(getElement(), evtType, handler),
145150
deregisterInteractionHandler: (evtType, handler) =>
146-
getElement().removeEventListener(evtType, handler as EventListener),
151+
eventManager.off(getElement(), evtType, handler),
147152
});
148153
149154
const accessor: SMUIFloatingLabelAccessor = {
@@ -162,6 +167,7 @@
162167
SMUIFloatingLabelUnmount && SMUIFloatingLabelUnmount(accessor);
163168
164169
instance?.destroy();
170+
eventManager.clear();
165171
};
166172
});
167173

packages/form-field/src/FormField.svelte

+7-6
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
exclude,
4040
prefixFilter,
4141
useActions,
42+
SvelteEventManager,
4243
} from '@smui/common/internal';
4344
4445
type OwnProps = {
@@ -90,6 +91,7 @@
9091
9192
let element: HTMLDivElement;
9293
let instance: MDCFormFieldFoundation | undefined = $state();
94+
let eventManager = new SvelteEventManager();
9395
let labelEl: HTMLLabelElement;
9496
let input: SMUIGenericInputAccessor | undefined = $state();
9597
@@ -117,18 +119,17 @@
117119
input.deactivateRipple();
118120
}
119121
},
120-
deregisterInteractionHandler: (evtType, handler) => {
121-
labelEl.removeEventListener(evtType, handler);
122-
},
123-
registerInteractionHandler: (evtType, handler) => {
124-
labelEl.addEventListener(evtType, handler);
125-
},
122+
deregisterInteractionHandler: (evtType, handler) =>
123+
eventManager.off(labelEl, evtType, handler),
124+
registerInteractionHandler: (evtType, handler) =>
125+
eventManager.on(labelEl, evtType, handler),
126126
});
127127
128128
instance.init();
129129
130130
return () => {
131131
instance?.destroy();
132+
eventManager.clear();
132133
};
133134
});
134135

packages/line-ripple/src/LineRipple.svelte

+9-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@
2121
import { onMount } from 'svelte';
2222
import type { SmuiAttrs } from '@smui/common';
2323
import type { ActionArray } from '@smui/common/internal';
24-
import { classMap, useActions } from '@smui/common/internal';
24+
import {
25+
classMap,
26+
useActions,
27+
SvelteEventManager,
28+
} from '@smui/common/internal';
2529
2630
type OwnProps = {
2731
/**
@@ -51,6 +55,7 @@
5155
5256
let element: HTMLDivElement;
5357
let instance: MDCLineRippleFoundation | undefined = $state();
58+
let eventManager = new SvelteEventManager();
5459
let internalClasses: { [k: string]: boolean } = $state({});
5560
let internalStyles: { [k: string]: string } = $state({});
5661
@@ -61,15 +66,16 @@
6166
hasClass,
6267
setStyle: addStyle,
6368
registerEventHandler: (evtType, handler) =>
64-
getElement().addEventListener(evtType, handler),
69+
eventManager.on(getElement(), evtType, handler),
6570
deregisterEventHandler: (evtType, handler) =>
66-
getElement().removeEventListener(evtType, handler),
71+
eventManager.off(getElement(), evtType, handler),
6772
});
6873
6974
instance.init();
7075
7176
return () => {
7277
instance?.destroy();
78+
eventManager.clear();
7379
};
7480
});
7581

0 commit comments

Comments
 (0)