-
-
Notifications
You must be signed in to change notification settings - Fork 305
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
[Feature]: Replace DateValue type with generic in calenar/date-picker component #1641
Comments
Also, it looks like generic doesn't solve the issue because the But maybe it's old information and something has changed since then. |
Having the same issues when trying to pass |
@mkeyy0 from what I've checked, private fields of |
Yeah, I personally ended up using different calendar library, while radix-vue calendar is good, I don't want to use |
Using It's quite annoying that it has to be done this way, but I don't think this is something Reka can solve by itself, except perhaps providing overloaded objects that apply markRaw in the constructor, but this actually means also shimming all the functions exposed by the lib... 😕 |
Aren't we disabling reactivity with this? I would really like to change date objects using Maybe we can use |
Yeah, you're disabling reactivity. Still, for most of the cases, it wouldn't be the problem as |
Objects from the date lib are immutable in practice, so disabling interior reactivity has no observable effect. All methods return a new instance, never mutate. ref(markRaw(new CalendarDate(...))) Shallow ref can do the trick for this specific case, not if you want reactivity and have a date object in structured data Mentioning it in the doc is a good call |
I was able to come up with the following snippet, which makes all date objects behave as marked raw, both at runtime and within the type system. import type { Raw } from 'vue'
import { CalendarDate, CalendarDateTime, Time, ZonedDateTime } from '@internationalized/date'
import { markRaw } from 'vue'
markRaw(CalendarDate.prototype)
markRaw(Time.prototype)
markRaw(CalendarDateTime.prototype)
markRaw(ZonedDateTime.prototype)
declare module '@internationalized/date' {
type RawBrand = keyof Raw<{}>
const RawSymbol: RawBrand
export interface CalendarDate {
[RawSymbol]?: true
}
export interface Time {
[RawSymbol]?: true
}
export interface CalendarDateTime {
[RawSymbol]?: true
}
export interface ZonedDateTime {
[RawSymbol]?: true
}
} |
In any way, I guess it makes sense to add a generic to the Calendar component. It will make types clearer and stricter. For example, if we provide
type Props<T extends DateValue = CalendarDate> = {
modelValue: T;
defaultValue: T;
placeholder: T;
minDate: NoInfer<T>; // no infer as this value doesn't affect actual type of the value
// ...rest
}
type Emits<T extends DateValue = CalendarDate> = {
'update:modelValue': [event: T]
} What do you think? |
I think it will add a lot of uncertainty to the component. For example:
It is much easier to create an intermediate computed value that transforms |
I'm not sure it adds any uncertainty to the component. To be honest, I don't quite understand how your points are related to the types I proposed. <script setup lang="ts>
const modelValue = shallowRef<CalendarDateTime>();
modelValue.value = new CalendarDateTime(2025, 1, 1, 0, 0, 0);
const someCopy = shallowRef<CalendarDateTime>();
</script>
<template>
<!-- Can't be safely done without generic types -->
<Calendar v-model="modelValue" @update:model-value="someCopy = $event" />
</template> But maybe you don't need |
From the application code — yes. But not from a component perspective.
By default, |
Yeah, makes sense. In this case |
If it didn't receive any prop, then the type will default to While it's convenient to work with "whatever object type we want", the argument could be made that it is more correct to only work with The latter is more constraining especially for v-model, but could be dealt with by using a writable computed or VueUse synchronized refs doing the conversion in both directions. Could even be a Reka-provided helper. |
Yeah, also, the example I provided is far from ideal because it's not how you use the calendar if you want date time to be honest. Because if you want the <script setup lang="ts">
const modelValue = shallowRef<ZonedDateTime>();
const placeholder = shallowRef(new CalendarDateTime(2025, 1, 1, 0, 0, 0));
</script>
<template>
<!-- Type error in placeholder, while in the current implementation no type errors -->
<Calendar v-model="modelValue" :placeholder="placeholder" />
</template> <script setup lang="ts">
const modelValue = shallowRef<CalendarDateTime>();
const placeholder = shallowRef(new CalendarDateTime(2025, 1, 1, 0, 0, 0));
</script>
<template>
<!-- no errors -->
<Calendar v-model="modelValue" :placeholder="placeholder" />
</template> |
Also, could you explain what problems with
|
But anyway, I'd like to mention that there's a case when multiple is set to I wrote something that can be potentially a solution, but it doesn't look good, but it works locally for me: <script
lang="ts"
setup
generic="TDate extends DateValue = CalendarDate, TMultiple extends boolean = false"
>
type Props = {
multiple?: TMultiple;
modelValue?: TMultiple extends true ? TDate[] | undefined : TDate | undefined;
placeholder?: TDate;
} & { class?: HTMLAttributes['class'] };
const props = defineProps<Props & { class?: HTMLAttributes['class'] }>();
const emits = defineEmits<{
/** Event handler called whenever the model value changes */
'update:modelValue': [event: TMultiple extends true ? TDate[] | undefined : TDate | undefined];
/** Event handler called whenever the placeholder value changes */
'update:placeholder': [event: TDate];
}>();
</script> With these types, when multiple is set to |
Describe the feature
I'm currently adding a Calendar component to my project and I found it a bit annoying to work with the Calendar component from a type perspective, as the default type for the
modelValue
isDateValue
you always have to cast your type toDateValue
like this:And as other props and emits are typed as
DateValue
you always have to writeas
or some people may write the actual if-check in functions likenextPage
,previousPage
update:modelValue
etc. And it's not great from a DX perspective too. I would suggest adding a generic instead that by default fallback to theCalendarDate
type and in case a user defined it as aCalendarDateTime
orZonedDateTime
it should infer other props and emit types based on this.From my perspective, it might be a good addition to v2 and will simplify working with date-related components and make types more narrow
If some help or other info is needed from me I would be happy to assit
Additional information
The text was updated successfully, but these errors were encountered: