@vaadin/dialog 25.2.11 → 25.2.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -351,6 +351,16 @@
351
351
  "package": "@vaadin/overlay/src/vaadin-overlay-mixin.js"
352
352
  }
353
353
  },
354
+ {
355
+ "kind": "method",
356
+ "name": "containsFocus",
357
+ "description": "Returns true if the overlay contains focus, including nested shadow roots.",
358
+ "return": {
359
+ "type": {
360
+ "text": "boolean"
361
+ }
362
+ }
363
+ },
354
364
  {
355
365
  "kind": "field",
356
366
  "name": "focusTrap",
@@ -976,6 +986,60 @@
976
986
  }
977
987
  ]
978
988
  },
989
+ {
990
+ "kind": "javascript-module",
991
+ "path": "src/vaadin-dialog-utils.js",
992
+ "declarations": [
993
+ {
994
+ "kind": "class",
995
+ "description": "Remembers where a pointer gesture started, to tell a click from a drag once it ends.",
996
+ "name": "ClickTracker",
997
+ "members": [
998
+ {
999
+ "kind": "method",
1000
+ "name": "isClick",
1001
+ "parameters": [
1002
+ {
1003
+ "name": "e",
1004
+ "type": {
1005
+ "text": "!MouseEvent | !TouchEvent"
1006
+ }
1007
+ }
1008
+ ],
1009
+ "description": "Returns true if the pointer hasn't moved since the gesture started.",
1010
+ "return": {
1011
+ "type": {
1012
+ "text": "boolean"
1013
+ }
1014
+ }
1015
+ },
1016
+ {
1017
+ "kind": "method",
1018
+ "name": "start",
1019
+ "parameters": [
1020
+ {
1021
+ "name": "e",
1022
+ "type": {
1023
+ "text": "!MouseEvent | !TouchEvent"
1024
+ }
1025
+ }
1026
+ ],
1027
+ "description": "Stores the position where the gesture started."
1028
+ }
1029
+ ]
1030
+ }
1031
+ ],
1032
+ "exports": [
1033
+ {
1034
+ "kind": "js",
1035
+ "name": "ClickTracker",
1036
+ "declaration": {
1037
+ "name": "ClickTracker",
1038
+ "module": "src/vaadin-dialog-utils.js"
1039
+ }
1040
+ }
1041
+ ]
1042
+ },
979
1043
  {
980
1044
  "kind": "javascript-module",
981
1045
  "path": "src/vaadin-dialog.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/dialog",
3
- "version": "25.2.11",
3
+ "version": "25.2.13",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -37,19 +37,20 @@
37
37
  ],
38
38
  "dependencies": {
39
39
  "@open-wc/dedupe-mixin": "^1.3.0",
40
- "@vaadin/component-base": "~25.2.11",
41
- "@vaadin/lit-renderer": "~25.2.11",
42
- "@vaadin/overlay": "~25.2.11",
43
- "@vaadin/vaadin-themable-mixin": "~25.2.11",
40
+ "@vaadin/a11y-base": "~25.2.13",
41
+ "@vaadin/component-base": "~25.2.13",
42
+ "@vaadin/lit-renderer": "~25.2.13",
43
+ "@vaadin/overlay": "~25.2.13",
44
+ "@vaadin/vaadin-themable-mixin": "~25.2.13",
44
45
  "lit": "^3.0.0"
45
46
  },
46
47
  "devDependencies": {
47
48
  "@vaadin/a11y-base": "~25.2.11",
48
- "@vaadin/aura": "~25.2.11",
49
- "@vaadin/chai-plugins": "~25.2.11",
50
- "@vaadin/test-runner-commands": "~25.2.11",
49
+ "@vaadin/aura": "~25.2.13",
50
+ "@vaadin/chai-plugins": "~25.2.13",
51
+ "@vaadin/test-runner-commands": "~25.2.13",
51
52
  "@vaadin/testing-helpers": "^2.0.0",
52
- "@vaadin/vaadin-lumo-styles": "~25.2.11",
53
+ "@vaadin/vaadin-lumo-styles": "~25.2.13",
53
54
  "sinon": "^22.0.0"
54
55
  },
