Skip to content

Commit

Permalink
calendar show today day; improve quick day select
Browse files Browse the repository at this point in the history
  • Loading branch information
vladkens committed Feb 6, 2024
1 parent 37ac9fb commit 801ebe0
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 48 deletions.
4 changes: 2 additions & 2 deletions src/components/Board.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import clsx from "clsx"
import { useAtomValue } from "jotai"
import { DateTime } from "luxon"
import { FC, useCallback, useEffect, useRef, useState } from "react"
import { ActiveTab, ComputedDate, useMutateTab } from "../store"
import { ActiveTab, ActualDate, useMutateTab } from "../store"
import { Place } from "../utils/geonames"
import { useExportEvent } from "../utils/share"
import { useInteraction } from "../utils/useInteraction"
Expand Down Expand Up @@ -82,7 +82,7 @@ export const Board: FC = () => {
const { places: rawPlaces } = useAtomValue(ActiveTab)
const [ordered, setOrdered] = useState<Place[]>([])

const date = useAtomValue(ComputedDate)
const date = useAtomValue(ActualDate)

const [range, setRange] = useState({ height: 0, top: 0, left: 0, opacity: 0 })
const [holdOn, setHoldOn] = useState<string | null>(null)
Expand Down
69 changes: 51 additions & 18 deletions src/components/ChangeBoardDate.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { IconCalendarMonth } from "@tabler/icons-react"
import { useAtom, useAtomValue } from "jotai"
import { FC, useRef, useState } from "react"
import { ActiveTab, QuickDates, SelectedDate } from "../store"
import clsx from "clsx"
import { useAtomValue, useSetAtom } from "jotai"
import { range } from "lodash-es"
import { DateTime } from "luxon"
import { FC, useEffect, useMemo, useRef, useState } from "react"
import { ActiveTab, ActualDate, PickedDate, SystemDate } from "../store"
import { encodeShareUrl } from "../utils/share"
import { useOnClickOutside } from "../utils/useOnClickOutside"
import { Button } from "./ui/Button"
Expand All @@ -10,45 +13,75 @@ import { ButtonIcon } from "./ui/ButtonIcon"
import { DatePicker } from "./ui/DatePicker"

export const ChangeBoardDate: FC = () => {
const [date, setDate] = useAtom(SelectedDate)
const dates = useAtomValue(QuickDates)
const setPickedDate = useSetAtom(PickedDate)
const systemDate = useAtomValue(SystemDate)
const actualDate = useAtomValue(ActualDate)
const activeTab = useAtomValue(ActiveTab)

const [dpActive, setDpActive] = useState(false)
const ref = useRef<HTMLDivElement>(null)
const [calActive, setCalActive] = useState(false)
const [quickDate, setQuickDate] = useState(actualDate)

const onDateChange = (value: string) => {
setDate(value)
setDpActive(false)
setPickedDate(value)
setQuickDate(value)
setCalActive(false)
}

const resetToday = () => {
setPickedDate(null)
setQuickDate(systemDate)
}

useOnClickOutside(ref, () => setDpActive(false))
const ref = useRef<HTMLDivElement>(null)
useOnClickOutside(ref, () => setCalActive(false))

const dates = useMemo(() => {
const dd = DateTime.fromISO(quickDate).set({ hour: 0 })!
const it = quickDate !== systemDate ? range(-3, 4) : range(-1, 6)
return it.map((x) => {
const dt = dd.plus({ days: x })
return { date: dt.toISODate()!, isWeekend: dt.isWeekend }
})
}, [quickDate, systemDate])

useEffect(() => {
setQuickDate(actualDate)
}, [systemDate])

return (
<div className="flex grow justify-between gap-1">
<div className="relative" ref={ref}>
<ButtonIcon
onClick={() => setDpActive((old) => !old)}
onClick={() => setCalActive(!calActive)}
icon={<IconCalendarMonth />}
size="sm"
/>

{dpActive && (
{calActive && (
<div className="absolute z-[100] rounded border bg-card p-1">
<DatePicker value={date} onChange={onDateChange} />
<DatePicker value={actualDate} onChange={onDateChange} />
</div>
)}
</div>

<div className="flex grow items-center gap-1">
{dates.map((x) => (
<Button key={x.date} onClick={() => setDate(x.date)} size="sm" disabled={x.isActive}>
{new Date(x.date).toLocaleDateString("en-US", {
month: "short",
day: "numeric",
})}
<Button
key={x.date}
onClick={() => setPickedDate(x.date)}
size="sm"
disabled={x.date === actualDate}
className={clsx(x.isWeekend && "text-red-500")}
>
{new Date(x.date).toLocaleDateString("en-US", { month: "short", day: "numeric" })}
</Button>
))}

{quickDate !== systemDate && (
<Button key="today" size="sm" onClick={resetToday}>
Today
</Button>
)}
</div>

<div className="flex items-center gap-2.5">
Expand Down
4 changes: 2 additions & 2 deletions src/components/Timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import clsx from "clsx"
import { useAtomValue } from "jotai"
import { DateTime } from "luxon"
import { FC, useEffect, useMemo, useReducer } from "react"
import { ActiveTab, ComputedDate, useGetHourCycle, useIsHome, useMutateTab } from "../store"
import { ActiveTab, ActualDate, useGetHourCycle, useIsHome, useMutateTab } from "../store"
import { Place } from "../utils/geonames"

const DayLabel: FC<{ date: DateTime; mode: "h12" | "h24" }> = ({ date, mode }) => {
Expand Down Expand Up @@ -58,7 +58,7 @@ const useGetTimeline = (place: Place) => {
const { home } = useAtomValue(ActiveTab)

const mode = useGetHourCycle(place)
const date = useAtomValue(ComputedDate)
const date = useAtomValue(ActualDate)

const ss = DateTime.fromISO(date, { zone: home.zone }).setZone(place.zone)
const dd = DateTime.now().setZone(place.zone)
Expand Down
11 changes: 8 additions & 3 deletions src/components/ui/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ export const DatePicker: FC<DatePickerProps> = ({ value, onChange }) => {
return [dd, weeks]
}, [ct])

const baseCls = "flex h-[28px] w-[28px] items-center justify-center leading-none rounded text-xs"
const baseCls = clsx(
"flex h-[28px] w-[28px] items-center justify-center leading-none rounded text-xs",
"border border-transparent",
)

const buttons = [
{ icon: IconChevronLeft, onClick: () => setCt(ct.minus({ month: 1 })) },
Expand All @@ -38,7 +41,7 @@ export const DatePicker: FC<DatePickerProps> = ({ value, onChange }) => {
{buttons.map((x, i) => (
<button
key={i}
className={clsx(baseCls, "hover:bg-rest cursor-pointer")}
className={clsx(baseCls, "cursor-pointer hover:bg-rest")}
onClick={x.onClick}
>
<x.icon size={20} />
Expand All @@ -60,6 +63,7 @@ export const DatePicker: FC<DatePickerProps> = ({ value, onChange }) => {
const ss = dd.plus({ week: week, day: x })

const isActive = ss.toISODate() === value
const isToday = !isActive && ss.toISODate() === DateTime.now().toISODate()
const isOtherMonth = !isActive && ss.month !== ct.month
const isWeekend = !isActive && !isOtherMonth && ss.isWeekend

Expand All @@ -70,8 +74,9 @@ export const DatePicker: FC<DatePickerProps> = ({ value, onChange }) => {
onClick={() => onChange(ss.toISODate())}
className={clsx(
baseCls,
"hover:bg-rest cursor-pointer",
"cursor-pointer hover:bg-rest",
isActive && "bg-primary font-medium text-body-content hover:bg-primary",
isToday && "border-yellow-400/40 bg-yellow-400/20",
isOtherMonth && "text-rest-content",
isWeekend && "text-red-500",
)}
Expand Down
34 changes: 11 additions & 23 deletions src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ export const useMutateTab = () => {
const setPlaces = useSetTabProp("places")
const _setHome = useSetTabProp("home")
const setName = useSetTabProp("name")
const setDate = useSetAtom(SelectedDate)

const addPlace = (placeId: PlaceId) => setPlaces((old) => uniqBy([...old, { id: placeId }], "id"))
const delPlace = (placeId: PlaceId) => setPlaces((old) => old.filter((x) => x.id !== placeId))
Expand All @@ -149,7 +148,6 @@ export const useMutateTab = () => {

const setHome = (placeId: PlaceId) => {
_setHome((_) => placeId)
setDate(null)
}

return { addPlace, delPlace, reorderPlaces, setHome, setName }
Expand All @@ -160,36 +158,26 @@ export const useIsHome = (left: Place) => {
return home.id === left.id
}

// General: Selected Date
// General: Current Tab Date

const todayDate = (zone: string) => DateTime.now().setZone(zone).set({ hour: 0 })

const IncreateDate = atom(0)
export const SelectedDate = atom<string | null>(null)
export const ComputedDate = atom((get) => {
get(IncreateDate) // trigger recompute on change
const RecalcSystemDate = atom(0)

export const SystemDate = atom((get) => {
get(RecalcSystemDate) // trigger recompute on change
const { home } = get(ActiveTab)
const date = get(SelectedDate)
return date ? date : todayDate(home.zone).toISODate()!
return DateTime.now().setZone(home.zone).set({ hour: 0 }).toISODate()!
})

export const QuickDates = atom((get) => {
const { home } = get(ActiveTab)
const current = get(ComputedDate)
const today = todayDate(home.zone)

const items = []
for (let i = -1; i <= 5; ++i) {
const date = today.plus({ days: i }).toISODate()!
items.push({ date, isActive: date === current })
}
export const PickedDate = atom<string | null>(null)

return items
export const ActualDate = atom((get) => {
const pickedDate = get(PickedDate)
const systemDate = get(SystemDate)
return pickedDate ?? systemDate
})

export const useFollowDateChange = () => {
const set = useSetAtom(IncreateDate)
const set = useSetAtom(RecalcSystemDate)

useEffect(() => {
let lt: string = ""
Expand Down

0 comments on commit 801ebe0

Please sign in to comment.