vue-command-kit 0.1.0
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/LICENSE +9 -0
- package/README.md +269 -0
- package/dist/index.d.ts +27 -0
- package/dist/injectionKeys.d.ts +7 -0
- package/dist/types.d.ts +48 -0
- package/dist/useCommandMenu.d.ts +20 -0
- package/dist/vue-cmdk.js +486 -0
- package/dist/vue-cmdk.umd.cjs +1 -0
- package/package.json +71 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 yvng-jie
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://img.shields.io/npm/v/vue-command-kit?color=blue&label=version" alt="npm">
|
|
3
|
+
<img src="https://img.shields.io/badge/vue-3.4%2B-brightgreen" alt="vue">
|
|
4
|
+
<img src="https://img.shields.io/badge/license-MIT-blue" alt="license">
|
|
5
|
+
<img src="https://img.shields.io/badge/bundle-3.4kB_gzip-green" alt="size">
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
<h1 align="center">โK โ vue-cmdk</h1>
|
|
9
|
+
<p align="center">
|
|
10
|
+
<b>A fast, composable, unstyled command palette for Vue 3.</b><br>
|
|
11
|
+
Press <kbd>โK</kbd> and take control.
|
|
12
|
+
</p>
|
|
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
|
+
---
|
|
48
|
+
|
|
49
|
+
## โจ Features
|
|
50
|
+
|
|
51
|
+
- **๐งฉ Compound component API** โ `<Command.Dialog>`, `<Command.List>`, `<Command.Item>`, etc.
|
|
52
|
+
- **๐ Unstyled** โ Bring your own CSS, zero opinions, full design control
|
|
53
|
+
- **๐ Built-in search** โ Fast case-insensitive filtering with keyword matching
|
|
54
|
+
- **โจ๏ธ Keyboard-first** โ Arrow keys, Enter, Escape โ all built-in, no config needed
|
|
55
|
+
- **๐ฆ Tiny** โ 3.4 kB gzipped, **zero runtime dependencies** (peer: `vue` only)
|
|
56
|
+
- **๐ฏ TypeScript** โ Full type inference and declaration files
|
|
57
|
+
- **๐ Dynamic items** โ Pass items as a reactive array, swap anytime
|
|
58
|
+
- **๐ Custom filter** โ Provide your own filter function
|
|
59
|
+
- **โฟ Accessible** โ ARIA attributes, focus trap, `aria-live` region
|
|
60
|
+
|
|
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
|
+
## ๐ Install
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
npm install vue-command-kit
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Quick Start
|
|
104
|
+
|
|
105
|
+
### Simple โ items prop
|
|
106
|
+
|
|
107
|
+
```vue
|
|
108
|
+
<script setup lang="ts">
|
|
109
|
+
import { ref } from 'vue'
|
|
110
|
+
import { Command } from 'vue-command-kit'
|
|
111
|
+
import type { CommandItemData } from 'vue-command-kit'
|
|
112
|
+
|
|
113
|
+
const visible = ref(false)
|
|
114
|
+
|
|
115
|
+
const items: CommandItemData[] = [
|
|
116
|
+
{ value: 'settings', label: 'Open settings', shortcut: 'โ,' },
|
|
117
|
+
{ value: 'home', label: 'Go to home', shortcut: 'โH' },
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
function onSelect(item: CommandItemData) {
|
|
121
|
+
console.log('selected:', item.value)
|
|
122
|
+
}
|
|
123
|
+
</script>
|
|
124
|
+
|
|
125
|
+
<template>
|
|
126
|
+
<button @click="visible = true">Open (โK)</button>
|
|
127
|
+
|
|
128
|
+
<Command.Dialog
|
|
129
|
+
:visible="visible"
|
|
130
|
+
:items="items"
|
|
131
|
+
@update:visible="visible = $event"
|
|
132
|
+
@select="onSelect"
|
|
133
|
+
/>
|
|
134
|
+
</template>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Advanced โ custom slot content
|
|
138
|
+
|
|
139
|
+
```vue
|
|
140
|
+
<Command.Dialog :visible="visible" @update:visible="visible = $event">
|
|
141
|
+
<template #header>
|
|
142
|
+
<Command.Input placeholder="Search..." />
|
|
143
|
+
</template>
|
|
144
|
+
<template #body>
|
|
145
|
+
<Command.List>
|
|
146
|
+
<Command.Group heading="Favorites">
|
|
147
|
+
<Command.Item value="home" label="Home" />
|
|
148
|
+
</Command.Group>
|
|
149
|
+
</Command.List>
|
|
150
|
+
</template>
|
|
151
|
+
</Command.Dialog>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### With custom filter
|
|
155
|
+
|
|
156
|
+
```vue
|
|
157
|
+
<script setup lang="ts">
|
|
158
|
+
import { Command } from 'vue-command-kit'
|
|
159
|
+
|
|
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
|
+
}
|
|
164
|
+
</script>
|
|
165
|
+
|
|
166
|
+
<template>
|
|
167
|
+
<Command.Dialog
|
|
168
|
+
:filter="myFilter"
|
|
169
|
+
...
|
|
170
|
+
/>
|
|
171
|
+
</template>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## ๐ API
|
|
175
|
+
|
|
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 |
|
|
187
|
+
|
|
188
|
+
### `<Command.Dialog>` Events
|
|
189
|
+
|
|
190
|
+
| Event | Payload | Description |
|
|
191
|
+
| ---------------- | ----------------- | -------------------------------- |
|
|
192
|
+
| `update:visible` | `boolean` | Emitted when visibility changes |
|
|
193
|
+
| `select` | `CommandItemData` | Emitted when an item is selected |
|
|
194
|
+
|
|
195
|
+
### Components
|
|
196
|
+
|
|
197
|
+
| Component | Description |
|
|
198
|
+
| --------------------- | ---------------------------------------------- |
|
|
199
|
+
| `<Command.Dialog>` | Modal dialog with mask, transition, focus trap |
|
|
200
|
+
| `<Command.Menu>` | Inline command menu (non-modal) |
|
|
201
|
+
| `<Command.Input>` | Search input with keyboard navigation |
|
|
202
|
+
| `<Command.List>` | Scrollable list rendering `groupedItems` |
|
|
203
|
+
| `<Command.Group>` | Group of items with heading |
|
|
204
|
+
| `<Command.Item>` | Single selectable command item |
|
|
205
|
+
| `<Command.Empty>` | Shown when no results match |
|
|
206
|
+
| `<Command.Separator>` | Visual separator |
|
|
207
|
+
| `<Command.Loading>` | Loading indicator |
|
|
208
|
+
|
|
209
|
+
### `CommandItemData`
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
interface CommandItemData {
|
|
213
|
+
value: string
|
|
214
|
+
label?: string
|
|
215
|
+
keywords?: string[]
|
|
216
|
+
shortcut?: string
|
|
217
|
+
group?: string
|
|
218
|
+
disabled?: boolean
|
|
219
|
+
icon?: Component
|
|
220
|
+
onSelect?: (item: CommandItemData) => void
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### `useCommandMenu()` Composable
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
import { useCommandMenu } from 'vue-command-kit'
|
|
228
|
+
import type { UseCommandMenuReturn, FilterFn } from 'vue-command-kit'
|
|
229
|
+
|
|
230
|
+
const menu: UseCommandMenuReturn = useCommandMenu(customFilter?)
|
|
231
|
+
menu.items.value = [...]
|
|
232
|
+
menu.open()
|
|
233
|
+
menu.close()
|
|
234
|
+
menu.toggle()
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Keyboard
|
|
238
|
+
|
|
239
|
+
| Key | Action |
|
|
240
|
+
| -------- | -------------------------------------- |
|
|
241
|
+
| `โ` `โ` | Navigate items (wraps around) |
|
|
242
|
+
| `Enter` | Select current item |
|
|
243
|
+
| `Escape` | Close dialog |
|
|
244
|
+
| `Tab` | Moves focus within dialog (focus trap) |
|
|
245
|
+
|
|
246
|
+
## ๐ค Contributing
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
# dev
|
|
250
|
+
pnpm dev
|
|
251
|
+
|
|
252
|
+
# type check
|
|
253
|
+
pnpm typecheck
|
|
254
|
+
|
|
255
|
+
# build
|
|
256
|
+
pnpm build
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
PRs and issues are welcome!
|
|
260
|
+
|
|
261
|
+
## ๐ License
|
|
262
|
+
|
|
263
|
+
MIT ยฉ [yvng-jie](https://github.com/yvng-jie)
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
<p align="center">
|
|
268
|
+
<sub>Made with โค๏ธ for the Vue community. Thanks to <a href="https://github.com/xiaoluoboding">@xiaoluoboding</a> for the original <a href="https://github.com/xiaoluoboding/vue-command-palette">vue-command-palette</a>.</sub>
|
|
269
|
+
</p>
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// vue-cmdk โ type definitions
|
|
2
|
+
export { useCommandMenu } from './useCommandMenu'
|
|
3
|
+
export type { FilterFn, UseCommandMenuReturn } from './useCommandMenu'
|
|
4
|
+
export type * from './types'
|
|
5
|
+
|
|
6
|
+
import type { Component } from 'vue'
|
|
7
|
+
|
|
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
|
|
18
|
+
}
|
|
19
|
+
export const CommandMenu: Component
|
|
20
|
+
export const CommandDialog: Component
|
|
21
|
+
export const CommandInput: Component
|
|
22
|
+
export const CommandList: Component
|
|
23
|
+
export const CommandGroup: Component
|
|
24
|
+
export const CommandItem: Component
|
|
25
|
+
export const CommandEmpty: Component
|
|
26
|
+
export const CommandSeparator: Component
|
|
27
|
+
export const CommandLoading: Component
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { InjectionKey } from 'vue';
|
|
2
|
+
import { CommandItemData } from './types';
|
|
3
|
+
import { UseCommandMenuReturn } from './useCommandMenu';
|
|
4
|
+
export declare const CMDK_STATE: InjectionKey<UseCommandMenuReturn>;
|
|
5
|
+
export declare const CMDK_LOADING: InjectionKey<() => boolean>;
|
|
6
|
+
export declare const CMDK_CLOSE_ON_SELECT: InjectionKey<() => boolean>;
|
|
7
|
+
export declare const CMDK_SELECT_HANDLER: InjectionKey<(item: CommandItemData) => void>;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Component, VNode } from 'vue';
|
|
2
|
+
/** A single command item */
|
|
3
|
+
export interface CommandItemData {
|
|
4
|
+
/** Unique value for this item */
|
|
5
|
+
value: string;
|
|
6
|
+
/** Display label (falls back to value) */
|
|
7
|
+
label?: string;
|
|
8
|
+
/** Optional keywords for search matching */
|
|
9
|
+
keywords?: string[];
|
|
10
|
+
/** Optional shortcut display (e.g. "โS") */
|
|
11
|
+
shortcut?: string;
|
|
12
|
+
/** Group this item belongs to */
|
|
13
|
+
group?: string;
|
|
14
|
+
/** Disabled state */
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
/** Custom render icon or prefix */
|
|
17
|
+
icon?: Component | VNode | (() => VNode);
|
|
18
|
+
/** Callback when item is selected */
|
|
19
|
+
onSelect?: (item: CommandItemData) => void;
|
|
20
|
+
}
|
|
21
|
+
/** Props shared across command components */
|
|
22
|
+
export interface CommandRootProps {
|
|
23
|
+
/** Controlled open state */
|
|
24
|
+
visible?: boolean;
|
|
25
|
+
/** Search query (v-model) */
|
|
26
|
+
searchQuery?: string;
|
|
27
|
+
/** Placeholder for search input */
|
|
28
|
+
placeholder?: string;
|
|
29
|
+
/** Custom filter function. Return items to display, or null to use default filter */
|
|
30
|
+
filter?: (items: CommandItemData[], query: string) => CommandItemData[];
|
|
31
|
+
/** Auto-focus input on open */
|
|
32
|
+
autoFocus?: boolean;
|
|
33
|
+
/** Close after selecting an item */
|
|
34
|
+
closeOnSelect?: boolean;
|
|
35
|
+
/** Whether the menu is loading */
|
|
36
|
+
loading?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/** Emitted events */
|
|
39
|
+
export interface CommandRootEmits {
|
|
40
|
+
(e: 'update:visible', value: boolean): void;
|
|
41
|
+
(e: 'update:searchQuery', value: string): void;
|
|
42
|
+
(e: 'select', item: CommandItemData): void;
|
|
43
|
+
}
|
|
44
|
+
/** Group definition */
|
|
45
|
+
export interface CommandGroupData {
|
|
46
|
+
heading: string;
|
|
47
|
+
items: CommandItemData[];
|
|
48
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Ref, ComputedRef } from 'vue';
|
|
2
|
+
import { CommandItemData, CommandGroupData } from './types';
|
|
3
|
+
export type FilterFn = (items: CommandItemData[], query: string) => CommandItemData[] | null;
|
|
4
|
+
export interface UseCommandMenuReturn {
|
|
5
|
+
visible: Ref<boolean>;
|
|
6
|
+
searchQuery: Ref<string>;
|
|
7
|
+
activeIndex: Ref<number>;
|
|
8
|
+
items: Ref<CommandItemData[]>;
|
|
9
|
+
toggle: () => void;
|
|
10
|
+
open: () => void;
|
|
11
|
+
close: () => void;
|
|
12
|
+
defaultFilter: (allItems: CommandItemData[], query: string) => CommandItemData[];
|
|
13
|
+
groupItems: (filtered: CommandItemData[]) => CommandGroupData[];
|
|
14
|
+
filteredItems: ComputedRef<CommandItemData[]>;
|
|
15
|
+
groupedItems: ComputedRef<CommandGroupData[]>;
|
|
16
|
+
selectNext: () => void;
|
|
17
|
+
selectPrev: () => void;
|
|
18
|
+
selectCurrent: () => void;
|
|
19
|
+
}
|
|
20
|
+
export declare function useCommandMenu(customFilter?: FilterFn, onItemSelect?: (item: CommandItemData) => void): UseCommandMenuReturn;
|
package/dist/vue-cmdk.js
ADDED
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
import { ref as O, computed as E, watch as C, onUnmounted as oe, defineComponent as I, provide as g, openBlock as u, createElementBlock as i, renderSlot as S, unref as m, inject as w, createElementVNode as k, createCommentVNode as $, toDisplayString as B, normalizeClass as ae, createBlock as T, resolveDynamicComponent as ne, Fragment as L, createTextVNode as N, createVNode as D, renderList as V, withCtx as j, nextTick as le, Teleport as se, Transition as re } from "vue";
|
|
2
|
+
const ce = {
|
|
3
|
+
"โ": "metaKey",
|
|
4
|
+
"โ": "ctrlKey",
|
|
5
|
+
"โฅ": "altKey",
|
|
6
|
+
"โง": "shiftKey"
|
|
7
|
+
};
|
|
8
|
+
function ue(o) {
|
|
9
|
+
let e = o;
|
|
10
|
+
const s = {};
|
|
11
|
+
let n = !0;
|
|
12
|
+
for (; n && e.length > 0; ) {
|
|
13
|
+
n = !1;
|
|
14
|
+
for (const [r, t] of Object.entries(ce))
|
|
15
|
+
if (e.startsWith(r)) {
|
|
16
|
+
s[t] = !0, e = e.slice(r.length), n = !0;
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return e ? { ...s, key: e.toLowerCase() } : null;
|
|
21
|
+
}
|
|
22
|
+
function ie(o, e) {
|
|
23
|
+
return e ? o.key.toLowerCase() === e.key && !!o.metaKey == !!e.metaKey && !!o.ctrlKey == !!e.ctrlKey && !!o.altKey == !!e.altKey && !!o.shiftKey == !!e.shiftKey : !1;
|
|
24
|
+
}
|
|
25
|
+
function q(o, e) {
|
|
26
|
+
const s = O(!1), n = O(""), r = O(0), t = O([]), v = () => {
|
|
27
|
+
s.value = !s.value;
|
|
28
|
+
}, a = () => {
|
|
29
|
+
s.value = !0, r.value = 0;
|
|
30
|
+
}, l = () => {
|
|
31
|
+
s.value = !1, n.value = "";
|
|
32
|
+
};
|
|
33
|
+
function h(c, f) {
|
|
34
|
+
if (!f.trim()) return c;
|
|
35
|
+
const d = f.toLowerCase();
|
|
36
|
+
return c.filter((_) => {
|
|
37
|
+
var b, G;
|
|
38
|
+
return !!(_.value.toLowerCase().includes(d) || (b = _.label) != null && b.toLowerCase().includes(d) || (G = _.keywords) != null && G.some((te) => te.toLowerCase().includes(d)));
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function p(c) {
|
|
42
|
+
const f = /* @__PURE__ */ new Map();
|
|
43
|
+
for (const _ of c) {
|
|
44
|
+
const b = _.group || "__ungrouped__";
|
|
45
|
+
f.has(b) || f.set(b, []), f.get(b).push(_);
|
|
46
|
+
}
|
|
47
|
+
const d = [];
|
|
48
|
+
f.has("__ungrouped__") && (d.push({ heading: "", items: f.get("__ungrouped__") }), f.delete("__ungrouped__"));
|
|
49
|
+
for (const [_, b] of f)
|
|
50
|
+
d.push({ heading: _, items: b });
|
|
51
|
+
return d;
|
|
52
|
+
}
|
|
53
|
+
const y = E(() => {
|
|
54
|
+
if (o) {
|
|
55
|
+
const c = o(t.value, n.value);
|
|
56
|
+
if (c !== null) return c;
|
|
57
|
+
}
|
|
58
|
+
return h(t.value, n.value);
|
|
59
|
+
}), Q = E(() => p(y.value));
|
|
60
|
+
function Y() {
|
|
61
|
+
const c = y.value.length;
|
|
62
|
+
c !== 0 && (r.value = (r.value + 1) % c);
|
|
63
|
+
}
|
|
64
|
+
function Z() {
|
|
65
|
+
const c = y.value.length;
|
|
66
|
+
c !== 0 && (r.value = (r.value - 1 + c) % c);
|
|
67
|
+
}
|
|
68
|
+
function ee() {
|
|
69
|
+
var f;
|
|
70
|
+
const c = y.value[r.value];
|
|
71
|
+
c && !c.disabled && ((f = c.onSelect) == null || f.call(c, c), l());
|
|
72
|
+
}
|
|
73
|
+
function P(c) {
|
|
74
|
+
var f;
|
|
75
|
+
for (const d of t.value) {
|
|
76
|
+
if (d.disabled || !d.shortcut) continue;
|
|
77
|
+
const _ = ue(d.shortcut);
|
|
78
|
+
if (ie(c, _)) {
|
|
79
|
+
c.preventDefault(), c.stopPropagation(), (f = d.onSelect) == null || f.call(d, d), e == null || e(d), l();
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
let x = null;
|
|
85
|
+
return C(
|
|
86
|
+
t,
|
|
87
|
+
(c) => {
|
|
88
|
+
x && (x(), x = null), c.some((d) => d.shortcut) && typeof window < "u" && (window.addEventListener("keydown", P), x = () => window.removeEventListener("keydown", P));
|
|
89
|
+
},
|
|
90
|
+
{ immediate: !0, flush: "sync" }
|
|
91
|
+
), oe(() => {
|
|
92
|
+
x && x();
|
|
93
|
+
}), {
|
|
94
|
+
visible: s,
|
|
95
|
+
searchQuery: n,
|
|
96
|
+
activeIndex: r,
|
|
97
|
+
items: t,
|
|
98
|
+
toggle: v,
|
|
99
|
+
open: a,
|
|
100
|
+
close: l,
|
|
101
|
+
defaultFilter: h,
|
|
102
|
+
groupItems: p,
|
|
103
|
+
filteredItems: y,
|
|
104
|
+
groupedItems: Q,
|
|
105
|
+
selectNext: Y,
|
|
106
|
+
selectPrev: Z,
|
|
107
|
+
selectCurrent: ee
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const K = Symbol("cmdk-state"), A = Symbol("cmdk-loading"), F = Symbol("cmdk-close-on-select"), M = Symbol("cmdk-select-handler"), de = {
|
|
111
|
+
"data-cmdk-root": "",
|
|
112
|
+
role: "combobox",
|
|
113
|
+
"aria-expanded": "true",
|
|
114
|
+
"aria-haspopup": "listbox"
|
|
115
|
+
}, fe = /* @__PURE__ */ I({
|
|
116
|
+
__name: "CommandMenu",
|
|
117
|
+
props: {
|
|
118
|
+
visible: { type: Boolean },
|
|
119
|
+
searchQuery: {},
|
|
120
|
+
placeholder: { default: "Type a command or search..." },
|
|
121
|
+
filter: {},
|
|
122
|
+
autoFocus: { type: Boolean, default: !0 },
|
|
123
|
+
closeOnSelect: { type: Boolean, default: !0 },
|
|
124
|
+
loading: { type: Boolean }
|
|
125
|
+
},
|
|
126
|
+
emits: ["update:visible", "update:searchQuery", "select"],
|
|
127
|
+
setup(o, { expose: e, emit: s }) {
|
|
128
|
+
const n = o, r = s, t = q(n.filter, (a) => {
|
|
129
|
+
r("select", a), n.closeOnSelect && t.close();
|
|
130
|
+
});
|
|
131
|
+
t.visible.value = n.visible ?? !0, t.searchQuery.value = n.searchQuery ?? "", g(K, t), g("cmdk-filter", n.filter), g(A, () => n.loading ?? !1), g(F, () => n.closeOnSelect), g(M, (a) => {
|
|
132
|
+
r("select", a), n.closeOnSelect && t.close();
|
|
133
|
+
}), C(
|
|
134
|
+
() => n.visible,
|
|
135
|
+
(a) => {
|
|
136
|
+
a !== void 0 && (t.visible.value = a);
|
|
137
|
+
}
|
|
138
|
+
), C(t.visible, (a) => r("update:visible", a)), C(
|
|
139
|
+
() => n.searchQuery,
|
|
140
|
+
(a) => {
|
|
141
|
+
a !== void 0 && (t.searchQuery.value = a);
|
|
142
|
+
}
|
|
143
|
+
), C(t.searchQuery, (a) => r("update:searchQuery", a));
|
|
144
|
+
function v(a) {
|
|
145
|
+
r("select", a), n.closeOnSelect && t.close();
|
|
146
|
+
}
|
|
147
|
+
return e({
|
|
148
|
+
open: t.open,
|
|
149
|
+
close: t.close,
|
|
150
|
+
toggle: t.toggle,
|
|
151
|
+
searchQuery: t.searchQuery,
|
|
152
|
+
items: t.items,
|
|
153
|
+
filteredItems: t.filteredItems
|
|
154
|
+
}), (a, l) => (u(), i("div", de, [
|
|
155
|
+
S(a.$slots, "default", {
|
|
156
|
+
items: m(t).items,
|
|
157
|
+
filteredItems: m(t).filteredItems,
|
|
158
|
+
searchQuery: m(t).searchQuery,
|
|
159
|
+
selectNext: m(t).selectNext,
|
|
160
|
+
selectPrev: m(t).selectPrev,
|
|
161
|
+
selectCurrent: m(t).selectCurrent,
|
|
162
|
+
handleSelect: v
|
|
163
|
+
})
|
|
164
|
+
]));
|
|
165
|
+
}
|
|
166
|
+
}), me = ["value", "placeholder", "autofocus"], R = /* @__PURE__ */ I({
|
|
167
|
+
__name: "CommandInput",
|
|
168
|
+
props: {
|
|
169
|
+
placeholder: { default: "Type a command or search..." },
|
|
170
|
+
autoFocus: { type: Boolean, default: !0 }
|
|
171
|
+
},
|
|
172
|
+
setup(o) {
|
|
173
|
+
const e = w(K), s = w(M, () => {
|
|
174
|
+
});
|
|
175
|
+
function n(t) {
|
|
176
|
+
e.searchQuery.value = t.target.value, e.activeIndex.value = 0;
|
|
177
|
+
}
|
|
178
|
+
function r(t) {
|
|
179
|
+
var v;
|
|
180
|
+
if (t.key === "ArrowDown")
|
|
181
|
+
t.preventDefault(), e.selectNext();
|
|
182
|
+
else if (t.key === "ArrowUp")
|
|
183
|
+
t.preventDefault(), e.selectPrev();
|
|
184
|
+
else if (t.key === "Enter") {
|
|
185
|
+
t.preventDefault();
|
|
186
|
+
const a = e.filteredItems.value[e.activeIndex.value];
|
|
187
|
+
a && !a.disabled && ((v = a.onSelect) == null || v.call(a, a), s(a));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return (t, v) => (u(), i("input", {
|
|
191
|
+
"data-cmdk-input": "",
|
|
192
|
+
value: m(e).searchQuery.value,
|
|
193
|
+
placeholder: o.placeholder,
|
|
194
|
+
role: "searchbox",
|
|
195
|
+
autocomplete: "off",
|
|
196
|
+
autocorrect: "off",
|
|
197
|
+
spellcheck: "false",
|
|
198
|
+
autofocus: o.autoFocus,
|
|
199
|
+
onInput: n,
|
|
200
|
+
onKeydown: r
|
|
201
|
+
}, null, 40, me));
|
|
202
|
+
}
|
|
203
|
+
}), ve = {
|
|
204
|
+
key: 0,
|
|
205
|
+
"data-cmdk-empty": ""
|
|
206
|
+
}, U = /* @__PURE__ */ I({
|
|
207
|
+
__name: "CommandEmpty",
|
|
208
|
+
setup(o) {
|
|
209
|
+
const e = w(K);
|
|
210
|
+
return (s, n) => m(e).searchQuery.value && m(e).filteredItems.value.length === 0 ? (u(), i("div", ve, [
|
|
211
|
+
S(s.$slots, "default", {}, () => [
|
|
212
|
+
n[0] || (n[0] = k("span", null, "No results found.", -1))
|
|
213
|
+
])
|
|
214
|
+
])) : $("", !0);
|
|
215
|
+
}
|
|
216
|
+
}), he = {
|
|
217
|
+
key: 0,
|
|
218
|
+
"data-cmdk-loading": ""
|
|
219
|
+
}, z = /* @__PURE__ */ I({
|
|
220
|
+
__name: "CommandLoading",
|
|
221
|
+
props: {
|
|
222
|
+
loading: { type: Boolean, default: !1 }
|
|
223
|
+
},
|
|
224
|
+
setup(o) {
|
|
225
|
+
return (e, s) => o.loading ? (u(), i("div", he, [
|
|
226
|
+
S(e.$slots, "default", {}, () => [
|
|
227
|
+
s[0] || (s[0] = k("span", null, "Loading...", -1))
|
|
228
|
+
])
|
|
229
|
+
])) : $("", !0);
|
|
230
|
+
}
|
|
231
|
+
}), pe = {
|
|
232
|
+
"data-cmdk-group": "",
|
|
233
|
+
role: "group"
|
|
234
|
+
}, ye = {
|
|
235
|
+
key: 0,
|
|
236
|
+
"data-cmdk-group-heading": "",
|
|
237
|
+
role: "presentation"
|
|
238
|
+
}, _e = {
|
|
239
|
+
"data-cmdk-group-items": "",
|
|
240
|
+
role: "group"
|
|
241
|
+
}, H = /* @__PURE__ */ I({
|
|
242
|
+
__name: "CommandGroup",
|
|
243
|
+
props: {
|
|
244
|
+
heading: {}
|
|
245
|
+
},
|
|
246
|
+
setup(o) {
|
|
247
|
+
return (e, s) => (u(), i("div", pe, [
|
|
248
|
+
o.heading ? (u(), i("div", ye, B(o.heading), 1)) : $("", !0),
|
|
249
|
+
k("div", _e, [
|
|
250
|
+
S(e.$slots, "default")
|
|
251
|
+
])
|
|
252
|
+
]));
|
|
253
|
+
}
|
|
254
|
+
}), ge = ["aria-selected", "aria-disabled", "data-value"], ke = {
|
|
255
|
+
key: 0,
|
|
256
|
+
"data-cmdk-item-icon": ""
|
|
257
|
+
}, be = { "data-cmdk-item-label": "" }, Ce = {
|
|
258
|
+
key: 1,
|
|
259
|
+
"data-cmdk-item-shortcut": ""
|
|
260
|
+
}, W = /* @__PURE__ */ I({
|
|
261
|
+
__name: "CommandItem",
|
|
262
|
+
props: {
|
|
263
|
+
value: {},
|
|
264
|
+
label: {},
|
|
265
|
+
keywords: {},
|
|
266
|
+
shortcut: {},
|
|
267
|
+
disabled: { type: Boolean, default: !1 },
|
|
268
|
+
icon: {},
|
|
269
|
+
onSelect: {}
|
|
270
|
+
},
|
|
271
|
+
setup(o) {
|
|
272
|
+
const e = o, s = w(K), n = E(() => ({
|
|
273
|
+
value: e.value,
|
|
274
|
+
label: e.label,
|
|
275
|
+
keywords: e.keywords,
|
|
276
|
+
shortcut: e.shortcut,
|
|
277
|
+
disabled: e.disabled,
|
|
278
|
+
icon: e.icon,
|
|
279
|
+
onSelect: e.onSelect
|
|
280
|
+
})), r = E(() => {
|
|
281
|
+
const l = s.filteredItems.value[s.activeIndex.value];
|
|
282
|
+
return (l == null ? void 0 : l.value) === e.value;
|
|
283
|
+
}), t = w(F, () => !0), v = w(M, () => {
|
|
284
|
+
});
|
|
285
|
+
function a() {
|
|
286
|
+
var l;
|
|
287
|
+
e.disabled || ((l = e.onSelect) == null || l.call(e, n.value), v(n.value), t() && s.close());
|
|
288
|
+
}
|
|
289
|
+
return (l, h) => (u(), i("div", {
|
|
290
|
+
"data-cmdk-item": "",
|
|
291
|
+
role: "option",
|
|
292
|
+
"aria-selected": r.value,
|
|
293
|
+
"aria-disabled": o.disabled,
|
|
294
|
+
"data-value": o.value,
|
|
295
|
+
class: ae({ active: r.value, disabled: o.disabled }),
|
|
296
|
+
onClick: a,
|
|
297
|
+
onPointerenter: h[0] || (h[0] = () => {
|
|
298
|
+
const p = m(s).filteredItems.value.findIndex((y) => y.value === o.value);
|
|
299
|
+
p >= 0 && (m(s).activeIndex.value = p);
|
|
300
|
+
})
|
|
301
|
+
}, [
|
|
302
|
+
S(l.$slots, "default", {
|
|
303
|
+
item: n.value,
|
|
304
|
+
active: r.value
|
|
305
|
+
}, () => [
|
|
306
|
+
o.icon ? (u(), i("span", ke, [
|
|
307
|
+
(u(), T(ne(o.icon)))
|
|
308
|
+
])) : $("", !0),
|
|
309
|
+
k("span", be, B(o.label || o.value), 1),
|
|
310
|
+
o.shortcut ? (u(), i("span", Ce, B(o.shortcut), 1)) : $("", !0)
|
|
311
|
+
])
|
|
312
|
+
], 42, ge));
|
|
313
|
+
}
|
|
314
|
+
}), Se = (o, e) => {
|
|
315
|
+
const s = o.__vccOpts || o;
|
|
316
|
+
for (const [n, r] of e)
|
|
317
|
+
s[n] = r;
|
|
318
|
+
return s;
|
|
319
|
+
}, we = {}, $e = {
|
|
320
|
+
"data-cmdk-separator": "",
|
|
321
|
+
role: "separator"
|
|
322
|
+
};
|
|
323
|
+
function Ie(o, e) {
|
|
324
|
+
return u(), i("div", $e);
|
|
325
|
+
}
|
|
326
|
+
const J = /* @__PURE__ */ Se(we, [["render", Ie]]), xe = {
|
|
327
|
+
"data-cmdk-list": "",
|
|
328
|
+
role: "listbox"
|
|
329
|
+
}, Le = {
|
|
330
|
+
"aria-live": "polite",
|
|
331
|
+
"aria-atomic": "true",
|
|
332
|
+
class: "sr-only"
|
|
333
|
+
}, X = /* @__PURE__ */ I({
|
|
334
|
+
__name: "CommandList",
|
|
335
|
+
setup(o) {
|
|
336
|
+
const e = w(K), s = w(A, () => !1), n = E(() => e.groupedItems.value);
|
|
337
|
+
return (r, t) => (u(), i("div", xe, [
|
|
338
|
+
k("div", Le, [
|
|
339
|
+
m(e).searchQuery.value && m(e).filteredItems.value.length === 0 ? (u(), i(L, { key: 0 }, [
|
|
340
|
+
N("No results found")
|
|
341
|
+
], 64)) : m(s)() ? (u(), i(L, { key: 1 }, [
|
|
342
|
+
N("Loading")
|
|
343
|
+
], 64)) : (u(), i(L, { key: 2 }, [
|
|
344
|
+
N(B(m(e).filteredItems.value.length) + " items", 1)
|
|
345
|
+
], 64))
|
|
346
|
+
]),
|
|
347
|
+
D(U),
|
|
348
|
+
D(z, {
|
|
349
|
+
loading: m(s)()
|
|
350
|
+
}, null, 8, ["loading"]),
|
|
351
|
+
(u(!0), i(L, null, V(n.value, (v) => (u(), i(L, {
|
|
352
|
+
key: v.heading || "__ungrouped__"
|
|
353
|
+
}, [
|
|
354
|
+
D(H, {
|
|
355
|
+
heading: v.heading
|
|
356
|
+
}, {
|
|
357
|
+
default: j(() => [
|
|
358
|
+
(u(!0), i(L, null, V(v.items, (a) => (u(), T(W, {
|
|
359
|
+
key: a.value,
|
|
360
|
+
value: a.value,
|
|
361
|
+
label: a.label,
|
|
362
|
+
shortcut: a.shortcut,
|
|
363
|
+
disabled: a.disabled,
|
|
364
|
+
icon: a.icon,
|
|
365
|
+
"on-select": a.onSelect
|
|
366
|
+
}, null, 8, ["value", "label", "shortcut", "disabled", "icon", "on-select"]))), 128))
|
|
367
|
+
]),
|
|
368
|
+
_: 2
|
|
369
|
+
}, 1032, ["heading"]),
|
|
370
|
+
v !== n.value[n.value.length - 1] ? (u(), T(J, { key: 0 })) : $("", !0)
|
|
371
|
+
], 64))), 128))
|
|
372
|
+
]));
|
|
373
|
+
}
|
|
374
|
+
}), De = { "data-cmdk-dialog-wrapper": "" }, Ke = { "data-cmdk-dialog-header": "" }, Ee = { "data-cmdk-dialog-body": "" }, Qe = {
|
|
375
|
+
key: 0,
|
|
376
|
+
"data-cmdk-dialog-footer": ""
|
|
377
|
+
}, Oe = /* @__PURE__ */ I({
|
|
378
|
+
__name: "CommandDialog",
|
|
379
|
+
props: {
|
|
380
|
+
visible: { type: Boolean, default: !1 },
|
|
381
|
+
placeholder: { default: "Type a command or search..." },
|
|
382
|
+
autoFocus: { type: Boolean, default: !0 },
|
|
383
|
+
closeOnSelect: { type: Boolean, default: !0 },
|
|
384
|
+
items: { default: () => [] },
|
|
385
|
+
filter: {},
|
|
386
|
+
loading: { type: Boolean, default: !1 }
|
|
387
|
+
},
|
|
388
|
+
emits: ["update:visible", "select"],
|
|
389
|
+
setup(o, { emit: e }) {
|
|
390
|
+
const s = o, n = e, r = q(s.filter, t);
|
|
391
|
+
g(K, r), g(A, () => s.loading), g(F, () => s.closeOnSelect), C(
|
|
392
|
+
() => s.items,
|
|
393
|
+
(l) => {
|
|
394
|
+
r.items.value = l;
|
|
395
|
+
},
|
|
396
|
+
{ immediate: !0 }
|
|
397
|
+
), C(
|
|
398
|
+
() => s.visible,
|
|
399
|
+
(l) => {
|
|
400
|
+
r.visible.value = l;
|
|
401
|
+
},
|
|
402
|
+
{ immediate: !0 }
|
|
403
|
+
), C(r.visible, async (l) => {
|
|
404
|
+
if (n("update:visible", l), l) {
|
|
405
|
+
await le();
|
|
406
|
+
const h = document.querySelector("[data-cmdk-input]");
|
|
407
|
+
h == null || h.focus();
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
function t(l) {
|
|
411
|
+
n("select", l), s.closeOnSelect && r.close();
|
|
412
|
+
}
|
|
413
|
+
g(M, t);
|
|
414
|
+
function v(l) {
|
|
415
|
+
l.target === l.currentTarget && r.close();
|
|
416
|
+
}
|
|
417
|
+
function a(l) {
|
|
418
|
+
if (l.key === "Escape" && (l.preventDefault(), r.close()), l.key === "Tab") {
|
|
419
|
+
const p = l.currentTarget.querySelectorAll(
|
|
420
|
+
'input, button, [href], select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
421
|
+
);
|
|
422
|
+
if (p.length === 0) return;
|
|
423
|
+
const y = p[0], Q = p[p.length - 1];
|
|
424
|
+
l.shiftKey ? document.activeElement === y && (l.preventDefault(), Q.focus()) : document.activeElement === Q && (l.preventDefault(), y.focus());
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return (l, h) => (u(), T(se, { to: "body" }, [
|
|
428
|
+
D(re, { name: "cmdk-dialog" }, {
|
|
429
|
+
default: j(() => [
|
|
430
|
+
m(r).visible.value ? (u(), i("div", {
|
|
431
|
+
key: 0,
|
|
432
|
+
"data-cmdk-dialog": "",
|
|
433
|
+
onKeydown: a
|
|
434
|
+
}, [
|
|
435
|
+
k("div", {
|
|
436
|
+
"data-cmdk-dialog-mask": "",
|
|
437
|
+
onClick: v
|
|
438
|
+
}),
|
|
439
|
+
k("div", De, [
|
|
440
|
+
k("div", Ke, [
|
|
441
|
+
S(l.$slots, "header", {}, () => [
|
|
442
|
+
D(R, {
|
|
443
|
+
placeholder: o.placeholder,
|
|
444
|
+
"auto-focus": o.autoFocus
|
|
445
|
+
}, null, 8, ["placeholder", "auto-focus"])
|
|
446
|
+
])
|
|
447
|
+
]),
|
|
448
|
+
k("div", Ee, [
|
|
449
|
+
S(l.$slots, "body", {}, () => [
|
|
450
|
+
D(X)
|
|
451
|
+
])
|
|
452
|
+
]),
|
|
453
|
+
l.$slots.footer ? (u(), i("div", Qe, [
|
|
454
|
+
S(l.$slots, "footer")
|
|
455
|
+
])) : $("", !0)
|
|
456
|
+
])
|
|
457
|
+
], 32)) : $("", !0)
|
|
458
|
+
]),
|
|
459
|
+
_: 3
|
|
460
|
+
})
|
|
461
|
+
]));
|
|
462
|
+
}
|
|
463
|
+
}), Te = {
|
|
464
|
+
Menu: fe,
|
|
465
|
+
Dialog: Oe,
|
|
466
|
+
Input: R,
|
|
467
|
+
List: X,
|
|
468
|
+
Group: H,
|
|
469
|
+
Item: W,
|
|
470
|
+
Empty: U,
|
|
471
|
+
Separator: J,
|
|
472
|
+
Loading: z
|
|
473
|
+
};
|
|
474
|
+
export {
|
|
475
|
+
Te as Command,
|
|
476
|
+
Oe as CommandDialog,
|
|
477
|
+
U as CommandEmpty,
|
|
478
|
+
H as CommandGroup,
|
|
479
|
+
R as CommandInput,
|
|
480
|
+
W as CommandItem,
|
|
481
|
+
X as CommandList,
|
|
482
|
+
z as CommandLoading,
|
|
483
|
+
fe as CommandMenu,
|
|
484
|
+
J as CommandSeparator,
|
|
485
|
+
q as useCommandMenu
|
|
486
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(m,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],e):(m=typeof globalThis<"u"?globalThis:m||self,e(m.VueCmdk={},m.Vue))})(this,(function(m,e){"use strict";const M={"โ":"metaKey","โ":"ctrlKey","โฅ":"altKey","โง":"shiftKey"};function F(n){let t=n;const r={};let l=!0;for(;l&&t.length>0;){l=!1;for(const[s,o]of Object.entries(M))if(t.startsWith(s)){r[o]=!0,t=t.slice(s.length),l=!0;break}}return t?{...r,key:t.toLowerCase()}:null}function j(n,t){return t?n.key.toLowerCase()===t.key&&!!n.metaKey==!!t.metaKey&&!!n.ctrlKey==!!t.ctrlKey&&!!n.altKey==!!t.altKey&&!!n.shiftKey==!!t.shiftKey:!1}function S(n,t){const r=e.ref(!1),l=e.ref(""),s=e.ref(0),o=e.ref([]),f=()=>{r.value=!r.value},a=()=>{r.value=!0,s.value=0},c=()=>{r.value=!1,l.value=""};function p(i,u){if(!u.trim())return i;const d=u.toLowerCase();return i.filter(y=>{var g,Q;return!!(y.value.toLowerCase().includes(d)||(g=y.label)!=null&&g.toLowerCase().includes(d)||(Q=y.keywords)!=null&&Q.some(me=>me.toLowerCase().includes(d)))})}function h(i){const u=new Map;for(const y of i){const g=y.group||"__ungrouped__";u.has(g)||u.set(g,[]),u.get(g).push(y)}const d=[];u.has("__ungrouped__")&&(d.push({heading:"",items:u.get("__ungrouped__")}),u.delete("__ungrouped__"));for(const[y,g]of u)d.push({heading:y,items:g});return d}const k=e.computed(()=>{if(n){const i=n(o.value,l.value);if(i!==null)return i}return p(o.value,l.value)}),B=e.computed(()=>h(k.value));function se(){const i=k.value.length;i!==0&&(s.value=(s.value+1)%i)}function ie(){const i=k.value.length;i!==0&&(s.value=(s.value-1+i)%i)}function de(){var u;const i=k.value[s.value];i&&!i.disabled&&((u=i.onSelect)==null||u.call(i,i),c())}function O(i){var u;for(const d of o.value){if(d.disabled||!d.shortcut)continue;const y=F(d.shortcut);if(j(i,y)){i.preventDefault(),i.stopPropagation(),(u=d.onSelect)==null||u.call(d,d),t==null||t(d),c();return}}}let C=null;return e.watch(o,i=>{C&&(C(),C=null),i.some(d=>d.shortcut)&&typeof window<"u"&&(window.addEventListener("keydown",O),C=()=>window.removeEventListener("keydown",O))},{immediate:!0,flush:"sync"}),e.onUnmounted(()=>{C&&C()}),{visible:r,searchQuery:l,activeIndex:s,items:o,toggle:f,open:a,close:c,defaultFilter:p,groupItems:h,filteredItems:k,groupedItems:B,selectNext:se,selectPrev:ie,selectCurrent:de}}const _=Symbol("cmdk-state"),E=Symbol("cmdk-loading"),w=Symbol("cmdk-close-on-select"),b=Symbol("cmdk-select-handler"),A={"data-cmdk-root":"",role:"combobox","aria-expanded":"true","aria-haspopup":"listbox"},x=e.defineComponent({__name:"CommandMenu",props:{visible:{type:Boolean},searchQuery:{},placeholder:{default:"Type a command or search..."},filter:{},autoFocus:{type:Boolean,default:!0},closeOnSelect:{type:Boolean,default:!0},loading:{type:Boolean}},emits:["update:visible","update:searchQuery","select"],setup(n,{expose:t,emit:r}){const l=n,s=r,o=S(l.filter,a=>{s("select",a),l.closeOnSelect&&o.close()});o.visible.value=l.visible??!0,o.searchQuery.value=l.searchQuery??"",e.provide(_,o),e.provide("cmdk-filter",l.filter),e.provide(E,()=>l.loading??!1),e.provide(w,()=>l.closeOnSelect),e.provide(b,a=>{s("select",a),l.closeOnSelect&&o.close()}),e.watch(()=>l.visible,a=>{a!==void 0&&(o.visible.value=a)}),e.watch(o.visible,a=>s("update:visible",a)),e.watch(()=>l.searchQuery,a=>{a!==void 0&&(o.searchQuery.value=a)}),e.watch(o.searchQuery,a=>s("update:searchQuery",a));function f(a){s("select",a),l.closeOnSelect&&o.close()}return t({open:o.open,close:o.close,toggle:o.toggle,searchQuery:o.searchQuery,items:o.items,filteredItems:o.filteredItems}),(a,c)=>(e.openBlock(),e.createElementBlock("div",A,[e.renderSlot(a.$slots,"default",{items:e.unref(o).items,filteredItems:e.unref(o).filteredItems,searchQuery:e.unref(o).searchQuery,selectNext:e.unref(o).selectNext,selectPrev:e.unref(o).selectPrev,selectCurrent:e.unref(o).selectCurrent,handleSelect:f})]))}}),P=["value","placeholder","autofocus"],N=e.defineComponent({__name:"CommandInput",props:{placeholder:{default:"Type a command or search..."},autoFocus:{type:Boolean,default:!0}},setup(n){const t=e.inject(_),r=e.inject(b,()=>{});function l(o){t.searchQuery.value=o.target.value,t.activeIndex.value=0}function s(o){var f;if(o.key==="ArrowDown")o.preventDefault(),t.selectNext();else if(o.key==="ArrowUp")o.preventDefault(),t.selectPrev();else if(o.key==="Enter"){o.preventDefault();const a=t.filteredItems.value[t.activeIndex.value];a&&!a.disabled&&((f=a.onSelect)==null||f.call(a,a),r(a))}}return(o,f)=>(e.openBlock(),e.createElementBlock("input",{"data-cmdk-input":"",value:e.unref(t).searchQuery.value,placeholder:n.placeholder,role:"searchbox",autocomplete:"off",autocorrect:"off",spellcheck:"false",autofocus:n.autoFocus,onInput:l,onKeydown:s},null,40,P))}}),G={key:0,"data-cmdk-empty":""},$=e.defineComponent({__name:"CommandEmpty",setup(n){const t=e.inject(_);return(r,l)=>e.unref(t).searchQuery.value&&e.unref(t).filteredItems.value.length===0?(e.openBlock(),e.createElementBlock("div",G,[e.renderSlot(r.$slots,"default",{},()=>[l[0]||(l[0]=e.createElementVNode("span",null,"No results found.",-1))])])):e.createCommentVNode("",!0)}}),q={key:0,"data-cmdk-loading":""},I=e.defineComponent({__name:"CommandLoading",props:{loading:{type:Boolean,default:!1}},setup(n){return(t,r)=>n.loading?(e.openBlock(),e.createElementBlock("div",q,[e.renderSlot(t.$slots,"default",{},()=>[r[0]||(r[0]=e.createElementVNode("span",null,"Loading...",-1))])])):e.createCommentVNode("",!0)}}),R={"data-cmdk-group":"",role:"group"},U={key:0,"data-cmdk-group-heading":"",role:"presentation"},z={"data-cmdk-group-items":"",role:"group"},V=e.defineComponent({__name:"CommandGroup",props:{heading:{}},setup(n){return(t,r)=>(e.openBlock(),e.createElementBlock("div",R,[n.heading?(e.openBlock(),e.createElementBlock("div",U,e.toDisplayString(n.heading),1)):e.createCommentVNode("",!0),e.createElementVNode("div",z,[e.renderSlot(t.$slots,"default")])]))}}),H=["aria-selected","aria-disabled","data-value"],W={key:0,"data-cmdk-item-icon":""},J={"data-cmdk-item-label":""},X={key:1,"data-cmdk-item-shortcut":""},D=e.defineComponent({__name:"CommandItem",props:{value:{},label:{},keywords:{},shortcut:{},disabled:{type:Boolean,default:!1},icon:{},onSelect:{}},setup(n){const t=n,r=e.inject(_),l=e.computed(()=>({value:t.value,label:t.label,keywords:t.keywords,shortcut:t.shortcut,disabled:t.disabled,icon:t.icon,onSelect:t.onSelect})),s=e.computed(()=>{const c=r.filteredItems.value[r.activeIndex.value];return(c==null?void 0:c.value)===t.value}),o=e.inject(w,()=>!0),f=e.inject(b,()=>{});function a(){var c;t.disabled||((c=t.onSelect)==null||c.call(t,l.value),f(l.value),o()&&r.close())}return(c,p)=>(e.openBlock(),e.createElementBlock("div",{"data-cmdk-item":"",role:"option","aria-selected":s.value,"aria-disabled":n.disabled,"data-value":n.value,class:e.normalizeClass({active:s.value,disabled:n.disabled}),onClick:a,onPointerenter:p[0]||(p[0]=()=>{const h=e.unref(r).filteredItems.value.findIndex(k=>k.value===n.value);h>=0&&(e.unref(r).activeIndex.value=h)})},[e.renderSlot(c.$slots,"default",{item:l.value,active:s.value},()=>[n.icon?(e.openBlock(),e.createElementBlock("span",W,[(e.openBlock(),e.createBlock(e.resolveDynamicComponent(n.icon)))])):e.createCommentVNode("",!0),e.createElementVNode("span",J,e.toDisplayString(n.label||n.value),1),n.shortcut?(e.openBlock(),e.createElementBlock("span",X,e.toDisplayString(n.shortcut),1)):e.createCommentVNode("",!0)])],42,H))}}),Y=(n,t)=>{const r=n.__vccOpts||n;for(const[l,s]of t)r[l]=s;return r},Z={},v={"data-cmdk-separator":"",role:"separator"};function ee(n,t){return e.openBlock(),e.createElementBlock("div",v)}const L=Y(Z,[["render",ee]]),te={"data-cmdk-list":"",role:"listbox"},oe={"aria-live":"polite","aria-atomic":"true",class:"sr-only"},K=e.defineComponent({__name:"CommandList",setup(n){const t=e.inject(_),r=e.inject(E,()=>!1),l=e.computed(()=>t.groupedItems.value);return(s,o)=>(e.openBlock(),e.createElementBlock("div",te,[e.createElementVNode("div",oe,[e.unref(t).searchQuery.value&&e.unref(t).filteredItems.value.length===0?(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[e.createTextVNode("No results found")],64)):e.unref(r)()?(e.openBlock(),e.createElementBlock(e.Fragment,{key:1},[e.createTextVNode("Loading")],64)):(e.openBlock(),e.createElementBlock(e.Fragment,{key:2},[e.createTextVNode(e.toDisplayString(e.unref(t).filteredItems.value.length)+" items",1)],64))]),e.createVNode($),e.createVNode(I,{loading:e.unref(r)()},null,8,["loading"]),(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(l.value,f=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:f.heading||"__ungrouped__"},[e.createVNode(V,{heading:f.heading},{default:e.withCtx(()=>[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(f.items,a=>(e.openBlock(),e.createBlock(D,{key:a.value,value:a.value,label:a.label,shortcut:a.shortcut,disabled:a.disabled,icon:a.icon,"on-select":a.onSelect},null,8,["value","label","shortcut","disabled","icon","on-select"]))),128))]),_:2},1032,["heading"]),f!==l.value[l.value.length-1]?(e.openBlock(),e.createBlock(L,{key:0})):e.createCommentVNode("",!0)],64))),128))]))}}),ne={"data-cmdk-dialog-wrapper":""},ae={"data-cmdk-dialog-header":""},le={"data-cmdk-dialog-body":""},ce={key:0,"data-cmdk-dialog-footer":""},T=e.defineComponent({__name:"CommandDialog",props:{visible:{type:Boolean,default:!1},placeholder:{default:"Type a command or search..."},autoFocus:{type:Boolean,default:!0},closeOnSelect:{type:Boolean,default:!0},items:{default:()=>[]},filter:{},loading:{type:Boolean,default:!1}},emits:["update:visible","select"],setup(n,{emit:t}){const r=n,l=t,s=S(r.filter,o);e.provide(_,s),e.provide(E,()=>r.loading),e.provide(w,()=>r.closeOnSelect),e.watch(()=>r.items,c=>{s.items.value=c},{immediate:!0}),e.watch(()=>r.visible,c=>{s.visible.value=c},{immediate:!0}),e.watch(s.visible,async c=>{if(l("update:visible",c),c){await e.nextTick();const p=document.querySelector("[data-cmdk-input]");p==null||p.focus()}});function o(c){l("select",c),r.closeOnSelect&&s.close()}e.provide(b,o);function f(c){c.target===c.currentTarget&&s.close()}function a(c){if(c.key==="Escape"&&(c.preventDefault(),s.close()),c.key==="Tab"){const h=c.currentTarget.querySelectorAll('input, button, [href], select, textarea, [tabindex]:not([tabindex="-1"])');if(h.length===0)return;const k=h[0],B=h[h.length-1];c.shiftKey?document.activeElement===k&&(c.preventDefault(),B.focus()):document.activeElement===B&&(c.preventDefault(),k.focus())}}return(c,p)=>(e.openBlock(),e.createBlock(e.Teleport,{to:"body"},[e.createVNode(e.Transition,{name:"cmdk-dialog"},{default:e.withCtx(()=>[e.unref(s).visible.value?(e.openBlock(),e.createElementBlock("div",{key:0,"data-cmdk-dialog":"",onKeydown:a},[e.createElementVNode("div",{"data-cmdk-dialog-mask":"",onClick:f}),e.createElementVNode("div",ne,[e.createElementVNode("div",ae,[e.renderSlot(c.$slots,"header",{},()=>[e.createVNode(N,{placeholder:n.placeholder,"auto-focus":n.autoFocus},null,8,["placeholder","auto-focus"])])]),e.createElementVNode("div",le,[e.renderSlot(c.$slots,"body",{},()=>[e.createVNode(K)])]),c.$slots.footer?(e.openBlock(),e.createElementBlock("div",ce,[e.renderSlot(c.$slots,"footer")])):e.createCommentVNode("",!0)])],32)):e.createCommentVNode("",!0)]),_:3})]))}}),re={Menu:x,Dialog:T,Input:N,List:K,Group:V,Item:D,Empty:$,Separator:L,Loading:I};m.Command=re,m.CommandDialog=T,m.CommandEmpty=$,m.CommandGroup=V,m.CommandInput=N,m.CommandItem=D,m.CommandList=K,m.CommandLoading=I,m.CommandMenu=x,m.CommandSeparator=L,m.useCommandMenu=S,Object.defineProperty(m,Symbol.toStringTag,{value:"Module"})}));
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vue-command-kit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "โK โ Fast, composable, unstyled command menu for Vue 3",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/vue-cmdk.umd.cjs",
|
|
7
|
+
"module": "./dist/vue-cmdk.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/vue-cmdk.js",
|
|
12
|
+
"require": "./dist/vue-cmdk.umd.cjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist/vue-cmdk.js",
|
|
17
|
+
"dist/vue-cmdk.umd.cjs",
|
|
18
|
+
"dist/*.d.ts"
|
|
19
|
+
],
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"keywords": [
|
|
22
|
+
"vue",
|
|
23
|
+
"command",
|
|
24
|
+
"palette",
|
|
25
|
+
"command-palette",
|
|
26
|
+
"cmdk",
|
|
27
|
+
"cmd-k",
|
|
28
|
+
"vue3",
|
|
29
|
+
"keyboard",
|
|
30
|
+
"menu"
|
|
31
|
+
],
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"author": "yvng-jie (https://github.com/yvng-jie)",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/yvng-jie/vue-cmdk"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/yvng-jie/vue-cmdk#readme",
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/yvng-jie/vue-cmdk/issues"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=22.13"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public",
|
|
47
|
+
"registry": "https://registry.npmjs.org"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"vue": "^3.4.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@microsoft/api-extractor": "^7.58.7",
|
|
54
|
+
"@types/node": "^25.9.1",
|
|
55
|
+
"@vitejs/plugin-vue": "^5.0.0",
|
|
56
|
+
"@vue/shared": "^3.5.35",
|
|
57
|
+
"highlight.js": "^11.11.1",
|
|
58
|
+
"typescript": "^5.7.0",
|
|
59
|
+
"vite": "^6.0.0",
|
|
60
|
+
"vite-plugin-dts": "^4.0.0",
|
|
61
|
+
"vue": "^3.5.0",
|
|
62
|
+
"vue-tsc": "^2.0.0"
|
|
63
|
+
},
|
|
64
|
+
"scripts": {
|
|
65
|
+
"dev": "vite",
|
|
66
|
+
"build": "vite build && node scripts/postbuild.mjs",
|
|
67
|
+
"preview": "vite preview",
|
|
68
|
+
"build:demo": "vite build --config vite.demo.config.ts",
|
|
69
|
+
"typecheck": "vue-tsc --noEmit"
|
|
70
|
+
}
|
|
71
|
+
}
|