55
56
  "customElements": "custom-elements.json",
@@ -57,5 +58,5 @@
57
58
  "web-types.json",
58
59
  "web-types.lit.json"
59
60
  ],
60
- "gitHead": "6b08306ffb11c5c7665b0af8050e4368e0c44a5a"
61
+ "gitHead": "601112f027fb53668eed85f9593fa6fe5222fa00"
61
62
  }
@@ -3,8 +3,9 @@
3
3
  * Copyright (c) 2017 - 2026 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
+ import { isKeyboardActive } from '@vaadin/a11y-base/src/focus-utils.js';
6
7
  import { isTouch } from '@vaadin/component-base/src/browser-utils.js';
7
- import { eventInWindow, getMouseOrFirstTouchEvent } from './vaadin-dialog-utils.js';
8
+ import { ClickTracker, eventInWindow, getMouseOrFirstTouchEvent } from './vaadin-dialog-utils.js';
8
9
 
9
10
  export const DialogDraggableMixin = (superClass) =>
10
11
  class VaadinDialogDraggableMixin extends superClass {
@@ -44,6 +45,7 @@ export const DialogDraggableMixin = (superClass) =>
44
45
  super.ready();
45
46
  this._originalBounds = {};
46
47
  this._originalMouseCoords = {};
48
+ this.__dragTracker = new ClickTracker();
47
49
  this._startDrag = this._startDrag.bind(this);
48
50
  this._drag = this._drag.bind(this);
49
51
  this._stopDrag = this._stopDrag.bind(this);
@@ -86,6 +88,8 @@ export const DialogDraggableMixin = (superClass) =>
86
88
  if ((isResizerContainer && !isResizerContainerScrollbar) || isContentPart || isDraggable) {
87
89
  // Signal that we're handling this drag event, so parent dialogs won't also drag
88
90
  e.preventDefault();
91
+ // `preventDefault()` above cancels the native focus change, restored in `_stopDrag()`
92
+ this.__dragTracker.start(e);
89
93
  this._originalBounds = this.$.overlay.getBounds();
90
94
  const event = getMouseOrFirstTouchEvent(e);
91
95
  this._originalMouseCoords = { top: event.pageY, left: event.pageX };
@@ -128,7 +132,12 @@ export const DialogDraggableMixin = (superClass) =>
128
132
  }
129
133
 
130
134
  /** @private */
131
- _stopDrag() {
135
+ _stopDrag(e) {
136
+ // Focus on click, not on drag, so that moving the dialog doesn't interrupt editing
137
+ if (this.__dragTracker.isClick(e) && !this.$.overlay.containsFocus()) {
138
+ this.focus({ preventScroll: true, focusVisible: isKeyboardActive() });
139
+ }
140
+
132
141
  this.dispatchEvent(new CustomEvent('dragged', { detail: { top: this.top, left: this.left } }));
133
142
  window.removeEventListener('mouseup', this._stopDrag);
134
143
  window.removeEventListener('touchend', this._stopDrag);
@@ -44,4 +44,9 @@ export declare class DialogOverlayMixinClass {
44
44
  * Retrieves the coordinates of the overlay.
45
45
  */
46
46
  getBounds(): DialogOverlayBounds;
47
+
48
+ /**
49
+ * Returns true if the overlay contains focus, including nested shadow roots.
50
+ */
51
+ containsFocus(): boolean;
47
52
  }
@@ -3,6 +3,7 @@
3
3
  * Copyright (c) 2017 - 2026 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
+ import { getDeepActiveElement } from '@vaadin/a11y-base/src/focus-utils.js';
6
7
  import { SlotObserver } from '@vaadin/component-base/src/slot-observer.js';
7
8
  import { OverlayMixin } from '@vaadin/overlay/src/vaadin-overlay-mixin.js';
8
9
  import { setOverlayStateAttribute } from '@vaadin/overlay/src/vaadin-overlay-utils.js';
@@ -59,6 +60,15 @@ export const DialogOverlayMixin = (superClass) =>
59
60
  return this.owner;
60
61
  }
61
62
 
63
+ /**
64
+ * Returns true if the overlay contains focus, including nested shadow roots.
65
+ *
66
+ * @return {boolean}
67
+ */
68
+ containsFocus() {
69
+ return this._deepContains(getDeepActiveElement());
70
+ }
71
+
62
72
  /**
63
73
  * Override method from OverlayMixin to use slotted div as a renderer root.
64
74
  * @protected
@@ -3,7 +3,8 @@
3
3
  * Copyright (c) 2017 - 2026 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
- import { eventInWindow, getMouseOrFirstTouchEvent } from './vaadin-dialog-utils.js';
6
+ import { isKeyboardActive } from '@vaadin/a11y-base/src/focus-utils.js';
7
+ import { ClickTracker, eventInWindow, getMouseOrFirstTouchEvent } from './vaadin-dialog-utils.js';
7
8
  export const DialogResizableMixin = (superClass) =>
8
9
  class VaadinDialogResizableMixin extends superClass {
9
10
  static get properties() {
@@ -24,6 +25,7 @@ export const DialogResizableMixin = (superClass) =>
24
25
  super.ready();
25
26
  this._originalBounds = {};
26
27
  this._originalMouseCoords = {};
28
+ this.__resizeTracker = new ClickTracker();
27
29
  this._resizeListeners = { start: {}, resize: {}, stop: {} };
28
30
  this._addResizeListeners();
29
31
  }
@@ -35,7 +37,7 @@ export const DialogResizableMixin = (superClass) =>
35
37
  const resizer = document.createElement('div');
36
38
  this._resizeListeners.start[direction] = (e) => this._startResize(e, direction);
37
39
  this._resizeListeners.resize[direction] = (e) => this._resize(e, direction);
38
- this._resizeListeners.stop[direction] = () => this._stopResize(direction);
40
+ this._resizeListeners.stop[direction] = (e) => this._stopResize(e, direction);
39
41
  if (direction.length === 1) {
40
42
  resizer.classList.add('edge');
41
43
  }
@@ -60,6 +62,8 @@ export const DialogResizableMixin = (superClass) =>
60
62
 
61
63
  if (e.button === 0 || e.touches) {
62
64
  e.preventDefault();
65
+ // `preventDefault()` above cancels the native focus change, restored in `_stopResize()`
66
+ this.__resizeTracker.start(e);
63
67
 
64
68
  this._originalBounds = this.$.overlay.getBounds();
65
69
  const event = getMouseOrFirstTouchEvent(e);
@@ -127,10 +131,16 @@ export const DialogResizableMixin = (superClass) =>
127
131
  }
128
132
 
129
133
  /**
134
+ * @param {!MouseEvent | !TouchEvent} e
130
135
  * @param {!DialogResizableDirection} direction
131
136
  * @protected
132
137
  */
133
- _stopResize(direction) {
138
+ _stopResize(e, direction) {
139
+ // Focus on click, not on resize, so that resizing the dialog doesn't interrupt editing
140
+ if (this.__resizeTracker.isClick(e) && !this.$.overlay.containsFocus()) {
141
+ this.focus({ preventScroll: true, focusVisible: isKeyboardActive() });
142
+ }
143
+
134
144
  window.removeEventListener('mousemove', this._resizeListeners.resize[direction]);
135
145
  window.removeEventListener('touchmove', this._resizeListeners.resize[direction]);
136
146
  window.removeEventListener('mouseup', this._resizeListeners.stop[direction]);
@@ -6,6 +6,7 @@
6
6
 
7
7
  /**
8
8
  * Checks if the argument is a touch event and if so, returns a first touch.
9
+ * On `touchend`, `touches` is empty and the released point is in `changedTouches`.
9
10
  * Otherwise, if the mouse event was passed, returns it as is.
10
11
  */
11
12
  declare function getMouseOrFirstTouchEvent(e: MouseEvent | TouchEvent): MouseEvent | Touch;
@@ -18,3 +19,18 @@ export { getMouseOrFirstTouchEvent };
18
19
  declare function eventInWindow(e: MouseEvent | TouchEvent): boolean;
19
20
 
20
21
  export { eventInWindow };
22
+
23
+ /**
24
+ * Remembers where a pointer gesture started, to tell a click from a drag once it ends.
25
+ */
26
+ export declare class ClickTracker {
27
+ /**
28
+ * Stores the position where the gesture started.
29
+ */
30
+ start(e: MouseEvent | TouchEvent): void;
31
+
32
+ /**
33
+ * Returns true if the pointer hasn't moved since the gesture started.
34
+ */
35
+ isClick(e: MouseEvent | TouchEvent): boolean;
36
+ }
@@ -6,13 +6,14 @@
6
6
 
7
7
  /**
8
8
  * Checks if the argument is a touch event and if so, returns a first touch.
9
+ * On `touchend`, `touches` is empty and the released point is in `changedTouches`.
9
10
  * Otherwise, if the mouse event was passed, returns it as is.
10
11
  * @param {!MouseEvent | !TouchEvent} e
11
12
  * @return {!MouseEvent | !Touch}
12
13
  * @protected
13
14
  */
14
15
  export function getMouseOrFirstTouchEvent(e) {
15
- return e.touches ? e.touches[0] : e;
16
+ return e.touches ? e.touches[0] || e.changedTouches[0] : e;
16
17
  }
17
18
 
18
19
  /**
@@ -24,3 +25,35 @@ export function getMouseOrFirstTouchEvent(e) {
24
25
  export function eventInWindow(e) {
25
26
  return e.clientX >= 0 && e.clientX <= window.innerWidth && e.clientY >= 0 && e.clientY <= window.innerHeight;
26
27
  }
28
+
29
+ // Pointer movement in pixels above which a gesture is a drag, not a click.
30
+ // Same value as `TRACK_DISTANCE` in `@vaadin/component-base/src/gestures.js`.
31
+ const DRAG_DISTANCE = 5;
32
+
33
+ /**
34
+ * Remembers where a pointer gesture started, to tell a click from a drag once it ends.
35
+ */
36
+ export class ClickTracker {
37
+ /** @type {{ x: number, y: number }} */
38
+ #start;
39
+
40
+ /**
41
+ * Stores the position where the gesture started.
42
+ * @param {!MouseEvent | !TouchEvent} e
43
+ */
44
+ start(e) {
45
+ // Viewport coordinates, so scrolling during the gesture doesn't count as movement
46
+ const { clientX, clientY } = getMouseOrFirstTouchEvent(e);
47
+ this.#start = { x: clientX, y: clientY };
48
+ }
49
+
50
+ /**
51
+ * Returns true if the pointer hasn't moved since the gesture started.
52
+ * @param {!MouseEvent | !TouchEvent} e
53
+ * @return {boolean}
54
+ */
55
+ isClick(e) {
56
+ const { clientX, clientY } = getMouseOrFirstTouchEvent(e);
57
+ return Math.abs(clientX - this.#start.x) < DRAG_DISTANCE && Math.abs(clientY - this.#start.y) < DRAG_DISTANCE;
58
+ }
59
+ }
package/web-types.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/dialog",
4
- "version": "25.2.11",
4
+ "version": "25.2.13",
5
5
  "description-markup": "markdown",
6
6
  "contributions": {
7
7
  "html": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/dialog",
4
- "version": "25.2.11",
4
+ "version": "25.2.13",
5
5
  "description-markup": "markdown",
6
6
  "framework": "lit",
7
7
  "framework-config": {