├── .github
├── FUNDING.yml
├── dependabot.yml
└── workflows
│ └── codeql-analysis.yml
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── README.md
├── docs
└── legacy
│ └── README.md
├── example
├── next-tailwind-ts
│ ├── .github
│ │ └── FUNDING.yml
│ ├── .gitignore
│ ├── .prettierignore
│ ├── .prettierrc.json
│ ├── CHANGELOG.md
│ ├── Dockerfile
│ ├── README.md
│ ├── cache-handler.js
│ ├── eslint.config.js
│ ├── next.config.ts
│ ├── package.json
│ ├── postcss.config.mjs
│ ├── public
│ │ └── thumbnail.png
│ ├── src
│ │ ├── app
│ │ │ ├── favicon.ico
│ │ │ ├── globals.css
│ │ │ ├── layout.tsx
│ │ │ └── page.tsx
│ │ ├── component
│ │ │ └── DateInput.tsx
│ │ └── lib
│ │ │ └── utils.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── vite-react-swc-ts
│ └── README.md
├── vite-react-ts
│ ├── .gitignore
│ ├── README.md
│ ├── eslint.config.js
│ ├── index.html
│ ├── package.json
│ ├── src
│ │ ├── App.css
│ │ ├── App.tsx
│ │ ├── index.css
│ │ ├── main.tsx
│ │ └── vite-env.d.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ ├── vite.config.ts
│ └── yarn.lock
└── with-vite-antd-tailwind
│ ├── .env.example
│ ├── .gitignore
│ ├── README.md
│ ├── docs
│ └── demo.png
│ ├── index.html
│ ├── jsconfig.json
│ ├── package.json
│ ├── public
│ ├── donate.png
│ └── favicon.ico
│ ├── src
│ ├── App.jsx
│ ├── components
│ │ ├── AdvancedGuide.jsx
│ │ ├── Basic.jsx
│ │ ├── CustomThaiDatePicker.css
│ │ ├── Donate.jsx
│ │ ├── EasyForm.jsx
│ │ ├── GetStarted.jsx
│ │ ├── Header.jsx
│ │ └── Loading.jsx
│ ├── index.css
│ ├── main.jsx
│ └── utils
│ │ ├── constant.js
│ │ └── function.js
│ ├── vite.config.js
│ └── yarn.lock
├── package.json
├── src
├── components
│ ├── CustomHeader.tsx
│ ├── CustomInputWrapped.tsx
│ ├── NavigateButton.tsx
│ ├── NavigateSelect.tsx
│ ├── index.test.tsx
│ └── index.tsx
├── config
│ ├── constants.ts
│ └── locale
│ │ ├── _lib
│ │ ├── buildFormatLongFn
│ │ │ └── index.js
│ │ ├── buildLocalizeFn
│ │ │ └── index.js
│ │ ├── buildMatchFn
│ │ │ └── index.js
│ │ └── buildMatchPatternFn
│ │ │ └── index.js
│ │ ├── esm-locale-th
│ │ ├── _lib
│ │ │ ├── formatDistance
│ │ │ │ └── index.js
│ │ │ ├── formatLong
│ │ │ │ └── index.js
│ │ │ ├── formatRelative
│ │ │ │ └── index.js
│ │ │ ├── localize
│ │ │ │ └── index.js
│ │ │ └── match
│ │ │ │ └── index.js
│ │ ├── index.d.ts
│ │ ├── index.js
│ │ ├── index.js.flow
│ │ ├── package.json
│ │ └── snapshot.md
│ │ ├── index.d.ts
│ │ └── th
│ │ ├── _lib
│ │ ├── formatDistance
│ │ │ └── index.js
│ │ ├── formatLong
│ │ │ └── index.js
│ │ ├── formatRelative
│ │ │ └── index.js
│ │ ├── localize
│ │ │ └── index.js
│ │ └── match
│ │ │ └── index.js
│ │ ├── index.d.ts
│ │ ├── index.js
│ │ ├── index.js.flow
│ │ ├── package.json
│ │ └── snapshot.md
├── external
│ └── react-datepicker.css
├── hooks
│ ├── useStylesheet.test.tsx
│ └── useStylesheet.ts
├── index.ts
├── setupTests.ts
├── typings.d.ts
└── utils
│ ├── YearListGenerator.test.ts
│ ├── YearListGenerator.ts
│ ├── index.test.ts
│ └── index.ts
├── tsconfig.json
└── yarn.lock
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: buildingwatsize # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: watsize # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12 | polar: # Replace with a single Polar username
13 | buy_me_a_coffee: watsize # Replace with a single Buy Me a Coffee username
14 | thanks_dev: # Replace with a single thanks.dev username
15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
16 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "npm" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "monthly"
12 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ main ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ main ]
20 | schedule:
21 | - cron: '39 7 * * 0'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 |
28 | strategy:
29 | fail-fast: false
30 | matrix:
31 | language: [ 'javascript' ]
32 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
33 | # Learn more:
34 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
35 |
36 | steps:
37 | - name: Checkout repository
38 | uses: actions/checkout@v2
39 |
40 | # Initializes the CodeQL tools for scanning.
41 | - name: Initialize CodeQL
42 | uses: github/codeql-action/init@v1
43 | with:
44 | languages: ${{ matrix.language }}
45 | # If you wish to specify custom queries, you can do so here or in a config file.
46 | # By default, queries listed here will override any specified in a config file.
47 | # Prefix the list here with "+" to use these queries and those in the config file.
48 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
49 |
50 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
51 | # If this step fails, then you should remove it and run the build manually (see below)
52 | - name: Autobuild
53 | uses: github/codeql-action/autobuild@v1
54 |
55 | # ℹ️ Command-line programs to run using the OS shell.
56 | # 📚 https://git.io/JvXDl
57 |
58 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
59 | # and modify them (or add more) to build your code if your project
60 | # uses a compiled language
61 |
62 | #- run: |
63 | # make bootstrap
64 | # make release
65 |
66 | - name: Perform CodeQL Analysis
67 | uses: github/codeql-action/analyze@v1
68 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # See https://help.github.com/ignore-files/ for more about ignoring files.
3 |
4 | # dependencies
5 | node_modules
6 |
7 | # builds
8 | build
9 | dist
10 | .rpt2_cache
11 |
12 | # misc
13 | .DS_Store
14 | .env
15 | .env.local
16 | .env.development.local
17 | .env.test.local
18 | .env.production.local
19 |
20 | npm-debug.log*
21 | yarn-debug.log*
22 | yarn-error.log*
23 |
24 | __snapshots__
25 | */.eslintcache
26 |
27 | coverage
28 |
29 | .pnp.*
30 | .yarn/*
31 | !.yarn/patches
32 | !.yarn/plugins
33 | !.yarn/releases
34 | !.yarn/sdks
35 | !.yarn/versions
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - node
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 🎉 Release - v2 🎉
4 |
5 | ## [v2.1.2] - `2025-02-13`
6 |
7 | ### Edited
8 |
9 | - Edited `peerDependencies` to support react 18 and later
10 |
11 | ## [v2.1.1] - `2025-02-13`
12 |
13 | ### Updated
14 |
15 | - Patched for `peerDependencies` back-compatibility with `react^18.3.1`
16 |
17 | ## [v2.1.0] - `2025-02-11`
18 |
19 | ### Updated
20 |
21 | - Supported `React 19` ✨
22 | - Updated dependencies
23 | - Migrated React 19 forwardRef
24 | - Migrated deprecated `React.MutableRefObject` to `React.RefObject`
25 |
26 | ### Edited
27 |
28 | - Edited `customInput` props type to `React.ElementType` to support more various kinds component
29 | - Edited more patched css
30 | - Refactor className, style with `cn` utils
31 |
32 | ## [v2.0.2] - `2024-11-19`
33 |
34 | ### Updated
35 |
36 | - Fixed known vulnerabilities
37 | - Updated dependencies
38 | - Fixed bugs on css in example project
39 |
40 | ## [v2.0.1] - `2024-09-20`
41 |
42 | ### Updated
43 |
44 | - Fixed known vulnerabilities
45 | - Updated dependencies
46 |
47 | ## [v2.0.0] - `2024-09-09`
48 |
49 | ### Added
50 |
51 | - `TypeScript` is now supported.
52 | - Added new props `noIntegratedStyle` which can be define to exclude integrated css
53 |
54 | ### Updated
55 |
56 | - Updated dependencies
57 |
58 | ## 🎉 Release - v1 🎉
59 |
60 | ## [v1.3.6] - `2024-08-15`
61 |
62 | ### Updated
63 |
64 | - Updated dependencies
65 |
66 | ## [v1.3.5] - `2024-06-21`
67 |
68 | ### Edited
69 |
70 | - Updated security alerts from Dependabot
71 |
72 | ## [v1.3.4] - `2024-06-21`
73 |
74 | ### Edited
75 |
76 | - Updated dependencies (react-datepicker@7.1.0)
77 | - Drop support of example source code with `create-react-app` (You can access via [Source Code v1.3.3](https://github.com/buildingwatsize/thaidatepicker-react/tree/v1.3.3/example))
78 |
79 | ## [v1.3.3] - `2024-05-10`
80 |
81 | ### Added
82 |
83 | - More example for Typescript project
84 |
85 | ### Edited
86 |
87 | - Fixed bugs for explicit define types on package.json
88 | - Code preparing for Typescript migration (defaultProps will be removed)
89 |
90 | ### Updated
91 |
92 | - dependencies
93 |
94 | ## [v1.3.2] - `2024-05-02`
95 |
96 | ### Updated
97 |
98 | - dependencies
99 |
100 | ## [v1.3.1] - `2024-04-19`
101 |
102 | ### Edited
103 |
104 | - Refactor code to explicit props declaration and also minimize the export
105 | - Refactor code to support webpack 5
106 | - Updated example for selectable with input formatting
107 |
108 | ## [v1.3.0] - `2024-04-18`
109 |
110 | ### Added
111 |
112 | - Support `dayjs/locale/th` as default
113 | - Support `dayjs/plugin/buddhistEra` as default
114 |
115 | ### Updated
116 |
117 | - dependencies
118 | - `example/with-vite-andt-tailwind`'s deps, source code, and also bumps the vite major version
119 |
120 | ## [v1.2.1] - `2024-02-20`
121 |
122 | ### Edited
123 |
124 | - Fixed bugs on `dist` output when publish to npmjs
125 |
126 | ## [v1.2.0] - `2024-02-14`
127 |
128 | ### Edited
129 |
130 | - 🌹 Happy Valentine Day
131 | - updated dependencies (`Alert!` bump major version. So if something not working as expected, don't hesitate for opening the issues/PR.)
132 | - updated demo `example/with-vite-antd-tailwind`
133 |
134 | ## [v1.1.0] - `2023-09-27`
135 |
136 | ### Edited
137 |
138 | - updated dependencies
139 |
140 | ## [v1.0.5] - `2023-06-12`
141 |
142 | ### Edited
143 |
144 | - Fixed package linking
145 |
146 | ## [v1.0.4] - `2023-06-09`
147 |
148 | ### Edited
149 |
150 | - bump dependencies
151 |
152 | ## [v1.0.3] - `2023-04-11`
153 |
154 | ### Added
155 |
156 | - Vercel Analytics for demo project
157 |
158 | ### Edited
159 |
160 | - bump dependencies
161 | - fixed z-index over the properties table in example
162 |
163 | ## [v1.0.2] - `2023-01-26`
164 |
165 | ### Added
166 |
167 | - some devDependencies like mockdate for ease to develop
168 | - supporting es import and be standalone without css
169 | - example for this new release
170 |
171 | ### Edited
172 |
173 | - code structure as component separated
174 | - package.json scripts
175 | - replace enzyme testing with react testing library
176 |
177 | ### Updated
178 |
179 | - dependencies and react@18.2
180 |
181 | ### Removed
182 |
183 | - eslint for reducing project complexity (using vscode extension instead)
184 | - antd, @ant-design/icons for reduce package size (customization available)
185 |
186 | ## [v1.0.0...v1.0.1] - `2023-01-25`
187 |
188 | I have to skipping for the old versions which was unpublished once on first created.
189 |
190 | ---
191 |
192 | ## Legacy Version - v0.x.x
193 |
194 | ## [v0.2.2] - `2022-06-16`
195 |
196 | - [Fixed] When the value of props.value changes, the state will no longer be updated.
197 | - [Updated] Dependencies
198 |
199 | ## [v0.2.1] - `2022-04-29`
200 |
201 | - [Removed] some dependency which not used anymore
202 | - [Edited] Example page for update latest version
203 |
204 | ## [v0.2.0] - `2022-04-29`
205 |
206 | - [Updated] Dependencies (Note: Upgrade major version of `React@18.1.0` and `react-datepicker`)
207 | - [Added] `inputProps` property for customizable of input
208 | - [Added] `inputProps` description on README.md and also a missing one `reactDatePickerProps`
209 | - [Edited] ESLint and prettier
210 | - [Edited] missing remove unused code
211 |
212 | ## [v0.1.5] - `2021-04-02`
213 |
214 | - [Updated] Dependencies
215 | - [Fixed] bug on "Form Submit" after click prev/next month button
216 |
217 | ## [v0.1.4] - `2021-01-19`
218 |
219 | - [Merged] [PR#2](https://github.com/buildingwatsize/thaidatepicker-react/pull/2)
220 | - [Added] CodeQL Analysis
221 | - [Added] Supported props `disabled` and `readOnly`
222 | - [Added] Supported year boundaries with `yearBoundary` props. The default is 99 btw It depends on `minDate` and `maxDate`
223 | - [Added] Clearable button will hide on `disabled` or `readOnly` props is true. The default is true
224 | - [Added] Supported more props of `react-datepicker` by using `reactDatePickerProps` props
225 | - [Fixed] some buggy on yarn
226 |
227 | ## [v0.1.3] - `2021-01-12`
228 |
229 | - [Merged] [PR#1](https://github.com/buildingwatsize/thaidatepicker-react/pull/1)
230 | - [Added] more features, displayFormat, inputStyle, and clearable
231 |
232 | ## [v0.1.2] - `2021-01-11`
233 |
234 | - [Fixed] Known vulnerabilities
235 | - [Added] Link for demo
236 | - [Added] display date format for input
237 | - [Added] clearable props to let user clear their selected date
238 | - [Added] inputStyle to control input box styling
239 |
240 | ## [v0.1.1] - `2020-05-11`
241 |
242 | - Initialized Project
243 |
244 | ## [v0.1.0] - `2020-05-11`
245 |
246 | - Initialized Project
247 |
248 | [v2.1.2]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v2.1.2
249 | [v2.1.1]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v2.1.1
250 | [v2.1.0]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v2.1.0
251 | [v2.0.2]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v2.0.2
252 | [v2.0.1]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v2.0.1
253 | [v2.0.0]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v2.0.0
254 | [v1.3.6]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v1.3.6
255 | [v1.3.5]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v1.3.5
256 | [v1.3.4]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v1.3.4
257 | [v1.3.3]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v1.3.3
258 | [v1.3.2]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v1.3.2
259 | [v1.3.1]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v1.3.1
260 | [v1.3.0]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v1.3.0
261 | [v1.2.1]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v1.2.1
262 | [v1.2.0]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v1.2.0
263 | [v1.1.0]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v1.1.0
264 | [v1.0.5]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v1.0.5
265 | [v1.0.4]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v1.0.4
266 | [v1.0.3]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v1.0.3
267 | [v1.0.2]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v1.0.2
268 | [v0.2.2]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v0.2.2
269 | [v0.2.1]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v0.2.1
270 | [v0.2.0]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v0.2.0
271 | [v0.1.5]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v0.1.5
272 | [v0.1.4]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v0.1.4
273 | [v0.1.3]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v0.1.3
274 | [v0.1.2]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v0.1.2
275 | [v0.1.1]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v0.1.1
276 | [v0.1.0]: https://github.com/buildingwatsize/thaidatepicker-react/releases/tag/v0.1.0
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # thaidatepicker-react
2 |
3 | [](https://www.npmjs.com/package/thaidatepicker-react)
4 | [](https://www.npmjs.com/package/thaidatepicker-react)
5 | [](https://github.com/buildingwatsize/thaidatepicker-react/actions/workflows/github-code-scanning/codeql)
6 | [](https://npmjs.org/package/thaidatepicker-react)
7 |
8 | ---
9 |
10 | > ## 🎉 RELEASE v2 🎉
11 | >
12 | > Thank you to everyone who used my little side project. I appreciate all you guys. Hope to keep it active.
13 |
14 | ---
15 |
16 | ## 📘 About
17 |
18 | The thaidatepicker-react is a component for ReactJS that likes other DatePicker library but all we need is Buddhist Year (25XX – aka Thai Year) come with the right render day on "Leap" year (example: Sat, 29 Feb 2020 must be equal to Sat, 29 Feb 2563) so I wish this component will be a useful thing to you :D. Happy Coding.
19 |
20 | ## ⚙ Install
21 |
22 | ```bash
23 | npm install thaidatepicker-react
24 | # or just `yarn add thaidatepicker-react`
25 | ```
26 |
27 | ## 📌 Example Usage
28 |
29 | ```jsx
30 | import React, { useState } from "react";
31 | import { ThaiDatePicker } from "thaidatepicker-react";
32 |
33 | const App = () => {
34 | const [selectedDate, setSelectedDate] = useState("2024-02-29");
35 | const [selectedThaiDate, setSelectedThaiDate] = useState("2567-02-29");
36 |
37 | const handleDatePickerChange = (christDate, buddhistDate) => {
38 | console.log(christDate);
39 | console.log(buddhistDate);
40 | setSelectedDate(christDate);
41 | setSelectedThaiDate(buddhistDate);
42 | };
43 |
44 | return (
45 | <>
46 |
50 |
christDate: {selectedDate}
51 | thaiDate: {selectedThaiDate}
52 | >
53 | );
54 | };
55 |
56 | export default App;
57 | ```
58 |
59 | ## 📋 Properties
60 |
61 | | **Property** | **Description** | **Type** | **Default** | **Version** |
62 | |--------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------|-------------|
63 | | **children** | the children element inside like {children} by default you don't need to defined as props. | _React.ReactNode \| null_ | null | |
64 | | **id** | #id for container element | _string_ | "thdpk-container" | |
65 | | **value** | A christ date value with fixed dayjs format (YYYY-MM-DD) | _string_ | "" | |
66 | | **onChange** | Handle function with maximum 2 parameters, `christDate` and `thaiDate` | _(christDate: string, thaiDate: string) => void_ | (_christDate: string, _thaiDate: string) => null | |
67 | | **disabled** | Disabled property for input | _boolean_ | false | |
68 | | **readOnly** | ReadOnly property for input | _boolean_ | false | |
69 | | **clearable** | Clear the value (please note clearable will work smoothly with onChange props) | _boolean_ | true | |
70 | | **placeholder** | Placeholder property for input | _string_ | "" | |
71 | | **header** | An object for setting up header component. To change button icon use `prevButtonIcon` and `nextButtonIcon`. To change className of button and select use `prevButtonClassName`, `nextButtonClassName`, `monthSelectClassName`, and `yearSelectClassName` | _Object { prevButtonIcon?: React.ReactNode; nextButtonIcon?: React.ReactNode; prevButtonClassName?: string; nextButtonClassName?: string; monthSelectClassName?: string; yearSelectClassName?: string; } \| null }_ | {} | |
72 | | **yearBoundary** | A config boundary ±Year (e.g. yearBoundary = 2; it means currentYear ± 2.) | _number_ | 99 | |
73 | | **inputProps** | An override input properties. | _(any & { displayFormat?: string; }) \| null_ | null | |
74 | | **reactDatePickerProps** | An override react-datepicker properties. See more (https://reactdatepicker.com/ or https://github.com/Hacker0x01/react-datepicker/blob/master/docs/datepicker.md) | _React.ComponentProps_ | {} | |
75 | | **minDate** | A config minimum selectable date. To use, you can provide the string like `2023-01-31`. (Note: It's will depend on yearBoundary too.) | _Date \| string_ | undefined | |
76 | | **maxDate** | A config maximum selectable date. To use, you can provide the string like `2023-12-31`. (Note: It's will depend on yearBoundary too.) | _Date \| string_ | undefined | |
77 | | **highlightDates** | A highlight selected date. To use, you can provide an array of Date like `[new Date()]` | _(Date \| HighlightDate)[]_ | GetHighlightByDate() | |
78 | | **customInput** | A config for using custom input element. To use, you can provide a name of element like `Input` | _React.ComponentType \| null_ | null | |
79 | | **noIntegratedStyle** | A config for define to exclude integrated css `Note: if you using 2 components, which the first one contain noIntegratedStyle props but the second is not. It will import css and then it apply to them all` | _boolean_ | false | v2.0.0 |
80 |
81 | ## 🎩 Some Useful Tricks
82 |
83 | 1. To style dates outside the selected month, use the `.react-datepicker__day--outside-month` CSS class.
84 |
85 | ```css
86 | .react-datepicker__day--outside-month {
87 | color: #aaa;
88 | }
89 | ```
90 |
91 | > However, be aware that the appearance may resemble the 'disabled' attribute, which could affect the user experience
92 |
93 | ## 📝 Need More Example?
94 |
95 | I made a couple difference stack demos. Try looking at the examples of "vite" or "next" projects on [./example](./example).
96 |
97 | - Document with Online Demo: [Demo](https://thaidatepicker-react-demo.vercel.app)
98 | - or alternate example link: [CodeSandbox](https://codesandbox.io/s/thaidatepicker-react-demo-basic-1m33mx?file=/src/App.js)
99 | - Bonus with NextJS: [CodeSandbox](https://codesandbox.io/s/thaidatepicker-react-demo-nextjs-jrsdep?file=/pages/index.js)
100 |
101 | ## Changelog
102 |
103 | Please see more [CHANGELOG.md](CHANGELOG.md)
104 |
105 | ## License
106 |
107 | MIT © [buildingwatsize](https://github.com/buildingwatsize)
108 |
109 | ## ⚒ Thanks a lot
110 |
111 | - [react-datepicker](https://reactdatepicker.com/)
112 | - [dayjs](https://github.com/iamkun/dayjs)
113 | - [@patch-lee](https://github.com/patch-lee) – Contributor
114 |
--------------------------------------------------------------------------------
/docs/legacy/README.md:
--------------------------------------------------------------------------------
1 | # thaidatepicker-react [Stable Legacy Version]
2 |
3 | * * *
4 |
5 | ## ⚠️ Alert: This is Legacy Version ⚠️
6 |
7 | for the latest version please go to [buildingwatsize/thaidatepicker-react](https://github.com/buildingwatsize/thaidatepicker-react)
8 |
9 | * * *
10 |
11 | [](https://www.npmjs.com/package/thaidatepicker-react) [](https://standardjs.com) [](https://www.npmjs.com/package/thaidatepicker-react)
12 |
13 | ## 📘 About
14 |
15 | Thaidatepicker-react is a component for ReactJS that likes other DatePicker, but all we need is Buddhist Year (25XX – aka Thai Year) come with the right render day (example: Sat, 29 Feb 2020 must be equal to Sat, 29 Feb 2563) so I wish this component will be a useful thing to you :D. Happy Coding.
16 |
17 | ## 📋 Features
18 |
19 | - `minDate` The minimum date that can be selected, possible value "2020-02-29", dayjs and also Moment.
20 | - `maxDate` The maximum date that can be selected, possible value "2020-02-29", dayjs and also Moment.
21 | - `value` The default value, possible value "2020-02-29", dayjs and also Moment.
22 | - `onChange` The handler function, this function returns a couple of value (ChristDate, BuddhistDate)
23 | - `displayFormat` The value's display format on Input, only display which not effected to the value, possible value is "ddd, DD MMMM YYYY" which is `dayjs formatting`. Note: Current is not supported "Localized formats" like "L LL LLL LLLL" or stuff. please see more at [dayjs](https://day.js.org/docs/en/display/format)
24 | - `clearable` The small button to allow user clear the selected value, possible value true, false
25 | - `inputStyle` The style customization.
26 | - `dateFormat` The format of value, possible value is "yyyy-MM-dd" please see more at [date-fns](https://date-fns.org/v2.12.0/docs/format)
27 | - `inputProps` Customizable an input component properties
28 | - `reactDatePickerProps` Customizable a react-datepicker properties
29 |
30 | ## ⚙ Install
31 |
32 | ```bash
33 | npm install --save thaidatepicker-react@0.2.2
34 | # or just `yarn add thaidatepicker-react@0.2.2`
35 | ```
36 |
37 | ## 📌 Usage
38 |
39 | ```jsx
40 | import React, { useState } from 'react'
41 | import dayjs from 'dayjs'
42 | import { WatDatePicker } from 'thaidatepicker-react'
43 |
44 | const App = () => {
45 | const [selectedDate, setSelectedDate] = useState("2020-04-27")
46 | const [selectedThaiDate, setSelectedThaiDate] = useState("2563-04-27")
47 |
48 | const handleWatDatePickerChange = (christDate, buddhistDate) => {
49 | console.log(christDate)
50 | console.log(buddhistDate);
51 | setSelectedDate(christDate)
52 | setSelectedThaiDate(buddhistDate)
53 | }
54 |
55 | return (
56 | <>
57 |
70 | >
71 | )
72 | }
73 |
74 | export default App
75 | ```
76 |
77 | ## 📝 Example
78 |
79 | Please go to `example` directory or click to [example/legacy/src/App.js](/example/legacy/src/App.js)
80 |
81 | - Online Demo: [Demo](https://buildingwatsize.github.io/thaidatepicker-react)
82 |
83 | ## Changelog
84 |
85 | Please see more [CHANGELOG.md](CHANGELOG.md)
86 |
87 | ## License
88 |
89 | MIT © [buildingwatsize](https://github.com/buildingwatsize)
90 |
91 | ## ⚒ Thanks a lot
92 |
93 | - [date-fns](https://date-fns.org/)
94 | - [react-datepicker](https://reactdatepicker.com/)
95 | - [ant-design](https://ant.design/)
96 | - [dayjs](https://github.com/iamkun/dayjs)
97 | - [@patch-lee](https://github.com/patch-lee) – Contributor
98 |
--------------------------------------------------------------------------------
/example/next-tailwind-ts/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: buildingwatsize # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: watsize # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12 | polar: # Replace with a single Polar username
13 | buy_me_a_coffee: watsize # Replace with a single Buy Me a Coffee username
14 | thanks_dev: # Replace with a single thanks.dev username
15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
16 |
--------------------------------------------------------------------------------
/example/next-tailwind-ts/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 | /build
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 |
32 | # env files (can opt-in for committing if needed)
33 | !.env*
34 | .env*
35 | !.env*.example
36 |
37 | # vercel
38 | .vercel
39 |
40 | # typescript
41 | *.tsbuildinfo
42 | next-env.d.ts
43 |
--------------------------------------------------------------------------------
/example/next-tailwind-ts/.prettierignore:
--------------------------------------------------------------------------------
1 | .next
--------------------------------------------------------------------------------
/example/next-tailwind-ts/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "prettier-plugin-tailwindcss"
4 | ]
5 | }
--------------------------------------------------------------------------------
/example/next-tailwind-ts/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## Version History
4 |
5 | ### [v0.5.2] - `2025-02-06`
6 |
7 | - Removed unused dep
8 | - Audit fix
9 |
10 | [v0.5.2]: https://github.com/owlsome-official/next-tailwind-ts/releases/tag/v0.5.2
11 |
12 | ### [v0.5.1] - `2025-02-06`
13 |
14 | - Updated dependencies
15 |
16 | [v0.5.1]: https://github.com/owlsome-official/next-tailwind-ts/releases/tag/v0.5.1
17 |
18 | ### [v0.5.0] - `2025-01-29`
19 |
20 | - Updated dependencies
21 | - Added Redis as Next.js Cache Handler
22 | - New look: [Pantone Color of the year 2025](https://www.pantone.com/color-of-the-year/2025) & [Palette](https://coolors.co/2e5266-bed0d6-e1dfde-9e7a68-481d24)
23 |
24 | [v0.5.0]: https://github.com/owlsome-official/next-tailwind-ts/releases/tag/v0.5.0
25 |
26 | ### [v0.4.0] - `2024-11-12`
27 |
28 | - Upgraded to ✨ [Next.js 15](https://nextjs.org/blog/next-15) come with React 19 ✨
29 | - Updated dependencies
30 | - Refactored on code structure
31 |
32 | [v0.4.0]: https://github.com/owlsome-official/next-tailwind-ts/releases/tag/v0.4.0
33 |
34 | ### [v0.3.1] - `2024-09-20`
35 |
36 | - Fixed known vulnerabilities
37 | - Updated dependencies
38 |
39 | [v0.3.1]: https://github.com/owlsome-official/next-tailwind-ts/releases/tag/v0.3.1
40 |
41 | ### [v0.3.0] - `2024-09-04`
42 |
43 | - Added supported cacheHandler with Redis for scalable infrastructure
44 | - Added Prettier configuration files
45 | - Updated Docker and dockerignore
46 | - Fixed known vulnerabilities via `npx yarn-audit-fix`
47 |
48 | [v0.3.0]: https://github.com/owlsome-official/next-tailwind-ts/releases/tag/v0.3.0
49 |
50 | ### [v0.2.8] - `2024-08-15`
51 |
52 | - Updated dependencies
53 |
54 | [v0.2.8]: https://github.com/owlsome-official/next-tailwind-ts/releases/tag/v0.2.8
55 |
56 | ### [v0.2.7] - `2024-08-15`
57 |
58 | - Updated dependencies
59 |
60 | [v0.2.7]: https://github.com/owlsome-official/next-tailwind-ts/releases/tag/v0.2.7
61 |
62 | ### [v0.2.6] - `2024-04-18`
63 |
64 | - Updated dependencies
65 |
66 | [v0.2.6]: https://github.com/owlsome-official/next-tailwind-ts/releases/tag/v0.2.6
67 |
68 | ### [v0.2.5] - `2024-04-17`
69 |
70 | - Updated dependencies
71 |
72 | [v0.2.5]: https://github.com/owlsome-official/next-tailwind-ts/releases/tag/v0.2.5
73 |
74 | ### [v0.2.4] - `2024-02-21`
75 |
76 | - Updated dependencies
77 |
78 | [v0.2.4]: https://github.com/owlsome-official/next-tailwind-ts/releases/tag/v0.2.4
79 |
80 | ### [v0.2.3] - `2024-02-07`
81 |
82 | - Updated default csp header
83 | - Adjusted compiler config for non-production env
84 |
85 | [v0.2.3]: https://github.com/owlsome-official/next-tailwind-ts/releases/tag/v0.2.3
86 |
87 | ### [v0.2.2] - `2024-02-06`
88 |
89 | - Added default CSP (pre-defined)
90 |
91 | [v0.2.2]: https://github.com/owlsome-official/next-tailwind-ts/releases/tag/v0.2.2
92 |
93 | ### [v0.2.1] - `2024-02-06`
94 |
95 | - Updated Dependencies
96 | - Added default next config
97 |
98 | [v0.2.1]: https://github.com/owlsome-official/next-tailwind-ts/releases/tag/v0.2.1
99 |
100 | ### [v0.2.0] - `2024-01-30`
101 |
102 | - Updated Next 14.1
103 |
104 | [v0.2.0]: https://github.com/owlsome-official/next-tailwind-ts/releases/tag/v0.2.0
105 |
106 | ### [v0.1.0] - `2023-01-26`
107 |
108 | - Initialized code structure with [Pantone Color of the year 2024](https://www.pantone.com/color-of-the-year/2024)
109 | - Icon from [Google Material Icons](https://iconbuddy.app/ic)
110 | - Palette from [coolors](https://coolors.co/ffbe98-d35269-c7efcf-826aed-0c1821)
111 |
112 | [v0.1.0]: https://github.com/owlsome-official/next-tailwind-ts/releases/tag/v0.1.0
113 |
--------------------------------------------------------------------------------
/example/next-tailwind-ts/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:22.13.1-alpine3.21 AS runner
2 |
3 | WORKDIR /app
4 |
5 | RUN addgroup --system --gid 1001 nodejs
6 | RUN adduser --system --uid 1001 nextjs
7 |
8 | RUN apk update && apk upgrade --no-cache && \
9 | apk add --no-cache openssl && \
10 | openssl version
11 |
12 | # should be use CDN instead (`public` and `.next/static`)
13 | COPY .env.local ./
14 | COPY ./public ./public
15 |
16 | # Automatically leverage output traces to reduce image size
17 | # https://nextjs.org/docs/advanced-features/output-file-tracing
18 | COPY --chown=nextjs:nodejs ./.next/standalone ./
19 | COPY --chown=nextjs:nodejs ./.next/static ./.next/static
20 |
21 | USER nextjs
22 |
23 | EXPOSE 3000
24 |
25 | ENV NODE_ENV=production
26 | ENV PORT 3000
27 | ENV HOSTNAME="0.0.0.0"
28 |
29 | CMD ["node", "server.js"]
--------------------------------------------------------------------------------
/example/next-tailwind-ts/README.md:
--------------------------------------------------------------------------------
1 | # 🔺 next-tailwind-ts 🔺
2 |
3 | ## 📘 About
4 |
5 | The Next.js template with TypeScript, and Tailwind CSS for scaffolding your project.
6 |
7 | ## 📝 Table of Contents
8 |
9 | - [🔺 next-tailwind-ts 🔺](#-next-tailwind-ts-)
10 | - [📘 About](#-about)
11 | - [📝 Table of Contents](#-table-of-contents)
12 | - [📦 Template contains](#-template-contains)
13 | - [💎 Pre-loaded dependencies](#-pre-loaded-dependencies)
14 | - [📝 Versions (Last 2 Minor Version)](#-versions-last-2-minor-version)
15 | - [v0.5.2 - `2025-02-06`](#v052---2025-02-06)
16 | - [v0.5.1 - `2025-02-06`](#v051---2025-02-06)
17 | - [v0.5.0 - `2025-01-29`](#v050---2025-01-29)
18 | - [v0.4.0 - `2024-11-12`](#v040---2024-11-12)
19 | - [Version History](#version-history)
20 | - [📌 Get Started](#-get-started)
21 | - [Want some more ?](#want-some-more-)
22 |
23 | ## 📦 Template contains
24 |
25 | - [x] React 19.x
26 | - [x] Next.js 15.x
27 | - [x] Tailwind CSS 4.x
28 | - [x] TypeScript 5.x
29 |
30 | ## 💎 Pre-loaded dependencies
31 |
32 | ```bash
33 | yarn create next-app next-tailwind-ts
34 | yarn add @neshca/cache-handler redis clsx tailwind-merge react-loading-randomizable
35 | ```
36 |
37 | ## 📝 Versions (Last 2 Minor Version)
38 |
39 | ### v0.5.2 - `2025-02-06`
40 |
41 | - Removed unused dep
42 | - Audit fix
43 |
44 | ### v0.5.1 - `2025-02-06`
45 |
46 | - Updated dependencies
47 |
48 | ### v0.5.0 - `2025-01-29`
49 |
50 | - Updated dependencies
51 | - Added Redis as Next.js Cache Handler
52 | - New look: [Pantone Color of the year 2025](https://www.pantone.com/color-of-the-year/2025) & [Palette](https://coolors.co/2e5266-bed0d6-e1dfde-9e7a68-481d24)
53 |
54 | ### v0.4.0 - `2024-11-12`
55 |
56 | - Upgraded to ✨ [Next.js 15](https://nextjs.org/blog/next-15) come with React 19 ✨
57 | - Updated dependencies
58 | - Refactored on code structure
59 |
60 | ### Version History
61 |
62 | ... [more](./CHANGELOG.md)
63 |
64 | ## 📌 Get Started
65 |
66 | 1. Initialized (don't forget to rename `my-project`)
67 |
68 | ```bash
69 | npx degit owlsome-official/next-tailwind-ts#main my-project
70 | ```
71 |
72 | 2. Go to project folder
73 |
74 | ```bash
75 | cd my-project
76 | ```
77 |
78 | 3. Set up dependencies
79 |
80 | ```bash
81 | yarn
82 | ```
83 |
84 | 4. Run!
85 |
86 | ```bash
87 | yarn dev
88 | ```
89 |
90 | ### Want some more ?
91 |
92 | - `✨ Recommended` - shadcn/ui
93 |
94 | A beauty and powerful UI Components, get started with it [shadcn/ui](https://ui.shadcn.com/docs/installation/next).
95 |
96 | - more Next.js?
97 |
98 | see [Next.js Doc](https://nextjs.org/docs)
99 |
100 | > 🌈 Next.js Template – Made with ❤️ by Watsize 🌈
101 |
--------------------------------------------------------------------------------
/example/next-tailwind-ts/cache-handler.js:
--------------------------------------------------------------------------------
1 | import { CacheHandler } from "@neshca/cache-handler";
2 | import createLruHandler from "@neshca/cache-handler/local-lru";
3 | import createRedisHandler from "@neshca/cache-handler/redis-strings";
4 | import { createClient } from "redis";
5 |
6 | const redisURL = process.env.REDIS_URL ?? "redis://localhost:6379";
7 | const client = createClient({
8 | url: redisURL,
9 | name: "next-cache",
10 | });
11 |
12 | client.on("error", (error) => {
13 | console.error({ title: "Redis Error", err: error, msg: error?.message });
14 | });
15 |
16 | CacheHandler.onCreation(async ({ buildId }) => {
17 | let redisHandler;
18 |
19 | if (buildId) {
20 | await client.connect();
21 |
22 | const keyPrefix = `REPLACE_WITH_YOUR_PROJECT_NAME-cache-${buildId}:`;
23 | redisHandler = createRedisHandler({
24 | client,
25 | timeoutMs: 5000,
26 | keyPrefix,
27 | });
28 | console.info({
29 | buildId,
30 | redisURL,
31 | connection: "connected",
32 | isReady: client.isReady,
33 | isOpen: client.isOpen,
34 | keyPrefix,
35 | });
36 | }
37 |
38 | const localHandler = createLruHandler();
39 | return {
40 | handlers: [redisHandler, localHandler],
41 | };
42 | });
43 |
44 | export default CacheHandler;
45 |
--------------------------------------------------------------------------------
/example/next-tailwind-ts/eslint.config.js:
--------------------------------------------------------------------------------
1 | import { FlatCompat } from "@eslint/eslintrc";
2 | import js from "@eslint/js";
3 | import path from "node:path";
4 | import { fileURLToPath } from "node:url";
5 |
6 | const __filename = fileURLToPath(import.meta.url);
7 | const __dirname = path.dirname(__filename);
8 | const compat = new FlatCompat({
9 | baseDirectory: __dirname,
10 | recommendedConfig: js.configs.recommended,
11 | allConfig: js.configs.all,
12 | });
13 | export default [...compat.extends("next/core-web-vitals", "next/typescript")];
14 |
--------------------------------------------------------------------------------
/example/next-tailwind-ts/next.config.ts:
--------------------------------------------------------------------------------
1 | import type { NextConfig } from "next";
2 | import pkg from "./package.json";
3 |
4 | const cspHeader = `
5 | script-src 'self' 'unsafe-eval' 'unsafe-inline';
6 | style-src 'self' 'unsafe-inline';
7 | img-src 'self' blob: data:;
8 | font-src 'self';
9 | object-src 'none';
10 | base-uri 'self';
11 | form-action 'self';
12 | frame-ancestors 'none';
13 | `;
14 |
15 | const baseSecurityHeader = [
16 | {
17 | key: "Referrer-Policy",
18 | value: "no-referrer",
19 | },
20 | {
21 | key: "Cross-Origin-Embedder-Policy",
22 | value: "require-corp",
23 | },
24 | {
25 | key: "Cross-Origin-Opener-Policy",
26 | value: "same-origin",
27 | },
28 | {
29 | key: "Cross-Origin-Resource-Policy",
30 | value: "same-origin",
31 | },
32 | {
33 | key: "Permissions-Policy",
34 | value:
35 | "accelerometer=(),autoplay=(),camera=(),display-capture=(),encrypted-media=(),fullscreen=(),geolocation=(),gyroscope=(),magnetometer=(),microphone=(),midi=(),payment=(),picture-in-picture=(),publickey-credentials-get=(),screen-wake-lock=(),sync-xhr=(self),usb=(),web-share=(),xr-spatial-tracking=()",
36 | },
37 | {
38 | key: "Strict-Transport-Security",
39 | value: "max-age=31536000; includeSubDomains; preload",
40 | },
41 | {
42 | key: "X-Content-Type-Options",
43 | value: "nosniff",
44 | },
45 | {
46 | key: "X-Frame-Options",
47 | value: "DENY",
48 | },
49 | {
50 | key: "X-DNS-Prefetch-Control",
51 | value: "on",
52 | },
53 | {
54 | key: "X-Permitted-Cross-Domain-Policies",
55 | value: "none",
56 | },
57 | {
58 | key: "x-powered-by",
59 | value: "owlsome-official/next-tailwind-ts",
60 | },
61 | ];
62 |
63 | const nextConfig: NextConfig = {
64 | async headers() {
65 | return [
66 | {
67 | source: "/(.*)",
68 | headers: [
69 | ...baseSecurityHeader,
70 | {
71 | key: "Content-Security-Policy",
72 | value: cspHeader.replace(/\n/g, ""),
73 | },
74 | {
75 | key: "X-App-Version",
76 | value: pkg.version,
77 | },
78 | ],
79 | },
80 | ];
81 | },
82 | output: "standalone",
83 | poweredByHeader: false,
84 | };
85 |
86 | export default nextConfig;
87 |
--------------------------------------------------------------------------------
/example/next-tailwind-ts/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "next-tailwind-ts",
3 | "version": "0.5.1",
4 | "type": "module",
5 | "scripts": {
6 | "dev": "next dev --turbopack",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "clsx": "^2.1.1",
13 | "next": "15.1.7",
14 | "react": "19.0.0",
15 | "react-dom": "19.0.0",
16 | "tailwind-merge": "^3.0.1",
17 | "thaidatepicker-react": "../.."
18 | },
19 | "devDependencies": {
20 | "@tailwindcss/postcss": "^4.0.6",
21 | "@types/node": "^22.13.1",
22 | "@types/react": "^19.0.8",
23 | "@types/react-dom": "^19.0.3",
24 | "eslint": "^9.20.0",
25 | "eslint-config-next": "15.1.7",
26 | "postcss": "^8.5.2",
27 | "prettier": "^3.5.0",
28 | "prettier-plugin-tailwindcss": "^0.6.11",
29 | "tailwindcss": "^4.0.6",
30 | "typescript": "^5.7.3"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/example/next-tailwind-ts/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | '@tailwindcss/postcss': {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/example/next-tailwind-ts/public/thumbnail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/buildingwatsize/thaidatepicker-react/2af782d164393929475eea518867a3e1afa73930/example/next-tailwind-ts/public/thumbnail.png
--------------------------------------------------------------------------------
/example/next-tailwind-ts/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/buildingwatsize/thaidatepicker-react/2af782d164393929475eea518867a3e1afa73930/example/next-tailwind-ts/src/app/favicon.ico
--------------------------------------------------------------------------------
/example/next-tailwind-ts/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss";
2 |
3 | @theme {
4 | --breakpoint-*: initial;
5 | --breakpoint-xs: 425px;
6 | --breakpoint-sm: 640px;
7 | --breakpoint-md: 768px;
8 | --breakpoint-lg: 1024px;
9 | --breakpoint-xl: 1280px;
10 | --breakpoint-2xl: 1536px;
11 |
12 | --color-background: var(--background);
13 | --color-foreground: var(--foreground);
14 | --color-primary: oklch(70% 0.1 155);
15 | --color-accent: oklch(90.49% 0.0282 128.65);
16 | --color-white: oklch(98.51% 0 0);
17 | }
18 |
19 | /*
20 | The default border color has changed to `currentColor` in Tailwind CSS v4,
21 | so we've added these compatibility styles to make sure everything still
22 | looks the same as it did with Tailwind CSS v3.
23 |
24 | If we ever want to remove these styles, we need to add an explicit border
25 | color utility to any element that depends on these defaults.
26 | */
27 | @layer base {
28 | *,
29 | ::after,
30 | ::before,
31 | ::backdrop,
32 | ::file-selector-button {
33 | border-color: var(--color-gray-200, currentColor);
34 | }
35 | }
36 |
37 | @layer base {
38 | input {
39 | @apply rounded-md border border-black ps-1;
40 | }
41 | }
42 |
43 | :root {
44 | --background: oklch(98.51% 0 0);
45 | --foreground: oklch(20.46% 0 0);
46 | }
47 |
48 | @media (prefers-color-scheme: dark) {
49 | :root {
50 | --background: oklch(14.48% 0 0);
51 | --foreground: oklch(98.51% 0 0);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/example/next-tailwind-ts/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata, Viewport } from "next";
2 | import { IBM_Plex_Sans_Thai } from "next/font/google";
3 |
4 | import { cn } from "@/lib/utils";
5 | import "./globals.css";
6 |
7 | const font = IBM_Plex_Sans_Thai({
8 | weight: ["400", "600"],
9 | subsets: ["thai", "latin"],
10 | });
11 |
12 | export const metadata: Metadata = {
13 | title: "next-tailwind-ts",
14 | description: "Powered by owlsome-official/next-tailwind-ts",
15 | };
16 | export const viewport: Viewport = {
17 | themeColor: "oklch(70% 0.1 155)",
18 | };
19 |
20 | export default function RootLayout({
21 | children,
22 | }: Readonly<{
23 | children: React.ReactNode;
24 | }>) {
25 | return (
26 |
27 |
28 |
29 | {children}
30 |
31 |
32 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/example/next-tailwind-ts/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | import DateInput from "@/component/DateInput";
2 |
3 | const Page = () => {
4 | return (
5 |
6 |
7 | thaidatepicker-react + NextJS Demo
8 |
9 |
10 |
11 | );
12 | };
13 |
14 | export default Page;
15 |
--------------------------------------------------------------------------------
/example/next-tailwind-ts/src/component/DateInput.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { useCallback, useState } from "react";
3 | import { ThaiDatePicker } from "thaidatepicker-react";
4 |
5 | type Props = {};
6 |
7 | const DateInput = (props: Props) => {
8 | const [selectedDate, setSelectedDate] = useState("2024-02-29"); // Showing date, represent the Leap Year
9 | const [selectedThaiDate, setSelectedThaiDate] =
10 | useState("2567-02-29");
11 |
12 | const handleChangeDatePickerCustom = useCallback(
13 | (christDate: string, buddhistDate: string) => {
14 | console.log(christDate);
15 | console.log(buddhistDate);
16 | setSelectedDate(christDate);
17 | setSelectedThaiDate(buddhistDate);
18 | },
19 | [],
20 | );
21 |
22 | return (
23 | <>
24 |
25 |
26 | christDate value:{" "}
27 | {selectedDate}
28 |
29 |
30 | buddhistDate value:{" "}
31 | {selectedThaiDate}
32 |
33 |
34 |
38 | >
39 | );
40 | };
41 |
42 | export default DateInput;
43 |
--------------------------------------------------------------------------------
/example/next-tailwind-ts/src/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { clsx, type ClassValue } from "clsx";
2 | import { twMerge } from "tailwind-merge";
3 |
4 | export const cn = (...args: ClassValue[]) => {
5 | return twMerge(clsx(args));
6 | };
7 |
--------------------------------------------------------------------------------
/example/next-tailwind-ts/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2017",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./src/*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------
/example/vite-react-swc-ts/README.md:
--------------------------------------------------------------------------------
1 | # React + SWC + TypeScript + Vite
2 |
3 | Due to problem React 19 with `@vitejs/plugin-react-swc`, I would recommend for all your guys to used `@vitejs/plugin-react` (without swc) instead until the chaos subsides.
--------------------------------------------------------------------------------
/example/vite-react-ts/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/example/vite-react-ts/README.md:
--------------------------------------------------------------------------------
1 | # React + TypeScript + Vite
2 |
3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4 |
5 | Currently, two official plugins are available:
6 |
7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9 |
10 | ## Expanding the ESLint configuration
11 |
12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
13 |
14 | - Configure the top-level `parserOptions` property like this:
15 |
16 | ```js
17 | export default tseslint.config({
18 | languageOptions: {
19 | // other options...
20 | parserOptions: {
21 | project: ['./tsconfig.node.json', './tsconfig.app.json'],
22 | tsconfigRootDir: import.meta.dirname,
23 | },
24 | },
25 | })
26 | ```
27 |
28 | - Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
29 | - Optionally add `...tseslint.configs.stylisticTypeChecked`
30 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:
31 |
32 | ```js
33 | // eslint.config.js
34 | import react from 'eslint-plugin-react'
35 |
36 | export default tseslint.config({
37 | // Set the react version
38 | settings: { react: { version: '18.3' } },
39 | plugins: {
40 | // Add the react plugin
41 | react,
42 | },
43 | rules: {
44 | // other rules...
45 | // Enable its recommended rules
46 | ...react.configs.recommended.rules,
47 | ...react.configs['jsx-runtime'].rules,
48 | },
49 | })
50 | ```
51 |
--------------------------------------------------------------------------------
/example/vite-react-ts/eslint.config.js:
--------------------------------------------------------------------------------
1 | import js from '@eslint/js'
2 | import globals from 'globals'
3 | import reactHooks from 'eslint-plugin-react-hooks'
4 | import reactRefresh from 'eslint-plugin-react-refresh'
5 | import tseslint from 'typescript-eslint'
6 |
7 | export default tseslint.config(
8 | { ignores: ['dist'] },
9 | {
10 | extends: [js.configs.recommended, ...tseslint.configs.recommended],
11 | files: ['**/*.{ts,tsx}'],
12 | languageOptions: {
13 | ecmaVersion: 2020,
14 | globals: globals.browser,
15 | },
16 | plugins: {
17 | 'react-hooks': reactHooks,
18 | 'react-refresh': reactRefresh,
19 | },
20 | rules: {
21 | ...reactHooks.configs.recommended.rules,
22 | 'react-refresh/only-export-components': [
23 | 'warn',
24 | { allowConstantExport: true },
25 | ],
26 | },
27 | },
28 | )
29 |
--------------------------------------------------------------------------------
/example/vite-react-ts/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/example/vite-react-ts/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vite-react-ts",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint .",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^19.0.0",
14 | "react-dom": "^19.0.0",
15 | "thaidatepicker-react": "link:../.."
16 | },
17 | "devDependencies": {
18 | "@eslint/js": "^9.19.0",
19 | "@types/react": "^19.0.8",
20 | "@types/react-dom": "^19.0.3",
21 | "@vitejs/plugin-react": "^4.3.4",
22 | "eslint": "^9.19.0",
23 | "eslint-plugin-react-hooks": "^5.0.0",
24 | "eslint-plugin-react-refresh": "^0.4.18",
25 | "globals": "^15.14.0",
26 | "typescript": "~5.7.2",
27 | "typescript-eslint": "^8.22.0",
28 | "vite": "^6.1.0"
29 | }
30 | }
--------------------------------------------------------------------------------
/example/vite-react-ts/src/App.css:
--------------------------------------------------------------------------------
1 | #root {
2 | max-width: 1280px;
3 | margin: 0 auto;
4 | padding: 2rem;
5 | text-align: center;
6 | }
7 |
--------------------------------------------------------------------------------
/example/vite-react-ts/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { ThaiDatePicker } from "thaidatepicker-react";
3 |
4 | const App = () => {
5 | const [selectedDate, setSelectedDate] = useState("2024-02-29");
6 | const [selectedThaiDate, setSelectedThaiDate] = useState("2567-02-29");
7 |
8 | const handleDatePickerChange = (christDate: string, buddhistDate: string) => {
9 | console.log(christDate);
10 | console.log(buddhistDate);
11 | setSelectedDate(christDate);
12 | setSelectedThaiDate(buddhistDate);
13 | };
14 | return (
15 | <>
16 |
17 | christDate: {selectedDate}
18 | thaiDate: {selectedThaiDate}
19 | >
20 | );
21 | };
22 |
23 | export default App;
24 |
--------------------------------------------------------------------------------
/example/vite-react-ts/src/index.css:
--------------------------------------------------------------------------------
1 | :root {
2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
3 | line-height: 1.5;
4 | font-weight: 400;
5 |
6 | color-scheme: light light;
7 | color: rgba(255, 255, 255, 0.87);
8 | background-color: #242424;
9 |
10 | font-synthesis: none;
11 | text-rendering: optimizeLegibility;
12 | -webkit-font-smoothing: antialiased;
13 | -moz-osx-font-smoothing: grayscale;
14 | }
15 |
16 | a {
17 | font-weight: 500;
18 | color: #646cff;
19 | text-decoration: inherit;
20 | }
21 | a:hover {
22 | color: #535bf2;
23 | }
24 |
25 | body {
26 | margin: 0;
27 | display: flex;
28 | place-items: center;
29 | min-width: 320px;
30 | min-height: 100vh;
31 | width: 100vw;
32 | justify-content: center;
33 | }
34 |
35 | h1 {
36 | font-size: 3.2em;
37 | line-height: 1.1;
38 | }
39 |
40 | button {
41 | border-radius: 8px;
42 | border: 1px solid transparent;
43 | padding: 0.6em 1.2em;
44 | font-size: 1em;
45 | font-weight: 500;
46 | font-family: inherit;
47 | background-color: white;
48 | cursor: pointer;
49 | transition: border-color 0.25s;
50 | }
51 | button:hover {
52 | border-color: #646cff;
53 | }
54 | button:focus,
55 | button:focus-visible {
56 | outline: 4px auto -webkit-focus-ring-color;
57 | }
58 |
59 | @media (prefers-color-scheme: light) {
60 | :root {
61 | color: #213547;
62 | background-color: #ffffff;
63 | }
64 | a:hover {
65 | color: #747bff;
66 | }
67 | button {
68 | background-color: #f9f9f9;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/example/vite-react-ts/src/main.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react'
2 | import { createRoot } from 'react-dom/client'
3 | import './index.css'
4 | import App from './App.tsx'
5 |
6 | createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/example/vite-react-ts/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/example/vite-react-ts/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
4 | "target": "ES2020",
5 | "useDefineForClassFields": true,
6 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
7 | "module": "ESNext",
8 | "skipLibCheck": true,
9 |
10 | /* Bundler mode */
11 | "moduleResolution": "bundler",
12 | "allowImportingTsExtensions": true,
13 | "isolatedModules": true,
14 | "moduleDetection": "force",
15 | "noEmit": true,
16 | "jsx": "react-jsx",
17 |
18 | /* Linting */
19 | "strict": true,
20 | "noUnusedLocals": true,
21 | "noUnusedParameters": true,
22 | "noFallthroughCasesInSwitch": true,
23 | "noUncheckedSideEffectImports": true
24 | },
25 | "include": ["src"]
26 | }
27 |
--------------------------------------------------------------------------------
/example/vite-react-ts/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | { "path": "./tsconfig.app.json" },
5 | { "path": "./tsconfig.node.json" }
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/example/vite-react-ts/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4 | "target": "ES2022",
5 | "lib": ["ES2023"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "isolatedModules": true,
13 | "moduleDetection": "force",
14 | "noEmit": true,
15 |
16 | /* Linting */
17 | "strict": true,
18 | "noUnusedLocals": true,
19 | "noUnusedParameters": true,
20 | "noFallthroughCasesInSwitch": true,
21 | "noUncheckedSideEffectImports": true
22 | },
23 | "include": ["vite.config.ts"]
24 | }
25 |
--------------------------------------------------------------------------------
/example/vite-react-ts/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vite.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/example/with-vite-antd-tailwind/.env.example:
--------------------------------------------------------------------------------
1 | VITE_PROJECT_NAME=with-vite-antd-tailwind
2 | VITE_PROJECT_DESCRIPTION=with-vite-antd-tailwind-example
3 | TAG=0.1.0
--------------------------------------------------------------------------------
/example/with-vite-antd-tailwind/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
26 | .pnp.*
27 | .yarn/*
28 | !.yarn/patches
29 | !.yarn/plugins
30 | !.yarn/releases
31 | !.yarn/sdks
32 | !.yarn/versions
--------------------------------------------------------------------------------
/example/with-vite-antd-tailwind/README.md:
--------------------------------------------------------------------------------
1 | # ⚡ thaidatepicker-react demo ⚡
2 |
3 | ## 📘 About
4 |
5 | This is a demo website to show how to use `thaidatepicker-react` package with `vite`, `tailwindcss`, and `ant-design`. I hope this demo will be an answer for all of the question / issues in the future.
6 |
7 | ## 📝 Table of Contents
8 |
9 | - [⚡ thaidatepicker-react demo ⚡](#-thaidatepicker-react-demo-)
10 | - [📘 About](#-about)
11 | - [📝 Table of Contents](#-table-of-contents)
12 | - [📦 Template contains](#-template-contains)
13 | - [☝️ Get started](#️-get-started)
14 | - [🖼️ Screenshot](#️-screenshot)
15 |
16 | ## 📦 Template contains
17 |
18 | - [x] React 18
19 | - [x] Vite (build tool)
20 | - [x] TailwindCSS (for className utils completeness)
21 | - [x] Ant Design CSS (for Component "plug-n-play")
22 |
23 | ## ☝️ Get started
24 |
25 | > Note: Please make sure the parent project has `dist` directory for local package. Run the command below if it doesn't exist.
26 | >
27 | > ```bash
28 | > # Run this command inside root directory `thaidatepicker-react`
29 | >
30 | > # install dependencies first
31 | > yarn
32 | >
33 | > # `yarn start` if debug is needed.
34 | > yarn build
35 | > ```
36 |
37 | 1. Install dependencies
38 |
39 | ```bash
40 | # Run this command inside `thaidatepicker-react/example/with-vite-antd-tailwind`
41 | yarn
42 | ```
43 |
44 | 2. Run
45 |
46 | ```bash
47 | yarn dev
48 | ```
49 |
50 | 3. Demo will appear on website [http://localhost:5173/](http://localhost:5173/)
51 |
52 | ## 🖼️ Screenshot
53 |
54 | 
55 |
56 | 🌈 Vite Template – Made with ❤️ by Watsize 🌈
57 |
--------------------------------------------------------------------------------
/example/with-vite-antd-tailwind/docs/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/buildingwatsize/thaidatepicker-react/2af782d164393929475eea518867a3e1afa73930/example/with-vite-antd-tailwind/docs/demo.png
--------------------------------------------------------------------------------
/example/with-vite-antd-tailwind/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | with-vite-antd-tailwind
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/example/with-vite-antd-tailwind/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": [
4 | "ESNext"
5 | ],
6 | "baseUrl": ".",
7 | "jsx": "react",
8 | "paths": {
9 | "components/*": [
10 | "./src/components/*"
11 | ],
12 | }
13 | },
14 | "exclude": [
15 | "./node_modules/**/"
16 | ]
17 | }
--------------------------------------------------------------------------------
/example/with-vite-antd-tailwind/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "with-vite-antd-tailwind",
3 | "homepage": "https://buildingwatsize.github.io/thaidatepicker-react",
4 | "private": true,
5 | "license": "MIT",
6 | "version": "0.1.0",
7 | "type": "module",
8 | "scripts": {
9 | "dev": "vite",
10 | "build": "vite build",
11 | "build:uat": "vite build --mode staging",
12 | "build:publish": "cp -f .env.example .env && vite build",
13 | "preview": "vite preview"
14 | },
15 | "dependencies": {
16 | "@ant-design/icons": "^5.6.1",
17 | "@tailwindcss/vite": "^4.0.6",
18 | "@vercel/analytics": "^1.5.0",
19 | "antd": "^5.24.0",
20 | "autoprefixer": "^10.4.20",
21 | "postcss": "^8.5.2",
22 | "react": "link:../../node_modules/react",
23 | "react-dom": "link:../../node_modules/react-dom",
24 | "tailwindcss": "^4.0.6",
25 | "thaidatepicker-react": "link:../.."
26 | },
27 | "devDependencies": {
28 | "@types/react": "^19.0.8",
29 | "@types/react-dom": "^19.0.3",
30 | "@vitejs/plugin-react": "^4.3.4",
31 | "vite": "^6.1.0"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/example/with-vite-antd-tailwind/public/donate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/buildingwatsize/thaidatepicker-react/2af782d164393929475eea518867a3e1afa73930/example/with-vite-antd-tailwind/public/donate.png
--------------------------------------------------------------------------------
/example/with-vite-antd-tailwind/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/buildingwatsize/thaidatepicker-react/2af782d164393929475eea518867a3e1afa73930/example/with-vite-antd-tailwind/public/favicon.ico
--------------------------------------------------------------------------------
/example/with-vite-antd-tailwind/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { Layout, Menu, Tag } from "antd";
2 | import React, { useEffect, useState } from "react";
3 |
4 | import Donate from "components/Donate";
5 | import Header from "components/Header";
6 | import Loading from "components/Loading";
7 | import { GetHash, ReplaceHash } from "utils/function";
8 |
9 | const AdvancedGuide = React.lazy(() => import("components/AdvancedGuide.jsx"));
10 | const Basic = React.lazy(() => import("components/Basic.jsx"));
11 | const EasyForm = React.lazy(() => import("components/EasyForm.jsx"));
12 | const GetStarted = React.lazy(() => import("components/GetStarted.jsx"));
13 |
14 | const { Content, Sider } = Layout;
15 | const items = [
16 | {
17 | key: "get_started",
18 | label: "Get Started",
19 | },
20 | {
21 | key: "basic",
22 | label: "Basic/Default view",
23 | },
24 | {
25 | key: "form",
26 | label: "With Ant Design Form",
27 | },
28 | {
29 | key: "advanced",
30 | label: "Advanced Guide",
31 | },
32 | {
33 | key: "donate",
34 | label: (
35 |
36 | Donate
37 |
38 | NEW
39 |
40 |
41 | ),
42 | },
43 | ];
44 | const renderItem = {
45 | get_started: ,
46 | basic: ,
47 | form: ,
48 | advanced: ,
49 | donate: ,
50 | };
51 |
52 | const App = () => {
53 | const [menuSelect, setMenuSelect] = useState(["get_started"]);
54 |
55 | useEffect(() => {
56 | const hash = GetHash();
57 | if (hash) setMenuSelect([hash.replace("#", "")]);
58 | }, []);
59 |
60 | return (
61 |
67 |
78 |
95 | {
101 | ReplaceHash(`#${select.key}`);
102 | setMenuSelect([select.key]);
103 | }}
104 | />
105 |
106 |
112 |
122 |
126 |
127 | }>
128 | {renderItem[menuSelect[0]]}
129 |
130 |
131 |
132 |
133 |
134 | );
135 | };
136 |
137 | export default App;
138 |
--------------------------------------------------------------------------------
/example/with-vite-antd-tailwind/src/components/AdvancedGuide.jsx:
--------------------------------------------------------------------------------
1 | import { Input, Typography } from "antd";
2 | import { useState } from "react";
3 | import { ThaiDatePicker } from "thaidatepicker-react";
4 |
5 | import "./CustomThaiDatePicker.css";
6 |
7 | const RenderWithCode = ({ title, description, children, code }) => {
8 | return (
9 |
10 |
11 |
{title}
12 | {description && (
13 |
{description}
14 | )}
15 |
16 | {children}
17 |
18 | {code}
19 |
20 |
21 | );
22 | };
23 |
24 | const AdvancedGuide = () => {
25 | const [value, setValue] = useState("");
26 | return (
27 |
28 |
Example
29 |
Note: All values were linked
30 |
31 | {/* // ## Guide 1 - Custom input ## // */}
32 |
setValue(christDate)}
37 | customInput={Input}
38 | inputProps={{
39 | displayFormat: "D MMMM YYYY",
40 | className: "w-full"
41 | }}
42 | />`}
43 | >
44 | setValue(christDate)}
47 | customInput={Input}
48 | inputProps={{ displayFormat: "D MMMM YYYY", className: "w-full" }}
49 | />
50 |
51 |
52 | {/* // ## Guide 2 - Another way custom input ## // */}
53 |
setValue(christDate)}
59 | clearable={false}
60 | reactDatePickerProps={{
61 | customInput: , // <-- ✅ className can be used on this line
62 | }}
63 | inputProps={{
64 | className: "bg-red-300", // <-- ❌ this is not working anymore
65 | }}
66 | />`}
67 | >
68 | setValue(christDate)}
71 | clearable={false}
72 | reactDatePickerProps={{
73 | customInput: , // className can be used on this line
74 | }}
75 | inputProps={{
76 | className: "bg-red-300", // <-- ❌ this is not working anymore
77 | }}
78 | />
79 |
80 |
81 | {/* // ## Guide 3 - Custom header of calendar ## // */}
82 |
setValue(christDate)}
87 | dateFormat={"yyyy MM dd"}
88 | inputProps={{
89 | displayFormat: "D MMMM YYYY",
90 | className: "w-full",
91 | }}
92 | header={{
93 | prevButtonIcon: "-",
94 | nextButtonIcon: "+",
95 | prevButtonClassName: "bg-red-400",
96 | nextButtonClassName: "bg-blue-400",
97 | monthSelectClassName: "text-red-800",
98 | yearSelectClassName: "text-green-800",
99 | }}
100 | />`}
101 | >
102 | setValue(christDate)}
105 | dateFormat={"yyyy MM dd"}
106 | inputProps={{
107 | displayFormat: "D MMMM YYYY",
108 | className: "w-full",
109 | }}
110 | header={{
111 | prevButtonIcon: "-",
112 | nextButtonIcon: "+",
113 | prevButtonClassName: "bg-red-400",
114 | nextButtonClassName: "bg-blue-400",
115 | monthSelectClassName: "text-red-800",
116 | yearSelectClassName: "text-green-800",
117 | }}
118 | />
119 |
120 |
121 | {/* // ## Guide 4 - Date formatting ## // */}
122 |
setValue(christDate)}
129 | inputProps={{
130 | displayFormat: "D MMM YY", // <-- ✅ using this instead!
131 | className: "w-full",
132 | }}
133 | dateFormat="yyyy_MM_dd" // <-- ❌ this won't work anymore!
134 | reactDatePickerProps={{
135 | dateFormat: "yyyy MM dd", // <-- ❌ and this won't work anymore too!
136 | }}
137 | />`}
138 | >
139 | setValue(christDate)}
142 | inputProps={{
143 | displayFormat: "D MMM YY", // <-- ✅ using this instead!
144 | className: "w-full",
145 | }}
146 | dateFormat="yyyy_MM_dd" // <-- ❌ this won't work anymore!
147 | reactDatePickerProps={{
148 | dateFormat: "yyyy MM dd", // <-- ❌ and this won't work anymore too!
149 | }}
150 | />
151 |
152 |
153 | {/* // ## Guide 5 - Work with id - styling ## // */}
154 |
setValue(christDate)}
161 | placeholder="Custom Styling"
162 | />`}
163 | >
164 | setValue(christDate)}
168 | placeholder="Custom Styling"
169 | />
170 |
171 |
172 | {/* // ## Guide 6 - Disabled ## // */}
173 |
`}
176 | >
177 |
178 |
179 |
180 | {/* // ## Guide 7 - ReadOnly ## // */}
181 |
`}
184 | >
185 |
186 |
187 |
188 | {/* // ## Guide X - TBD ## // */}
189 |
194 | // Your code here
195 |
196 |
197 | );
198 | };
199 |
200 | export default AdvancedGuide;
201 |
--------------------------------------------------------------------------------
/example/with-vite-antd-tailwind/src/components/Basic.jsx:
--------------------------------------------------------------------------------
1 | import { Typography } from "antd";
2 | import React, { useState } from "react";
3 | import { ThaiDatePicker } from "thaidatepicker-react";
4 | import { basicSrcCode } from "utils/constant";
5 |
6 | const Basic = () => {
7 | const [selectedDate, setSelectedDate] = useState();
8 | const [selectedThaiDate, setSelectedThaiDate] = useState();
9 |
10 | const handleDatePickerChange = (christDate, buddhistDate) => {
11 | console.log({ christDate, buddhistDate });
12 | setSelectedDate(christDate);
13 | setSelectedThaiDate(buddhistDate);
14 | };
15 |
16 | return (
17 | <>
18 | Basic/Default View
19 | Let's try to change to be something! You can see what a magic on console
20 | too.
21 |
22 |
23 |
24 | Input
25 |
29 |
30 |
31 |
32 | christDate: {selectedDate}
33 |
34 |
35 | buddhistDate:{" "}
36 | {selectedThaiDate}
37 |
38 |
39 |
40 | Source Code
41 |
42 | {basicSrcCode}
43 |
44 | >
45 | );
46 | };
47 |
48 | export default Basic;
49 |
--------------------------------------------------------------------------------
/example/with-vite-antd-tailwind/src/components/CustomThaiDatePicker.css:
--------------------------------------------------------------------------------
1 | #custom-id .react-datepicker__input-container input {
2 | width: 100%;
3 | box-sizing: border-box;
4 | margin: 0;
5 | padding: 4px 11px;
6 | border: 1px solid #d9d9d9;
7 | border-radius: 6px;
8 | transition: all 0.2s;
9 | font-size: 14px;
10 | line-height: 1.5714285714285714;
11 | color: rgba(255, 255, 255, 0.88);
12 | background-color: rgba(255, 77, 79, 0.88);
13 | font-weight: bold;
14 | }
15 | #custom-id .react-datepicker__header select:hover,
16 | #custom-id .react-datepicker__input-container input:hover {
17 | border-color: #ff4d4f;
18 | border-inline-end-width: 1px;
19 | }
20 | #custom-id .react-datepicker__header select:focus,
21 | #custom-id .react-datepicker__input-container input:focus {
22 | border-color: #ff4d4f;
23 | box-shadow: 0 0 0 2px rgb(5 130 5 / 12%);
24 | border-inline-end-width: 1px;
25 | outline: 0;
26 | }
27 | #custom-id .react-datepicker__input-container input::placeholder {
28 | color: rgba(0, 0, 0, 0.25);
29 | }
30 |
31 | #custom-id .react-datepicker__header select {
32 | border-radius: 6px;
33 | }
34 | #custom-id .react-datepicker__header button {
35 | border: 0px;
36 | border-radius: 6px;
37 | }
38 | #custom-id .react-datepicker__header button:hover {
39 | background-color: #dbdbdb;
40 | }
41 |
42 | #custom-id .react-datepicker__day--keyboard-selected,
43 | #custom-id .react-datepicker__day--selected {
44 | background-color: #ff4d4f !important;
45 | }
46 | #custom-id .react-datepicker__close-icon {
47 | display: none;
48 | }
49 | #custom-id .react-datepicker__triangle::before,
50 | #custom-id .react-datepicker__triangle::after {
51 | left: -3rem !important;
52 | }
53 |
54 | .ant-form-item-has-error #custom-id .react-datepicker__input-container input {
55 | border-color: #ff1d1d;
56 | }
57 |
--------------------------------------------------------------------------------
/example/with-vite-antd-tailwind/src/components/Donate.jsx:
--------------------------------------------------------------------------------
1 | import { Image, Typography } from "antd";
2 | import React from "react";
3 |
4 | const Donate = () => {
5 | return (
6 | <>
7 | Donate
8 |
9 |
10 |
11 | Simple ways to make the world a better place. Please checkout to
12 | another site.
13 |
14 |
19 | https://resume-watsize.vercel.app/donate
20 |
21 |
22 |
23 |
24 | >
25 | );
26 | };
27 |
28 | export default Donate;
29 |
--------------------------------------------------------------------------------
/example/with-vite-antd-tailwind/src/components/EasyForm.jsx:
--------------------------------------------------------------------------------
1 | import { Button, Form, Input, Typography } from "antd";
2 | import React, { useCallback } from "react";
3 | import { ThaiDatePicker } from "thaidatepicker-react";
4 | import { easySrcCode } from "utils/constant";
5 |
6 | const EasyForm = () => {
7 | const handleValuesChange = useCallback((values) => {
8 | console.log(values);
9 | }, []);
10 | const handleFinish = useCallback((values) => {
11 | console.log(values);
12 | }, []);
13 |
14 | return (
15 |
81 | );
82 | };
83 |
84 | export default EasyForm;
85 |
--------------------------------------------------------------------------------
/example/with-vite-antd-tailwind/src/components/GetStarted.jsx:
--------------------------------------------------------------------------------
1 | import { List, Table, Typography } from "antd";
2 | import React, { useState } from "react";
3 | import { ThaiDatePicker } from "thaidatepicker-react";
4 |
5 | import { basicSrcCode, propsColumns, propsDataSource } from "../utils/constant";
6 |
7 | const GetStarted = () => {
8 | const listItems = [
9 | {
10 | title: "Github:",
11 | content: (
12 |
13 | https://github.com/buildingwatsize/thaidatepicker-react
14 |
15 | ),
16 | },
17 | {
18 | title: "Installation:",
19 | content: (
20 |
21 |
22 | NPM:{" "}
23 |
24 | npm install --save thaidatepicker-react
25 |
26 |
27 |
28 | YARN:{" "}
29 |
30 | yarn add thaidatepicker-react
31 |
32 |
33 |
34 | ),
35 | },
36 | {
37 | title: "Usage:",
38 | content: (
39 |
40 | {basicSrcCode}
41 |
42 | ),
43 | },
44 | {
45 | title: "Example:",
46 | content: ,
47 | },
48 | {
49 | title: "Properties",
50 | content: (
51 | <>
52 |
60 | >
61 | ),
62 | },
63 | ];
64 | return (
65 | <>
66 | Get Started
67 | (
71 |
72 |
73 |
{item.title}
74 |
{item.content}
75 |
76 |
77 | )}
78 | split={false}
79 | />
80 | >
81 | );
82 | };
83 |
84 | const DemoThaiDatePicker = () => {
85 | const [value, setValue] = useState("");
86 | return (
87 | <>
88 | setValue(v)}
91 | inputProps={{ displayFormat: "DD MMMM YYYY" }}
92 | reactDatePickerProps={{ popperClassName: "z-10!" }} // which used temporary for displaying over the Properties table
93 | />
94 |
95 | Note: {`inputProps={{ displayFormat: "DD MMMM YYYY" }}`}
96 |
97 | >
98 | );
99 | };
100 |
101 | export default GetStarted;
102 |
--------------------------------------------------------------------------------
/example/with-vite-antd-tailwind/src/components/Header.jsx:
--------------------------------------------------------------------------------
1 | import { Typography } from "antd";
2 | import React from "react";
3 |
4 | const Header = ({ title = "", className = "" }) => {
5 | return (
6 |
7 |
8 | {title}
9 |
10 |
11 | );
12 | };
13 |
14 | export default Header;
15 |
--------------------------------------------------------------------------------
/example/with-vite-antd-tailwind/src/components/Loading.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const Loading = () => {
4 | return (
5 |
6 |
13 |
17 |
21 |
22 |
Loading...
23 |
24 | );
25 | };
26 |
27 | export default Loading;
28 |
--------------------------------------------------------------------------------
/example/with-vite-antd-tailwind/src/index.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss";
2 |
3 | @theme {
4 | --text-*: initial;
5 | --text-h1: 3.052rem;
6 | --text-h2: 2.441rem;
7 | --text-h3: 1.953rem;
8 | --text-h4: 1.563rem;
9 | --text-h5: 1.25rem;
10 | --text-p: 1rem;
11 | --text-small: 0.8rem;
12 | --text-mini: 0.64rem;
13 | --text-tiny: 0.512rem;
14 | }
15 |
16 | @layer base {
17 | *,
18 | ::after,
19 | ::before,
20 | ::backdrop,
21 | ::file-selector-button {
22 | border-color: var(--color-gray-200, currentColor);
23 | }
24 |
25 | input {
26 | @apply border-[#424874] px-1 border rounded-md;
27 | }
28 | }
29 |
30 | body {
31 | position: absolute;
32 | inset: 0;
33 | margin: 0;
34 | background-color: #dcd6f7;
35 | color: #424874;
36 | }
37 |
38 | :root {
39 | margin: 0;
40 | padding: 0;
41 | font-size: 100%;
42 | min-height: 100vh;
43 | }
44 |
--------------------------------------------------------------------------------
/example/with-vite-antd-tailwind/src/main.jsx:
--------------------------------------------------------------------------------
1 | import { inject } from "@vercel/analytics";
2 | import React from "react";
3 | import ReactDOM from "react-dom/client";
4 | import App from "./App";
5 | import "./index.css";
6 |
7 | inject();
8 |
9 | ReactDOM.createRoot(document.getElementById("root")).render(
10 |
11 |
12 |
13 | );
14 |
--------------------------------------------------------------------------------
/example/with-vite-antd-tailwind/src/utils/constant.js:
--------------------------------------------------------------------------------
1 | export const propsDataSource = [
2 | {
3 | key: "1",
4 | property: "children",
5 | description:
6 | "the children element inside like {children} by default you don't need to defined as props.",
7 | type: "any",
8 | default: "-",
9 | version: "",
10 | },
11 | {
12 | key: "2",
13 | property: "id",
14 | description: "#id for container element",
15 | type: "string",
16 | default: "-",
17 | version: "",
18 | },
19 | {
20 | key: "3",
21 | property: "value",
22 | description: "A christ date value with fixed dayjs format (YYYY-MM-DD)",
23 | type: "string",
24 | default: "-",
25 | version: "",
26 | },
27 | {
28 | key: "4",
29 | property: "onChange",
30 | description:
31 | "Handle function with maximum 2 parameters, `christDate` and `thaiDate`",
32 | type: "function(christDate, thaiDate)",
33 | default: "-",
34 | version: "",
35 | },
36 | {
37 | key: "5",
38 | property: "disabled",
39 | description: "Disabled property for input",
40 | type: "boolean",
41 | default: "false",
42 | version: "",
43 | },
44 | {
45 | key: "6",
46 | property: "readOnly",
47 | description: "ReadOnly property for input",
48 | type: "boolean",
49 | default: "false",
50 | version: "",
51 | },
52 | {
53 | key: "7",
54 | property: "clearable",
55 | description:
56 | "Clear the value (please note clearable will work smoothly with onChange props)",
57 | type: "boolean",
58 | default: "true",
59 | version: "",
60 | },
61 | {
62 | key: "8",
63 | property: "placeholder",
64 | description: "Placeholder property for input",
65 | type: "string",
66 | default: "-",
67 | version: "",
68 | },
69 | {
70 | key: "9",
71 | property: "header",
72 | description: `
73 | An object for setting up header component.
74 | To change button icon use \`prevButtonIcon\` and \`nextButtonIcon\`.
75 | To change className of button and select use \`prevButtonClassName\`, \`nextButtonClassName\`, \`monthSelectClassName\`, and \`yearSelectClassName\`
76 | `,
77 | type: `Object {
78 | prevButtonIcon: type any | default undefined,
79 | nextButtonIcon: type any | default undefined,
80 | prevButtonClassName: type any | default undefined,
81 | nextButtonClassName: type any | default undefined,
82 | monthSelectClassName: type any | default undefined,
83 | yearSelectClassName: type any | default undefined
84 | }`,
85 | default: "{}",
86 | version: "",
87 | },
88 | {
89 | key: "10",
90 | property: "yearBoundary",
91 | description:
92 | "A config boundary ±Year (e.g. yearBoundary = 2; it means currentYear ± 2.)",
93 | type: "number",
94 | default: 99,
95 | version: "",
96 | },
97 | {
98 | key: "11",
99 | property: "inputProps",
100 | description: "An override input properties.",
101 | type: "Object",
102 | default: "-",
103 | version: "",
104 | },
105 | {
106 | key: "12",
107 | property: "reactDatePickerProps",
108 | description:
109 | "An override react-datepicker properties. See more (https://reactdatepicker.com/ or https://github.com/Hacker0x01/react-datepicker/blob/master/docs/datepicker.md)",
110 | type: "Object",
111 | default: "-",
112 | version: "",
113 | },
114 | {
115 | key: "13",
116 | property: "minDate",
117 | description:
118 | "A config minimum selectable date. To use, you can provide the string like `2023-01-31`. (Note: It's will depend on yearBoundary too.)",
119 | type: "string",
120 | default: "-",
121 | version: "",
122 | },
123 | {
124 | key: "14",
125 | property: "maxDate",
126 | description:
127 | "A config maximum selectable date. To use, you can provide the string like `2023-12-31`. (Note: It's will depend on yearBoundary too.)",
128 | type: "string",
129 | default: "-",
130 | version: "",
131 | },
132 | {
133 | key: "15",
134 | property: "highlightDates",
135 | description:
136 | 'A highlight selected date. To use, you can provide an array of Date like `["new Date()"]`',
137 | type: "Date[]",
138 | default: "-",
139 | version: "",
140 | },
141 | {
142 | key: "16",
143 | property: "customInput",
144 | description:
145 | "A config for using custom input element. To use, you can provide a name of element like `Input`",
146 | type: "any",
147 | default: "-",
148 | version: "",
149 | },
150 | ];
151 | export const propsColumns = [
152 | {
153 | title: "Property",
154 | dataIndex: "property",
155 | key: "property",
156 | className: "font-bold",
157 | fixed: "left",
158 | },
159 | {
160 | title: "Description",
161 | dataIndex: "description",
162 | key: "description",
163 | },
164 | {
165 | title: "Type",
166 | dataIndex: "type",
167 | key: "type",
168 | className: "text-purple-600",
169 | },
170 | {
171 | title: "Default",
172 | dataIndex: "default",
173 | key: "default",
174 | },
175 | {
176 | title: "Version",
177 | dataIndex: "version",
178 | key: "version",
179 | },
180 | ];
181 | export const basicSrcCode = `import React, { useState } from "react";
182 | import { ThaiDatePicker } from "thaidatepicker-react";
183 |
184 | const App = () => {
185 | const [selectedDate, setSelectedDate] = useState();
186 | // const [selectedThaiDate, setSelectedThaiDate] = useState();
187 |
188 | const handleDatePickerChange = (christDate, buddhistDate) => {
189 | console.log({ christDate, buddhistDate });
190 | setSelectedDate(christDate);
191 | // setSelectedThaiDate(buddhistDate);
192 | };
193 |
194 | return (
195 |
196 |
197 |
198 | );
199 | };
200 |
201 | export default App;`;
202 |
203 | export const easySrcCode = ` `;
210 |
--------------------------------------------------------------------------------
/example/with-vite-antd-tailwind/src/utils/function.js:
--------------------------------------------------------------------------------
1 | export const GetHash = () => {
2 | return location.hash;
3 | };
4 | export const ReplaceHash = (hash = "#hash") => {
5 | if (history.pushState) {
6 | history.pushState(null, null, hash);
7 | } else {
8 | location.hash = hash;
9 | }
10 | };
11 |
--------------------------------------------------------------------------------
/example/with-vite-antd-tailwind/vite.config.js:
--------------------------------------------------------------------------------
1 | import tailwindcss from "@tailwindcss/vite";
2 | import react from "@vitejs/plugin-react";
3 | import { resolve } from "path";
4 | import { defineConfig } from "vite";
5 |
6 | // https://vitejs.dev/config/
7 | export default defineConfig({
8 | build: {
9 | sourcemap: false,
10 | },
11 | resolve: {
12 | alias: {
13 | // '@': resolve(__dirname, './src'),
14 | apis: resolve(__dirname, "./src/apis"),
15 | assets: resolve(__dirname, "./src/assets"),
16 | components: resolve(__dirname, "./src/components"),
17 | layouts: resolve(__dirname, "./src/layouts"),
18 | pages: resolve(__dirname, "./src/pages"),
19 | utils: resolve(__dirname, "./src/utils"),
20 | },
21 | },
22 | plugins: [react(), tailwindcss()],
23 | });
24 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "thaidatepicker-react",
3 | "version": "2.1.2",
4 | "description": "Thaidatepicker-react is a component for ReactJS that likes other DatePicker, but all we need is Buddhist Year (25XX - aka Thai Year) come with the right render day (example: Sat, 29 Feb 2020 must be equal to Sat, 29 Feb 2563) so I wish this component will be a useful thing to you :D. Happy Coding.",
5 | "type": "module",
6 | "directories": {
7 | "doc": "docs",
8 | "example": "example"
9 | },
10 | "scripts": {
11 | "build": "yarn run clean && microbundle --format cjs,modern --css inline --jsx React.createElement",
12 | "clean": "rm -rf ./dist/* ./example/*/dist",
13 | "initial": "yarn install && cd ./example/with-vite-antd-tailwind && yarn install",
14 | "predeploy": "yarn run build && cd ./example/with-vite-antd-tailwind && yarn run build",
15 | "start": "yarn run clean && microbundle watch --no-compress --format cjs,modern --css inline --jsx React.createElement",
16 | "test": "cross-env CI=1 tsc && react-scripts test --env=jsdom",
17 | "test-coverage": "cross-env CI=1 tsc && react-scripts test --env=jsdom --coverage"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/buildingwatsize/thaidatepicker-react.git"
22 | },
23 | "keywords": [
24 | "react",
25 | "datepicker",
26 | "buddhist",
27 | "thai",
28 | "leap",
29 | "year",
30 | "date"
31 | ],
32 | "author": "buildingwatsize",
33 | "license": "MIT",
34 | "bugs": {
35 | "url": "https://github.com/buildingwatsize/thaidatepicker-react/issues"
36 | },
37 | "homepage": "https://github.com/buildingwatsize/thaidatepicker-react#readme",
38 | "dependencies": {
39 | "dayjs": "^1.11.13",
40 | "react-datepicker": "^8.0.0"
41 | },
42 | "devDependencies": {
43 | "@testing-library/dom": "^10.4.0",
44 | "@testing-library/jest-dom": "^6.6.3",
45 | "@testing-library/react": "^16.2.0",
46 | "@types/jest": "^29.5.14",
47 | "@types/react": "^19.0.8",
48 | "@types/react-dom": "^19.0.3",
49 | "clsx": "^2.1.1",
50 | "cross-env": "^7.0.3",
51 | "microbundle": "^0.15.1",
52 | "mockdate": "^3.0.5",
53 | "react": "^19.0.0",
54 | "react-dom": "^19.0.0",
55 | "react-scripts": "^5.0.1",
56 | "tailwind-merge": "^3.0.1",
57 | "typescript": "^5.7.3"
58 | },
59 | "peerDependencies": {
60 | "react": "^18.0.0 || ^19.0.0"
61 | },
62 | "files": [
63 | "dist"
64 | ],
65 | "source": "src/index.ts",
66 | "main": "dist/index.cjs",
67 | "module": "dist/index.modern.js",
68 | "types": "dist/index.d.ts"
69 | }
--------------------------------------------------------------------------------
/src/components/CustomHeader.tsx:
--------------------------------------------------------------------------------
1 | import dayjs from "dayjs";
2 | import React, { PropsWithChildren } from "react";
3 | import { type ReactDatePickerCustomHeaderProps } from "react-datepicker";
4 |
5 | import { THAI_MONTH_LIST } from "../config/constants";
6 | import { cn, ConvertToThaiYear } from "../utils";
7 | import NavigateButton from "./NavigateButton";
8 | import NavigateSelect from "./NavigateSelect";
9 |
10 | export const HeaderContainer: React.FC = ({
11 | className = "",
12 | children,
13 | }: PropsWithChildren<{ className?: string }>) => (
14 | {children}
15 | );
16 |
17 | export const CustomHeader = (
18 | prevButtonIcon: React.ReactNode = "<",
19 | nextButtonIcon: React.ReactNode = ">",
20 | prevButtonClassName: string = "",
21 | nextButtonClassName: string = "",
22 | monthSelectClassName: string = "",
23 | yearSelectClassName: string = "",
24 | yearOptions: Array = []
25 | ) => {
26 | return ({
27 | date,
28 | changeYear,
29 | changeMonth,
30 | decreaseMonth,
31 | increaseMonth,
32 | prevMonthButtonDisabled,
33 | nextMonthButtonDisabled,
34 | }: ReactDatePickerCustomHeaderProps) => {
35 | return (
36 |
37 |
42 | {prevButtonIcon}
43 |
44 |
45 |
49 | changeMonth(THAI_MONTH_LIST.indexOf(target.value))
50 | }
51 | >
52 | {THAI_MONTH_LIST.map((option) => (
53 |
54 | {option}
55 |
56 | ))}
57 |
58 |
59 | changeYear(Number(target.value))}
63 | >
64 | {yearOptions.map((option) => (
65 |
66 | {ConvertToThaiYear(option)}
67 |
68 | ))}
69 |
70 |
71 |
76 | {nextButtonIcon}
77 |
78 |
79 | );
80 | };
81 | };
82 |
--------------------------------------------------------------------------------
/src/components/CustomInputWrapped.tsx:
--------------------------------------------------------------------------------
1 | import dayjs from "dayjs";
2 | import React, { ForwardedRef, forwardRef, RefObject, useMemo } from "react";
3 | import { ConvertToThaiYear } from "../utils";
4 |
5 | const isReact19OrNewer =
6 | parseInt(`${React.version}.`.split(".").at(0) ?? "", 10) >= 19;
7 |
8 | const componentWithRefBinding = (
9 | combinedProps: any,
10 | ref: RefObject | ForwardedRef,
11 | InputComponent?: React.ElementType | null
12 | ) => {
13 | const { _displayFormat, value, onChange, ...props } = combinedProps;
14 | const thaiDate = useMemo(() => {
15 | if (value) {
16 | const date = dayjs(value);
17 | const thaiYear = ConvertToThaiYear(date.year());
18 | if (_displayFormat) {
19 | let wrappedDisplayFormat = _displayFormat
20 | .replace(/YYYY/, `${thaiYear}`)
21 | .replace(/YY/, `${thaiYear % 100}`);
22 | return `${date.format(wrappedDisplayFormat)}`;
23 | }
24 | return `${thaiYear}${date.format("-MM-DD")}`;
25 | }
26 | return "";
27 | }, [value]);
28 |
29 | if (InputComponent) {
30 | return (
31 |
37 | );
38 | }
39 | return (
40 |
47 | );
48 | };
49 | export const CustomInputWrapped = (
50 | InputComponent?: React.ElementType | null,
51 | inputProps?: React.InputHTMLAttributes,
52 | displayFormat?: string
53 | ) => {
54 | if (isReact19OrNewer) {
55 | return ({ ref, ...props }: any) =>
56 | componentWithRefBinding(
57 | { ...props, ...inputProps, _displayFormat: displayFormat },
58 | ref,
59 | InputComponent
60 | );
61 | }
62 | return forwardRef((props, ref) =>
63 | componentWithRefBinding(
64 | { ...props, ...inputProps, _displayFormat: displayFormat },
65 | ref,
66 | InputComponent
67 | )
68 | );
69 | };
70 |
--------------------------------------------------------------------------------
/src/components/NavigateButton.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { cn } from "../utils";
3 |
4 | const NavigateButton = ({
5 | className,
6 | type = "button",
7 | disabled,
8 | onClick,
9 | children,
10 | ...props
11 | }: React.ComponentProps<"button">) => {
12 | return (
13 |
20 | {children}
21 |
22 | );
23 | };
24 |
25 | export default NavigateButton;
26 |
--------------------------------------------------------------------------------
/src/components/NavigateSelect.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { cn } from "../utils";
3 |
4 | const NavigateSelect = ({
5 | className,
6 | value,
7 | onChange,
8 | children,
9 | ...props
10 | }: React.SelectHTMLAttributes) => {
11 | return (
12 |
18 | {children}
19 |
20 | );
21 | };
22 |
23 | export default NavigateSelect;
24 |
--------------------------------------------------------------------------------
/src/components/index.test.tsx:
--------------------------------------------------------------------------------
1 | import { fireEvent, render, screen } from "@testing-library/react";
2 | import { ThaiDatePicker } from ".";
3 |
4 | describe("ThaiDatePicker", () => {
5 | test("should be truthy", () => {
6 | expect(ThaiDatePicker).toBeTruthy();
7 | });
8 | test("should be visible", () => {
9 | let view = render( );
10 | expect(view).not.toBeNull();
11 | });
12 | test("should be render with input inside", () => {
13 | render( );
14 | const inputElement = screen.queryByTestId("thdpk-input");
15 | expect(inputElement).toBeInTheDocument();
16 | });
17 | test("should be render with minDate, maxDate props", () => {
18 | render( );
19 | const inputElement = screen.queryByTestId("thdpk-input");
20 | expect(inputElement).toBeInTheDocument();
21 | });
22 | test("should have value as ThaiDate format", () => {
23 | [
24 | {
25 | christDateInput: "1980-08-11",
26 | thaiDateOutput: "2523-08-11",
27 | },
28 | {
29 | christDateInput: "2030-12-31",
30 | thaiDateOutput: "2573-12-31",
31 | },
32 | {
33 | christDateInput: "2525-01-31",
34 | thaiDateOutput: "3068-01-31",
35 | },
36 | {
37 | christDateInput: "",
38 | thaiDateOutput: "",
39 | },
40 | ].forEach((tc) => {
41 | const { unmount } = render( );
42 | const inputElement = screen.queryByTestId(
43 | "thdpk-input"
44 | ) as HTMLInputElement;
45 | expect(inputElement?.value).toBe(tc.thaiDateOutput);
46 | unmount();
47 | });
48 | });
49 | test("should be have value showing format as inputProps.displayFormat (DD MM YYYY)", () => {
50 | render(
51 |
55 | );
56 | const inputElement = screen.queryByTestId(
57 | "thdpk-input"
58 | ) as HTMLInputElement;
59 | expect(inputElement.value).toBe("31 01 2566");
60 | });
61 | test("should be have value showing format as inputProps.displayFormat (DD M YY)", () => {
62 | render(
63 |
67 | );
68 | const inputElement = screen.queryByTestId(
69 | "thdpk-input"
70 | ) as HTMLInputElement;
71 | expect(inputElement.value).toBe("02 1 43");
72 | });
73 | test("should be changeable value", () => {
74 | const mockOnChange = jest.fn();
75 | render( );
76 | const inputElement = screen.queryByTestId(
77 | "thdpk-input"
78 | ) as HTMLInputElement;
79 | inputElement.addEventListener("change", mockOnChange);
80 |
81 | fireEvent.change(inputElement, { target: { value: "2023-12-31" } });
82 | expect(mockOnChange).toHaveBeenCalledTimes(1);
83 | expect(inputElement.value).toBe("2566-12-31");
84 |
85 | fireEvent.change(inputElement, { target: { value: "1999-12-30" } });
86 | expect(inputElement.value).toBe("2542-12-30");
87 | });
88 | test("should be have customize an input", () => {
89 | let interceptInputValue = "";
90 | const FakeInput = ({
91 | value,
92 | onChange,
93 | }: {
94 | value: string;
95 | onChange: (e: React.ChangeEvent) => void;
96 | }) => {
97 | interceptInputValue = value;
98 | return (
99 |
100 | );
101 | };
102 | render( );
103 | const inputElement = screen.queryByTestId(
104 | "thdpk-input"
105 | ) as HTMLInputElement;
106 | expect(inputElement.value).toBe("2566-01-31");
107 |
108 | expect(interceptInputValue).toBe(inputElement.value);
109 | });
110 | });
111 |
112 | // describe("WatDatePicker (Legacy name compatibility)", () => {
113 | // test("WatDatePicker to be truthy", () => {
114 | // expect(WatDatePicker).toBeTruthy();
115 | // });
116 | // test("WatDatePicker can be render with input inside", () => {
117 | // render( );
118 | // const inputElement = screen.queryByTestId("thdpk-input");
119 | // expect(inputElement).toBeInTheDocument();
120 | // });
121 | // test("should be have value showing format as inputProps.displayFormat (DD MM YYYY)", () => {
122 | // render(
123 | //
127 | // );
128 | // const inputElement = screen.queryByTestId(
129 | // "thdpk-input"
130 | // ) as HTMLInputElement;
131 | // expect(inputElement.value).toBe("31 01 2566");
132 | // });
133 | // });
134 |
--------------------------------------------------------------------------------
/src/components/index.tsx:
--------------------------------------------------------------------------------
1 | import dayjs from "dayjs";
2 | import "dayjs/locale/th";
3 | import buddhistEra from "dayjs/plugin/buddhistEra";
4 | import React, { useMemo, useRef, useState } from "react";
5 | import DatePicker, { registerLocale, setDefaultLocale } from "react-datepicker";
6 |
7 | import th from "../config/locale/th";
8 | import { useStylesheet } from "../hooks/useStylesheet";
9 | import { ConvertToThaiYear, GetHighlightByDate } from "../utils";
10 | import YearListGenerator from "../utils/YearListGenerator";
11 | import { CustomHeader } from "./CustomHeader";
12 | import { CustomInputWrapped } from "./CustomInputWrapped";
13 |
14 | const locale = "th";
15 | registerLocale(locale, th);
16 | setDefaultLocale(locale);
17 |
18 | dayjs.locale(locale);
19 | dayjs.extend(buddhistEra);
20 |
21 | const yearGenerator = new YearListGenerator(dayjs);
22 |
23 | const ReactDatePicker =
24 | (DatePicker as unknown as { default: typeof DatePicker }).default ??
25 | DatePicker;
26 |
27 | export interface HighlightDate {
28 | [className: string]: Date[];
29 | }
30 | export interface ThaiDatePickerProps {
31 | children?: React.ReactNode | null;
32 | clearable?: boolean;
33 | customInput?: React.ElementType | null;
34 | disabled?: boolean;
35 | header?: {
36 | prevButtonIcon?: React.ReactNode;
37 | nextButtonIcon?: React.ReactNode;
38 | prevButtonClassName?: string;
39 | nextButtonClassName?: string;
40 | monthSelectClassName?: string;
41 | yearSelectClassName?: string;
42 | } | null;
43 | highlightDates?: (Date | HighlightDate)[];
44 | id?: string;
45 | inputProps?:
46 | | (any & {
47 | displayFormat?: string;
48 | })
49 | | null;
50 | maxDate?: Date | string;
51 | minDate?: Date | string;
52 | noIntegratedStyle?: boolean;
53 | onChange?: (christDate: string, thaiDate: string) => void;
54 | placeholder?: string;
55 | reactDatePickerProps?: React.ComponentProps;
56 | readOnly?: boolean;
57 | value?: string;
58 | yearBoundary?: number;
59 | }
60 |
61 | const defaultProps = {
62 | children: null,
63 | clearable: true,
64 | customInput: null,
65 | disabled: false,
66 | header: null,
67 | highlightDates: GetHighlightByDate(),
68 | id: "thdpk-container",
69 | inputProps: null,
70 | maxDate: undefined,
71 | minDate: undefined,
72 | noIntegratedStyle: false,
73 | onChange: (_christDate: string, _thaiDate: string) => null,
74 | placeholder: "",
75 | reactDatePickerProps: {},
76 | readOnly: false,
77 | value: "",
78 | yearBoundary: 99,
79 | };
80 |
81 | export const ThaiDatePicker = ({
82 | children = defaultProps.children,
83 | clearable = defaultProps.clearable,
84 | customInput = defaultProps.customInput,
85 | disabled = defaultProps.disabled,
86 | header = defaultProps.header,
87 | highlightDates = defaultProps.highlightDates,
88 | id = defaultProps.id,
89 | inputProps = defaultProps.inputProps,
90 | maxDate = defaultProps.maxDate,
91 | minDate = defaultProps.minDate,
92 | noIntegratedStyle = defaultProps.noIntegratedStyle,
93 | onChange = defaultProps.onChange,
94 | placeholder = defaultProps.placeholder,
95 | reactDatePickerProps = defaultProps.reactDatePickerProps,
96 | readOnly = defaultProps.readOnly,
97 | value = defaultProps.value,
98 | yearBoundary = defaultProps.yearBoundary,
99 | }: ThaiDatePickerProps) => {
100 | const isClearable = !(disabled || readOnly) && (clearable ?? true);
101 | const {
102 | prevButtonIcon,
103 | nextButtonIcon,
104 | prevButtonClassName,
105 | nextButtonClassName,
106 | monthSelectClassName,
107 | yearSelectClassName,
108 | } = header ?? {};
109 |
110 | const datePickerRef = useRef(null);
111 | useStylesheet(datePickerRef, !noIntegratedStyle);
112 |
113 | const [selectedDate, setSelectedDate] = useState(
114 | value ? dayjs(value) : null
115 | );
116 |
117 | const valueAsDate = useMemo(() => {
118 | if (value) {
119 | return new Date(value);
120 | } else if (value === "") {
121 | return null;
122 | }
123 | return selectedDate ? new Date(selectedDate.toDate()) : null;
124 | }, [value, selectedDate]);
125 | const minDateAsDate = useMemo(
126 | () => (minDate ? new Date(minDate) : undefined),
127 | [minDate]
128 | );
129 | const maxDateAsDate = useMemo(
130 | () => (maxDate ? new Date(maxDate) : undefined),
131 | [maxDate]
132 | );
133 | const YearOptions = useMemo(
134 | () => yearGenerator.Generate(yearBoundary, minDateAsDate, maxDateAsDate),
135 | [yearBoundary, minDateAsDate, maxDateAsDate]
136 | );
137 | const CustomInput = useMemo(() => {
138 | const { displayFormat, style, ...remainInputProps } = inputProps ?? {};
139 | return CustomInputWrapped(
140 | customInput,
141 | {
142 | ...remainInputProps,
143 | style: { width: "100%", ...style },
144 | },
145 | displayFormat
146 | );
147 | }, [customInput, inputProps]);
148 |
149 | const handleChange = (date: any) => {
150 | const dayjsObj = dayjs(date);
151 | if (dayjsObj.isValid()) {
152 | setSelectedDate(dayjsObj);
153 | let christDate = dayjsObj.format("YYYY-MM-DD");
154 | let thaiDate = `${ConvertToThaiYear(dayjsObj.year())}${dayjsObj.format(
155 | "-MM-DD"
156 | )}`;
157 | onChange?.(christDate, thaiDate);
158 | return;
159 | }
160 | onChange?.("", "");
161 | };
162 |
163 | return (
164 |
165 |
188 | {children}
189 |
190 |
191 | );
192 | };
193 |
--------------------------------------------------------------------------------
/src/config/constants.ts:
--------------------------------------------------------------------------------
1 | export const THAI_MONTH_LIST: Array = [
2 | "มกราคม",
3 | "กุมภาพันธ์",
4 | "มีนาคม",
5 | "เมษายน",
6 | "พฤษภาคม",
7 | "มิถุนายน",
8 | "กรกฎาคม",
9 | "สิงหาคม",
10 | "กันยายน",
11 | "ตุลาคม",
12 | "พฤศจิกายน",
13 | "ธันวาคม",
14 | ];
15 |
--------------------------------------------------------------------------------
/src/config/locale/_lib/buildFormatLongFn/index.js:
--------------------------------------------------------------------------------
1 | export default function buildFormatLongFn(args) {
2 | return function (dirtyOptions) {
3 | var options = dirtyOptions || {};
4 | var width = options.width ? String(options.width) : args.defaultWidth;
5 | var format = args.formats[width] || args.formats[args.defaultWidth];
6 | return format;
7 | };
8 | }
--------------------------------------------------------------------------------
/src/config/locale/_lib/buildLocalizeFn/index.js:
--------------------------------------------------------------------------------
1 | export default function buildLocalizeFn(args) {
2 | return function (dirtyIndex, dirtyOptions) {
3 | var options = dirtyOptions || {};
4 | var context = options.context ? String(options.context) : 'standalone';
5 | var valuesArray;
6 |
7 | if (context === 'formatting' && args.formattingValues) {
8 | var defaultWidth = args.defaultFormattingWidth || args.defaultWidth;
9 | var width = options.width ? String(options.width) : defaultWidth;
10 | valuesArray = args.formattingValues[width] || args.formattingValues[defaultWidth];
11 | } else {
12 | var _defaultWidth = args.defaultWidth;
13 |
14 | var _width = options.width ? String(options.width) : args.defaultWidth;
15 |
16 | valuesArray = args.values[_width] || args.values[_defaultWidth];
17 | }
18 |
19 | var index = args.argumentCallback ? args.argumentCallback(dirtyIndex) : dirtyIndex;
20 | return valuesArray[index];
21 | };
22 | }
--------------------------------------------------------------------------------
/src/config/locale/_lib/buildMatchFn/index.js:
--------------------------------------------------------------------------------
1 | export default function buildMatchFn(args) {
2 | return function (dirtyString, dirtyOptions) {
3 | var string = String(dirtyString);
4 | var options = dirtyOptions || {};
5 | var width = options.width;
6 | var matchPattern = (width && args.matchPatterns[width]) || args.matchPatterns[args.defaultMatchWidth];
7 | var matchResult = string.match(matchPattern);
8 |
9 | if (!matchResult) {
10 | return null;
11 | }
12 |
13 | var matchedString = matchResult[0];
14 | var parsePatterns = (width && args.parsePatterns[width]) || args.parsePatterns[args.defaultParseWidth];
15 | var value;
16 |
17 | if (Object.prototype.toString.call(parsePatterns) === '[object Array]') {
18 | value = findIndex(parsePatterns, function (pattern) {
19 | return pattern.test(matchedString);
20 | });
21 | } else {
22 | value = findKey(parsePatterns, function (pattern) {
23 | return pattern.test(matchedString);
24 | });
25 | }
26 |
27 | value = args.valueCallback ? args.valueCallback(value) : value;
28 | value = options.valueCallback ? options.valueCallback(value) : value;
29 | return {
30 | value: value,
31 | rest: string.slice(matchedString.length)
32 | };
33 | };
34 | }
35 |
36 | function findKey(object, predicate) {
37 | for (var key in object) {
38 | if (object.hasOwnProperty(key) && predicate(object[key])) {
39 | return key;
40 | }
41 | }
42 | }
43 |
44 | function findIndex(array, predicate) {
45 | for (var key = 0; key < array.length; key++) {
46 | if (predicate(array[key])) {
47 | return key;
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/src/config/locale/_lib/buildMatchPatternFn/index.js:
--------------------------------------------------------------------------------
1 | export default function buildMatchPatternFn(args) {
2 | return function (dirtyString, dirtyOptions) {
3 | var string = String(dirtyString);
4 | var options = dirtyOptions || {};
5 | var matchResult = string.match(args.matchPattern);
6 |
7 | if (!matchResult) {
8 | return null;
9 | }
10 |
11 | var matchedString = matchResult[0];
12 | var parseResult = string.match(args.parsePattern);
13 |
14 | if (!parseResult) {
15 | return null;
16 | }
17 |
18 | var value = args.valueCallback ? args.valueCallback(parseResult[0]) : parseResult[0];
19 | value = options.valueCallback ? options.valueCallback(value) : value;
20 | return {
21 | value: value,
22 | rest: string.slice(matchedString.length)
23 | };
24 | };
25 | }
--------------------------------------------------------------------------------
/src/config/locale/esm-locale-th/_lib/formatDistance/index.js:
--------------------------------------------------------------------------------
1 | var formatDistanceLocale = {
2 | lessThanXSeconds: {
3 | one: 'น้อยกว่า 1 วินาที',
4 | other: 'น้อยกว่า {{count}} วินาที'
5 | },
6 | xSeconds: {
7 | one: '1 วินาที',
8 | other: '{{count}} วินาที'
9 | },
10 | halfAMinute: 'ครึ่งนาที',
11 | lessThanXMinutes: {
12 | one: 'น้อยกว่า 1 นาที',
13 | other: 'น้อยกว่า {{count}} นาที'
14 | },
15 | xMinutes: {
16 | one: '1 นาที',
17 | other: '{{count}} นาที'
18 | },
19 | aboutXHours: {
20 | one: 'ประมาณ 1 ชั่วโมง',
21 | other: 'ประมาณ {{count}} ชั่วโมง'
22 | },
23 | xHours: {
24 | one: '1 ชั่วโมง',
25 | other: '{{count}} ชั่วโมง'
26 | },
27 | xDays: {
28 | one: '1 วัน',
29 | other: '{{count}} วัน'
30 | },
31 | aboutXMonths: {
32 | one: 'ประมาณ 1 เดือน',
33 | other: 'ประมาณ {{count}} เดือน'
34 | },
35 | xMonths: {
36 | one: '1 เดือน',
37 | other: '{{count}} เดือน'
38 | },
39 | aboutXYears: {
40 | one: 'ประมาณ 1 ปี',
41 | other: 'ประมาณ {{count}} ปี'
42 | },
43 | xYears: {
44 | one: '1 ปี',
45 | other: '{{count}} ปี'
46 | },
47 | overXYears: {
48 | one: 'มากกว่า 1 ปี',
49 | other: 'มากกว่า {{count}} ปี'
50 | },
51 | almostXYears: {
52 | one: 'เกือบ 1 ปี',
53 | other: 'เกือบ {{count}} ปี'
54 | }
55 | };
56 | export default function formatDistance(token, count, options) {
57 | options = options || {};
58 | var result;
59 |
60 | if (typeof formatDistanceLocale[token] === 'string') {
61 | result = formatDistanceLocale[token];
62 | } else if (count === 1) {
63 | result = formatDistanceLocale[token].one;
64 | } else {
65 | result = formatDistanceLocale[token].other.replace('{{count}}', count);
66 | }
67 |
68 | if (options.addSuffix) {
69 | if (options.comparison > 0) {
70 | if (token === 'halfAMinute') {
71 | return 'ใน' + result;
72 | } else {
73 | return 'ใน ' + result;
74 | }
75 | } else {
76 | return result + 'ที่ผ่านมา';
77 | }
78 | }
79 |
80 | return result;
81 | }
--------------------------------------------------------------------------------
/src/config/locale/esm-locale-th/_lib/formatLong/index.js:
--------------------------------------------------------------------------------
1 | import buildFormatLongFn from '../../../_lib/buildFormatLongFn/index.js';
2 | var dateFormats = {
3 | full: 'วันEEEEที่ do MMMM y',
4 | long: 'do MMMM y',
5 | medium: 'd MMM y',
6 | short: 'dd/MM/yyyy'
7 | };
8 | var timeFormats = {
9 | full: 'H:mm:ss น. zzzz',
10 | long: 'H:mm:ss น. z',
11 | medium: 'H:mm:ss น.',
12 | short: 'H:mm น.'
13 | };
14 | var dateTimeFormats = {
15 | full: "{{date}} 'เวลา' {{time}}",
16 | long: "{{date}} 'เวลา' {{time}}",
17 | medium: '{{date}}, {{time}}',
18 | short: '{{date}}, {{time}}'
19 | };
20 | var formatLong = {
21 | date: buildFormatLongFn({
22 | formats: dateFormats,
23 | defaultWidth: 'full'
24 | }),
25 | time: buildFormatLongFn({
26 | formats: timeFormats,
27 | defaultWidth: 'medium'
28 | }),
29 | dateTime: buildFormatLongFn({
30 | formats: dateTimeFormats,
31 | defaultWidth: 'full'
32 | })
33 | };
34 | export default formatLong;
--------------------------------------------------------------------------------
/src/config/locale/esm-locale-th/_lib/formatRelative/index.js:
--------------------------------------------------------------------------------
1 | var formatRelativeLocale = {
2 | lastWeek: "eeee'ที่แล้วเวลา' p",
3 | yesterday: "'เมื่อวานนี้เวลา' p",
4 | today: "'วันนี้เวลา' p",
5 | tomorrow: "'พรุ่งนี้เวลา' p",
6 | nextWeek: "eeee 'เวลา' p",
7 | other: 'P'
8 | };
9 | export default function formatRelative(token, _date, _baseDate, _options) {
10 | return formatRelativeLocale[token];
11 | }
--------------------------------------------------------------------------------
/src/config/locale/esm-locale-th/_lib/localize/index.js:
--------------------------------------------------------------------------------
1 | import buildLocalizeFn from '../../../_lib/buildLocalizeFn/index.js';
2 | var eraValues = {
3 | narrow: ['B', 'คศ'],
4 | abbreviated: ['BC', 'ค.ศ.'],
5 | wide: ['ปีก่อนคริสตกาล', 'คริสต์ศักราช']
6 | };
7 | var quarterValues = {
8 | narrow: ['1', '2', '3', '4'],
9 | abbreviated: ['Q1', 'Q2', 'Q3', 'Q4'],
10 | wide: ['ไตรมาสแรก', 'ไตรมาสที่สอง', 'ไตรมาสที่สาม', 'ไตรมาสที่สี่']
11 | };
12 | var dayValues = {
13 | narrow: ['อา.', 'จ.', 'อ.', 'พ.', 'พฤ.', 'ศ.', 'ส.'],
14 | short: ['อา.', 'จ.', 'อ.', 'พ.', 'พฤ.', 'ศ.', 'ส.'],
15 | abbreviated: ['อา.', 'จ.', 'อ.', 'พ.', 'พฤ.', 'ศ.', 'ส.'],
16 | wide: ['อาทิตย์', 'จันทร์', 'อังคาร', 'พุธ', 'พฤหัสบดี', 'ศุกร์', 'เสาร์']
17 | };
18 | var monthValues = {
19 | narrow: ['ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.', 'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.', 'ก.ย.', 'ต.ค.', 'พ.ย.', 'ธ.ค.'],
20 | abbreviated: ['ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.', 'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.', 'ก.ย.', 'ต.ค.', 'พ.ย.', 'ธ.ค.'],
21 | wide: ['มกราคม', 'กุมภาพันธ์', 'มีนาคม', 'เมษายน', 'พฤษภาคม', 'มิถุนายน', 'กรกฎาคม', 'สิงหาคม', 'กันยายน', 'ตุลาคม', 'พฤศจิกายน', 'ธันวาคม']
22 | };
23 | var dayPeriodValues = {
24 | narrow: {
25 | am: 'ก่อนเที่ยง',
26 | pm: 'หลังเที่ยง',
27 | midnight: 'เที่ยงคืน',
28 | noon: 'เที่ยง',
29 | morning: 'เช้า',
30 | afternoon: 'บ่าย',
31 | evening: 'เย็น',
32 | night: 'กลางคืน'
33 | },
34 | abbreviated: {
35 | am: 'ก่อนเที่ยง',
36 | pm: 'หลังเที่ยง',
37 | midnight: 'เที่ยงคืน',
38 | noon: 'เที่ยง',
39 | morning: 'เช้า',
40 | afternoon: 'บ่าย',
41 | evening: 'เย็น',
42 | night: 'กลางคืน'
43 | },
44 | wide: {
45 | am: 'ก่อนเที่ยง',
46 | pm: 'หลังเที่ยง',
47 | midnight: 'เที่ยงคืน',
48 | noon: 'เที่ยง',
49 | morning: 'เช้า',
50 | afternoon: 'บ่าย',
51 | evening: 'เย็น',
52 | night: 'กลางคืน'
53 | }
54 | };
55 | var formattingDayPeriodValues = {
56 | narrow: {
57 | am: 'ก่อนเที่ยง',
58 | pm: 'หลังเที่ยง',
59 | midnight: 'เที่ยงคืน',
60 | noon: 'เที่ยง',
61 | morning: 'ตอนเช้า',
62 | afternoon: 'ตอนกลางวัน',
63 | evening: 'ตอนเย็น',
64 | night: 'ตอนกลางคืน'
65 | },
66 | abbreviated: {
67 | am: 'ก่อนเที่ยง',
68 | pm: 'หลังเที่ยง',
69 | midnight: 'เที่ยงคืน',
70 | noon: 'เที่ยง',
71 | morning: 'ตอนเช้า',
72 | afternoon: 'ตอนกลางวัน',
73 | evening: 'ตอนเย็น',
74 | night: 'ตอนกลางคืน'
75 | },
76 | wide: {
77 | am: 'ก่อนเที่ยง',
78 | pm: 'หลังเที่ยง',
79 | midnight: 'เที่ยงคืน',
80 | noon: 'เที่ยง',
81 | morning: 'ตอนเช้า',
82 | afternoon: 'ตอนกลางวัน',
83 | evening: 'ตอนเย็น',
84 | night: 'ตอนกลางคืน'
85 | }
86 | };
87 |
88 | function ordinalNumber(dirtyNumber) {
89 | var number = Number(dirtyNumber);
90 | return number;
91 | }
92 |
93 | var localize = {
94 | ordinalNumber: ordinalNumber,
95 | era: buildLocalizeFn({
96 | values: eraValues,
97 | defaultWidth: 'wide'
98 | }),
99 | quarter: buildLocalizeFn({
100 | values: quarterValues,
101 | defaultWidth: 'wide',
102 | argumentCallback: function (quarter) {
103 | return Number(quarter) - 1;
104 | }
105 | }),
106 | month: buildLocalizeFn({
107 | values: monthValues,
108 | defaultWidth: 'wide'
109 | }),
110 | day: buildLocalizeFn({
111 | values: dayValues,
112 | defaultWidth: 'wide'
113 | }),
114 | dayPeriod: buildLocalizeFn({
115 | values: dayPeriodValues,
116 | defaultWidth: 'wide',
117 | formattingValues: formattingDayPeriodValues,
118 | defaultFormattingWidth: 'wide'
119 | })
120 | };
121 | export default localize;
--------------------------------------------------------------------------------
/src/config/locale/esm-locale-th/_lib/match/index.js:
--------------------------------------------------------------------------------
1 | import buildMatchPatternFn from '../../../_lib/buildMatchPatternFn/index.js';
2 | import buildMatchFn from '../../../_lib/buildMatchFn/index.js';
3 | var matchOrdinalNumberPattern = /^\d+/i;
4 | var parseOrdinalNumberPattern = /\d+/i;
5 | var matchEraPatterns = {
6 | narrow: /^([bB]|[aA]|คศ)/i,
7 | abbreviated: /^([bB]\.?\s?[cC]\.?|b\.?\s?c\.?\s?e\.?|a\.?\s?d\.?|c\.?\s?e\.?|ค\.?ศ\.?)/i,
8 | wide: /^(ก่อนคริสตกาล|คริสต์ศักราช|คริสตกาล)/i
9 | };
10 | var parseEraPatterns = {
11 | any: [/^[bB]/i, /^(^[aA]|ค\.?ศ\.?|คริสตกาล|คริสต์ศักราช|)/i]
12 | };
13 | var matchQuarterPatterns = {
14 | narrow: /^[1234]/i,
15 | abbreviated: /^q[1234]/i,
16 | wide: /^ไตรมาส(ที่)? ?[1234]/i
17 | };
18 | var parseQuarterPatterns = {
19 | any: [/(1|แรก|หนึ่ง)/i, /(2|สอง)/i, /(3|สาม)/i, /(4|สี่)/i]
20 | };
21 | var matchMonthPatterns = {
22 | narrow: /^(ม\.?ค\.?|ก\.?พ\.?|มี\.?ค\.?|เม\.?ย\.?|พ\.?ค\.?|มิ\.?ย\.?|ก\.?ค\.?|ส\.?ค\.?|ก\.?ย\.?|ต\.?ค\.?|พ\.?ย\.?|ธ\.?ค\.?)/i,
23 | abbreviated: /^(ม\.?ค\.?|ก\.?พ\.?|มี\.?ค\.?|เม\.?ย\.?|พ\.?ค\.?|มิ\.?ย\.?|ก\.?ค\.?|ส\.?ค\.?|ก\.?ย\.?|ต\.?ค\.?|พ\.?ย\.?|ธ\.?ค\.?')/i,
24 | wide: /^(มกราคม|กุมภาพันธ์|มีนาคม|เมษายน|พฤษภาคม|มิถุนายน|กรกฎาคม|สิงหาคม|กันยายน|ตุลาคม|พฤศจิกายน|ธันวาคม)/i
25 | };
26 | var parseMonthPatterns = {
27 | wide: [/^มก/i, /^กุม/i, /^มี/i, /^เม/i, /^พฤษ/i, /^มิ/i, /^กรก/i, /^ส/i, /^กัน/i, /^ต/i, /^พฤศ/i, /^ธ/i],
28 | any: [/^ม\.?ค\.?/i, /^ก\.?พ\.?/i, /^มี\.?ค\.?/i, /^เม\.?ย\.?/i, /^พ\.?ค\.?/i, /^มิ\.?ย\.?/i, /^ก\.?ค\.?/i, /^ส\.?ค\.?/i, /^ก\.?ย\.?/i, /^ต\.?ค\.?/i, /^พ\.?ย\.?/i, /^ธ\.?ค\.?/i]
29 | };
30 | var matchDayPatterns = {
31 | narrow: /^(อา\.?|จ\.?|อ\.?|พฤ\.?|พ\.?|ศ\.?|ส\.?)/i,
32 | short: /^(อา\.?|จ\.?|อ\.?|พฤ\.?|พ\.?|ศ\.?|ส\.?)/i,
33 | abbreviated: /^(อา\.?|จ\.?|อ\.?|พฤ\.?|พ\.?|ศ\.?|ส\.?)/i,
34 | wide: /^(อาทิตย์|จันทร์|อังคาร|พุธ|พฤหัสบดี|ศุกร์|เสาร์)/i
35 | };
36 | var parseDayPatterns = {
37 | wide: [/^อา/i, /^จั/i, /^อั/i, /^พุธ/i, /^พฤ/i, /^ศ/i, /^เส/i],
38 | any: [/^อา/i, /^จ/i, /^อ/i, /^พ(?!ฤ)/i, /^พฤ/i, /^ศ/i, /^ส/i]
39 | };
40 | var matchDayPeriodPatterns = {
41 | any: /^(ก่อนเที่ยง|หลังเที่ยง|เที่ยงคืน|เที่ยง|(ตอน.*?)?.*(เที่ยง|เช้า|บ่าย|เย็น|กลางคืน))/i
42 | };
43 | var parseDayPeriodPatterns = {
44 | any: {
45 | am: /^ก่อนเที่ยง/i,
46 | pm: /^หลังเที่ยง/i,
47 | midnight: /^เที่ยงคืน/i,
48 | noon: /^เที่ยง/i,
49 | morning: /เช้า/i,
50 | afternoon: /บ่าย/i,
51 | evening: /เย็น/i,
52 | night: /กลางคืน/i
53 | }
54 | };
55 | var match = {
56 | ordinalNumber: buildMatchPatternFn({
57 | matchPattern: matchOrdinalNumberPattern,
58 | parsePattern: parseOrdinalNumberPattern,
59 | valueCallback: function (value) {
60 | return parseInt(value, 10);
61 | }
62 | }),
63 | era: buildMatchFn({
64 | matchPatterns: matchEraPatterns,
65 | defaultMatchWidth: 'wide',
66 | parsePatterns: parseEraPatterns,
67 | defaultParseWidth: 'any'
68 | }),
69 | quarter: buildMatchFn({
70 | matchPatterns: matchQuarterPatterns,
71 | defaultMatchWidth: 'wide',
72 | parsePatterns: parseQuarterPatterns,
73 | defaultParseWidth: 'any',
74 | valueCallback: function (index) {
75 | return index + 1;
76 | }
77 | }),
78 | month: buildMatchFn({
79 | matchPatterns: matchMonthPatterns,
80 | defaultMatchWidth: 'wide',
81 | parsePatterns: parseMonthPatterns,
82 | defaultParseWidth: 'any'
83 | }),
84 | day: buildMatchFn({
85 | matchPatterns: matchDayPatterns,
86 | defaultMatchWidth: 'wide',
87 | parsePatterns: parseDayPatterns,
88 | defaultParseWidth: 'any'
89 | }),
90 | dayPeriod: buildMatchFn({
91 | matchPatterns: matchDayPeriodPatterns,
92 | defaultMatchWidth: 'any',
93 | parsePatterns: parseDayPeriodPatterns,
94 | defaultParseWidth: 'any'
95 | })
96 | };
97 | export default match;
--------------------------------------------------------------------------------
/src/config/locale/esm-locale-th/index.d.ts:
--------------------------------------------------------------------------------
1 | // This file is generated automatically by `scripts/build/typings.js`. Please, don't change it.
2 |
3 | import { th } from 'date-fns/locale'
4 | export default th
5 |
--------------------------------------------------------------------------------
/src/config/locale/esm-locale-th/index.js:
--------------------------------------------------------------------------------
1 | import formatDistance from './_lib/formatDistance/index.js';
2 | import formatLong from './_lib/formatLong/index.js';
3 | import formatRelative from './_lib/formatRelative/index.js';
4 | import localize from './_lib/localize/index.js';
5 | import match from './_lib/match/index.js';
6 | /**
7 | * @type {Locale}
8 | * @category Locales
9 | * @summary Thai locale.
10 | * @language Thai
11 | * @iso-639-2 tha
12 | * @author Athiwat Hirunworawongkun [@athivvat]{@link https://github.com/athivvat}
13 | * @author [@hawkup]{@link https://github.com/hawkup}
14 | * @author Jirawat I. [@nodtem66]{@link https://github.com/nodtem66}
15 | */
16 |
17 | var locale = {
18 | code: 'th',
19 | formatDistance: formatDistance,
20 | formatLong: formatLong,
21 | formatRelative: formatRelative,
22 | localize: localize,
23 | match: match,
24 | options: {
25 | weekStartsOn: 0
26 | /* Sunday */
27 | ,
28 | firstWeekContainsDate: 1
29 | }
30 | };
31 | export default locale;
--------------------------------------------------------------------------------
/src/config/locale/esm-locale-th/index.js.flow:
--------------------------------------------------------------------------------
1 | // @flow
2 | // This file is generated automatically by `scripts/build/typings.js`. Please, don't change it.
3 |
4 | export type Locale = {
5 | code?: string,
6 | formatDistance?: (...args: Array) => any,
7 | formatRelative?: (...args: Array) => any,
8 | localize?: {
9 | ordinalNumber: (...args: Array) => any,
10 | era: (...args: Array) => any,
11 | quarter: (...args: Array) => any,
12 | month: (...args: Array) => any,
13 | day: (...args: Array) => any,
14 | dayPeriod: (...args: Array) => any
15 | },
16 | formatLong?: {
17 | date: (...args: Array) => any,
18 | time: (...args: Array) => any,
19 | dateTime: (...args: Array) => any
20 | },
21 | match?: {
22 | ordinalNumber: (...args: Array) => any,
23 | era: (...args: Array) => any,
24 | quarter: (...args: Array) => any,
25 | month: (...args: Array) => any,
26 | day: (...args: Array) => any,
27 | dayPeriod: (...args: Array) => any
28 | },
29 | options?: {
30 | weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6,
31 | firstWeekContainsDate?: 1 | 2 | 3 | 4 | 5 | 6 | 7
32 | }
33 | }
34 |
35 | declare module.exports: Locale
36 |
--------------------------------------------------------------------------------
/src/config/locale/esm-locale-th/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "sideEffects": false,
3 | "typings": "../../../typings.d.ts"
4 | }
--------------------------------------------------------------------------------
/src/config/locale/th/_lib/formatDistance/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = formatDistance;
7 | var formatDistanceLocale = {
8 | lessThanXSeconds: {
9 | one: 'น้อยกว่า 1 วินาที',
10 | other: 'น้อยกว่า {{count}} วินาที'
11 | },
12 | xSeconds: {
13 | one: '1 วินาที',
14 | other: '{{count}} วินาที'
15 | },
16 | halfAMinute: 'ครึ่งนาที',
17 | lessThanXMinutes: {
18 | one: 'น้อยกว่า 1 นาที',
19 | other: 'น้อยกว่า {{count}} นาที'
20 | },
21 | xMinutes: {
22 | one: '1 นาที',
23 | other: '{{count}} นาที'
24 | },
25 | aboutXHours: {
26 | one: 'ประมาณ 1 ชั่วโมง',
27 | other: 'ประมาณ {{count}} ชั่วโมง'
28 | },
29 | xHours: {
30 | one: '1 ชั่วโมง',
31 | other: '{{count}} ชั่วโมง'
32 | },
33 | xDays: {
34 | one: '1 วัน',
35 | other: '{{count}} วัน'
36 | },
37 | aboutXMonths: {
38 | one: 'ประมาณ 1 เดือน',
39 | other: 'ประมาณ {{count}} เดือน'
40 | },
41 | xMonths: {
42 | one: '1 เดือน',
43 | other: '{{count}} เดือน'
44 | },
45 | aboutXYears: {
46 | one: 'ประมาณ 1 ปี',
47 | other: 'ประมาณ {{count}} ปี'
48 | },
49 | xYears: {
50 | one: '1 ปี',
51 | other: '{{count}} ปี'
52 | },
53 | overXYears: {
54 | one: 'มากกว่า 1 ปี',
55 | other: 'มากกว่า {{count}} ปี'
56 | },
57 | almostXYears: {
58 | one: 'เกือบ 1 ปี',
59 | other: 'เกือบ {{count}} ปี'
60 | }
61 | };
62 |
63 | function formatDistance(token, count, options) {
64 | options = options || {};
65 | var result;
66 |
67 | if (typeof formatDistanceLocale[token] === 'string') {
68 | result = formatDistanceLocale[token];
69 | } else if (count === 1) {
70 | result = formatDistanceLocale[token].one;
71 | } else {
72 | result = formatDistanceLocale[token].other.replace('{{count}}', count);
73 | }
74 |
75 | if (options.addSuffix) {
76 | if (options.comparison > 0) {
77 | if (token === 'halfAMinute') {
78 | return 'ใน' + result;
79 | } else {
80 | return 'ใน ' + result;
81 | }
82 | } else {
83 | return result + 'ที่ผ่านมา';
84 | }
85 | }
86 |
87 | return result;
88 | }
89 |
90 | module.exports = exports.default;
--------------------------------------------------------------------------------
/src/config/locale/th/_lib/formatLong/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = void 0;
7 |
8 | var _index = _interopRequireDefault(require("../../../_lib/buildFormatLongFn/index.js"));
9 |
10 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11 |
12 | var dateFormats = {
13 | full: 'วันEEEEที่ do MMMM y',
14 | long: 'do MMMM y',
15 | medium: 'd MMM y',
16 | short: 'dd/MM/yyyy'
17 | };
18 | var timeFormats = {
19 | full: 'H:mm:ss น. zzzz',
20 | long: 'H:mm:ss น. z',
21 | medium: 'H:mm:ss น.',
22 | short: 'H:mm น.'
23 | };
24 | var dateTimeFormats = {
25 | full: "{{date}} 'เวลา' {{time}}",
26 | long: "{{date}} 'เวลา' {{time}}",
27 | medium: '{{date}}, {{time}}',
28 | short: '{{date}}, {{time}}'
29 | };
30 | var formatLong = {
31 | date: (0, _index.default)({
32 | formats: dateFormats,
33 | defaultWidth: 'full'
34 | }),
35 | time: (0, _index.default)({
36 | formats: timeFormats,
37 | defaultWidth: 'medium'
38 | }),
39 | dateTime: (0, _index.default)({
40 | formats: dateTimeFormats,
41 | defaultWidth: 'full'
42 | })
43 | };
44 | var _default = formatLong;
45 | exports.default = _default;
46 | module.exports = exports.default;
--------------------------------------------------------------------------------
/src/config/locale/th/_lib/formatRelative/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = formatRelative;
7 | var formatRelativeLocale = {
8 | lastWeek: "eeee'ที่แล้วเวลา' p",
9 | yesterday: "'เมื่อวานนี้เวลา' p",
10 | today: "'วันนี้เวลา' p",
11 | tomorrow: "'พรุ่งนี้เวลา' p",
12 | nextWeek: "eeee 'เวลา' p",
13 | other: 'P'
14 | };
15 |
16 | function formatRelative(token, _date, _baseDate, _options) {
17 | return formatRelativeLocale[token];
18 | }
19 |
20 | module.exports = exports.default;
--------------------------------------------------------------------------------
/src/config/locale/th/_lib/localize/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = void 0;
7 |
8 | var _index = _interopRequireDefault(require("../../../_lib/buildLocalizeFn/index.js"));
9 |
10 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11 |
12 | var eraValues = {
13 | narrow: ['B', 'คศ'],
14 | abbreviated: ['BC', 'ค.ศ.'],
15 | wide: ['ปีก่อนคริสตกาล', 'คริสต์ศักราช']
16 | };
17 | var quarterValues = {
18 | narrow: ['1', '2', '3', '4'],
19 | abbreviated: ['Q1', 'Q2', 'Q3', 'Q4'],
20 | wide: ['ไตรมาสแรก', 'ไตรมาสที่สอง', 'ไตรมาสที่สาม', 'ไตรมาสที่สี่']
21 | };
22 | var dayValues = {
23 | narrow: ['อา.', 'จ.', 'อ.', 'พ.', 'พฤ.', 'ศ.', 'ส.'],
24 | short: ['อา.', 'จ.', 'อ.', 'พ.', 'พฤ.', 'ศ.', 'ส.'],
25 | abbreviated: ['อา.', 'จ.', 'อ.', 'พ.', 'พฤ.', 'ศ.', 'ส.'],
26 | wide: ['อาทิตย์', 'จันทร์', 'อังคาร', 'พุธ', 'พฤหัสบดี', 'ศุกร์', 'เสาร์']
27 | };
28 | var monthValues = {
29 | narrow: ['ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.', 'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.', 'ก.ย.', 'ต.ค.', 'พ.ย.', 'ธ.ค.'],
30 | abbreviated: ['ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.', 'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.', 'ก.ย.', 'ต.ค.', 'พ.ย.', 'ธ.ค.'],
31 | wide: ['มกราคม', 'กุมภาพันธ์', 'มีนาคม', 'เมษายน', 'พฤษภาคม', 'มิถุนายน', 'กรกฎาคม', 'สิงหาคม', 'กันยายน', 'ตุลาคม', 'พฤศจิกายน', 'ธันวาคม']
32 | };
33 | var dayPeriodValues = {
34 | narrow: {
35 | am: 'ก่อนเที่ยง',
36 | pm: 'หลังเที่ยง',
37 | midnight: 'เที่ยงคืน',
38 | noon: 'เที่ยง',
39 | morning: 'เช้า',
40 | afternoon: 'บ่าย',
41 | evening: 'เย็น',
42 | night: 'กลางคืน'
43 | },
44 | abbreviated: {
45 | am: 'ก่อนเที่ยง',
46 | pm: 'หลังเที่ยง',
47 | midnight: 'เที่ยงคืน',
48 | noon: 'เที่ยง',
49 | morning: 'เช้า',
50 | afternoon: 'บ่าย',
51 | evening: 'เย็น',
52 | night: 'กลางคืน'
53 | },
54 | wide: {
55 | am: 'ก่อนเที่ยง',
56 | pm: 'หลังเที่ยง',
57 | midnight: 'เที่ยงคืน',
58 | noon: 'เที่ยง',
59 | morning: 'เช้า',
60 | afternoon: 'บ่าย',
61 | evening: 'เย็น',
62 | night: 'กลางคืน'
63 | }
64 | };
65 | var formattingDayPeriodValues = {
66 | narrow: {
67 | am: 'ก่อนเที่ยง',
68 | pm: 'หลังเที่ยง',
69 | midnight: 'เที่ยงคืน',
70 | noon: 'เที่ยง',
71 | morning: 'ตอนเช้า',
72 | afternoon: 'ตอนกลางวัน',
73 | evening: 'ตอนเย็น',
74 | night: 'ตอนกลางคืน'
75 | },
76 | abbreviated: {
77 | am: 'ก่อนเที่ยง',
78 | pm: 'หลังเที่ยง',
79 | midnight: 'เที่ยงคืน',
80 | noon: 'เที่ยง',
81 | morning: 'ตอนเช้า',
82 | afternoon: 'ตอนกลางวัน',
83 | evening: 'ตอนเย็น',
84 | night: 'ตอนกลางคืน'
85 | },
86 | wide: {
87 | am: 'ก่อนเที่ยง',
88 | pm: 'หลังเที่ยง',
89 | midnight: 'เที่ยงคืน',
90 | noon: 'เที่ยง',
91 | morning: 'ตอนเช้า',
92 | afternoon: 'ตอนกลางวัน',
93 | evening: 'ตอนเย็น',
94 | night: 'ตอนกลางคืน'
95 | }
96 | };
97 |
98 | function ordinalNumber(dirtyNumber) {
99 | var number = Number(dirtyNumber);
100 | return number;
101 | }
102 |
103 | var localize = {
104 | ordinalNumber: ordinalNumber,
105 | era: (0, _index.default)({
106 | values: eraValues,
107 | defaultWidth: 'wide'
108 | }),
109 | quarter: (0, _index.default)({
110 | values: quarterValues,
111 | defaultWidth: 'wide',
112 | argumentCallback: function (quarter) {
113 | return Number(quarter) - 1;
114 | }
115 | }),
116 | month: (0, _index.default)({
117 | values: monthValues,
118 | defaultWidth: 'wide'
119 | }),
120 | day: (0, _index.default)({
121 | values: dayValues,
122 | defaultWidth: 'wide'
123 | }),
124 | dayPeriod: (0, _index.default)({
125 | values: dayPeriodValues,
126 | defaultWidth: 'wide',
127 | formattingValues: formattingDayPeriodValues,
128 | defaultFormattingWidth: 'wide'
129 | })
130 | };
131 | var _default = localize;
132 | exports.default = _default;
133 | module.exports = exports.default;
--------------------------------------------------------------------------------
/src/config/locale/th/_lib/match/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = void 0;
7 |
8 | var _index = _interopRequireDefault(require("../../../_lib/buildMatchPatternFn/index.js"));
9 |
10 | var _index2 = _interopRequireDefault(require("../../../_lib/buildMatchFn/index.js"));
11 |
12 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13 |
14 | var matchOrdinalNumberPattern = /^\d+/i;
15 | var parseOrdinalNumberPattern = /\d+/i;
16 | var matchEraPatterns = {
17 | narrow: /^([bB]|[aA]|คศ)/i,
18 | abbreviated: /^([bB]\.?\s?[cC]\.?|b\.?\s?c\.?\s?e\.?|a\.?\s?d\.?|c\.?\s?e\.?|ค\.?ศ\.?)/i,
19 | wide: /^(ก่อนคริสตกาล|คริสต์ศักราช|คริสตกาล)/i
20 | };
21 | var parseEraPatterns = {
22 | any: [/^[bB]/i, /^(^[aA]|ค\.?ศ\.?|คริสตกาล|คริสต์ศักราช|)/i]
23 | };
24 | var matchQuarterPatterns = {
25 | narrow: /^[1234]/i,
26 | abbreviated: /^q[1234]/i,
27 | wide: /^ไตรมาส(ที่)? ?[1234]/i
28 | };
29 | var parseQuarterPatterns = {
30 | any: [/(1|แรก|หนึ่ง)/i, /(2|สอง)/i, /(3|สาม)/i, /(4|สี่)/i]
31 | };
32 | var matchMonthPatterns = {
33 | narrow: /^(ม\.?ค\.?|ก\.?พ\.?|มี\.?ค\.?|เม\.?ย\.?|พ\.?ค\.?|มิ\.?ย\.?|ก\.?ค\.?|ส\.?ค\.?|ก\.?ย\.?|ต\.?ค\.?|พ\.?ย\.?|ธ\.?ค\.?)/i,
34 | abbreviated: /^(ม\.?ค\.?|ก\.?พ\.?|มี\.?ค\.?|เม\.?ย\.?|พ\.?ค\.?|มิ\.?ย\.?|ก\.?ค\.?|ส\.?ค\.?|ก\.?ย\.?|ต\.?ค\.?|พ\.?ย\.?|ธ\.?ค\.?')/i,
35 | wide: /^(มกราคม|กุมภาพันธ์|มีนาคม|เมษายน|พฤษภาคม|มิถุนายน|กรกฎาคม|สิงหาคม|กันยายน|ตุลาคม|พฤศจิกายน|ธันวาคม)/i
36 | };
37 | var parseMonthPatterns = {
38 | wide: [/^มก/i, /^กุม/i, /^มี/i, /^เม/i, /^พฤษ/i, /^มิ/i, /^กรก/i, /^ส/i, /^กัน/i, /^ต/i, /^พฤศ/i, /^ธ/i],
39 | any: [/^ม\.?ค\.?/i, /^ก\.?พ\.?/i, /^มี\.?ค\.?/i, /^เม\.?ย\.?/i, /^พ\.?ค\.?/i, /^มิ\.?ย\.?/i, /^ก\.?ค\.?/i, /^ส\.?ค\.?/i, /^ก\.?ย\.?/i, /^ต\.?ค\.?/i, /^พ\.?ย\.?/i, /^ธ\.?ค\.?/i]
40 | };
41 | var matchDayPatterns = {
42 | narrow: /^(อา\.?|จ\.?|อ\.?|พฤ\.?|พ\.?|ศ\.?|ส\.?)/i,
43 | short: /^(อา\.?|จ\.?|อ\.?|พฤ\.?|พ\.?|ศ\.?|ส\.?)/i,
44 | abbreviated: /^(อา\.?|จ\.?|อ\.?|พฤ\.?|พ\.?|ศ\.?|ส\.?)/i,
45 | wide: /^(อาทิตย์|จันทร์|อังคาร|พุธ|พฤหัสบดี|ศุกร์|เสาร์)/i
46 | };
47 | var parseDayPatterns = {
48 | wide: [/^อา/i, /^จั/i, /^อั/i, /^พุธ/i, /^พฤ/i, /^ศ/i, /^เส/i],
49 | any: [/^อา/i, /^จ/i, /^อ/i, /^พ(?!ฤ)/i, /^พฤ/i, /^ศ/i, /^ส/i]
50 | };
51 | var matchDayPeriodPatterns = {
52 | any: /^(ก่อนเที่ยง|หลังเที่ยง|เที่ยงคืน|เที่ยง|(ตอน.*?)?.*(เที่ยง|เช้า|บ่าย|เย็น|กลางคืน))/i
53 | };
54 | var parseDayPeriodPatterns = {
55 | any: {
56 | am: /^ก่อนเที่ยง/i,
57 | pm: /^หลังเที่ยง/i,
58 | midnight: /^เที่ยงคืน/i,
59 | noon: /^เที่ยง/i,
60 | morning: /เช้า/i,
61 | afternoon: /บ่าย/i,
62 | evening: /เย็น/i,
63 | night: /กลางคืน/i
64 | }
65 | };
66 | var match = {
67 | ordinalNumber: (0, _index.default)({
68 | matchPattern: matchOrdinalNumberPattern,
69 | parsePattern: parseOrdinalNumberPattern,
70 | valueCallback: function (value) {
71 | return parseInt(value, 10);
72 | }
73 | }),
74 | era: (0, _index2.default)({
75 | matchPatterns: matchEraPatterns,
76 | defaultMatchWidth: 'wide',
77 | parsePatterns: parseEraPatterns,
78 | defaultParseWidth: 'any'
79 | }),
80 | quarter: (0, _index2.default)({
81 | matchPatterns: matchQuarterPatterns,
82 | defaultMatchWidth: 'wide',
83 | parsePatterns: parseQuarterPatterns,
84 | defaultParseWidth: 'any',
85 | valueCallback: function (index) {
86 | return index + 1;
87 | }
88 | }),
89 | month: (0, _index2.default)({
90 | matchPatterns: matchMonthPatterns,
91 | defaultMatchWidth: 'wide',
92 | parsePatterns: parseMonthPatterns,
93 | defaultParseWidth: 'any'
94 | }),
95 | day: (0, _index2.default)({
96 | matchPatterns: matchDayPatterns,
97 | defaultMatchWidth: 'wide',
98 | parsePatterns: parseDayPatterns,
99 | defaultParseWidth: 'any'
100 | }),
101 | dayPeriod: (0, _index2.default)({
102 | matchPatterns: matchDayPeriodPatterns,
103 | defaultMatchWidth: 'any',
104 | parsePatterns: parseDayPeriodPatterns,
105 | defaultParseWidth: 'any'
106 | })
107 | };
108 | var _default = match;
109 | exports.default = _default;
110 | module.exports = exports.default;
--------------------------------------------------------------------------------
/src/config/locale/th/index.d.ts:
--------------------------------------------------------------------------------
1 | // This file is generated automatically by `scripts/build/typings.js`. Please, don't change it.
2 |
3 | import { th } from 'date-fns/locale'
4 | export default th
5 |
--------------------------------------------------------------------------------
/src/config/locale/th/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = void 0;
7 |
8 | var _index = _interopRequireDefault(require("./_lib/formatDistance/index.js"));
9 |
10 | var _index2 = _interopRequireDefault(require("./_lib/formatLong/index.js"));
11 |
12 | var _index3 = _interopRequireDefault(require("./_lib/formatRelative/index.js"));
13 |
14 | var _index4 = _interopRequireDefault(require("./_lib/localize/index.js"));
15 |
16 | var _index5 = _interopRequireDefault(require("./_lib/match/index.js"));
17 |
18 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
19 |
20 | /**
21 | * @type {Locale}
22 | * @category Locales
23 | * @summary Thai locale.
24 | * @language Thai
25 | * @iso-639-2 tha
26 | * @author Athiwat Hirunworawongkun [@athivvat]{@link https://github.com/athivvat}
27 | * @author [@hawkup]{@link https://github.com/hawkup}
28 | * @author Jirawat I. [@nodtem66]{@link https://github.com/nodtem66}
29 | */
30 | var locale = {
31 | code: 'th',
32 | formatDistance: _index.default,
33 | formatLong: _index2.default,
34 | formatRelative: _index3.default,
35 | localize: _index4.default,
36 | match: _index5.default,
37 | options: {
38 | weekStartsOn: 0
39 | /* Sunday */
40 | ,
41 | firstWeekContainsDate: 1
42 | }
43 | };
44 | var _default = locale;
45 | exports.default = _default;
46 | module.exports = exports.default;
--------------------------------------------------------------------------------
/src/config/locale/th/index.js.flow:
--------------------------------------------------------------------------------
1 | // @flow
2 | // This file is generated automatically by `scripts/build/typings.js`. Please, don't change it.
3 |
4 | export type Locale = {
5 | code?: string,
6 | formatDistance?: (...args: Array) => any,
7 | formatRelative?: (...args: Array) => any,
8 | localize?: {
9 | ordinalNumber: (...args: Array) => any,
10 | era: (...args: Array) => any,
11 | quarter: (...args: Array) => any,
12 | month: (...args: Array) => any,
13 | day: (...args: Array) => any,
14 | dayPeriod: (...args: Array) => any
15 | },
16 | formatLong?: {
17 | date: (...args: Array) => any,
18 | time: (...args: Array) => any,
19 | dateTime: (...args: Array) => any
20 | },
21 | match?: {
22 | ordinalNumber: (...args: Array) => any,
23 | era: (...args: Array) => any,
24 | quarter: (...args: Array) => any,
25 | month: (...args: Array) => any,
26 | day: (...args: Array) => any,
27 | dayPeriod: (...args: Array) => any
28 | },
29 | options?: {
30 | weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6,
31 | firstWeekContainsDate?: 1 | 2 | 3 | 4 | 5 | 6 | 7
32 | }
33 | }
34 |
35 | declare module.exports: Locale
36 |
--------------------------------------------------------------------------------
/src/config/locale/th/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "sideEffects": false,
3 | "module": "../esm-locale-th/index.js",
4 | "typings": "../typings.d.ts"
5 | }
--------------------------------------------------------------------------------
/src/external/react-datepicker.css:
--------------------------------------------------------------------------------
1 | .react-datepicker__year-read-view--down-arrow,
2 | .react-datepicker__month-read-view--down-arrow,
3 | .react-datepicker__month-year-read-view--down-arrow,
4 | .react-datepicker__navigation-icon::before {
5 | border-color: #ccc;
6 | border-style: solid;
7 | border-width: 3px 3px 0 0;
8 | content: "";
9 | display: block;
10 | height: 9px;
11 | position: absolute;
12 | top: 6px;
13 | width: 9px;
14 | }
15 | .react-datepicker-popper[data-placement^="bottom"] .react-datepicker__triangle {
16 | fill: #f0f0f0;
17 | color: #f0f0f0;
18 | stroke: #aeaeae;
19 | }
20 | .react-datepicker-popper[data-placement^="top"] .react-datepicker__triangle {
21 | fill: #fff;
22 | color: #fff;
23 | stroke: #aeaeae;
24 | }
25 | .react-datepicker-wrapper {
26 | display: inline-block;
27 | padding: 0;
28 | border: 0;
29 | }
30 | .react-datepicker {
31 | font-family: "Helvetica Neue", helvetica, arial, sans-serif;
32 | font-size: 0.8rem;
33 | background-color: #fff;
34 | color: #000;
35 | border: 1px solid #aeaeae;
36 | border-radius: 0.3rem;
37 | display: inline-block;
38 | position: relative;
39 | line-height: initial;
40 | }
41 | .react-datepicker--time-only .react-datepicker__time-container {
42 | border-left: 0;
43 | }
44 | .react-datepicker--time-only .react-datepicker__time,
45 | .react-datepicker--time-only .react-datepicker__time-box {
46 | border-bottom-left-radius: 0.3rem;
47 | border-bottom-right-radius: 0.3rem;
48 | }
49 | .react-datepicker-popper {
50 | z-index: 1;
51 | line-height: 0;
52 | }
53 | .react-datepicker__header {
54 | text-align: center;
55 | background-color: #f0f0f0;
56 | border-bottom: 1px solid #aeaeae;
57 | border-top-left-radius: 0.3rem;
58 | padding: 8px 0;
59 | position: relative;
60 | }
61 | .react-datepicker__header--time {
62 | padding-bottom: 8px;
63 | padding-left: 5px;
64 | padding-right: 5px;
65 | }
66 | .react-datepicker__header--time:not(.react-datepicker__header--time--only) {
67 | border-top-left-radius: 0;
68 | }
69 | .react-datepicker__header:not(.react-datepicker__header--has-time-select) {
70 | border-top-right-radius: 0.3rem;
71 | }
72 | .react-datepicker__year-dropdown-container--select,
73 | .react-datepicker__month-dropdown-container--select,
74 | .react-datepicker__month-year-dropdown-container--select,
75 | .react-datepicker__year-dropdown-container--scroll,
76 | .react-datepicker__month-dropdown-container--scroll,
77 | .react-datepicker__month-year-dropdown-container--scroll {
78 | display: inline-block;
79 | margin: 0 15px;
80 | }
81 | .react-datepicker__current-month,
82 | .react-datepicker-time__header,
83 | .react-datepicker-year-header {
84 | margin-top: 0;
85 | color: #000;
86 | font-weight: bold;
87 | font-size: 0.944rem;
88 | }
89 | .react-datepicker-time__header {
90 | text-overflow: ellipsis;
91 | white-space: nowrap;
92 | overflow: hidden;
93 | }
94 | .react-datepicker__navigation {
95 | align-items: center;
96 | background: none;
97 | display: flex;
98 | justify-content: center;
99 | text-align: center;
100 | cursor: pointer;
101 | position: absolute;
102 | top: 2px;
103 | padding: 0;
104 | border: none;
105 | z-index: 1;
106 | height: 32px;
107 | width: 32px;
108 | text-indent: -999em;
109 | overflow: hidden;
110 | }
111 | .react-datepicker__navigation--previous {
112 | left: 2px;
113 | }
114 | .react-datepicker__navigation--next {
115 | right: 2px;
116 | }
117 | .react-datepicker__navigation--next--with-time:not(
118 | .react-datepicker__navigation--next--with-today-button
119 | ) {
120 | right: 85px;
121 | }
122 | .react-datepicker__navigation--years {
123 | position: relative;
124 | top: 0;
125 | display: block;
126 | margin-left: auto;
127 | margin-right: auto;
128 | }
129 | .react-datepicker__navigation--years-previous {
130 | top: 4px;
131 | }
132 | .react-datepicker__navigation--years-upcoming {
133 | top: -4px;
134 | }
135 | .react-datepicker__navigation:hover *::before {
136 | border-color: #a6a6a6;
137 | }
138 | .react-datepicker__navigation-icon {
139 | position: relative;
140 | top: -1px;
141 | font-size: 20px;
142 | width: 0;
143 | }
144 | .react-datepicker__navigation-icon--next {
145 | left: -2px;
146 | }
147 | .react-datepicker__navigation-icon--next::before {
148 | transform: rotate(45deg);
149 | left: -7px;
150 | }
151 | .react-datepicker__navigation-icon--previous {
152 | right: -2px;
153 | }
154 | .react-datepicker__navigation-icon--previous::before {
155 | transform: rotate(225deg);
156 | right: -7px;
157 | }
158 | .react-datepicker__month-container {
159 | float: left;
160 | }
161 | .react-datepicker__year {
162 | margin: 0.4rem;
163 | text-align: center;
164 | }
165 | .react-datepicker__year-wrapper {
166 | display: flex;
167 | flex-wrap: wrap;
168 | max-width: 180px;
169 | }
170 | .react-datepicker__year .react-datepicker__year-text {
171 | display: inline-block;
172 | width: 4rem;
173 | margin: 2px;
174 | }
175 | .react-datepicker__month {
176 | margin: 0.4rem;
177 | text-align: center;
178 | }
179 | .react-datepicker__month .react-datepicker__month-text,
180 | .react-datepicker__month .react-datepicker__quarter-text {
181 | display: inline-block;
182 | width: 4rem;
183 | margin: 2px;
184 | }
185 | .react-datepicker__input-time-container {
186 | clear: both;
187 | width: 100%;
188 | float: left;
189 | margin: 5px 0 10px 15px;
190 | text-align: left;
191 | }
192 | .react-datepicker__input-time-container .react-datepicker-time__caption {
193 | display: inline-block;
194 | }
195 | .react-datepicker__input-time-container
196 | .react-datepicker-time__input-container {
197 | display: inline-block;
198 | }
199 | .react-datepicker__input-time-container
200 | .react-datepicker-time__input-container
201 | .react-datepicker-time__input {
202 | display: inline-block;
203 | margin-left: 10px;
204 | }
205 | .react-datepicker__input-time-container
206 | .react-datepicker-time__input-container
207 | .react-datepicker-time__input
208 | input {
209 | width: auto;
210 | }
211 | .react-datepicker__input-time-container
212 | .react-datepicker-time__input-container
213 | .react-datepicker-time__input
214 | input[type="time"]::-webkit-inner-spin-button,
215 | .react-datepicker__input-time-container
216 | .react-datepicker-time__input-container
217 | .react-datepicker-time__input
218 | input[type="time"]::-webkit-outer-spin-button {
219 | -webkit-appearance: none;
220 | margin: 0;
221 | }
222 | .react-datepicker__input-time-container
223 | .react-datepicker-time__input-container
224 | .react-datepicker-time__input
225 | input[type="time"] {
226 | -moz-appearance: textfield;
227 | }
228 | .react-datepicker__input-time-container
229 | .react-datepicker-time__input-container
230 | .react-datepicker-time__delimiter {
231 | margin-left: 5px;
232 | display: inline-block;
233 | }
234 | .react-datepicker__time-container {
235 | float: right;
236 | border-left: 1px solid #aeaeae;
237 | width: 85px;
238 | }
239 | .react-datepicker__time-container--with-today-button {
240 | display: inline;
241 | border: 1px solid #aeaeae;
242 | border-radius: 0.3rem;
243 | position: absolute;
244 | right: -87px;
245 | top: 0;
246 | }
247 | .react-datepicker__time-container .react-datepicker__time {
248 | position: relative;
249 | background: #fff;
250 | border-bottom-right-radius: 0.3rem;
251 | }
252 | .react-datepicker__time-container
253 | .react-datepicker__time
254 | .react-datepicker__time-box {
255 | width: 85px;
256 | overflow-x: hidden;
257 | margin: 0 auto;
258 | text-align: center;
259 | border-bottom-right-radius: 0.3rem;
260 | }
261 | .react-datepicker__time-container
262 | .react-datepicker__time
263 | .react-datepicker__time-box
264 | ul.react-datepicker__time-list {
265 | list-style: none;
266 | margin: 0;
267 | height: calc(195px + 1.7rem / 2);
268 | overflow-y: scroll;
269 | padding-right: 0;
270 | padding-left: 0;
271 | width: 100%;
272 | box-sizing: content-box;
273 | }
274 | .react-datepicker__time-container
275 | .react-datepicker__time
276 | .react-datepicker__time-box
277 | ul.react-datepicker__time-list
278 | li.react-datepicker__time-list-item {
279 | height: 30px;
280 | padding: 5px 10px;
281 | white-space: nowrap;
282 | }
283 | .react-datepicker__time-container
284 | .react-datepicker__time
285 | .react-datepicker__time-box
286 | ul.react-datepicker__time-list
287 | li.react-datepicker__time-list-item:hover {
288 | cursor: pointer;
289 | background-color: #f0f0f0;
290 | }
291 | .react-datepicker__time-container
292 | .react-datepicker__time
293 | .react-datepicker__time-box
294 | ul.react-datepicker__time-list
295 | li.react-datepicker__time-list-item--selected {
296 | background-color: #216ba5;
297 | color: #fff;
298 | font-weight: bold;
299 | }
300 | .react-datepicker__time-container
301 | .react-datepicker__time
302 | .react-datepicker__time-box
303 | ul.react-datepicker__time-list
304 | li.react-datepicker__time-list-item--selected:hover {
305 | background-color: #216ba5;
306 | }
307 | .react-datepicker__time-container
308 | .react-datepicker__time
309 | .react-datepicker__time-box
310 | ul.react-datepicker__time-list
311 | li.react-datepicker__time-list-item--disabled {
312 | color: #ccc;
313 | }
314 | .react-datepicker__time-container
315 | .react-datepicker__time
316 | .react-datepicker__time-box
317 | ul.react-datepicker__time-list
318 | li.react-datepicker__time-list-item--disabled:hover {
319 | cursor: default;
320 | background-color: rgba(0, 0, 0, 0);
321 | }
322 | .react-datepicker__week-number {
323 | color: #ccc;
324 | display: inline-block;
325 | width: 1.7rem;
326 | line-height: 1.7rem;
327 | text-align: center;
328 | margin: 0.166rem;
329 | }
330 | .react-datepicker__week-number.react-datepicker__week-number--clickable {
331 | cursor: pointer;
332 | }
333 | .react-datepicker__week-number.react-datepicker__week-number--clickable:not(
334 | .react-datepicker__week-number--selected,
335 | .react-datepicker__week-number--keyboard-selected
336 | ):hover {
337 | border-radius: 0.3rem;
338 | background-color: #f0f0f0;
339 | }
340 | .react-datepicker__week-number--selected {
341 | border-radius: 0.3rem;
342 | background-color: #216ba5;
343 | color: #fff;
344 | }
345 | .react-datepicker__week-number--selected:hover {
346 | background-color: #1d5d90;
347 | }
348 | .react-datepicker__week-number--keyboard-selected {
349 | border-radius: 0.3rem;
350 | background-color: #2a87d0;
351 | color: #fff;
352 | }
353 | .react-datepicker__week-number--keyboard-selected:hover {
354 | background-color: #1d5d90;
355 | }
356 | .react-datepicker__day-names {
357 | white-space: nowrap;
358 | margin-bottom: -8px;
359 | }
360 | .react-datepicker__week {
361 | white-space: nowrap;
362 | }
363 | .react-datepicker__day-name,
364 | .react-datepicker__day,
365 | .react-datepicker__time-name {
366 | color: #000;
367 | display: inline-block;
368 | width: 1.7rem;
369 | line-height: 1.7rem;
370 | text-align: center;
371 | margin: 0.166rem;
372 | }
373 | .react-datepicker__day,
374 | .react-datepicker__month-text,
375 | .react-datepicker__quarter-text,
376 | .react-datepicker__year-text {
377 | cursor: pointer;
378 | }
379 | .react-datepicker__day:hover,
380 | .react-datepicker__month-text:hover,
381 | .react-datepicker__quarter-text:hover,
382 | .react-datepicker__year-text:hover {
383 | border-radius: 0.3rem;
384 | background-color: #f0f0f0;
385 | }
386 | .react-datepicker__day--today,
387 | .react-datepicker__month-text--today,
388 | .react-datepicker__quarter-text--today,
389 | .react-datepicker__year-text--today {
390 | font-weight: bold;
391 | }
392 | .react-datepicker__day--highlighted,
393 | .react-datepicker__month-text--highlighted,
394 | .react-datepicker__quarter-text--highlighted,
395 | .react-datepicker__year-text--highlighted {
396 | border-radius: 0.3rem;
397 | background-color: #3dcc4a;
398 | color: #fff;
399 | }
400 | .react-datepicker__day--highlighted:hover,
401 | .react-datepicker__month-text--highlighted:hover,
402 | .react-datepicker__quarter-text--highlighted:hover,
403 | .react-datepicker__year-text--highlighted:hover {
404 | background-color: #32be3f;
405 | }
406 | .react-datepicker__day--highlighted-custom-1,
407 | .react-datepicker__month-text--highlighted-custom-1,
408 | .react-datepicker__quarter-text--highlighted-custom-1,
409 | .react-datepicker__year-text--highlighted-custom-1 {
410 | color: #f0f;
411 | }
412 | .react-datepicker__day--highlighted-custom-2,
413 | .react-datepicker__month-text--highlighted-custom-2,
414 | .react-datepicker__quarter-text--highlighted-custom-2,
415 | .react-datepicker__year-text--highlighted-custom-2 {
416 | color: green;
417 | }
418 | .react-datepicker__day--holidays,
419 | .react-datepicker__month-text--holidays,
420 | .react-datepicker__quarter-text--holidays,
421 | .react-datepicker__year-text--holidays {
422 | position: relative;
423 | border-radius: 0.3rem;
424 | background-color: #ff6803;
425 | color: #fff;
426 | }
427 | .react-datepicker__day--holidays .overlay,
428 | .react-datepicker__month-text--holidays .overlay,
429 | .react-datepicker__quarter-text--holidays .overlay,
430 | .react-datepicker__year-text--holidays .overlay {
431 | position: absolute;
432 | bottom: 100%;
433 | left: 50%;
434 | transform: translateX(-50%);
435 | background-color: #333;
436 | color: #fff;
437 | padding: 4px;
438 | border-radius: 4px;
439 | white-space: nowrap;
440 | visibility: hidden;
441 | opacity: 0;
442 | transition: visibility 0s, opacity 0.3s ease-in-out;
443 | }
444 | .react-datepicker__day--holidays:hover,
445 | .react-datepicker__month-text--holidays:hover,
446 | .react-datepicker__quarter-text--holidays:hover,
447 | .react-datepicker__year-text--holidays:hover {
448 | background-color: #cf5300;
449 | }
450 | .react-datepicker__day--holidays:hover .overlay,
451 | .react-datepicker__month-text--holidays:hover .overlay,
452 | .react-datepicker__quarter-text--holidays:hover .overlay,
453 | .react-datepicker__year-text--holidays:hover .overlay {
454 | visibility: visible;
455 | opacity: 1;
456 | }
457 | .react-datepicker__day--selected,
458 | .react-datepicker__day--in-selecting-range,
459 | .react-datepicker__day--in-range,
460 | .react-datepicker__month-text--selected,
461 | .react-datepicker__month-text--in-selecting-range,
462 | .react-datepicker__month-text--in-range,
463 | .react-datepicker__quarter-text--selected,
464 | .react-datepicker__quarter-text--in-selecting-range,
465 | .react-datepicker__quarter-text--in-range,
466 | .react-datepicker__year-text--selected,
467 | .react-datepicker__year-text--in-selecting-range,
468 | .react-datepicker__year-text--in-range {
469 | border-radius: 0.3rem;
470 | background-color: #216ba5;
471 | color: #fff;
472 | }
473 | .react-datepicker__day--selected:hover,
474 | .react-datepicker__day--in-selecting-range:hover,
475 | .react-datepicker__day--in-range:hover,
476 | .react-datepicker__month-text--selected:hover,
477 | .react-datepicker__month-text--in-selecting-range:hover,
478 | .react-datepicker__month-text--in-range:hover,
479 | .react-datepicker__quarter-text--selected:hover,
480 | .react-datepicker__quarter-text--in-selecting-range:hover,
481 | .react-datepicker__quarter-text--in-range:hover,
482 | .react-datepicker__year-text--selected:hover,
483 | .react-datepicker__year-text--in-selecting-range:hover,
484 | .react-datepicker__year-text--in-range:hover {
485 | background-color: #1d5d90;
486 | }
487 | .react-datepicker__day--keyboard-selected,
488 | .react-datepicker__month-text--keyboard-selected,
489 | .react-datepicker__quarter-text--keyboard-selected,
490 | .react-datepicker__year-text--keyboard-selected {
491 | border-radius: 0.3rem;
492 | background-color: #bad9f1;
493 | color: #000;
494 | }
495 | .react-datepicker__day--keyboard-selected:hover,
496 | .react-datepicker__month-text--keyboard-selected:hover,
497 | .react-datepicker__quarter-text--keyboard-selected:hover,
498 | .react-datepicker__year-text--keyboard-selected:hover {
499 | background-color: #1d5d90;
500 | }
501 | .react-datepicker__day--in-selecting-range:not(
502 | .react-datepicker__day--in-range,
503 | .react-datepicker__month-text--in-range,
504 | .react-datepicker__quarter-text--in-range,
505 | .react-datepicker__year-text--in-range
506 | ),
507 | .react-datepicker__month-text--in-selecting-range:not(
508 | .react-datepicker__day--in-range,
509 | .react-datepicker__month-text--in-range,
510 | .react-datepicker__quarter-text--in-range,
511 | .react-datepicker__year-text--in-range
512 | ),
513 | .react-datepicker__quarter-text--in-selecting-range:not(
514 | .react-datepicker__day--in-range,
515 | .react-datepicker__month-text--in-range,
516 | .react-datepicker__quarter-text--in-range,
517 | .react-datepicker__year-text--in-range
518 | ),
519 | .react-datepicker__year-text--in-selecting-range:not(
520 | .react-datepicker__day--in-range,
521 | .react-datepicker__month-text--in-range,
522 | .react-datepicker__quarter-text--in-range,
523 | .react-datepicker__year-text--in-range
524 | ) {
525 | background-color: rgba(33, 107, 165, 0.5);
526 | }
527 | .react-datepicker__month--selecting-range
528 | .react-datepicker__day--in-range:not(
529 | .react-datepicker__day--in-selecting-range,
530 | .react-datepicker__month-text--in-selecting-range,
531 | .react-datepicker__quarter-text--in-selecting-range,
532 | .react-datepicker__year-text--in-selecting-range
533 | ),
534 | .react-datepicker__year--selecting-range
535 | .react-datepicker__day--in-range:not(
536 | .react-datepicker__day--in-selecting-range,
537 | .react-datepicker__month-text--in-selecting-range,
538 | .react-datepicker__quarter-text--in-selecting-range,
539 | .react-datepicker__year-text--in-selecting-range
540 | ),
541 | .react-datepicker__month--selecting-range
542 | .react-datepicker__month-text--in-range:not(
543 | .react-datepicker__day--in-selecting-range,
544 | .react-datepicker__month-text--in-selecting-range,
545 | .react-datepicker__quarter-text--in-selecting-range,
546 | .react-datepicker__year-text--in-selecting-range
547 | ),
548 | .react-datepicker__year--selecting-range
549 | .react-datepicker__month-text--in-range:not(
550 | .react-datepicker__day--in-selecting-range,
551 | .react-datepicker__month-text--in-selecting-range,
552 | .react-datepicker__quarter-text--in-selecting-range,
553 | .react-datepicker__year-text--in-selecting-range
554 | ),
555 | .react-datepicker__month--selecting-range
556 | .react-datepicker__quarter-text--in-range:not(
557 | .react-datepicker__day--in-selecting-range,
558 | .react-datepicker__month-text--in-selecting-range,
559 | .react-datepicker__quarter-text--in-selecting-range,
560 | .react-datepicker__year-text--in-selecting-range
561 | ),
562 | .react-datepicker__year--selecting-range
563 | .react-datepicker__quarter-text--in-range:not(
564 | .react-datepicker__day--in-selecting-range,
565 | .react-datepicker__month-text--in-selecting-range,
566 | .react-datepicker__quarter-text--in-selecting-range,
567 | .react-datepicker__year-text--in-selecting-range
568 | ),
569 | .react-datepicker__month--selecting-range
570 | .react-datepicker__year-text--in-range:not(
571 | .react-datepicker__day--in-selecting-range,
572 | .react-datepicker__month-text--in-selecting-range,
573 | .react-datepicker__quarter-text--in-selecting-range,
574 | .react-datepicker__year-text--in-selecting-range
575 | ),
576 | .react-datepicker__year--selecting-range
577 | .react-datepicker__year-text--in-range:not(
578 | .react-datepicker__day--in-selecting-range,
579 | .react-datepicker__month-text--in-selecting-range,
580 | .react-datepicker__quarter-text--in-selecting-range,
581 | .react-datepicker__year-text--in-selecting-range
582 | ) {
583 | background-color: #f0f0f0;
584 | color: #000;
585 | }
586 | .react-datepicker__day--disabled,
587 | .react-datepicker__month-text--disabled,
588 | .react-datepicker__quarter-text--disabled,
589 | .react-datepicker__year-text--disabled {
590 | cursor: default;
591 | color: #ccc;
592 | }
593 | .react-datepicker__day--disabled:hover,
594 | .react-datepicker__month-text--disabled:hover,
595 | .react-datepicker__quarter-text--disabled:hover,
596 | .react-datepicker__year-text--disabled:hover {
597 | background-color: rgba(0, 0, 0, 0);
598 | }
599 | .react-datepicker__day--disabled .overlay,
600 | .react-datepicker__month-text--disabled .overlay,
601 | .react-datepicker__quarter-text--disabled .overlay,
602 | .react-datepicker__year-text--disabled .overlay {
603 | position: absolute;
604 | bottom: 70%;
605 | left: 50%;
606 | transform: translateX(-50%);
607 | background-color: #333;
608 | color: #fff;
609 | padding: 4px;
610 | border-radius: 4px;
611 | white-space: nowrap;
612 | visibility: hidden;
613 | opacity: 0;
614 | transition: visibility 0s, opacity 0.3s ease-in-out;
615 | }
616 | .react-datepicker__input-container {
617 | position: relative;
618 | display: inline-block;
619 | width: 100%;
620 | }
621 | .react-datepicker__input-container .react-datepicker__calendar-icon {
622 | position: absolute;
623 | padding: 0.5rem;
624 | box-sizing: content-box;
625 | }
626 | .react-datepicker__view-calendar-icon input {
627 | padding: 6px 10px 5px 25px;
628 | }
629 | .react-datepicker__year-read-view,
630 | .react-datepicker__month-read-view,
631 | .react-datepicker__month-year-read-view {
632 | border: 1px solid rgba(0, 0, 0, 0);
633 | border-radius: 0.3rem;
634 | position: relative;
635 | }
636 | .react-datepicker__year-read-view:hover,
637 | .react-datepicker__month-read-view:hover,
638 | .react-datepicker__month-year-read-view:hover {
639 | cursor: pointer;
640 | }
641 | .react-datepicker__year-read-view:hover
642 | .react-datepicker__year-read-view--down-arrow,
643 | .react-datepicker__year-read-view:hover
644 | .react-datepicker__month-read-view--down-arrow,
645 | .react-datepicker__month-read-view:hover
646 | .react-datepicker__year-read-view--down-arrow,
647 | .react-datepicker__month-read-view:hover
648 | .react-datepicker__month-read-view--down-arrow,
649 | .react-datepicker__month-year-read-view:hover
650 | .react-datepicker__year-read-view--down-arrow,
651 | .react-datepicker__month-year-read-view:hover
652 | .react-datepicker__month-read-view--down-arrow {
653 | border-top-color: #b3b3b3;
654 | }
655 | .react-datepicker__year-read-view--down-arrow,
656 | .react-datepicker__month-read-view--down-arrow,
657 | .react-datepicker__month-year-read-view--down-arrow {
658 | transform: rotate(135deg);
659 | right: -16px;
660 | top: 0;
661 | }
662 | .react-datepicker__year-dropdown,
663 | .react-datepicker__month-dropdown,
664 | .react-datepicker__month-year-dropdown {
665 | background-color: #f0f0f0;
666 | position: absolute;
667 | width: 50%;
668 | left: 25%;
669 | top: 30px;
670 | z-index: 1;
671 | text-align: center;
672 | border-radius: 0.3rem;
673 | border: 1px solid #aeaeae;
674 | }
675 | .react-datepicker__year-dropdown:hover,
676 | .react-datepicker__month-dropdown:hover,
677 | .react-datepicker__month-year-dropdown:hover {
678 | cursor: pointer;
679 | }
680 | .react-datepicker__year-dropdown--scrollable,
681 | .react-datepicker__month-dropdown--scrollable,
682 | .react-datepicker__month-year-dropdown--scrollable {
683 | height: 150px;
684 | overflow-y: scroll;
685 | }
686 | .react-datepicker__year-option,
687 | .react-datepicker__month-option,
688 | .react-datepicker__month-year-option {
689 | line-height: 20px;
690 | width: 100%;
691 | display: block;
692 | margin-left: auto;
693 | margin-right: auto;
694 | }
695 | .react-datepicker__year-option:first-of-type,
696 | .react-datepicker__month-option:first-of-type,
697 | .react-datepicker__month-year-option:first-of-type {
698 | border-top-left-radius: 0.3rem;
699 | border-top-right-radius: 0.3rem;
700 | }
701 | .react-datepicker__year-option:last-of-type,
702 | .react-datepicker__month-option:last-of-type,
703 | .react-datepicker__month-year-option:last-of-type {
704 | -webkit-user-select: none;
705 | -moz-user-select: none;
706 | -ms-user-select: none;
707 | user-select: none;
708 | border-bottom-left-radius: 0.3rem;
709 | border-bottom-right-radius: 0.3rem;
710 | }
711 | .react-datepicker__year-option:hover,
712 | .react-datepicker__month-option:hover,
713 | .react-datepicker__month-year-option:hover {
714 | background-color: #ccc;
715 | }
716 | .react-datepicker__year-option:hover
717 | .react-datepicker__navigation--years-upcoming,
718 | .react-datepicker__month-option:hover
719 | .react-datepicker__navigation--years-upcoming,
720 | .react-datepicker__month-year-option:hover
721 | .react-datepicker__navigation--years-upcoming {
722 | border-bottom-color: #b3b3b3;
723 | }
724 | .react-datepicker__year-option:hover
725 | .react-datepicker__navigation--years-previous,
726 | .react-datepicker__month-option:hover
727 | .react-datepicker__navigation--years-previous,
728 | .react-datepicker__month-year-option:hover
729 | .react-datepicker__navigation--years-previous {
730 | border-top-color: #b3b3b3;
731 | }
732 | .react-datepicker__year-option--selected,
733 | .react-datepicker__month-option--selected,
734 | .react-datepicker__month-year-option--selected {
735 | position: absolute;
736 | left: 15px;
737 | }
738 | .react-datepicker__close-icon {
739 | cursor: pointer;
740 | background-color: rgba(0, 0, 0, 0);
741 | border: 0;
742 | outline: 0;
743 | padding: 0 6px 0 0;
744 | position: absolute;
745 | top: 0;
746 | right: 0;
747 | height: 100%;
748 | display: table-cell;
749 | vertical-align: middle;
750 | }
751 | .react-datepicker__close-icon::after {
752 | cursor: pointer;
753 | background-color: #216ba5;
754 | color: #fff;
755 | border-radius: 50%;
756 | height: 16px;
757 | width: 16px;
758 | padding: 2px;
759 | font-size: 12px;
760 | line-height: 1;
761 | text-align: center;
762 | display: table-cell;
763 | vertical-align: middle;
764 | content: "×";
765 | }
766 | .react-datepicker__close-icon--disabled {
767 | cursor: default;
768 | }
769 | .react-datepicker__close-icon--disabled::after {
770 | cursor: default;
771 | background-color: #ccc;
772 | }
773 | .react-datepicker__today-button {
774 | background: #f0f0f0;
775 | border-top: 1px solid #aeaeae;
776 | cursor: pointer;
777 | text-align: center;
778 | font-weight: bold;
779 | padding: 5px 0;
780 | clear: left;
781 | }
782 | .react-datepicker__portal {
783 | position: fixed;
784 | width: 100vw;
785 | height: 100vh;
786 | background-color: rgba(0, 0, 0, 0.8);
787 | left: 0;
788 | top: 0;
789 | justify-content: center;
790 | align-items: center;
791 | display: flex;
792 | z-index: 2147483647;
793 | }
794 | .react-datepicker__portal .react-datepicker__day-name,
795 | .react-datepicker__portal .react-datepicker__day,
796 | .react-datepicker__portal .react-datepicker__time-name {
797 | width: 3rem;
798 | line-height: 3rem;
799 | }
800 | @media (max-width: 400px), (max-height: 550px) {
801 | .react-datepicker__portal .react-datepicker__day-name,
802 | .react-datepicker__portal .react-datepicker__day,
803 | .react-datepicker__portal .react-datepicker__time-name {
804 | width: 2rem;
805 | line-height: 2rem;
806 | }
807 | }
808 | .react-datepicker__portal .react-datepicker__current-month,
809 | .react-datepicker__portal .react-datepicker-time__header {
810 | font-size: 1.44rem;
811 | }
812 | .react-datepicker__children-container {
813 | width: 13.8rem;
814 | margin: 0.4rem;
815 | padding-right: 0.2rem;
816 | padding-left: 0.2rem;
817 | height: auto;
818 | }
819 | .react-datepicker__aria-live {
820 | position: absolute;
821 | clip-path: circle(0);
822 | border: 0;
823 | height: 1px;
824 | margin: -1px;
825 | overflow: hidden;
826 | padding: 0;
827 | width: 1px;
828 | white-space: nowrap;
829 | }
830 | .react-datepicker__calendar-icon {
831 | width: 1em;
832 | height: 1em;
833 | vertical-align: -0.125em;
834 | }
835 |
836 | /* CUSTOM CSS */
837 | .tdpk-header {
838 | display: inline-flex;
839 | justify-content: space-evenly;
840 | gap: 4px;
841 | padding: 2px 8px;
842 | }
843 | .tdpk-header-prev-btn {
844 | cursor: pointer;
845 | padding: 0.3rem 0.6rem;
846 | border: 1px solid #aeaeae80;
847 | border-radius: 0.3rem;
848 | }
849 | .tdpk-header-prev-btn:hover {
850 | background-color: #ccc6;
851 | }
852 | .tdpk-header-select-month {
853 | cursor: pointer;
854 | padding: 0.3rem 0.6rem;
855 | border: 1px solid #aeaeae80;
856 | border-radius: 0.3rem;
857 | }
858 | .tdpk-header-select-month:hover {
859 | background-color: #ccc6;
860 | }
861 | .tdpk-header-select-year {
862 | cursor: pointer;
863 | padding: 0.3rem 0.6rem;
864 | border: 1px solid #aeaeae80;
865 | border-radius: 0.3rem;
866 | }
867 | .tdpk-header-select-year:hover {
868 | background-color: #ccc6;
869 | }
870 | .tdpk-header-next-btn {
871 | cursor: pointer;
872 | padding: 0.3rem 0.6rem;
873 | border: 1px solid #aeaeae80;
874 | border-radius: 0.3rem;
875 | }
876 | .tdpk-header-next-btn:hover {
877 | background-color: #ccc6;
878 | }
879 |
880 | /* PATCHED */
881 | .react-datepicker-wrapper,
882 | .react-datepicker__input-container {
883 | display: inline-flex;
884 | }
885 | .react-datepicker__close-icon {
886 | cursor: pointer;
887 | background-color: transparent;
888 | border: 0;
889 | outline: 0;
890 | padding: 0.25rem;
891 | margin-inline-end: 0.25rem;
892 | position: absolute;
893 | top: 0;
894 | right: 0;
895 | height: 100%;
896 | display: table-cell;
897 | vertical-align: middle;
898 | }
899 | .react-datepicker__close-icon:focus,
900 | .react-datepicker__close-icon:focus-visible {
901 | outline: none;
902 | }
903 | .react-datepicker__close-icon::after {
904 | cursor: pointer;
905 | background-color: #ccc;
906 | color: #fff;
907 | border-radius: 50%;
908 | height: 0.75rem;
909 | width: 0.75rem;
910 | padding: 0;
911 | font-size: 12px;
912 | line-height: 1;
913 | text-align: center;
914 | display: table-cell;
915 | vertical-align: middle;
916 | content: "×";
917 | }
918 | .react-datepicker__day--keyboard-selected,
919 | .react-datepicker__month-text--keyboard-selected,
920 | .react-datepicker__quarter-text--keyboard-selected,
921 | .react-datepicker__year-text--keyboard-selected {
922 | border-radius: 0.3rem;
923 | background-color: #2579ba;
924 | color: #fff;
925 | }
926 | .react-datepicker-wrapper {
927 | display: inline-block;
928 | padding: 0;
929 | border: 0;
930 | width: 100%;
931 | }
932 |
--------------------------------------------------------------------------------
/src/hooks/useStylesheet.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, screen } from "@testing-library/react";
2 | import { useRef } from "react";
3 | import { useStylesheet } from "./useStylesheet";
4 |
5 | describe("Test hooks/useStylesheet", () => {
6 | beforeEach(() => {
7 | document.head.replaceChildren("");
8 | });
9 |
10 | test("should be visible", () => {
11 | const TestComponent = () => {
12 | const ref = useRef(null);
13 | useStylesheet(ref);
14 | return (
15 |
16 | children
17 |
18 | );
19 | };
20 |
21 | render( );
22 | const container = screen.getByTestId("test-container");
23 | expect(container).toBeVisible();
24 | expect(container).toHaveTextContent("children");
25 | expect(
26 | document.head.querySelector("style[id='external_react-datepicker.css']")
27 | ).not.toBeNull();
28 | });
29 |
30 | test("should be not found any external css when disabled", () => {
31 | const TestComponent = () => {
32 | const ref = useRef(null);
33 | useStylesheet(ref, false);
34 | return (
35 |
36 | children
37 |
38 | );
39 | };
40 |
41 | render( );
42 | const container = screen.getByTestId("test-container");
43 | expect(container).toBeVisible();
44 | expect(container).toHaveTextContent("children");
45 | expect(
46 | document.head.querySelector("style[id='external_react-datepicker.css']")
47 | ).toBeNull();
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/src/hooks/useStylesheet.ts:
--------------------------------------------------------------------------------
1 | // Thanks idea: https://dev.to/omgovich/the-tiniest-css-in-js-solution-for-your-open-source-react-components-1o94
2 | import { useEffect, useLayoutEffect } from "react";
3 |
4 | // React currently throws a warning when using useLayoutEffect on the server.
5 | // To get around it, we can conditionally useEffect on the server (no-op) and
6 | // useLayoutEffect in the browser.
7 | const useIsomorphicLayoutEffect =
8 | typeof window !== "undefined" ? useLayoutEffect : useEffect;
9 |
10 | // Bundler is configured to load this as a processed minified CSS-string
11 | import styles from "../external/react-datepicker.css";
12 |
13 | const styleElementMap = new Map();
14 | const getParentDocument = (nodeRef: React.RefObject) => {
15 | return nodeRef.current ? nodeRef.current?.["ownerDocument"] : document;
16 | };
17 |
18 | /**
19 | * Injects CSS code into the document's
20 | */
21 | export const useStylesheet = (
22 | nodeRef: React.RefObject,
23 | enable: boolean = true
24 | ) => {
25 | useIsomorphicLayoutEffect(() => {
26 | const parentDocument = getParentDocument(nodeRef);
27 |
28 | if (
29 | typeof parentDocument !== "undefined" &&
30 | !styleElementMap.has(parentDocument) &&
31 | enable
32 | ) {
33 | const styleElement = parentDocument.createElement("style");
34 | styleElement.id = "external_react-datepicker.css";
35 | styleElement.innerHTML = styles as string;
36 | styleElementMap.set(parentDocument, styleElement);
37 |
38 | parentDocument.head.appendChild(styleElement);
39 | }
40 | }, []);
41 | };
42 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | // just like previous version (you can keep using my nickname as the component name :D)
2 | // export { ThaiDatePicker, ThaiDatePicker as WatDatePicker } from "./components";
3 | export * from "./components";
4 |
--------------------------------------------------------------------------------
/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | import "@testing-library/jest-dom";
2 | import "@testing-library/jest-dom/jest-globals";
3 |
--------------------------------------------------------------------------------
/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*.css" {
2 | export default string;
3 | }
4 |
--------------------------------------------------------------------------------
/src/utils/YearListGenerator.test.ts:
--------------------------------------------------------------------------------
1 | import dayjs from "dayjs";
2 | import MockDate from "mockdate";
3 |
4 | import YearListGenerator from "./YearListGenerator";
5 |
6 | describe("Test utils/YearListGenerator", () => {
7 | beforeEach(() => {
8 | MockDate.set(new Date("2023-12-31"));
9 | });
10 |
11 | afterEach(() => {
12 | MockDate.reset();
13 | });
14 |
15 | // RangeMaker
16 | [
17 | {
18 | input: {
19 | startVal: 1,
20 | endVal: 100,
21 | increment: 10,
22 | },
23 | expected: {
24 | return: [1, 11, 21, 31, 41, 51, 61, 71, 81, 91],
25 | length: 10,
26 | },
27 | },
28 | {
29 | input: {
30 | startVal: 99,
31 | endVal: 105,
32 | increment: 2,
33 | },
34 | expected: {
35 | return: [99, 101, 103, 105],
36 | length: 4,
37 | },
38 | },
39 | {
40 | input: {
41 | startVal: 0,
42 | endVal: 1,
43 | increment: -10,
44 | },
45 | expected: {
46 | return: [],
47 | length: 0,
48 | },
49 | },
50 | {
51 | input: {
52 | startVal: 100,
53 | endVal: 1,
54 | increment: 0,
55 | },
56 | expected: {
57 | return: [],
58 | length: 0,
59 | },
60 | },
61 | {
62 | input: {
63 | startVal: 100,
64 | endVal: -100,
65 | increment: 1,
66 | },
67 | expected: {
68 | return: [],
69 | length: 0,
70 | },
71 | },
72 | ].forEach((tc) => {
73 | test(`RangeMaker should be have length = ${
74 | tc.expected.length
75 | } and return = ${tc.expected.return} when input ${JSON.stringify(
76 | tc.input
77 | )}`, () => {
78 | const generator = new YearListGenerator();
79 | const range = generator.RangeMaker(...Object.values(tc.input));
80 | expect(JSON.stringify(range)).toBe(JSON.stringify(tc.expected.return));
81 | expect(range.length).toBe(tc.expected.length);
82 | });
83 | });
84 |
85 | // RangeMakerNoArgs
86 | test(`RangeMakerNoArgs should be not to throw any error`, () => {
87 | expect(() => {
88 | const generator = new YearListGenerator();
89 | const result = generator.RangeMaker();
90 | expect(result.length).toBe(0);
91 | }).not.toThrow();
92 | });
93 |
94 | // Generate
95 | [
96 | {
97 | input: {
98 | scope: 5,
99 | minDate: "",
100 | maxDate: "",
101 | },
102 | expected: {
103 | return: [
104 | 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026, 2027, 2028,
105 | ],
106 | length: 11,
107 | },
108 | mock: {
109 | yearFn: 2023,
110 | },
111 | },
112 | {
113 | input: {
114 | scope: 1,
115 | minDate: "",
116 | maxDate: "",
117 | },
118 | expected: {
119 | return: [2022, 2023, 2024],
120 | length: 3, // current + (past_1y) + (future_1y) = 3
121 | },
122 | },
123 | {
124 | input: {
125 | scope: 0,
126 | minDate: "",
127 | maxDate: "",
128 | },
129 | expected: {
130 | return: [2023],
131 | length: 1, // current + (past_0y) + (future_0y) = 1
132 | },
133 | },
134 | {
135 | input: {
136 | scope: -1,
137 | minDate: "",
138 | maxDate: "",
139 | },
140 | expected: {
141 | return: [],
142 | length: 0, // stuck on requirement of RangeMaker (which return [])
143 | },
144 | },
145 | {
146 | input: {
147 | scope: 0,
148 | minDate: false,
149 | maxDate: false,
150 | },
151 | expected: {
152 | return: [2023],
153 | length: 1, // just 1 (current)
154 | },
155 | },
156 | {
157 | input: {
158 | scope: 0,
159 | minDate: "2025-12-31",
160 | maxDate: false,
161 | },
162 | expected: {
163 | return: [],
164 | length: 0, // over current year
165 | },
166 | },
167 | {
168 | input: {
169 | scope: 0,
170 | minDate: false,
171 | maxDate: "2025-12-31",
172 | },
173 | expected: {
174 | return: [2023, 2024, 2025],
175 | length: 3, // current + increment diff 2 year = 3
176 | },
177 | },
178 | {
179 | input: {},
180 | expected: {
181 | return: [
182 | 1924, 1925, 1926, 1927, 1928, 1929, 1930, 1931, 1932, 1933, 1934,
183 | 1935, 1936, 1937, 1938, 1939, 1940, 1941, 1942, 1943, 1944, 1945,
184 | 1946, 1947, 1948, 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956,
185 | 1957, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967,
186 | 1968, 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978,
187 | 1979, 1980, 1981, 1982, 1983, 1984, 1985, 1986, 1987, 1988, 1989,
188 | 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
189 | 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,
190 | 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022,
191 | 2023, 2024, 2025, 2026, 2027, 2028, 2029, 2030, 2031, 2032, 2033,
192 | 2034, 2035, 2036, 2037, 2038, 2039, 2040, 2041, 2042, 2043, 2044,
193 | 2045, 2046, 2047, 2048, 2049, 2050, 2051, 2052, 2053, 2054, 2055,
194 | 2056, 2057, 2058, 2059, 2060, 2061, 2062, 2063, 2064, 2065, 2066,
195 | 2067, 2068, 2069, 2070, 2071, 2072, 2073, 2074, 2075, 2076, 2077,
196 | 2078, 2079, 2080, 2081, 2082, 2083, 2084, 2085, 2086, 2087, 2088,
197 | 2089, 2090, 2091, 2092, 2093, 2094, 2095, 2096, 2097, 2098, 2099,
198 | 2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107, 2108, 2109, 2110,
199 | 2111, 2112, 2113, 2114, 2115, 2116, 2117, 2118, 2119, 2120, 2121,
200 | 2122,
201 | ],
202 | length: 199, // current + (past_99y) + (future_99y) = 199
203 | },
204 | },
205 | ].forEach((tc) => {
206 | test(`Generate should be have length = ${tc.expected.length} and return = ${
207 | tc.expected.return
208 | } when input ${JSON.stringify(tc.input)}`, () => {
209 | const generator = new YearListGenerator(dayjs);
210 | const result = generator.Generate(...Object.values(tc.input));
211 | expect(JSON.stringify(result)).toBe(JSON.stringify(tc.expected.return));
212 | expect(result.length).toBe(tc.expected.length);
213 | });
214 | });
215 |
216 | // Generate with not defined dateLibrary
217 | test(`GenerateNoDateLib should be throw error TypeError`, () => {
218 | expect(() => {
219 | const generator = new YearListGenerator();
220 | generator.Generate();
221 | }).toThrow(TypeError);
222 | });
223 | });
224 |
--------------------------------------------------------------------------------
/src/utils/YearListGenerator.ts:
--------------------------------------------------------------------------------
1 | class YearListGenerator {
2 | dateLibrary: (_d?: any) => any;
3 |
4 | constructor(dateLibrary?: any) {
5 | this.dateLibrary = dateLibrary;
6 | }
7 |
8 | RangeMaker(startVal = 0, endVal = 0, increment = 0) {
9 | let list: Array = [];
10 | if (increment <= 0) {
11 | return list;
12 | }
13 | for (let index = startVal; index <= endVal; index = index + increment) {
14 | list = [...list, index];
15 | }
16 | return list;
17 | }
18 |
19 | Generate(scope: number = 99, minDate: any, maxDate: any) {
20 | const scopeYear = scope;
21 | const currentYear = this.dateLibrary().year();
22 | const minYear = minDate
23 | ? this.dateLibrary(minDate).year()
24 | : currentYear - scopeYear;
25 | const maxYear = maxDate
26 | ? this.dateLibrary(maxDate).year()
27 | : currentYear + scopeYear;
28 | return this.RangeMaker(minYear, maxYear, 1);
29 | }
30 | }
31 |
32 | export default YearListGenerator;
33 |
--------------------------------------------------------------------------------
/src/utils/index.test.ts:
--------------------------------------------------------------------------------
1 | import { ConvertToThaiYear, GetHighlightByDate } from ".";
2 |
3 | describe("Test utils/ConvertToThaiYear", () => {
4 | [
5 | {
6 | input: 2023,
7 | expected: 2566,
8 | },
9 | {
10 | input: 1999,
11 | expected: 2542,
12 | },
13 | ].forEach((tc) => {
14 | test(`should be ${tc.expected} when input ${tc.input}`, () => {
15 | const actual = ConvertToThaiYear(tc.input);
16 | expect(actual).toBe(tc.expected);
17 | });
18 | });
19 | });
20 |
21 | describe("Test utils/GetHighlightByDate", () => {
22 | const inputDate = new Date();
23 | [
24 | {
25 | input: inputDate,
26 | expected: [
27 | {
28 | "react-datepicker__day--highlighted-today": [inputDate],
29 | },
30 | ],
31 | },
32 | {
33 | input: new Date("2023-12-31"),
34 | expected: [
35 | {
36 | "react-datepicker__day--highlighted-today": [new Date("2023-12-31")],
37 | },
38 | ],
39 | },
40 | ].forEach((tc) => {
41 | test(`should be ${tc.expected} when input ${tc.input}`, () => {
42 | const actual = GetHighlightByDate(tc.input);
43 | expect(actual).toMatchObject(tc.expected);
44 | });
45 | });
46 |
47 | test(`should be got 1 elem when input undefined`, () => {
48 | const actual = GetHighlightByDate();
49 | expect(actual.length).toBe(1);
50 | });
51 | });
52 |
--------------------------------------------------------------------------------
/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | import { clsx, type ClassValue } from "clsx";
2 | import { twMerge } from "tailwind-merge";
3 |
4 | /**
5 | * Convert christ year to buddhist year (+543)
6 | */
7 | export const ConvertToThaiYear = (christYear: number) => {
8 | return christYear + 543;
9 | };
10 |
11 | /**
12 | * Get highlighted list by date
13 | */
14 | export const GetHighlightByDate = (date = new Date()) => [
15 | {
16 | "react-datepicker__day--highlighted-today": [date],
17 | },
18 | ];
19 |
20 | /**
21 | * Merge and deduplicate classnames using tailwind-merge and clsx.
22 | */
23 | export const cn = (...inputs: ClassValue[]) => {
24 | return twMerge(clsx(inputs));
25 | };
26 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "module": "ESNext",
5 | "jsx": "preserve",
6 | "moduleResolution": "Bundler",
7 | "strict": true,
8 | "noEmit": true,
9 | "allowSyntheticDefaultImports": true,
10 | "isolatedModules": true,
11 | "skipLibCheck": true,
12 | "allowJs": true,
13 | },
14 | "include": [
15 | "src/**/*",
16 | ],
17 | "exclude": [
18 | "**/node_modules",
19 | "src/**/*.test.ts",
20 | "src/**/*.test.tsx",
21 | "src/setupTests.ts"
22 | ]
23 | }
--------------------------------------------------------------------------------