feat(command-dialog-fuse): add new Demo component for custom filter-search with useFuse
This commit is contained in:
parent
a906ca1883
commit
070a51647e
|
|
@ -31,6 +31,7 @@
|
||||||
"embla-carousel": "8.0.0-rc19",
|
"embla-carousel": "8.0.0-rc19",
|
||||||
"embla-carousel-autoplay": "8.0.0-rc19",
|
"embla-carousel-autoplay": "8.0.0-rc19",
|
||||||
"embla-carousel-vue": "8.0.0-rc19",
|
"embla-carousel-vue": "8.0.0-rc19",
|
||||||
|
"fuse.js": "^7.0.0",
|
||||||
"lucide-vue-next": "^0.276.0",
|
"lucide-vue-next": "^0.276.0",
|
||||||
"radix-vue": "^1.3.0",
|
"radix-vue": "^1.3.0",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,106 @@ watch(CmdJ, (v) => {
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
```
|
```
|
||||||
|
### Dialog with custom filter (Fuse.js)
|
||||||
|
|
||||||
|
<ComponentPreview name="CommandDialogUseFuseDemo" />
|
||||||
|
|
||||||
|
For this Demo we will use the [useFuse](https://vueuse.org/integrations/useFuse/) integration. Don't forget to install `fuse.js` and `@vueuse/integrations`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm install fuse.js @vueuse/integrations
|
||||||
|
```
|
||||||
|
|
||||||
|
Code Example:
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useMagicKeys } from '@vueuse/core'
|
||||||
|
|
||||||
|
import { ref, watch } from 'vue'
|
||||||
|
import { useFuse } from '@vueuse/integrations/useFuse'
|
||||||
|
import {
|
||||||
|
CommandDialog,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList,
|
||||||
|
CommandSeparator,
|
||||||
|
} from '@/lib/registry/default/ui/command'
|
||||||
|
|
||||||
|
const open = ref(false)
|
||||||
|
|
||||||
|
const { Meta_J, Ctrl_J } = useMagicKeys({
|
||||||
|
passive: false,
|
||||||
|
onEventFired(e) {
|
||||||
|
if (e.key === 'j' && (e.metaKey || e.ctrlKey))
|
||||||
|
e.preventDefault()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
watch([Meta_J, Ctrl_J], (v) => {
|
||||||
|
if (v[0] || v[1])
|
||||||
|
handleOpenChange()
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleOpenChange() {
|
||||||
|
open.value = !open.value
|
||||||
|
}
|
||||||
|
|
||||||
|
function customFiltering(val: string[], term: string) {
|
||||||
|
const { results } = useFuse(term, val, {
|
||||||
|
matchAllWhenSearchEmpty: true,
|
||||||
|
fuseOptions: {
|
||||||
|
shouldSort: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return results.value?.map(v => v.item)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p class="text-sm text-muted-foreground">
|
||||||
|
Press
|
||||||
|
<kbd
|
||||||
|
class="pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100"
|
||||||
|
>
|
||||||
|
<span class="text-xs">⌘</span>J
|
||||||
|
</kbd>
|
||||||
|
</p>
|
||||||
|
<CommandDialog v-model:open="open" :filter-function="customFiltering">
|
||||||
|
<CommandInput placeholder="Type a command or search..." />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No results found.</CommandEmpty>
|
||||||
|
<CommandGroup heading="Suggestions">
|
||||||
|
<CommandItem value="calendar">
|
||||||
|
Calendar
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem value="search-emoji">
|
||||||
|
Search Emoji
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem value="calculator">
|
||||||
|
Calculator
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
<CommandSeparator />
|
||||||
|
<CommandGroup heading="Settings">
|
||||||
|
<CommandItem value="profile">
|
||||||
|
Profile
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem value="billing">
|
||||||
|
Billing
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem value="settings">
|
||||||
|
Settings
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</CommandDialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
### Combobox
|
### Combobox
|
||||||
|
|
||||||
You can use the `<Command />` component as a combobox. See the [Combobox](/docs/components/combobox) page for more information.
|
You can use the `<Command />` component as a combobox. See the [Combobox](/docs/components/combobox) page for more information.
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useMagicKeys } from '@vueuse/core'
|
||||||
|
|
||||||
|
import { ref, watch } from 'vue'
|
||||||
|
import { useFuse } from '@vueuse/integrations/useFuse'
|
||||||
|
import {
|
||||||
|
CommandDialog,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList,
|
||||||
|
CommandSeparator,
|
||||||
|
} from '@/lib/registry/default/ui/command'
|
||||||
|
|
||||||
|
const open = ref(false)
|
||||||
|
|
||||||
|
const { Meta_M, Ctrl_M } = useMagicKeys({
|
||||||
|
passive: false,
|
||||||
|
onEventFired(e) {
|
||||||
|
if (e.key === 'm' && (e.metaKey || e.ctrlKey))
|
||||||
|
e.preventDefault()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
watch([Meta_M, Ctrl_M], (v) => {
|
||||||
|
if (v[0] || v[1])
|
||||||
|
handleOpenChange()
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleOpenChange() {
|
||||||
|
open.value = !open.value
|
||||||
|
}
|
||||||
|
|
||||||
|
function customFiltering(val: string[], term: string) {
|
||||||
|
const { results } = useFuse(term, val, {
|
||||||
|
matchAllWhenSearchEmpty: true,
|
||||||
|
fuseOptions: {
|
||||||
|
shouldSort: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return results.value?.map(v => v.item)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p class="text-sm text-muted-foreground">
|
||||||
|
Press
|
||||||
|
<kbd
|
||||||
|
class="pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100"
|
||||||
|
>
|
||||||
|
<span class="text-xs">⌘</span>M
|
||||||
|
</kbd>
|
||||||
|
</p>
|
||||||
|
<CommandDialog v-model:open="open" :filter-function="customFiltering">
|
||||||
|
<CommandInput placeholder="Type a command or search..." />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No results found.</CommandEmpty>
|
||||||
|
<CommandGroup heading="Suggestions">
|
||||||
|
<CommandItem value="calendar">
|
||||||
|
Calendar
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem value="search-emoji">
|
||||||
|
Search Emoji
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem value="calculator">
|
||||||
|
Calculator
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
<CommandSeparator />
|
||||||
|
<CommandGroup heading="Settings">
|
||||||
|
<CommandItem value="profile">
|
||||||
|
Profile
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem value="billing">
|
||||||
|
Billing
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem value="settings">
|
||||||
|
Settings
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</CommandDialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
<script setup lang="ts" generic="T">
|
<script setup lang="ts" generic="T">
|
||||||
|
import { reactiveOmit } from '@vueuse/core'
|
||||||
import { useEmitAsProps } from 'radix-vue'
|
import { useEmitAsProps } from 'radix-vue'
|
||||||
import type { DialogRootEmits, DialogRootProps } from 'radix-vue'
|
import type { DialogRootEmits, DialogRootProps } from 'radix-vue'
|
||||||
import Command from './Command.vue'
|
import Command from './Command.vue'
|
||||||
|
|
@ -16,7 +17,7 @@ const emitsAsProps = useEmitAsProps(emits)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Dialog v-bind="{ ...props, ...emitsAsProps }">
|
<Dialog v-bind="{ ...reactiveOmit(props, 'filterFunction'), ...emitsAsProps }">
|
||||||
<DialogContent class="p-0 overflow-hidden shadow-lg">
|
<DialogContent class="p-0 overflow-hidden shadow-lg">
|
||||||
<Command :filter-function="props.filterFunction" class="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
|
<Command :filter-function="props.filterFunction" class="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
|
||||||
<slot />
|
<slot />
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useMagicKeys } from '@vueuse/core'
|
||||||
|
|
||||||
|
import { ref, watch } from 'vue'
|
||||||
|
import { useFuse } from '@vueuse/integrations/useFuse'
|
||||||
|
import {
|
||||||
|
CommandDialog,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList,
|
||||||
|
CommandSeparator,
|
||||||
|
} from '@/lib/registry/default/ui/command'
|
||||||
|
|
||||||
|
const open = ref(false)
|
||||||
|
|
||||||
|
const { Meta_M, Ctrl_M } = useMagicKeys({
|
||||||
|
passive: false,
|
||||||
|
onEventFired(e) {
|
||||||
|
if (e.key === 'm' && (e.metaKey || e.ctrlKey))
|
||||||
|
e.preventDefault()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
watch([Meta_M, Ctrl_M], (v) => {
|
||||||
|
if (v[0] || v[1])
|
||||||
|
handleOpenChange()
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleOpenChange() {
|
||||||
|
open.value = !open.value
|
||||||
|
}
|
||||||
|
|
||||||
|
function customFiltering(val: string[], term: string) {
|
||||||
|
const { results } = useFuse(term, val, {
|
||||||
|
matchAllWhenSearchEmpty: true,
|
||||||
|
fuseOptions: {
|
||||||
|
shouldSort: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return results.value?.map(v => v.item)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p class="text-sm text-muted-foreground">
|
||||||
|
Press
|
||||||
|
<kbd
|
||||||
|
class="pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100"
|
||||||
|
>
|
||||||
|
<span class="text-xs">⌘</span>M
|
||||||
|
</kbd>
|
||||||
|
</p>
|
||||||
|
<CommandDialog v-model:open="open" :filter-function="customFiltering">
|
||||||
|
<CommandInput placeholder="Type a command or search..." />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No results found.</CommandEmpty>
|
||||||
|
<CommandGroup heading="Suggestions">
|
||||||
|
<CommandItem value="calendar">
|
||||||
|
Calendar
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem value="search-emoji">
|
||||||
|
Search Emoji
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem value="calculator">
|
||||||
|
Calculator
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
<CommandSeparator />
|
||||||
|
<CommandGroup heading="Settings">
|
||||||
|
<CommandItem value="profile">
|
||||||
|
Profile
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem value="billing">
|
||||||
|
Billing
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem value="settings">
|
||||||
|
Settings
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</CommandDialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -92,6 +92,9 @@ importers:
|
||||||
embla-carousel-vue:
|
embla-carousel-vue:
|
||||||
specifier: 8.0.0-rc19
|
specifier: 8.0.0-rc19
|
||||||
version: 8.0.0-rc19(vue@3.4.8)
|
version: 8.0.0-rc19(vue@3.4.8)
|
||||||
|
fuse.js:
|
||||||
|
specifier: ^7.0.0
|
||||||
|
version: 7.0.0
|
||||||
lucide-vue-next:
|
lucide-vue-next:
|
||||||
specifier: ^0.276.0
|
specifier: ^0.276.0
|
||||||
version: 0.276.0(vue@3.4.8)
|
version: 0.276.0(vue@3.4.8)
|
||||||
|
|
@ -182,7 +185,7 @@ importers:
|
||||||
version: 4.5.0(@types/node@20.8.10)
|
version: 4.5.0(@types/node@20.8.10)
|
||||||
vitepress:
|
vitepress:
|
||||||
specifier: ^1.0.0-rc.24
|
specifier: ^1.0.0-rc.24
|
||||||
version: 1.0.0-rc.24(@algolia/client-search@4.22.0)(@types/node@20.8.10)(postcss@8.4.33)(search-insights@2.13.0)(typescript@5.2.2)
|
version: 1.0.0-rc.24(@algolia/client-search@4.22.0)(@types/node@20.8.10)(fuse.js@7.0.0)(postcss@8.4.33)(search-insights@2.13.0)(typescript@5.2.2)
|
||||||
vue-tsc:
|
vue-tsc:
|
||||||
specifier: ^1.8.27
|
specifier: ^1.8.27
|
||||||
version: 1.8.27(typescript@5.2.2)
|
version: 1.8.27(typescript@5.2.2)
|
||||||
|
|
@ -5193,7 +5196,7 @@ packages:
|
||||||
- '@vue/composition-api'
|
- '@vue/composition-api'
|
||||||
- vue
|
- vue
|
||||||
|
|
||||||
/@vueuse/integrations@10.5.0(focus-trap@7.5.4)(vue@3.4.8):
|
/@vueuse/integrations@10.5.0(focus-trap@7.5.4)(fuse.js@7.0.0)(vue@3.4.8):
|
||||||
resolution: {integrity: sha512-fm5sXLCK0Ww3rRnzqnCQRmfjDURaI4xMsx+T+cec0ngQqHx/JgUtm8G0vRjwtonIeTBsH1Q8L3SucE+7K7upJQ==}
|
resolution: {integrity: sha512-fm5sXLCK0Ww3rRnzqnCQRmfjDURaI4xMsx+T+cec0ngQqHx/JgUtm8G0vRjwtonIeTBsH1Q8L3SucE+7K7upJQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
async-validator: '*'
|
async-validator: '*'
|
||||||
|
|
@ -5237,6 +5240,7 @@ packages:
|
||||||
'@vueuse/core': 10.5.0(vue@3.4.8)
|
'@vueuse/core': 10.5.0(vue@3.4.8)
|
||||||
'@vueuse/shared': 10.5.0(vue@3.4.8)
|
'@vueuse/shared': 10.5.0(vue@3.4.8)
|
||||||
focus-trap: 7.5.4
|
focus-trap: 7.5.4
|
||||||
|
fuse.js: 7.0.0
|
||||||
vue-demi: 0.14.6(vue@3.4.8)
|
vue-demi: 0.14.6(vue@3.4.8)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@vue/composition-api'
|
- '@vue/composition-api'
|
||||||
|
|
@ -8423,6 +8427,10 @@ packages:
|
||||||
/function-bind@1.1.2:
|
/function-bind@1.1.2:
|
||||||
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
||||||
|
|
||||||
|
/fuse.js@7.0.0:
|
||||||
|
resolution: {integrity: sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
/gauge@3.0.2:
|
/gauge@3.0.2:
|
||||||
resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==}
|
resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
@ -14301,7 +14309,7 @@ packages:
|
||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/vitepress@1.0.0-rc.24(@algolia/client-search@4.22.0)(@types/node@20.8.10)(postcss@8.4.33)(search-insights@2.13.0)(typescript@5.2.2):
|
/vitepress@1.0.0-rc.24(@algolia/client-search@4.22.0)(@types/node@20.8.10)(fuse.js@7.0.0)(postcss@8.4.33)(search-insights@2.13.0)(typescript@5.2.2):
|
||||||
resolution: {integrity: sha512-RpnL8cnOGwiRlBbrYQUm9sYkJbtyOt/wYXk2diTcokY4yvks/5lq9LuSt+MURWB6ZqwpSNHvTmxgaSfLoG0/OA==}
|
resolution: {integrity: sha512-RpnL8cnOGwiRlBbrYQUm9sYkJbtyOt/wYXk2diTcokY4yvks/5lq9LuSt+MURWB6ZqwpSNHvTmxgaSfLoG0/OA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
|
@ -14319,7 +14327,7 @@ packages:
|
||||||
'@vitejs/plugin-vue': 4.3.1(vite@4.5.0)(vue@3.4.8)
|
'@vitejs/plugin-vue': 4.3.1(vite@4.5.0)(vue@3.4.8)
|
||||||
'@vue/devtools-api': 6.5.1
|
'@vue/devtools-api': 6.5.1
|
||||||
'@vueuse/core': 10.5.0(vue@3.4.8)
|
'@vueuse/core': 10.5.0(vue@3.4.8)
|
||||||
'@vueuse/integrations': 10.5.0(focus-trap@7.5.4)(vue@3.4.8)
|
'@vueuse/integrations': 10.5.0(focus-trap@7.5.4)(fuse.js@7.0.0)(vue@3.4.8)
|
||||||
focus-trap: 7.5.4
|
focus-trap: 7.5.4
|
||||||
mark.js: 8.11.1
|
mark.js: 8.11.1
|
||||||
minisearch: 6.1.0
|
minisearch: 6.1.0
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user