From f516d7b36da51565f4ab05a4c9cfe5e5b4015124 Mon Sep 17 00:00:00 2001 From: Inesh Bose Date: Tue, 12 Nov 2024 14:28:18 +0000 Subject: [PATCH] feat(InputMenu/SelectMenu): add `create-item` prop (#2472) Co-authored-by: Benjamin Canac --- docs/content/3.components/input-menu.md | 36 +++++ docs/content/3.components/select-menu.md | 42 ++++++ src/runtime/components/Alert.vue | 2 +- src/runtime/components/Carousel.vue | 2 +- src/runtime/components/CommandPalette.vue | 4 +- src/runtime/components/InputMenu.vue | 86 ++++++++++-- src/runtime/components/Modal.vue | 2 +- src/runtime/components/SelectMenu.vue | 90 ++++++++++-- src/runtime/components/Slideover.vue | 2 +- src/runtime/components/Table.vue | 2 +- src/runtime/components/Toast.vue | 2 +- src/runtime/locale/ar.ts | 6 +- src/runtime/locale/cs.ts | 6 +- src/runtime/locale/de.ts | 6 +- src/runtime/locale/en.ts | 6 +- src/runtime/locale/fr.ts | 6 +- src/runtime/locale/it.ts | 6 +- src/runtime/locale/ru.ts | 6 +- src/runtime/locale/zh_hans.ts | 6 +- src/runtime/locale/zh_hant.ts | 6 +- src/runtime/types/locale.ts | 2 + test/components/InputMenu.spec.ts | 3 +- test/components/SelectMenu.spec.ts | 3 +- .../__snapshots__/InputMenu-vue.spec.ts.snap | 110 +++++++++++++++ .../__snapshots__/InputMenu.spec.ts.snap | 110 +++++++++++++++ .../__snapshots__/SelectMenu-vue.spec.ts.snap | 132 ++++++++++++++++++ .../__snapshots__/SelectMenu.spec.ts.snap | 132 ++++++++++++++++++ 27 files changed, 766 insertions(+), 50 deletions(-) diff --git a/docs/content/3.components/input-menu.md b/docs/content/3.components/input-menu.md index 5308dafd..019cae31 100644 --- a/docs/content/3.components/input-menu.md +++ b/docs/content/3.components/input-menu.md @@ -214,6 +214,42 @@ props: --- :: +### Create Item + +Use the `create-item` prop to allow user input. + +::component-code +--- +prettier: true +ignore: + - modelValue + - items +external: + - items + - modelValue +items: + createItem: + - true + - 'always' +props: + modelValue: 'Backlog' + items: + - Backlog + - Todo + - In Progress + - Done + createItem: true +--- +:: + +::note +The create option shows when no match is found by default. Set it to `always` to show it even when similar values exist. +:: + +::tip{to="#emits"} +Use the `@create` event to handle the creation of the item. You will receive the event and the item as arguments. +:: + ### Content Use the `content` prop to control how the InputMenu content is rendered, like its `align` or `side` for example. diff --git a/docs/content/3.components/select-menu.md b/docs/content/3.components/select-menu.md index 69085995..5dd514fc 100644 --- a/docs/content/3.components/select-menu.md +++ b/docs/content/3.components/select-menu.md @@ -235,6 +235,48 @@ props: --- :: +::tip +You can set the `search-input` prop to `false` to hide the search input. +:: + +### Create Item + +Use the `create-item` prop to allow user input. + +::component-code +--- +prettier: true +ignore: + - modelValue + - items + - class +external: + - items + - modelValue +items: + createItem: + - true + - 'always' +props: + modelValue: 'Backlog' + createItem: true + items: + - Backlog + - Todo + - In Progress + - Done + class: 'w-48' +--- +:: + +::note +The create option shows when no match is found by default. Set it to `always` to show it even when similar values exist. +:: + +::tip{to="#emits"} +Use the `@create` event to handle the creation of the item. You will receive the event and the item as arguments. +:: + ### Content Use the `content` prop to control how the SelectMenu content is rendered, like its `align` or `side` for example. diff --git a/src/runtime/components/Alert.vue b/src/runtime/components/Alert.vue index 624ba5c9..1389583e 100644 --- a/src/runtime/components/Alert.vue +++ b/src/runtime/components/Alert.vue @@ -3,7 +3,6 @@ import { tv, type VariantProps } from 'tailwind-variants' import type { AppConfig } from '@nuxt/schema' import _appConfig from '#build/app.config' import theme from '#build/ui/alert' -import { useLocale } from '../composables/useLocale' import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta' import type { AvatarProps, ButtonProps } from '../types' @@ -66,6 +65,7 @@ extendDevtoolsMeta({ defaultProps: { title: 'Heads up!' } }) import { computed } from 'vue' import { Primitive } from 'radix-vue' import { useAppConfig } from '#imports' +import { useLocale } from '../composables/useLocale' import UIcon from './Icon.vue' import UAvatar from './Avatar.vue' import UButton from './Button.vue' diff --git a/src/runtime/components/Carousel.vue b/src/runtime/components/Carousel.vue index 3a16d975..ffd0a590 100644 --- a/src/runtime/components/Carousel.vue +++ b/src/runtime/components/Carousel.vue @@ -10,7 +10,6 @@ import type { FadeOptionsType } from 'embla-carousel-fade' import type { WheelGesturesPluginOptions } from 'embla-carousel-wheel-gestures' import _appConfig from '#build/app.config' import theme from '#build/ui/carousel' -import { useLocale } from '../composables/useLocale' import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta' import type { ButtonProps } from '../types' import type { AcceptableValue, PartialString } from '../types/utils' @@ -101,6 +100,7 @@ import useEmblaCarousel from 'embla-carousel-vue' import { useForwardProps } from 'radix-vue' import { reactivePick, computedAsync } from '@vueuse/core' import { useAppConfig } from '#imports' +import { useLocale } from '../composables/useLocale' import UButton from './Button.vue' const props = withDefaults(defineProps>(), { diff --git a/src/runtime/components/CommandPalette.vue b/src/runtime/components/CommandPalette.vue index 895fc222..220e63fa 100644 --- a/src/runtime/components/CommandPalette.vue +++ b/src/runtime/components/CommandPalette.vue @@ -7,7 +7,6 @@ import type { UseFuseOptions } from '@vueuse/integrations/useFuse' import _appConfig from '#build/app.config' import theme from '#build/ui/command-palette' import type { UseComponentIconsProps } from '../composables/useComponentIcons' -import { useLocale } from '../composables/useLocale' import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta' import type { AvatarProps, ButtonProps, ChipProps, KbdProps, InputProps } from '../types' import type { DynamicSlots, PartialString } from '../types/utils' @@ -36,7 +35,7 @@ export interface CommandPaletteGroup { slot?: string items?: T[] /** - * Wether to filter group items with [useFuse](https://vueuse.org/integrations/useFuse). + * Whether to filter group items with [useFuse](https://vueuse.org/integrations/useFuse). * When `false`, items will not be filtered which is useful for custom filtering (useAsyncData, useFetch, etc.). * @defaultValue true */ @@ -125,6 +124,7 @@ import { defu } from 'defu' import { reactivePick } from '@vueuse/core' import { useFuse } from '@vueuse/integrations/useFuse' import { useAppConfig } from '#imports' +import { useLocale } from '../composables/useLocale' import { omit, get } from '../utils' import { highlight } from '../utils/fuse' import UIcon from './Icon.vue' diff --git a/src/runtime/components/InputMenu.vue b/src/runtime/components/InputMenu.vue index 90717347..1f4a3de5 100644 --- a/src/runtime/components/InputMenu.vue +++ b/src/runtime/components/InputMenu.vue @@ -6,7 +6,6 @@ import type { AppConfig } from '@nuxt/schema' import _appConfig from '#build/app.config' import theme from '#build/ui/input-menu' import type { UseComponentIconsProps } from '../composables/useComponentIcons' -import { useLocale } from '../composables/useLocale' import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta' import type { AvatarProps, ChipProps, InputProps } from '../types' import type { AcceptableValue, ArrayOrWrapped, PartialString, MaybeArrayOfArray, MaybeArrayOfArrayItem, SelectModelValue, SelectModelValueEmits, SelectItemKey } from '../types/utils' @@ -98,6 +97,11 @@ export interface InputMenuProps, I extends Ma items?: I /** Highlight the ring color like a focus state. */ highlight?: boolean + /** + * Determines if custom user input that does not exist in options can be added. + * @defaultValue false + */ + createItem?: boolean | 'always' | { placement?: 'top' | 'bottom', when?: 'empty' | 'always' } class?: any ui?: PartialString /** The controlled value of the Combobox. Can be binded-with with `v-model`. */ @@ -110,6 +114,7 @@ export type InputMenuEmits = Omit, change: [payload: Event] blur: [payload: FocusEvent] focus: [payload: FocusEvent] + create: [payload: Event, item: T] } & SelectModelValueEmits type SlotProps = (props: { item: T, index: number }) => any @@ -124,21 +129,23 @@ export interface InputMenuSlots { 'item-trailing': SlotProps 'tags-item-text': SlotProps 'tags-item-delete': SlotProps + 'create-item-label'(props: { item: T }): any } extendDevtoolsMeta({ defaultProps: { items: ['Option 1', 'Option 2', 'Option 3'] } })