Skip to content

Commit 3ee3bd5

Browse files
committed
reafactor: NumberFormat and DatetimeFormat components
1 parent a9816b5 commit 3ee3bd5

File tree

3 files changed

+97
-91
lines changed

3 files changed

+97
-91
lines changed

src/components/DatetimeFormat.ts

Lines changed: 12 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { h, defineComponent, SetupContext, VNodeArrayChildren } from 'vue'
1+
import { defineComponent, SetupContext, PropType } from 'vue'
22
import { useI18n } from '../i18n'
33
import { DateTimeOptions } from '../runtime'
4-
import { isString, isPlainObject, isArray } from '../utils'
4+
import { renderFormatter, FormattableProps } from './formatRenderer'
55

66
const DATETIME_FORMAT_KEYS = [
77
'dateStyle',
@@ -33,7 +33,7 @@ export const DatetimeFormat = defineComponent({
3333
type: String
3434
},
3535
value: {
36-
type: [Number, Date],
36+
type: [Number, Date] as PropType<number | Date>,
3737
required: true
3838
},
3939
format: {
@@ -44,50 +44,16 @@ export const DatetimeFormat = defineComponent({
4444
}
4545
},
4646
setup(props, context: SetupContext) {
47-
const { slots, attrs } = context
4847
const i18n = useI18n()
4948

50-
return () => {
51-
const options = { part: true } as DateTimeOptions
52-
let orverrides = {} as Intl.DateTimeFormatOptions
53-
54-
if (props.locale) {
55-
options.locale = props.locale
56-
}
57-
58-
if (isString(props.format)) {
59-
options.key = props.format
60-
} else if (isPlainObject(props.format)) {
61-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
62-
if (isString((props.format as any).key)) {
63-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
64-
options.key = (props.format as any).key
65-
}
66-
// Filter out datetime format options only
67-
orverrides = Object.keys(props.format).reduce((options, prop) => {
68-
return DATETIME_FORMAT_KEYS.includes(prop)
69-
? Object.assign({}, options, { [prop]: props.format[prop] })
70-
: options
71-
}, {})
72-
}
73-
74-
const parts = i18n.__datetimeParts(...[props.value, options, orverrides])
75-
76-
let children = [options.key] as VNodeArrayChildren
77-
if (isArray(parts)) {
78-
children = parts.map((part, index) => {
79-
const slot = slots[part.type]
80-
return slot
81-
? slot({ [part.type]: part.value, index, parts })
82-
: [part.value]
83-
})
84-
} else if (isString(parts)) {
85-
children = [parts]
86-
}
87-
88-
return props.tag
89-
? h(props.tag, { ...attrs }, children)
90-
: h('span', { ...attrs }, children)
91-
}
49+
return renderFormatter<
50+
FormattableProps<number | Date, Intl.DateTimeFormatOptions>,
51+
number | Date,
52+
Intl.DateTimeFormatOptions,
53+
DateTimeOptions,
54+
Intl.DateTimeFormatPart
55+
>(props, context, DATETIME_FORMAT_KEYS, (...args: unknown[]) =>
56+
i18n.__datetimeParts(...args)
57+
)
9258
}
9359
})

src/components/NumberFormat.ts

Lines changed: 12 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { h, defineComponent, SetupContext, VNodeArrayChildren } from 'vue'
1+
import { defineComponent, SetupContext } from 'vue'
22
import { useI18n } from '../i18n'
33
import { NumberOptions } from '../runtime'
4-
import { isString, isPlainObject, isArray } from '../utils'
4+
import { renderFormatter, FormattableProps } from './formatRenderer'
55

66
const NUMBER_FORMAT_KEYS = [
77
'localeMatcher',
@@ -28,7 +28,7 @@ export const NumberFormat = defineComponent({
2828
type: String
2929
},
3030
value: {
31-
type: [Number, Date],
31+
type: Number,
3232
required: true
3333
},
3434
format: {
@@ -39,49 +39,16 @@ export const NumberFormat = defineComponent({
3939
}
4040
},
4141
setup(props, context: SetupContext) {
42-
const { slots, attrs } = context
4342
const i18n = useI18n()
4443

45-
return () => {
46-
const options = { part: true } as NumberOptions
47-
let orverrides = {} as Intl.NumberFormatOptions
48-
49-
if (props.locale) {
50-
options.locale = props.locale
51-
}
52-
53-
if (isString(props.format)) {
54-
options.key = props.format
55-
} else if (isPlainObject(props.format)) {
56-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
57-
if (isString((props.format as any).key)) {
58-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
59-
options.key = (props.format as any).key
60-
}
61-
// Filter out number format options only
62-
orverrides = Object.keys(props.format).reduce((options, prop) => {
63-
return NUMBER_FORMAT_KEYS.includes(prop)
64-
? Object.assign({}, options, { [prop]: props.format[prop] })
65-
: options
66-
}, {})
67-
}
68-
69-
const parts = i18n.__numberParts(...[props.value, options, orverrides])
70-
let children = [options.key] as VNodeArrayChildren
71-
if (isArray(parts)) {
72-
children = parts.map((part, index) => {
73-
const slot = slots[part.type]
74-
return slot
75-
? slot({ [part.type]: part.value, index, parts })
76-
: [part.value]
77-
})
78-
} else if (isString(parts)) {
79-
children = [parts]
80-
}
81-
82-
return props.tag
83-
? h(props.tag, { ...attrs }, children)
84-
: h('span', { ...attrs }, children)
85-
}
44+
return renderFormatter<
45+
FormattableProps<number, Intl.NumberFormatOptions>,
46+
number,
47+
Intl.NumberFormatOptions,
48+
NumberOptions,
49+
Intl.NumberFormatPart
50+
>(props, context, NUMBER_FORMAT_KEYS, (...args: unknown[]) =>
51+
i18n.__numberParts(...args)
52+
)
8653
}
8754
})

src/components/formatRenderer.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { h, RenderFunction, SetupContext, VNodeArrayChildren } from 'vue'
2+
import { Locale, NumberOptions, DateTimeOptions } from '../runtime'
3+
import { isString, isPlainObject, isArray } from '../utils'
4+
5+
export type FormattableProps<Value, Format> = {
6+
tag?: string
7+
value: Value
8+
format?: string | Format
9+
locale?: Locale
10+
}
11+
12+
type FormatOptions = NumberOptions | DateTimeOptions
13+
type FormatPartReturn = Intl.NumberFormatPart | Intl.DateTimeFormatPart
14+
type FormatOverrideOptions =
15+
| Intl.NumberFormatOptions
16+
| Intl.DateTimeFormatOptions
17+
18+
export function renderFormatter<
19+
Props extends FormattableProps<Value, Format>,
20+
Value,
21+
Format extends FormatOverrideOptions,
22+
Arg extends FormatOptions,
23+
Return extends FormatPartReturn
24+
>(
25+
props: Props,
26+
context: SetupContext,
27+
slotKeys: string[],
28+
partFormatter: (...args: unknown[]) => string | Return[]
29+
): RenderFunction {
30+
const { slots, attrs } = context
31+
32+
return () => {
33+
const options = { part: true } as Arg
34+
let orverrides = {} as FormatOverrideOptions
35+
36+
if (props.locale) {
37+
options.locale = props.locale
38+
}
39+
40+
if (isString(props.format)) {
41+
options.key = props.format
42+
} else if (isPlainObject(props.format)) {
43+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
44+
if (isString((props.format as any).key)) {
45+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
46+
options.key = (props.format as any).key
47+
}
48+
// Filter out number format options only
49+
orverrides = Object.keys(props.format).reduce((options, prop) => {
50+
return slotKeys.includes(prop)
51+
? Object.assign({}, options, { [prop]: (props.format as any)[prop] }) // eslint-disable-line @typescript-eslint/no-explicit-any
52+
: options
53+
}, {})
54+
}
55+
56+
const parts = partFormatter(...[props.value, options, orverrides])
57+
let children = [options.key] as VNodeArrayChildren
58+
if (isArray(parts)) {
59+
children = parts.map((part, index) => {
60+
const slot = slots[part.type]
61+
return slot
62+
? slot({ [part.type]: part.value, index, parts })
63+
: [part.value]
64+
})
65+
} else if (isString(parts)) {
66+
children = [parts]
67+
}
68+
69+
return props.tag
70+
? h(props.tag, { ...attrs }, children)
71+
: h('span', { ...attrs }, children)
72+
}
73+
}

0 commit comments

Comments
 (0)