vue-command-kit 0.1.0 โ†’ 0.1.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.
package/README.md CHANGED
@@ -11,89 +11,23 @@
11
11
  Press <kbd>โŒ˜K</kbd> and take control.
12
12
  </p>
13
13
 
14
- ---
15
-
16
- ## ๐Ÿง  Inspiration
17
-
18
- This project is heavily inspired by two great projects:
19
-
20
- | Project | Author | Description |
21
- | --- | --- | --- |
22
- | [`vue-command-palette`](https://github.com/xiaoluoboding/vue-command-palette) | [@xiaoluoboding](https://github.com/xiaoluoboding) | The first composable command palette for Vue, with 590 โ˜… |
23
- | [`cmdk`](https://github.com/pacocoursey/cmdk) | [@pacocoursey](https://github.com/pacocoursey) | Fast, unstyled command menu React component (10k+ โ˜…) |
24
-
25
- ### Why another one?
26
-
27
- [`vue-command-palette`](https://github.com/xiaoluoboding/vue-command-palette) pioneered the โŒ˜K experience for Vue. Big thanks to [@xiaoluoboding](https://github.com/xiaoluoboding) for the original idea and work ๐Ÿ™Œ
28
-
29
- However, the project has been **inactive since September 2023** โ€” 9 issues remain unanswered, no dependencies have been updated in years, and the bundle size is 28 kB. The Vue ecosystem deserves a **well-maintained, lightweight alternative** that keeps up with modern standards.
30
-
31
- **vue-cmdk** is built to fill that gap: same compound component API, zero dependencies, half the size, with full TypeScript support and a commitment to ongoing maintenance.
32
-
33
- ### vs vue-command-palette (legacy reference)
34
-
35
- | Feature | `vue-command-palette` | `vue-cmdk` |
36
- | ---------------- | :----------------------: | :-----------: |
37
- | ๐Ÿ“ฆ Bundle (min) | 28.2 kB | **11.8 kB** |
38
- | ๐Ÿ“ฆ Bundle (gzip) | 9.6 kB | **3.4 kB** |
39
- | ๐Ÿ” Search | fuse.js (extra dep) | **Built-in** |
40
- | ๐Ÿ“‹ TypeScript | Partial | **Full** |
41
- | ๐Ÿ”„ Async items | โŒ | โœ… |
42
- | ๐Ÿงฉ Custom filter | โŒ | โœ… |
43
- | ๐Ÿ”’ Focus trap | โŒ | โœ… |
44
- | ๐Ÿงน Dependencies | 3 (fuse.js, nanoid, ...) | **0** |
45
- | ๐Ÿ”ง Maintenance | โŒ Inactive since 2023 | โœ… **Active** |
46
-
47
- ---
14
+ <p align="center">
15
+ <img src="./demo.gif" alt="vue-cmdk demo" width="720">
16
+ </p>
48
17
 
49
18
  ## โœจ Features
50
19
 
51
- - **๐Ÿงฉ Compound component API** โ€” `<Command.Dialog>`, `<Command.List>`, `<Command.Item>`, etc.
20
+ - **๐Ÿงฉ Compound component API** โ€” `<Command.Dialog>`, `<Command.Input>`, `<Command.Item>`, etc.
52
21
  - **๐Ÿ’„ Unstyled** โ€” Bring your own CSS, zero opinions, full design control
53
22
  - **๐Ÿ” Built-in search** โ€” Fast case-insensitive filtering with keyword matching
54
23
  - **โŒจ๏ธ Keyboard-first** โ€” Arrow keys, Enter, Escape โ€” all built-in, no config needed
24
+ - **๐Ÿ”Š Global shortcut** โ€” Register shortcuts on items (e.g. `โŒ˜S`) and they work globally
55
25
  - **๐Ÿ“ฆ Tiny** โ€” 3.4 kB gzipped, **zero runtime dependencies** (peer: `vue` only)
56
26
  - **๐ŸŽฏ TypeScript** โ€” Full type inference and declaration files
57
27
  - **๐Ÿ”„ Dynamic items** โ€” Pass items as a reactive array, swap anytime
58
28
  - **๐Ÿ›  Custom filter** โ€” Provide your own filter function
59
29
  - **โ™ฟ Accessible** โ€” ARIA attributes, focus trap, `aria-live` region
60
30
 
61
- ## ๐Ÿ“Š Comparison with React cmdk
62
-
63
- `vue-cmdk` is a Vue 3 port inspired by the excellent [`cmdk`](https://github.com/pacocoursey/cmdk) (React). Below is the current feature parity status:
64
-
65
- | # | Feature | React `cmdk` | `vue-cmdk` | Status |
66
- | --- | -------------------------------------------- | :-------------: | :--------------: | :--------: |
67
- | 1 | `Command` root `value` / `onValueChange` | โœ… | โŒ | ๐Ÿ“‹ Planned |
68
- | 2 | `Command` root `shouldFilter` | โœ… | โŒ | ๐Ÿ“‹ Planned |
69
- | 3 | `Command` root `loop` | โœ… | โŒ | ๐Ÿ“‹ Planned |
70
- | 4 | `Command` root `label` (aria-label) | โœ… | โŒ | ๐Ÿ“‹ Planned |
71
- | 5 | `Command.Dialog` `open` / `onOpenChange` | โœ… `open` | โœ… `visible` | โœ… |
72
- | 6 | `Command.Dialog` `container` (portal target) | โœ… | โŒ | ๐Ÿ’ก Future |
73
- | 7 | `Command.Input` `value` / `onValueChange` | โœ… | โœ… `searchQuery` | โœ… |
74
- | 8 | `Command.Item` `forceMount` | โœ… | โŒ | ๐Ÿ“‹ Planned |
75
- | 9 | `Command.Item` `keywords` | โœ… | โœ… | โœ… |
76
- | 10 | `Command.Item` `onSelect` | โœ… | โœ… | โœ… |
77
- | 11 | `Command.Item` auto value from textContent | โœ… | โŒ | ๐Ÿ“‹ Planned |
78
- | 12 | `Command.Group` `forceMount` | โœ… | โŒ | ๐Ÿ“‹ Planned |
79
- | 13 | `Command.Group` `heading` | โœ… | โœ… | โœ… |
80
- | 14 | `Command.Separator` `alwaysRender` | โœ… | โŒ | ๐Ÿ’ก Future |
81
- | 15 | `Command.Empty` | โœ… | โœ… | โœ… |
82
- | 16 | `Command.Loading` | โœ… | โœ… | โœ… |
83
- | 17 | `useCommandState()` state selector | โœ… | โŒ | ๐Ÿ“‹ Planned |
84
- | 18 | **Nested items / Pages** | โœ… (pattern) | โŒ | ๐Ÿ“‹ Planned |
85
- | 19 | **Built-in search / filtering** | โœ… | โœ… | โœ… |
86
- | 20 | **Custom filter function** | โœ… (rank-based) | โœ… (item-based) | โœ… |
87
- | 21 | **Global shortcut listener** | โŒ (manual) | โœ… (built-in) | โœ… Bonus |
88
- | 22 | **Keyboard navigation** | โœ… | โœ… | โœ… |
89
- | 23 | **Focus trap** | โœ… (Radix) | โœ… (custom) | โœ… |
90
- | 24 | **Zero dependencies** | โŒ (Radix UI) | โœ… (0 deps) | โœ… Better |
91
- | 25 | **TypeScript** | โœ… | โœ… | โœ… |
92
- | 26 | **Unstyled** | โœ… | โœ… | โœ… |
93
- | 27 | **Bundle size (gzip)** | ~7 kB | **3.4 kB** | โœ… Smaller |
94
-
95
- > **Legend** โ€” โœ… Done ยท ๐Ÿ“‹ High/Medium priority ยท ๐Ÿ’ก Low priority / Nice to have
96
-
97
31
  ## ๐Ÿš€ Install
98
32
 
99
33
  ```bash
@@ -106,20 +40,20 @@ npm install vue-command-kit
106
40
 
107
41
  ```vue
108
42
  <script setup lang="ts">
109
- import { ref } from 'vue'
110
- import { Command } from 'vue-command-kit'
111
- import type { CommandItemData } from 'vue-command-kit'
43
+ import { ref } from 'vue'
44
+ import { Command } from 'vue-command-kit'
45
+ import type { CommandItemData } from 'vue-command-kit'
112
46
 
113
- const visible = ref(false)
47
+ const visible = ref(false)
114
48
 
115
- const items: CommandItemData[] = [
116
- { value: 'settings', label: 'Open settings', shortcut: 'โŒ˜,' },
117
- { value: 'home', label: 'Go to home', shortcut: 'โŒ˜H' },
118
- ]
49
+ const items: CommandItemData[] = [
50
+ { value: 'settings', label: 'Open settings', shortcut: 'โŒ˜,' },
51
+ { value: 'home', label: 'Go to home', shortcut: 'โŒ˜H' },
52
+ ]
119
53
 
120
- function onSelect(item: CommandItemData) {
121
- console.log('selected:', item.value)
122
- }
54
+ function onSelect(item: CommandItemData) {
55
+ console.log('selected:', item.value)
56
+ }
123
57
  </script>
124
58
 
125
59
  <template>
@@ -155,74 +89,151 @@ npm install vue-command-kit
155
89
 
156
90
  ```vue
157
91
  <script setup lang="ts">
158
- import { Command } from 'vue-command-kit'
92
+ import { Command } from 'vue-command-kit'
159
93
 
160
- function myFilter(items: CommandItemData[], query: string) {
161
- // Return filtered items, or null to use default filter
162
- return items.filter((item) => item.label?.includes(query))
163
- }
94
+ function myFilter(items: CommandItemData[], query: string) {
95
+ return items.filter((item) => item.label?.includes(query))
96
+ }
97
+ </script>
98
+
99
+ <template>
100
+ <Command.Dialog :filter="myFilter" ... />
101
+ </template>
102
+ ```
103
+
104
+ ### With v-model:searchQuery
105
+
106
+ ```vue
107
+ <script setup lang="ts">
108
+ import { ref } from 'vue'
109
+ import { Command } from 'vue-command-kit'
110
+ import type { CommandItemData } from 'vue-command-kit'
111
+
112
+ const visible = ref(false)
113
+ const query = ref('')
114
+
115
+ const items: CommandItemData[] = [
116
+ { value: 'home', label: 'Home', keywords: ['dashboard'] },
117
+ { value: 'settings', label: 'Settings' },
118
+ ]
164
119
  </script>
165
120
 
166
121
  <template>
167
122
  <Command.Dialog
168
- :filter="myFilter"
169
- ...
123
+ :visible="visible"
124
+ :items="items"
125
+ v-model:searchQuery="query"
126
+ @update:visible="visible = $event"
170
127
  />
171
128
  </template>
172
129
  ```
173
130
 
174
- ## ๐Ÿ“– API
131
+ ### Async data + loading
175
132
 
176
- ### `<Command.Dialog>` Props
177
-
178
- | Prop | Type | Default | Description |
179
- | --------------- | ------------------- | --------------------- | ---------------------------- |
180
- | `visible` | `boolean` | `false` | Controlled open state |
181
- | `items` | `CommandItemData[]` | `[]` | Items to display |
182
- | `placeholder` | `string` | `'Type a command...'` | Input placeholder |
183
- | `filter` | `FilterFn` | โ€” | Custom filter function |
184
- | `loading` | `boolean` | `false` | Show loading state |
185
- | `autoFocus` | `boolean` | `true` | Auto-focus input on open |
186
- | `closeOnSelect` | `boolean` | `true` | Close dialog after selection |
133
+ ```vue
134
+ <script setup lang="ts">
135
+ import { ref, watch } from 'vue'
136
+ import { Command } from 'vue-command-kit'
137
+ import type { CommandItemData } from 'vue-command-kit'
138
+
139
+ const visible = ref(false)
140
+ const loading = ref(false)
141
+ const items = ref<CommandItemData[]>([])
142
+
143
+ watch(visible, async (v) => {
144
+ if (v) {
145
+ loading.value = true
146
+ const data = await fetch('/api/commands').then((r) => r.json())
147
+ items.value = data.map((d: any) => ({
148
+ value: d.id,
149
+ label: d.name,
150
+ group: d.category,
151
+ }))
152
+ loading.value = false
153
+ }
154
+ })
155
+ </script>
187
156
 
188
- ### `<Command.Dialog>` Events
157
+ <template>
158
+ <Command.Dialog
159
+ :visible="visible"
160
+ :items="items"
161
+ :loading="loading"
162
+ @update:visible="visible = $event"
163
+ @select="console.log('selected', $event)"
164
+ />
165
+ </template>
166
+ ```
189
167
 
190
- | Event | Payload | Description |
191
- | ---------------- | ----------------- | -------------------------------- |
192
- | `update:visible` | `boolean` | Emitted when visibility changes |
193
- | `select` | `CommandItemData` | Emitted when an item is selected |
168
+ ## ๐Ÿ“– API
194
169
 
195
170
  ### Components
196
171
 
197
172
  | Component | Description |
198
173
  | --------------------- | ---------------------------------------------- |
199
174
  | `<Command.Dialog>` | Modal dialog with mask, transition, focus trap |
200
- | `<Command.Menu>` | Inline command menu (non-modal) |
175
+ | `<Command.Menu>` | Inline command menu (non-modal) with slots |
201
176
  | `<Command.Input>` | Search input with keyboard navigation |
202
- | `<Command.List>` | Scrollable list rendering `groupedItems` |
177
+ | `<Command.List>` | Scrollable list rendering grouped items |
203
178
  | `<Command.Group>` | Group of items with heading |
204
179
  | `<Command.Item>` | Single selectable command item |
205
180
  | `<Command.Empty>` | Shown when no results match |
206
181
  | `<Command.Separator>` | Visual separator |
207
182
  | `<Command.Loading>` | Loading indicator |
208
183
 
184
+ ### `<Command.Dialog>` Props
185
+
186
+ | Prop | Type | Default | Description |
187
+ | --------------- | ------------------- | ------------------------------- | ---------------------------------------------- |
188
+ | `visible` | `boolean` | `false` | Controlled open state |
189
+ | `items` | `CommandItemData[]` | `[]` | Items to display |
190
+ | `searchQuery` | `string` | `''` | Search query (`v-model:searchQuery`) |
191
+ | `value` | `string` | โ€” | Selected item value (`v-model:value`) |
192
+ | `placeholder` | `string` | `'Type a command or search...'` | Input placeholder |
193
+ | `filter` | `FilterFn` | โ€” | Custom filter function |
194
+ | `loading` | `boolean` | `false` | Show loading state |
195
+ | `autoFocus` | `boolean` | `true` | Auto-focus input on open |
196
+ | `closeOnSelect` | `boolean` | `true` | Close dialog after selection |
197
+ | `shouldFilter` | `boolean` | `true` | When `false`, skip built-in filtering |
198
+ | `loop` | `boolean` | `true` | When `false`, keyboard nav stops at boundaries |
199
+ | `label` | `string` | `'Command menu'` | `aria-label` for the dialog |
200
+
201
+ ### `<Command.Dialog>` Events
202
+
203
+ | Event | Payload | Description |
204
+ | -------------------- | ----------------- | --------------------------------- |
205
+ | `update:visible` | `boolean` | Emitted when visibility changes |
206
+ | `update:searchQuery` | `string` | Emitted when search query changes |
207
+ | `update:value` | `string` | Emitted when an item is selected |
208
+ | `select` | `CommandItemData` | Emitted when an item is selected |
209
+
209
210
  ### `CommandItemData`
210
211
 
211
212
  ```ts
212
213
  interface CommandItemData {
214
+ /** Unique value for this item */
213
215
  value: string
216
+ /** Display label (falls back to value) */
214
217
  label?: string
218
+ /** Optional keywords for search matching */
215
219
  keywords?: string[]
220
+ /** Optional shortcut display (e.g. "โŒ˜S") */
216
221
  shortcut?: string
222
+ /** Group this item belongs to */
217
223
  group?: string
224
+ /** Disabled state */
218
225
  disabled?: boolean
219
- icon?: Component
226
+ /** Custom render icon or prefix */
227
+ icon?: Component | VNode | (() => VNode)
228
+ /** Callback when item is selected */
220
229
  onSelect?: (item: CommandItemData) => void
221
230
  }
222
231
  ```
223
232
 
224
233
  ### `useCommandMenu()` Composable
225
234
 
235
+ Use the composable for programmatic control outside of `Command.Dialog` / `Command.Menu`.
236
+
226
237
  ```ts
227
238
  import { useCommandMenu } from 'vue-command-kit'
228
239
  import type { UseCommandMenuReturn, FilterFn } from 'vue-command-kit'
@@ -234,6 +245,38 @@ menu.close()
234
245
  menu.toggle()
235
246
  ```
236
247
 
248
+ Returns:
249
+
250
+ | Return | Type | Description |
251
+ | ----------------- | ------------------------- | ------------------------------------------------ |
252
+ | `visible` | `Ref<boolean>` | Open state |
253
+ | `searchQuery` | `Ref<string>` | Current search query |
254
+ | `activeIndex` | `Ref<number>` | Currently highlighted item index |
255
+ | `items` | `Ref<CommandItemData[]>` | Raw item list |
256
+ | `filteredItems` | `ComputedRef<...>` | Items after filtering |
257
+ | `groupedItems` | `ComputedRef<...>` | Filtered items grouped by `group` field |
258
+ | `open()` | `() => void` | Open the menu |
259
+ | `close()` | `() => void` | Close and reset search |
260
+ | `toggle()` | `() => void` | Toggle open state |
261
+ | `selectNext()` | `() => void` | Move active index down |
262
+ | `selectPrev()` | `() => void` | Move active index up |
263
+ | `selectCurrent()` | `() => void` | Select currently active item |
264
+ | `defaultFilter()` | `(items, query) => items` | Default filter implementation (case-insensitive) |
265
+
266
+ ## ๐Ÿ“ฆ Bundle Size
267
+
268
+ | Format | Size |
269
+ | ------- | ---------- |
270
+ | ESM | 11.8 kB |
271
+ | UMD | 11.8 kB |
272
+ | Gzipped | **3.4 kB** |
273
+
274
+ Zero runtime dependencies. Peer dependency only on `vue ^3.4.0`.
275
+
276
+ ## ๐Ÿค Acknowledgements
277
+
278
+ vue-cmdk is inspired by [`cmdk`](https://github.com/pacocoursey/cmdk) by [@pacocoursey](https://github.com/pacocoursey) โ€” a fast, unstyled command menu for React.
279
+
237
280
  ### Keyboard
238
281
 
239
282
  | Key | Action |
@@ -245,17 +288,70 @@ menu.toggle()
245
288
 
246
289
  ## ๐Ÿค Contributing
247
290
 
291
+ ### Prerequisites
292
+
293
+ - **Node.js** >= 22.13
294
+ - **pnpm** >= 11.x
295
+
296
+ ### Setup
297
+
248
298
  ```bash
249
- # dev
299
+ # Clone the repo
300
+ git clone https://github.com/yvng-jie/vue-cmdk.git
301
+ cd vue-cmdk
302
+
303
+ # Install dependencies
304
+ pnpm install
305
+
306
+ # Start demo dev server
250
307
  pnpm dev
308
+ ```
309
+
310
+ ### Scripts
251
311
 
252
- # type check
253
- pnpm typecheck
312
+ | Command | Description |
313
+ | ----------------- | ----------------------------------------- |
314
+ | `pnpm dev` | Start demo dev server at `localhost:5173` |
315
+ | `pnpm build` | Build the library + type declarations |
316
+ | `pnpm typecheck` | Run TypeScript type checking |
317
+ | `pnpm preview` | Preview production build |
318
+ | `pnpm build:demo` | Build demo site to `dist-demo/` |
254
319
 
255
- # build
256
- pnpm build
320
+ ### Project Structure
321
+
322
+ ```
323
+ src/
324
+ โ”œโ”€โ”€ useCommandMenu.ts # core composable (state, filter, shortcuts)
325
+ โ”œโ”€โ”€ useCommandRoot.ts # shared composable (provide/inject wiring)
326
+ โ”œโ”€โ”€ types.ts # TypeScript type definitions
327
+ โ”œโ”€โ”€ injectionKeys.ts # provide/inject keys
328
+ โ”œโ”€โ”€ utils/
329
+ โ”‚ โ””โ”€โ”€ injectStrict.ts # type-safe inject helper
330
+ โ”œโ”€โ”€ CommandMenu.vue # inline command menu
331
+ โ”œโ”€โ”€ CommandDialog.vue # modal dialog command palette
332
+ โ”œโ”€โ”€ CommandInput.vue # search input
333
+ โ”œโ”€โ”€ CommandList.vue # scrollable filtered list
334
+ โ”œโ”€โ”€ CommandGroup.vue # group with heading
335
+ โ”œโ”€โ”€ CommandItem.vue # single selectable item
336
+ โ”œโ”€โ”€ CommandEmpty.vue # empty state
337
+ โ”œโ”€โ”€ CommandSeparator.vue # visual separator
338
+ โ”œโ”€โ”€ CommandLoading.vue # loading indicator
339
+ โ”œโ”€โ”€ index.ts # barrel exports
340
+ โ””โ”€โ”€ env.d.ts # type shims
341
+ demo/
342
+ โ”œโ”€โ”€ App.vue # demo application
343
+ โ”œโ”€โ”€ main.ts # demo entry
344
+ โ””โ”€โ”€ style.css # demo styles
257
345
  ```
258
346
 
347
+ ### Pull Request Process
348
+
349
+ 1. Fork the repo and create a feature branch from `main`
350
+ 2. Make your changes and run `pnpm typecheck && pnpm build`
351
+ 3. Test your changes with `pnpm dev` (demo app)
352
+ 4. Update `CHANGELOG.md` with a description of your changes
353
+ 5. Submit a PR with a clear description of what and why
354
+
259
355
  PRs and issues are welcome!
260
356
 
261
357
  ## ๐Ÿ“„ License
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.d.ts CHANGED
@@ -6,15 +6,15 @@ export type * from './types'
6
6
  import type { Component } from 'vue'
7
7
 
8
8
  export const Command: {
9
- Menu: Component
10
- Dialog: Component
11
- Input: Component
12
- List: Component
13
- Group: Component
14
- Item: Component
15
- Empty: Component
16
- Separator: Component
17
- Loading: Component
9
+ CommandMenu: Component
10
+ CommandDialog: Component
11
+ CommandInput: Component
12
+ CommandList: Component
13
+ CommandGroup: Component
14
+ CommandItem: Component
15
+ CommandEmpty: Component
16
+ CommandSeparator: Component
17
+ CommandLoading: Component
18
18
  }
19
19
  export const CommandMenu: Component
20
20
  export const CommandDialog: Component
@@ -5,3 +5,4 @@ export declare const CMDK_STATE: InjectionKey<UseCommandMenuReturn>;
5
5
  export declare const CMDK_LOADING: InjectionKey<() => boolean>;
6
6
  export declare const CMDK_CLOSE_ON_SELECT: InjectionKey<() => boolean>;
7
7
  export declare const CMDK_SELECT_HANDLER: InjectionKey<(item: CommandItemData) => void>;
8
+ export declare const CMDK_ITEM_INDEX_MAP: InjectionKey<() => Map<string, number>>;
package/dist/types.d.ts CHANGED
@@ -34,13 +34,27 @@ export interface CommandRootProps {
34
34
  closeOnSelect?: boolean;
35
35
  /** Whether the menu is loading */
36
36
  loading?: boolean;
37
+ /** aria-label for the dialog/menu */
38
+ label?: string;
39
+ /** When false, skip built-in filtering and show all items (custom filter still applies) */
40
+ shouldFilter?: boolean;
41
+ /** When false, keyboard navigation stops at boundaries instead of wrapping around */
42
+ loop?: boolean;
43
+ /** Controlled selected item value (v-model) */
44
+ value?: string;
37
45
  }
38
46
  /** Emitted events */
39
47
  export interface CommandRootEmits {
40
48
  (e: 'update:visible', value: boolean): void;
41
49
  (e: 'update:searchQuery', value: string): void;
50
+ (e: 'update:value', value: string): void;
42
51
  (e: 'select', item: CommandItemData): void;
43
52
  }
53
+ /** Props for CommandDialog โ€” extends shared root props with items array */
54
+ export interface CommandDialogProps extends CommandRootProps {
55
+ /** Command items to display */
56
+ items?: CommandItemData[];
57
+ }
44
58
  /** Group definition */
45
59
  export interface CommandGroupData {
46
60
  heading: string;
@@ -1,10 +1,19 @@
1
1
  import { Ref, ComputedRef } from 'vue';
2
2
  import { CommandItemData, CommandGroupData } from './types';
3
3
  export type FilterFn = (items: CommandItemData[], query: string) => CommandItemData[] | null;
4
+ export interface UseCommandMenuOptions {
5
+ /** Custom filter function */
6
+ filter?: FilterFn;
7
+ /** When false, skip built-in filtering */
8
+ shouldFilter?: boolean;
9
+ /** When false, keyboard navigation stops at boundaries */
10
+ loop?: boolean;
11
+ }
4
12
  export interface UseCommandMenuReturn {
5
13
  visible: Ref<boolean>;
6
14
  searchQuery: Ref<string>;
7
15
  activeIndex: Ref<number>;
16
+ selectedValue: Ref<string | undefined>;
8
17
  items: Ref<CommandItemData[]>;
9
18
  toggle: () => void;
10
19
  open: () => void;
@@ -17,4 +26,4 @@ export interface UseCommandMenuReturn {
17
26
  selectPrev: () => void;
18
27
  selectCurrent: () => void;
19
28
  }
20
- export declare function useCommandMenu(customFilter?: FilterFn, onItemSelect?: (item: CommandItemData) => void): UseCommandMenuReturn;
29
+ export declare function useCommandMenu(optionsOrFilter?: UseCommandMenuOptions | FilterFn, onItemSelect?: (item: CommandItemData) => void): UseCommandMenuReturn;
@@ -0,0 +1,22 @@
1
+ import { CommandItemData, CommandRootEmits } from './types';
2
+ import { useCommandMenu, FilterFn } from './useCommandMenu';
3
+ export interface UseCommandRootOptions {
4
+ filter?: FilterFn;
5
+ loading?: boolean;
6
+ closeOnSelect?: boolean;
7
+ shouldFilter?: boolean;
8
+ loop?: boolean;
9
+ value?: string;
10
+ }
11
+ export interface UseCommandRootReturn {
12
+ state: ReturnType<typeof useCommandMenu>;
13
+ handleSelect: (item: CommandItemData) => void;
14
+ }
15
+ /**
16
+ * Shared composable for root command components (Menu, Dialog).
17
+ *
18
+ * - Creates the command menu state
19
+ * - Provides all injection keys so child components can consume them
20
+ * - Returns the state and a select handler
21
+ */
22
+ export declare function useCommandRoot(options: UseCommandRootOptions, emit: CommandRootEmits, onItemSelect?: (item: CommandItemData) => void): UseCommandRootReturn;
@@ -0,0 +1,8 @@
1
+ import { InjectionKey } from 'vue';
2
+ /**
3
+ * Type-safe inject that throws a clear error when a required value is missing.
4
+ *
5
+ * Use this instead of `inject(key)!` to get early feedback when a child
6
+ * component is used outside of `<CommandMenu>` or `<CommandDialog>`.
7
+ */
8
+ export declare function injectStrict<T>(key: InjectionKey<T>, componentName: string): T;