@bysivin/jumpbar 0.0.0-stage → 0.2.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 +21 -0
- package/README.md +241 -2
- package/dist/core.js +148 -0
- package/dist/core.js.map +1 -0
- package/dist/index.js +583 -0
- package/dist/index.js.map +1 -0
- package/package.json +70 -4
- package/tailwind.cjs +56 -0
- package/theme.css +33 -0
- package/types/core.d.ts +76 -0
- package/types/index.d.ts +77 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
import { groupBySection, rank, parseQuery } from "./core.js";
|
|
2
|
+
import { createFrecency, fuzzyScore, normalize } from "./core.js";
|
|
3
|
+
import { ref, shallowRef, computed, reactive, watch, openBlock, createElementBlock, Fragment, renderList, toDisplayString, createCommentVNode, withModifiers, normalizeClass, createElementVNode, nextTick, onBeforeUnmount, createBlock, Teleport, unref } from "vue";
|
|
4
|
+
const DEFAULT_DEBOUNCE = 120;
|
|
5
|
+
const DEFAULT_LIMIT = 100;
|
|
6
|
+
const NO_FRECENCY = { record: () => {
|
|
7
|
+
}, score: () => 0 };
|
|
8
|
+
const createCommandBar = (initialOptions = {}) => {
|
|
9
|
+
const options = {
|
|
10
|
+
prefixes: [],
|
|
11
|
+
context: () => ({}),
|
|
12
|
+
frecency: NO_FRECENCY,
|
|
13
|
+
onSelect: () => {
|
|
14
|
+
},
|
|
15
|
+
onClose: () => {
|
|
16
|
+
},
|
|
17
|
+
onError: (error) => {
|
|
18
|
+
throw error;
|
|
19
|
+
},
|
|
20
|
+
debounce: DEFAULT_DEBOUNCE,
|
|
21
|
+
limit: DEFAULT_LIMIT,
|
|
22
|
+
...initialOptions
|
|
23
|
+
};
|
|
24
|
+
let sources = [];
|
|
25
|
+
let context = {};
|
|
26
|
+
let controller = null;
|
|
27
|
+
let timers = [];
|
|
28
|
+
let sequence = 0;
|
|
29
|
+
const isOpen = ref(false);
|
|
30
|
+
const query = ref("");
|
|
31
|
+
const pages = shallowRef([]);
|
|
32
|
+
const items = shallowRef([]);
|
|
33
|
+
const results = shallowRef([]);
|
|
34
|
+
const selectedIndex = ref(0);
|
|
35
|
+
const pendingCount = ref(0);
|
|
36
|
+
const page = computed(() => pages.value.at(-1) ?? null);
|
|
37
|
+
const isLoading = computed(() => pendingCount.value > 0);
|
|
38
|
+
const sections = computed(() => {
|
|
39
|
+
const list = page.value && results.value.length ? results.value : [...items.value, ...results.value];
|
|
40
|
+
const seen = /* @__PURE__ */ new Set();
|
|
41
|
+
const unique = list.filter((item) => {
|
|
42
|
+
if (seen.has(item.id)) return false;
|
|
43
|
+
seen.add(item.id);
|
|
44
|
+
return true;
|
|
45
|
+
});
|
|
46
|
+
return groupBySection(unique);
|
|
47
|
+
});
|
|
48
|
+
const visible = computed(
|
|
49
|
+
() => sections.value.flatMap((section) => section.items)
|
|
50
|
+
);
|
|
51
|
+
const selected = computed(() => visible.value[selectedIndex.value] ?? null);
|
|
52
|
+
const buildRequest = () => {
|
|
53
|
+
var _a;
|
|
54
|
+
const { prefix, text } = parseQuery(query.value, options.prefixes);
|
|
55
|
+
return {
|
|
56
|
+
query: query.value,
|
|
57
|
+
text,
|
|
58
|
+
prefix,
|
|
59
|
+
page: ((_a = page.value) == null ? void 0 : _a.id) ?? null,
|
|
60
|
+
context
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
const isActive = (source, request) => {
|
|
64
|
+
var _a;
|
|
65
|
+
return request.text.length >= (source.minQuery ?? 0) && (((_a = source.when) == null ? void 0 : _a.call(source, request)) ?? true);
|
|
66
|
+
};
|
|
67
|
+
const collectCommands = (request) => sources.flatMap(
|
|
68
|
+
(source) => source.commands && isActive(source, request) ? source.commands(request) : []
|
|
69
|
+
);
|
|
70
|
+
const delayOf = (source) => source.debounce ?? options.debounce;
|
|
71
|
+
const publish = (batch) => {
|
|
72
|
+
results.value = batch.searchers.flatMap(
|
|
73
|
+
(searcher) => batch.fresh.get(searcher.id) ?? []
|
|
74
|
+
);
|
|
75
|
+
selectedIndex.value = Math.min(
|
|
76
|
+
selectedIndex.value,
|
|
77
|
+
Math.max(visible.value.length - 1, 0)
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
const runSearchers = (batch, delay) => {
|
|
81
|
+
batch.searchers.filter((source) => delayOf(source) === delay).forEach(async (source) => {
|
|
82
|
+
try {
|
|
83
|
+
const list = await source.search({
|
|
84
|
+
...batch.request,
|
|
85
|
+
signal: batch.signal
|
|
86
|
+
});
|
|
87
|
+
if (batch.sequence !== sequence) return;
|
|
88
|
+
batch.fresh.set(source.id, list);
|
|
89
|
+
publish(batch);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
if (batch.sequence !== sequence) return;
|
|
92
|
+
batch.fresh.set(source.id, []);
|
|
93
|
+
publish(batch);
|
|
94
|
+
options.onError(error, source);
|
|
95
|
+
} finally {
|
|
96
|
+
if (batch.sequence === sequence) pendingCount.value -= 1;
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
const run = () => {
|
|
101
|
+
timers.forEach(clearTimeout);
|
|
102
|
+
timers = [];
|
|
103
|
+
controller == null ? void 0 : controller.abort();
|
|
104
|
+
sequence += 1;
|
|
105
|
+
const request = buildRequest();
|
|
106
|
+
if (!request.text) results.value = [];
|
|
107
|
+
const ranked = rank(collectCommands(request), {
|
|
108
|
+
text: request.text,
|
|
109
|
+
prefix: request.prefix,
|
|
110
|
+
page: request.page,
|
|
111
|
+
scopes: context.scopes ?? [],
|
|
112
|
+
frecency: options.frecency
|
|
113
|
+
});
|
|
114
|
+
items.value = request.text ? ranked.slice(0, options.limit) : ranked;
|
|
115
|
+
selectedIndex.value = 0;
|
|
116
|
+
const searchRequest = { ...request, matches: items.value.length };
|
|
117
|
+
const searchers = sources.filter(
|
|
118
|
+
(source) => source.search && isActive(source, searchRequest)
|
|
119
|
+
);
|
|
120
|
+
pendingCount.value = searchers.length;
|
|
121
|
+
if (!searchers.length) {
|
|
122
|
+
results.value = [];
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
controller = new AbortController();
|
|
126
|
+
const batch = {
|
|
127
|
+
sequence,
|
|
128
|
+
request: searchRequest,
|
|
129
|
+
signal: controller.signal,
|
|
130
|
+
fresh: /* @__PURE__ */ new Map(),
|
|
131
|
+
searchers
|
|
132
|
+
};
|
|
133
|
+
new Set(searchers.map(delayOf)).forEach((delay) => {
|
|
134
|
+
timers.push(setTimeout(() => runSearchers(batch, delay), delay));
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
const navigate = (nextPages, nextQuery = "") => {
|
|
138
|
+
pages.value = nextPages;
|
|
139
|
+
query.value = nextQuery;
|
|
140
|
+
results.value = [];
|
|
141
|
+
run();
|
|
142
|
+
};
|
|
143
|
+
const open = ({ page: pageId = null, query: initialQuery = "" } = {}) => {
|
|
144
|
+
context = options.context();
|
|
145
|
+
isOpen.value = true;
|
|
146
|
+
pages.value = [];
|
|
147
|
+
query.value = "";
|
|
148
|
+
const target = pageId ? collectCommands(buildRequest()).find((item) => item.id === pageId) ?? {
|
|
149
|
+
id: pageId
|
|
150
|
+
} : null;
|
|
151
|
+
navigate(target ? [target] : [], initialQuery);
|
|
152
|
+
};
|
|
153
|
+
const close = () => {
|
|
154
|
+
if (!isOpen.value) return;
|
|
155
|
+
timers.forEach(clearTimeout);
|
|
156
|
+
timers = [];
|
|
157
|
+
controller == null ? void 0 : controller.abort();
|
|
158
|
+
sequence += 1;
|
|
159
|
+
pendingCount.value = 0;
|
|
160
|
+
isOpen.value = false;
|
|
161
|
+
options.onClose();
|
|
162
|
+
};
|
|
163
|
+
const back = () => {
|
|
164
|
+
if (!page.value) return;
|
|
165
|
+
navigate(pages.value.slice(0, -1));
|
|
166
|
+
};
|
|
167
|
+
const select = (item) => {
|
|
168
|
+
if (!item) return;
|
|
169
|
+
if (item.page) {
|
|
170
|
+
navigate([...pages.value, item]);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (!item.run) return;
|
|
174
|
+
options.frecency.record(item.id);
|
|
175
|
+
options.onSelect(item);
|
|
176
|
+
close();
|
|
177
|
+
item.run(context);
|
|
178
|
+
};
|
|
179
|
+
const setQuery = (value) => {
|
|
180
|
+
query.value = value;
|
|
181
|
+
run();
|
|
182
|
+
};
|
|
183
|
+
const moveSelection = (delta) => {
|
|
184
|
+
const count = visible.value.length;
|
|
185
|
+
if (!count) return;
|
|
186
|
+
selectedIndex.value = (selectedIndex.value + delta + count) % count;
|
|
187
|
+
};
|
|
188
|
+
const toggle = () => isOpen.value ? close() : open();
|
|
189
|
+
const register = (source) => {
|
|
190
|
+
sources = [...sources, source];
|
|
191
|
+
return () => {
|
|
192
|
+
sources = sources.filter((registered) => registered !== source);
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
const configure = (overrides) => Object.assign(options, overrides);
|
|
196
|
+
const dispose = () => {
|
|
197
|
+
close();
|
|
198
|
+
sources = [];
|
|
199
|
+
};
|
|
200
|
+
return {
|
|
201
|
+
isOpen,
|
|
202
|
+
query,
|
|
203
|
+
page,
|
|
204
|
+
pages,
|
|
205
|
+
visible,
|
|
206
|
+
sections,
|
|
207
|
+
selectedIndex,
|
|
208
|
+
selected,
|
|
209
|
+
isLoading,
|
|
210
|
+
open,
|
|
211
|
+
close,
|
|
212
|
+
toggle,
|
|
213
|
+
back,
|
|
214
|
+
select,
|
|
215
|
+
setQuery,
|
|
216
|
+
moveSelection,
|
|
217
|
+
register,
|
|
218
|
+
configure,
|
|
219
|
+
dispose
|
|
220
|
+
};
|
|
221
|
+
};
|
|
222
|
+
let shared = null;
|
|
223
|
+
const useCommandBar = () => {
|
|
224
|
+
shared ?? (shared = createCommandBar());
|
|
225
|
+
return shared;
|
|
226
|
+
};
|
|
227
|
+
const _hoisted_1$1 = ["aria-label"];
|
|
228
|
+
const _hoisted_2$1 = {
|
|
229
|
+
key: 0,
|
|
230
|
+
class: "px-3 pt-3 pb-1 text-xs font-medium text-jumpbar-muted-foreground first:pt-1"
|
|
231
|
+
};
|
|
232
|
+
const _hoisted_3$1 = ["id", "aria-selected", "onMousemove", "onClick"];
|
|
233
|
+
const _hoisted_4$1 = {
|
|
234
|
+
key: 0,
|
|
235
|
+
class: "relative flex items-center justify-center flex-shrink-0 rounded-md size-6 bg-jumpbar-muted text-jumpbar-muted-foreground"
|
|
236
|
+
};
|
|
237
|
+
const _hoisted_5$1 = ["src", "alt", "onError"];
|
|
238
|
+
const _hoisted_6$1 = {
|
|
239
|
+
key: 1,
|
|
240
|
+
class: "text-xs font-medium leading-none"
|
|
241
|
+
};
|
|
242
|
+
const _hoisted_7$1 = {
|
|
243
|
+
key: 1,
|
|
244
|
+
class: "flex items-center justify-center flex-shrink-0 size-6"
|
|
245
|
+
};
|
|
246
|
+
const _hoisted_8$1 = { class: "flex items-center flex-1 min-w-0 gap-1.5 overflow-hidden" };
|
|
247
|
+
const _hoisted_9$1 = { class: "truncate text-jumpbar-muted-foreground" };
|
|
248
|
+
const _hoisted_10$1 = { class: "flex-shrink-0 max-w-full truncate" };
|
|
249
|
+
const _hoisted_11 = {
|
|
250
|
+
key: 1,
|
|
251
|
+
class: "truncate text-jumpbar-muted-foreground"
|
|
252
|
+
};
|
|
253
|
+
const _hoisted_12 = {
|
|
254
|
+
key: 2,
|
|
255
|
+
class: "flex-shrink-0 text-xs text-jumpbar-muted-foreground"
|
|
256
|
+
};
|
|
257
|
+
const _hoisted_13 = {
|
|
258
|
+
key: 3,
|
|
259
|
+
class: "flex items-center gap-1"
|
|
260
|
+
};
|
|
261
|
+
const _hoisted_14 = {
|
|
262
|
+
key: 4,
|
|
263
|
+
class: "flex-shrink-0 i-lucide-chevron-right size-4 text-jumpbar-subtle-foreground rtl:-scale-x-100"
|
|
264
|
+
};
|
|
265
|
+
const _hoisted_15 = {
|
|
266
|
+
key: 0,
|
|
267
|
+
"aria-hidden": "true",
|
|
268
|
+
class: "animate-pulse motion-reduce:animate-none"
|
|
269
|
+
};
|
|
270
|
+
const PLACEHOLDER_ROWS = 3;
|
|
271
|
+
const _sfc_main$1 = {
|
|
272
|
+
__name: "CommandBarList",
|
|
273
|
+
props: {
|
|
274
|
+
sections: { type: Array, required: true },
|
|
275
|
+
items: { type: Array, required: true },
|
|
276
|
+
selectedIndex: { type: Number, required: true },
|
|
277
|
+
pending: { type: Boolean, default: false }
|
|
278
|
+
},
|
|
279
|
+
emits: ["select", "activate"],
|
|
280
|
+
setup(__props, { emit: __emit }) {
|
|
281
|
+
const props = __props;
|
|
282
|
+
const emit = __emit;
|
|
283
|
+
const STATUS_CLASSES = {
|
|
284
|
+
online: "bg-jumpbar-online",
|
|
285
|
+
busy: "bg-jumpbar-busy",
|
|
286
|
+
offline: "bg-jumpbar-offline"
|
|
287
|
+
};
|
|
288
|
+
const listRef = ref(null);
|
|
289
|
+
const failedImages = reactive(/* @__PURE__ */ new Set());
|
|
290
|
+
let pointer = { x: null, y: null };
|
|
291
|
+
const onPointerMove = (event, index) => {
|
|
292
|
+
if (event.clientX === pointer.x && event.clientY === pointer.y) return;
|
|
293
|
+
pointer = { x: event.clientX, y: event.clientY };
|
|
294
|
+
emit("activate", index);
|
|
295
|
+
};
|
|
296
|
+
const indexes = computed(
|
|
297
|
+
() => new Map(props.items.map((item, index) => [item.id, index]))
|
|
298
|
+
);
|
|
299
|
+
const initials = (name) => (name ?? "").split(/\s+/).slice(0, 2).map((word) => [...word][0] ?? "").join("").toUpperCase();
|
|
300
|
+
watch(
|
|
301
|
+
[() => props.selectedIndex, () => props.sections],
|
|
302
|
+
([index]) => {
|
|
303
|
+
var _a;
|
|
304
|
+
const list = listRef.value;
|
|
305
|
+
if (!list) return;
|
|
306
|
+
if (index === 0) list.scrollTop = 0;
|
|
307
|
+
else {
|
|
308
|
+
(_a = list.querySelector('[aria-selected="true"]')) == null ? void 0 : _a.scrollIntoView({ block: "nearest" });
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
{ flush: "post" }
|
|
312
|
+
);
|
|
313
|
+
return (_ctx, _cache) => {
|
|
314
|
+
return openBlock(), createElementBlock("div", {
|
|
315
|
+
ref_key: "listRef",
|
|
316
|
+
ref: listRef,
|
|
317
|
+
role: "listbox",
|
|
318
|
+
class: "px-2 py-2 overflow-y-auto"
|
|
319
|
+
}, [
|
|
320
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(__props.sections, (section) => {
|
|
321
|
+
return openBlock(), createElementBlock("div", {
|
|
322
|
+
key: section.section ?? "",
|
|
323
|
+
role: "group",
|
|
324
|
+
"aria-label": section.section ?? void 0
|
|
325
|
+
}, [
|
|
326
|
+
section.section ? (openBlock(), createElementBlock("p", _hoisted_2$1, toDisplayString(section.section), 1)) : createCommentVNode("", true),
|
|
327
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(section.items, (item) => {
|
|
328
|
+
return openBlock(), createElementBlock("div", {
|
|
329
|
+
id: `command-bar-item-${item.id}`,
|
|
330
|
+
key: item.id,
|
|
331
|
+
role: "option",
|
|
332
|
+
"aria-selected": indexes.value.get(item.id) === __props.selectedIndex,
|
|
333
|
+
class: normalizeClass(["relative flex items-center h-10 gap-3 px-3 text-sm rounded-lg cursor-pointer select-none text-jumpbar-foreground transition-colors duration-75", {
|
|
334
|
+
"bg-jumpbar-muted before:absolute before:inset-y-2 before:start-0 before:w-0.5 before:rounded-full before:bg-jumpbar-primary": indexes.value.get(item.id) === __props.selectedIndex
|
|
335
|
+
}]),
|
|
336
|
+
onMousedown: _cache[0] || (_cache[0] = withModifiers(() => {
|
|
337
|
+
}, ["prevent"])),
|
|
338
|
+
onMousemove: ($event) => onPointerMove($event, indexes.value.get(item.id)),
|
|
339
|
+
onClick: ($event) => emit("select", item)
|
|
340
|
+
}, [
|
|
341
|
+
item.avatar ? (openBlock(), createElementBlock("span", _hoisted_4$1, [
|
|
342
|
+
item.avatar.src && !failedImages.has(item.avatar.src) ? (openBlock(), createElementBlock("img", {
|
|
343
|
+
key: 0,
|
|
344
|
+
src: item.avatar.src,
|
|
345
|
+
alt: item.avatar.name,
|
|
346
|
+
class: "object-cover rounded-md size-6",
|
|
347
|
+
onError: ($event) => failedImages.add(item.avatar.src)
|
|
348
|
+
}, null, 40, _hoisted_5$1)) : (openBlock(), createElementBlock("span", _hoisted_6$1, toDisplayString(initials(item.avatar.name)), 1)),
|
|
349
|
+
item.avatar.status ? (openBlock(), createElementBlock("span", {
|
|
350
|
+
key: 2,
|
|
351
|
+
class: normalizeClass(["absolute -bottom-px -end-px border rounded-full size-2 border-jumpbar-background", STATUS_CLASSES[item.avatar.status]])
|
|
352
|
+
}, null, 2)) : createCommentVNode("", true)
|
|
353
|
+
])) : (openBlock(), createElementBlock("span", _hoisted_7$1, [
|
|
354
|
+
createElementVNode("span", {
|
|
355
|
+
class: normalizeClass(["size-4", [
|
|
356
|
+
item.icon,
|
|
357
|
+
indexes.value.get(item.id) === __props.selectedIndex ? "text-jumpbar-foreground" : "text-jumpbar-muted-foreground"
|
|
358
|
+
]])
|
|
359
|
+
}, null, 2)
|
|
360
|
+
])),
|
|
361
|
+
createElementVNode("span", _hoisted_8$1, [
|
|
362
|
+
item.parentTitle ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
|
|
363
|
+
createElementVNode("span", _hoisted_9$1, toDisplayString(item.parentTitle), 1),
|
|
364
|
+
_cache[1] || (_cache[1] = createElementVNode("span", { class: "flex-shrink-0 i-lucide-chevron-right size-3 text-jumpbar-subtle-foreground rtl:-scale-x-100" }, null, -1))
|
|
365
|
+
], 64)) : createCommentVNode("", true),
|
|
366
|
+
createElementVNode("span", _hoisted_10$1, toDisplayString(item.title), 1),
|
|
367
|
+
item.subtitle ? (openBlock(), createElementBlock("span", _hoisted_11, toDisplayString(item.subtitle), 1)) : createCommentVNode("", true)
|
|
368
|
+
]),
|
|
369
|
+
item.badge ? (openBlock(), createElementBlock("span", _hoisted_12, toDisplayString(item.badge), 1)) : createCommentVNode("", true),
|
|
370
|
+
item.shortcut ? (openBlock(), createElementBlock("span", _hoisted_13, [
|
|
371
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(item.shortcut, (key) => {
|
|
372
|
+
return openBlock(), createElementBlock("kbd", {
|
|
373
|
+
key,
|
|
374
|
+
class: "inline-flex items-center justify-center h-5 px-1 text-xs rounded min-w-5 text-jumpbar-muted-foreground bg-jumpbar-subtle outline outline-1 outline-jumpbar-border"
|
|
375
|
+
}, toDisplayString(key), 1);
|
|
376
|
+
}), 128))
|
|
377
|
+
])) : createCommentVNode("", true),
|
|
378
|
+
item.page ? (openBlock(), createElementBlock("span", _hoisted_14)) : createCommentVNode("", true)
|
|
379
|
+
], 42, _hoisted_3$1);
|
|
380
|
+
}), 128))
|
|
381
|
+
], 8, _hoisted_1$1);
|
|
382
|
+
}), 128)),
|
|
383
|
+
__props.pending ? (openBlock(), createElementBlock("div", _hoisted_15, [
|
|
384
|
+
_cache[3] || (_cache[3] = createElementVNode("div", { class: "px-3 pt-3 pb-1" }, [
|
|
385
|
+
createElementVNode("span", { class: "block w-20 h-3 rounded bg-jumpbar-muted" })
|
|
386
|
+
], -1)),
|
|
387
|
+
(openBlock(), createElementBlock(Fragment, null, renderList(PLACEHOLDER_ROWS, (row) => {
|
|
388
|
+
return createElementVNode("div", {
|
|
389
|
+
key: row,
|
|
390
|
+
class: "flex items-center h-10 gap-3 px-3"
|
|
391
|
+
}, [..._cache[2] || (_cache[2] = [
|
|
392
|
+
createElementVNode("span", { class: "flex-shrink-0 rounded-md size-6 bg-jumpbar-muted" }, null, -1),
|
|
393
|
+
createElementVNode("span", { class: "h-3 rounded w-36 bg-jumpbar-muted" }, null, -1),
|
|
394
|
+
createElementVNode("span", { class: "h-3 rounded w-24 bg-jumpbar-subtle" }, null, -1)
|
|
395
|
+
])]);
|
|
396
|
+
}), 64))
|
|
397
|
+
])) : createCommentVNode("", true)
|
|
398
|
+
], 512);
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
const _hoisted_1 = ["dir"];
|
|
403
|
+
const _hoisted_2 = { class: "flex items-center gap-2 px-4 h-14" };
|
|
404
|
+
const _hoisted_3 = {
|
|
405
|
+
key: 0,
|
|
406
|
+
class: "flex-shrink-0 px-2 py-0.5 text-xs rounded-md bg-jumpbar-muted text-jumpbar-muted-foreground max-w-48 truncate"
|
|
407
|
+
};
|
|
408
|
+
const _hoisted_4 = {
|
|
409
|
+
key: 1,
|
|
410
|
+
class: "flex-shrink-0 px-2 py-0.5 text-xs font-medium rounded-md bg-jumpbar-primary-soft text-jumpbar-primary max-w-48 truncate"
|
|
411
|
+
};
|
|
412
|
+
const _hoisted_5 = ["aria-expanded", "aria-activedescendant", "value", "placeholder"];
|
|
413
|
+
const _hoisted_6 = {
|
|
414
|
+
key: 2,
|
|
415
|
+
class: "flex-shrink-0 i-lucide-loader-circle size-4 animate-spin text-jumpbar-subtle-foreground"
|
|
416
|
+
};
|
|
417
|
+
const _hoisted_7 = { class: "border-t border-jumpbar-border" };
|
|
418
|
+
const _hoisted_8 = {
|
|
419
|
+
key: 1,
|
|
420
|
+
class: "px-4 py-10 text-sm text-center text-jumpbar-muted-foreground"
|
|
421
|
+
};
|
|
422
|
+
const _hoisted_9 = {
|
|
423
|
+
key: 0,
|
|
424
|
+
class: "flex items-center gap-4 px-4 text-xs border-t h-9 border-jumpbar-border text-jumpbar-subtle-foreground"
|
|
425
|
+
};
|
|
426
|
+
const _hoisted_10 = { class: "ms-0.5" };
|
|
427
|
+
const CLOSE_ANIMATION_MS = 150;
|
|
428
|
+
const PLACEHOLDER_DELAY_MS = 150;
|
|
429
|
+
const _sfc_main = {
|
|
430
|
+
__name: "CommandBar",
|
|
431
|
+
props: {
|
|
432
|
+
placeholder: { type: String, default: "" },
|
|
433
|
+
emptyText: { type: String, default: "" },
|
|
434
|
+
scopeLabel: { type: String, default: "" },
|
|
435
|
+
hints: { type: Array, default: () => [] },
|
|
436
|
+
dir: { type: String, default: "ltr" }
|
|
437
|
+
},
|
|
438
|
+
setup(__props) {
|
|
439
|
+
const props = __props;
|
|
440
|
+
const bar = useCommandBar();
|
|
441
|
+
const dialogRef = ref(null);
|
|
442
|
+
const inputRef = ref(null);
|
|
443
|
+
const isClosing = ref(false);
|
|
444
|
+
let closeTimer = null;
|
|
445
|
+
const placeholder = computed(
|
|
446
|
+
() => {
|
|
447
|
+
var _a;
|
|
448
|
+
return ((_a = bar.page.value) == null ? void 0 : _a.placeholder) ?? props.placeholder;
|
|
449
|
+
}
|
|
450
|
+
);
|
|
451
|
+
const activeId = computed(
|
|
452
|
+
() => bar.selected.value ? `command-bar-item-${bar.selected.value.id}` : void 0
|
|
453
|
+
);
|
|
454
|
+
const isEmpty = computed(
|
|
455
|
+
() => !bar.visible.value.length && !bar.isLoading.value
|
|
456
|
+
);
|
|
457
|
+
const showPlaceholder = ref(false);
|
|
458
|
+
let placeholderTimer = null;
|
|
459
|
+
watch(bar.isLoading, (loading) => {
|
|
460
|
+
clearTimeout(placeholderTimer);
|
|
461
|
+
if (!loading) {
|
|
462
|
+
showPlaceholder.value = false;
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
placeholderTimer = setTimeout(() => {
|
|
466
|
+
showPlaceholder.value = true;
|
|
467
|
+
}, PLACEHOLDER_DELAY_MS);
|
|
468
|
+
});
|
|
469
|
+
const KEY_ACTIONS = {
|
|
470
|
+
ArrowDown: () => bar.moveSelection(1),
|
|
471
|
+
ArrowUp: () => bar.moveSelection(-1),
|
|
472
|
+
Enter: () => bar.select(bar.selected.value),
|
|
473
|
+
Escape: () => bar.close(),
|
|
474
|
+
Backspace: () => bar.query.value ? false : bar.back()
|
|
475
|
+
};
|
|
476
|
+
const onKeydown = (event) => {
|
|
477
|
+
if (event.isComposing || event.keyCode === 229) return;
|
|
478
|
+
const action = KEY_ACTIONS[event.key];
|
|
479
|
+
if (!action || action() === false) return;
|
|
480
|
+
event.preventDefault();
|
|
481
|
+
};
|
|
482
|
+
watch(bar.isOpen, async (open) => {
|
|
483
|
+
clearTimeout(closeTimer);
|
|
484
|
+
if (!open) {
|
|
485
|
+
isClosing.value = true;
|
|
486
|
+
closeTimer = setTimeout(() => {
|
|
487
|
+
dialogRef.value.close();
|
|
488
|
+
isClosing.value = false;
|
|
489
|
+
}, CLOSE_ANIMATION_MS);
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
isClosing.value = false;
|
|
493
|
+
if (!dialogRef.value.open) dialogRef.value.showModal();
|
|
494
|
+
await nextTick();
|
|
495
|
+
inputRef.value.focus();
|
|
496
|
+
});
|
|
497
|
+
onBeforeUnmount(() => {
|
|
498
|
+
clearTimeout(closeTimer);
|
|
499
|
+
clearTimeout(placeholderTimer);
|
|
500
|
+
});
|
|
501
|
+
return (_ctx, _cache) => {
|
|
502
|
+
var _a;
|
|
503
|
+
return openBlock(), createBlock(Teleport, { to: "body" }, [
|
|
504
|
+
createElementVNode("dialog", {
|
|
505
|
+
ref_key: "dialogRef",
|
|
506
|
+
ref: dialogRef,
|
|
507
|
+
dir: __props.dir,
|
|
508
|
+
class: normalizeClass([
|
|
509
|
+
"w-[calc(100vw-2rem)] max-w-2xl p-0 mx-auto mt-[clamp(2rem,10vh,6rem)] mb-auto overflow-hidden shadow-2xl rounded-2xl bg-jumpbar-background outline outline-1 outline-jumpbar-ring text-jumpbar-foreground motion-reduce:animate-none backdrop:bg-jumpbar-overlay max-sm:w-full max-sm:max-w-full max-sm:mt-auto max-sm:mb-0 max-sm:rounded-b-none",
|
|
510
|
+
isClosing.value ? "animate-command-bar-out backdrop:animate-fade-out" : "animate-pop-in backdrop:animate-fade-in"
|
|
511
|
+
]),
|
|
512
|
+
onCancel: _cache[2] || (_cache[2] = withModifiers((...args) => unref(bar).close && unref(bar).close(...args), ["prevent"])),
|
|
513
|
+
onClose: _cache[3] || (_cache[3] = (...args) => unref(bar).close && unref(bar).close(...args)),
|
|
514
|
+
onClick: _cache[4] || (_cache[4] = withModifiers((...args) => unref(bar).close && unref(bar).close(...args), ["self"]))
|
|
515
|
+
}, [
|
|
516
|
+
createElementVNode("div", _hoisted_2, [
|
|
517
|
+
_cache[5] || (_cache[5] = createElementVNode("span", { class: "flex-shrink-0 i-lucide-search size-4 text-jumpbar-subtle-foreground" }, null, -1)),
|
|
518
|
+
__props.scopeLabel ? (openBlock(), createElementBlock("span", _hoisted_3, toDisplayString(__props.scopeLabel), 1)) : createCommentVNode("", true),
|
|
519
|
+
unref(bar).page.value ? (openBlock(), createElementBlock("span", _hoisted_4, toDisplayString(unref(bar).page.value.title), 1)) : createCommentVNode("", true),
|
|
520
|
+
createElementVNode("input", {
|
|
521
|
+
ref_key: "inputRef",
|
|
522
|
+
ref: inputRef,
|
|
523
|
+
role: "combobox",
|
|
524
|
+
"aria-autocomplete": "list",
|
|
525
|
+
"aria-controls": "command-bar-list",
|
|
526
|
+
"aria-expanded": unref(bar).isOpen.value,
|
|
527
|
+
"aria-activedescendant": activeId.value,
|
|
528
|
+
value: unref(bar).query.value,
|
|
529
|
+
placeholder: placeholder.value,
|
|
530
|
+
autocomplete: "off",
|
|
531
|
+
spellcheck: "false",
|
|
532
|
+
class: "flex-1 h-full min-w-0 text-base bg-transparent border-0 outline-none text-jumpbar-foreground placeholder:text-jumpbar-subtle-foreground",
|
|
533
|
+
onInput: _cache[0] || (_cache[0] = ($event) => unref(bar).setQuery($event.target.value)),
|
|
534
|
+
onKeydown
|
|
535
|
+
}, null, 40, _hoisted_5),
|
|
536
|
+
unref(bar).isLoading.value ? (openBlock(), createElementBlock("span", _hoisted_6)) : createCommentVNode("", true)
|
|
537
|
+
]),
|
|
538
|
+
createElementVNode("div", _hoisted_7, [
|
|
539
|
+
unref(bar).visible.value.length || showPlaceholder.value ? (openBlock(), createBlock(_sfc_main$1, {
|
|
540
|
+
id: "command-bar-list",
|
|
541
|
+
key: ((_a = unref(bar).page.value) == null ? void 0 : _a.id) ?? "root",
|
|
542
|
+
class: "max-h-[60vh] max-sm:max-h-[70vh] animate-fade-in-up motion-reduce:animate-none",
|
|
543
|
+
sections: unref(bar).sections.value,
|
|
544
|
+
items: unref(bar).visible.value,
|
|
545
|
+
"selected-index": unref(bar).selectedIndex.value,
|
|
546
|
+
pending: showPlaceholder.value,
|
|
547
|
+
onActivate: _cache[1] || (_cache[1] = ($event) => unref(bar).selectedIndex.value = $event),
|
|
548
|
+
onSelect: unref(bar).select
|
|
549
|
+
}, null, 8, ["sections", "items", "selected-index", "pending", "onSelect"])) : isEmpty.value ? (openBlock(), createElementBlock("p", _hoisted_8, toDisplayString(__props.emptyText), 1)) : createCommentVNode("", true)
|
|
550
|
+
]),
|
|
551
|
+
__props.hints.length ? (openBlock(), createElementBlock("div", _hoisted_9, [
|
|
552
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(__props.hints, (hint) => {
|
|
553
|
+
return openBlock(), createElementBlock("span", {
|
|
554
|
+
key: hint.label,
|
|
555
|
+
class: "flex items-center gap-1"
|
|
556
|
+
}, [
|
|
557
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(hint.keys, (key) => {
|
|
558
|
+
return openBlock(), createElementBlock("kbd", {
|
|
559
|
+
key,
|
|
560
|
+
class: "inline-flex items-center justify-center h-5 px-1 rounded min-w-5 text-jumpbar-muted-foreground bg-jumpbar-subtle outline outline-1 outline-jumpbar-border"
|
|
561
|
+
}, toDisplayString(key), 1);
|
|
562
|
+
}), 128)),
|
|
563
|
+
createElementVNode("span", _hoisted_10, toDisplayString(hint.label), 1)
|
|
564
|
+
]);
|
|
565
|
+
}), 128))
|
|
566
|
+
])) : createCommentVNode("", true)
|
|
567
|
+
], 42, _hoisted_1)
|
|
568
|
+
]);
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
export {
|
|
573
|
+
_sfc_main as CommandBar,
|
|
574
|
+
createCommandBar,
|
|
575
|
+
createFrecency,
|
|
576
|
+
fuzzyScore,
|
|
577
|
+
groupBySection,
|
|
578
|
+
normalize,
|
|
579
|
+
parseQuery,
|
|
580
|
+
rank,
|
|
581
|
+
useCommandBar
|
|
582
|
+
};
|
|
583
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/vue/useCommandBar.js","../src/vue/CommandBarList.vue","../src/vue/CommandBar.vue"],"sourcesContent":["import { computed, ref, shallowRef } from 'vue';\nimport { parseQuery } from '../core/parse';\nimport { rank, groupBySection } from '../core/rank';\n\nconst DEFAULT_DEBOUNCE = 120;\nconst DEFAULT_LIMIT = 100;\nconst NO_FRECENCY = { record: () => {}, score: () => 0 };\n\nexport const createCommandBar = (initialOptions = {}) => {\n const options = {\n prefixes: [],\n context: () => ({}),\n frecency: NO_FRECENCY,\n onSelect: () => {},\n onClose: () => {},\n onError: error => {\n throw error;\n },\n debounce: DEFAULT_DEBOUNCE,\n limit: DEFAULT_LIMIT,\n ...initialOptions,\n };\n\n let sources = [];\n let context = {};\n let controller = null;\n let timers = [];\n let sequence = 0;\n\n const isOpen = ref(false);\n const query = ref('');\n const pages = shallowRef([]);\n const items = shallowRef([]);\n const results = shallowRef([]);\n const selectedIndex = ref(0);\n const pendingCount = ref(0);\n\n const page = computed(() => pages.value.at(-1) ?? null);\n const isLoading = computed(() => pendingCount.value > 0);\n\n const sections = computed(() => {\n const list =\n page.value && results.value.length\n ? results.value\n : [...items.value, ...results.value];\n const seen = new Set();\n const unique = list.filter(item => {\n if (seen.has(item.id)) return false;\n seen.add(item.id);\n return true;\n });\n return groupBySection(unique);\n });\n const visible = computed(() =>\n sections.value.flatMap(section => section.items)\n );\n const selected = computed(() => visible.value[selectedIndex.value] ?? null);\n\n const buildRequest = () => {\n const { prefix, text } = parseQuery(query.value, options.prefixes);\n return {\n query: query.value,\n text,\n prefix,\n page: page.value?.id ?? null,\n context,\n };\n };\n\n const isActive = (source, request) =>\n request.text.length >= (source.minQuery ?? 0) &&\n (source.when?.(request) ?? true);\n\n const collectCommands = request =>\n sources.flatMap(source =>\n source.commands && isActive(source, request)\n ? source.commands(request)\n : []\n );\n\n const delayOf = source => source.debounce ?? options.debounce;\n\n const publish = batch => {\n results.value = batch.searchers.flatMap(\n searcher => batch.fresh.get(searcher.id) ?? []\n );\n selectedIndex.value = Math.min(\n selectedIndex.value,\n Math.max(visible.value.length - 1, 0)\n );\n };\n\n const runSearchers = (batch, delay) => {\n batch.searchers\n .filter(source => delayOf(source) === delay)\n .forEach(async source => {\n try {\n const list = await source.search({\n ...batch.request,\n signal: batch.signal,\n });\n if (batch.sequence !== sequence) return;\n batch.fresh.set(source.id, list);\n publish(batch);\n } catch (error) {\n if (batch.sequence !== sequence) return;\n batch.fresh.set(source.id, []);\n publish(batch);\n options.onError(error, source);\n } finally {\n if (batch.sequence === sequence) pendingCount.value -= 1;\n }\n });\n };\n\n const run = () => {\n timers.forEach(clearTimeout);\n timers = [];\n controller?.abort();\n sequence += 1;\n\n const request = buildRequest();\n if (!request.text) results.value = [];\n const ranked = rank(collectCommands(request), {\n text: request.text,\n prefix: request.prefix,\n page: request.page,\n scopes: context.scopes ?? [],\n frecency: options.frecency,\n });\n items.value = request.text ? ranked.slice(0, options.limit) : ranked;\n selectedIndex.value = 0;\n\n const searchRequest = { ...request, matches: items.value.length };\n const searchers = sources.filter(\n source => source.search && isActive(source, searchRequest)\n );\n pendingCount.value = searchers.length;\n if (!searchers.length) {\n results.value = [];\n return;\n }\n\n controller = new AbortController();\n const batch = {\n sequence,\n request: searchRequest,\n signal: controller.signal,\n fresh: new Map(),\n searchers,\n };\n new Set(searchers.map(delayOf)).forEach(delay => {\n timers.push(setTimeout(() => runSearchers(batch, delay), delay));\n });\n };\n\n const navigate = (nextPages, nextQuery = '') => {\n pages.value = nextPages;\n query.value = nextQuery;\n results.value = [];\n run();\n };\n\n const open = ({ page: pageId = null, query: initialQuery = '' } = {}) => {\n context = options.context();\n isOpen.value = true;\n pages.value = [];\n query.value = '';\n const target = pageId\n ? (collectCommands(buildRequest()).find(item => item.id === pageId) ?? {\n id: pageId,\n })\n : null;\n navigate(target ? [target] : [], initialQuery);\n };\n\n const close = () => {\n if (!isOpen.value) return;\n timers.forEach(clearTimeout);\n timers = [];\n controller?.abort();\n sequence += 1;\n pendingCount.value = 0;\n isOpen.value = false;\n options.onClose();\n };\n\n const back = () => {\n if (!page.value) return;\n navigate(pages.value.slice(0, -1));\n };\n\n const select = item => {\n if (!item) return;\n if (item.page) {\n navigate([...pages.value, item]);\n return;\n }\n if (!item.run) return;\n options.frecency.record(item.id);\n options.onSelect(item);\n close();\n item.run(context);\n };\n\n const setQuery = value => {\n query.value = value;\n run();\n };\n\n const moveSelection = delta => {\n const count = visible.value.length;\n if (!count) return;\n selectedIndex.value = (selectedIndex.value + delta + count) % count;\n };\n\n const toggle = () => (isOpen.value ? close() : open());\n\n const register = source => {\n sources = [...sources, source];\n return () => {\n sources = sources.filter(registered => registered !== source);\n };\n };\n\n const configure = overrides => Object.assign(options, overrides);\n\n const dispose = () => {\n close();\n sources = [];\n };\n\n return {\n isOpen,\n query,\n page,\n pages,\n visible,\n sections,\n selectedIndex,\n selected,\n isLoading,\n open,\n close,\n toggle,\n back,\n select,\n setQuery,\n moveSelection,\n register,\n configure,\n dispose,\n };\n};\n\nlet shared = null;\n\nexport const useCommandBar = () => {\n shared ??= createCommandBar();\n return shared;\n};\n","<script setup>\nimport { computed, reactive, ref, watch } from 'vue';\n\nconst props = defineProps({\n sections: { type: Array, required: true },\n items: { type: Array, required: true },\n selectedIndex: { type: Number, required: true },\n pending: { type: Boolean, default: false },\n});\n\nconst emit = defineEmits(['select', 'activate']);\n\nconst PLACEHOLDER_ROWS = 3;\n\nconst STATUS_CLASSES = {\n online: 'bg-jumpbar-online',\n busy: 'bg-jumpbar-busy',\n offline: 'bg-jumpbar-offline',\n};\n\nconst listRef = ref(null);\nconst failedImages = reactive(new Set());\nlet pointer = { x: null, y: null };\n\nconst onPointerMove = (event, index) => {\n if (event.clientX === pointer.x && event.clientY === pointer.y) return;\n pointer = { x: event.clientX, y: event.clientY };\n emit('activate', index);\n};\n\nconst indexes = computed(\n () => new Map(props.items.map((item, index) => [item.id, index]))\n);\n\nconst initials = name =>\n (name ?? '')\n .split(/\\s+/)\n .slice(0, 2)\n .map(word => [...word][0] ?? '')\n .join('')\n .toUpperCase();\n\nwatch(\n [() => props.selectedIndex, () => props.sections],\n ([index]) => {\n const list = listRef.value;\n if (!list) return;\n if (index === 0) list.scrollTop = 0;\n else {\n list\n .querySelector('[aria-selected=\"true\"]')\n ?.scrollIntoView({ block: 'nearest' });\n }\n },\n { flush: 'post' }\n);\n</script>\n\n<template>\n <div ref=\"listRef\" role=\"listbox\" class=\"px-2 py-2 overflow-y-auto\">\n <div\n v-for=\"section in sections\"\n :key=\"section.section ?? ''\"\n role=\"group\"\n :aria-label=\"section.section ?? undefined\"\n >\n <p\n v-if=\"section.section\"\n class=\"px-3 pt-3 pb-1 text-xs font-medium text-jumpbar-muted-foreground first:pt-1\"\n >\n {{ section.section }}\n </p>\n <div\n v-for=\"item in section.items\"\n :id=\"`command-bar-item-${item.id}`\"\n :key=\"item.id\"\n role=\"option\"\n :aria-selected=\"indexes.get(item.id) === selectedIndex\"\n class=\"relative flex items-center h-10 gap-3 px-3 text-sm rounded-lg cursor-pointer select-none text-jumpbar-foreground transition-colors duration-75\"\n :class=\"{\n 'bg-jumpbar-muted before:absolute before:inset-y-2 before:start-0 before:w-0.5 before:rounded-full before:bg-jumpbar-primary':\n indexes.get(item.id) === selectedIndex,\n }\"\n @mousedown.prevent\n @mousemove=\"onPointerMove($event, indexes.get(item.id))\"\n @click=\"emit('select', item)\"\n >\n <span\n v-if=\"item.avatar\"\n class=\"relative flex items-center justify-center flex-shrink-0 rounded-md size-6 bg-jumpbar-muted text-jumpbar-muted-foreground\"\n >\n <img\n v-if=\"item.avatar.src && !failedImages.has(item.avatar.src)\"\n :src=\"item.avatar.src\"\n :alt=\"item.avatar.name\"\n class=\"object-cover rounded-md size-6\"\n @error=\"failedImages.add(item.avatar.src)\"\n />\n <span v-else class=\"text-xs font-medium leading-none\">\n {{ initials(item.avatar.name) }}\n </span>\n <span\n v-if=\"item.avatar.status\"\n class=\"absolute -bottom-px -end-px border rounded-full size-2 border-jumpbar-background\"\n :class=\"STATUS_CLASSES[item.avatar.status]\"\n />\n </span>\n <span\n v-else\n class=\"flex items-center justify-center flex-shrink-0 size-6\"\n >\n <span\n class=\"size-4\"\n :class=\"[\n item.icon,\n indexes.get(item.id) === selectedIndex\n ? 'text-jumpbar-foreground'\n : 'text-jumpbar-muted-foreground',\n ]\"\n />\n </span>\n <span class=\"flex items-center flex-1 min-w-0 gap-1.5 overflow-hidden\">\n <template v-if=\"item.parentTitle\">\n <span class=\"truncate text-jumpbar-muted-foreground\">{{ item.parentTitle }}</span>\n <span\n class=\"flex-shrink-0 i-lucide-chevron-right size-3 text-jumpbar-subtle-foreground rtl:-scale-x-100\"\n />\n </template>\n <span class=\"flex-shrink-0 max-w-full truncate\">{{\n item.title\n }}</span>\n <span v-if=\"item.subtitle\" class=\"truncate text-jumpbar-muted-foreground\">\n {{ item.subtitle }}\n </span>\n </span>\n <span v-if=\"item.badge\" class=\"flex-shrink-0 text-xs text-jumpbar-muted-foreground\">\n {{ item.badge }}\n </span>\n <span v-if=\"item.shortcut\" class=\"flex items-center gap-1\">\n <kbd\n v-for=\"key in item.shortcut\"\n :key=\"key\"\n class=\"inline-flex items-center justify-center h-5 px-1 text-xs rounded min-w-5 text-jumpbar-muted-foreground bg-jumpbar-subtle outline outline-1 outline-jumpbar-border\"\n >\n {{ key }}\n </kbd>\n </span>\n <span\n v-if=\"item.page\"\n class=\"flex-shrink-0 i-lucide-chevron-right size-4 text-jumpbar-subtle-foreground rtl:-scale-x-100\"\n />\n </div>\n </div>\n <div\n v-if=\"pending\"\n aria-hidden=\"true\"\n class=\"animate-pulse motion-reduce:animate-none\"\n >\n <div class=\"px-3 pt-3 pb-1\">\n <span class=\"block w-20 h-3 rounded bg-jumpbar-muted\" />\n </div>\n <div\n v-for=\"row in PLACEHOLDER_ROWS\"\n :key=\"row\"\n class=\"flex items-center h-10 gap-3 px-3\"\n >\n <span class=\"flex-shrink-0 rounded-md size-6 bg-jumpbar-muted\" />\n <span class=\"h-3 rounded w-36 bg-jumpbar-muted\" />\n <span class=\"h-3 rounded w-24 bg-jumpbar-subtle\" />\n </div>\n </div>\n </div>\n</template>\n","<script setup>\nimport { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue';\nimport { useCommandBar } from './useCommandBar';\nimport CommandBarList from './CommandBarList.vue';\n\nconst props = defineProps({\n placeholder: { type: String, default: '' },\n emptyText: { type: String, default: '' },\n scopeLabel: { type: String, default: '' },\n hints: { type: Array, default: () => [] },\n dir: { type: String, default: 'ltr' },\n});\n\nconst CLOSE_ANIMATION_MS = 150;\nconst PLACEHOLDER_DELAY_MS = 150;\n\nconst bar = useCommandBar();\nconst dialogRef = ref(null);\nconst inputRef = ref(null);\nconst isClosing = ref(false);\nlet closeTimer = null;\n\nconst placeholder = computed(\n () => bar.page.value?.placeholder ?? props.placeholder\n);\nconst activeId = computed(() =>\n bar.selected.value ? `command-bar-item-${bar.selected.value.id}` : undefined\n);\nconst isEmpty = computed(\n () => !bar.visible.value.length && !bar.isLoading.value\n);\nconst showPlaceholder = ref(false);\nlet placeholderTimer = null;\n\nwatch(bar.isLoading, loading => {\n clearTimeout(placeholderTimer);\n if (!loading) {\n showPlaceholder.value = false;\n return;\n }\n placeholderTimer = setTimeout(() => {\n showPlaceholder.value = true;\n }, PLACEHOLDER_DELAY_MS);\n});\n\nconst KEY_ACTIONS = {\n ArrowDown: () => bar.moveSelection(1),\n ArrowUp: () => bar.moveSelection(-1),\n Enter: () => bar.select(bar.selected.value),\n Escape: () => bar.close(),\n Backspace: () => (bar.query.value ? false : bar.back()),\n};\n\nconst onKeydown = event => {\n if (event.isComposing || event.keyCode === 229) return;\n const action = KEY_ACTIONS[event.key];\n if (!action || action() === false) return;\n event.preventDefault();\n};\n\nwatch(bar.isOpen, async open => {\n clearTimeout(closeTimer);\n if (!open) {\n isClosing.value = true;\n closeTimer = setTimeout(() => {\n dialogRef.value.close();\n isClosing.value = false;\n }, CLOSE_ANIMATION_MS);\n return;\n }\n isClosing.value = false;\n if (!dialogRef.value.open) dialogRef.value.showModal();\n await nextTick();\n inputRef.value.focus();\n});\n\nonBeforeUnmount(() => {\n clearTimeout(closeTimer);\n clearTimeout(placeholderTimer);\n});\n</script>\n\n<template>\n <Teleport to=\"body\">\n <dialog\n ref=\"dialogRef\"\n :dir=\"dir\"\n class=\"w-[calc(100vw-2rem)] max-w-2xl p-0 mx-auto mt-[clamp(2rem,10vh,6rem)] mb-auto overflow-hidden shadow-2xl rounded-2xl bg-jumpbar-background outline outline-1 outline-jumpbar-ring text-jumpbar-foreground motion-reduce:animate-none backdrop:bg-jumpbar-overlay max-sm:w-full max-sm:max-w-full max-sm:mt-auto max-sm:mb-0 max-sm:rounded-b-none\"\n :class=\"\n isClosing\n ? 'animate-command-bar-out backdrop:animate-fade-out'\n : 'animate-pop-in backdrop:animate-fade-in'\n \"\n @cancel.prevent=\"bar.close\"\n @close=\"bar.close\"\n @click.self=\"bar.close\"\n >\n <div class=\"flex items-center gap-2 px-4 h-14\">\n <span class=\"flex-shrink-0 i-lucide-search size-4 text-jumpbar-subtle-foreground\" />\n <span\n v-if=\"scopeLabel\"\n class=\"flex-shrink-0 px-2 py-0.5 text-xs rounded-md bg-jumpbar-muted text-jumpbar-muted-foreground max-w-48 truncate\"\n >\n {{ scopeLabel }}\n </span>\n <span\n v-if=\"bar.page.value\"\n class=\"flex-shrink-0 px-2 py-0.5 text-xs font-medium rounded-md bg-jumpbar-primary-soft text-jumpbar-primary max-w-48 truncate\"\n >\n {{ bar.page.value.title }}\n </span>\n <input\n ref=\"inputRef\"\n role=\"combobox\"\n aria-autocomplete=\"list\"\n aria-controls=\"command-bar-list\"\n :aria-expanded=\"bar.isOpen.value\"\n :aria-activedescendant=\"activeId\"\n :value=\"bar.query.value\"\n :placeholder=\"placeholder\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n class=\"flex-1 h-full min-w-0 text-base bg-transparent border-0 outline-none text-jumpbar-foreground placeholder:text-jumpbar-subtle-foreground\"\n @input=\"bar.setQuery($event.target.value)\"\n @keydown=\"onKeydown\"\n />\n <span\n v-if=\"bar.isLoading.value\"\n class=\"flex-shrink-0 i-lucide-loader-circle size-4 animate-spin text-jumpbar-subtle-foreground\"\n />\n </div>\n <div class=\"border-t border-jumpbar-border\">\n <CommandBarList\n v-if=\"bar.visible.value.length || showPlaceholder\"\n id=\"command-bar-list\"\n :key=\"bar.page.value?.id ?? 'root'\"\n class=\"max-h-[60vh] max-sm:max-h-[70vh] animate-fade-in-up motion-reduce:animate-none\"\n :sections=\"bar.sections.value\"\n :items=\"bar.visible.value\"\n :selected-index=\"bar.selectedIndex.value\"\n :pending=\"showPlaceholder\"\n @activate=\"bar.selectedIndex.value = $event\"\n @select=\"bar.select\"\n />\n <p\n v-else-if=\"isEmpty\"\n class=\"px-4 py-10 text-sm text-center text-jumpbar-muted-foreground\"\n >\n {{ emptyText }}\n </p>\n </div>\n <div\n v-if=\"hints.length\"\n class=\"flex items-center gap-4 px-4 text-xs border-t h-9 border-jumpbar-border text-jumpbar-subtle-foreground\"\n >\n <span\n v-for=\"hint in hints\"\n :key=\"hint.label\"\n class=\"flex items-center gap-1\"\n >\n <kbd\n v-for=\"key in hint.keys\"\n :key=\"key\"\n class=\"inline-flex items-center justify-center h-5 px-1 rounded min-w-5 text-jumpbar-muted-foreground bg-jumpbar-subtle outline outline-1 outline-jumpbar-border\"\n >\n {{ key }}\n </kbd>\n <span class=\"ms-0.5\">{{ hint.label }}</span>\n </span>\n </div>\n </dialog>\n </Teleport>\n</template>\n"],"names":["_createElementBlock","_Fragment","_renderList","_openBlock","_hoisted_2","_toDisplayString","_hoisted_4","_hoisted_6","_normalizeClass","_hoisted_7","_createElementVNode","_hoisted_8","_hoisted_9","_hoisted_10","_createBlock","_Teleport","_unref","CommandBarList"],"mappings":"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"}
|