chore(CommandPalette): handle loading state (#221)

This commit is contained in:
Benjamin Canac
2023-05-22 16:00:31 +02:00
committed by GitHub
parent e7eea067b2
commit bdaf2dbbd4
9 changed files with 71 additions and 10 deletions

View File

@@ -184,6 +184,7 @@ export default defineAppConfig({
commandPalette: {
default: {
icon: 'i-octicon-search-24',
loadingIcon: 'i-octicon-sync-24',
selectedIcon: 'i-octicon-check-24',
empty: {
icon: 'i-octicon-search-24'

View File

@@ -29,7 +29,7 @@ baseProps:
### Chip
Use the `chipColor`, `chipVariant` and `chipPosition` props to display a chip on the Avatar.
Use the `chip-color`, `chip-variant` and `chip-position` props to display a chip on the Avatar.
::component-card
---

View File

@@ -113,7 +113,7 @@ Button
Use any icon from [Iconify](https://icones.js.org) by setting the `icon` prop by using this pattern: `i-{collection_name}-{icon_name}`.
Use the `leading` and `trailing` props to set the icon position or the `leadingIcon` and `trailingIcon` props to set a different icon for each position.
Use the `leading` and `trailing` props to set the icon position or the `leading-icon` and `trailing-icon` props to set a different icon for each position.
::component-card
---
@@ -163,7 +163,7 @@ Button
Use the `loading` prop to show a loading icon and disable the Button.
Use the `loadingIcon` prop to set a different icon or change it globally in `ui.button.default.loadingIcon`. Defaults to `i-heroicons-arrow-path-20-solid`.
Use the `loading-icon` prop to set a different icon or change it globally in `ui.button.default.loadingIcon`. Defaults to `i-heroicons-arrow-path-20-solid`.
::component-card
---

View File

@@ -56,7 +56,7 @@ props:
Use any icon from [Iconify](https://icones.js.org) by setting the `icon` prop by using this pattern: `i-{collection_name}-{icon_name}`.
Use the `leading` and `trailing` props to set the icon position or the `leadingIcon` and `trailingIcon` props to set a different icon for each position.
Use the `leading` and `trailing` props to set the icon position or the `leading-icon` and `trailing-icon` props to set a different icon for each position.
::component-card
---
@@ -94,7 +94,7 @@ excludedProps:
Use the `loading` prop to show a loading icon and disable the Input.
Use the `loadingIcon` prop to set a different icon or change it globally in `ui.input.default.loadingIcon`. Defaults to `i-heroicons-arrow-path-20-solid`.
Use the `loading-icon` prop to set a different icon or change it globally in `ui.input.default.loadingIcon`. Defaults to `i-heroicons-arrow-path-20-solid`.
::component-card
---

View File

@@ -78,7 +78,7 @@ props:
Use any icon from [Iconify](https://icones.js.org) by setting the `icon` prop by using this pattern: `i-{collection_name}-{icon_name}`.
Use the `trailingIcon` prop to set a different icon or change it globally in `ui.select.default.trailingIcon`. Defaults to `i-heroicons-chevron-down-20-solid`.
Use the `trailing-icon` prop to set a different icon or change it globally in `ui.select.default.trailingIcon`. Defaults to `i-heroicons-chevron-down-20-solid`.
::component-card
---

View File

@@ -136,9 +136,9 @@ const selected = ref(people[0])
Use any icon from [Iconify](https://icones.js.org) by setting the `icon` prop by using this pattern: `i-{collection_name}-{icon_name}`.
Use the `trailingIcon` prop to set a different icon or change it globally in `ui.select.default.trailingIcon`. Defaults to `i-heroicons-chevron-down-20-solid`.
Use the `trailing-icon` prop to set a different icon or change it globally in `ui.select.default.trailingIcon`. Defaults to `i-heroicons-chevron-down-20-solid`.
Use the `selectedIcon` prop to set a different icon or change it globally in `ui.selectMenu.default.selectedIcon`. Defaults to `i-heroicons-check-20-solid`.
Use the `selected-icon` prop to set a different icon or change it globally in `ui.selectMenu.default.selectedIcon`. Defaults to `i-heroicons-check-20-solid`.
::component-card
---

View File

@@ -160,7 +160,7 @@ function onSelect (option) {
Use any icon from [Iconify](https://icones.js.org) by setting the `icon` prop by using this pattern: `i-{collection_name}-{icon_name}`.
Use the `selectedIcon` prop to set a different icon or change it globally in `ui.commandPalette.default.selectedIcon`. Defaults to `i-heroicons-check-20-solid`.
Use the `selected-icon` prop to set a different icon or change it globally in `ui.commandPalette.default.selectedIcon`. Defaults to `i-heroicons-check-20-solid`.
::component-card
---
@@ -174,6 +174,24 @@ excludedProps:
---
::
### Loading
Use the `loading` prop to show a loading icon.
Use the `loading-icon` prop to set a different icon or change it globally in `ui.commandPalette.default.loadingIcon`. Defaults to `i-heroicons-arrow-path-20-solid`.
::component-card
---
padding: false
baseProps:
empty: null
props:
loading: true
excludedProps:
- icon
---
::
### Placeholder
Use the `placeholder` prop to change the input placeholder
@@ -218,6 +236,8 @@ Use the `empty` prop to display a message when there are no results.
You can pass an `object` through the `empty` prop or globally through `ui.commandPalette.default.empty`. Here is the default:
You can also set it to `null` to hide the empty label.
::component-card
---
padding: false
@@ -299,6 +319,10 @@ const groups = computed(() => {
```
::
::alert{icon="i-heroicons-light-bulb"}
The `loading` state will automatically be enabled when a `search` function is loading. You can disable this behaviour by setting the `loading-icon` prop to `null` or globally in `ui.commandPalette.default.loadingIcon`.
::
## Themes
Our theming system provides a lot of flexibility to customize the component. Here is some examples of what you can do.

View File

@@ -546,6 +546,7 @@ const commandPalette = {
},
default: {
icon: 'i-heroicons-magnifying-glass-20-solid',
loadingIcon: 'i-heroicons-arrow-path-20-solid',
empty: {
icon: 'i-heroicons-magnifying-glass-20-solid',
label: 'We couldn\'t find any items.',

View File

@@ -8,7 +8,7 @@
>
<div :class="ui.wrapper">
<div v-show="searchable" :class="ui.input.wrapper">
<UIcon v-if="icon" :name="icon" :class="[ui.input.icon.base, ui.input.icon.size]" aria-hidden="true" />
<UIcon v-if="iconName" :name="iconName" :class="iconClass" aria-hidden="true" />
<ComboboxInput
ref="comboboxInput"
:value="query"
@@ -74,6 +74,7 @@ import type { Group, Command } from '../../types/command-palette'
import UIcon from '../elements/Icon.vue'
import UButton from '../elements/Button.vue'
import type { Button as ButtonType } from '../../types/button'
import { classNames } from '../../utils'
import CommandPaletteGroup from './CommandPaletteGroup.vue'
import { useAppConfig } from '#imports'
// TODO: Remove
@@ -112,6 +113,10 @@ export default defineComponent({
type: Boolean,
default: true
},
loading: {
type: Boolean,
default: false
},
groups: {
type: Array as PropType<Group[]>,
default: () => []
@@ -120,6 +125,10 @@ export default defineComponent({
type: String,
default: () => appConfig.ui.commandPalette.default.icon
},
loadingIcon: {
type: String,
default: () => appConfig.ui.commandPalette.default.loadingIcon
},
selectedIcon: {
type: String,
default: () => appConfig.ui.commandPalette.default.selectedIcon
@@ -175,6 +184,7 @@ export default defineComponent({
const query = ref('')
const comboboxInput = ref<ComponentPublicInstance<HTMLInputElement>>()
const comboboxApi = ref(null)
const isLoading = ref(false)
onMounted(() => {
if (props.autoselect) {
@@ -231,10 +241,17 @@ export default defineComponent({
const debouncedSearch = useDebounceFn(async () => {
const searchableGroups = props.groups.filter(group => !!group.search)
if (!searchableGroups.length) {
return
}
isLoading.value = true
await Promise.all(searchableGroups.map(async (group) => {
searchResults.value[group.key] = await group.search(query.value)
}))
isLoading.value = false
}, props.debounce)
watch(query, () => {
@@ -247,6 +264,22 @@ export default defineComponent({
}, 0)
})
const iconName = computed(() => {
if ((props.loading || isLoading.value) && props.loadingIcon) {
return props.loadingIcon
}
return props.icon
})
const iconClass = computed(() => {
return classNames(
ui.value.input.icon.base,
ui.value.input.icon.size,
((props.loading || isLoading.value) && props.loadingIcon) && 'animate-spin'
)
})
// Methods
function activateFirstOption () {
@@ -292,6 +325,8 @@ export default defineComponent({
groups,
comboboxInput,
query,
iconName,
iconClass,
onSelect,
onClear
}