@vollowx/seele 0.10.4 → 0.11.1

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 (37) hide show
  1. package/custom-elements.json +5 -14
  2. package/package.json +4 -4
  3. package/src/base/controllers/list-controller.js +1 -1
  4. package/src/base/controllers/popover-controller.js +6 -6
  5. package/src/base/input.js +3 -8
  6. package/src/base/menu-item.js +2 -2
  7. package/src/base/menu.js +185 -38
  8. package/src/base/mixins/focus-delegated.js +17 -0
  9. package/src/base/select.js +123 -187
  10. package/src/base/slider.js +3 -8
  11. package/src/base/tooltip.js +1 -1
  12. package/src/m3/button/common-button-styles.css.js +1 -1
  13. package/src/m3/button/common-button-toggle.js +8 -3
  14. package/src/m3/button/icon-button-styles.css.js +1 -1
  15. package/src/m3/button/icon-button-toggle.js +8 -3
  16. package/src/m3/button/icon-button.js +6 -1
  17. package/src/m3/button/shared-button-styles.css.js +1 -1
  18. package/src/m3/button/shared-button-toggle-styles.css.js +1 -1
  19. package/src/m3/checkbox-styles.css.js +1 -1
  20. package/src/m3/fab-styles.css.js +1 -1
  21. package/src/m3/field/field-styles.css.js +1 -1
  22. package/src/m3/field/filled-field-styles.css.js +1 -1
  23. package/src/m3/field/outlined-field-styles.css.js +1 -1
  24. package/src/m3/focus-ring-styles.css.js +1 -1
  25. package/src/m3/menu-styles.css.js +1 -1
  26. package/src/m3/menu.js +5 -4
  27. package/src/m3/ripple-styles.css.js +1 -1
  28. package/src/m3/select/filled-select.js +4 -5
  29. package/src/m3/select/outlined-select.js +4 -5
  30. package/src/m3/select/select-styles.css.js +1 -1
  31. package/src/m3/select/select.js +27 -7
  32. package/src/m3/slider-styles.css.js +1 -1
  33. package/src/m3/switch-styles.css.js +1 -1
  34. package/src/m3/toolbar-styles.css.js +1 -1
  35. package/src/m3/tooltip-styles.css.js +1 -1
  36. package/src/base/menu-utils.js +0 -104
  37. package/src/m3/menu-part-styles.css.js +0 -2
@@ -125,7 +125,7 @@
125
125
  "kind": "field",
126
126
  "name": "color",
127
127
  "type": {
128
- "text": "| 'surface'\r\n | 'primary-container'\r\n | 'secondary-container'\r\n | 'tertiary-container'\r\n | 'primary'\r\n | 'secondary'\r\n | 'tertiary'"
128
+ "text": "| 'surface'\n | 'primary-container'\n | 'secondary-container'\n | 'tertiary-container'\n | 'primary'\n | 'secondary'\n | 'tertiary'"
129
129
  },
130
130
  "default": "'primary'",
131
131
  "attribute": "color",
@@ -144,7 +144,7 @@
144
144
  {
145
145
  "name": "color",
146
146
  "type": {
147
- "text": "| 'surface'\r\n | 'primary-container'\r\n | 'secondary-container'\r\n | 'tertiary-container'\r\n | 'primary'\r\n | 'secondary'\r\n | 'tertiary'"
147
+ "text": "| 'surface'\n | 'primary-container'\n | 'secondary-container'\n | 'tertiary-container'\n | 'primary'\n | 'secondary'\n | 'tertiary'"
148
148
  },
149
149
  "default": "'primary'",
150
150
  "fieldName": "color"
@@ -656,7 +656,7 @@
656
656
  "text": "array"
657
657
  },
658
658
  "readonly": true,
659
- "default": "[ 'md-menu-item', 'md-menu-item-checkbox', 'md-menu-item-radio', ]"
659
+ "default": "[ 'md-menu-item', 'md-menu-item-checkbox', 'md-menu-item-radio', 'md-option', ]"
660
660
  },
661
661
  {
662
662
  "kind": "field",
@@ -666,15 +666,6 @@
666
666
  },
667
667
  "readonly": true,
668
668
  "default": "{ show: 300, hide: 200 }"
669
- },
670
- {
671
- "kind": "field",
672
- "name": "_scrollPadding",
673
- "type": {
674
- "text": "number"
675
- },
676
- "readonly": true,
677
- "default": "4"
678
669
  }
