kopia lustrzana https://github.com/shoelace-style/shoelace
Add sl-initial-focus to dialog + drawer
rodzic
abf03a8f9e
commit
fc8e48cb6a
|
@ -108,4 +108,34 @@ By default, dialogs are closed when the user clicks or taps on the overlay. To p
|
|||
</script>
|
||||
```
|
||||
|
||||
### Customizing Initial Focus
|
||||
|
||||
By default, the dialog's panel will gain focus when opened. To set focus on a different element, listen for the `sl-initial-focus` event.
|
||||
|
||||
```html preview
|
||||
<sl-dialog label="Dialog" class="dialog-focus">
|
||||
<sl-input placeholder="I will have focus when the dialog is opened"></sl-input>
|
||||
<sl-button slot="footer" type="primary">Close</sl-button>
|
||||
</sl-dialog>
|
||||
|
||||
<sl-button>Open Dialog</sl-button>
|
||||
|
||||
<script>
|
||||
(() => {
|
||||
const dialog = document.querySelector('.dialog-focus');
|
||||
const input = dialog.querySelector('sl-input');
|
||||
const openButton = dialog.nextElementSibling;
|
||||
const closeButton = dialog.querySelector('sl-button[slot="footer"]');
|
||||
|
||||
openButton.addEventListener('click', () => dialog.show());
|
||||
closeButton.addEventListener('click', () => dialog.hide());
|
||||
|
||||
dialog.addEventListener('sl-initial-focus', event => {
|
||||
event.preventDefault();
|
||||
input.setFocus()
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
```
|
||||
|
||||
[component-metadata:sl-dialog]
|
||||
|
|
|
@ -204,4 +204,36 @@ By default, drawers are closed when the user clicks or taps on the overlay. To p
|
|||
</script>
|
||||
```
|
||||
|
||||
### Customizing Initial Focus
|
||||
|
||||
By default, the drawer's panel will gain focus when opened. To set focus on a different element, listen for the `sl-initial-focus` event.
|
||||
|
||||
```html preview
|
||||
<sl-drawer label="Drawer" class="drawer-focus">
|
||||
<sl-input placeholder="I will have focus when the drawer is opened"></sl-input>
|
||||
<sl-button slot="footer" type="primary">Close</sl-button>
|
||||
</sl-drawer>
|
||||
|
||||
<sl-button>Open Drawer</sl-button>
|
||||
|
||||
<script>
|
||||
(() => {
|
||||
const drawer = document.querySelector('.drawer-focus');
|
||||
const input = drawer.querySelector('sl-input');
|
||||
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-initial-focus', event => {
|
||||
// preventScroll is necessary for the transition to work properly,
|
||||
// otherwise the drawer will appear immediately
|
||||
event.preventDefault();
|
||||
input.setFocus({ preventScroll: true })
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
```
|
||||
|
||||
[component-metadata:sl-drawer]
|
||||
|
|
|
@ -13,6 +13,8 @@ _During the beta period, these restrictions may be relaxed in the event of a mis
|
|||
- Reworked animations into a separate module ([`@shoelace-style/animations`](https://github.com/shoelace-style/animations)) so it's more maintainable and animations are sync with the latest version of animate.css
|
||||
- Animation and easing names are now camelcase (e.g. `easeInOut` instead of `ease-in-out`)
|
||||
- Added initial E2E tests [#169](https://github.com/shoelace-style/shoelace/pull/169)
|
||||
- Added the `FocusOptions` argument to all components that have a `setFocus()` method
|
||||
- Added `sl-initial-focus` event to `sl-dialog` and `sl-drawer` so focus can be customized to a specific element
|
||||
- Fixed a bug where `sl-hide` would be emitted twice when closing an alert with `hide()`
|
||||
- Fixed a bug in `sl-color-picker` where the toggle button was smaller than the preview button in Safari
|
||||
- Fixed a bug in `sl-tab-group` where activating a nested tab group didn't work properly [#299](https://github.com/shoelace-style/shoelace/issues/299)
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
"serve": "node dev-server.js",
|
||||
"start": "concurrently --kill-others \"npm run dev\" \"npm run serve\"",
|
||||
"test.watch": "stencil test --spec --e2e --watchAll",
|
||||
"test": "stencil test --spec --e2e",
|
||||
"test": "stencil test --spec --e2e --coverage",
|
||||
"version": "npm run build"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -183,7 +183,7 @@ export namespace Components {
|
|||
/**
|
||||
* Sets focus on the button.
|
||||
*/
|
||||
"setFocus": () => Promise<void>;
|
||||
"setFocus": (options?: FocusOptions) => Promise<void>;
|
||||
/**
|
||||
* The button's size.
|
||||
*/
|
||||
|
@ -253,7 +253,7 @@ export namespace Components {
|
|||
/**
|
||||
* Sets focus on the checkbox.
|
||||
*/
|
||||
"setFocus": () => Promise<void>;
|
||||
"setFocus": (options?: FocusOptions) => Promise<void>;
|
||||
/**
|
||||
* The checkbox's value attribute.
|
||||
*/
|
||||
|
@ -751,7 +751,7 @@ export namespace Components {
|
|||
/**
|
||||
* Sets focus on the input.
|
||||
*/
|
||||
"setFocus": () => Promise<void>;
|
||||
"setFocus": (options?: FocusOptions) => Promise<void>;
|
||||
/**
|
||||
* Replaces a range of text with a new string.
|
||||
*/
|
||||
|
@ -809,7 +809,7 @@ export namespace Components {
|
|||
/**
|
||||
* Sets focus on the button.
|
||||
*/
|
||||
"setFocus": () => Promise<void>;
|
||||
"setFocus": (options?: FocusOptions) => Promise<void>;
|
||||
/**
|
||||
* A unique value to store in the menu item. This can be used as a way to identify menu items when selected.
|
||||
*/
|
||||
|
@ -873,7 +873,7 @@ export namespace Components {
|
|||
/**
|
||||
* Sets focus on the radio.
|
||||
*/
|
||||
"setFocus": () => Promise<void>;
|
||||
"setFocus": (options?: FocusOptions) => Promise<void>;
|
||||
/**
|
||||
* The radio's value attribute.
|
||||
*/
|
||||
|
@ -911,7 +911,7 @@ export namespace Components {
|
|||
/**
|
||||
* Sets focus on the input.
|
||||
*/
|
||||
"setFocus": () => Promise<void>;
|
||||
"setFocus": (options?: FocusOptions) => Promise<void>;
|
||||
/**
|
||||
* The input's step attribute.
|
||||
*/
|
||||
|
@ -957,7 +957,7 @@ export namespace Components {
|
|||
/**
|
||||
* Sets focus on the rating.
|
||||
*/
|
||||
"setFocus": () => Promise<void>;
|
||||
"setFocus": (options?: FocusOptions) => Promise<void>;
|
||||
/**
|
||||
* The current rating.
|
||||
*/
|
||||
|
@ -1103,7 +1103,7 @@ export namespace Components {
|
|||
/**
|
||||
* Sets focus on the switch.
|
||||
*/
|
||||
"setFocus": () => Promise<void>;
|
||||
"setFocus": (options?: FocusOptions) => Promise<void>;
|
||||
/**
|
||||
* The switch's value attribute.
|
||||
*/
|
||||
|
@ -1133,7 +1133,7 @@ export namespace Components {
|
|||
/**
|
||||
* Sets focus to the tab.
|
||||
*/
|
||||
"setFocus": () => Promise<void>;
|
||||
"setFocus": (options?: FocusOptions) => Promise<void>;
|
||||
}
|
||||
interface SlTabGroup {
|
||||
/**
|
||||
|
@ -1265,7 +1265,7 @@ export namespace Components {
|
|||
/**
|
||||
* Sets focus on the textarea.
|
||||
*/
|
||||
"setFocus": () => Promise<void>;
|
||||
"setFocus": (options?: FocusOptions) => Promise<void>;
|
||||
/**
|
||||
* Replaces a range of text with a new string.
|
||||
*/
|
||||
|
@ -2031,6 +2031,10 @@ declare namespace LocalJSX {
|
|||
* Emitted when the dialog closes. Calling `event.preventDefault()` will prevent it from being closed.
|
||||
*/
|
||||
"onSl-hide"?: (event: CustomEvent<any>) => void;
|
||||
/**
|
||||
* Emitted when the dialog opens and the panel gains focus. Calling `event.preventDefault()` will prevent focus and allow you to set it on a different element in the dialog, such as an input or button.
|
||||
*/
|
||||
"onSl-initial-focus"?: (event: CustomEvent<any>) => void;
|
||||
/**
|
||||
* Emitted when the overlay is clicked. Calling `event.preventDefault()` will prevent the dialog from closing.
|
||||
*/
|
||||
|
@ -2069,6 +2073,10 @@ declare namespace LocalJSX {
|
|||
* Emitted when the drawer closes. Calling `event.preventDefault()` will prevent it from being closed.
|
||||
*/
|
||||
"onSl-hide"?: (event: CustomEvent<any>) => void;
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
"onSl-initial-focus"?: (event: CustomEvent<any>) => void;
|
||||
/**
|
||||
* Emitted when the overlay is clicked. Calling `event.preventDefault()` will prevent the drawer from closing.
|
||||
*/
|
||||
|
|
|
@ -73,6 +73,12 @@ export class Dialog {
|
|||
/** Emitted after the dialog closes and all transitions are complete. */
|
||||
@Event({ eventName: 'sl-after-hide' }) slAfterHide: EventEmitter;
|
||||
|
||||
/**
|
||||
* Emitted when the dialog opens and the panel gains focus. Calling `event.preventDefault()` will prevent focus and
|
||||
* allow you to set it on a different element in the dialog, such as an input or button.
|
||||
*/
|
||||
@Event({ eventName: 'sl-initial-focus' }) slInitialFocus: EventEmitter;
|
||||
|
||||
/** Emitted when the overlay is clicked. Calling `event.preventDefault()` will prevent the dialog from closing. */
|
||||
@Event({ eventName: 'sl-overlay-dismiss' }) slOverlayDismiss: EventEmitter;
|
||||
|
||||
|
@ -120,6 +126,16 @@ export class Dialog {
|
|||
this.modal.activate();
|
||||
|
||||
lockBodyScrolling(this.host);
|
||||
|
||||
if (this.open) {
|
||||
// Wait for the next frame before setting initial focus so the dialog is technically visible
|
||||
requestAnimationFrame(() => {
|
||||
const slInitialFocus = this.slInitialFocus.emit();
|
||||
if (!slInitialFocus.defaultPrevented) {
|
||||
this.panel.focus({ preventScroll: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Hides the dialog */
|
||||
|
@ -173,10 +189,6 @@ export class Dialog {
|
|||
this.willShow = false;
|
||||
this.willHide = false;
|
||||
this.open ? this.slAfterShow.emit() : this.slAfterHide.emit();
|
||||
|
||||
if (this.open) {
|
||||
this.panel.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ let id = 0;
|
|||
* @status stable
|
||||
*
|
||||
* @slot - The drawer's content.
|
||||
* @slot label - The dialog's label. Alternatively, you can use the label prop.
|
||||
* @slot label - The drawer's label. Alternatively, you can use the label prop.
|
||||
* @slot footer - The drawer's footer, usually one or more buttons representing various options.
|
||||
*
|
||||
* @part base - The component's base wrapper.
|
||||
|
@ -81,6 +81,12 @@ export class Drawer {
|
|||
/** Emitted after the drawer closes and all transitions are complete. */
|
||||
@Event({ eventName: 'sl-after-hide' }) slAfterHide: EventEmitter;
|
||||
|
||||
/**
|
||||
* 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({ eventName: 'sl-initial-focus' }) slInitialFocus: EventEmitter;
|
||||
|
||||
/** Emitted when the overlay is clicked. Calling `event.preventDefault()` will prevent the drawer from closing. */
|
||||
@Event({ eventName: 'sl-overlay-dismiss' }) slOverlayDismiss: EventEmitter;
|
||||
|
||||
|
@ -131,6 +137,16 @@ export class Drawer {
|
|||
this.modal.activate();
|
||||
lockBodyScrolling(this.host);
|
||||
}
|
||||
|
||||
if (this.open) {
|
||||
// Wait for the next frame before setting initial focus so the drawer is technically visible
|
||||
requestAnimationFrame(() => {
|
||||
const slInitialFocus = this.slInitialFocus.emit();
|
||||
if (!slInitialFocus.defaultPrevented) {
|
||||
this.panel.focus({ preventScroll: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Hides the drawer */
|
||||
|
@ -184,10 +200,6 @@ export class Drawer {
|
|||
this.willShow = false;
|
||||
this.willHide = false;
|
||||
this.open ? this.slAfterShow.emit() : this.slAfterHide.emit();
|
||||
|
||||
if (this.open) {
|
||||
this.panel.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue