Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

Commit

Permalink
refactor: Make TS easier to wrap (#4407)
Browse files Browse the repository at this point in the history
### Goal

Make it easier for frameworks to wrap our TypeScript code.

Refs #4225

### How

**Isolate framework-specific code from framework-agnostic code.**

Specifically, this PR:

* Maintains full backward compatibility with existing code
* Moves all component definitions from `index.ts` to `component.ts`
    - `index.ts` is now purely re-exporting other files
* Moves component-dependent types from `types.ts` to `component.ts`
    - Framework-related types are now quarantined in a single file: `component.ts`
    - All other files are now purely framework-agnostic, and can be safely wrapped by frameworks that don't use our components
* Makes `import` paths more specific by switching from `@material/foo/index` to e.g. `@material/foo/foundation` or `@material/foo/types`
    - The only exception is `component.ts` files: Since they're basically our default "framework", they can safely import _other_ "framework" types via `@material/foo/index`
* Updates class & interface export syntax to isolate the `default` export line for future removal if necessary:
    ```ts
    export class MDCFooFoundation {
      // ...
    }
    export default MDCFooFoundation;
    ```
* Combines `typings/custom.d.ts` and `typings/dom.ie.d.ts` into `packages/mdc-dom/externs.d.ts` so the types will be publicly visible
* Adds `MDCFooFactory` types for components that need them (e.g., `MDCRipple`, `MDCList`)

### Packages

* [x] `animation`
* [x] `checkbox`
* [x] `chips`
* [x] `dialog`
* [x] `dom`
* [x] `drawer`
* [x] `floating-label`
* [x] `form-field`
* [x] `grid-list`
* [x] `icon-button`
* [x] `icon-toggle` _(deprecated)_
* [x] `line-ripple`
* [x] `linear-progress`
* [x] `list`
* [x] `menu`
* [x] `menu-surface`
* [x] `notched-outline`
* [x] `radio`
* [x] `ripple`
* [x] `select`
* [x] `selection-control`
* [x] `slider`
* [x] `snackbar`
* [x] `switch`
* [x] `tab`
* [x] `tab-bar`
* [x] `tab-indicator`
* [x] `tab-scroller`
* [x] `tab-tabs` _(deprecated)_
* [x] `textfield`
* [x] `toolbar` _(deprecated)_
* [x] `top-app-bar`
  • Loading branch information
acdvorak authored Feb 21, 2019
1 parent 1ec12b6 commit 72e8b66
Show file tree
Hide file tree
Showing 245 changed files with 5,600 additions and 5,073 deletions.
29 changes: 19 additions & 10 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
node_modules
# macOS filesystem
.DS_Store
/build
/coverage
packages/*/dist
out

# Editors and IDEs
.idea/
.vscode/

# Generated files
*.log
.idea
*.sw*
.closure-tmp
.site-generator-tmp
.typescript-tmp
.vscode
node_modules/

# Build output
/build/
/coverage/
packages/*/dist

# Material.io site generator test (`npm run test:site`)
.site-generator-tmp/

# Used by internal sync & rewrite scripts
.rewrite-tmp/
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"build:demos": "npm run clean && mkdirp build && webpack --config=demos/webpack.config.js --progress --colors",
"build:min": "mkdirp build && cross-env MDC_ENV=production webpack -p --progress --colors",
"changelog": "standard-changelog -i CHANGELOG.md -k packages/material-components-web/package.json",
"clean": "del-cli build/** build .typescript-tmp/** .typescript-tmp",
"clean": "del-cli build/** build .rewrite-tmp/** .rewrite-tmp",
"clean:site": "del-cli .site-generator-tmp/** .site-generator-tmp",
"dist": "npm run build && npm run build:min",
"dev": "npm run clean && cross-env MDC_ENV=development webpack-dev-server --config=demos/webpack.config.js --progress --inline --hot --host 0.0.0.0",
Expand All @@ -16,7 +16,7 @@
"fix:css": "stylelint --fix \"packages/**/*.scss\"; stylelint --fix --config=test/screenshot/.stylelintrc.yaml \"test/screenshot/**/*.scss\"",
"fix": "npm-run-all --parallel fix:*",
"lint:css": "stylelint \"packages/**/*.scss\" && stylelint --config=test/screenshot/.stylelintrc.yaml \"test/screenshot/**/*.scss\"",
"lint:js": "eslint packages test scripts webpack.config.js demos/webpack.config.js karma.conf.js",
"lint:js": "eslint test scripts webpack.config.js demos/webpack.config.js karma.conf.js",
"lint:ts": "tslint --exclude \"test/**/*.d.ts\" \"packages/**/*.ts\" \"test/**/*.ts\" \"scripts/**/*.ts\"",
"lint:html": "find test/screenshot/spec/ -name '*.html' | grep -v 'index.html$' | xargs htmllint",
"lint:imports": "node scripts/check-imports.js",
Expand Down
104 changes: 5 additions & 99 deletions packages/mdc-animation/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* @license
* Copyright 2016 Google Inc.
* Copyright 2019 Google Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand All @@ -21,102 +21,8 @@
* THE SOFTWARE.
*/

type StandardCssPropertyName = (
'animation' | 'transform' | 'transition'
);
type PrefixedCssPropertyName = (
'-webkit-animation' | '-webkit-transform' | '-webkit-transition'
);
type StandardJsEventType = (
'animationend' | 'animationiteration' | 'animationstart' | 'transitionend'
);
type PrefixedJsEventType = (
'webkitAnimationEnd' | 'webkitAnimationIteration' | 'webkitAnimationStart' | 'webkitTransitionEnd'
);
import * as util from './util';

type CssVendorPropertyMap = { [K in StandardCssPropertyName]: CssVendorProperty };
type JsVendorPropertyMap = { [K in StandardJsEventType]: JsVendorProperty };

interface CssVendorProperty {
prefixed: PrefixedCssPropertyName;
standard: StandardCssPropertyName;
}

interface JsVendorProperty {
cssProperty: StandardCssPropertyName;
prefixed: PrefixedJsEventType;
standard: StandardJsEventType;
}

const cssPropertyNameMap: CssVendorPropertyMap = {
animation: {
prefixed: '-webkit-animation',
standard: 'animation',
},
transform: {
prefixed: '-webkit-transform',
standard: 'transform',
},
transition: {
prefixed: '-webkit-transition',
standard: 'transition',
},
};

const jsEventTypeMap: JsVendorPropertyMap = {
animationend: {
cssProperty: 'animation',
prefixed: 'webkitAnimationEnd',
standard: 'animationend',
},
animationiteration: {
cssProperty: 'animation',
prefixed: 'webkitAnimationIteration',
standard: 'animationiteration',
},
animationstart: {
cssProperty: 'animation',
prefixed: 'webkitAnimationStart',
standard: 'animationstart',
},
transitionend: {
cssProperty: 'transition',
prefixed: 'webkitTransitionEnd',
standard: 'transitionend',
},
};

function isWindow(windowObj: Window): boolean {
return Boolean(windowObj.document) && typeof windowObj.document.createElement === 'function';
}

function getCorrectPropertyName(windowObj: Window, cssProperty: StandardCssPropertyName):
StandardCssPropertyName | PrefixedCssPropertyName {
if (isWindow(windowObj) && cssProperty in cssPropertyNameMap) {
const el = windowObj.document.createElement('div');
const {standard, prefixed} = cssPropertyNameMap[cssProperty];
const isStandard = standard in el.style;
return isStandard ? standard : prefixed;
}
return cssProperty;
}

function getCorrectEventName(windowObj: Window, eventType: StandardJsEventType):
StandardJsEventType | PrefixedJsEventType {
if (isWindow(windowObj) && eventType in jsEventTypeMap) {
const el = windowObj.document.createElement('div');
const {standard, prefixed, cssProperty} = jsEventTypeMap[eventType];
const isStandard = cssProperty in el.style;
return isStandard ? standard : prefixed;
}
return eventType;
}

export {
PrefixedCssPropertyName,
StandardCssPropertyName,
PrefixedJsEventType,
StandardJsEventType,
getCorrectEventName,
getCorrectPropertyName,
};
export {util}; // New namespace
export * from './types';
export * from './util'; // Old namespace for backward compatibility
30 changes: 23 additions & 7 deletions packages/mdc-drawer/types.ts → packages/mdc-animation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,28 @@
* THE SOFTWARE.
*/

import {MDCList} from '@material/list/index';
import * as FocusTrapLib from 'focus-trap';
export type StandardCssPropertyName =
'animation' | 'transform' | 'transition';

export type FocusTrapFactory = (
element: HTMLElement | string,
userOptions?: FocusTrapLib.Options,
) => FocusTrapLib.FocusTrap;
export type PrefixedCssPropertyName =
'-webkit-animation' | '-webkit-transform' | '-webkit-transition';

export type ListFactory = (el: Element) => MDCList;
export type StandardJsEventType =
'animationend' | 'animationiteration' | 'animationstart' | 'transitionend';

export type PrefixedJsEventType =
'webkitAnimationEnd' | 'webkitAnimationIteration' | 'webkitAnimationStart' | 'webkitTransitionEnd';

export interface CssVendorProperty {
prefixed: PrefixedCssPropertyName;
standard: StandardCssPropertyName;
}

export interface JsVendorProperty {
cssProperty: StandardCssPropertyName;
prefixed: PrefixedJsEventType;
standard: StandardJsEventType;
}

export type CssVendorPropertyMap = { [K in StandardCssPropertyName]: CssVendorProperty };
export type JsVendorPropertyMap = { [K in StandardJsEventType]: JsVendorProperty };
92 changes: 92 additions & 0 deletions packages/mdc-animation/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* @license
* Copyright 2016 Google Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

import {
CssVendorPropertyMap, JsVendorPropertyMap,
PrefixedCssPropertyName, PrefixedJsEventType,
StandardCssPropertyName, StandardJsEventType,
} from './types';

const cssPropertyNameMap: CssVendorPropertyMap = {
animation: {
prefixed: '-webkit-animation',
standard: 'animation',
},
transform: {
prefixed: '-webkit-transform',
standard: 'transform',
},
transition: {
prefixed: '-webkit-transition',
standard: 'transition',
},
};

const jsEventTypeMap: JsVendorPropertyMap = {
animationend: {
cssProperty: 'animation',
prefixed: 'webkitAnimationEnd',
standard: 'animationend',
},
animationiteration: {
cssProperty: 'animation',
prefixed: 'webkitAnimationIteration',
standard: 'animationiteration',
},
animationstart: {
cssProperty: 'animation',
prefixed: 'webkitAnimationStart',
standard: 'animationstart',
},
transitionend: {
cssProperty: 'transition',
prefixed: 'webkitTransitionEnd',
standard: 'transitionend',
},
};

function isWindow(windowObj: Window): boolean {
return Boolean(windowObj.document) && typeof windowObj.document.createElement === 'function';
}

export function getCorrectPropertyName(windowObj: Window, cssProperty: StandardCssPropertyName):
StandardCssPropertyName | PrefixedCssPropertyName {
if (isWindow(windowObj) && cssProperty in cssPropertyNameMap) {
const el = windowObj.document.createElement('div');
const {standard, prefixed} = cssPropertyNameMap[cssProperty];
const isStandard = standard in el.style;
return isStandard ? standard : prefixed;
}
return cssProperty;
}

export function getCorrectEventName(windowObj: Window, eventType: StandardJsEventType):
StandardJsEventType | PrefixedJsEventType {
if (isWindow(windowObj) && eventType in jsEventTypeMap) {
const el = windowObj.document.createElement('div');
const {standard, prefixed, cssProperty} = jsEventTypeMap[eventType];
const isStandard = cssProperty in el.style;
return isStandard ? standard : prefixed;
}
return eventType;
}
11 changes: 7 additions & 4 deletions packages/mdc-auto-init/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@

// tslint:disable:only-arrow-functions

import {MDCComponent, MDCFoundation} from '@material/base/index';
import {MDCComponent} from '@material/base/component';
import {MDCFoundation} from '@material/base/foundation';

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>;
}

