mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
feat: enhance AmharicDatePicker with Ethiopian time selection and formatting
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
||||
Group,
|
||||
MantineSize,
|
||||
Popover,
|
||||
Select,
|
||||
TextInput,
|
||||
} from '@mantine/core';
|
||||
import { TimeInput } from '@mantine/dates';
|
||||
@@ -51,6 +52,40 @@ function toAmharicDisplay(date: Date): string {
|
||||
}
|
||||
}
|
||||
|
||||
export type EthPeriod = 'lelit' | 'tewat' | 'ken' | 'mata';
|
||||
|
||||
// Ethiopian day starts at 6am. Period is picked from the 24h hour; the
|
||||
// displayed hour is the Western hour shifted 6, wrapped onto a 12-hour dial.
|
||||
// Each period only spans 6 hours (not 12): ጠዋት/ማታ show 12,1..5, ቀን/ሌሊት show
|
||||
// 6..11 — offering all 12 hours in every period would let a user pick e.g.
|
||||
// "ጠዋት 7", which has no 06:00–11:59 preimage.
|
||||
const ETH_PERIODS: { value: EthPeriod; label: string; hours: number[] }[] = [
|
||||
{ value: 'tewat', label: 'ጠዋት', hours: [12, 1, 2, 3, 4, 5] }, // 06:00–11:59
|
||||
{ value: 'ken', label: 'ቀን', hours: [6, 7, 8, 9, 10, 11] }, // 12:00–17:59
|
||||
{ value: 'mata', label: 'ማታ', hours: [12, 1, 2, 3, 4, 5] }, // 18:00–23:59
|
||||
{ value: 'lelit', label: 'ሌሊት', hours: [6, 7, 8, 9, 10, 11] }, // 00:00–05:59
|
||||
];
|
||||
|
||||
export function toEthTime(h24: number): { period: EthPeriod; hour: number } {
|
||||
const period: EthPeriod =
|
||||
h24 < 6 ? 'lelit' : h24 < 12 ? 'tewat' : h24 < 18 ? 'ken' : 'mata';
|
||||
return { period, hour: ((h24 + 6) % 12) || 12 };
|
||||
}
|
||||
|
||||
export function fromEthTime(period: EthPeriod, hour: number): number {
|
||||
// Inverse of the shift, then re-add the 12h that %12 discarded for the
|
||||
// afternoon/night pair of periods.
|
||||
const pm = period === 'ken' || period === 'mata';
|
||||
return ((hour + 6) % 12) + (pm ? 12 : 0);
|
||||
}
|
||||
|
||||
function ethTimeLabel(date: Date): string {
|
||||
const { period, hour } = toEthTime(date.getHours());
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
const label = ETH_PERIODS.find((p) => p.value === period)?.label ?? '';
|
||||
return `${hour}:${minutes} ${label}`;
|
||||
}
|
||||
|
||||
// react-day-picker calls these with the Gregorian Date it tracks internally;
|
||||
// override so the caption/dropdown show Amharic month names instead of the
|
||||
// library's Latin transliteration (triggered by numerals="latn" below).
|
||||
@@ -215,7 +250,9 @@ export function AmharicDatePicker({
|
||||
: '';
|
||||
const timeLabel =
|
||||
withTime && selected
|
||||
? ` ${selected.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })}`
|
||||
? calendarType === 'AMH'
|
||||
? ` ${ethTimeLabel(selected)}`
|
||||
: ` ${selected.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })}`
|
||||
: '';
|
||||
const displayValue = dateLabel + timeLabel;
|
||||
|
||||
@@ -329,7 +366,61 @@ export function AmharicDatePicker({
|
||||
/>
|
||||
)}
|
||||
|
||||
{withTime && (
|
||||
{withTime && calendarType === 'AMH' && (() => {
|
||||
const { period, hour } = selected ? toEthTime(selected.getHours()) : { period: 'tewat' as EthPeriod, hour: 12 };
|
||||
const minute = selected ? selected.getMinutes() : 0;
|
||||
const periodHours = ETH_PERIODS.find((p) => p.value === period)?.hours ?? ETH_PERIODS[0].hours;
|
||||
const setTime = (h24: number, m: number) =>
|
||||
onChange?.(
|
||||
formatWireValue(mergeDateTime(selected, undefined, { hours: h24, minutes: m }), dateFormat, true),
|
||||
);
|
||||
return (
|
||||
<Group grow mt="sm">
|
||||
<Select
|
||||
label={t('common.time')}
|
||||
disabled={!selected}
|
||||
value={period}
|
||||
data={ETH_PERIODS.map((p) => ({ value: p.value, label: p.label }))}
|
||||
allowDeselect={false}
|
||||
onChange={(next) => {
|
||||
if (!next) return;
|
||||
// Keep the same slot index within the new period so the
|
||||
// hour never lands outside its 6-hour range.
|
||||
const slot = Math.max(periodHours.indexOf(hour), 0);
|
||||
const nextHours = ETH_PERIODS.find((p) => p.value === next)?.hours ?? periodHours;
|
||||
setTime(fromEthTime(next as EthPeriod, nextHours[slot]), minute);
|
||||
}}
|
||||
/>
|
||||
<Select
|
||||
label=" "
|
||||
disabled={!selected}
|
||||
value={String(hour)}
|
||||
data={periodHours.map((h) => ({ value: String(h), label: String(h) }))}
|
||||
allowDeselect={false}
|
||||
onChange={(next) => {
|
||||
if (!next) return;
|
||||
setTime(fromEthTime(period, Number(next)), minute);
|
||||
}}
|
||||
/>
|
||||
<Select
|
||||
label=" "
|
||||
disabled={!selected}
|
||||
value={String(minute).padStart(2, '0')}
|
||||
data={Array.from({ length: 60 }, (_, m) => ({
|
||||
value: String(m).padStart(2, '0'),
|
||||
label: String(m).padStart(2, '0'),
|
||||
}))}
|
||||
allowDeselect={false}
|
||||
onChange={(next) => {
|
||||
if (next == null) return;
|
||||
setTime(fromEthTime(period, hour), Number(next));
|
||||
}}
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
})()}
|
||||
|
||||
{withTime && calendarType === 'EN' && (
|
||||
<TimeInput
|
||||
label={t('common.time')}
|
||||
mt="sm"
|
||||
|
||||
Reference in New Issue
Block a user