├── .husky ├── .gitignore ├── pre-commit └── post-merge ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── issue-template.md │ ├── feature-request.md │ └── bug-report.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── unit.yml ├── .prettierignore ├── docs ├── src │ ├── styles │ │ ├── styles.scss │ │ ├── prism │ │ │ ├── prism.scss │ │ │ ├── _line-numbers.scss │ │ │ └── _theme.scss │ │ └── _base.scss │ ├── examples │ │ ├── range │ │ │ ├── index.js │ │ │ └── range.svelte │ │ ├── theme │ │ │ ├── index.js │ │ │ └── theme.svelte │ │ ├── single │ │ │ ├── index.js │ │ │ └── single.svelte │ │ ├── presets │ │ │ ├── index.js │ │ │ └── presets.svelte │ │ └── index.js │ ├── index.js │ ├── config.js │ └── App.svelte ├── favicon.png ├── .gitignore ├── package.json ├── vite.config.js └── index.html ├── src ├── index.js ├── index.d.ts ├── actions.js ├── datepicker.test.js ├── datepicker.d.ts └── datepicker.svelte ├── .eslintignore ├── svelte.config.js ├── vitest.setup.js ├── .lintstagedrc ├── .editorconfig ├── .gitignore ├── .prettierrc.json ├── vite.config.js ├── tsconfig.json ├── CHANGELOG.md ├── .eslintrc.json ├── LICENSE ├── package.json └── README.md /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @dysfunc 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | package.json 2 | /src/**/*.snap.js 3 | -------------------------------------------------------------------------------- /docs/src/styles/styles.scss: -------------------------------------------------------------------------------- 1 | @import "./base"; 2 | @import "./prism/prism"; 3 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export { default as DatePicker } from './datepicker.svelte'; 2 | -------------------------------------------------------------------------------- /src/index.d.ts: -------------------------------------------------------------------------------- 1 | export { default as DatePicker } from './datepicker.svelte'; 2 | -------------------------------------------------------------------------------- /docs/src/styles/prism/prism.scss: -------------------------------------------------------------------------------- 1 | @import "./line-numbers"; 2 | @import "./theme"; 3 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | config 2 | node_modules 3 | scripts 4 | src/**/*.snap.js 5 | .eslintrc.js 6 | -------------------------------------------------------------------------------- /docs/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svelte-plugins/datepicker/HEAD/docs/favicon.png -------------------------------------------------------------------------------- /docs/src/examples/range/index.js: -------------------------------------------------------------------------------- 1 | export { default as RangePicker } from './range.svelte'; 2 | -------------------------------------------------------------------------------- /docs/src/examples/theme/index.js: -------------------------------------------------------------------------------- 1 | export { default as ThemePicker } from './theme.svelte'; 2 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | !.gitignore 3 | *.log 4 | 5 | # generated 6 | build 7 | /node_modules 8 | -------------------------------------------------------------------------------- /docs/src/examples/single/index.js: -------------------------------------------------------------------------------- 1 | export { default as SinglePicker } from './single.svelte'; 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . "$(dirname "$0")/_/husky.sh" 4 | 5 | pnpm dlx lint-staged 6 | -------------------------------------------------------------------------------- /docs/src/examples/presets/index.js: -------------------------------------------------------------------------------- 1 | export { default as PresetsOnlyPicker } from './presets.svelte'; 2 | -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; 2 | 3 | export default { 4 | preprocess: vitePreprocess() 5 | }; 6 | -------------------------------------------------------------------------------- /docs/src/index.js: -------------------------------------------------------------------------------- 1 | import App from './app.svelte' 2 | 3 | import './styles/styles.scss'; 4 | 5 | const app = new App({ target: document.body }); 6 | 7 | export default app; 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Issue 3 | about: Track a new feature, enhancement, or general task. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | -------------------------------------------------------------------------------- /vitest.setup.js: -------------------------------------------------------------------------------- 1 | import { afterEach, vi } from 'vitest'; 2 | import { cleanup } from '@testing-library/svelte'; 3 | 4 | import '@testing-library/jest-dom/vitest'; 5 | 6 | afterEach(cleanup); 7 | -------------------------------------------------------------------------------- /.lintstagedrc: -------------------------------------------------------------------------------- 1 | { 2 | "./src/**/*.{test.js,js,ts,json,svelte}": [ 3 | "svelte-check --tsconfig ./tsconfig.json", 4 | "eslint -c ./.eslintrc.json --fix", 5 | "prettier --write" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /docs/src/examples/index.js: -------------------------------------------------------------------------------- 1 | export { SinglePicker } from './single'; 2 | export { PresetsOnlyPicker } from './presets'; 3 | export { RangePicker } from './range'; 4 | export { ThemePicker } from './theme'; 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | insert_final_newline = true 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | end_of_line = lf 8 | 9 | [*.{js,json}] 10 | indent_style = space 11 | indent_size = 2 12 | -------------------------------------------------------------------------------- /.husky/post-merge: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # git hook to run a command after `git pull` if a specified file was changed 4 | # Run `chmod +x .husky/post-merge` to make it executable then put it into `.git/hooks/`. 5 | 6 | changes="$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)" 7 | 8 | check() { 9 | echo "$changes" | grep --quiet "$1" && eval "$2" 10 | } 11 | 12 | check package.json "pnpm install" 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | 3 | # except 4 | !.editorconfig 5 | !.eslintignore 6 | !.eslintrc.json 7 | !.github 8 | !.gitignore 9 | !.gitkeep 10 | !.lintstagedrc 11 | !.node-version 12 | !.npmignore 13 | !.npmrc 14 | !.nvmrc 15 | !.prettierignore 16 | !.prettierrc.json 17 | !.lintstagedrc 18 | !.husky 19 | !.storybook 20 | 21 | # generated 22 | .vercel 23 | /node_modules 24 | /dist 25 | /package-lock.json 26 | 27 | # testing 28 | coverage 29 | artifacts 30 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "type": "module", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "serve": "vite preview" 8 | }, 9 | "devDependencies": { 10 | "@svelte-plugins/datepicker": "../", 11 | "@sveltejs/vite-plugin-svelte": "^3.0.1", 12 | "autoprefixer": "^10.4.16", 13 | "date-fns": "^2.30.0", 14 | "sass": "^1.69.5", 15 | "svelte": "^4.2.7", 16 | "svelte-prismjs": "^1.0.2", 17 | "vite": "^5.0.2" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "trailingComma": "none", 4 | "tabWidth": 2, 5 | "semi": true, 6 | "bracketSpacing": true, 7 | "arrowParens": "always", 8 | "singleQuote": true, 9 | "svelteSortOrder": "options-scripts-markup-styles", 10 | "svelteStrictMode": false, 11 | "svelteBracketNewLine": true, 12 | "svelteAllowShorthand": true, 13 | "plugins": ["prettier-plugin-svelte"], 14 | "overrides": [{ 15 | "files": "*.svelte", 16 | "options": { 17 | "parser": "svelte" 18 | } 19 | }] 20 | } 21 | -------------------------------------------------------------------------------- /src/actions.js: -------------------------------------------------------------------------------- 1 | export const clickOutside = (node, config = {}) => { 2 | const options = { 3 | include: [], 4 | onClickOutside: () => {}, 5 | ...config 6 | }; 7 | 8 | const detect = ({ target }) => { 9 | if (!node.contains(target) || options.include.some((i) => target.isSameNode(i))) { 10 | options.onClickOutside(); 11 | } 12 | }; 13 | 14 | document.addEventListener('click', detect, { passive: true, capture: true }); 15 | 16 | return { 17 | destroy() { 18 | document.removeEventListener('click', detect); 19 | } 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /docs/src/styles/_base.scss: -------------------------------------------------------------------------------- 1 | html { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | body { 7 | box-sizing: border-box; 8 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif; 9 | margin: 0; 10 | padding: 0; 11 | position: relative; 12 | } 13 | 14 | html, 15 | body { 16 | background-color: #fff; 17 | height: 100%; 18 | } 19 | 20 | .github-ribbon { 21 | position: fixed; 22 | right: 0; 23 | top: 0; 24 | z-index: 100; 25 | } 26 | 27 | .github-ribbon img { 28 | height: 125px !important; 29 | width: 125px !important; 30 | } 31 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import { svelte } from '@sveltejs/vite-plugin-svelte'; 3 | 4 | export default defineConfig({ 5 | plugins: [svelte({ emitCss: false })], 6 | test: { 7 | globals: true, 8 | environment: 'jsdom', 9 | setupFiles: ['vitest.setup.js'], 10 | testTimeout: 20000, 11 | alias: [ 12 | { 13 | find: /^svelte$/, 14 | replacement: 'svelte/internal' 15 | } 16 | ], 17 | include: ['./src/**/*.test.js'], 18 | resolveSnapshotPath: (testPath, snapExtension) => testPath.replace(/\.test\.([tj]s?)/, `${snapExtension}.$1`) 19 | } 20 | }); 21 | 22 | -------------------------------------------------------------------------------- /docs/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import { svelte } from '@sveltejs/vite-plugin-svelte'; 3 | import autoprefixer from 'autoprefixer' 4 | 5 | export default defineConfig(({ command, mode }) => { 6 | const isProduction = mode === 'production' 7 | 8 | return { 9 | base: '', 10 | css: { 11 | postcss: { 12 | plugins: [ 13 | autoprefixer({ 14 | overrideBrowserslist: ['last 1 version', 'ie >= 11'], 15 | }), 16 | ], 17 | }, 18 | }, 19 | build: { 20 | minify: isProduction, 21 | outDir: 'build', 22 | }, 23 | plugins: [ 24 | svelte() 25 | ] 26 | } 27 | }) 28 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/svelte/tsconfig.json", 3 | "include": [ 4 | "src/**/*.d.ts" 5 | ], 6 | "exclude": [ 7 | "node_modules/*", 8 | "src/**/*.test.js", 9 | "src/**/*.js", 10 | "src/**/*.svelte" 11 | ], 12 | "compilerOptions": { 13 | "baseUrl": "./", 14 | "moduleResolution": "node", 15 | "target": "es2022", 16 | "allowJs": false, 17 | "checkJs": false, 18 | "esModuleInterop": true, 19 | "forceConsistentCasingInFileNames": true, 20 | "resolveJsonModule": true, 21 | "skipLibCheck": true, 22 | "sourceMap": true, 23 | "paths": { 24 | "@svelte-plugins/datepicker": ["src/index"] 25 | }, 26 | "types": ["vitest/globals"] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest a new feature or functionality enhancement 4 | title: '✨ ' 5 | labels: 'Type: Feature' 6 | assignees: '' 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.1.2](https://github.com/svelte-plugins/datepicker/releases/tag/v0.1.0) - 2023-11-24 9 | 10 | - 🏗 build(tooling): fix linting, formatting and precommit hooks by 11 | 12 | ## [0.1.1](https://github.com/svelte-plugins/datepicker/releases/tag/v0.1.0) - 2023-11-23 13 | 14 | - refactor(picker): updates to dev tooling and general code cleanup 15 | 16 | ## [0.1.0](https://github.com/svelte-plugins/datepicker/releases/tag/v0.1.0) - 2023-11-22 17 | 18 | - Initial release 19 | -------------------------------------------------------------------------------- /docs/src/styles/prism/_line-numbers.scss: -------------------------------------------------------------------------------- 1 | pre[class*="language-"].line-numbers { 2 | counter-reset: linenumber; 3 | padding-left: 3.8em; 4 | position: relative; 5 | } 6 | 7 | pre[class*="language-"].line-numbers>code { 8 | position: relative; 9 | white-space: inherit; 10 | } 11 | 12 | .line-numbers .line-numbers-rows { 13 | border-right: 0; 14 | font-size: 100%; 15 | left: -3.8em; 16 | letter-spacing: -1px; 17 | position: absolute; 18 | pointer-events: none; 19 | top: 0; 20 | user-select: none; 21 | width: 3em; 22 | } 23 | 24 | .line-numbers-rows>span { 25 | counter-increment: linenumber; 26 | display: block; 27 | } 28 | 29 | .line-numbers-rows>span:before { 30 | color: #495162; 31 | content: counter(linenumber); 32 | display: block; 33 | padding-right: 0.8em; 34 | text-align: right; 35 | } 36 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es2017": true, 4 | "commonjs": true, 5 | "node": true 6 | }, 7 | "parserOptions": { 8 | "ecmaVersion": 2020, 9 | "sourceType": "module" 10 | }, 11 | "plugins": [ 12 | "vitest" 13 | ], 14 | "extends": [ 15 | "eslint:recommended", 16 | "plugin:vitest/recommended" 17 | ], 18 | "ignorePatterns": [ 19 | "**/*.snap.js" 20 | ], 21 | "overrides": [ 22 | { 23 | "files": [ 24 | "*.ts" 25 | ], 26 | "parser": "@typescript-eslint/parser" 27 | }, 28 | { 29 | "files": [ 30 | "*.svelte" 31 | ], 32 | "parser": "svelte-eslint-parser" 33 | } 34 | ], 35 | "rules": { 36 | "eqeqeq": "error", 37 | "no-useless-escape": "off", 38 | "no-unused-expressions": "off", 39 | "no-inner-declarations": "off", 40 | "no-unused-vars": "off", 41 | "no-self-assign": "off", 42 | "no-undef": "off" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report a bug to help us improve the overall user experience 4 | title: '🐛 ' 5 | labels: 'Type: Bug' 6 | assignees: '' 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 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 (please complete the following information):** 27 | 28 | - OS: [e.g. iOS] 29 | - Browser [e.g. chrome, safari] 30 | - Version [e.g. 22] 31 | 32 | **Smartphone (please complete the following information):** 33 | 34 | - Device: [e.g. iPhone6] 35 | - OS: [e.g. iOS8.1] 36 | - Browser [e.g. stock browser, safari] 37 | - Version [e.g. 22] 38 | 39 | **Additional context** 40 | Add any other context about the problem here. 41 | -------------------------------------------------------------------------------- /docs/src/config.js: -------------------------------------------------------------------------------- 1 | const today = new Date(); 2 | 3 | export const defaultConfig = { 4 | startDate: today, 5 | endDate: today, 6 | startDateTime: '00:00', 7 | endDateTime: '00:00', 8 | today, 9 | defaultYear: today.getFullYear(), 10 | defaultMonth: today.getMonth(), 11 | startOfWeek: 0, 12 | isMultipane: false, 13 | isRange: false, 14 | isOpen: false, 15 | align: 'left', 16 | theme: '', 17 | disabledDates: [], 18 | onDayClick: () => { }, 19 | onNavigationChange: () => { }, 20 | showYearControls: true, 21 | showPresets: false, 22 | showTimePicker: false, 23 | enableFutureDates: false, 24 | enablePastDates: true, 25 | presetLabels: ['Today', 'Last 7 Days', 'Last 30 Days', 'Last 60 Days', 'Last 90 Days', 'Last Year'], 26 | dowLabels: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], 27 | monthLabels: [ 28 | 'January', 29 | 'February', 30 | 'March', 31 | 'April', 32 | 'May', 33 | 'June', 34 | 'July', 35 | 'August', 36 | 'September', 37 | 'October', 38 | 'November', 39 | 'December' 40 | ] 41 | }; 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-present Kieran Boyle 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 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Why these changes?** 2 | 3 | **How do we test?** 4 | 5 | **Screenshots** 6 | 7 | 38 | -------------------------------------------------------------------------------- /.github/workflows/unit.yml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | on: 3 | push: 4 | branches: 5 | - main 6 | paths: 7 | - src/** 8 | pull_request: 9 | types: [opened, synchronize, reopened] 10 | jobs: 11 | tests: 12 | runs-on: ubuntu-latest 13 | name: Tests 14 | if: | 15 | ( 16 | !contains(github.event.head_commit.message, '[skip ci]') && 17 | !contains(github.event.head_commit.message, 'version bump') 18 | ) 19 | steps: 20 | - name: Checkout repository 21 | uses: actions/checkout@v3 22 | - name: Install pnpm 23 | uses: pnpm/action-setup@v3 24 | with: 25 | version: 8.11 26 | - name: Install Node 27 | uses: actions/setup-node@v3 28 | with: 29 | node-version: 20 30 | cache: 'pnpm' 31 | registry-url: 'https://registry.npmjs.org' 32 | - name: Install dependencies 33 | run: pnpm install 34 | - name: Set Timezone 35 | uses: szenius/set-timezone@v1.2 36 | with: 37 | timezoneLinux: "America/Los_Angeles" 38 | timezoneMacos: "America/Los_Angeles" 39 | timezoneWindows: "Pacific Standard Time" 40 | - name: Run tests 41 | run: pnpm test 42 | - name: Upload artifacts 43 | uses: actions/upload-artifact@v4 44 | if: failure() 45 | with: 46 | name: unit-tests 47 | path: ${{ github.workspace }}/coverage/ 48 | retention-days: 5 49 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @svelte-plugins/datepicker 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | Fork me on GitHub 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svelte-plugins/datepicker", 3 | "description": "A simple datepicker component designed for Svelte.", 4 | "version": "1.0.11", 5 | "license": "MIT", 6 | "author": "Kieran Boyle (https://github.com/dysfunc)", 7 | "homepage": "https://github.com/svelte-plugins/datepicker", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/svelte-plugins/datepicker.git" 11 | }, 12 | "type": "module", 13 | "types": "./dist/index.d.ts", 14 | "svelte": "./dist/index.js", 15 | "exports": { 16 | ".": { 17 | "types": "./dist/index.d.ts", 18 | "svelte": "./dist/index.js", 19 | "import": "./dist/index.js" 20 | }, 21 | "./package.json": "./package.json" 22 | }, 23 | "files": [ 24 | "dist", 25 | "src" 26 | ], 27 | "keywords": [ 28 | "datepicker", 29 | "timepicker", 30 | "daterange", 31 | "date picker", 32 | "time picker", 33 | "range picker", 34 | "date range", 35 | "calendar", 36 | "date-time", 37 | "picker", 38 | "svelte", 39 | "components", 40 | "javascript" 41 | ], 42 | "scripts": { 43 | "start": "vite & npm --prefix ./docs install && npm --prefix ./docs run dev", 44 | "build": "svelte-package --input ./src && pnpm lint:package", 45 | "build:docs": "npm --prefix ./docs run build", 46 | "deploy:docs": "pnpm build:docs && npx gh-pages -d docs/build", 47 | "check": "svelte-check --tsconfig ./tsconfig.json", 48 | "lint": "eslint -c ./.eslintrc.json --fix \"src/**/*.{test.js,js,ts,json,svelte}\"", 49 | "lint:package": "publint --strict", 50 | "format": "prettier --write \"src/**/*.{test.js,js,ts,json,svelte}\"", 51 | "test": "vitest --run --coverage", 52 | "test:watch": "vitest", 53 | "test:coverage": "vitest --run --coverage && open ./coverage/index.html" 54 | }, 55 | "devDependencies": { 56 | "@sveltejs/package": "^2.2.4", 57 | "@sveltejs/vite-plugin-svelte": "^3.0.1", 58 | "@testing-library/jest-dom": "^6.4.0", 59 | "@testing-library/svelte": "^4.0.5", 60 | "@tsconfig/svelte": "^5.0.2", 61 | "@types/node": "^20.10.5", 62 | "@typescript-eslint/parser": "^6.18.1", 63 | "@vitest/coverage-v8": "^0.34.6", 64 | "eslint": "^8.54.0", 65 | "eslint-plugin-svelte": "^2.35.1", 66 | "eslint-plugin-vitest": "^0.3.10", 67 | "husky": "^6.0.0", 68 | "jsdom": "^23.0.0", 69 | "lint-staged": "^10.5.4", 70 | "prettier": "^3.1.0", 71 | "prettier-plugin-svelte": "^3.1.2", 72 | "publint": "^0.2.7", 73 | "svelte": "^4.2.8", 74 | "svelte-check": "^3.6.2", 75 | "svelte-preprocess": "^5.1.3", 76 | "typescript": "^5.2.2", 77 | "vite": "^5.0.12", 78 | "vitest": "^0.34.6" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /docs/src/examples/single/single.svelte: -------------------------------------------------------------------------------- 1 | 42 | 43 | 44 | 45 | 46 | 47 | 50 | import { DatePicker } from '@svelte-plugins/datepicker'; 51 | import { format } from 'date-fns'; 52 | 53 | let startDate = new Date(); 54 | let dateFormat = 'MM/dd/yy'; 55 | let isOpen = false; 56 | 57 | const toggleDatePicker = () => (isOpen = !isOpen); 58 | 59 | const formatDate = (dateString) => { 60 | if (isNaN(new Date(dateString))) { 61 | return ''; 62 | } 63 | 64 | return dateString && format(new Date(dateString), dateFormat) || ''; 65 | }; 66 | let formattedStartDate = formatDate(startDate); 67 | 68 | const onChange = () => { 69 | startDate = new Date(formattedStartDate); 70 | }; 71 | 72 | $: formattedStartDate = formatDate(startDate); 73 | 74 | 75 | 76 | 77 | 78 | 79 | 86 | `} /> 87 | 88 | 95 | -------------------------------------------------------------------------------- /src/datepicker.test.js: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/svelte'; 2 | import DatePicker from './datepicker.svelte'; 3 | 4 | const today = new Date('1/1/2020'); 5 | 6 | const config = { 7 | startDate: today, 8 | endDate: today, 9 | startDateTime: '00:00', 10 | endDateTime: '00:00', 11 | startOfWeek: 0, 12 | isMultipane: false, 13 | isRange: false, 14 | isOpen: false, 15 | align: 'left', 16 | theme: '', 17 | disabledDates: [], 18 | enabledDates: [], 19 | onDayClick: () => {}, 20 | showYearControls: true, 21 | showPresets: false, 22 | showTimePicker: false, 23 | enableFutureDates: false, 24 | enablePastDates: true, 25 | presetLabels: ['Today', 'Last 7 Days', 'Last 30 Days', 'Last 60 Days', 'Last 90 Days', 'Last Year'], 26 | dowLabels: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], 27 | monthLabels: [ 28 | 'January', 29 | 'February', 30 | 'March', 31 | 'April', 32 | 'May', 33 | 'June', 34 | 'July', 35 | 'August', 36 | 'September', 37 | 'October', 38 | 'November', 39 | 'December' 40 | ] 41 | }; 42 | 43 | vi.setSystemTime(today); 44 | 45 | describe('Components: DatePicker', () => { 46 | let TestHarness; 47 | 48 | beforeEach(() => { 49 | TestHarness = (props = {}) => render(DatePicker, props); 50 | }); 51 | 52 | it('should render a single datepicker', () => { 53 | const { container } = TestHarness(config); 54 | expect(container).toMatchSnapshot(); 55 | }); 56 | 57 | it('should render a range picker', () => { 58 | const { container } = TestHarness({ ...config, isRange: true, isMultipane: true }); 59 | expect(container).toMatchSnapshot(); 60 | }); 61 | 62 | it('should render a date picker with time selection', () => { 63 | const { container } = TestHarness({ ...config, showTimePicker: true }); 64 | expect(container).toMatchSnapshot(); 65 | }); 66 | 67 | it('should render a range picker with presets', () => { 68 | const { container } = TestHarness({ ...config, isRange: true, showPresets: true }); 69 | expect(container).toMatchSnapshot(); 70 | }); 71 | 72 | it('should render a date picker without year controls', () => { 73 | const { container } = TestHarness({ ...config, showYearControls: false }); 74 | expect(container).toMatchSnapshot(); 75 | }); 76 | 77 | it('should render a date picker with Monday as start of week', () => { 78 | const { container } = TestHarness({ ...config, startOfWeek: 1 }); 79 | expect(container).toMatchSnapshot(); 80 | }); 81 | 82 | it('should render a date picker with my custom locale', () => { 83 | const { container } = TestHarness({ 84 | ...config, 85 | dowLabels: ['Au', 'Bo', 'Cu', 'De', 'Eh', 'Fr', 'Ga'], 86 | monthLabels: [ 87 | 'Qanuary', 88 | 'Webruary', 89 | 'Earch', 90 | 'Rpril', 91 | 'Tay', 92 | 'Yune', 93 | 'Uuly', 94 | 'Iugust', 95 | 'Oeptember', 96 | 'Pctober', 97 | 'Aovember', 98 | 'Secember' 99 | ] 100 | }); 101 | 102 | expect(container).toMatchSnapshot(); 103 | }); 104 | }); 105 | -------------------------------------------------------------------------------- /src/datepicker.d.ts: -------------------------------------------------------------------------------- 1 | import type { SvelteComponent } from 'svelte'; 2 | 3 | export interface DatePickerProps { 4 | /** 5 | * Represents the start date for a date picker. 6 | * @default null 7 | */ 8 | startDate?: any; 9 | 10 | /** 11 | * Represents the end date for a date picker. 12 | * @default null 13 | */ 14 | endDate?: any; 15 | 16 | /** 17 | * Represents the start time for the date picker (in HH:mm format). 18 | * @default '00:00' 19 | */ 20 | startDateTime?: string; 21 | 22 | /** 23 | * Represents the end time for the date picker (in HH:mm format). 24 | * @default '00:00' 25 | */ 26 | endDateTime?: string; 27 | 28 | /** 29 | * Represents the current date. 30 | */ 31 | today?: Date; 32 | 33 | /** 34 | * Represents the default year for the date picker. 35 | */ 36 | defaultYear?: number; 37 | 38 | /** 39 | * Represents the default month for the date picker. 40 | */ 41 | defaultMonth?: number; 42 | 43 | /** 44 | * Represents the start day of the week (0 for Sunday, 1 for Monday, etc.). 45 | */ 46 | startOfWeek?: number; 47 | 48 | /** 49 | * Indicates whether the date picker has multiple panes. 50 | */ 51 | isMultipane?: boolean; 52 | 53 | /** 54 | * Indicates whether the date picker is in range mode. 55 | */ 56 | isRange?: boolean; 57 | 58 | /** 59 | * Indicates whether the date picker is open. 60 | */ 61 | isOpen?: boolean; 62 | 63 | /** 64 | * Specifies the alignment of the date picker (e.g., 'left', 'center', 'right'). 65 | */ 66 | align?: string; 67 | 68 | /** 69 | * Represents the theme of the date picker. 70 | */ 71 | theme?: string; 72 | 73 | /** 74 | * An array of disabled dates. 75 | */ 76 | disabledDates?: string[]; 77 | 78 | /** 79 | * An array of enabled dates. 80 | */ 81 | enabledDates?: string[]; 82 | 83 | /** 84 | * Callback function to handle when the date change events. 85 | */ 86 | onDateChange?: (event: Object) => void; 87 | 88 | /** 89 | * Callback function to handle day click events. 90 | */ 91 | onDayClick?: (event: Object) => void; 92 | 93 | /** 94 | * Callback function to handle the navigation click event for months and years 95 | * @type {(event: Object) => void} 96 | */ 97 | onNavigationChange?: (event: Object) => void; 98 | 99 | /** 100 | * Indicates whether the date picker should always be shown. 101 | */ 102 | alwaysShow?: boolean; 103 | 104 | /** 105 | * Indicates whether year controls are displayed in the date picker. 106 | */ 107 | showYearControls?: boolean; 108 | 109 | /** 110 | * Indicates whether preset options are displayed in the date picker. 111 | */ 112 | showPresets?: boolean; 113 | 114 | /** 115 | * Indicates whether preset options should only be shown for range pickers. 116 | */ 117 | showPresetsOnly?: boolean; 118 | 119 | /** 120 | * Indicates whether the time picker is shown in the date picker. 121 | */ 122 | showTimePicker?: boolean; 123 | 124 | /** 125 | * Indicates whether future dates are enabled. 126 | */ 127 | enableFutureDates?: boolean; 128 | 129 | /** 130 | * Indicates whether past dates are enabled. 131 | */ 132 | enablePastDates?: boolean; 133 | 134 | /** 135 | * An array of preset date range labels. 136 | */ 137 | presetLabels?: string[]; 138 | 139 | /** 140 | * An array of preset date ranges with labels and start/end timestamps. 141 | */ 142 | presetRanges?: Object[]; 143 | 144 | /** 145 | * An array of day-of-week labels. 146 | */ 147 | dowLabels?: string[]; 148 | 149 | /** 150 | * An array of month labels. 151 | */ 152 | monthLabels?: string[]; 153 | 154 | /** 155 | * Determines if the default font "Rubik" should be loaded. 156 | */ 157 | includeFont?: boolean; 158 | } 159 | 160 | export interface DatePickerEvents { 161 | [key: string]: any; 162 | } 163 | 164 | export interface DatePickerSlots { 165 | default?: {}; 166 | } 167 | 168 | export default class DatePicker extends SvelteComponent {} 169 | -------------------------------------------------------------------------------- /docs/src/styles/prism/_theme.scss: -------------------------------------------------------------------------------- 1 | code[class*="language-"], 2 | pre[class*="language-"] { 3 | background: #f3f3f3; 4 | color: #e06C75; 5 | direction: ltr; 6 | font-family: 'ui-monospace', 'SFMono-Regular', Menlo, Monaco, Consolas, 'Courier New', monospace; 7 | font-size: 13px; 8 | hyphens: none; 9 | line-height: 1.5; 10 | tab-size: 2; 11 | text-align: left; 12 | text-shadow: none; 13 | white-space: pre; 14 | word-break: normal; 15 | word-spacing: normal; 16 | } 17 | 18 | code[class*="language-"]::-moz-selection, 19 | code[class*="language-"] *::-moz-selection, 20 | pre[class*="language-"] *::-moz-selection { 21 | background: #3e4451; 22 | color: inherit; 23 | text-shadow: none; 24 | } 25 | 26 | code[class*="language-"]::selection, 27 | code[class*="language-"] *::selection, 28 | pre[class*="language-"] *::selection { 29 | background: #3e4451; 30 | color: inherit; 31 | text-shadow: none; 32 | } 33 | 34 | pre[class*="language-"] { 35 | border-radius: 0.3em; 36 | margin: 0.5em 0; 37 | overflow: auto; 38 | padding: 1em; 39 | } 40 | 41 | :not(pre)>code[class*="language-"] { 42 | border-radius: 0.3em; 43 | padding: 0.2em 0.3em; 44 | white-space: normal; 45 | } 46 | 47 | @media print { 48 | code[class*="language-"], 49 | pre[class*="language-"] { 50 | text-shadow: none; 51 | } 52 | } 53 | 54 | .token { 55 | 56 | &.comment, 57 | &.prolog, 58 | &.cdata { 59 | color: #7a7f89; 60 | } 61 | 62 | &.doctype, 63 | &.punctuation, 64 | &.entity { 65 | color: #abb2bf; 66 | } 67 | 68 | &.attr-name, 69 | &.class-name, 70 | &.boolean, 71 | &.constant, 72 | &.number, 73 | &.atrule { 74 | color: #d4b375; 75 | } 76 | 77 | &.keyword { 78 | color: #9661a9; 79 | } 80 | 81 | &.property, 82 | &.tag, 83 | &.symbol, 84 | &.deleted, 85 | &.important { 86 | color: #e06c75; 87 | } 88 | 89 | &.selector, 90 | &.string, 91 | &.char, 92 | &.builtin, 93 | &.inserted, 94 | &.regex, 95 | &.attr-value, 96 | &.attr-value>.token.punctuation { 97 | color: #98c379; 98 | } 99 | 100 | &.variable, 101 | &.operator, 102 | &.function { 103 | color: #61afef; 104 | } 105 | 106 | &.url { 107 | color: #56b6c2; 108 | } 109 | 110 | &.attr-value>.token.punctuation.attr-equals, 111 | &.special-attr>.token.attr-value>.token.value.css { 112 | color: #abb2bf; 113 | } 114 | } 115 | 116 | .language-css { 117 | .token { 118 | &.selector { 119 | color: #e06c75; 120 | } 121 | 122 | &.property { 123 | color: #abb2bf; 124 | } 125 | 126 | &.function, 127 | &.url>.token.function { 128 | color: #56b6c2; 129 | } 130 | 131 | &.token.string.url { 132 | color: #98c379; 133 | } 134 | 135 | &.important, 136 | &.atrule .token.rule { 137 | color: #c678dd; 138 | } 139 | } 140 | } 141 | 142 | .language-javascript { 143 | .token { 144 | &.operator { 145 | color: #c678dd; 146 | } 147 | 148 | &.template-string>.token.interpolation>.token.interpolation-punctuation.punctuation { 149 | color: #be5046; 150 | } 151 | } 152 | } 153 | 154 | .language-json { 155 | .token { 156 | &.operator { 157 | color: #abb2bf; 158 | } 159 | 160 | &.null.keyword { 161 | color: #d19a66; 162 | } 163 | } 164 | } 165 | 166 | .language-markdown { 167 | 168 | .token.url, 169 | .token.url>.token.operator, 170 | .token.url-reference.url>.token.string { 171 | color: #abb2bf; 172 | } 173 | 174 | .token.url>.token.content { 175 | color: #61afef; 176 | } 177 | 178 | .token.url>.token.url, 179 | .token.url-reference.url { 180 | color: #56b6c2; 181 | } 182 | 183 | .token.blockquote.punctuation, 184 | .token.hr.punctuation { 185 | color: #5c6370; 186 | font-style: italic; 187 | } 188 | 189 | .token.code-snippet { 190 | color: #98c379; 191 | } 192 | 193 | .token.bold .token.content { 194 | color: #d19a66; 195 | } 196 | 197 | .token.italic .token.content { 198 | color: #c678dd; 199 | } 200 | 201 | .token.strike .token.content, 202 | .token.strike .token.punctuation, 203 | .token.list.punctuation, 204 | .token.title.important>.token.punctuation { 205 | color: #e06c75; 206 | } 207 | } 208 | 209 | .token { 210 | &.bold { 211 | font-weight: bold; 212 | } 213 | 214 | &.comment, 215 | &.italic { 216 | font-style: italic; 217 | } 218 | 219 | &.entity { 220 | cursor: help; 221 | } 222 | 223 | &.namespace { 224 | opacity: .8; 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /docs/src/examples/presets/presets.svelte: -------------------------------------------------------------------------------- 1 | 49 | 50 |
51 | 62 |
63 | 64 |
65 | {#if startDate} 66 | {formattedStartDate} - {formattedEndDate} 67 | {:else} 68 | Pick a date 69 | {/if} 70 |
71 | {#if startDate} 72 | 73 | 74 | 75 | {/if} 76 |
77 |
78 |
79 | 80 | 82 | import { DatePicker } from '@svelte-plugins/datepicker'; 83 | import { format } from 'date-fns'; 84 | 85 | const today = new Date(); 86 | 87 | const MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000; 88 | 89 | const getDateFromToday = (days) => { 90 | return Date.now() - days * MILLISECONDS_IN_DAY; 91 | }; 92 | 93 | let startDate = getDateFromToday(29); 94 | let endDate = today; 95 | let dateFormat = 'MMM d, yyyy'; 96 | let isOpen = false; 97 | 98 | let formattedStartDate = ''; 99 | 100 | const onClearDates = () => { 101 | startDate = ''; 102 | endDate = ''; 103 | }; 104 | 105 | const onDateChange = (args) => { 106 | console.log(args, 'onDateChange'); 107 | }; 108 | 109 | const toggleDatePicker = () => (isOpen = !isOpen); 110 | 111 | const formatDate = (dateString) => { 112 | if (isNaN(new Date(dateString))) { 113 | return ''; 114 | } 115 | 116 | return dateString && format(new Date(dateString), dateFormat) || ''; 117 | }; 118 | 119 | $: formattedStartDate = formatDate(startDate); 120 | $: formattedEndDate = formatDate(endDate); 121 | 122 | 123 |
124 | 125 |
126 | 127 |
128 | {#if startDate} 129 | {formattedStartDate} - {formattedEndDate} 130 | {:else} 131 | Pick a date 132 | {/if} 133 |
134 | {#if startDate} 135 | 136 | 137 | 138 | {/if} 139 |
140 |
141 |
142 | 143 | 165 | `} /> 166 | 167 | 189 | -------------------------------------------------------------------------------- /docs/src/examples/range/range.svelte: -------------------------------------------------------------------------------- 1 | 51 | 52 |
53 | 64 |
65 | 66 |
67 | {#if startDate} 68 | {formattedStartDate} - {formattedEndDate} 69 | {:else} 70 | Pick a date 71 | {/if} 72 |
73 | {#if startDate} 74 | 75 | 76 | 77 | {/if} 78 |
79 |
80 |
81 | 82 | 84 | import { DatePicker } from '@svelte-plugins/datepicker'; 85 | import { format } from 'date-fns'; 86 | 87 | const today = new Date(); 88 | 89 | const MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000; 90 | 91 | const getDateFromToday = (days) => { 92 | return Date.now() - days * MILLISECONDS_IN_DAY; 93 | }; 94 | 95 | let startDate = getDateFromToday(29); 96 | let endDate = today; 97 | let dateFormat = 'MMM d, yyyy'; 98 | let isOpen = false; 99 | 100 | let formattedStartDate = ''; 101 | 102 | const onClearDates = () => { 103 | startDate = ''; 104 | endDate = ''; 105 | }; 106 | 107 | const toggleDatePicker = () => (isOpen = !isOpen); 108 | 109 | const formatDate = (dateString) => { 110 | if (isNaN(new Date(dateString))) { 111 | return ''; 112 | } 113 | 114 | return dateString && format(new Date(dateString), dateFormat) || ''; 115 | }; 116 | 117 | $: formattedStartDate = formatDate(startDate); 118 | $: formattedEndDate = formatDate(endDate); 119 | 120 | 121 |
122 | 123 |
124 | 125 |
126 | {#if startDate} 127 | {formattedStartDate} - {formattedEndDate} 128 | {:else} 129 | Pick a date 130 | {/if} 131 |
132 | {#if startDate} 133 | 134 | 135 | 136 | {/if} 137 |
138 |
139 |
140 | 141 | 163 | `} /> 164 | 165 | {#if showPresets} 166 |

You can customize the presets by defining your own:

167 | 168 | 190 | `} /> 191 | {/if} 192 | 193 | 215 | -------------------------------------------------------------------------------- /docs/src/examples/theme/theme.svelte: -------------------------------------------------------------------------------- 1 | 35 | 36 |
37 | 46 |
47 | 48 |
49 | {#if startDate} 50 | {formattedStartDate} - {formattedEndDate} 51 | {:else} 52 | Pick a date 53 | {/if} 54 |
55 | {#if startDate} 56 | 57 | 58 | 59 | {/if} 60 |
61 |
62 |
63 | 64 | 67 | import { DatePicker } from '@svelte-plugins/datepicker'; 68 | import { format } from 'date-fns'; 69 | 70 | const today = new Date(); 71 | 72 | const MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000; 73 | 74 | const getDateFromToday = (days) => { 75 | return Date.now() - days * MILLISECONDS_IN_DAY; 76 | }; 77 | 78 | let startDate = getDateFromToday(29); 79 | let endDate = today; 80 | let dateFormat = 'MMM d, yyyy'; 81 | let isOpen = false; 82 | 83 | let formattedStartDate = ''; 84 | 85 | const onClearDates = () => { 86 | startDate = ''; 87 | endDate = ''; 88 | }; 89 | 90 | const toggleDatePicker = () => (isOpen = !isOpen); 91 | const formatDate = (dateString) => dateString && format(new Date(dateString), dateFormat) || ''; 92 | 93 | $: formattedStartDate = formatDate(startDate); 94 | $: formattedEndDate = formatDate(endDate); 95 | 96 | 97 |
98 | 99 |
100 | 101 |
102 | {#if startDate} 103 | {formattedStartDate} - {formattedEndDate} 104 | {:else} 105 | Pick a date 106 | {/if} 107 |
108 | {#if startDate} 109 | 110 | 111 | 112 | {/if} 113 |
114 |
115 |
116 | 117 | 165 | `} /> 166 | 167 | 217 | -------------------------------------------------------------------------------- /docs/src/App.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 |
16 |
17 |

@svelte-plugins/datepicker (dysfunc)

18 |

A simple datepicker component written in Svelte.

19 |
20 | 21 |
22 | 23 |
24 |

Overview

25 | 26 | The DatePicker component allows users to easily select a date or date-range from one or more calendars. 27 | 28 |
29 | 30 |
31 |
32 | 33 |
34 |

Props

35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 164 | 165 | 166 | 167 | 168 | 169 | 184 | 185 | 186 | 187 | 188 | 189 | 201 | 202 | 203 | 204 | 205 | 206 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 236 | 237 | 238 | 239 | 240 | 241 | 258 | 259 | 260 | 261 | 262 | 263 | 274 | 275 | 276 | 277 | 278 | 279 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 |
NameTypeDescriptionDefault
startDateobjectThe start date string or date object.null
endDateobjectThe end date string or date object.null
startDateTimestringThe start date time string in 24 hour format.00:00
endDateTimestringThe end date time string in 24 hour format.00:00
defaultYearnumberThe year you want to show as the default.{today.getFullYear()}
alignstring 77 | The edge alignment of the datepicker. 78 |
Accepted values left or right
79 |
left
isRangebooleanChanges the date picker into a range picker and allows start and end date selection.false
isMultipanebooleanIf true, two calendar months will be shown side-by-side instead of one.false
isOpenbooleanIf true, the picker will be shown without user interaction.false
showPresetsbooleanIf true, the picker will show the preset ranges for selection.false
showPresetsOnlybooleanIf true, the preset ranges will only show. Requires range and showPreset to be set.false
showYearControlsbooleanIf true, the picker will hide the year navigation controls.false
showTimePickerbooleanIf true, the picker will show the time picker.false
enableFutureDatesbooleanIf true, the picker will allow the user to select future dates.false
enablePastDatesbooleanIf disabled, the picker will prevent the user from selecting anything prior to today.true
themestringThe theme name will be assigned to the theme data-attribute datepicker.[data-picker-theme="theme_value_here"]`.empty
defaultMonthnumber 146 | The month you want to show as the default. 147 |
Accepted values 0-11
148 |
149 |
    150 |
  • 0 - January
  • 151 |
  • 1 - February
  • 152 |
  • 2 - March
  • 153 |
  • 3 - April
  • 154 |
  • 4 - May
  • 155 |
  • 5 - June
  • 156 |
  • 6 - July
  • 157 |
  • 7 - August
  • 158 |
  • 8 - September
  • 159 |
  • 9 - October
  • 160 |
  • 10 - November
  • 161 |
  • 11 - December
  • 162 |
163 |
{today.getMonth()}
startOfWeeknumber 170 | Defines what day will start your week. 171 |
Accepted values 0-6
172 | 173 |
174 |
    175 |
  • 0 - Sunday
  • 176 |
  • 1 - Monday
  • 177 |
  • 2 - Tuesday
  • 178 |
  • 3 - Wednesday
  • 179 |
  • 4 - Thursday
  • 180 |
  • 5 - Friday
  • 181 |
  • 6 - Saturday
  • 182 |
183 |
0
disabledDatesarray 190 | Determines which dates will be disabled. 191 |
Accepted format 'MM/DD/YYYY'
192 | 193 |
194 | 195 |
    196 |
  • ['1/1/2023'] - Disables January 1st, 2023
  • 197 |
  • ['1/1/2023', '1/2/2023'] - Disables January 1st and 2nd, 2023
  • 198 |
  • ['1/1/2023:1/10/2023'] - Disables the range January 1st to 10th, 2023
  • 199 |
200 |
[]
enabledDatesarray 207 | Determines which dates will be enabled only. 208 |
Accepted format 'MM/DD/YYYY'
209 | 210 |
211 | 212 |
    213 |
  • ['1/1/2023'] - Enables January 1st, 2023
  • 214 |
  • ['1/1/2023', '1/2/2023'] - Enables January 1st and 2nd, 2023
  • 215 |
  • ['1/1/2023:1/10/2023'] - Enables the range January 1st to 10th, 2023
  • 216 |
217 |
[]
dowLabelsarrayAn array of strings containing the days of the week. 225 | [   226 |
    227 |
  •   "Su",
  • 228 |
  •   "Mo",
  • 229 |
  •   "Tu",
  • 230 |
  •   "We",
  • 231 |
  •   "Th",
  • 232 |
  •   "Fr",
  • 233 |
  •   "Sa"
  • 234 |
] 235 |
monthLabelsarrayAn array of strings containing the months of the year. 242 | [   243 |
    244 |
  •   "January",
  • 245 |
  •   "February",
  • 246 |
  •   "March",
  • 247 |
  •   "April",
  • 248 |
  •   "May",
  • 249 |
  •   "June",
  • 250 |
  •   "July",
  • 251 |
  •   "August",
  • 252 |
  •   "September",
  • 253 |
  •   "October",
  • 254 |
  •   "November",
  • 255 |
  •   "Decemeber"
  • 256 |
] 257 |
presetLabelsarrayAn array of strings of the default preset labels. 264 | [   265 |
    266 |
  •   "Today",
  • 267 |
  •   "Last 7 Days",
  • 268 |
  •   "Last 30 Days",
  • 269 |
  •   "Last 60 Days",
  • 270 |
  •   "Last 90 Days",
  • 271 |
  •   "Last Year"
  • 272 |
] 273 |
presetRangesarrayAn array of objects containing preset configurations. 280 | [{   281 |
    282 |
  •   "label": "string",
  • 283 |
  •   "start": "Date",
  • 284 |
  •   "end": "Date"
  • 285 |
}] 286 |
includeFontbooleanIf false, the default font "Rubik" will not be loaded.true
295 |
296 | 297 |
298 | 299 |
300 |

Events

301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 |
NameTypeDescriptionDefault
onDateChangefunctionCallback function to handle when date changes.None
onDayClickfunctionCallback function to handle day click events.None
onNavigationChangefunctionCallback function to handle the navigation click event for months and years.None
328 |
329 | 330 |
331 | 332 |
333 |

Examples

334 |

Below are different examples of how you can configure the datepicker.

335 | 336 |

Single Date

337 | 338 |
339 | 340 |
341 | 342 |

Date Range

343 | 344 |
345 | 346 |
347 | 348 |

Multipane Range

349 | 350 |
351 | 352 |
353 | 354 |

Preset Ranges

355 | 356 |
357 | 358 |
359 | 360 |

Presets Only

361 | 362 |
363 | 364 |
365 | 366 |

Time Picker

367 | 368 |
369 | 370 |
371 | 372 |

Disable Past dates

373 | 374 |
375 | 376 |
377 | 378 |

Enable Future dates

379 | 380 |
381 | 382 |
383 | 384 |

Disabled Dates

385 | 386 |
387 | 388 |
389 | 390 |

Enabled Dates

391 | 392 |
393 | 394 |
395 | 396 |

Theme

397 | 398 |
399 | 400 |
401 |
402 |
403 | 404 | 505 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @svelte-plugins/datepicker 2 | 3 | A simple datepicker component designed for Svelte. 4 | 5 | Try it in the [Svelte REPL](https://svelte.dev/repl/cae0ce6e92634878b6e1a587146decbd?version=4.2.7). 6 | 7 | ## Install 8 | 9 | ```bash 10 | # npm 11 | > npm install svelte @svelte-plugins/datepicker 12 | 13 | # pnpm 14 | > pnpm install svelte @svelte-plugins/datepicker 15 | 16 | # yarn 17 | > yarn add svelte @svelte-plugins/datepicker 18 | ``` 19 | 20 | ## Using the DatePicker component 21 | ```svelte 22 | 49 | 50 | 51 | 52 | 53 | 54 | 61 | ``` 62 | 63 | ## API 64 | 65 | ### Props 66 | | Prop | Description | Default | 67 | | :---------------- | :------------------------------------------------------------------------------------ | :-------------------------- | 68 | | startDate | The start date string or date object. | `object` (default: `null`) 69 | | endDate | The end date string or date object. | `object` (default: `null`) 70 | | startDateTime | The start date time string in 24 hour format. | `string` (default: `00:00`) 71 | | endDateTime | The end date time string in 24 hour format. | `string` (default: `00:00`) 72 | | defaultYear | The year you want to show as the default. | `number` (default: `2023`) 73 | | align | The edge alignment of the datepicker. | `string` (default: `left`) 74 | | enabledDates | An array of date strings to enable only. | `array` (default: [...]) 75 | | disabledDates | An array of date strings to disable. | `array` (default: [...]) 76 | | isRange | Changes the date picker into a range picker and allows start and end date selection. | `boolean` (default: `false`) 77 | | isMultipane | If true, two calendar months will be shown side-by-side instead of one. | `boolean` (default: `false`) 78 | | isOpen | If true, the picker will be shown without user interaction. | `boolean` (default: `false`) 79 | | showPresets | If true, the picker will show the preset ranges for selection. | `boolean` (default: `false`) 80 | | showPresetsOnly | If true, the picker will show only preset ranges. | `boolean` (default: `false`) 81 | | showYearControls | If true, the picker will hide the year navigation controls. | `boolean` (default: `false`) 82 | | showTimePicker | If true, the picker will show the time picker. | `boolean` (default: `false`) 83 | | enableFutureDates | If true, the picker will allow the user to select future dates. | `boolean` (default: `false`) 84 | | enablePastDates | If disabled, the picker will prevent the user from selecting anything prior to today. | `boolean` (default: `true`) 85 | | theme | The theme name that should be assigned to the theme data-attribute. | `string` (default: `''`) 86 | | presetRanges | The array of present configs to replace the default set with. | `array` (default: [...]) 87 | | includeFont | If false, the default Rubik font will not be loaded | `boolean` (default: `true`) 88 | 89 | ### Events 90 | | Prop | Description | Default | 91 | | :----------------- | :------------------------------------------------------------------------------------ | :-------------------------- | 92 | | onDateChange | Callback function to handle date change events. | `function` 93 | | onDayClick | Callback function to handle day click events. | `function` 94 | | onNavigationChange | Callback function to handle the navigation click event for months and years | `function` 95 | 96 | ## Theming 97 | You can customize DatePicker theme using several methods: 98 | - Assign a theme class name via the `theme` property that includes all of your CSS variables overrides 99 | - Define the overrides directly using the `style` property 100 | - Override the CSS variables globally 101 | 102 | DatePicker CSS variables: 103 | 104 | ```css 105 | /** 106 | * Common Variables 107 | */ 108 | --datepicker-border-color: #e8e9ea; 109 | 110 | --datepicker-border-radius-small: .125rem; 111 | --datepicker-border-radius-base: .25rem; 112 | --datepicker-border-radius-large: .5rem; 113 | --datepicker-border-radius-xlarge: .75rem; 114 | --datepicker-border-radius-xxlarge: 1rem; 115 | --datepicker-border-radius-xxxlarge: 1.125rem; 116 | 117 | --datepicker-state-active: #0087ff; 118 | --datepicker-state-hover: #e7f7fc; 119 | 120 | --datepicker-color: #21333d; 121 | 122 | --datepicker-font-family: 'Rubik', sans-serif; 123 | 124 | --datepicker-font-size-jumbo: 1.75rem; 125 | --datepicker-font-size-xxxlarge: 1.5rem; 126 | --datepicker-font-size-xxlarge: 1.375rem; 127 | --datepicker-font-size-xlarge: 1.25rem; 128 | --datepicker-font-size-large: 1.125rem; 129 | --datepicker-font-size-base: 14px; 130 | --datepicker-font-size-medium: 0.89rem; 131 | --datepicker-font-size-small: 0.75rem; 132 | --datepicker-font-size-xsmall: 0.625rem; 133 | --datepicker-font-size-xxsmall: 0.5rem; 134 | --datepicker-font-size-xxxsmall: 0.375rem; 135 | --datepicker-font-weight-thin: 100; 136 | --datepicker-font-weight-light: 300; 137 | --datepicker-font-weight-base: 400; 138 | --datepicker-font-weight-medium: 500; 139 | --datepicker-font-weight-bold: 700; 140 | --datepicker-font-weight-black: 900; 141 | 142 | --datepicker-spacing: 8px; 143 | 144 | --datepicker-margin-xsmall: calc(var(--datepicker-spacing) / 4); 145 | --datepicker-margin-small: calc(var(--datepicker-spacing) / 2); 146 | --datepicker-margin-base: var(--datepicker-spacing); 147 | --datepicker-margin-large: calc(var(--datepicker-spacing) * 2); 148 | --datepicker-margin-xlarge: calc(var(--datepicker-spacing) * 3); 149 | --datepicker-margin-xxlarge: calc(var(--datepicker-spacing) * 4); 150 | --datepicker-margin-xxxlarge: calc(var(--datepicker-spacing) * 5); 151 | --datepicker-margin-jumbo: calc(var(--datepicker-spacing) * 6); 152 | 153 | --datepicker-padding-xsmall: calc(var(--datepicker-spacing) / 4); 154 | --datepicker-padding-small: calc(var(--datepicker-spacing) / 2); 155 | --datepicker-padding-base: var(--datepicker-spacing); 156 | --datepicker-padding-large: calc(var(--datepicker-spacing) * 2); 157 | --datepicker-padding-xlarge: calc(var(--datepicker-spacing) * 3); 158 | --datepicker-padding-xxlarge: calc(var(--datepicker-spacing) * 4); 159 | --datepicker-padding-xxxlarge: calc(var(--datepicker-spacing) * 5); 160 | --datepicker-padding-jumbo: calc(var(--datepicker-spacing) * 6); 161 | 162 | /** 163 | * Container 164 | */ 165 | --datepicker-container-background: #fff; 166 | --datepicker-container-border: 1px solid var(--datepicker-border-color); 167 | --datepicker-container-border-radius: 12px; 168 | --datepicker-container-box-shadow: 0 1px 20px rgba(0, 0, 0, 0.1); 169 | --datepicker-container-font-family: var(--datepicker-font-family); 170 | --datepicker-container-left: 0; 171 | --datepicker-container-position: absolute; 172 | --datepicker-container-top: 105%; 173 | --datepicker-container-width: fit-content; 174 | --datepicker-container-zindex: 99; 175 | 176 | /** 177 | * Calendar 178 | */ 179 | --datepicker-calendar-border: 0; 180 | --datepicker-calendar-padding: var(--datepicker-padding-base) var(--datepicker-padding-large) var(--datepicker-padding-xlarge); 181 | --datepicker-calendar-position: relative; 182 | --datepicker-calendar-width: 310px; 183 | 184 | --datepicker-calendar-split-border: 1px solid var(--datepicker-border-color); 185 | 186 | /** 187 | * Calendar Header 188 | */ 189 | --datepicker-calendar-header-align-items: center; 190 | --datepicker-calendar-header-color: var(--datepicker-color); 191 | --datepicker-calendar-header-display: flex; 192 | --datepicker-calendar-header-font-size: var(--datepicker-font-size-large); 193 | --datepicker-calendar-header-justify-content: space-between; 194 | --datepicker-calendar-header-margin: 0 0 var(--datepicker-margin-xlarge) 0; 195 | --datepicker-calendar-header-padding: var(--datepicker-padding-large) var(--datepicker-padding-base); 196 | --datepicker-calendar-header-user-select: none; 197 | 198 | /** 199 | * Calendar Header Month Navigation 200 | */ 201 | --datepicker-calendar-header-month-nav-background: transparent; 202 | --datepicker-calendar-header-month-nav-background-hover: #f5f5f5; 203 | --datepicker-calendar-header-month-nav-border: 0; 204 | --datepicker-calendar-header-month-nav-cursor: pointer; 205 | --datepicker-calendar-header-month-nav-border-radius: 20px; 206 | --datepicker-calendar-header-month-nav-color: var(--datepicker-color); 207 | --datepicker-calendar-header-month-nav-cursor: pointer; 208 | --datepicker-calendar-header-month-nav-font-size: var(--datepicker-font-size-large); 209 | --datepicker-calendar-header-month-nav-height: 32px; 210 | --datepicker-calendar-header-month-nav-margin-left: -8px; 211 | --datepicker-calendar-header-month-nav-padding: var(--datepicker-padding-small); 212 | --datepicker-calendar-header-month-nav-text-align: center; 213 | --datepicker-calendar-header-month-nav-width: 32px; 214 | 215 | --datepicker-calendar-header-month-nav-icon-next-background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAACLSURBVHgB7ZTLCYAwEERHbcASUpIlaAd2YDoxlmIX3ixFEwwYQQL5kCWwD94ph5mwywIMUzmLlYRBe1lXENBrT+oSgktwiepLNJ63EWkl3AOltBMCkHh/kEv5F9SCGN8IzKntEYfAdwQb0kYaHO4uoUJBBIdzOAoiKMMNQ47wDvEceA7Zrp3BMLVyA56LVFYQOkngAAAAAElFTkSuQmCC') no-repeat center center; 216 | --datepicker-calendar-header-month-nav-icon-next-background-size: 16px 16px; 217 | --datepicker-calendar-header-month-nav-icon-next-filter: invert(0); 218 | --datepicker-calendar-header-month-nav-icon-next-height: 16px; 219 | --datepicker-calendar-header-month-nav-icon-next-margin: auto; 220 | --datepicker-calendar-header-month-nav-icon-next-width: 16px; 221 | 222 | --datepicker-calendar-header-month-nav-icon-prev-background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAACKSURBVHgB7ZbBDYAgDEW/xgEcgZHcQDYRJ5ER3EhHcAPtAQMHQwIiSNKXvAMH+CUNDQDDVM5kLMJCnsYBmXHDN1IgIxzO4QIZ+Ty8gT9cOuuZ3BHHQa4hGxTszVOpnoJaFMbXAk2OzvpNC+7zojYVewFcBBdRVRE9CqCR4EvWIR4JO5iC5jzD/IoLU/FXPXheCj0AAAAASUVORK5CYII=') no-repeat center center; 223 | --datepicker-calendar-header-month-nav-icon-prev-background-size: 16px 16px; 224 | --datepicker-calendar-header-month-nav-icon-prev-filter: invert(0); 225 | --datepicker-calendar-header-month-nav-icon-prev-height: 16px; 226 | --datepicker-calendar-header-month-nav-icon-prev-margin: auto; 227 | --datepicker-calendar-header-month-nav-icon-prev-width: 16px; 228 | 229 | /** 230 | * Calendar Header Text 231 | */ 232 | --datepicker-calendar-header-text-align-items: center; 233 | --datepicker-calendar-header-text-color: var(--datepicker-color); 234 | --datepicker-calendar-header-text-display: flex; 235 | --datepicker-calendar-header-text-font-size: inherit; 236 | --datepicker-calendar-header-text-font-weight: var(--datepicker-font-weight-medium); 237 | --datepicker-calendar-header-text-gap: 8px; 238 | 239 | /** 240 | * Calendar Header Year Navigation Container 241 | */ 242 | --datepicker-calendar-header-year-align-items: center; 243 | --datepicker-calendar-header-year-display: flex; 244 | --datepicker-calendar-header-year-flex-direction: column; 245 | --datepicker-calendar-header-year-margin: 0; 246 | 247 | /** 248 | * Calendar Header Year Navigation Controls 249 | */ 250 | --datepicker-calendar-header-year-nav-display: block; 251 | --datepicker-calendar-header-year-nav-color: var(--datepicker-color); 252 | --datepicker-calendar-header-year-nav-height: 12px; 253 | --datepicker-calendar-header-year-nav-line-height: 12px; 254 | --datepicker-calendar-header-year-nav-margin: -2px 0 0 0; 255 | --datepicker-calendar-header-year-nav-padding: 0; 256 | --datepicker-calendar-header-year-nav-width: 12px; 257 | --datepicker-calendar-header-year-nav-icon-font-size: 13px; 258 | 259 | --datepicker-calendar-header-year-nav-icon-next-background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAABuSURBVHgB7c7BCYAwDIXhBy7gKB2hm9Vx3UJzqCASRWOTHvo+yDG8HyAiGt2Ef7LcLLeigyK31SsIdh4Pj9DGwyKu40u9kAht/OAe8TTuHvFm3C3iy3jziGQYv4vIMMjGcS0iwSjBWN/on4hoADu88UW4KXFVfgAAAABJRU5ErkJggg==') no-repeat center center; 260 | --datepicker-calendar-header-year-nav-icon-next-background-size: 12px 12px; 261 | --datepicker-calendar-header-year-nav-icon-next-display: block; 262 | --datepicker-calendar-header-year-nav-icon-next-filter: invert(0); 263 | --datepicker-calendar-header-year-nav-icon-next-height: 12px; 264 | --datepicker-calendar-header-year-nav-icon-next-width: 12px; 265 | 266 | --datepicker-calendar-header-year-nav-icon-prev-background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAB3SURBVHgB7dTRCYAwDATQAxdwlI6QzZpx3UIrKJSC1aS2fngP7kvi3VcBIqK/m26+S8qcssBHWu5Dynokwi5m9wIHyX5gHRGL2wAndYwoyxWN1DDi9XLLiG7lT0Z0L6+NGFZ+NWJoeW2EYjD9svy0PzACIiJqsAHF2EaCcjFGaQAAAABJRU5ErkJggg==') no-repeat center center; 267 | --datepicker-calendar-header-year-nav-icon-prev-background-size: 12px 12px; 268 | --datepicker-calendar-header-year-nav-icon-prev-display: block; 269 | --datepicker-calendar-header-year-nav-icon-prev-filter: invert(0); 270 | --datepicker-calendar-header-year-nav-icon-prev-height: 12px; 271 | --datepicker-calendar-header-year-nav-icon-prev-width: 12px; 272 | 273 | /** 274 | * Presets 275 | */ 276 | --datepicker-presets-border: 1px solid var(--datepicker-border-color); 277 | --datepicker-presets-padding: 24px; 278 | --datepicker-presets-minwidth: 180px; 279 | --datepicker-presets-maxwidth: 200px; 280 | 281 | /** 282 | * Presets Button 283 | */ 284 | --datepicker-presets-button-background: transparent; 285 | --datepicker-presets-button-background-hover: var(--datepicker-state-hover); 286 | --datepicker-presets-button-background-active: var(--datepicker-state-active); 287 | 288 | --datepicker-presets-button-border: 0; 289 | --datepicker-presets-button-border-radius: 40px; 290 | --datepicker-presets-button-border-radius-active: 20px; 291 | 292 | --datepicker-presets-button-color: var(--datepicker-color); 293 | --datepicker-presets-button-color-active: #fff; 294 | --datepicker-presets-button-color-hover: var(--datepicker-color); 295 | --datepicker-presets-button-color-focus: var(--datepicker-color); 296 | 297 | --datepicker-presets-button-cursor: pointer; 298 | --datepicker-presets-button-cursor-active: default; 299 | 300 | --datepicker-presets-button-font-family: var(--datepicker-font-family); 301 | --datepicker-presets-button-font-size: var(--datepicker-font-size-base); 302 | --datepicker-presets-button-font-weight-active: var(--datepicker-font-weight-medium); 303 | --datepicker-presets-button-outline-focus: 5px auto -webkit-focus-ring-color; 304 | 305 | --datepicker-presets-button-margin: var(--datepicker-margin-small) 0; 306 | --datepicker-presets-button-padding: calc(var(--datepicker-padding-base) + 2px) var(--datepicker-padding-large); 307 | --datepicker-presets-button-text-align: left; 308 | --datepicker-presets-button-zindex-focus: 10; 309 | 310 | /** 311 | * Timepicker Container 312 | */ 313 | --datepicker-timepicker-container-align-items: center; 314 | --datepicker-timepicker-container-display: flex; 315 | --datepicker-timepicker-container-justify-content: space-around; 316 | --datepicker-timepicker-container-margin-bottom: var(--datepicker-margin-xlarge); 317 | 318 | /** 319 | * Timepicker Input 320 | */ 321 | --datepicker-timepicker-input-border: 1px solid var(--datepicker-border-color); 322 | --datepicker-timepicker-input-border-radius: var(--datepicker-border-radius-base); 323 | --datepicker-timepicker-input-display: block; 324 | --datepicker-timepicker-input-font-family: var(--datepicker-font-family); 325 | --datepicker-timepicker-input-margin: 0 auto; 326 | --datepicker-timepicker-input-padding: var(--datepicker-padding-small) var(--datepicker-padding-base); 327 | 328 | /** 329 | * Calendar DOW (Days of Week) 330 | */ 331 | --datepicker-calendar-dow-color: #8b9198; 332 | --datepicker-calendar-dow-font-size: var(--datepicker-font-size-base); 333 | --datepicker-calendar-dow-font-weight: var(--datepicker-font-weight-medium); 334 | --datepicker-calendar-dow-margin-bottom: var(--datepicker-margin-large); 335 | --datepicker-calendar-dow-text-align: center; 336 | 337 | /** 338 | * Calendar Month 339 | */ 340 | --datepicker-calendar-container-display: grid; 341 | --datepicker-calendar-container-grid-template-columns: repeat(7, 1fr); 342 | --datepicker-calendar-container-grid-gap: 0; 343 | --datepicker-calendar-container-width: fit-content; 344 | 345 | /** 346 | * Calendar Day Container 347 | */ 348 | --datepicker-calendar-day-container-appearance: none; 349 | --datepicker-calendar-day-container-background: inherit; 350 | --datepicker-calendar-day-container-border: 0; 351 | --datepicker-calendar-day-container-margin: 0; 352 | --datepicker-calendar-day-container-padding: 0; 353 | --datepicker-calendar-day-container-position: relative; 354 | --datepicker-calendar-day-container-text-align: center; 355 | 356 | /** 357 | * Calendar Day 358 | */ 359 | --datepicker-calendar-day-align-items: center; 360 | --datepicker-calendar-day-background-hover: #f5f5f5; 361 | --datepicker-calendar-day-border: 1px solid transparent; 362 | --datepicker-calendar-day-border: 1px solid transparent; 363 | --datepicker-calendar-day-border-radius: 100%; 364 | --datepicker-calendar-day-color: #232a32; 365 | --datepicker-calendar-day-color-disabled: #b9bdc1; 366 | --datepicker-calendar-day-color-hover: #232a32; 367 | --datepicker-calendar-day-cursor: pointer; 368 | --datepicker-calendar-day-cursor-disabled: default; 369 | --datepicker-calendar-day-display: flex; 370 | --datepicker-calendar-day-height: 40px; 371 | --datepicker-calendar-day-justify-content: center; 372 | --datepicker-calendar-day-font-family: var(--datepicker-font-family); 373 | --datepicker-calendar-day-font-size: var(--datepicker-font-size-base); 374 | --datepicker-calendar-day-margin-bottom: 1px; 375 | --datepicker-calendar-day-padding: var(--datepicker-padding-base); 376 | --datepicker-calendar-day-text-align: center; 377 | --datepicker-calendar-day-width: 40px; 378 | --datepicker-calendar-day-zindex-focus: 12; 379 | 380 | /** 381 | * Calendar Days Outside of Month 382 | */ 383 | --datepicker-calendar-day-other-border: 0; 384 | --datepicker-calendar-day-other-box-shadow: none; 385 | --datepicker-calendar-day-other-color: #d1d3d6; 386 | 387 | /** 388 | * Calendar Today 389 | */ 390 | --datepicker-calendar-today-background: transparent; 391 | --datepicker-calendar-today-border: 1px solid #232a32; 392 | --datepicker-calendar-today-cursor: default; 393 | --datepicker-calendar-today-font-weight: var(--datepicker-font-weight-bold); 394 | 395 | /** 396 | * Calendar Range 397 | */ 398 | --datepicker-calendar-range-background: var(--datepicker-state-hover); 399 | --datepicker-calendar-range-background-disabled: var(--datepicker-state-hover); 400 | --datepicker-calendar-range-border: 0; 401 | --datepicker-calendar-range-border-radius: 0; 402 | --datepicker-calendar-range-color: var(--datepicker-color); 403 | --datepicker-calendar-range-color-disabled: #ffc0b7; 404 | --datepicker-calendar-range-cursor: default; 405 | --datepicker-calendar-range-font-weight: var(--datepicker-font-weight-base); 406 | 407 | /** 408 | * Calendar Range Start & End 409 | */ 410 | --datepicker-calendar-range-start-box-shadow: inset -20px 0 0 var(--datepicker-state-hover); 411 | --datepicker-calendar-range-end-box-shadow: inset 20px 0 0 var(--datepicker-state-hover); 412 | --datepicker-calendar-range-start-box-shadow-selected: inset -20px 0 0 #eceff1; 413 | --datepicker-calendar-range-end-box-shadow-selected: inset 20px 0 0 #eceff1; 414 | 415 | --datepicker-calendar-range-start-end-background: #f5f5f5; 416 | --datepicker-calendar-range-start-end-color: #232a32; 417 | 418 | /** 419 | * Calendar Range Selected 420 | */ 421 | --datepicker-calendar-range-selected-background: var(--datepicker-state-active); 422 | --datepicker-calendar-range-selected-border-radius: 20px; 423 | --datepicker-calendar-range-selected-color: #fff; 424 | --datepicker-calendar-range-selected-font-weight: var(--datepicker-font-weight-medium); 425 | 426 | --datepicker-calendar-range-selected-start-border-radius: 20px; 427 | 428 | /** 429 | * Calendar Range Hover 430 | */ 431 | --datepicker-calendar-range-included-background: #eceff1; 432 | --datepicker-calendar-range-included-box-shadow: inset 20px 0 0 #eceff1; 433 | --datepicker-calendar-range-included-color: #232a32; 434 | --datepicker-calendar-range-included-font-weight: var(--datepicker-font-weight-base); 435 | --datepicker-calendar-range-included-height: var(--datepicker-calendar-day-height); 436 | ``` 437 | 438 | ### Using the theme property 439 | 440 | ```svelte 441 | ... 442 | 443 | 470 | ``` 471 | 472 | ## Changelog 473 | 474 | [Changelog](CHANGELOG.md) 475 | 476 | ## License 477 | 478 | [MIT](LICENSE) 479 | -------------------------------------------------------------------------------- /src/datepicker.svelte: -------------------------------------------------------------------------------- 1 | 937 | 938 |
939 | 940 |
947 | {#if isRange && showPresets} 948 |
949 | {#each presetRanges as option} 950 | 958 | {/each} 959 |
960 | {/if} 961 |
962 |
963 | 966 | 967 |
{monthLabels[startDateMonth]} {startDateYear}
968 | {#if showYearControls} 969 |
970 | 973 | 976 |
977 | {/if} 978 |
979 | 982 |
983 | 984 | {#if showTimePicker} 985 |
986 | (startDate = updateTime('start', startDate))} /> 987 | 988 | {#if isRange} 989 | (endDate = updateTime('end', endDate))} 994 | /> 995 | {/if} 996 |
997 | {/if} 998 | 999 |
1000 | {#each dowLabels as text, labelIndex (text)} 1001 | {dowLabels[(labelIndex + startOfWeek) % 7]} 1002 | {/each} 1003 | 1004 | {#each { length: 6 } as week, weekIndex (weekIndex)} 1005 | {#if startDateCalendar[weekIndex]} 1006 | {#each { length: 7 } as d, dayIndex (dayIndex)} 1007 | {#if startDateCalendar[weekIndex][dayIndex] !== 0} 1008 | 1030 | {:else} 1031 |
 
1032 | {/if} 1033 | {/each} 1034 | {/if} 1035 | {/each} 1036 |
1037 |
1038 | 1039 | {#if isRange && isMultipane} 1040 |
1041 |
1042 | 1045 | 1046 |
{monthLabels[endDateMonth]} {endDateYear}
1047 | 1048 | {#if showYearControls} 1049 |
1050 | 1053 | 1056 |
1057 | {/if} 1058 |
1059 | 1062 |
1063 | 1064 | {#if showTimePicker} 1065 |
1066 | (endDate = updateTime('end', endDate))} /> 1067 |
1068 | {/if} 1069 | 1070 |
1071 | {#each dowLabels as text, labelIndex (text)} 1072 | {dowLabels[(labelIndex + startOfWeek) % 7]} 1073 | {/each} 1074 | 1075 | {#each { length: 6 } as week, weekIndex (weekIndex)} 1076 | {#if endDateCalendar[weekIndex]} 1077 | {#each { length: 7 } as d, dayIndex (dayIndex)} 1078 | {#if endDateCalendar[weekIndex][dayIndex] !== 0} 1079 | 1101 | {:else} 1102 |
 
1103 | {/if} 1104 | {/each} 1105 | {/if} 1106 | {/each} 1107 |
1108 |
1109 | {/if} 1110 |
1111 |
1112 | 1113 | 1114 | {#if includeFont} 1115 | 1119 | {/if} 1120 | 1121 | 1122 | 1938 | --------------------------------------------------------------------------------