diff --git a/packages/base/src/util/isValidPropertyName.js b/packages/base/src/util/isValidPropertyName.js index 3f1c89fbe0c0..a211911cecdb 100644 --- a/packages/base/src/util/isValidPropertyName.js +++ b/packages/base/src/util/isValidPropertyName.js @@ -4,6 +4,7 @@ const allowList = [ "disabled", "title", "hidden", + "draggable", ]; /** diff --git a/packages/main/src/Dialog.hbs b/packages/main/src/Dialog.hbs index 41abff6844a9..2f4566a3882f 100644 --- a/packages/main/src/Dialog.hbs +++ b/packages/main/src/Dialog.hbs @@ -1,7 +1,10 @@ {{>include "./Popup.hbs"}} {{#*inline "beforeContent"}} -
+
{{#if header.length }} {{else}} diff --git a/packages/main/src/Dialog.js b/packages/main/src/Dialog.js index 1980b1cd6876..a5dfbc12d853 100644 --- a/packages/main/src/Dialog.js +++ b/packages/main/src/Dialog.js @@ -1,4 +1,4 @@ -import { isPhone } from "@ui5/webcomponents-base/dist/Device.js"; +import { isPhone, isDesktop } from "@ui5/webcomponents-base/dist/Device.js"; import Popup from "./Popup.js"; // Template @@ -52,7 +52,7 @@ const metadata = { /** * Determines whether the ui5-dialog should be stretched to fullscreen. *

- * Note: The ui5-dialog will be stretched to aproximetly + * Note: The ui5-dialog will be stretched to approximately * 90% of the viewport. * * @type {boolean} @@ -63,12 +63,32 @@ const metadata = { type: Boolean, }, + /** + * Determines whether the ui5-dialog is draggable. + * If this property is set to true, the Dialog will be draggable by its header. + *

+ * Note: The ui5-dialog can be draggable only in desktop mode. + * @defaultvalue false + * @since 1.0.0-rc.9 + * @public + */ + draggable: { + type: Boolean, + }, + /** * @private */ onPhone: { type: Boolean, }, + + /** + * @private + */ + onDesktop: { + type: Boolean, + }, }, }; @@ -89,6 +109,9 @@ const metadata = { *

Structure

* A ui5-dialog consists of a header, content, and a footer for action buttons. * The ui5-dialog is usually displayed at the center of the screen. + * Its position can be changed by the user. To enable this, you need to set the property draggable accordingly. + + * *

Responsive Behavior

* The stretch property can be used to stretch the @@ -121,10 +144,6 @@ class Dialog extends Popup { return [PopupsCommonCss, dialogCSS]; } - onBeforeRendering() { - this.onPhone = isPhone(); - } - get isModal() { // Required by Popup.js return true; } @@ -147,6 +166,107 @@ class Dialog extends Popup { }, }; } + + onBeforeRendering() { + this.onPhone = isPhone(); + this.onDesktop = isDesktop(); + } + + onEnterDOM() { + this._dragMouseMoveHandler = this._onDragMouseMove.bind(this); + this._dragMouseUpHandler = this._onDragMouseUp.bind(this); + } + + onExitDOM() { + this._dragMouseMoveHandler = null; + this._dragMouseUpHandler = null; + } + + /** + * Event handlers + */ + _onDragMouseDown(event) { + if (!(this.draggable && this.onDesktop)) { + return; + } + + // only allow dragging on the header's whitespace + if (!event.target.classList.contains("ui5-popup-header-root") + && event.target.getAttribute("slot") !== "header") { + return; + } + + event.preventDefault(); + + const { + top, + left, + } = this.getBoundingClientRect(); + const { + width, + height, + } = window.getComputedStyle(this); + + Object.assign(this.style, { + transform: "none", + top: `${top}px`, + left: `${left}px`, + width: `${Math.round(Number(width) * 100) / 100}px`, + height: `${Math.round(Number(height) * 100) / 100}px`, + }); + + this._x = event.clientX; + this._y = event.clientY; + + this._attachDragHandlers(); + } + + _onDragMouseMove(event) { + event.preventDefault(); + + const calcX = this._x - event.clientX; + const calcY = this._y - event.clientY; + const { + left, + top, + } = this.getBoundingClientRect(); + + + Object.assign(this.style, { + left: `${Math.floor(left - calcX)}px`, + top: `${Math.floor(top - calcY)}px`, + }); + + this._x = event.clientX; + this._y = event.clientY; + } + + _onDragMouseUp() { + this._x = null; + this._y = null; + + this._detachDragHandlers(); + } + + _attachDragHandlers() { + window.addEventListener("mousemove", this._dragMouseMoveHandler); + window.addEventListener("mouseup", this._dragMouseUpHandler); + this.addEventListener("ui5-before-close", this._recenter); + } + + _detachDragHandlers() { + window.removeEventListener("mousemove", this._dragMouseMoveHandler); + window.removeEventListener("mouseup", this._dragMouseUpHandler); + } + + _recenter() { + Object.assign(this.style, { + top: "", + left: "", + transform: "", + }); + this.removeEventListener("ui5-before-close", this._recenter); + } } Dialog.define(); diff --git a/packages/main/src/themes/Dialog.css b/packages/main/src/themes/Dialog.css index 4ec769490920..42d88e60ceed 100644 --- a/packages/main/src/themes/Dialog.css +++ b/packages/main/src/themes/Dialog.css @@ -21,6 +21,15 @@ max-width: 100vw; } +:host([draggable]) .ui5-popup-header-root, +:host([draggable]) ::slotted([slot="header"]) { + cursor: move; +} + +:host([draggable]) .ui5-popup-header-root * { + cursor: auto; +} + .ui5-popup-root { display: flex; flex-direction: column; diff --git a/packages/main/test/pages/Dialog.html b/packages/main/test/pages/Dialog.html index 461c327e15c4..63a3035d37be 100644 --- a/packages/main/test/pages/Dialog.html +++ b/packages/main/test/pages/Dialog.html @@ -38,11 +38,15 @@

Open empty dialog (no focusable element) -

+
+
Open wide dialog -

+
+
Open wide dialog 2 -

+
+
+ Open draggable dialog @@ -185,6 +189,17 @@ + +
Draggable dialog
+ +

Move this dialog around the screen by dragging it by its header.

+

This feature available only on Desktop.

+ +
+ OK +
+
+