refactor(checkbox): use native input type=checkbox

This commit is contained in:
Adam Bradley
2017-11-21 22:31:27 -06:00
parent 7a5682fbbe
commit fece34beb0
7 changed files with 153 additions and 88 deletions

View File

@ -594,6 +594,7 @@ declare global {
color?: string,
mode?: 'ios' | 'md',
name?: string,
checked?: boolean,
disabled?: boolean,
value?: string

View File

@ -123,6 +123,27 @@ $checkbox-ios-item-end-margin-start: 0 !default;
}
// iOS Checkbox Keyboard Focus
// -----------------------------------------
.checkbox-key .checkbox-icon::after {
position: absolute;
top: -9px;
left: -9px;
display: block;
width: 36px;
height: 36px;
background: #86A8DF;
opacity: .3;
border-radius: 50%;
content: '';
}
// iOS Checkbox Within An Item
// -----------------------------------------

View File

@ -142,6 +142,27 @@ $checkbox-md-item-end-margin-start: 0 !default;
}
// Material Design Checkbox Keyboard Focus
// -----------------------------------------
.checkbox-key .checkbox-icon::after {
position: absolute;
top: -12px;
left: -12px;
display: block;
width: 36px;
height: 36px;
background: #86A8DF;
opacity: .3;
border-radius: 50%;
content: '';
}
// Material Design Checkbox Within An Item
// -----------------------------------------

View File

