@featherk/composables 0.10.3 → 0.10.5
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/docs/menu/usePopupMenu.md +59 -29
- package/package.json +1 -1
|
@@ -6,35 +6,16 @@ Composable for accessible Kendo UI for Vue `Popup` + `Menu` action menus. It man
|
|
|
6
6
|
|
|
7
7
|
## Quick Start
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const isOpen = ref(false);
|
|
15
|
-
const triggerRef = ref<HTMLElement | null>(null);
|
|
16
|
-
const menuRef = ref<HTMLElement | null>(null);
|
|
17
|
-
const panelRef = ref<HTMLElement | null>(null);
|
|
18
|
-
|
|
19
|
-
const menu = usePopupMenu({
|
|
20
|
-
isOpen,
|
|
21
|
-
triggerRef,
|
|
22
|
-
menuRef,
|
|
23
|
-
focusTargetRef: panelRef,
|
|
24
|
-
requestShow: () => (isOpen.value = true),
|
|
25
|
-
requestHide: () => (isOpen.value = false),
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
const onSelect = (event: Parameters<typeof menu.handleMenuSelect>[0]) => {
|
|
29
|
-
menu.handleMenuSelect(event, (selected) => {
|
|
30
|
-
// Keep application-specific actions in the consuming component.
|
|
31
|
-
if (selected.item?.text === "Archive") archiveRecord();
|
|
32
|
-
});
|
|
33
|
-
};
|
|
34
|
-
</script>
|
|
9
|
+
1. Create parent-owned refs for `isOpen`, `triggerRef`, `menuRef`, and an optional keyboard focus target.
|
|
10
|
+
2. Call `usePopupMenu(...)` with `requestShow` and `requestHide` that update the parent-owned `isOpen` state.
|
|
11
|
+
3. Route Kendo Menu `@select` through `handleMenuSelect(event, onAction)` so the composable closes first and your business action runs second.
|
|
12
|
+
4. Bind trigger button `@click` and `@keydown` to the composable handlers.
|
|
13
|
+
5. Bind Popup `@close` and Menu `@keydown.escape` so keyboard and mouse close paths restore focus correctly.
|
|
35
14
|
|
|
15
|
+
```vue
|
|
36
16
|
<template>
|
|
37
17
|
<section ref="panelRef">
|
|
18
|
+
<!-- Step 4: bind trigger handlers -->
|
|
38
19
|
<button
|
|
39
20
|
ref="triggerRef"
|
|
40
21
|
type="button"
|
|
@@ -45,6 +26,7 @@ const onSelect = (event: Parameters<typeof menu.handleMenuSelect>[0]) => {
|
|
|
45
26
|
Actions
|
|
46
27
|
</button>
|
|
47
28
|
|
|
29
|
+
<!-- Step 5: bind popup/menu close hooks for modality-aware focus restoration -->
|
|
48
30
|
<Popup :show="isOpen" @close="menu.handlePopupClose">
|
|
49
31
|
<Menu
|
|
50
32
|
ref="menuRef"
|
|
@@ -54,16 +36,57 @@ const onSelect = (event: Parameters<typeof menu.handleMenuSelect>[0]) => {
|
|
|
54
36
|
</Popup>
|
|
55
37
|
</section>
|
|
56
38
|
</template>
|
|
39
|
+
|
|
40
|
+
<script setup lang="ts">
|
|
41
|
+
import { ref } from "vue";
|
|
42
|
+
import {
|
|
43
|
+
usePopupMenu,
|
|
44
|
+
type KendoMenuSelectEvent,
|
|
45
|
+
} from "@featherk/composables/menu";
|
|
46
|
+
|
|
47
|
+
// Step 1: parent-owned menu state and element refs
|
|
48
|
+
const isOpen = ref(false);
|
|
49
|
+
const triggerRef = ref<HTMLElement | null>(null);
|
|
50
|
+
const menuRef = ref<HTMLElement | null>(null);
|
|
51
|
+
const panelRef = ref<HTMLElement | null>(null);
|
|
52
|
+
|
|
53
|
+
// Step 2: wire composable show/hide ownership to parent state
|
|
54
|
+
const menu = usePopupMenu({
|
|
55
|
+
isOpen,
|
|
56
|
+
triggerRef,
|
|
57
|
+
menuRef,
|
|
58
|
+
focusTargetRef: panelRef,
|
|
59
|
+
requestShow: () => (isOpen.value = true),
|
|
60
|
+
requestHide: () => (isOpen.value = false),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Step 3: run close+modality handling first, then app-specific action logic
|
|
64
|
+
const onSelect = (event: KendoMenuSelectEvent) => {
|
|
65
|
+
menu.handleMenuSelect(event, (selected) => {
|
|
66
|
+
// Keep application-specific actions in the consuming component.
|
|
67
|
+
if (selected.item?.text === "Archive") archiveRecord();
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
</script>
|
|
57
71
|
```
|
|
58
72
|
|
|
59
73
|
## Grid Action Cell
|
|
60
74
|
|
|
61
75
|
For a virtualized Kendo Grid, use `resolveFocusTarget` to locate the current row when the popup closes by keyboard. The resolver is evaluated at focus time, after the grid and popup have settled.
|
|
62
76
|
|
|
77
|
+
1. Read `showMenu` from the row item and keep open/close state parent-owned.
|
|
78
|
+
2. Create trigger and menu refs as component refs (`ComponentPublicInstance`) for Kendo wrappers.
|
|
79
|
+
3. Call `usePopupMenu(...)` and map `requestShow`/`requestHide` to row-specific emits.
|
|
80
|
+
4. Provide `resolveFocusTarget` so keyboard close restores focus to the current virtualized row.
|
|
81
|
+
5. Route menu selection through `handleMenuSelect(event, onAction)` so close/focus behavior runs before row action logic.
|
|
82
|
+
|
|
63
83
|
```ts
|
|
64
84
|
<script setup lang="ts">
|
|
65
85
|
import { computed, ref, type ComponentPublicInstance } from "vue";
|
|
66
|
-
import {
|
|
86
|
+
import {
|
|
87
|
+
usePopupMenu,
|
|
88
|
+
type KendoMenuSelectEvent,
|
|
89
|
+
} from "@featherk/composables/menu";
|
|
67
90
|
|
|
68
91
|
const props = defineProps<{ dataItem: { id: string; showMenu: boolean } }>();
|
|
69
92
|
const emit = defineEmits<{
|
|
@@ -71,15 +94,21 @@ const emit = defineEmits<{
|
|
|
71
94
|
"update:hideMenu": [id: string];
|
|
72
95
|
}>();
|
|
73
96
|
|
|
97
|
+
// Step 1: read row-owned open state from the data item
|
|
98
|
+
const isOpen = computed(() => props.dataItem.showMenu);
|
|
99
|
+
|
|
100
|
+
// Step 2: Kendo refs are component instances; usePopupMenu resolves $el internally
|
|
74
101
|
const triggerRef = ref<ComponentPublicInstance | null>(null);
|
|
75
102
|
const menuRef = ref<ComponentPublicInstance | null>(null);
|
|
76
103
|
|
|
104
|
+
// Step 3: keep popup ownership in parent row state via emits
|
|
77
105
|
const menu = usePopupMenu({
|
|
78
|
-
isOpen
|
|
106
|
+
isOpen,
|
|
79
107
|
triggerRef,
|
|
80
108
|
menuRef,
|
|
81
109
|
requestShow: () => emit("update:showMenu", props.dataItem.id),
|
|
82
110
|
requestHide: () => emit("update:hideMenu", props.dataItem.id),
|
|
111
|
+
// Step 4: keyboard close restores focus to current virtualized grid row
|
|
83
112
|
resolveFocusTarget: () => {
|
|
84
113
|
const trigger = triggerRef.value?.$el as HTMLElement | undefined;
|
|
85
114
|
return (
|
|
@@ -88,7 +117,8 @@ const menu = usePopupMenu({
|
|
|
88
117
|
},
|
|
89
118
|
});
|
|
90
119
|
|
|
91
|
-
|
|
120
|
+
// Step 5: let usePopupMenu close first, then run row-specific business action
|
|
121
|
+
const onSelect = (event: KendoMenuSelectEvent) => {
|
|
92
122
|
menu.handleMenuSelect(event, (selected) => {
|
|
93
123
|
// Row-specific action logic remains here.
|
|
94
124
|
runGridAction(props.dataItem.id, selected.item?.text);
|