fix(overlays): support backdrop with local overlay
This commit is contained in:
parent
5c4974146e
commit
e472b64f7b
6 changed files with 286 additions and 5 deletions
|
|
@ -169,7 +169,7 @@ The interaction states of the `<lion-checkbox-group>` are evaluated in order to
|
|||
{() => {
|
||||
loadDefaultFeedbackMessages();
|
||||
const validate = () => {
|
||||
const checkboxGroup = document.querySelector('#scientistsGroup');
|
||||
const checkboxGroup = document.querySelector('#scientists');
|
||||
checkboxGroup.submitted = !checkboxGroup.submitted;
|
||||
};
|
||||
return html`
|
||||
|
|
@ -201,7 +201,7 @@ The interaction states of the `<lion-checkbox-group>` are evaluated in order to
|
|||
import { Required, loadDefaultFeedbackMessages } from '@lion/validate';
|
||||
loadDefaultFeedbackMessages();
|
||||
const validate = () => {
|
||||
const checkboxGroup = document.querySelector('#scientistsGroup');
|
||||
const checkboxGroup = document.querySelector('#scientists');
|
||||
checkboxGroup.submitted = !checkboxGroup.submitted;
|
||||
};
|
||||
```
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ export class OverlayController {
|
|||
placementMode: null,
|
||||
contentNode: config.contentNode,
|
||||
invokerNode: config.invokerNode,
|
||||
backdropNode: config.backdropNode,
|
||||
referenceNode: null,
|
||||
elementToFocusAfterHide: config.invokerNode,
|
||||
inheritsReferenceWidth: '',
|
||||
|
|
@ -423,10 +424,38 @@ export class OverlayController {
|
|||
*/
|
||||
_handleBackdrop({ animation = true, phase }) {
|
||||
if (this.placementMode === 'local') {
|
||||
return; // coming soon...
|
||||
switch (phase) {
|
||||
case 'init':
|
||||
if (!this.backdropNode) {
|
||||
this.backdropNode = document.createElement('div');
|
||||
this.backdropNode.classList.add('local-overlays__backdrop');
|
||||
}
|
||||
this.backdropNode.slot = '_overlay-shadow-outlet';
|
||||
this._contentNodeWrapper.parentElement.insertBefore(
|
||||
this.backdropNode,
|
||||
this._contentNodeWrapper,
|
||||
);
|
||||
break;
|
||||
case 'show':
|
||||
this.__hasActiveBackdrop = true;
|
||||
break;
|
||||
case 'hide':
|
||||
if (!this.backdropNode) {
|
||||
return;
|
||||
}
|
||||
this.__hasActiveBackdrop = false;
|
||||
break;
|
||||
case 'teardown':
|
||||
if (!this.backdropNode) {
|
||||
return;
|
||||
}
|
||||
this.backdropNode.parentNode.removeChild(this.backdropNode);
|
||||
break;
|
||||
/* no default */
|
||||
}
|
||||
return;
|
||||
}
|
||||
const { backdropNode } = this;
|
||||
|
||||
switch (phase) {
|
||||
case 'init':
|
||||
this.backdropNode = document.createElement('div');
|
||||
|
|
|
|||
|
|
@ -50,10 +50,11 @@ export const OverlayMixin = dedupeMixin(
|
|||
* @returns {OverlayController}
|
||||
*/
|
||||
// eslint-disable-next-line
|
||||
_defineOverlay({ contentNode, invokerNode }) {
|
||||
_defineOverlay({ contentNode, invokerNode, backdropNode }) {
|
||||
return new OverlayController({
|
||||
contentNode,
|
||||
invokerNode,
|
||||
backdropNode,
|
||||
...this._defineOverlayConfig(), // wc provided in the class as defaults
|
||||
...this.config, // user provided (e.g. in template)
|
||||
popperConfig: {
|
||||
|
|
@ -144,6 +145,10 @@ export const OverlayMixin = dedupeMixin(
|
|||
return Array.from(this.children).find(child => child.slot === 'invoker');
|
||||
}
|
||||
|
||||
get _overlayBackdropNode() {
|
||||
return Array.from(this.children).find(child => child.slot === 'backdrop');
|
||||
}
|
||||
|
||||
get _overlayContentNode() {
|
||||
if (this._cachedOverlayContentNode) {
|
||||
return this._cachedOverlayContentNode;
|
||||
|
|
@ -176,6 +181,7 @@ export const OverlayMixin = dedupeMixin(
|
|||
this._overlayCtrl = this._defineOverlay({
|
||||
contentNode: this._overlayContentNode,
|
||||
invokerNode: this._overlayInvokerNode,
|
||||
backdropNode: this._overlayBackdropNode,
|
||||
});
|
||||
this.__syncToOverlayController();
|
||||
this.__setupSyncFromOverlayController();
|
||||
|
|
|
|||
|
|
@ -584,3 +584,162 @@ Below an example is shown with the `isBlocking` option, which makes use of the O
|
|||
</div>
|
||||
</demo-overlay-system>
|
||||
```
|
||||
|
||||
|
||||
## Local Backdrop
|
||||
We provide a possibility to add a backdrop to a locally placed overlay.
|
||||
You can pass your backdropNode as a configuration parameter and control its styling reacting upon OverlayController events.
|
||||
Here is the example below
|
||||
|
||||
<Story name="Local backdrop">
|
||||
{() => {
|
||||
let backdropNode = document.createElement('div');
|
||||
backdropNode.classList.add('local-backdrop-01');
|
||||
return html`
|
||||
<style>
|
||||
.local-backdrop-01 {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
background-color: red;
|
||||
opacity: 0.3;
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<demo-overlay-system
|
||||
@before-opened=${(e) => backdropNode.style.display = 'block'}
|
||||
@before-closed=${(e) => backdropNode.style.display = 'none'}
|
||||
.config=${{ hasBackdrop: true, placementMode: 'local', backdropNode }}
|
||||
>
|
||||
<button slot="invoker">Click me to open the overlay!</button>
|
||||
<div slot="content" class="demo-overlay">
|
||||
Hello! You can close this notification here:
|
||||
<button
|
||||
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
|
||||
>
|
||||
⨯
|
||||
</button>
|
||||
</div>
|
||||
</demo-overlay-system>
|
||||
`}}
|
||||
</Story>
|
||||
|
||||
```js
|
||||
let backdropNode = document.createElement('div');
|
||||
backdropNode.classList.add('local-backdrop-01');
|
||||
return html`
|
||||
<style>
|
||||
.local-backdrop-01 {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
background-color: red;
|
||||
opacity: 0.3;
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<demo-overlay-system
|
||||
@before-opened=${(e) => backdropNode.style.display = 'block'}
|
||||
@before-closed=${(e) => backdropNode.style.display = 'none'}
|
||||
.config=${{ hasBackdrop: true, placementMode: 'local', backdropNode }}
|
||||
>
|
||||
<button slot="invoker">Click me to open the overlay!</button>
|
||||
<div slot="content" class="demo-overlay">
|
||||
Hello! You can close this notification here:
|
||||
<button
|
||||
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
|
||||
>
|
||||
⨯
|
||||
</button>
|
||||
</div>
|
||||
</demo-overlay-system>
|
||||
```
|
||||
|
||||
## Declarative Local Backdrop
|
||||
Another way to add custom backdrop is declaratively add an element with `slot="backdrop"`.
|
||||
|
||||
<Story name="Declarative Local Backdrop">
|
||||
{() => {
|
||||
const beforeOpened = () => {
|
||||
document.querySelector('.local-backdrop-02').style.display = 'block';
|
||||
}
|
||||
const beforeClosed = () => {
|
||||
document.querySelector('.local-backdrop-02').style.display = 'none';
|
||||
}
|
||||
return html`
|
||||
<style>
|
||||
.local-backdrop-02 {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
background-color: red;
|
||||
opacity: 0.3;
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<demo-overlay-system
|
||||
@before-opened=${beforeOpened}
|
||||
@before-closed=${beforeClosed}
|
||||
.config=${{ hasBackdrop: true, placementMode: 'local' }}
|
||||
>
|
||||
<div slot="backdrop" class="local-backdrop-02"></div>
|
||||
<button slot="invoker">Click me to open the overlay!</button>
|
||||
<div slot="content" class="demo-overlay">
|
||||
Hello! You can close this notification here:
|
||||
<button
|
||||
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
|
||||
>
|
||||
⨯
|
||||
</button>
|
||||
</div>
|
||||
</demo-overlay-system>
|
||||
`}}
|
||||
</Story>
|
||||
|
||||
```js
|
||||
const beforeOpened = () => {
|
||||
document.querySelector('.local-backdrop-02').style.display = 'block';
|
||||
}
|
||||
const beforeClosed = () => {
|
||||
document.querySelector('.local-backdrop-02').style.display = 'none';
|
||||
}
|
||||
return html`
|
||||
<style>
|
||||
.local-backdrop-02 {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
background-color: red;
|
||||
opacity: 0.3;
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<demo-overlay-system
|
||||
@before-opened=${beforeOpened}
|
||||
@before-closed=${beforeClosed}
|
||||
.config=${{ hasBackdrop: true, placementMode: 'local' }}
|
||||
>
|
||||
<div slot="backdrop" class="local-backdrop-02"></div>
|
||||
<button slot="invoker">Click me to open the overlay!</button>
|
||||
<div slot="content" class="demo-overlay">
|
||||
Hello! You can close this notification here:
|
||||
<button
|
||||
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
|
||||
>
|
||||
⨯
|
||||
</button>
|
||||
</div>
|
||||
</demo-overlay-system>
|
||||
```
|
||||
|
|
|
|||
|
|
@ -671,6 +671,88 @@ describe('OverlayController', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('locally placed overlay with hasBackdrop', () => {
|
||||
it('has no backdrop by default', async () => {
|
||||
const ctrl = new OverlayController({
|
||||
...withLocalTestConfig(),
|
||||
});
|
||||
await ctrl.show();
|
||||
expect(ctrl.backdropNode).to.be.undefined;
|
||||
});
|
||||
|
||||
it('supports a backdrop option', async () => {
|
||||
const ctrl = new OverlayController({
|
||||
...withLocalTestConfig(),
|
||||
hasBackdrop: false,
|
||||
});
|
||||
await ctrl.show();
|
||||
expect(ctrl.backdropNode).to.be.undefined;
|
||||
await ctrl.hide();
|
||||
|
||||
const backdropNode = document.createElement('div');
|
||||
backdropNode.classList.add('custom-backdrop');
|
||||
|
||||
const controllerWithBackdrop = new OverlayController({
|
||||
...withLocalTestConfig(),
|
||||
hasBackdrop: true,
|
||||
backdropNode,
|
||||
});
|
||||
await controllerWithBackdrop.show();
|
||||
expect(controllerWithBackdrop.backdropNode).to.have.class('custom-backdrop');
|
||||
});
|
||||
|
||||
it('reenables the backdrop when shown/hidden/shown', async () => {
|
||||
const backdropNode = document.createElement('div');
|
||||
backdropNode.classList.add('custom-backdrop');
|
||||
|
||||
const ctrl = new OverlayController({
|
||||
...withLocalTestConfig(),
|
||||
hasBackdrop: true,
|
||||
backdropNode,
|
||||
});
|
||||
await ctrl.show();
|
||||
expect(ctrl.backdropNode).to.have.class('custom-backdrop');
|
||||
await ctrl.hide();
|
||||
await ctrl.show();
|
||||
expect(ctrl.backdropNode).to.have.class('custom-backdrop');
|
||||
});
|
||||
|
||||
it('adds and stacks backdrops if .hasBackdrop is enabled', async () => {
|
||||
const backdropNode = document.createElement('div');
|
||||
backdropNode.classList.add('custom-backdrop-zero');
|
||||
|
||||
const ctrl0 = new OverlayController({
|
||||
...withLocalTestConfig(),
|
||||
hasBackdrop: true,
|
||||
backdropNode,
|
||||
});
|
||||
await ctrl0.show();
|
||||
expect(ctrl0.backdropNode).to.have.class('custom-backdrop-zero');
|
||||
|
||||
const ctrl1 = new OverlayController({
|
||||
...withLocalTestConfig(),
|
||||
hasBackdrop: false,
|
||||
});
|
||||
await ctrl1.show();
|
||||
expect(ctrl0.backdropNode).to.have.class('custom-backdrop-zero');
|
||||
expect(ctrl1.backdropNode).to.be.undefined;
|
||||
|
||||
const anotherBackdropNode = document.createElement('div');
|
||||
anotherBackdropNode.classList.add('custom-backdrop-two');
|
||||
|
||||
const ctrl2 = new OverlayController({
|
||||
...withLocalTestConfig(),
|
||||
hasBackdrop: true,
|
||||
backdropNode: anotherBackdropNode,
|
||||
});
|
||||
await ctrl2.show();
|
||||
|
||||
expect(ctrl0.backdropNode).to.have.class('custom-backdrop-zero');
|
||||
expect(ctrl1.backdropNode).to.be.undefined;
|
||||
expect(ctrl2.backdropNode).to.have.class('custom-backdrop-two');
|
||||
});
|
||||
});
|
||||
|
||||
describe('isBlocking', () => {
|
||||
it('prevents showing of other overlays', async () => {
|
||||
const ctrl0 = new OverlayController({
|
||||
|
|
|
|||
|
|
@ -2266,6 +2266,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@open-wc/dedupe-mixin/-/dedupe-mixin-1.1.1.tgz#3ac8e498422ef316276bbe4aa687e35bd10c6871"
|
||||
integrity sha512-Y1+h5nQjJnDHP+8OceZB47I4D7iOiYnM0jXYLGEi96IusR93et30BIyEEQAJ4AvYfbuIrdbf0L5vQWfszU6/Jg==
|
||||
|
||||
"@open-wc/dedupe-mixin@^1.2.1":
|
||||
version "1.2.10"
|
||||
resolved "https://registry.yarnpkg.com/@open-wc/dedupe-mixin/-/dedupe-mixin-1.2.10.tgz#4992874e98b8c49ed71e7e17d2adc7538d62260b"
|
||||
integrity sha512-I3/aKV8OJ5LkZLOvTiGRgKs+o7VVz3EUozbc7yeKJo7x8+j+NHWhVvtNHE8GXAXbN3s4KmMWQt1mXWCEZwNg7g==
|
||||
|
||||
"@open-wc/demoing-storybook@^1.10.4":
|
||||
version "1.10.5"
|
||||
resolved "https://registry.yarnpkg.com/@open-wc/demoing-storybook/-/demoing-storybook-1.10.5.tgz#3886e01fcc3b13485d5bdb4904f4ec627895609f"
|
||||
|
|
|
|||
Loading…
Reference in a new issue