-
Notifications
You must be signed in to change notification settings - Fork 2.1k
feat(auto-init): Convert JS to TypeScript #4395
Changes from all commits
4e99e51
e00153b
86a52f0
e9690cf
6b6f7fb
78e0b7f
50f256c
cbd6218
eb01f18
59adfbc
8aae7e3
3c83f31
5d3f489
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,16 +21,30 @@ | |
* THE SOFTWARE. | ||
*/ | ||
|
||
const registry = Object.create(null); | ||
// tslint:disable:only-arrow-functions | ||
|
||
const CONSOLE_WARN = console.warn.bind(console); | ||
import {MDCComponent, MDCFoundation} from '@material/base/index'; | ||
|
||
function _emit(evtType, evtData, shouldBubble = false) { | ||
interface ComponentClass { | ||
// tslint:disable-next-line:no-any a component can pass in anything it needs to the constructor | ||
new<F extends MDCFoundation>(root: Element, foundation?: F, ...args: any[]): MDCComponent<F>; | ||
attachTo<F extends MDCFoundation>(root: Element): MDCComponent<F>; | ||
} | ||
|
||
interface Registry { | ||
[key: string]: ComponentClass; | ||
} | ||
|
||
const registry: Registry = {}; | ||
|
||
const CONSOLE_WARN = console.warn.bind(console); // tslint:disable-line:no-console | ||
|
||
function _emit<T extends object>(evtType: string, evtData: T, shouldBubble = false) { | ||
let evt; | ||
if (typeof CustomEvent === 'function') { | ||
evt = new CustomEvent(evtType, { | ||
detail: evtData, | ||
evt = new CustomEvent<T>(evtType, { | ||
bubbles: shouldBubble, | ||
detail: evtData, | ||
}); | ||
} else { | ||
evt = document.createEvent('CustomEvent'); | ||
|
@@ -40,36 +54,39 @@ function _emit(evtType, evtData, shouldBubble = false) { | |
document.dispatchEvent(evt); | ||
} | ||
|
||
/* istanbul ignore next: optional argument is not a branch statement */ | ||
/** | ||
* Auto-initializes all mdc components on a page. | ||
* Auto-initializes all MDC components on a page. | ||
*/ | ||
export default function mdcAutoInit(root = document, warn = CONSOLE_WARN) { | ||
export function mdcAutoInit(root = document, warn = CONSOLE_WARN) { | ||
const components = []; | ||
const nodes = root.querySelectorAll('[data-mdc-auto-init]'); | ||
for (let i = 0, node; (node = nodes[i]); i++) { | ||
const ctorName = node.dataset.mdcAutoInit; | ||
const nodes: Element[] = [].slice.call(root.querySelectorAll('[data-mdc-auto-init]')); | ||
|
||
for (const node of nodes) { | ||
const ctorName = node.getAttribute('data-mdc-auto-init'); | ||
if (!ctorName) { | ||
throw new Error('(mdc-auto-init) Constructor name must be given.'); | ||
} | ||
|
||
const Ctor = registry[ctorName]; | ||
if (typeof Ctor !== 'function') { | ||
const Constructor = registry[ctorName]; // tslint:disable-line:variable-name | ||
if (typeof Constructor !== 'function') { | ||
throw new Error( | ||
`(mdc-auto-init) Could not find constructor in registry for ${ctorName}`); | ||
} | ||
|
||
if (node[ctorName]) { | ||
if (Object.getOwnPropertyDescriptor(node, ctorName)) { | ||
warn(`(mdc-auto-init) Component already initialized for ${node}. Skipping...`); | ||
continue; | ||
} | ||
|
||
// TODO: Should we make an eslint rule for an attachTo() static method? | ||
const component = Ctor.attachTo(node); | ||
// See https://github.com/Microsoft/TypeScript/issues/14600 for discussion of static interface support in TS | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the TODO above this obsolete? (Like, would our build actually fail without an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately TypeScript won't catch that because it doesn't do static type checking.
It does, however, verify that the required constructor signature exists (via |
||
const component = Constructor.attachTo(node); | ||
Object.defineProperty(node, ctorName, { | ||
configurable: true, | ||
enumerable: false, | ||
value: component, | ||
writable: false, | ||
enumerable: false, | ||
configurable: true, | ||
}); | ||
components.push(component); | ||
} | ||
|
@@ -78,22 +95,27 @@ export default function mdcAutoInit(root = document, warn = CONSOLE_WARN) { | |
return components; | ||
} | ||
|
||
mdcAutoInit.register = function(componentName, Ctor, warn = CONSOLE_WARN) { | ||
if (typeof Ctor !== 'function') { | ||
throw new Error(`(mdc-auto-init) Invalid Ctor value ${Ctor}. Expected function`); | ||
// Constructor is PascalCased because it is a direct reference to a class, rather than an instance of a class. | ||
// tslint:disable-next-line:variable-name | ||
acdvorak marked this conversation as resolved.
Show resolved
Hide resolved
|
||
mdcAutoInit.register = function(componentName: string, Constructor: ComponentClass, warn = CONSOLE_WARN) { | ||
if (typeof Constructor !== 'function') { | ||
throw new Error(`(mdc-auto-init) Invalid Ctor value ${Constructor}. Expected function`); | ||
} | ||
if (registry[componentName]) { | ||
warn( | ||
`(mdc-auto-init) Overriding registration for ${componentName} with ${Ctor}. ` + | ||
`(mdc-auto-init) Overriding registration for ${componentName} with ${Constructor}. ` + | ||
`Was: ${registry[componentName]}`); | ||
} | ||
registry[componentName] = Ctor; | ||
registry[componentName] = Constructor; | ||
}; | ||
|
||
mdcAutoInit.deregister = function(componentName) { | ||
mdcAutoInit.deregister = function(componentName: string) { | ||
delete registry[componentName]; | ||
}; | ||
|
||
mdcAutoInit.deregisterAll = function() { | ||
Object.keys(registry).forEach(this.deregister, this); | ||
const keys = Object.keys(registry) as string[]; | ||
keys.forEach(this.deregister, this); | ||
}; | ||
|
||
export default mdcAutoInit; |
Uh oh!
There was an error while loading. Please reload this page.