@vollowx/seele 0.13.7 → 0.13.9

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.
Files changed (94) hide show
  1. package/README.md +33 -15
  2. package/package.json +2 -2
  3. package/src/all.d.ts +5 -0
  4. package/src/all.js +5 -0
  5. package/src/base/autocomplete.js +13 -13
  6. package/src/base/button.d.ts +4 -2
  7. package/src/base/button.js +15 -11
  8. package/src/base/checkbox.js +3 -8
  9. package/src/base/controllers/popover-controller.js +1 -1
  10. package/src/base/experimental/popup-styles.css.js +2 -0
  11. package/src/base/experimental/popup.d.ts +40 -0
  12. package/src/base/experimental/popup.js +278 -0
  13. package/src/base/mixins/attachable.d.ts +5 -2
  14. package/src/base/mixins/attachable.js +63 -23
  15. package/src/base/mixins/button-toggle-mixin.d.ts +7 -0
  16. package/src/base/mixins/button-toggle-mixin.js +101 -0
  17. package/src/base/mixins/internals-attached.d.ts +4 -0
  18. package/src/base/mixins/internals-attached.js +6 -0
  19. package/src/base/radio.d.ts +3 -0
  20. package/src/base/radio.js +28 -16
  21. package/src/base/tab-panel.d.ts +3 -3
  22. package/src/base/tab-panel.js +9 -11
  23. package/src/base/tab.d.ts +2 -8
  24. package/src/base/tab.js +16 -35
  25. package/src/base/tabs.d.ts +6 -6
  26. package/src/base/tabs.js +101 -99
  27. package/src/base/toggle-button.d.ts +6 -45
  28. package/src/base/toggle-button.js +6 -135
  29. package/src/base/tooltip.d.ts +2 -1
  30. package/src/base/tooltip.js +2 -2
  31. package/src/core/ensure-ready.d.ts +2 -1
  32. package/src/core/ensure-ready.js +12 -7
  33. package/src/m3/button/common-button-toggle.d.ts +4 -10
  34. package/src/m3/button/common-button-toggle.js +6 -40
  35. package/src/m3/button/icon-button-toggle.d.ts +4 -6
  36. package/src/m3/button/icon-button-toggle.js +6 -33
  37. package/src/m3/button/shared-button-styles.css.js +1 -1
  38. package/src/m3/button/shared-button-toggle-styles.css.js +1 -1
  39. package/src/m3/button-group/standard-button-group-styles.css.js +1 -1
  40. package/src/m3/button-group/standard-button-group.d.ts +2 -0
  41. package/src/m3/button-group/standard-button-group.js +44 -38
  42. package/src/m3/checkbox/checkbox-styles.css.js +1 -1
  43. package/src/m3/checkbox/checkbox.d.ts +1 -1
  44. package/src/m3/checkbox/checkbox.js +2 -5
  45. package/src/m3/dialog/dialog-styles.css.js +1 -1
  46. package/src/m3/dialog/dialog.d.ts +3 -1
  47. package/src/m3/dialog/dialog.js +6 -4
  48. package/src/m3/experimental/popup-styles.css.js +2 -0
  49. package/src/m3/experimental/popup.d.ts +9 -0
  50. package/src/m3/experimental/popup.js +11 -0
  51. package/src/m3/fab/fab-styles.css.js +1 -1
  52. package/src/m3/field/field-styles.css.js +1 -1
  53. package/src/m3/field/filled-field-styles.css.js +1 -1
  54. package/src/m3/focus-ring/focus-ring-styles.css.js +1 -1
  55. package/src/m3/focus-ring/focus-ring.d.ts +2 -1
  56. package/src/m3/focus-ring/focus-ring.js +4 -7
  57. package/src/m3/list/list-item-styles.css.js +1 -1
  58. package/src/m3/menu/menu-styles.css.js +1 -1
  59. package/src/m3/nagivation/navigation-rail-item-styles.css.js +2 -0
  60. package/src/m3/nagivation/navigation-rail-item.d.ts +21 -0
  61. package/src/m3/nagivation/navigation-rail-item.js +44 -0
  62. package/src/m3/nagivation/navigation-rail-styles.css.js +2 -0
  63. package/src/m3/nagivation/navigation-rail.d.ts +12 -0
  64. package/src/m3/nagivation/navigation-rail.js +17 -0
  65. package/src/m3/radio/radio-styles.css.js +1 -1
  66. package/src/m3/radio/radio.d.ts +3 -0
  67. package/src/m3/radio/radio.js +12 -4
  68. package/src/m3/ripple/ripple-styles.css.js +1 -1
  69. package/src/m3/ripple/ripple.d.ts +3 -13
  70. package/src/m3/ripple/ripple.js +28 -31
  71. package/src/m3/{system → styles}/defaults.css +1 -1
  72. package/src/m3/styles/motion-expressive.css +15 -0
  73. package/src/m3/styles/motion-standard.css +15 -0
  74. package/src/m3/styles/typography.css +24 -0
  75. package/src/m3/switch/switch-styles.css.js +1 -1
  76. package/src/m3/switch/switch.d.ts +4 -2
  77. package/src/m3/switch/switch.js +14 -12
  78. package/src/m3/tab/secondary-tab-styles.css.js +2 -0
  79. package/src/m3/tab/secondary-tab.d.ts +5 -0
  80. package/src/m3/tab/secondary-tab.js +15 -0
  81. package/src/m3/tab/tab-panel-styles.css.js +1 -1
  82. package/src/m3/tab/tab-styles.css.js +1 -1
  83. package/src/m3/tab/tab.d.ts +3 -0
  84. package/src/m3/tab/tab.js +68 -3
  85. package/src/m3/tab/tabs-styles.css.js +1 -1
  86. package/src/m3/tab/tabs.d.ts +1 -7
  87. package/src/m3/tab/tabs.js +6 -39
  88. package/src/m3/toolbar/toolbar-styles.css.js +1 -1
  89. package/src/win98/button/button-styles.css.js +1 -1
  90. package/src/m3/system/typography.css +0 -34
  91. /package/src/m3/{system → styles}/base.css +0 -0
  92. /package/src/m3/{system → styles}/elevation.css +0 -0
  93. /package/src/m3/{system → styles}/motion.d.ts +0 -0
  94. /package/src/m3/{system → styles}/motion.js +0 -0
