routie dev init since i didn't adhere to any proper guidance up until now
This commit is contained in:
+34
@@ -0,0 +1,34 @@
|
||||
@layer vuetify-components {
|
||||
.v-command-palette > .v-overlay__content > .v-sheet {
|
||||
display: flex;
|
||||
flex: 1 1 100%;
|
||||
flex-direction: column;
|
||||
}
|
||||
.v-command-palette--density-default .v-command-palette__input-container {
|
||||
padding-block: 8px;
|
||||
padding-inline: 16px;
|
||||
}
|
||||
.v-command-palette--density-comfortable .v-command-palette__input-container {
|
||||
padding-block: 4px;
|
||||
padding-inline: 12px;
|
||||
}
|
||||
.v-command-palette--density-compact .v-command-palette__input-container {
|
||||
padding-block: 0px;
|
||||
padding-inline: 8px;
|
||||
}
|
||||
.v-command-palette__content {
|
||||
overflow-y: auto;
|
||||
}
|
||||
.v-command-palette__no-data {
|
||||
opacity: 0.6;
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
.v-command-palette__list .v-list-subheader {
|
||||
opacity: 0.7;
|
||||
user-select: none;
|
||||
}
|
||||
.v-command-palette__list .v-divider {
|
||||
margin-block: 4px;
|
||||
}
|
||||
}
|
||||
+4634
File diff suppressed because it is too large
Load Diff
+313
@@ -0,0 +1,313 @@
|
||||
import { createVNode as _createVNode, createElementVNode as _createElementVNode, mergeProps as _mergeProps, normalizeClass as _normalizeClass, normalizeStyle as _normalizeStyle } from "vue";
|
||||
// Styles
|
||||
import "./VCommandPalette.css";
|
||||
|
||||
// Components
|
||||
import { VCommandPaletteSymbol } from "./shared.js";
|
||||
import { VCommandPaletteItem } from "./VCommandPaletteItem.js";
|
||||
import { VDialog } from "../../components/VDialog/index.js";
|
||||
import { makeVDialogProps } from "../../components/VDialog/VDialog.js";
|
||||
import { VList } from "../../components/VList/index.js";
|
||||
import { VSheet } from "../../components/VSheet/index.js";
|
||||
import { VTextField } from "../../components/VTextField/index.js"; // Composables
|
||||
import { useCommandPaletteNavigation } from "./composables/useCommandPaletteNavigation.js";
|
||||
import { makeDensityProps } from "../../composables/density.js";
|
||||
import { makeFilterProps, useFilter } from "../../composables/filter.js";
|
||||
import { useHotkey } from "../../composables/hotkey/index.js";
|
||||
import { useLocale } from "../../composables/locale.js";
|
||||
import { useProxiedModel } from "../../composables/proxiedModel.js"; // Utilities
|
||||
import { computed, nextTick, onUnmounted, provide, ref, shallowRef, toRef, watch, watchEffect } from 'vue';
|
||||
import { isActionItem } from "./types.js";
|
||||
import { genericComponent, omit, propsFactory, useRender } from "../../util/index.js"; // Types
|
||||
export const makeVCommandPaletteProps = propsFactory({
|
||||
modelValue: Boolean,
|
||||
search: String,
|
||||
items: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '$vuetify.command.search'
|
||||
},
|
||||
inputIcon: {
|
||||
type: String,
|
||||
default: '$search'
|
||||
},
|
||||
hotkey: String,
|
||||
closeOnSelect: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
noDataText: {
|
||||
type: String,
|
||||
default: '$vuetify.noDataText'
|
||||
},
|
||||
listProps: Object,
|
||||
...makeFilterProps({
|
||||
filterKeys: ['title', 'subtitle']
|
||||
}),
|
||||
...makeDensityProps(),
|
||||
...omit(makeVDialogProps({
|
||||
maxWidth: 500
|
||||
}), ['modelValue'])
|
||||
}, 'VCommandPalette');
|
||||
export const VCommandPalette = genericComponent()({
|
||||
name: 'VCommandPalette',
|
||||
props: makeVCommandPaletteProps(),
|
||||
emits: {
|
||||
'update:modelValue': value => true,
|
||||
'update:search': value => true,
|
||||
'click:item': (item, event) => true,
|
||||
'before-select': payload => true
|
||||
},
|
||||
setup(props, {
|
||||
emit,
|
||||
slots
|
||||
}) {
|
||||
const {
|
||||
t
|
||||
} = useLocale();
|
||||
const isOpen = useProxiedModel(props, 'modelValue');
|
||||
const searchQuery = useProxiedModel(props, 'search');
|
||||
const searchInputRef = ref();
|
||||
const dialogRef = ref();
|
||||
const previouslyFocusedElement = shallowRef(null);
|
||||
const internalItems = computed(() => props.items.map((item, index) => ({
|
||||
value: index,
|
||||
type: item.type,
|
||||
raw: item,
|
||||
...('title' in item && {
|
||||
title: item.title
|
||||
}),
|
||||
...('subtitle' in item && {
|
||||
subtitle: item.subtitle
|
||||
})
|
||||
})));
|
||||
const {
|
||||
filteredItems: filterResult
|
||||
} = useFilter(props, internalItems, searchQuery);
|
||||
const filteredItems = computed(() => filterResult.value.map(item => item.raw));
|
||||
const itemsForList = computed(() => {
|
||||
return filteredItems.value.map((item, idx) => ({
|
||||
...item,
|
||||
value: idx
|
||||
}));
|
||||
});
|
||||
function executeItem(item, event) {
|
||||
if ('onClick' in item && item.onClick) {
|
||||
item.onClick(event, item.value);
|
||||
}
|
||||
emit('click:item', item, event);
|
||||
if (!isActionItem(item) || !props.closeOnSelect) return;
|
||||
let shouldClose = true;
|
||||
emit('before-select', {
|
||||
item,
|
||||
event,
|
||||
preventDefault: () => {
|
||||
shouldClose = false;
|
||||
}
|
||||
});
|
||||
if (shouldClose) {
|
||||
isOpen.value = false;
|
||||
}
|
||||
}
|
||||
const navigation = useCommandPaletteNavigation({
|
||||
filteredItems,
|
||||
onItemClick: (item, event) => {
|
||||
executeItem(item, event);
|
||||
}
|
||||
});
|
||||
provide(VCommandPaletteSymbol, {
|
||||
items: computed(() => props.items),
|
||||
filteredItems,
|
||||
selectedIndex: navigation.selectedIndex,
|
||||
search: searchQuery,
|
||||
setSelectedIndex: navigation.setSelectedIndex
|
||||
});
|
||||
|
||||
// Register main hotkey with cleanup - using toRef for reactivity
|
||||
const hotkeyUnsubscribe = useHotkey(toRef(props, 'hotkey'), () => {
|
||||
isOpen.value = !isOpen.value;
|
||||
});
|
||||
watchEffect(onCleanup => {
|
||||
if (!isOpen.value) {
|
||||
return;
|
||||
}
|
||||
const hotkeyUnsubscribes = [];
|
||||
function registerItemHotkeys(items) {
|
||||
items.forEach(item => {
|
||||
if (isActionItem(item) && item.hotkey) {
|
||||
const unsubscribe = useHotkey(item.hotkey, event => {
|
||||
event.preventDefault();
|
||||
executeItem(item, event);
|
||||
}, {
|
||||
inputs: true
|
||||
});
|
||||
hotkeyUnsubscribes.push(unsubscribe);
|
||||
}
|
||||
});
|
||||
}
|
||||
registerItemHotkeys(props.items);
|
||||
onCleanup(() => {
|
||||
hotkeyUnsubscribes.forEach(unsubscribe => unsubscribe?.());
|
||||
});
|
||||
});
|
||||
function findNextSelectableIndex(startIndex, direction) {
|
||||
const items = filteredItems.value;
|
||||
if (items.length === 0) return -1;
|
||||
let index = startIndex;
|
||||
const maxIterations = items.length;
|
||||
for (let i = 0; i < maxIterations; i++) {
|
||||
index += direction;
|
||||
if (index >= items.length) index = 0;
|
||||
if (index < 0) index = items.length - 1;
|
||||
if (isActionItem(items[index])) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
function handleSearchKeydown(e) {
|
||||
switch (e.key) {
|
||||
case 'ArrowDown':
|
||||
{
|
||||
e.preventDefault();
|
||||
const nextIndex = findNextSelectableIndex(navigation.selectedIndex.value, 1);
|
||||
if (nextIndex !== -1) {
|
||||
navigation.setSelectedIndex(nextIndex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'ArrowUp':
|
||||
{
|
||||
e.preventDefault();
|
||||
const prevIndex = findNextSelectableIndex(navigation.selectedIndex.value, -1);
|
||||
if (prevIndex !== -1) {
|
||||
navigation.setSelectedIndex(prevIndex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'Enter':
|
||||
e.preventDefault();
|
||||
navigation.executeSelected(e);
|
||||
break;
|
||||
case 'Escape':
|
||||
e.preventDefault();
|
||||
isOpen.value = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
watch(isOpen, (newValue, oldValue) => {
|
||||
if (newValue && !oldValue) {
|
||||
previouslyFocusedElement.value = document.activeElement;
|
||||
searchQuery.value = '';
|
||||
navigation.reset();
|
||||
|
||||
// Use requestAnimationFrame to ensure DOM is fully rendered
|
||||
nextTick(() => {
|
||||
requestAnimationFrame(() => {
|
||||
if (searchInputRef.value && typeof searchInputRef.value.focus === 'function') {
|
||||
searchInputRef.value.focus();
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (!newValue && oldValue) {
|
||||
nextTick(() => {
|
||||
previouslyFocusedElement.value?.focus({
|
||||
preventScroll: true
|
||||
});
|
||||
previouslyFocusedElement.value = null;
|
||||
});
|
||||
}
|
||||
}, {
|
||||
immediate: true
|
||||
});
|
||||
onUnmounted(() => {
|
||||
hotkeyUnsubscribe();
|
||||
previouslyFocusedElement.value = null;
|
||||
});
|
||||
useRender(() => {
|
||||
const dialogProps = VDialog.filterProps(omit(props, ['modelValue', 'class', 'style']));
|
||||
return _createVNode(VDialog, _mergeProps({
|
||||
"ref": dialogRef,
|
||||
"class": "v-command-palette",
|
||||
"modelValue": isOpen.value,
|
||||
"onUpdate:modelValue": $event => isOpen.value = $event,
|
||||
"scrollable": true
|
||||
}, dialogProps), {
|
||||
activator: slots.activator,
|
||||
default: () => _createVNode(VSheet, {
|
||||
"class": _normalizeClass(props.class),
|
||||
"style": _normalizeStyle(props.style)
|
||||
}, {
|
||||
default: () => [slots.prepend?.(), _createElementVNode("div", {
|
||||
"class": "v-command-palette__input-container"
|
||||
}, [slots.input?.() ?? _createVNode(VTextField, {
|
||||
"ref": searchInputRef,
|
||||
"modelValue": searchQuery.value,
|
||||
"onUpdate:modelValue": $event => searchQuery.value = $event,
|
||||
"density": props.density,
|
||||
"placeholder": t(props.placeholder),
|
||||
"prependInnerIcon": props.inputIcon,
|
||||
"autocomplete": "off",
|
||||
"autofocus": true,
|
||||
"singleLine": true,
|
||||
"hideDetails": true,
|
||||
"variant": "solo",
|
||||
"flat": true,
|
||||
"bgColor": "transparent",
|
||||
"onKeydown": handleSearchKeydown
|
||||
}, {
|
||||
'append-inner': slots['input.append-inner']
|
||||
})]), _createElementVNode("div", {
|
||||
"class": "v-command-palette__content"
|
||||
}, [filteredItems.value.length > 0 ? _createVNode(VList, _mergeProps({
|
||||
"key": "list",
|
||||
"class": "v-command-palette__list",
|
||||
"density": props.density,
|
||||
"items": itemsForList.value,
|
||||
"itemType": "type",
|
||||
"itemProps": true,
|
||||
"activatable": true
|
||||
}, props.listProps, {
|
||||
"navigationStrategy": "track",
|
||||
"navigationIndex": navigation.selectedIndex.value,
|
||||
"onUpdate:navigationIndex": navigation.setSelectedIndex
|
||||
}), {
|
||||
prepend: slots['list.prepend'],
|
||||
subheader: slots['list.subheader'],
|
||||
item: ({
|
||||
props: itemProps
|
||||
}) => slots.item?.({
|
||||
item: itemProps,
|
||||
index: itemProps.index
|
||||
}) ?? _createVNode(VCommandPaletteItem, {
|
||||
"key": `item-${itemProps.index}`,
|
||||
"item": itemProps,
|
||||
"index": itemProps.index,
|
||||
"onExecute": event => navigation.execute(itemProps.index, event)
|
||||
}, {
|
||||
prepend: slots['item.prepend'] ? () => slots['item.prepend']?.({
|
||||
item: itemProps,
|
||||
index: itemProps.index
|
||||
}) : undefined,
|
||||
title: slots['item.title'] ? () => slots['item.title']?.({
|
||||
item: itemProps,
|
||||
index: itemProps.index
|
||||
}) : undefined,
|
||||
append: slots['item.append'] ? () => slots['item.append']?.({
|
||||
item: itemProps,
|
||||
index: itemProps.index
|
||||
}) : undefined
|
||||
})
|
||||
}) : _createElementVNode("div", {
|
||||
"key": "no-data",
|
||||
"class": "v-command-palette__no-data"
|
||||
}, [slots['no-data']?.() || t(props.noDataText)])]), slots.append?.()]
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
//# sourceMappingURL=VCommandPalette.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+42
@@ -0,0 +1,42 @@
|
||||
@use '../../styles/tools';
|
||||
@use './variables' as *;
|
||||
|
||||
@include tools.layer('components') {
|
||||
.v-command-palette {
|
||||
> .v-overlay__content > .v-sheet {
|
||||
display: flex;
|
||||
flex: 1 1 100%;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@at-root {
|
||||
@include tools.density('v-command-palette', $command-palette-density) using ($modifier) {
|
||||
.v-command-palette__input-container {
|
||||
padding-block: $command-palette-input-padding-block + $modifier;
|
||||
padding-inline: $command-palette-input-padding-inline + $modifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
&__no-data {
|
||||
opacity: $command-palette-no-data-opacity;
|
||||
padding: $command-palette-no-data-padding;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__list {
|
||||
.v-list-subheader {
|
||||
opacity: $command-palette-subheader-opacity;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.v-divider {
|
||||
margin-block: $command-palette-divider-margin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
import type { PropType } from 'vue';
|
||||
import type { VCommandPaletteActionItem } from './types.js';
|
||||
export declare const makeVCommandPaletteItemProps: <Defaults extends {
|
||||
item?: unknown;
|
||||
index?: unknown;
|
||||
onExecute?: unknown;
|
||||
} = {}>(defaults?: Defaults | undefined) => {
|
||||
item: unknown extends Defaults["item"] ? {
|
||||
type: PropType<VCommandPaletteActionItem>;
|
||||
required: true;
|
||||
} : Omit<{
|
||||
type: PropType<VCommandPaletteActionItem>;
|
||||
required: true;
|
||||
}, "default" | "type"> & {
|
||||
type: PropType<unknown extends Defaults["item"] ? VCommandPaletteActionItem : VCommandPaletteActionItem | Defaults["item"]>;
|
||||
default: unknown extends Defaults["item"] ? VCommandPaletteActionItem : VCommandPaletteActionItem | Defaults["item"];
|
||||
};
|
||||
index: unknown extends Defaults["index"] ? {
|
||||
type: NumberConstructor;
|
||||
required: true;
|
||||
} : Omit<{
|
||||
type: NumberConstructor;
|
||||
required: true;
|
||||
}, "default" | "type"> & {
|
||||
type: PropType<unknown extends Defaults["index"] ? number : number | Defaults["index"]>;
|
||||
default: unknown extends Defaults["index"] ? number : number | Defaults["index"];
|
||||
};
|
||||
onExecute: unknown extends Defaults["onExecute"] ? PropType<(event: MouseEvent | KeyboardEvent) => void> : {
|
||||
type: PropType<unknown extends Defaults["onExecute"] ? (event: MouseEvent | KeyboardEvent) => void : ((event: MouseEvent | KeyboardEvent) => void) | Defaults["onExecute"]>;
|
||||
default: unknown extends Defaults["onExecute"] ? (event: MouseEvent | KeyboardEvent) => void : ((event: MouseEvent | KeyboardEvent) => void) | Defaults["onExecute"];
|
||||
};
|
||||
};
|
||||
export type VCommandPaletteItemSlots = {
|
||||
prepend: never;
|
||||
title: never;
|
||||
append: never;
|
||||
};
|
||||
export declare const VCommandPaletteItem: {
|
||||
new (...args: any[]): import("vue").CreateComponentPublicInstanceWithMixins<{
|
||||
item: VCommandPaletteActionItem;
|
||||
index: number;
|
||||
} & {
|
||||
onExecute?: ((event: MouseEvent | KeyboardEvent) => void) | undefined;
|
||||
} & {
|
||||
$children?: {
|
||||
prepend?: (() => import("vue").VNodeChild) | undefined;
|
||||
title?: (() => import("vue").VNodeChild) | undefined;
|
||||
append?: (() => import("vue").VNodeChild) | undefined;
|
||||
} | {
|
||||
$stable?: boolean;
|
||||
} | {} | import("vue").VNodeChild;
|
||||
'v-slots'?: {
|
||||
prepend?: false | (() => import("vue").VNodeChild) | undefined;
|
||||
title?: false | (() => import("vue").VNodeChild) | undefined;
|
||||
append?: false | (() => import("vue").VNodeChild) | undefined;
|
||||
} | undefined;
|
||||
} & {
|
||||
"v-slot:append"?: false | (() => import("vue").VNodeChild) | undefined;
|
||||
"v-slot:prepend"?: false | (() => import("vue").VNodeChild) | undefined;
|
||||
"v-slot:title"?: false | (() => import("vue").VNodeChild) | undefined;
|
||||
}, void, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, Record<string, any>, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, {}, true, {}, import("vue").SlotsType<Partial<{
|
||||
prepend: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
||||
[key: string]: any;
|
||||
}>[];
|
||||
title: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
||||
[key: string]: any;
|
||||
}>[];
|
||||
append: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
||||
[key: string]: any;
|
||||
}>[];
|
||||
}>>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, {}, any, import("vue").ComponentProvideOptions, {
|
||||
P: {};
|
||||
B: {};
|
||||
D: {};
|
||||
C: {};
|
||||
M: {};
|
||||
Defaults: {};
|
||||
}, {
|
||||
item: VCommandPaletteActionItem;
|
||||
index: number;
|
||||
} & {
|
||||
onExecute?: ((event: MouseEvent | KeyboardEvent) => void) | undefined;
|
||||
} & {
|
||||
$children?: {
|
||||
prepend?: (() => import("vue").VNodeChild) | undefined;
|
||||
title?: (() => import("vue").VNodeChild) | undefined;
|
||||
append?: (() => import("vue").VNodeChild) | undefined;
|
||||
} | {
|
||||
$stable?: boolean;
|
||||
} | {} | import("vue").VNodeChild;
|
||||
'v-slots'?: {
|
||||
prepend?: false | (() => import("vue").VNodeChild) | undefined;
|
||||
title?: false | (() => import("vue").VNodeChild) | undefined;
|
||||
append?: false | (() => import("vue").VNodeChild) | undefined;
|
||||
} | undefined;
|
||||
} & {
|
||||
"v-slot:append"?: false | (() => import("vue").VNodeChild) | undefined;
|
||||
"v-slot:prepend"?: false | (() => import("vue").VNodeChild) | undefined;
|
||||
"v-slot:title"?: false | (() => import("vue").VNodeChild) | undefined;
|
||||
}, {}, {}, {}, {}, {}>;
|
||||
__isFragment?: never;
|
||||
__isTeleport?: never;
|
||||
__isSuspense?: never;
|
||||
} & import("vue").ComponentOptionsBase<{
|
||||
item: VCommandPaletteActionItem;
|
||||
index: number;
|
||||
} & {
|
||||
onExecute?: ((event: MouseEvent | KeyboardEvent) => void) | undefined;
|
||||
} & {
|
||||
$children?: {
|
||||
prepend?: (() => import("vue").VNodeChild) | undefined;
|
||||
title?: (() => import("vue").VNodeChild) | undefined;
|
||||
append?: (() => import("vue").VNodeChild) | undefined;
|
||||
} | {
|
||||
$stable?: boolean;
|
||||
} | {} | import("vue").VNodeChild;
|
||||
'v-slots'?: {
|
||||
prepend?: false | (() => import("vue").VNodeChild) | undefined;
|
||||
title?: false | (() => import("vue").VNodeChild) | undefined;
|
||||
append?: false | (() => import("vue").VNodeChild) | undefined;
|
||||
} | undefined;
|
||||
} & {
|
||||
"v-slot:append"?: false | (() => import("vue").VNodeChild) | undefined;
|
||||
"v-slot:prepend"?: false | (() => import("vue").VNodeChild) | undefined;
|
||||
"v-slot:title"?: false | (() => import("vue").VNodeChild) | undefined;
|
||||
}, void, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, Record<string, any>, string, {}, {}, string, import("vue").SlotsType<Partial<{
|
||||
prepend: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
||||
[key: string]: any;
|
||||
}>[];
|
||||
title: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
||||
[key: string]: any;
|
||||
}>[];
|
||||
append: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
||||
[key: string]: any;
|
||||
}>[];
|
||||
}>>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, import("vue").ComponentProvideOptions> & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps & import("../../util/index.js").FilterPropsOptions<{
|
||||
item: {
|
||||
type: PropType<VCommandPaletteActionItem>;
|
||||
required: true;
|
||||
};
|
||||
index: {
|
||||
type: NumberConstructor;
|
||||
required: true;
|
||||
};
|
||||
onExecute: PropType<(event: MouseEvent | KeyboardEvent) => void>;
|
||||
}, import("vue").ExtractPropTypes<{
|
||||
item: {
|
||||
type: PropType<VCommandPaletteActionItem>;
|
||||
required: true;
|
||||
};
|
||||
index: {
|
||||
type: NumberConstructor;
|
||||
required: true;
|
||||
};
|
||||
onExecute: PropType<(event: MouseEvent | KeyboardEvent) => void>;
|
||||
}>>;
|
||||
export type VCommandPaletteItem = InstanceType<typeof VCommandPaletteItem>;
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import { createVNode as _createVNode } from "vue";
|
||||
// Components
|
||||
import { VHotkey } from "../../components/VHotkey/index.js";
|
||||
import { VListItem } from "../../components/VList/index.js"; // Utilities
|
||||
import { genericComponent, propsFactory, useRender } from "../../util/index.js"; // Types
|
||||
export const makeVCommandPaletteItemProps = propsFactory({
|
||||
item: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
onExecute: Function
|
||||
}, 'VCommandPaletteItem');
|
||||
export const VCommandPaletteItem = genericComponent()({
|
||||
name: 'VCommandPaletteItem',
|
||||
props: makeVCommandPaletteItemProps(),
|
||||
setup(props, {
|
||||
slots
|
||||
}) {
|
||||
useRender(() => _createVNode(VListItem, {
|
||||
"index": props.index,
|
||||
"value": props.item.value,
|
||||
"title": props.item.title,
|
||||
"subtitle": props.item.subtitle,
|
||||
"prependIcon": props.item.prependIcon,
|
||||
"prependAvatar": props.item.prependAvatar,
|
||||
"appendIcon": props.item.appendIcon,
|
||||
"appendAvatar": props.item.appendAvatar,
|
||||
"onClick": props.onExecute
|
||||
}, {
|
||||
prepend: slots.prepend,
|
||||
title: slots.title,
|
||||
append: slots.append ?? (props.item.hotkey ? () => _createVNode(VHotkey, {
|
||||
"keys": props.item.hotkey
|
||||
}, null) : undefined)
|
||||
}));
|
||||
}
|
||||
});
|
||||
//# sourceMappingURL=VCommandPaletteItem.js.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"VCommandPaletteItem.js","names":["VHotkey","VListItem","genericComponent","propsFactory","useRender","makeVCommandPaletteItemProps","item","type","Object","required","index","Number","onExecute","Function","VCommandPaletteItem","name","props","setup","slots","_createVNode","value","title","subtitle","prependIcon","prependAvatar","appendIcon","appendAvatar","prepend","append","hotkey","undefined"],"sources":["../../../src/labs/VCommandPalette/VCommandPaletteItem.tsx"],"sourcesContent":["// Components\nimport { VHotkey } from '@/components/VHotkey'\nimport { VListItem } from '@/components/VList'\n\n// Utilities\nimport { genericComponent, propsFactory, useRender } from '@/util'\n\n// Types\nimport type { PropType } from 'vue'\nimport type { VCommandPaletteActionItem } from './types'\n\nexport const makeVCommandPaletteItemProps = propsFactory({\n item: {\n type: Object as PropType<VCommandPaletteActionItem>,\n required: true,\n },\n index: {\n type: Number,\n required: true,\n },\n onExecute: Function as PropType<(event: MouseEvent | KeyboardEvent) => void>,\n}, 'VCommandPaletteItem')\n\nexport type VCommandPaletteItemSlots = {\n prepend: never\n title: never\n append: never\n}\n\nexport const VCommandPaletteItem = genericComponent<VCommandPaletteItemSlots>()({\n name: 'VCommandPaletteItem',\n\n props: makeVCommandPaletteItemProps(),\n\n setup (props, { slots }) {\n useRender(() => (\n <VListItem\n index={ props.index }\n value={ props.item.value }\n title={ props.item.title }\n subtitle={ props.item.subtitle }\n prependIcon={ props.item.prependIcon }\n prependAvatar={ props.item.prependAvatar }\n appendIcon={ props.item.appendIcon }\n appendAvatar={ props.item.appendAvatar }\n onClick={ props.onExecute }\n v-slots={{\n prepend: slots.prepend,\n title: slots.title,\n append: slots.append ?? (props.item.hotkey ? () => <VHotkey keys={ props.item.hotkey } /> : undefined),\n }}\n />\n ))\n },\n})\n\nexport type VCommandPaletteItem = InstanceType<typeof VCommandPaletteItem>\n"],"mappings":";AAAA;AAAA,SACSA,OAAO;AAAA,SACPC,SAAS,2CAElB;AAAA,SACSC,gBAAgB,EAAEC,YAAY,EAAEC,SAAS,+BAElD;AAIA,OAAO,MAAMC,4BAA4B,GAAGF,YAAY,CAAC;EACvDG,IAAI,EAAE;IACJC,IAAI,EAAEC,MAA6C;IACnDC,QAAQ,EAAE;EACZ,CAAC;EACDC,KAAK,EAAE;IACLH,IAAI,EAAEI,MAAM;IACZF,QAAQ,EAAE;EACZ,CAAC;EACDG,SAAS,EAAEC;AACb,CAAC,EAAE,qBAAqB,CAAC;AAQzB,OAAO,MAAMC,mBAAmB,GAAGZ,gBAAgB,CAA2B,CAAC,CAAC;EAC9Ea,IAAI,EAAE,qBAAqB;EAE3BC,KAAK,EAAEX,4BAA4B,CAAC,CAAC;EAErCY,KAAKA,CAAED,KAAK,EAAE;IAAEE;EAAM,CAAC,EAAE;IACvBd,SAAS,CAAC,MAAAe,YAAA,CAAAlB,SAAA;MAAA,SAEEe,KAAK,CAACN,KAAK;MAAA,SACXM,KAAK,CAACV,IAAI,CAACc,KAAK;MAAA,SAChBJ,KAAK,CAACV,IAAI,CAACe,KAAK;MAAA,YACbL,KAAK,CAACV,IAAI,CAACgB,QAAQ;MAAA,eAChBN,KAAK,CAACV,IAAI,CAACiB,WAAW;MAAA,iBACpBP,KAAK,CAACV,IAAI,CAACkB,aAAa;MAAA,cAC3BR,KAAK,CAACV,IAAI,CAACmB,UAAU;MAAA,gBACnBT,KAAK,CAACV,IAAI,CAACoB,YAAY;MAAA,WAC5BV,KAAK,CAACJ;IAAS,GAChB;MACPe,OAAO,EAAET,KAAK,CAACS,OAAO;MACtBN,KAAK,EAAEH,KAAK,CAACG,KAAK;MAClBO,MAAM,EAAEV,KAAK,CAACU,MAAM,KAAKZ,KAAK,CAACV,IAAI,CAACuB,MAAM,GAAG,MAAAV,YAAA,CAAAnB,OAAA;QAAA,QAAsBgB,KAAK,CAACV,IAAI,CAACuB;MAAM,QAAK,GAAGC,SAAS;IACvG,CAAC,CAEJ,CAAC;EACJ;AACF,CAAC,CAAC","ignoreList":[]}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
$command-palette-density: ('default': 0, 'comfortable': -1, 'compact': -2) !default;
|
||||
$command-palette-input-padding-block: 8px !default;
|
||||
$command-palette-input-padding-inline: 16px !default;
|
||||
$command-palette-no-data-padding: 16px !default;
|
||||
$command-palette-no-data-opacity: 0.6 !default;
|
||||
$command-palette-subheader-opacity: 0.7 !default;
|
||||
$command-palette-divider-margin: 4px !default;
|
||||
Generated
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
import type { ComputedRef, Ref } from 'vue';
|
||||
import type { VCommandPaletteItem } from '../types.js';
|
||||
export interface UseCommandPaletteNavigationOptions {
|
||||
filteredItems: ComputedRef<VCommandPaletteItem[]>;
|
||||
onItemClick: (item: VCommandPaletteItem, event: KeyboardEvent | MouseEvent) => void;
|
||||
}
|
||||
export interface UseCommandPaletteNavigationReturn {
|
||||
selectedIndex: Readonly<Ref<number>>;
|
||||
getSelectedItem: () => VCommandPaletteItem | undefined;
|
||||
execute: (index: number, event: KeyboardEvent | MouseEvent) => void;
|
||||
executeSelected: (event: KeyboardEvent | MouseEvent) => void;
|
||||
reset: () => void;
|
||||
setSelectedIndex: (index: number) => void;
|
||||
}
|
||||
export declare function useCommandPaletteNavigation(options: UseCommandPaletteNavigationOptions): UseCommandPaletteNavigationReturn;
|
||||
Generated
Vendored
+89
@@ -0,0 +1,89 @@
|
||||
// Utilities
|
||||
import { readonly, ref, shallowRef, watch } from 'vue';
|
||||
|
||||
// Types
|
||||
import { isActionItem } from "../types.js";
|
||||
function getItemKey(item) {
|
||||
if (!isActionItem(item)) return undefined;
|
||||
return item.value !== undefined ? String(item.value) : item.title;
|
||||
}
|
||||
function findFirstSelectableIndex(items) {
|
||||
return items.findIndex(item => isActionItem(item));
|
||||
}
|
||||
export function useCommandPaletteNavigation(options) {
|
||||
const selectedIndex = ref(0);
|
||||
const selectedItemKey = shallowRef(undefined);
|
||||
watch(() => options.filteredItems.value, (newItems, oldItems) => {
|
||||
if (newItems.length === 0) {
|
||||
selectedIndex.value = -1;
|
||||
selectedItemKey.value = undefined;
|
||||
return;
|
||||
}
|
||||
if (selectedItemKey.value !== undefined) {
|
||||
const newIndex = newItems.findIndex(item => isActionItem(item) && getItemKey(item) === selectedItemKey.value);
|
||||
if (newIndex !== -1) {
|
||||
selectedIndex.value = newIndex;
|
||||
return;
|
||||
}
|
||||
}
|
||||
const firstSelectableIndex = findFirstSelectableIndex(newItems);
|
||||
if (firstSelectableIndex !== -1) {
|
||||
selectedIndex.value = firstSelectableIndex;
|
||||
selectedItemKey.value = getItemKey(newItems[firstSelectableIndex]);
|
||||
return;
|
||||
}
|
||||
selectedIndex.value = 0;
|
||||
selectedItemKey.value = undefined;
|
||||
}, {
|
||||
immediate: true
|
||||
});
|
||||
function getSelectedItem() {
|
||||
return options.filteredItems.value[selectedIndex.value];
|
||||
}
|
||||
function execute(index, event) {
|
||||
const item = options.filteredItems.value[index];
|
||||
if (item) {
|
||||
options.onItemClick(item, event);
|
||||
}
|
||||
}
|
||||
function executeSelected(event) {
|
||||
const item = getSelectedItem();
|
||||
if (item) {
|
||||
options.onItemClick(item, event);
|
||||
}
|
||||
}
|
||||
function reset() {
|
||||
const items = options.filteredItems.value;
|
||||
if (items.length === 0) {
|
||||
selectedIndex.value = -1;
|
||||
selectedItemKey.value = undefined;
|
||||
return;
|
||||
}
|
||||
const firstSelectableIndex = findFirstSelectableIndex(items);
|
||||
if (firstSelectableIndex !== -1) {
|
||||
selectedIndex.value = firstSelectableIndex;
|
||||
selectedItemKey.value = getItemKey(items[firstSelectableIndex]);
|
||||
return;
|
||||
}
|
||||
selectedIndex.value = 0;
|
||||
selectedItemKey.value = undefined;
|
||||
}
|
||||
function setSelectedIndex(index) {
|
||||
// Ignore VList's reset to -1 when we have items - we manage selection on filter changes
|
||||
if (index === -1 && options.filteredItems.value.length > 0) {
|
||||
return;
|
||||
}
|
||||
selectedIndex.value = index;
|
||||
const item = options.filteredItems.value[index];
|
||||
selectedItemKey.value = item ? getItemKey(item) : undefined;
|
||||
}
|
||||
return {
|
||||
selectedIndex: readonly(selectedIndex),
|
||||
getSelectedItem,
|
||||
execute,
|
||||
executeSelected,
|
||||
reset,
|
||||
setSelectedIndex
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=useCommandPaletteNavigation.js.map
|
||||
Generated
Vendored
+1
File diff suppressed because one or more lines are too long
+2
@@ -0,0 +1,2 @@
|
||||
export { VCommandPalette } from './VCommandPalette.js';
|
||||
export { VCommandPaletteItem } from './VCommandPaletteItem.js';
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export { VCommandPalette } from "./VCommandPalette.js";
|
||||
export { VCommandPaletteItem } from "./VCommandPaletteItem.js";
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","names":["VCommandPalette","VCommandPaletteItem"],"sources":["../../../src/labs/VCommandPalette/index.ts"],"sourcesContent":["export { VCommandPalette } from './VCommandPalette'\nexport { VCommandPaletteItem } from './VCommandPaletteItem'\n"],"mappings":"SAASA,eAAe;AAAA,SACfC,mBAAmB","ignoreList":[]}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import type { InjectionKey, Ref } from 'vue';
|
||||
import type { VCommandPaletteItem } from './types.js';
|
||||
export interface CommandPaletteProvide {
|
||||
items: Ref<VCommandPaletteItem[]>;
|
||||
filteredItems: Ref<VCommandPaletteItem[]>;
|
||||
selectedIndex: Ref<number>;
|
||||
search: Ref<string>;
|
||||
setSelectedIndex: (index: number) => void;
|
||||
}
|
||||
export declare const VCommandPaletteSymbol: InjectionKey<CommandPaletteProvide>;
|
||||
export declare function useCommandPalette(): CommandPaletteProvide;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// Utilities
|
||||
import { inject } from 'vue';
|
||||
|
||||
// Types
|
||||
|
||||
export const VCommandPaletteSymbol = Symbol.for('vuetify:command-palette');
|
||||
export function useCommandPalette() {
|
||||
const commandPalette = inject(VCommandPaletteSymbol);
|
||||
if (!commandPalette) {
|
||||
throw new Error('[Vuetify] useCommandPalette must be used within VCommandPalette');
|
||||
}
|
||||
return commandPalette;
|
||||
}
|
||||
//# sourceMappingURL=shared.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"shared.js","names":["inject","VCommandPaletteSymbol","Symbol","for","useCommandPalette","commandPalette","Error"],"sources":["../../../src/labs/VCommandPalette/shared.ts"],"sourcesContent":["// Utilities\nimport { inject } from 'vue'\n\n// Types\nimport type { InjectionKey, Ref } from 'vue'\nimport type { VCommandPaletteItem } from './types'\n\nexport interface CommandPaletteProvide {\n items: Ref<VCommandPaletteItem[]>\n filteredItems: Ref<VCommandPaletteItem[]>\n selectedIndex: Ref<number>\n search: Ref<string>\n setSelectedIndex: (index: number) => void\n}\n\nexport const VCommandPaletteSymbol: InjectionKey<CommandPaletteProvide> = Symbol.for('vuetify:command-palette')\n\nexport function useCommandPalette () {\n const commandPalette = inject(VCommandPaletteSymbol)\n\n if (!commandPalette) {\n throw new Error('[Vuetify] useCommandPalette must be used within VCommandPalette')\n }\n\n return commandPalette\n}\n"],"mappings":"AAAA;AACA,SAASA,MAAM,QAAQ,KAAK;;AAE5B;;AAYA,OAAO,MAAMC,qBAA0D,GAAGC,MAAM,CAACC,GAAG,CAAC,yBAAyB,CAAC;AAE/G,OAAO,SAASC,iBAAiBA,CAAA,EAAI;EACnC,MAAMC,cAAc,GAAGL,MAAM,CAACC,qBAAqB,CAAC;EAEpD,IAAI,CAACI,cAAc,EAAE;IACnB,MAAM,IAAIC,KAAK,CAAC,iEAAiE,CAAC;EACpF;EAEA,OAAOD,cAAc;AACvB","ignoreList":[]}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
// @ts-ignore
|
||||
import type { RouteLocationRaw } from 'vue-router';
|
||||
export interface BaseVListItem {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
prependIcon?: string;
|
||||
appendIcon?: string;
|
||||
prependAvatar?: string;
|
||||
appendAvatar?: string;
|
||||
}
|
||||
interface NavigableItemProps {
|
||||
to?: RouteLocationRaw;
|
||||
href?: string;
|
||||
}
|
||||
export interface VCommandPaletteActionItem extends BaseVListItem, NavigableItemProps {
|
||||
type?: 'item';
|
||||
onClick?: (event: MouseEvent | KeyboardEvent, value?: any) => void;
|
||||
value?: any;
|
||||
hotkey?: string;
|
||||
}
|
||||
export interface VCommandPaletteSubheader {
|
||||
type: 'subheader';
|
||||
title: string;
|
||||
inset?: boolean;
|
||||
}
|
||||
export interface VCommandPaletteDivider {
|
||||
type: 'divider';
|
||||
inset?: boolean;
|
||||
}
|
||||
export type VCommandPaletteItem = VCommandPaletteActionItem | VCommandPaletteSubheader | VCommandPaletteDivider;
|
||||
export declare function isActionItem(item: any): item is VCommandPaletteActionItem;
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// Types
|
||||
|
||||
export function isActionItem(item) {
|
||||
return !item.type || item.type === 'item';
|
||||
}
|
||||
//# sourceMappingURL=types.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.js","names":["isActionItem","item","type"],"sources":["../../../src/labs/VCommandPalette/types.ts"],"sourcesContent":["// Types\nimport type { RouteLocationRaw } from 'vue-router'\n\nexport interface BaseVListItem {\n title?: string\n subtitle?: string\n prependIcon?: string\n appendIcon?: string\n prependAvatar?: string\n appendAvatar?: string\n}\n\ninterface NavigableItemProps {\n to?: RouteLocationRaw\n href?: string\n}\n\nexport interface VCommandPaletteActionItem extends BaseVListItem, NavigableItemProps {\n type?: 'item'\n onClick?: (event: MouseEvent | KeyboardEvent, value?: any) => void\n value?: any\n hotkey?: string\n}\n\nexport interface VCommandPaletteSubheader {\n type: 'subheader'\n title: string\n inset?: boolean\n}\n\nexport interface VCommandPaletteDivider {\n type: 'divider'\n inset?: boolean\n}\n\nexport type VCommandPaletteItem =\n | VCommandPaletteActionItem\n | VCommandPaletteSubheader\n | VCommandPaletteDivider\n\nexport function isActionItem (item: any): item is VCommandPaletteActionItem {\n return !item.type || item.type === 'item'\n}\n"],"mappings":"AAAA;;AAwCA,OAAO,SAASA,YAAYA,CAAEC,IAAS,EAAqC;EAC1E,OAAO,CAACA,IAAI,CAACC,IAAI,IAAID,IAAI,CAACC,IAAI,KAAK,MAAM;AAC3C","ignoreList":[]}
|
||||
Reference in New Issue
Block a user