@ -9,7 +9,7 @@ ion-checkbox {
display: inline-block;
}
.checkbox-cover {
ion-checkbox input {
@include position(0, null, null, 0);
position: absolute;
@ -19,4 +19,8 @@ ion-checkbox {
background: transparent;
cursor: pointer;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}

View File

@ -1,5 +1,5 @@
import { BlurEvent, CheckboxInput, CheckedInputChangeEvent, FocusEvent, StyleEvent } from '../../utils/input-interfaces';
import { Component, CssClassMap, Event, EventEmitter, Listen, Prop, PropDidChange } from '@stencil/core';
import { Component, CssClassMap, Event, EventEmitter, Prop, PropDidChange, State } from '@stencil/core';
@Component({
@ -13,10 +13,46 @@ import { Component, CssClassMap, Event, EventEmitter, Listen, Prop, PropDidChang
}
})
export class Checkbox implements CheckboxInput {
private checkboxId: string;
private labelId: string;
private didLoad: boolean;
private inputId: string;
private nativeInput: HTMLInputElement;
private styleTmr: any;
@State() keyFocus: boolean;
/**
* @input {string} The color to use from your Sass `$colors` map.
* Default options are: `"primary"`, `"secondary"`, `"danger"`, `"light"`, and `"dark"`.
* For more information, see [Theming your App](/docs/theming/theming-your-app).
*/
@Prop() color: string;
/**
* @input {string} The mode determines which platform styles to use.
* Possible values are: `"ios"` or `"md"`.
* For more information, see [Platform Styles](/docs/theming/platform-specific-styles).
*/
@Prop() mode: 'ios' | 'md';
/**
*/
@Prop() name: string;
/**
* @input {boolean} If true, the checkbox is selected. Defaults to `false`.
*/
@Prop({ mutable: true }) checked = false;
/*
* @input {boolean} If true, the user cannot interact with the checkbox. Default false.
*/
@Prop() disabled = false;
/**
* @input {string} the value of the checkbox.
*/
@Prop({ mutable: true }) value: string;
/**
* @output {Event} Emitted when the checked property has changed.
*/
@ -37,55 +73,51 @@ export class Checkbox implements CheckboxInput {
*/
@Event() ionStyle: EventEmitter<StyleEvent>;
/**
* @input {string} The color to use from your Sass `$colors` map.
* Default options are: `"primary"`, `"secondary"`, `"danger"`, `"light"`, and `"dark"`.
* For more information, see [Theming your App](/docs/theming/theming-your-app).
*/
@Prop() color: string;
/**
* @input {string} The mode determines which platform styles to use.
* Possible values are: `"ios"` or `"md"`.
* For more information, see [Platform Styles](/docs/theming/platform-specific-styles).
*/
@Prop() mode: 'ios' | 'md';
/**
* @input {boolean} If true, the checkbox is selected. Defaults to `false`.
*/
@Prop({ mutable: true }) checked: boolean = false;
/*
* @input {boolean} If true, the user cannot interact with the checkbox. Default false.
*/
@Prop() disabled: boolean = false;
/**
* @input {string} the value of the checkbox.
*/
@Prop({ mutable: true }) value: string;
componentWillLoad() {
this.inputId = 'ion-cb-' + (checkboxIds++);
if (this.value === undefined) {
this.value = this.inputId;
}
this.emitStyle();
}
componentDidLoad() {
this.nativeInput.checked = this.checked;
this.didLoad = true;
const parentItem = this.nativeInput.closest('ion-item');
if (parentItem) {
const itemLabel = parentItem.querySelector('ion-label');
if (itemLabel) {
itemLabel.id = this.inputId + '-lbl';
this.nativeInput.setAttribute('aria-labelledby', itemLabel.id);
}
}
}
@PropDidChange('checked')
checkedChanged(isChecked: boolean) {
this.ionChange.emit({
checked: isChecked,
value: this.value
});
if (this.nativeInput.checked !== isChecked) {
// keep the checked value and native input `nync
this.nativeInput.checked = isChecked;
}
if (this.didLoad) {
this.ionChange.emit({
checked: isChecked,
value: this.value
});
}
this.emitStyle();
}
@PropDidChange('disabled')
disabledChanged() {
disabledChanged(isDisabled: boolean) {
this.nativeInput.disabled = isDisabled;
this.emitStyle();
}
private emitStyle() {
emitStyle() {
clearTimeout(this.styleTmr);
this.styleTmr = setTimeout(() => {
@ -96,22 +128,29 @@ export class Checkbox implements CheckboxInput {
});
}
@Listen('keydown.space')
onSpace(ev: KeyboardEvent) {
this.toggle();
ev.stopPropagation();
ev.preventDefault();
onChange() {
this.checked = !this.checked;
}
toggle() {
this.checked = !this.checked;
onKeyUp() {
this.keyFocus = true;
}
onFocus() {
this.ionFocus.emit();
}
onBlur() {
this.keyFocus = false;
this.ionBlur.emit();
}
hostData() {
return {
class: {
'checkbox-checked': this.checked,
'checkbox-disabled': this.disabled
'checkbox-disabled': this.disabled,
'checkbox-key': this.keyFocus
}
};
}
@ -126,16 +165,19 @@ export class Checkbox implements CheckboxInput {
<div class={checkboxClasses}>
<div class='checkbox-inner'></div>
</div>,
<button
class='checkbox-cover'
onClick={() => this.toggle()}
id={this.checkboxId}
aria-checked={this.checked ? 'true' : false}
aria-disabled={this.disabled ? 'true' : false}
aria-labelledby={this.labelId}
role='checkbox'
tabIndex={0}
/>
<input
type='checkbox'
onChange={this.onChange.bind(this)}
onFocus={this.onFocus.bind(this)}
onBlur={this.onBlur.bind(this)}
onKeyUp={this.onKeyUp.bind(this)}
id={this.inputId}
name={this.name}
value={this.value}
disabled={this.disabled}
ref={r => this.nativeInput = (r as any)}/>
];
}
}
let checkboxIds = 0;

View File

@ -10,50 +10,22 @@ Placed in an `ion-item` or used as a stand-alone checkbox.
<ion-item>
<ion-label>Pepperoni</ion-label>
<ion-checkbox [(ngModel)]="pepperoni"></ion-checkbox>
<ion-checkbox value="pepperoni" checked></ion-checkbox>
</ion-item>
<ion-item>
<ion-label>Sausage</ion-label>
<ion-checkbox [(ngModel)]="sausage" disabled="true"></ion-checkbox>
<ion-checkbox value="sausage" disabled></ion-checkbox>
</ion-item>
<ion-item>
<ion-label>Mushrooms</ion-label>
<ion-checkbox [(ngModel)]="mushrooms"></ion-checkbox>
<ion-checkbox value="mushrooms"></ion-checkbox>
</ion-item>
</ion-list>
```
@advanced
```html
<!-- Call function when state changes -->
<ion-list>
<ion-item>
<ion-label>Cucumber</ion-label>
<ion-checkbox [(ngModel)]="cucumber" (ionChange)="updateCucumber()"></ion-checkbox>
</ion-item>
</ion-list>
```
```ts
@Component({
templateUrl: 'main.html'
})
class SaladPage {
cucumber: boolean;
updateCucumber() {
console.log('Cucumbers new state:' + this.cucumber);
}
}
```
<!-- Auto Generated Below -->

View File

@ -40,6 +40,10 @@
<ion-label>Dark</ion-label>
<ion-checkbox color="dark" checked></ion-checkbox>
</ion-item>
<ion-item>
<ion-label>Unchecked by Default</ion-label>
<ion-checkbox></ion-checkbox>
</ion-item>
<ion-item>
<ion-label>Disabled</ion-label>
<ion-checkbox disabled></ion-checkbox>