From 426a242d26c42dc48dc1ecf0199d2f42ae4dd6ae Mon Sep 17 00:00:00 2001 From: Lea Verou Date: Tue, 17 Dec 2024 01:57:48 -0500 Subject: [PATCH] Define button-group util and use it in button-group and radio-group --- src/components/button-group/button-group.css | 16 ----- src/components/button-group/button-group.ts | 14 ++-- src/components/button/button.css | 70 -------------------- src/components/radio-group/radio-group.css | 4 ++ src/components/radio-group/radio-group.ts | 20 +++--- src/styles/utilities/button-group.css | 57 ++++++++++++++++ 6 files changed, 76 insertions(+), 105 deletions(-) create mode 100644 src/styles/utilities/button-group.css diff --git a/src/components/button-group/button-group.css b/src/components/button-group/button-group.css index e835cadcb..feb4b28f5 100644 --- a/src/components/button-group/button-group.css +++ b/src/components/button-group/button-group.css @@ -1,19 +1,3 @@ :host { display: inline-flex; } - -.button-group { - display: flex; - position: relative; - flex-wrap: wrap; - isolation: isolate; -} - -:host([orientation='vertical']) .button-group { - flex-direction: column; -} - -/* Show the focus indicator above other buttons */ -::slotted(:focus) { - z-index: 1 !important; -} diff --git a/src/components/button-group/button-group.ts b/src/components/button-group/button-group.ts index 656a3bd2e..bf188cd52 100644 --- a/src/components/button-group/button-group.ts +++ b/src/components/button-group/button-group.ts @@ -1,6 +1,7 @@ import { html } from 'lit'; import { customElement, property, query, state } from 'lit/decorators.js'; import WebAwesomeElement from '../../internal/webawesome-element.js'; +import buttonGroupStyles from '../../styles/utilities/button-group.css'; import styles from './button-group.css'; /** @@ -15,7 +16,7 @@ import styles from './button-group.css'; */ @customElement('wa-button-group') export default class WaButtonGroup extends WebAwesomeElement { - static shadowStyle = styles; + static shadowStyle = [buttonGroupStyles, styles]; @query('slot') defaultSlot: HTMLSlotElement; @@ -82,20 +83,19 @@ export default class WaButtonGroup extends WebAwesomeElement { } render() { - // eslint-disable-next-line lit-a11y/mouse-events-have-key-events return html` -
- -
+ @slotchange=${this.handleSlotChange} + > `; } } diff --git a/src/components/button/button.css b/src/components/button/button.css index 326384048..b230a3967 100644 --- a/src/components/button/button.css +++ b/src/components/button/button.css @@ -397,76 +397,6 @@ } */ -:host(.wa-button-group__button--inner) .button { - border-radius: 0; -} - -:host(.wa-button-group-horizontal.wa-button-group__button--first:not(.wa-button-group__button--last)) .button { - border-start-end-radius: 0; - border-end-end-radius: 0; -} - -:host(.wa-button-group-horizontal.wa-button-group__button--last:not(.wa-button-group__button--first)) .button { - border-start-start-radius: 0; - border-end-start-radius: 0; -} - -:host(.wa-button-group-vertical.wa-button-group__button--first:not(.wa-button-group__button--last)) .button { - border-end-start-radius: 0; - border-end-end-radius: 0; -} - -:host(.wa-button-group-vertical.wa-button-group__button--last:not(.wa-button-group__button--first)) .button { - border-start-start-radius: 0; - border-start-end-radius: 0; -} - -/* All except the first */ -:host( - .wa-button-group-horizontal.wa-button-group-horizontal.wa-button-group__button:not(.wa-button-group__button--first) - ) { - margin-inline-start: calc(-1 * var(--border-width)); -} - -:host( - .wa-button-group-vertical.wa-button-group-horizontal.wa-button-group__button:not(.wa-button-group__button--first) - ) { - margin-block-start: calc(-1 * var(--border-width)); -} - -/* Add a visual separator between filled buttons */ -:host(.wa-button-group__button:not(.wa-button-group__button--first, .wa-button-group__button--radio)) .button:after { - content: ''; - position: absolute; - z-index: 2; /* Keep separators visible on hover */ -} - -:host( - .wa-button-group-horizontal.wa-button-group__button:not( - .wa-button-group__button--first, - .wa-button-group__button--radio - ) - ) - .button:after { - top: 0; - bottom: 0; - inset-inline-start: 0; - border-left: solid max(var(--border-width), 1px) var(--border-color, rgb(0 0 0 / 0.3)); -} - -:host( - .wa-button-group-vertical.wa-button-group__button:not( - .wa-button-group__button--first, - .wa-button-group__button--radio - ) - ) - .button:after { - left: 0; - right: 0; - inset-block-start: 0; - border-top: solid max(var(--border-width), 1px) var(--border-color, rgb(0 0 0 / 0.3)); -} - /* Bump hovered, focused, and checked buttons up so their focus ring isn't clipped */ :host(.wa-button-group__button--hover) { z-index: 1; diff --git a/src/components/radio-group/radio-group.css b/src/components/radio-group/radio-group.css index 0aa46c352..b3b244f0a 100644 --- a/src/components/radio-group/radio-group.css +++ b/src/components/radio-group/radio-group.css @@ -17,3 +17,7 @@ content: var(--wa-form-control-required-content); margin-inline-start: var(--wa-form-control-required-content-offset); } + +.wa-button-group { + display: flex; +} diff --git a/src/components/radio-group/radio-group.ts b/src/components/radio-group/radio-group.ts index 1f78b9ef9..8be532030 100644 --- a/src/components/radio-group/radio-group.ts +++ b/src/components/radio-group/radio-group.ts @@ -9,7 +9,8 @@ import { RequiredValidator } from '../../internal/validators/required-validator. import { watch } from '../../internal/watch.js'; import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-element.js'; import formControlStyles from '../../styles/shadow/form-control.css'; -import '../button-group/button-group.js'; +import sizeStyles from '../../styles/shadow/size.css'; +import buttonGroupStyles from '../../styles/utilities/button-group.css'; import type WaRadioButton from '../radio-button/radio-button.js'; import '../radio/radio.js'; import type WaRadio from '../radio/radio.js'; @@ -41,7 +42,7 @@ import styles from './radio-group.css'; */ @customElement('wa-radio-group') export default class WaRadioGroup extends WebAwesomeFormAssociatedElement { - static shadowStyle = [formControlStyles, styles]; + static shadowStyle = [sizeStyles, buttonGroupStyles, formControlStyles, styles]; static get validators() { const validators = isServer @@ -314,7 +315,6 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement { const hasHintSlot = this.hasUpdated ? this.hasSlotController.test('hint') : this.withHint; const hasLabel = this.label ? true : !!hasLabelSlot; const hasHint = this.hint ? true : !!hasHintSlot; - const defaultSlot = html` `; return html`
${this.label} - + :hover, + &::slotted(:hover) { + z-index: 1; + } + + /* Focus and checked are always on top */ + > :focus, + &::slotted(:focus), + > [aria-checked='true'], + > [checked], + &::slotted([aria-checked='true']), + &::slotted([checked]) { + z-index: 2 !important; + } +} + +/* Horizontal */ +.wa-button-group:not([aria-orientation='vertical']) { + > :not(:first-child), + &::slotted(:not(:first-child)) { + border-start-start-radius: 0 !important; + border-end-start-radius: 0 !important; + border-inline-start-color: var(--border-color, rgb(0 0 0 / 0.3)); + margin-inline-start: calc(-1 * var(--border-width, 1px)); + } + + > :not(:last-child), + &::slotted(:not(:last-child)) { + border-start-end-radius: 0 !important; + border-end-end-radius: 0 !important; + } +} + +/* Vertical */ +.wa-button-group[aria-orientation='vertical'] { + flex-direction: column; + + > :not(:first-child), + &::slotted(:not(:first-child)) { + border-start-start-radius: 0 !important; + border-start-end-radius: 0 !important; + border-block-start-color: var(--border-color, rgb(0 0 0 / 0.3)); + margin-block-start: calc(-1 * var(--border-width, 1px)); + } + + > :not(:last-child), + &::slotted(:not(:last-child)) { + border-end-start-radius: 0 !important; + border-end-end-radius: 0 !important; + } +}