Expand Down Expand Up @@ -58,6 +60,7 @@ function _emit<T extends object>(evtType: string, evtData: T, shouldBubble = fal
/**
* Auto-initializes all MDC components on a page.
*/

export function mdcAutoInit(root = document, warn = CONSOLE_WARN) {
const components = [];
const nodes: Element[] = [].slice.call(root.querySelectorAll('[data-mdc-auto-init]'));
Expand All @@ -71,7 +74,7 @@ export function mdcAutoInit(root = document, warn = CONSOLE_WARN) {
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}`);
`(mdc-auto-init) Could not find constructor in registry for ${ctorName}`);
}

if (Object.getOwnPropertyDescriptor(node, ctorName)) {
Expand Down Expand Up @@ -103,8 +106,8 @@ mdcAutoInit.register = function(componentName: string, Constructor: ComponentCla
}
if (registry[componentName]) {
warn(
`(mdc-auto-init) Overriding registration for ${componentName} with ${Constructor}. ` +
`Was: ${registry[componentName]}`);
`(mdc-auto-init) Overriding registration for ${componentName} with ${Constructor}. ` +
`Was: ${registry[componentName]}`);
}
registry[componentName] = Constructor;
};
Expand Down
14 changes: 7 additions & 7 deletions packages/mdc-base/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import {MDCFoundation} from './foundation';
import {CustomEventListener, EventType, SpecificEventListener} from './types';

class MDCComponent<FoundationType extends MDCFoundation> {
export class MDCComponent<FoundationType extends MDCFoundation> {
static attachTo(root: Element): MDCComponent<MDCFoundation<{}>> {
// Subclasses which extend MDCBase should provide an attachTo() method that takes a root element and
// returns an instantiated component with its root set to that element. Also note that in the cases of
Expand All @@ -37,10 +37,10 @@ class MDCComponent<FoundationType extends MDCFoundation> {
protected foundation_: FoundationType;

constructor(
root: Element,
foundation?: FoundationType,
// tslint:disable-next-line:no-any a component can pass in anything it needs to the constructor
...args: any[]
root: Element,
foundation?: FoundationType,
// tslint:disable-next-line:no-any a component can pass in anything it needs to the constructor
...args: any[]
) {
this.root_ = root;
this.initialize(...args);
Expand All @@ -63,7 +63,7 @@ class MDCComponent<FoundationType extends MDCFoundation> {
// Subclasses must override this method to return a properly configured foundation class for the
// component.
throw new Error('Subclasses must override getDefaultFoundation to return a properly configured ' +
'foundation class');
'foundation class');
}

initialSyncWithDOM() {
Expand Down Expand Up @@ -118,4 +118,4 @@ class MDCComponent<FoundationType extends MDCFoundation> {
}
}

export {MDCComponent as default, MDCComponent};
export default MDCComponent;
4 changes: 4 additions & 0 deletions typings/dom.ie.d.ts → packages/mdc-base/externs.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@
declare interface Element {
msMatchesSelector?: (selector: string) => boolean;
}

declare interface Window {
CSS: CSS;
}
Loading

0 comments on commit 72e8b66

Please sign in to comment.