Refactor: checkbox

- Reduce class names by using `:has()`, native pseudo-classes, or `[part~=foo]` selectors
- Remove `hasFocus` property since it’s no longer needed
- Move the icon hiding logic to CSS
This commit is contained in:
Lea Verou
2024-12-13 17:17:45 -05:00
parent 587db10022
commit b1fffc7df9
2 changed files with 57 additions and 86 deletions

View File

@@ -16,7 +16,20 @@
display: inline-block;
}
.checkbox {
:host([size='small']) {
font-size: var(--wa-font-size-s);
}
:host([size='medium']),
:host(:not([size])) {
font-size: var(--wa-font-size-m);
}
:host([size='large']) {
font-size: var(--wa-font-size-l);
}
[part~='base'] {
position: relative;
display: flex;
align-items: flex-start;
@@ -24,18 +37,13 @@
color: var(--wa-form-control-value-color);
vertical-align: middle;
cursor: pointer;
}
font-size: inherit;
.checkbox--small {
font-size: var(--wa-font-size-s);
}
.checkbox--medium {
font-size: var(--wa-font-size-m);
}
.checkbox--large {
font-size: var(--wa-font-size-l);
/* Disabled */
&:has(input:disabled) {
opacity: 0.5;
cursor: not-allowed;
}
}
.checkbox__control {
@@ -54,13 +62,33 @@
box-shadow: var(--box-shadow);
color: var(--wa-form-control-value-color);
transition:
background var(--wa-transition-normal) var(--wa-transition-easing),
border-color var(--wa-transition-fast) var(--wa-transition-easing),
box-shadow var(--wa-transition-fast) var(--wa-transition-easing),
color var(--wa-transition-fast) var(--wa-transition-easing);
background var(--wa-transition-normal),
border-color var(--wa-transition-fast),
box-shadow var(--wa-transition-fast),
color var(--wa-transition-fast);
transition-timing-function: var(--wa-transition-easing);
/* Focus */
&:has(> input:focus-visible:not(:checked, :disabled)) {
outline: var(--wa-focus-ring);
outline-offset: var(--wa-focus-ring-offset);
}
/* Checked/indeterminate */
&:has(:checked, :indeterminate) {
color: var(--checked-icon-color);
border-color: var(--border-color-checked);
background-color: var(--background-color-checked);
}
/* Checked/indeterminate + focus */
&:has(> input:focus-visible:is(:checked, :indeterminate):not(:disabled)) {
outline: var(--wa-focus-ring);
outline-offset: var(--wa-focus-ring-offset);
}
}
.checkbox__input {
input {
position: absolute;
opacity: 0;
padding: 0;
@@ -70,43 +98,15 @@
width: 100%;
}
.checkbox__icon--invisible {
visibility: hidden;
}
.checkbox__checked-icon,
.checkbox__indeterminate-icon {
[part~='icon'] {
display: inline-flex;
input:not(:checked, :indeterminate) + & {
visibility: hidden;
}
}
/* Focus */
.checkbox:not(.checkbox--checked):not(.checkbox--disabled) .checkbox__control:has(> input:focus-visible) {
outline: var(--wa-focus-ring);
outline-offset: var(--wa-focus-ring-offset);
}
/* Checked/indeterminate */
.checkbox--checked .checkbox__control,
.checkbox--indeterminate .checkbox__control {
color: var(--checked-icon-color);
border-color: var(--border-color-checked);
background-color: var(--background-color-checked);
}
/* Checked/indeterminate + focus */
.checkbox.checkbox--checked:not(.checkbox--disabled) .checkbox__control:has(> input:focus-visible),
.checkbox.checkbox--indeterminate:not(.checkbox--disabled) .checkbox__control:has(> input:focus-visible) {
outline: var(--wa-focus-ring);
outline-offset: var(--wa-focus-ring-offset);
}
/* Disabled */
.checkbox--disabled {
opacity: 0.5;
cursor: not-allowed;
}
.checkbox__label {
[part~='label'] {
display: inline-block;
line-height: var(--toggle-size);
margin-inline-start: var(--wa-space-xs);
@@ -114,7 +114,7 @@
-webkit-user-select: none;
}
:host([required]) .checkbox__label::after {
:host([required]) [part~='label']::after {
content: var(--wa-form-control-required-content);
color: var(--wa-form-control-required-content-color);
margin-inline-start: var(--wa-form-control-required-content-offset);

View File

@@ -1,6 +1,6 @@
import '../icon/icon.js';
import { classMap } from 'lit/directives/class-map.js';
import { customElement, property, query, state } from 'lit/decorators.js';
import { customElement, property, query } from 'lit/decorators.js';
import { HasSlotController } from '../../internal/slot.js';
import { html, isServer } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js';
@@ -79,8 +79,6 @@ export default class WaCheckbox extends WebAwesomeFormAssociatedElement {
@query('input[type="checkbox"]') input: HTMLInputElement;
@state() private hasFocus = false;
@property() title = ''; // make reactive to pass through
/** The name of the checkbox, submitted as a name/value pair with form data. */
@@ -137,7 +135,6 @@ export default class WaCheckbox extends WebAwesomeFormAssociatedElement {
}
private handleBlur() {
this.hasFocus = false;
this.dispatchEvent(new WaBlurEvent());
}
@@ -146,7 +143,6 @@ export default class WaCheckbox extends WebAwesomeFormAssociatedElement {
}
private handleFocus() {
this.hasFocus = true;
this.dispatchEvent(new WaFocusEvent());
}
@@ -216,7 +212,6 @@ export default class WaCheckbox extends WebAwesomeFormAssociatedElement {
const iconName = isIndeterminate ? 'indeterminate' : 'check';
const iconState = isIndeterminate ? 'indeterminate' : 'check';
const iconVisible = this.checked || this.indeterminate;
//
// NOTE: we use a `<div>` around the label slot because of this Chrome bug.
@@ -233,25 +228,8 @@ export default class WaCheckbox extends WebAwesomeFormAssociatedElement {
'form-control--has-hint': hasHint
})}
>
<label
part="base"
class=${classMap({
checkbox: true,
'checkbox--checked': this.checked,
'checkbox--disabled': this.disabled,
'checkbox--focused': this.hasFocus,
'checkbox--indeterminate': this.indeterminate,
'checkbox--small': this.size === 'small',
'checkbox--medium': this.size === 'medium',
'checkbox--large': this.size === 'large'
})}
>
<span
part="control${this.checked ? ' control--checked' : ''}${this.indeterminate
? ' control--indeterminate'
: ''}"
class="checkbox__control"
>
<label part="base">
<span class="checkbox__control">
<input
class="checkbox__input"
type="checkbox"
@@ -270,17 +248,10 @@ export default class WaCheckbox extends WebAwesomeFormAssociatedElement {
@focus=${this.handleFocus}
/>
<wa-icon
part=${`${iconState}-icon`}
class=${`checkbox__${iconState}-icon ${iconVisible ? '' : 'checkbox__icon--invisible'}`}
library="system"
name=${iconName}
></wa-icon>
<wa-icon part="${iconState}-icon icon" library="system" name=${iconName}></wa-icon>
</span>
<div part="label" class="checkbox__label">
<slot></slot>
</div>
<slot part="label"></slot>
</label>
<div aria-hidden=${hasHint ? 'false' : 'true'} class="form-control__hint" id="hint" part="form-control-hint">