@vaadin/popover 24.5.0-alpha5 → 24.5.0-alpha7

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/lit.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './src/lit/renderer-directives.js';
package/lit.js ADDED
@@ -0,0 +1 @@
1
+ export * from './src/lit/renderer-directives.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/popover",
3
- "version": "24.5.0-alpha5",
3
+ "version": "24.5.0-alpha7",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -20,6 +20,8 @@
20
20
  "module": "vaadin-popover.js",
21
21
  "type": "module",
22
22
  "files": [
23
+ "lit.d.ts",
24
+ "lit.js",
23
25
  "src",
24
26
  "theme",
25
27
  "vaadin-*.d.ts",
@@ -35,12 +37,13 @@
35
37
  ],
36
38
  "dependencies": {
37
39
  "@open-wc/dedupe-mixin": "^1.3.0",
38
- "@vaadin/a11y-base": "24.5.0-alpha5",
39
- "@vaadin/component-base": "24.5.0-alpha5",
40
- "@vaadin/overlay": "24.5.0-alpha5",
41
- "@vaadin/vaadin-lumo-styles": "24.5.0-alpha5",
42
- "@vaadin/vaadin-material-styles": "24.5.0-alpha5",
43
- "@vaadin/vaadin-themable-mixin": "24.5.0-alpha5",
40
+ "@vaadin/a11y-base": "24.5.0-alpha7",
41
+ "@vaadin/component-base": "24.5.0-alpha7",
42
+ "@vaadin/lit-renderer": "24.5.0-alpha7",
43
+ "@vaadin/overlay": "24.5.0-alpha7",
44
+ "@vaadin/vaadin-lumo-styles": "24.5.0-alpha7",
45
+ "@vaadin/vaadin-material-styles": "24.5.0-alpha7",
46
+ "@vaadin/vaadin-themable-mixin": "24.5.0-alpha7",
44
47
  "lit": "^3.0.0"
45
48
  },
46
49
  "devDependencies": {
@@ -52,5 +55,5 @@
52
55
  "web-types.json",
53
56
  "web-types.lit.json"
54
57
  ],
55
- "gitHead": "4e57d240ababf0c2db9d674891b90bdf3812e6ae"
58
+ "gitHead": "89f77a69ae0eba6247f2b3084941f9395d7134e1"
56
59
  }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2024 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import type { DirectiveResult } from 'lit/directive.js';
7
+ import { LitRendererDirective, type LitRendererResult } from '@vaadin/lit-renderer';
8
+ import type { Popover } from '../vaadin-popover.js';
9
+
10
+ export type PopoverLitRenderer = (popover: Popover) => LitRendererResult;
11
+
12
+ export class PopoverRendererDirective extends LitRendererDirective<Popover, PopoverLitRenderer> {
13
+ /**
14
+ * Adds the renderer callback to the popover.
15
+ */
16
+ addRenderer(): void;
17
+
18
+ /**
19
+ * Runs the renderer callback on the popover.
20
+ */
21
+ runRenderer(): void;
22
+
23
+ /**
24
+ * Removes the renderer callback from the popover.
25
+ */
26
+ removeRenderer(): void;
27
+ }
28
+
29
+ /**
30
+ * A Lit directive for populating the content of the `<vaadin-popover-overlay>` element.
31
+ *
32
+ * The directive accepts a renderer callback returning a Lit template and assigns it to the popover
33
+ * via the `renderer` property. The renderer is called once to populate the content when assigned
34
+ * and whenever a single dependency or an array of dependencies changes.
35
+ * It is not guaranteed that the renderer will be called immediately (synchronously) in both cases.
36
+ *
37
+ * Dependencies can be a single value or an array of values.
38
+ * Values are checked against previous values with strict equality (`===`),
39
+ * so the check won't detect nested property changes inside objects or arrays.
40
+ * When dependencies are provided as an array, each item is checked against the previous value
41
+ * at the same index with strict equality. Nested arrays are also checked only by strict
42
+ * equality.
43
+ *
44
+ * Example of usage:
45
+ * ```js
46
+ * `<vaadin-popover
47
+ * ${popoverRenderer((popover) => html`...`)}
48
+ * ></vaadin-popover>`
49
+ * ```
50
+ *
51
+ * @param renderer the renderer callback that returns a Lit template.
52
+ * @param dependencies a single dependency or an array of dependencies
53
+ * which trigger a re-render when changed.
54
+ */
55
+ export declare function popoverRenderer(
56
+ renderer: PopoverLitRenderer,
57
+ dependencies?: unknown,
58
+ ): DirectiveResult<typeof PopoverRendererDirective>;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2024 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { directive } from 'lit/directive.js';
7
+ import { LitRendererDirective } from '@vaadin/lit-renderer';
8
+
9
+ export class PopoverRendererDirective extends LitRendererDirective {
10
+ /**
11
+ * Adds the renderer callback to the popover.
12
+ */
13
+ addRenderer() {
14
+ this.element.renderer = (root, popover) => {
15
+ this.renderRenderer(root, popover);
16
+ };
17
+ }
18
+
19
+ /**
20
+ * Runs the renderer callback on the popover.
21
+ */
22
+ runRenderer() {
23
+ this.element.requestContentUpdate();
24
+ }
25
+
26
+ /**
27
+ * Removes the renderer callback from the popover.
28
+ */
29
+ removeRenderer() {
30
+ this.element.renderer = null;
31
+ }
32
+ }
33
+
34
+ /**
35
+ * A Lit directive for populating the content of the `<vaadin-popover-overlay>` element.
36
+ *
37
+ * The directive accepts a renderer callback returning a Lit template and assigns it to the popover
38
+ * via the `renderer` property. The renderer is called once to populate the content when assigned
39
+ * and whenever a single dependency or an array of dependencies changes.
40
+ * It is not guaranteed that the renderer will be called immediately (synchronously) in both cases.
41
+ *
42
+ * Dependencies can be a single value or an array of values.
43
+ * Values are checked against previous values with strict equality (`===`),
44
+ * so the check won't detect nested property changes inside objects or arrays.
45
+ * When dependencies are provided as an array, each item is checked against the previous value
46
+ * at the same index with strict equality. Nested arrays are also checked only by strict
47
+ * equality.
48
+ *
49
+ * Example of usage:
50
+ * ```js
51
+ * `<vaadin-popover
52
+ * ${popoverRenderer((popover) => html`...`)}
53
+ * ></vaadin-popover>`
54
+ * ```
55
+ *
56
+ * @param renderer the renderer callback that returns a Lit template.
57
+ * @param dependencies a single dependency or an array of dependencies
58
+ * which trigger a re-render when changed.
59
+ */
60
+ export const popoverRenderer = directive(PopoverRendererDirective);
@@ -59,6 +59,8 @@ export const PopoverOverlayMixin = (superClass) =>
59
59
  return;
