Skip to content

Commit

Permalink
add sl-request-close event
Browse files Browse the repository at this point in the history
  • Loading branch information
claviska committed Jun 21, 2021
1 parent 95ba1b5 commit 1c010ff
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 42 deletions.
20 changes: 11 additions & 9 deletions docs/components/dialog.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,27 +76,29 @@ By design, a dialog's height will never exceed that of the viewport. As such, di
</script>
```

### Ignoring Clicks on the Overlay
### Preventing the Dialog from Closing

By default, dialogs are closed when the user clicks or taps on the overlay. To prevent this behavior, cancel the `sl-overlay-dismiss` event.
By default, dialogs will close when the user clicks the close button, clicks the overlay, or presses the <kbd>Escape</kbd> key. In most cases, the default behavior is the best behavior in terms of UX. However, there are situations where this may be undesirable, such as when data loss will occur.

To keep the dialog open in such cases, you can cancel the `sl-request-close` event. When canceled, the dialog will remain open and pulse briefly to draw the user's attention to it.

```html preview
<sl-dialog label="Dialog" class="dialog-no-overlay-dismiss">
This dialog will not be closed when you click outside of it.
<sl-button slot="footer" type="primary">Close</sl-button>
<sl-dialog label="Dialog" class="dialog-deny-close">
This dialog will not close unless you use the button below.
<sl-button slot="footer" type="primary">Save &amp; Close</sl-button>
</sl-dialog>

<sl-button>Open Dialog</sl-button>

<script>
const dialog = document.querySelector('.dialog-no-overlay-dismiss');
const dialog = document.querySelector('.dialog-deny-close');
const openButton = dialog.nextElementSibling;
const closeButton = dialog.querySelector('sl-button[slot="footer"]');
const saveButton = dialog.querySelector('sl-button[slot="footer"]');
openButton.addEventListener('click', () => dialog.show());
closeButton.addEventListener('click', () => dialog.hide());
saveButton.addEventListener('click', () => dialog.hide());
dialog.addEventListener('sl-overlay-dismiss', event => event.preventDefault());
dialog.addEventListener('sl-request-close', event => event.preventDefault());
</script>
```

Expand Down
17 changes: 10 additions & 7 deletions docs/components/drawer.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,27 +164,30 @@ By design, a drawer's height will never exceed 100% of its container. As such, d
</script>
```

### Ignoring Clicks on the Overlay
### Preventing the Drawer from Closing

By default, drawers will close when the user clicks the close button, clicks the overlay, or presses the <kbd>Escape</kbd> key. In most cases, the default behavior is the best behavior in terms of UX. However, there are situations where this may be undesirable, such as when data loss will occur.

To keep the drawer open in such cases, you can cancel the `sl-request-close` event. When canceled, the drawer will remain open and pulse briefly to draw the user's attention to it.

By default, drawers are closed when the user clicks or taps on the overlay. To prevent this behavior, cancel the `sl-overlay-dismiss` event.

```html preview
<sl-drawer label="Drawer" class="drawer-no-overlay-dismiss">
This drawer will not be closed when you click outside of it.
<sl-button slot="footer" type="primary">Close</sl-button>
<sl-drawer label="Drawer" class="drawer-deny-close">
This dialog will not close unless you use the button below.
<sl-button slot="footer" type="primary">Save &amp; Close</sl-button>
</sl-drawer>

<sl-button>Open Drawer</sl-button>

<script>
const drawer = document.querySelector('.drawer-no-overlay-dismiss');
const drawer = document.querySelector('.drawer-deny-close');
const openButton = drawer.nextElementSibling;
const closeButton = drawer.querySelector('sl-button[type="primary"]');
openButton.addEventListener('click', () => drawer.show());
closeButton.addEventListener('click', () => drawer.hide());
drawer.addEventListener('sl-overlay-dismiss', event => event.preventDefault());
drawer.addEventListener('sl-request-close', event => event.preventDefault());
</script>
```

