Skip to content
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

Infer-multiple #1558

Open
wants to merge 2 commits into
base: v2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions packages/core/src/Accordion/AccordionRoot.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<script lang="ts">
import type { ComputedRef, Ref } from 'vue'
import type { PrimitiveProps } from '@/Primitive'
import type { AcceptableValue, DataOrientation, Direction, SingleOrMultipleProps, SingleOrMultipleType } from '@/shared/types'
import type { AcceptableValue, DataOrientation, Direction, SingleOrMultipleType, SingleOrMultipleTypeProps } from '@/shared/types'
import { createContext, useDirection, useForwardExpose } from '@/shared'

export interface AccordionRootProps<T = string | string[]>
extends PrimitiveProps, SingleOrMultipleProps<T> {
export interface AccordionRootProps<S extends SingleOrMultipleType = SingleOrMultipleType, T = string>
extends PrimitiveProps, SingleOrMultipleTypeProps<S, T> {
/**
* When type is "single", allows closing content when clicking trigger for an open item.
* When type is "multiple", this prop has no effect.
Expand Down Expand Up @@ -43,11 +43,11 @@ export interface AccordionRootProps<T = string | string[]>
unmountOnHide?: boolean
}

export type AccordionRootEmits<T extends SingleOrMultipleType = SingleOrMultipleType> = {
export type AccordionRootEmits<S extends SingleOrMultipleType = SingleOrMultipleType> = {
/**
* Event handler called when the expanded state of an item changes
*/
'update:modelValue': [value: (T extends 'single' ? string : string[]) | undefined]
'update:modelValue': [value: (S extends 'single' ? string : string[]) | undefined]
}

export type AccordionRootContext<P extends AccordionRootProps> = {
Expand All @@ -66,24 +66,24 @@ export const [injectAccordionRootContext, provideAccordionRootContext]
= createContext<AccordionRootContext<AccordionRootProps>>('AccordionRoot')
</script>

<script setup lang="ts" generic="T extends (string | string[]), ExplicitType extends SingleOrMultipleType">
<script setup lang="ts" generic="S extends SingleOrMultipleType = SingleOrMultipleType, T extends string = string">
import { Primitive } from '@/Primitive'
import { useSingleOrMultipleValue } from '@/shared/useSingleOrMultipleValue'
import { toRefs } from 'vue'

const props = withDefaults(defineProps<AccordionRootProps<T>>(), {
const props = withDefaults(defineProps<AccordionRootProps<S, T>>(), {
disabled: false,
orientation: 'vertical',
collapsible: false,
unmountOnHide: true,
})

const emits = defineEmits<AccordionRootEmits<ExplicitType>>()
const emits = defineEmits<AccordionRootEmits<S>>()

defineSlots<{
default: (props: {
/** Current active value */
modelValue: typeof modelValue.value
modelValue: SingleOrMultipleTypeProps<S, T>['modelValue']
}) => any
}>()

Expand Down Expand Up @@ -113,6 +113,6 @@ provideAccordionRootContext({
:as-child="asChild"
:as="as"
>
<slot :model-value="modelValue" />
<slot :model-value="modelValue as any" />
</Primitive>
</template>
38 changes: 10 additions & 28 deletions packages/core/src/Calendar/CalendarRoot.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { type Formatter, createContext, useDirection, useLocale } from '@/shared
import { useCalendar, useCalendarState } from './useCalendar'
import { getDefaultDate, handleCalendarInitialFocus } from '@/shared/date'
import type { Grid, Matcher, WeekDayFormat } from '@/date'
import type { Direction } from '@/shared/types'
import type { Direction, SingleOrMultipleProps } from '@/shared/types'

type CalendarRootContext = {
locale: Ref<string>
Expand Down Expand Up @@ -42,9 +42,7 @@ type CalendarRootContext = {
dir: Ref<Direction>
}

interface BaseCalendarRootProps extends PrimitiveProps {
/** The default value for the calendar */
defaultValue?: DateValue
export interface CalendarRootProps<S extends boolean = false> extends PrimitiveProps, SingleOrMultipleProps<S, DateValue> {
/** The default placeholder date */
defaultPlaceholder?: DateValue
/** The placeholder date, which is used to determine what month to display when no date is selected. This updates as the user navigates the calendar and can be used to programmatically control the calendar view */
Expand Down Expand Up @@ -87,25 +85,9 @@ interface BaseCalendarRootProps extends PrimitiveProps {
prevPage?: (placeholder: DateValue) => DateValue
}

export interface MultipleCalendarRootProps extends BaseCalendarRootProps {
/** The controlled checked state of the calendar. Can be bound as `v-model`. */
modelValue?: DateValue[] | undefined
/** Whether or not multiple dates can be selected */
multiple: true
}

export interface SingleCalendarRootProps extends BaseCalendarRootProps {
/** The controlled checked state of the calendar. Can be bound as `v-model`. */
modelValue?: DateValue | undefined
/** Whether or not multiple dates can be selected */
multiple?: false
}

export type CalendarRootProps = MultipleCalendarRootProps | SingleCalendarRootProps

export type CalendarRootEmits = {
export type CalendarRootEmits<S extends boolean = false> = {
/** Event handler called whenever the model value changes */
'update:modelValue': [date: DateValue | undefined]
'update:modelValue': [date: S extends false ? DateValue | undefined : DateValue[] | undefined]
/** Event handler called whenever the placeholder value changes */
'update:placeholder': [date: DateValue]
}
Expand All @@ -114,20 +96,19 @@ export const [injectCalendarRootContext, provideCalendarRootContext]
= createContext<CalendarRootContext>('CalendarRoot')
</script>

<script setup lang="ts">
<script setup lang="ts" generic="S extends boolean = false">
import { onMounted, toRefs, watch } from 'vue'
import { Primitive, usePrimitiveElement } from '@/Primitive'
import { useVModel } from '@vueuse/core'

const props = withDefaults(defineProps<CalendarRootProps>(), {
const props = withDefaults(defineProps<CalendarRootProps<S>>(), {
defaultValue: undefined,
as: 'div',
pagedNavigation: false,
preventDeselect: false,
weekStartsOn: 0,
weekdayFormat: 'narrow',
fixedWeeks: false,
multiple: false,
numberOfMonths: 1,
disabled: false,
readonly: false,
Expand All @@ -136,7 +117,7 @@ const props = withDefaults(defineProps<CalendarRootProps>(), {
isDateDisabled: undefined,
isDateUnavailable: undefined,
})
const emits = defineEmits<CalendarRootEmits>()
const emits = defineEmits<CalendarRootEmits<S>>()
defineSlots<{
default: (props: {
/** The current date of the placeholder */
Expand All @@ -152,7 +133,7 @@ defineSlots<{
/** Whether or not to always display 6 weeks in the calendar */
fixedWeeks: boolean
/** The current date of the calendar */
modelValue: DateValue | undefined
modelValue: CalendarRootProps<S>['modelValue']
}) => any
}>()

Expand Down Expand Up @@ -196,6 +177,7 @@ const defaultDate = getDefaultDate({
})

const placeholder = useVModel(props, 'placeholder', emits, {
// @ts-expect-error ignoring DateValue types
defaultValue: props.defaultPlaceholder ?? defaultDate.copy(),
passive: (props.placeholder === undefined) as false,
}) as Ref<DateValue>
Expand Down Expand Up @@ -345,7 +327,7 @@ provideCalendarRootContext({
:week-starts-on="weekStartsOn"
:locale="locale"
:fixed-weeks="fixedWeeks"
:model-value="modelValue"
:model-value="modelValue as any"
/>
<div
style="border: 0px; clip: rect(0px, 0px, 0px, 0px); clip-path: inset(50%); height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: absolute; white-space: nowrap; width: 1px;"
Expand Down
20 changes: 10 additions & 10 deletions packages/core/src/Combobox/ComboboxRoot.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Ref } from 'vue'
import type { ListboxRootProps } from '@/Listbox'
import { createContext, useDirection, useFilter } from '@/shared'
import { usePrimitiveElement } from '@/Primitive'
import type { AcceptableValue, GenericComponentInstance } from '@/shared/types'
import type { AcceptableValue, GenericComponentInstance, SingleOrMultipleProps } from '@/shared/types'

type ComboboxRootContext<T> = {
modelValue: Ref<T | Array<T>>
Expand Down Expand Up @@ -33,16 +33,16 @@ type ComboboxRootContext<T> = {
export const [injectComboboxRootContext, provideComboboxRootContext]
= createContext<ComboboxRootContext<AcceptableValue>>('ComboboxRoot')

export type ComboboxRootEmits<T = AcceptableValue> = {
export type ComboboxRootEmits<S extends boolean = false, T = AcceptableValue> = {
/** Event handler called when the value changes. */
'update:modelValue': [value: T]
'update:modelValue': [value: S extends false ? T : T[]]
/** Event handler when highlighted element changes. */
'highlight': [payload: { ref: HTMLElement, value: T } | undefined]
/** Event handler called when the open state of the combobox changes. */
'update:open': [value: boolean]
}

export interface ComboboxRootProps<T = AcceptableValue> extends Omit<ListboxRootProps<T>, 'orientation' | 'selectionBehavior' > {
export interface ComboboxRootProps<S extends boolean = false, T = AcceptableValue> extends Omit<ListboxRootProps<S, T>, 'orientation' | 'selectionBehavior' > {
/** The controlled open state of the Combobox. Can be binded with with `v-model:open`. */
open?: boolean
/** The open state of the combobox when it is initially rendered. <br> Use when you do not need to control its open state. */
Expand All @@ -59,24 +59,24 @@ export interface ComboboxRootProps<T = AcceptableValue> extends Omit<ListboxRoot
}
</script>

<script setup lang="ts" generic="T extends AcceptableValue = AcceptableValue">
<script setup lang="ts" generic="S extends boolean = false, T extends AcceptableValue = AcceptableValue">
import { computed, nextTick, reactive, ref, toRefs, watch } from 'vue'
import { PopperRoot } from '@/Popper'
import { type EventHookOn, createEventHook, useVModel } from '@vueuse/core'
import { ListboxRoot } from '@/Listbox'

const props = withDefaults(defineProps<ComboboxRootProps<T>>(), {
const props = withDefaults(defineProps<ComboboxRootProps<S, T>>(), {
open: undefined,
resetSearchTermOnBlur: true,
})
const emits = defineEmits<ComboboxRootEmits<T>>()
const emits = defineEmits<ComboboxRootEmits<S, T>>()

defineSlots<{
default: (props: {
/** Current open state */
open: typeof open.value
/** Current active value */
modelValue: typeof modelValue.value
modelValue: SingleOrMultipleProps<S, T>['modelValue']
}) => any
}>()

Expand Down Expand Up @@ -222,7 +222,7 @@ provideComboboxRootContext({
<ListboxRoot
ref="primitiveElement"
v-bind="$attrs"
v-model="modelValue"
v-model="modelValue as any"
:style="{
pointerEvents: open ? 'auto' : undefined,
}"
Expand All @@ -239,7 +239,7 @@ provideComboboxRootContext({
>
<slot
:open="open"
:model-value="modelValue"
:model-value="modelValue as any"
/>
</ListboxRoot>
</PopperRoot>
Expand Down
29 changes: 12 additions & 17 deletions packages/core/src/Listbox/ListboxRoot.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { createContext, findValuesBetween, useDirection, useFormControl, useKbd, useTypeahead } from '@/shared'
import { Primitive } from '..'
import { type PrimitiveProps, usePrimitiveElement } from '@/Primitive'
import type { AcceptableValue, DataOrientation, Direction, FormFieldProps } from '@/shared/types'
import type { AcceptableValue, DataOrientation, Direction, FormFieldProps, SingleOrMultipleProps } from '@/shared/types'
import { getFocusIntent } from '@/RovingFocus/utils'

type ListboxRootContext<T> = {
Expand Down Expand Up @@ -38,13 +38,7 @@ type ListboxRootContext<T> = {
export const [injectListboxRootContext, provideListboxRootContext]
= createContext<ListboxRootContext<AcceptableValue>>('ListboxRoot')

export interface ListboxRootProps<T = AcceptableValue> extends PrimitiveProps, FormFieldProps {
/** The controlled value of the listbox. Can be binded with with `v-model`. */
modelValue?: T | Array<T>
/** The value of the listbox when initially rendered. Use when you do not need to control the state of the Listbox */
defaultValue?: T | Array<T>
/** Whether multiple options can be selected or not. */
multiple?: boolean
export interface ListboxRootProps<S extends boolean = false, T = AcceptableValue> extends PrimitiveProps, FormFieldProps, SingleOrMultipleProps<S, T> {
/** The orientation of the listbox. <br>Mainly so arrow navigation is done accordingly (left & right vs. up & down) */
orientation?: DataOrientation
/** The reading direction of the listbox when applicable. <br> If omitted, inherits globally from `ConfigProvider` or assumes LTR (left-to-right) reading mode. */
Expand All @@ -62,9 +56,9 @@ export interface ListboxRootProps<T = AcceptableValue> extends PrimitiveProps, F
by?: string | ((a: T, b: T) => boolean)
}

export type ListboxRootEmits<T = AcceptableValue> = {
export type ListboxRootEmits<S extends boolean = false, T extends AcceptableValue = AcceptableValue> = {
/** Event handler called when the value changes. */
'update:modelValue': [value: T]
'update:modelValue': [value: S extends true ? T[] : T]
/** Event handler when highlighted element changes. */
'highlight': [payload: { ref: HTMLElement, value: T } | undefined]
/** Event handler called when container is being focused. Can be prevented. */
Expand All @@ -74,23 +68,23 @@ export type ListboxRootEmits<T = AcceptableValue> = {
}
</script>

<script setup lang="ts" generic="T extends AcceptableValue = AcceptableValue">
<script setup lang="ts" generic="S extends boolean = false, T extends AcceptableValue = AcceptableValue">
import { type EventHook, createEventHook, useVModel } from '@vueuse/core'
import { type Ref, nextTick, ref, toRefs, watch } from 'vue'
import { compare } from './utils'
import { useCollection } from '@/Collection'
import { VisuallyHiddenInput } from '@/VisuallyHidden'

const props = withDefaults(defineProps<ListboxRootProps>(), {
const props = withDefaults(defineProps<ListboxRootProps<S, T>>(), {
selectionBehavior: 'toggle',
orientation: 'vertical',
})
const emits = defineEmits<ListboxRootEmits>()
const emits = defineEmits<ListboxRootEmits<S, T>>()

defineSlots<{
default: (props: {
/** Current active value */
modelValue: typeof modelValue.value
modelValue: SingleOrMultipleProps<S, T>['modelValue']
}) => any
}>()

Expand All @@ -107,7 +101,7 @@ const firstValue = ref<T>()
const isUserAction = ref(false)
const focusable = ref(true)
const modelValue = useVModel(props, 'modelValue', emits, {
defaultValue: props.defaultValue ?? (multiple.value ? [] : undefined),
defaultValue: props.defaultValue ?? (multiple.value ? ([] as any) : undefined),
passive: (props.modelValue === undefined) as false,
deep: true,
}) as Ref<T | T[] | undefined>
Expand All @@ -128,7 +122,7 @@ function onValueChange(val: T) {
}
else {
if (props.selectionBehavior === 'toggle') {
if (compare(modelValue.value, val, props.by))
if (compare(modelValue.value as T, val, props.by))
modelValue.value = undefined
else
modelValue.value = val
Expand Down Expand Up @@ -364,6 +358,7 @@ provideListboxRootContext({
virtualFocusHook,
virtualKeydownHook,
virtualHighlightHook,
// @ts-expect-error ignoring
by: props.by,
firstValue,
selectionBehavior,
Expand Down Expand Up @@ -397,7 +392,7 @@ provideListboxRootContext({
}
}"
>
<slot :model-value="modelValue" />
<slot :model-value="modelValue as any" />

<VisuallyHiddenInput
v-if="isFormControl && name"
Expand Down
Loading
Loading