@vaadin/component-base 22.0.0-beta2 → 22.0.2

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/index.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  export { ActiveMixin } from './src/active-mixin.js';
2
+ export { ControllerMixin } from './src/controller-mixin.js';
2
3
  export { DirMixin } from './src/dir-mixin.js';
3
4
  export { DisabledMixin } from './src/disabled-mixin.js';
4
5
  export { ElementMixin } from './src/element-mixin.js';
5
6
  export { FocusMixin } from './src/focus-mixin.js';
6
7
  export { KeyboardMixin } from './src/keyboard-mixin.js';
8
+ export { SlotController } from './src/slot-controller.js';
7
9
  export { SlotMixin } from './src/slot-mixin.js';
8
10
  export { TabindexMixin } from './src/tabindex-mixin.js';
package/index.js CHANGED
@@ -1,8 +1,10 @@
1
1
  export { ActiveMixin } from './src/active-mixin.js';
2
+ export { ControllerMixin } from './src/controller-mixin.js';
2
3
  export { DirMixin } from './src/dir-mixin.js';
3
4
  export { DisabledMixin } from './src/disabled-mixin.js';
4
5
  export { ElementMixin } from './src/element-mixin.js';
5
6
  export { FocusMixin } from './src/focus-mixin.js';
6
7
  export { KeyboardMixin } from './src/keyboard-mixin.js';
8
+ export { SlotController } from './src/slot-controller.js';
7
9
  export { SlotMixin } from './src/slot-mixin.js';
8
10
  export { TabindexMixin } from './src/tabindex-mixin.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/component-base",
3
- "version": "22.0.0-beta2",
3
+ "version": "22.0.2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -38,8 +38,8 @@
38
38
  },
39
39
  "devDependencies": {
40
40
  "@esm-bundle/chai": "^4.3.4",
41
- "@vaadin/testing-helpers": "^0.3.0",
41
+ "@vaadin/testing-helpers": "^0.3.2",
42
42
  "sinon": "^9.2.4"
43
43
  },
44
- "gitHead": "f13833683e6667f6ca6678452db14aa6b7eac4a4"
44
+ "gitHead": "df21370c4a655a38eac11f79686021ab3b0887ad"
45
45
  }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2021 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+
