@vaadin/component-base 23.2.0-dev.8a7678b70 → 23.3.0-alpha1
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.
- package/README.md +1 -1
- package/custom_typings/vaadin.d.ts +0 -1
- package/package.json +2 -2
- package/src/a11y-announcer.d.ts +1 -1
- package/src/active-mixin.d.ts +4 -4
- package/src/active-mixin.js +11 -14
- package/src/controller-mixin.d.ts +3 -3
- package/src/debounce.d.ts +1 -1
- package/src/dir-mixin.d.ts +2 -2
- package/src/disabled-mixin.d.ts +1 -1
- package/src/dom-utils.d.ts +11 -0
- package/src/dom-utils.js +51 -0
- package/src/element-mixin.d.ts +3 -3
- package/src/element-mixin.js +1 -1
- package/src/focus-mixin.d.ts +2 -2
- package/src/focus-mixin.js +2 -23
- package/src/focus-trap-controller.d.ts +1 -1
- package/src/focus-trap-controller.js +5 -1
- package/src/focus-utils.d.ts +6 -0
- package/src/focus-utils.js +32 -0
- package/src/gestures.js +1 -1
- package/src/keyboard-direction-mixin.d.ts +41 -0
- package/src/keyboard-direction-mixin.js +192 -0
- package/src/keyboard-mixin.d.ts +2 -2
- package/src/media-query-controller.d.ts +1 -1
- package/src/overflow-controller.d.ts +34 -0
- package/src/overflow-controller.js +115 -0
- package/src/polylit-mixin.d.ts +3 -3
- package/src/polylit-mixin.js +11 -10
- package/src/resize-mixin.d.ts +2 -2
- package/src/slot-controller.d.ts +2 -1
- package/src/slot-controller.js +6 -2
- package/src/slot-mixin.d.ts +2 -2
- package/src/tabindex-mixin.d.ts +6 -6
- package/src/tooltip-controller.d.ts +84 -0
- package/src/tooltip-controller.js +128 -0
- package/src/virtualizer-iron-list-adapter.js +15 -5
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ A set of helpers and mixins used by Vaadin components.
|
|
|
4
4
|
|
|
5
5
|
## Contributing
|
|
6
6
|
|
|
7
|
-
Read the [contributing guide](https://vaadin.com/docs/latest/
|
|
7
|
+
Read the [contributing guide](https://vaadin.com/docs/latest/contributing/overview) to learn about our development process, how to propose bugfixes and improvements, and how to test your changes to Vaadin components.
|
|
8
8
|
|
|
9
9
|
## License
|
|
10
10
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/component-base",
|
|
3
|
-
"version": "23.
|
|
3
|
+
"version": "23.3.0-alpha1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -42,5 +42,5 @@
|
|
|
42
42
|
"@vaadin/testing-helpers": "^0.3.2",
|
|
43
43
|
"sinon": "^13.0.2"
|
|
44
44
|
},
|
|
45
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "beabc527d4b1274eb798ff701d406fed45cfe638"
|
|
46
46
|
}
|
package/src/a11y-announcer.d.ts
CHANGED
|
@@ -7,4 +7,4 @@
|
|
|
7
7
|
/**
|
|
8
8
|
* Cause a text string to be announced by screen readers.
|
|
9
9
|
*/
|
|
10
|
-
export function announce(text: string, options?: { mode?: '
|
|
10
|
+
export function announce(text: string, options?: { mode?: 'alert' | 'assertive' | 'polite'; timeout?: number }): void;
|
package/src/active-mixin.d.ts
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* Copyright (c) 2021 - 2022 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
|
-
import { DisabledMixinClass } from './disabled-mixin.js';
|
|
8
|
-
import { KeyboardMixinClass } from './keyboard-mixin.js';
|
|
6
|
+
import type { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
|
+
import type { DisabledMixinClass } from './disabled-mixin.js';
|
|
8
|
+
import type { KeyboardMixinClass } from './keyboard-mixin.js';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* A mixin to toggle the `active` attribute.
|
|
@@ -18,7 +18,7 @@ import { KeyboardMixinClass } from './keyboard-mixin.js';
|
|
|
18
18
|
*/
|
|
19
19
|
export declare function ActiveMixin<T extends Constructor<HTMLElement>>(
|
|
20
20
|
base: T,
|
|
21
|
-
):
|
|
21
|
+
): Constructor<ActiveMixinClass> & Constructor<DisabledMixinClass> & Constructor<KeyboardMixinClass> & T;
|
|
22
22
|
|
|
23
23
|
export declare class ActiveMixinClass {
|
|
24
24
|
/**
|
package/src/active-mixin.js
CHANGED
|
@@ -79,21 +79,18 @@ export const ActiveMixin = (superclass) =>
|
|
|
79
79
|
|
|
80
80
|
if (this._shouldSetActive(event) && this._activeKeys.includes(event.key)) {
|
|
81
81
|
this._setActive(true);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Removes the `active` attribute from the element if the activation key is released.
|
|
87
|
-
*
|
|
88
|
-
* @param {KeyboardEvent} event
|
|
89
|
-
* @protected
|
|
90
|
-
* @override
|
|
91
|
-
*/
|
|
92
|
-
_onKeyUp(event) {
|
|
93
|
-
super._onKeyUp(event);
|
|
94
82
|
|
|
95
|
-
|
|
96
|
-
|
|
83
|
+
// Element can become hidden before the `keyup` event, e.g. on button click.
|
|
84
|
+
// Use document listener to ensure `active` attribute is removed correctly.
|
|
85
|
+
document.addEventListener(
|
|
86
|
+
'keyup',
|
|
87
|
+
(e) => {
|
|
88
|
+
if (this._activeKeys.includes(e.key)) {
|
|
89
|
+
this._setActive(false);
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
{ once: true },
|
|
93
|
+
);
|
|
97
94
|
}
|
|
98
95
|
}
|
|
99
96
|
|
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
* Copyright (c) 2021 - 2022 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
|
-
import { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
6
|
+
import type { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
|
+
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* A mixin for connecting controllers to the element.
|
|
11
11
|
*/
|
|
12
12
|
export declare function ControllerMixin<T extends Constructor<HTMLElement>>(
|
|
13
13
|
superclass: T,
|
|
14
|
-
):
|
|
14
|
+
): Constructor<ControllerMixinClass> & T;
|
|
15
15
|
|
|
16
16
|
export declare class ControllerMixinClass
|
|
17
17
|
implements Pick<ReactiveControllerHost, 'addController' | 'removeController'>
|
package/src/debounce.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Code distributed by Google as part of the polymer project is also
|
|
8
8
|
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
|
9
9
|
*/
|
|
10
|
-
import { AsyncInterface } from './async.js';
|
|
10
|
+
import type { AsyncInterface } from './async.js';
|
|
11
11
|
|
|
12
12
|
export declare class Debouncer {
|
|
13
13
|
/**
|
package/src/dir-mixin.d.ts
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
* Copyright (c) 2021 - 2022 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { Constructor } from '@open-wc/dedupe-mixin';
|
|
6
|
+
import type { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* A mixin to handle `dir` attribute based on the one set on the `<html>` element.
|
|
10
10
|
*/
|
|
11
|
-
export declare function DirMixin<T extends Constructor<HTMLElement>>(base: T):
|
|
11
|
+
export declare function DirMixin<T extends Constructor<HTMLElement>>(base: T): Constructor<DirMixinClass> & T;
|
|
12
12
|
|
|
13
13
|
export declare class DirMixinClass {
|
|
14
14
|
protected __getNormalizedScrollLeft(element: Element | null): number;
|
package/src/disabled-mixin.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Copyright (c) 2021 - 2022 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { Constructor } from '@open-wc/dedupe-mixin';
|
|
6
|
+
import type { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* A mixin to provide disabled property for field components.
|
package/src/dom-utils.d.ts
CHANGED
|
@@ -12,3 +12,14 @@
|
|
|
12
12
|
* and involves both the light DOM and ancestor shadow DOM trees.
|
|
13
13
|
*/
|
|
14
14
|
export function getAncestorRootNodes(node: Node): Node[];
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Adds a value to an attribute containing space-delimited values.
|
|
18
|
+
*/
|
|
19
|
+
export function addValueToAttribute(element: HTMLElement, attr: string, value: string): void;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Removes a value from an attribute containing space-delimited values.
|
|
23
|
+
* If the value is the last one, the whole attribute is removed.
|
|
24
|
+
*/
|
|
25
|
+
export function removeValueFromAttribute(element: HTMLElement, attr: string, value: string): void;
|
package/src/dom-utils.js
CHANGED
|
@@ -39,3 +39,54 @@ export function getAncestorRootNodes(node) {
|
|
|
39
39
|
|
|
40
40
|
return result;
|
|
41
41
|
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @param {string} value
|
|
45
|
+
* @return {Set<string>}
|
|
46
|
+
*/
|
|
47
|
+
function deserializeAttributeValue(value) {
|
|
48
|
+
if (!value) {
|
|
49
|
+
return new Set();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return new Set(value.split(' '));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @param {Set<string>} values
|
|
57
|
+
* @return {string}
|
|
58
|
+
*/
|
|
59
|
+
function serializeAttributeValue(values) {
|
|
60
|
+
return [...values].join(' ');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Adds a value to an attribute containing space-delimited values.
|
|
65
|
+
*
|
|
66
|
+
* @param {HTMLElement} element
|
|
67
|
+
* @param {string} attr
|
|
68
|
+
* @param {string} value
|
|
69
|
+
*/
|
|
70
|
+
export function addValueToAttribute(element, attr, value) {
|
|
71
|
+
const values = deserializeAttributeValue(element.getAttribute(attr));
|
|
72
|
+
values.add(value);
|
|
73
|
+
element.setAttribute(attr, serializeAttributeValue(values));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Removes a value from an attribute containing space-delimited values.
|
|
78
|
+
* If the value is the last one, the whole attribute is removed.
|
|
79
|
+
*
|
|
80
|
+
* @param {HTMLElement} element
|
|
81
|
+
* @param {string} attr
|
|
82
|
+
* @param {string} value
|
|
83
|
+
*/
|
|
84
|
+
export function removeValueFromAttribute(element, attr, value) {
|
|
85
|
+
const values = deserializeAttributeValue(element.getAttribute(attr));
|
|
86
|
+
values.delete(value);
|
|
87
|
+
if (values.size === 0) {
|
|
88
|
+
element.removeAttribute(attr);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
element.setAttribute(attr, serializeAttributeValue(values));
|
|
92
|
+
}
|
package/src/element-mixin.d.ts
CHANGED
|
@@ -5,15 +5,15 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import '../custom_typings/vaadin-usage-statistics.js';
|
|
7
7
|
import '../custom_typings/vaadin.js';
|
|
8
|
-
import { Constructor } from '@open-wc/dedupe-mixin';
|
|
9
|
-
import { DirMixinClass } from './dir-mixin.js';
|
|
8
|
+
import type { Constructor } from '@open-wc/dedupe-mixin';
|
|
9
|
+
import type { DirMixinClass } from './dir-mixin.js';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* A mixin providing common logic for Vaadin components.
|
|
13
13
|
*/
|
|
14
14
|
export declare function ElementMixin<T extends Constructor<HTMLElement>>(
|
|
15
15
|
superclass: T,
|
|
16
|
-
):
|
|
16
|
+
): Constructor<DirMixinClass> & Constructor<ElementMixinClass> & T;
|
|
17
17
|
|
|
18
18
|
export declare class ElementMixinClass {
|
|
19
19
|
static version: string;
|
package/src/element-mixin.js
CHANGED
package/src/focus-mixin.d.ts
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
* Copyright (c) 2021 - 2022 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { Constructor } from '@open-wc/dedupe-mixin';
|
|
6
|
+
import type { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* A mixin to handle `focused` and `focus-ring` attributes based on focus.
|
|
10
10
|
*/
|
|
11
|
-
export declare function FocusMixin<T extends Constructor<HTMLElement>>(base: T):
|
|
11
|
+
export declare function FocusMixin<T extends Constructor<HTMLElement>>(base: T): Constructor<FocusMixinClass> & T;
|
|
12
12
|
|
|
13
13
|
export declare class FocusMixinClass {
|
|
14
14
|
protected readonly _keyboardActive: boolean;
|
package/src/focus-mixin.js
CHANGED
|
@@ -4,28 +4,7 @@
|
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
6
|
import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
|
|
7
|
-
|
|
8
|
-
// We consider the keyboard to be active if the window has received a keydown
|
|
9
|
-
// event since the last mousedown event.
|
|
10
|
-
let keyboardActive = false;
|
|
11
|
-
|
|
12
|
-
// Listen for top-level keydown and mousedown events.
|
|
13
|
-
// Use capture phase so we detect events even if they're handled.
|
|
14
|
-
window.addEventListener(
|
|
15
|
-
'keydown',
|
|
16
|
-
() => {
|
|
17
|
-
keyboardActive = true;
|
|
18
|
-
},
|
|
19
|
-
{ capture: true },
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
window.addEventListener(
|
|
23
|
-
'mousedown',
|
|
24
|
-
() => {
|
|
25
|
-
keyboardActive = false;
|
|
26
|
-
},
|
|
27
|
-
{ capture: true },
|
|
28
|
-
);
|
|
7
|
+
import { isKeyboardActive } from './focus-utils.js';
|
|
29
8
|
|
|
30
9
|
/**
|
|
31
10
|
* A mixin to handle `focused` and `focus-ring` attributes based on focus.
|
|
@@ -40,7 +19,7 @@ export const FocusMixin = dedupingMixin(
|
|
|
40
19
|
* @return {boolean}
|
|
41
20
|
*/
|
|
42
21
|
get _keyboardActive() {
|
|
43
|
-
return
|
|
22
|
+
return isKeyboardActive();
|
|
44
23
|
}
|
|
45
24
|
|
|
46
25
|
/** @protected */
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Copyright (c) 2021 - 2022 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { ReactiveController } from 'lit';
|
|
6
|
+
import type { ReactiveController } from 'lit';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* A controller for trapping focus within a DOM node.
|
|
@@ -125,7 +125,11 @@ export class FocusTrapController {
|
|
|
125
125
|
const step = backward ? -1 : 1;
|
|
126
126
|
const currentIndex = this.__focusedElementIndex;
|
|
127
127
|
const nextIndex = (focusableElements.length + currentIndex + step) % focusableElements.length;
|
|
128
|
-
focusableElements[nextIndex]
|
|
128
|
+
const element = focusableElements[nextIndex];
|
|
129
|
+
element.focus();
|
|
130
|
+
if (element.localName === 'input') {
|
|
131
|
+
element.select();
|
|
132
|
+
}
|
|
129
133
|
}
|
|
130
134
|
|
|
131
135
|
/**
|
package/src/focus-utils.d.ts
CHANGED
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Returns true if the window has received a keydown
|
|
9
|
+
* event since the last mousedown event.
|
|
10
|
+
*/
|
|
11
|
+
export declare function isKeyboardActive(): boolean;
|
|
12
|
+
|
|
7
13
|
/**
|
|
8
14
|
* Returns true if the element is hidden, false otherwise.
|
|
9
15
|
*
|
package/src/focus-utils.js
CHANGED
|
@@ -4,6 +4,38 @@
|
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
// We consider the keyboard to be active if the window has received a keydown
|
|
8
|
+
// event since the last mousedown event.
|
|
9
|
+
let keyboardActive = false;
|
|
10
|
+
|
|
11
|
+
// Listen for top-level keydown and mousedown events.
|
|
12
|
+
// Use capture phase so we detect events even if they're handled.
|
|
13
|
+
window.addEventListener(
|
|
14
|
+
'keydown',
|
|
15
|
+
() => {
|
|
16
|
+
keyboardActive = true;
|
|
17
|
+
},
|
|
18
|
+
{ capture: true },
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
window.addEventListener(
|
|
22
|
+
'mousedown',
|
|
23
|
+
() => {
|
|
24
|
+
keyboardActive = false;
|
|
25
|
+
},
|
|
26
|
+
{ capture: true },
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Returns true if the window has received a keydown
|
|
31
|
+
* event since the last mousedown event.
|
|
32
|
+
*
|
|
33
|
+
* @return {boolean}
|
|
34
|
+
*/
|
|
35
|
+
export function isKeyboardActive() {
|
|
36
|
+
return keyboardActive;
|
|
37
|
+
}
|
|
38
|
+
|
|
7
39
|
/**
|
|
8
40
|
* Returns true if the element is hidden directly with `display: none` or `visibility: hidden`,
|
|
9
41
|
* false otherwise.
|
package/src/gestures.js
CHANGED
|
@@ -277,7 +277,7 @@ function _handleNative(ev) {
|
|
|
277
277
|
}
|
|
278
278
|
if (!ev[HANDLED_OBJ]) {
|
|
279
279
|
ev[HANDLED_OBJ] = {};
|
|
280
|
-
if (type.
|
|
280
|
+
if (type.startsWith('touch')) {
|
|
281
281
|
const t = ev.changedTouches[0];
|
|
282
282
|
if (type === 'touchstart') {
|
|
283
283
|
// Only handle the first finger
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2022 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import type { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
|
+
import type { KeyboardMixinClass } from './keyboard-mixin.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A mixin for navigating items with keyboard.
|
|
11
|
+
*/
|
|
12
|
+
export declare function KeyboardDirectionMixin<T extends Constructor<HTMLElement>>(
|
|
13
|
+
base: T,
|
|
14
|
+
): Constructor<KeyboardDirectionMixinClass> & Constructor<KeyboardMixinClass> & T;
|
|
15
|
+
|
|
16
|
+
export declare class KeyboardDirectionMixinClass {
|
|
17
|
+
protected readonly focused: Element | null;
|
|
18
|
+
|
|
19
|
+
protected readonly _vertical: boolean;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Returns index of the next item that satisfies the given condition,
|
|
23
|
+
* based on the index of the current item and a numeric increment.
|
|
24
|
+
*/
|
|
25
|
+
protected _getAvailableIndex(
|
|
26
|
+
items: Element[],
|
|
27
|
+
index: number,
|
|
28
|
+
increment: number,
|
|
29
|
+
condition: (item: Element) => boolean,
|
|
30
|
+
): number;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Focus the item at given index. Override this method to add custom logic.
|
|
34
|
+
*/
|
|
35
|
+
protected _focus(index: number, navigating: boolean): void;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Focus the given item. Override this method to add custom logic.
|
|
39
|
+
*/
|
|
40
|
+
protected _focusItem(item: Element, navigating: boolean): void;
|
|
41
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2022 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { isElementFocused, isElementHidden } from './focus-utils.js';
|
|
7
|
+
import { KeyboardMixin } from './keyboard-mixin.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A mixin for navigating items with keyboard.
|
|
11
|
+
*
|
|
12
|
+
* @polymerMixin
|
|
13
|
+
* @mixes KeyboardMixin
|
|
14
|
+
*/
|
|
15
|
+
export const KeyboardDirectionMixin = (superclass) =>
|
|
16
|
+
class KeyboardDirectionMixinClass extends KeyboardMixin(superclass) {
|
|
17
|
+
/** @protected */
|
|
18
|
+
focus() {
|
|
19
|
+
const items = this._getItems();
|
|
20
|
+
if (Array.isArray(items)) {
|
|
21
|
+
const idx = this._getAvailableIndex(items, 0, null, (item) => !isElementHidden(item));
|
|
22
|
+
if (idx >= 0) {
|
|
23
|
+
items[idx].focus();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @return {Element | null}
|
|
30
|
+
* @protected
|
|
31
|
+
*/
|
|
32
|
+
get focused() {
|
|
33
|
+
return (this._getItems() || []).find(isElementFocused);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @return {boolean}
|
|
38
|
+
* @protected
|
|
39
|
+
*/
|
|
40
|
+
get _vertical() {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get the list of items participating in keyboard navigation.
|
|
46
|
+
* By default, it treats all the light DOM children as items.
|
|
47
|
+
* Override this method to provide custom list of elements.
|
|
48
|
+
*
|
|
49
|
+
* @return {Element[]}
|
|
50
|
+
* @protected
|
|
51
|
+
*/
|
|
52
|
+
_getItems() {
|
|
53
|
+
return Array.from(this.children);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Override an event listener from `KeyboardMixin`.
|
|
58
|
+
*
|
|
59
|
+
* @param {!KeyboardEvent} event
|
|
60
|
+
* @protected
|
|
61
|
+
* @override
|
|
62
|
+
*/
|
|
63
|
+
_onKeyDown(event) {
|
|
64
|
+
super._onKeyDown(event);
|
|
65
|
+
|
|
66
|
+
if (event.metaKey || event.ctrlKey) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const { key } = event;
|
|
71
|
+
const items = this._getItems() || [];
|
|
72
|
+
const currentIdx = items.indexOf(this.focused);
|
|
73
|
+
|
|
74
|
+
let idx;
|
|
75
|
+
let increment;
|
|
76
|
+
|
|
77
|
+
const isRTL = !this._vertical && this.getAttribute('dir') === 'rtl';
|
|
78
|
+
const dirIncrement = isRTL ? -1 : 1;
|
|
79
|
+
|
|
80
|
+
if (this.__isPrevKey(key)) {
|
|
81
|
+
increment = -dirIncrement;
|
|
82
|
+
idx = currentIdx - dirIncrement;
|
|
83
|
+
} else if (this.__isNextKey(key)) {
|
|
84
|
+
increment = dirIncrement;
|
|
85
|
+
idx = currentIdx + dirIncrement;
|
|
86
|
+
} else if (key === 'Home') {
|
|
87
|
+
increment = 1;
|
|
88
|
+
idx = 0;
|
|
89
|
+
} else if (key === 'End') {
|
|
90
|
+
increment = -1;
|
|
91
|
+
idx = items.length - 1;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
idx = this._getAvailableIndex(items, idx, increment, (item) => !isElementHidden(item));
|
|
95
|
+
|
|
96
|
+
if (idx >= 0) {
|
|
97
|
+
event.preventDefault();
|
|
98
|
+
this._focus(idx, true);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @param {string} key
|
|
104
|
+
* @return {boolean}
|
|
105
|
+
* @private
|
|
106
|
+
*/
|
|
107
|
+
__isPrevKey(key) {
|
|
108
|
+
return this._vertical ? key === 'ArrowUp' : key === 'ArrowLeft';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @param {string} key
|
|
113
|
+
* @return {boolean}
|
|
114
|
+
* @private
|
|
115
|
+
*/
|
|
116
|
+
__isNextKey(key) {
|
|
117
|
+
return this._vertical ? key === 'ArrowDown' : key === 'ArrowRight';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Focus the item at given index. Override this method to add custom logic.
|
|
122
|
+
*
|
|
123
|
+
* @param {number} index
|
|
124
|
+
* @param {boolean} navigating
|
|
125
|
+
* @protected
|
|
126
|
+
*/
|
|
127
|
+
_focus(index, navigating = false) {
|
|
128
|
+
const items = this._getItems();
|
|
129
|
+
|
|
130
|
+
this._focusItem(items[index], navigating);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Focus the given item. Override this method to add custom logic.
|
|
135
|
+
*
|
|
136
|
+
* @param {Element} item
|
|
137
|
+
* @param {boolean} navigating
|
|
138
|
+
* @protected
|
|
139
|
+
*/
|
|
140
|
+
_focusItem(item) {
|
|
141
|
+
if (item) {
|
|
142
|
+
item.focus();
|
|
143
|
+
|
|
144
|
+
// Generally, the items are expected to implement `FocusMixin`
|
|
145
|
+
// that would set this attribute based on the `keydown` event.
|
|
146
|
+
// We set it manually to handle programmatic focus() calls.
|
|
147
|
+
item.setAttribute('focus-ring', '');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Returns index of the next item that satisfies the given condition,
|
|
153
|
+
* based on the index of the current item and a numeric increment.
|
|
154
|
+
*
|
|
155
|
+
* @param {Element[]} items - array of items to iterate over
|
|
156
|
+
* @param {number} index - index of the current item
|
|
157
|
+
* @param {number} increment - numeric increment, can be either 1 or -1
|
|
158
|
+
* @param {Function} condition - function used to check the item
|
|
159
|
+
* @return {number}
|
|
160
|
+
* @protected
|
|
161
|
+
*/
|
|
162
|
+
_getAvailableIndex(items, index, increment, condition) {
|
|
163
|
+
const totalItems = items.length;
|
|
164
|
+
let idx = index;
|
|
165
|
+
for (let i = 0; typeof idx === 'number' && i < totalItems; i += 1, idx += increment || 1) {
|
|
166
|
+
if (idx < 0) {
|
|
167
|
+
idx = totalItems - 1;
|
|
168
|
+
} else if (idx >= totalItems) {
|
|
169
|
+
idx = 0;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const item = items[idx];
|
|
173
|
+
|
|
174
|
+
if (!item.hasAttribute('disabled') && this.__isMatchingItem(item, condition)) {
|
|
175
|
+
return idx;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return -1;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Returns true if the item matches condition.
|
|
183
|
+
*
|
|
184
|
+
* @param {Element} item - item to check
|
|
185
|
+
* @param {Function} condition - function used to check the item
|
|
186
|
+
* @return {number}
|
|
187
|
+
* @private
|
|
188
|
+
*/
|
|
189
|
+
__isMatchingItem(item, condition) {
|
|
190
|
+
return typeof condition === 'function' ? condition(item) : true;
|
|
191
|
+
}
|
|
192
|
+
};
|
package/src/keyboard-mixin.d.ts
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
* Copyright (c) 2021 - 2022 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { Constructor } from '@open-wc/dedupe-mixin';
|
|
6
|
+
import type { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* A mixin that manages keyboard handling.
|
|
10
10
|
* The mixin subscribes to the keyboard events while an actual implementation
|
|
11
11
|
* for the event handlers is left to the client (a component or another mixin).
|
|
12
12
|
*/
|
|
13
|
-
export declare function KeyboardMixin<T extends Constructor<HTMLElement>>(base: T):
|
|
13
|
+
export declare function KeyboardMixin<T extends Constructor<HTMLElement>>(base: T): Constructor<KeyboardMixinClass> & T;
|
|
14
14
|
|
|
15
15
|
export declare class KeyboardMixinClass {
|
|
16
16
|
/**
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Copyright (c) 2021 - 2022 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { ReactiveController } from 'lit';
|
|
6
|
+
import type { ReactiveController } from 'lit';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* A controller for listening on media query changes.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2021 - 2022 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import type { ReactiveController } from 'lit';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A controller that detects if content inside the element overflows its scrolling viewport,
|
|
10
|
+
* and sets the `overflow` attribute on the host with a value that indicates the directions
|
|
11
|
+
* where content is overflowing. Supported values are: `top`, `bottom`, `start`, `end`.
|
|
12
|
+
*/
|
|
13
|
+
export class OverflowController implements ReactiveController {
|
|
14
|
+
/**
|
|
15
|
+
* The controller host element.
|
|
16
|
+
*/
|
|
17
|
+
host: HTMLElement;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The element that wraps scrollable content.
|
|
21
|
+
* If not set, the host element is used.
|
|
22
|
+
*/
|
|
23
|
+
scrollTarget: HTMLElement;
|
|
24
|
+
|
|
25
|
+
constructor(host: HTMLElement, scrollTarget?: HTMLElement);
|
|
26
|
+
|
|
27
|
+
hostConnected(): void;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Setup a scroll listener and observers to update overflow.
|
|
31
|
+
* Also performs one-time update synchronously when called.
|
|
32
|
+
*/
|
|
33
|
+
protected observe(): void;
|
|
34
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2021 - 2022 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { FlattenedNodesObserver } from '@polymer/polymer/lib/utils/flattened-nodes-observer.js';
|
|
7
|
+
import { animationFrame } from './async.js';
|
|
8
|
+
import { Debouncer } from './debounce.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A controller that detects if content inside the element overflows its scrolling viewport,
|
|
12
|
+
* and sets the `overflow` attribute on the host with a value that indicates the directions
|
|
13
|
+
* where content is overflowing. Supported values are: `top`, `bottom`, `start`, `end`.
|
|
14
|
+
*/
|
|
15
|
+
export class OverflowController {
|
|
16
|
+
constructor(host, scrollTarget) {
|
|
17
|
+
/**
|
|
18
|
+
* The controller host element.
|
|
19
|
+
*
|
|
20
|
+
* @type {HTMLElement}
|
|
21
|
+
*/
|
|
22
|
+
this.host = host;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The element that wraps scrollable content.
|
|
26
|
+
* If not set, the host element is used.
|
|
27
|
+
*
|
|
28
|
+
* @type {HTMLElement}
|
|
29
|
+
*/
|
|
30
|
+
this.scrollTarget = scrollTarget || host;
|
|
31
|
+
|
|
32
|
+
/** @private */
|
|
33
|
+
this.__boundOnScroll = this.__onScroll.bind(this);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
hostConnected() {
|
|
37
|
+
if (!this.initialized) {
|
|
38
|
+
this.initialized = true;
|
|
39
|
+
|
|
40
|
+
this.observe();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Setup scroll listener and observers to update overflow.
|
|
46
|
+
* Also performs one-time update synchronously when called.
|
|
47
|
+
* @protected
|
|
48
|
+
*/
|
|
49
|
+
observe() {
|
|
50
|
+
this.__resizeObserver = new ResizeObserver(() => {
|
|
51
|
+
this.__debounceOverflow = Debouncer.debounce(this.__debounceOverflow, animationFrame, () => {
|
|
52
|
+
this.__updateOverflow();
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
this.__resizeObserver.observe(this.host);
|
|
57
|
+
|
|
58
|
+
this.__childObserver = new FlattenedNodesObserver(this.host, (info) => {
|
|
59
|
+
info.addedNodes.forEach((node) => {
|
|
60
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
61
|
+
this.__resizeObserver.observe(node);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
info.removedNodes.forEach((node) => {
|
|
66
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
67
|
+
this.__resizeObserver.unobserve(node);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
this.__updateOverflow();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Update overflow attribute on scroll
|
|
75
|
+
this.scrollTarget.addEventListener('scroll', this.__boundOnScroll);
|
|
76
|
+
|
|
77
|
+
this.__updateOverflow();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** @private */
|
|
81
|
+
__onScroll() {
|
|
82
|
+
this.__updateOverflow();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** @private */
|
|
86
|
+
__updateOverflow() {
|
|
87
|
+
const target = this.scrollTarget;
|
|
88
|
+
|
|
89
|
+
let overflow = '';
|
|
90
|
+
|
|
91
|
+
if (target.scrollTop > 0) {
|
|
92
|
+
overflow += ' top';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (target.scrollTop < target.scrollHeight - target.clientHeight) {
|
|
96
|
+
overflow += ' bottom';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const scrollLeft = Math.abs(target.scrollLeft);
|
|
100
|
+
if (scrollLeft > 0) {
|
|
101
|
+
overflow += ' start';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (scrollLeft < target.scrollWidth - target.clientWidth) {
|
|
105
|
+
overflow += ' end';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
overflow = overflow.trim();
|
|
109
|
+
if (overflow.length > 0 && this.host.getAttribute('overflow') !== overflow) {
|
|
110
|
+
this.host.setAttribute('overflow', overflow);
|
|
111
|
+
} else if (overflow.length === 0 && this.host.hasAttribute('overflow')) {
|
|
112
|
+
this.host.removeAttribute('overflow');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
package/src/polylit-mixin.d.ts
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
* Copyright (c) 2021 - 2022 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
|
-
import { LitElement } from 'lit';
|
|
6
|
+
import type { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
|
+
import type { LitElement } from 'lit';
|
|
8
8
|
|
|
9
|
-
export declare function PolylitMixin<T extends Constructor<LitElement>>(base: T):
|
|
9
|
+
export declare function PolylitMixin<T extends Constructor<LitElement>>(base: T): Constructor<PolylitMixinClass> & T;
|
|
10
10
|
|
|
11
11
|
export declare class PolylitMixinClass {
|
|
12
12
|
ready(): void;
|
package/src/polylit-mixin.js
CHANGED
|
@@ -10,7 +10,8 @@ const caseMap = {};
|
|
|
10
10
|
const CAMEL_TO_DASH = /([A-Z])/g;
|
|
11
11
|
|
|
12
12
|
function camelToDash(camel) {
|
|
13
|
-
|
|
13
|
+
caseMap[camel] = caseMap[camel] || camel.replace(CAMEL_TO_DASH, '-$1').toLowerCase();
|
|
14
|
+
return caseMap[camel];
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
function upper(name) {
|
|
@@ -165,10 +166,9 @@ const PolylitMixinImplementation = (superclass) => {
|
|
|
165
166
|
}
|
|
166
167
|
|
|
167
168
|
/** @protected */
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
169
|
+
firstUpdated() {
|
|
170
|
+
super.firstUpdated();
|
|
171
|
+
|
|
172
172
|
this.$ = this.$ || {};
|
|
173
173
|
this.shadowRoot.querySelectorAll('[id]').forEach((node) => {
|
|
174
174
|
this.$[node.id] = node;
|
|
@@ -176,11 +176,7 @@ const PolylitMixinImplementation = (superclass) => {
|
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
/** @protected */
|
|
179
|
-
|
|
180
|
-
super.firstUpdated();
|
|
181
|
-
|
|
182
|
-
this.ready();
|
|
183
|
-
}
|
|
179
|
+
ready() {}
|
|
184
180
|
|
|
185
181
|
/** @protected */
|
|
186
182
|
updated(props) {
|
|
@@ -199,6 +195,11 @@ const PolylitMixinImplementation = (superclass) => {
|
|
|
199
195
|
if (this.constructor.__notifyProps) {
|
|
200
196
|
this.__runNotifyProps(props, this.constructor.__notifyProps);
|
|
201
197
|
}
|
|
198
|
+
|
|
199
|
+
if (!this.__isReadyInvoked) {
|
|
200
|
+
this.ready();
|
|
201
|
+
this.__isReadyInvoked = true;
|
|
202
|
+
}
|
|
202
203
|
}
|
|
203
204
|
|
|
204
205
|
/** @protected */
|
package/src/resize-mixin.d.ts
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
* Copyright (c) 2021 - 2022 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { Constructor } from '@open-wc/dedupe-mixin';
|
|
6
|
+
import type { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* A mixin that uses a ResizeObserver to listen to host size changes.
|
|
10
10
|
*/
|
|
11
|
-
export declare function ResizeMixin<T extends Constructor<HTMLElement>>(base: T):
|
|
11
|
+
export declare function ResizeMixin<T extends Constructor<HTMLElement>>(base: T): Constructor<ResizeMixinClass> & T;
|
|
12
12
|
|
|
13
13
|
export declare class ResizeMixinClass {
|
|
14
14
|
/**
|
package/src/slot-controller.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Copyright (c) 2021 - 2022 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { ReactiveController } from 'lit';
|
|
6
|
+
import type { ReactiveController } from 'lit';
|
|
7
7
|
|
|
8
8
|
export class SlotController extends EventTarget implements ReactiveController {
|
|
9
9
|
/**
|
|
@@ -27,6 +27,7 @@ export class SlotController extends EventTarget implements ReactiveController {
|
|
|
27
27
|
slotName: string,
|
|
28
28
|
slotFactory?: () => HTMLElement,
|
|
29
29
|
slotInitializer?: (host: HTMLElement, node: HTMLElement) => void,
|
|
30
|
+
useUniqueId?: boolean,
|
|
30
31
|
);
|
|
31
32
|
|
|
32
33
|
hostConnected(): void;
|
package/src/slot-controller.js
CHANGED
|
@@ -23,14 +23,18 @@ export class SlotController extends EventTarget {
|
|
|
23
23
|
return `${prefix}-${host.localName}-${generateUniqueId()}`;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
constructor(host, slotName, slotFactory, slotInitializer) {
|
|
26
|
+
constructor(host, slotName, slotFactory, slotInitializer, useUniqueId) {
|
|
27
27
|
super();
|
|
28
28
|
|
|
29
29
|
this.host = host;
|
|
30
30
|
this.slotName = slotName;
|
|
31
31
|
this.slotFactory = slotFactory;
|
|
32
32
|
this.slotInitializer = slotInitializer;
|
|
33
|
-
|
|
33
|
+
|
|
34
|
+
// Only generate the default ID if requested by the controller.
|
|
35
|
+
if (useUniqueId) {
|
|
36
|
+
this.defaultId = SlotController.generateId(slotName, host);
|
|
37
|
+
}
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
hostConnected() {
|
package/src/slot-mixin.d.ts
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
* Copyright (c) 2021 - 2022 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { Constructor } from '@open-wc/dedupe-mixin';
|
|
6
|
+
import type { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* A mixin to provide content for named slots defined by component.
|
|
10
10
|
*/
|
|
11
|
-
export declare function SlotMixin<T extends Constructor<HTMLElement>>(base: T):
|
|
11
|
+
export declare function SlotMixin<T extends Constructor<HTMLElement>>(base: T): Constructor<SlotMixinClass> & T;
|
|
12
12
|
|
|
13
13
|
export declare class SlotMixinClass {
|
|
14
14
|
/**
|
package/src/tabindex-mixin.d.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* Copyright (c) 2021 - 2022 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
|
-
import { DisabledMixinClass } from './disabled-mixin.js';
|
|
6
|
+
import type { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
|
+
import type { DisabledMixinClass } from './disabled-mixin.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* A mixin to toggle the `tabindex` attribute.
|
|
@@ -14,23 +14,23 @@ import { DisabledMixinClass } from './disabled-mixin.js';
|
|
|
14
14
|
*/
|
|
15
15
|
export declare function TabindexMixin<T extends Constructor<HTMLElement>>(
|
|
16
16
|
base: T,
|
|
17
|
-
):
|
|
17
|
+
): Constructor<DisabledMixinClass> & Constructor<TabindexMixinClass> & T;
|
|
18
18
|
|
|
19
19
|
export declare class TabindexMixinClass {
|
|
20
20
|
/**
|
|
21
21
|
* Indicates whether the element can be focused and where it participates in sequential keyboard navigation.
|
|
22
22
|
*/
|
|
23
|
-
tabindex: number |
|
|
23
|
+
tabindex: number | null | undefined;
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Stores the last known tabindex since the element has been disabled.
|
|
27
27
|
*/
|
|
28
|
-
protected _lastTabIndex: number |
|
|
28
|
+
protected _lastTabIndex: number | null | undefined;
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* When the user has changed tabindex while the element is disabled,
|
|
32
32
|
* the observer reverts tabindex to -1 and rather saves the new tabindex value to apply it later.
|
|
33
33
|
* The new value will be applied as soon as the element becomes enabled.
|
|
34
34
|
*/
|
|
35
|
-
protected _tabindexChanged(tabindex: number |
|
|
35
|
+
protected _tabindexChanged(tabindex: number | null | undefined): void;
|
|
36
36
|
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2022 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { SlotController } from './slot-controller.js';
|
|
7
|
+
|
|
8
|
+
type TooltipPosition =
|
|
9
|
+
| 'bottom-end'
|
|
10
|
+
| 'bottom-start'
|
|
11
|
+
| 'bottom'
|
|
12
|
+
| 'end-bottom'
|
|
13
|
+
| 'end-top'
|
|
14
|
+
| 'end'
|
|
15
|
+
| 'start-bottom'
|
|
16
|
+
| 'start-top'
|
|
17
|
+
| 'start'
|
|
18
|
+
| 'top-end'
|
|
19
|
+
| 'top-start'
|
|
20
|
+
| 'top';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* A controller that manages the slotted tooltip element.
|
|
24
|
+
*/
|
|
25
|
+
export class TooltipController extends SlotController {
|
|
26
|
+
/**
|
|
27
|
+
* Object with properties passed to `textGenerator`
|
|
28
|
+
* function to be used for generating tooltip text.
|
|
29
|
+
*/
|
|
30
|
+
context: Record<string, unknown>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* When true, the tooltip is controlled programmatically
|
|
34
|
+
* instead of reacting to focus and mouse events.
|
|
35
|
+
*/
|
|
36
|
+
manual: boolean;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* When true, the tooltip is opened programmatically.
|
|
40
|
+
* Only works if `manual` is set to `true`.
|
|
41
|
+
*/
|
|
42
|
+
opened: boolean;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Position of the tooltip with respect to its target.
|
|
46
|
+
*/
|
|
47
|
+
position: TooltipPosition;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* An HTML element to attach the tooltip to.
|
|
51
|
+
*/
|
|
52
|
+
target: HTMLElement;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Set a context object to be used by text generator.
|
|
56
|
+
*/
|
|
57
|
+
setContext(context: Record<string, unknown>): void;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Toggle manual state on the slotted tooltip.
|
|
61
|
+
*/
|
|
62
|
+
setManual(manual: boolean): void;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Toggle opened state on the slotted tooltip.
|
|
66
|
+
*/
|
|
67
|
+
setOpened(opened: boolean): void;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Set position on the slotted tooltip.
|
|
71
|
+
*/
|
|
72
|
+
setPosition(position: TooltipPosition): void;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Set function used to detect whether to show
|
|
76
|
+
* the tooltip based on a condition.
|
|
77
|
+
*/
|
|
78
|
+
setShouldShow(shouldShow: (target: HTMLElement) => boolean): void;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Set an HTML element to attach the tooltip to.
|
|
82
|
+
*/
|
|
83
|
+
setTarget(target: HTMLElement): void;
|
|
84
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2022 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { SlotController } from './slot-controller.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A controller that manages the slotted tooltip element.
|
|
10
|
+
*/
|
|
11
|
+
export class TooltipController extends SlotController {
|
|
12
|
+
constructor(host) {
|
|
13
|
+
// Do not provide slot factory to create tooltip lazily.
|
|
14
|
+
super(host, 'tooltip');
|
|
15
|
+
|
|
16
|
+
this.setTarget(host);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Override to initialize the newly added custom tooltip.
|
|
21
|
+
*
|
|
22
|
+
* @param {Node} tooltipNode
|
|
23
|
+
* @protected
|
|
24
|
+
* @override
|
|
25
|
+
*/
|
|
26
|
+
initCustomNode(tooltipNode) {
|
|
27
|
+
tooltipNode.target = this.target;
|
|
28
|
+
|
|
29
|
+
if (this.context !== undefined) {
|
|
30
|
+
tooltipNode.context = this.context;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (this.manual !== undefined) {
|
|
34
|
+
tooltipNode.manual = this.manual;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (this.opened !== undefined) {
|
|
38
|
+
tooltipNode.opened = this.opened;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (this.position !== undefined) {
|
|
42
|
+
tooltipNode.position = this.position;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (this.shouldShow !== undefined) {
|
|
46
|
+
tooltipNode.shouldShow = this.shouldShow;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Set a context object to be used by text generator.
|
|
52
|
+
* @param {object} context
|
|
53
|
+
*/
|
|
54
|
+
setContext(context) {
|
|
55
|
+
this.context = context;
|
|
56
|
+
|
|
57
|
+
const tooltipNode = this.node;
|
|
58
|
+
if (tooltipNode) {
|
|
59
|
+
tooltipNode.context = context;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Toggle manual state on the slotted tooltip.
|
|
65
|
+
* @param {boolean} manual
|
|
66
|
+
*/
|
|
67
|
+
setManual(manual) {
|
|
68
|
+
this.manual = manual;
|
|
69
|
+
|
|
70
|
+
const tooltipNode = this.node;
|
|
71
|
+
if (tooltipNode) {
|
|
72
|
+
tooltipNode.manual = manual;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Toggle opened state on the slotted tooltip.
|
|
78
|
+
* @param {boolean} opened
|
|
79
|
+
*/
|
|
80
|
+
setOpened(opened) {
|
|
81
|
+
this.opened = opened;
|
|
82
|
+
|
|
83
|
+
const tooltipNode = this.node;
|
|
84
|
+
if (tooltipNode) {
|
|
85
|
+
tooltipNode.opened = opened;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Set position on the slotted tooltip.
|
|
91
|
+
* @param {string} position
|
|
92
|
+
*/
|
|
93
|
+
setPosition(position) {
|
|
94
|
+
this.position = position;
|
|
95
|
+
|
|
96
|
+
const tooltipNode = this.node;
|
|
97
|
+
if (tooltipNode) {
|
|
98
|
+
tooltipNode.position = position;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Set function used to detect whether to show
|
|
104
|
+
* the tooltip based on a condition.
|
|
105
|
+
* @param {Function} shouldShow
|
|
106
|
+
*/
|
|
107
|
+
setShouldShow(shouldShow) {
|
|
108
|
+
this.shouldShow = shouldShow;
|
|
109
|
+
|
|
110
|
+
const tooltipNode = this.node;
|
|
111
|
+
if (tooltipNode) {
|
|
112
|
+
tooltipNode.shouldShow = shouldShow;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Set an HTML element to attach the tooltip to.
|
|
118
|
+
* @param {HTMLElement} target
|
|
119
|
+
*/
|
|
120
|
+
setTarget(target) {
|
|
121
|
+
this.target = target;
|
|
122
|
+
|
|
123
|
+
const tooltipNode = this.node;
|
|
124
|
+
if (tooltipNode) {
|
|
125
|
+
tooltipNode.target = target;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -55,7 +55,9 @@ export class IronListAdapter {
|
|
|
55
55
|
if (this.reorderElements) {
|
|
56
56
|
// Reordering the physical elements cancels the user's grab of the scroll bar handle on Safari.
|
|
57
57
|
// Need to defer reordering until the user lets go of the scroll bar handle.
|
|
58
|
-
this.scrollTarget.addEventListener('mousedown', () =>
|
|
58
|
+
this.scrollTarget.addEventListener('mousedown', () => {
|
|
59
|
+
this.__mouseDown = true;
|
|
60
|
+
});
|
|
59
61
|
this.scrollTarget.addEventListener('mouseup', () => {
|
|
60
62
|
this.__mouseDown = false;
|
|
61
63
|
if (this.__pendingReorder) {
|
|
@@ -298,7 +300,9 @@ export class IronListAdapter {
|
|
|
298
300
|
/** @private */
|
|
299
301
|
_isClientFull() {
|
|
300
302
|
// Workaround an issue in iron-list that can cause it to freeze on fast scroll
|
|
301
|
-
setTimeout(() =>
|
|
303
|
+
setTimeout(() => {
|
|
304
|
+
this.__clientFull = true;
|
|
305
|
+
});
|
|
302
306
|
return this.__clientFull || super._isClientFull();
|
|
303
307
|
}
|
|
304
308
|
|
|
@@ -378,7 +382,9 @@ export class IronListAdapter {
|
|
|
378
382
|
this.__debouncerWheelAnimationFrame = Debouncer.debounce(
|
|
379
383
|
this.__debouncerWheelAnimationFrame,
|
|
380
384
|
animationFrame,
|
|
381
|
-
() =>
|
|
385
|
+
() => {
|
|
386
|
+
this._wheelAnimationFrame = false;
|
|
387
|
+
},
|
|
382
388
|
);
|
|
383
389
|
|
|
384
390
|
const momentum = Math.abs(e.deltaX) + Math.abs(deltaY);
|
|
@@ -394,7 +400,9 @@ export class IronListAdapter {
|
|
|
394
400
|
this._debouncerIgnoreNewWheel = Debouncer.debounce(
|
|
395
401
|
this._debouncerIgnoreNewWheel,
|
|
396
402
|
timeOut.after(this.timeouts.IGNORE_WHEEL),
|
|
397
|
-
() =>
|
|
403
|
+
() => {
|
|
404
|
+
this._ignoreNewWheel = false;
|
|
405
|
+
},
|
|
398
406
|
);
|
|
399
407
|
} else if ((this._hasResidualMomentum && momentum <= this._previousMomentum) || this._ignoreNewWheel) {
|
|
400
408
|
e.preventDefault();
|
|
@@ -494,7 +502,9 @@ export class IronListAdapter {
|
|
|
494
502
|
if (isSafari) {
|
|
495
503
|
const { transform } = this.scrollTarget.style;
|
|
496
504
|
this.scrollTarget.style.transform = 'translateZ(0)';
|
|
497
|
-
setTimeout(() =>
|
|
505
|
+
setTimeout(() => {
|
|
506
|
+
this.scrollTarget.style.transform = transform;
|
|
507
|
+
});
|
|
498
508
|
}
|
|
499
509
|
}
|
|
500
510
|
|