├── .eslintignore ├── .eslintrc.cjs ├── .github ├── CODE_OF_CONDUCT.md ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── commitlint.yml │ └── release.yml ├── .gitignore ├── .husky ├── commit-msg └── prepare-commit-msg ├── CHANGELOG.md ├── LICENSE ├── README.md ├── commitlint.config.js ├── docs ├── .postcssrc.cjs ├── .vitepress │ ├── config.ts │ └── theme │ │ ├── index.js │ │ ├── tailwind.postcss │ │ └── tailwind.postcss:Zone.Identifier ├── DemoLayout.vue ├── advanced-features.md ├── dark.png ├── demo.md ├── events.md ├── index.md ├── installation.md ├── light.png ├── logo.png ├── props.md └── theming-options.md ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.cjs ├── src ├── App.vue ├── VueTailwindDatePicker.vue ├── assets │ ├── base.css │ ├── logo.svg │ └── main.css ├── components │ ├── Calendar.vue │ ├── Header.vue │ ├── Month.vue │ ├── Shortcut.vue │ ├── Week.vue │ └── Year.vue ├── composables │ ├── date.ts │ └── dom.ts ├── entry.esm.ts ├── entry.ts ├── index.css ├── keys.ts ├── main.ts ├── types.ts ├── utils.ts └── vue-shim.d.ts ├── tailwind.config.ts ├── tsconfig.json └── vite.config.ts /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | docs/.vitepress/cache 4 | docs/.vitepress/dist 5 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | const process = require('node:process') 2 | 3 | process.env.ESLINT_TSCONFIG = 'tsconfig.json' 4 | 5 | module.exports = { 6 | extends: '@antfu', 7 | } 8 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | e.elreco@gmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: elreco 4 | patreon: # Replace with a single Patreon username 5 | open_collective: 6 | ko_fi: # 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 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop & mobile (please complete the following information):** 27 | - Browser [e.g. chrome, safari] 28 | - Library version [e.g. 3.0.0] -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. -------------------------------------------------------------------------------- /.github/workflows/commitlint.yml: -------------------------------------------------------------------------------- 1 | name: Lint Commit Messages 2 | on: [pull_request] 3 | 4 | jobs: 5 | commitlint: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v3 9 | - uses: wagoid/commitlint-github-action@v5 -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | permissions: 8 | contents: read # for checkout 9 | 10 | jobs: 11 | release: 12 | name: Release 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: write # to be able to publish a GitHub release 16 | issues: write # to be able to comment on released issues 17 | pull-requests: write # to be able to comment on released pull requests 18 | id-token: write # to enable use of OIDC for npm provenance 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v3 22 | with: 23 | fetch-depth: 0 24 | - name: Setup Node.js 25 | uses: actions/setup-node@v3 26 | with: 27 | node-version: "lts/*" 28 | - name: Install dependencies 29 | run: npm clean-install 30 | - name: Build 31 | run: npm run build 32 | - name: Verify the integrity of provenance attestations and registry signatures for installed dependencies 33 | run: npm audit signatures 34 | - name: Release 35 | env: 36 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 37 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 38 | HUSKY: 0 39 | run: npx semantic-release -------------------------------------------------------------------------------- /.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 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | /cypress/videos/ 18 | /cypress/screenshots/ 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | 30 | # vitepress 31 | docs/.vitepress/cache 32 | docs/.vitepress/dist -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx --no -- commitlint --edit ${1} 5 | -------------------------------------------------------------------------------- /.husky/prepare-commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | exec < /dev/tty && node_modules/.bin/cz --hook || true 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [1.6.5](https://github.com/elreco/vue-tailwind-datepicker/compare/v1.6.4...v1.6.5) (2024-02-05) 2 | 3 | 4 | ### :repeat: Chore 5 | 6 | * update gh token ([ea501c7](https://github.com/elreco/vue-tailwind-datepicker/commit/ea501c7878594ab1d656de89fe5102074dfe847d)) 7 | 8 | ## [1.6.3](https://github.com/elreco/vue-tailwind-datepicker/compare/v1.6.2...v1.6.3) (2024-02-05) 9 | 10 | 11 | ### :repeat: Chore 12 | 13 | * update release ([c325818](https://github.com/elreco/vue-tailwind-datepicker/commit/c3258189611781a7353e52d038f096bc57da4189)) 14 | 15 | ## [1.6.2](https://github.com/elreco/vue-tailwind-datepicker/compare/v1.6.1...v1.6.2) (2024-01-31) 16 | 17 | 18 | ### Bug Fixes 19 | 20 | * export types ([5f6173c](https://github.com/elreco/vue-tailwind-datepicker/commit/5f6173c6c248d56b9a88ce54ba9fd3ee25ac8316)) 21 | 22 | ## [1.6.1](https://github.com/elreco/vue-tailwind-datepicker/compare/v1.6.0...v1.6.1) (2023-10-12) 23 | 24 | 25 | ### Bug Fixes 26 | 27 | * **version:** fix version ([edc239c](https://github.com/elreco/vue-tailwind-datepicker/commit/edc239c3a268a60318b99ee6baa89e451397e5ea)) 28 | 29 | # [1.6.0](https://github.com/elreco/vue-tailwind-datepicker/compare/v1.5.4...v1.6.0) (2023-10-12) 30 | 31 | 32 | ### Bug Fixes 33 | 34 | * **shortcuts:** update css to fix visual glitch when picker use as-single use-range at the same time ([90c96ff](https://github.com/elreco/vue-tailwind-datepicker/commit/90c96ffda97cd03cb1be26bfa71f1da63d246476)) 35 | 36 | 37 | ### Features 38 | 39 | * **typing:** add event typing and refactor locale loading ([2445527](https://github.com/elreco/vue-tailwind-datepicker/commit/244552703323873f93683abde217039e8a902cb2)) 40 | 41 | ## [1.5.4](https://github.com/elreco/vue-tailwind-datepicker/compare/v1.5.3...v1.5.4) (2023-10-12) 42 | 43 | 44 | ### Bug Fixes 45 | 46 | * **release:** add build step ([1a2edf7](https://github.com/elreco/vue-tailwind-datepicker/commit/1a2edf78da228efae239c7832310e08512ecb0ab)) 47 | 48 | ## [1.0.1](https://github.com/elreco/vue-tailwind-datepicker/compare/v1.0.0...v1.0.1) (2023-10-12) 49 | 50 | 51 | ### Bug Fixes 52 | 53 | * **release:** git plugins ([ce26f42](https://github.com/elreco/vue-tailwind-datepicker/commit/ce26f426192dce259de0d9d307a9bb00b19efc02)) 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Kenhyuwa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue Tailwind Datepicker 2 | 3 |