Expand Down
3 changes: 3 additions & 0 deletions docs/resources/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ _During the beta period, these restrictions may be relaxed in the event of a mis

## Next

- 🚨 BREAKING: removed the `sl-overlay-click` event from `sl-dialog` and `sl-drawer` (use `sl-request-close` instead) [#471](https://github.com/shoelace-style/shoelace/discussions/471)
- Added `sl-request-close` event to `sl-dialog` and `sl-drawer`
- Added `dialog.denyClose` and `drawer.denyClose` animations
- Fixed a bug in `sl-color-picker` where setting `value` immediately wouldn't trigger an update

## 2.0.0-beta.44
Expand Down
36 changes: 23 additions & 13 deletions src/components/dialog/dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ let id = 0;
*
* @animation dialog.show - The animation to use when showing the dialog.
* @animation dialog.hide - The animation to use when hiding the dialog.
* @animation dialog.denyClose - The animation to use when a request to close the dialog is denied.
* @animation dialog.overlay.show - The animation to use when showing the dialog's overlay.
* @animation dialog.overlay.hide - The animation to use when hiding the dialog's overlay.
*/
Expand Down Expand Up @@ -92,8 +93,12 @@ export default class SlDialog extends LitElement {
*/
@event('sl-initial-focus') slInitialFocus: EventEmitter<void>;

/** Emitted when the overlay is clicked. Calling `event.preventDefault()` will prevent the dialog from closing. */
@event('sl-overlay-dismiss') slOverlayDismiss: EventEmitter<void>;
/**
* Emitted when the user attempts to close the dialog by clicking the close button, clicking the overlay, or pressing
* the escape key. Calling `event.preventDefault()` will prevent the dialog from closing. Avoid using this unless
* closing the dialog will result in destructive behavior such as data loss.
*/
@event('sl-request-close') slRequestClose: EventEmitter<void>;

connectedCallback() {
super.connectedCallback();
Expand Down Expand Up @@ -131,14 +136,21 @@ export default class SlDialog extends LitElement {
return waitForEvent(this, 'sl-after-hide');
}

handleCloseClick() {
private requestClose() {
const slRequestClose = this.slRequestClose.emit({ cancelable: true });
if (slRequestClose.defaultPrevented) {
const animation = getAnimation(this, 'dialog.denyClose');
animateTo(this.panel, animation.keyframes, animation.options);
return;
}

this.hide();
}

handleKeyDown(event: KeyboardEvent) {
if (event.key === 'Escape') {
event.stopPropagation();
this.hide();
this.requestClose();
}
}

Expand Down Expand Up @@ -206,13 +218,6 @@ export default class SlDialog extends LitElement {
}
}

handleOverlayClick() {
const slOverlayDismiss = this.slOverlayDismiss.emit({ cancelable: true });
if (!slOverlayDismiss.defaultPrevented) {
this.hide();
}
}

handleSlotChange() {
this.hasFooter = hasSlot(this, 'footer');
}
Expand All @@ -228,7 +233,7 @@ export default class SlDialog extends LitElement {
})}
@keydown=${this.handleKeyDown}
>
<div part="overlay" class="dialog__overlay" @click=${this.handleOverlayClick} tabindex="-1"></div>
<div part="overlay" class="dialog__overlay" @click=${this.requestClose} tabindex="-1"></div>
<div
part="panel"
Expand All @@ -251,7 +256,7 @@ export default class SlDialog extends LitElement {
class="dialog__close"
name="x"
library="system"
@click="${this.handleCloseClick}"
@click="${this.requestClose}"
></sl-icon-button>
</header>
`
Expand Down Expand Up @@ -286,6 +291,11 @@ setDefaultAnimation('dialog.hide', {
options: { duration: 250, easing: 'ease' }
});

setDefaultAnimation('dialog.denyClose', {
keyframes: [{ transform: 'scale(1)' }, { transform: 'scale(1.02)' }, { transform: 'scale(1)' }],
options: { duration: 250 }
});

setDefaultAnimation('dialog.overlay.show', {
keyframes: [{ opacity: 0 }, { opacity: 1 }],
options: { duration: 250 }
Expand Down
37 changes: 24 additions & 13 deletions src/components/drawer/drawer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ let id = 0;
* @animation drawer.hideEnd - The animation to use when hiding a drawer with `end` placement.
* @animation drawer.hideBottom - The animation to use when hiding a drawer with `bottom` placement.
* @animation drawer.hideStart - The animation to use when hiding a drawer with `start` placement.
* @animation drawer.denyClose - The animation to use when a request to close the drawer is denied.
* @animation drawer.overlay.show - The animation to use when showing the drawer's overlay.
* @animation drawer.overlay.hide - The animation to use when hiding the drawer's overlay.
*/
Expand Down Expand Up @@ -106,8 +107,12 @@ export default class SlDrawer extends LitElement {
/** Emitted when the drawer opens and the panel gains focus. Calling `event.preventDefault()` will prevent focus and allow you to set it on a different element in the drawer, such as an input or button. */
@event('sl-initial-focus') slInitialFocus: EventEmitter<void>;

/** Emitted when the overlay is clicked. Calling `event.preventDefault()` will prevent the drawer from closing. */
@event('sl-overlay-dismiss') slOverlayDismiss: EventEmitter<void>;
/**
* Emitted when the user attempts to close the drawer by clicking the close button, clicking the overlay, or pressing
* the escape key. Calling `event.preventDefault()` will prevent the drawer from closing. Avoid using this unless
* closing the drawer will result in destructive behavior such as data loss.
*/
@event('sl-request-close') slRequestClose: EventEmitter<void>;

connectedCallback() {
super.connectedCallback();
Expand Down Expand Up @@ -145,14 +150,21 @@ export default class SlDrawer extends LitElement {
return waitForEvent(this, 'sl-after-hide');
}

handleCloseClick() {
private requestClose() {
const slRequestClose = this.slRequestClose.emit({ cancelable: true });
if (slRequestClose.defaultPrevented) {
const animation = getAnimation(this, 'drawer.denyClose');
animateTo(this.panel, animation.keyframes, animation.options);
return;
}

this.hide();
}

handleKeyDown(event: KeyboardEvent) {
if (event.key === 'Escape') {
event.stopPropagation();
this.hide();
this.requestClose();
}
}

Expand Down Expand Up @@ -223,13 +235,6 @@ export default class SlDrawer extends LitElement {
}
}

handleOverlayClick() {
const slOverlayDismiss = this.slOverlayDismiss.emit({ cancelable: true });
if (!slOverlayDismiss.defaultPrevented) {
this.hide();
}
}

handleSlotChange() {
this.hasFooter = hasSlot(this, 'footer');
}
Expand All @@ -251,7 +256,7 @@ export default class SlDrawer extends LitElement {
})}
@keydown=${this.handleKeyDown}
>
<div part="overlay" class="drawer__overlay" @click=${this.handleOverlayClick} tabindex="-1"></div>
<div part="overlay" class="drawer__overlay" @click=${this.requestClose} tabindex="-1"></div>
<div
part="panel"
Expand All @@ -275,7 +280,7 @@ export default class SlDrawer extends LitElement {
class="drawer__close"
name="x"
library="system"
@click=${this.handleCloseClick}
@click=${this.requestClose}
></sl-icon-button>
</header>
`
Expand Down Expand Up @@ -362,6 +367,12 @@ setDefaultAnimation('drawer.hideStart', {
options: { duration: 250, easing: 'ease' }
});

// Deny close
setDefaultAnimation('drawer.denyClose', {
keyframes: [{ transform: 'scale(1)' }, { transform: 'scale(1.01)' }, { transform: 'scale(1)' }],
options: { duration: 250 }
});

// Overlay
setDefaultAnimation('drawer.overlay.show', {
keyframes: [{ opacity: 0 }, { opacity: 1 }],
Expand Down

0 comments on commit 1c010ff

Please sign in to comment.