Compare commits

...

6 Commits

Author SHA1 Message Date
Cory LaViska
4e9223953e remove unused controller 2024-03-21 15:10:24 -04:00
Cory LaViska
cf9044d826 remove unused controller 2024-03-21 15:10:16 -04:00
Cory LaViska
252d710175 remove slot detection from card, dialog, drawer 2024-03-21 14:47:29 -04:00
Cory LaViska
58d14ed1d2 remove slot detection from buttons 2024-03-21 13:55:40 -04:00
Cory LaViska
879e912a5c remove slot detection from breadcrumb items 2024-03-21 13:55:31 -04:00
Cory LaViska
c69e98cd7f remove slot detection from alert 2024-03-21 13:55:22 -04:00
26 changed files with 209 additions and 346 deletions

View File

@@ -328,7 +328,7 @@ wa-select[label="Signet"]::part(form-control-help-text) {
</div>
</form>
<wa-dialog id="icon-chooser" label="Browse Icons">
<wa-dialog id="icon-chooser" label="Browse Icons" with-header>
<div style="display: grid; grid-template-rows: minmax(0, auto) minmax(0, 1fr); height: 100%; gap: 1rem;">
<div style="display: flex; gap: 1.25rem;">
<wa-input name="icon-search" placeholder="Search Icons" clearable style="flex: 1 1 auto;">

View File

@@ -9,6 +9,15 @@ layout: ../../../layouts/ComponentLayout.astro
<wa-icon slot="icon" name="circle-info" variant="regular"></wa-icon>
This is a standard alert. You can customize its content and even the icon.
</wa-alert>
<wa-alert open>
<wa-icon slot="icon" name="circle-info" variant="regular"></wa-icon>
<div style="border: solid 2px tomato; height: 2rem;"></div>
</wa-alert>
<wa-alert open>
<div style="border: solid 2px tomato; height: 2rem;"></div>
</wa-alert>
```
```jsx:react

View File

@@ -5,7 +5,7 @@ layout: ../../../layouts/ComponentLayout.astro
---
```html:preview
<wa-card class="card-overview">
<wa-card with-image with-footer class="card-overview">
<img
slot="image"
src="https://images.unsplash.com/photo-1559209172-0ff8f6d49ff7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80"
@@ -62,7 +62,7 @@ const css = `
const App = () => (
<>
<WaCard className="card-overview">
<WaCard with-image with-footer className="card-overview">
<img
slot="image"
src="https://images.unsplash.com/photo-1559209172-0ff8f6d49ff7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80"
@@ -129,7 +129,7 @@ const App = () => (
Headers can be used to display titles and more.
```html:preview
<wa-card class="card-header">
<wa-card with-header class="card-header">
<div slot="header">
Header Title
<wa-icon-button name="gear" variant="solid" label="Settings"></wa-icon-button>
@@ -185,7 +185,7 @@ const css = `
const App = () => (
<>
<WaCard className="card-header">
<WaCard with-header className="card-header">
<div slot="header">
Header Title
<WaIconButton name="gear" variant="solid"></WaIconButton>
@@ -203,7 +203,7 @@ const App = () => (
Footers can be used to display actions, summaries, or other relevant content.
```html:preview
<wa-card class="card-footer">
<wa-card with-footer class="card-footer">
This card has a footer. You can put all sorts of things in it!
<div slot="footer">
@@ -244,7 +244,7 @@ const css = `
const App = () => (
<>
<WaCard className="card-footer">
<WaCard with-footer className="card-footer">
This card has a footer. You can put all sorts of things in it!
<div slot="footer">
<WaRating></WaRating>
@@ -264,7 +264,7 @@ const App = () => (
Cards accept an `image` slot. The image is displayed atop the card and stretches to fit.
```html:preview
<wa-card class="card-image">
<wa-card with-image class="card-image">
<img
slot="image"
src="https://images.unsplash.com/photo-1547191783-94d5f8f6d8b1?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=400&q=80"
@@ -291,7 +291,7 @@ const css = `
const App = () => (
<>
<WaCard className="card-image">
<WaCard with-image className="card-image">
<img
slot="image"
src="https://images.unsplash.com/photo-1547191783-94d5f8f6d8b1?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=400&q=80"

View File

@@ -7,7 +7,7 @@ layout: ../../../layouts/ComponentLayout.astro
<!-- cspell:dictionaries lorem-ipsum -->
```html:preview
<wa-dialog label="Dialog" class="dialog-overview">
<wa-dialog label="Dialog" with-header with-footer class="dialog-overview">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<wa-button slot="footer" variant="brand">Close</wa-button>
</wa-dialog>
@@ -34,7 +34,7 @@ const App = () => {
return (
<>
<WaDialog label="Dialog" open={open} onWaAfterHide={() => setOpen(false)}>
<WaDialog label="Dialog" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
Close
@@ -54,7 +54,7 @@ const App = () => {
Use the `--width` custom property to set the dialog's width.
```html:preview
<wa-dialog label="Dialog" class="dialog-width" style="--width: 50vw;">
<wa-dialog label="Dialog" with-header with-footer class="dialog-width" style="--width: 50vw;">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<wa-button slot="footer" variant="brand">Close</wa-button>
</wa-dialog>
@@ -81,7 +81,7 @@ const App = () => {
return (
<>
<WaDialog label="Dialog" open={open} style={{ '--width': '50vw' }} onWaAfterHide={() => setOpen(false)}>
<WaDialog label="Dialog" with-header with-footer open={open} style={{ '--width': '50vw' }} onWaAfterHide={() => setOpen(false)}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
Close
@@ -99,7 +99,7 @@ const App = () => {
By design, a dialog's height will never exceed that of the viewport. As such, dialogs will not scroll with the page ensuring the header and footer are always accessible to the user.
```html:preview
<wa-dialog label="Dialog" class="dialog-scrolling">
<wa-dialog label="Dialog" with-header with-footer class="dialog-scrolling">
<div style="height: 150vh; border: dashed 2px var(--wa-color-surface-border); padding: 0 1rem;">
<p>Scroll down and give it a try! 👇</p>
</div>
@@ -128,7 +128,7 @@ const App = () => {
return (
<>
<WaDialog label="Dialog" open={open} onWaAfterHide={() => setOpen(false)}>
<WaDialog label="Dialog" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)}>
<div
style={{
height: '150vh',
@@ -155,7 +155,7 @@ const App = () => {
The header shows a functional close button by default. You can use the `header-actions` slot to add additional [icon buttons](/components/icon-button) if needed.
```html:preview
<wa-dialog label="Dialog" class="dialog-header-actions">
<wa-dialog label="Dialog" with-header with-footer class="dialog-header-actions">
<wa-icon-button class="new-window" slot="header-actions" name="arrow-up-right-from-square" variant="solid"></wa-icon-button>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<wa-button slot="footer" variant="brand">Close</wa-button>
@@ -186,7 +186,7 @@ const App = () => {
return (
<>
<WaDialog label="Dialog" open={open} onWaAfterHide={() => setOpen(false)}>
<WaDialog label="Dialog" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)}>
<WaIconButton
class="new-window"
slot="header-actions"
@@ -214,7 +214,7 @@ To keep the dialog open in such cases, you can cancel the `wa-request-close` eve
You can use `event.detail.source` to determine what triggered the request to close. This example prevents the dialog from closing when the overlay is clicked, but allows the close button or [[Escape]] to dismiss it.
```html:preview
<wa-dialog label="Dialog" class="dialog-deny-close">
<wa-dialog label="Dialog" with-header with-footer class="dialog-deny-close">
This dialog will not close when you click on the overlay.
<wa-button slot="footer" variant="brand">Close</wa-button>
</wa-dialog>
@@ -255,7 +255,7 @@ const App = () => {
return (
<>
<WaDialog label="Dialog" open={open} onWaRequestClose={handleRequestClose} onWaAfterHide={() => setOpen(false)}>
<WaDialog label="Dialog" with-header with-footer open={open} onWaRequestClose={handleRequestClose} onWaAfterHide={() => setOpen(false)}>
This dialog will not close when you click on the overlay.
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
Close
@@ -273,7 +273,7 @@ const App = () => {
By default, the dialog's panel will gain focus when opened. This allows a subsequent tab press to focus on the first tabbable element in the dialog. If you want a different element to have focus, add the `autofocus` attribute to it as shown below.
```html:preview
<wa-dialog label="Dialog" class="dialog-focus">
<wa-dialog label="Dialog" with-header with-footer class="dialog-focus">
<wa-input autofocus placeholder="I will have focus when the dialog is opened"></wa-input>
<wa-button slot="footer" variant="brand">Close</wa-button>
</wa-dialog>
@@ -302,7 +302,7 @@ const App = () => {
return (
<>
<WaDialog label="Dialog" open={open} onWaAfterHide={() => setOpen(false)}>
<WaDialog label="Dialog" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)}>
<WaInput autofocus placeholder="I will have focus when the dialog is opened" />
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
Close

View File

@@ -7,7 +7,7 @@ layout: ../../../layouts/ComponentLayout.astro
<!-- cspell:dictionaries lorem-ipsum -->
```html:preview
<wa-drawer label="Drawer" class="drawer-overview">
<wa-drawer label="Drawer" with-header with-footer class="drawer-overview">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<wa-button slot="footer" variant="brand">Close</wa-button>
</wa-drawer>
@@ -34,7 +34,7 @@ const App = () => {
return (
<>
<WaDrawer label="Drawer" open={open} onWaAfterHide={() => setOpen(false)}>
<WaDrawer label="Drawer" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
Close
@@ -54,7 +54,7 @@ const App = () => {
By default, drawers slide in from the end. To make the drawer slide in from the start, set the `placement` attribute to `start`.
```html:preview
<wa-drawer label="Drawer" placement="start" class="drawer-placement-start">
<wa-drawer label="Drawer" placement="start" with-header with-footer class="drawer-placement-start">
This drawer slides in from the start.
<wa-button slot="footer" variant="brand">Close</wa-button>
</wa-drawer>
@@ -81,7 +81,7 @@ const App = () => {
return (
<>
<WaDrawer label="Drawer" placement="start" open={open} onWaAfterHide={() => setOpen(false)}>
<WaDrawer label="Drawer" placement="start" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)}>
This drawer slides in from the start.
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
Close
@@ -99,7 +99,7 @@ const App = () => {
To make the drawer slide in from the top, set the `placement` attribute to `top`.
```html:preview
<wa-drawer label="Drawer" placement="top" class="drawer-placement-top">
<wa-drawer label="Drawer" placement="top" with-header with-footer class="drawer-placement-top">
This drawer slides in from the top.
<wa-button slot="footer" variant="brand">Close</wa-button>
</wa-drawer>
@@ -126,7 +126,7 @@ const App = () => {
return (
<>
<WaDrawer label="Drawer" placement="top" open={open} onWaAfterHide={() => setOpen(false)}>
<WaDrawer label="Drawer" placement="top" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)}>
This drawer slides in from the top.
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
Close
@@ -144,7 +144,7 @@ const App = () => {
To make the drawer slide in from the bottom, set the `placement` attribute to `bottom`.
```html:preview
<wa-drawer label="Drawer" placement="bottom" class="drawer-placement-bottom">
<wa-drawer label="Drawer" placement="bottom" with-header with-footer class="drawer-placement-bottom">
This drawer slides in from the bottom.
<wa-button slot="footer" variant="brand">Close</wa-button>
</wa-drawer>
@@ -171,7 +171,7 @@ const App = () => {
return (
<>
<WaDrawer label="Drawer" placement="bottom" open={open} onWaAfterHide={() => setOpen(false)}>
<WaDrawer label="Drawer" placement="bottom" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)}>
This drawer slides in from the bottom.
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
Close
@@ -184,84 +184,12 @@ const App = () => {
};
```
### Contained to an Element
By default, drawers slide out of their [containing block](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#Identifying_the_containing_block), which is usually the viewport. To make a drawer slide out of a parent element, add the `contained` attribute to the drawer and apply `position: relative` to its parent.
Unlike normal drawers, contained drawers are not modal. This means they do not show an overlay, they do not trap focus, and they are not dismissible with [[Escape]]. This is intentional to allow users to interact with elements outside of the drawer.
```html:preview
<div
style="position: relative; border: solid 2px var(--wa-color-surface-border); height: 300px; padding: 1rem; margin-bottom: 1rem;"
>
The drawer will be contained to this box. This content won't shift or be affected in any way when the drawer opens.
<wa-drawer label="Drawer" contained class="drawer-contained" style="--size: 50%;">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<wa-button slot="footer" variant="brand">Close</wa-button>
</wa-drawer>
</div>
<wa-button>Toggle Drawer</wa-button>
<script>
const drawer = document.querySelector('.drawer-contained');
const openButton = drawer.parentElement.nextElementSibling;
const closeButton = drawer.querySelector('wa-button[variant="brand"]');
openButton.addEventListener('click', () => (drawer.open = !drawer.open));
closeButton.addEventListener('click', () => drawer.hide());
</script>
```
```jsx:react
import { useState } from 'react';
import WaButton from '@shoelace-style/shoelace/dist/react/button';
import WaDrawer from '@shoelace-style/shoelace/dist/react/drawer';
const App = () => {
const [open, setOpen] = useState(false);
return (
<>
<div
style={{
position: 'relative',
border: 'solid 2px var(--wa-color-surface-border)',
height: '300px',
padding: '1rem',
marginBottom: '1rem'
}}
>
The drawer will be contained to this box. This content won't shift or be affected in any way when the drawer
opens.
<WaDrawer
label="Drawer"
contained
no-modal
open={open}
onWaAfterHide={() => setOpen(false)}
style={{ '--size': '50%' }}
>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
Close
</WaButton>
</WaDrawer>
</div>
<WaButton onClick={() => setOpen(true)}>Open Drawer</WaButton>
</>
);
};
```
### Custom Size
Use the `--size` custom property to set the drawer's size. This will be applied to the drawer's width or height depending on its `placement`.
```html:preview
<wa-drawer label="Drawer" class="drawer-custom-size" style="--size: 50vw;">
<wa-drawer label="Drawer" with-header with-footer class="drawer-custom-size" style="--size: 50vw;">
This drawer is always 50% of the viewport.
<wa-button slot="footer" variant="brand">Close</wa-button>
</wa-drawer>
@@ -288,7 +216,7 @@ const App = () => {
return (
<>
<WaDrawer label="Drawer" open={open} onWaAfterHide={() => setOpen(false)} style={{ '--size': '50vw' }}>
<WaDrawer label="Drawer" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)} style={{ '--size': '50vw' }}>
This drawer is always 50% of the viewport.
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
Close
@@ -306,7 +234,7 @@ const App = () => {
By design, a drawer's height will never exceed 100% of its container. As such, drawers will not scroll with the page to ensure the header and footer are always accessible to the user.
```html:preview
<wa-drawer label="Drawer" class="drawer-scrolling">
<wa-drawer label="Drawer" with-header with-footer class="drawer-scrolling">
<div style="height: 150vh; border: dashed 2px var(--wa-color-surface-border); padding: 0 1rem;">
<p>Scroll down and give it a try! 👇</p>
</div>
@@ -335,7 +263,7 @@ const App = () => {
return (
<>
<WaDrawer label="Drawer" open={open} onWaAfterHide={() => setOpen(false)}>
<WaDrawer label="Drawer" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)}>
<div
style={{
height: '150vh',
@@ -361,7 +289,7 @@ const App = () => {
The header shows a functional close button by default. You can use the `header-actions` slot to add additional [icon buttons](/components/icon-button) if needed.
```html:preview
<wa-drawer label="Drawer" class="drawer-header-actions">
<wa-drawer label="Drawer" with-header with-footer class="drawer-header-actions">
<wa-icon-button class="new-window" slot="header-actions" name="arrow-up-right-from-square" variant="solid"></wa-icon-button>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<wa-button slot="footer" variant="brand">Close</wa-button>
@@ -392,7 +320,7 @@ const App = () => {
return (
<>
<WaDrawer label="Drawer" open={open} onWaAfterHide={() => setOpen(false)}>
<WaDrawer label="Drawer" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)}>
<WaIconButton slot="header-actions" name="arrow-up-right-from-square" onClick={() => window.open(location.href)} />
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
@@ -415,7 +343,7 @@ To keep the drawer open in such cases, you can cancel the `wa-request-close` eve
You can use `event.detail.source` to determine what triggered the request to close. This example prevents the drawer from closing when the overlay is clicked, but allows the close button or [[Escape]] to dismiss it.
```html:preview
<wa-drawer label="Drawer" class="drawer-deny-close">
<wa-drawer label="Drawer" with-header with-footer class="drawer-deny-close">
This drawer will not close when you click on the overlay.
<wa-button slot="footer" variant="brand">Close</wa-button>
</wa-drawer>
@@ -456,7 +384,7 @@ const App = () => {
return (
<>
<WaDrawer label="Drawer" open={open} onWaRequestClose={handleRequestClose} onWaAfterHide={() => setOpen(false)}>
<WaDrawer label="Drawer" with-header with-footer open={open} onWaRequestClose={handleRequestClose} onWaAfterHide={() => setOpen(false)}>
This drawer will not close when you click on the overlay.
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
Save &amp; Close
@@ -474,7 +402,7 @@ const App = () => {
By default, the drawer's panel will gain focus when opened. This allows a subsequent tab press to focus on the first tabbable element in the drawer. If you want a different element to have focus, add the `autofocus` attribute to it as shown below.
```html:preview
<wa-drawer label="Drawer" class="drawer-focus">
<wa-drawer label="Drawer" with-header with-footer class="drawer-focus">
<wa-input autofocus placeholder="I will have focus when the drawer is opened"></wa-input>
<wa-button slot="footer" variant="brand">Close</wa-button>
</wa-drawer>
@@ -503,7 +431,7 @@ const App = () => {
return (
<>
<WaDrawer label="Drawer" open={open} onWaAfterHide={() => setOpen(false)}>
<WaDrawer label="Drawer" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)}>
<WaInput autofocus placeholder="I will have focus when the drawer is opened" />
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
Close

View File

@@ -6,7 +6,7 @@ description: TODO
## Card
```html:preview
<wa-card class="card-overview">
<wa-card with-image class="card-overview">
<img
slot="image"
src="https://images.unsplash.com/photo-1559209172-0ff8f6d49ff7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80"

View File

@@ -281,7 +281,7 @@ Internally, each component uses the [BEM methodology](http://getbem.com/) for cl
### Boolean Props
Boolean props should _always_ default to `false`, otherwise there's no way for the user to unset them using only attributes. To keep the API as friendly and consistent as possible, use a property such as `noHeader` and a corresponding kebab-case attribute such as `no-header`.
Boolean props should _always_ default to `false`, otherwise there's no way for the user to unset them using only attributes. To keep the API as friendly and consistent as possible, use a property such as `noValue` and a corresponding kebab-case attribute such as `no-value`.
When naming boolean props that hide or disable things, prefix them with `no-`, e.g. `no-spin-buttons` and avoid using other verbs such as `hide-` and `disable-` for consistency.

View File

@@ -15,7 +15,7 @@
fun.
</p>
<wa-dialog id="dialog"> I'm just a lowly dialog. </wa-dialog>
<wa-dialog id="dialog" label="Dialog" with-header> I'm just a lowly dialog. </wa-dialog>
<wa-button>Open Dialog</wa-button>
</wa-page>

View File

@@ -133,7 +133,7 @@
text-align: center;
"
>
<wa-card class="wa-card--muted" style="--padding: 8px">
<wa-card with-header class="wa-card--muted" style="--padding: 8px">
<h3 slot="header">Total listening time</h3>
<p>
@@ -146,7 +146,7 @@
</p>
</wa-card>
<wa-card class="wa-card--muted" style="--padding: 8px">
<wa-card with-header class="wa-card--muted" style="--padding: 8px">
<h3 slot="header">Total songs played</h3>
<p>
@@ -160,7 +160,7 @@
</p>
</wa-card>
<wa-card class="wa-card--muted" style="--padding: 8px">
<wa-card with-header class="wa-card--muted" style="--padding: 8px">
<h3 slot="header">Average listening session</h3>
<p>
@@ -174,7 +174,7 @@
</p>
</wa-card>
<wa-card class="wa-card--muted" style="--padding: 8px">
<wa-card with-header class="wa-card--muted" style="--padding: 8px">
<h3 slot="header">Average track listening time</h3>
<p>

View File

@@ -1,7 +1,6 @@
import { animateTo, stopAnimations } from '../../internal/animate.js';
import { classMap } from 'lit/directives/class-map.js';
import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry.js';
import { HasSlotController } from '../../internal/slot.js';
import { html } from 'lit';
import { LocalizeController } from '../../utilities/localize.js';
import { property, query } from 'lit/decorators.js';
@@ -55,7 +54,6 @@ export default class WaAlert extends WebAwesomeElement {
static dependencies = { 'wa-icon-button': WaIconButton };
private autoHideTimeout: number;
private readonly hasSlotController = new HasSlotController(this, 'icon', 'suffix');
private readonly localize = new LocalizeController(this);
@query('[part~="base"]') base: HTMLElement;
@@ -198,7 +196,6 @@ export default class WaAlert extends WebAwesomeElement {
alert: true,
'alert--open': this.open,
'alert--closable': this.closable,
'alert--has-icon': this.hasSlotController.test('icon'),
'alert--brand': this.variant === 'brand',
'alert--success': this.variant === 'success',
'alert--neutral': this.variant === 'neutral',

View File

@@ -56,10 +56,10 @@ export default css`
border-width: var(--border-width);
color: var(--content-color);
font: inherit;
padding: var(--padding);
margin: inherit;
}
.alert:not(.alert--has-icon) .alert__icon,
.alert:not(.alert--closable) .alert__close-button {
display: none;
}
@@ -70,13 +70,15 @@ export default css`
align-items: center;
color: var(--icon-color);
font-size: var(--icon-size);
padding-inline-start: var(--padding);
}
.alert__icon ::slotted(*) {
margin-inline-end: var(--padding) !important;
}
.alert__message {
flex: 1 1 auto;
display: block;
padding: var(--padding);
overflow: hidden;
}
@@ -86,7 +88,7 @@ export default css`
align-items: center;
color: currentColor;
font-size: var(--wa-font-size-m);
padding-inline-end: var(--padding);
padding-inline-start: var(--padding);
}
.alert__close-button:hover::part(base) {

View File

@@ -1,5 +1,3 @@
import { classMap } from 'lit/directives/class-map.js';
import { HasSlotController } from '../../internal/slot.js';
import { html } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js';
import { property } from 'lit/decorators.js';
@@ -29,8 +27,6 @@ import type { CSSResultGroup } from 'lit';
export default class WaBreadcrumbItem extends WebAwesomeElement {
static styles: CSSResultGroup = [componentStyles, styles];
private readonly hasSlotController = new HasSlotController(this, 'prefix', 'suffix');
/**
* Optional URL to direct the user to when the breadcrumb item is activated. When set, a link will be rendered
* internally. When unset, a button will be rendered instead.
@@ -47,14 +43,7 @@ export default class WaBreadcrumbItem extends WebAwesomeElement {
const isLink = this.href ? true : false;
return html`
<div
part="base"
class=${classMap({
'breadcrumb-item': true,
'breadcrumb-item--has-prefix': this.hasSlotController.test('prefix'),
'breadcrumb-item--has-suffix': this.hasSlotController.test('suffix')
})}
>
<div part="base" class="breadcrumb-item">
<span part="prefix" class="breadcrumb-item__prefix">
<slot name="prefix"></slot>
</span>

View File

@@ -58,14 +58,13 @@ export default css`
align-items: center;
}
.breadcrumb-item--has-prefix .breadcrumb-item__prefix {
.breadcrumb-item__prefix,
.breadcrumb-item__suffix {
display: inline-flex;
margin-inline-end: var(--wa-space-s);
}
.breadcrumb-item--has-suffix .breadcrumb-item__suffix {
display: inline-flex;
margin-inline-start: var(--wa-space-s);
::slotted(*) {
margin-inline-end: var(--wa-space-s) !important;
}
}
:host(:last-of-type) .breadcrumb-item__separator {

View File

@@ -1,6 +1,5 @@
import { classMap } from 'lit/directives/class-map.js';
import { FormControlController, validValidityState } from '../../internal/form.js';
import { HasSlotController } from '../../internal/slot.js';
import { html, literal } from 'lit/static-html.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { LocalizeController } from '../../utilities/localize.js';
@@ -63,7 +62,6 @@ export default class WaButton extends WebAwesomeElement implements WebAwesomeFor
private readonly formControlController = new FormControlController(this, {
assumeInteractionOn: ['click']
});
private readonly hasSlotController = new HasSlotController(this, '[default]', 'prefix', 'suffix');
private readonly localize = new LocalizeController(this);
@query('.button') button: HTMLButtonElement | HTMLLinkElement;
@@ -288,10 +286,7 @@ export default class WaButton extends WebAwesomeElement implements WebAwesomeFor
'button--standard': !this.outline,
'button--outline': this.outline,
'button--pill': this.pill,
'button--rtl': this.localize.dir() === 'rtl',
'button--has-label': this.hasSlotController.test('[default]'),
'button--has-prefix': this.hasSlotController.test('prefix'),
'button--has-suffix': this.hasSlotController.test('suffix')
'button--rtl': this.localize.dir() === 'rtl'
})}
?disabled=${ifDefined(isLink ? undefined : this.disabled)}
type=${ifDefined(isLink ? undefined : this.type)}

View File

@@ -213,13 +213,13 @@ export default css`
pointer-events: none;
}
.button:hover:not(.button--disabled) {
.button:hover:not(.button--disabled, .button--loading) {
background: var(--background-hover, var(--background, none));
border-color: var(--border-color-hover, var(--border-color, transparent));
color: var(--label-color-hover, var(--label-color));
}
.button:active:not(.button--disabled) {
.button:active:not(.button--disabled, .button--loading) {
background: var(--background-active, var(--background, none));
border-color: var(--border-color-active, var(--border-color, transparent));
color: var(--label-color-active, var(--label-color));
@@ -300,6 +300,21 @@ export default css`
align-items: center;
}
.button--small .button__caret {
margin-inline-start: calc(-0.5 * var(--wa-space-xs));
margin-inline-end: var(--wa-space-xs);
}
.button--medium .button__caret {
margin-inline-start: calc(-0.5 * var(--wa-space-s));
margin-inline-end: var(--wa-space-s);
}
.button--large .button__caret {
margin-inline-start: calc(-0.5 * var(--wa-space-m));
margin-inline-end: var(--wa-space-m);
}
.button--caret .button__caret::part(svg) {
width: 0.875em;
height: 0.875em;
@@ -354,70 +369,46 @@ export default css`
* Button spacing
*/
.button--has-label.button--small .button__label {
padding: 0 var(--wa-space-s);
.button--small {
::slotted([slot='prefix']) {
margin-inline-start: var(--wa-space-xs) !important;
}
::slotted([slot='suffix']) {
margin-inline-end: var(--wa-space-xs) !important;
}
.button__label {
padding: 0 var(--wa-space-s);
}
}
.button--has-label.button--medium .button__label {
padding: 0 var(--wa-space-m);
.button--medium {
::slotted([slot='prefix']) {
margin-inline-start: var(--wa-space-s) !important;
}
::slotted([slot='suffix']) {
margin-inline-end: var(--wa-space-s) !important;
}
.button__label {
padding: 0 var(--wa-space-m);
}
}
.button--has-label.button--large .button__label {
padding: 0 var(--wa-space-l);
}
.button--large {
::slotted([slot='prefix']) {
margin-inline-start: var(--wa-space-m) !important;
}
.button--has-prefix.button--small {
padding-inline-start: var(--wa-space-xs);
}
::slotted([slot='suffix']) {
margin-inline-end: var(--wa-space-m) !important;
}
.button--has-prefix.button--small .button__label {
padding-inline-start: var(--wa-space-xs);
}
.button--has-prefix.button--medium {
padding-inline-start: var(--wa-space-s);
}
.button--has-prefix.button--medium .button__label {
padding-inline-start: var(--wa-space-s);
}
.button--has-prefix.button--large {
padding-inline-start: var(--wa-space-m);
}
.button--has-prefix.button--large .button__label {
padding-inline-start: var(--wa-space-m);
}
.button--has-suffix.button--small,
.button--caret.button--small {
padding-inline-end: var(--wa-space-xs);
}
.button--has-suffix.button--small .button__label,
.button--caret.button--small .button__label {
padding-inline-end: var(--wa-space-xs);
}
.button--has-suffix.button--medium,
.button--caret.button--medium {
padding-inline-end: var(--wa-space-s);
}
.button--has-suffix.button--medium .button__label,
.button--caret.button--medium .button__label {
padding-inline-end: var(--wa-space-s);
}
.button--has-suffix.button--large,
.button--caret.button--large {
padding-inline-end: var(--wa-space-m);
}
.button--has-suffix.button--large .button__label,
.button--caret.button--large .button__label {
padding-inline-end: var(--wa-space-m);
.button__label {
padding: 0 var(--wa-space-l);
}
}
/*

View File

@@ -1,6 +1,6 @@
import { classMap } from 'lit/directives/class-map.js';
import { HasSlotController } from '../../internal/slot.js';
import { html } from 'lit';
import { property } from 'lit/decorators.js';
import componentStyles from '../../styles/component.styles.js';
import styles from './card.styles.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
@@ -34,7 +34,14 @@ import type { CSSResultGroup } from 'lit';
export default class WaCard extends WebAwesomeElement {
static styles: CSSResultGroup = [componentStyles, styles];
private readonly hasSlotController = new HasSlotController(this, 'footer', 'header', 'image');
/** Renders the card with a header */
@property({ attribute: 'with-header', type: Boolean }) withHeader = false;
/** Renders the card with an image */
@property({ attribute: 'with-image', type: Boolean }) withImage = false;
/** Renders the card with a footer */
@property({ attribute: 'with-footer', type: Boolean }) withFooter = false;
render() {
return html`
@@ -42,9 +49,9 @@ export default class WaCard extends WebAwesomeElement {
part="base"
class=${classMap({
card: true,
'card--has-footer': this.hasSlotController.test('footer'),
'card--has-image': this.hasSlotController.test('image'),
'card--has-header': this.hasSlotController.test('header')
'card--has-footer': this.withFooter,
'card--has-image': this.withImage,
'card--has-header': this.withHeader
})}
>
<slot name="image" part="image" class="card__image"></slot>

View File

@@ -37,6 +37,8 @@ export default css`
.card__image::slotted(img) {
display: block;
width: 100%;
border-bottom-left-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
.card:not(.card--has-image) .card__image {

View File

@@ -29,7 +29,7 @@ describe('<wa-card>', () => {
describe('when provided an element in the slot "header" to render a header', () => {
before(async () => {
el = await fixture<WaCard>(
html`<wa-card>
html`<wa-card with-header>
<div slot="header">Header Title</div>
This card has a header. You can put all sorts of things in it!
</wa-card>`
@@ -65,7 +65,7 @@ describe('<wa-card>', () => {
describe('when provided an element in the slot "footer" to render a footer', () => {
before(async () => {
el = await fixture<WaCard>(
html`<wa-card>
html`<wa-card with-footer>
This card has a footer. You can put all sorts of things in it!
<div slot="footer">Footer Content</div>
@@ -102,7 +102,7 @@ describe('<wa-card>', () => {
describe('when provided an element in the slot "image" to render a image', () => {
before(async () => {
el = await fixture<WaCard>(
html`<wa-card>
html`<wa-card with-image>
<img
slot="image"
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"

View File

@@ -1,7 +1,6 @@
import { animateTo, stopAnimations } from '../../internal/animate.js';
import { classMap } from 'lit/directives/class-map.js';
import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry.js';
import { HasSlotController } from '../../internal/slot.js';
import { html } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js';
import { LocalizeController } from '../../utilities/localize.js';
@@ -71,7 +70,6 @@ export default class WaDialog extends WebAwesomeElement {
'wa-icon-button': WaIconButton
};
private readonly hasSlotController = new HasSlotController(this, 'footer');
private readonly localize = new LocalizeController(this);
private originalTrigger: HTMLElement | null;
public modal = new Modal(this);
@@ -88,16 +86,16 @@ export default class WaDialog extends WebAwesomeElement {
@property({ type: Boolean, reflect: true }) open = false;
/**
* The dialog's label as displayed in the header. You should always include a relevant label even when using
* `no-header`, as it is required for proper accessibility. If you need to display HTML, use the `label` slot instead.
* The dialog's label as displayed in the header. You should always include a relevant label, as it is required for
* proper accessibility. If you need to display HTML, use the `label` slot instead.
*/
@property({ reflect: true }) label = '';
/**
* Disables the header. This will also remove the default close button, so please ensure you provide an easy,
* accessible way for users to dismiss the dialog.
*/
@property({ attribute: 'no-header', type: Boolean, reflect: true }) noHeader = false;
/** Renders the dialog with a header. */
@property({ attribute: 'with-header', type: Boolean, reflect: true }) withHeader = false;
/** Renders the dialog with a footer. */
@property({ attribute: 'with-footer', type: Boolean, reflect: true }) withFooter = false;
firstUpdated() {
this.dialog.hidden = !this.open;
@@ -272,7 +270,8 @@ export default class WaDialog extends WebAwesomeElement {
class=${classMap({
dialog: true,
'dialog--open': this.open,
'dialog--has-footer': this.hasSlotController.test('footer')
'dialog--with-header': this.withHeader,
'dialog--with-footer': this.withFooter
})}
>
<div part="overlay" class="dialog__overlay" @click=${() => this.requestClose('overlay')} tabindex="-1"></div>
@@ -283,11 +282,11 @@ export default class WaDialog extends WebAwesomeElement {
role="dialog"
aria-modal="true"
aria-hidden=${this.open ? 'false' : 'true'}
aria-label=${ifDefined(this.noHeader ? this.label : undefined)}
aria-labelledby=${ifDefined(!this.noHeader ? 'title' : undefined)}
aria-label=${ifDefined(this.withHeader ? undefined : this.label)}
aria-labelledby=${ifDefined(this.withHeader ? 'title' : undefined)}
tabindex="-1"
>
${!this.noHeader
${this.withHeader
? html`
<header part="header" class="dialog__header">
<h2 part="title" class="dialog__title" id="title">
@@ -314,9 +313,13 @@ export default class WaDialog extends WebAwesomeElement {
}
<div part="body" class="dialog__body" tabindex="-1"><slot></slot></div>
<footer part="footer" class="dialog__footer">
<slot name="footer"></slot>
</footer>
${this.withFooter
? html`
<footer part="footer" class="dialog__footer">
<slot name="footer"></slot>
</footer>
`
: ''}
</div>
</div>
`;

View File

@@ -99,10 +99,6 @@ export default css`
margin-inline-start: var(--wa-spacing-xs);
}
.dialog:not(.dialog--has-footer) .dialog__footer {
display: none;
}
.dialog__overlay {
position: fixed;
top: 0;

View File

@@ -6,10 +6,10 @@ import { sendKeys } from '@web/test-runner-commands';
import sinon from 'sinon';
import type WaDialog from './dialog.js';
describe('<wa-dialog>', () => {
describe('<wa-dialog with-header>', () => {
it('should be visible with the open attribute', async () => {
const el = await fixture<WaDialog>(html`
<wa-dialog open>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
<wa-dialog with-header open>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
`);
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
@@ -18,7 +18,7 @@ describe('<wa-dialog>', () => {
it('should not be visible without the open attribute', async () => {
const el = await fixture<WaDialog>(html`
<wa-dialog>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
<wa-dialog with-header>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
`);
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
@@ -27,7 +27,7 @@ describe('<wa-dialog>', () => {
it('should emit wa-show and wa-after-show when calling show()', async () => {
const el = await fixture<WaDialog>(html`
<wa-dialog>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
<wa-dialog with-header>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
`);
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
const showHandler = sinon.spy();
@@ -47,7 +47,7 @@ describe('<wa-dialog>', () => {
it('should emit wa-hide and wa-after-hide when calling hide()', async () => {
const el = await fixture<WaDialog>(html`
<wa-dialog open>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
<wa-dialog with-header open>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
`);
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
const hideHandler = sinon.spy();
@@ -67,7 +67,7 @@ describe('<wa-dialog>', () => {
it('should emit wa-show and wa-after-show when setting open = true', async () => {
const el = await fixture<WaDialog>(html`
<wa-dialog>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
<wa-dialog with-header>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
`);
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
const showHandler = sinon.spy();
@@ -87,7 +87,7 @@ describe('<wa-dialog>', () => {
it('should emit wa-hide and wa-after-hide when setting open = false', async () => {
const el = await fixture<WaDialog>(html`
<wa-dialog open>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
<wa-dialog with-header open>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
`);
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
const hideHandler = sinon.spy();
@@ -107,7 +107,7 @@ describe('<wa-dialog>', () => {
it('should not close when wa-request-close is prevented', async () => {
const el = await fixture<WaDialog>(html`
<wa-dialog open>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
<wa-dialog with-header open>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
`);
const overlay = el.shadowRoot!.querySelector<HTMLElement>('[part~="overlay"]')!;
@@ -120,7 +120,7 @@ describe('<wa-dialog>', () => {
});
it('should allow initial focus to be set', async () => {
const el = await fixture<WaDialog>(html` <wa-dialog><input /></wa-dialog> `);
const el = await fixture<WaDialog>(html` <wa-dialog with-header><input /></wa-dialog> `);
const input = el.querySelector('input')!;
const initialFocusHandler = sinon.spy((event: Event) => {
event.preventDefault();
@@ -137,7 +137,7 @@ describe('<wa-dialog>', () => {
});
it('should close when pressing Escape', async () => {
const el = await fixture<WaDialog>(html` <wa-dialog open></wa-dialog> `);
const el = await fixture<WaDialog>(html` <wa-dialog with-header open></wa-dialog> `);
const hideHandler = sinon.spy();
el.addEventListener('wa-hide', hideHandler);
@@ -162,7 +162,7 @@ describe('<wa-dialog>', () => {
render() {
return html`
<h1>Dialog Example</h1>
<wa-dialog label="Dialog" class="dialog-overview">
<wa-dialog with-header label="Dialog" class="dialog-overview">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<br />
<label><input type="checkbox" />A</label>
@@ -200,7 +200,7 @@ describe('<wa-dialog>', () => {
const dialog = container.shadowRoot?.querySelector('wa-dialog');
if (!dialog) {
throw Error('Could not find <wa-dialog> element.');
throw Error('Could not find <wa-dialog with-header> element.');
}
const closeButton = dialog.shadowRoot?.querySelector('wa-icon-button');

View File

@@ -1,7 +1,6 @@
import { animateTo, stopAnimations } from '../../internal/animate.js';
import { classMap } from 'lit/directives/class-map.js';
import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry.js';
import { HasSlotController } from '../../internal/slot.js';
import { html } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js';
import { LocalizeController } from '../../utilities/localize.js';
@@ -78,7 +77,6 @@ export default class WaDrawer extends WebAwesomeElement {
static styles: CSSResultGroup = [componentStyles, styles];
static dependencies = { 'wa-icon-button': WaIconButton };
private readonly hasSlotController = new HasSlotController(this, 'footer');
private readonly localize = new LocalizeController(this);
private originalTrigger: HTMLElement | null;
public modal = new Modal(this);
@@ -95,36 +93,27 @@ export default class WaDrawer extends WebAwesomeElement {
@property({ type: Boolean, reflect: true }) open = false;
/**
* The drawer's label as displayed in the header. You should always include a relevant label even when using
* `no-header`, as it is required for proper accessibility. If you need to display HTML, use the `label` slot instead.
* The drawer's label as displayed in the header. You should always include a relevant label, as it is required for
* proper accessibility. If you need to display HTML, use the `label` slot instead.
*/
@property({ reflect: true }) label = '';
/** The direction from which the drawer will open. */
@property({ reflect: true }) placement: 'top' | 'end' | 'bottom' | 'start' = 'end';
/**
* By default, the drawer slides out of its containing block (usually the viewport). To make the drawer slide out of
* its parent element, set this attribute and add `position: relative` to the parent.
*/
@property({ type: Boolean, reflect: true }) contained = false;
/** Renders the drawer with a header. */
@property({ attribute: 'with-header', type: Boolean, reflect: true }) withHeader = false;
/**
* Removes the header. This will also remove the default close button, so please ensure you provide an easy,
* accessible way for users to dismiss the drawer.
*/
@property({ attribute: 'no-header', type: Boolean, reflect: true }) noHeader = false;
/** Renders the drawer with a footer. */
@property({ attribute: 'with-footer', type: Boolean, reflect: true }) withFooter = false;
firstUpdated() {
this.drawer.hidden = !this.open;
if (this.open) {
this.addOpenListeners();
if (!this.contained) {
this.modal.activate();
lockBodyScrolling(this);
}
this.modal.activate();
lockBodyScrolling(this);
}
}
@@ -152,10 +141,8 @@ export default class WaDrawer extends WebAwesomeElement {
private addOpenListeners() {
if ('CloseWatcher' in window) {
this.closeWatcher?.destroy();
if (!this.contained) {
this.closeWatcher = new CloseWatcher();
this.closeWatcher.onclose = () => this.requestClose('keyboard');
}
this.closeWatcher = new CloseWatcher();
this.closeWatcher.onclose = () => this.requestClose('keyboard');
} else {
document.addEventListener('keydown', this.handleDocumentKeyDown);
this.closeWatcher?.destroy();
@@ -167,11 +154,6 @@ export default class WaDrawer extends WebAwesomeElement {
}
private handleDocumentKeyDown = (event: KeyboardEvent) => {
// Contained drawers aren't modal and don't response to the escape key
if (this.contained) {
return;
}
if (event.key === 'Escape' && this.modal.isActive() && this.open) {
event.stopImmediatePropagation();
this.requestClose('keyboard');
@@ -185,12 +167,8 @@ export default class WaDrawer extends WebAwesomeElement {
this.emit('wa-show');
this.addOpenListeners();
this.originalTrigger = document.activeElement as HTMLElement;
// Lock body scrolling only if the drawer isn't contained
if (!this.contained) {
this.modal.activate();
lockBodyScrolling(this);
}
this.modal.activate();
lockBodyScrolling(this);
// When the drawer is shown, Safari will attempt to set focus on whatever element has autofocus. This causes the
// drawer's animation to jitter, so we'll temporarily remove the attribute, call `focus({ preventScroll: true })`
@@ -239,11 +217,8 @@ export default class WaDrawer extends WebAwesomeElement {
// Hide
this.emit('wa-hide');
this.removeOpenListeners();
if (!this.contained) {
this.modal.deactivate();
unlockBodyScrolling(this);
}
this.modal.deactivate();
unlockBodyScrolling(this);
await Promise.all([stopAnimations(this.drawer), stopAnimations(this.overlay)]);
const panelAnimation = getAnimation(this, `drawer.hide${uppercaseFirstLetter(this.placement)}`, {
@@ -279,19 +254,6 @@ export default class WaDrawer extends WebAwesomeElement {
}
}
@watch('contained', { waitUntilFirstUpdate: true })
handleNoModalChange() {
if (this.open && !this.contained) {
this.modal.activate();
lockBodyScrolling(this);
}
if (this.open && this.contained) {
this.modal.deactivate();
unlockBodyScrolling(this);
}
}
/** Shows the drawer. */
async show() {
if (this.open) {
@@ -323,10 +285,9 @@ export default class WaDrawer extends WebAwesomeElement {
'drawer--end': this.placement === 'end',
'drawer--bottom': this.placement === 'bottom',
'drawer--start': this.placement === 'start',
'drawer--contained': this.contained,
'drawer--fixed': !this.contained,
'drawer--rtl': this.localize.dir() === 'rtl',
'drawer--has-footer': this.hasSlotController.test('footer')
'drawer--with-header': this.withHeader,
'drawer--with-footer': this.withFooter
})}
>
<div part="overlay" class="drawer__overlay" @click=${() => this.requestClose('overlay')} tabindex="-1"></div>
@@ -337,11 +298,11 @@ export default class WaDrawer extends WebAwesomeElement {
role="dialog"
aria-modal="true"
aria-hidden=${this.open ? 'false' : 'true'}
aria-label=${ifDefined(this.noHeader ? this.label : undefined)}
aria-labelledby=${ifDefined(!this.noHeader ? 'title' : undefined)}
aria-label=${ifDefined(this.withHeader ? undefined : this.label)}
aria-labelledby=${ifDefined(this.withHeader ? 'title' : undefined)}
tabindex="0"
>
${!this.noHeader
${this.withHeader
? html`
<header part="header" class="drawer__header">
<h2 part="title" class="drawer__title" id="title">
@@ -367,9 +328,13 @@ export default class WaDrawer extends WebAwesomeElement {
<slot part="body" class="drawer__body"></slot>
<footer part="footer" class="drawer__footer">
<slot name="footer"></slot>
</footer>
${this.withFooter
? html`
<footer part="footer" class="drawer__footer">
<slot name="footer"></slot>
</footer>
`
: ''}
</div>
</div>
`;

View File

@@ -11,6 +11,8 @@ export default css`
}
.drawer {
position: fixed;
z-index: var(--wa-z-index-drawer);
top: 0;
inset-inline-start: 0;
width: 100%;
@@ -19,16 +21,6 @@ export default css`
overflow: hidden;
}
.drawer--contained {
position: absolute;
z-index: initial;
}
.drawer--fixed {
position: fixed;
z-index: var(--wa-z-index-drawer);
}
.drawer__panel {
position: absolute;
display: flex;
@@ -129,10 +121,6 @@ export default css`
margin-inline-end: var(--wa-spacing-xs);
}
.drawer:not(.drawer--has-footer) .drawer__footer {
display: none;
}
.drawer__overlay {
display: block;
position: fixed;
@@ -144,10 +132,6 @@ export default css`
pointer-events: all;
}
.drawer--contained .drawer__overlay {
display: none;
}
@media (forced-colors: active) {
.drawer__panel {
border: solid 1px white;

View File

@@ -1,5 +1,5 @@
import { classMap } from 'lit/directives/class-map.js';
import { getTextContent, HasSlotController } from '../../internal/slot.js';
import { getTextContent } from '../../internal/slot.js';
import { html } from 'lit';
import { LocalizeController } from '../../utilities/localize.js';
import { property, query } from 'lit/decorators.js';
@@ -67,8 +67,7 @@ export default class WaMenuItem extends WebAwesomeElement {
@property({ type: Boolean, reflect: true }) disabled = false;
private readonly localize = new LocalizeController(this);
private readonly hasSlotController = new HasSlotController(this, 'submenu');
private submenuController: SubmenuController = new SubmenuController(this, this.hasSlotController, this.localize);
private submenuController: SubmenuController = new SubmenuController(this, this.localize);
connectedCallback() {
super.connectedCallback();
@@ -150,7 +149,7 @@ export default class WaMenuItem extends WebAwesomeElement {
}
isSubmenu() {
return this.hasSlotController.test('submenu');
return this.querySelector(`:scope > [slot="submenu"]`) !== null;
}
render() {

View File

@@ -1,5 +1,4 @@
import { createRef, ref, type Ref } from 'lit/directives/ref.js';
import { type HasSlotController } from '../../internal/slot.js';
import { html } from 'lit';
import { type LocalizeController } from '../../utilities/localize.js';
import type { ReactiveController, ReactiveControllerHost } from 'lit';
@@ -14,22 +13,20 @@ export class SubmenuController implements ReactiveController {
private isConnected = false;
private isPopupConnected = false;
private skidding = 0;
private readonly hasSlotController: HasSlotController;
private readonly localize: LocalizeController;
private readonly submenuOpenDelay = 100;
constructor(
host: ReactiveControllerHost & WaMenuItem,
hasSlotController: HasSlotController,
localize: LocalizeController
) {
constructor(host: ReactiveControllerHost & WaMenuItem, localize: LocalizeController) {
(this.host = host).addController(this);
this.hasSlotController = hasSlotController;
this.localize = localize;
}
private hasSubmenu() {
return this.host.querySelector(`:scope > [slot="submenu"]`) !== null;
}
hostConnected() {
if (this.hasSlotController.test('submenu') && !this.host.disabled) {
if (this.hasSubmenu() && !this.host.disabled) {
this.addListeners();
}
}
@@ -39,7 +36,7 @@ export class SubmenuController implements ReactiveController {
}
hostUpdated() {
if (this.hasSlotController.test('submenu') && !this.host.disabled) {
if (this.hasSubmenu() && !this.host.disabled) {
this.addListeners();
this.updateSkidding();
} else {
@@ -93,7 +90,7 @@ export class SubmenuController implements ReactiveController {
};
private handleMouseOver = () => {
if (this.hasSlotController.test('submenu')) {
if (this.hasSubmenu()) {
this.enableSubmenu();
}
};

View File

@@ -147,7 +147,7 @@ it('Should allow tabbing to slotted elements', async () => {
it('Should account for when focus is changed from outside sources (like clicking)', async () => {
const dialog = await fixture(html`
<wa-dialog open="" label="Dialog" class="dialog-overview">
<wa-dialog open="" label="Dialog" with-header with-footer class="dialog-overview">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<wa-input placeholder="tab to me"></wa-input>
<wa-button slot="footer" variant="primary">Close</wa-button>
@@ -188,12 +188,12 @@ it('Should respect nested modal instances', async () => {
await fixture(html`
<div>
<wa-button id="open-dialog-1" @click=${() => dialogOne().show()}></wa-button>
<wa-dialog id="dialog-1" label="Dialog 1">
<wa-dialog id="dialog-1" label="Dialog 1" with-header with-footer>
<wa-button @click=${() => dialogTwo().show()} id="open-dialog-2">Open Dialog 2</wa-button>
<wa-button slot="footer" variant="primary">Close</wa-button>
</wa-dialog>
<wa-dialog id="dialog-2" label="Dialog 2">
<wa-dialog id="dialog-2" label="Dialog 2" with-header with-footer>
<wa-input id="focus-1" autofocus="" placeholder="I will have focus when the dialog is opened"></wa-input>
<wa-input id="focus-2" placeholder="Second input"></wa-input>
<wa-button slot="footer" variant="primary" class="close-2">Close</wa-button>