@featherk/ui 0.12.1 → 0.12.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.
@@ -2,7 +2,10 @@
2
2
 
3
3
  [Back to UI README](https://github.com/NantHealth/featherk/blob/integration/packages/ui/README.md)
4
4
 
5
- `ActionCell` renders a configured set of repeatable Kendo action buttons. The parent owns one `useActiveIdRegistry`, one `usePopupMenu`, and one shared Kendo `Popup` and `Menu`; ActionCell registers every button with that parent-owned lifecycle.
5
+ `ActionCell` renders a configured set of repeatable Kendo action buttons. Triggers with an
6
+ `items` factory register with the parent-owned shared popup lifecycle; triggers without one
7
+ are direct actions. `ActionCell` renders the `<td>` for a Kendo Grid custom cell and accepts
8
+ Kendo `GridCellProps`.
6
9
 
7
10
  ## Quick Start
8
11
 
@@ -16,14 +19,12 @@
16
19
  <table>
17
20
  <tbody>
18
21
  <tr v-for="row in rows" :key="row.id">
19
- <td>
20
- <!-- Step 3: repeat the trigger container for each data item. -->
21
- <ActionCell
22
- :data-item="row"
23
- :config="config"
24
- :menu-controller="menuController"
25
- />
26
- </td>
22
+ <!-- Step 3: repeat the table-cell component for each data item. -->
23
+ <ActionCell
24
+ :data-item="row"
25
+ :config="config"
26
+ :menu-controller="menuController"
27
+ />
27
28
  </tr>
28
29
  </tbody>
29
30
  </table>
@@ -38,15 +39,13 @@
38
39
  import { computed, ref, type ComponentPublicInstance } from "vue";
39
40
  import { Popup } from "@progress/kendo-vue-popup";
40
41
  import { Menu } from "@progress/kendo-vue-layout";
41
- import {
42
- useActiveIdRegistry,
43
- usePopupMenu,
44
- } from "@featherk/composables";
42
+ import { useActiveIdRegistry } from "@featherk/composables/registry";
43
+ import { usePopupMenu } from "@featherk/composables/menu";
45
44
  import {
46
45
  ActionCell,
47
46
  type ActionCellConfig,
48
47
  type ActionCellMenuController,
49
- } from "@featherk/ui";
48
+ } from "@featherk/ui/table";
50
49
 
51
50
  type Row = { id: number; name: string };
52
51
  const rows = ref<Row[]>([]);
@@ -92,12 +91,30 @@ const activeMenuItems = computed(() => []);
92
91
 
93
92
  `useCompositeId()` supplies component-local DOM IDs for ActionCell buttons. Do not use those IDs for the shared registry: `menuController.getTriggerId` must return a deterministic identity derived from the trigger and row, such as `patient:42`.
94
93
 
94
+ ## Grid Cells
95
+
96
+ `ActionCell` accepts the Grid-cell rendering subset of `GridCellProps` and merges the
97
+ Kendo-provided `className` with its own `fkui-action-cell` class. It intentionally omits
98
+ `onCellclick` and `onCellkeydown`: use Grid-level row handlers such as `useGridRowAction`
99
+ for row interaction. In a Grid cell template, merge the cell props and shared-menu props into
100
+ one binding:
101
+
102
+ ```vue
103
+ <template #actionsCell="{ props }">
104
+ <ActionCell v-bind="{ ...props, ...actionCellMenu.actionCellProps }" />
105
+ </template>
106
+ ```
107
+
108
+ `ActionCell` owns the custom-cell `<td>`; use it directly in the Grid cell template rather
109
+ than nesting it inside a caller-owned table cell.
110
+
95
111
  ## Keyboard Navigation
96
112
 
97
- `ActionCell` defaults each trigger button to `tabindex="-1"`, preserving the roving-focus
98
- model used by Kendo UI for Vue Grid with `useGridA11y`. When using `ActionCell` in a standard
99
- HTML table or another context where the action buttons should participate in sequential Tab
100
- navigation, pass `:tab-index="0"`:
113
+ `ActionCell` defaults each trigger button to `tabindex="-1"`, preserving the row-focused
114
+ navigation model used by `useGridA11y`. It does not participate in Kendo's built-in
115
+ per-cell `navigatable` mode. When using `ActionCell` in a standard HTML table or another
116
+ context where the action buttons should participate in sequential Tab navigation, pass
117
+ `:tab-index="0"`:
101
118
 
