From c4b0fdf2840709772933d937b3eff87a9c59ae80 Mon Sep 17 00:00:00 2001
From: lynnjepsen
Date: Thu, 22 Jun 2017 12:53:31 -0700
Subject: [PATCH 01/16] refactor(button): Remove button's dependency on
typography, by specifying letter-spacing
---
packages/mdc-button/mdc-button.scss | 2 +-
packages/mdc-button/package.json | 3 +--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/packages/mdc-button/mdc-button.scss b/packages/mdc-button/mdc-button.scss
index 4ca6d36ef2b..aba6412a41a 100644
--- a/packages/mdc-button/mdc-button.scss
+++ b/packages/mdc-button/mdc-button.scss
@@ -27,7 +27,6 @@
@include mdc-ripple-base;
@include mdc-ripple-bg((pseudo: "::before"));
@include mdc-ripple-fg((pseudo: "::after"));
- @include mdc-typography(body2);
@include mdc-theme-prop(color, text-primary-on-light);
display: inline-block;
@@ -41,6 +40,7 @@
background: transparent;
font-size: 14px; // Override font to specifically be px as spec defined pt
font-weight: 500;
+ letter-spacing: .04em;
line-height: 36px; // Override line-height so text aligns centered
text-align: center;
text-decoration: none;
diff --git a/packages/mdc-button/package.json b/packages/mdc-button/package.json
index b9bad00184a..952a1af1863 100644
--- a/packages/mdc-button/package.json
+++ b/packages/mdc-button/package.json
@@ -16,7 +16,6 @@
"@material/animation": "^0.2.3",
"@material/elevation": "^0.1.8",
"@material/ripple": "^0.6.2",
- "@material/theme": "^0.1.5",
- "@material/typography": "^0.2.2"
+ "@material/theme": "^0.1.5"
}
}
From 548b4bc28f42ce4d8435134efeee87b8a0b3fd84 Mon Sep 17 00:00:00 2001
From: Susie Sahim
Date: Thu, 22 Jun 2017 15:47:53 -0700
Subject: [PATCH 02/16] feat(menu): annotate mdc-menu for closure compiler
Resolves #339 Added new annotations to mdc-menu. Corrected a couple of annotations in base.
---
package.json | 3 +-
packages/mdc-base/component.js | 4 +-
packages/mdc-menu/simple/adapter.js | 160 +++++++++++++++++++++++++
packages/mdc-menu/simple/constants.js | 4 +
packages/mdc-menu/simple/foundation.js | 151 +++++++++++++++++------
packages/mdc-menu/simple/index.js | 31 ++++-
packages/mdc-menu/util.js | 65 +++++++---
7 files changed, 356 insertions(+), 62 deletions(-)
create mode 100644 packages/mdc-menu/simple/adapter.js
diff --git a/package.json b/package.json
index 7c6b1e6f0f2..99a42ffd282 100644
--- a/package.json
+++ b/package.json
@@ -158,6 +158,7 @@
},
"closureWhitelist": [
"mdc-animation",
- "mdc-base"
+ "mdc-base",
+ "mdc-menu"
]
}
diff --git a/packages/mdc-base/component.js b/packages/mdc-base/component.js
index 46304ecc673..1c99603eaee 100644
--- a/packages/mdc-base/component.js
+++ b/packages/mdc-base/component.js
@@ -39,7 +39,7 @@ export default class MDCComponent {
* @param {...?} args
*/
constructor(root, foundation = undefined, ...args) {
- /** @private {!Element} */
+ /** @protected {!Element} */
this.root_ = root;
this.initialize(...args);
// Note that we initialize foundation here and not within the constructor's default param so that
@@ -104,7 +104,7 @@ export default class MDCComponent {
* with the given data.
* @param {string} evtType
* @param {!Object} evtData
- * @param {boolean} shouldBubble
+ * @param {boolean=} shouldBubble
*/
emit(evtType, evtData, shouldBubble = false) {
let evt;
diff --git a/packages/mdc-menu/simple/adapter.js b/packages/mdc-menu/simple/adapter.js
new file mode 100644
index 00000000000..e48c04f0798
--- /dev/null
+++ b/packages/mdc-menu/simple/adapter.js
@@ -0,0 +1,160 @@
+/**
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* eslint no-unused-vars: [2, {"args": "none"}] */
+
+/**
+ * Adapter for MDC Simple Menu. Provides an interface for managing
+ * - classes
+ * - dom
+ * - focus
+ * - position
+ * - dimensions
+ * - event handlers
+ *
+ * Additionally, provides type information for the adapter to the Closure
+ * compiler.
+ *
+ * Implement this adapter for your framework of choice to delegate updates to
+ * the component in your framework of choice. See architecture documentation
+ * for more details.
+ * https://github.com/material-components/material-components-web/blob/master/docs/architecture.md
+ *
+ * @record
+ */
+export default class MDCSimpleMenuAdapter {
+ /** @param {string} className */
+ addClass(className) {}
+
+ /** @param {string} className */
+ removeClass(className) {}
+
+ /**
+ * @param {string} className
+ * @return {boolean}
+ */
+ hasClass(className) {}
+
+ /** @return {boolean} */
+ hasNecessaryDom() {}
+
+ /**
+ * @param {EventTarget} target
+ * @param {string} attributeName
+ * @return {string}
+ */
+ getAttributeForEventTarget(target, attributeName) {}
+
+ /** @return {{ width: number, height: number }} */
+ getInnerDimensions() {}
+
+ /** @return {boolean} */
+ hasAnchor() {}
+
+ /** @return {{width: number, height: number, top: number, right: number, bottom: number, left: number}} */
+ getAnchorDimensions() {}
+
+ /** @return {{ width: number, height: number }} */
+ getWindowDimensions() {}
+
+ /**
+ * @param {number} x
+ * @param {number} y
+ */
+ setScale(x, y) {}
+
+ /**
+ * @param {number} x
+ * @param {number} y
+ */
+ setInnerScale(x, y) {}
+
+ /** @return {number} */
+ getNumberOfItems() {}
+
+ /**
+ * @param {string} type
+ * @param {function(!Event)} handler
+ */
+ registerInteractionHandler(type, handler) {}
+
+ /**
+ * @param {string} type
+ * @param {function(!Event)} handler
+ */
+ deregisterInteractionHandler(type, handler) {}
+
+ /** @param {function(!Event)} handler */
+ registerBodyClickHandler(handler) {}
+
+ /** @param {function(!Event)} handler */
+ deregisterBodyClickHandler(handler) {}
+
+ /**
+ * @param {number} index
+ * @return {{top: number, height: number}}
+ */
+ getYParamsForItemAtIndex(index) {}
+
+ /**
+ * @param {number} index
+ * @param {string|null} value
+ */
+ setTransitionDelayForItemAtIndex(index, value) {}
+
+ /**
+ * @param {EventTarget} target
+ * @return {number}
+ */
+ getIndexForEventTarget(target) {}
+
+ /** @param {{index: number}} evtData */
+ notifySelected(evtData) {}
+
+ notifyCancel() {}
+
+ saveFocus() {}
+
+ restoreFocus() {}
+
+ /** @return {boolean} */
+ isFocused() {}
+
+ focus() {}
+
+ /** @return {number} */
+ getFocusedItemIndex() /* number */ {}
+
+ /** @param {number} index */
+ focusItemAtIndex(index) {}
+
+ /** @return {boolean} */
+ isRtl() {}
+
+ /** @param {string} origin */
+ setTransformOrigin(origin) {}
+
+ /** @param {{
+ * top: (string|undefined),
+ * right: (string|undefined),
+ * bottom: (string|undefined),
+ * left: (string|undefined)
+ * }} position */
+ setPosition(position) {}
+
+ /** @return {number} */
+ getAccurateTime() {}
+}
diff --git a/packages/mdc-menu/simple/constants.js b/packages/mdc-menu/simple/constants.js
index 3cc7bf6788b..c2c7c667aff 100644
--- a/packages/mdc-menu/simple/constants.js
+++ b/packages/mdc-menu/simple/constants.js
@@ -13,6 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+/** @enum {string} */
export const cssClasses = {
ROOT: 'mdc-simple-menu',
OPEN: 'mdc-simple-menu--open',
@@ -22,6 +24,7 @@ export const cssClasses = {
BOTTOM_RIGHT: 'mdc-simple-menu--open-from-bottom-right',
};
+/** @enum {string} */
export const strings = {
ITEMS_SELECTOR: '.mdc-simple-menu__items',
SELECTED_EVENT: 'MDCSimpleMenu:selected',
@@ -29,6 +32,7 @@ export const strings = {
ARIA_DISABLED_ATTR: 'aria-disabled',
};
+/** @enum {number} */
export const numbers = {
// Amount of time to wait before triggering a selected event on the menu. Note that this time
// will most likely be bumped up once interactive lists are supported to allow for the ripple to
diff --git a/packages/mdc-menu/simple/foundation.js b/packages/mdc-menu/simple/foundation.js
index b58fc97c07d..479dd4d42c9 100644
--- a/packages/mdc-menu/simple/foundation.js
+++ b/packages/mdc-menu/simple/foundation.js
@@ -14,78 +14,110 @@
* limitations under the License.
*/
-import {MDCFoundation} from '@material/base';
+import MDCFoundation from '@material/base/foundation';
+import MDCSimpleMenuAdapter from './adapter';
import {cssClasses, strings, numbers} from './constants';
import {clamp, bezierProgress} from '../util';
+/**
+ * @extends {MDCFoundation}
+ */
export default class MDCSimpleMenuFoundation extends MDCFoundation {
+ /** @return enum{cssClasses} */
static get cssClasses() {
return cssClasses;
}
+ /** @return enum{strings} */
static get strings() {
return strings;
}
+ /** @return enum{numbers} */
static get numbers() {
return numbers;
}
+ /**
+ * {@see MDCSimpleMenuAdapter} for typing information on parameters and return
+ * types.
+ * @return {!MDCSimpleMenuAdapter}
+ */
static get defaultAdapter() {
- return {
- addClass: (/* className: string */) => {},
- removeClass: (/* className: string */) => {},
- hasClass: (/* className: string */) => {},
- hasNecessaryDom: () => /* boolean */ false,
- getAttributeForEventTarget: (/* target: EventTarget, attributeName: string */) => {},
- getInnerDimensions: () => /* { width: number, height: number } */ ({}),
- hasAnchor: () => /* boolean */ false,
- getAnchorDimensions: () =>
- /* { width: number, height: number, top: number, right: number, bottom: number, left: number } */ ({}),
- getWindowDimensions: () => /* { width: number, height: number } */ ({}),
- setScale: (/* x: number, y: number */) => {},
- setInnerScale: (/* x: number, y: number */) => {},
- getNumberOfItems: () => /* number */ 0,
- registerInteractionHandler: (/* type: string, handler: EventListener */) => {},
- deregisterInteractionHandler: (/* type: string, handler: EventListener */) => {},
- registerBodyClickHandler: (/* handler: EventListener */) => {},
- deregisterBodyClickHandler: (/* handler: EventListener */) => {},
- getYParamsForItemAtIndex: (/* index: number */) => /* {top: number, height: number} */ ({}),
- setTransitionDelayForItemAtIndex: (/* index: number, value: string */) => {},
- getIndexForEventTarget: (/* target: EventTarget */) => /* number */ 0,
- notifySelected: (/* evtData: {index: number} */) => {},
+ return /** @type {!MDCSimpleMenuAdapter} */ ({
+ addClass: () => {},
+ removeClass: () => {},
+ hasClass: () => false,
+ hasNecessaryDom: () => false,
+ getAttributeForEventTarget: () => {},
+ getInnerDimensions: () => ({}),
+ hasAnchor: () => false,
+ getAnchorDimensions: () => ({}),
+ getWindowDimensions: () => ({}),
+ setScale: () => {},
+ setInnerScale: () => {},
+ getNumberOfItems: () => 0,
+ registerInteractionHandler: () => {},
+ deregisterInteractionHandler: () => {},
+ registerBodyClickHandler: () => {},
+ deregisterBodyClickHandler: () => {},
+ getYParamsForItemAtIndex: () => ({}),
+ setTransitionDelayForItemAtIndex: () => {},
+ getIndexForEventTarget: () => 0,
+ notifySelected: () => {},
notifyCancel: () => {},
saveFocus: () => {},
restoreFocus: () => {},
- isFocused: () => /* boolean */ false,
+ isFocused: () => false,
focus: () => {},
- getFocusedItemIndex: () => /* number */ -1,
- focusItemAtIndex: (/* index: number */) => {},
- isRtl: () => /* boolean */ false,
- setTransformOrigin: (/* origin: string */) => {},
- setPosition: (/* position: { top: string, right: string, bottom: string, left: string } */) => {},
- getAccurateTime: () => /* number */ 0,
- };
+ getFocusedItemIndex: () => -1,
+ focusItemAtIndex: () => {},
+ isRtl: () => false,
+ setTransformOrigin: () => {},
+ setPosition: () => {},
+ getAccurateTime: () => 0,
+ });
}
+ /** @param {!MDCSimpleMenuAdapter} adapter */
constructor(adapter) {
super(Object.assign(MDCSimpleMenuFoundation.defaultAdapter, adapter));
+
+ /** @private {function(!Event)} */
this.clickHandler_ = (evt) => this.handlePossibleSelected_(evt);
+ /** @private {function(!Event)} */
this.keydownHandler_ = (evt) => this.handleKeyboardDown_(evt);
+ /** @private {function(!Event)} */
this.keyupHandler_ = (evt) => this.handleKeyboardUp_(evt);
+ /** @private {function(!Event)} */
this.documentClickHandler_ = (evt) => {
this.adapter_.notifyCancel();
this.close(evt);
};
+ /** @private {boolean} */
this.isOpen_ = false;
+ /** @private {number} */
this.startScaleX_ = 0;
+ /** @private {number} */
this.startScaleY_ = 0;
+ /** @private {number} */
this.targetScale_ = 1;
+ /** @private {number} */
this.scaleX_ = 0;
+ /** @private {number} */
this.scaleY_ = 0;
+ /** @private {boolean} */
this.running_ = false;
+ /** @private {number} */
this.selectedTriggerTimerId_ = 0;
+ /** @private {number} */
this.animationRequestId_ = 0;
+ /** @private {!{ width: number, height: number }} */
+ this.dimensions_;
+ /** @private {number} */
+ this.startTime_;
+ /** @private {number} */
+ this.itemHeight_;
}
init() {
@@ -118,7 +150,10 @@ export default class MDCSimpleMenuFoundation extends MDCFoundation {
this.adapter_.deregisterBodyClickHandler(this.documentClickHandler_);
}
- // Calculate transition delays for individual menu items, so that they fade in one at a time.
+ /**
+ * Calculates transition delays for individual menu items, so that they fade in one at a time.
+ * @private
+ */
applyTransitionDelays_() {
const {BOTTOM_LEFT, BOTTOM_RIGHT} = MDCSimpleMenuFoundation.cssClasses;
const numItems = this.adapter_.getNumberOfItems();
@@ -139,7 +174,10 @@ export default class MDCSimpleMenuFoundation extends MDCFoundation {
}
}
- // Remove transition delays from menu items.
+ /**
+ * Removes transition delays from menu items.
+ * @private
+ */
removeTransitionDelays_() {
const numItems = this.adapter_.getNumberOfItems();
for (let i = 0; i < numItems; i++) {
@@ -147,7 +185,10 @@ export default class MDCSimpleMenuFoundation extends MDCFoundation {
}
}
- // Animate menu opening or closing.
+ /**
+ * Animates menu opening or closing.
+ * @private
+ */
animationLoop_() {
const time = this.adapter_.getAccurateTime();
const {TRANSITION_DURATION_MS, TRANSITION_X1, TRANSITION_Y1, TRANSITION_X2, TRANSITION_Y2,
@@ -199,7 +240,10 @@ export default class MDCSimpleMenuFoundation extends MDCFoundation {
}
}
- // Starts the open or close animation.
+ /**
+ * Starts the open or close animation.
+ * @private
+ */
animateMenu_() {
this.startTime_ = this.adapter_.getAccurateTime();
this.startScaleX_ = this.scaleX_;
@@ -213,6 +257,10 @@ export default class MDCSimpleMenuFoundation extends MDCFoundation {
}
}
+ /**
+ * @param {?number} focusIndex
+ * @private
+ */
focusOnOpen_(focusIndex) {
if (focusIndex === null) {
// First, try focusing the menu.
@@ -226,7 +274,12 @@ export default class MDCSimpleMenuFoundation extends MDCFoundation {
}
}
- // Handle keys that we want to repeat on hold (tab and arrows).
+ /**
+ * Handle keys that we want to repeat on hold (tab and arrows).
+ * @param {!Event} evt
+ * @return {boolean}
+ * @private
+ */
handleKeyboardDown_(evt) {
// Do nothing if Alt, Ctrl or Meta are pressed.
if (evt.altKey || evt.ctrlKey || evt.metaKey) {
@@ -276,7 +329,12 @@ export default class MDCSimpleMenuFoundation extends MDCFoundation {
return true;
}
- // Handle keys that we don't want to repeat on hold (Enter, Space, Escape).
+ /**
+ * Handle keys that we don't want to repeat on hold (Enter, Space, Escape).
+ * @param {!Event} evt
+ * @return {boolean}
+ * @private
+ */
handleKeyboardUp_(evt) {
// Do nothing if Alt, Ctrl or Meta are pressed.
if (evt.altKey || evt.ctrlKey || evt.metaKey) {
@@ -300,6 +358,10 @@ export default class MDCSimpleMenuFoundation extends MDCFoundation {
return true;
}
+ /**
+ * @param {!Event} evt
+ * @private
+ */
handlePossibleSelected_(evt) {
if (this.adapter_.getAttributeForEventTarget(evt.target, strings.ARIA_DISABLED_ATTR) === 'true') {
return;
@@ -319,6 +381,7 @@ export default class MDCSimpleMenuFoundation extends MDCFoundation {
}, numbers.SELECTED_TRIGGER_DELAY);
}
+ /** @private */
autoPosition_() {
if (!this.adapter_.hasAnchor()) {
return;
@@ -365,7 +428,11 @@ export default class MDCSimpleMenuFoundation extends MDCFoundation {
this.adapter_.setPosition(position);
}
- // Open the menu.
+
+ /**
+ * Open the menu.
+ * @param {{focusIndex: ?number}=} options
+ */
open({focusIndex = null} = {}) {
this.adapter_.saveFocus();
this.adapter_.addClass(MDCSimpleMenuFoundation.cssClasses.ANIMATING);
@@ -381,7 +448,10 @@ export default class MDCSimpleMenuFoundation extends MDCFoundation {
this.isOpen_ = true;
}
- // Close the menu.
+ /**
+ * Closes the menu.
+ * @param {Event=} evt
+ */
close(evt = null) {
const targetIsDisabled = evt ?
this.adapter_.getAttributeForEventTarget(evt.target, strings.ARIA_DISABLED_ATTR) === 'true' :
@@ -402,7 +472,10 @@ export default class MDCSimpleMenuFoundation extends MDCFoundation {
this.adapter_.restoreFocus();
}
+ /** @return {boolean} */
isOpen() {
return this.isOpen_;
}
}
+
+
diff --git a/packages/mdc-menu/simple/index.js b/packages/mdc-menu/simple/index.js
index b3b1259be57..c3d3af30063 100644
--- a/packages/mdc-menu/simple/index.js
+++ b/packages/mdc-menu/simple/index.js
@@ -14,21 +14,37 @@
* limitations under the License.
*/
-import {MDCComponent} from '@material/base';
+import MDCComponent from '@material/base/component';
import MDCSimpleMenuFoundation from './foundation';
import {getTransformPropertyName} from '../util';
export {MDCSimpleMenuFoundation};
+/**
+ * @extends MDCComponent
+ */
export class MDCSimpleMenu extends MDCComponent {
+ /** @param {...?} args */
+ constructor(...args) {
+ super(...args);
+ /** @private {!Element} */
+ this.previousFocus_;
+ }
+
+ /**
+ * @param {!Element} root
+ * @return {!MDCSimpleMenu}
+ */
static attachTo(root) {
return new MDCSimpleMenu(root);
}
+ /** @return {boolean} */
get open() {
return this.foundation_.isOpen();
}
+ /** @param {boolean} value */
set open(value) {
if (value) {
this.foundation_.open();
@@ -37,6 +53,7 @@ export class MDCSimpleMenu extends MDCComponent {
}
}
+ /** @param {{focusIndex: ?number}=} options */
show({focusIndex = null} = {}) {
this.foundation_.open({focusIndex: focusIndex});
}
@@ -45,20 +62,26 @@ export class MDCSimpleMenu extends MDCComponent {
this.foundation_.close();
}
- /* Return the item container element inside the component. */
+ /**
+ * Return the item container element inside the component.
+ * @return {?Element}
+ */
get itemsContainer_() {
return this.root_.querySelector(MDCSimpleMenuFoundation.strings.ITEMS_SELECTOR);
}
- /* Return the items within the menu. Note that this only contains the set of elements within
+ /**
+ * Return the items within the menu. Note that this only contains the set of elements within
* the items container that are proper list items, and not supplemental / presentational DOM
* elements.
+ * @return {!Array}
*/
get items() {
const {itemsContainer_: itemsContainer} = this;
return [].slice.call(itemsContainer.querySelectorAll('.mdc-list-item[role]'));
}
+ /** @return {!MDCSimpleMenuFoundation} */
getDefaultFoundation() {
return new MDCSimpleMenuFoundation({
addClass: (className) => this.root_.classList.add(className),
@@ -97,7 +120,7 @@ export class MDCSimpleMenu extends MDCComponent {
index: evtData.index,
item: this.items[evtData.index],
}),
- notifyCancel: () => this.emit(MDCSimpleMenuFoundation.strings.CANCEL_EVENT),
+ notifyCancel: () => this.emit(MDCSimpleMenuFoundation.strings.CANCEL_EVENT, {}),
saveFocus: () => {
this.previousFocus_ = document.activeElement;
},
diff --git a/packages/mdc-menu/util.js b/packages/mdc-menu/util.js
index fbe944c6c7b..32d3372410f 100644
--- a/packages/mdc-menu/util.js
+++ b/packages/mdc-menu/util.js
@@ -14,9 +14,15 @@
* limitations under the License.
*/
+/** @type {string|undefined} */
let storedTransformPropertyName_;
-// Returns the name of the correct transform property to use on the current browser.
+/**
+ * Returns the name of the correct transform property to use on the current browser.
+ * @param {!Window} globalObj
+ * @param {boolean=} forceRefresh
+ * @return {string}
+ */
export function getTransformPropertyName(globalObj, forceRefresh = false) {
if (storedTransformPropertyName_ === undefined || forceRefresh) {
const el = globalObj.document.createElement('div');
@@ -27,27 +33,48 @@ export function getTransformPropertyName(globalObj, forceRefresh = false) {
return storedTransformPropertyName_;
}
-// Clamps a value between the minimum and the maximum, returning the clamped value.
+/**
+ * Clamps a value between the minimum and the maximum, returning the clamped value.
+ * @param {number} value
+ * @param {number} min
+ * @param {number} max
+ * @return {number}
+ */
export function clamp(value, min = 0, max = 1) {
return Math.min(max, Math.max(min, value));
}
-// Returns the easing value to apply at time t, for a given cubic bezier curve.
-// Control points P0 and P3 are assumed to be (0,0) and (1,1), respectively.
-// Paramters are as follows:
-// - time: The current time in the animation, scaled between 0 and 1.
-// - x1: The x value of control point P1.
-// - y1: The y value of control point P1.
-// - x2: The x value of control point P2.
-// - y2: The y value of control point P2.
+
+/**
+ * Returns the easing value to apply at time t, for a given cubic bezier curve.
+ * Control points P0 and P3 are assumed to be (0,0) and (1,1), respectively.
+ * Parameters are as follows:
+ * - time: The current time in the animation, scaled between 0 and 1.
+ * - x1: The x value of control point P1.
+ * - y1: The y value of control point P1.
+ * - x2: The x value of control point P2.
+ * - y2: The y value of control point P2.
+ * @param {number} time
+ * @param {number} x1
+ * @param {number} y1
+ * @param {number} x2
+ * @param {number} y2
+ * @return {number}
+ */
export function bezierProgress(time, x1, y1, x2, y2) {
return getBezierCoordinate_(solvePositionFromXValue_(time, x1, x2), y1, y2);
}
-// Compute a single coordinate at a position point between 0 and 1.
-// c1 and c2 are the matching coordinate on control points P1 and P2, respectively.
-// Control points P0 and P3 are assumed to be (0,0) and (1,1), respectively.
-// Adapted from https://github.com/google/closure-library/blob/master/closure/goog/math/bezier.js.
+/**
+ * Compute a single coordinate at a position point between 0 and 1.
+ * c1 and c2 are the matching coordinate on control points P1 and P2, respectively.
+ * Control points P0 and P3 are assumed to be (0,0) and (1,1), respectively.
+ * Adapted from https://github.com/google/closure-library/blob/master/closure/goog/math/bezier.js.
+ * @param {number} t
+ * @param {number} c1
+ * @param {number} c2
+ * @return {number}
+ */
function getBezierCoordinate_(t, c1, c2) {
// Special case start and end.
if (t === 0 || t === 1) {
@@ -67,8 +94,14 @@ function getBezierCoordinate_(t, c1, c2) {
return ic0 + t * (ic1 - ic0);
}
-// Project a point onto the Bezier curve, from a given X. Calculates the position t along the curve.
-// Adapted from https://github.com/google/closure-library/blob/master/closure/goog/math/bezier.js.
+/**
+ * Project a point onto the Bezier curve, from a given X. Calculates the position t along the curve.
+ * Adapted from https://github.com/google/closure-library/blob/master/closure/goog/math/bezier.js.
+ * @param {number} xVal
+ * @param {number} x1
+ * @param {number} x2
+ * @return {number}
+ */
function solvePositionFromXValue_(xVal, x1, x2) {
const EPSILON = 1e-6;
const MAX_ITERATIONS = 8;
From d570a360ffefc83a68560261ff0cf5e244c99e38 Mon Sep 17 00:00:00 2001
From: Lynn Jepsen
Date: Thu, 22 Jun 2017 15:58:52 -0700
Subject: [PATCH 03/16] feat(infrastructure): Create script for that rewrites
.scss imports (#831)
---
package.json | 2 +
...rite-sass-import-statements-for-closure.js | 89 +++++++++++++++++++
scripts/sass-closure-rewriter.sh | 45 ++++++++++
3 files changed, 136 insertions(+)
create mode 100644 scripts/rewrite-sass-import-statements-for-closure.js
create mode 100755 scripts/sass-closure-rewriter.sh
diff --git a/package.json b/package.json
index 99a42ffd282..eae5eacff0f 100644
--- a/package.json
+++ b/package.json
@@ -75,6 +75,7 @@
"recast": "^0.12.3",
"resolve": "^1.3.2",
"sass-loader": "^6.0.4",
+ "scss-parser": "^1.0.0",
"semver": "^5.3.0",
"standard-changelog": "0.0.1",
"style-loader": "^0.18.0",
@@ -86,6 +87,7 @@
"stylelint-selector-bem-pattern": "^1.0.0",
"testdouble": "3.0.0",
"to-slug-case": "^1.0.0",
+ "query-ast": "^1.0.1",
"validate-commit-msg": "^2.6.1",
"webpack": "^2.2.1",
"webpack-dev-server": "^2.4.3"
diff --git a/scripts/rewrite-sass-import-statements-for-closure.js b/scripts/rewrite-sass-import-statements-for-closure.js
new file mode 100644
index 00000000000..d9d32ff9403
--- /dev/null
+++ b/scripts/rewrite-sass-import-statements-for-closure.js
@@ -0,0 +1,89 @@
+/**
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview Rewrites import statements such that:
+ *
+ * ```js
+ * import [ from] '@material/$PKG[/files...]';
+ * ```
+ * becomes
+ * ```js
+ * import [ from] 'mdc-$PKG/';
+ * ```
+ * The RESOLVED_FILE_PATH is the file that node's module resolution algorithm would have resolved the import
+ * source to.
+ */
+
+const fs = require('fs');
+const path = require('path');
+
+const {parse, stringify} = require('scss-parser');
+const createQueryWrapper = require('query-ast');
+const glob = require('glob');
+
+main(process.argv);
+
+function main(argv) {
+ if (argv.length < 3) {
+ console.error('Missing root directory path');
+ process.exit(1);
+ }
+
+ const rootDir = path.resolve(process.argv[2]);
+ const srcFiles = glob.sync(`${rootDir}/**/*.scss`);
+ srcFiles.forEach((srcFile) => transform(srcFile, rootDir));
+}
+
+function transform(srcFile, rootDir) {
+ const src = fs.readFileSync(srcFile, 'utf8');
+ const ast = parse(src);
+
+ const $ = createQueryWrapper(ast);
+ $('atrule').has('atkeyword').find('string_double').replace((n) => {
+ if (n.parent.children[0].node.value === 'import') {
+ return {
+ type: 'string_double',
+ value: rewriteImportDeclaration(n.node.value, srcFile, rootDir),
+ };
+ }
+ return n.node;
+ });
+
+ const scss = stringify($().get(0));
+
+ fs.writeFileSync(srcFile, scss, 'utf8');
+ console.log(`[rewrite] ${srcFile}`);
+}
+
+function rewriteImportDeclaration(importSource, srcFile, rootDir) {
+ const pathParts = importSource.split('/');
+ const isMDCImport = pathParts[0] === '@material';
+ if (isMDCImport) {
+ const modName = pathParts[1]; // @material/
+ const atMaterialReplacementPath = `${rootDir}/${modName}`;
+ const rewrittenImportSource = [atMaterialReplacementPath].concat(pathParts.slice(2)).join('/');
+ importSource = rewrittenImportSource;
+ }
+
+ let resolvedImportSource = importSource;
+ const needsClosureModuleRootResolution = path.isAbsolute(importSource);
+ if (needsClosureModuleRootResolution) {
+ const pathToImport = importSource.replace('@material', rootDir);
+ resolvedImportSource = path.relative(path.dirname(srcFile), pathToImport);
+ }
+ return resolvedImportSource;
+}
diff --git a/scripts/sass-closure-rewriter.sh b/scripts/sass-closure-rewriter.sh
new file mode 100755
index 00000000000..99786d5aa6c
--- /dev/null
+++ b/scripts/sass-closure-rewriter.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+# Rewrites our .scss files to be compatible with closure-stylesheets
+# in our internal Blaze infrastructure.
+
+##
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+set -e
+
+function log() {
+ echo -e "\033[36m[closure-rewriter]\033[0m" "$@"
+}
+
+CLOSURE_TMP=.closure-tmp
+CLOSURE_PKGDIR=$CLOSURE_TMP/packages
+
+log "Prepping packages for rewrite"
+
+rm -fr $CLOSURE_TMP/**
+mkdir -p $CLOSURE_PKGDIR
+PACKAGE_NAMES=$(ls packages)
+for pkg in $PACKAGE_NAMES ; do
+ if [[ $pkg != *"mdc-"* ]]; then
+ continue
+ fi
+ cp -r "packages/$pkg" $CLOSURE_PKGDIR
+done
+rm -fr $CLOSURE_PKGDIR/**/{node_modules,dist}
+
+log "Rewriting all import statements to be closure compatible"
+node scripts/rewrite-sass-import-statements-for-closure.js $CLOSURE_PKGDIR
From 86160de9cec153dee91c1193a5548f245fc471ec Mon Sep 17 00:00:00 2001
From: "Kenneth G. Franqueiro"
Date: Fri, 23 Jun 2017 11:47:53 -0400
Subject: [PATCH 04/16] docs: Fix various typos (#854)
---
packages/mdc-base/README.md | 2 +-
packages/mdc-checkbox/README.md | 2 +-
packages/mdc-list/README.md | 4 ++--
packages/mdc-ripple/README.md | 3 +--
packages/mdc-rtl/README.md | 2 +-
packages/mdc-textfield/README.md | 4 ++--
packages/mdc-toolbar/README.md | 4 ++--
7 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/packages/mdc-base/README.md b/packages/mdc-base/README.md
index 2a00d39850f..ceda825b43e 100644
--- a/packages/mdc-base/README.md
+++ b/packages/mdc-base/README.md
@@ -56,7 +56,7 @@ mdc-base exposes two classes: `MDCComponent` (the default export) which all comp
### MDCFoundation
-MDCFoundation provides the basic mechanisms for implementing a foundation classes. Subclasses are expected to:
+MDCFoundation provides the basic mechanisms for implementing foundation classes. Subclasses are expected to:
- Provide implementations of the proper static getters where necessary.
- Provide `init()` and `destroy()` lifecycle methods
diff --git a/packages/mdc-checkbox/README.md b/packages/mdc-checkbox/README.md
index f2569c2cf5a..cea1d90af6c 100644
--- a/packages/mdc-checkbox/README.md
+++ b/packages/mdc-checkbox/README.md
@@ -210,7 +210,7 @@ The adapter for checkboxes must provide the following functions, with correct si
| `deregisterChangeHandler(handler: EventListener) => void` | Deregisters an event handler that was previously passed to `registerChangeHandler`. |
| `getNativeControl() => HTMLInputElement?` | Returns the native checkbox control, if available. Note that if this control is not available, the methods that rely on it will exit gracefully.|
| `forceLayout() => void` | Force-trigger a layout on the root element. This is needed to restart animations correctly. If you find that you do not need to do this, you can simply make it a no-op. |
-| `isAttachedToDOM() => boolean` | Returns true if the component is currently attached to the DOM, false otherwise.` |
+| `isAttachedToDOM() => boolean` | Returns true if the component is currently attached to the DOM, false otherwise. |
#### MDCCheckboxFoundation API
diff --git a/packages/mdc-list/README.md b/packages/mdc-list/README.md
index f7b5182e26c..1c57da2fc04 100644
--- a/packages/mdc-list/README.md
+++ b/packages/mdc-list/README.md
@@ -18,7 +18,7 @@ path: /catalog/lists/
MDC List provides styles which implement [Material Design Lists](https://material.io/guidelines/components/lists.html) - "A single continuous column of tessellated subdivisions of equal width." Both single-line and two-line lists are supported (with
three-line lists [coming soon](https://github.com/material-components/material-components-web/issues/31)). MDC
-Lists are design to be accessible and RTL aware.
+Lists are designed to be accessible and RTL aware.
## Design & API Documentation
@@ -41,7 +41,7 @@ npm install --save @material/list
### Basic Lists
-A basic lists consists simply of the list itself, and list items taking up one line.
+A basic list consists simply of the list itself, and list items taking up one line.
```html
diff --git a/packages/mdc-ripple/README.md b/packages/mdc-ripple/README.md
index 2a9fee5f4f0..0f5e66512bf 100644
--- a/packages/mdc-ripple/README.md
+++ b/packages/mdc-ripple/README.md
@@ -112,8 +112,7 @@ argument, with which you can specify the following parameters:
| Parameter | Description | Default |
| --- | --- | --- |
-| `pseudo` | The name of the pseudo-element you want to use to style the ripple. Using pseudo-elements to style ripples obviates the need for any extra DOM and is recommended. However,
-if given `null` it will style the element directly, rather than attaching styles to the pseudo element. | `null` |
+| `pseudo` | The name of the pseudo-element you want to use to style the ripple. Using pseudo-elements to style ripples obviates the need for any extra DOM and is recommended. However, if given `null` it will style the element directly, rather than attaching styles to the pseudo element. | `null` |
| `radius` | For _bounded_ ripples, specifies radii of the ripple circles. Can be any valid numeric CSS unit. | `100%` |
| `theme-style` | When provided, will use a style specified by `mdc-theme` to provide colors to the ripple. For example, passing `(theme-style: primary)` would make the ripples the color of the theme's primary color. Note that there are some current limitations here. See [below](#caveat-theme-custom-variables) | `null` |
| `base-color` | The RGB color (_without_ an alpha component) of the ripple. This will only be used if `theme-style` isn't specified. | `black` |
diff --git a/packages/mdc-rtl/README.md b/packages/mdc-rtl/README.md
index 0f1aad84b14..6df7e33a141 100644
--- a/packages/mdc-rtl/README.md
+++ b/packages/mdc-rtl/README.md
@@ -96,7 +96,7 @@ will emit the following css:
padding-right: 4px;
}
```
-*N.B.**: checking for `[dir="rtl"]` on an ancestor element works in most cases, it will sometimes
+**N.B.**: checking for `[dir="rtl"]` on an ancestor element works in most cases, it will sometimes
lead to false negatives for more complex layouts, e.g.
```html
diff --git a/packages/mdc-textfield/README.md b/packages/mdc-textfield/README.md
index 324e0eaadf3..aa4b49a34f8 100644
--- a/packages/mdc-textfield/README.md
+++ b/packages/mdc-textfield/README.md
@@ -110,7 +110,7 @@ information to users, as well for validation messages (covered below).
```
-Help text appears on input field focus and disappear on input field blur by default when using
+Help text appears on input field focus and disappears on input field blur by default when using
the textfield JS component.
#### Persistent help text
@@ -331,7 +331,7 @@ initializes when given an `mdc-textfield--box` root element. Otherwise, the fiel
### Using the foundation class
-Because MDC Textfield is a feature-rich and relatively complex component, it's adapter is a bit more
+Because MDC Textfield is a feature-rich and relatively complex component, its adapter is a bit more
complicated.
| Method Signature | Description |
diff --git a/packages/mdc-toolbar/README.md b/packages/mdc-toolbar/README.md
index 9bf713fb499..c531d496765 100644
--- a/packages/mdc-toolbar/README.md
+++ b/packages/mdc-toolbar/README.md
@@ -18,7 +18,7 @@ path: /catalog/toolbar/
MDC Toolbar acts as a container for multiple rows containing items such as
application title, navigation menu, and tabs, among other things. Toolbars
-scroll with content by default, but supports fixed behavior as well.
+scroll with content by default, but support fixed behavior as well.
When using the **fixed** pattern, a persistent elevation is added to toolbar.
When using the **waterfall** pattern, a toolbar will have no elevation when the
@@ -52,7 +52,7 @@ npm install --save @material/toolbar
## Usage
-Wrap the items with `mdc-toolbar` class in following way:
+Wrap the items with `mdc-toolbar` class in the following way:
```html