diff --git a/packages/dialog/src/LionDialog.js b/packages/dialog/src/LionDialog.js
index 390a20459..a35d920a4 100644
--- a/packages/dialog/src/LionDialog.js
+++ b/packages/dialog/src/LionDialog.js
@@ -1,6 +1,7 @@
-import { LionOverlay, OverlayController, withModalDialogConfig } from '@lion/overlays';
+import { OverlayController, withModalDialogConfig, OverlayMixin } from '@lion/overlays';
+import { LitElement, html } from '@lion/core';
-export class LionDialog extends LionOverlay {
+export class LionDialog extends OverlayMixin(LitElement) {
// eslint-disable-next-line class-methods-use-this
_defineOverlay({ contentNode, invokerNode }) {
return new OverlayController({
@@ -11,4 +12,27 @@ export class LionDialog extends LionOverlay {
...this.config, // lit-property set by user for overrides
});
}
+
+ _setupOpenCloseListeners() {
+ this.__close = () => {
+ this.opened = false;
+ };
+ this.__toggle = () => {
+ this.opened = !this.opened;
+ };
+ this._overlayCtrl.invokerNode.addEventListener('click', this.__toggle);
+ this._overlayCtrl.contentNode.addEventListener('close', this.__close);
+ }
+
+ _teardownOpenCloseListeners() {
+ this._overlayCtrl.invokerNode.removeEventListener('click', this.__toggle);
+ this._overlayCtrl.contentNode.removeEventListener('close', this.__close);
+ }
+
+ render() {
+ return html`
+
+
+ `;
+ }
}
diff --git a/packages/input-datepicker/src/LionInputDatepicker.js b/packages/input-datepicker/src/LionInputDatepicker.js
index aa62fea9b..9b5c04d2e 100644
--- a/packages/input-datepicker/src/LionInputDatepicker.js
+++ b/packages/input-datepicker/src/LionInputDatepicker.js
@@ -264,8 +264,6 @@ export class LionInputDatepicker extends OverlayMixin(LionInputDate) {
...withModalDialogConfig(),
contentNode,
invokerNode,
- elementToFocusAfterHide: invokerNode,
- hidesOnOutsideClick: true,
});
return ctrl;
}
diff --git a/packages/overlays/index.js b/packages/overlays/index.js
index 04d690a13..7719aab23 100644
--- a/packages/overlays/index.js
+++ b/packages/overlays/index.js
@@ -7,5 +7,3 @@ export { OverlayMixin } from './src/OverlayMixin.js';
export { withBottomSheetConfig } from './src/configurations/withBottomSheetConfig.js';
export { withModalDialogConfig } from './src/configurations/withModalDialogConfig.js';
export { withDropdownConfig } from './src/configurations/withDropdownConfig.js';
-
-export { LionOverlay } from './src/LionOverlay.js';
diff --git a/packages/overlays/lion-overlay.js b/packages/overlays/lion-overlay.js
deleted file mode 100644
index 45327afaa..000000000
--- a/packages/overlays/lion-overlay.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import { LionOverlay } from './src/LionOverlay.js';
-
-customElements.define('lion-overlay', LionOverlay);
diff --git a/packages/overlays/src/LionOverlay.js b/packages/overlays/src/LionOverlay.js
deleted file mode 100644
index 7eb3cb294..000000000
--- a/packages/overlays/src/LionOverlay.js
+++ /dev/null
@@ -1,95 +0,0 @@
-import { LitElement, html } from '@lion/core';
-import { OverlayMixin } from './OverlayMixin.js';
-import { OverlayController } from './OverlayController.js';
-
-export class LionOverlay extends OverlayMixin(LitElement) {
- static get properties() {
- return {
- config: {
- type: Object,
- },
- };
- }
-
- constructor() {
- super();
- this.config = {};
- }
-
- get config() {
- return this._config;
- }
-
- set config(value) {
- if (this._overlayCtrl) {
- this._overlayCtrl.updateConfig(value);
- }
- this._config = value;
- }
-
- render() {
- return html`
-
-
- `;
- }
-
- // FIXME: This should be refactored to Array.from(this.children).find(child => child.slot === 'content')
- // When this issue is fixed https://github.com/ing-bank/lion/issues/382
- /**
- * @override
- * Overrides OverlayMixin
- * Important to use this override, so that later, contentTemplates can also be accepted
- */
- get _overlayContentNode() {
- const contentNode = this.querySelector('[slot=content]');
- if (contentNode) {
- this._cachedOverlayContentNode = contentNode;
- }
- return contentNode || this._cachedOverlayContentNode;
- }
-
- /**
- * @override
- * Overrides OverlayMixin
- */
- get _overlayInvokerNode() {
- return Array.from(this.children).find(child => child.slot === 'invoker');
- }
-
- // eslint-disable-next-line class-methods-use-this
- _defineOverlay({ contentNode, invokerNode }) {
- return new OverlayController({
- placementMode: 'global', // have to set a default
- contentNode,
- invokerNode,
- ...this.config,
- });
- }
-
- _setupShowHideListeners() {
- this.__close = () => {
- this.opened = false;
- };
- this.__toggle = () => {
- this.opened = !this.opened;
- };
- this._overlayCtrl.invokerNode.addEventListener('click', this.__toggle);
- this._overlayCtrl.contentNode.addEventListener('close', this.__close);
- }
-
- _teardownShowHideListeners() {
- this._overlayCtrl.invokerNode.removeEventListener('click', this.__toggle);
- this._overlayCtrl.contentNode.removeEventListener('close', this.__close);
- }
-
- connectedCallback() {
- super.connectedCallback();
- this._setupShowHideListeners();
- }
-
- disconnectedCallback() {
- super.disconnectedCallback();
- this._teardownShowHideListeners();
- }
-}
diff --git a/packages/overlays/src/OverlayController.js b/packages/overlays/src/OverlayController.js
index 2d88e154d..2865fcbe5 100644
--- a/packages/overlays/src/OverlayController.js
+++ b/packages/overlays/src/OverlayController.js
@@ -103,18 +103,6 @@ export class OverlayController {
* @param {OverlayConfig} cfgToAdd
*/
updateConfig(cfgToAdd) {
- // only updating the viewportConfig
- if (Object.keys(cfgToAdd).length === 1 && Object.keys(cfgToAdd)[0] === 'viewportConfig') {
- this.updateViewportConfig(cfgToAdd.viewportConfig);
- return;
- }
-
- // only updating the popperConfig
- if (Object.keys(cfgToAdd).length === 1 && Object.keys(cfgToAdd)[0] === 'popperConfig') {
- this.updatePopperConfig(cfgToAdd.popperConfig);
- return;
- }
-
// Teardown all previous configs
this._handleFeatures({ phase: 'teardown' });
@@ -164,7 +152,7 @@ export class OverlayController {
// TODO: Instead, prefetch it or use a preloader-manager to load it during idle time
this.constructor.popperModule = preloadPopper();
}
- this.__mergePopperConfigs(this.popperConfig || {});
+ this.__mergePopperConfigs(this.config.popperConfig || {});
}
this._handleFeatures({ phase: 'init' });
}
@@ -312,7 +300,6 @@ export class OverlayController {
// Otherwise we assume the 'outside world' has, purposefully, taken over
// if (this._contentNodeWrapper.activeElement) {
if (this.elementToFocusAfterHide) {
- console.log(this.elementToFocusAfterHide);
this.elementToFocusAfterHide.focus();
}
// }
@@ -556,8 +543,7 @@ export class OverlayController {
}
}
- // Popper does not export a nice method to update an existing instance with a new config. Therefore we recreate the instance.
- // TODO: Send a merge request to Popper to abstract their logic in the constructor to an exposed method which takes in the user config.
+ // TODO: Remove when no longer required by OverlayMixin (after updateConfig works properly while opened)
async updatePopperConfig(config = {}) {
this.__mergePopperConfigs(config);
if (this.isShown) {
@@ -566,12 +552,6 @@ export class OverlayController {
}
}
- updateViewportConfig(newConfig) {
- this._handlePosition({ phase: 'hide' });
- this.viewportConfig = newConfig;
- this._handlePosition({ phase: 'show' });
- }
-
teardown() {
this._handleFeatures({ phase: 'teardown' });
}
@@ -607,14 +587,19 @@ export class OverlayController {
},
};
- // Deep merging default config, previously configured user config, new user config
- this.popperConfig = {
+ /**
+ * Deep merging:
+ * - default config
+ * - previously configured user config
+ * - new user added config
+ */
+ this.config.popperConfig = {
...defaultConfig,
- ...(this.popperConfig || {}),
+ ...(this.config.popperConfig || {}),
...(config || {}),
modifiers: {
...defaultConfig.modifiers,
- ...((this.popperConfig && this.popperConfig.modifiers) || {}),
+ ...((this.config.popperConfig && this.config.popperConfig.modifiers) || {}),
...((config && config.modifiers) || {}),
},
};
@@ -627,7 +612,7 @@ export class OverlayController {
}
const { default: Popper } = await this.constructor.popperModule;
this._popper = new Popper(this._referenceNode, this._contentNodeWrapper, {
- ...this.popperConfig,
+ ...this.config.popperConfig,
});
}
diff --git a/packages/overlays/src/OverlayMixin.js b/packages/overlays/src/OverlayMixin.js
index 25af76523..749a2184f 100644
--- a/packages/overlays/src/OverlayMixin.js
+++ b/packages/overlays/src/OverlayMixin.js
@@ -15,10 +15,17 @@ export const OverlayMixin = dedupeMixin(
type: Boolean,
reflect: true,
},
- popperConfig: Object,
+ config: {
+ type: Object,
+ },
};
}
+ constructor() {
+ super();
+ this.config = {};
+ }
+
get opened() {
return this._overlayCtrl.isShown;
}
@@ -30,44 +37,63 @@ export const OverlayMixin = dedupeMixin(
}
}
- __syncOpened() {
- if (this._opened) {
- this._overlayCtrl.show();
- } else {
- this._overlayCtrl.hide();
- }
+ get config() {
+ return this._config;
}
- get popperConfig() {
- return this._popperConfig;
- }
-
- set popperConfig(config) {
- this._popperConfig = {
- ...this._popperConfig,
- ...config,
- };
- this.__syncPopper();
- }
-
- __syncPopper() {
+ set config(value) {
if (this._overlayCtrl) {
- this._overlayCtrl.updatePopperConfig(this._popperConfig);
+ this._overlayCtrl.updateConfig(value);
}
+ this._config = value;
}
+ /**
+ * @overridable method `_overlayTemplate`
+ * Be aware that the overlay will be placed in a different shadow root.
+ * Therefore, style encapsulation should be provided by the contents of
+ * _overlayTemplate
+ * @return {TemplateResult}
+ */
+
+ /**
+ * @overridable method `_defineOverlay`
+ * @desc returns an instance of a (dynamic) overlay controller
+ * @returns {OverlayController}
+ */
+ // eslint-disable-next-line
+ _defineOverlay({ contentNode, invokerNode }) {}
+
+ /**
+ * @overridable
+ * @desc use this method to setup your open and close event listeners
+ * For example, set a click event listener on _overlayInvokerNode to set opened to true
+ */
+ // eslint-disable-next-line class-methods-use-this
+ _setupOpenCloseListeners() {}
+
+ /**
+ * @overridable
+ * @desc use this method to tear down your event listeners
+ */
+ // eslint-disable-next-line class-methods-use-this
+ _teardownOpenCloseListeners() {}
+
connectedCallback() {
if (super.connectedCallback) {
super.connectedCallback();
}
this._createOverlay();
+ this._setupOpenCloseListeners();
this.__syncOpened();
this.__syncPopper();
}
firstUpdated(c) {
super.firstUpdated(c);
- this._createOutletForLocalOverlay();
+ if (this._overlayCtrl.config.placementMode === 'local') {
+ this._createOutletForLocalOverlay();
+ }
}
updated(c) {
@@ -77,6 +103,27 @@ export const OverlayMixin = dedupeMixin(
}
}
+ disconnectedCallback() {
+ if (super.disconnectedCallback) {
+ super.disconnectedCallback();
+ }
+ this._teardownOpenCloseListeners();
+ }
+
+ get _overlayInvokerNode() {
+ return Array.from(this.children).find(child => child.slot === 'invoker');
+ }
+
+ // FIXME: This should be refactored to Array.from(this.children).find(child => child.slot === 'content')
+ // When this issue is fixed https://github.com/ing-bank/lion/issues/382
+ get _overlayContentNode() {
+ const contentNode = this.querySelector('[slot=content]');
+ if (contentNode) {
+ this._cachedOverlayContentNode = contentNode;
+ }
+ return contentNode || this._cachedOverlayContentNode;
+ }
+
_renderOverlayContent() {
render(this._overlayTemplate(), this.__contentParent, {
scopeName: this.localName,
@@ -84,18 +131,6 @@ export const OverlayMixin = dedupeMixin(
});
}
- /**
- * @desc Two options for a Subclasser:
- * - 1: Define a template in `._overlayTemplate`. In this case the overlay content is
- * predefined and thus belongs to the web component. Examples: datepicker.
- * - 2: Define a getter `_overlayContentNode` that returns a node reference to a (content
- * projected) node. Used when Application Developer is in charge of the content. Examples:
- * popover, dialog, bottom sheet, dropdown, tooltip, select, combobox etc.
- */
- get __managesOverlayViaTemplate() {
- return Boolean(this._overlayTemplate);
- }
-
_createOverlay() {
let contentNode;
if (this.__managesOverlayViaTemplate) {
@@ -128,19 +163,30 @@ export const OverlayMixin = dedupeMixin(
}
/**
- * @overridable method `_overlayTemplate`
- * Be aware that the overlay will be placed in a different shadow root.
- * Therefore, style encapsulation should be provided by the contents of
- * _overlayTemplate
- * @return {TemplateResult}
+ * @desc Two options for a Subclasser:
+ * - 1: Define a template in `._overlayTemplate`. In this case the overlay content is
+ * predefined and thus belongs to the web component. Examples: datepicker.
+ * - 2: Define a getter `_overlayContentNode` that returns a node reference to a (content
+ * projected) node. Used when Application Developer is in charge of the content. Examples:
+ * popover, dialog, bottom sheet, dropdown, tooltip, select, combobox etc.
*/
+ get __managesOverlayViaTemplate() {
+ return Boolean(this._overlayTemplate);
+ }
- /**
- * @overridable method `_defineOverlay`
- * @desc returns an instance of a (dynamic) overlay controller
- * @returns {OverlayController}
- */
- // eslint-disable-next-line
- _defineOverlay({ contentNode, invokerNode }) {}
+ __syncOpened() {
+ if (this._opened) {
+ this._overlayCtrl.show();
+ } else {
+ this._overlayCtrl.hide();
+ }
+ }
+
+ __syncPopper() {
+ if (this._overlayCtrl) {
+ // TODO: Use updateConfig directly.. but first check if this sync is even still needed! Maybe we can remove it.
+ this._overlayCtrl.updatePopperConfig(this.config.popperConfig);
+ }
+ }
},
);
diff --git a/packages/overlays/stories/index.stories.js b/packages/overlays/stories/index.stories.js
index 9404080b6..32ffdacf7 100644
--- a/packages/overlays/stories/index.stories.js
+++ b/packages/overlays/stories/index.stories.js
@@ -1,9 +1,14 @@
import { storiesOf, html, withKnobs } from '@open-wc/demoing-storybook';
-import { css, render } from '@lion/core';
+import { css, render, LitElement } from '@lion/core';
import '@lion/icon/lion-icon.js';
import '@lion/button/lion-button.js';
-import { withBottomSheetConfig, withDropdownConfig, withModalDialogConfig } from '../index.js';
-import '../lion-overlay.js';
+import {
+ withBottomSheetConfig,
+ withDropdownConfig,
+ withModalDialogConfig,
+ OverlayMixin,
+ OverlayController,
+} from '../index.js';
function renderOffline(litHtmlTemplate) {
const offlineRenderContainer = document.createElement('div');
@@ -51,7 +56,7 @@ const overlayDemoStyle = css`
margin-top: 68px;
}
- lion-overlay {
+ lion-demo-overlay {
padding: 10px;
}
@@ -88,7 +93,45 @@ const overlayDemoStyle = css`
}
`;
-storiesOf('Overlay System | Overlay Component', module)
+customElements.define(
+ 'lion-demo-overlay',
+ class extends OverlayMixin(LitElement) {
+ // eslint-disable-next-line class-methods-use-this
+ _defineOverlay({ contentNode, invokerNode }) {
+ return new OverlayController({
+ placementMode: 'global', // have to set a default
+ contentNode,
+ invokerNode,
+ ...this.config,
+ });
+ }
+
+ _setupOpenCloseListeners() {
+ this.__close = () => {
+ this.opened = false;
+ };
+ this.__toggle = () => {
+ this.opened = !this.opened;
+ };
+ this._overlayCtrl.invokerNode.addEventListener('click', this.__toggle);
+ this._overlayCtrl.contentNode.addEventListener('close', this.__close);
+ }
+
+ _teardownOpenCloseListeners() {
+ this._overlayCtrl.invokerNode.removeEventListener('click', this.__toggle);
+ this._overlayCtrl.contentNode.removeEventListener('close', this.__close);
+ }
+
+ render() {
+ return html`
+
+
+ `;
+ }
+ },
+);
+
+storiesOf('Overlay System | Overlay as a WC', module)
.addDecorator(withKnobs)
.add(
'Default',
@@ -97,8 +140,9 @@ storiesOf('Overlay System | Overlay Component', module)
${overlayDemoStyle}
- Important note: Your slot="content" gets moved to global overlay container.
- After initialization it is no longer a child of lion-overlay
+ Important note: For placementMode: 'global', your
+ slot="content" gets moved to global overlay container. After initialization it
+ is no longer a child of lion-demo-overlay
To close your overlay from some action performed inside the content slot, fire a
@@ -111,7 +155,7 @@ storiesOf('Overlay System | Overlay Component', module)
The demo below demonstrates this
-
+ Overlay
Hello! You can close this notification here:
@@ -121,13 +165,13 @@ storiesOf('Overlay System | Overlay Component', module)
>⨯
- Invoker button
-
- `);
- await el._overlayCtrl.show();
- expect(el._overlayCtrl.trapsKeyboardFocus).to.be.false;
-
- el.config = { viewportConfig: { placement: 'left' } };
- expect(el._overlayCtrl.viewportConfig.placement).to.equal('left');
- expect(
- el._overlayCtrl._contentNodeWrapper.classList.contains(
- 'global-overlays__overlay-container--left',
- ),
- );
- });
- });
-});
diff --git a/packages/popup/README.md b/packages/popup/README.md
deleted file mode 100644
index 873dadf49..000000000
--- a/packages/popup/README.md
+++ /dev/null
@@ -1,34 +0,0 @@
-# Popup
-
-[//]: # 'AUTO INSERT HEADER PREPUBLISH'
-
-`lion-popup` is a component used for basic popups on click.
-Its purpose is to show content appearing when the user clicks an invoker element with the cursor or with the keyboard.
-
-## Features
-
-- Show content when clicking the invoker
-- Use the position property to position the content popup relative to the invoker
-
-## How to use
-
-### Installation
-
-```sh
-npm i --save @lion/popup
-```
-
-```js
-import '@lion/popup/lion-popup.js';
-```
-
-### Example
-
-```html
-
-