-
+
${!this.indeterminate ? html` ` : ''}
diff --git a/src/components/radio-button/radio-button.css b/src/components/radio-button/radio-button.css
index 47b93cb80..1319aca64 100644
--- a/src/components/radio-button/radio-button.css
+++ b/src/components/radio-button/radio-button.css
@@ -32,7 +32,6 @@
/*
* Checked buttons
*/
-
:host([checked]) {
--indicator-color: var(--wa-form-control-activated-color);
--indicator-width: var(--wa-border-width-s);
@@ -43,3 +42,41 @@
--border-color: var(--indicator-color);
}
}
+
+/*
+ * Active buttons
+ */
+button:active {
+ --text-color-active: var(--wa-form-control-activated-color);
+ --border-color-active: var(--wa-form-control-activated-color);
+}
+
+/* Horizontal radio pill buttons */
+:host([data-wa-radio-horizontal][data-wa-radio-first]:not([data-wa-radio-last])) .wa-pill {
+ border-start-end-radius: 0;
+ border-end-end-radius: 0;
+}
+
+:host([data-wa-radio-horizontal][data-wa-radio-inner]) .wa-pill {
+ border-radius: 0;
+}
+
+:host([data-wa-radio-horizontal][data-wa-radio-last]:not([data-wa-radio-first])) .wa-pill {
+ border-start-start-radius: 0;
+ border-end-start-radius: 0;
+}
+
+/* Vertical radio pill buttons */
+:host([data-wa-radio-vertical][data-wa-radio-first]:not([data-wa-radio-last])) .wa-pill {
+ border-end-start-radius: 0;
+ border-end-end-radius: 0;
+}
+
+:host([data-wa-radio-vertical][data-wa-radio-inner]) .wa-pill {
+ border-radius: 0;
+}
+
+:host([data-wa-radio-vertical][data-wa-radio-last]:not([data-wa-radio-first])) .wa-pill {
+ border-start-start-radius: 0;
+ border-start-end-radius: 0;
+}
diff --git a/src/components/radio-button/radio-button.ts b/src/components/radio-button/radio-button.ts
index 8edadd9e4..b0582d026 100644
--- a/src/components/radio-button/radio-button.ts
+++ b/src/components/radio-button/radio-button.ts
@@ -9,7 +9,6 @@ import nativeStyles from '../../styles/native/button.css';
import appearanceStyles from '../../styles/utilities/appearance.css';
import sizeStyles from '../../styles/utilities/size.css';
import variantStyles from '../../styles/utilities/variants.css';
-import buttonStyles from '../button/button.css';
import styles from './radio-button.css';
/**
@@ -49,7 +48,7 @@ import styles from './radio-button.css';
*/
@customElement('wa-radio-button')
export default class WaRadioButton extends WebAwesomeFormAssociatedElement {
- static shadowStyle = [variantStyles, appearanceStyles, sizeStyles, nativeStyles, buttonStyles, styles];
+ static shadowStyle = [variantStyles, appearanceStyles, sizeStyles, nativeStyles, styles];
static rectProxy = 'input';
private readonly hasSlotController = new HasSlotController(this, '[default]', 'prefix', 'suffix');
diff --git a/src/components/radio-group/radio-group.ts b/src/components/radio-group/radio-group.ts
index 0426abb18..c27474cb0 100644
--- a/src/components/radio-group/radio-group.ts
+++ b/src/components/radio-group/radio-group.ts
@@ -38,8 +38,6 @@ import styles from './radio-group.css';
* @csspart form-control-input - The input's wrapper.
* @csspart radios - The wrapper than surrounds radio items, styled as a flex container by default.
* @csspart hint - The hint's wrapper.
- * @csspart button-group - The button group that wraps radio buttons.
- * @csspart button-group__base - The button group's `base` part.
*/
@customElement('wa-radio-group')
export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
@@ -65,7 +63,6 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
@query('slot:not([name])') defaultSlot: HTMLSlotElement;
- @state() private hasButtonGroup = false;
@state() private hasRadioButtons = false;
/**
@@ -150,7 +147,7 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
clickedRadio.checked = true;
const radios = this.getAllRadios();
- const hasButtonGroup = radios.some(radio => radio.tagName.toLowerCase() === 'wa-radio-button');
+ const hasRadioButtons = radios.some(radio => radio.tagName.toLowerCase() === 'wa-radio-button');
for (const radio of radios) {
if (clickedRadio === radio) {
continue;
@@ -158,7 +155,7 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
radio.checked = false;
- if (!hasButtonGroup) {
+ if (!hasRadioButtons) {
radio.setAttribute('tabindex', '-1');
}
}
@@ -183,6 +180,15 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
// Detect the presence of radio buttons
this.hasRadioButtons = radios.some(radio => radio.localName === 'wa-radio-button');
+ // Add data attributes to support styling
+ radios.forEach((radio, index) => {
+ radio.toggleAttribute('data-wa-radio-horizontal', this.orientation !== 'vertical');
+ radio.toggleAttribute('data-wa-radio-vertical', this.orientation === 'vertical');
+ 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);
+ });
+
await Promise.all(
// Sync the checked state and size
radios.map(async radio => {
@@ -196,10 +202,8 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
}),
);
- this.hasButtonGroup = radios.some(radio => radio.tagName.toLowerCase() === 'wa-radio-button');
-
if (radios.length > 0 && !radios.some(radio => radio.checked)) {
- if (this.hasButtonGroup) {
+ if (this.hasRadioButtons) {
const buttonRadio = radios[0].shadowRoot?.querySelector('button');
if (buttonRadio) {
@@ -210,7 +214,7 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
}
}
- if (this.hasButtonGroup) {
+ if (this.hasRadioButtons) {
const buttonGroup = this.shadowRoot?.querySelector('wa-button-group');
if (buttonGroup) {
@@ -276,12 +280,12 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
index = 0;
}
- const hasButtonGroup = radios.some(radio => radio.tagName.toLowerCase() === 'wa-radio-button');
+ const hasRadioButtons = radios.some(radio => radio.tagName.toLowerCase() === 'wa-radio-button');
this.getAllRadios().forEach(radio => {
radio.checked = false;
- if (!hasButtonGroup) {
+ if (!hasRadioButtons) {
radio.setAttribute('tabindex', '-1');
}
});
@@ -289,7 +293,7 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
this.value = radios[index].value;
radios[index].checked = true;
- if (!hasButtonGroup) {
+ if (!hasRadioButtons) {
radios[index].setAttribute('tabindex', '0');
radios[index].focus();
} else {
@@ -351,7 +355,7 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
` elements. You can use `` to group items visually.
* @slot label - The input's label. Alternatively, you can use the `label` attribute.
@@ -670,9 +672,9 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
this.selectionChanged();
}
- // This method must be called whenever the selection changes. It will update the selected options cache, the current
- // value, and the display value
- private selectionChanged() {
+ // @internal This method must be called whenever the selection changes. It will update the selected options cache, the
+ // current value, and the display value. The option component uses it internally to update labels as they change.
+ public selectionChanged() {
const options = this.getAllOptions();
// Update selected options cache
@@ -711,6 +713,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
this.updateValidity();
});
}
+
protected get tags() {
return this.selectedOptions.map((option, index) => {
if (index < this.maxOptionsVisible || this.maxOptionsVisible <= 0) {
diff --git a/src/components/tab/tab.css b/src/components/tab/tab.css
index 8311b1e50..a87dd526a 100644
--- a/src/components/tab/tab.css
+++ b/src/components/tab/tab.css
@@ -15,6 +15,14 @@
-webkit-user-select: none;
cursor: pointer;
transition: color var(--wa-transition-fast) var(--wa-transition-easing);
+
+ ::slotted(wa-icon:first-child) {
+ margin-inline-end: var(--wa-space-xs);
+ }
+
+ ::slotted(wa-icon:last-child) {
+ margin-inline-start: var(--wa-space-xs);
+ }
}
:host(:hover:not([disabled])) .tab {
diff --git a/src/styles/native/button.css b/src/styles/native/button.css
index 01e93e9cb..c5e76aeef 100644
--- a/src/styles/native/button.css
+++ b/src/styles/native/button.css
@@ -81,10 +81,6 @@ input:is([type='button'], [type='reset'], [type='submit']),
* States
*/
- &::-moz-focus-inner {
- border: 0;
- }
-
&:focus {
outline: none;
}
@@ -103,6 +99,11 @@ input:is([type='button'], [type='reset'], [type='submit']),
pointer-events: none;
}
}
+
+ /* Keep it last so Safari doesn't stop parsing this block */
+ &::-moz-focus-inner {
+ border: 0;
+ }
}
/**
diff --git a/src/styles/native/checkbox.css b/src/styles/native/checkbox.css
index 06ea80516..447a2a866 100644
--- a/src/styles/native/checkbox.css
+++ b/src/styles/native/checkbox.css
@@ -38,6 +38,16 @@ input[type='checkbox']:where(:not(:host *)) {
height: 100%;
width: 100%;
}
+
+ &:indeterminate::after {
+ background-color: currentColor;
+ content: '';
+ mask: url('data:image/svg+xml;utf8, ')
+ center no-repeat;
+ position: absolute;
+ height: 100%;
+ width: 100%;
+ }
}
input[type='checkbox']:where(:not(:host *)),
diff --git a/src/styles/utilities/button-group.css b/src/styles/utilities/button-group.css
index e4adb4d7a..95b4dd231 100644
--- a/src/styles/utilities/button-group.css
+++ b/src/styles/utilities/button-group.css
@@ -20,6 +20,11 @@
}
/* Horizontal */
+.wa-button-group[aria-orientation='horizontal'] {
+ /* TODO - see https://github.com/shoelace-style/webawesome/issues/374 */
+ align-items: end;
+}
+
.wa-button-group:not([aria-orientation='vertical']):not(.wa-button-group-vertical) {
> :not(:first-child),
&::slotted(:not(:first-child)) {
diff --git a/src/webawesome.loader.ts b/src/webawesome.loader.ts
index 868636d7f..48ec46d41 100644
--- a/src/webawesome.loader.ts
+++ b/src/webawesome.loader.ts
@@ -3,3 +3,12 @@ import { startLoader } from './webawesome.js';
export * from './webawesome.js';
startLoader();
+
+// Remove `wa-cloak` when the autoloader finishes OR after two seconds. This prevents the entire screen from flashing
+// when unregistered components get added later on.
+Promise.race([
+ new Promise(resolve => document.addEventListener('wa-discovery-complete', resolve)),
+ new Promise(resolve => setTimeout(resolve, 2000)),
+]).then(() => {
+ document.querySelectorAll('.wa-cloak').forEach(el => el.classList.remove('wa-cloak'));
+});