rn-grid-date-time-picker 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/lib/commonjs/components/DateTimePicker.js +50 -13
- package/lib/commonjs/components/DateTimePicker.js.map +1 -1
- package/lib/commonjs/components/calendar/CalendarHeader.js +3 -1
- package/lib/commonjs/components/calendar/CalendarHeader.js.map +1 -1
- package/lib/commonjs/components/calendar/DayGrid.js +4 -1
- package/lib/commonjs/components/calendar/DayGrid.js.map +1 -1
- package/lib/commonjs/components/calendar/calendarStyles.js +2 -1
- package/lib/commonjs/components/calendar/calendarStyles.js.map +1 -1
- package/lib/commonjs/components/field/RangeFields.js +2 -0
- package/lib/commonjs/components/field/RangeFields.js.map +1 -1
- package/lib/commonjs/components/field/SegmentedInput.js +1 -1
- package/lib/commonjs/components/field/SegmentedInput.js.map +1 -1
- package/lib/commonjs/components/field/ValueField.js +57 -5
- package/lib/commonjs/components/field/ValueField.js.map +1 -1
- package/lib/commonjs/components/field/fieldStyles.js +17 -0
- package/lib/commonjs/components/field/fieldStyles.js.map +1 -1
- package/lib/commonjs/components/field/fieldText.js +12 -3
- package/lib/commonjs/components/field/fieldText.js.map +1 -1
- package/lib/commonjs/components/theme/defaultStrings.js +6 -2
- package/lib/commonjs/components/theme/defaultStrings.js.map +1 -1
- package/lib/commonjs/components/theme/direction.js +17 -0
- package/lib/commonjs/components/theme/direction.js.map +1 -0
- package/lib/commonjs/components/theme/styleKeys.js +6 -1
- package/lib/commonjs/components/theme/styleKeys.js.map +1 -1
- package/lib/commonjs/components/time/timeStyles.js +4 -5
- package/lib/commonjs/components/time/timeStyles.js.map +1 -1
- package/lib/commonjs/components/types.js.map +1 -1
- package/lib/commonjs/core/calendarSystem.js +39 -1
- package/lib/commonjs/core/calendarSystem.js.map +1 -1
- package/lib/commonjs/index.js +6 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/components/DateTimePicker.js +52 -15
- package/lib/module/components/DateTimePicker.js.map +1 -1
- package/lib/module/components/calendar/CalendarHeader.js +4 -2
- package/lib/module/components/calendar/CalendarHeader.js.map +1 -1
- package/lib/module/components/calendar/DayGrid.js +5 -2
- package/lib/module/components/calendar/DayGrid.js.map +1 -1
- package/lib/module/components/calendar/calendarStyles.js +2 -1
- package/lib/module/components/calendar/calendarStyles.js.map +1 -1
- package/lib/module/components/field/RangeFields.js +2 -0
- package/lib/module/components/field/RangeFields.js.map +1 -1
- package/lib/module/components/field/SegmentedInput.js +1 -1
- package/lib/module/components/field/SegmentedInput.js.map +1 -1
- package/lib/module/components/field/ValueField.js +59 -7
- package/lib/module/components/field/ValueField.js.map +1 -1
- package/lib/module/components/field/fieldStyles.js +17 -0
- package/lib/module/components/field/fieldStyles.js.map +1 -1
- package/lib/module/components/field/fieldText.js +10 -2
- package/lib/module/components/field/fieldText.js.map +1 -1
- package/lib/module/components/theme/defaultStrings.js +4 -1
- package/lib/module/components/theme/defaultStrings.js.map +1 -1
- package/lib/module/components/theme/direction.js +13 -0
- package/lib/module/components/theme/direction.js.map +1 -0
- package/lib/module/components/theme/styleKeys.js +6 -1
- package/lib/module/components/theme/styleKeys.js.map +1 -1
- package/lib/module/components/time/timeStyles.js +4 -5
- package/lib/module/components/time/timeStyles.js.map +1 -1
- package/lib/module/components/types.js.map +1 -1
- package/lib/module/core/calendarSystem.js +37 -0
- package/lib/module/core/calendarSystem.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/commonjs/src/components/DateTimePicker.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/components/calendar/CalendarHeader.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/components/calendar/DayGrid.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/components/calendar/calendarStyles.d.ts +1 -1
- package/lib/typescript/commonjs/src/components/calendar/calendarStyles.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/components/field/RangeFields.d.ts +2 -0
- package/lib/typescript/commonjs/src/components/field/RangeFields.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/components/field/ValueField.d.ts +4 -0
- package/lib/typescript/commonjs/src/components/field/ValueField.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/components/field/fieldStyles.d.ts +15 -0
- package/lib/typescript/commonjs/src/components/field/fieldStyles.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/components/field/fieldText.d.ts +5 -0
- package/lib/typescript/commonjs/src/components/field/fieldText.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/components/theme/defaultStrings.d.ts +2 -0
- package/lib/typescript/commonjs/src/components/theme/defaultStrings.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/components/theme/direction.d.ts +8 -0
- package/lib/typescript/commonjs/src/components/theme/direction.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/components/theme/styleKeys.d.ts +1 -1
- package/lib/typescript/commonjs/src/components/theme/styleKeys.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/components/time/timeStyles.d.ts +0 -2
- package/lib/typescript/commonjs/src/components/time/timeStyles.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/components/types.d.ts +7 -0
- package/lib/typescript/commonjs/src/components/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/core/calendarSystem.d.ts +10 -0
- package/lib/typescript/commonjs/src/core/calendarSystem.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/index.d.ts +2 -2
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/components/DateTimePicker.d.ts.map +1 -1
- package/lib/typescript/module/src/components/calendar/CalendarHeader.d.ts.map +1 -1
- package/lib/typescript/module/src/components/calendar/DayGrid.d.ts.map +1 -1
- package/lib/typescript/module/src/components/calendar/calendarStyles.d.ts +1 -1
- package/lib/typescript/module/src/components/calendar/calendarStyles.d.ts.map +1 -1
- package/lib/typescript/module/src/components/field/RangeFields.d.ts +2 -0
- package/lib/typescript/module/src/components/field/RangeFields.d.ts.map +1 -1
- package/lib/typescript/module/src/components/field/ValueField.d.ts +4 -0
- package/lib/typescript/module/src/components/field/ValueField.d.ts.map +1 -1
- package/lib/typescript/module/src/components/field/fieldStyles.d.ts +15 -0
- package/lib/typescript/module/src/components/field/fieldStyles.d.ts.map +1 -1
- package/lib/typescript/module/src/components/field/fieldText.d.ts +5 -0
- package/lib/typescript/module/src/components/field/fieldText.d.ts.map +1 -1
- package/lib/typescript/module/src/components/theme/defaultStrings.d.ts +2 -0
- package/lib/typescript/module/src/components/theme/defaultStrings.d.ts.map +1 -1
- package/lib/typescript/module/src/components/theme/direction.d.ts +8 -0
- package/lib/typescript/module/src/components/theme/direction.d.ts.map +1 -0
- package/lib/typescript/module/src/components/theme/styleKeys.d.ts +1 -1
- package/lib/typescript/module/src/components/theme/styleKeys.d.ts.map +1 -1
- package/lib/typescript/module/src/components/time/timeStyles.d.ts +0 -2
- package/lib/typescript/module/src/components/time/timeStyles.d.ts.map +1 -1
- package/lib/typescript/module/src/components/types.d.ts +7 -0
- package/lib/typescript/module/src/components/types.d.ts.map +1 -1
- package/lib/typescript/module/src/core/calendarSystem.d.ts +10 -0
- package/lib/typescript/module/src/core/calendarSystem.d.ts.map +1 -1
- package/lib/typescript/module/src/index.d.ts +2 -2
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/DateTimePicker.tsx +92 -47
- package/src/components/calendar/CalendarHeader.tsx +3 -2
- package/src/components/calendar/DayGrid.tsx +5 -2
- package/src/components/calendar/calendarStyles.ts +2 -1
- package/src/components/field/RangeFields.tsx +4 -0
- package/src/components/field/SegmentedInput.tsx +1 -1
- package/src/components/field/ValueField.tsx +83 -15
- package/src/components/field/fieldStyles.ts +6 -0
- package/src/components/field/fieldText.ts +25 -4
- package/src/components/theme/defaultStrings.ts +11 -1
- package/src/components/theme/direction.ts +12 -0
- package/src/components/theme/styleKeys.ts +5 -0
- package/src/components/time/timeStyles.ts +2 -3
- package/src/components/types.ts +7 -0
- package/src/core/calendarSystem.ts +42 -0
- package/src/index.tsx +2 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
-
import { Pressable, Text, View } from 'react-native';
|
|
2
|
+
import { Pressable, ScrollView, Text, View } from 'react-native';
|
|
3
3
|
import type { SegmentEditorAction } from '../../core/segmentEditor';
|
|
4
4
|
import type { ParsedSegments } from '../../core/segments';
|
|
5
5
|
import type { DateTimeValue, PickerType, TimeFormat } from '../../core/types';
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
type IconSet,
|
|
12
12
|
} from '../../icons';
|
|
13
13
|
import { useTypedSegments } from '../hooks/useTypedSegments';
|
|
14
|
+
import type { ScrollViewRef } from '../nativeTypes';
|
|
14
15
|
import type { PickerStrings } from '../theme/defaultStrings';
|
|
15
16
|
import type { PickerStyles } from '../theme/styleKeys';
|
|
16
17
|
import { useNumerals } from '../theme/numerals';
|
|
@@ -18,6 +19,7 @@ import type { ThemeTokens } from '../theme/tokens';
|
|
|
18
19
|
import type { InstructionRenderProps, LabelRenderProps } from '../types';
|
|
19
20
|
import FieldMessages from './FieldMessages';
|
|
20
21
|
import {
|
|
22
|
+
formatValueParts,
|
|
21
23
|
formatValueText,
|
|
22
24
|
placeholderText,
|
|
23
25
|
segmentErrorText,
|
|
@@ -26,6 +28,16 @@ import {
|
|
|
26
28
|
import { buildFieldStyles } from './fieldStyles';
|
|
27
29
|
import SegmentedInput from './SegmentedInput';
|
|
28
30
|
|
|
31
|
+
// Hebrew and Arabic letters; Arabic-Indic digits (٠–٩, ۰–۹) are left out, since
|
|
32
|
+
// digits don't set the reading direction.
|
|
33
|
+
const RTL_LETTER = '--ٟ٪-ۯۺ-ࣿ';
|
|
34
|
+
const FIRST_LETTER_RTL = new RegExp(
|
|
35
|
+
`^[^A-Za-zÀ-ɏ${RTL_LETTER}]*[${RTL_LETTER}]`
|
|
36
|
+
);
|
|
37
|
+
/** Whether a text reads right to left: its first letter is Hebrew or Arabic. */
|
|
38
|
+
export const startsRightToLeft = (text: string): boolean =>
|
|
39
|
+
FIRST_LETTER_RTL.test(text);
|
|
40
|
+
|
|
29
41
|
export interface ValueFieldProps {
|
|
30
42
|
type: PickerType;
|
|
31
43
|
/** The value to show (or to start typing from). */
|
|
@@ -79,6 +91,8 @@ export interface ValueFieldProps {
|
|
|
79
91
|
tokens: ThemeTokens;
|
|
80
92
|
styles?: PickerStyles;
|
|
81
93
|
strings: PickerStrings;
|
|
94
|
+
/** The texts of its typed-input errors. Default `strings`. */
|
|
95
|
+
errorStrings?: PickerStrings;
|
|
82
96
|
testID?: string;
|
|
83
97
|
}
|
|
84
98
|
|
|
@@ -160,10 +174,12 @@ const ValueField: React.FC<ValueFieldProps> = (props) => {
|
|
|
160
174
|
tokens,
|
|
161
175
|
styles,
|
|
162
176
|
strings,
|
|
177
|
+
errorStrings = strings,
|
|
163
178
|
testID,
|
|
164
179
|
} = props;
|
|
165
180
|
const s = buildFieldStyles(tokens);
|
|
166
181
|
const ico = resolveIcons(iconOverrides);
|
|
182
|
+
const scrollRef = useRef<ScrollViewRef>(null);
|
|
167
183
|
const [lastParsed, setLastParsed] = useState<ParsedSegments | null>(null);
|
|
168
184
|
const { state, dispatch, getState } = useTypedSegments(
|
|
169
185
|
value,
|
|
@@ -205,13 +221,25 @@ const ValueField: React.FC<ValueFieldProps> = (props) => {
|
|
|
205
221
|
const typing = !!allowTyping && editable;
|
|
206
222
|
const typedError =
|
|
207
223
|
typing && !state.focused && lastParsed && !lastParsed.isEmpty
|
|
208
|
-
? segmentErrorText(lastParsed.errors,
|
|
224
|
+
? segmentErrorText(lastParsed.errors, errorStrings, yearRange)
|
|
209
225
|
: '';
|
|
210
226
|
const error = typedError || errorMessage || '';
|
|
211
227
|
const hasValue = typing ? !state.parsed.isEmpty : !!value || !!displayText;
|
|
212
228
|
const text =
|
|
213
229
|
displayText ??
|
|
214
230
|
formatValueText(value, type, dateFormat, timeFormat, dateText, strings);
|
|
231
|
+
// With a fieldDateText style, a single date-time shows its date and time
|
|
232
|
+
// apart (no sideways scroll).
|
|
233
|
+
const valueParts =
|
|
234
|
+
styles?.fieldDateText && displayText === undefined && value
|
|
235
|
+
? formatValueParts(value, type, dateFormat, timeFormat, dateText, strings)
|
|
236
|
+
: null;
|
|
237
|
+
const parts = valueParts?.time ? valueParts : null;
|
|
238
|
+
const valueStyle = [
|
|
239
|
+
s.text,
|
|
240
|
+
styles?.fieldText,
|
|
241
|
+
disabled && [s.disabledText, styles?.fieldTextDisabled],
|
|
242
|
+
];
|
|
215
243
|
|
|
216
244
|
const clear = () => {
|
|
217
245
|
dispatch({ type: 'reset', value: null });
|
|
@@ -300,19 +328,59 @@ const ValueField: React.FC<ValueFieldProps> = (props) => {
|
|
|
300
328
|
onPress={onPress}
|
|
301
329
|
style={fieldStyle}
|
|
302
330
|
>
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
331
|
+
{parts ? (
|
|
332
|
+
<>
|
|
333
|
+
<Text
|
|
334
|
+
numberOfLines={1}
|
|
335
|
+
ellipsizeMode="clip"
|
|
336
|
+
style={[valueStyle, s.textScrolled, styles?.fieldDateText]}
|
|
337
|
+
>
|
|
338
|
+
{digits(`${parts.date}${strings.dateTimeSeparator ?? ' '}`)}
|
|
339
|
+
</Text>
|
|
340
|
+
<Text numberOfLines={1} ellipsizeMode="clip" style={valueStyle}>
|
|
341
|
+
{digits(parts.time)}
|
|
342
|
+
</Text>
|
|
343
|
+
</>
|
|
344
|
+
) : text ? (
|
|
345
|
+
// A value too long for the box scrolls sideways; a tap still opens.
|
|
346
|
+
<ScrollView
|
|
347
|
+
ref={scrollRef}
|
|
348
|
+
horizontal
|
|
349
|
+
showsHorizontalScrollIndicator={false}
|
|
350
|
+
keyboardShouldPersistTaps="always"
|
|
351
|
+
style={s.textScroll}
|
|
352
|
+
contentContainerStyle={s.textScrollContent}
|
|
353
|
+
// Right-to-left text (a Hijri date) starts at its right end.
|
|
354
|
+
onContentSizeChange={() => {
|
|
355
|
+
if (startsRightToLeft(text))
|
|
356
|
+
scrollRef.current?.scrollToEnd({ animated: false });
|
|
357
|
+
}}
|
|
358
|
+
>
|
|
359
|
+
<Text
|
|
360
|
+
numberOfLines={1}
|
|
361
|
+
// Android can measure a line a pixel short (right-to-left text
|
|
362
|
+
// most often) and would then swap its last word for "…".
|
|
363
|
+
ellipsizeMode="clip"
|
|
364
|
+
style={[valueStyle, s.textScrolled]}
|
|
365
|
+
>
|
|
366
|
+
{digits(text)}
|
|
367
|
+
</Text>
|
|
368
|
+
</ScrollView>
|
|
369
|
+
) : (
|
|
370
|
+
<Text
|
|
371
|
+
numberOfLines={1}
|
|
372
|
+
style={[
|
|
373
|
+
s.text,
|
|
374
|
+
styles?.fieldText,
|
|
375
|
+
s.placeholder,
|
|
376
|
+
styles?.fieldPlaceholder,
|
|
377
|
+
disabled && s.disabledText,
|
|
378
|
+
]}
|
|
379
|
+
>
|
|
380
|
+
{placeholder ||
|
|
381
|
+
placeholderText(type, dateFormat, timeFormat, strings)}
|
|
382
|
+
</Text>
|
|
383
|
+
)}
|
|
316
384
|
{icons}
|
|
317
385
|
</Pressable>
|
|
318
386
|
);
|
|
@@ -32,6 +32,10 @@ const create = (t: ThemeTokens) =>
|
|
|
32
32
|
fontSize: t.fontSizeMedium,
|
|
33
33
|
color: t.text,
|
|
34
34
|
},
|
|
35
|
+
textScroll: { flex: 1 },
|
|
36
|
+
textScrollContent: { flexGrow: 1, alignItems: 'center' },
|
|
37
|
+
// In the scroll, or as a date set apart from its time, the text keeps its full width.
|
|
38
|
+
textScrolled: { flex: 0, flexShrink: 0 },
|
|
35
39
|
placeholder: { color: t.placeholder },
|
|
36
40
|
disabledText: { color: t.disabledText },
|
|
37
41
|
icons: { flexDirection: 'row', alignItems: 'center', marginStart: 8 },
|
|
@@ -55,6 +59,8 @@ const create = (t: ThemeTokens) =>
|
|
|
55
59
|
alignItems: 'center',
|
|
56
60
|
flexWrap: 'nowrap',
|
|
57
61
|
},
|
|
62
|
+
// After a date, the time sits in the middle of the space left before the icons.
|
|
63
|
+
segmentTime: { flexGrow: 1, justifyContent: 'center' },
|
|
58
64
|
segment: {
|
|
59
65
|
paddingVertical: 8,
|
|
60
66
|
paddingHorizontal: 0,
|
|
@@ -36,6 +36,22 @@ export const formatTimeText = (
|
|
|
36
36
|
/** Writes a value's date some other way, e.g. as a Hijri date. */
|
|
37
37
|
export type DateTextFormatter = (value: DateTimeValue) => string;
|
|
38
38
|
|
|
39
|
+
/** A value's date and time as two texts; the time is '' for a date or a value without one. */
|
|
40
|
+
export const formatValueParts = (
|
|
41
|
+
value: DateTimeValue,
|
|
42
|
+
type: PickerType,
|
|
43
|
+
dateFormat: string,
|
|
44
|
+
timeFormat: TimeFormat,
|
|
45
|
+
dateText?: DateTextFormatter,
|
|
46
|
+
periods: PeriodLabels = DEFAULT_STRINGS
|
|
47
|
+
): { date: string; time: string } => ({
|
|
48
|
+
date: dateText ? dateText(value) : formatDateText(value, dateFormat),
|
|
49
|
+
time:
|
|
50
|
+
type === 'datetime' && value.time
|
|
51
|
+
? formatTimeText(value, timeFormat, periods)
|
|
52
|
+
: '',
|
|
53
|
+
});
|
|
54
|
+
|
|
39
55
|
export const formatValueText = (
|
|
40
56
|
value: DateTimeValue | null,
|
|
41
57
|
type: PickerType,
|
|
@@ -45,10 +61,15 @@ export const formatValueText = (
|
|
|
45
61
|
periods: PeriodLabels = DEFAULT_STRINGS
|
|
46
62
|
): string => {
|
|
47
63
|
if (!value) return '';
|
|
48
|
-
const date
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
64
|
+
const { date, time } = formatValueParts(
|
|
65
|
+
value,
|
|
66
|
+
type,
|
|
67
|
+
dateFormat,
|
|
68
|
+
timeFormat,
|
|
69
|
+
dateText,
|
|
70
|
+
periods
|
|
71
|
+
);
|
|
72
|
+
return time ? `${date}${periods.dateTimeSeparator ?? ' '}${time}` : date;
|
|
52
73
|
};
|
|
53
74
|
|
|
54
75
|
/** '21-10-2026 – 25-10-2026'; just the start (with the dash) while the end is open. */
|
|
@@ -124,9 +124,19 @@ export const DEFAULT_STRINGS: Required<PickerStrings> = {
|
|
|
124
124
|
a11yRangeEnd: 'end of range',
|
|
125
125
|
};
|
|
126
126
|
|
|
127
|
+
/** The keys that have a value: one left `undefined` (or `null`) keeps what's under it. */
|
|
128
|
+
export const compactStrings = (
|
|
129
|
+
overrides?: Partial<PickerStrings>
|
|
130
|
+
): Partial<PickerStrings> =>
|
|
131
|
+
overrides
|
|
132
|
+
? (Object.fromEntries(
|
|
133
|
+
Object.entries(overrides).filter(([, v]) => v != null)
|
|
134
|
+
) as Partial<PickerStrings>)
|
|
135
|
+
: {};
|
|
136
|
+
|
|
127
137
|
/** The English defaults with the caller's overrides on top. */
|
|
128
138
|
export const resolveStrings = (
|
|
129
139
|
overrides?: Partial<PickerStrings>
|
|
130
140
|
): Required<PickerStrings> => {
|
|
131
|
-
return { ...DEFAULT_STRINGS, ...overrides };
|
|
141
|
+
return { ...DEFAULT_STRINGS, ...compactStrings(overrides) };
|
|
132
142
|
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
import { I18nManager } from 'react-native';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Whether a part of the picker is laid out right to left. The picker sets it
|
|
6
|
+
* for the calendar and the time rows while the Hijri tab shows; elsewhere the
|
|
7
|
+
* app's direction (`I18nManager.isRTL`) decides.
|
|
8
|
+
*/
|
|
9
|
+
export const RtlContext = createContext<boolean | null>(null);
|
|
10
|
+
|
|
11
|
+
export const useIsRTL = (): boolean =>
|
|
12
|
+
useContext(RtlContext) ?? I18nManager.isRTL;
|
|
@@ -25,6 +25,11 @@ export const STYLE_KEYS = [
|
|
|
25
25
|
'fieldText',
|
|
26
26
|
/** The value's text while the field is disabled (the placeholder keeps its own colour). */
|
|
27
27
|
'fieldTextDisabled',
|
|
28
|
+
/**
|
|
29
|
+
* Sets the date of a single date-time value apart from its time, e.g.
|
|
30
|
+
* `{ minWidth: '50%' }` starts the time at the middle of the field.
|
|
31
|
+
*/
|
|
32
|
+
'fieldDateText',
|
|
28
33
|
'fieldPlaceholder',
|
|
29
34
|
'fieldSegment',
|
|
30
35
|
'fieldSegmentFocused',
|
|
@@ -23,13 +23,13 @@ const create = (t: ThemeTokens) =>
|
|
|
23
23
|
fontSize: t.fontSizeLarge,
|
|
24
24
|
color: t.heading,
|
|
25
25
|
},
|
|
26
|
-
// Times read hh:mm in every language, so the boxes don't mirror in RTL.
|
|
27
26
|
boxes: {
|
|
28
27
|
flexDirection: 'row',
|
|
29
28
|
alignItems: 'center',
|
|
30
29
|
gap: 22,
|
|
31
|
-
direction: 'ltr',
|
|
32
30
|
},
|
|
31
|
+
// The boxes follow the row's direction: right to left (the Hijri tab, or a
|
|
32
|
+
// right-to-left app), the hour sits on the right and AM / PM on the left.
|
|
33
33
|
clock: { flexDirection: 'row', alignItems: 'center' },
|
|
34
34
|
// Each box is 50 × 46 with a 1 pt border and radius 6.
|
|
35
35
|
boxWrap: { width: 50 },
|
|
@@ -104,7 +104,6 @@ const create = (t: ThemeTokens) =>
|
|
|
104
104
|
flexDirection: 'row',
|
|
105
105
|
alignItems: 'center',
|
|
106
106
|
alignSelf: 'center',
|
|
107
|
-
direction: 'ltr',
|
|
108
107
|
},
|
|
109
108
|
wheelColumn: { width: 64, height: 200, overflow: 'hidden' },
|
|
110
109
|
wheelHighlight: {
|
package/src/components/types.ts
CHANGED
|
@@ -391,6 +391,13 @@ export interface DateTimePickerProps {
|
|
|
391
391
|
theme?: Partial<ThemeTokens>;
|
|
392
392
|
styles?: PickerStyles;
|
|
393
393
|
strings?: Partial<PickerStrings>;
|
|
394
|
+
/**
|
|
395
|
+
* The texts while the Hijri tab shows (e.g. Arabic), for its calendar and
|
|
396
|
+
* time rows, the error messages inside the picker, and a date picked in it
|
|
397
|
+
* (the on-screen field's errors keep `strings`). A key left out (or `undefined`)
|
|
398
|
+
* keeps AM / PM in `hijriLocale` (ص / م), then `strings`.
|
|
399
|
+
*/
|
|
400
|
+
hijriStrings?: Partial<PickerStrings>;
|
|
394
401
|
testID?: string;
|
|
395
402
|
|
|
396
403
|
// events
|
|
@@ -255,6 +255,48 @@ const writeHijriDate = (d: CalendarDate, locale: string): string | null => {
|
|
|
255
255
|
return `${h.day} ${hijriMonthNames(locale)[h.month - 1]} ${h.year}`;
|
|
256
256
|
};
|
|
257
257
|
|
|
258
|
+
export interface Periods {
|
|
259
|
+
am: string;
|
|
260
|
+
pm: string;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const ARABIC_PERIODS: Periods = { am: 'ص', pm: 'م' };
|
|
264
|
+
const periodCache = new Map<string, Periods | null>();
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* AM and PM in the Hijri locale's language, as Intl writes them ('ar-SA' →
|
|
268
|
+
* ص / م). An Arabic locale whose engine gives none, or Latin ones, gets ص / م;
|
|
269
|
+
* another locale gets null (the picker keeps strings.am / pm).
|
|
270
|
+
*/
|
|
271
|
+
export const hijriPeriods = (locale: string): Periods | null => {
|
|
272
|
+
if (!periodCache.has(locale)) periodCache.set(locale, readPeriods(locale));
|
|
273
|
+
return periodCache.get(locale)!;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const readPeriods = (locale: string): Periods | null => {
|
|
277
|
+
const arabic = /^ar\b/i.test(locale);
|
|
278
|
+
try {
|
|
279
|
+
const f = new Intl.DateTimeFormat(locale, {
|
|
280
|
+
timeZone: 'UTC',
|
|
281
|
+
hour: 'numeric',
|
|
282
|
+
hour12: true,
|
|
283
|
+
});
|
|
284
|
+
const period = (hour: number) =>
|
|
285
|
+
f
|
|
286
|
+
.formatToParts(utcDate(2026, 0, 1, hour))
|
|
287
|
+
// Some engines write the part type in lower case.
|
|
288
|
+
.find((p) => p.type.toLowerCase() === 'dayperiod')
|
|
289
|
+
?.value.trim();
|
|
290
|
+
const am = period(9);
|
|
291
|
+
const pm = period(21);
|
|
292
|
+
if (am && pm && am !== pm && !(arabic && /[A-Za-z]/.test(am + pm)))
|
|
293
|
+
return { am, pm };
|
|
294
|
+
} catch {
|
|
295
|
+
// An engine without this locale falls through.
|
|
296
|
+
}
|
|
297
|
+
return arabic ? ARABIC_PERIODS : null;
|
|
298
|
+
};
|
|
299
|
+
|
|
258
300
|
/**
|
|
259
301
|
* The system the picker uses: the requested one, Gregorian by default.
|
|
260
302
|
* Anything Hijri falls back to Gregorian when unsupported.
|
package/src/index.tsx
CHANGED
|
@@ -31,7 +31,7 @@ export {
|
|
|
31
31
|
todayIn,
|
|
32
32
|
weekdayOf,
|
|
33
33
|
} from './core/calendarDate';
|
|
34
|
-
export { hijriDateText } from './core/calendarSystem';
|
|
34
|
+
export { hijriDateText, hijriPeriods } from './core/calendarSystem';
|
|
35
35
|
export { arabicDigits } from './core/numerals';
|
|
36
36
|
|
|
37
37
|
// Types
|
|
@@ -71,7 +71,7 @@ export type {
|
|
|
71
71
|
SheetSize,
|
|
72
72
|
Slot,
|
|
73
73
|
} from './components/types';
|
|
74
|
-
export type { CalendarSystem } from './core/calendarSystem';
|
|
74
|
+
export type { CalendarSystem, Periods } from './core/calendarSystem';
|
|
75
75
|
export type { Numerals } from './core/numerals';
|
|
76
76
|
export type { PickerStrings } from './components/theme/defaultStrings';
|
|
77
77
|
export type {
|