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

Commit

Permalink
feat(form-field): Make form field labels trigger input ripples.
Browse files Browse the repository at this point in the history
This PR creates MDCFormField, which manages the runtime relationship
between an input element and its label, enabling label events to
trigger input ripples. Implements #9.
  • Loading branch information
Sérgio Gomes authored and sgomes committed Feb 17, 2017
1 parent 494c10b commit c441157
Show file tree
Hide file tree
Showing 13 changed files with 449 additions and 7 deletions.
11 changes: 10 additions & 1 deletion demos/checkbox.html
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,16 @@ <h2>Dark Theme</h2>
(function(global) {
'use strict';
var MDCCheckbox = global.mdc.checkbox.MDCCheckbox;
var checkbox = new MDCCheckbox(document.getElementById('mdc-js-checkbox'));
var MDCFormField = global.mdc.formField.MDCFormField;

var checkbox = document.getElementById('mdc-js-checkbox');
var checkboxInstance = new MDCCheckbox(document.getElementById('mdc-js-checkbox'));

var formField = checkbox.parentElement;
var formFieldInstance = new MDCFormField(formField);

formFieldInstance.input = checkboxInstance;

document.getElementById('make-ind').addEventListener('click', function() {
checkbox.indeterminate = true;
});
Expand Down
15 changes: 12 additions & 3 deletions demos/radio.html
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,18 @@ <h2>Disabled</h2>
<script>
(function(global) {
'use strict';
var radios = document.querySelectorAll('.mdc-radio:not([data-demo-no-js])');
for (var i = 0, radio; radio = radios[i]; i++) {
global.mdc.radio.MDCRadio.attachTo(radio);
var MDCFormField = global.mdc.formField.MDCFormField;
var MDCRadio = global.mdc.radio.MDCRadio;

var formFields = document.querySelectorAll('.mdc-form-field');
for (var i = 0, formField; formField = formFields[i]; i++) {
var formFieldInstance = new MDCFormField(formField);

var radio = formField.querySelector('.mdc-radio:not([data-demo-no-js])');
if (radio) {
var radioInstance = new MDCRadio(radio);
formFieldInstance.input = radioInstance;
}
}
})(this);
</script>
Expand Down
2 changes: 2 additions & 0 deletions packages/material-components-web/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import * as base from '@material/base';
import * as checkbox from '@material/checkbox';
import * as formField from '@material/form-field';
import * as iconToggle from '@material/icon-toggle';
import * as radio from '@material/radio';
import * as ripple from '@material/ripple';
Expand All @@ -41,6 +42,7 @@ autoInit.register('MDCSelect', select.MDCSelect);
export {
base,
checkbox,
formField,
iconToggle,
radio,
ripple,
Expand Down
4 changes: 4 additions & 0 deletions packages/mdc-checkbox/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ export class MDCCheckbox extends MDCComponent {
});
}

get ripple() {
return this.ripple_;
}

get checked() {
return this.foundation_.isChecked();
}
Expand Down
83 changes: 81 additions & 2 deletions packages/mdc-form-field/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
# MDC Form Field

MDC Form Field provides an `mdc-form-field` helper class for easily making theme-aware, RTL-aware
form field + label combos.
form field + label combos. It also provides an `MDCFormField` class for easily making input ripples
respond to label events.

## Installation

```
npm install --save @material/form-field
```

## Usage
## CSS Usage

The `mdc-form-field` class can be used as a wrapper element with any `input` + `label` combo:

Expand Down Expand Up @@ -71,3 +72,81 @@ checkbox:

`mdc-form-field` is dark theme aware, and will change the text color to the "primary on dark" text
color when used within a dark theme.


## JS Usage

### Including in code

#### ES2015

```javascript
import {MDCFormField, MDCFormFieldFoundation} from 'mdc-form-field';
```

#### CommonJS

```javascript
const mdcFormField = require('mdc-form-field');
const MDCFormField = mdcFormField.MDCFormField;
const MDCFormFieldFoundation = mdcFormField.MDCFormFieldFoundation;
```

#### AMD

```javascript
require(['path/to/mdc-form-field'], mdcFormField => {
const MDCFormField = mdcFormField.MDCFormField;
const MDCFormFieldFoundation = mdcFormField.MDCFormFieldFoundation;
});
```

#### Global

```javascript
const MDCFormField = mdc.radio.MDCFormField;
const MDCFormFieldFoundation = mdc.radio.MDCFormFieldFoundation;
```

### Instantiation

```javascript
import {MDCFormField} from 'mdc-form-field';

const formField = new MDCFormField(document.querySelector('.mdc-form-field'));
```

### MDCFormField API

The `MDCFormField` functionality is exposed through a single accessor method.

#### MDCFormField.input

Read-write property that works with an instance of an MDC-Web input element.

In order for the label ripple integration to work correctly, this property needs to be set to a
valid instance of an MDC-Web input element which exposes a `ripple` getter.

```javascript
const formField = new MDCFormField(document.querySelector('.mdc-form-field'));
const radio = new MDCRadio(document.querySelector('.mdc-radio'));

formField.input = radio;
```

No action is taken if the `input` property is not set or the input instance doesn't expose a
`ripple` getter.


### Adapter

The adapter for `MDCFormField` is extremely simple, providing only methods for adding and
removing event listeners from the label, as well as methods for activating and deactivating
the input ripple.

| Method Signature | Description |
| --- | --- |
| `registerInteractionHandler(type: string, handler: EventListener) => void` | Adds an event listener `handler` for event type `type` to the label. |
| `deregisterInteractionHandler(type: string, handler: EventListener) => void` | Removes an event listener `handler` for event type `type` to the label. |
| `activateInputRipple() => void` | Activates the ripple on the input element. Should call `activate` on the input element's `ripple` property. |
| `deactivateInputRipple() => void` | Deactivates the ripple on the input element. Should call `deactivate` on the input element's `ripple` property. |
23 changes: 23 additions & 0 deletions packages/mdc-form-field/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* 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.
*/

export const cssClasses = {
ROOT: 'mdc-form-field',
};

export const strings = {
LABEL_SELECTOR: '.mdc-form-field > label',
};
55 changes: 55 additions & 0 deletions packages/mdc-form-field/foundation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* 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.
*/

import {MDCFoundation} from '@material/base';
import {cssClasses, strings} from './constants';

export default class MDCFormFieldFoundation extends MDCFoundation {
static get cssClasses() {
return cssClasses;
}

static get strings() {
return strings;
}

static get defaultAdapter() {
return {
registerInteractionHandler: (/* type: string, handler: EventListener */) => {},
deregisterInteractionHandler: (/* type: string, handler: EventListener */) => {},
activateInputRipple: () => {},
deactivateInputRipple: () => {},
};
}

constructor(adapter) {
super(Object.assign(MDCFormFieldFoundation.defaultAdapter, adapter));
this.clickHandler_ = (evt) => this.handleClick_(evt);
}

init() {
this.adapter_.registerInteractionHandler('click', this.clickHandler_);
}

destroy() {
this.adapter_.deregisterInteractionHandler('click', this.clickHandler_);
}

handleClick_(evt) {
this.adapter_.activateInputRipple();
requestAnimationFrame(() => this.adapter_.deactivateInputRipple());
}
}
55 changes: 55 additions & 0 deletions packages/mdc-form-field/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* 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.
*/

import {MDCComponent} from '@material/base';
import MDCFormFieldFoundation from './foundation';

export {MDCFormFieldFoundation};

export class MDCFormField extends MDCComponent {
static attachTo(root) {
return new MDCFormField(root);
}

set input(input) {
this.input_ = input;
}

get input() {
return this.input_;
}

get label_() {
return this.root_.querySelector(MDCFormFieldFoundation.strings.LABEL_SELECTOR);
}

getDefaultFoundation() {
return new MDCFormFieldFoundation({
registerInteractionHandler: (type, handler) => this.label_.addEventListener(type, handler),
deregisterInteractionHandler: (type, handler) => this.label_.removeEventListener(type, handler),
activateInputRipple: () => {
if (this.input_ && this.input_.ripple) {
this.input_.ripple.activate();
}
},
deactivateInputRipple: () => {
if (this.input_ && this.input_.ripple) {
this.input_.ripple.deactivate();
}
},
});
}
}
4 changes: 3 additions & 1 deletion packages/mdc-form-field/package.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
{
"name": "@material/form-field",
"description": "Material Components for the web wrapper styles for laying out form fields and labels next to one another",
"description": "Material Components for the web wrapper for laying out form fields and labels next to one another",
"version": "0.1.1",
"license": "Apache-2.0",
"keywords": [
"material components",
"material design",
"form"
],
"main": "index.js",
"repository": {
"type": "git",
"url": "https://github.com/material-components/material-components-web.git"
},
"dependencies": {
"@material/base": "^0.1.1",
"@material/rtl": "^0.1.1",
"@material/theme": "^0.1.1",
"@material/typography": "^0.1.1"
Expand Down
4 changes: 4 additions & 0 deletions packages/mdc-radio/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ export class MDCRadio extends MDCComponent {
this.foundation_.setDisabled(disabled);
}

get ripple() {
return this.ripple_;
}

constructor(...args) {
super(...args);
this.ripple_ = this.initRipple_();
Expand Down
Loading

0 comments on commit c441157

Please sign in to comment.