From 768d68951b5e87cd7cfb6e02af68bd9f5052f835 Mon Sep 17 00:00:00 2001 From: Astrian Zheng Date: Thu, 20 Feb 2025 16:36:13 +1100 Subject: [PATCH] feat: add SingleWeekPicker component and integrate with SingleDatePicker in the app --- playground/app.scss | 11 ++- playground/app.tsx | 15 +--- src/components/SingleWeekPicker.tsx | 135 ++++++++++++++++++++++++++++ src/index.ts | 3 +- src/style.scss | 58 +++++++----- src/utils/calculateWeekNum.ts | 18 ++++ src/utils/index.ts | 3 +- 7 files changed, 204 insertions(+), 39 deletions(-) create mode 100644 src/components/SingleWeekPicker.tsx create mode 100644 src/utils/calculateWeekNum.ts diff --git a/playground/app.scss b/playground/app.scss index f5c3583..b471a02 100644 --- a/playground/app.scss +++ b/playground/app.scss @@ -10,8 +10,13 @@ body { justify-content: center; align-items: center; .border { - background: #fff; - border-radius: 5px; - box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + display: flex; + flex-direction: column; + gap: 0.5rem; + > * { + background: #fff; + border-radius: 5px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + } } } \ No newline at end of file diff --git a/playground/app.tsx b/playground/app.tsx index 670b7cc..b500108 100644 --- a/playground/app.tsx +++ b/playground/app.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { SingleDatePicker } from "../src/index" +import { SingleDatePicker, SingleWeekPicker } from "../src/index" import './app.scss' export default () => { @@ -9,18 +9,9 @@ export default () => { } return (
-
- alert('close')} - /> + +
) } \ No newline at end of file diff --git a/src/components/SingleWeekPicker.tsx b/src/components/SingleWeekPicker.tsx new file mode 100644 index 0000000..a2fb1ad --- /dev/null +++ b/src/components/SingleWeekPicker.tsx @@ -0,0 +1,135 @@ +import { useEffect, useState } from "react" +import { generateUniqueId, applyColor, getL10Weekday, getCalendarDates, calculateWeekNum } from "../utils" + +export interface SingleWeekPickerProps { + /** + * The language code that will be used to localize the panel. + * Accept standard ISO 639-1 language code, such as 'zh-CN', 'en-US', 'ja-JP', etc. Note + * that it will not effect to the screen reader, but the screen reader will still read the + * date in the user’s language. + * @default navigator.language + */ + localization?: string + + /** + * The main color of the panel, including the text color and the border color. + *@default '#000000' + */ + mainColor?: string + + /** + * The accent color of the panel, including the background color of the selected date. + *@default '#000000' + */ + accentColor?: string + + /** + * The reversed color of the panel, including the text color of the selected date. + *@default '#ffffff' + */ + reversedColor?: string + + /** + * The hover color of the panel, including the hover background color of the date. + *@default '#00000017' + */ + hoverColor?: string + + /** + * The border color of the panel, including the divider color between the header and the body. + *@default '#e0e0e0' + */ + borderColor?: string +} + +/** + * SingleWeekPicker + * A panel that allows users to select a week. + * + * @component + * + * @param + */ +export default ({ localization, mainColor = '#000000', accentColor = '#000000', reversedColor = '#ffffff', hoverColor = '#00000017', borderColor = '#e0e0e0' }: SingleWeekPickerProps) => { + const [currentMonth, setCurrentMonth] = useState(new Date().getMonth()) + const [currentYear, setCurrentYear] = useState(new Date().getFullYear()) + const [selectMonth, setSelectMonth] = useState(false) + const [calendarWeeks, setCalendarWeeks] = useState([]) + const [l10nDays, setL10nDays] = useState([]) + const uniqueId = generateUniqueId() + + function skipToLastMonth() { + if (currentMonth === 0) { + setCurrentMonth(11) + setCurrentYear(currentYear - 1) + } + else setCurrentMonth(currentMonth - 1) + } + + function skipToNextMonth() { + if (currentMonth === 11) { + setCurrentMonth(0) + setCurrentYear(currentYear + 1) + } + else setCurrentMonth(currentMonth + 1) + } + + useEffect(() => { + applyColor(uniqueId, { + mainColor: mainColor, + accentColor: accentColor, + reversedColor: reversedColor, + hoverColor: hoverColor, + borderColor: borderColor + }) + }, [mainColor, accentColor, reversedColor, hoverColor, borderColor]) + + useEffect(() => { + const i18n = localization || navigator.language + setL10nDays(getL10Weekday(i18n)) + }, [localization]) + + useEffect(() => { + const dates = getCalendarDates(currentMonth, currentYear) + let weeks: Date[][] = [] + for (let i = 0; i < dates.length; i += 7) + weeks.push(dates.slice(i, i + 7)) + setCalendarWeeks(weeks) + }, [currentMonth, currentYear]) + + useEffect(() => { + const date = new Date(2033, 0, 1) + console.log(date) + console.log(calculateWeekNum(date)) + }, []) + + if (selectMonth) { + return + } else { + return + } +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 75a3e89..6319e94 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ import './style.scss' -export {default as SingleDatePicker} from './components/SingleDatePicker' \ No newline at end of file +export {default as SingleDatePicker} from './components/SingleDatePicker' +export {default as SingleWeekPicker} from './components/SingleWeekPicker' \ No newline at end of file diff --git a/src/style.scss b/src/style.scss index e80b364..e248d85 100644 --- a/src/style.scss +++ b/src/style.scss @@ -71,6 +71,38 @@ &.grid{ display: grid; grid-template-columns: repeat(7, 1fr); + .item.date { + border-radius: 50%; + position: relative; + &.extra-month { + cursor: default; + } + .today-indicator { + position: absolute; + bottom: 0.25rem; + width: 0.25rem; + height: 0.25rem; + } + + &.active { + background: var(--datenel-accent-color); + color: var(--datenel-reversed-color); + } + } + } + &.flex { + display: flex; + flex-direction: column; + .listitem { + padding: 0; + display: grid; + grid-template-columns: repeat(7, 1fr); + border-radius: 1rem; + .item.date { + background: none; + } + } + } .item { width: 2rem; @@ -79,33 +111,15 @@ justify-content: center; align-items: center; font-size: 0.75rem; - } - .item.day-indicator { - opacity: 0.5; - } - - button.item.date { - border-radius: 50%; - position: relative; &.extra-month { opacity: 0.3; - cursor: default; } - &:hover { - background: var(--datenel-hover-color); - } - .today-indicator { - position: absolute; - bottom: 0.25rem; - width: 0.25rem; - height: 0.25rem; - } - - &.active { - background: var(--datenel-accent-color); - color: var(--datenel-reversed-color); + &.day-indicator { + opacity: 0.5; } } + + } .month-selector-body { diff --git a/src/utils/calculateWeekNum.ts b/src/utils/calculateWeekNum.ts new file mode 100644 index 0000000..0b5d59c --- /dev/null +++ b/src/utils/calculateWeekNum.ts @@ -0,0 +1,18 @@ +export default (date: Date): { weekYear: number, weekNum: number } => { + const tempDate = new Date(date) + tempDate.setHours(0, 0, 0, 0) + + tempDate.setDate(tempDate.getDate() + 3 - (tempDate.getDay() || 7)) + + const firstThursday = new Date(tempDate.getFullYear(), 0, 4) + firstThursday.setDate(firstThursday.getDate() + 3 - (firstThursday.getDay() || 7)) + + const diffInDays = Math.floor((tempDate.getTime() - firstThursday.getTime()) / (24 * 60 * 60 * 1000)) + + const weekNum = Math.ceil((diffInDays + 1) / 7) + + return { + weekYear: weekNum === 53 ? tempDate.getFullYear() + 1 : tempDate.getFullYear(), + weekNum + } +} \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts index 54fd6db..314d343 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,4 +1,5 @@ export { default as getCalendarDates } from './getCalendarDates' export { default as getL10Weekday } from './getL10Weekday' export { default as generateUniqueId } from './generateUniqueId' -export { default as applyColor } from './applyColor' \ No newline at end of file +export { default as applyColor } from './applyColor' +export { default as calculateWeekNum } from './calculateWeekNum' \ No newline at end of file