mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
feat(AmharicDatePicker): add time input functionality and update date formatting
feat(i18n): add translation for time label in Amharic and English fix(main): ensure date styles are imported correctly
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
Popover,
|
||||
TextInput,
|
||||
} from '@mantine/core';
|
||||
import { TimeInput } from '@mantine/dates';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
import { DayPicker as EthiopicDayPicker } from '@daypicker/ethiopic';
|
||||
import { DayPicker as GregorianDayPicker } from '@daypicker/react';
|
||||
@@ -65,19 +66,40 @@ const ETH_FORMATTERS = {
|
||||
// midnight — reading back with getUTC*() (not local get*()) keeps the
|
||||
// calendar day stable regardless of the runtime's timezone, avoiding the
|
||||
// same off-by-one class of bug the UTC-noon workaround above exists for.
|
||||
function parseISO(value?: string | null): Date | null {
|
||||
function parseISO(value?: string | null, withTime = false): Date | null {
|
||||
if (!value) return null;
|
||||
const d = new Date(value);
|
||||
if (isNaN(d.getTime())) return null;
|
||||
return new Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());
|
||||
return withTime
|
||||
? d
|
||||
: new Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());
|
||||
}
|
||||
|
||||
function formatISO(date: Date): string {
|
||||
return new Date(
|
||||
function formatISO(date: Date, withTime = false): string {
|
||||
return withTime
|
||||
? date.toISOString()
|
||||
: new Date(
|
||||
Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()),
|
||||
).toISOString();
|
||||
}
|
||||
|
||||
// Combines a calendar day with a time-of-day, keeping whichever half isn't
|
||||
// changing. `base` is the currently selected Date (may be null if nothing
|
||||
// picked yet); `day`/`time` override only the half that's provided.
|
||||
function mergeDateTime(
|
||||
base: Date | null,
|
||||
day?: Date,
|
||||
time?: { hours: number; minutes: number },
|
||||
): Date {
|
||||
const result = day ? new Date(day) : new Date(base ?? new Date());
|
||||
if (time) {
|
||||
result.setHours(time.hours, time.minutes, 0, 0);
|
||||
} else if (day && base) {
|
||||
result.setHours(base.getHours(), base.getMinutes(), base.getSeconds(), 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function toEthiopicDateLabel(date: Date | string): string {
|
||||
const d = typeof date === 'string' ? parseISO(date) : date;
|
||||
if (!d) return '';
|
||||
@@ -108,6 +130,9 @@ export interface AmharicDatePickerProps {
|
||||
size?: MantineSize;
|
||||
name?: string;
|
||||
onBlur?: () => void;
|
||||
/** Show a time-of-day field alongside the calendar. Off by default —
|
||||
* most callers only need a calendar day. */
|
||||
withTime?: boolean;
|
||||
}
|
||||
|
||||
export function AmharicDatePicker({
|
||||
@@ -121,6 +146,7 @@ export function AmharicDatePicker({
|
||||
size,
|
||||
name,
|
||||
onBlur,
|
||||
withTime = false,
|
||||
}: AmharicDatePickerProps) {
|
||||
const { t, i18n } = useTranslation();
|
||||
const [calendarType, setCalendarType] = useState<'EN' | 'AMH'>(() =>
|
||||
@@ -128,9 +154,9 @@ export function AmharicDatePicker({
|
||||
);
|
||||
const [opened, { close, toggle }] = useDisclosure(false);
|
||||
|
||||
const selected = parseISO(value);
|
||||
const selected = parseISO(value, withTime);
|
||||
|
||||
const displayValue = selected
|
||||
const dateLabel = selected
|
||||
? calendarType === 'EN'
|
||||
? selected.toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
@@ -139,6 +165,11 @@ export function AmharicDatePicker({
|
||||
})
|
||||
: toAmharicDisplay(selected)
|
||||
: '';
|
||||
const timeLabel =
|
||||
withTime && selected
|
||||
? ` ${selected.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })}`
|
||||
: '';
|
||||
const displayValue = dateLabel + timeLabel;
|
||||
|
||||
return (
|
||||
<Popover
|
||||
@@ -199,7 +230,7 @@ export function AmharicDatePicker({
|
||||
captionLayout="dropdown"
|
||||
formatters={ETH_FORMATTERS}
|
||||
onSelect={(date: Date | undefined) => {
|
||||
onChange?.(date ? formatISO(date) : '');
|
||||
onChange?.(date ? formatISO(mergeDateTime(selected, date), withTime) : '');
|
||||
close();
|
||||
}}
|
||||
/>
|
||||
@@ -212,12 +243,32 @@ export function AmharicDatePicker({
|
||||
endMonth={YEAR_DROPDOWN_END}
|
||||
captionLayout="dropdown"
|
||||
onSelect={(date: Date | undefined) => {
|
||||
onChange?.(date ? formatISO(date) : '');
|
||||
onChange?.(date ? formatISO(mergeDateTime(selected, date), withTime) : '');
|
||||
close();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{withTime && (
|
||||
<TimeInput
|
||||
label={t('common.time')}
|
||||
mt="sm"
|
||||
disabled={!selected}
|
||||
value={
|
||||
selected
|
||||
? `${String(selected.getHours()).padStart(2, '0')}:${String(selected.getMinutes()).padStart(2, '0')}`
|
||||
: ''
|
||||
}
|
||||
onChange={(e) => {
|
||||
const [h, m] = e.currentTarget.value.split(':').map(Number);
|
||||
if (Number.isNaN(h) || Number.isNaN(m)) return;
|
||||
onChange?.(
|
||||
formatISO(mergeDateTime(selected, undefined, { hours: h, minutes: m }), true),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Group justify="space-between" mt="sm">
|
||||
<Button
|
||||
variant="subtle"
|
||||
@@ -233,7 +284,7 @@ export function AmharicDatePicker({
|
||||
variant="light"
|
||||
size="xs"
|
||||
onClick={() => {
|
||||
onChange?.(formatISO(new Date()));
|
||||
onChange?.(formatISO(new Date(), withTime));
|
||||
close();
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -92,6 +92,7 @@ export const am: Translations = {
|
||||
toggleTheme: 'ብርሃን / ጨለማ ገጽታ ቀይር',
|
||||
welcome: 'እንኳን ደህና መጡ',
|
||||
switchCalendar: 'የቀን መቁጠሪያ ዓይነት ቀይር',
|
||||
time: 'ሰዓት',
|
||||
},
|
||||
|
||||
auth: {
|
||||
|
||||
@@ -90,6 +90,7 @@ export const en = {
|
||||
toggleTheme: 'Toggle light / dark mode',
|
||||
welcome: 'Welcome',
|
||||
switchCalendar: 'Switch calendar type',
|
||||
time: 'Time',
|
||||
},
|
||||
|
||||
auth: {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import '@mantine/core/styles.css';
|
||||
import '@mantine/dates/styles.css';
|
||||
import '@mantine/notifications/styles.css';
|
||||
import './app/theme/portal.css';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user