4 | 5 | Vue Tailwind Datepicker 6 |

7 | A Datepicker component for Vue 3 using Tailwind and dayjs. 8 |

9 | 10 | [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) 11 | 12 | 13 | ## Documentation 14 | 15 | Go to [full documentation](https://vue-tailwind-datepicker.com) 16 | 17 | ## Installation 18 | 19 | ⚠️ Vue Tailwind Datepicker uses Tailwind CSS (with the @tailwindcss/forms plugin) & Day.js under the hood, **you must install those packages** before. 20 | You can follow [this tutorial](https://dev.to/elreco/add-a-tailwind-datepicker-to-your-vue-3-application-57j2). 21 | 22 | ### Install via npm 23 | 24 | ``` 25 | npm install vue-tailwind-datepicker 26 | ``` 27 | 28 | ### Install via yarn 29 | 30 | ``` 31 | yarn add vue-tailwind-datepicker 32 | ``` 33 | 34 | ## Simple Usage 35 | 36 | How it works, 37 | 38 | ```vue 39 | 49 | 50 | 55 | ``` 56 | 57 | ## Theming options 58 | 59 | **Light Mode** 60 | 61 | ![Light Mode](https://github.com/elreco/vue-tailwind-datepicker/blob/main/docs/light.png?raw=true) 62 | 63 | **Dark Mode** 64 | 65 | ![Dark Mode](https://github.com/elreco/vue-tailwind-datepicker/blob/main/docs/dark.png?raw=true) 66 | 67 | ## Changelog 68 | 69 | All notable changes to this project will be documented in the [Releases Page](https://github.com/elreco/vue-tailwind-datepicker/releases). 70 | 71 | ## Sponsor 72 | 73 | - [tailwindai.dev](https://www.tailwindai.dev) 74 | 75 | ## License 76 | 77 | The [MIT](LICENSE) License. Please [see](http://opensource.org/licenses/MIT) for more information. 78 | 79 | ## Thanks to 80 | 81 | - [kenhyuwa](https://github.com/kenhyuwa) 82 | - [Vue](https://v3.vuejs.org/) 83 | - [Tailwind CSS](https://tailwindcss.com/) 84 | - [day.js](https://day.js.org/) 85 | - and other support... 86 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | export default { extends: ['@commitlint/config-conventional'] } 2 | -------------------------------------------------------------------------------- /docs/.postcssrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | 'postcss-preset-env': { 5 | autoprefixer: { flexbox: 'no-2009' }, 6 | features: { 'custom-properties': false }, 7 | stage: 3 8 | }, 9 | 'postcss-prefix-selector': { 10 | prefix: ':not(:where(.vp-raw *))', 11 | includeFiles: [/vp-doc\.css/], 12 | transform(prefix, _selector) { 13 | const [selector, pseudo = ''] = _selector.split(/(:\S*)$/) 14 | return selector + prefix + pseudo 15 | } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /docs/.vitepress/config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitepress' 2 | import { version } from '../../package.json' 3 | 4 | export default defineConfig({ 5 | title: 'Vue Tailwind Datepicker', 6 | lastUpdated: true, 7 | description: 'Components, composables and configurations for Vue 3', 8 | themeConfig: { 9 | search: { 10 | provider: 'local' 11 | }, 12 | /* logo: 'https://github.com/elreco/vue-tailwind-datepicker/blob/main/docs/logo.png?raw=true', */ 13 | socialLinks: [{ icon: 'github', link: 'https://github.com/elreco/vue-tailwind-datepicker' }], 14 | footer: { 15 | message: 'Released under the MIT License.', 16 | copyright: `Copyright © ${new Date().getFullYear()} Alexandre Le Corre` 17 | }, 18 | sidebar: [ 19 | { 20 | text: 'Get Started', 21 | items: [ 22 | { text: 'Installation', link: '/installation' }, 23 | { text: 'Demo', link: '/demo' } 24 | ] 25 | }, 26 | { 27 | text: 'Customization', 28 | items: [ 29 | { text: 'Theming options', link: '/theming-options' }, 30 | { text: 'Props', link: '/props' }, 31 | { text: 'Events', link: '/events' }, 32 | { text: 'Advanced Features', link: '/advanced-features' }, 33 | ] 34 | }, 35 | ], 36 | nav: [ 37 | { text: 'Home', link: '/' }, 38 | { text: 'Get Started', link: '/installation' }, 39 | { text: 'Demo', link: '/demo' }, 40 | { 41 | text: version, 42 | items: [ 43 | { 44 | text: 'Changelog', 45 | link: 'https://github.com/elreco/vue-tailwind-datepicker/releases' 46 | }, 47 | ] 48 | } 49 | ] 50 | }, 51 | vite: { 52 | optimizeDeps: { 53 | include: ['@headlessui/vue', 'dayjs'], 54 | exclude: [] 55 | }, 56 | } 57 | }); 58 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/index.js: -------------------------------------------------------------------------------- 1 | import './tailwind.postcss' 2 | 3 | import DefaultTheme from 'vitepress/theme' 4 | export default { ...DefaultTheme } -------------------------------------------------------------------------------- /docs/.vitepress/theme/tailwind.postcss: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --vp-z-index-nav: 10 !important; 7 | } -------------------------------------------------------------------------------- /docs/.vitepress/theme/tailwind.postcss:Zone.Identifier: -------------------------------------------------------------------------------- 1 | [ZoneTransfer] 2 | ZoneId=3 3 | -------------------------------------------------------------------------------- /docs/DemoLayout.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | -------------------------------------------------------------------------------- /docs/advanced-features.md: -------------------------------------------------------------------------------- 1 | 52 | 53 | # Advanced Features 54 | 55 | You can also access to advanced features like if you need different `model` values, apply a different language or customizing your shortcuts. 56 | 57 | ## Use Array 58 | 59 | 60 | 63 | 64 | 65 | ```vue 66 | 71 | 72 | 75 | ``` 76 | 77 | ## Use Object 78 | 79 | 80 | 83 | 84 | 85 | ```vue 86 | 94 | 95 | 98 | ``` 99 | 100 | ## Use String 101 | 102 | 103 | 106 | 107 | 108 | ```vue 109 | 114 | 115 | 118 | ``` 119 | 120 | ## Custom shortcuts 121 | 122 | Create your custom shortcuts. 123 | 124 | 125 | 129 | 130 | 131 | ```vue 132 | 154 | 155 | 158 | ``` 159 | 160 | ## Localization (i18n) 161 | 162 | Vue Tailwind Datepicker extend to day.js
163 | [List of supported locales](https://github.com/iamkun/dayjs/tree/dev/src/locale) 164 | 165 | 166 | 172 | 173 | 174 | ```vue 175 | 192 | 193 | 201 | ``` 202 | -------------------------------------------------------------------------------- /docs/dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elreco/vue-tailwind-datepicker/9ac422205c39c0946dfaf0a24d00830ef7d6202f/docs/dark.png -------------------------------------------------------------------------------- /docs/demo.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | # Demo 10 | 11 | ::: tip 12 | You can also test it with [the playground](https://stackblitz.com/edit/vue-tailwind-datepicker?file=src/components/Playground.vue)! 13 | ::: 14 | 15 | 16 | 19 | 20 | 21 | ```vue 22 | 28 | 29 | 32 | ``` 33 | -------------------------------------------------------------------------------- /docs/events.md: -------------------------------------------------------------------------------- 1 | 32 | 33 | # Events 34 | 35 | ## selectMonth 36 | 37 | Changed month event from dropdown for left/single calendar. 38 | 39 | 40 | 44 | 45 | 46 | 51 | 52 | 53 | ```vue 54 | 62 | 63 | 74 | ``` 75 | 76 | ::: info 77 | The same works with `no-input` prop. 78 | ::: 79 | 80 | ## selectYear 81 | 82 | Changed year event from dropdown for left/single calendar. 83 | 84 | 85 | 89 | 90 | 91 | 96 | 97 | 98 | ```vue 99 | 107 | 108 | 119 | ``` 120 | 121 | ::: info 122 | The same works with `no-input` prop. 123 | ::: 124 | 125 | ## selectRightMonth 126 | 127 | Changed month event from dropdown for right calendar. 128 | 129 | 130 | 134 | 135 | 136 | ```vue 137 | 145 | 146 | 152 | ``` 153 | 154 | ::: info 155 | The same works with `no-input` prop. 156 | ::: 157 | 158 | ## selectRightYear 159 | 160 | Changed year event from dropdown for right calendar. 161 | 162 | 163 | 167 | 168 | 169 | ```vue 170 | 178 | 179 | 185 | ``` 186 | 187 | ::: info 188 | The same works with `no-input` prop. 189 | ::: 190 | 191 | ## clickPrev 192 | 193 | Click previous button event for left/single calendar. 194 | 195 | 196 | 200 | 201 | 202 | 207 | 208 | 209 | ```vue 210 | 218 | 219 | 230 | ``` 231 | 232 | ::: info 233 | The same works with `no-input` prop. 234 | ::: 235 | 236 | ## clickNext 237 | 238 | Click next button event for left/single calendar. 239 | 240 | 241 | 245 | 246 | 247 | 252 | 253 | 254 | ```vue 255 | 263 | 264 | 275 | ``` 276 | 277 | ::: info 278 | The same works with `no-input` prop. 279 | ::: 280 | 281 | ## clickRightPrev 282 | 283 | Click previous button event for right calendar. 284 | 285 | 286 | 290 | 291 | 292 | ```vue 293 | 301 | 302 | 308 | ``` 309 | 310 | ::: info 311 | The same works with `no-input` prop. 312 | ::: 313 | 314 | ## clickRightNext 315 | 316 | Click next button event for right calendar. 317 | 318 | 319 | 323 | 324 | 325 | ```vue 326 | 334 | 335 | 341 | ``` 342 | 343 | ::: info 344 | The same works with `no-input` prop. 345 | ::: 346 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | 4 | hero: 5 | name: Vue Tailwind Datepicker 6 | tagline: A Datepicker component for Vue 3 using Tailwind and dayjs. 7 | image: 8 | src: https://github.com/elreco/vue-tailwind-datepicker/blob/main/docs/logo.png?raw=true 9 | alt: Hey Brick 10 | actions: 11 | - theme: brand 12 | text: Get Started 13 | link: /installation 14 | - theme: alt 15 | text: View on GitHub 16 | link: https://github.com/elreco/vue-tailwind-datepicker 17 | - theme: alt 18 | text: Playground 19 | link: https://stackblitz.com/edit/vue-tailwind-datepicker?file=src/components/Playground.vue 20 | features: 21 | - icon: ⚡️ 22 | title: Powerful 23 | details: A Vue 3 components which offers a great range of features. 24 | - icon: 🖖 25 | title: Customizable 26 | details: Supports multiple modes such as range, single date, various pickers, shortcuts and many more. 27 | - icon: 🛠️ 28 | title: Tailwind CSS 29 | details: Get a modern datepicker component using Tailwind CSS. 30 | --- 31 | -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | ::: warning 4 | ⚠️ Vue Tailwind Datepicker uses Tailwind CSS (with the @tailwindcss/forms plugin) & Day.js under the hood, **you must install those packages** before. 5 | You can follow [this tutorial](https://dev.to/elreco/add-a-tailwind-datepicker-to-your-vue-3-application-57j2). 6 | ::: 7 | 8 | ## Install via npm 9 | 10 | ``` 11 | $ npm install vue-tailwind-datepicker 12 | ``` 13 | 14 | ## Install via yarn 15 | 16 | ``` 17 | $ yarn add vue-tailwind-datepicker 18 | ``` 19 | 20 | ## How it works 21 | 22 | Setup the component globally 23 | 24 | ```js 25 | // main.js 26 | import { createApp } from "vue"; 27 | import App from "@/App.vue"; 28 | import VueTailwindDatepicker from "vue-tailwind-datepicker"; 29 | // ... 30 | 31 | const app = createApp(App); 32 | 33 | app.use(VueTailwindDatepicker); 34 | app.mount("#app"); 35 | ``` 36 | 37 | Setup as a single component 38 | 39 | ```vue 40 | 41 | 47 | 48 | 51 | ``` 52 | 53 | ## Add Tailwind CSS configuration 54 | 55 | ```js 56 | // tailwind.config.js 57 | const colors = require("tailwindcss/colors"); 58 | 59 | module.exports = { 60 | content: [ 61 | "./index.html", 62 | "./src/**/*.{vue,js,ts,jsx,tsx}", 63 | "./node_modules/vue-tailwind-datepicker/**/*.js", 64 | ], 65 | theme: { 66 | extend: { 67 | colors: { 68 | "vtd-primary": colors.sky, // Light mode Datepicker color 69 | "vtd-secondary": colors.gray, // Dark mode Datepicker color 70 | }, 71 | }, 72 | }, 73 | plugins: [require("@tailwindcss/forms")], 74 | }; 75 | ``` 76 | 77 | ## Setup in Nuxt3 78 | 79 | 1. Install Vue Tailwind Datepicker along with Tailwind CSS and Day.js. 80 | 81 | 2. Create a plugin for Vue Tailwind Datepicker in the plugins directory of your Nuxt project. For example, vue-tailwind-datepicker.js: 82 | 83 | ```js 84 | import { defineNuxtPlugin } from '#app' 85 | import VueTailwindDatepicker from 'vue-tailwind-datepicker' 86 | 87 | export default defineNuxtPlugin((nuxtApp) => { 88 | nuxtApp.vueApp.component('VueTailwindDatepicker', VueTailwindDatepicker) 89 | }) 90 | ``` 91 | 92 | 3. Register the plugin in your nuxt.config.js: 93 | 94 | ```js 95 | export default { 96 | plugins: [ 97 | '~/plugins/vue-tailwind-datepicker.js' 98 | ], 99 | } 100 | ``` 101 | 102 | 4. Now, you can use the `` component in any of your Nuxt pages or components. 103 | -------------------------------------------------------------------------------- /docs/light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elreco/vue-tailwind-datepicker/9ac422205c39c0946dfaf0a24d00830ef7d6202f/docs/light.png -------------------------------------------------------------------------------- /docs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elreco/vue-tailwind-datepicker/9ac422205c39c0946dfaf0a24d00830ef7d6202f/docs/logo.png -------------------------------------------------------------------------------- /docs/props.md: -------------------------------------------------------------------------------- 1 | 45 | 46 | # Props 47 | 48 | All available `props`, to setting up Vue Tailwind Datepicker. 49 | 50 | ## Default Configuration 51 | 52 | The datepicker if you don't set any `props`. 53 | 54 | 55 | 58 | 59 | 60 | ```vue 61 | 66 | 67 | 70 | ``` 71 | 72 | ## Overlay 73 | 74 | Using datepicker with backdrop, by default `overlay` is false. 75 | 76 | 77 | 81 | 82 | 83 | ```vue 84 | 89 | 90 | 93 | ``` 94 | 95 | ## Input classes 96 | 97 | You can apply apply your own input classes using Tailwind CSS. 98 | 99 | 100 | 104 | 105 | 106 | ```vue 107 | 112 | 113 | 119 | ``` 120 | 121 | ## Disabled 122 | 123 | The datepicker can be fully disabled as well. 124 | 125 | 126 | 130 | 131 | 132 | ```vue 133 | 138 | 139 | 142 | ``` 143 | 144 | ## Single Date 145 | 146 | Using date picker as single date. 147 | 148 | 149 | 153 | 154 | 155 | ```vue 156 | 161 | 162 | 165 | ``` 166 | 167 | ## Use Range 168 | 169 | Using date picker as single date, but datepicker with range. 170 | 171 | 172 | 177 | 178 | 179 | ```vue 180 | 185 | 186 | 189 | ``` 190 | 191 | ## Placeholder 192 | 193 | Change placeholder, by default placeholder use `formatter.date` object. 194 | 195 | 196 | 200 | 201 | 202 | ```vue 203 | 208 | 209 | 212 | ``` 213 | 214 | ## Separator 215 | 216 | Change placeholder, by default placeholder use `formatter.date` object. 217 | 218 | 219 | 223 | 224 | 225 | ```vue 226 | 231 | 232 | 235 | ``` 236 | 237 | ## Without Input 238 | 239 | Display Datepicker only without input 240 | 241 | 242 | 247 | 248 | 249 | ```vue 250 | 255 | 256 | 259 | ``` 260 | 261 | ## Formatter 262 | 263 | Change formatter, default `formatter`: 264 | 265 | ```js 266 | { 267 | date: 'YYYY-MM-DD HH:mm:ss', 268 | month: 'MMM' 269 | } 270 | ``` 271 | 272 | 273 | 277 | 278 | 279 | ```vue 280 | 289 | 290 | 293 | ``` 294 | 295 | ## Auto apply 296 | 297 | Change auto apply, by default `autoApply` is true. 298 | 299 | 300 | 304 | 305 | 306 | ```vue 307 | 312 | 313 | 316 | ``` 317 | 318 | ## Start from 319 | 320 | Change start from of datepicker, by default `startFrom` is new Date(). 321 | 322 | 323 | 327 | 328 | 329 | ```vue 330 | 336 | 337 | 340 | ``` 341 | 342 | ## Weekdays size 343 | 344 | If you need to use a minimum number of characters for the name of the days of the week (Sun -> Su, Mon -> Mo, etc.), use `min`, by default `weekdaysSize` is `short` (Sun, Mon, etc.). 345 | 346 | 347 | 351 | 352 | 353 | ```vue 354 | 359 | 360 | 363 | ``` 364 | 365 | ## Week number 366 | 367 | If you want to show week number in the calendar 368 | 369 | 370 | 374 | 375 | 376 | 377 | ## Shortcuts 378 | 379 | Display or not the dates shortcuts, default value is true. 380 | 381 | 382 | 386 | 387 | 388 | ```vue 389 | 394 | 395 | 398 | ``` 399 | 400 | ## Disable date 401 | 402 | Disable some dates in range. 403 | 404 | 405 | 409 | 410 | 411 | ```vue 412 | 420 | 421 | 424 | ``` 425 | 426 | ## Slot 427 | 428 | Two slot are available, a global one that surround the full input and a smaller one just for the icon in the input 429 | 430 | ### Global slot 431 | 432 | 433 |
434 | 438 |
439 |
440 | 448 |
449 |
450 | 470 |
471 |
472 |
473 |
474 |
475 | 476 | ```vue 477 | 482 | 483 | 526 | ``` 527 | 528 | ### inputIcon slot 529 | 530 | 531 |
532 | 533 | 536 | 537 |
538 |
539 | 540 | ```vue 541 | 544 | 547 | 548 | ``` 549 | 550 | 551 | ## Options 552 | 553 | Change default options 554 | 555 | 556 | 560 | 561 | 562 | ```vue 563 | 581 | 582 | 589 | ``` 590 | -------------------------------------------------------------------------------- /docs/theming-options.md: -------------------------------------------------------------------------------- 1 | # Theming options 2 | 3 | Theme of Vue Tailwind Datepicker is customizable, so you can customize your theme with any color you want, via Tailwind CSS configuration. And all will work well. 4 | 5 | ```js 6 | const colors = require("tailwindcss/colors"); 7 | 8 | module.exports = { 9 | content: [ 10 | "./index.html", 11 | "./src/**/*.{vue,js,ts,jsx,tsx}", 12 | "./node_modules/vue-tailwind-datepicker/**/*.js", 13 | ], 14 | theme: { 15 | extend: { 16 | colors: { 17 | "vtd-primary": colors.sky, 18 | "vtd-secondary": colors.gray, 19 | }, 20 | }, 21 | }, 22 | plugins: [require("@tailwindcss/forms")], 23 | }; 24 | ``` 25 | 26 | ## Light mode 27 | 28 | Light mode color system using custom color `vtd-primary`. 29 | 30 | ## Dark mode 31 | 32 | Dark mode color system using color palette `vtd-secondary`. Vue Tailwind Datepicker work it well with Tailwind CSS `dark` mode configuration. 33 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-tailwind-datepicker", 3 | "type": "module", 4 | "version": "1.7.3", 5 | "description": "A date range picker component for Vue.js 3 using Tailwind CSS", 6 | "author": { 7 | "name": "elreco", 8 | "email": "alexandre@heypongo.com", 9 | "url": "https://github.com/elreco" 10 | }, 11 | "homepage": "https://vue-tailwind-datepicker.com", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/elreco/vue-tailwind-datepicker.git" 15 | }, 16 | "bugs": { 17 | "url": "https://github.com/elreco/vue-tailwind-datepicker/issues", 18 | "email": "alexandre@heypongo.com" 19 | }, 20 | "keywords": [ 21 | "vue-tailwind-datepicker", 22 | "vue-datepicker", 23 | "vue-daterangepicker", 24 | "vue-daterange-picker", 25 | "tailwind-datepicker", 26 | "tailwind-daterange-picker" 27 | ], 28 | "exports": { 29 | ".": { 30 | "import": "./dist/vue-tailwind-datepicker.js", 31 | "require": "./dist/vue-tailwind-datepicker.umd.cjs", 32 | "types": "./dist/types.d.ts" 33 | } 34 | }, 35 | "main": "./dist/vue-tailwind-datepicker.umd.cjs", 36 | "module": "./dist/vue-tailwind-datepicker.js", 37 | "types": "./dist/types.d.ts", 38 | "files": [ 39 | "dist" 40 | ], 41 | "contributors": [], 42 | "scripts": { 43 | "dev": "vite", 44 | "build": "vite build", 45 | "preview": "vite preview --port 4173", 46 | "lint": "eslint", 47 | "lint:fix": "eslint --fix", 48 | "docs:dev": "vitepress dev docs", 49 | "docs:build": "vitepress build docs", 50 | "docs:serve": "vitepress serve docs", 51 | "typecheck": "vue-tsc --noEmit", 52 | "commit": "cz", 53 | "semantic-release": "semantic-release" 54 | }, 55 | "release": { 56 | "branches": [ 57 | "main" 58 | ], 59 | "plugins": [ 60 | "@semantic-release/changelog", 61 | "@semantic-release/npm", 62 | [ 63 | "@semantic-release/commit-analyzer", 64 | { 65 | "releaseRules": [ 66 | { 67 | "type": "refactor", 68 | "release": "patch" 69 | }, 70 | { 71 | "type": "docs", 72 | "scope": "README", 73 | "release": "patch" 74 | }, 75 | { 76 | "type": "test", 77 | "release": "patch" 78 | }, 79 | { 80 | "type": "style", 81 | "release": "patch" 82 | }, 83 | { 84 | "type": "perf", 85 | "release": "patch" 86 | }, 87 | { 88 | "type": "ci", 89 | "release": "patch" 90 | }, 91 | { 92 | "type": "build", 93 | "release": "patch" 94 | }, 95 | { 96 | "type": "chore", 97 | "release": "patch" 98 | }, 99 | { 100 | "type": "no-release", 101 | "release": false 102 | } 103 | ] 104 | } 105 | ], 106 | [ 107 | "@semantic-release/release-notes-generator", 108 | { 109 | "preset": "conventionalcommits", 110 | "presetConfig": { 111 | "types": [ 112 | { 113 | "type": "feat", 114 | "section": ":sparkles: Features", 115 | "hidden": false 116 | }, 117 | { 118 | "type": "fix", 119 | "section": ":bug: Fixes", 120 | "hidden": false 121 | }, 122 | { 123 | "type": "docs", 124 | "section": ":memo: Documentation", 125 | "hidden": false 126 | }, 127 | { 128 | "type": "style", 129 | "section": ":barber: Code-style", 130 | "hidden": false 131 | }, 132 | { 133 | "type": "refactor", 134 | "section": ":zap: Refactor", 135 | "hidden": false 136 | }, 137 | { 138 | "type": "perf", 139 | "section": ":fast_forward: Performance", 140 | "hidden": false 141 | }, 142 | { 143 | "type": "test", 144 | "section": ":white_check_mark: Tests", 145 | "hidden": false 146 | }, 147 | { 148 | "type": "ci", 149 | "section": ":repeat: CI", 150 | "hidden": false 151 | }, 152 | { 153 | "type": "chore", 154 | "section": ":repeat: Chore", 155 | "hidden": false 156 | } 157 | ] 158 | } 159 | } 160 | ], 161 | [ 162 | "@semantic-release/github", 163 | { 164 | "assets": [] 165 | } 166 | ] 167 | ] 168 | }, 169 | "peerDependencies": { 170 | "dayjs": "^1.11.10", 171 | "tailwindcss": "^3.3.3", 172 | "vue": "^3.3.4" 173 | }, 174 | "dependencies": { 175 | "@vueuse/core": "^10.5.0", 176 | "vite-plugin-css-injected-by-js": "^3.3.0" 177 | }, 178 | "devDependencies": { 179 | "@antfu/eslint-config": "^0.43.1", 180 | "@commitlint/cli": "^17.7.2", 181 | "@commitlint/config-conventional": "^17.7.0", 182 | "@headlessui/vue": "^1.7.16", 183 | "@semantic-release/changelog": "^6.0.3", 184 | "@semantic-release/commit-analyzer": "^11.0.0", 185 | "@semantic-release/git": "^10.0.1", 186 | "@semantic-release/npm": "^11.0.0", 187 | "@semantic-release/release-notes-generator": "^12.0.0", 188 | "@tailwindcss/forms": "^0.5.6", 189 | "@types/fs-extra": "^11.0.2", 190 | "@types/node": "^20.8.4", 191 | "@vitejs/plugin-vue": "^4.4.0", 192 | "autoprefixer": "^10.4.16", 193 | "commitizen": "^4.3.0", 194 | "cz-conventional-changelog": "^3.3.0", 195 | "dayjs": "^1.11.10", 196 | "esbuild": "^0.19.4", 197 | "eslint": "^8.51.0", 198 | "fs-extra": "^11.1.1", 199 | "husky": "^8.0.3", 200 | "postcss": "^8.4.31", 201 | "postcss-prefix-selector": "^1.16.0", 202 | "postcss-preset-env": "^9.2.0", 203 | "semantic-release": "^22.0.5", 204 | "tailwindcss": "^3.3.3", 205 | "vite": "^4.4.11", 206 | "vite-plugin-dts": "3.6.0", 207 | "vitepress": "^1.0.0-rc.29", 208 | "vue": "^3.3.4", 209 | "vue-tsc": "^1.8.15" 210 | }, 211 | "config": { 212 | "commitizen": { 213 | "path": "./node_modules/cz-conventional-changelog" 214 | } 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | module.exports = { 3 | plugins: { 4 | tailwindcss: {}, 5 | autoprefixer: {}, 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 46 | -------------------------------------------------------------------------------- /src/VueTailwindDatePicker.vue: -------------------------------------------------------------------------------- 1 | 1431 | 1432 | 1609 | 1610 | 1633 | -------------------------------------------------------------------------------- /src/assets/base.css: -------------------------------------------------------------------------------- 1 | /* color palette from */ 2 | :root { 3 | --vt-c-white: #ffffff; 4 | --vt-c-white-soft: #f8f8f8; 5 | --vt-c-white-mute: #f2f2f2; 6 | 7 | --vt-c-black: #181818; 8 | --vt-c-black-soft: #222222; 9 | --vt-c-black-mute: #282828; 10 | 11 | --vt-c-indigo: #2c3e50; 12 | 13 | --vt-c-divider-light-1: rgba(60, 60, 60, 0.29); 14 | --vt-c-divider-light-2: rgba(60, 60, 60, 0.12); 15 | --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); 16 | --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); 17 | 18 | --vt-c-text-light-1: var(--vt-c-indigo); 19 | --vt-c-text-light-2: rgba(60, 60, 60, 0.66); 20 | --vt-c-text-dark-1: var(--vt-c-white); 21 | --vt-c-text-dark-2: rgba(235, 235, 235, 0.64); 22 | } 23 | 24 | /* semantic color variables for this project */ 25 | :root { 26 | --color-background: var(--vt-c-white); 27 | --color-background-soft: var(--vt-c-white-soft); 28 | --color-background-mute: var(--vt-c-white-mute); 29 | 30 | --color-border: var(--vt-c-divider-light-2); 31 | --color-border-hover: var(--vt-c-divider-light-1); 32 | 33 | --color-heading: var(--vt-c-text-light-1); 34 | --color-text: var(--vt-c-text-light-1); 35 | 36 | --section-gap: 160px; 37 | } 38 | 39 | @media (prefers-color-scheme: dark) { 40 | :root { 41 | --color-background: var(--vt-c-black); 42 | --color-background-soft: var(--vt-c-black-soft); 43 | --color-background-mute: var(--vt-c-black-mute); 44 | 45 | --color-border: var(--vt-c-divider-dark-2); 46 | --color-border-hover: var(--vt-c-divider-dark-1); 47 | 48 | --color-heading: var(--vt-c-text-dark-1); 49 | --color-text: var(--vt-c-text-dark-2); 50 | } 51 | } 52 | 53 | *, 54 | *::before, 55 | *::after { 56 | box-sizing: border-box; 57 | margin: 0; 58 | position: relative; 59 | font-weight: normal; 60 | } 61 | 62 | body { 63 | min-height: 100vh; 64 | color: var(--color-text); 65 | background: var(--color-background); 66 | transition: 67 | color 0.5s, 68 | background-color 0.5s; 69 | line-height: 1.6; 70 | font-family: 71 | Inter, 72 | -apple-system, 73 | BlinkMacSystemFont, 74 | "Segoe UI", 75 | Roboto, 76 | Oxygen, 77 | Ubuntu, 78 | Cantarell, 79 | "Fira Sans", 80 | "Droid Sans", 81 | "Helvetica Neue", 82 | sans-serif; 83 | font-size: 15px; 84 | text-rendering: optimizeLegibility; 85 | -webkit-font-smoothing: antialiased; 86 | -moz-osx-font-smoothing: grayscale; 87 | } 88 | -------------------------------------------------------------------------------- /src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/main.css: -------------------------------------------------------------------------------- 1 | @import "./base.css"; 2 | 3 | #app { 4 | max-width: 1280px; 5 | margin: 0 auto; 6 | padding: 2rem; 7 | 8 | font-weight: normal; 9 | } 10 | 11 | a, 12 | .green { 13 | text-decoration: none; 14 | color: hsla(160, 100%, 37%, 1); 15 | transition: 0.4s; 16 | } 17 | 18 | @media (hover: hover) { 19 | a:hover { 20 | background-color: hsla(160, 100%, 37%, 0.2); 21 | } 22 | } 23 | 24 | @media (min-width: 1024px) { 25 | body { 26 | display: flex; 27 | place-items: center; 28 | } 29 | 30 | #app { 31 | display: grid; 32 | grid-template-columns: 1fr 1fr; 33 | padding: 0 2rem; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/components/Calendar.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 79 | -------------------------------------------------------------------------------- /src/components/Header.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 81 | -------------------------------------------------------------------------------- /src/components/Month.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 26 | -------------------------------------------------------------------------------- /src/components/Shortcut.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 119 | -------------------------------------------------------------------------------- /src/components/Week.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 20 | -------------------------------------------------------------------------------- /src/components/Year.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 25 | -------------------------------------------------------------------------------- /src/composables/date.ts: -------------------------------------------------------------------------------- 1 | import type { Dayjs } from 'dayjs' 2 | import type { DatePickerDay } from '~/types' 3 | 4 | export default function useDate() { 5 | const usePreviousDate = (date: Dayjs) => { 6 | const display = [] 7 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 8 | const firstDay = date.localeData().firstDayOfWeek() 9 | for (let i = 0; i <= date.date(0 - firstDay).day(); i++) 10 | display.push(date.date(0).subtract(i, 'day')) 11 | 12 | return display.sort((a, b) => a.date() - b.date()) 13 | } 14 | 15 | const useCurrentDate = (date: Dayjs) => { 16 | return Array.from( 17 | { 18 | length: date.daysInMonth(), 19 | }, 20 | (v, k) => date.date(k + 1), 21 | ) 22 | } 23 | 24 | const useNextDate = (date: Dayjs) => { 25 | const display = [] 26 | for ( 27 | let i = 1; 28 | i <= 42 - (usePreviousDate(date).length + date.daysInMonth()); 29 | i++ 30 | ) 31 | display.push(date.date(i).month(date.month()).add(1, 'month')) 32 | 33 | return display 34 | } 35 | 36 | const useDisableDate = ( 37 | date: Dayjs, 38 | { disableDate }: { disableDate: boolean | ((date: Date) => boolean) }, 39 | ) => { 40 | if (typeof disableDate === 'function') 41 | return disableDate(date.toDate()) 42 | else return false 43 | } 44 | 45 | const useBetweenRange = ( 46 | date: DatePickerDay, 47 | { previous, next }: { previous: Dayjs; next: Dayjs }, 48 | ) => { 49 | const pattern = previous.isAfter(next, 'date') ? '(]' : '[)' 50 | 51 | return !!(date.isBetween(previous, next, 'date', pattern) && !date.off) 52 | } 53 | 54 | const useToValueFromString = ( 55 | date: Dayjs, 56 | { formatter }: { formatter: { date: string; month: string } }, 57 | ) => { 58 | return date.format(formatter.date) 59 | } 60 | 61 | const useToValueFromArray = ( 62 | { previous, next }: { previous: Dayjs; next: Dayjs }, 63 | { 64 | formatter, 65 | separator, 66 | }: { formatter: { date: string; month: string }; separator: string }, 67 | ) => { 68 | return `${previous.format(formatter.date)}${separator}${next.format( 69 | formatter.date, 70 | )}` 71 | } 72 | return { 73 | usePreviousDate, 74 | useCurrentDate, 75 | useNextDate, 76 | useDisableDate, 77 | useBetweenRange, 78 | useToValueFromString, 79 | useToValueFromArray, 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/composables/dom.ts: -------------------------------------------------------------------------------- 1 | export default function useDom() { 2 | const useVisibleViewport = (el: HTMLElement | null) => { 3 | if (el) { 4 | const { right } = el.getBoundingClientRect() 5 | const vWidth = window.innerWidth || document.documentElement.clientWidth 6 | return right > vWidth 7 | } 8 | else { 9 | return null 10 | } 11 | } 12 | 13 | return { 14 | useVisibleViewport, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/entry.esm.ts: -------------------------------------------------------------------------------- 1 | // Import vue component 2 | import type { App } from 'vue' 3 | import component from './VueTailwindDatePicker.vue' 4 | 5 | // Default export is installable instance of component. 6 | // IIFE injects install function into component, allowing component 7 | // to be registered via Vue.use() as well as Vue.component(), 8 | export default /* #__PURE__ */ (() => { 9 | // Get component instance 10 | const installable = component 11 | 12 | // Attach install function executed by Vue.use() 13 | installable.install = (app: App) => { 14 | app.component('VueTailwindDatepicker', installable) 15 | } 16 | return installable 17 | })() 18 | -------------------------------------------------------------------------------- /src/entry.ts: -------------------------------------------------------------------------------- 1 | // iife/cjs usage extends esm default export - so import it all 2 | import component, * as namedExports from './entry.esm' 3 | 4 | // Attach named exports directly to component. IIFE/CJS will 5 | // only expose one global var, with named exports exposed as properties of 6 | // that global var (eg. plugin.namedExport) 7 | Object.entries(namedExports).forEach(([exportName, exported]) => { 8 | if (exportName !== 'default') 9 | component[exportName] = exported 10 | }) 11 | 12 | export default component 13 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /src/keys.ts: -------------------------------------------------------------------------------- 1 | import type { InjectionKey, Ref } from 'vue' 2 | import type { Dayjs } from 'dayjs' 3 | import type { DatePickerDay } from './types' 4 | 5 | const isBetweenRangeKey: InjectionKey<(date: DatePickerDay) => boolean> = 6 | Symbol('isBetweenRange') 7 | const betweenRangeClassesKey: InjectionKey<(date: Dayjs) => string> = Symbol( 8 | 'betweenRangeClasses', 9 | ) 10 | const datepickerClassesKey: InjectionKey< 11 | (date: DatePickerDay) => string | undefined 12 | > = Symbol('datepickerClasses') 13 | const atMouseOverKey: InjectionKey<(date: Dayjs) => false | undefined> = 14 | Symbol('atMouseOver') 15 | const setToTodayKey: InjectionKey< 16 | (close?: ((ref?: Ref | HTMLElement) => void) | undefined) => void 17 | > = Symbol('setToToday') 18 | const setToYesterdayKey: InjectionKey< 19 | (close?: ((ref?: Ref | HTMLElement) => void) | undefined) => void 20 | > = Symbol('setToYesterday') 21 | const setToLastDayKey: InjectionKey< 22 | (day: number, close?: ((ref?: Ref | HTMLElement) => void) | undefined) => void 23 | > = Symbol('setToLastDay') 24 | const setToThisMonthKey: InjectionKey< 25 | (close?: ((ref?: Ref | HTMLElement) => void) | undefined) => void 26 | > = Symbol('setToThisMonth') 27 | const setToLastMonthKey: InjectionKey< 28 | (close?: ((ref?: Ref | HTMLElement) => void) | undefined) => void 29 | > = Symbol('setToLastMonth') 30 | const setToCustomShortcutKey: InjectionKey< 31 | ( 32 | item: { label: string; atClick: () => Date[] }, 33 | close?: (ref?: Ref | HTMLElement) => void, 34 | ) => void 35 | > = Symbol('setToCustomShortcut') 36 | 37 | export { 38 | isBetweenRangeKey, 39 | betweenRangeClassesKey, 40 | datepickerClassesKey, 41 | atMouseOverKey, 42 | setToTodayKey, 43 | setToYesterdayKey, 44 | setToLastDayKey, 45 | setToThisMonthKey, 46 | setToLastMonthKey, 47 | setToCustomShortcutKey, 48 | } 49 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | 4 | import './index.css' 5 | 6 | createApp(App).mount('#app') 7 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import type { Dayjs } from 'dayjs' 2 | 3 | export type LengthArray< 4 | T, 5 | N extends number, 6 | R extends T[] = [], 7 | > = number extends N 8 | ? T[] 9 | : R['length'] extends N 10 | ? R 11 | : LengthArray 12 | 13 | export interface DatePickerDay extends Dayjs { 14 | today: boolean 15 | active: boolean 16 | off: boolean 17 | sunday: boolean 18 | disabled: boolean 19 | inRange: boolean | undefined 20 | hovered: boolean 21 | duration: boolean 22 | } 23 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import type { InjectionKey } from 'vue' 2 | import { inject } from 'vue' 3 | 4 | export function injectStrict(key: InjectionKey, fallback?: T) { 5 | const resolved = inject(key, fallback) 6 | 7 | if (!resolved) 8 | throw new Error(`Could not resolve ${key.description}`) 9 | 10 | return resolved 11 | } 12 | 13 | export const localesMap = Object.fromEntries( 14 | Object.entries(import.meta.glob('../node_modules/dayjs/esm/locale/*.js', { import: 'default' })).map( 15 | ([path, loadLocale]) => [path.match(/([\w-]*)\.js$/)?.[1], loadLocale], 16 | ), 17 | ) as Record Promise> 18 | 19 | -------------------------------------------------------------------------------- /src/vue-shim.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.vue" { 2 | import { type DefineComponent } from "vue"; 3 | const component: DefineComponent<{}, {}, any>; 4 | export default component; 5 | } 6 | -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss'; 2 | import { cyan, fuchsia, gray, lime, orange, sky } from 'tailwindcss/colors'; 3 | import { fontFamily } from 'tailwindcss/defaultTheme'; 4 | import TailwindFormPlugin from '@tailwindcss/forms' 5 | 6 | export default { 7 | content: [ 8 | "./index.html", 9 | "./src/**/*.{vue,js,ts,jsx,tsx}", 10 | "./docs/**/*.{js,ts,vue,md}", 11 | ], 12 | darkMode: "class", 13 | theme: { 14 | extend: { 15 | colors: { 16 | cyan, 17 | fuchsia, 18 | lime, 19 | orange, 20 | "light-blue": sky, 21 | "vtd-primary": sky, 22 | "vtd-secondary": gray, 23 | }, 24 | fontFamily: { 25 | sans: ["Inter", ...fontFamily.sans], 26 | }, 27 | opacity: { 28 | 85: '0.85', 29 | }, 30 | }, 31 | }, 32 | plugins: [TailwindFormPlugin()], 33 | } satisfies Config; 34 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["DOM", "ESNext"], 5 | "jsx": "preserve", 6 | "module": "ESNext", 7 | "moduleResolution": "Bundler", 8 | "baseUrl": ".", 9 | "paths": { 10 | "~/*": ["src/*"] 11 | }, 12 | "types": ["node", "vite/client"], 13 | "resolveJsonModule": true, 14 | "allowJs": true, 15 | "isolatedModules": true, 16 | "esModuleInterop": true, 17 | "forceConsistentCasingInFileNames": true, 18 | "strict": true, 19 | "strictNullChecks": true, 20 | "noUnusedLocals": true, 21 | "skipLibCheck": true 22 | }, 23 | "exclude": [ 24 | "dist", 25 | "node_modules", 26 | "cypress", 27 | "postcss.config.cjs", 28 | "tailwind.config.ts" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import path from "node:path"; 2 | import { defineConfig } from "vite"; 3 | import vue from "@vitejs/plugin-vue"; 4 | import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js"; 5 | import dts from "vite-plugin-dts"; 6 | 7 | // https://vitejs.dev/config/ 8 | export default defineConfig({ 9 | build: { 10 | lib: { 11 | entry: path.resolve(__dirname, "src/entry.ts"), 12 | name: "VueTailwindDatepicker", 13 | fileName: "vue-tailwind-datepicker", 14 | }, 15 | rollupOptions: { 16 | external: ["vue"], 17 | output: { 18 | // Provide global variables to use in the UMD build 19 | // Add external deps here 20 | globals: { 21 | vue: "Vue", 22 | }, 23 | }, 24 | }, 25 | }, 26 | plugins: [vue(), cssInjectedByJsPlugin(), dts({ rollupTypes: true })], 27 | }) 28 | --------------------------------------------------------------------------------