679
670
  ],
680
671
  "superclass": {
@@ -1184,8 +1175,8 @@
1184
1175
  }
1185
1176
  ],
1186
1177
  "superclass": {
1187
- "name": "Switch",
1188
- "module": "/src/base/switch.js"
1178
+ "name": "ToggleButton",
1179
+ "module": "/src/base/toggle-button.js"
1189
1180
  },
1190
1181
  "tagName": "md-switch",
1191
1182
  "customElement": true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vollowx/seele",
3
- "version": "0.10.4",
3
+ "version": "0.11.1",
4
4
  "description": "Standard Extensible Elements. A web components library that can be styled and extended freely, pre-providing components in Material Design 3.",
5
5
  "author": "vollowx",
6
6
  "license": "Apache-2.0",
@@ -54,14 +54,14 @@
54
54
  "@web/dev-server": "^0.4.6",
55
55
  "@web/dev-server-esbuild": "^1.0.4",
56
56
  "chokidar": "^5.0.0",
57
- "lightningcss": "^1.30.2",
57
+ "lightningcss": "^1.31.1",
58
58
  "postcss": "^8.5.6",
59
59
  "postcss-cli": "^11.0.1",
60
60
  "postcss-sorting": "^9.1.0",
61
- "prettier": "^3.7.4"
61
+ "prettier": "^3.8.1"
62
62
  },
63
63
  "dependencies": {
64
- "@floating-ui/dom": "^1.7.4",
64
+ "@floating-ui/dom": "^1.7.5",
65
65
  "@swc/helpers": "^0.5.18",
66
66
  "lit": "^3.3.2"
67
67
  }
