Skip to content

Commit

Permalink
feat(date): support typescript module augmentation for adapters
Browse files Browse the repository at this point in the history
closes #18710
  • Loading branch information
KaelWD committed Apr 15, 2024
1 parent b6b9be5 commit 20ffadc
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 26 deletions.
46 changes: 31 additions & 15 deletions packages/docs/src/pages/en/features/dates.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,6 @@ This feature was introduced in [v3.4.0 (Blackguard)](/getting-started/release-no

The date composable provides a shared architecture that is used by components such as date picker and calendar. The default implementation is built using the native Date object, but can be swapped out for another date library. If no other date adapter is given, the default Vuetify one is used.

The following example demonstrates explicitly importing the Vuetify date adapter and passing it to the date options.

```js { resource="src/plugins/vuetify.js" }
import { createVuetify } from 'vuetify'
import { VuetifyDateAdapter } from 'vuetify/date/adapters/vuetify'

export default createVuetify({
date: {
adapter: VuetifyDateAdapter,
},
})
```

Within your application, import the **useDate** function and use it to access the date composable.

```html { resource="src/views/Date.vue" }
Expand Down Expand Up @@ -124,11 +111,40 @@ The built-in date adapter implements a subset of functionality from the [DateIOF
import { createVuetify } from 'vuetify'
import LuxonAdapter from "@date-io/luxon"

const luxon = new LuxonAdapter({ locale: "sv" });
export default createVuetify({
date: {
adapter: LuxonAdapter,
},
})
```

For TypeScript users, an interface is also exposed for [module augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation):

```ts { resource="src/plugins/vuetify.js" }
export default createVuetify({
...
})

declare module 'vuetify' {
namespace DateModule {
interface Adapter extends LuxonAdapter {}
}
}
```

### Localization

The date composable will use the current vuetify [locale](/features/internationalization/) for formatting and getting the first day of the week. These do not always line up perfectly, so a list of aliases can be provided to map language codes to locales. The following configuration will look up `en` keys for translations, but use `en-GB` for date formatting:

```js { resource="src/plugins/vuetify.js" }
export default createVuetify({
locale: {
locale: 'en',
},
date: {
adapter: luxon,
locale: {
en: 'en-GB',
},
},
})
```
Expand Down
5 changes: 5 additions & 0 deletions packages/vuetify/build/rollup.types.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,13 @@ function createTypesConfig (input, output, renderChunk, filter) {
code = new MagicString(code)

if (renderChunk) await renderChunk(code)

// vue-router is optional but we need to include some of its types
code.replaceAll(/import([^;])*?from 'vue-router'/gm, '// @ts-ignore\n$&')

// tsc adds extra export statements to namespaces
code.replaceAll(/^\s*export \{\s*\};?$/gm, '')

const map = code.generateMap({
// source: 'source.js',
// file: 'converted.js.map',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function expectAssignable<T, T2 extends T = T> (value: T2): void {}

describe('date.ts', () => {
// Cannot define properties that don't exist in date-io
expectAssignable<DateAdapter>({} as IUtils<Date>)
expectAssignable<DateAdapter>({} as IUtils<Date, string>)
// @ts-expect-error Can implement a subset of date-io
expectAssignable<IUtils<Date>>({} as DateAdapter)

Expand Down
18 changes: 10 additions & 8 deletions packages/vuetify/src/composables/date/date.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,24 @@ import type { LocaleInstance } from '@/composables/locale'
// Adapters
import { VuetifyDateAdapter } from './adapters/vuetify'

export interface DateInstance<T = DateInstanceType['instanceType']> extends DateAdapter<T> {
export interface DateInstance extends DateModule.InternalAdapter {
locale?: any
}

/** Supports module augmentation to specify date object types */
export interface DateInstanceType {
instanceType: unknown
/** Supports module augmentation to specify date adapter types */
export namespace DateModule {
interface Adapter {}

export type InternalAdapter = {} extends Adapter ? DateAdapter<Date> : Adapter
}

export type InternalDateOptions<T = unknown> = {
adapter: (new (options: { locale: any, formats?: any }) => DateInstance<T>) | DateInstance<T>
export type InternalDateOptions = {
adapter: (new (options: { locale: any, formats?: any }) => DateInstance) | DateInstance
formats?: Record<string, any>
locale: Record<string, any>
}

export type DateOptions<T = any> = Partial<InternalDateOptions<T>>
export type DateOptions = Partial<InternalDateOptions>

export const DateOptionsSymbol: InjectionKey<InternalDateOptions> = Symbol.for('vuetify:date-options')
export const DateAdapterSymbol: InjectionKey<DateInstance> = Symbol.for('vuetify:date-adapter')
Expand Down Expand Up @@ -105,7 +107,7 @@ function createInstance (options: InternalDateOptions, locale: LocaleInstance) {
return instance
}

export function useDate () {
export function useDate (): DateInstance {
const options = inject(DateOptionsSymbol)

if (!options) throw new Error('[Vuetify] Could not find injected date options')
Expand Down
2 changes: 1 addition & 1 deletion packages/vuetify/src/composables/date/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { createDate, useDate, DateAdapterSymbol } from './date'
export type { DateAdapter } from './DateAdapter'
export type { DateOptions, DateInstance } from './date'
export type { DateOptions, DateInstance, DateModule } from './date'
2 changes: 1 addition & 1 deletion packages/vuetify/src/framework.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import type { IconOptions } from '@/composables/icons'
import type { LocaleOptions, RtlOptions } from '@/composables/locale'
import type { ThemeOptions } from '@/composables/theme'
export * from './composables'
export type { DateOptions, DateInstance } from '@/composables/date'
export type { DateOptions, DateInstance, DateModule } from '@/composables/date'

export interface VuetifyOptions {
aliases?: Record<string, any>
Expand Down

0 comments on commit 20ffadc

Please sign in to comment.