60
60
  }
61
61
 
62
+ this.removeAttribute('arrow-centered');
63
+
62
64
  // Center the overlay horizontally
63
65
  if (this.position === 'bottom' || this.position === 'top') {
64
66
  const targetRect = this.positionTarget.getBoundingClientRect();
@@ -70,6 +72,8 @@ export const PopoverOverlayMixin = (superClass) =>
70
72
  const left = overlayRect.left + offset;
71
73
  if (left > 0) {
72
74
  this.style.left = `${left}px`;
75
+ // Center the pointer arrow horizontally
76
+ this.setAttribute('arrow-centered', '');
73
77
  }
74
78
  }
75
79
 
@@ -77,6 +81,8 @@ export const PopoverOverlayMixin = (superClass) =>
77
81
  const right = parseFloat(this.style.right) + offset;
78
82
  if (right > 0) {
79
83
  this.style.right = `${right}px`;
84
+ // Center the pointer arrow horizontally
85
+ this.setAttribute('arrow-centered', '');
80
86
  }
81
87
  }
82
88
  }
@@ -82,6 +82,17 @@ class PopoverOverlay extends PopoverOverlayMixin(DirMixin(ThemableMixin(PolylitM
82
82
  :host([position^='end'][end-aligned]) [part='overlay'] {
83
83
  margin-inline-end: var(--vaadin-popover-offset-end, 0);
84
84
  }
85
+
86
+ [part='arrow'] {
87
+ display: none;
88
+ position: absolute;
89
+ height: 0;
90
+ width: 0;
91
+ }
92
+
93
+ :host([theme~='arrow']) [part='arrow'] {
94
+ display: block;
95
+ }
85
96
  `,
86
97
  ];
87
98
  }
@@ -91,6 +102,7 @@ class PopoverOverlay extends PopoverOverlayMixin(DirMixin(ThemableMixin(PolylitM
91
102
  return html`
92
103
  <div id="backdrop" part="backdrop" hidden ?hidden="${!this.withBackdrop}"></div>
93
104
  <div part="overlay" id="overlay" tabindex="0">
105
+ <div part="arrow"></div>
94
106
  <div part="content" id="content"><slot></slot></div>
95
107
  </div>
96
108
  `;
@@ -43,6 +43,12 @@ export type PopoverEventMap = HTMLElementEventMap & PopoverCustomEventMap;
43
43
  * See [`<vaadin-overlay>`](#/elements/vaadin-overlay) documentation
44
44
  * for `<vaadin-popover-overlay>` parts.
45
45
  *
46
+ * In addition to `<vaadin-overlay>` parts, the following parts are available for styling:
47
+ *
48
+ * Part name | Description
49
+ * -----------------|-------------------------------------------
50
+ * `arrow` | Optional arrow pointing to the target when using `theme="arrow"`
51
+ *
46
52
  * The following state attributes are available for styling:
47
53
  *
48
54
  * Attribute | Description
@@ -85,6 +91,12 @@ declare class Popover extends PopoverPositionMixin(
85
91
  */
86
92
  accessibleNameRef: string | null | undefined;
87
93
 
94
+ /**
95
+ * When true, the popover content automatically receives focus after
96
+ * it is opened. Modal popovers use this behavior by default.
97
+ */
98
+ autofocus: boolean;
99
+
88
100
  /**
89
101
  * Height to be set on the overlay content.
90
102
  *
@@ -6,7 +6,12 @@
6
6
  import './vaadin-popover-overlay.js';
7
7
  import { html, LitElement } from 'lit';
8
8
  import { ifDefined } from 'lit/directives/if-defined.js';
9
- import { isKeyboardActive } from '@vaadin/a11y-base/src/focus-utils.js';
9
+ import {
10
+ getDeepActiveElement,
11
+ getFocusableElements,
12
+ isElementFocused,
13
+ isKeyboardActive,
14
+ } from '@vaadin/a11y-base/src/focus-utils.js';
10
15
  import { defineCustomElement } from '@vaadin/component-base/src/define.js';
11
16
  import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
12
17
  import { OverlayClassMixin } from '@vaadin/component-base/src/overlay-class-mixin.js';
@@ -139,6 +144,12 @@ class PopoverOpenedStateController {
139
144
  * See [`<vaadin-overlay>`](#/elements/vaadin-overlay) documentation
140
145
  * for `<vaadin-popover-overlay>` parts.
141
146
  *
147
+ * In addition to `<vaadin-overlay>` parts, the following parts are available for styling:
148
+ *
149
+ * Part name | Description
150
+ * -----------------|-------------------------------------------
151
+ * `arrow` | Optional arrow pointing to the target when using `theme="arrow"`
152
+ *
142
153
  * The following state attributes are available for styling:
143
154
  *
144
155
  * Attribute | Description
@@ -199,6 +210,14 @@ class Popover extends PopoverPositionMixin(
199
210
  type: String,
200
211
  },
201
212
 
213
+ /**
214
+ * When true, the popover content automatically receives focus after
215
+ * it is opened. Modal popovers use this behavior by default.
216
+ */
217
+ autofocus: {
218
+ type: Boolean,
219
+ },
220
+
202
221
  /**
203
222
  * Height to be set on the overlay content.
204
223
  *
@@ -374,7 +393,6 @@ class Popover extends PopoverPositionMixin(
374
393
  this.__onGlobalClick = this.__onGlobalClick.bind(this);
375
394
  this.__onGlobalKeyDown = this.__onGlobalKeyDown.bind(this);
376
395
  this.__onTargetClick = this.__onTargetClick.bind(this);
377
- this.__onTargetKeydown = this.__onTargetKeydown.bind(this);
378
396
  this.__onTargetFocusIn = this.__onTargetFocusIn.bind(this);
379
397
  this.__onTargetFocusOut = this.__onTargetFocusOut.bind(this);
380
398
  this.__onTargetMouseEnter = this.__onTargetMouseEnter.bind(this);
@@ -415,6 +433,7 @@ class Popover extends PopoverPositionMixin(
415
433
  .restoreFocusNode="${this.target}"
416
434
  @vaadin-overlay-escape-press="${this.__onEscapePress}"
417
435
  @vaadin-overlay-outside-click="${this.__onOutsideClick}"
436
+ @vaadin-overlay-open="${this.__onOverlayOpened}"
418
437
  @vaadin-overlay-closed="${this.__onOverlayClosed}"
419
438
  ></vaadin-popover-overlay>
420
439
  `;
@@ -445,14 +464,14 @@ class Popover extends PopoverPositionMixin(
445
464
  connectedCallback() {
446
465
  super.connectedCallback();
447
466
 
448
- document.addEventListener('click', this.__onGlobalClick, true);
467
+ document.documentElement.addEventListener('click', this.__onGlobalClick, true);
449
468
  }
450
469
 
451
470
  /** @protected */
452
471
  disconnectedCallback() {
453
472
  super.disconnectedCallback();
454
473
 
455
- document.removeEventListener('click', this.__onGlobalClick, true);
474
+ document.documentElement.removeEventListener('click', this.__onGlobalClick, true);
456
475
 
457
476
  this._openedStateController.close(true);
458
477
  }
@@ -464,7 +483,6 @@ class Popover extends PopoverPositionMixin(
464
483
  */
465
484
  _addTargetListeners(target) {
466
485
  target.addEventListener('click', this.__onTargetClick);
467
- target.addEventListener('keydown', this.__onTargetKeydown);
468
486
  target.addEventListener('mouseenter', this.__onTargetMouseEnter);
469
487
  target.addEventListener('mouseleave', this.__onTargetMouseLeave);
470
488
  target.addEventListener('focusin', this.__onTargetFocusIn);
@@ -478,7 +496,6 @@ class Popover extends PopoverPositionMixin(
478
496
  */
479
497
  _removeTargetListeners(target) {
480
498
  target.removeEventListener('click', this.__onTargetClick);
481
- target.removeEventListener('keydown', this.__onTargetKeydown);
482
499
  target.removeEventListener('mouseenter', this.__onTargetMouseEnter);
483
500
  target.removeEventListener('mouseleave', this.__onTargetMouseLeave);
484
501
  target.removeEventListener('focusin', this.__onTargetFocusIn);
@@ -559,9 +576,13 @@ class Popover extends PopoverPositionMixin(
559
576
  * @private
560
577
  */
561
578
  __onGlobalKeyDown(event) {
579
+ // Modal popover uses overlay logic for Esc key and focus trap.
580
+ if (this.modal) {
581
+ return;
582
+ }
583
+
562
584
  if (
563
585
  event.key === 'Escape' &&
564
- !this.modal &&
565
586
  !this.noCloseOnEsc &&
566
587
  this.opened &&
567
588
  !this.__isManual &&
@@ -571,16 +592,84 @@ class Popover extends PopoverPositionMixin(
571
592
  event.stopPropagation();
572
593
  this._openedStateController.close(true);
573
594
  }
595
+
596
+ // Include popover content in the Tab order after the target.
597
+ if (event.key === 'Tab') {
598
+ if (event.shiftKey) {
599
+ this.__onGlobalShiftTab(event);
600
+ } else {
601
+ this.__onGlobalTab(event);
602
+ }
603
+ }
604
+ }
605
+
606
+ /** @private */
607
+ __onGlobalTab(event) {
608
+ const overlayPart = this._overlayElement.$.overlay;
609
+
610
+ // Move focus to the popover content on target element Tab
611
+ if (this.target && isElementFocused(this.target)) {
612
+ event.preventDefault();
613
+ overlayPart.focus();
614
+ return;
615
+ }
616
+
617
+ // Move focus to the next element after target on content Tab
618
+ const lastFocusable = this.__getLastFocusable(overlayPart);
619
+ if (lastFocusable && isElementFocused(lastFocusable)) {
620
+ const focusable = this.__getNextBodyFocusable(this.target);
621
+ if (focusable && focusable !== overlayPart) {
622
+ event.preventDefault();
623
+ focusable.focus();
624
+ return;
625
+ }
626
+ }
627
+
628
+ // Prevent focusing the popover content on previous element Tab
629
+ const activeElement = getDeepActiveElement();
630
+ const nextFocusable = this.__getNextBodyFocusable(activeElement);
631
+ if (nextFocusable === overlayPart && lastFocusable) {
632
+ // Move focus to the last overlay focusable and do NOT prevent keydown
633
+ // to move focus outside the popover content (e.g. to the URL bar).
634
+ lastFocusable.focus();
635
+ }
574
636
  }
575
637
 
576
638
  /** @private */
577
- __onTargetKeydown(event) {
578
- // Prevent restoring focus after target blur on Tab key
579
- if (event.key === 'Tab' && this.__shouldRestoreFocus) {
580
- this.__shouldRestoreFocus = false;
639
+ __onGlobalShiftTab(event) {
640
+ const overlayPart = this._overlayElement.$.overlay;
641
+
642
+ // Move focus back to the target on overlay content Shift + Tab
643
+ if (this.target && isElementFocused(overlayPart)) {
644
+ event.preventDefault();
645
+ this.target.focus();
646
+ return;
647
+ }
648
+
649
+ // Move focus back to the popover on next element Shift + Tab
650
+ const nextFocusable = this.__getNextBodyFocusable(this.target);
651
+ if (nextFocusable && isElementFocused(nextFocusable)) {
652
+ const lastFocusable = this.__getLastFocusable(overlayPart);
653
+ if (lastFocusable) {
654
+ event.preventDefault();
655
+ lastFocusable.focus();
656
+ }
581
657
  }
582
658
  }
583
659
 
660
+ /** @private */
661
+ __getNextBodyFocusable(target) {
662
+ const focusables = getFocusableElements(document.body);
663
+ const idx = focusables.findIndex((el) => el === target);
664
+ return focusables[idx + 1];
665
+ }
666
+
667
+ /** @private */
668
+ __getLastFocusable(container) {
669
+ const focusables = getFocusableElements(container);
670
+ return focusables.pop();
671
+ }
672
+
584
673
  /** @private */
585
674
  __onTargetFocusIn() {
586
675
  this.__focusInside = true;
@@ -702,6 +791,13 @@ class Popover extends PopoverPositionMixin(
702
791
  this.opened = event.detail.value;
703
792
  }
704
793
 
794
+ /** @private */
795
+ __onOverlayOpened() {
796
+ if (this.autofocus && !this.modal) {
797
+ this._overlayElement.$.overlay.focus();
798
+ }
799
+ }
800
+
705
801
  /** @private */
706
802
  __onOverlayClosed() {
707
803
  // Reset restoring focus state after a timeout to make sure focus was restored
@@ -1,3 +1,4 @@
1
1
  import '@vaadin/vaadin-lumo-styles/color.js';
2
+ import '@vaadin/vaadin-lumo-styles/spacing.js';
2
3
  import '@vaadin/vaadin-lumo-styles/style.js';
3
4
  import '@vaadin/vaadin-lumo-styles/typography.js';
@@ -1,4 +1,5 @@
1
1
  import '@vaadin/vaadin-lumo-styles/color.js';
2
+ import '@vaadin/vaadin-lumo-styles/spacing.js';
2
3
  import '@vaadin/vaadin-lumo-styles/style.js';
3
4
  import '@vaadin/vaadin-lumo-styles/typography.js';
4
5
  import { overlay } from '@vaadin/vaadin-lumo-styles/mixins/overlay.js';
@@ -6,15 +7,97 @@ import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themab
6
7
 
7
8
  const popoverOverlay = css`
8
9
  :host {
9
- --vaadin-popover-offset-top: var(--lumo-space-xs);
10
- --vaadin-popover-offset-bottom: var(--lumo-space-xs);
11
- --vaadin-popover-offset-start: var(--lumo-space-xs);
12
- --vaadin-popover-offset-end: var(--lumo-space-xs);
10
+ --vaadin-popover-offset-top: var(--_vaadin-popover-default-offset);
11
+ --vaadin-popover-offset-bottom: var(--_vaadin-popover-default-offset);
12
+ --vaadin-popover-offset-start: var(--_vaadin-popover-default-offset);
13
+ --vaadin-popover-offset-end: var(--_vaadin-popover-default-offset);
14
+ --vaadin-popover-arrow-size: 0.5rem;
15
+ --_vaadin-popover-default-offset: var(--lumo-space-xs);
13
16
  }
14
17
 
15
18
  [part='content'] {
16
19
  padding: var(--lumo-space-xs) var(--lumo-space-s);
17
20
  }
21
+
22
+ :host([theme~='arrow']) {
23
+ --_vaadin-popover-default-offset: calc(var(--lumo-space-s) + var(--vaadin-popover-arrow-size) / 2);
24
+ }
25
+
26
+ /* top / bottom position */
27
+ :host([theme~='arrow'][position^='top']) [part='arrow'],
28
+ :host([theme~='arrow'][position^='bottom']) [part='arrow'] {
29
+ border-left: var(--vaadin-popover-arrow-size) solid transparent;
30
+ border-right: var(--vaadin-popover-arrow-size) solid transparent;
31
+ }
32
+
33
+ :host([theme~='arrow'][position^='bottom'][bottom-aligned]) [part='arrow'],
34
+ :host([theme~='arrow'][position^='top'][bottom-aligned]) [part='arrow'] {
35
+ bottom: calc(var(--vaadin-popover-arrow-size) * -1);
36
+ border-top: var(--vaadin-popover-arrow-size) solid var(--lumo-base-color);
37
+ filter: drop-shadow(0 2px 1px var(--lumo-shade-10pct));
38
+ }
39
+
40
+ :host([theme~='arrow'][position^='bottom'][top-aligned]) [part='arrow'],
41
+ :host([theme~='arrow'][position^='top'][top-aligned]) [part='arrow'] {
42
+ top: calc(var(--vaadin-popover-arrow-size) * -1);
43
+ border-bottom: var(--vaadin-popover-arrow-size) solid var(--lumo-base-color);
44
+ filter: drop-shadow(0 -2px 1px var(--lumo-shade-10pct));
45
+ }
46
+
47
+ :host([theme~='arrow'][position^='bottom'][start-aligned]) [part='arrow'],
48
+ :host([theme~='arrow'][position^='top'][start-aligned]) [part='arrow'] {
49
+ transform: translateX(-50%);
50
+ inset-inline-start: 1.5rem;
51
+ }
52
+
53
+ :host([theme~='arrow'][position^='bottom'][end-aligned]) [part='arrow'],
54
+ :host([theme~='arrow'][position^='top'][end-aligned]) [part='arrow'] {
55
+ transform: translateX(50%);
56
+ inset-inline-end: 1.5rem;
57
+ }
58
+
59
+ :host([theme~='arrow'][position^='bottom'][arrow-centered]) [part='arrow'],
60
+ :host([theme~='arrow'][position^='top'][arrow-centered]) [part='arrow'] {
61
+ transform: translateX(-50%);
62
+ inset-inline-start: 50%;
63
+ }
64
+
65
+ /* start / end position */
66
+ :host([theme~='arrow'][position^='start']) [part='arrow'],
67
+ :host([theme~='arrow'][position^='end']) [part='arrow'] {
68
+ border-top: var(--vaadin-popover-arrow-size) solid transparent;
69
+ border-bottom: var(--vaadin-popover-arrow-size) solid transparent;
70
+ }
71
+
72
+ :host([theme~='arrow'][position^='start'][start-aligned]) [part='arrow'],
73
+ :host([theme~='arrow'][position^='end'][start-aligned]) [part='arrow'] {
74
+ inset-inline-start: calc(var(--vaadin-popover-arrow-size) * -1);
75
+ border-right: var(--vaadin-popover-arrow-size) solid var(--lumo-base-color);
76
+ filter: drop-shadow(-2px 0 1px var(--lumo-shade-10pct));
77
+ }
78
+
79
+ :host([theme~='arrow'][position^='start'][end-aligned]) [part='arrow'],
80
+ :host([theme~='arrow'][position^='end'][end-aligned]) [part='arrow'] {
81
+ inset-inline-end: calc(var(--vaadin-popover-arrow-size) * -1);
82
+ border-left: var(--vaadin-popover-arrow-size) solid var(--lumo-base-color);
83
+ filter: drop-shadow(2px 0 1px var(--lumo-shade-10pct));
84
+ }
85
+
86
+ :host([theme~='arrow'][position^='start'][top-aligned]) [part='arrow'],
87
+ :host([theme~='arrow'][position^='end'][top-aligned]) [part='arrow'] {
88
+ top: 0.5rem;
89
+ }
90
+
91
+ :host([theme~='arrow'][position='start'][top-aligned]) [part='arrow'],
92
+ :host([theme~='arrow'][position='end'][top-aligned]) [part='arrow'] {
93
+ top: 50%;
94
+ transform: translateY(-50%);
95
+ }
96
+
97
+ :host([theme~='arrow'][position^='start'][bottom-aligned]) [part='arrow'],
98
+ :host([theme~='arrow'][position^='end'][bottom-aligned]) [part='arrow'] {
99
+ bottom: 0.5rem;
100
+ }
18
101
  `;
19
102
 
20
103
  registerStyles('vaadin-popover-overlay', [overlay, popoverOverlay], { moduleId: 'lumo-popover-overlay' });
@@ -1 +1 @@
1
- export {};
1
+ import '@vaadin/vaadin-material-styles/color.js';
@@ -1,17 +1,100 @@
1
+ import '@vaadin/vaadin-material-styles/color.js';
1
2
  import { overlay } from '@vaadin/vaadin-material-styles/mixins/overlay.js';
2
3
  import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
3
4
 
4
5
  const popoverOverlay = css`
5
6
  :host {
6
- --vaadin-popover-offset-top: 0.25rem;
7
- --vaadin-popover-offset-bottom: 0.25rem;
8
- --vaadin-popover-offset-start: 0.25rem;
9
- --vaadin-popover-offset-end: 0.25rem;
7
+ --vaadin-popover-offset-top: var(--_vaadin-popover-default-offset);
8
+ --vaadin-popover-offset-bottom: var(--_vaadin-popover-default-offset);
9
+ --vaadin-popover-offset-start: var(--_vaadin-popover-default-offset);
10
+ --vaadin-popover-offset-end: var(--_vaadin-popover-default-offset);
11
+ --vaadin-popover-arrow-size: 0.5rem;
12
+ --_vaadin-popover-default-offset: 0.25rem;
10
13
  }
11
14
 
12
15
  [part='content'] {
13
16
  padding: 0.25rem 0.5rem;
14
17
  }
18
+
19
+ :host([theme~='arrow']) {
20
+ --_vaadin-popover-default-offset: calc(0.25rem + var(--vaadin-popover-arrow-size) / 1.25);
21
+ }
22
+
23
+ /* top / bottom position */
24
+ :host([theme~='arrow'][position^='top']) [part='arrow'],
25
+ :host([theme~='arrow'][position^='bottom']) [part='arrow'] {
26
+ border-left: var(--vaadin-popover-arrow-size) solid transparent;
27
+ border-right: var(--vaadin-popover-arrow-size) solid transparent;
28
+ }
29
+
30
+ :host([theme~='arrow'][position^='bottom'][bottom-aligned]) [part='arrow'],
31
+ :host([theme~='arrow'][position^='top'][bottom-aligned]) [part='arrow'] {
32
+ bottom: calc(var(--vaadin-popover-arrow-size) * -1);
33
+ border-top: var(--vaadin-popover-arrow-size) solid var(--material-background-color);
34
+ filter: drop-shadow(0 2px 1px rgba(0, 0, 0, 0.14));
35
+ }
36
+
37
+ :host([theme~='arrow'][position^='bottom'][top-aligned]) [part='arrow'],
38
+ :host([theme~='arrow'][position^='top'][top-aligned]) [part='arrow'] {
39
+ top: calc(var(--vaadin-popover-arrow-size) * -1);
40
+ border-bottom: var(--vaadin-popover-arrow-size) solid var(--material-background-color);
41
+ filter: drop-shadow(0 -2px 1px rgba(0, 0, 0, 0.14));
42
+ }
43
+
44
+ :host([theme~='arrow'][position^='bottom'][start-aligned]) [part='arrow'],
45
+ :host([theme~='arrow'][position^='top'][start-aligned]) [part='arrow'] {
46
+ transform: translateX(-50%);
47
+ inset-inline-start: 1.5rem;
48
+ }
49
+
50
+ :host([theme~='arrow'][position^='bottom'][end-aligned]) [part='arrow'],
51
+ :host([theme~='arrow'][position^='top'][end-aligned]) [part='arrow'] {
52
+ transform: translateX(50%);
53
+ inset-inline-end: 1.5rem;
54
+ }
55
+
56
+ :host([theme~='arrow'][position^='bottom'][arrow-centered]) [part='arrow'],
57
+ :host([theme~='arrow'][position^='top'][arrow-centered]) [part='arrow'] {
58
+ transform: translateX(-50%);
59
+ inset-inline-start: 50%;
60
+ }
61
+
62
+ /* start / end position */
63
+ :host([theme~='arrow'][position^='start']) [part='arrow'],
64
+ :host([theme~='arrow'][position^='end']) [part='arrow'] {
65
+ border-top: var(--vaadin-popover-arrow-size) solid transparent;
66
+ border-bottom: var(--vaadin-popover-arrow-size) solid transparent;
67
+ }
68
+
69
+ :host([theme~='arrow'][position^='start'][start-aligned]) [part='arrow'],
70
+ :host([theme~='arrow'][position^='end'][start-aligned]) [part='arrow'] {
71
+ inset-inline-start: calc(var(--vaadin-popover-arrow-size) * -1);
72
+ border-right: var(--vaadin-popover-arrow-size) solid var(--material-background-color);
73
+ filter: drop-shadow(-2px 0 1px rgba(0, 0, 0, 0.14));
74
+ }
75
+
76
+ :host([theme~='arrow'][position^='start'][end-aligned]) [part='arrow'],
77
+ :host([theme~='arrow'][position^='end'][end-aligned]) [part='arrow'] {
78
+ inset-inline-end: calc(var(--vaadin-popover-arrow-size) * -1);
79
+ border-left: var(--vaadin-popover-arrow-size) solid var(--material-background-color);
80
+ filter: drop-shadow(2px 0 1px rgba(0, 0, 0, 0.14));
81
+ }
82
+
83
+ :host([theme~='arrow'][position^='start'][top-aligned]) [part='arrow'],
84
+ :host([theme~='arrow'][position^='end'][top-aligned]) [part='arrow'] {
85
+ top: 0.5rem;
86
+ }
87
+
88
+ :host([theme~='arrow'][position='start'][top-aligned]) [part='arrow'],
89
+ :host([theme~='arrow'][position='end'][top-aligned]) [part='arrow'] {
90
+ top: 50%;
91
+ transform: translateY(-50%);
92
+ }
93
+
94
+ :host([theme~='arrow'][position^='start'][bottom-aligned]) [part='arrow'],
95
+ :host([theme~='arrow'][position^='end'][bottom-aligned]) [part='arrow'] {
96
+ bottom: 0.5rem;
97
+ }
15
98
  `;
16
99
 
17
100
  registerStyles('vaadin-popover-overlay', [overlay, popoverOverlay], { moduleId: 'material-popover-overlay' });
package/web-types.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/popover",
4
- "version": "24.5.0-alpha5",
4
+ "version": "24.5.0-alpha7",
5
5
  "description-markup": "markdown",
6
6
  "contributions": {
7
7
  "html": {
8
8
  "elements": [
9
9
  {
10
10
  "name": "vaadin-popover",
11
- "description": "`<vaadin-popover>` is a Web Component for creating overlays\nthat are positioned next to specified DOM element (target).\n\nUnlike `<vaadin-tooltip>`, the popover supports rich content\nthat can be provided by using `renderer` function.\n\n### Styling\n\n`<vaadin-popover>` uses `<vaadin-popover-overlay>` internal\nthemable component as the actual visible overlay.\n\nSee [`<vaadin-overlay>`](https://cdn.vaadin.com/vaadin-web-components/24.5.0-alpha5/#/elements/vaadin-overlay) documentation\nfor `<vaadin-popover-overlay>` parts.\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n-----------------|----------------------------------------\n`position` | Reflects the `position` property value.\n\nNote: the `theme` attribute value set on `<vaadin-popover>` is\npropagated to the internal `<vaadin-popover-overlay>` component.\n\n### Custom CSS Properties\n\nThe following custom CSS properties are available on the `<vaadin-popover>` element:\n\nCustom CSS property | Description\n---------------------------------|-------------\n`--vaadin-popover-offset-top` | Used as an offset when the popover is aligned vertically below the target\n`--vaadin-popover-offset-bottom` | Used as an offset when the popover is aligned vertically above the target\n`--vaadin-popover-offset-start` | Used as an offset when the popover is aligned horizontally after the target\n`--vaadin-popover-offset-end` | Used as an offset when the popover is aligned horizontally before the target\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.",
11
+ "description": "`<vaadin-popover>` is a Web Component for creating overlays\nthat are positioned next to specified DOM element (target).\n\nUnlike `<vaadin-tooltip>`, the popover supports rich content\nthat can be provided by using `renderer` function.\n\n### Styling\n\n`<vaadin-popover>` uses `<vaadin-popover-overlay>` internal\nthemable component as the actual visible overlay.\n\nSee [`<vaadin-overlay>`](https://cdn.vaadin.com/vaadin-web-components/24.5.0-alpha7/#/elements/vaadin-overlay) documentation\nfor `<vaadin-popover-overlay>` parts.\n\nIn addition to `<vaadin-overlay>` parts, the following parts are available for styling:\n\nPart name | Description\n-----------------|-------------------------------------------\n`arrow` | Optional arrow pointing to the target when using `theme=\"arrow\"`\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n-----------------|----------------------------------------\n`position` | Reflects the `position` property value.\n\nNote: the `theme` attribute value set on `<vaadin-popover>` is\npropagated to the internal `<vaadin-popover-overlay>` component.\n\n### Custom CSS Properties\n\nThe following custom CSS properties are available on the `<vaadin-popover>` element:\n\nCustom CSS property | Description\n---------------------------------|-------------\n`--vaadin-popover-offset-top` | Used as an offset when the popover is aligned vertically below the target\n`--vaadin-popover-offset-bottom` | Used as an offset when the popover is aligned vertically above the target\n`--vaadin-popover-offset-start` | Used as an offset when the popover is aligned horizontally after the target\n`--vaadin-popover-offset-end` | Used as an offset when the popover is aligned horizontally before the target\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.",
12
12
  "attributes": [
13
13
  {
14
14
  "name": "position",
@@ -54,6 +54,17 @@
54
54
  ]
55
55
  }
56
56
  },
57
+ {
58
+ "name": "autofocus",
59
+ "description": "When true, the popover content automatically receives focus after\nit is opened. Modal popovers use this behavior by default.",
60
+ "value": {
61
+ "type": [
62
+ "boolean",
63
+ "null",
64
+ "undefined"
65
+ ]
66
+ }
67
+ },
57
68
  {
58
69
  "name": "content-height",
59
70
  "description": "Height to be set on the overlay content.",
@@ -244,6 +255,17 @@
244
255
  ]
245
256
  }
246
257
  },
258
+ {
259
+ "name": "autofocus",
260
+ "description": "When true, the popover content automatically receives focus after\nit is opened. Modal popovers use this behavior by default.",
261
+ "value": {
262
+ "type": [
263
+ "boolean",
264
+ "null",
265
+ "undefined"
266
+ ]
267
+ }
268
+ },
247
269
  {
248
270
  "name": "contentHeight",
249
271
  "description": "Height to be set on the overlay content.",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/popover",
4
- "version": "24.5.0-alpha5",
4
+ "version": "24.5.0-alpha7",
5
5
  "description-markup": "markdown",
6
6
  "framework": "lit",
7
7
  "framework-config": {
@@ -16,9 +16,16 @@
16
16
  "elements": [
17
17
  {
18
18
  "name": "vaadin-popover",
19
- "description": "`<vaadin-popover>` is a Web Component for creating overlays\nthat are positioned next to specified DOM element (target).\n\nUnlike `<vaadin-tooltip>`, the popover supports rich content\nthat can be provided by using `renderer` function.\n\n### Styling\n\n`<vaadin-popover>` uses `<vaadin-popover-overlay>` internal\nthemable component as the actual visible overlay.\n\nSee [`<vaadin-overlay>`](https://cdn.vaadin.com/vaadin-web-components/24.5.0-alpha5/#/elements/vaadin-overlay) documentation\nfor `<vaadin-popover-overlay>` parts.\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n-----------------|----------------------------------------\n`position` | Reflects the `position` property value.\n\nNote: the `theme` attribute value set on `<vaadin-popover>` is\npropagated to the internal `<vaadin-popover-overlay>` component.\n\n### Custom CSS Properties\n\nThe following custom CSS properties are available on the `<vaadin-popover>` element:\n\nCustom CSS property | Description\n---------------------------------|-------------\n`--vaadin-popover-offset-top` | Used as an offset when the popover is aligned vertically below the target\n`--vaadin-popover-offset-bottom` | Used as an offset when the popover is aligned vertically above the target\n`--vaadin-popover-offset-start` | Used as an offset when the popover is aligned horizontally after the target\n`--vaadin-popover-offset-end` | Used as an offset when the popover is aligned horizontally before the target\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.",
19
+ "description": "`<vaadin-popover>` is a Web Component for creating overlays\nthat are positioned next to specified DOM element (target).\n\nUnlike `<vaadin-tooltip>`, the popover supports rich content\nthat can be provided by using `renderer` function.\n\n### Styling\n\n`<vaadin-popover>` uses `<vaadin-popover-overlay>` internal\nthemable component as the actual visible overlay.\n\nSee [`<vaadin-overlay>`](https://cdn.vaadin.com/vaadin-web-components/24.5.0-alpha7/#/elements/vaadin-overlay) documentation\nfor `<vaadin-popover-overlay>` parts.\n\nIn addition to `<vaadin-overlay>` parts, the following parts are available for styling:\n\nPart name | Description\n-----------------|-------------------------------------------\n`arrow` | Optional arrow pointing to the target when using `theme=\"arrow\"`\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n-----------------|----------------------------------------\n`position` | Reflects the `position` property value.\n\nNote: the `theme` attribute value set on `<vaadin-popover>` is\npropagated to the internal `<vaadin-popover-overlay>` component.\n\n### Custom CSS Properties\n\nThe following custom CSS properties are available on the `<vaadin-popover>` element:\n\nCustom CSS property | Description\n---------------------------------|-------------\n`--vaadin-popover-offset-top` | Used as an offset when the popover is aligned vertically below the target\n`--vaadin-popover-offset-bottom` | Used as an offset when the popover is aligned vertically above the target\n`--vaadin-popover-offset-start` | Used as an offset when the popover is aligned horizontally after the target\n`--vaadin-popover-offset-end` | Used as an offset when the popover is aligned horizontally before the target\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.",
20
20
  "extension": true,
21
21
  "attributes": [
22
+ {
23
+ "name": "?autofocus",
24
+ "description": "When true, the popover content automatically receives focus after\nit is opened. Modal popovers use this behavior by default.",
25
+ "value": {
26
+ "kind": "expression"
27
+ }
28
+ },
22
29
  {
23
30
  "name": "?opened",
24
31
  "description": "True if the popover overlay is opened, false otherwise.",