@vollowx/seele 0.14.0 → 0.14.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.
Files changed (52) hide show
  1. package/README.md +3 -3
  2. package/custom-elements.json +195 -153
  3. package/package.json +4 -4
  4. package/src/all.d.ts +2 -1
  5. package/src/all.js +2 -1
  6. package/src/base/autocomplete.d.ts +1 -0
  7. package/src/base/autocomplete.js +17 -5
  8. package/src/base/composed-menu.d.ts +16 -0
  9. package/src/base/composed-menu.js +36 -0
  10. package/src/base/menu.js +26 -0
  11. package/src/base/popup-styles.css.js +1 -1
  12. package/src/base/popup.d.ts +22 -9
  13. package/src/base/popup.js +131 -83
  14. package/src/base/positioning.d.ts +2 -0
  15. package/src/base/positioning.js +23 -0
  16. package/src/base/progressbar.d.ts +13 -0
  17. package/src/base/progressbar.js +32 -0
  18. package/src/base/slider.d.ts +1 -0
  19. package/src/base/slider.js +1 -0
  20. package/src/base/tooltip-styles.css.js +2 -0
  21. package/src/base/tooltip.d.ts +9 -8
  22. package/src/base/tooltip.js +143 -56
  23. package/src/m3/autocomplete/autocomplete-styles.css.js +1 -1
  24. package/src/m3/autocomplete/autocomplete.js +1 -1
  25. package/src/m3/badge/badge-styles.css.js +1 -1
  26. package/src/m3/item/item-styles.css.js +1 -1
  27. package/src/m3/list/list-item-styles.css.js +1 -1
  28. package/src/m3/loading/loading-styles.css.js +2 -0
  29. package/src/m3/{loading-indicator/loading-indicator.d.ts → loading/loading.d.ts} +9 -3
  30. package/src/m3/{loading-indicator/loading-indicator.js → loading/loading.js} +15 -9
  31. package/src/m3/menu/composed-menu.d.ts +4 -9
  32. package/src/m3/menu/composed-menu.js +7 -27
  33. package/src/m3/menu/menu-styles.css.js +1 -1
  34. package/src/m3/navigation/navigation-rail-styles.css.js +1 -1
  35. package/src/m3/popup/popup-styles.css.js +1 -1
  36. package/src/m3/popup/popup.d.ts +1 -0
  37. package/src/m3/popup/popup.js +4 -0
  38. package/src/m3/progress/linear-progress-styles.css.js +2 -0
  39. package/src/m3/progress/linear-progress.d.ts +22 -0
  40. package/src/m3/progress/linear-progress.js +35 -0
  41. package/src/m3/radio/radio-styles.css.js +1 -1
  42. package/src/m3/select/select-styles.css.js +1 -1
  43. package/src/m3/slider/slider-styles.css.js +1 -1
  44. package/src/m3/styles/color-default-dark.css +51 -0
  45. package/src/m3/styles/color-default-light.css +51 -0
  46. package/src/m3/switch/switch-styles.css.js +1 -1
  47. package/src/m3/tooltip/tooltip-styles.css.js +1 -1
  48. package/src/m3/tooltip/tooltip.d.ts +0 -7
  49. package/src/m3/tooltip/tooltip.js +1 -8
  50. package/src/base/controllers/popover-controller.d.ts +0 -35
  51. package/src/base/controllers/popover-controller.js +0 -163
  52. package/src/m3/loading-indicator/loading-indicator-styles.css.js +0 -2
package/README.md CHANGED
@@ -56,7 +56,9 @@ seele relies on the following modern web standards:
56
56
  | [`:dir()`][:dir] | 2023 | 120 | - | - |
57
57
  | [Constructable Stylesheets][constructable-stylesheets] | 2024 | - | 126 | - |
58
58
  | [`:state()`][:state] | 2024 | 125 | 126 | - |
59
+ | [`@starting-style`][starting-style] | 2024 | - | - | 17.5 |
59
60
  | [`aria*Element`][aria-active] and similar | 2025 | 135 | 136 | 16.4 |
