zaman-backoffice 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.cjs +26 -0
- package/.husky/commit-msg +4 -0
- package/.husky/pre-commit +4 -0
- package/.idea/git_toolbox_blame.xml +6 -0
- package/.idea/git_toolbox_prj.xml +15 -0
- package/.idea/inspectionProfiles/Project_Default.xml +6 -0
- package/.idea/material_theme_project_new.xml +12 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/.idea/zaman-backoffice2.iml +12 -0
- package/.prettierrc +9 -0
- package/.travis.yml +10 -0
- package/client/index.html +12 -0
- package/client/main.tsx +84 -0
- package/client/style.css +62 -0
- package/cypress/fixtures/example.json +5 -0
- package/cypress/support/commands.ts +38 -0
- package/cypress/support/component-index.html +12 -0
- package/cypress/support/component.ts +39 -0
- package/cypress/videos/Calendar/CalendarComponent.cy.tsx.mp4 +0 -0
- package/cypress/videos/DatePicker/DatePicker.cy.tsx.mp4 +0 -0
- package/cypress.config.ts +10 -0
- package/help/banner.png +0 -0
- package/jest.config.ts +47 -0
- package/package.json +1 -1
- package/src/components/CalendarItem/CalendarItem.styled.tsx +96 -0
- package/src/components/CalendarItem/CalendarItem.types.ts +7 -0
- package/src/components/CalendarItem/index.ts +1 -0
- package/src/components/CalendarWrapper/CalendarWrapper.styled.tsx +19 -0
- package/src/components/CalendarWrapper/index.ts +1 -0
- package/src/components/FloatingElement/FloatingElement.styled.tsx +8 -0
- package/src/components/FloatingElement/FloatingElement.tsx +83 -0
- package/src/components/FloatingElement/FloatingElement.types.ts +8 -0
- package/src/components/FloatingElement/index.tsx +1 -0
- package/src/components/Header/Header.styled.tsx +40 -0
- package/src/components/Header/Header.tsx +46 -0
- package/src/components/Header/Header.types.ts +6 -0
- package/src/components/Header/index.ts +1 -0
- package/src/components/IconButton/IconButton.styled.tsx +22 -0
- package/src/components/IconButton/IconButton.tsx +3 -0
- package/src/components/IconButton/index.tsx +1 -0
- package/src/components/Icons/ChevronLeft/index.tsx +22 -0
- package/src/components/Icons/ChevronRight/index.tsx +22 -0
- package/src/components/Modal/Modal.styled.tsx +23 -0
- package/src/components/Modal/Modal.tsx +29 -0
- package/src/components/Modal/index.tsx +1 -0
- package/src/components/Modal/types.ts +7 -0
- package/src/components/MonthPicker/Month.styled.tsx +11 -0
- package/src/components/MonthPicker/MonthPicker.tsx +35 -0
- package/src/components/MonthPicker/MonthPicker.types.ts +4 -0
- package/src/components/MonthPicker/index.ts +1 -0
- package/src/components/RenderCalendar/RenderCalendar.tsx +31 -0
- package/src/components/RenderCalendar/RenderCalendar.types.ts +10 -0
- package/src/components/RenderCalendar/index.ts +1 -0
- package/src/components/YearPicker/YearPicker.styled.tsx +14 -0
- package/src/components/YearPicker/YearPicker.tsx +49 -0
- package/src/components/YearPicker/YearPicker.types.ts +4 -0
- package/src/components/YearPicker/index.ts +1 -0
- package/src/constants.ts +6 -0
- package/src/hooks/__tests__/useClickOutside.test.tsx +32 -0
- package/src/hooks/useCalendarHandlers.tsx +113 -0
- package/src/hooks/useClickOutside.tsx +23 -0
- package/src/hooks/useSlideCalendar.tsx +95 -0
- package/src/hooks/useTimePicker.tsx +95 -0
- package/src/index.tsx +4 -0
- package/src/packages/Calendar/Calendar.styled.tsx +42 -0
- package/src/packages/Calendar/Calendar.tsx +159 -0
- package/src/packages/Calendar/Calendar.types.ts +31 -0
- package/src/packages/Calendar/CalendarComponent.cy.tsx +69 -0
- package/src/packages/Calendar/index.ts +2 -0
- package/src/packages/CalendarProvider/CalendarProvider.tsx +30 -0
- package/src/packages/CalendarProvider/CalendarProvider.types.ts +6 -0
- package/src/packages/CalendarProvider/index.ts +2 -0
- package/src/packages/DatePicker/DatePicker.cy.tsx +54 -0
- package/src/packages/DatePicker/DatePicker.tsx +127 -0
- package/src/packages/DatePicker/DatePicker.types.ts +26 -0
- package/src/packages/DatePicker/index.ts +2 -0
- package/src/packages/TimePicker/TimePicker.styled.tsx +77 -0
- package/src/packages/TimePicker/TimePicker.tsx +121 -0
- package/src/packages/TimePicker/TimePicker.types.ts +16 -0
- package/src/packages/TimePicker/components/Numbers/Numbers.styled.tsx +36 -0
- package/src/packages/TimePicker/components/Numbers/Numbers.tsx +58 -0
- package/src/packages/TimePicker/components/Numbers/Numbers.types.ts +14 -0
- package/src/packages/TimePicker/components/Numbers/index.ts +1 -0
- package/src/packages/TimePicker/index.ts +2 -0
- package/src/style/animation.ts +23 -0
- package/src/style/classNames.ts +8 -0
- package/src/style/colorPallete.ts +16 -0
- package/src/style/colors.ts +15 -0
- package/src/style/hexToHSL.ts +52 -0
- package/src/style/radius.ts +28 -0
- package/src/types.ts +75 -0
- package/src/utils/dateHelper/dateHelper.ts +67 -0
- package/src/utils/dateHelper/index.ts +1 -0
- package/src/utils/dateTimeFormat/dateTimeFormat.ts +43 -0
- package/src/utils/dateTimeFormat/index.ts +1 -0
- package/src/utils/format/format.test.ts +37 -0
- package/src/utils/format/format.ts +56 -0
- package/src/utils/format/format.types.ts +11 -0
- package/src/utils/format/index.ts +1 -0
- package/src/utils/index.ts +21 -0
- package/src/utils/locale.ts +13 -0
- package/src/utils/locales/en.ts +89 -0
- package/src/utils/locales/fa.ts +89 -0
- package/src/utils/locales/index.ts +10 -0
- package/src/utils/locales/locales.types.ts +11 -0
- package/src/utils/month/index.ts +1 -0
- package/src/utils/month/month.ts +54 -0
- package/src/utils/month/month.types.ts +11 -0
- package/src/utils/timePicker.ts +107 -0
- package/src/utils/type.ts +0 -0
- package/tsconfig.json +22 -0
- package/tsconfig.test.json +7 -0
- package/vite.config.ts +7 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
import type React from 'react'
|
2
|
+
import { useEffect } from 'react'
|
3
|
+
|
4
|
+
function useClickOutside(
|
5
|
+
ref: React.RefObject<HTMLElement>,
|
6
|
+
handler: () => void
|
7
|
+
) {
|
8
|
+
useEffect(() => {
|
9
|
+
function handleClickOutside({ target }: MouseEvent) {
|
10
|
+
if (ref.current != null && !ref.current.contains(target as Node)) {
|
11
|
+
handler()
|
12
|
+
}
|
13
|
+
}
|
14
|
+
// Bind the event listener
|
15
|
+
document.addEventListener('mousedown', handleClickOutside)
|
16
|
+
return () => {
|
17
|
+
// Unbind the event listener on clean up
|
18
|
+
document.removeEventListener('mousedown', handleClickOutside)
|
19
|
+
}
|
20
|
+
}, [ref, handler])
|
21
|
+
}
|
22
|
+
|
23
|
+
export default useClickOutside
|
@@ -0,0 +1,95 @@
|
|
1
|
+
import dayjs from 'dayjs'
|
2
|
+
import { useRef } from 'react'
|
3
|
+
import type { Dispatch, RefObject, SetStateAction } from 'react'
|
4
|
+
import { isRtl } from '../utils'
|
5
|
+
import { CALENDAR_WIDTH, TIME, ANIMATE_FUNC } from '../constants'
|
6
|
+
import getDays from '../utils/month'
|
7
|
+
import type { DaysInMonth } from '../utils/month/month.types'
|
8
|
+
|
9
|
+
const toRight = () => {
|
10
|
+
if (isRtl()) {
|
11
|
+
return CALENDAR_WIDTH
|
12
|
+
}
|
13
|
+
return CALENDAR_WIDTH * -1
|
14
|
+
}
|
15
|
+
|
16
|
+
interface UseSliderTypes {
|
17
|
+
daysElementRefs: RefObject<HTMLDivElement[]>
|
18
|
+
days: DaysInMonth[]
|
19
|
+
setDays: Dispatch<SetStateAction<DaysInMonth[]>>
|
20
|
+
}
|
21
|
+
export const useSlideCalendar = ({
|
22
|
+
daysElementRefs,
|
23
|
+
days,
|
24
|
+
setDays
|
25
|
+
}: UseSliderTypes) => {
|
26
|
+
const isAnimating = useRef(false)
|
27
|
+
const currentMonth = days[0].middleOfMonth
|
28
|
+
|
29
|
+
const slideToTheNextMonth = () => {
|
30
|
+
if (isAnimating.current) {
|
31
|
+
return
|
32
|
+
}
|
33
|
+
const nextMonth = dayjs(currentMonth).add(1, 'month')
|
34
|
+
const newValue = getDays(nextMonth.toDate())
|
35
|
+
|
36
|
+
setDays([...days, newValue])
|
37
|
+
|
38
|
+
requestAnimationFrame(() => {
|
39
|
+
isAnimating.current = true
|
40
|
+
// @ts-expect-error I will check this out later
|
41
|
+
const [firstItemRef, lastItemRef] = daysElementRefs.current
|
42
|
+
firstItemRef.style.transition = `transform ${TIME}ms ${ANIMATE_FUNC}`
|
43
|
+
firstItemRef.style.transform = `translateX(${toRight()}px)`
|
44
|
+
|
45
|
+
lastItemRef.style.transition = `transform ${TIME}ms ${ANIMATE_FUNC}`
|
46
|
+
lastItemRef.style.transform = `translateX(${toRight()}px)`
|
47
|
+
|
48
|
+
setTimeout(() => {
|
49
|
+
setDays((oldItems) => {
|
50
|
+
return oldItems.filter((items) => items.id === newValue.id)
|
51
|
+
})
|
52
|
+
lastItemRef.style.transition = null
|
53
|
+
lastItemRef.style.transform = null
|
54
|
+
isAnimating.current = false
|
55
|
+
}, TIME + 50)
|
56
|
+
})
|
57
|
+
}
|
58
|
+
const slideToPrevMonth = () => {
|
59
|
+
if (isAnimating.current) {
|
60
|
+
return
|
61
|
+
}
|
62
|
+
const prevMonth = dayjs(currentMonth).subtract(1, 'month')
|
63
|
+
const newValue = getDays(prevMonth.toDate())
|
64
|
+
|
65
|
+
setDays([newValue, ...days])
|
66
|
+
|
67
|
+
requestAnimationFrame(() => {
|
68
|
+
isAnimating.current = true
|
69
|
+
const [firstItemRef, lastItemRef] = daysElementRefs.current
|
70
|
+
firstItemRef.style.transform = `translateX(${toRight()}px)`
|
71
|
+
lastItemRef.style.transform = `translateX(${toRight()}px)`
|
72
|
+
|
73
|
+
requestAnimationFrame(() => {
|
74
|
+
lastItemRef.style.transition = `transform ${TIME}ms ${ANIMATE_FUNC}`
|
75
|
+
lastItemRef.style.transform = `translateX(${0}px)`
|
76
|
+
|
77
|
+
firstItemRef.style.transition = `transform ${TIME}ms ${ANIMATE_FUNC}`
|
78
|
+
firstItemRef.style.transform = `translateX(${0}px)`
|
79
|
+
|
80
|
+
setTimeout(() => {
|
81
|
+
setDays((oldItems) => {
|
82
|
+
return oldItems.filter((items) => items.id === newValue.id)
|
83
|
+
})
|
84
|
+
firstItemRef.style.transition = null
|
85
|
+
firstItemRef.style.transform = null
|
86
|
+
isAnimating.current = false
|
87
|
+
}, TIME + 50)
|
88
|
+
})
|
89
|
+
})
|
90
|
+
}
|
91
|
+
return {
|
92
|
+
slideToTheNextMonth,
|
93
|
+
slideToPrevMonth
|
94
|
+
}
|
95
|
+
}
|
@@ -0,0 +1,95 @@
|
|
1
|
+
import type React from 'react'
|
2
|
+
import { useState } from 'react'
|
3
|
+
import dayjs from 'dayjs'
|
4
|
+
import { getAngelValues } from '../utils/timePicker'
|
5
|
+
import { type DatePickerValue } from '../types'
|
6
|
+
import { type onChangePayload } from '../packages/TimePicker/TimePicker.types'
|
7
|
+
|
8
|
+
interface useTimePickerType {
|
9
|
+
timeConvention?: 'am' | 'pm'
|
10
|
+
clockTime?: 24 | 12
|
11
|
+
defaultValue?: DatePickerValue
|
12
|
+
onChange?: (payload: onChangePayload) => void
|
13
|
+
}
|
14
|
+
export const useTimePicker = ({
|
15
|
+
defaultValue,
|
16
|
+
clockTime,
|
17
|
+
timeConvention,
|
18
|
+
onChange
|
19
|
+
}: useTimePickerType) => {
|
20
|
+
const time =
|
21
|
+
defaultValue !== undefined ? dayjs(defaultValue) : dayjs().startOf('date')
|
22
|
+
const [selecting, setSelecting] = useState<boolean>(false)
|
23
|
+
const [selectingHour, setSelectingHour] = useState<boolean>(false)
|
24
|
+
const [isInsideHour, setInsideHour] = useState<boolean>(false)
|
25
|
+
const hourFormat = clockTime === 24 ? 'HH' : 'h'
|
26
|
+
const [hour, setHour] = useState<number>(
|
27
|
+
parseInt(time.format(hourFormat), 10)
|
28
|
+
)
|
29
|
+
const [minute, setMinute] = useState<number>(parseInt(time.format('mm'), 10))
|
30
|
+
|
31
|
+
const handleChangeMinute = (e: React.MouseEvent | React.TouchEvent) => {
|
32
|
+
const { value } = getAngelValues(e, 6)
|
33
|
+
setMinute(value)
|
34
|
+
}
|
35
|
+
const handleChangeHour = (e: React.MouseEvent | React.TouchEvent) => {
|
36
|
+
const { value, delta } = getAngelValues(e)
|
37
|
+
if (clockTime === 24) {
|
38
|
+
if (Math.round(delta) < 85) {
|
39
|
+
setHour(value)
|
40
|
+
setInsideHour(true)
|
41
|
+
} else {
|
42
|
+
setHour(value + 12)
|
43
|
+
setInsideHour(false)
|
44
|
+
}
|
45
|
+
return
|
46
|
+
}
|
47
|
+
setHour(value)
|
48
|
+
}
|
49
|
+
const handleMouseMove = (e: React.MouseEvent) => {
|
50
|
+
e.preventDefault()
|
51
|
+
if (!selecting) {
|
52
|
+
return
|
53
|
+
}
|
54
|
+
if (selectingHour) {
|
55
|
+
return handleChangeHour(e)
|
56
|
+
}
|
57
|
+
return handleChangeMinute(e)
|
58
|
+
}
|
59
|
+
const handleSelecting = (e: React.MouseEvent | React.TouchEvent) => {
|
60
|
+
setSelecting(true)
|
61
|
+
if (selectingHour) {
|
62
|
+
return handleChangeHour(e)
|
63
|
+
} else {
|
64
|
+
return handleChangeMinute(e)
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
const handleMouseUp = () => {
|
69
|
+
if (selectingHour) {
|
70
|
+
if (typeof onChange === 'function') {
|
71
|
+
onChange({
|
72
|
+
hour,
|
73
|
+
minute,
|
74
|
+
...(clockTime === 12 && { timeConvention })
|
75
|
+
})
|
76
|
+
}
|
77
|
+
setSelecting(false)
|
78
|
+
setSelectingHour(false)
|
79
|
+
setInsideHour(false)
|
80
|
+
return
|
81
|
+
}
|
82
|
+
setSelecting(false)
|
83
|
+
setSelectingHour(true)
|
84
|
+
}
|
85
|
+
|
86
|
+
return {
|
87
|
+
hour,
|
88
|
+
minute,
|
89
|
+
isInsideHour,
|
90
|
+
selectingHour,
|
91
|
+
handleMouseMove,
|
92
|
+
handleMouseUp,
|
93
|
+
handleSelecting
|
94
|
+
}
|
95
|
+
}
|
package/src/index.tsx
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
import styled from '@emotion/styled'
|
2
|
+
import { CALENDAR_HEIGHT, CALENDAR_WIDTH } from '../../constants'
|
3
|
+
import { radius } from '../../style/radius'
|
4
|
+
|
5
|
+
export const Wrapper = styled.div`
|
6
|
+
overflow: hidden;
|
7
|
+
position: relative;
|
8
|
+
width: ${CALENDAR_WIDTH}px;
|
9
|
+
height: ${CALENDAR_HEIGHT}px;
|
10
|
+
border: 1px solid ${(props) => props.theme.colors.gray[40]};
|
11
|
+
background-color: #fff;
|
12
|
+
border-radius: ${(props) => radius[props.theme.round].wrapper}px;
|
13
|
+
`
|
14
|
+
|
15
|
+
export const WrapperDays = styled.div`
|
16
|
+
display: flex;
|
17
|
+
position: absolute;
|
18
|
+
overflow: hidden;
|
19
|
+
right: ${(props) => (props.theme.direction === 'rtl' ? '8px' : 'unset')};
|
20
|
+
left: ${(props) => (props.theme.direction !== 'rtl' ? '8px' : 'unset')};
|
21
|
+
`
|
22
|
+
|
23
|
+
export const SlideDays = styled.div`
|
24
|
+
will-change: transform;
|
25
|
+
display: flex;
|
26
|
+
flex-direction: column;
|
27
|
+
width: ${CALENDAR_WIDTH}px;
|
28
|
+
gap: 4px;
|
29
|
+
`
|
30
|
+
export const Days = styled.div`
|
31
|
+
display: flex;
|
32
|
+
gap: 4px;
|
33
|
+
`
|
34
|
+
export const SubHeader = styled.div`
|
35
|
+
display: flex;
|
36
|
+
justify-content: center;
|
37
|
+
align-items: center;
|
38
|
+
margin-top: 12px;
|
39
|
+
margin-bottom: 8px;
|
40
|
+
height: 24px;
|
41
|
+
gap: 4px;
|
42
|
+
`
|
@@ -0,0 +1,159 @@
|
|
1
|
+
import React, {
|
2
|
+
type ForwardedRef,
|
3
|
+
forwardRef,
|
4
|
+
useMemo,
|
5
|
+
useRef,
|
6
|
+
useState
|
7
|
+
} from 'react'
|
8
|
+
import {
|
9
|
+
Days,
|
10
|
+
SlideDays,
|
11
|
+
SubHeader,
|
12
|
+
Wrapper,
|
13
|
+
WrapperDays
|
14
|
+
} from './Calendar.styled'
|
15
|
+
import Header from '../../components/Header'
|
16
|
+
import { useSlideCalendar } from '../../hooks/useSlideCalendar'
|
17
|
+
import CalendarItem from '../../components/CalendarItem'
|
18
|
+
import {
|
19
|
+
isInBetween,
|
20
|
+
sameDay,
|
21
|
+
selectMonth,
|
22
|
+
selectYear
|
23
|
+
} from '../../utils/dateHelper/dateHelper'
|
24
|
+
import formatDate from '../../utils/format'
|
25
|
+
import getDays from '../../utils/month'
|
26
|
+
import MonthPicker from '../../components/MonthPicker'
|
27
|
+
import YearPicker from '../../components/YearPicker'
|
28
|
+
import { DayName } from '../../components/Header/Header.styled'
|
29
|
+
import locales from '../../utils/locales'
|
30
|
+
import localeCache from '../../utils/locale'
|
31
|
+
import type { DaysInMonth } from '../../utils/month/month.types'
|
32
|
+
import type { CalendarProps } from './Calendar.types'
|
33
|
+
import type { Pickers } from '../../types'
|
34
|
+
import useCalendarHandlers from '../../hooks/useCalendarHandlers'
|
35
|
+
import { CalendarText } from '../../components/CalendarItem/CalendarItem.styled'
|
36
|
+
import { DaysButton } from '../../style/classNames'
|
37
|
+
|
38
|
+
const Calendar = (props: CalendarProps, ref: ForwardedRef<HTMLDivElement>) => {
|
39
|
+
const { locale } = localeCache
|
40
|
+
const { defaultValue, weekends, range = false } = props
|
41
|
+
const startDate = defaultValue === undefined ? new Date() : defaultValue
|
42
|
+
// memo
|
43
|
+
const getAllDays = useMemo(() => getDays(defaultValue), [])
|
44
|
+
// states
|
45
|
+
const [days, setDays] = useState<DaysInMonth[]>([getAllDays])
|
46
|
+
const [picker, setPicker] = useState<Pickers>('days')
|
47
|
+
// refs
|
48
|
+
const daysElementRefs = useRef<HTMLDivElement[]>([])
|
49
|
+
// handlers
|
50
|
+
const slideHandlers = useSlideCalendar({
|
51
|
+
daysElementRefs,
|
52
|
+
days,
|
53
|
+
setDays
|
54
|
+
})
|
55
|
+
const { from, to, handlers } = useCalendarHandlers(props)
|
56
|
+
|
57
|
+
const togglePickers = () => {
|
58
|
+
if (picker === 'month' || picker === 'year') {
|
59
|
+
setPicker('days')
|
60
|
+
return
|
61
|
+
}
|
62
|
+
setPicker('year')
|
63
|
+
}
|
64
|
+
const handleNextMonth = () => {
|
65
|
+
if (picker === 'days') {
|
66
|
+
return slideHandlers.slideToTheNextMonth()
|
67
|
+
}
|
68
|
+
}
|
69
|
+
const handlePrevMonth = () => {
|
70
|
+
if (picker === 'days') {
|
71
|
+
return slideHandlers.slideToPrevMonth()
|
72
|
+
}
|
73
|
+
}
|
74
|
+
const handleMonthSelect = (month: number) => {
|
75
|
+
const date = selectMonth(days[0].middleOfMonth, month)
|
76
|
+
setDays([getDays(date)])
|
77
|
+
setPicker('days')
|
78
|
+
}
|
79
|
+
const handleYearSelect = (year: number) => {
|
80
|
+
const date = selectYear(startDate, year)
|
81
|
+
setDays([getDays(date)])
|
82
|
+
setPicker('month')
|
83
|
+
}
|
84
|
+
return (
|
85
|
+
<Wrapper
|
86
|
+
ref={ref}
|
87
|
+
className={props.className !== null ? props.className : ''}
|
88
|
+
>
|
89
|
+
<Header
|
90
|
+
monthName={days[0].monthName}
|
91
|
+
onNextClick={handleNextMonth}
|
92
|
+
onPrevClick={handlePrevMonth}
|
93
|
+
onClickOnTitle={togglePickers}
|
94
|
+
/>
|
95
|
+
{picker === 'year' ? (
|
96
|
+
<YearPicker value={startDate} onYearSelect={handleYearSelect} />
|
97
|
+
) : null}
|
98
|
+
{picker === 'month' ? (
|
99
|
+
<MonthPicker value={startDate} onMonthSelect={handleMonthSelect} />
|
100
|
+
) : null}
|
101
|
+
{picker === 'days' ? (
|
102
|
+
<>
|
103
|
+
<SubHeader>
|
104
|
+
{locales[locale].shortWeekDays.map((day) => (
|
105
|
+
<DayName key={day.key}>{day.name}</DayName>
|
106
|
+
))}
|
107
|
+
</SubHeader>
|
108
|
+
<WrapperDays>
|
109
|
+
{days.map((weeks, idx) => (
|
110
|
+
<SlideDays
|
111
|
+
key={weeks.id}
|
112
|
+
className="item"
|
113
|
+
ref={(el: HTMLDivElement) => {
|
114
|
+
daysElementRefs.current[idx] = el
|
115
|
+
}}
|
116
|
+
role="rowgroup"
|
117
|
+
>
|
118
|
+
{weeks.weeks.map((week, id) => (
|
119
|
+
<Days key={id} role="row" aria-rowindex={id + 1}>
|
120
|
+
{week.map((day, idx) => (
|
121
|
+
<CalendarItem
|
122
|
+
key={day.date.getTime()}
|
123
|
+
className={DaysButton}
|
124
|
+
data-value={day.date}
|
125
|
+
data-disabled={day.disabled}
|
126
|
+
data-range={props.range}
|
127
|
+
data-selected={!range && sameDay(startDate, day.date)}
|
128
|
+
data-start-range={
|
129
|
+
from != null && sameDay(from, day.date)
|
130
|
+
}
|
131
|
+
data-in-range={isInBetween(day.date, from, to)}
|
132
|
+
data-end-range={to != null && sameDay(to, day.date)}
|
133
|
+
data-weekend={weekends?.some((wDay) => wDay === idx)}
|
134
|
+
type="button"
|
135
|
+
role="gridcell"
|
136
|
+
aria-colindex={idx + 1}
|
137
|
+
tabIndex={0}
|
138
|
+
aria-selected={!range && sameDay(startDate, day.date)}
|
139
|
+
{...handlers}
|
140
|
+
>
|
141
|
+
<CalendarText className="cl-text">
|
142
|
+
{formatDate(day.date, 'DD')}
|
143
|
+
</CalendarText>
|
144
|
+
</CalendarItem>
|
145
|
+
))}
|
146
|
+
</Days>
|
147
|
+
))}
|
148
|
+
</SlideDays>
|
149
|
+
))}
|
150
|
+
</WrapperDays>
|
151
|
+
</>
|
152
|
+
) : null}
|
153
|
+
</Wrapper>
|
154
|
+
)
|
155
|
+
}
|
156
|
+
|
157
|
+
Calendar.displayName = 'Calendar'
|
158
|
+
|
159
|
+
export default forwardRef(Calendar)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import type {
|
2
|
+
DaysRange,
|
3
|
+
DatePickerValue,
|
4
|
+
onRangeDatePickerChangePayload,
|
5
|
+
onDatePickerChangePayload
|
6
|
+
} from '../../types'
|
7
|
+
|
8
|
+
export interface CalendarBaseProps {
|
9
|
+
defaultValue?: Date
|
10
|
+
weekends?: DaysRange[]
|
11
|
+
className?: string
|
12
|
+
}
|
13
|
+
export interface CalendarRangeProps {
|
14
|
+
range: true
|
15
|
+
from?: DatePickerValue
|
16
|
+
to?: DatePickerValue
|
17
|
+
rangeValue?: Date[]
|
18
|
+
onChange?: (args: onRangeDatePickerChangePayload) => void
|
19
|
+
}
|
20
|
+
|
21
|
+
export interface CalendarDefaultProps {
|
22
|
+
range?: false | undefined
|
23
|
+
onChange?: (args: onDatePickerChangePayload) => void
|
24
|
+
}
|
25
|
+
|
26
|
+
export type OnChangePayload =
|
27
|
+
| onRangeDatePickerChangePayload
|
28
|
+
| onDatePickerChangePayload
|
29
|
+
|
30
|
+
export type CalendarProps = CalendarBaseProps &
|
31
|
+
(CalendarRangeProps | CalendarDefaultProps)
|
@@ -0,0 +1,69 @@
|
|
1
|
+
import React from 'react'
|
2
|
+
import Calendar from './Calendar'
|
3
|
+
import { CalendarProvider } from '../../index'
|
4
|
+
import {
|
5
|
+
DaysButton,
|
6
|
+
HeaderClass,
|
7
|
+
IconNextButton,
|
8
|
+
IconPrevButton,
|
9
|
+
YearPickerButton
|
10
|
+
} from '../../style/classNames'
|
11
|
+
|
12
|
+
describe('Calendar Component', () => {
|
13
|
+
it('renders', () => {
|
14
|
+
const initialDate = '1994 08 09'
|
15
|
+
cy.mount(
|
16
|
+
<CalendarProvider locale={'fa'}>
|
17
|
+
<Calendar defaultValue={new Date(initialDate)} onChange={() => {}} />
|
18
|
+
</CalendarProvider>
|
19
|
+
)
|
20
|
+
.get(`.${HeaderClass}`)
|
21
|
+
.findByText('مرداد ۱۳۷۳')
|
22
|
+
.get(`.${DaysButton}[aria-selected=true]`)
|
23
|
+
.should('have.text', '۱۸')
|
24
|
+
})
|
25
|
+
it('renders en locale', () => {
|
26
|
+
const initialDate = '1994 08 09'
|
27
|
+
cy.mount(
|
28
|
+
<CalendarProvider locale={'en'}>
|
29
|
+
<Calendar defaultValue={new Date(initialDate)} onChange={() => {}} />
|
30
|
+
</CalendarProvider>
|
31
|
+
)
|
32
|
+
.get(`.${HeaderClass}`)
|
33
|
+
.findByText('Aug 1994')
|
34
|
+
.get(`.${DaysButton}[aria-selected=true]`)
|
35
|
+
.should('have.text', '9')
|
36
|
+
})
|
37
|
+
it('scenario 1 choose the year and the month', () => {
|
38
|
+
const initialDate = '1994 08 09'
|
39
|
+
// going to Year and Month Picker state
|
40
|
+
cy.mount(
|
41
|
+
<CalendarProvider locale={'fa'}>
|
42
|
+
<Calendar defaultValue={new Date(initialDate)} onChange={() => {}} />
|
43
|
+
</CalendarProvider>
|
44
|
+
)
|
45
|
+
.get(`.${HeaderClass}`)
|
46
|
+
.click()
|
47
|
+
.get(`.${YearPickerButton}[aria-selected=true]`)
|
48
|
+
.should('have.text', '۱۳۷۳')
|
49
|
+
// change the year
|
50
|
+
cy.findByText('۱۴۰۲').click()
|
51
|
+
// click on the current month
|
52
|
+
cy.findByText('مرداد').click()
|
53
|
+
// now year should be changed
|
54
|
+
cy.get(`.${HeaderClass}`).should('have.text', 'مرداد ۱۴۰۲')
|
55
|
+
// go to previous month
|
56
|
+
cy.get(`.${IconPrevButton}`).click()
|
57
|
+
// now month should be changed
|
58
|
+
cy.get(`.${HeaderClass}`).should('have.text', 'تیر ۱۴۰۲')
|
59
|
+
cy.wait(300)
|
60
|
+
// go to previous month
|
61
|
+
cy.get(`.${IconNextButton}`).click()
|
62
|
+
// now month should be changed
|
63
|
+
cy.get(`.${HeaderClass}`).should('have.text', 'مرداد ۱۴۰۲')
|
64
|
+
// go to previous month
|
65
|
+
cy.get(`.${IconNextButton}`).click()
|
66
|
+
// now month should be changed
|
67
|
+
cy.get(`.${HeaderClass}`).should('have.text', 'شهریور ۱۴۰۲')
|
68
|
+
})
|
69
|
+
})
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import React, { useMemo } from 'react'
|
2
|
+
import { ThemeProvider } from '@emotion/react'
|
3
|
+
import { makeColorPallet } from '../../style/colorPallete'
|
4
|
+
import { ACCENT_COLOR } from '../../constants'
|
5
|
+
import localeCache from '../../utils/locale'
|
6
|
+
import { gray } from '../../style/colors'
|
7
|
+
import type { CalendarProviderProps } from './CalendarProvider.types'
|
8
|
+
|
9
|
+
export const CalendarProvider = (props: CalendarProviderProps) => {
|
10
|
+
const {
|
11
|
+
accentColor = ACCENT_COLOR,
|
12
|
+
locale,
|
13
|
+
round = 'thin',
|
14
|
+
direction = 'rtl'
|
15
|
+
} = props
|
16
|
+
useMemo(() => localeCache.setLocale(locale), [locale])
|
17
|
+
const primaryColors = useMemo(() => makeColorPallet(accentColor), [])
|
18
|
+
|
19
|
+
const theme = {
|
20
|
+
colors: {
|
21
|
+
primary: primaryColors,
|
22
|
+
gray
|
23
|
+
},
|
24
|
+
round,
|
25
|
+
direction
|
26
|
+
}
|
27
|
+
return <ThemeProvider theme={theme}>{props.children}</ThemeProvider>
|
28
|
+
}
|
29
|
+
|
30
|
+
export default CalendarProvider
|
@@ -0,0 +1,54 @@
|
|
1
|
+
import React from 'react'
|
2
|
+
import DatePicker from './DatePicker'
|
3
|
+
import { HeaderClass } from '../../style/classNames'
|
4
|
+
|
5
|
+
describe('<DatePicker />', () => {
|
6
|
+
const initialDate = '2023 04 25'
|
7
|
+
it('renders', () => {
|
8
|
+
const onChangeSpy = cy.spy().as('onChangeSpy')
|
9
|
+
cy.mount(<DatePicker defaultValue={initialDate} onChange={onChangeSpy} />)
|
10
|
+
.get('input')
|
11
|
+
.click()
|
12
|
+
|
13
|
+
// change the year
|
14
|
+
cy.get(`.${HeaderClass}`).click()
|
15
|
+
// click on the current month
|
16
|
+
cy.findByText('۱۴۰۲').click()
|
17
|
+
// click on the mordad month
|
18
|
+
cy.findByText('مرداد').click()
|
19
|
+
// click on the mordad month
|
20
|
+
cy.findByText('۱۸').click()
|
21
|
+
cy.get('@onChangeSpy').should('have.callCount', 1)
|
22
|
+
cy.get('@onChangeSpy').should('be.calledWith', { value: new Date('Tue, 08 Aug 2023 20:30:00 GMT') })
|
23
|
+
// now input's value should be changed
|
24
|
+
cy.get('input').should('have.value', '۱۴۰۲/۰۵/۱۸')
|
25
|
+
})
|
26
|
+
it('check range renders', () => {
|
27
|
+
const onChangeSpy = cy.spy().as('onChangeSpy')
|
28
|
+
cy.mount(<DatePicker defaultValue={initialDate} onChange={onChangeSpy} range />)
|
29
|
+
.get('input')
|
30
|
+
.click()
|
31
|
+
|
32
|
+
// change the year
|
33
|
+
cy.get(`.${HeaderClass}`).click()
|
34
|
+
// click on the current month
|
35
|
+
cy.findByText('۱۴۰۲').click()
|
36
|
+
// click on the mordad month
|
37
|
+
cy.findByText('مرداد').click()
|
38
|
+
// click on the mordad month
|
39
|
+
cy.findByText('۱۸').click()
|
40
|
+
// click on the mordad month
|
41
|
+
cy.findByText('۱۹').click()
|
42
|
+
cy.get('@onChangeSpy').should('have.callCount', 1)
|
43
|
+
cy.get('@onChangeSpy').should('be.calledWith', { from: new Date('Tue, 08 Aug 2023 20:30:00 GMT'), to: new Date('Tue, 09 Aug 2023 20:30:00 GMT') })
|
44
|
+
})
|
45
|
+
it('close the DatePicker ', () => {
|
46
|
+
const onChangeSpy = cy.spy().as('onChangeSpy')
|
47
|
+
cy.mount(<DatePicker defaultValue={initialDate} onChange={onChangeSpy} />)
|
48
|
+
.get('input')
|
49
|
+
.click()
|
50
|
+
|
51
|
+
cy.get('body').click()
|
52
|
+
cy.get(`.${HeaderClass}`).should('not.exist')
|
53
|
+
})
|
54
|
+
})
|