2022-08-17 15:37:37 +00:00
import { html } from 'lit' ;
2021-05-27 21:00:43 +00:00
import { customElement , property , query , state } from 'lit/decorators.js' ;
2021-09-29 12:40:26 +00:00
import { classMap } from 'lit/directives/class-map.js' ;
import { ifDefined } from 'lit/directives/if-defined.js' ;
import { live } from 'lit/directives/live.js' ;
2022-06-28 21:59:52 +00:00
import { defaultValue } from '../../internal/default-value' ;
2022-03-24 12:01:09 +00:00
import { FormSubmitController } from '../../internal/form' ;
2022-08-17 15:37:37 +00:00
import ShoelaceElement from '../../internal/shoelace-element' ;
2022-03-24 12:01:09 +00:00
import { watch } from '../../internal/watch' ;
2021-07-10 00:45:44 +00:00
import styles from './checkbox.styles' ;
2022-07-19 12:27:39 +00:00
import type { CSSResultGroup } from 'lit' ;
2021-02-26 14:09:13 +00:00
/ * *
* @since 2.0
* @status stable
*
2021-06-25 20:25:46 +00:00
* @slot - The checkbox ' s label .
2021-02-26 14:09:13 +00:00
*
2021-06-25 20:25:46 +00:00
* @event sl - blur - Emitted when the control loses focus .
* @event sl - change - Emitted when the control ' s checked state changes .
* @event sl - focus - Emitted when the control gains focus .
2021-06-24 22:24:54 +00:00
*
2022-03-09 20:54:18 +00:00
* @csspart base - The component ' s internal wrapper .
2021-06-25 20:25:46 +00:00
* @csspart control - The checkbox control .
* @csspart checked - icon - The container the wraps the checked icon .
* @csspart indeterminate - icon - The container that wraps the indeterminate icon .
* @csspart label - The checkbox label .
2021-02-26 14:09:13 +00:00
* /
2021-03-18 13:04:23 +00:00
@customElement ( 'sl-checkbox' )
2022-08-17 15:37:37 +00:00
export default class SlCheckbox extends ShoelaceElement {
2022-07-19 12:27:39 +00:00
static styles : CSSResultGroup = styles ;
2021-03-06 17:01:39 +00:00
@query ( 'input[type="checkbox"]' ) input : HTMLInputElement ;
2021-02-26 14:09:13 +00:00
2022-01-16 05:47:14 +00:00
// @ts-expect-error -- Controller is currently unused
private readonly formSubmitController = new FormSubmitController ( this , {
2022-06-28 21:59:52 +00:00
value : ( control : SlCheckbox ) = > ( control . checked ? control . value || 'on' : undefined ) ,
defaultValue : ( control : SlCheckbox ) = > control . defaultChecked ,
setValue : ( control : SlCheckbox , checked : boolean ) = > ( control . checked = checked )
2022-01-11 14:18:20 +00:00
} ) ;
2021-03-06 17:01:39 +00:00
2021-03-24 14:21:21 +00:00
@state ( ) private hasFocus = false ;
2021-02-26 14:09:13 +00:00
2022-08-31 21:14:34 +00:00
/** Name of the HTML form control. Submitted with the form as part of a name/value pair. */
2021-04-02 11:47:25 +00:00
@property ( ) name : string ;
2021-02-26 14:09:13 +00:00
2022-08-31 21:14:34 +00:00
/** Value of the HTML form control. Primarily used to differentiate a list of related checkboxes that have the same name. */
2021-04-02 11:47:25 +00:00
@property ( ) value : string ;
2021-02-26 14:09:13 +00:00
2022-08-31 21:14:34 +00:00
/** Disables the checkbox (so the user can't check / uncheck it). */
2021-07-01 00:04:46 +00:00
@property ( { type : Boolean , reflect : true } ) disabled = false ;
2021-02-26 14:09:13 +00:00
/** Makes the checkbox a required field. */
2021-07-01 00:04:46 +00:00
@property ( { type : Boolean , reflect : true } ) required = false ;
2021-02-26 14:09:13 +00:00
/** Draws the checkbox in a checked state. */
2021-07-01 00:04:46 +00:00
@property ( { type : Boolean , reflect : true } ) checked = false ;
2021-02-26 14:09:13 +00:00
2022-08-31 21:14:34 +00:00
/** Draws the checkbox in an indeterminate state. Usually applies to a checkbox that represents "select all" or "select none" when the items to which it applies are a mix of selected and unselected. */
2021-07-01 00:04:46 +00:00
@property ( { type : Boolean , reflect : true } ) indeterminate = false ;
2021-02-26 14:09:13 +00:00
/** This will be true when the control is in an invalid state. Validity is determined by the `required` prop. */
2021-07-01 00:04:46 +00:00
@property ( { type : Boolean , reflect : true } ) invalid = false ;
2021-03-06 17:01:39 +00:00
2022-06-28 21:59:52 +00:00
/** Gets or sets the default value used to reset this element. The initial value corresponds to the one originally specified in the HTML that created this element. */
@defaultValue ( 'checked' )
defaultChecked = false ;
2021-03-06 17:01:39 +00:00
firstUpdated() {
2021-06-16 12:42:02 +00:00
this . invalid = ! this . input . checkValidity ( ) ;
2021-02-26 14:09:13 +00:00
}
2021-04-01 12:40:48 +00:00
/** Simulates a click on the checkbox. */
click() {
this . input . click ( ) ;
}
2021-02-26 14:09:13 +00:00
/** Sets focus on the checkbox. */
2021-03-29 11:16:40 +00:00
focus ( options? : FocusOptions ) {
2021-02-26 14:09:13 +00:00
this . input . focus ( options ) ;
}
/** Removes focus from the checkbox. */
2021-03-29 11:16:40 +00:00
blur() {
2021-02-26 14:09:13 +00:00
this . input . blur ( ) ;
}
/** Checks for validity and shows the browser's validation message if the control is invalid. */
reportValidity() {
return this . input . reportValidity ( ) ;
}
/** Sets a custom validation message. If `message` is not empty, the field will be considered invalid. */
setCustomValidity ( message : string ) {
this . input . setCustomValidity ( message ) ;
this . invalid = ! this . input . checkValidity ( ) ;
}
handleClick() {
2021-03-06 17:01:39 +00:00
this . checked = ! this . checked ;
2021-02-26 14:09:13 +00:00
this . indeterminate = false ;
2022-09-16 20:21:40 +00:00
this . emit ( 'sl-change' ) ;
2021-02-26 14:09:13 +00:00
}
handleBlur() {
this . hasFocus = false ;
2022-09-16 20:21:40 +00:00
this . emit ( 'sl-blur' ) ;
2021-02-26 14:09:13 +00:00
}
2022-01-16 05:47:14 +00:00
@watch ( 'disabled' , { waitUntilFirstUpdate : true } )
2021-06-25 23:22:05 +00:00
handleDisabledChange() {
// Disabled form controls are always valid, so we need to recheck validity when the state changes
2022-01-16 05:47:14 +00:00
this . input . disabled = this . disabled ;
this . invalid = ! this . input . checkValidity ( ) ;
2021-06-25 23:22:05 +00:00
}
2021-02-26 14:09:13 +00:00
handleFocus() {
this . hasFocus = true ;
2022-09-16 20:21:40 +00:00
this . emit ( 'sl-focus' ) ;
2021-02-26 14:09:13 +00:00
}
2021-06-15 13:26:35 +00:00
@watch ( 'checked' , { waitUntilFirstUpdate : true } )
@watch ( 'indeterminate' , { waitUntilFirstUpdate : true } )
2021-02-26 14:09:13 +00:00
handleStateChange() {
2021-06-16 12:42:02 +00:00
this . invalid = ! this . input . checkValidity ( ) ;
2021-02-26 14:09:13 +00:00
}
render() {
return html `
< label
part = "base"
class = $ { classMap ( {
checkbox : true ,
'checkbox--checked' : this . checked ,
'checkbox--disabled' : this . disabled ,
'checkbox--focused' : this . hasFocus ,
'checkbox--indeterminate' : this . indeterminate
} ) }
>
2021-08-27 12:38:11 +00:00
< input
2021-08-27 12:50:31 +00:00
class = "checkbox__input"
2021-08-27 12:38:11 +00:00
type = "checkbox"
name = $ { ifDefined ( this . name ) }
value = $ { ifDefined ( this . value ) }
. indeterminate = $ { live ( this . indeterminate ) }
. checked = $ { live ( this . checked ) }
. disabled = $ { this . disabled }
. required = $ { this . required }
aria - checked = $ { this . checked ? 'true' : 'false' }
@click = $ { this . handleClick }
@blur = $ { this . handleBlur }
@focus = $ { this . handleFocus }
/ >
2021-02-26 14:09:13 +00:00
< span part = "control" class = "checkbox__control" >
$ { this . checked
? html `
2022-06-29 12:09:25 +00:00
< svg part = "checked-icon" class = "checkbox__icon" viewBox = "0 0 16 16" >
< g stroke = "none" stroke - width = "1" fill = "none" fill - rule = "evenodd" stroke - linecap = "round" >
< g stroke = "currentColor" stroke - width = "2" >
< g transform = "translate(3.428571, 3.428571)" >
< path d = "M0,5.71428571 L3.42857143,9.14285714" > < / path >
< path d = "M9.14285714,0 L3.42857143,9.14285714" > < / path >
2021-02-26 14:09:13 +00:00
< / g >
< / g >
2022-06-29 12:09:25 +00:00
< / g >
< / svg >
2021-02-26 14:09:13 +00:00
`
: '' }
$ { ! this . checked && this . indeterminate
? html `
2022-06-29 12:09:25 +00:00
< svg part = "indeterminate-icon" class = "checkbox__icon" viewBox = "0 0 16 16" >
< g stroke = "none" stroke - width = "1" fill = "none" fill - rule = "evenodd" stroke - linecap = "round" >
< g stroke = "currentColor" stroke - width = "2" >
< g transform = "translate(2.285714, 6.857143)" >
< path d = "M10.2857143,1.14285714 L1.14285714,1.14285714" > < / path >
2021-02-26 14:09:13 +00:00
< / g >
< / g >
2022-06-29 12:09:25 +00:00
< / g >
< / svg >
2021-02-26 14:09:13 +00:00
`
: '' }
< / span >
2022-03-08 22:34:17 +00:00
< span part = "label" class = "checkbox__label" >
2021-03-06 17:01:39 +00:00
< slot > < / slot >
2021-02-26 14:09:13 +00:00
< / span >
< / label >
` ;
}
}
2021-03-12 14:07:38 +00:00
2021-03-12 14:09:08 +00:00
declare global {
interface HTMLElementTagNameMap {
'sl-checkbox' : SlCheckbox ;
}
}