61
+ | [Popover API][popover-api] | 2025 | - | - | 17 |
60
62
  | [`CSSNumericValue`][css-numeric-value] | not yet | - | Nightly | - |
61
63
 
62
64
  _* `CSSNumericValue` is worked around currently so it does not raise the minimum
@@ -66,14 +68,12 @@ All of the above result in the following minimum requirements:
66
68
 
67
69
  - Chromium: >= 135
68
70
  - Firefox: >= 136
69
- - Safari: >= 16.4
71
+ - Safari: >= 17.5
70
72
 
71
73
  It is 2026 now, so you don't really need to worry about this. However, in the
72
74
  future, the following web features might be used and require higher browser
73
75
  versions:
74
76
 
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
77
  - [`anchor()`][anchor] — Baseline 2026, Firefox 147, Safari 26, _will remove the dependency `floating-dom`_
78
78
 
79
79
  _* omitting a specific browser version means using that standard does not raise
@@ -1436,42 +1436,6 @@
1436
1436
  }
1437
1437
  ]
1438
1438
  },
1439
- {
1440
- "kind": "javascript-module",
1441
- "path": "src/m3/experimental/popup.ts",
1442
- "declarations": [
1443
- {
1444
- "kind": "class",
1445
- "description": "",
1446
- "name": "M3Popup",
1447
- "members": [],
1448
- "superclass": {
1449
- "name": "Popup",
1450
- "module": "/src/base/experimental/popup.js"
1451
- },
1452
- "tagName": "md-popup",
1453
- "customElement": true
1454
- }
1455
- ],
1456
- "exports": [
1457
- {
1458
- "kind": "js",
1459
- "name": "M3Popup",
1460
- "declaration": {
1461
- "name": "M3Popup",
1462
- "module": "src/m3/experimental/popup.ts"
1463
- }
1464
- },
1465
- {
1466
- "kind": "custom-element-definition",
1467
- "name": "md-popup",
1468
- "declaration": {
1469
- "name": "M3Popup",
1470
- "module": "src/m3/experimental/popup.ts"
1471
- }
1472
- }
1473
- ]
1474
- },
1475
1439
  {
1476
1440
  "kind": "javascript-module",
1477
1441
  "path": "src/m3/fab/fab.ts",
@@ -2834,12 +2798,12 @@
2834
2798
  },
