From 5d16e448272bb7e6115ba5e4ee9f989af9b008fd Mon Sep 17 00:00:00 2001 From: eythaann Date: Thu, 19 Dec 2024 17:43:30 -0500 Subject: [PATCH] enh(#224): add support for multi-language on dates --- changelog.md | 1 + src/apps/shared/styles/reset.css | 4 + src/apps/toolbar/i18n/index.ts | 1 + src/apps/toolbar/modules/Date/Calendar.tsx | 166 ++++++++++++++++----- src/apps/toolbar/modules/Date/infra.tsx | 9 +- static/themes/default/theme.toolbar.css | 138 ++++++++--------- 6 files changed, 209 insertions(+), 110 deletions(-) diff --git a/changelog.md b/changelog.md index 442bc8d1..40039068 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,7 @@ ## [Unreleased] ### features - add kill process option on context menu for dock items. +- multi-language calendar and date module on toolbar. ### fix - slu-service was being closed on exit code 1. diff --git a/src/apps/shared/styles/reset.css b/src/apps/shared/styles/reset.css index d920a0db..aab9593c 100644 --- a/src/apps/shared/styles/reset.css +++ b/src/apps/shared/styles/reset.css @@ -163,3 +163,7 @@ color: inherit !important; } } + +.ant-picker-cell { + padding: 0 !important; +} \ No newline at end of file diff --git a/src/apps/toolbar/i18n/index.ts b/src/apps/toolbar/i18n/index.ts index 92afa253..dfab69ca 100644 --- a/src/apps/toolbar/i18n/index.ts +++ b/src/apps/toolbar/i18n/index.ts @@ -1,3 +1,4 @@ +import 'moment/min/locales'; import i18n from 'i18next'; import yaml from 'js-yaml'; import { initReactI18next } from 'react-i18next'; diff --git a/src/apps/toolbar/modules/Date/Calendar.tsx b/src/apps/toolbar/modules/Date/Calendar.tsx index 12bd35db..1b9ed2d2 100644 --- a/src/apps/toolbar/modules/Date/Calendar.tsx +++ b/src/apps/toolbar/modules/Date/Calendar.tsx @@ -1,8 +1,9 @@ import { Calendar, Popover, Row } from 'antd'; -import { CalendarMode } from 'antd/es/calendar/generateCalendar'; -import moment, { Moment } from 'moment'; +import { CalendarMode, HeaderRender } from 'antd/es/calendar/generateCalendar'; +import moment from 'moment'; import momentGenerateConfig from 'rc-picker/es/generate/moment'; -import { PropsWithChildren, useCallback, useState, WheelEvent } from 'react'; +import { PropsWithChildren, useCallback, useEffect, useState, WheelEvent } from 'react'; +import { useTranslation } from 'react-i18next'; import './infra.css'; import { BackgroundByLayersV2 } from '../../../seelenweg/components/BackgroundByLayers/infra'; @@ -12,12 +13,102 @@ import { useWindowFocusChange } from 'src/apps/shared/hooks'; import { Icon } from '../../../shared/components/Icon'; import { cx } from '../../../shared/styles'; -const MomentCalendar = Calendar.generateCalendar(momentGenerateConfig); +const short_week_days = { + inner: ['Su', 'Mn', 'Tu', 'We', 'Th', 'Fr', 'Sa'], +}; + +const MomentCalendar = Calendar.generateCalendar({ + ...momentGenerateConfig, + locale: { + ...momentGenerateConfig.locale, + getShortWeekDays: () => short_week_days.inner, + }, +}); + +const DateCalendarHeader: HeaderRender = (props) => { + const { type, value: date, onChange, onTypeChange } = props; + + if (type === 'month') { + return ( + + onTypeChange('year')}> + {date.format('MMMM YYYY')} + +
+ + + +
+
+ ); + } + + return ( + + onTypeChange('month')}> + {date.format('YYYY')} + +
+
+ + + +
+ + ); +}; function DateCalendar() { - const [date, setDate] = useState(moment()); + const { i18n } = useTranslation(); + + const [date, setDate] = useState(moment().locale(i18n.language)); const [viewMode, setViewMode] = useState('month'); + useEffect(() => { + setDate(date.locale(i18n.language)); + const start = date.clone().startOf('isoWeek'); + short_week_days.inner = [ + start.day(0).format('dd'), + start.day(1).format('dd'), + start.day(2).format('dd'), + start.day(3).format('dd'), + start.day(4).format('dd'), + start.day(5).format('dd'), + start.day(6).format('dd'), + start.day(7).format('dd'), + ]; + }, [i18n.language]); + const onWheel = useCallback((e: WheelEvent) => { e.preventDefault(); e.stopPropagation(); @@ -32,42 +123,49 @@ function DateCalendar() { }, []); return ( - e.stopPropagation()}> + e.stopPropagation()} + >
setViewMode(mode)} className="calendar" fullscreen={false} mode={viewMode} - headerRender={(props) => props.type == 'month' ? ( - - setViewMode('year')}>{date.format('MMMM YYYY')} -
- - - - - ) : ( - - setViewMode('month')}>{date.format('YYYY')} -
- - - - - )} - fullCellRender={(current, info) => info.type == 'date' ? ( -
{Number(current.format('DD'))}
- ) : ( -
{ - setDate(current); - setViewMode('month'); - }}>{current.format('MMMM')}
- )}/> + headerRender={DateCalendarHeader} + fullCellRender={(current, info) => + info.type == 'date' ? ( +
setDate(current)} + > + {Number(current.format('DD'))} +
+ ) : ( +
{ + setDate(current); + setViewMode('month'); + }} + > + {current.format('MMMM')} +
+ ) + } + />
); diff --git a/src/apps/toolbar/modules/Date/infra.tsx b/src/apps/toolbar/modules/Date/infra.tsx index 0eab0361..4a5e85ed 100644 --- a/src/apps/toolbar/modules/Date/infra.tsx +++ b/src/apps/toolbar/modules/Date/infra.tsx @@ -1,6 +1,7 @@ import { DateToolbarItem } from '@seelen-ui/lib/types'; import moment from 'moment'; import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; import { Item } from '../item/infra/infra'; @@ -17,15 +18,17 @@ interface Props { export function DateModule({ module }: Props) { const dateFormat = useSelector(Selectors.dateFormat); - const [date, setDate] = useState(moment().format(dateFormat)); + const { i18n } = useTranslation(); + + const [date, setDate] = useState(moment().locale(i18n.language).format(dateFormat)); let interval = dateFormat.includes('ss') ? 1000 : 1000 * 60; useInterval( () => { - setDate(moment().format(dateFormat)); + setDate(moment().locale(i18n.language).format(dateFormat)); }, interval, - [dateFormat], + [dateFormat, i18n.language], ); return ( diff --git a/static/themes/default/theme.toolbar.css b/static/themes/default/theme.toolbar.css index 565ed1b1..c36f13b4 100644 --- a/static/themes/default/theme.toolbar.css +++ b/static/themes/default/theme.toolbar.css @@ -11,7 +11,14 @@ } } -#root, .fast-settings, .tray, .wlan-selector, .media-control, .notifications, .notification-arrival, .calendar-container { +#root, +.fast-settings, +.tray, +.wlan-selector, +.media-control, +.notifications, +.notification-arrival, +.calendar-container { font-size: 0.8rem; font-weight: 500; @@ -201,7 +208,7 @@ padding: 8px 14px 0; display: flex; justify-content: space-between; - align-items: center + align-items: center; } .wlan-selector-refresh { @@ -252,7 +259,7 @@ color: var(--color-gray-600); border: 1px solid var(--color-gray-600); border-radius: 4px; - padding: 2px 4px + padding: 2px 4px; } } @@ -539,96 +546,81 @@ margin: var(--popups-margin); width: 350px; padding: 10px; - display: flex; - flex-direction: column; - gap: 10px; - max-height: calc(100vh - var(--config-height) - (var(--popups-margin) * 2)); .bg-layer-1 { - opacity: 0.4; - filter: saturate(0); - background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 250 250' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='10' numOctaves='3' stitchTiles='stitch '/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E"); - background-size: cover; - border-radius: 12px; - } - - .bg-layer-2 { background-color: var(--color-gray-50); border-radius: 12px; } - & .calendar-cell-value { - position: relative; - - top: 6px; - height: calc(100% + 12px); - - text-align: center; - background-color: rgba(175, 175, 175, 0.1); - padding-top: 6px; - - transition: color 0.4s ease-in-out; - - &.calendar-cell-off-month { - opacity: 0.8; - } - - &.calendar-cell-today { - &::before { - content: ""; - position: absolute; - top: calc(10% - 2px); - left: 10%; - width: 80%; - height: 80%; - background-color: var(--color-white); - border-radius: 12px; - z-index: -1; - } - - color: var(--config-accent-color); - } + .calendar-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 6px; - &.calendar-cell-month { - padding-top: 18px; + .calendar-date { + font-size: 1.2em; + text-transform: capitalize; + transition: 0.2s ease-in; + transition-property: color, background-color; + border-radius: 4px; + padding: 0 4px; &:hover { color: var(--config-accent-color); + background-color: var(--color-gray-200); } } - } - - & .calendar-header { - & .calendar-header-placeholder { - flex: 1; - } - > .calendar-date { - &:hover { - color: var(--config-accent-color); + .calendar-actions { + display: flex; + align-items: center; + gap: 2px; + + .calendar-navigator { + font-size: 1.2em; + padding: 3px 8px; + cursor: pointer; + background-color: transparent; + transition: 0.2s ease-in; + transition-property: color, background-color; + border-radius: 4px; + + &:hover { + color: var(--config-accent-color); + background-color: var(--color-gray-200); + } } + } + } - font-size: 1.25em; - margin: 5px; - font-style: italic; - text-decoration: underline; - cursor: pointer; + .calendar-cell-value { + --margin: 4px; + position: relative; + transition: background-color 0.2s ease-in; + border-radius: 4px; + width: calc(100% - (var(--margin) * 2)); + height: calc(100% - (var(--margin) * 2)); + display: flex; + justify-content: center; + align-items: center; + margin: var(--margin); - transition: color 0.4s ease-in-out; + &.calendar-cell-selected { + background-color: var(--color-gray-100); + color: var(--config-accent-color); } - > .calendar-navigator { - &:hover { - color: var(--config-accent-color); - } + &:hover { + background-color: var(--color-gray-200); + } - font-size: 1.25em; - padding: 3px 8px; - margin: 2px; - cursor: pointer; - background-color: transparent; + &.calendar-cell-today { + background-color: rgba(var(--config-accent-color-rgb), 0.2); + } - transition: color 0.4s ease-in-out; + &.calendar-cell-month { + text-transform: capitalize; } } }