102
119
  ```vue
103
120
  <ActionCell
@@ -114,5 +131,6 @@ This is a component-level focus policy and is intentionally separate from
114
131
  ## Types
115
132
 
116
133
  - `ActionCellConfig<TData>`: stable configuration object containing `triggers`.
117
- - `ActionCellTrigger<TData>`: defines `key`, `icon`, `ariaLabel`, optional `items(dataItem)`, and optional `disabled(dataItem)`.
134
+ - `ActionCellTrigger<TData>`: defines `key`, `icon`, `ariaLabel`, optional `items(dataItem)` for a menu trigger, and optional `disabled(dataItem)`. Without `items`, the trigger is a direct action.
135
+ - `ActionCellMenuItem`: Kendo-compatible menu item with `id`, `text`, optional `cssClass`, and optional `data`. `useActionCellMenu` passes the entire item to `onActionSelected`; direct actions pass `undefined` and are identified by `trigger.key`.
118
136
  - `ActionCellMenuController<TData>`: parent-owned registry, trigger-ID resolver, and popup click/keyboard delegation contract.
@@ -50,6 +50,8 @@ parent.
50
50
  This does not replace `useActiveIdRegistry` or `usePopupMenu` — it wraps them for the
51
51
  specific `ActionCell` shared-instance shape. Reach for the lower-level composables directly
52
52
  when driving a popup/menu that isn't backed by `ActionCell` (e.g. a row-level context menu).
53
+ Triggers without an `items` factory are direct actions: `useActionCellMenu` calls
54
+ `onActionSelected(undefined, row, trigger)` directly and does not register or open a popup.
53
55
 
54
56
  ## Quick Start
55
57
 
@@ -58,8 +60,8 @@ when driving a popup/menu that isn't backed by `ActionCell` (e.g. a row-level co
58
60
  2. Create a `menuRef` for the single shared Kendo `Menu` instance.
59
61
  3. Spread `actionCellProps` onto every `<ActionCell>` instance.
60
62
  4. Spread `popupProps`/`menuProps` onto the single shared `<Popup>`/`<Menu>`.
61
- 5. Handle the selected action in `onActionSelected`, which already receives the resolved row
62
- and trigger.
63
+ 5. Handle the selected item in `onActionSelected`; dispatch on `item.id` and use the
64
+ resolved row and trigger for the business action. Direct actions pass `undefined` as item.
63
65
 
64
66
  ```vue
65
67
  <template>
@@ -73,7 +75,10 @@ when driving a popup/menu that isn't backed by `ActionCell` (e.g. a row-level co
73
75
 
74
76
  <!-- Step 4 -->
75
77
  <Popup v-bind="actionCellMenu.popupProps.value">
76
- <strong v-if="actionCellMenu.activeTitle.value">
78
+ <strong
79
+ v-if="actionCellMenu.activeTitle.value"
80
+ class="fk-menu-title"
81
+ >
77
82
  {{ actionCellMenu.activeTitle.value }}
78
83
  </strong>
79
84
  <Menu ref="menuRef" v-bind="actionCellMenu.menuProps.value" />
@@ -82,7 +87,11 @@ when driving a popup/menu that isn't backed by `ActionCell` (e.g. a row-level co
82
87
 
83
88
  <script setup lang="ts">
84
89
  import { ref } from "vue";
85
- import { ActionCell, useActionCellMenu, type ActionCellConfig } from "@featherk/ui";
90
+ import {
91
+ ActionCell,
92
+ useActionCellMenu,
93
+ type ActionCellConfig,
94
+ } from "@featherk/ui/table";
86
95
 
87
96
  // Step 2
88
97
  const menuRef = ref(null);
