mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-12 12:09:26 +00:00
Revamp dialog styles, port to Essentials
Refactor dialog CSS Add native dialog stub
This commit is contained in:
@@ -1,89 +1,24 @@
|
||||
:host {
|
||||
--background-color: var(--wa-color-surface-raised);
|
||||
--border-radius: var(--wa-panel-border-radius);
|
||||
--box-shadow: var(--wa-shadow-l);
|
||||
--width: 31rem;
|
||||
--spacing: var(--wa-space-xl);
|
||||
--show-duration: 200ms;
|
||||
--hide-duration: 200ms;
|
||||
|
||||
display: contents;
|
||||
}
|
||||
|
||||
:host(:not([open])) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:host([open]) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dialog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: var(--width);
|
||||
max-width: calc(100% - var(--wa-space-2xl));
|
||||
max-height: calc(100% - var(--wa-space-2xl));
|
||||
background-color: var(--background-color);
|
||||
border-radius: var(--border-radius);
|
||||
border: none;
|
||||
box-shadow: var(--box-shadow);
|
||||
padding: 0;
|
||||
dialog {
|
||||
width: inherit;
|
||||
max-width: inherit;
|
||||
max-height: inherit;
|
||||
background-color: inherit;
|
||||
border-radius: inherit;
|
||||
border: inherit;
|
||||
box-shadow: inherit;
|
||||
padding: inherit;
|
||||
margin: auto;
|
||||
|
||||
&.show {
|
||||
animation: show-dialog var(--show-duration) ease;
|
||||
|
||||
&::backdrop {
|
||||
animation: show-backdrop var(--show-duration, 200ms) ease;
|
||||
}
|
||||
}
|
||||
|
||||
&.hide {
|
||||
animation: show-dialog var(--hide-duration) ease reverse;
|
||||
|
||||
&::backdrop {
|
||||
animation: show-backdrop var(--hide-duration, 200ms) ease reverse;
|
||||
}
|
||||
}
|
||||
|
||||
&.pulse {
|
||||
animation: pulse 250ms ease;
|
||||
}
|
||||
}
|
||||
|
||||
.dialog:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Ensure there's enough vertical padding for phones that don't update vh when chrome appears (e.g. iPhone) */
|
||||
@media screen and (max-width: 420px) {
|
||||
.dialog {
|
||||
max-height: 80vh;
|
||||
}
|
||||
}
|
||||
|
||||
.dialog--open {
|
||||
display: flex;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.header {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
padding: var(--spacing);
|
||||
padding-block-end: 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
align-self: center;
|
||||
flex: 1 1 auto;
|
||||
font-family: inherit;
|
||||
font-size: var(--wa-font-size-l);
|
||||
font-weight: var(--wa-font-weight-heading);
|
||||
line-height: var(--wa-line-height-condensed);
|
||||
margin: 0;
|
||||
transition: inherit;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
@@ -93,81 +28,13 @@
|
||||
flex-wrap: wrap;
|
||||
justify-content: end;
|
||||
gap: var(--wa-space-2xs);
|
||||
padding-inline-start: var(--spacing);
|
||||
}
|
||||
margin-inline-start: auto;
|
||||
|
||||
.header-actions wa-icon-button,
|
||||
.header-actions ::slotted(wa-icon-button) {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: var(--wa-font-size-m);
|
||||
}
|
||||
|
||||
.body {
|
||||
flex: 1 1 auto;
|
||||
display: block;
|
||||
padding: var(--spacing);
|
||||
overflow: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.footer {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--wa-space-xs);
|
||||
justify-content: end;
|
||||
padding: var(--spacing);
|
||||
padding-block-start: 0;
|
||||
}
|
||||
|
||||
.footer ::slotted(wa-button:not(:first-of-type)) {
|
||||
margin-inline-start: var(--wa-spacing-xs);
|
||||
}
|
||||
|
||||
.dialog::backdrop {
|
||||
/*
|
||||
NOTE: the ::backdrop element doesn't inherit properly in Safari yet, but it will in 17.4! At that time, we can
|
||||
remove the fallback values here.
|
||||
*/
|
||||
background-color: var(--wa-color-overlay-modal, rgb(0 0 0 / 0.25));
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
scale: 1;
|
||||
}
|
||||
50% {
|
||||
scale: 1.02;
|
||||
}
|
||||
100% {
|
||||
scale: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes show-dialog {
|
||||
from {
|
||||
opacity: 0;
|
||||
scale: 0.8;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
scale: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes show-backdrop {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (forced-colors: active) {
|
||||
.dialog {
|
||||
border: solid 1px white;
|
||||
wa-icon-button,
|
||||
::slotted(wa-icon-button) {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: var(--wa-font-size-m);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { html, isServer } from 'lit';
|
||||
import { customElement, property, query } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { customElement, property, query, state } from 'lit/decorators.js';
|
||||
import { WaAfterHideEvent } from '../../events/after-hide.js';
|
||||
import { WaAfterShowEvent } from '../../events/after-show.js';
|
||||
import { WaHideEvent } from '../../events/hide.js';
|
||||
@@ -9,6 +8,7 @@ import { animateWithClass } from '../../internal/animate.js';
|
||||
import { lockBodyScrolling, unlockBodyScrolling } from '../../internal/scroll.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
import dialogStyles from '../../styles/native/dialog.css';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import '../icon-button/icon-button.js';
|
||||
import styles from './dialog.css';
|
||||
@@ -35,6 +35,7 @@ import styles from './dialog.css';
|
||||
* behavior such as data loss.
|
||||
* @event wa-after-hide - Emitted after the dialog closes and all animations are complete.
|
||||
*
|
||||
* @csspart base - The inner `<dialog>` used to render this component.
|
||||
* @csspart header - The dialog's header. This element wraps the title and header actions.
|
||||
* @csspart header-actions - Optional actions to add to the header. Works best with `<wa-icon-button>`.
|
||||
* @csspart title - The dialog's title.
|
||||
@@ -43,23 +44,16 @@ import styles from './dialog.css';
|
||||
* @csspart body - The dialog's body.
|
||||
* @csspart footer - The dialog's footer.
|
||||
*
|
||||
* @cssproperty --background-color - The dialog's background color.
|
||||
* @cssproperty --border-radius - The radius of the dialog's corners.
|
||||
* @cssproperty --box-shadow - The shadow effects around the edges of the dialog.
|
||||
* @cssproperty --spacing - The amount of space around and between the dialog's content.
|
||||
* @cssproperty --width - The preferred width of the dialog. Note that the dialog will shrink to accommodate smaller screens.
|
||||
* @cssproperty [--show-duration=200ms] - The animation duration when showing the dialog.
|
||||
* @cssproperty [--hide-duration=200ms] - The animation duration when hiding the dialog.
|
||||
*/
|
||||
@customElement('wa-dialog')
|
||||
export default class WaDialog extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static shadowStyle = [dialogStyles, styles];
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
private originalTrigger: HTMLElement | null;
|
||||
private closeWatcher: CloseWatcher | null;
|
||||
|
||||
@query('.dialog') dialog: HTMLDialogElement;
|
||||
@query('dialog') dialog: HTMLDialogElement;
|
||||
|
||||
/**
|
||||
* Indicates whether or not the dialog is open. You can toggle this attribute to show and hide the dialog, or you can
|
||||
@@ -82,6 +76,9 @@ export default class WaDialog extends WebAwesomeElement {
|
||||
/** When enabled, the dialog will be closed when the user clicks outside of it. */
|
||||
@property({ attribute: 'light-dismiss', type: Boolean }) lightDismiss = false;
|
||||
|
||||
@state()
|
||||
hasOpened = this.open;
|
||||
|
||||
firstUpdated() {
|
||||
if (this.open) {
|
||||
this.addOpenListeners();
|
||||
@@ -103,14 +100,12 @@ export default class WaDialog extends WebAwesomeElement {
|
||||
|
||||
if (waHideEvent.defaultPrevented) {
|
||||
this.open = true;
|
||||
animateWithClass(this.dialog, 'pulse');
|
||||
animateWithClass(this.dialog, 'wa-dialog-pulse');
|
||||
return;
|
||||
}
|
||||
|
||||
this.removeOpenListeners();
|
||||
|
||||
await animateWithClass(this.dialog, 'hide');
|
||||
|
||||
this.open = false;
|
||||
this.dialog.close();
|
||||
unlockBodyScrolling(this);
|
||||
@@ -166,7 +161,7 @@ export default class WaDialog extends WebAwesomeElement {
|
||||
if (this.lightDismiss) {
|
||||
this.requestClose(this.dialog);
|
||||
} else {
|
||||
await animateWithClass(this.dialog, 'pulse');
|
||||
await animateWithClass(this.dialog, 'wa-dialog-pulse');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -203,6 +198,7 @@ export default class WaDialog extends WebAwesomeElement {
|
||||
this.addOpenListeners();
|
||||
this.originalTrigger = document.activeElement as HTMLElement;
|
||||
this.open = true;
|
||||
this.hasOpened = true;
|
||||
this.dialog.showModal();
|
||||
|
||||
lockBodyScrolling(this);
|
||||
@@ -215,28 +211,20 @@ export default class WaDialog extends WebAwesomeElement {
|
||||
}
|
||||
});
|
||||
|
||||
await animateWithClass(this.dialog, 'show');
|
||||
|
||||
this.dispatchEvent(new WaAfterShowEvent());
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<dialog
|
||||
part="dialog"
|
||||
class=${classMap({
|
||||
dialog: true,
|
||||
'dialog--open': this.open,
|
||||
'dialog--with-header': this.withHeader,
|
||||
'dialog--with-footer': this.withFooter,
|
||||
})}
|
||||
part="base"
|
||||
@cancel=${this.handleDialogCancel}
|
||||
@click=${this.handleDialogClick}
|
||||
@pointerdown=${this.handleDialogPointerDown}
|
||||
>
|
||||
${this.withHeader
|
||||
? html`
|
||||
<header part="header" class="header">
|
||||
<header part="header">
|
||||
<h2 part="title" class="title" id="title">
|
||||
<!-- If there's no label, use an invisible character to prevent the header from collapsing -->
|
||||
<slot name="label"> ${this.label.length > 0 ? this.label : String.fromCharCode(65279)} </slot>
|
||||
@@ -258,11 +246,11 @@ export default class WaDialog extends WebAwesomeElement {
|
||||
`
|
||||
: ''}
|
||||
|
||||
<div part="body" class="body"><slot></slot></div>
|
||||
<slot part="body" class="body"></slot>
|
||||
|
||||
${this.withFooter
|
||||
? html`
|
||||
<footer part="footer" class="footer">
|
||||
<footer part="footer">
|
||||
<slot name="footer"></slot>
|
||||
</footer>
|
||||
`
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
@import url('native/details.css');
|
||||
@import url('native/tables.css');
|
||||
@import url('native/blockquote.css');
|
||||
@import url('native/dialog.css');
|
||||
|
||||
@import url('utilities/size.css');
|
||||
@import url('utilities/variants.css');
|
||||
|
||||
141
src/styles/native/dialog.css
Normal file
141
src/styles/native/dialog.css
Normal file
@@ -0,0 +1,141 @@
|
||||
/* <dialog> by itself or <wa-dialog> */
|
||||
:host,
|
||||
dialog:not(:host *, .wa-off, .wa-off-deep *) {
|
||||
width: 31rem;
|
||||
max-width: calc(100% - var(--wa-space-2xl));
|
||||
max-height: calc(100% - var(--wa-space-2xl));
|
||||
background-color: var(--wa-color-surface-raised);
|
||||
padding: var(--wa-space-xl);
|
||||
border-radius: var(--wa-panel-border-radius);
|
||||
border: none;
|
||||
box-shadow: var(--wa-shadow-l);
|
||||
transition: var(--wa-transition-slow, 200ms) var(--wa-transition-easing);
|
||||
transition-behavior: allow-discrete !important;
|
||||
}
|
||||
|
||||
/* <dialog> wherever it is */
|
||||
dialog:not(.wa-off, .wa-off-deep *) {
|
||||
flex-direction: column;
|
||||
inset: 0;
|
||||
|
||||
&[open] {
|
||||
display: flex;
|
||||
|
||||
&::backdrop {
|
||||
@starting-style {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@starting-style {
|
||||
opacity: 0;
|
||||
scale: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
&:not([open]) {
|
||||
scale: 0.8;
|
||||
|
||||
&,
|
||||
&::backdrop {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.wa-dialog-pulse {
|
||||
animation: wa-dialog-pulse 250ms ease;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&::backdrop {
|
||||
/* NOTE: the ::backdrop element doesn't inherit properly before Safari 17.4 */
|
||||
background-color: var(--wa-color-overlay-modal, rgb(0 0 0 / 0.25));
|
||||
transition: inherit;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Ensure there's enough vertical padding for phones that don't update vh when chrome appears (e.g. iPhone) */
|
||||
@media screen and (max-width: 420px) {
|
||||
max-height: 80vh;
|
||||
}
|
||||
|
||||
@media (forced-colors: active) {
|
||||
border: solid 1px white;
|
||||
}
|
||||
|
||||
> * {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
> slot {
|
||||
display: block;
|
||||
}
|
||||
|
||||
> :nth-child(2):not(:last-child) {
|
||||
padding-block-start: inherit;
|
||||
}
|
||||
|
||||
> :nth-last-child(2):not(:first-child) {
|
||||
padding-block-end: inherit;
|
||||
}
|
||||
|
||||
/* Dialog body */
|
||||
> :not(header, footer) {
|
||||
&:where(header:first-child + *, :first-child):where(:last-child, :has(+ footer:last-child)) {
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
}
|
||||
|
||||
> header {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
padding-block-end: 0;
|
||||
}
|
||||
|
||||
> footer {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--wa-space-xs);
|
||||
justify-content: end;
|
||||
padding-block-start: 0;
|
||||
|
||||
::slotted(wa-button:not(:first-of-type)),
|
||||
::slotted(button:not(:first-of-type)),
|
||||
> wa-button:not(:first-of-type),
|
||||
> button:not(:first-of-type) {
|
||||
margin-inline-start: var(--wa-spacing-xs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes wa-dialog-pulse {
|
||||
from,
|
||||
to {
|
||||
scale: 1;
|
||||
}
|
||||
50% {
|
||||
scale: 1.02;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes wa-fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
dialog > header > h2 {
|
||||
align-self: center;
|
||||
flex: 1 1 auto;
|
||||
font-family: inherit;
|
||||
font-size: var(--wa-font-size-l);
|
||||
font-weight: var(--wa-font-weight-heading);
|
||||
line-height: var(--wa-line-height-condensed);
|
||||
margin: 0;
|
||||
}
|
||||
Reference in New Issue
Block a user