mirror of
https://github.com/material-components/material-web.git
synced 2026-03-09 00:09:23 +08:00
193 lines
5.7 KiB
TypeScript
193 lines
5.7 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2019 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import '../../focus/focus-ring.js';
|
|
import '../../ripple/ripple.js';
|
|
|
|
import {html, LitElement, nothing, PropertyValues, TemplateResult} from 'lit';
|
|
import {property, query, queryAsync, state} from 'lit/decorators.js';
|
|
import {classMap} from 'lit/directives/class-map.js';
|
|
import {when} from 'lit/directives/when.js';
|
|
|
|
import {dispatchActivationClick, isActivationClick, redispatchEvent} from '../../controller/events.js';
|
|
import {FormController, getFormValue} from '../../controller/form-controller.js';
|
|
import {stringConverter} from '../../controller/string-converter.js';
|
|
import {ariaProperty} from '../../decorators/aria-property.js';
|
|
import {pointerPress, shouldShowStrongFocus} from '../../focus/strong-focus.js';
|
|
import {ripple} from '../../ripple/directive.js';
|
|
import {MdRipple} from '../../ripple/ripple.js';
|
|
|
|
/**
|
|
* A checkbox component.
|
|
*/
|
|
export class Checkbox extends LitElement {
|
|
/**
|
|
* @nocollapse
|
|
*/
|
|
static formAssociated = true;
|
|
|
|
/**
|
|
* Whether or not the checkbox is selected.
|
|
*/
|
|
@property({type: Boolean, reflect: true}) checked = false;
|
|
|
|
/**
|
|
* Whether or not the checkbox is disabled.
|
|
*/
|
|
@property({type: Boolean, reflect: true}) disabled = false;
|
|
|
|
/**
|
|
* Whether or not the checkbox is invalid.
|
|
*/
|
|
@property({type: Boolean, reflect: true}) error = false;
|
|
|
|
/**
|
|
* Whether or not the checkbox is indeterminate.
|
|
*
|
|
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#indeterminate_state_checkboxes
|
|
*/
|
|
@property({type: Boolean, reflect: true}) indeterminate = false;
|
|
|
|
/**
|
|
* The value of the checkbox that is submitted with a form when selected.
|
|
*
|
|
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#value
|
|
*/
|
|
@property() value = 'on';
|
|
|
|
/**
|
|
* The HTML name to use in form submission.
|
|
*/
|
|
@property({type: String, reflect: true, converter: stringConverter})
|
|
name = '';
|
|
|
|
/**
|
|
* The associated form element with which this element's value will submit.
|
|
*/
|
|
get form() {
|
|
return this.closest('form');
|
|
}
|
|
|
|
@ariaProperty // tslint:disable-line:no-new-decorators
|
|
@property({type: String, attribute: 'data-aria-label', noAccessor: true})
|
|
override ariaLabel!: string;
|
|
|
|
@state() private prevChecked = false;
|
|
@state() private prevDisabled = false;
|
|
@state() private prevIndeterminate = false;
|
|
@queryAsync('md-ripple') private readonly ripple!: Promise<MdRipple|null>;
|
|
@query('input') private readonly input!: HTMLInputElement|null;
|
|
@state() private showFocusRing = false;
|
|
@state() private showRipple = false;
|
|
|
|
constructor() {
|
|
super();
|
|
this.addController(new FormController(this));
|
|
this.addEventListener('click', (event: MouseEvent) => {
|
|
if (!isActivationClick(event)) {
|
|
return;
|
|
}
|
|
this.focus();
|
|
dispatchActivationClick(this.input!);
|
|
});
|
|
}
|
|
|
|
override focus() {
|
|
this.input?.focus();
|
|
}
|
|
|
|
[getFormValue]() {
|
|
return this.checked ? this.value : null;
|
|
}
|
|
|
|
protected override update(changed: PropertyValues<Checkbox>) {
|
|
if (changed.has('checked') || changed.has('disabled') ||
|
|
changed.has('indeterminate')) {
|
|
this.prevChecked = changed.get('checked') ?? this.checked;
|
|
this.prevDisabled = changed.get('disabled') ?? this.disabled;
|
|
this.prevIndeterminate =
|
|
changed.get('indeterminate') ?? this.indeterminate;
|
|
}
|
|
|
|
super.update(changed);
|
|
}
|
|
|
|
protected override render(): TemplateResult {
|
|
const prevNone = !this.prevChecked && !this.prevIndeterminate;
|
|
const prevChecked = this.prevChecked && !this.prevIndeterminate;
|
|
const prevIndeterminate = this.prevIndeterminate;
|
|
const isChecked = this.checked && !this.indeterminate;
|
|
const isIndeterminate = this.indeterminate;
|
|
|
|
const containerClasses = classMap({
|
|
'selected': isChecked || isIndeterminate,
|
|
'unselected': !isChecked && !isIndeterminate,
|
|
'checked': isChecked,
|
|
'indeterminate': isIndeterminate,
|
|
'error': this.error && !this.disabled,
|
|
'prev-unselected': prevNone,
|
|
'prev-checked': prevChecked,
|
|
'prev-indeterminate': prevIndeterminate,
|
|
'prev-disabled': this.prevDisabled,
|
|
});
|
|
|
|
return html`
|
|
<div class="container ${containerClasses}">
|
|
<div class="outline"></div>
|
|
<div class="background"></div>
|
|
<md-focus-ring .visible=${this.showFocusRing}></md-focus-ring>
|
|
${when(this.showRipple, this.renderRipple)}
|
|
<svg class="icon" viewBox="0 0 18 18">
|
|
<rect class="mark short" />
|
|
<rect class="mark long" />
|
|
</svg>
|
|
</div>
|
|
<input type="checkbox"
|
|
aria-checked=${isIndeterminate ? 'mixed' : nothing}
|
|
aria-label=${this.ariaLabel || nothing}
|
|
?disabled=${this.disabled}
|
|
.indeterminate=${this.indeterminate}
|
|
.checked=${this.checked}
|
|
@blur=${this.handleBlur}
|
|
@change=${this.handleChange}
|
|
@focus=${this.handleFocus}
|
|
@pointerdown=${this.handlePointerDown}
|
|
${ripple(this.getRipple)}
|
|
>
|
|
`;
|
|
}
|
|
|
|
private handleBlur() {
|
|
this.showFocusRing = false;
|
|
}
|
|
|
|
private handleChange(event: Event) {
|
|
const target = event.target as HTMLInputElement;
|
|
this.checked = target.checked;
|
|
this.indeterminate = target.indeterminate;
|
|
|
|
redispatchEvent(this, event);
|
|
}
|
|
|
|
private handleFocus() {
|
|
this.showFocusRing = shouldShowStrongFocus();
|
|
}
|
|
|
|
private handlePointerDown() {
|
|
pointerPress();
|
|
this.showFocusRing = shouldShowStrongFocus();
|
|
}
|
|
|
|
private readonly getRipple = () => { // bind to this
|
|
this.showRipple = true;
|
|
return this.ripple;
|
|
};
|
|
|
|
private readonly renderRipple = () => { // bind to this
|
|
return html`<md-ripple ?disabled=${this.disabled} unbounded></md-ripple>`;
|
|
};
|
|
}
|