@limetech/lime-elements 38.33.6 → 38.33.7
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/CHANGELOG.md +8 -0
- package/dist/cjs/limel-breadcrumbs_8.cjs.entry.js +147 -21
- package/dist/cjs/limel-breadcrumbs_8.cjs.entry.js.map +1 -1
- package/dist/collection/components/menu/menu.js +147 -21
- package/dist/collection/components/menu/menu.js.map +1 -1
- package/dist/collection/test-assets/icons/home.svg +6 -0
- package/dist/esm/limel-breadcrumbs_8.entry.js +147 -21
- package/dist/esm/limel-breadcrumbs_8.entry.js.map +1 -1
- package/dist/lime-elements/lime-elements.esm.js +1 -1
- package/dist/lime-elements/{p-eb121716.entry.js → p-033a0aa9.entry.js} +4 -4
- package/dist/lime-elements/p-033a0aa9.entry.js.map +1 -0
- package/dist/types/components/menu/menu.d.ts +39 -24
- package/package.json +1 -1
- package/dist/lime-elements/p-eb121716.entry.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
## [38.33.7](https://github.com/Lundalogik/lime-elements/compare/v38.33.6...v38.33.7) (2025-12-12)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
* **menu:** improve keyboard navigation ([eecb117](https://github.com/Lundalogik/lime-elements/commit/eecb11780af9be340b365f1b2f62f77b3133a57b))
|
|
8
|
+
|
|
1
9
|
## [38.33.6](https://github.com/Lundalogik/lime-elements/compare/v38.33.5...v38.33.6) (2025-12-11)
|
|
2
10
|
|
|
3
11
|
|
|
@@ -2514,10 +2514,10 @@ const Menu = class {
|
|
|
2514
2514
|
if (breadcrumbsItems.length === 0) {
|
|
2515
2515
|
return;
|
|
2516
2516
|
}
|
|
2517
|
-
return (index.h("limel-breadcrumbs", { style: {
|
|
2517
|
+
return (index.h("limel-breadcrumbs", { ref: this.setBreadcrumbsElement, style: {
|
|
2518
2518
|
'border-bottom': 'solid 1px rgb(var(--contrast-500))',
|
|
2519
2519
|
'flex-shrink': '0',
|
|
2520
|
-
}, onSelect: this.handleBreadcrumbsSelect, items: breadcrumbsItems }));
|
|
2520
|
+
}, onSelect: this.handleBreadcrumbsSelect, onKeyDown: this.handleBreadcrumbsKeyDown, items: breadcrumbsItems }));
|
|
2521
2521
|
};
|
|
2522
2522
|
this.handleBreadcrumbsSelect = (event) => {
|
|
2523
2523
|
if (!event.detail.menuItem) {
|
|
@@ -2582,8 +2582,8 @@ const Menu = class {
|
|
|
2582
2582
|
this.loadingSubItems = false;
|
|
2583
2583
|
};
|
|
2584
2584
|
// Key handler for the input search field
|
|
2585
|
-
// Will change focus to the first/last item
|
|
2586
|
-
// list to enable selection with the keyboard
|
|
2585
|
+
// Will change focus to breadcrumbs (if present) or the first/last item
|
|
2586
|
+
// in the dropdown list to enable selection with the keyboard
|
|
2587
2587
|
this.handleInputKeyDown = (event) => {
|
|
2588
2588
|
const isForwardTab = event.key === keycodes.TAB &&
|
|
2589
2589
|
!event.altKey &&
|
|
@@ -2594,26 +2594,58 @@ const Menu = class {
|
|
|
2594
2594
|
if (!isForwardTab && !isUp && !isDown) {
|
|
2595
2595
|
return;
|
|
2596
2596
|
}
|
|
2597
|
-
if (!this.list) {
|
|
2598
|
-
return;
|
|
2599
|
-
}
|
|
2600
2597
|
event.stopPropagation();
|
|
2601
2598
|
event.preventDefault();
|
|
2602
2599
|
if (isForwardTab || isDown) {
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2600
|
+
if (this.focusBreadcrumbs()) {
|
|
2601
|
+
return;
|
|
2602
|
+
}
|
|
2603
|
+
this.focusFirstListItem();
|
|
2606
2604
|
return;
|
|
2607
2605
|
}
|
|
2608
2606
|
if (isUp) {
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
listElement === null || listElement === void 0 ? void 0 : listElement.focus();
|
|
2607
|
+
// Focus the last list item (wrapping behavior)
|
|
2608
|
+
this.focusLastListItem();
|
|
2612
2609
|
}
|
|
2613
2610
|
};
|
|
2614
|
-
// Key handler for the menu list
|
|
2611
|
+
// Key handler for the menu list (capture phase)
|
|
2612
|
+
// Handles Up arrow on first item and Down arrow on last item
|
|
2613
|
+
// Must run in capture phase to intercept before MDC Menu wraps focus
|
|
2614
|
+
// Only intercepts when there's a search input or breadcrumbs to navigate to
|
|
2615
|
+
this.handleListKeyDownCapture = (event) => {
|
|
2616
|
+
const isUp = event.key === keycodes.ARROW_UP;
|
|
2617
|
+
const isDown = event.key === keycodes.ARROW_DOWN;
|
|
2618
|
+
if (!isUp && !isDown) {
|
|
2619
|
+
return;
|
|
2620
|
+
}
|
|
2621
|
+
// Up on first item: go to breadcrumbs or search input (if they exist)
|
|
2622
|
+
if (isUp && this.isFirstListItemFocused()) {
|
|
2623
|
+
// Try to focus breadcrumbs first
|
|
2624
|
+
if (this.focusBreadcrumbs()) {
|
|
2625
|
+
event.stopPropagation();
|
|
2626
|
+
event.preventDefault();
|
|
2627
|
+
return;
|
|
2628
|
+
}
|
|
2629
|
+
// Then try search input
|
|
2630
|
+
if (this.searchInput) {
|
|
2631
|
+
event.stopPropagation();
|
|
2632
|
+
event.preventDefault();
|
|
2633
|
+
this.searchInput.focus();
|
|
2634
|
+
}
|
|
2635
|
+
// If neither exists, let MDC Menu handle wrap-around
|
|
2636
|
+
return;
|
|
2637
|
+
}
|
|
2638
|
+
// Down on last item: go to search input (if it exists)
|
|
2639
|
+
if (isDown && this.isLastListItemFocused() && this.searchInput) {
|
|
2640
|
+
event.stopPropagation();
|
|
2641
|
+
event.preventDefault();
|
|
2642
|
+
this.searchInput.focus();
|
|
2643
|
+
}
|
|
2644
|
+
// If no search input, let MDC Menu handle wrap-around
|
|
2645
|
+
};
|
|
2646
|
+
// Key handler for the menu list (bubble phase)
|
|
2615
2647
|
// Will change focus to the search field if using shift+tab
|
|
2616
|
-
// And can go forward/back with
|
|
2648
|
+
// And can go forward/back with right/left arrow keys
|
|
2617
2649
|
this.handleMenuKeyDown = (event) => {
|
|
2618
2650
|
var _a;
|
|
2619
2651
|
const isBackwardTab = event.key === keycodes.TAB &&
|
|
@@ -2629,8 +2661,9 @@ const Menu = class {
|
|
|
2629
2661
|
event.stopPropagation();
|
|
2630
2662
|
event.preventDefault();
|
|
2631
2663
|
(_a = this.searchInput) === null || _a === void 0 ? void 0 : _a.focus();
|
|
2664
|
+
return;
|
|
2632
2665
|
}
|
|
2633
|
-
|
|
2666
|
+
if (!this.gridLayout && (isLeft || isRight)) {
|
|
2634
2667
|
const currentItem = this.getCurrentItem();
|
|
2635
2668
|
event.stopPropagation();
|
|
2636
2669
|
event.preventDefault();
|
|
@@ -2642,17 +2675,43 @@ const Menu = class {
|
|
|
2642
2675
|
}
|
|
2643
2676
|
}
|
|
2644
2677
|
};
|
|
2678
|
+
// Key handler for breadcrumbs
|
|
2679
|
+
// Up arrow: focus search input
|
|
2680
|
+
// Down arrow: focus first list item
|
|
2681
|
+
this.handleBreadcrumbsKeyDown = (event) => {
|
|
2682
|
+
var _a;
|
|
2683
|
+
const isUp = event.key === keycodes.ARROW_UP;
|
|
2684
|
+
const isDown = event.key === keycodes.ARROW_DOWN;
|
|
2685
|
+
if (!isUp && !isDown) {
|
|
2686
|
+
return;
|
|
2687
|
+
}
|
|
2688
|
+
event.stopPropagation();
|
|
2689
|
+
event.preventDefault();
|
|
2690
|
+
if (isUp) {
|
|
2691
|
+
(_a = this.searchInput) === null || _a === void 0 ? void 0 : _a.focus();
|
|
2692
|
+
return;
|
|
2693
|
+
}
|
|
2694
|
+
if (isDown) {
|
|
2695
|
+
this.focusFirstListItem();
|
|
2696
|
+
}
|
|
2697
|
+
};
|
|
2645
2698
|
this.clearSearch = () => {
|
|
2646
2699
|
this.searchValue = '';
|
|
2647
2700
|
this.searchResults = null;
|
|
2648
2701
|
this.loadingSubItems = false;
|
|
2649
2702
|
};
|
|
2650
2703
|
this.getCurrentItem = () => {
|
|
2651
|
-
var _a, _b, _c;
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2704
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2705
|
+
let menuElement = (_c = (_b = (_a = this.list) === null || _a === void 0 ? void 0 : _a.shadowRoot) === null || _b === void 0 ? void 0 : _b.activeElement) !== null && _c !== void 0 ? _c : null;
|
|
2706
|
+
if (menuElement && menuElement.getAttribute('role') !== 'menuitem') {
|
|
2707
|
+
menuElement = menuElement.closest('[role="menuitem"]');
|
|
2708
|
+
}
|
|
2709
|
+
if (!menuElement) {
|
|
2710
|
+
menuElement = (_e = (_d = this.list) === null || _d === void 0 ? void 0 : _d.shadowRoot) === null || _e === void 0 ? void 0 : _e.querySelector('[role="menuitem"][tabindex="0"]');
|
|
2711
|
+
}
|
|
2712
|
+
const dataIndex = Number.parseInt((_f = menuElement === null || menuElement === void 0 ? void 0 : menuElement.dataset.index) !== null && _f !== void 0 ? _f : '0', 10);
|
|
2713
|
+
const item = this.visibleItems[dataIndex];
|
|
2714
|
+
return (item !== null && item !== void 0 ? item : this.visibleItems[0]);
|
|
2656
2715
|
};
|
|
2657
2716
|
this.goForward = (currentItem) => {
|
|
2658
2717
|
this.handleSelect(currentItem, false);
|
|
@@ -2745,7 +2804,13 @@ const Menu = class {
|
|
|
2745
2804
|
this.handleSelect(event.detail);
|
|
2746
2805
|
};
|
|
2747
2806
|
this.setListElement = (element) => {
|
|
2807
|
+
if (this.list) {
|
|
2808
|
+
this.list.removeEventListener('keydown', this.handleListKeyDownCapture, true);
|
|
2809
|
+
}
|
|
2748
2810
|
this.list = element;
|
|
2811
|
+
if (this.list) {
|
|
2812
|
+
this.list.addEventListener('keydown', this.handleListKeyDownCapture, true);
|
|
2813
|
+
}
|
|
2749
2814
|
};
|
|
2750
2815
|
this.setFocus = () => {
|
|
2751
2816
|
setTimeout(() => {
|
|
@@ -2771,6 +2836,67 @@ const Menu = class {
|
|
|
2771
2836
|
this.setSearchElement = (element) => {
|
|
2772
2837
|
this.searchInput = element;
|
|
2773
2838
|
};
|
|
2839
|
+
this.setBreadcrumbsElement = (element) => {
|
|
2840
|
+
this.breadcrumbs = element;
|
|
2841
|
+
};
|
|
2842
|
+
/**
|
|
2843
|
+
* Focuses the first focusable element inside breadcrumbs.
|
|
2844
|
+
* Returns true if breadcrumbs exist and were focused,
|
|
2845
|
+
* false otherwise.
|
|
2846
|
+
*/
|
|
2847
|
+
this.focusBreadcrumbs = () => {
|
|
2848
|
+
var _a;
|
|
2849
|
+
if (!this.breadcrumbs) {
|
|
2850
|
+
return false;
|
|
2851
|
+
}
|
|
2852
|
+
const focusableElement = (_a = this.breadcrumbs.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('button, a');
|
|
2853
|
+
if (focusableElement) {
|
|
2854
|
+
focusableElement.focus();
|
|
2855
|
+
return true;
|
|
2856
|
+
}
|
|
2857
|
+
return false;
|
|
2858
|
+
};
|
|
2859
|
+
this.focusFirstListItem = () => {
|
|
2860
|
+
const listItems = this.getListItems();
|
|
2861
|
+
const firstItem = listItems === null || listItems === void 0 ? void 0 : listItems[0];
|
|
2862
|
+
firstItem === null || firstItem === void 0 ? void 0 : firstItem.focus();
|
|
2863
|
+
};
|
|
2864
|
+
this.focusLastListItem = () => {
|
|
2865
|
+
const listItems = this.getListItems();
|
|
2866
|
+
const lastItem = listItems === null || listItems === void 0 ? void 0 : listItems.at(-1);
|
|
2867
|
+
lastItem === null || lastItem === void 0 ? void 0 : lastItem.focus();
|
|
2868
|
+
};
|
|
2869
|
+
this.isFirstListItemFocused = () => {
|
|
2870
|
+
var _a;
|
|
2871
|
+
const listItems = this.getListItems();
|
|
2872
|
+
if (!listItems) {
|
|
2873
|
+
return false;
|
|
2874
|
+
}
|
|
2875
|
+
const firstItem = listItems[0];
|
|
2876
|
+
const activeElement = (_a = this.list.shadowRoot) === null || _a === void 0 ? void 0 : _a.activeElement;
|
|
2877
|
+
return firstItem === activeElement;
|
|
2878
|
+
};
|
|
2879
|
+
this.isLastListItemFocused = () => {
|
|
2880
|
+
var _a;
|
|
2881
|
+
const listItems = this.getListItems();
|
|
2882
|
+
if (!listItems) {
|
|
2883
|
+
return false;
|
|
2884
|
+
}
|
|
2885
|
+
const lastItem = listItems.at(-1);
|
|
2886
|
+
const activeElement = (_a = this.list.shadowRoot) === null || _a === void 0 ? void 0 : _a.activeElement;
|
|
2887
|
+
return lastItem === activeElement;
|
|
2888
|
+
};
|
|
2889
|
+
this.getListItems = () => {
|
|
2890
|
+
var _a;
|
|
2891
|
+
if (!this.list) {
|
|
2892
|
+
return null;
|
|
2893
|
+
}
|
|
2894
|
+
const items = (_a = this.list.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.mdc-deprecated-list-item');
|
|
2895
|
+
if (!(items === null || items === void 0 ? void 0 : items.length)) {
|
|
2896
|
+
return null;
|
|
2897
|
+
}
|
|
2898
|
+
return [...items];
|
|
2899
|
+
};
|
|
2774
2900
|
this.focusMenuItem = () => {
|
|
2775
2901
|
var _a;
|
|
2776
2902
|
if (!this.list) {
|