@daryasidorovich/modsen-datepicker 1.0.0

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.
Files changed (183) hide show
  1. package/.babelrc.json +7 -0
  2. package/.github/workflows/chromatic.yml +25 -0
  3. package/.github/workflows/ci.yml +56 -0
  4. package/.husky/commit-msg +1 -0
  5. package/.husky/pre-commit +1 -0
  6. package/.prettierignore +16 -0
  7. package/.prettierrc +6 -0
  8. package/.storybook/main.js +93 -0
  9. package/.storybook/preview-head.html +6 -0
  10. package/.storybook/preview.js +26 -0
  11. package/.storybook/preview.tsx +18 -0
  12. package/README.md +302 -0
  13. package/commitlint.config.js +23 -0
  14. package/dist/components/BaseCalendar/index.d.ts +2 -0
  15. package/dist/components/CalendarGrid/CalendarGridWithTasks/Tooltip/index.d.ts +8 -0
  16. package/dist/components/CalendarGrid/CalendarGridWithTasks/index.d.ts +2 -0
  17. package/dist/components/CalendarGrid/CalendarGridWithoutTasks/CalendarGridWithoutTasks.test.d.ts +1 -0
  18. package/dist/components/CalendarGrid/CalendarGridWithoutTasks/index.d.ts +4 -0
  19. package/dist/components/CalendarGrid/index.d.ts +2 -0
  20. package/dist/components/CalendarGrid/types.d.ts +21 -0
  21. package/dist/components/Clear/index.d.ts +5 -0
  22. package/dist/components/DatePicker/index.d.ts +5 -0
  23. package/dist/components/Day/index.d.ts +16 -0
  24. package/dist/components/Input/index.d.ts +10 -0
  25. package/dist/components/Modal/CrudModal/index.d.ts +2 -0
  26. package/dist/components/Modal/index.d.ts +12 -0
  27. package/dist/components/Modal/types.d.ts +12 -0
  28. package/dist/components/Month/index.d.ts +7 -0
  29. package/dist/components/RangePicker/index.d.ts +6 -0
  30. package/dist/components/TasksManager/AddModal/index.d.ts +6 -0
  31. package/dist/components/TasksManager/DeleteModal/index.d.ts +6 -0
  32. package/dist/components/TasksManager/EditModal/index.d.ts +7 -0
  33. package/dist/components/TasksManager/index.d.ts +9 -0
  34. package/dist/components/Week/index.d.ts +17 -0
  35. package/dist/components/WeekDay/index.d.ts +7 -0
  36. package/dist/constants/dateMask.d.ts +21 -0
  37. package/dist/constants/firstDayOfWeek.d.ts +5 -0
  38. package/dist/constants/holidays.d.ts +1 -0
  39. package/dist/constants/modes.d.ts +5 -0
  40. package/dist/constants/monthsNames.d.ts +1 -0
  41. package/dist/constants/weekdaysNames.d.ts +1 -0
  42. package/dist/hooks/useCalendar.d.ts +7 -0
  43. package/dist/hooks/useLockBodyScroll.d.ts +2 -0
  44. package/dist/hooks/useModal.d.ts +13 -0
  45. package/dist/hooks/useTooltip.d.ts +9 -0
  46. package/dist/hooks/useValidate.d.ts +11 -0
  47. package/dist/index.cjs +17250 -0
  48. package/dist/index.cjs.map +1 -0
  49. package/dist/index.d.ts +11 -0
  50. package/dist/index.js +17233 -0
  51. package/dist/index.js.map +1 -0
  52. package/dist/services/calendarService.d.ts +19 -0
  53. package/dist/services/decorators/withHolidays.d.ts +2 -0
  54. package/dist/services/decorators/withHolidays.test.d.ts +1 -0
  55. package/dist/services/decorators/withMinMax.d.ts +2 -0
  56. package/dist/services/decorators/withMinMax.test.d.ts +1 -0
  57. package/dist/services/holidayService.d.ts +5 -0
  58. package/dist/setupTests.d.ts +1 -0
  59. package/dist/store/localStorage.d.ts +3 -0
  60. package/dist/store/tasksStore.d.ts +8 -0
  61. package/dist/store/tasksStore.test.d.ts +1 -0
  62. package/dist/store/types.d.ts +18 -0
  63. package/dist/utils/calendar.d.ts +7 -0
  64. package/dist/utils/calendar.test.d.ts +1 -0
  65. package/dist/utils/controlDate.d.ts +1 -0
  66. package/dist/utils/dateChecks.d.ts +10 -0
  67. package/dist/utils/dateChecks.test.d.ts +1 -0
  68. package/dist/utils/formatDate.d.ts +3 -0
  69. package/dist/utils/formatDate.test.d.ts +1 -0
  70. package/dist/utils/schema.d.ts +6 -0
  71. package/dist/utils/schema.test.d.ts +1 -0
  72. package/dist/utils/weekdayOrder.d.ts +1 -0
  73. package/dist/utils/weekdayOrder.test.d.ts +1 -0
  74. package/docs/base-calendar.png +0 -0
  75. package/docs/dark-theme.png +0 -0
  76. package/docs/datepicker.png +0 -0
  77. package/docs/rangepicker-with-error.png +0 -0
  78. package/docs/rangepicker.png +0 -0
  79. package/eslint.config.ts +80 -0
  80. package/jest.config.json +21 -0
  81. package/package.json +106 -0
  82. package/rollup.config.js +65 -0
  83. package/src/assets/actions/Delete.svg +4 -0
  84. package/src/assets/actions/Edit.svg +3 -0
  85. package/src/assets/arrows/Next.svg +4 -0
  86. package/src/assets/arrows/Prev.svg +4 -0
  87. package/src/assets/input/Calendar.svg +3 -0
  88. package/src/assets/input/Clear.svg +3 -0
  89. package/src/components/BaseCalendar/BaseCalendar.stories.tsx +77 -0
  90. package/src/components/BaseCalendar/index.tsx +63 -0
  91. package/src/components/BaseCalendar/styles.module.scss +15 -0
  92. package/src/components/CalendarGrid/CalendarGrid.stories.tsx +136 -0
  93. package/src/components/CalendarGrid/CalendarGridWithTasks/Tooltip/index.tsx +27 -0
  94. package/src/components/CalendarGrid/CalendarGridWithTasks/Tooltip/styles.module.scss +9 -0
  95. package/src/components/CalendarGrid/CalendarGridWithTasks/index.tsx +51 -0
  96. package/src/components/CalendarGrid/CalendarGridWithoutTasks/CalendarGridWithoutTasks.test.tsx +223 -0
  97. package/src/components/CalendarGrid/CalendarGridWithoutTasks/index.tsx +36 -0
  98. package/src/components/CalendarGrid/CalendarGridWithoutTasks/styles.module.scss +11 -0
  99. package/src/components/CalendarGrid/index.tsx +19 -0
  100. package/src/components/CalendarGrid/types.ts +26 -0
  101. package/src/components/Clear/Clear.stories.tsx +16 -0
  102. package/src/components/Clear/index.tsx +12 -0
  103. package/src/components/Clear/styles.module.scss +34 -0
  104. package/src/components/DatePicker/DatePicker.stories.tsx +127 -0
  105. package/src/components/DatePicker/index.tsx +72 -0
  106. package/src/components/DatePicker/styles.module.scss +10 -0
  107. package/src/components/Day/Day.stories.tsx +75 -0
  108. package/src/components/Day/index.tsx +95 -0
  109. package/src/components/Day/styles.module.scss +97 -0
  110. package/src/components/Input/Input.stories.tsx +37 -0
  111. package/src/components/Input/index.tsx +64 -0
  112. package/src/components/Input/styles.module.scss +79 -0
  113. package/src/components/Modal/CrudModal/index.tsx +43 -0
  114. package/src/components/Modal/CrudModal/styles.module.scss +44 -0
  115. package/src/components/Modal/index.tsx +66 -0
  116. package/src/components/Modal/styles.module.scss +40 -0
  117. package/src/components/Modal/types.ts +13 -0
  118. package/src/components/Month/Month.stories.tsx +19 -0
  119. package/src/components/Month/index.tsx +31 -0
  120. package/src/components/Month/styles.module.scss +30 -0
  121. package/src/components/RangePicker/RangePicker.stories.tsx +134 -0
  122. package/src/components/RangePicker/index.tsx +144 -0
  123. package/src/components/RangePicker/styles.module.scss +19 -0
  124. package/src/components/TasksManager/AddModal/index.tsx +62 -0
  125. package/src/components/TasksManager/DeleteModal/index.tsx +22 -0
  126. package/src/components/TasksManager/EditModal/index.tsx +96 -0
  127. package/src/components/TasksManager/index.tsx +112 -0
  128. package/src/components/TasksManager/styles.module.scss +62 -0
  129. package/src/components/Week/Week.stories.tsx +49 -0
  130. package/src/components/Week/index.tsx +68 -0
  131. package/src/components/Week/styles.module.scss +7 -0
  132. package/src/components/WeekDay/WeekDay.stories.tsx +38 -0
  133. package/src/components/WeekDay/index.tsx +35 -0
  134. package/src/components/WeekDay/styles.module.scss +19 -0
  135. package/src/constants/dateMask.ts +23 -0
  136. package/src/constants/firstDayOfWeek.ts +7 -0
  137. package/src/constants/holidays.ts +32 -0
  138. package/src/constants/modes.ts +6 -0
  139. package/src/constants/monthsNames.ts +14 -0
  140. package/src/constants/weekdaysNames.ts +1 -0
  141. package/src/hooks/useCalendar.ts +62 -0
  142. package/src/hooks/useLockBodyScroll.ts +19 -0
  143. package/src/hooks/useModal.ts +45 -0
  144. package/src/hooks/useTooltip.ts +41 -0
  145. package/src/hooks/useValidate.ts +80 -0
  146. package/src/index.ts +15 -0
  147. package/src/services/calendarService.ts +62 -0
  148. package/src/services/decorators/withHolidays.test.ts +64 -0
  149. package/src/services/decorators/withHolidays.ts +12 -0
  150. package/src/services/decorators/withMinMax.test.ts +77 -0
  151. package/src/services/decorators/withMinMax.ts +19 -0
  152. package/src/services/holidayService.ts +56 -0
  153. package/src/setupTests.ts +1 -0
  154. package/src/store/localStorage.ts +33 -0
  155. package/src/store/tasksStore.test.tsx +208 -0
  156. package/src/store/tasksStore.tsx +146 -0
  157. package/src/store/types.ts +22 -0
  158. package/src/styles/_border.scss +9 -0
  159. package/src/styles/_colors.scss +53 -0
  160. package/src/styles/_gap.scss +4 -0
  161. package/src/styles/_globals.scss +61 -0
  162. package/src/styles/_grid.scss +9 -0
  163. package/src/styles/_mixins.scss +6 -0
  164. package/src/styles/_reset.scss +98 -0
  165. package/src/styles/_theme-mixins.scss +51 -0
  166. package/src/styles/_typography.scss +10 -0
  167. package/src/styles/_variables.scss +8 -0
  168. package/src/styles/main.scss +3 -0
  169. package/src/types/types.d.ts +15 -0
  170. package/src/utils/calendar.test.ts +154 -0
  171. package/src/utils/calendar.ts +82 -0
  172. package/src/utils/controlDate.ts +3 -0
  173. package/src/utils/dateChecks.test.ts +216 -0
  174. package/src/utils/dateChecks.ts +75 -0
  175. package/src/utils/formatDate.test.ts +57 -0
  176. package/src/utils/formatDate.ts +27 -0
  177. package/src/utils/schema.test.ts +40 -0
  178. package/src/utils/schema.ts +8 -0
  179. package/src/utils/weekdayOrder.test.ts +11 -0
  180. package/src/utils/weekdayOrder.ts +7 -0
  181. package/tsconfig.json +31 -0
  182. package/vitest.config.ts +38 -0
  183. package/vitest.shims.d.ts +1 -0