7
+ const testUserAgent = (regexp) => regexp.test(navigator.userAgent);
8
+
9
+ const testPlatform = (regexp) => regexp.test(navigator.platform);
10
+
11
+ const testVendor = (regexp) => regexp.test(navigator.vendor);
12
+
13
+ export const isAndroid = testUserAgent(/Android/);
14
+
15
+ export const isChrome = testUserAgent(/Chrome/) && testVendor(/Google Inc/);
16
+
17
+ export const isFirefox = testUserAgent(/Firefox/);
18
+
19
+ // iPadOS 13 lies and says it's a Mac, but we can distinguish by detecting touch support.
20
+ export const isIPad = testPlatform(/^iPad/) || (testPlatform(/^Mac/) && navigator.maxTouchPoints > 1);
21
+
22
+ export const isIPhone = testPlatform(/^iPhone/);
23
+
24
+ export const isIOS = isIPhone || isIPad;
25
+
26
+ export const isSafari = testUserAgent(/^((?!chrome|android).)*safari/i);
27
+
28
+ export const isTouch = (() => {
29
+ try {
30
+ document.createEvent('TouchEvent');
31
+ return true;
32
+ } catch (e) {
33
+ return false;
34
+ }
35
+ })();
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2021 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { Constructor } from '@open-wc/dedupe-mixin';
7
+ import { ReactiveController, ReactiveControllerHost } from 'lit';
8
+
9
+ /**
10
+ * A mixin for connecting controllers to the element.
11
+ */
12
+ export declare function ControllerMixin<T extends Constructor<HTMLElement>>(
13
+ superclass: T
14
+ ): T & Constructor<ControllerMixinClass>;
15
+
16
+ export declare class ControllerMixinClass
17
+ implements Pick<ReactiveControllerHost, 'addController' | 'removeController'>
18
+ {
19
+ /**
20
+ * Registers a controller to participate in the element update cycle.
21
+ */
22
+ addController(controller: ReactiveController): void;
23
+
24
+ /**
25
+ * Removes a controller from the element.
26
+ */
27
+ removeController(controller: ReactiveController): void;
28
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2021 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
7
+
8
+ /**
9
+ * A mixin for connecting controllers to the element.
10
+ *
11
+ * @polymerMixin
12
+ */
13
+ export const ControllerMixin = dedupingMixin(
14
+ (superClass) =>
15
+ class ControllerMixinClass extends superClass {
16
+ constructor() {
17
+ super();
18
+
19
+ /**
20
+ * @type {Set<import('lit').ReactiveController>}
21
+ */
22
+ this.__controllers = new Set();
23
+ }
24
+
25
+ /** @protected */
26
+ connectedCallback() {
27
+ super.connectedCallback();
28
+
29
+ this.__controllers.forEach((c) => {
30
+ c.hostConnected && c.hostConnected();
31
+ });
32
+ }
33
+
34
+ /** @protected */
35
+ disconnectedCallback() {
36
+ super.disconnectedCallback();
37
+
38
+ this.__controllers.forEach((c) => {
39
+ c.hostDisconnected && c.hostDisconnected();
40
+ });
41
+ }
42
+
43
+ /**
44
+ * Registers a controller to participate in the element update cycle.
45
+ *
46
+ * @param {import('lit').ReactiveController} controller
47
+ * @protected
48
+ */
49
+ addController(controller) {
50
+ this.__controllers.add(controller);
51
+ // Call hostConnected if a controller is added after the element is attached.
52
+ if (this.$ !== undefined && this.isConnected && controller.hostConnected) {
53
+ controller.hostConnected();
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Removes a controller from the element.
59
+ *
60
+ * @param {import('lit').ReactiveController} controller
61
+ * @protected
62
+ */
63
+ removeController(controller) {
64
+ this.__controllers.delete(controller);
65
+ }
66
+ }
67
+ );
@@ -6,18 +6,14 @@
6
6
  import '../custom_typings/vaadin-usage-statistics.js';
7
7
  import '../custom_typings/vaadin.js';
8
8
  import { Constructor } from '@open-wc/dedupe-mixin';
9
- import { ReactiveControllerHost } from 'lit';
10
9
  import { DirMixinClass } from './dir-mixin.js';
11
10
 
12
11
  /**
13
- * A mixin to provide common logic for Vaadin components.
12
+ * A mixin providing common logic for Vaadin components.
14
13
  */
15
14
  export declare function ElementMixin<T extends Constructor<HTMLElement>>(
16
15
  superclass: T
17
- ): T &
18
- Constructor<DirMixinClass> &
19
- Constructor<ElementMixinClass> &
20
- Pick<ReactiveControllerHost, 'addController' | 'removeController'>;
16
+ ): T & Constructor<DirMixinClass> & Constructor<ElementMixinClass>;
21
17
 
22
18
  export declare class ElementMixinClass {
23
19
  static version: string;
@@ -32,7 +32,7 @@ const registered = new Set();
32
32
  export const ElementMixin = (superClass) =>
33
33
  class VaadinElementMixin extends DirMixin(superClass) {
34
34
  static get version() {
35
- return '22.0.0-beta2';
35
+ return '22.0.2';
36
36
  }
37
37
 
38
38
  /** @protected */
@@ -58,50 +58,10 @@ export const ElementMixin = (superClass) =>
58
58
  constructor() {
59
59
  super();
60
60
 
61
- this.__controllers = new Set();
62
-
63
61
  if (document.doctype === null) {
64
62
  console.warn(
65
63
  'Vaadin components require the "standards mode" declaration. Please add <!DOCTYPE html> to the HTML document.'
66
64
  );
67
65
  }
68
66
  }
69
-
70
- /** @protected */
71
- connectedCallback() {
72
- super.connectedCallback();
73
-
74
- this.__controllers.forEach((c) => {
75
- c.hostConnected && c.hostConnected();
76
- });
77
- }
78
-
79
- /** @protected */
80
- disconnectedCallback() {
81
- super.disconnectedCallback();
82
-
83
- this.__controllers.forEach((c) => {
84
- c.hostDisconnected && c.hostDisconnected();
85
- });
86
- }
87
-
88
- /**
89
- * Registers a controller to participate in the element update cycle.
90
- * @protected
91
- */
92
- addController(controller) {
93
- this.__controllers.add(controller);
94
- // Call hostConnected if a controller is added after the element is attached.
95
- if (this.$ !== undefined && this.isConnected && controller.hostConnected) {
96
- controller.hostConnected();
97
- }
98
- }
99
-
100
- /**
101
- * Removes a controller from the element.
102
- * @protected
103
- */
104
- removeController(controller) {
105
- this.__controllers.delete(controller);
106
- }
107
67
  };
@@ -7,8 +7,8 @@
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 { animationFrame, idlePeriod, microTask } from '@vaadin/component-base/src/async.js';
11
- import { Debouncer, enqueueDebouncer, flush } from '@vaadin/component-base/src/debounce.js';
10
+ import { animationFrame, idlePeriod, microTask } from './async.js';
11
+ import { Debouncer, enqueueDebouncer, flush } from './debounce.js';
12
12
 
13
13
  const IOS = navigator.userAgent.match(/iP(?:hone|ad;(?: U;)? CPU) OS (\d+)/);
14
14
  const IOS_TOUCH_SCROLLING = IOS && IOS[1] >= 8;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2021 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { ReactiveController } from 'lit';
7
+
8
+ export class SlotController implements ReactiveController {
9
+ constructor(
10
+ host: HTMLElement,
11
+ slotName: string,
12
+ slotFactory?: () => HTMLElement,
13
+ slotInitializer?: (host: HTMLElement, node: HTMLElement) => void
14
+ );
15
+
16
+ hostConnected(): void;
17
+
18
+ /**
19
+ * The controller host element.
20
+ */
21
+ host: HTMLElement;
22
+
23
+ /**
24
+ * The slotted node managed by the controller.
25
+ */
26
+ node: HTMLElement;
27
+
28
+ /**
29
+ * Return a reference to the node managed by the controller.
30
+ */
31
+ getSlotChild(): Node;
32
+
33
+ protected initialized: boolean;
34
+
35
+ protected defaultNode: Node;
36
+
37
+ /**
38
+ * Override to initialize the newly added custom node.
39
+ */
40
+ protected initCustomNode(node: Node): void;
41
+
42
+ /**
43
+ * Override to cleanup slotted node when it's removed.
44
+ */
45
+ protected teardownNode(node: Node): void;
46
+
47
+ /**
48
+ * Setup the observer to manage slot content changes.
49
+ */
50
+ protected observe(): void;
51
+ }
@@ -0,0 +1,122 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2021 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
+
8
+ /**
9
+ * A controller for providing content to slot element and observing changes.
10
+ */
11
+ export class SlotController {
12
+ constructor(host, slotName, slotFactory, slotInitializer) {
13
+ this.host = host;
14
+ this.slotName = slotName;
15
+ this.slotFactory = slotFactory;
16
+ this.slotInitializer = slotInitializer;
17
+ }
18
+
19
+ hostConnected() {
20
+ if (!this.initialized) {
21
+ const { host, slotName, slotFactory, slotInitializer } = this;
22
+
23
+ const slotted = this.getSlotChild();
24
+
25
+ if (!slotted) {
26
+ // Slot factory is optional, some slots don't have default content.
27
+ if (slotFactory) {
28
+ const slotContent = slotFactory(host);
29
+ if (slotContent instanceof Element) {
30
+ if (slotName !== '') {
31
+ slotContent.setAttribute('slot', slotName);
32
+ }
33
+ host.appendChild(slotContent);
34
+ this.node = slotContent;
35
+
36
+ // Store reference to not pass default node to `initCustomNode`.
37
+ this.defaultNode = slotContent;
38
+ }
39
+ }
40
+ } else {
41
+ this.node = slotted;
42
+ }
43
+
44
+ // Don't try to bind `this` to initializer (normally it's arrow function).
45
+ // Instead, pass the host as a first argument to access component's state.
46
+ if (slotInitializer) {
47
+ slotInitializer(host, this.node);
48
+ }
49
+
50
+ // TODO: Consider making this behavior opt-in to improve performance.
51
+ this.observe();
52
+
53
+ this.initialized = true;
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Return a reference to the node managed by the controller.
59
+ * @return {Node}
60
+ */
61
+ getSlotChild() {
62
+ const { slotName } = this;
63
+ return Array.from(this.host.childNodes).find((node) => {
64
+ // Either an element (any slot) or a text node (only un-named slot).
65
+ return (
66
+ (node.nodeType === Node.ELEMENT_NODE && node.slot === slotName) ||
67
+ (node.nodeType === Node.TEXT_NODE && node.textContent.trim() && slotName === '')
68
+ );
69
+ });
70
+ }
71
+
72
+ /**
73
+ * Override to initialize the newly added custom node.
74
+ *
75
+ * @param {Node} _node
76
+ * @protected
77
+ */
78
+ initCustomNode(_node) {}
79
+
80
+ /**
81
+ * Override to teardown slotted node when it's removed.
82
+ *
83
+ * @param {Node} _node
84
+ * @protected
85
+ */
86
+ teardownNode(_node) {}
87
+
88
+ /**
89
+ * Setup the observer to manage slot content changes.
90
+ * @protected
91
+ */
92
+ observe() {
93
+ const { slotName } = this;
94
+ const selector = slotName === '' ? 'slot:not([name])' : `slot[name=${slotName}]`;
95
+ const slot = this.host.shadowRoot.querySelector(selector);
96
+
97
+ this.__slotObserver = new FlattenedNodesObserver(slot, (info) => {
98
+ // TODO: support default slot with multiple nodes (e.g. confirm-dialog)
99
+ const current = this.node;
100
+ const newNode = info.addedNodes.find((node) => node !== current);
101
+
102
+ if (info.removedNodes.length) {
103
+ info.removedNodes.forEach((node) => {
104
+ this.teardownNode(node);
105
+ });
106
+ }
107
+
108
+ if (newNode) {
109
+ // Custom node is added, remove the current one.
110
+ if (current && current.isConnected) {
111
+ this.host.removeChild(current);
112
+ }
113
+
114
+ this.node = newNode;
115
+
116
+ if (newNode !== this.defaultNode) {
117
+ this.initCustomNode(newNode);
118
+ }
119
+ }
120
+ });
121
+ }
122
+ }
@@ -1,5 +1,11 @@
1
- import { animationFrame, timeOut } from '@vaadin/component-base/src/async.js';
2
- import { Debouncer, flush } from '@vaadin/component-base/src/debounce.js';
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2021 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { animationFrame, timeOut } from './async.js';
7
+ import { isSafari } from './browser-utils.js';
8
+ import { Debouncer, flush } from './debounce.js';
3
9
  import { ironList } from './iron-list-core.js';
4
10
 
5
11
  // iron-list can by default handle sizes up to around 100000.
@@ -20,8 +26,6 @@ export class IronListAdapter {
20
26
  // Iron-list uses this value to determine how many pages of elements to render
21
27
  this._maxPages = 1.3;
22
28
 
23
- this.__safari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
24
-
25
29
  this.timeouts = {
26
30
  SCROLL_REORDER: 500,
27
31
  IGNORE_WHEEL: 500
@@ -268,6 +272,8 @@ export class IronListAdapter {
268
272
  if (!el.hidden) {
269
273
  el.__virtualIndex = vidx + (this._vidxOffset || 0);
270
274
  this.__updateElement(el, el.__virtualIndex);
275
+ } else {
276
+ delete el.__lastUpdatedIndex;
271
277
  }
272
278
  }, itemSet);
273
279
  }
@@ -447,7 +453,7 @@ export class IronListAdapter {
447
453
  // Due to a rendering bug, reordering the rows can make parts of the scroll target disappear
448
454
  // on Safari when using sticky positioning in case the scroll target is inside a flexbox.
449
455
  // This issue manifests with grid (the header can disappear if grid is used inside a flexbox)
450
- if (this.__safari) {
456
+ if (isSafari) {
451
457
  const { transform } = this.scrollTarget.style;
452
458
  this.scrollTarget.style.transform = 'translateZ(0)';
453
459
  setTimeout(() => (this.scrollTarget.style.transform = transform));