2835
2799
  {
2836
2800
  "kind": "javascript-module",
2837
- "path": "src/m3/loading-indicator/loading-indicator.ts",
2801
+ "path": "src/m3/loading/loading.ts",
2838
2802
  "declarations": [
2839
2803
  {
2840
2804
  "kind": "class",
2841
2805
  "description": "",
2842
- "name": "M3LoadingIndicator",
2806
+ "name": "M3Loading",
2843
2807
  "members": [
2844
2808
  {
2845
2809
  "kind": "field",
@@ -2992,18 +2956,75 @@
2992
2956
  "exports": [
2993
2957
  {
2994
2958
  "kind": "js",
2995
- "name": "M3LoadingIndicator",
2959
+ "name": "M3Loading",
2996
2960
  "declaration": {
2997
- "name": "M3LoadingIndicator",
2998
- "module": "src/m3/loading-indicator/loading-indicator.ts"
2961
+ "name": "M3Loading",
2962
+ "module": "src/m3/loading/loading.ts"
2999
2963
  }
3000
2964
  },
3001
2965
  {
3002
2966
  "kind": "custom-element-definition",
3003
2967
  "name": "md-loading",
3004
2968
  "declaration": {
3005
- "name": "M3LoadingIndicator",
3006
- "module": "src/m3/loading-indicator/loading-indicator.ts"
2969
+ "name": "M3Loading",
2970
+ "module": "src/m3/loading/loading.ts"
2971
+ }
2972
+ }
2973
+ ]
2974
+ },
2975
+ {
2976
+ "kind": "javascript-module",
2977
+ "path": "src/m3/menu/composed-menu.ts",
2978
+ "declarations": [
2979
+ {
2980
+ "kind": "class",
2981
+ "description": "",
2982
+ "name": "M3ComposedMenu",
2983
+ "members": [
2984
+ {
2985
+ "kind": "field",
2986
+ "name": "vibrant",
2987
+ "type": {
2988
+ "text": "boolean"
2989
+ },
2990
+ "default": "false",
2991
+ "attribute": "vibrant",
2992
+ "reflects": true
2993
+ }
2994
+ ],
2995
+ "attributes": [
2996
+ {
2997
+ "name": "vibrant",
2998
+ "type": {
2999
+ "text": "boolean"
3000
+ },
3001
+ "default": "false",
3002
+ "fieldName": "vibrant"
3003
+ }
3004
+ ],
3005
+ "superclass": {
3006
+ "name": "ComposedMenu",
3007
+ "module": "/src/base/composed-menu.js"
3008
+ },
3009
+ "tagName": "md-composed-menu",
3010
+ "customElement": true
3011
+ }
3012
+ ],
3013
+ "exports": [
3014
+ {
3015
+ "kind": "js",
3016
+ "name": "M3ComposedMenu",
3017
+ "declaration": {
3018
+ "name": "M3ComposedMenu",
3019
+ "module": "src/m3/menu/composed-menu.ts"
3020
+ }
3021
+ },
3022
+ {
3023
+ "kind": "custom-element-definition",
3024
+ "name": "md-composed-menu",
3025
+ "declaration": {
3026
+ "name": "M3ComposedMenu",
3027
+ "module": "src/m3/menu/composed-menu.ts"
3007
3028
  }
3008
3029
  }
3009
3030
  ]
@@ -3093,10 +3114,7 @@
3093
3114
  "name": "M3Menu",
3094
3115
  "cssParts": [
3095
3116
  {
3096
- "name": "menu-surface"
3097
- },
3098
- {
3099
- "name": "list"
3117
+ "name": "items"
3100
3118
  }
3101
3119
  ],
3102
3120
  "slots": [
@@ -3105,37 +3123,7 @@
3105
3123
  "name": ""
3106
3124
  }
3107
3125
  ],
3108
- "members": [
3109
- {
3110
- "kind": "field",
3111
- "name": "_durations",
3112
- "type": {
3113
- "text": "object"
3114
- },
3115
- "readonly": true,
3116
- "default": "{ show: 300, hide: 150 }"
3117
- },
3118
- {
3119
- "kind": "field",
3120
- "name": "color",
3121
- "type": {
3122
- "text": "'standard' | 'vibrant'"
3123
- },
3124
- "default": "'standard'",
3125
- "attribute": "color",
3126
- "reflects": true
3127
- }
3128
- ],
3129
- "attributes": [
3130
- {
3131
- "name": "color",
3132
- "type": {
3133
- "text": "'standard' | 'vibrant'"
3134
- },
3135
- "default": "'standard'",
3136
- "fieldName": "color"
3137
- }
3138
- ],
3126
+ "members": [],
3139
3127
  "superclass": {
3140
3128
  "name": "Menu",
3141
3129
  "module": "/src/base/menu.js"
@@ -3295,6 +3283,134 @@
3295
3283
  }
3296
3284
  ]
3297
3285
  },
3286
+ {
3287
+ "kind": "javascript-module",
3288
+ "path": "src/m3/popup/popup.ts",
3289
+ "declarations": [
3290
+ {
3291
+ "kind": "class",
3292
+ "description": "",
3293
+ "name": "M3Popup",
3294
+ "members": [
3295
+ {
3296
+ "kind": "field",
3297
+ "name": "quick",
3298
+ "type": {
3299
+ "text": "boolean"
3300
+ },
3301
+ "default": "false",
3302
+ "attribute": "quick",
3303
+ "reflects": true
3304
+ },
3305
+ {
3306
+ "kind": "field",
3307
+ "name": "vibrant",
3308
+ "type": {
3309
+ "text": "boolean"
3310
+ },
3311
+ "default": "false",
3312
+ "attribute": "vibrant",
3313
+ "reflects": true
3314
+ }
3315
+ ],
3316
+ "attributes": [
3317
+ {
3318
+ "name": "quick",
3319
+ "type": {
3320
+ "text": "boolean"
3321
+ },
3322
+ "default": "false",
3323
+ "fieldName": "quick"
3324
+ },
3325
+ {
3326
+ "name": "vibrant",
3327
+ "type": {
3328
+ "text": "boolean"
3329
+ },
3330
+ "default": "false",
3331
+ "fieldName": "vibrant"
3332
+ }
3333
+ ],
3334
+ "superclass": {
3335
+ "name": "Popup",
3336
+ "module": "/src/base/popup.js"
3337
+ },
3338
+ "tagName": "md-popup",
3339
+ "customElement": true
3340
+ }
3341
+ ],
3342
+ "exports": [
3343
+ {
3344
+ "kind": "js",
3345
+ "name": "M3Popup",
3346
+ "declaration": {
3347
+ "name": "M3Popup",
3348
+ "module": "src/m3/popup/popup.ts"
3349
+ }
3350
+ },
3351
+ {
3352
+ "kind": "custom-element-definition",
3353
+ "name": "md-popup",
3354
+ "declaration": {
3355
+ "name": "M3Popup",
3356
+ "module": "src/m3/popup/popup.ts"
3357
+ }
3358
+ }
3359
+ ]
3360
+ },
3361
+ {
3362
+ "kind": "javascript-module",
3363
+ "path": "src/m3/progress/linear-progress.ts",
3364
+ "declarations": [
3365
+ {
3366
+ "kind": "class",
3367
+ "description": "TODO: wavy?\nNOTE: buffering progress indicator is no longer documented in MD3",
3368
+ "name": "M3LinearProgress",
3369
+ "cssParts": [
3370
+ {
3371
+ "name": "track"
3372
+ },
3373
+ {
3374
+ "name": "gap"
3375
+ },
3376
+ {
3377
+ "name": "active"
3378
+ },
3379
+ {
3380
+ "name": "before"
3381
+ },
3382
+ {
3383
+ "name": "after"
3384
+ }
3385
+ ],
3386
+ "members": [],
3387
+ "superclass": {
3388
+ "name": "ProgressBar",
3389
+ "module": "/src/base/progressbar.js"
3390
+ },
3391
+ "tagName": "md-linear-progress",
3392
+ "customElement": true
3393
+ }
3394
+ ],
3395
+ "exports": [
3396
+ {
3397
+ "kind": "js",
3398
+ "name": "M3LinearProgress",
3399
+ "declaration": {
3400
+ "name": "M3LinearProgress",
3401
+ "module": "src/m3/progress/linear-progress.ts"
3402
+ }
3403
+ },
3404
+ {
3405
+ "kind": "custom-element-definition",
3406
+ "name": "md-linear-progress",
3407
+ "declaration": {
3408
+ "name": "M3LinearProgress",
3409
+ "module": "src/m3/progress/linear-progress.ts"
3410
+ }
3411
+ }
3412
+ ]
3413
+ },
3298
3414
  {
3299
3415
  "kind": "javascript-module",
3300
3416
  "path": "src/m3/radio/radio.ts",
@@ -3686,30 +3802,6 @@
3686
3802
  "module": "src/m3/select/select.ts"
3687
3803
  }
3688
3804
  }
3689
- ],
3690
- "events": [
3691
- {
3692
- "type": {
3693
- "text": "Event"
3694
- },
3695
- "description": "Fired when the selected value has changed.",
3696
- "name": "change",
3697
- "inheritedFrom": {
3698
- "name": "M3Select",
3699
- "module": "src/m3/select/select.ts"
3700
- }
3701
- },
3702
- {
3703
- "type": {
3704
- "text": "Event"
3705
- },
3706
- "description": "Fired when the selected value has changed.",
3707
- "name": "input",
3708
- "inheritedFrom": {
3709
- "name": "M3Select",
3710
- "module": "src/m3/select/select.ts"
3711
- }
3712
- }
3713
3805
  ]
