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 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>
@@ -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>;
@@ -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;
@@ -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
+ }