-
Notifications
You must be signed in to change notification settings - Fork 54
Expand file tree
/
Copy pathcontrollable.ts
More file actions
71 lines (63 loc) · 2.46 KB
/
controllable.ts
File metadata and controls
71 lines (63 loc) · 2.46 KB
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
import type {CustomElementClass, CustomElement} from './custom-element.js'
import {createAbility} from './ability.js'
export interface Controllable {
[attachShadowCallback]?(shadowRoot: ShadowRoot): void
[attachInternalsCallback]?(internals: ElementInternals): void
}
export interface ControllableClass {
// TS mandates Constructors that get mixins have `...args: any[]`
// eslint-disable-next-line @typescript-eslint/no-explicit-any
new (...args: any[]): Controllable
}
export const attachShadowCallback = Symbol()
export const attachInternalsCallback = Symbol()
const shadows = new WeakMap<Controllable, ShadowRoot | undefined>()
const internals = new WeakMap<Controllable, ElementInternals>()
const internalsCalled = new WeakSet()
export const controllable = createAbility(
<T extends CustomElementClass>(Class: T): T & ControllableClass =>
class extends Class {
// TS mandates Constructors that get mixins have `...args: any[]`
// eslint-disable-next-line @typescript-eslint/no-explicit-any
constructor(...args: any[]) {
super(...args)
const shadowRoot = this.shadowRoot
if (shadowRoot && shadowRoot !== shadows.get(this)) this[attachShadowCallback](shadowRoot)
if (!internalsCalled.has(this)) {
try {
this.attachInternals()
} catch {
// Ignore errors
}
}
}
connectedCallback() {
this.setAttribute('data-catalyst', '')
super.connectedCallback?.()
}
attachShadow(...args: [init: ShadowRootInit]): ShadowRoot {
const shadowRoot = super.attachShadow(...args)
this[attachShadowCallback](shadowRoot)
return shadowRoot
}
[attachShadowCallback](this: CustomElement & Controllable, shadowRoot: ShadowRoot) {
shadows.set(this, shadowRoot)
}
attachInternals(): ElementInternals {
if (internals.has(this) && !internalsCalled.has(this)) {
internalsCalled.add(this)
return internals.get(this)!
}
const elementInternals = super.attachInternals()
this[attachInternalsCallback](elementInternals)
internals.set(this, elementInternals)
return elementInternals
}
[attachInternalsCallback](elementInternals: ElementInternals) {
const shadowRoot = elementInternals.shadowRoot
if (shadowRoot && shadowRoot !== shadows.get(this)) {
this[attachShadowCallback](shadowRoot)
}
}
}
)