3714
3806
  }
3715
3807
  ],
@@ -3919,30 +4011,6 @@
3919
4011
  "module": "src/m3/select/select.ts"
3920
4012
  }
3921
4013
  }
3922
- ],
3923
- "events": [
3924
- {
3925
- "type": {
3926
- "text": "Event"
3927
- },
3928
- "description": "Fired when the selected value has changed.",
3929
- "name": "change",
3930
- "inheritedFrom": {
3931
- "name": "M3Select",
3932
- "module": "src/m3/select/select.ts"
3933
- }
3934
- },
3935
- {
3936
- "type": {
3937
- "text": "Event"
3938
- },
3939
- "description": "Fired when the selected value has changed.",
3940
- "name": "input",
3941
- "inheritedFrom": {
3942
- "name": "M3Select",
3943
- "module": "src/m3/select/select.ts"
3944
- }
3945
- }
3946
4014
  ]
3947
4015
  }
3948
4016
  ],
@@ -4015,22 +4083,6 @@
4015
4083
  "privacy": "protected"
4016
4084
  }
4017
4085
  ],
4018
- "events": [
4019
- {
4020
- "type": {
4021
- "text": "Event"
4022
- },
4023
- "description": "Fired when the selected value has changed.",
4024
- "name": "change"
4025
- },
4026
- {
4027
- "type": {
4028
- "text": "Event"
4029
- },
4030
- "description": "Fired when the selected value has changed.",
4031
- "name": "input"
4032
- }
4033
- ],
4034
4086
  "attributes": [
4035
4087
  {
4036
4088
  "name": "label",
@@ -4911,17 +4963,7 @@
4911
4963
  "kind": "class",
4912
4964
  "description": "",
4913
4965
  "name": "M3Tooltip",
4914
- "members": [
4915
- {
4916
- "kind": "field",
4917
- "name": "_durations",
4918
- "type": {
4919
- "text": "object"
4920
- },
4921
- "readonly": true,
4922
- "default": "{ show: 150, hide: 150 }"
4923
- }
4924
- ],
4966
+ "members": [],
4925
4967
  "superclass": {
4926
4968
  "name": "Tooltip",
4927
4969
  "module": "/src/base/tooltip.js"
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vollowx/seele",
3
- "version": "0.14.0",
4
- "description": "A extensible Web Components library, with Material Design 3 components pre-provided.",
3
+ "version": "0.14.2",
4
+ "description": "An extensible Web Components library, with Material 3 components pre-provided.",
5
5
  "author": "vollowx",
6
6
  "license": "Apache-2.0",
7
7
  "type": "module",
@@ -53,13 +53,13 @@
53
53
  },
54
54
  "devDependencies": {
55
55
  "@custom-elements-manifest/analyzer": "^0.11.0",
56
- "@types/node": "^26.1.2",
56
+ "@types/node": "^26.6.2",
57
57
  "@web/dev-server": "^0.4.6",
58
58
  "@web/dev-server-esbuild": "^1.0.5",
59
59
  "chokidar": "^5.0.0",
60
60
  "lightningcss": "^1.33.0",
61
61
  "minify-html-literals": "^1.3.5",
62
- "prettier": "^3.9.6",
62
+ "prettier": "^3.9.8",
63
63
  "typescript": "^7.0.2"
64
64
  },
65
65
  "dependencies": {
package/src/all.d.ts CHANGED
@@ -14,13 +14,14 @@ import './m3/focus-ring/focus-ring.js';
14
14
  import './m3/item/item.js';
15
15
  import './m3/list/list.js';
16
16
  import './m3/list/list-item.js';
17
- import './m3/loading-indicator/loading-indicator.js';
17
+ import './m3/loading/loading.js';
18
18
  import './m3/menu/menu.js';
19
19
  import './m3/menu/menu-item.js';
20
20
  import './m3/menu/composed-menu.js';
21
21
  import './m3/navigation/navigation-rail.js';
22
22
  import './m3/navigation/navigation-rail-item.js';
23
23
  import './m3/popup/popup.js';
24
+ import './m3/progress/linear-progress.js';
24
25
  import './m3/radio/radio.js';
25
26
  import './m3/ripple/ripple.js';
26
27
  import './m3/select/filled-select.js';
package/src/all.js CHANGED
@@ -14,13 +14,14 @@ import './m3/focus-ring/focus-ring.js';
14
14
  import './m3/item/item.js';
15
15
  import './m3/list/list.js';
16
16
  import './m3/list/list-item.js';
17
- import './m3/loading-indicator/loading-indicator.js';
17
+ import './m3/loading/loading.js';
18
18
  import './m3/menu/menu.js';
19
19
  import './m3/menu/menu-item.js';
20
20
  import './m3/menu/composed-menu.js';
21
21
  import './m3/navigation/navigation-rail.js';
22
22
  import './m3/navigation/navigation-rail-item.js';
23
23
  import './m3/popup/popup.js';
24
+ import './m3/progress/linear-progress.js';
24
25
  import './m3/radio/radio.js';
25
26
  import './m3/ripple/ripple.js';
26
27
  import './m3/select/filled-select.js';
@@ -21,6 +21,7 @@ export declare class Autocomplete extends Base {
21
21
  */
22
22
  renderMenu(): import("lit-html").TemplateResult<1>;
23
23
  constructor();
24
+ firstUpdated(): Promise<void>;
24
25
  private handleInputSlotChange;
25
26
  protected handleItemsSlotChange(): Promise<void>;
26
27
  private handleInput;
@@ -1,7 +1,7 @@
1
1
  import { __decorate } from "tslib";
2
2
  import { LitElement, html, isServer } from 'lit';
3
3
  import { property, query, queryAssignedElements } from 'lit/decorators.js';
4
- import { ensureReady } from '../core/ensure-ready.js';
4
+ import { ensureSlottedReady } from '../core/ensure-ready.js';
5
5
  import { InternalsAttached } from './mixins/internals-attached.js';
6
6
  import { FocusDelegated } from './mixins/focus-delegated.js';
7
7
  const Base = FocusDelegated(InternalsAttached(LitElement));
@@ -25,6 +25,10 @@ export class Autocomplete extends Base {
25
25
  super();
26
26
  this.open = false;
27
27
  this.mode = 'none';
28
+ /**
29
+ * To prevent multiple calls.
30
+ */
31
+ this.#inputBound = false;
28
32
  if (!isServer) {
29
33
  this.addEventListener('focusout', this.#handleFocusOut.bind(this));
30
34
  }
@@ -45,12 +49,19 @@ export class Autocomplete extends Base {
45
49
  this.$popup.hide();
46
50
  });
47
51
  }
48
- // TODO: handle multiple calls on this function, currently double-call
49
- // disallowed
52
+ async firstUpdated() {
53
+ await this.handleInputSlotChange();
54
+ }
55
+ /**
56
+ * To prevent multiple calls.
57
+ */
58
+ #inputBound;
50
59
  async handleInputSlotChange() {
51
- await ensureReady(this.$input);
60
+ if (this.#inputBound)
61
+ return;
62
+ await ensureSlottedReady(this, () => this.inputSlotElements);
52
63
  const input = this.$input;
53
- const $realInput = input.$inputOrTextarea;
64
+ const $realInput = input?.$inputOrTextarea;
54
65
  $realInput.role = 'combobox';
55
66
  $realInput.ariaHasPopup = 'listbox';
56
67
  $realInput.ariaAutoComplete = this.mode;
@@ -59,6 +70,7 @@ export class Autocomplete extends Base {
59
70
  input.addEventListener('keydown', this.handleInputKeydown.bind(this));
60
71
  this.$popup.$ariaControl = $realInput;
61
72
  this.$popup.attach(input);
73
+ this.#inputBound = true;
62
74
  }
63
75
  async handleItemsSlotChange() {
64
76
  // Initial filter based on current input value (if any)
@@ -0,0 +1,16 @@
1
+ import { handleControlChange } from './mixins/attachable.js';
2
+ import { Popup } from './popup.js';
3
+ import type { Menu } from './menu.js';
4
+ /**
5
+ * A popup that wraps a `Menu`. Use this for standalone menus anchored to a
6
+ * button or other control.
7
+ *
8
+ * @slot - menu items
9
+ * @csspart menu - the internal menu part
10
+ */
11
+ export declare class ComposedMenu extends Popup {
12
+ $menu: Menu;
13
+ render(): import("lit-html").TemplateResult<1>;
14
+ constructor();
15
+ [handleControlChange](prev: HTMLElement | null, next: HTMLElement | null): void;
16
+ }
@@ -0,0 +1,36 @@
1
+ import { __decorate } from "tslib";
2
+ import { html } from 'lit';
3
+ import { query } from 'lit/decorators.js';
4
+ import { ensureReady } from '../core/ensure-ready.js';
5
+ import { internals } from './mixins/internals-attached.js';
6
+ import { handleControlChange } from './mixins/attachable.js';
7
+ import { Popup } from './popup.js';
8
+ /**
9
+ * A popup that wraps a `Menu`. Use this for standalone menus anchored to a
10
+ * button or other control.
11
+ *
12
+ * @slot - menu items
13
+ * @csspart menu - the internal menu part
14
+ */
15
+ export class ComposedMenu extends Popup {
16
+ render() {
17
+ return html `<md-menu part="menu"><slot></slot></md-menu>`;
18
+ }
19
+ constructor() {
20
+ super();
21
+ this[internals].role = 'application';
22
+ }
23
+ [handleControlChange](prev, next) {
24
+ if (next) {
25
+ next.ariaHasPopup = 'menu';
26
+ ensureReady(this.$menu).then(() => {
27
+ next.ariaControlsElements = [this.$menu];
28
+ this.$menu[internals].ariaLabelledByElements = [next];
29
+ });
30
+ }
31
+ super[handleControlChange](prev, next);
32
+ }
33
+ }
34
+ __decorate([
35
+ query('md-menu')
36
+ ], ComposedMenu.prototype, "$menu", void 0);
package/src/base/menu.js CHANGED
@@ -63,6 +63,7 @@ export class Menu extends InternalsAttached(LitElement) {
63
63
  },
64
64
  wrapNavigation: () => false,
65
65
  });
66
+ this.#suppressClick = false;
66
67
  this[internals].role = 'menu';
67
68
  if (!this.hasAttribute('tabindex')) {
68
69
  this.setAttribute('tabindex', '0');
@@ -73,6 +74,8 @@ export class Menu extends InternalsAttached(LitElement) {
73
74
  this.addEventListener('focusout', this.#handleFocusOut.bind(this));
74
75
  this.addEventListener('mouseover', this.#handleMouseOver.bind(this));
75
76
  this.addEventListener('click', this.#handleClick.bind(this));
77
+ this.addEventListener('pointerdown', this.#handlePointerDown.bind(this));
78
+ this.addEventListener('pointerup', this.#handlePointerUp.bind(this));
76
79
  }
77
80
  }
78
81
  #handleFocusIn() {
@@ -147,9 +150,32 @@ export class Menu extends InternalsAttached(LitElement) {
147
150
  this.listController._focusItem(item.item);
148
151
  }
149
152
  #handleClick(event) {
153
+ if (this.#suppressClick) {
154
+ this.#suppressClick = false;
155
+ return;
156
+ }
157
+ const item = this.#getItemFromEvent(event);
158
+ if (!item)
159
+ return;
160
+ item.item.focused = false;
161
+ this.#dispatchAction({ ...item });
162
+ if (!this.keepOpenAction)
163
+ this.#dispatchHide();
164
+ }
165
+ #suppressClick;
166
+ #handlePointerDown(_event) {
167
+ this.#suppressClick = false;
168
+ }
169
+ #handlePointerUp(event) {
170
+ if (event.pointerType === 'touch')
171
+ return;
172
+ if (event.button !== 0)
173
+ return;
150
174
  const item = this.#getItemFromEvent(event);
151
175
  if (!item)
152
176
  return;
177
+ // Ignore the `click` that follows a mouse release on an item.
178
+ this.#suppressClick = true;
153
179
  item.item.focused = false;
154
180
  this.#dispatchAction({ ...item });
155
181
  if (!this.keepOpenAction)
@@ -1,2 +1,2 @@
1
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}`;
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}:host([strategy=fixed]){position:fixed}`;