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 +210 -114
- package/dist/__tests__/CommandDialog.test.d.ts +1 -0
- package/dist/__tests__/CommandInput.test.d.ts +1 -0
- package/dist/__tests__/CommandItem.test.d.ts +1 -0
- package/dist/__tests__/CommandList.test.d.ts +1 -0
- package/dist/__tests__/injectStrict.test.d.ts +1 -0
- package/dist/__tests__/parseShortcut.test.d.ts +1 -0
- package/dist/__tests__/useCommandMenu.test.d.ts +1 -0
- package/dist/__tests__/useCommandRoot.test.d.ts +1 -0
- package/dist/index.d.ts +9 -9
- package/dist/injectionKeys.d.ts +1 -0
- package/dist/types.d.ts +14 -0
- package/dist/useCommandMenu.d.ts +10 -1
- package/dist/useCommandRoot.d.ts +22 -0
- package/dist/utils/injectStrict.d.ts +8 -0
- package/dist/vue-cmdk.js +374 -300
- package/dist/vue-cmdk.umd.cjs +1 -1
- package/package.json +47 -10
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
|
-
|
|
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.
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
43
|
+
import { ref } from 'vue'
|
|
44
|
+
import { Command } from 'vue-command-kit'
|
|
45
|
+
import type { CommandItemData } from 'vue-command-kit'
|
|
112
46
|
|
|
113
|
-
|
|
47
|
+
const visible = ref(false)
|
|
114
48
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
121
|
-
|
|
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
|
-
|
|
92
|
+
import { Command } from 'vue-command-kit'
|
|
159
93
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
:
|
|
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
|
-
|
|
131
|
+
### Async data + loading
|
|
175
132
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
#
|
|
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
|
-
|
|
253
|
-
|
|
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
|
-
|
|
256
|
-
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
package/dist/injectionKeys.d.ts
CHANGED
|
@@ -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;
|
package/dist/useCommandMenu.d.ts
CHANGED
|
@@ -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(
|
|
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;
|