├── .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 | [![NPM](https://img.shields.io/npm/v/thaidatepicker-react)](https://www.npmjs.com/package/thaidatepicker-react) 4 | [![NPM](https://img.shields.io/badge/Watsize-Library-289548)](https://www.npmjs.com/package/thaidatepicker-react) 5 | [![CodeQL](https://github.com/buildingwatsize/thaidatepicker-react/actions/workflows/github-code-scanning/codeql/badge.svg?branch=main)](https://github.com/buildingwatsize/thaidatepicker-react/actions/workflows/github-code-scanning/codeql) 6 | [![Downloads](https://img.shields.io/npm/dm/thaidatepicker-react.svg)](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 | [![NPM](https://img.shields.io/npm/v/thaidatepicker-react.svg)](https://www.npmjs.com/package/thaidatepicker-react) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) [![NPM](https://img.shields.io/badge/BAAC-Library-289548)](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 | ![Demo](docs/demo.png) 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 | donate 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 |
16 | 17 | Ant Design Form with thaidatepicker-react 18 | 19 |
31 | Information 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 47 | 48 | 49 | Address 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 70 | 73 | 74 |
75 | 76 | Source Code 77 | 78 |
{easySrcCode}
79 |
80 |
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 | 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 | 56 | ))} 57 | 58 | 59 | changeYear(Number(target.value))} 63 | > 64 | {yearOptions.map((option) => ( 65 | 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 | 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 | 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 | } --------------------------------------------------------------------------------