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

Commit

Permalink
feat(checkbox): Annotate mdc-checkbox for closure (#867)
Browse files Browse the repository at this point in the history
Resolves #334
  • Loading branch information
nckh authored and lynnmercier committed Jul 5, 2017
1 parent 0a8a75d commit a6956b8
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 15 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@
"closureWhitelist": [
"mdc-animation",
"mdc-base",
"mdc-checkbox",
"mdc-menu",
"mdc-ripple"
]
Expand Down
74 changes: 74 additions & 0 deletions packages/mdc-checkbox/adapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* 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 Checkbox. Provides an interface for managing
* - classes
* - dom
* - 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 MDCCheckboxAdapter {

/** @param {string} className */
addClass(className) {}

/** @param {string} className */
removeClass(className) {}

/** @param {!EventListener} handler */
registerAnimationEndHandler(handler) {}

/** @param {!EventListener} handler */
deregisterAnimationEndHandler(handler) {}

/** @param {!EventListener} handler */
registerChangeHandler(handler) {}

/** @param {!EventListener} handler */
deregisterChangeHandler(handler) {}

/** @return {InputElementState} */
getNativeControl() {}

forceLayout() {}

/** @return {boolean} */
isAttachedToDOM() {}

}

/**
* @typedef {!{
* checked: boolean,
* indeterminate: boolean,
* disabled: boolean,
* value: ?string
* }}
*/
export let InputElementState;
5 changes: 4 additions & 1 deletion packages/mdc-checkbox/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@
* limitations under the License.
*/

/** @const {string} */
const ROOT = 'mdc-checkbox';

/** @enum {string} */
export const cssClasses = {
ROOT: 'mdc-checkbox',
UPGRADED: 'mdc-checkbox--upgraded',
CHECKED: 'mdc-checkbox--checked',
INDETERMINATE: 'mdc-checkbox--indeterminate',
Expand All @@ -30,6 +31,7 @@ export const cssClasses = {
ANIM_INDETERMINATE_UNCHECKED: 'mdc-checkbox--anim-indeterminate-unchecked',
};

/** @enum {string} */
export const strings = {
NATIVE_CONTROL_SELECTOR: `.${ROOT}__native-control`,
TRANSITION_STATE_INIT: 'init',
Expand All @@ -38,6 +40,7 @@ export const strings = {
TRANSITION_STATE_INDETERMINATE: 'indeterminate',
};

/** @enum {number} */
export const numbers = {
ANIM_END_LATCH_MS: 100,
};
62 changes: 54 additions & 8 deletions packages/mdc-checkbox/foundation.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,18 @@
* limitations under the License.
*/

import {MDCFoundation} from '@material/base';
import MDCFoundation from '@material/base/foundation';
/* eslint-disable no-unused-vars */
import {MDCCheckboxAdapter, InputElementState} from './adapter';
/* eslint-enable no-unused-vars */
import {cssClasses, strings, numbers} from './constants';

/** @const {!Array<string>} */
const CB_PROTO_PROPS = ['checked', 'indeterminate'];

/**
* @extends {MDCFoundation<!MDCCheckboxAdapter>}
*/
export default class MDCCheckboxFoundation extends MDCFoundation {
static get cssClasses() {
return cssClasses;
Expand All @@ -40,7 +47,7 @@ export default class MDCCheckboxFoundation extends MDCFoundation {
deregisterAnimationEndHandler: (/* handler: EventListener */) => {},
registerChangeHandler: (/* handler: EventListener */) => {},
deregisterChangeHandler: (/* handler: EventListener */) => {},
getNativeControl: () => /* HTMLInputElement */ {},
getNativeControl: () => /* InputElementState */ {},
forceLayout: () => {},
isAttachedToDOM: () => /* boolean */ {},
};
Expand All @@ -49,17 +56,25 @@ export default class MDCCheckboxFoundation extends MDCFoundation {
constructor(adapter) {
super(Object.assign(MDCCheckboxFoundation.defaultAdapter, adapter));

/** @private {string} */
this.currentCheckState_ = strings.TRANSITION_STATE_INIT;

/** @private {string} */
this.currentAnimationClass_ = '';

/** @private {number} */
this.animEndLatchTimer_ = 0;
this.animEndHandler_ = () => {

this.animEndHandler_ = /** @private {!EventListener} */ (() => {
clearTimeout(this.animEndLatchTimer_);
this.animEndLatchTimer_ = setTimeout(() => {
this.adapter_.removeClass(this.currentAnimationClass_);
this.adapter_.deregisterAnimationEndHandler(this.animEndHandler_);
}, numbers.ANIM_END_LATCH_MS);
};
this.changeHandler_ = () => this.transitionCheckState_();
});

this.changeHandler_ = /** @private {!EventListener} */ (
() => this.transitionCheckState_());
}

init() {
Expand All @@ -73,26 +88,32 @@ export default class MDCCheckboxFoundation extends MDCFoundation {
this.uninstallPropertyChangeHooks_();
}

/** @return {boolean} */
isChecked() {
return this.getNativeControl_().checked;
}

/** @param {boolean} checked */
setChecked(checked) {
this.getNativeControl_().checked = checked;
}

/** @return {boolean} */
isIndeterminate() {
return this.getNativeControl_().indeterminate;
}

/** @param {boolean} indeterminate */
setIndeterminate(indeterminate) {
this.getNativeControl_().indeterminate = indeterminate;
}

/** @return {boolean} */
isDisabled() {
return this.getNativeControl_().disabled;
}

/** @param {boolean} disabled */
setDisabled(disabled) {
this.getNativeControl_().disabled = disabled;
if (disabled) {
Expand All @@ -102,14 +123,17 @@ export default class MDCCheckboxFoundation extends MDCFoundation {
}
}

/** @return {?string} */
getValue() {
return this.getNativeControl_().value;
}

/** @param {?string} value */
setValue(value) {
this.getNativeControl_().value = value;
}

/** @private */
installPropertyChangeHooks_() {
const nativeCb = this.getNativeControl_();
const cbProto = Object.getPrototypeOf(nativeCb);
Expand All @@ -119,7 +143,7 @@ export default class MDCCheckboxFoundation extends MDCFoundation {
// We have to check for this descriptor, since some browsers (Safari) don't support its return.
// See: https://bugs.webkit.org/show_bug.cgi?id=49739
if (validDescriptor(desc)) {
Object.defineProperty(nativeCb, controlState, {
const nativeCbDesc = /** @type {!ObjectPropertyDescriptor} */ ({
get: desc.get,
set: (state) => {
desc.set.call(nativeCb, state);
Expand All @@ -128,22 +152,26 @@ export default class MDCCheckboxFoundation extends MDCFoundation {
configurable: desc.configurable,
enumerable: desc.enumerable,
});
Object.defineProperty(nativeCb, controlState, nativeCbDesc);
}
});
}

/** @private */
uninstallPropertyChangeHooks_() {
const nativeCb = this.getNativeControl_();
const cbProto = Object.getPrototypeOf(nativeCb);

CB_PROTO_PROPS.forEach((controlState) => {
const desc = Object.getOwnPropertyDescriptor(cbProto, controlState);
const desc = /** @type {!ObjectPropertyDescriptor} */ (
Object.getOwnPropertyDescriptor(cbProto, controlState));
if (validDescriptor(desc)) {
Object.defineProperty(nativeCb, controlState, desc);
}
});
}

/** @private */
transitionCheckState_() {
const nativeCb = this.adapter_.getNativeControl();
if (!nativeCb) {
Expand Down Expand Up @@ -174,6 +202,11 @@ export default class MDCCheckboxFoundation extends MDCFoundation {
}
}

/**
* @param {!InputElementState} nativeCb
* @return {string}
* @private
*/
determineCheckState_(nativeCb) {
const {
TRANSITION_STATE_INDETERMINATE,
Expand All @@ -187,6 +220,11 @@ export default class MDCCheckboxFoundation extends MDCFoundation {
return nativeCb.checked ? TRANSITION_STATE_CHECKED : TRANSITION_STATE_UNCHECKED;
}

/**
* @param {string} oldState
* @param {string} newState
* @return {string}
*/
getTransitionAnimationClass_(oldState, newState) {
const {
TRANSITION_STATE_INIT,
Expand Down Expand Up @@ -220,6 +258,10 @@ export default class MDCCheckboxFoundation extends MDCFoundation {
}
}

/**
* @return {!InputElementState}
* @private
*/
getNativeControl_() {
return this.adapter_.getNativeControl() || {
checked: false,
Expand All @@ -230,6 +272,10 @@ export default class MDCCheckboxFoundation extends MDCFoundation {
}
}

/**
* @param {ObjectPropertyDescriptor|undefined} inputPropDesc
* @return {boolean}
*/
function validDescriptor(inputPropDesc) {
return inputPropDesc && typeof inputPropDesc.set === 'function';
return !!inputPropDesc && typeof inputPropDesc.set === 'function';
}
Loading

0 comments on commit a6956b8

Please sign in to comment.