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();
|
loadDefaultFeedbackMessages();
|
||||||
const validate = () => {
|
const validate = () => {
|
||||||
const checkboxGroup = document.querySelector('#scientistsGroup');
|
const checkboxGroup = document.querySelector('#scientists');
|
||||||
checkboxGroup.submitted = !checkboxGroup.submitted;
|
checkboxGroup.submitted = !checkboxGroup.submitted;
|
||||||
};
|
};
|
||||||
return html`
|
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';
|
import { Required, loadDefaultFeedbackMessages } from '@lion/validate';
|
||||||
loadDefaultFeedbackMessages();
|
loadDefaultFeedbackMessages();
|
||||||
const validate = () => {
|
const validate = () => {
|
||||||
const checkboxGroup = document.querySelector('#scientistsGroup');
|
const checkboxGroup = document.querySelector('#scientists');
|
||||||
checkboxGroup.submitted = !checkboxGroup.submitted;
|
checkboxGroup.submitted = !checkboxGroup.submitted;
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ export class OverlayController {
|
||||||
placementMode: null,
|
placementMode: null,
|
||||||
contentNode: config.contentNode,
|
contentNode: config.contentNode,
|
||||||
invokerNode: config.invokerNode,
|
invokerNode: config.invokerNode,
|
||||||
|
backdropNode: config.backdropNode,
|
||||||
referenceNode: null,
|
referenceNode: null,
|
||||||
elementToFocusAfterHide: config.invokerNode,
|
elementToFocusAfterHide: config.invokerNode,
|
||||||
inheritsReferenceWidth: '',
|
inheritsReferenceWidth: '',
|
||||||
|
|
@ -423,10 +424,38 @@ export class OverlayController {
|
||||||
*/
|
*/
|
||||||
_handleBackdrop({ animation = true, phase }) {
|
_handleBackdrop({ animation = true, phase }) {
|
||||||
if (this.placementMode === 'local') {
|
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;
|
const { backdropNode } = this;
|
||||||
|
|
||||||
switch (phase) {
|
switch (phase) {
|
||||||
case 'init':
|
case 'init':
|
||||||
this.backdropNode = document.createElement('div');
|
this.backdropNode = document.createElement('div');
|
||||||
|
|
|
||||||
|
|
@ -50,10 +50,11 @@ export const OverlayMixin = dedupeMixin(
|
||||||
* @returns {OverlayController}
|
* @returns {OverlayController}
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
_defineOverlay({ contentNode, invokerNode }) {
|
_defineOverlay({ contentNode, invokerNode, backdropNode }) {
|
||||||
return new OverlayController({
|
return new OverlayController({
|
||||||
contentNode,
|
contentNode,
|
||||||
invokerNode,
|
invokerNode,
|
||||||
|
backdropNode,
|
||||||
...this._defineOverlayConfig(), // wc provided in the class as defaults
|
...this._defineOverlayConfig(), // wc provided in the class as defaults
|
||||||
...this.config, // user provided (e.g. in template)
|
...this.config, // user provided (e.g. in template)
|
||||||
popperConfig: {
|
popperConfig: {
|
||||||
|
|
@ -144,6 +145,10 @@ export const OverlayMixin = dedupeMixin(
|
||||||
return Array.from(this.children).find(child => child.slot === 'invoker');
|
return Array.from(this.children).find(child => child.slot === 'invoker');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get _overlayBackdropNode() {
|
||||||
|
return Array.from(this.children).find(child => child.slot === 'backdrop');
|
||||||
|
}
|
||||||
|
|
||||||
get _overlayContentNode() {
|
get _overlayContentNode() {
|
||||||
if (this._cachedOverlayContentNode) {
|
if (this._cachedOverlayContentNode) {
|
||||||
return this._cachedOverlayContentNode;
|
return this._cachedOverlayContentNode;
|
||||||
|
|
@ -176,6 +181,7 @@ export const OverlayMixin = dedupeMixin(
|
||||||
this._overlayCtrl = this._defineOverlay({
|
this._overlayCtrl = this._defineOverlay({
|
||||||
contentNode: this._overlayContentNode,
|
contentNode: this._overlayContentNode,
|
||||||
invokerNode: this._overlayInvokerNode,
|
invokerNode: this._overlayInvokerNode,
|
||||||
|
backdropNode: this._overlayBackdropNode,
|
||||||
});
|
});
|
||||||
this.__syncToOverlayController();
|
this.__syncToOverlayController();
|
||||||
this.__setupSyncFromOverlayController();
|
this.__setupSyncFromOverlayController();
|
||||||
|
|
|
||||||
|
|
@ -584,3 +584,162 @@ Below an example is shown with the `isBlocking` option, which makes use of the O
|
||||||
</div>
|
</div>
|
||||||
</demo-overlay-system>
|
</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', () => {
|
describe('isBlocking', () => {
|
||||||
it('prevents showing of other overlays', async () => {
|
it('prevents showing of other overlays', async () => {
|
||||||
const ctrl0 = new OverlayController({
|
const ctrl0 = new OverlayController({
|
||||||
|
|
|
||||||
|
|
@ -2266,6 +2266,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@open-wc/dedupe-mixin/-/dedupe-mixin-1.1.1.tgz#3ac8e498422ef316276bbe4aa687e35bd10c6871"
|
resolved "https://registry.yarnpkg.com/@open-wc/dedupe-mixin/-/dedupe-mixin-1.1.1.tgz#3ac8e498422ef316276bbe4aa687e35bd10c6871"
|
||||||
integrity sha512-Y1+h5nQjJnDHP+8OceZB47I4D7iOiYnM0jXYLGEi96IusR93et30BIyEEQAJ4AvYfbuIrdbf0L5vQWfszU6/Jg==
|
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":
|
"@open-wc/demoing-storybook@^1.10.4":
|
||||||
version "1.10.5"
|
version "1.10.5"
|
||||||
resolved "https://registry.yarnpkg.com/@open-wc/demoing-storybook/-/demoing-storybook-1.10.5.tgz#3886e01fcc3b13485d5bdb4904f4ec627895609f"
|
resolved "https://registry.yarnpkg.com/@open-wc/demoing-storybook/-/demoing-storybook-1.10.5.tgz#3886e01fcc3b13485d5bdb4904f4ec627895609f"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue