diff --git a/CHANGELOG.md b/CHANGELOG.md
index 53b8f0dc7fb..0319b90bbc9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,28 @@
+
+# [0.14.0](https://github.com/material-components/material-components-web/compare/v0.13.0...v0.14.0) (2017-06-26)
+
+
+### Bug Fixes
+
+* **base:** Fix compiler warnings (#788) ([56d8fff](https://github.com/material-components/material-components-web/commit/56d8fff)), closes [(#788](https://github.com/(/issues/788)
+* **button:** Sets text on raised buttons on dark theme to text-primary-on-primary (#853) ([49170d6](https://github.com/material-components/material-components-web/commit/49170d6))
+* **infrastructure:** Harden closure declaration source rewriting (#835) ([7c6da3a](https://github.com/material-components/material-components-web/commit/7c6da3a))
+* **ripple:** Remove fg deactivation class when animation finishes ([4985b4b](https://github.com/material-components/material-components-web/commit/4985b4b))
+* **toolbar:** Adjusting sibling elements on mobile landscape (#846) ([798091f](https://github.com/material-components/material-components-web/commit/798091f))
+
+### Features
+
+* **infrastructure:** Create script for that rewrites .scss imports (#831) ([bd0123b](https://github.com/material-components/material-components-web/commit/bd0123b))
+* **layout-grid:** Add fixed column width layout grid modifier. (#816) ([94d62ad](https://github.com/material-components/material-components-web/commit/94d62ad)), closes [(#816](https://github.com/(/issues/816) [#748](https://github.com/material-components/material-components-web/issues/748)
+* **menu:** annotate mdc-menu for closure compiler ([b188d4f](https://github.com/material-components/material-components-web/commit/b188d4f)), closes [#339](https://github.com/material-components/material-components-web/issues/339)
+* **menu:** Export util (#824) ([7d0394b](https://github.com/material-components/material-components-web/commit/7d0394b)), closes [#823](https://github.com/material-components/material-components-web/issues/823)
+* **ripple:** Add layout() method to component ([ef99024](https://github.com/material-components/material-components-web/commit/ef99024))
+* **ripple:** export util from @material/ripple (#751) ([27c172a](https://github.com/material-components/material-components-web/commit/27c172a)), closes [#253](https://github.com/material-components/material-components-web/issues/253)
+* **ripple:** Reduce the fade out time for foreground ripple effect ([9394b5f](https://github.com/material-components/material-components-web/commit/9394b5f))
+* **textfield:** Implement text field boxes ([cfa3737](https://github.com/material-components/material-components-web/commit/cfa3737)), closes [#673](https://github.com/material-components/material-components-web/issues/673)
+
+
+
# [0.13.0](https://github.com/material-components/material-components-web/compare/v0.12.0...v0.13.0) (2017-06-12)
diff --git a/package.json b/package.json
index 7c6b1e6f0f2..5f9499c4894 100644
--- a/package.json
+++ b/package.json
@@ -51,7 +51,7 @@
"extract-text-webpack-plugin": "^2.1.2",
"glob": "^7.1.1",
"google-closure-compiler": "^20170521.0.0",
- "husky": "^0.13.1",
+ "husky": "^0.14.0",
"istanbul": "^0.4.4",
"istanbul-instrumenter-loader": "^2.0.0",
"json-loader": "^0.5.4",
@@ -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"
@@ -158,6 +160,8 @@
},
"closureWhitelist": [
"mdc-animation",
- "mdc-base"
+ "mdc-base",
+ "mdc-menu",
+ "mdc-ripple"
]
}
diff --git a/packages/material-components-web/package.json b/packages/material-components-web/package.json
index 30949e9dd1b..02ef526d5c9 100644
--- a/packages/material-components-web/package.json
+++ b/packages/material-components-web/package.json
@@ -1,7 +1,7 @@
{
"name": "material-components-web",
"description": "Modular and customizable Material Design UI components for the web",
- "version": "0.13.0",
+ "version": "0.14.0",
"license": "Apache-2.0",
"keywords": [
"material components",
@@ -15,31 +15,31 @@
"dependencies": {
"@material/animation": "^0.2.3",
"@material/auto-init": "^0.1.2",
- "@material/base": "^0.2.0",
- "@material/button": "^0.3.7",
- "@material/card": "^0.2.3",
- "@material/checkbox": "^0.3.6",
- "@material/dialog": "^0.3.1",
- "@material/drawer": "^0.5.0",
- "@material/elevation": "^0.1.8",
- "@material/fab": "^0.3.9",
- "@material/form-field": "^0.2.7",
- "@material/grid-list": "^0.2.4",
- "@material/icon-toggle": "^0.1.12",
- "@material/layout-grid": "^0.2.0",
- "@material/linear-progress": "^0.1.2",
- "@material/list": "^0.2.10",
- "@material/menu": "^0.3.0",
- "@material/radio": "^0.2.5",
- "@material/ripple": "^0.6.2",
- "@material/select": "^0.3.8",
- "@material/slider": "^0.1.0",
- "@material/snackbar": "^0.2.1",
- "@material/switch": "^0.1.8",
- "@material/tabs": "^0.2.1",
- "@material/textfield": "^0.2.11",
+ "@material/base": "^0.2.1",
+ "@material/button": "^0.3.8",
+ "@material/card": "^0.2.4",
+ "@material/checkbox": "^0.3.7",
+ "@material/dialog": "^0.3.2",
+ "@material/drawer": "^0.5.1",
+ "@material/elevation": "^0.1.9",
+ "@material/fab": "^0.3.10",
+ "@material/form-field": "^0.2.8",
+ "@material/grid-list": "^0.2.5",
+ "@material/icon-toggle": "^0.1.13",
+ "@material/layout-grid": "^0.3.0",
+ "@material/linear-progress": "^0.1.3",
+ "@material/list": "^0.2.11",
+ "@material/menu": "^0.4.0",
+ "@material/radio": "^0.2.6",
+ "@material/ripple": "^0.7.0",
+ "@material/select": "^0.3.9",
+ "@material/slider": "^0.1.1",
+ "@material/snackbar": "^0.2.2",
+ "@material/switch": "^0.1.9",
+ "@material/tabs": "^0.2.2",
+ "@material/textfield": "^0.3.0",
"@material/theme": "^0.1.5",
- "@material/toolbar": "^0.4.0",
+ "@material/toolbar": "^0.4.1",
"@material/typography": "^0.2.2"
}
}
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-base/component.js b/packages/mdc-base/component.js
index 46304ecc673..6ff445cd36a 100644
--- a/packages/mdc-base/component.js
+++ b/packages/mdc-base/component.js
@@ -35,16 +35,16 @@ export default class MDCComponent {
/**
* @param {!Element} root
- * @param {!F} foundation
+ * @param {F=} foundation
* @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
// this.root_ is defined and can be used within the foundation class.
- /** @private {!F} */
+ /** @protected {!F} */
this.foundation_ = foundation === undefined ? this.getDefaultFoundation() : foundation;
this.foundation_.init();
this.initialSyncWithDOM();
@@ -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-base/foundation.js b/packages/mdc-base/foundation.js
index eb78a1f2d75..303821ca0b8 100644
--- a/packages/mdc-base/foundation.js
+++ b/packages/mdc-base/foundation.js
@@ -49,10 +49,10 @@ export default class MDCFoundation {
}
/**
- * @param {!A} adapter
+ * @param {A=} adapter
*/
constructor(adapter = {}) {
- /** @private {!A} */
+ /** @protected {!A} */
this.adapter_ = adapter;
}
diff --git a/packages/mdc-base/package.json b/packages/mdc-base/package.json
index bf2bdd4470e..ff0db158295 100644
--- a/packages/mdc-base/package.json
+++ b/packages/mdc-base/package.json
@@ -1,7 +1,7 @@
{
"name": "@material/base",
"description": "The set of base classes for Material Components for the web",
- "version": "0.2.0",
+ "version": "0.2.1",
"license": "Apache-2.0",
"main": "index.js",
"repository": {
diff --git a/packages/mdc-button/mdc-button.scss b/packages/mdc-button/mdc-button.scss
index 4ca6d36ef2b..8307edf6cb7 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;
@@ -39,9 +38,10 @@
border-radius: 2px;
outline: none;
background: transparent;
- font-size: 14px; // Override font to specifically be px as spec defined pt
+ font-size: 14px;
font-weight: 500;
- line-height: 36px; // Override line-height so text aligns centered
+ letter-spacing: .04em;
+ line-height: 36px;
text-align: center;
text-decoration: none;
text-transform: uppercase;
@@ -115,6 +115,7 @@
@include mdc-theme-dark(".mdc-button") {
@include mdc-theme-prop(background-color, primary);
+ @include mdc-theme-prop(color, text-primary-on-primary);
// postcss-bem-linter: ignore
&::before {
diff --git a/packages/mdc-button/package.json b/packages/mdc-button/package.json
index b9bad00184a..098d099d713 100644
--- a/packages/mdc-button/package.json
+++ b/packages/mdc-button/package.json
@@ -1,7 +1,7 @@
{
"name": "@material/button",
"description": "The Material Components for the web button component",
- "version": "0.3.7",
+ "version": "0.3.8",
"license": "Apache 2.0",
"keywords": [
"material components",
@@ -14,9 +14,8 @@
},
"dependencies": {
"@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/elevation": "^0.1.9",
+ "@material/ripple": "^0.7.0",
+ "@material/theme": "^0.1.5"
}
}
diff --git a/packages/mdc-card/package.json b/packages/mdc-card/package.json
index bd505c980b3..88b9a511fca 100644
--- a/packages/mdc-card/package.json
+++ b/packages/mdc-card/package.json
@@ -1,6 +1,6 @@
{
"name": "@material/card",
- "version": "0.2.3",
+ "version": "0.2.4",
"description": "The Material Components for the web card component",
"license": "Apache-2.0",
"keywords": [
@@ -13,9 +13,9 @@
"url": "https://github.com/material-components/material-components-web.git"
},
"dependencies": {
- "@material/elevation": "^0.1.8",
+ "@material/elevation": "^0.1.9",
"@material/theme": "^0.1.5",
"@material/typography": "^0.2.2",
- "@material/rtl": "^0.1.5"
+ "@material/rtl": "^0.1.6"
}
}
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-checkbox/package.json b/packages/mdc-checkbox/package.json
index 6c3bb674aef..08f41017342 100644
--- a/packages/mdc-checkbox/package.json
+++ b/packages/mdc-checkbox/package.json
@@ -1,7 +1,7 @@
{
"name": "@material/checkbox",
"description": "The Material Components for the web checkbox component",
- "version": "0.3.6",
+ "version": "0.3.7",
"license": "Apache-2.0",
"keywords": [
"material components",
@@ -15,9 +15,9 @@
},
"dependencies": {
"@material/animation": "^0.2.3",
- "@material/base": "^0.2.0",
- "@material/ripple": "^0.6.2",
- "@material/rtl": "^0.1.5",
+ "@material/base": "^0.2.1",
+ "@material/ripple": "^0.7.0",
+ "@material/rtl": "^0.1.6",
"@material/theme": "^0.1.5"
}
}
diff --git a/packages/mdc-dialog/package.json b/packages/mdc-dialog/package.json
index d1df5ae5463..ef14a4b2d22 100644
--- a/packages/mdc-dialog/package.json
+++ b/packages/mdc-dialog/package.json
@@ -1,6 +1,6 @@
{
"name": "@material/dialog",
- "version": "0.3.1",
+ "version": "0.3.2",
"description": "The Material Components Web dialog component",
"license": "Apache-2.0",
"keywords": [
@@ -16,10 +16,10 @@
},
"dependencies": {
"@material/animation": "^0.2.3",
- "@material/base": "^0.2.0",
- "@material/elevation": "^0.1.8",
- "@material/ripple": "^0.6.2",
- "@material/rtl": "^0.1.5",
+ "@material/base": "^0.2.1",
+ "@material/elevation": "^0.1.9",
+ "@material/ripple": "^0.7.0",
+ "@material/rtl": "^0.1.6",
"@material/theme": "^0.1.5",
"@material/typography": "^0.1.1",
"focus-trap": "^2.3.0"
diff --git a/packages/mdc-drawer/package.json b/packages/mdc-drawer/package.json
index fdd96cdd7af..ff4fa498d99 100644
--- a/packages/mdc-drawer/package.json
+++ b/packages/mdc-drawer/package.json
@@ -1,6 +1,6 @@
{
"name": "@material/drawer",
- "version": "0.5.0",
+ "version": "0.5.1",
"description": "The Material Components Web drawer component",
"license": "Apache-2.0",
"keywords": [
@@ -16,9 +16,9 @@
},
"dependencies": {
"@material/animation": "^0.2.3",
- "@material/base": "^0.2.0",
- "@material/elevation": "^0.1.8",
- "@material/rtl": "^0.1.5",
+ "@material/base": "^0.2.1",
+ "@material/elevation": "^0.1.9",
+ "@material/rtl": "^0.1.6",
"@material/theme": "^0.1.5",
"@material/typography": "^0.2.2"
}
diff --git a/packages/mdc-elevation/README.md b/packages/mdc-elevation/README.md
index 0c71d1fda76..c4eb50a4b38 100644
--- a/packages/mdc-elevation/README.md
+++ b/packages/mdc-elevation/README.md
@@ -16,13 +16,7 @@ path: /catalog/elevation/
-->
-MDC Elevation provides Sass mixins and CSS classes which are used to provide [shadows and
-elevation](https://material.io/guidelines/what-is-material/elevation-shadows.html) to our material
-components.
-
-The elevation values are mapped out in a "z-space" and range from `0` to `24`.
-Our implementation is based on [Scott Hyndman's work](http://codepen.io/shyndman/full/ojxmdY/),
-which was created in collaboration with the designers on the Material Design team.
+Shadows provide important visual cues about objects’ depth and directional movement. They are the only visual cue indicating the amount of separation between surfaces. An object’s elevation determines the appearance of its shadow. The elevation values are mapped out in a "z-space" and range from `0` to `24`.
> **A note about "z-space"**: Within the spec, elevation is normally referred to as having a `dp`
> value. In other words, how many "pixels" above the base material is a piece of material elevated.
@@ -50,102 +44,22 @@ npm install --save @material/elevation
## Usage
-### Sass Mixin
-
-MDC Elevation exports an `mdc-elevation` mixin which can be used to set the elevation on a selector.
-It works by assigning the correct elevation value to a selector's `box-shadow` property.
+### Sass Mixins, Variables, and Functions
-`mdc-elevation` takes one `$z-value` argument which represents the z-space for that given elevation. For example, [cards](https://material.io/guidelines/components/cards.html) have a resting elevation of `2dp`. Implementing that using MDC Elevation looks like the following:
+| Mixin | Description |
+| ----------------------------------------------- | - |
+| `mdc-elevation($z-value)` | Sets the elevation to the z-space for that given elevation |
+| `mdc-elevation-transition($duration, $easing)` | Applies the correct css rules to transition an element between elevations |
-```scss
-@import "@material/elevation/mixins";
-
-.mdc-card {
- @include mdc-elevation(2);
- // ...
-}
-```
+| Variable | Description |
+| ------------------------------------------- | - |
+| `mdc-elevation-transition-duration` | Default duration value for elevation transitions |
+| `mdc-elevation-transition-timing-function` | Default easing value for elevation transitions |
-It is also quite simple to alias common elevations throughout your application by leveraging this
-mixin to export classes:
-
-```scss
-$elevations: (low, medium-low, medium, medium-high, high);
-
-@for $i from 1 through length($elevations) {
- $elev: nth($elevations, $i);
- $z: $i * 2;
- .my-app-elevation--#{$elev} {
- @include mdc-elevation($z);
- }
-}
-```
-
-Note that importing `mdc-elevation/mixins` does not output any CSS.
-
-### CSS Classes
-
-MDC Elevation also includes a CSS file that exports all z values as `mdc-elevation--z Text that floats near the material surface Text that floats far away from the material surface My elevation will change at some point...
diff --git a/packages/mdc-list/package.json b/packages/mdc-list/package.json
index fb1b605c698..2825f2e7380 100644
--- a/packages/mdc-list/package.json
+++ b/packages/mdc-list/package.json
@@ -1,7 +1,7 @@
{
"name": "@material/list",
"description": "The Material Components for the web list component",
- "version": "0.2.10",
+ "version": "0.2.11",
"license": "Apache-2.0",
"keywords": [
"material components",
@@ -13,9 +13,9 @@
"url": "https://github.com/material-components/material-components-web.git"
},
"dependencies": {
- "@material/rtl": "^0.1.5",
+ "@material/rtl": "^0.1.6",
"@material/typography": "^0.2.2",
- "@material/ripple": "^0.6.2",
+ "@material/ripple": "^0.7.0",
"@material/theme": "^0.1.5"
}
}
diff --git a/packages/mdc-menu/package.json b/packages/mdc-menu/package.json
index e798bdc1932..424ef474dc1 100644
--- a/packages/mdc-menu/package.json
+++ b/packages/mdc-menu/package.json
@@ -1,6 +1,6 @@
{
"name": "@material/menu",
- "version": "0.3.0",
+ "version": "0.4.0",
"description": "The Material Components for the web menu component",
"license": "Apache-2.0",
"keywords": [
@@ -15,8 +15,8 @@
},
"dependencies": {
"@material/animation": "^0.2.3",
- "@material/base": "^0.2.0",
- "@material/elevation": "^0.1.8",
+ "@material/base": "^0.2.1",
+ "@material/elevation": "^0.1.9",
"@material/theme": "^0.1.5",
"@material/typography": "^0.2.2"
}
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;
diff --git a/packages/mdc-radio/package.json b/packages/mdc-radio/package.json
index ec6065544bc..dff21f36fc0 100644
--- a/packages/mdc-radio/package.json
+++ b/packages/mdc-radio/package.json
@@ -1,7 +1,7 @@
{
"name": "@material/radio",
"description": "The Material Components for the web radio component",
- "version": "0.2.5",
+ "version": "0.2.6",
"license": "Apache-2.0",
"keywords": [
"material components",
@@ -15,8 +15,8 @@
},
"dependencies": {
"@material/animation": "^0.2.3",
- "@material/base": "^0.2.0",
- "@material/ripple": "^0.6.2",
+ "@material/base": "^0.2.1",
+ "@material/ripple": "^0.7.0",
"@material/theme": "^0.1.5"
}
}
diff --git a/packages/mdc-ripple/README.md b/packages/mdc-ripple/README.md
index 2a9fee5f4f0..72d1562d2f1 100644
--- a/packages/mdc-ripple/README.md
+++ b/packages/mdc-ripple/README.md
@@ -33,6 +33,7 @@ path: /catalog/ripples/
- [Specifying known element dimensions](#specifying-known-element-dimensions)
- [Caveat: Safari](#caveat-safari)
- [Caveat: Theme Custom Variables](#caveat-theme-custom-variables)
+ - [The util API](#the-util-api)
MDC Ripple provides the Javascript and CSS required to provide components (or any element at all) with a material "ink ripple" interaction effect. It is designed to be efficient, uninvasive, and usable without adding any extra DOM to your elements.
@@ -112,8 +113,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` |
@@ -126,13 +126,13 @@ First import the ripple JS
##### ES2015
```javascript
-import {MDCRipple, MDCRippleFoundation} from '@material/ripple';
+import {MDCRipple, MDCRippleFoundation, util} from '@material/ripple';
```
##### CommonJS
```javascript
-const {MDCRipple, MDCRippleFoundation} = require('@material/ripple');
+const {MDCRipple, MDCRippleFoundation, util} = require('@material/ripple');
```
##### AMD
@@ -141,6 +141,7 @@ const {MDCRipple, MDCRippleFoundation} = require('@material/ripple');
require('path/to/@material/ripple', function(mdcRipple) {
const MDCRipple = mdcRipple.MDCRipple;
const MDCRippleFoundation = mdcRipple.MDCRippleFoundation;
+ const util = mdcRipple.util;
});
```
@@ -149,6 +150,7 @@ require('path/to/@material/ripple', function(mdcRipple) {
```javascript
const MDCRipple = mdc.ripple.MDCRipple;
const MDCRippleFoundation = mdc.ripple.MDCRippleFoundation;
+const util = mdc.ripple.util;
```
Then, simply initialize the ripple with the correct DOM element.
@@ -409,3 +411,23 @@ background: color(var(--mdc-theme-primary) a(6%));
But as far as we know, no browsers yet support it. We have added a `@supports` clause into our code
to make sure that it can be used as soon as browsers adopt it, but for now this means that _changes
to your theme via a custom variable will not propagate to ripples._ We don't see this being a gigantic issue as we envision most users configuring one theme via sass. For places where you do need this, special treatment will have to be given.
+
+### The util API
+
+External frameworks and libraries can use the following utility methods when integrating a component.
+
+#### util.supportsCssVariables(windowObj) => Boolean
+
+Determine whether the current browser supports CSS variables (custom properties).
+
+#### util.applyPassive(globalObj = window, forceRefresh = false) => object
+
+Determine whether the current browser supports passive event listeners, and if so, use them.
+
+#### getMatchesProperty(HTMLElementPrototype) => Function
+
+Choose the correct matches property to use on the current browser.
+
+#### getNormalizedEventCoords(ev, pageOffset, clientRect) => object
+
+Determines X/Y coordinates of an event normalized for touch events and ripples.
diff --git a/packages/mdc-ripple/adapter.js b/packages/mdc-ripple/adapter.js
new file mode 100644
index 00000000000..367dfb8732f
--- /dev/null
+++ b/packages/mdc-ripple/adapter.js
@@ -0,0 +1,94 @@
+/**
+ * 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 Ripple. Provides an interface for managing
+ * - classes
+ * - dom
+ * - CSS variables
+ * - position
+ * - dimensions
+ * - scroll position
+ * - event handlers
+ * - unbounded, active and disabled states
+ *
+ * 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 MDCRippleAdapter {
+
+ /** @return {boolean} */
+ browserSupportsCssVars() {}
+
+ /** @return {boolean} */
+ isUnbounded() {}
+
+ /** @return {boolean} */
+ isSurfaceActive() {}
+
+ /** @return {boolean} */
+ isSurfaceDisabled() {}
+
+ /** @param {string} className */
+ addClass(className) {}
+
+ /** @param {string} className */
+ removeClass(className) {}
+
+ /**
+ * @param {string} evtType
+ * @param {!Function} handler
+ */
+ registerInteractionHandler(evtType, handler) {}
+
+ /**
+ * @param {string} evtType
+ * @param {!Function} handler
+ */
+ deregisterInteractionHandler(evtType, handler) {}
+
+ /**
+ * @param {!Function} handler
+ */
+ registerResizeHandler(handler) {}
+
+ /**
+ * @param {!Function} handler
+ */
+ deregisterResizeHandler(handler) {}
+
+ /**
+ * @param {string} varName
+ * @param {?number|string} value
+ */
+ updateCssVariable(varName, value) {}
+
+ /** @return {!ClientRect} */
+ computeBoundingRect() {}
+
+ /** @return {{x: number, y: number}} */
+ getWindowPageOffset() {}
+
+}
diff --git a/packages/mdc-ripple/foundation.js b/packages/mdc-ripple/foundation.js
index 866cfdb081b..e63ba2ab595 100644
--- a/packages/mdc-ripple/foundation.js
+++ b/packages/mdc-ripple/foundation.js
@@ -14,11 +14,55 @@
* limitations under the License.
*/
-import {MDCFoundation} from '@material/base';
-
+import MDCFoundation from '@material/base/foundation';
+import MDCRippleAdapter from './adapter';
import {cssClasses, strings, numbers} from './constants';
import {getNormalizedEventCoords} from './util';
+/**
+ * @typedef {!{
+ * isActivated: (boolean|undefined),
+ * hasDeactivationUXRun: (boolean|undefined),
+ * wasActivatedByPointer: (boolean|undefined),
+ * wasElementMadeActive: (boolean|undefined),
+ * activationStartTime: (number|undefined),
+ * activationEvent: Event,
+ * isProgrammatic: (boolean|undefined)
+ * }}
+ */
+let ActivationStateType;
+
+/**
+ * @typedef {!{
+ * activate: (string|undefined),
+ * deactivate: (string|undefined),
+ * focus: (string|undefined),
+ * blur: (string|undefined)
+ * }}
+ */
+let ListenerInfoType;
+
+/**
+ * @typedef {!{
+ * activate: function(!Event),
+ * deactivate: function(!Event),
+ * focus: function(),
+ * blur: function()
+ * }}
+ */
+let ListenersType;
+
+/**
+ * @typedef {!{
+ * x: number,
+ * y: number
+ * }}
+ */
+let PointType;
+
+/**
+ * @enum {string}
+ */
const DEACTIVATION_ACTIVATION_PAIRS = {
mouseup: 'mousedown',
pointerup: 'pointerdown',
@@ -27,6 +71,9 @@ const DEACTIVATION_ACTIVATION_PAIRS = {
blur: 'focus',
};
+/**
+ * @extends {MDCFoundation}
+ */
export default class MDCRippleFoundation extends MDCFoundation {
static get cssClasses() {
return cssClasses;
@@ -58,10 +105,13 @@ export default class MDCRippleFoundation extends MDCFoundation {
};
}
- // We compute this property so that we are not querying information about the client
- // until the point in time where the foundation requests it. This prevents scenarios where
- // client-side feature-detection may happen too early, such as when components are rendered on the server
- // and then initialized at mount time on the client.
+ /**
+ * We compute this property so that we are not querying information about the client
+ * until the point in time where the foundation requests it. This prevents scenarios where
+ * client-side feature-detection may happen too early, such as when components are rendered on the server
+ * and then initialized at mount time on the client.
+ * @return {boolean}
+ */
get isSupported_() {
return this.adapter_.browserSupportsCssVars();
}
@@ -69,12 +119,25 @@ export default class MDCRippleFoundation extends MDCFoundation {
constructor(adapter) {
super(Object.assign(MDCRippleFoundation.defaultAdapter, adapter));
+ /** @private {number} */
this.layoutFrame_ = 0;
- this.frame_ = {width: 0, height: 0};
+
+ /** @private {!ClientRect} */
+ this.frame_ = /** @type {!ClientRect} */ ({width: 0, height: 0});
+
+ /** @private {!ActivationStateType} */
this.activationState_ = this.defaultActivationState_();
+
+ /** @private {number} */
this.xfDuration_ = 0;
+
+ /** @private {number} */
this.initialSize_ = 0;
+
+ /** @private {number} */
this.maxRadius_ = 0;
+
+ /** @private {!Array<{ListenerInfoType}>} */
this.listenerInfos_ = [
{activate: 'touchstart', deactivate: 'touchend'},
{activate: 'pointerdown', deactivate: 'pointerup'},
@@ -82,6 +145,8 @@ export default class MDCRippleFoundation extends MDCFoundation {
{activate: 'keydown', deactivate: 'keyup'},
{focus: 'focus', blur: 'blur'},
];
+
+ /** @private {!ListenersType} */
this.listeners_ = {
activate: (e) => this.activate_(e),
deactivate: (e) => this.deactivate_(e),
@@ -92,21 +157,38 @@ export default class MDCRippleFoundation extends MDCFoundation {
() => this.adapter_.removeClass(MDCRippleFoundation.cssClasses.BG_FOCUSED)
),
};
+
+ /** @private {!Function} */
this.resizeHandler_ = () => this.layout();
+
+ /** @private {!{left: number, top:number}} */
this.unboundedCoords_ = {
left: 0,
top: 0,
};
+
+ /** @private {number} */
this.fgScale_ = 0;
+
+ /** @private {number} */
this.activationTimer_ = 0;
+
+ /** @private {number} */
this.fgDeactivationRemovalTimer_ = 0;
+
+ /** @private {boolean} */
this.activationAnimationHasEnded_ = false;
+
+ /** @private {!Function} */
this.activationTimerCallback_ = () => {
this.activationAnimationHasEnded_ = true;
this.runDeactivationUXLogicIfReady_();
};
}
+ /**
+ * @return {!ActivationStateType}
+ */
defaultActivationState_() {
return {
isActivated: false,
@@ -135,6 +217,7 @@ export default class MDCRippleFoundation extends MDCFoundation {
});
}
+ /** @private */
addEventListeners_() {
this.listenerInfos_.forEach((info) => {
Object.keys(info).forEach((k) => {
@@ -144,6 +227,10 @@ export default class MDCRippleFoundation extends MDCFoundation {
this.adapter_.registerResizeHandler(this.resizeHandler_);
}
+ /**
+ * @param {Event} e
+ * @private
+ */
activate_(e) {
if (this.adapter_.isSurfaceDisabled()) {
return;
@@ -182,6 +269,7 @@ export default class MDCRippleFoundation extends MDCFoundation {
this.activate_(null);
}
+ /** @private */
animateActivation_() {
const {VAR_FG_TRANSLATE_START, VAR_FG_TRANSLATE_END} = MDCRippleFoundation.strings;
const {
@@ -215,6 +303,10 @@ export default class MDCRippleFoundation extends MDCFoundation {
this.activationTimer_ = setTimeout(() => this.activationTimerCallback_(), DEACTIVATION_TIMEOUT_MS);
}
+ /**
+ * @private
+ * @return {{startPoint: PointType, endPoint: PointType}}
+ */
getFgTranslationCoordinates_() {
const {activationState_: activationState} = this;
const {activationEvent, wasActivatedByPointer} = activationState;
@@ -222,7 +314,8 @@ export default class MDCRippleFoundation extends MDCFoundation {
let startPoint;
if (wasActivatedByPointer) {
startPoint = getNormalizedEventCoords(
- activationEvent, this.adapter_.getWindowPageOffset(), this.adapter_.computeBoundingRect()
+ /** @type {!Event} */ (activationEvent),
+ this.adapter_.getWindowPageOffset(), this.adapter_.computeBoundingRect()
);
} else {
startPoint = {
@@ -244,6 +337,7 @@ export default class MDCRippleFoundation extends MDCFoundation {
return {startPoint, endPoint};
}
+ /** @private */
runDeactivationUXLogicIfReady_() {
const {FG_DEACTIVATION} = MDCRippleFoundation.cssClasses;
const {hasDeactivationUXRun, isActivated} = this.activationState_;
@@ -257,6 +351,7 @@ export default class MDCRippleFoundation extends MDCFoundation {
}
}
+ /** @private */
rmBoundedActivationClasses_() {
const {BG_ACTIVE_FILL, FG_ACTIVATION} = MDCRippleFoundation.cssClasses;
this.adapter_.removeClass(BG_ACTIVE_FILL);
@@ -265,6 +360,10 @@ export default class MDCRippleFoundation extends MDCFoundation {
this.adapter_.computeBoundingRect();
}
+ /**
+ * @param {Event} e
+ * @private
+ */
deactivate_(e) {
const {activationState_: activationState} = this;
// This can happen in scenarios such as when you have a keyup event that blurs the element.
@@ -274,7 +373,8 @@ export default class MDCRippleFoundation extends MDCFoundation {
// Programmatic deactivation.
if (activationState.isProgrammatic) {
const evtObject = null;
- requestAnimationFrame(() => this.animateDeactivation_(evtObject, Object.assign({}, activationState)));
+ const state = /** @type {!ActivationStateType} */ (Object.assign({}, activationState));
+ requestAnimationFrame(() => this.animateDeactivation_(evtObject, state));
this.activationState_ = this.defaultActivationState_();
return;
}
@@ -291,7 +391,7 @@ export default class MDCRippleFoundation extends MDCFoundation {
needsActualDeactivation = e.type === 'mouseup';
}
- const state = Object.assign({}, activationState);
+ const state = /** @type {!ActivationStateType} */ (Object.assign({}, activationState));
requestAnimationFrame(() => {
if (needsDeactivationUX) {
this.activationState_.hasDeactivationUXRun = true;
@@ -308,6 +408,11 @@ export default class MDCRippleFoundation extends MDCFoundation {
this.deactivate_(null);
}
+ /**
+ * @param {Event} e
+ * @param {!ActivationStateType} options
+ * @private
+ */
animateDeactivation_(e, {wasActivatedByPointer, wasElementMadeActive}) {
const {BG_FOCUSED} = MDCRippleFoundation.cssClasses;
if (wasActivatedByPointer || wasElementMadeActive) {
@@ -331,6 +436,7 @@ export default class MDCRippleFoundation extends MDCFoundation {
});
}
+ /** @private */
removeEventListeners_() {
this.listenerInfos_.forEach((info) => {
Object.keys(info).forEach((k) => {
@@ -340,6 +446,7 @@ export default class MDCRippleFoundation extends MDCFoundation {
this.adapter_.deregisterResizeHandler(this.resizeHandler_);
}
+ /** @private */
removeCssVars_() {
const {strings} = MDCRippleFoundation;
Object.keys(strings).forEach((k) => {
@@ -359,6 +466,7 @@ export default class MDCRippleFoundation extends MDCFoundation {
});
}
+ /** @private */
layoutInternal_() {
this.frame_ = this.adapter_.computeBoundingRect();
@@ -375,6 +483,7 @@ export default class MDCRippleFoundation extends MDCFoundation {
this.updateLayoutCssVars_();
}
+ /** @private */
updateLayoutCssVars_() {
const {
VAR_SURFACE_WIDTH, VAR_SURFACE_HEIGHT, VAR_FG_SIZE,
diff --git a/packages/mdc-ripple/index.js b/packages/mdc-ripple/index.js
index ada56fc38ce..7b63ca57c71 100644
--- a/packages/mdc-ripple/index.js
+++ b/packages/mdc-ripple/index.js
@@ -14,36 +14,61 @@
* limitations under the License.
*/
-import {MDCComponent} from '@material/base';
+import MDCComponent from '@material/base/component';
+import MDCRippleAdapter from './adapter';
import MDCRippleFoundation from './foundation';
-import {applyPassive, supportsCssVariables, getMatchesProperty} from './util';
+import * as util from './util';
export {MDCRippleFoundation};
+export {util};
+/**
+ * @extends MDCComponent
+ */
export class MDCRipple extends MDCComponent {
+ /** @param {...?} args */
+ constructor(...args) {
+ super(...args);
+
+ /** @type {boolean} */
+ this.disabled = false;
+
+ /** @private {boolean} */
+ this.unbounded_;
+ }
+
+ /**
+ * @param {!Element} root
+ * @param {{isUnbounded: (boolean|undefined)}=} options
+ * @return {!MDCRipple}
+ */
static attachTo(root, {isUnbounded = undefined} = {}) {
const ripple = new MDCRipple(root);
// Only override unbounded behavior if option is explicitly specified
if (isUnbounded !== undefined) {
- ripple.unbounded = isUnbounded;
+ ripple.unbounded = /** @type {boolean} */ (isUnbounded);
}
return ripple;
}
+ /**
+ * @param {!MDCRipple} instance
+ * @return {!MDCRippleAdapter}
+ */
static createAdapter(instance) {
- const MATCHES = getMatchesProperty(HTMLElement.prototype);
+ const MATCHES = util.getMatchesProperty(HTMLElement.prototype);
return {
- browserSupportsCssVars: () => supportsCssVariables(window),
+ browserSupportsCssVars: () => util.supportsCssVariables(window),
isUnbounded: () => instance.unbounded,
isSurfaceActive: () => instance.root_[MATCHES](':active'),
isSurfaceDisabled: () => instance.disabled,
addClass: (className) => instance.root_.classList.add(className),
removeClass: (className) => instance.root_.classList.remove(className),
registerInteractionHandler: (evtType, handler) =>
- instance.root_.addEventListener(evtType, handler, applyPassive()),
+ instance.root_.addEventListener(evtType, handler, util.applyPassive()),
deregisterInteractionHandler: (evtType, handler) =>
- instance.root_.removeEventListener(evtType, handler, applyPassive()),
+ instance.root_.removeEventListener(evtType, handler, util.applyPassive()),
registerResizeHandler: (handler) => window.addEventListener('resize', handler),
deregisterResizeHandler: (handler) => window.removeEventListener('resize', handler),
updateCssVariable: (varName, value) => instance.root_.style.setProperty(varName, value),
@@ -52,10 +77,12 @@ export class MDCRipple extends MDCComponent {
};
}
+ /** @return {boolean} */
get unbounded() {
return this.unbounded_;
}
+ /** @param {boolean} unbounded */
set unbounded(unbounded) {
const {UNBOUNDED} = MDCRippleFoundation.cssClasses;
this.unbounded_ = Boolean(unbounded);
@@ -78,6 +105,7 @@ export class MDCRipple extends MDCComponent {
this.foundation_.layout();
}
+ /** @return {!MDCRippleFoundation} */
getDefaultFoundation() {
return new MDCRippleFoundation(MDCRipple.createAdapter(this));
}
diff --git a/packages/mdc-ripple/package.json b/packages/mdc-ripple/package.json
index 24dc30d096c..da613b7881f 100644
--- a/packages/mdc-ripple/package.json
+++ b/packages/mdc-ripple/package.json
@@ -1,7 +1,7 @@
{
"name": "@material/ripple",
"description": "The Material Components for the web Ink Ripple effect for web element interactions",
- "version": "0.6.2",
+ "version": "0.7.0",
"license": "Apache-2.0",
"keywords": [
"material components",
@@ -14,7 +14,7 @@
"url": "https://github.com/material-components/material-components-web.git"
},
"dependencies": {
- "@material/base": "^0.2.0",
+ "@material/base": "^0.2.1",
"@material/theme": "^0.1.5"
}
}
diff --git a/packages/mdc-ripple/util.js b/packages/mdc-ripple/util.js
index 721312efb41..a919865e7dd 100644
--- a/packages/mdc-ripple/util.js
+++ b/packages/mdc-ripple/util.js
@@ -14,8 +14,13 @@
* limitations under the License.
*/
+/** @private {boolean|undefined} */
let supportsPassive_;
+/**
+ * @param {!Window} windowObj
+ * @return {boolean|undefined}
+ */
export function supportsCssVariables(windowObj) {
const supportsFunctionPresent = windowObj.CSS && typeof windowObj.CSS.supports === 'function';
if (!supportsFunctionPresent) {
@@ -32,7 +37,13 @@ export function supportsCssVariables(windowObj) {
return explicitlySupportsCssVars || weAreFeatureDetectingSafari10plus;
}
-// Determine whether the current browser supports passive event listeners, and if so, use them.
+//
+/**
+ * Determine whether the current browser supports passive event listeners, and if so, use them.
+ * @param {!Window=} globalObj
+ * @param {boolean=} forceRefresh
+ * @return {boolean|{passive: boolean}}
+ */
export function applyPassive(globalObj = window, forceRefresh = false) {
if (supportsPassive_ === undefined || forceRefresh) {
let isSupported = false;
@@ -48,12 +59,22 @@ export function applyPassive(globalObj = window, forceRefresh = false) {
return supportsPassive_ ? {passive: true} : false;
}
+/**
+ * @param {!Object} HTMLElementPrototype
+ * @return {!Array