package/README.md CHANGED
@@ -33,12 +33,12 @@ What's the differences comparing to other projects?
33
33
 
34
34
  ## Getting started
35
35
 
36
- Check out [Overview](./docs/00-Overview.md).
36
+ Check out [Installation](https://tideover.cc/seele/docs/#installation).
37
37
 
38
38
  ## Documentations
39
39
 
40
- - Previous [documentations and demos](https://tideover.cc/seele/docs/), being replaced
41
- - [Documentations](./docs/00-Overview.md)
40
+ - [Documentations](https://tideover.cc/seele/docs/), ([source](./docs/00-Overview.md))
41
+ - [Backup site](https://lucaz.srht.site/)
42
42
 
43
43
  ## Download
44
44
 
@@ -50,25 +50,34 @@ Check out [Overview](./docs/00-Overview.md).
50
50
 
51
51
  seele relies on the following modern web standards:
52
52
 
53
- - [`ElementInternals`](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals), Baseline 2023
54
- - [`:dir()`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:dir), Baseline 2023, C 120, F 49
55
- - [Constructable Stylesheets](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/CSSStyleSheet), Baseline 2024, C[^1] 90, F 126
56
- - [`:state()`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:state), Baseline 2024, C 125, F 126
57
- - [`ariaActiveDescendantElement`](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/ariaActiveDescendantElement) and other similar ones, Baseline 2025, C 135, F 136
53
+ | feature | baseline | Chromium | Firefox | Safari |
54
+ | --- | :---: | :---: | :---: | :---: |
55
+ | [`ElementInternals`][element-internals] | 2023 | - | - | - |
56
+ | [`:dir()`][:dir] | 2023 | 120 | - | - |
57
+ | [Constructable Stylesheets][constructable-stylesheets] | 2024 | - | 126 | - |
58
+ | [`:state()`][:state] | 2024 | 125 | 126 | - |
59
+ | [`aria*Element`][aria-active] and similar | 2025 | 135 | 136 | 16.4 |
60
+ | [`CSSNumericValue`][css-numeric-value] | not yet | - | Nightly | - |
58
61
 
59
- And all that result in:
62
+ _* `CSSNumericValue` is worked around currently so it does not raise the minimum
63
+ version of Firefox_
64
+
65
+ All of the above result in the following minimum requirements:
60
66
 
61
67
  - Chromium: >= 135
62
68
  - Firefox: >= 136
69
+ - Safari: >= 16.4
63
70
 
64
- It is 2026 now, you don't really need to worry about this. But in the future,
65
- these following web features might be used and require higher browser versions:
71
+ It is 2026 now, so you don't really need to worry about this. However, in the
72
+ future, the following web features might be used and require higher browser
73
+ versions:
66
74
 
67
- - [anchor()](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/anchor), Baseline 2026, C 125, F 147, will remove the dependency `floating-dom`
68
- - [`::view-*`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/::view-transition), Baseline 2025, C 111, F 144, will optimize some animations for menu, dialog, etc
69
- - [`CSSNumericValue`](https://developer.mozilla.org/en-US/docs/Web/API/CSSNumericValue), not Baseline, C 66, F Nightly, worked around currently
75
+ - [Popover API][popover-api] — Baseline 2025, Safari 17, _will improve menu, popup, and combobox_
76
+ - [`@starting-style`][starting-style] — Baseline 2025, Safari 17.5
77
+ - [`anchor()`][anchor] — Baseline 2026, Firefox 147, Safari 26, _will remove the dependency `floating-dom`_
70
78
 
71
- [^1]: C = Chromium, F = Firefox.
79
+ _* omitting a specific browser version means using that standard does not raise
80
+ the minimum required version of seele_
72
81
 
73
82
  ## Other Information
74
83
 
@@ -79,3 +88,12 @@ these following web features might be used and require higher browser versions:
79
88
  [web-comps]: https://developer.mozilla.org/en-US/docs/Web/API/Web_components
80
89
  [apg-patterns]: https://www.w3.org/WAI/ARIA/apg/patterns/
81
90
  [lit]: https://lit.dev/
91
+ [element-internals]: https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals
92
+ [:dir]: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:dir
93
+ [constructable-stylesheets]: https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/CSSStyleSheet
94
+ [:state]: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:state
95
+ [aria-active]: https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/ariaActiveDescendantElement
96
+ [css-numeric-value]: https://developer.mozilla.org/en-US/docs/Web/API/CSSNumericValue
97
+ [popover-api]: https://developer.mozilla.org/en-US/docs/Web/API/Popover_API
98
+ [starting-style]: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@starting-style
99
+ [anchor]: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/anchor
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vollowx/seele",
3
- "version": "0.13.7",
3
+ "version": "0.13.9",
4
4
  "description": "A extensible Web Components library, with Material Design 3 components pre-provided.",
5
5
  "author": "vollowx",
6
6
  "license": "Apache-2.0",
@@ -20,7 +20,7 @@
20
20
  "files": [
21
21
  "src/**/*.js",
22
22
  "src/**/*.d.ts",
23
- "src/m3/system/*.css",
23
+ "src/m3/styles/*.css",
24
24
  "custom-elements.json",
25
25
  "LICENSE",
26
26
  "README.md"
package/src/all.d.ts CHANGED
@@ -17,6 +17,8 @@ import './m3/list/list-item.js';
17
17
  import './m3/loading-indicator/loading-indicator.js';
18
18
  import './m3/menu/menu.js';
19
19
  import './m3/menu/menu-item.js';
20
+ import './m3/nagivation/navigation-rail.js';
21
+ import './m3/nagivation/navigation-rail-item.js';
20
22
  import './m3/radio/radio.js';
21
23
  import './m3/ripple/ripple.js';
22
24
  import './m3/select/filled-select.js';
@@ -26,9 +28,12 @@ import './m3/slider/slider.js';
26
28
  import './m3/switch/switch.js';
27
29
  import './m3/tab/tabs.js';
28
30
  import './m3/tab/tab.js';
31
+ import './m3/tab/secondary-tab.js';
29
32
  import './m3/tab/tab-panel.js';
30
33
  import './m3/text-field/filled-text-field.js';
31
34
  import './m3/text-field/outlined-text-field.js';
32
35
  import './m3/toolbar/toolbar.js';
33
36
  import './m3/tooltip/tooltip.js';
34
37
  import './win98/button/button.js';
38
+ import './base/experimental/popup.js';
39
+ import './m3/experimental/popup.js';
package/src/all.js CHANGED
@@ -17,6 +17,8 @@ import './m3/list/list-item.js';
17
17
  import './m3/loading-indicator/loading-indicator.js';
18
18
  import './m3/menu/menu.js';
19
19
  import './m3/menu/menu-item.js';
20
+ import './m3/nagivation/navigation-rail.js';
21
+ import './m3/nagivation/navigation-rail-item.js';
20
22
  import './m3/radio/radio.js';
21
23
  import './m3/ripple/ripple.js';
22
24
  import './m3/select/filled-select.js';
@@ -26,9 +28,12 @@ import './m3/slider/slider.js';
26
28
  import './m3/switch/switch.js';
27
29
  import './m3/tab/tabs.js';
28
30
  import './m3/tab/tab.js';
31
+ import './m3/tab/secondary-tab.js';
29
32
  import './m3/tab/tab-panel.js';
30
33
  import './m3/text-field/filled-text-field.js';
31
34
  import './m3/text-field/outlined-text-field.js';
32
35
  import './m3/toolbar/toolbar.js';
33
36
  import './m3/tooltip/tooltip.js';
34
37
  import './win98/button/button.js';
38
+ import './base/experimental/popup.js';
39
+ import './m3/experimental/popup.js';
@@ -56,19 +56,19 @@ export class Autocomplete extends Base {
56
56
  }
57
57
  // TODO: handle multiple calls on this function, currently double-call
58
58
  // disallowed
59
- handleInputSlotChange() {
60
- ensureReady(this.$input).then((input) => {
61
- const $realInput = input.$inputOrTextarea;
62
- $realInput.role = 'combobox';
63
- $realInput.ariaExpanded = String(this.open);
64
- $realInput.ariaHasPopup = 'listbox';
65
- $realInput.ariaAutoComplete = this.mode;
66
- $realInput.ariaControlsElements = [this.$menu];
67
- input.addEventListener('input', this.handleInput.bind(this));
68
- input.addEventListener('keydown', this.handleInputKeydown.bind(this));
69
- input.addEventListener('click', () => (this.open = !this.open));
70
- this.$menu.attach($realInput);
71
- });
59
+ async handleInputSlotChange() {
60
+ await ensureReady(this.$input);
61
+ const input = this.$input;
62
+ const $realInput = input.$inputOrTextarea;
63
+ $realInput.role = 'combobox';
64
+ $realInput.ariaExpanded = String(this.open);
65
+ $realInput.ariaHasPopup = 'listbox';
66
+ $realInput.ariaAutoComplete = this.mode;
67
+ $realInput.ariaControlsElements = [this.$menu];
68
+ input.addEventListener('input', this.handleInput.bind(this));
69
+ input.addEventListener('keydown', this.handleInputKeydown.bind(this));
70
+ input.addEventListener('click', () => (this.open = !this.open));
71
+ this.$menu.attach($realInput);
72
72
  }
73
73
  handleItemsSlotChange() {
74
74
  // Initial filter based on current input value (if any)
@@ -1,4 +1,5 @@
1
1
  import { LitElement } from 'lit';
2
+ import { updateInternals } from './mixins/internals-attached.js';
2
3
  declare const Base: Constructor<import("./mixins/form-associated.js").FormAssociatedInterface> & Constructor<import("./mixins/internals-attached.js").InternalsAttachedInterface> & typeof LitElement;
3
4
  export declare class Button extends Base {
4
5
  #private;
@@ -7,7 +8,8 @@ export declare class Button extends Base {
7
8
  constructor();
8
9
  connectedCallback(): void;
9
10
  disconnectedCallback(): void;
10
- protected updated(changed: Map<string, any>): void;
11
- protected updateState(): void;
11
+ updated(changed: Map<string, any>): void;
12
+ [updateInternals](): void;
13
+ _handleClick(e: Event): void;
12
14
  }
13
15
  export {};
@@ -1,7 +1,7 @@
1
1
  import { __decorate } from "tslib";
2
2
  import { LitElement } from 'lit';
3
3
  import { property } from 'lit/decorators.js';
4
- import { InternalsAttached, internals } from './mixins/internals-attached.js';
4
+ import { InternalsAttached, internals, updateInternals, } from './mixins/internals-attached.js';
5
5
  import { FormAssociated } from './mixins/form-associated.js';
6
6
  import { hiddenStyles } from './hidden-styles.css.js';
7
7
  const Base = FormAssociated(InternalsAttached(LitElement));
@@ -17,20 +17,21 @@ export class Button extends Base {
17
17
  e.stopPropagation();
18
18
  if (e.key === 'Enter')
19
19
  this.click();
20
+ else
21
+ this[internals].states.add('active');
22
+ // TODO: consider handle `pointerdown` and ditch native `:active` selector
23
+ // TODO: supporting `Enter` also requires listening on `focusout`
20
24
  };
21
25
  this.#handleKeyUp = (e) => {
22
26
  if (e.key === ' ') {
27
+ this[internals].states.delete('active');
23
28
  e.preventDefault();
24
29
  e.stopPropagation();
25
30
  this.click();
26
31
  }
27
32
  };
28
- this.#handleClick = () => {
29
- if (this.type !== 'button')
30
- this[internals].form?.[this.type]();
31
- };
32
33
  this[internals].role = 'button';
33
- this.updateState();
34
+ this[updateInternals]();
34
35
  // This is set to prevent a first-paint-time transition for styles like
35
36
  // `border-radius` on `:host`. Also in `ToggleButton`.
36
37
  // Double `requestAnimationFrame` is necessary since the callback inside is
@@ -41,26 +42,29 @@ export class Button extends Base {
41
42
  super.connectedCallback();
42
43
  this.addEventListener('keydown', this.#handleKeyDown);
43
44
  this.addEventListener('keyup', this.#handleKeyUp);
44
- this.addEventListener('click', this.#handleClick);
45
+ this.addEventListener('click', this._handleClick.bind(this));
45
46
  requestAnimationFrame(() => requestAnimationFrame(() => this.removeAttribute('notransition')));
46
47
  }
47
48
  disconnectedCallback() {
48
49
  super.disconnectedCallback();
49
50
  this.removeEventListener('keydown', this.#handleKeyDown);
50
51
  this.removeEventListener('keyup', this.#handleKeyUp);
51
- this.removeEventListener('click', this.#handleClick);
52
+ this.removeEventListener('click', this._handleClick.bind(this));
52
53
  }
53
54
  updated(changed) {
54
55
  if (changed.has('disabled'))
55
- this.updateState();
56
+ this[updateInternals]();
56
57
  }
57
- updateState() {
58
+ [updateInternals]() {
58
59
  this.tabIndex = this.disabled ? -1 : 0;
59
60
  this[internals].ariaDisabled = String(this.disabled);
60
61
  }
61
62
  #handleKeyDown;
62
63
  #handleKeyUp;
63
- #handleClick;
64
+ _handleClick(e) {
65
+ if (this.type !== 'button')
66
+ this[internals].form?.[this.type]();
67
+ }
64
68
  }
65
69
  __decorate([
66
70
  property({ reflect: true })
@@ -2,7 +2,7 @@ import { __decorate } from "tslib";
2
2
  import { LitElement } from 'lit';
3
3
  import { property } from 'lit/decorators.js';
4
4
  import { FormAssociated } from './mixins/form-associated.js';
5
- import { InternalsAttached, internals } from './mixins/internals-attached.js';
5
+ import { InternalsAttached, internals, replaceStates, } from './mixins/internals-attached.js';
6
6
  import { hiddenStyles } from './hidden-styles.css.js';
7
7
  const PROPERTY_FROM_ARIA_CHECKED = {
8
8
  true: 'checked',
@@ -63,9 +63,7 @@ export class Checkbox extends Base {
63
63
  updateState() {
64
64
  const prevAriaChecked = this[internals]
65
65
  .ariaChecked;
66
- this[internals].states.delete('was-unchecked');
67
- this[internals].states.delete('was-checked');
68
- this[internals].states.delete('was-indeterminate');
66
+ this[replaceStates](['was-unchecked', 'was-checked', 'was-indeterminate'], []);
69
67
  if (prevAriaChecked && PROPERTY_FROM_ARIA_CHECKED[prevAriaChecked]) {
70
68
  this[internals].states.add(`was-${PROPERTY_FROM_ARIA_CHECKED[prevAriaChecked]}`);
71
69
  }
@@ -76,10 +74,7 @@ export class Checkbox extends Base {
76
74
  : 'false';
77
75
  const currentAriaChecked = this[internals]
78
76
  .ariaChecked;
79
- this[internals].states.delete('unchecked');
80
- this[internals].states.delete('checked');
81
- this[internals].states.delete('indeterminate');
82
- this[internals].states.add(`${PROPERTY_FROM_ARIA_CHECKED[currentAriaChecked]}`);
77
+ this[replaceStates](['unchecked', 'checked', 'indeterminate'], [PROPERTY_FROM_ARIA_CHECKED[currentAriaChecked]]);
83
78
  this.tabIndex = this.disabled ? -1 : 0;
84
79
  this[internals].ariaDisabled = String(this.disabled);
85
80
  this[internals].setFormValue(this.checked ? 'on' : null);
@@ -134,7 +134,7 @@ export class PopoverController {
134
134
  this.cleanupAutoUpdate?.();
135
135
  this.cleanupAutoUpdate = undefined;
136
136
  }
137
- reposition() {
137
+ async reposition() {
138
138
  const trigger = this.config.trigger();
139
139
  const popover = this.config.popover();
140
140
  if (!trigger || !popover)
@@ -0,0 +1,2 @@
1
+ import { css } from 'lit';
2
+ export const popupStyles = css`:host([notransition]){transition:none!important}:host{box-sizing:border-box;border:none;margin:0;padding:0;position:absolute;inset:auto;overflow:auto}`;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * NOTE: demos at dev/pending.html
3
+ * NOTE: should apply writing style on event listeners to other files
4
+ * TODO: consider removing popoverController, but in case of tooltip, reserve
5
+ * for now
6
+ * TODO: hijack all `hidePopover()`s to allow more complex JS animations
7
+ * TODO: simpler Menu that just inherits List - do we really need a separated
8
+ * Menu? Why not just `<md-list role="presentation">`?
9
+ *
10
+ * Design: TO BE ADDED
11
+ */
12
+ import { LitElement, PropertyValues } from 'lit';
13
+ import { Placement, Strategy } from '@floating-ui/dom';
14
+ import { autoAttachToParent, handleControlChange } from '../mixins/attachable.js';
15
+ export declare function lookForTabbable(root: Node): HTMLElement | null;
16
+ declare const Popup_base: Constructor<import("../mixins/attachable.js").AttachableInterface> & Constructor<import("../mixins/internals-attached.js").InternalsAttachedInterface> & typeof LitElement;
17
+ export declare class Popup extends Popup_base {
18
+ #private;
19
+ open: boolean;
20
+ noFocusControl: boolean;
21
+ placement: Placement;
22
+ strategy: Strategy;
23
+ offset: number;
24
+ windowPadding: number;
25
+ static styles: any[];
26
+ render(): import("lit-html").TemplateResult<1>;
27
+ constructor();
28
+ connectedCallback(): void;
29
+ disconnectedCallback(): void;
30
+ protected updated(changedProperties: PropertyValues<this>): void;
31
+ [autoAttachToParent]: boolean;
32
+ [handleControlChange](prev: HTMLElement | null, next: HTMLElement | null): void;
33
+ toggle(): Promise<void>;
34
+ show(): Promise<void>;
35
+ hide(): Promise<void>;
36
+ _show(): Promise<void>;
37
+ _hide(): Promise<void>;
38
+ _cleanup(): void;
39
+ }
40
+ export {};
@@ -0,0 +1,278 @@
1
+ /**
2
+ * NOTE: demos at dev/pending.html
3
+ * NOTE: should apply writing style on event listeners to other files
4
+ * TODO: consider removing popoverController, but in case of tooltip, reserve
5
+ * for now
6
+ * TODO: hijack all `hidePopover()`s to allow more complex JS animations
7
+ * TODO: simpler Menu that just inherits List - do we really need a separated
8
+ * Menu? Why not just `<md-list role="presentation">`?
9
+ *
10
+ * Design: TO BE ADDED
11
+ */
12
+ var _a;
13
+ import { __decorate } from "tslib";
14
+ import { LitElement, html, isServer } from 'lit';
15
+ import { customElement, property } from 'lit/decorators.js';
16
+ import { autoUpdate, computePosition, arrow, flip, offset, shift, } from '@floating-ui/dom';
17
+ import { InternalsAttached } from '../mixins/internals-attached.js';
18
+ import { Attachable, autoAttachToParent, handleControlChange, } from '../mixins/attachable.js';
19
+ import { transformOriginFromArrow } from '../controllers/popover-controller.js';
20
+ import { popupStyles } from './popup-styles.css.js';
21
+ // Returns the first tabbable element within an eleent
22
+ //
23
+ // TODO: reverse search?
24
+ export function lookForTabbable(root) {
25
+ const FOCUSABLE_SELECTOR = [
26
+ 'a[href]',
27
+ 'button',
28
+ 'input',
29
+ 'textarea',
30
+ 'select',
31
+ 'summary',
32
+ '[tabindex]',
33
+ '[contenteditable=true]',
34
+ ].join(',');
35
+ const isTabbable = (el) => el.matches(FOCUSABLE_SELECTOR) &&
36
+ !el.hasAttribute('disabled') &&
37
+ el.getAttribute('tabindex') !== '-1';
38
+ if ('shadowRoot' in root && root.shadowRoot) {
39
+ const shadowResult = lookForTabbable(root.shadowRoot);
40
+ if (shadowResult)
41
+ return shadowResult;
42
+ }
43
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, {
44
+ acceptNode: (node) => {
45
+ const el = node;
46
+ return isTabbable(el) || el.shadowRoot
47
+ ? NodeFilter.FILTER_ACCEPT
48
+ : NodeFilter.FILTER_SKIP;
49
+ },
50
+ });
51
+ let currentNode = walker.nextNode();
52
+ while (currentNode) {
53
+ if (isTabbable(currentNode)) {
54
+ return currentNode;
55
+ }
56
+ if (currentNode.shadowRoot) {
57
+ const shadowFocusable = lookForTabbable(currentNode.shadowRoot);
58
+ if (shadowFocusable) {
59
+ return shadowFocusable;
60
+ }
61
+ }
62
+ currentNode = walker.nextNode();
63
+ }
64
+ return null;
65
+ }
66
+ let Popup = class Popup extends Attachable(InternalsAttached(LitElement)) {
67
+ static { this.styles = [popupStyles]; }
68
+ render() {
69
+ return html `<slot></slot>`;
70
+ }
71
+ constructor() {
72
+ super();
73
+ this.open = false;
74
+ this.noFocusControl = false;
75
+ this.placement = 'bottom-start';
76
+ this.strategy = 'absolute';
77
+ this.offset = 0;
78
+ this.windowPadding = 8;
79
+ this[_a] = false;
80
+ this.#onRequestHide = () => {
81
+ this.hide();
82
+ };
83
+ // #allowHidePopover = false;
84
+ // #onBeforeToggle = (e: ToggleEvent) => {
85
+ // // Intercept browser light-dismiss (ESC, click outside) or external hidePopover() calls
86
+ // if (e.newState === 'closed' && !this.#allowHidePopover) {
87
+ // e.preventDefault();
88
+ // if (this.open) {
89
+ // this.hide();
90
+ // }
91
+ // }
92
+ // };
93
+ this.#onToggle = (e) => {
94
+ e.preventDefault();
95
+ if (e.newState === 'closed' && this.open)
96
+ this.hide();
97
+ };
98
+ this.#onFocusOut = (e) => {
99
+ // for `this` and `this.$control`
100
+ if (this.noFocusControl || !this.open)
101
+ return;
102
+ const target = e.relatedTarget;
103
+ if (target && (this.contains(target) || this.$control?.contains(target))) {
104
+ return;
105
+ }
106
+ if (target) {
107
+ this.hide();
108
+ return;
109
+ }
110
+ requestAnimationFrame(() => {
111
+ if (!document.hasFocus())
112
+ this.hide();
113
+ });
114
+ };
115
+ this.#onTriggerClick = (e) => {
116
+ e.preventDefault();
117
+ this.toggle();
118
+ };
119
+ this.#dummyArrow = isServer ? null : document.createElement('div');
120
+ if (!this.hasAttribute('popover'))
121
+ this.setAttribute('popover', 'auto');
122
+ if (!isServer) {
123
+ this.addEventListener('request-popup-hide', this.#onRequestHide);
124
+ // this.addEventListener('beforetoggle', this.#onBeforeToggle);
125
+ this.addEventListener('toggle', this.#onToggle);
126
+ this.addEventListener('focusout', this.#onFocusOut);
127
+ }
128
+ this.setAttribute('notransition', '');
129
+ }
130
+ connectedCallback() {
131
+ super.connectedCallback();
132
+ requestAnimationFrame(() => requestAnimationFrame(() => this.removeAttribute('notransition')));
133
+ }
134
+ disconnectedCallback() {
135
+ this._cleanup();
136
+ super.disconnectedCallback();
137
+ }
138
+ updated(changedProperties) {
139
+ if (changedProperties.has('open')) {
140
+ if (this.open)
141
+ this._show();
142
+ else
143
+ this._hide();
144
+ this.#syncTriggerAria();
145
+ }
146
+ }
147
+ [(_a = autoAttachToParent, handleControlChange)](prev, next) {
148
+ if (prev) {
149
+ prev.removeEventListener('click', this.#onTriggerClick);
150
+ prev.removeEventListener('focusout', this.#onFocusOut);
151
+ }
152
+ if (next) {
153
+ next.addEventListener('click', this.#onTriggerClick);
154
+ next.addEventListener('focusout', this.#onFocusOut);
155
+ next.ariaHasPopup = 'true';
156
+ next.ariaExpanded = String(this.open);
157
+ next.ariaControlsElements = [this];
158
+ }
159
+ }
160
+ #onRequestHide;
161
+ // #allowHidePopover = false;
162
+ // #onBeforeToggle = (e: ToggleEvent) => {
163
+ // // Intercept browser light-dismiss (ESC, click outside) or external hidePopover() calls
164
+ // if (e.newState === 'closed' && !this.#allowHidePopover) {
165
+ // e.preventDefault();
166
+ // if (this.open) {
167
+ // this.hide();
168
+ // }
169
+ // }
170
+ // };
171
+ #onToggle;
172
+ #onFocusOut;
173
+ #onTriggerClick;
174
+ #syncTriggerAria() {
175
+ this.$control?.setAttribute('aria-expanded', String(this.open));
176
+ }
177
+ #focusFirstInteractiveElement() {
178
+ if (this.noFocusControl)
179
+ return;
180
+ const autoFocusEl = this.querySelector('[autofocus]');
181
+ if (autoFocusEl) {
182
+ autoFocusEl.focus();
183
+ return;
184
+ }
185
+ const firstFocusable = lookForTabbable(this);
186
+ if (firstFocusable) {
187
+ firstFocusable.focus();
188
+ }
189
+ }
190
+ async toggle() {
191
+ this.open = !this.open;
192
+ }
193
+ async show() {
194
+ this.open = true;
195
+ }
196
+ async hide() {
197
+ this.open = false;
198
+ }
199
+ async _show() {
200
+ if (this.isConnected && !this.matches(':popover-open'))
201
+ this.showPopover();
202
+ const trigger = this.$control;
203
+ if (trigger) {
204
+ this._cleanup();
205
+ this.#cleanupAutoUpdate = autoUpdate(trigger, this, () => this.#reposition());
206
+ await this.#reposition();
207
+ }
208
+ // await this._showing();
209
+ requestAnimationFrame(() => {
210
+ if (this.open)
211
+ this.#focusFirstInteractiveElement();
212
+ });
213
+ }
214
+ async _hide() {
215
+ // await this._hiding();
216
+ this._cleanup();
217
+ if (this.matches(':popover-open'))
218
+ this.hidePopover();
219
+ // if (this.matches(':popover-open')) {
220
+ // this.#allowHidePopover = true;
221
+ // try {
222
+ // this.hidePopover();
223
+ // } finally {
224
+ // this.#allowHidePopover = false;
225
+ // }
226
+ // }
227
+ }
228
+ // async _showing(): Promise<void> {}
229
+ // async _hiding(): Promise<void> {}
230
+ #cleanupAutoUpdate;
231
+ #dummyArrow;
232
+ async #reposition() {
233
+ const trigger = this.$control;
234
+ if (!trigger)
235
+ return;
236
+ const { x, y, placement, middlewareData } = await computePosition(trigger, this, {
237
+ placement: this.placement,
238
+ strategy: this.strategy,
239
+ middleware: [
240
+ offset(this.offset),
241
+ flip({ padding: this.windowPadding }),
242
+ shift({ padding: this.windowPadding, crossAxis: true }),
243
+ arrow({ element: this.#dummyArrow }),
244
+ ],
245
+ });
246
+ Object.assign(this.style, {
247
+ left: `${x}px`,
248
+ top: `${y}px`,
249
+ transformOrigin: transformOriginFromArrow(placement, middlewareData.arrow),
250
+ });
251
+ }
252
+ _cleanup() {
253
+ this.#cleanupAutoUpdate?.();
254
+ this.#cleanupAutoUpdate = undefined;
255
+ }
256
+ };
257
+ __decorate([
258
+ property({ type: Boolean, reflect: true })
259
+ ], Popup.prototype, "open", void 0);
260
+ __decorate([
261
+ property({ type: Boolean, attribute: 'no-focus-control' })
262
+ ], Popup.prototype, "noFocusControl", void 0);
263
+ __decorate([
264
+ property({ type: String })
265
+ ], Popup.prototype, "placement", void 0);
266
+ __decorate([
267
+ property({ type: String })
268
+ ], Popup.prototype, "strategy", void 0);
269
+ __decorate([
270
+ property({ type: Number })
271
+ ], Popup.prototype, "offset", void 0);
272
+ __decorate([
273
+ property({ type: Number })
274
+ ], Popup.prototype, "windowPadding", void 0);
275
+ Popup = __decorate([
276
+ customElement('complementary-popup')
277
+ ], Popup);
278
+ export { Popup };
@@ -1,9 +1,12 @@
1
1
  import { LitElement } from 'lit';
2
+ export declare const autoAttachToParent: unique symbol;
3
+ export declare const handleControlChange: unique symbol;
2
4
  export declare class AttachableInterface {
3
- $control: HTMLElement;
5
+ $control: HTMLElement | null;
4
6
  htmlFor: string | null;
5
7
  attach(control: HTMLElement, force?: boolean): void;
6
8
  detach(): void;
7
- handleControlChange(prev: HTMLElement | null, next: HTMLElement | null): void;
9
+ [autoAttachToParent]: boolean;
10
+ [handleControlChange](prev: HTMLElement | null, next: HTMLElement | null): void;
8
11
  }
9
12
  export declare const Attachable: <T extends Constructor<LitElement>>(superClass: T) => Constructor<AttachableInterface> & T;