@@ -1,4 +1,4 @@
1
- import { getIndexByLetter } from '../menu-utils.js';
1
+ import { getIndexByLetter } from '../menu.js';
2
2
  export class ListController {
3
3
  constructor(host, config){
4
4
  this._focusedIndex = -1;
@@ -33,13 +33,13 @@ export class PopoverController {
33
33
  this.#closeTimer = null;
34
34
  // TODO: Provide the ability to specify an arrow element.
35
35
  this._dummyArrow = isServer ? null : document.createElement('div');
36
- this.#handleClickOutside = (event)=>{
36
+ this.#handleClickAway = (event)=>{
37
37
  const trigger = this.config.trigger();
38
38
  const popover = this.config.popover();
39
39
  const path = event.composedPath();
40
40
  if (trigger && path.includes(trigger)) return;
41
41
  if (popover && path.includes(popover)) return;
42
- this.config.onClickOutside?.();
42
+ this.config.onClickAway?.();
43
43
  };
44
44
  (this.host = host).addController(this);
45
45
  this.config = config;
@@ -52,7 +52,7 @@ export class PopoverController {
52
52
  }
53
53
  hostDisconnected() {
54
54
  this.cleanupAutoUpdate?.();
55
- window.removeEventListener('pointerup', this.#handleClickOutside);
55
+ window.removeEventListener('pointerup', this.#handleClickAway);
56
56
  }
57
57
  #openTimer;
58
58
  #closeTimer;
@@ -63,7 +63,7 @@ export class PopoverController {
63
63
  // TODO: Use a global event listener to manage this more effectively
64
64
  setTimeout(()=>{
65
65
  if (this._open) {
66
- window.addEventListener('pointerup', this.#handleClickOutside);
66
+ window.addEventListener('pointerup', this.#handleClickAway);
67
67
  }
68
68
  }, 0);
69
69
  clearTimeout(this.#openTimer);
@@ -99,7 +99,7 @@ export class PopoverController {
99
99
  async animateClose() {
100
100
  if (!this._open) return;
101
101
  this._open = false;
102
- window.removeEventListener('pointerup', this.#handleClickOutside);
102
+ window.removeEventListener('pointerup', this.#handleClickAway);
103
103
  clearTimeout(this.#openTimer);
104
104
  clearTimeout(this.#closeTimer);
105
105
  const closeDuration = this.config.durations.close();
@@ -151,5 +151,5 @@ export class PopoverController {
151
151
  });
152
152
  });
153
153
  }
154
- #handleClickOutside;
154
+ #handleClickAway;
155
155
  }
package/src/base/input.js CHANGED
@@ -6,16 +6,11 @@ import { _ as _ts_decorate } from "@swc/helpers/_/_ts_decorate";
6
6
  */ import { LitElement, html, nothing } from 'lit';
7
7
  import { property, query } from 'lit/decorators.js';
8
8
  import { live } from 'lit/directives/live.js';
9
- import { FormAssociated } from './mixins/form-associated.js';
10
9
  import { InternalsAttached, internals } from './mixins/internals-attached.js';
11
- const Base = FormAssociated(InternalsAttached(LitElement));
10
+ import { FocusDelegated } from './mixins/focus-delegated.js';
11
+ import { FormAssociated } from './mixins/form-associated.js';
12
+ const Base = FormAssociated(FocusDelegated(InternalsAttached(LitElement)));
12
13
  export class Input extends Base {
13
- static{
14
- this.shadowRootOptions = {
15
- ...LitElement.shadowRootOptions,
16
- delegatesFocus: true
17
- };
18
- }
19
14
  render() {
20
15
  const isTextarea = this.type === 'textarea';
21
16
  const minLength = this.minLength > -1 ? this.minLength : nothing;
@@ -1,11 +1,11 @@
1
1
  import { ListItem } from './list-item.js';
2
2
  export const MenuItemMixin = (superClass)=>{
3
- class OptionElement extends superClass {
3
+ class MenuItemElement extends superClass {
4
4
  constructor(...args){
5
5
  super(...args), this._role = 'menuitem';
6
6
  }
7
7
  }
8
- return OptionElement;
8
+ return MenuItemElement;
9
9
  };
10
10
  export class MenuItem extends MenuItemMixin(ListItem) {
11
11
  }
package/src/base/menu.js CHANGED
@@ -4,29 +4,29 @@ import { property, query, queryAssignedElements } from 'lit/decorators.js';
4
4
  import { setFocusVisible } from '../core/focus-visible.js';
5
5
  import { Attachable } from './mixins/attachable.js';
6
6
  import { InternalsAttached } from './mixins/internals-attached.js';
7
+ import { FocusDelegated } from './mixins/focus-delegated.js';
7
8
  import { PopoverController } from './controllers/popover-controller.js';
8
9
  import { ListController } from './controllers/list-controller.js';
9
- import { MenuActions, getActionFromKey, getUpdatedIndex, scrollItemIntoView } from './menu-utils.js';
10
- const Base = InternalsAttached(Attachable(LitElement));
10
+ const Base = FocusDelegated(InternalsAttached(Attachable(LitElement)));
11
11
  /**
12
12
  * @csspart menu
13
13
  * @csspart items
14
14
  *
15
- * @fires {Event} select - Fired when a menu item has been selected.
16
- * @fires {Event} open - Fired when the menu is opened.
17
- * @fires {Event} close - Fired when the menu is closed.
15
+ * @fires {Event} open - Fires when the menu is opened.
16
+ * @fires {Event} close - Fires when the menu is closed.
17
+ * @fires {MenuSelectEvent} select - Fires when an item is selected.
18
+ * @fires {MenuItemFocusEvent} item-focus - Fires when an item is focused
19
+ *
20
+ * FIXME: aria-activedescendant may not work in and out shadow DOM
18
21
  */ export class Menu extends Base {
19
- static{
20
- this.shadowRootOptions = {
21
- ...LitElement.shadowRootOptions,
22
- delegatesFocus: true
23
- };
22
+ get $items() {
23
+ return this.listController.items || [];
24
24
  }
25
25
  render() {
26
26
  return html`<div
27
27
  part="menu"
28
- role="menu"
29
- tabindex="0"
28
+ role="${this.type}"
29
+ tabindex="${this.tabIndex}"
30
30
  @keydown=${this.#handleKeyDown.bind(this)}
31
31
  @focusout=${this.#handleFocusOut.bind(this)}
32
32
  >
@@ -44,7 +44,7 @@ const Base = InternalsAttached(Attachable(LitElement));
44
44
  this.$control.addEventListener('focusout', this.#handleFocusOut.bind(this));
45
45
  }
46
46
  this.updateComplete.then(()=>{
47
- this.listController.items.forEach((item)=>{
47
+ this.$items.forEach((item)=>{
48
48
  item.addEventListener('mouseover', this.#handleItemMouseOver.bind(this));
49
49
  item.addEventListener('click', this.#handleItemClick.bind(this));
50
50
  });
@@ -68,8 +68,10 @@ const Base = InternalsAttached(Attachable(LitElement));
68
68
  this.$control.ariaExpanded = 'true';
69
69
  }
70
70
  this.popoverController.animateOpen().then(()=>{
71
- this.$menu.focus();
72
- this.listController.focusFirstItem();
71
+ if (!this.noFocusControl) {
72
+ this.$menu.focus();
73
+ this.listController.focusFirstItem();
74
+ }
73
75
  });
74
76
  } else {
75
77
  this.dispatchEvent(new Event('close', {
@@ -82,7 +84,7 @@ const Base = InternalsAttached(Attachable(LitElement));
82
84
  }
83
85
  this.popoverController.animateClose().then(()=>{
84
86
  if (this.$lastFocused) {
85
- this.$lastFocused.focus();
87
+ if (!this.noFocusControl) this.$lastFocused.focus();
86
88
  this.$lastFocused = null;
87
89
  }
88
90
  });
@@ -92,7 +94,7 @@ const Base = InternalsAttached(Attachable(LitElement));
92
94
  #handleKeyDown(event) {
93
95
  if (event.defaultPrevented) return;
94
96
  const action = getActionFromKey(event, this.open);
95
- const items = this.listController.items;
97
+ const items = this.$items;
96
98
  const currentIndex = this.listController.currentIndex;
97
99
  const maxIndex = items.length - 1;
98
100
  switch(action){
@@ -111,10 +113,10 @@ const Base = InternalsAttached(Attachable(LitElement));
111
113
  case MenuActions.CloseSelect:
112
114
  event.preventDefault();
113
115
  if (currentIndex >= 0) {
114
- this.listController.items[currentIndex].focused = false;
116
+ items[currentIndex].focused = false;
115
117
  this.dispatchEvent(new CustomEvent('select', {
116
118
  detail: {
117
- item: this.listController.items[currentIndex],
119
+ item: items[currentIndex],
118
120
  index: currentIndex
119
121
  },
120
122
  bubbles: true,
@@ -163,10 +165,20 @@ const Base = InternalsAttached(Attachable(LitElement));
163
165
  bubbles: true,
164
166
  composed: true
165
167
  }));
166
- if (this.keepOpenClickItem) return;
167
- this.open = false;
168
+ if (!this.keepOpenClickItem) this.open = false;
169
+ }
170
+ get currentIndex() {
171
+ return this.listController?.currentIndex;
172
+ }
173
+ focusFirstItem() {
174
+ this.listController.focusFirstItem();
175
+ }
176
+ focusLastItem() {
177
+ this.listController.focusLastItem();
178
+ }
179
+ focusItem(item) {
180
+ this.listController._focusItem(item);
168
181
  }
169
- // Exposed functions
170
182
  show() {
171
183
  this.open = true;
172
184
  }
@@ -177,7 +189,7 @@ const Base = InternalsAttached(Attachable(LitElement));
177
189
  super(...args), this._possibleItemTags = [], this._durations = {
178
190
  show: 0,
179
191
  hide: 0
180
- }, this._scrollPadding = 0, this.open = false, this.quick = false, this.align = 'bottom-start', this.alignStrategy = 'absolute', this.offset = 0, this.keepOpenBlur = false, this.keepOpenClickItem = false, this.keepOpenClickOutside = false, this.$lastFocused = null, this.popoverController = new PopoverController(this, {
192
+ }, this._scrollPadding = 0, this.type = 'menu', this.open = false, this.quick = false, this.offset = 0, this.align = 'bottom-start', this.alignStrategy = 'absolute', this.keepOpenBlur = false, this.keepOpenClickItem = false, this.keepOpenClickAway = false, this.noListControl = false, this.noFocusControl = false, this.tabIndex = 0, this.$lastFocused = null, this.popoverController = new PopoverController(this, {
181
193
  popover: ()=>this.$menu,
182
194
  trigger: ()=>this.$control,
183
195
  positioning: {
@@ -190,8 +202,8 @@ const Base = InternalsAttached(Attachable(LitElement));
190
202
  open: ()=>this.quick ? 0 : this._durations.show,
191
203
  close: ()=>this.quick ? 0 : this._durations.hide
192
204
  },
193
- onClickOutside: ()=>{
194
- if (!this.keepOpenClickOutside) this.open = false;
205
+ onClickAway: ()=>{
206
+ if (!this.keepOpenClickAway) this.open = false;
195
207
  }
196
208
  }), this.listController = new ListController(this, {
197
209
  isItem: (item)=>this._possibleItemTags.includes(item.tagName.toLowerCase()) && !item.hasAttribute('disabled'),
@@ -201,13 +213,23 @@ const Base = InternalsAttached(Attachable(LitElement));
201
213
  },
202
214
  focusItem: (item)=>{
203
215
  item.focused = true;
204
- this.$menu.setAttribute('aria-activedescendant', item.id);
216
+ if (!this.noFocusControl) this.$menu.setAttribute('aria-activedescendant', item.id);
205
217
  scrollItemIntoView(this.$menu, item, this._scrollPadding);
218
+ this.dispatchEvent(new CustomEvent('item-focus', {
219
+ detail: {
220
+ item: item
221
+ },
222
+ bubbles: true,
223
+ composed: true
224
+ }));
206
225
  },
207
226
  wrapNavigation: ()=>false
208
227
  });
209
228
  }
210
229
  }
230
+ _ts_decorate([
231
+ property()
232
+ ], Menu.prototype, "type", void 0);
211
233
  _ts_decorate([
212
234
  property({
213
235
  type: Boolean,
@@ -216,10 +238,14 @@ _ts_decorate([
216
238
  ], Menu.prototype, "open", void 0);
217
239
  _ts_decorate([
218
240
  property({
219
- type: Boolean,
220
- reflect: true
241
+ type: Boolean
221
242
  })
222
243
  ], Menu.prototype, "quick", void 0);
244
+ _ts_decorate([
245
+ property({
246
+ type: Number
247
+ })
248
+ ], Menu.prototype, "offset", void 0);
223
249
  _ts_decorate([
224
250
  property({
225
251
  reflect: true
@@ -228,30 +254,46 @@ _ts_decorate([
228
254
  _ts_decorate([
229
255
  property({
230
256
  type: String,
231
- reflect: true
257
+ reflect: true,
258
+ attribute: 'align-strategy'
232
259
  })
233
260
  ], Menu.prototype, "alignStrategy", void 0);
234
261
  _ts_decorate([
235
262
  property({
236
- type: Number,
237
- reflect: true
263
+ type: Boolean,
264
+ attribute: 'keep-open-blur'
238
265
  })
239
- ], Menu.prototype, "offset", void 0);
266
+ ], Menu.prototype, "keepOpenBlur", void 0);
240
267
  _ts_decorate([
241
268
  property({
242
- type: Boolean
269
+ type: Boolean,
270
+ attribute: 'keep-open-click-item'
243
271
  })
244
- ], Menu.prototype, "keepOpenBlur", void 0);
272
+ ], Menu.prototype, "keepOpenClickItem", void 0);
245
273
  _ts_decorate([
246
274
  property({
247
- type: Boolean
275
+ type: Boolean,
276
+ attribute: 'keep-open-click-away'
248
277
  })
249
- ], Menu.prototype, "keepOpenClickItem", void 0);
278
+ ], Menu.prototype, "keepOpenClickAway", void 0);
250
279
  _ts_decorate([
251
280
  property({
252
- type: Boolean
281
+ type: Boolean,
282
+ attribute: 'no-list-control'
283
+ })
284
+ ], Menu.prototype, "noListControl", void 0);
285
+ _ts_decorate([
286
+ property({
287
+ type: Boolean,
288
+ attribute: 'no-focus-control'
253
289
  })
254
- ], Menu.prototype, "keepOpenClickOutside", void 0);
290
+ ], Menu.prototype, "noFocusControl", void 0);
291
+ _ts_decorate([
292
+ property({
293
+ type: Number,
294
+ attribute: 'data-tabindex'
295
+ })
296
+ ], Menu.prototype, "tabIndex", void 0);
255
297
  _ts_decorate([
256
298
  query('[part="menu"]')
257
299
  ], Menu.prototype, "$menu", void 0);
@@ -260,3 +302,108 @@ _ts_decorate([
260
302
  flatten: true
261
303
  })
262
304
  ], Menu.prototype, "slotItems", void 0);
305
+ // Reference: https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-select-only/
306
+ export const MenuActions = {
307
+ Close: 0,
308
+ CloseSelect: 1,
309
+ First: 2,
310
+ Last: 3,
311
+ Next: 4,
312
+ Open: 5,
313
+ PageDown: 6,
314
+ PageUp: 7,
315
+ Previous: 8,
316
+ Select: 9,
317
+ Type: 10
318
+ };
319
+ export function filterOptions(options = [], filter, exclude = []) {
320
+ return options.filter((option)=>{
321
+ const matches = option.toLowerCase().indexOf(filter.toLowerCase()) === 0;
322
+ return matches && exclude.indexOf(option) < 0;
323
+ });
324
+ }
325
+ export function getActionFromKey(event, menuOpen) {
326
+ const { key, altKey, ctrlKey, metaKey } = event;
327
+ const openKeys = [
328
+ 'ArrowDown',
329
+ 'ArrowUp',
330
+ 'Enter',
331
+ ' '
332
+ ];
333
+ if (!menuOpen && openKeys.includes(key)) {
334
+ return MenuActions.Open;
335
+ }
336
+ if (key === 'Home') {
337
+ return MenuActions.First;
338
+ }
339
+ if (key === 'End') {
340
+ return MenuActions.Last;
341
+ }
342
+ if (key === 'Backspace' || key === 'Clear' || key.length === 1 && key !== ' ' && !altKey && !ctrlKey && !metaKey) {
343
+ return MenuActions.Type;
344
+ }
345
+ if (menuOpen) {
346
+ if (key === 'ArrowUp' && altKey) {
347
+ return MenuActions.CloseSelect;
348
+ } else if (key === 'ArrowDown' && !altKey) {
349
+ return MenuActions.Next;
350
+ } else if (key === 'ArrowUp') {
351
+ return MenuActions.Previous;
352
+ } else if (key === 'PageUp') {
353
+ return MenuActions.PageUp;
354
+ } else if (key === 'PageDown') {
355
+ return MenuActions.PageDown;
356
+ } else if (key === 'Escape') {
357
+ return MenuActions.Close;
358
+ } else if (key === 'Enter' || key === ' ') {
359
+ return MenuActions.CloseSelect;
360
+ }
361
+ }
362
+ return undefined;
363
+ }
364
+ export function getIndexByLetter(options, filter, startIndex = 0) {
365
+ const orderedOptions = [
366
+ ...options.slice(startIndex),
367
+ ...options.slice(0, startIndex)
368
+ ];
369
+ const firstMatch = filterOptions(orderedOptions, filter)[0];
370
+ const allSameLetter = (array)=>array.every((letter)=>letter === array[0]);
371
+ if (firstMatch) {
372
+ return options.indexOf(firstMatch);
373
+ } else if (allSameLetter(filter.split(''))) {
374
+ const matches = filterOptions(orderedOptions, filter[0]);
375
+ return options.indexOf(matches[0]);
376
+ } else {
377
+ return -1;
378
+ }
379
+ }
380
+ export function getUpdatedIndex(currentIndex, maxIndex, action) {
381
+ const pageSize = 10;
382
+ switch(action){
383
+ case MenuActions.First:
384
+ return 0;
385
+ case MenuActions.Last:
386
+ return maxIndex;
387
+ case MenuActions.Previous:
388
+ return Math.max(0, currentIndex - 1);
389
+ case MenuActions.Next:
390
+ return Math.min(maxIndex, currentIndex + 1);
391
+ case MenuActions.PageUp:
392
+ return Math.max(0, currentIndex - pageSize);
393
+ case MenuActions.PageDown:
394
+ return Math.min(maxIndex, currentIndex + pageSize);
395
+ default:
396
+ return currentIndex;
397
+ }
398
+ }
399
+ export function scrollItemIntoView(menu, item, paddingY = 0) {
400
+ if (!menu) return;
401
+ // Basic scroll into view logic
402
+ const menuRect = menu.getBoundingClientRect();
403
+ const itemRect = item.getBoundingClientRect();
404
+ if (itemRect.bottom + paddingY > menuRect.bottom) {
405
+ menu.scrollTop += itemRect.bottom - menuRect.bottom + paddingY;
406
+ } else if (itemRect.top - paddingY < menuRect.top) {
407
+ menu.scrollTop -= menuRect.top - itemRect.top + paddingY;
408
+ }
409
+ }
@@ -0,0 +1,17 @@
1
+ import { LitElement } from 'lit';
2
+ import { internals } from './internals-attached.js';
3
+ export const FocusDelegated = (superClass)=>{
4
+ class FocusDelegatedElement extends superClass {
5
+ static{
6
+ this.shadowRootOptions = {
7
+ ...LitElement.shadowRootOptions,
8
+ delegatesFocus: true
9
+ };
10
+ }
11
+ constructor(...args){
12
+ super();
13
+ this[internals].role = 'presentation';
14
+ }
15
+ }
16
+ return FocusDelegatedElement;
17
+ };