package/.babelrc.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "presets": [
3
+ ["@babel/preset-env", { "targets": { "node": "current" } }],
4
+ ["@babel/preset-react", { "runtime": "automatic" }],
5
+ "@babel/preset-typescript"
6
+ ]
7
+ }
@@ -0,0 +1,25 @@
1
+ name: 'Chromatic'
2
+
3
+ on: push
4
+
5
+ jobs:
6
+ chromatic:
7
+ name: Run Chromatic
8
+ runs-on: ubuntu-latest
9
+ steps:
10
+ - name: Checkout code
11
+ uses: actions/checkout@v7
12
+ with:
13
+ fetch-depth: 0
14
+ - uses: actions/setup-node@v7
15
+ with:
16
+ node-version: 24.19.0
17
+ - name: Install dependencies
18
+ run: npm ci
19
+ - name: Run Chromatic
20
+ uses: chromaui/action@latest
21
+ with:
22
+ projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
23
+ env:
24
+ STORYBOOK_HOLIDAY_API_KEY: ${{ secrets.STORYBOOK_HOLIDAY_API_KEY }}
25
+ STORYBOOK_HOLIDAY_API_URL: ${{ secrets.STORYBOOK_HOLIDAY_API_URL }}
@@ -0,0 +1,56 @@
1
+ name: CI for datepicker
2
+
3
+ on:
4
+ push:
5
+ branches: [main, dev]
6
+ pull_request:
7
+ branches: [main, dev]
8
+
9
+ jobs:
10
+ lint:
11
+ name: Lint
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v7
15
+ - uses: actions/setup-node@v7
16
+ with:
17
+ node-version: 24.19.0
18
+ cache: 'npm'
19
+ - run: npm ci
20
+ - run: npm run lint
21
+
22
+ format:
23
+ name: Format
24
+ runs-on: ubuntu-latest
25
+ steps:
26
+ - uses: actions/checkout@v7
27
+ - uses: actions/setup-node@v7
28
+ with:
29
+ node-version: 24.19.0
30
+ cache: 'npm'
31
+ - run: npm ci
32
+ - run: npm run format:check
33
+
34
+ test:
35
+ name: Test
36
+ runs-on: ubuntu-latest
37
+ steps:
38
+ - uses: actions/checkout@v7
39
+ - uses: actions/setup-node@v7
40
+ with:
41
+ node-version: 24.19.0
42
+ cache: 'npm'
43
+ - run: npm ci
44
+ - run: npm test
45
+
46
+ build:
47
+ name: Build
48
+ runs-on: ubuntu-latest
49
+ steps:
50
+ - uses: actions/checkout@v7
51
+ - uses: actions/setup-node@v7
52
+ with:
53
+ node-version: 24.19.0
54
+ cache: 'npm'
55
+ - run: npm ci
56
+ - run: npm run build
@@ -0,0 +1 @@
1
+ npx commitlint --edit "$1"
@@ -0,0 +1 @@
1
+ npx lint-staged
@@ -0,0 +1,16 @@
1
+ # Ignore artifacts:
2
+ build
3
+ coverage
4
+ node_modules
5
+ dist
6
+ build
7
+ coverage
8
+ .next
9
+ out
10
+ .cache
11
+ *.min.js
12
+ *.min.css
13
+ *.map
14
+ package-lock.json
15
+ yarn.lock
16
+ pnpm-lock.yaml
package/.prettierrc ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "singleQuote": true,
3
+ "trailingComma": "es5",
4
+ "tabWidth": 2,
5
+ "semi": true
6
+ }
@@ -0,0 +1,93 @@
1
+ /** @type { import('@storybook/react-vite').StorybookConfig } */
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import svgr from 'vite-plugin-svgr';
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+
8
+ const config = {
9
+ stories: ['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
10
+ addons: [
11
+ '@storybook/addon-themes',
12
+ '@storybook/addon-vitest',
13
+ '@storybook/addon-a11y',
14
+ '@storybook/addon-docs',
15
+ ],
16
+ framework: {
17
+ name: '@storybook/react-vite',
18
+ options: {},
19
+ },
20
+ docs: {
21
+ autodocs: true,
22
+ },
23
+ typescript: {
24
+ check: false,
25
+ reactDocgen: 'react-docgen-typescript',
26
+ },
27
+ async viteFinal(config) {
28
+ config.optimizeDeps = {
29
+ include: ['react', 'react-dom'],
30
+ };
31
+
32
+ config.plugins = config.plugins || [];
33
+ config.plugins.push(
34
+ svgr({
35
+ include: '**/*.svg?react',
36
+ })
37
+ );
38
+
39
+ config.css = {
40
+ modules: {
41
+ localsConvention: 'camelCase',
42
+ },
43
+ preprocessorOptions: {
44
+ scss: {
45
+ additionalData: `
46
+ @use "@styles/variables" as *;
47
+ @use "@styles/border" as *;
48
+ @use "@styles/mixins" as *;
49
+ @use "@styles/theme-mixins" as *;
50
+ @use "@styles/colors" as *;
51
+ @use "@styles/typography" as *;
52
+ @use "@styles/gap" as *;
53
+ `,
54
+ },
55
+ },
56
+ };
57
+
58
+ config.define = {
59
+ ...config.define,
60
+ 'process.env.STORYBOOK_HOLIDAY_API_KEY': JSON.stringify(
61
+ process.env.STORYBOOK_HOLIDAY_API_KEY
62
+ ),
63
+ 'process.env.STORYBOOK_HOLIDAY_API_URL': JSON.stringify(
64
+ process.env.STORYBOOK_HOLIDAY_API_URL
65
+ ),
66
+ };
67
+
68
+ config.resolve = {
69
+ alias: {
70
+ '@': path.resolve(__dirname, '../src'),
71
+ '@components': path.resolve(__dirname, '../src/components'),
72
+ '@styles': path.resolve(__dirname, '../src/styles'),
73
+ '@assets': path.resolve(__dirname, '../src/assets'),
74
+ '@utils': path.resolve(__dirname, '../src/utils'),
75
+ },
76
+ };
77
+
78
+ config.server = {
79
+ ...config.server,
80
+ proxy: {
81
+ '/holidays': {
82
+ target: 'https://holidayapi.com/v1',
83
+ changeOrigin: true,
84
+ secure: true,
85
+ },
86
+ },
87
+ };
88
+
89
+ return config;
90
+ },
91
+ };
92
+
93
+ export default config;
@@ -0,0 +1,6 @@
1
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
2
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
3
+ <link
4
+ href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@300..800&display=swap"
5
+ rel="stylesheet"
6
+ />
@@ -0,0 +1,26 @@
1
+ /** @type { import('@storybook/react-vite').Preview } */
2
+ import '../src/styles/main.scss';
3
+
4
+ import { withThemeByClassName } from '@storybook/addon-themes';
5
+
6
+ const preview = {
7
+ parameters: {
8
+ controls: {
9
+ matchers: {
10
+ color: /(background|color)$/i,
11
+ date: /Date$/i,
12
+ },
13
+ },
14
+ },
15
+ decorators: [
16
+ withThemeByClassName({
17
+ themes: {
18
+ light: 'theme-light',
19
+ dark: 'theme-dark',
20
+ },
21
+ defaultTheme: 'light',
22
+ }),
23
+ ],
24
+ };
25
+
26
+ export default preview;
@@ -0,0 +1,18 @@
1
+ import type { Preview } from '@storybook/react-vite';
2
+
3
+ const preview: Preview = {
4
+ parameters: {
5
+ controls: {
6
+ matchers: {
7
+ color: /(background|color)$/i,
8
+ date: /Date$/i,
9
+ },
10
+ },
11
+
12
+ a11y: {
13
+ test: 'todo',
14
+ },
15
+ },
16
+ };
17
+
18
+ export default preview;
package/README.md ADDED
@@ -0,0 +1,302 @@
1
+ <div align="center">
2
+
3
+ # Modsen DatePicker Library
4
+
5
+ **Three calendar components for React: BaseCalendar, DatePicker and RangePicker**
6
+
7
+ [Installation](#installation) • [Usage](#usage) • [API](#api) • [Types](#types) • [Demo](https://6a88497b3bbc2742a15109b2-cxguycdfyi.chromatic.com/)
8
+
9
+ </div>
10
+
11
+ ---
12
+
13
+ ## Features
14
+
15
+ - **BaseCalendar** — a simple calendar for viewing and selecting dates
16
+ - **DatePicker** — single date selection via `selectedDate`
17
+ - **RangePicker** — range selection via `rangeStart` / `rangeEnd`
18
+ - **Flexible configuration** — `minDate`, `maxDate`, first day of week, display modes
19
+ - **Hide weekends** — `hideWeekends`
20
+ - **Holidays** — `showHolidays`, highlights public holidays based on the region (browser language)
21
+ - **Tasks** — `enableTasks`, shows task indicators on dates and enables the built-in task manager- **Light and dark themes** — via CSS class on `<html>`
22
+ - **TypeScript** — fully typed props out of the box
23
+
24
+ ---
25
+
26
+ ## Installation
27
+
28
+ ```bash
29
+ npm install modsen-datepicker
30
+ ```
31
+
32
+ Or via yarn:
33
+
34
+ ```bash
35
+ yarn add modsen-datepicker
36
+ ```
37
+
38
+ Requires **React 19+**.
39
+
40
+ ---
41
+
42
+ ## API
43
+
44
+ ### Shared props (all components)
45
+
46
+ | Prop | Type | Default | Description |
47
+ | ---------------- | ------------------------- | ------------ | -------------------------------------------------- |
48
+ | `monthDate` | `Date` | `new Date()` | **Required.** The month being displayed |
49
+ | `minDate` | `Date` | — | Minimum selectable date |
50
+ | `maxDate` | `Date` | — | Maximum selectable date |
51
+ | `firstDayOfWeek` | `FIRST_DAY_OF_WEEK_TYPES` | `0` | First day of the week (`0` — Sunday, `1` — Monday) |
52
+ | `mode` | `MODES_TYPES` | `'month'` | Display mode: `'week'` or `'month'` |
53
+ | `hideWeekends` | `boolean` | `false` | Hide Saturday and Sunday |
54
+ | `showHolidays` | `boolean` | `false` | Highlight holidays |
55
+ | `enableTasks` | `boolean` | `false` | Enable task indicators and tasks manager |
56
+
57
+ ---
58
+
59
+ ## Usage
60
+
61
+ ### DatePicker
62
+
63
+ Single date selection. Extends the shared props with:
64
+
65
+ | Prop | Type | Description |
66
+ | -------------- | ----------------------------------- | ----------------------------------------- |
67
+ | `selectedDate` | `Date` | The selected date |
68
+ | `onChange` | `(date: Date \| undefined) => void` | Called when a date is selected or cleared |
69
+
70
+ ```tsx
71
+ import { useState } from 'react';
72
+ import { DatePicker } from 'modsen-datepicker';
73
+
74
+ const Example = () => {
75
+ const [selectedDate, setSelectedDate] = useState<Date | undefined>();
76
+
77
+ return (
78
+ <DatePicker
79
+ monthDate={new Date()}
80
+ selectedDate={selectedDate}
81
+ onChange={(date) => setSelectedDate(date)}
82
+ />
83
+ );
84
+ };
85
+ ```
86
+
87
+ <div align="center">
88
+
89
+ ![DatePicker](/docs/datepicker.png)
90
+ </div>
91
+
92
+ ### RangePicker
93
+
94
+ Range selection. Extends the shared props with:
95
+
96
+ | Prop | Type | Description |
97
+ | --------------- | ----------------------- | -------------------------------------------------- |
98
+ | `rangeStart` | `Date` | Start of the range |
99
+ | `rangeEnd` | `Date` | End of the range |
100
+ | `onChangeStart` | `(date?: Date) => void` | Called when the start of the range changes |
101
+ | `onChangeEnd` | `(date?: Date) => void` | Called when the end of the range changes or clears |
102
+
103
+ ```tsx
104
+ import { useState } from 'react';
105
+ import { RangePicker } from 'modsen-datepicker';
106
+
107
+ const Example = () => {
108
+ const [rangeStart, setRangeStart] = useState<Date | undefined>();
109
+ const [rangeEnd, setRangeEnd] = useState<Date | undefined>();
110
+
111
+ return (
112
+ <RangePicker
113
+ monthDate={new Date()}
114
+ rangeStart={rangeStart}
115
+ rangeEnd={rangeEnd}
116
+ onChangeStart={setRangeStart}
117
+ onChangeEnd={setRangeEnd}
118
+ />
119
+ );
120
+ };
121
+ ```
122
+
123
+ <div align="center">
124
+
125
+ ![RangePicker](/docs/rangepicker.png)
126
+ </div>
127
+
128
+ ### BaseCalendar
129
+
130
+ A simple calendar for viewing and selecting dates. It has no built-in selection logic — you control everything through `onDateClick`. Accepts `selectedDate`, `rangeStart` and `rangeEnd` purely for display.
131
+
132
+ | Prop | Type | Description |
133
+ | -------------- | -------------------------------------- | ----------------------------------------------------------------- |
134
+ | `selectedDate` | `Date` | The date to highlight as selected |
135
+ | `rangeStart` | `Date` | Start of the range to highlight |
136
+ | `rangeEnd` | `Date` | End of the range to highlight |
137
+ | `onDateClick` | `(e: MouseEvent) => void, date: Date)` | Called on every date click — define your own selection logic here |
138
+
139
+ ```tsx
140
+ import { BaseCalendar, FIRST_DAY_OF_WEEK } from 'modsen-datepicker';
141
+
142
+ const Example = () => {
143
+ return (
144
+ <BaseCalendar
145
+ monthDate={new Date()}
146
+ selectedDate={new Date()}
147
+ firstDayOfWeek={FIRST_DAY_OF_WEEK.MONDAY}
148
+ onDateClick={(date, e) => {
149
+ if (e.ctrlKey) {
150
+ console.log('Ctrl + click', date);
151
+ } else {
152
+ console.log('Click', date);
153
+ }
154
+ }}
155
+ />
156
+ );
157
+ };
158
+ ```
159
+
160
+ <div align="center">
161
+
162
+ ![BaseCalendar](/docs/base-calendar.png)
163
+ </div>
164
+
165
+ ## Callbacks
166
+
167
+ All callbacks receive the **final, validated** value — never an intermediate state.
168
+
169
+ | Component | Callback | Signature | When it fires |
170
+ | ------------- | --------------- | ----------------------- | ----------------------------------------------- |
171
+ | `DatePicker` | `onChange` | `(date: Date | undefined) => void` | On date select or clear |
172
+ | `RangePicker` | `onChangeStart` | `(date?: Date) => void` | When the start of the range changes |
173
+ | `RangePicker` | `onChangeEnd` | `(date?: Date) => void` | When the end of the range changes or is cleared |
174
+
175
+ ## Input validation
176
+
177
+ The input is `masked` (dd/mm/yyyy) and validated:
178
+
179
+ - **Mask** — accepts only digits and inserts separators automatically.
180
+
181
+ - **Invalid date**- — if the value cannot be parsed, the input shows Invalid date and the selection is cleared.
182
+
183
+ - **Out of range** — if minDate or maxDate is set and the value falls outside, the input shows the corresponding error and the value is rejected.
184
+
185
+ <div align="center">
186
+ ![Validation](/docs/rangepicker-with-error.png)
187
+
188
+ </div>
189
+
190
+ ## Interaction
191
+
192
+ ### Single date (DatePicker)
193
+
194
+ A regular click selects a date and calls `onChange` with the new value.
195
+
196
+ ```tsx
197
+ <DatePicker
198
+ monthDate={new Date()}
199
+ selectedDate={selectedDate}
200
+ onChange={(date) => setSelectedDate(date)}
201
+ />
202
+ ```
203
+
204
+ Clicking the same date again clears the selection and calls `onChange(undefined)`.
205
+
206
+ ### Range (RangePicker)
207
+
208
+ Clicks build the range step by step:
209
+
210
+ 1. First click sets `rangeStart` — `onChangeStart(date)` is called.
211
+ 2. Second click sets `rangeEnd` — `onChangeEnd(date)` is called.
212
+ 3. If the second click is earlier than `rangeStart`, the values are swapped and both callbacks receive the corrected pair.
213
+ 4. If the range is already complete, the next click starts a new range — `onChangeStart(date)` and `onChangeEnd(undefined)`.
214
+
215
+ ```tsx
216
+ <RangePicker
217
+ monthDate={new Date()}
218
+ rangeStart={rangeStart}
219
+ rangeEnd={rangeEnd}
220
+ onChangeStart={setRangeStart}
221
+ onChangeEnd={setRangeEnd}
222
+ />
223
+ ```
224
+
225
+ ### Ctrl + click
226
+
227
+ If the range is already selected and the user holds **Ctrl** while clicking, the end of the range is reset to the clicked date. The start stays the same. Only `onChangeEnd(date)` is called.
228
+
229
+ ## Tasks
230
+
231
+ Tasks are disabled by default. Enable them with the `enableTasks` prop:
232
+
233
+ ```tsx
234
+ <DatePicker enableTasks />
235
+ ```
236
+
237
+ When `enableTasks` is on, a **double click** on any date opens a dialog where you can add, edit and delete tasks for that day.
238
+
239
+ Tasks are stored in `localStorage`, so they persist between sessions without any backend.
240
+
241
+ ## Types
242
+
243
+ All types are exported from the package:
244
+
245
+ ```ts
246
+ import type { FIRST_DAY_OF_WEEK_TYPES, MODES_TYPES } from 'modsen-datepicker';
247
+
248
+ import { FIRST_DAY_OF_WEEK, MODES } from 'modsen-datepicker';
249
+ ```
250
+
251
+ ### `FIRST_DAY_OF_WEEK`
252
+
253
+ Defines which day the week starts on.
254
+
255
+ | Constant | Value | Day |
256
+ | -------------------------- | ----- | ------ |
257
+ | `FIRST_DAY_OF_WEEK.SUNDAY` | `0` | Sunday |
258
+ | `FIRST_DAY_OF_WEEK.MONDAY` | `1` | Monday |
259
+
260
+ ```ts
261
+ export type FIRST_DAY_OF_WEEK_TYPES = 0 | 1;
262
+ ```
263
+
264
+ **Example:**
265
+
266
+ ```tsx
267
+ <BaseCalendar
268
+ monthDate={new Date()}
269
+ firstDayOfWeek={FIRST_DAY_OF_WEEK.MONDAY}
270
+ />
271
+ ```
272
+
273
+ ### `MODES`
274
+
275
+ Defines the calendar display mode.
276
+
277
+ | Constant | Value | Description |
278
+ | ------------- | --------- | ----------- |
279
+ | `MODES.WEEK` | `'week'` | Week view |
280
+ | `MODES.MONTH` | `'month'` | Month view |
281
+
282
+ ```ts
283
+ export type MODES_TYPES = 'week' | 'month';
284
+ ```
285
+
286
+ **Example:**
287
+
288
+ ```tsx
289
+ <BaseCalendar monthDate={new Date()} mode={MODES.WEEK} />
290
+ ```
291
+
292
+ ## Theming
293
+
294
+ The library ships with light and dark themes out of the box — no extra configuration is required. The theme is applied automatically via a class on the `<html>` element:
295
+
296
+ - `theme-light` — light theme
297
+ - `theme-dark` — dark theme
298
+
299
+ <div align="center">
300
+
301
+ ![Dark theme](/docs/dark-theme.png)
302
+ </div>
@@ -0,0 +1,23 @@
1
+ export default {
2
+ extends: ['@commitlint/config-conventional'],
3
+ rules: {
4
+ 'type-enum': [
5
+ 2,
6
+ 'always',
7
+ [
8
+ 'feat',
9
+ 'fix',
10
+ 'docs',
11
+ 'style',
12
+ 'refactor',
13
+ 'test',
14
+ 'chore',
15
+ 'build',
16
+ 'ci',
17
+ ],
18
+ ],
19
+ 'subject-max-length': [2, 'always', 100],
20
+ 'subject-empty': [2, 'never'],
21
+ 'type-empty': [2, 'never'],
22
+ },
23
+ };
@@ -0,0 +1,2 @@
1
+ import { BaseCalendarProps } from '../CalendarGrid/types';
2
+ export declare const BaseCalendar: ({ mode, monthDate, firstDayOfWeek, hideWeekends, ...props }: BaseCalendarProps) => import("react").JSX.Element;
@@ -0,0 +1,8 @@
1
+ interface TooltipProps {
2
+ isVisible: boolean;
3
+ text: string;
4
+ x: number;
5
+ y: number;
6
+ }
7
+ export declare const Tooltip: ({ isVisible, text, x, y }: TooltipProps) => import("react").ReactPortal | undefined;
8
+ export {};
@@ -0,0 +1,2 @@
1
+ import { CalendarProps } from '../types';
2
+ export declare const CalendarGridWithTasks: (props: CalendarProps) => import("react").JSX.Element;
@@ -0,0 +1,4 @@
1
+ import { CalendarProps } from '../types';
2
+ export declare const CalendarGridWithoutTasks: ({ children, ...props }: CalendarProps & {
3
+ children?: React.ReactNode;
4
+ }) => import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ import { CalendarProps } from './types';
2
+ export declare const CalendarGrid: (props: CalendarProps) => import("react").JSX.Element;
@@ -0,0 +1,21 @@
1
+ import { FIRST_DAY_OF_WEEK_TYPES } from '@/constants/firstDayOfWeek';
2
+ import { MODES_TYPES } from '@/constants/modes';
3
+ export interface CalendarProps {
4
+ minDate?: Date;
5
+ maxDate?: Date;
6
+ monthDate: Date;
7
+ firstDayOfWeek?: FIRST_DAY_OF_WEEK_TYPES;
8
+ selectedDate?: Date;
9
+ mode?: MODES_TYPES;
10
+ rangeStart?: Date;
11
+ rangeEnd?: Date;
12
+ hideWeekends?: boolean;
13
+ showHolidays?: boolean;
14
+ enableTasks?: boolean;
15
+ hasTasks?: (date: Date) => boolean;
16
+ onDateClick?: (e: React.MouseEvent<HTMLButtonElement>, date: Date) => void;
17
+ onDateDoubleClick?: (date: Date) => void;
18
+ onMouseEnter?: (e: React.MouseEvent<HTMLButtonElement>) => void;
19
+ onMouseLeave?: () => void;
20
+ }
21
+ export type BaseCalendarProps = Omit<CalendarProps, 'hasTasks' | 'onDoubleDateClick' | 'onMouseEnter' | 'onMouseLeave'>;
@@ -0,0 +1,5 @@
1
+ interface ClearProps {
2
+ onClick: () => void;
3
+ }
4
+ export declare const Clear: ({ onClick }: ClearProps) => import("react").JSX.Element;
5
+ export {};
@@ -0,0 +1,5 @@
1
+ import { BaseCalendarProps, CalendarProps } from '../CalendarGrid/types';
2
+ export interface DatePickerProps extends BaseCalendarProps, Pick<CalendarProps, 'selectedDate'> {
3
+ onChange?: (date: Date | undefined) => void;
4
+ }
5
+ export declare const DatePicker: ({ selectedDate, monthDate, onChange, ...props }: DatePickerProps) => import("react").JSX.Element;
@@ -0,0 +1,16 @@
1
+ export interface DayProps {
2
+ date: Date;
3
+ isSelected?: boolean;
4
+ isDisabled?: boolean;
5
+ isHoliday?: boolean;
6
+ hasTasks?: boolean;
7
+ isRangeStart?: boolean;
8
+ isRangeEnd?: boolean;
9
+ isRangeMiddle?: boolean;
10
+ isCurrentMonth: boolean;
11
+ onClick?: (e: React.MouseEvent<HTMLButtonElement>, date: Date) => void;
12
+ onDoubleClick?: (date: Date) => void;
13
+ onMouseEnter?: (e: React.MouseEvent<HTMLButtonElement>) => void;
14
+ onMouseLeave?: () => void;
15
+ }
16
+ export declare const Day: ({ date, isSelected, isCurrentMonth, isDisabled, isHoliday, hasTasks, isRangeStart, isRangeEnd, isRangeMiddle, onClick, onDoubleClick, onMouseEnter, onMouseLeave, }: DayProps) => import("react").JSX.Element;