@@ -93,8 +102,13 @@ const actionCellMenu = useActionCellMenu({
93
102
  rows,
94
103
  getRowId: (row) => row.id,
95
104
  menuRef,
96
- onActionSelected: (action, row, trigger) => {
97
- // Step 5: row-specific business logic
105
+ onActionSelected: (item, row, trigger) => {
106
+ // Step 5: menu actions dispatch on item.id; direct actions use trigger.key.
107
+ if (item) {
108
+ runAction(item.id, row, trigger);
109
+ } else {
110
+ runDirectAction(trigger.key, row, trigger);
111
+ }
98
112
  },
99
113
  });
100
114
  </script>
@@ -112,15 +126,27 @@ const actionCellMenu = useActionCellMenu({
112
126
  | `triggerMode` | No | Forwarded to `usePopupMenu`. Defaults to `"button"`. |
113
127
  | `anchor` | No | Forwarded to `usePopupMenu` for offset positioning. Defaults to `true` because the shared popup targets dynamically resolved ActionCell triggers. |
114
128
  | `resolveFocusTarget` | No | Forwarded to `usePopupMenu` for keyboard-close focus restoration. |
115
- | `coexistsWith` | No | Sibling `useActiveIdRegistry` instances (e.g. a hand-rolled row-context menu) to deactivate whenever a trigger in this collection opens. |
116
- | `onActionSelected` | Yes | Runs after a menu item is selected, with the resolved `(action, row, trigger)`. |
129
+ | `group` | No | A `useExclusiveGroup()` instance that deactivates other registered members before this menu opens. |
130
+ | `onActionSelected` | Yes | Runs after a menu item is selected, with the original `(item, row, trigger)`. Direct actions pass `undefined` as `item`. |
117
131
 
118
132
  Each trigger in `config.triggers` may also define an optional `title?: (dataItem) => string`.
119
133
  When set, the resolved string is exposed via the returned `activeTitle` — it is *not* mixed
120
134
  into `menuProps.items`, since `useActionCellMenu` never renders markup and can't dictate how
121
135
  a consumer's `<Menu>` implementation would render a fake "title" item. Render `activeTitle`
122
- yourself as real markup (e.g. `<strong v-if="activeTitle">{{ activeTitle }}</strong>`) as a
123
- sibling of `<Menu>` inside the `<Popup>`.
136
+ yourself as real markup with the shared `fk-menu-title` class (e.g.
137
+ `<strong v-if="activeTitle" class="fk-menu-title">{{ activeTitle }}</strong>`) as a sibling
138
+ of `<Menu>` inside the `<Popup>`.
139
+
140
+ ### Exclusive Menus
141
+
142
+ Use `useExclusiveGroup` from `@featherk/composables/registry` when ActionCell menus and
143
+ other active-id registries must be mutually exclusive. `useActionCellMenu` registers its own
144
+ registry automatically; manually wired registries call `group.register()` from their owning
145
+ Vue setup.
146
+
147
+ When a trigger defines both `items` and `title`, its resolved `activeTitle` is also applied
148
+ as the `aria-label` of the rendered `role="menubar"`. Direct-action triggers and menu
149
+ triggers without a title do not receive a menu label.
124
150
 
125
151
  ## Return shape
126
152
 
@@ -164,12 +190,6 @@ require defining lookup lifecycle/re-render behavior before changing the public
164
190
  `triggerMode: "row"` — see
165
191
  [ActionCellGridDemo.vue](https://github.com/NantHealth/featherk/blob/integration/demos/src/views/ui/ActionCellGridDemo.vue)
166
192
  for a worked example of the two coexisting.
167
- - **Coexisting with a sibling menu (e.g. a row-level context menu) does not give you mutual
168
- exclusivity for free.** Each `usePopupMenu` instance only closes on outside clicks or its
169
- own trigger; a click on this row's `ActionCell` button is *inside* a sibling row-menu's
170
- ignored trigger element (the `<tr>`), so the sibling's own outside-click handling never
171
- fires for it, and vice versa. Pass the sibling's registry via `coexistsWith` to close it
172
- automatically whenever an `ActionCell` trigger opens — this covers one direction only. The
173
- sibling must still deactivate *this* composable's registry (exposed via
174
- `actionCellProps.menuController.registry`) when it activates; see `ActionCellGridDemo.vue`
175
- linked above for that remaining call site.
193
+ - **Coexisting with a sibling menu (e.g. a row-level context menu) requires a shared
194
+ `useExclusiveGroup`.** Register the sibling registry with the group; `useActionCellMenu`
195
+ registers its own registry automatically, so opening either menu deactivates the other.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@featherk/ui",
3
- "version": "0.12.1",
3
+ "version": "0.12.2",
4
4
  "main": "dist/featherk-ui.umd.js",
5
5
  "module": "dist/featherk-ui.es.js",
6
6
  "types": "dist/index.d.ts",
@@ -39,6 +39,7 @@
39
39
  "@featherk/composables": ">=0.8.2",
40
40
  "@progress/kendo-vue-buttons": "^6.4.1",
41
41
  "@progress/kendo-vue-dropdowns": "^6.4.1",
42
+ "@progress/kendo-vue-grid": "^6.4.1",
42
43
  "@progress/kendo-vue-inputs": "^6.4.1",
43
44
  "@progress/kendo-vue-labels": "^6.4.1"
44
45
  },