-
Notifications
You must be signed in to change notification settings - Fork 889
feat(CommandPalette)!: add trailing-icon
to the input and actions slot
#4397
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v4
Are you sure you want to change the base?
Changes from all commits
deae9ec
b3e8852
801375d
48077c1
7be03e0
c82828d
c025fbf
c3ec805
203a36a
d6fe4dc
0bc7158
3635fad
b559d4f
5b712cc
9ffacce
910cce6
93184c1
0f0202b
7360b77
2863ad9
0621b27
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
<script setup lang="ts"> | ||
const toast = useToast() | ||
|
||
const selectedStatus = ref('In Progress') | ||
|
||
const statusItems = [ | ||
{ | ||
label: 'Backlog', | ||
icon: 'i-lucide-circle', | ||
onClick: () => { | ||
selectedStatus.value = 'Backlog' | ||
toast.add({ title: 'Status changed to Backlog' }) | ||
} | ||
}, | ||
{ | ||
label: 'Todo', | ||
icon: 'i-lucide-circle-dot', | ||
onClick: () => { | ||
selectedStatus.value = 'Todo' | ||
toast.add({ title: 'Status changed to Todo' }) | ||
} | ||
}, | ||
{ | ||
label: 'In Progress', | ||
icon: 'i-lucide-loader-circle', | ||
onClick: () => { | ||
selectedStatus.value = 'In Progress' | ||
toast.add({ title: 'Status changed to In Progress' }) | ||
} | ||
}, | ||
{ | ||
label: 'Done', | ||
icon: 'i-lucide-circle-check', | ||
onClick: () => { | ||
selectedStatus.value = 'Done' | ||
toast.add({ title: 'Status changed to Done' }) | ||
} | ||
} | ||
] | ||
|
||
const groups = [ | ||
{ | ||
id: 'tasks', | ||
label: 'Tasks', | ||
items: [ | ||
{ | ||
label: 'Design new landing page', | ||
suffix: 'High priority', | ||
icon: 'i-lucide-layout', | ||
chip: { | ||
label: 'In Progress', | ||
color: 'info' as const | ||
} | ||
}, | ||
{ | ||
label: 'Fix authentication bug', | ||
suffix: 'Critical', | ||
icon: 'i-lucide-bug', | ||
chip: { | ||
label: 'Todo', | ||
color: 'warning' as const | ||
} | ||
}, | ||
{ | ||
label: 'Update documentation', | ||
suffix: 'Medium priority', | ||
icon: 'i-lucide-file-text', | ||
chip: { | ||
label: 'Backlog', | ||
color: 'neutral' as const | ||
} | ||
}, | ||
{ | ||
label: 'Implement dark mode', | ||
suffix: 'Low priority', | ||
icon: 'i-lucide-moon', | ||
chip: { | ||
label: 'Done', | ||
color: 'success' as const | ||
} | ||
}, | ||
{ | ||
label: 'Optimize performance', | ||
suffix: 'High priority', | ||
icon: 'i-lucide-zap', | ||
chip: { | ||
label: 'In Progress', | ||
color: 'info' as const | ||
} | ||
}, | ||
{ | ||
label: 'Add unit tests', | ||
suffix: 'Medium priority', | ||
icon: 'i-lucide-test-tube', | ||
chip: { | ||
label: 'Todo', | ||
color: 'warning' as const | ||
} | ||
} | ||
] | ||
} | ||
] | ||
</script> | ||
|
||
<template> | ||
<UCommandPalette :groups="groups" placeholder="Search tasks..." class="flex-1 h-80"> | ||
<template #actions> | ||
<UDropdownMenu :items="statusItems"> | ||
<UButton :label="selectedStatus" color="neutral" variant="outline" trailing-icon="i-lucide-chevron-down" /> | ||
</UDropdownMenu> | ||
</template> | ||
</UCommandPalette> | ||
</template> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,7 +34,7 @@ export interface CommandPaletteItem extends Omit<LinkProps, 'type' | 'raw' | 'cu | |
children?: CommandPaletteItem[] | ||
onSelect?(e?: Event): void | ||
class?: any | ||
ui?: Pick<CommandPalette['slots'], 'item' | 'itemLeadingIcon' | 'itemLeadingAvatarSize' | 'itemLeadingAvatar' | 'itemLeadingChipSize' | 'itemLeadingChip' | 'itemLabel' | 'itemLabelPrefix' | 'itemLabelBase' | 'itemLabelSuffix' | 'itemTrailing' | 'itemTrailingKbds' | 'itemTrailingKbdsSize' | 'itemTrailingHighlightedIcon' | 'itemTrailingIcon'> | ||
ui?: Pick<CommandPalette['slots'], 'item' | 'itemLeadingIcon' | 'itemLeadingAvatarSize' | 'itemLeadingAvatar' | 'itemLeadingChipSize' | 'itemLeadingChip' | 'itemLabel' | 'itemLabelPrefix' | 'itemLabelBase' | 'itemLabelSuffix' | 'itemTrailing' | 'itemTrailingKbds' | 'itemTrailingKbdsSize' | 'itemTrailingHighlightedIcon' | 'itemTrailingIcon' | 'childrenIcon'> | ||
[key: string]: any | ||
} | ||
|
||
|
@@ -70,6 +70,12 @@ export interface CommandPaletteProps<G extends CommandPaletteGroup<T> = CommandP | |
* @IconifyIcon | ||
*/ | ||
icon?: IconProps['name'] | ||
/** | ||
* The icon displayed on the right side of the input. | ||
* @defaultValue appConfig.ui.icons.search | ||
* @IconifyIcon | ||
*/ | ||
trailingIcon?: IconProps['name'] | ||
/** | ||
* The icon displayed when an item is selected. | ||
* @defaultValue appConfig.ui.icons.check | ||
|
@@ -81,7 +87,7 @@ export interface CommandPaletteProps<G extends CommandPaletteGroup<T> = CommandP | |
* @defaultValue appConfig.ui.icons.chevronRight | ||
* @IconifyIcon | ||
*/ | ||
trailingIcon?: IconProps['name'] | ||
childrenIcon?: IconProps['name'] | ||
/** | ||
* The placeholder text for the input. | ||
* @defaultValue t('commandPalette.placeholder') | ||
|
@@ -167,6 +173,7 @@ export type CommandPaletteSlots<G extends CommandPaletteGroup<T> = CommandPalett | |
'empty'(props: { searchTerm?: string }): any | ||
'footer'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any | ||
'back'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any | ||
'actions'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any | ||
'close'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any | ||
'item': SlotProps<T> | ||
'item-leading': SlotProps<T> | ||
|
@@ -234,7 +241,8 @@ const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate<{ item: C | |
}) | ||
|
||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.commandPalette || {}) })({ | ||
virtualize: !!props.virtualize | ||
virtualize: !!props.virtualize, | ||
close: !!(props.close || slots.close) | ||
})) | ||
|
||
const fuse = computed(() => defu({}, props.fuse, { | ||
|
@@ -407,8 +415,8 @@ function onSelect(e: Event, item: T) { | |
<slot :name="((item.slot ? `${item.slot}-trailing` : group?.slot ? `${group.slot}-trailing` : `item-trailing`) as keyof CommandPaletteSlots<G, T>)" :item="(item as any)" :index="index"> | ||
<UIcon | ||
v-if="item.children && item.children.length > 0" | ||
:name="trailingIcon || appConfig.ui.icons.chevronRight" | ||
:class="ui.itemTrailingIcon({ class: [props.ui?.itemTrailingIcon, item.ui?.itemTrailingIcon] })" | ||
:name="childrenIcon || appConfig.ui.icons.chevronRight" | ||
:class="ui.childrenIcon({ class: [props.ui?.childrenIcon, item.ui?.childrenIcon] })" | ||
/> | ||
|
||
<span v-else-if="item.kbds?.length" :class="ui.itemTrailingKbds({ class: [props.ui?.itemTrailingKbds, item.ui?.itemTrailingKbds] })"> | ||
|
@@ -454,7 +462,11 @@ function onSelect(e: Event, item: T) { | |
</slot> | ||
</template> | ||
|
||
<template v-if="close || !!slots.close" #trailing> | ||
<template v-if="trailingIcon || close || !!slots.close || !!slots.actions" #trailing> | ||
<UIcon v-if="trailingIcon" :name="trailingIcon" :class="ui.trailingIcon({ class: props.ui?.trailingIcon })" /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should use the |
||
<div v-if="!!slots.actions" :class="ui.actions({ class: props.ui?.actions })"> | ||
<slot name="actions" :ui="ui" /> | ||
</div> | ||
<slot name="close" :ui="ui"> | ||
<UButton | ||
v-if="close" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,8 +4,10 @@ export default (options: Required<ModuleOptions>) => ({ | |
slots: { | ||
root: 'flex flex-col min-h-0 min-w-0 divide-y divide-default', | ||
input: '[&>input]:h-12', | ||
actions: '', | ||
close: '', | ||
back: 'p-0', | ||
trailingIcon: 'shrink-0 size-5 text-dimmed', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This shouldn't be needed. |
||
content: 'relative overflow-hidden flex flex-col', | ||
footer: 'p-1', | ||
viewport: 'relative scroll-py-1 overflow-y-auto flex-1 focus:outline-none', | ||
|
@@ -19,6 +21,7 @@ export default (options: Required<ModuleOptions>) => ({ | |
itemLeadingChip: 'shrink-0 size-5', | ||
itemLeadingChipSize: 'md', | ||
itemTrailing: 'ms-auto inline-flex gap-1.5 items-center', | ||
childrenIcon: 'shrink-0 size-5', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This shouldn't be needed. |
||
itemTrailingIcon: 'shrink-0 size-5', | ||
itemTrailingHighlightedIcon: 'shrink-0 size-5 text-dimmed hidden group-data-highlighted:inline-flex', | ||
itemTrailingKbds: 'hidden lg:inline-flex items-center shrink-0 gap-0.5', | ||
|
@@ -51,6 +54,11 @@ export default (options: Required<ModuleOptions>) => ({ | |
true: { | ||
itemLeadingIcon: 'animate-spin' | ||
} | ||
}, | ||
close: { | ||
true: { | ||
actions: 'me-2' | ||
} | ||
} | ||
} | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not keep using
ui.itemTrailingIcon
here? π€There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because in this case the icon only appears when the item has children, with
itemTrailingIcon
you'd think there'd be an icon for each item I think π€