Skip to content

Commit 0a9eb6e

Browse files
Gurgencmath10
authored andcommitted
feat(v1-components): UiDate & utilities for date/time formatting with automatic vue-i18n locale detection
1 parent 8995b73 commit 0a9eb6e

File tree

15 files changed

+1180
-8
lines changed

15 files changed

+1180
-8
lines changed

packages/v1-components/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
"@omnicajs/vue-remote": "^0.2.5",
5858
"@remote-ui/rpc": "^1.4.5",
5959
"@retailcrm/image-preview": "^1.0.2",
60-
"remark-gfm": "^4.0.0"
60+
"date-fns": "^4.1.0"
6161
},
6262
"devDependencies": {
6363
"@storybook/addon-a11y": "^8.4.7",
@@ -76,18 +76,22 @@
7676
"@storybook/vue3-vite": "^8.4.7",
7777
"@vitejs/plugin-vue": "^5.1.4",
7878
"@vue/compiler-sfc": "^3.5.12",
79+
"@vue/test-utils": "^2.4.6",
7980
"@yandex/ymaps3-types": "^0.0.21",
8081
"less": "^4.2.0",
8182
"react": "^18.3.1",
8283
"react-dom": "^18.3.1",
84+
"remark-gfm": "^4.0.0",
8385
"storybook": "^8.4.7",
8486
"tsx": "^4.19.2",
8587
"typescript": "^5.6.3",
8688
"uuid": "^11.0.3",
8789
"vite": "^5.4.11",
8890
"vite-plugin-dts": "^4.3.0",
8991
"vite-svg-loader": "^5.1.0",
92+
"vitest": "^3.0.4",
9093
"vue": "^3.5.12",
94+
"vue-i18n": "10",
9195
"vue3-perfect-scrollbar": "^1.6.0"
9296
}
9397
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export type Locale = 'en-GB' | 'es-ES' | 'ru-RU'
2+
3+
export type UiDateProperties = {
4+
date: Date | string;
5+
withTime: boolean;
6+
locale?: Locale;
7+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import type { Locale } from '@/common/components/date'
2+
3+
import { getCurrentInstance } from 'vue'
4+
5+
import { format } from 'date-fns'
6+
import { enGB, es, ru } from 'date-fns/locale'
7+
8+
export const formatDateLat = (date: Date | string) => format(date, 'dd/MM/yyyy', { locale: enGB })
9+
export const formatDateRu = (date: Date | string) => format(date, 'dd.MM.yyyy', { locale: ru })
10+
11+
export const formatTime = (date: Date | string) => format(date, 'HH:mm', { locale: enGB })
12+
13+
export const formatDate = (date: Date | string, locale: Locale | undefined = undefined) => {
14+
return (locale ?? detectLocale()) === 'ru-RU'
15+
? formatDateRu(date)
16+
: formatDateLat(date)
17+
}
18+
19+
export const formatDateTime = (date: Date | string, locale: Locale | undefined = undefined) => format(date, 'Pp', {
20+
locale: {
21+
'en-GB': enGB,
22+
'es-ES': es,
23+
'ru-RU': ru,
24+
}[locale ?? detectLocale()] ?? enGB,
25+
})
26+
27+
function detectLocale(): Locale {
28+
const instance = getCurrentInstance()
29+
// Safely access i18n instance with proper type checking
30+
const i18n = instance?.appContext?.app?.config?.globalProperties?.$i18n
31+
32+
if (i18n) {
33+
try {
34+
const locale = (
35+
typeof i18n.locale === 'object'
36+
? i18n.locale.value
37+
: i18n.locale
38+
) ?? 'en-GB'
39+
40+
if (locale.startsWith('en')) return 'en-GB'
41+
if (locale.startsWith('es')) return 'es-ES'
42+
if (locale.startsWith('ru')) return 'ru-RU'
43+
} catch (error) {
44+
console.warn('Error detecting locale:', error)
45+
}
46+
}
47+
48+
return 'en-GB'
49+
}

packages/v1-components/src/host.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export { default as UiAvatar } from '@/host/components/avatar/UiAvatar.vue'
22
export { default as UiAvatarList } from '@/host/components/avatar/UiAvatarList.vue'
33
export { default as UiButton } from '@/host/components/button/UiButton.vue'
44
export { default as UiCheckbox } from '@/host/components/checkbox/UiCheckbox.vue'
5+
export { default as UiDate } from '@/host/components/date/UiDate.vue'
56
export { default as UiError } from '@/host/components/error/UiError.vue'
67
export { default as UiImage } from '@/host/components/image/UiImage.vue'
78
export { default as UiLink } from '@/host/components/link/UiLink.vue'
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<template>
2+
<time v-bind="{ datetime: formatISO(date), ...$attrs }">
3+
{{ withTime ? formatDateTime(date, locale) : formatDate(date, locale) }}
4+
</time>
5+
</template>
6+
7+
<script lang="ts" setup>
8+
import type { Locale } from '@/common/components/date'
9+
import type { PropType } from 'vue'
10+
11+
import { formatISO } from 'date-fns'
12+
13+
import {
14+
formatDate,
15+
formatDateTime,
16+
} from '@/common/formatters/date'
17+
18+
defineProps({
19+
date: {
20+
type: [Date, String] as PropType<Date | string>,
21+
required: true,
22+
},
23+
24+
locale: {
25+
type: null as unknown as PropType<Locale | undefined>,
26+
validator: (value) => typeof value === 'undefined' || ['en-GB', 'es-ES', 'ru-RU'].includes(value as Locale),
27+
default: undefined,
28+
},
29+
30+
withTime: {
31+
type: Boolean,
32+
default: false,
33+
},
34+
})
35+
</script>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type { DefineComponent } from '@/common/vue'
2+
3+
import type {
4+
UiDateProperties,
5+
} from '@/common/components/date'
6+
7+
declare const UiDate: DefineComponent<
8+
UiDateProperties
9+
>
10+
11+
export default UiDate
12+

packages/v1-components/src/remote.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,10 @@ export * from '@/remote/components/yandex-map'
1717

1818
export { usePreview } from '@/common/preview'
1919

20-
export { ImageWorkersKey } from '@/common/preview'
20+
export { ImageWorkersKey } from '@/common/preview'
21+
22+
export {
23+
formatDate,
24+
formatDateTime,
25+
formatTime,
26+
} from '@/common/formatters/date'
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Функции форматирования даты и времени
2+
3+
В этой документации описывается работа функций `formatDate()`, `formatDateTime()` и `formatTime()`.
4+
5+
## formatDate(value, locale?)
6+
7+
Функция `formatDate()` форматирует даты в соответствии с текущими настройками локализации. Принимает два параметра:
8+
9+
* `date` – объект `Date` или строка в формате, распознаваемым методом `Date.parse`;
10+
* `locale` – локаль, используемая для форматирования; поддерживаются `'en-GB'`, `'es-ES'`, и `'ru-RU'`,
11+
по-умолчанию `'en-GB'`; Также можно указать `undefined` чтобы использовалась локаль по-умолчанию;
12+
13+
### Примеры
14+
15+
```javascript
16+
formatDate(new Date('2024-01-22'), 'en-GB') // "22/01/24"
17+
formatDate(new Date('2024-01-22'), 'es-ES') // "22/01/24"
18+
formatDate(new Date('2024-01-22'), 'ru-RU') // "22.01.24"
19+
```
20+
21+
### Шаблоны форматирования по локалям
22+
23+
| Локаль | Стандартный формат |
24+
| -------- | ------------------ |
25+
| `en-GB` | dd/mm/yy |
26+
| `es-ES` | dd/mm/yy |
27+
| `ru-RU` | dd.mm.yy |
28+
29+
## formatDateTime(value, locale?)
30+
31+
Функция `formatDateTime()` объединяет форматирование даты и времени. Аргументы функции такие же, как и у `formatDate`.
32+
Использует формат даты в соответствии с локалью и добавляет время в 24-часовом формате.
33+
34+
### Примеры
35+
36+
```javascript
37+
formatDateTime(new Date('2024-01-22'), 'en-GB') // Вернёт "22/01/24 15:30"
38+
formatDateTime(new Date('2024-01-22'), 'es-ES') // Вернёт "22/01/24 15:30"
39+
formatDateTime(new Date('2024-01-22'), 'ru-RU') // "22.01.24 15:30"
40+
```
41+
42+
## formatTime(value)
43+
44+
Функция `formatTime()` форматирует время в 24-часовом формате (HH:mm).
45+
46+
### Параметры
47+
48+
* `date` – объект `Date` или строка в формате, распознаваемым методом `Date.parse`;
49+
50+
### Примеры
51+
52+
```javascript
53+
formatTime(new Date('2024-01-22 15:30:00')) // "15:30"
54+
formatTime('2024-01-22 15:30:00') // "15:30"
55+
56+
// С невалидными значениями
57+
formatTime(null) // Вернёт ""
58+
formatTime(undefined) // Вернёт ""
59+
```
60+

packages/v1-components/storybook/main.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { dirname, join } from 'path'
55

66
import remarkGfm from 'remark-gfm'
77

8-
98
/* eslint-disable @typescript-eslint/ban-ts-comment */
109
// @ts-ignore Because of PHPStorm tsconfig recognition issues for import.meta specifically
1110
const require = createRequire(import.meta.url)
@@ -51,7 +50,6 @@ const config: StorybookConfig = {
5150
stories: [
5251
'./Intro.mdx',
5352
'./docs/**/*.mdx',
54-
'../src/**/*.mdx',
5553
'./**/*.stories.@(js|jsx|mjs|ts|tsx)',
5654
],
5755
viteFinal: async (config) => {
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import ToReact from '../ToReact.ts'
2+
import UiDate from '@/host/components/date/UiDate.vue'
3+
4+
# UiDate
5+
6+
Компонент `UiDate` является обёрткой над HTML-элементом `<time>`.
7+
Он отображает отформатированные значения даты или даты и времени и поддерживает локализацию.
8+
Вы также можете включить или отключить отображение времени.
9+
10+
## API
11+
12+
### Свойства
13+
14+
* `date` – объект `Date` или строка в формате, распознаваемым методом `Date.parse`; значение даты и времени,
15+
используемые для отображения;
16+
* `locale` – локаль, используемая для форматирования; поддерживаются `'en-GB'`, `'es-ES'`, и `'ru-RU'`,
17+
по-умолчанию `'en-GB'`;
18+
* `withTime` – флаг, указывающий, что необходимо также отобразить время; по-умолчанию `false`.
19+
20+
Этот пример отображает только дату, используя локаль по умолчанию (`en-GB`).
21+
22+
<ToReact is={UiDate} date={new Date()} />
23+
24+
### Локализация с vue-i18n
25+
26+
Если ваше расширение использует [vue-i18n](https://github.com/intlify/vue-i18n), то `UiDate` компонент может
27+
автоматически извлечь текущую используемую локаль и использовать её для форматирования. В этом случае можно не указывать
28+
локаль отдельно.
29+
30+
## Применение
31+
32+
### Стандартное применение
33+
34+
```html
35+
<template>
36+
<UiDate :date="new Date()" locale="ru-RU" />
37+
</template>
38+
```
39+
40+
Результат:
41+
42+
<ToReact is={UiDate} date={new Date()} locale="ru-RU" />
43+
44+
### С отображением времени
45+
46+
```html
47+
<template>
48+
<UiDate :date="new Date()" locale="ru-RU" with-time />
49+
</template>
50+
```
51+
52+
Результат:
53+
54+
<ToReact is={UiDate} date={new Date()} locale="ru-RU" withTime />

0 commit comments

Comments
 (0)