Remove dayjs and convert existing to Temporal
This commit is contained in:
parent
a74fee9318
commit
d2bee83db4
|
|
@ -15,7 +15,6 @@
|
||||||
"@js-temporal/polyfill": "^0.4.4",
|
"@js-temporal/polyfill": "^0.4.4",
|
||||||
"@microsoft/microsoft-graph-client": "^3.0.5",
|
"@microsoft/microsoft-graph-client": "^3.0.5",
|
||||||
"accept-language": "^3.0.18",
|
"accept-language": "^3.0.18",
|
||||||
"dayjs": "^1.11.7",
|
|
||||||
"gapi-script": "^1.2.0",
|
"gapi-script": "^1.2.0",
|
||||||
"goober": "^2.1.13",
|
"goober": "^2.1.13",
|
||||||
"hue-map": "^1.0.0",
|
"hue-map": "^1.0.0",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { FieldValues, useController, UseControllerProps } from 'react-hook-form'
|
import { FieldValues, useController, UseControllerProps } from 'react-hook-form'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { Temporal } from '@js-temporal/polyfill'
|
||||||
|
|
||||||
import { Description, Label, Wrapper } from '/src/components/Field/Field'
|
import { Description, Label, Wrapper } from '/src/components/Field/Field'
|
||||||
import ToggleField from '/src/components/ToggleField/ToggleField'
|
import ToggleField from '/src/components/ToggleField/ToggleField'
|
||||||
|
|
@ -27,7 +28,7 @@ const CalendarField = <TValues extends FieldValues>({
|
||||||
const [innerValue, setInnerValue] = useState({
|
const [innerValue, setInnerValue] = useState({
|
||||||
specific: [],
|
specific: [],
|
||||||
week: [],
|
week: [],
|
||||||
} satisfies Record<typeof type, string[]>)
|
} satisfies Record<typeof type, Temporal.PlainDate[]>)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setInnerValue({ ...innerValue, [type]: field.value })
|
setInnerValue({ ...innerValue, [type]: field.value })
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,29 @@
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
import { useCallback, useMemo, useRef, useState } from 'react'
|
||||||
import { rotateArray } from '@giraugh/tools'
|
import { rotateArray } from '@giraugh/tools'
|
||||||
import { Dayjs } from 'dayjs'
|
import { Temporal } from '@js-temporal/polyfill'
|
||||||
import { ChevronLeft, ChevronRight } from 'lucide-react'
|
import { ChevronLeft, ChevronRight } from 'lucide-react'
|
||||||
|
|
||||||
import Button from '/src/components/Button/Button'
|
import Button from '/src/components/Button/Button'
|
||||||
import { useDayjs } from '/src/config/dayjs'
|
|
||||||
import { useTranslation } from '/src/i18n/client'
|
import { useTranslation } from '/src/i18n/client'
|
||||||
import { useStore } from '/src/stores'
|
import { useStore } from '/src/stores'
|
||||||
import useSettingsStore from '/src/stores/settingsStore'
|
import useSettingsStore from '/src/stores/settingsStore'
|
||||||
import { makeClass } from '/src/utils'
|
import { getWeekdayNames, makeClass } from '/src/utils'
|
||||||
|
|
||||||
import styles from './Month.module.scss'
|
import styles from './Month.module.scss'
|
||||||
|
|
||||||
interface MonthProps {
|
interface MonthProps {
|
||||||
/** Array of dates in `DDMMYYYY` format */
|
/** Stringified PlainDate `YYYY-MM-DD` */
|
||||||
value: string[]
|
value: string[]
|
||||||
onChange: (value: string[]) => void
|
onChange: (value: string[]) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const Month = ({ value, onChange }: MonthProps) => {
|
const Month = ({ value, onChange }: MonthProps) => {
|
||||||
const { t } = useTranslation('home')
|
const { t, i18n } = useTranslation('home')
|
||||||
const dayjs = useDayjs()
|
|
||||||
|
|
||||||
const weekStart = useStore(useSettingsStore, state => state.weekStart) ?? 0
|
const weekStart = useStore(useSettingsStore, state => state.weekStart) ?? 1
|
||||||
|
|
||||||
const [page, setPage] = useState({
|
const [page, setPage] = useState<Temporal.PlainYearMonth>(Temporal.Now.plainDateISO().toPlainYearMonth())
|
||||||
month: dayjs().month(),
|
const dates = useMemo(() => calculateMonth(page, weekStart), [page, weekStart])
|
||||||
year: dayjs().year(),
|
|
||||||
})
|
|
||||||
const [dates, setDates] = useState(calculateMonth(dayjs().month(page.month).year(page.year), weekStart))
|
|
||||||
|
|
||||||
// Ref and state required to rerender but also access static version in callbacks
|
// Ref and state required to rerender but also access static version in callbacks
|
||||||
const selectingRef = useRef<string[]>([])
|
const selectingRef = useRef<string[]>([])
|
||||||
|
|
@ -41,12 +36,6 @@ const Month = ({ value, onChange }: MonthProps) => {
|
||||||
const startPos = useRef({ x: 0, y: 0 })
|
const startPos = useRef({ x: 0, y: 0 })
|
||||||
const mode = useRef<'add' | 'remove'>()
|
const mode = useRef<'add' | 'remove'>()
|
||||||
|
|
||||||
// Update month view
|
|
||||||
useEffect(() => {
|
|
||||||
dayjs.updateLocale(dayjs.locale(), { weekStart })
|
|
||||||
setDates(calculateMonth(dayjs().month(page.month).year(page.year), weekStart))
|
|
||||||
}, [weekStart, page])
|
|
||||||
|
|
||||||
const handleFinishSelection = useCallback(() => {
|
const handleFinishSelection = useCallback(() => {
|
||||||
if (mode.current === 'add') {
|
if (mode.current === 'add') {
|
||||||
onChange([...value, ...selectingRef.current])
|
onChange([...value, ...selectingRef.current])
|
||||||
|
|
@ -60,31 +49,19 @@ const Month = ({ value, onChange }: MonthProps) => {
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<Button
|
<Button
|
||||||
title={t<string>('form.dates.tooltips.previous')}
|
title={t<string>('form.dates.tooltips.previous')}
|
||||||
onClick={() => {
|
onClick={() => setPage(page.subtract({ months: 1 }))}
|
||||||
if (page.month - 1 < 0) {
|
|
||||||
setPage({ month: 11, year: page.year - 1 })
|
|
||||||
} else {
|
|
||||||
setPage({ ...page, month: page.month - 1 })
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
icon={<ChevronLeft />}
|
icon={<ChevronLeft />}
|
||||||
/>
|
/>
|
||||||
<span>{dayjs.months()[page.month]} {page.year}</span>
|
<span>{page.toPlainDate({ day: 1 }).toLocaleString(i18n.language, { month: 'long', year: 'numeric' })}</span>
|
||||||
<Button
|
<Button
|
||||||
title={t<string>('form.dates.tooltips.next')}
|
title={t<string>('form.dates.tooltips.next')}
|
||||||
onClick={() => {
|
onClick={() => setPage(page.add({ months: 1 }))}
|
||||||
if (page.month + 1 > 11) {
|
|
||||||
setPage({ month: 0, year: page.year + 1 })
|
|
||||||
} else {
|
|
||||||
setPage({ ...page, month: page.month + 1 })
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
icon={<ChevronRight />}
|
icon={<ChevronRight />}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.dayLabels}>
|
<div className={styles.dayLabels}>
|
||||||
{(rotateArray(dayjs.weekdaysShort(), -weekStart)).map(name =>
|
{(rotateArray(getWeekdayNames(i18n.language, 'short'), weekStart)).map(name =>
|
||||||
<label key={name}>{name}</label>
|
<label key={name}>{name}</label>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -96,27 +73,27 @@ const Month = ({ value, onChange }: MonthProps) => {
|
||||||
className={makeClass(
|
className={makeClass(
|
||||||
styles.date,
|
styles.date,
|
||||||
date.month !== page.month && styles.otherMonth,
|
date.month !== page.month && styles.otherMonth,
|
||||||
date.isToday && styles.today,
|
date.equals(Temporal.Now.plainDateISO()) && styles.today,
|
||||||
(
|
(
|
||||||
(!(mode.current === 'remove' && selecting.includes(date.str)) && value.includes(date.str))
|
(!(mode.current === 'remove' && selecting.includes(date.toString())) && value.includes(date.toString()))
|
||||||
|| (mode.current === 'add' && selecting.includes(date.str))
|
|| (mode.current === 'add' && selecting.includes(date.toString()))
|
||||||
) && styles.selected,
|
) && styles.selected,
|
||||||
)}
|
)}
|
||||||
key={date.str}
|
key={date.toString()}
|
||||||
title={`${date.day} ${dayjs.months()[date.month]}${date.isToday ? ` (${t('form.dates.tooltips.today')})` : ''}`}
|
title={`${date.toLocaleString(i18n.language, { day: 'numeric', month: 'long' })}${date.equals(Temporal.Now.plainDateISO()) ? ` (${t('form.dates.tooltips.today')})` : ''}`}
|
||||||
onKeyDown={e => {
|
onKeyDown={e => {
|
||||||
if (e.key === ' ' || e.key === 'Enter') {
|
if (e.key === ' ' || e.key === 'Enter') {
|
||||||
if (value.includes(date.str)) {
|
if (value.includes(date.toString())) {
|
||||||
onChange(value.filter(d => d !== date.str))
|
onChange(value.filter(d => d !== date.toString()))
|
||||||
} else {
|
} else {
|
||||||
onChange([...value, date.str])
|
onChange([...value, date.toString()])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onPointerDown={e => {
|
onPointerDown={e => {
|
||||||
startPos.current = { x, y }
|
startPos.current = { x, y }
|
||||||
mode.current = value.includes(date.str) ? 'remove' : 'add'
|
mode.current = value.includes(date.toString()) ? 'remove' : 'add'
|
||||||
setSelecting([date.str])
|
setSelecting([date.toString()])
|
||||||
e.currentTarget.releasePointerCapture(e.pointerId)
|
e.currentTarget.releasePointerCapture(e.pointerId)
|
||||||
|
|
||||||
document.addEventListener('pointerup', handleFinishSelection, { once: true })
|
document.addEventListener('pointerup', handleFinishSelection, { once: true })
|
||||||
|
|
@ -129,10 +106,10 @@ const Month = ({ value, onChange }: MonthProps) => {
|
||||||
found.push({ y: cy, x: cx })
|
found.push({ y: cy, x: cx })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setSelecting(found.map(d => dates[d.y][d.x].str))
|
setSelecting(found.map(d => dates[d.y][d.x].toString()))
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>{date.day}</button>)
|
>{date.toLocaleString(i18n.language, { day: 'numeric' })}</button>)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
@ -140,32 +117,19 @@ const Month = ({ value, onChange }: MonthProps) => {
|
||||||
|
|
||||||
export default Month
|
export default Month
|
||||||
|
|
||||||
interface Date {
|
|
||||||
str: string
|
|
||||||
day: number
|
|
||||||
month: number
|
|
||||||
isToday: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Calculate the dates to show for the month in a 2d array */
|
/** Calculate the dates to show for the month in a 2d array */
|
||||||
const calculateMonth = (date: Dayjs, weekStart: 0 | 1) => {
|
const calculateMonth = (month: Temporal.PlainYearMonth, weekStart: 0 | 1) => {
|
||||||
const daysInMonth = date.daysInMonth()
|
const daysBefore = month.toPlainDate({ day: 1 }).dayOfWeek - (weekStart ? 0 : 1)
|
||||||
const daysBefore = date.date(1).day() - weekStart
|
const daysAfter = 6 - month.toPlainDate({ day: month.daysInMonth }).dayOfWeek + (weekStart ? 0 : 1)
|
||||||
const daysAfter = 6 - date.date(daysInMonth).day() + weekStart
|
|
||||||
|
|
||||||
const dates: Date[][] = []
|
const dates: Temporal.PlainDate[][] = []
|
||||||
let curDate = date.date(1).subtract(daysBefore, 'day')
|
let curDate = month.toPlainDate({ day: 1 }).subtract({ days: daysBefore })
|
||||||
let y = 0
|
let y = 0
|
||||||
let x = 0
|
let x = 0
|
||||||
for (let i = 0; i < daysBefore + daysInMonth + daysAfter; i++) {
|
for (let i = 0; i < daysBefore + month.daysInMonth + daysAfter; i++) {
|
||||||
if (x === 0) dates[y] = []
|
if (x === 0) dates[y] = []
|
||||||
dates[y][x] = {
|
dates[y][x] = curDate
|
||||||
str: curDate.format('DDMMYYYY'),
|
curDate = curDate.add({ days: 1 })
|
||||||
day: curDate.date(),
|
|
||||||
month: curDate.month(),
|
|
||||||
isToday: curDate.isToday(),
|
|
||||||
}
|
|
||||||
curDate = curDate.add(1, 'day')
|
|
||||||
x++
|
x++
|
||||||
if (x > 6) {
|
if (x > 6) {
|
||||||
x = 0
|
x = 0
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { useCallback, useMemo, useRef, useState } from 'react'
|
import { useCallback, useMemo, useRef, useState } from 'react'
|
||||||
import { rotateArray } from '@giraugh/tools'
|
import { range, rotateArray } from '@giraugh/tools'
|
||||||
|
import { Temporal } from '@js-temporal/polyfill'
|
||||||
|
|
||||||
import { useDayjs } from '/src/config/dayjs'
|
|
||||||
import { useTranslation } from '/src/i18n/client'
|
import { useTranslation } from '/src/i18n/client'
|
||||||
import { useStore } from '/src/stores'
|
import { useStore } from '/src/stores'
|
||||||
import useSettingsStore from '/src/stores/settingsStore'
|
import useSettingsStore from '/src/stores/settingsStore'
|
||||||
|
|
@ -11,22 +11,17 @@ import { makeClass } from '/src/utils'
|
||||||
import styles from '../Month/Month.module.scss'
|
import styles from '../Month/Month.module.scss'
|
||||||
|
|
||||||
interface WeekdaysProps {
|
interface WeekdaysProps {
|
||||||
/** Array of weekdays as numbers from 0-6 (as strings) */
|
/** dayOfWeek 1-7 as a string */
|
||||||
value: string[]
|
value: string[]
|
||||||
onChange: (value: string[]) => void
|
onChange: (value: string[]) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const Weekdays = ({ value, onChange }: WeekdaysProps) => {
|
const Weekdays = ({ value, onChange }: WeekdaysProps) => {
|
||||||
const { t } = useTranslation('home')
|
const { t, i18n } = useTranslation('home')
|
||||||
const dayjs = useDayjs()
|
|
||||||
|
|
||||||
const weekStart = useStore(useSettingsStore, state => state.weekStart) ?? 0
|
const weekStart = useStore(useSettingsStore, state => state.weekStart) ?? 1
|
||||||
|
|
||||||
const weekdays = useMemo(() => rotateArray(dayjs.weekdaysShort().map((name, i) => ({
|
const weekdays = useMemo(() => rotateArray(range(1, 7).map(i => Temporal.Now.plainDateISO().add({ days: i - Temporal.Now.plainDateISO().dayOfWeek })), weekStart), [weekStart])
|
||||||
name,
|
|
||||||
isToday: dayjs().day() === i,
|
|
||||||
str: String(i),
|
|
||||||
})), -weekStart), [weekStart])
|
|
||||||
|
|
||||||
// Ref and state required to rerender but also access static version in callbacks
|
// Ref and state required to rerender but also access static version in callbacks
|
||||||
const selectingRef = useRef<string[]>([])
|
const selectingRef = useRef<string[]>([])
|
||||||
|
|
@ -54,27 +49,27 @@ const Weekdays = ({ value, onChange }: WeekdaysProps) => {
|
||||||
type="button"
|
type="button"
|
||||||
className={makeClass(
|
className={makeClass(
|
||||||
styles.date,
|
styles.date,
|
||||||
day.isToday && styles.today,
|
day.equals(Temporal.Now.plainDateISO()) && styles.today,
|
||||||
(
|
(
|
||||||
(!(mode.current === 'remove' && selecting.includes(day.str)) && value.includes(day.str))
|
(!(mode.current === 'remove' && selecting.includes(day.dayOfWeek.toString())) && value.includes(day.dayOfWeek.toString()))
|
||||||
|| (mode.current === 'add' && selecting.includes(day.str))
|
|| (mode.current === 'add' && selecting.includes(day.dayOfWeek.toString()))
|
||||||
) && styles.selected,
|
) && styles.selected,
|
||||||
)}
|
)}
|
||||||
key={day.name}
|
key={day.toString()}
|
||||||
title={day.isToday ? t<string>('form.dates.tooltips.today') : undefined}
|
title={day.equals(Temporal.Now.plainDateISO()) ? t<string>('form.dates.tooltips.today') : undefined}
|
||||||
onKeyDown={e => {
|
onKeyDown={e => {
|
||||||
if (e.key === ' ' || e.key === 'Enter') {
|
if (e.key === ' ' || e.key === 'Enter') {
|
||||||
if (value.includes(day.str)) {
|
if (value.includes(day.dayOfWeek.toString())) {
|
||||||
onChange(value.filter(d => d !== day.str))
|
onChange(value.filter(d => d !== day.dayOfWeek.toString()))
|
||||||
} else {
|
} else {
|
||||||
onChange([...value, day.str])
|
onChange([...value, day.dayOfWeek.toString()])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onPointerDown={e => {
|
onPointerDown={e => {
|
||||||
startPos.current = i
|
startPos.current = i
|
||||||
mode.current = value.includes(day.str) ? 'remove' : 'add'
|
mode.current = value.includes(day.dayOfWeek.toString()) ? 'remove' : 'add'
|
||||||
setSelecting([day.str])
|
setSelecting([day.dayOfWeek.toString()])
|
||||||
e.currentTarget.releasePointerCapture(e.pointerId)
|
e.currentTarget.releasePointerCapture(e.pointerId)
|
||||||
|
|
||||||
document.addEventListener('pointerup', handleFinishSelection, { once: true })
|
document.addEventListener('pointerup', handleFinishSelection, { once: true })
|
||||||
|
|
@ -83,12 +78,12 @@ const Weekdays = ({ value, onChange }: WeekdaysProps) => {
|
||||||
if (mode.current) {
|
if (mode.current) {
|
||||||
const found = []
|
const found = []
|
||||||
for (let ci = Math.min(startPos.current, i); ci < Math.max(startPos.current, i) + 1; ci++) {
|
for (let ci = Math.min(startPos.current, i); ci < Math.max(startPos.current, i) + 1; ci++) {
|
||||||
found.push(weekdays[ci].str)
|
found.push(weekdays[ci].dayOfWeek.toString())
|
||||||
}
|
}
|
||||||
setSelecting(found)
|
setSelecting(found)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>{day.name}</button>
|
>{day.toLocaleString(i18n.language, { weekday: 'short' })}</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { SubmitHandler, useForm } from 'react-hook-form'
|
import { SubmitHandler, useForm } from 'react-hook-form'
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
|
import { Temporal } from '@js-temporal/polyfill'
|
||||||
|
|
||||||
import Button from '/src/components/Button/Button'
|
import Button from '/src/components/Button/Button'
|
||||||
import CalendarField from '/src/components/CalendarField/CalendarField'
|
import CalendarField from '/src/components/CalendarField/CalendarField'
|
||||||
|
|
@ -11,7 +12,6 @@ import SelectField from '/src/components/SelectField/SelectField'
|
||||||
import TextField from '/src/components/TextField/TextField'
|
import TextField from '/src/components/TextField/TextField'
|
||||||
import TimeRangeField from '/src/components/TimeRangeField/TimeRangeField'
|
import TimeRangeField from '/src/components/TimeRangeField/TimeRangeField'
|
||||||
import { API_BASE } from '/src/config/api'
|
import { API_BASE } from '/src/config/api'
|
||||||
import { useDayjs } from '/src/config/dayjs'
|
|
||||||
import { useTranslation } from '/src/i18n/client'
|
import { useTranslation } from '/src/i18n/client'
|
||||||
import timezones from '/src/res/timezones.json'
|
import timezones from '/src/res/timezones.json'
|
||||||
|
|
||||||
|
|
@ -36,7 +36,6 @@ const defaultValues: Fields = {
|
||||||
|
|
||||||
const CreateForm = () => {
|
const CreateForm = () => {
|
||||||
const { t } = useTranslation('home')
|
const { t } = useTranslation('home')
|
||||||
const dayjs = useDayjs()
|
|
||||||
const { push } = useRouter()
|
const { push } = useRouter()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|
@ -58,38 +57,38 @@ const CreateForm = () => {
|
||||||
if (dates.length === 0) {
|
if (dates.length === 0) {
|
||||||
return setError(t('form.errors.no_dates'))
|
return setError(t('form.errors.no_dates'))
|
||||||
}
|
}
|
||||||
const isSpecificDates = dates[0].length === 8
|
|
||||||
if (time.start === time.end) {
|
if (time.start === time.end) {
|
||||||
return setError(t('form.errors.same_times'))
|
return setError(t('form.errors.same_times'))
|
||||||
}
|
}
|
||||||
|
|
||||||
const times = dates.reduce((times, date) => {
|
// If format is `YYYY-MM-DD` or `d`
|
||||||
|
const isSpecificDates = dates[0].length !== 1
|
||||||
|
|
||||||
|
const times = dates.reduce((times, dateStr) => {
|
||||||
const day = []
|
const day = []
|
||||||
|
const date = isSpecificDates
|
||||||
|
? Temporal.PlainDate.from(dateStr)
|
||||||
|
: Temporal.Now.plainDateISO().add({ days: Number(dateStr) - Temporal.Now.plainDateISO().dayOfWeek })
|
||||||
|
|
||||||
for (let i = time.start; i < (time.start > time.end ? 24 : time.end); i++) {
|
for (let i = time.start; i < (time.start > time.end ? 24 : time.end); i++) {
|
||||||
|
const dateTime = date.toZonedDateTime({ timeZone: timezone, plainTime: Temporal.PlainTime.from({ hour: i }) }).withTimeZone('UTC')
|
||||||
if (isSpecificDates) {
|
if (isSpecificDates) {
|
||||||
day.push(
|
// Format as `HHmm-DDMMYYYY`
|
||||||
dayjs.tz(date, 'DDMMYYYY', timezone)
|
day.push(`${dateTime.hour.toString().padStart(2, '0')}${dateTime.minute.toString().padStart(2, '0')}-${dateTime.day.toString().padStart(2, '0')}${dateTime.month.toString().padStart(2, '0')}${dateTime.year.toString().padStart(4, '0')}`)
|
||||||
.hour(i).minute(0).utc().format('HHmm-DDMMYYYY')
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
day.push(
|
// Format as `HHmm-d`
|
||||||
dayjs().tz(timezone)
|
day.push(`${dateTime.hour.toString().padStart(2, '0')}${dateTime.minute.toString().padStart(2, '0')}-${String(dateTime.dayOfWeek === 7 ? 0 : dateTime.dayOfWeek)}`)
|
||||||
.day(Number(date)).hour(i).minute(0).utc().format('HHmm-d')
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (time.start > time.end) {
|
if (time.start > time.end) {
|
||||||
for (let i = 0; i < time.end; i++) {
|
for (let i = 0; i < time.end; i++) {
|
||||||
|
const dateTime = date.toZonedDateTime({ timeZone: timezone, plainTime: Temporal.PlainTime.from({ hour: i }) }).withTimeZone('UTC')
|
||||||
if (isSpecificDates) {
|
if (isSpecificDates) {
|
||||||
day.push(
|
// Format as `HHmm-DDMMYYYY`
|
||||||
dayjs.tz(date, 'DDMMYYYY', timezone)
|
day.push(`${dateTime.hour.toString().padStart(2, '0')}${dateTime.minute.toString().padStart(2, '0')}-${dateTime.day.toString().padStart(2, '0')}${dateTime.month.toString().padStart(2, '0')}${dateTime.year.toString().padStart(4, '0')}`)
|
||||||
.hour(i).minute(0).utc().format('HHmm-DDMMYYYY')
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
day.push(
|
// Format as `HHmm-d`
|
||||||
dayjs().tz(timezone)
|
day.push(`${dateTime.hour.toString().padStart(2, '0')}${dateTime.minute.toString().padStart(2, '0')}-${String(dateTime.dayOfWeek === 7 ? 0 : dateTime.dayOfWeek)}`)
|
||||||
.day(Number(date)).hour(i).minute(0).utc().format('HHmm-d')
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
import { Temporal } from '@js-temporal/polyfill'
|
||||||
|
|
||||||
import Content from '/src/components/Content/Content'
|
import Content from '/src/components/Content/Content'
|
||||||
import Section from '/src/components/Section/Section'
|
import Section from '/src/components/Section/Section'
|
||||||
import { useDayjs } from '/src/config/dayjs'
|
|
||||||
import { useTranslation } from '/src/i18n/client'
|
import { useTranslation } from '/src/i18n/client'
|
||||||
import { useStore } from '/src/stores'
|
import { useStore } from '/src/stores'
|
||||||
import useRecentsStore from '/src/stores/recentsStore'
|
import useRecentsStore from '/src/stores/recentsStore'
|
||||||
|
import { relativeTimeFormat } from '/src/utils'
|
||||||
|
|
||||||
import styles from './Recents.module.scss'
|
import styles from './Recents.module.scss'
|
||||||
|
|
||||||
|
|
@ -17,8 +18,7 @@ interface RecentsProps {
|
||||||
|
|
||||||
const Recents = ({ target }: RecentsProps) => {
|
const Recents = ({ target }: RecentsProps) => {
|
||||||
const recents = useStore(useRecentsStore, state => state.recents)
|
const recents = useStore(useRecentsStore, state => state.recents)
|
||||||
const { t } = useTranslation(['home', 'common'])
|
const { t, i18n } = useTranslation(['home', 'common'])
|
||||||
const dayjs = useDayjs()
|
|
||||||
|
|
||||||
return recents?.length ? <Section id="recents">
|
return recents?.length ? <Section id="recents">
|
||||||
<Content>
|
<Content>
|
||||||
|
|
@ -28,8 +28,8 @@ const Recents = ({ target }: RecentsProps) => {
|
||||||
<span className={styles.name}>{event.name}</span>
|
<span className={styles.name}>{event.name}</span>
|
||||||
<span
|
<span
|
||||||
className={styles.date}
|
className={styles.date}
|
||||||
title={dayjs.unix(event.created_at).format('D MMMM, YYYY')}
|
title={Temporal.Instant.fromEpochSeconds(event.created_at).toLocaleString(i18n.language, { dateStyle: 'long' })}
|
||||||
>{t('common:created', { date: dayjs.unix(event.created_at).fromNow() })}</span>
|
>{t('common:created', { date: relativeTimeFormat(Temporal.Instant.fromEpochSeconds(event.created_at), i18n.language) })}</span>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</Content>
|
</Content>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
import { maps } from 'hue-map'
|
import { maps } from 'hue-map'
|
||||||
|
import { MapKey } from 'hue-map/dist/maps'
|
||||||
import { Settings as SettingsIcon } from 'lucide-react'
|
import { Settings as SettingsIcon } from 'lucide-react'
|
||||||
|
|
||||||
import SelectField from '/src/components/SelectField/SelectField'
|
import SelectField from '/src/components/SelectField/SelectField'
|
||||||
|
|
@ -64,8 +65,8 @@ const Settings = () => {
|
||||||
'Sunday': t('options.weekStart.options.Sunday'),
|
'Sunday': t('options.weekStart.options.Sunday'),
|
||||||
'Monday': t('options.weekStart.options.Monday'),
|
'Monday': t('options.weekStart.options.Monday'),
|
||||||
}}
|
}}
|
||||||
value={store?.weekStart === 0 ? 'Sunday' : 'Monday'}
|
value={store?.weekStart === 1 ? 'Sunday' : 'Monday'}
|
||||||
onChange={value => store?.setWeekStart(value === 'Sunday' ? 0 : 1)}
|
onChange={value => store?.setWeekStart(value === 'Sunday' ? 1 : 0)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ToggleField
|
<ToggleField
|
||||||
|
|
@ -103,7 +104,7 @@ const Settings = () => {
|
||||||
}}
|
}}
|
||||||
isSmall
|
isSmall
|
||||||
value={store?.colormap}
|
value={store?.colormap}
|
||||||
onChange={event => store?.setColormap(event.target.value)}
|
onChange={event => store?.setColormap(event.target.value as MapKey)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ToggleField
|
<ToggleField
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
import { useRef } from 'react'
|
import { useRef } from 'react'
|
||||||
import { FieldValues, useController, UseControllerProps } from 'react-hook-form'
|
import { FieldValues, useController, UseControllerProps } from 'react-hook-form'
|
||||||
import dayjs from 'dayjs'
|
import { Temporal } from '@js-temporal/polyfill'
|
||||||
|
|
||||||
import { Description, Label, Wrapper } from '/src/components/Field/Field'
|
import { Description, Label, Wrapper } from '/src/components/Field/Field'
|
||||||
|
import { useTranslation } from '/src/i18n/client'
|
||||||
import { useStore } from '/src/stores'
|
import { useStore } from '/src/stores'
|
||||||
import useSettingsStore from '/src/stores/settingsStore'
|
import useSettingsStore from '/src/stores/settingsStore'
|
||||||
|
|
||||||
|
|
@ -81,6 +82,7 @@ interface HandleProps {
|
||||||
|
|
||||||
const Handle = ({ value, onChange, labelPadding }: HandleProps) => {
|
const Handle = ({ value, onChange, labelPadding }: HandleProps) => {
|
||||||
const timeFormat = useStore(useSettingsStore, state => state.timeFormat)
|
const timeFormat = useStore(useSettingsStore, state => state.timeFormat)
|
||||||
|
const { i18n } = useTranslation()
|
||||||
|
|
||||||
const isMoving = useRef(false)
|
const isMoving = useRef(false)
|
||||||
const rangeRect = useRef({ left: 0, width: 0 })
|
const rangeRect = useRef({ left: 0, width: 0 })
|
||||||
|
|
@ -106,7 +108,7 @@ const Handle = ({ value, onChange, labelPadding }: HandleProps) => {
|
||||||
left: `calc(${value * 4.166}% - 11px)`,
|
left: `calc(${value * 4.166}% - 11px)`,
|
||||||
'--extra-padding': labelPadding,
|
'--extra-padding': labelPadding,
|
||||||
} as React.CSSProperties}
|
} as React.CSSProperties}
|
||||||
data-label={timeFormat === '24h' ? times[value] : dayjs().hour(Number(times[value])).format('ha')}
|
data-label={Temporal.PlainTime.from({ hour: Number(times[value] === '24' ? '00' : times[value]) }).toLocaleString(i18n.language, { hour: 'numeric', hour12: timeFormat === '12h' })}
|
||||||
onMouseDown={() => {
|
onMouseDown={() => {
|
||||||
document.addEventListener('mousemove', handleMouseMove)
|
document.addEventListener('mousemove', handleMouseMove)
|
||||||
isMoving.current = true
|
isMoving.current = true
|
||||||
|
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
import { useCallback, useState } from 'react'
|
|
||||||
import { isKeyOfObject } from '@giraugh/tools'
|
|
||||||
import dayjs from 'dayjs'
|
|
||||||
import customParseFormat from 'dayjs/plugin/customParseFormat'
|
|
||||||
import isToday from 'dayjs/plugin/isToday'
|
|
||||||
import localeData from 'dayjs/plugin/localeData'
|
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
|
||||||
import timezone from 'dayjs/plugin/timezone'
|
|
||||||
import updateLocale from 'dayjs/plugin/updateLocale'
|
|
||||||
import utc from 'dayjs/plugin/utc'
|
|
||||||
|
|
||||||
import { useTranslation } from '/src/i18n/client'
|
|
||||||
import { languageDetails } from '/src/i18n/options'
|
|
||||||
import { useStore } from '/src/stores'
|
|
||||||
import useSettingsStore from '/src/stores/settingsStore'
|
|
||||||
|
|
||||||
dayjs.extend(customParseFormat)
|
|
||||||
dayjs.extend(isToday)
|
|
||||||
dayjs.extend(localeData)
|
|
||||||
dayjs.extend(relativeTime)
|
|
||||||
dayjs.extend(timezone)
|
|
||||||
dayjs.extend(updateLocale)
|
|
||||||
dayjs.extend(utc)
|
|
||||||
|
|
||||||
export const useDayjs = () => {
|
|
||||||
const { i18n } = useTranslation()
|
|
||||||
const store = useStore(useSettingsStore, state => state)
|
|
||||||
const [updateInstance, setUpdateInstance] = useState(0)
|
|
||||||
|
|
||||||
const instance = useCallback(dayjs, [updateInstance, dayjs])
|
|
||||||
|
|
||||||
const handleLanguageChange = useCallback((lng: string) => {
|
|
||||||
if (isKeyOfObject(lng, languageDetails)) {
|
|
||||||
store?.setWeekStart(languageDetails[lng].weekStart)
|
|
||||||
store?.setTimeFormat(languageDetails[lng].timeFormat)
|
|
||||||
|
|
||||||
languageDetails[lng]?.import().then(() => {
|
|
||||||
dayjs.locale(lng)
|
|
||||||
setUpdateInstance(updateInstance + 1)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, [store])
|
|
||||||
|
|
||||||
i18n.on('languageChanged', handleLanguageChange)
|
|
||||||
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
|
|
@ -2,12 +2,11 @@
|
||||||
|
|
||||||
import { initReactI18next, useTranslation as useTranslationHook } from 'react-i18next'
|
import { initReactI18next, useTranslation as useTranslationHook } from 'react-i18next'
|
||||||
import { cookies } from 'next/dist/client/components/headers' // risky disky (undocumented???)
|
import { cookies } from 'next/dist/client/components/headers' // risky disky (undocumented???)
|
||||||
import dayjs from 'dayjs'
|
|
||||||
import i18next from 'i18next'
|
import i18next from 'i18next'
|
||||||
import LanguageDetector from 'i18next-browser-languagedetector'
|
import LanguageDetector from 'i18next-browser-languagedetector'
|
||||||
import resourcesToBackend from 'i18next-resources-to-backend'
|
import resourcesToBackend from 'i18next-resources-to-backend'
|
||||||
|
|
||||||
import { cookieName, getOptions, languageDetails } from './options'
|
import { cookieName, getOptions } from './options'
|
||||||
|
|
||||||
i18next
|
i18next
|
||||||
.use(initReactI18next)
|
.use(initReactI18next)
|
||||||
|
|
@ -24,11 +23,5 @@ i18next
|
||||||
excludeCacheFor: [],
|
excludeCacheFor: [],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then(() => {
|
|
||||||
// Set dayjs locale
|
|
||||||
languageDetails[i18next.resolvedLanguage as keyof typeof languageDetails]?.import().then(() => {
|
|
||||||
dayjs.locale(i18next.resolvedLanguage)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
export const useTranslation: typeof useTranslationHook = (ns, options) => useTranslationHook(ns, options)
|
export const useTranslation: typeof useTranslationHook = (ns, options) => useTranslationHook(ns, options)
|
||||||
|
|
|
||||||
|
|
@ -27,89 +27,71 @@ interface LanguageDetails {
|
||||||
/** 0: Sunday, 1: Monday */
|
/** 0: Sunday, 1: Monday */
|
||||||
weekStart: 0 | 1
|
weekStart: 0 | 1
|
||||||
timeFormat: '12h' | '24h'
|
timeFormat: '12h' | '24h'
|
||||||
/** The separator to show between hours and minutes (default `:`) */
|
|
||||||
separator?: string
|
|
||||||
/** Day.js locale import */
|
|
||||||
import: () => Promise<unknown>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const languageDetails: Record<typeof languages[number], LanguageDetails> = {
|
export const languageDetails: Record<typeof languages[number], LanguageDetails> = {
|
||||||
'en': { // English (US)
|
'en': { // English (US)
|
||||||
name: 'English (US)',
|
name: 'English (US)',
|
||||||
import: () => import('dayjs/locale/en'),
|
|
||||||
weekStart: 0,
|
weekStart: 0,
|
||||||
timeFormat: '12h',
|
timeFormat: '12h',
|
||||||
},
|
},
|
||||||
'en-GB': { // English (UK)
|
'en-GB': { // English (UK)
|
||||||
name: 'English (UK)',
|
name: 'English (UK)',
|
||||||
import: () => import('dayjs/locale/en-gb'),
|
|
||||||
weekStart: 1,
|
weekStart: 1,
|
||||||
timeFormat: '12h',
|
timeFormat: '12h',
|
||||||
},
|
},
|
||||||
'de': { // German
|
'de': { // German
|
||||||
name: 'Deutsch',
|
name: 'Deutsch',
|
||||||
import: () => import('dayjs/locale/de'),
|
|
||||||
weekStart: 1,
|
weekStart: 1,
|
||||||
timeFormat: '24h',
|
timeFormat: '24h',
|
||||||
},
|
},
|
||||||
'es': { // Spanish
|
'es': { // Spanish
|
||||||
name: 'Español',
|
name: 'Español',
|
||||||
import: () => import('dayjs/locale/es'),
|
|
||||||
weekStart: 1,
|
weekStart: 1,
|
||||||
timeFormat: '24h',
|
timeFormat: '24h',
|
||||||
},
|
},
|
||||||
'fr': { // French
|
'fr': { // French
|
||||||
name: 'Français',
|
name: 'Français',
|
||||||
import: () => import('dayjs/locale/fr'),
|
|
||||||
weekStart: 1,
|
weekStart: 1,
|
||||||
timeFormat: '24h',
|
timeFormat: '24h',
|
||||||
},
|
},
|
||||||
'hi': { // Hindi
|
'hi': { // Hindi
|
||||||
name: 'हिंदी',
|
name: 'हिंदी',
|
||||||
import: () => import('dayjs/locale/hi'),
|
|
||||||
weekStart: 1,
|
weekStart: 1,
|
||||||
timeFormat: '12h',
|
timeFormat: '12h',
|
||||||
},
|
},
|
||||||
'id': { // Indonesian
|
'id': { // Indonesian
|
||||||
name: 'Indonesia',
|
name: 'Indonesia',
|
||||||
import: () => import('dayjs/locale/id'),
|
|
||||||
weekStart: 1,
|
weekStart: 1,
|
||||||
timeFormat: '24h',
|
timeFormat: '24h',
|
||||||
separator: '.',
|
|
||||||
},
|
},
|
||||||
'ja': { // Japanese
|
'ja': { // Japanese
|
||||||
name: '日本語',
|
name: '日本語',
|
||||||
import: () => import('dayjs/locale/ja'),
|
|
||||||
weekStart: 0,
|
weekStart: 0,
|
||||||
timeFormat: '12h',
|
timeFormat: '12h',
|
||||||
},
|
},
|
||||||
'ko': { // Korean
|
'ko': { // Korean
|
||||||
name: '한국어',
|
name: '한국어',
|
||||||
import: () => import('dayjs/locale/ko'),
|
|
||||||
weekStart: 0,
|
weekStart: 0,
|
||||||
timeFormat: '24h',
|
timeFormat: '24h',
|
||||||
},
|
},
|
||||||
'pl': { // Polish
|
'pl': { // Polish
|
||||||
name: 'Polskie',
|
name: 'Polskie',
|
||||||
import: () => import('dayjs/locale/pl'),
|
|
||||||
weekStart: 1,
|
weekStart: 1,
|
||||||
timeFormat: '12h',
|
timeFormat: '12h',
|
||||||
},
|
},
|
||||||
'pt-BR': { // Portuguese (Brazil)
|
'pt-BR': { // Portuguese (Brazil)
|
||||||
name: 'Português (do Brasil)',
|
name: 'Português (do Brasil)',
|
||||||
import: () => import('dayjs/locale/pt-br'),
|
|
||||||
weekStart: 0,
|
weekStart: 0,
|
||||||
timeFormat: '24h',
|
timeFormat: '24h',
|
||||||
},
|
},
|
||||||
'ru': { // Russian
|
'ru': { // Russian
|
||||||
name: 'Pусский',
|
name: 'Pусский',
|
||||||
import: () => import('dayjs/locale/ru'),
|
|
||||||
weekStart: 1,
|
weekStart: 1,
|
||||||
timeFormat: '24h',
|
timeFormat: '24h',
|
||||||
},
|
},
|
||||||
// 'zh-CN': { // Chinese
|
// 'zh-CN': { // Chinese
|
||||||
// name: '中文',
|
// name: '中文',
|
||||||
// import: () => import('dayjs/locale/zh-cn'),
|
|
||||||
// weekStart: 1,
|
// weekStart: 1,
|
||||||
// timeFormat: '12h',
|
// timeFormat: '12h',
|
||||||
// },
|
// },
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
import { cookies, headers } from 'next/headers'
|
import { cookies, headers } from 'next/headers'
|
||||||
import acceptLanguage from 'accept-language'
|
import acceptLanguage from 'accept-language'
|
||||||
import dayjs from 'dayjs'
|
|
||||||
import { createInstance } from 'i18next'
|
import { createInstance } from 'i18next'
|
||||||
import resourcesToBackend from 'i18next-resources-to-backend'
|
import resourcesToBackend from 'i18next-resources-to-backend'
|
||||||
|
|
||||||
import { cookieName, fallbackLng, getOptions, languageDetails, languages } from './options'
|
import { cookieName, fallbackLng, getOptions, languages } from './options'
|
||||||
|
|
||||||
type Mutable<T> = { -readonly [K in keyof T]: Mutable<T[K]> }
|
type Mutable<T> = { -readonly [K in keyof T]: Mutable<T[K]> }
|
||||||
|
|
||||||
|
|
@ -25,11 +24,6 @@ export const useTranslation = async (ns: string | string[], options: { keyPrefix
|
||||||
?? acceptLanguage.get(headers().get('Accept-Language'))
|
?? acceptLanguage.get(headers().get('Accept-Language'))
|
||||||
?? fallbackLng
|
?? fallbackLng
|
||||||
|
|
||||||
// Set dayjs locale
|
|
||||||
languageDetails[language as keyof typeof languageDetails]?.import().then(() => {
|
|
||||||
dayjs.locale(language)
|
|
||||||
})
|
|
||||||
|
|
||||||
const i18nextInstance = await initI18next(language, ns)
|
const i18nextInstance = await initI18next(language, ns)
|
||||||
return {
|
return {
|
||||||
t: i18nextInstance.getFixedT(language, Array.isArray(ns) ? ns[0] : ns, options.keyPrefix),
|
t: i18nextInstance.getFixedT(language, Array.isArray(ns) ? ns[0] : ns, options.keyPrefix),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { ColorMap } from 'hue-map/dist/maps'
|
import { MapKey } from 'hue-map/dist/maps'
|
||||||
import { create } from 'zustand'
|
import { create } from 'zustand'
|
||||||
import { persist } from 'zustand/middleware'
|
import { persist } from 'zustand/middleware'
|
||||||
|
|
||||||
|
|
@ -6,17 +6,18 @@ type TimeFormat = '12h' | '24h'
|
||||||
type Theme = 'System' | 'Light' | 'Dark'
|
type Theme = 'System' | 'Light' | 'Dark'
|
||||||
|
|
||||||
interface SettingsStore {
|
interface SettingsStore {
|
||||||
|
/** 0: Monday, 1: Sunday */
|
||||||
weekStart: 0 | 1
|
weekStart: 0 | 1
|
||||||
timeFormat: TimeFormat
|
timeFormat: TimeFormat
|
||||||
theme: Theme
|
theme: Theme
|
||||||
highlight: boolean
|
highlight: boolean
|
||||||
colormap: 'crabfit' | ColorMap
|
colormap: 'crabfit' | MapKey
|
||||||
|
|
||||||
setWeekStart: (weekStart: 0 | 1) => void
|
setWeekStart: (weekStart: 0 | 1) => void
|
||||||
setTimeFormat: (timeFormat: TimeFormat) => void
|
setTimeFormat: (timeFormat: TimeFormat) => void
|
||||||
setTheme: (theme: Theme) => void
|
setTheme: (theme: Theme) => void
|
||||||
setHighlight: (highlight: boolean) => void
|
setHighlight: (highlight: boolean) => void
|
||||||
setColormap: (colormap: 'crabfit' | ColorMap) => void
|
setColormap: (colormap: 'crabfit' | MapKey) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const useSettingsStore = create<SettingsStore>()(persist(
|
const useSettingsStore = create<SettingsStore>()(persist(
|
||||||
|
|
@ -33,7 +34,18 @@ const useSettingsStore = create<SettingsStore>()(persist(
|
||||||
setHighlight: highlight => set({ highlight }),
|
setHighlight: highlight => set({ highlight }),
|
||||||
setColormap: colormap => set({ colormap }),
|
setColormap: colormap => set({ colormap }),
|
||||||
}),
|
}),
|
||||||
{ name: 'crabfit-settings' },
|
{
|
||||||
|
name: 'crabfit-settings',
|
||||||
|
version: 1,
|
||||||
|
migrate: (persistedState, version) => {
|
||||||
|
if (version === 0) {
|
||||||
|
// Weekstart used to be 0 for Sunday, but now it's been swapped
|
||||||
|
(persistedState as SettingsStore).weekStart = (persistedState as SettingsStore).weekStart === 1 ? 0 : 1
|
||||||
|
return persistedState as SettingsStore
|
||||||
|
}
|
||||||
|
return persistedState as SettingsStore
|
||||||
|
},
|
||||||
|
},
|
||||||
))
|
))
|
||||||
|
|
||||||
export default useSettingsStore
|
export default useSettingsStore
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Temporal } from '@js-temporal/polyfill'
|
import { Temporal } from '@js-temporal/polyfill'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Take times as strings and convert to Dayjs objects
|
* Take times as strings in UTC and convert to ZonedDateTime objects in the timezone supplied
|
||||||
* @param times An array of strings in `HHmm-d` or `HHmm-DDMMYYYY` format
|
* @param times An array of strings in `HHmm-d` or `HHmm-DDMMYYYY` format
|
||||||
* @param timezone The target timezone
|
* @param timezone The target timezone
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -645,11 +645,6 @@ damerau-levenshtein@^1.0.8:
|
||||||
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
|
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
|
||||||
integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==
|
integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==
|
||||||
|
|
||||||
dayjs@^1.11.7:
|
|
||||||
version "1.11.7"
|
|
||||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2"
|
|
||||||
integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==
|
|
||||||
|
|
||||||
debug@^3.2.6, debug@^3.2.7:
|
debug@^3.2.6, debug@^3.2.7:
|
||||||
version "3.2.7"
|
version "3.2.7"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue