mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-12 04:09:12 +00:00
Merge branch 'next' into themer-rework
This commit is contained in:
@@ -60,12 +60,22 @@ Set the `appearance` attribute to `button` on all radios to render a radio butto
|
||||
</wa-radio-group>
|
||||
```
|
||||
|
||||
### Disabling Options
|
||||
### Disabling
|
||||
|
||||
Radios and radio buttons can be disabled by adding the `disabled` attribute to the respective options inside the radio group.
|
||||
To disable the entire radio group, add the `disabled` attribute to the radio group.
|
||||
|
||||
```html {.example}
|
||||
<wa-radio-group label="Select an option" name="a" value="1">
|
||||
<wa-radio-group label="Select an option" disabled>
|
||||
<wa-radio value="1">Option 1</wa-radio>
|
||||
<wa-radio value="2" disabled>Option 2</wa-radio>
|
||||
<wa-radio value="3">Option 3</wa-radio>
|
||||
</wa-radio-group>
|
||||
```
|
||||
|
||||
To disable individual options, add the `disabled` attribute to the respective options.
|
||||
|
||||
```html {.example}
|
||||
<wa-radio-group label="Select an option">
|
||||
<wa-radio value="1">Option 1</wa-radio>
|
||||
<wa-radio value="2" disabled>Option 2</wa-radio>
|
||||
<wa-radio value="3">Option 3</wa-radio>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { PropertyValues } from 'lit';
|
||||
import { html, isServer } from 'lit';
|
||||
import { customElement, property, query, state } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { uniqueId } from '../../internal/math.js';
|
||||
import { HasSlotController } from '../../internal/slot.js';
|
||||
import { RequiredValidator } from '../../internal/validators/required-validator.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-form-associated-element.js';
|
||||
import formControlStyles from '../../styles/component/form-control.css';
|
||||
import sizeStyles from '../../styles/utilities/size.css';
|
||||
@@ -73,6 +73,9 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
|
||||
/** The name of the radio group, submitted as a name/value pair with form data. */
|
||||
@property({ reflect: true }) name: string | null = null;
|
||||
|
||||
/** Disables the radio group and all child radios. */
|
||||
@property({ type: Boolean, reflect: true }) disabled = false;
|
||||
|
||||
/** The orientation in which to show radio items. */
|
||||
@property({ reflect: true }) orientation: 'horizontal' | 'vertical' = 'vertical';
|
||||
|
||||
@@ -141,6 +144,12 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
|
||||
return radio;
|
||||
}
|
||||
|
||||
updated(changedProperties: PropertyValues<this>) {
|
||||
if (changedProperties.has('disabled') || changedProperties.has('value')) {
|
||||
this.syncRadioElements();
|
||||
}
|
||||
}
|
||||
|
||||
formResetCallback(...args: Parameters<WebAwesomeFormAssociatedElement['formResetCallback']>) {
|
||||
this.value = this.defaultValue;
|
||||
|
||||
@@ -152,7 +161,7 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
|
||||
private handleRadioClick = (e: Event) => {
|
||||
const clickedRadio = (e.target as HTMLElement).closest<WaRadio>('wa-radio');
|
||||
|
||||
if (!clickedRadio || clickedRadio.disabled) {
|
||||
if (!clickedRadio || clickedRadio.disabled || (clickedRadio as any).forceDisabled || this.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -199,6 +208,9 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
|
||||
radio.toggleAttribute('data-wa-radio-first', index === 0);
|
||||
radio.toggleAttribute('data-wa-radio-inner', index !== 0 && index !== radios.length - 1);
|
||||
radio.toggleAttribute('data-wa-radio-last', index === radios.length - 1);
|
||||
|
||||
// Set forceDisabled state based on radio group's disabled state
|
||||
(radio as WaRadio).forceDisabled = this.disabled;
|
||||
});
|
||||
|
||||
// If at least one radio button exists, we assume it's a radio button group
|
||||
@@ -216,18 +228,42 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
|
||||
}),
|
||||
);
|
||||
|
||||
if (radios.length > 0 && !radios.some(radio => radio.checked)) {
|
||||
radios[0].setAttribute('tabindex', '0');
|
||||
// Manage tabIndex based on disabled state and checked status
|
||||
if (this.disabled) {
|
||||
// If radio group is disabled, all radios should not be tabbable
|
||||
radios.forEach(radio => {
|
||||
radio.tabIndex = -1;
|
||||
});
|
||||
} else {
|
||||
// Normal tabbing behavior
|
||||
const enabledRadios = radios.filter(radio => !radio.disabled);
|
||||
const checkedRadio = enabledRadios.find(radio => radio.checked);
|
||||
|
||||
if (enabledRadios.length > 0) {
|
||||
if (checkedRadio) {
|
||||
// If there's a checked radio, it should be tabbable
|
||||
enabledRadios.forEach(radio => {
|
||||
radio.tabIndex = radio.checked ? 0 : -1;
|
||||
});
|
||||
} else {
|
||||
// If no radio is checked, first enabled radio should be tabbable
|
||||
enabledRadios.forEach((radio, index) => {
|
||||
radio.tabIndex = index === 0 ? 0 : -1;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Disabled radios should never be tabbable
|
||||
radios
|
||||
.filter(radio => radio.disabled)
|
||||
.forEach(radio => {
|
||||
radio.tabIndex = -1;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@watch('value')
|
||||
handleValueChange() {
|
||||
this.syncRadioElements();
|
||||
}
|
||||
|
||||
private handleKeyDown(event: KeyboardEvent) {
|
||||
if (!['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', ' '].includes(event.key)) {
|
||||
if (!['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', ' '].includes(event.key) || this.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -287,6 +323,8 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
|
||||
|
||||
/** Sets focus on the radio group. */
|
||||
public focus(options?: FocusOptions) {
|
||||
if (this.disabled) return;
|
||||
|
||||
const radios = this.getAllRadios();
|
||||
const checked = radios.find(radio => radio.checked);
|
||||
const firstEnabledRadio = radios.find(radio => !radio.disabled);
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
}
|
||||
|
||||
/* Disabled */
|
||||
:host([disabled]) {
|
||||
:host(:state(disabled)) {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
@@ -142,7 +142,7 @@
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
:host([appearance='button']:hover:not([disabled], :state(checked))) {
|
||||
:host([appearance='button']:hover:not(:state(disabled), :state(checked))) {
|
||||
background-color: color-mix(in srgb, var(--wa-color-surface-default) 95%, var(--wa-color-mix-hover));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,9 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
|
||||
|
||||
@state() checked = false;
|
||||
|
||||
/** @internal Used by radio group to force disable radios while preserving their original disabled state. */
|
||||
@state() forceDisabled = false;
|
||||
|
||||
/**
|
||||
* The string pointing to a form's id.
|
||||
*/
|
||||
@@ -79,7 +82,7 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
|
||||
private setInitialAttributes() {
|
||||
this.setAttribute('role', 'radio');
|
||||
this.tabIndex = 0;
|
||||
this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
|
||||
this.setAttribute('aria-disabled', this.disabled || this.forceDisabled ? 'true' : 'false');
|
||||
}
|
||||
|
||||
updated(changedProperties: PropertyValues<this>) {
|
||||
@@ -88,12 +91,24 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
|
||||
if (changedProperties.has('checked')) {
|
||||
this.customStates.set('checked', this.checked);
|
||||
this.setAttribute('aria-checked', this.checked ? 'true' : 'false');
|
||||
this.tabIndex = this.checked ? 0 : -1;
|
||||
// Only set tabIndex if not disabled
|
||||
if (!this.disabled && !this.forceDisabled) {
|
||||
this.tabIndex = this.checked ? 0 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (changedProperties.has('disabled')) {
|
||||
this.customStates.set('disabled', this.disabled);
|
||||
this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
|
||||
if (changedProperties.has('disabled') || changedProperties.has('forceDisabled')) {
|
||||
const effectivelyDisabled = this.disabled || this.forceDisabled;
|
||||
this.customStates.set('disabled', effectivelyDisabled);
|
||||
this.setAttribute('aria-disabled', effectivelyDisabled ? 'true' : 'false');
|
||||
|
||||
// Set tabIndex based on disabled state
|
||||
if (effectivelyDisabled) {
|
||||
this.tabIndex = -1;
|
||||
} else {
|
||||
// Restore proper tabIndex - this will be managed by the radio group
|
||||
this.tabIndex = this.checked ? 0 : -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,8 +119,9 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
|
||||
// We override `setValue` because we don't want to set form values from here. We want to do that in "RadioGroup" itself.
|
||||
}
|
||||
|
||||
// Update the handleClick method (around line 75)
|
||||
private handleClick = () => {
|
||||
if (!this.disabled) {
|
||||
if (!this.disabled && !this.forceDisabled) {
|
||||
this.checked = true;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user