├── .editorconfig ├── .gitattributes ├── .gitignore ├── README.md ├── babel.config.js ├── cypress.json ├── dist ├── vue-persian-datetime-picker.common.js ├── vue-persian-datetime-picker.umd.js └── vue-persian-datetime-picker.umd.min.js ├── docs.deploy.sh ├── docs ├── .vuepress │ ├── config.js │ ├── enhanceApp.js │ ├── public │ │ ├── fonts │ │ │ ├── IRANSansWeb.eot │ │ │ ├── IRANSansWeb.ttf │ │ │ ├── IRANSansWeb.woff │ │ │ ├── IRANSansWeb.woff2 │ │ │ ├── IRANSansWeb_Bold.eot │ │ │ ├── IRANSansWeb_Bold.ttf │ │ │ ├── IRANSansWeb_Bold.woff │ │ │ ├── IRANSansWeb_Bold.woff2 │ │ │ ├── IRANSansWeb_Light.eot │ │ │ ├── IRANSansWeb_Light.ttf │ │ │ ├── IRANSansWeb_Light.woff │ │ │ ├── IRANSansWeb_Light.woff2 │ │ │ └── IRANSans_Medium.eot │ │ └── vue-persian-datetime-picker.png │ └── styles │ │ ├── index.styl │ │ └── palette.styl ├── config │ └── README.md ├── guide │ ├── README.md │ ├── append-to.md │ ├── auto-submit.md │ ├── custom-input.md │ ├── datetime-picker.md │ ├── different-input-format.md │ ├── disabling.md │ ├── editable.md │ ├── events.md │ ├── examples.md │ ├── formatting.md │ ├── highlight.md │ ├── initial-value.md │ ├── input-settings.md │ ├── installation.md │ ├── label.md │ ├── localization.md │ ├── min-max.md │ ├── multiple.md │ ├── popover.md │ ├── range.md │ ├── recommend-config.md │ ├── simple-mode.md │ ├── slots.md │ ├── theme.md │ ├── time-picker.md │ ├── timezone.md │ ├── use-router.md │ └── view.md └── index.md ├── index.d.ts ├── package-lock.json ├── package.json ├── src ├── VuePersianDatetimePicker.vue ├── assets │ └── scss │ │ ├── _transitions.scss │ │ ├── _variables.scss │ │ └── style.scss ├── components │ ├── Arrow.vue │ ├── Btn.vue │ ├── CalendarIcon.vue │ ├── EditIcon.vue │ ├── LocaleChange.vue │ ├── TimeIcon.vue │ ├── simple │ │ ├── SimpleMode.vue │ │ └── SimpleModeColumn.vue │ └── time │ │ ├── TimeColumn.vue │ │ └── TimeSection.vue └── modules │ ├── core.js │ ├── mixins.js │ ├── moment.locale.fa.js │ ├── popover-util.js │ └── utils.js ├── tests ├── e2e │ ├── .eslintrc.js │ ├── plugins │ │ └── index.js │ ├── specs │ │ └── test.js │ └── support │ │ ├── commands.js │ │ └── index.js └── unit │ └── example.spec.js └── vue.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | /tests/e2e/videos/ 5 | /tests/e2e/screenshots/ 6 | /docs/.vuepress/dist/ 7 | 8 | 9 | # local env files 10 | .env.local 11 | .env.*.local 12 | 13 | # Log files 14 | npm-debug.log* 15 | yarn-debug.log* 16 | yarn-error.log* 17 | pnpm-debug.log* 18 | 19 | # Editor directories and files 20 | .idea 21 | .vscode 22 | *.suo 23 | *.ntvs* 24 | *.njsproj 25 | *.sln 26 | *.sw? 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-persian-datetime-picker 2 | 3 | [![npm version](https://badge.fury.io/js/vue-persian-datetime-picker.svg)](https://www.npmjs.com/package/vue-persian-datetime-picker) 4 | 5 | > A vue plugin to select jalali date and time 6 | 7 | See documentation and demo at [vue-persian-datetime-picker](https://talkhabi.github.io/vue-persian-datetime-picker) 8 | 9 | If you are using vuejs 3, please refer to [this repository](https://github.com/talkhabi/vue3-persian-datetime-picker). 10 | 11 | 12 | ## Installation 13 | ### browser 14 | ```html 15 | 16 | 17 | 18 | 19 |
20 | 21 |
22 | 33 | ``` 34 | 35 | ### npm 36 | ```bash 37 | npm install vue-persian-datetime-picker --save 38 | ``` 39 | 40 | Configuration for moment to ignore loading locales 41 | ```javascript 42 | // webpack.config.js: 43 | module.exports.plugins = [ 44 | //... 45 | new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/) 46 | //... 47 | ] 48 | 49 | // vue.config.js: 50 | module.exports = { 51 | //.. 52 | configureWebpack: { 53 | plugins: [new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)] 54 | }, 55 | //... 56 | } 57 | ``` 58 | 59 | ### Usage 60 | ```javascript 61 | // main.js 62 | //... 63 | import VuePersianDatetimePicker from 'vue-persian-datetime-picker'; 64 | Vue.component('date-picker', VuePersianDatetimePicker); 65 | //... 66 | ``` 67 | Or in component 68 | ```html 69 | 74 | 75 | 88 | ``` 89 | 90 | 91 | ## You can also set default values: 92 | ```javascript 93 | // main.js 94 | import VuePersianDatetimePicker from 'vue-persian-datetime-picker'; 95 | Vue.use(VuePersianDatetimePicker, { 96 | name: 'custom-date-picker', 97 | props: { 98 | inputFormat: 'YYYY-MM-DD HH:mm', 99 | format: 'jYYYY-jMM-jDD HH:mm', 100 | editable: false, 101 | inputClass: 'form-control my-custom-class-name', 102 | placeholder: 'Please select a date', 103 | altFormat: 'YYYY-MM-DD HH:mm', 104 | color: '#00acc1', 105 | autoSubmit: false, 106 | //... 107 | //... And whatever you want to set as default 108 | //... 109 | } 110 | }); 111 | ``` 112 | Then use in component 113 | ```html 114 | 115 | ``` 116 | 117 | ### [Click to see full documentation and demo](https://talkhabi.github.io/vue-persian-datetime-picker) 118 | 119 | ## Built With 120 | * [Vue.js](https://vuejs.org/) - The Progressive JavaScript Framework. 121 | * [Moment.js](https://momentjs.com/) - Parse, validate, manipulate, and display dates and times in JavaScript. 122 | * [moment-jalaali](https://github.com/jalaali/moment-jalaali) - A Jalaali (Jalali, Persian, Khorshidi, Shamsi) calendar system plugin for moment.js. 123 | 124 | 125 | ## License 126 | 127 | This project is licensed under the MIT License 128 | 129 | 130 | ## Change log 131 | 132 | ### 2.10.4 (2022-03-04) 133 | * Fixed [change the time with keyboard in popover mode](https://github.com/talkhabi/vue-persian-datetime-picker/issues/214) 134 | * Fixed popover configuration 135 | 136 | ### 2.10.3 (2022-01-06) 137 | * Fixed [popover position in rtl pages](https://github.com/talkhabi/vue-persian-datetime-picker/issues/204) 138 | * Fixed [keep the selected day in simple mode](https://github.com/talkhabi/vue-persian-datetime-picker/issues/207) 139 | 140 | ### 2.10.2 (2021-12-24) 141 | * Fixed [popover mode when using custom input](https://github.com/talkhabi/vue-persian-datetime-picker/issues/200) 142 | * Fixed [change jump-minute by muse wheel](https://github.com/talkhabi/vue-persian-datetime-picker/issues/198) 143 | 144 | ### 2.10.1 (2021-11-12) 145 | * Fixed build problem #191 #190 #188 146 | 147 | ### 2.10.0 (2021-11-03) 148 | * Feat: Added support for display format when using [`custom-input`](https://talkhabi.github.io/vue-persian-datetime-picker/guide/custom-input.html) 149 | 150 | ### 2.9.1 (2021-10-31) 151 | * Fixed: [Using jumpMinute and roundMinute together](https://github.com/talkhabi/vue-persian-datetime-picker/issues/182) 152 | 153 | ### 2.9.0 (2021-09-27) 154 | * Feat: [Added simple-mode](https://talkhabi.github.io/vue-persian-datetime-picker/guide/simple-mode.html) 155 | * Feat: [Added `input-attrs`](https://github.com/talkhabi/vue-persian-datetime-picker/issues/170) 156 | * Feat: [Added `@next-month` and `@prev-month` events](https://github.com/talkhabi/vue-persian-datetime-picker/issues/176) 157 | * Feat: Added `@year-change` and `@month-change` events 158 | * Fixed: [Mouse wheel in Firefox](https://github.com/talkhabi/vue-persian-datetime-picker/issues/174) 159 | * Fixed: Prevent selected dates from being reset 160 | 161 | ### 2.8.0 (2021-08-01) 162 | * Feat: [Added ability to control route in show/close action](https://talkhabi.github.io/vue-persian-datetime-picker/guide/use-router.html) 163 | * Fixed Date change animation and transition 164 | 165 | ### 2.7.0 (2021-07-23) 166 | * Fixed [moment.parseZone](https://github.com/talkhabi/vue-persian-datetime-picker/issues/156) 167 | * Fixed Unwanted change of date when using `auto-submit` and `multiple` together 168 | * Feat: [Added slots for next-month and prev-month button](https://github.com/talkhabi/vue-persian-datetime-picker/issues/147) 169 | * Feat: [Ability to change the time using the keyboard](https://github.com/talkhabi/vue-persian-datetime-picker/issues/159) 170 | 171 | ### 2.6.1 (2021-05-17) 172 | * Fixed [year-month](https://github.com/talkhabi/vue-persian-datetime-picker/issues/150) 173 | 174 | ### 2.6.0 (2021-02-05) 175 | * Added [multiple selection](https://talkhabi.github.io/vue-persian-datetime-picker/guide/multiple.html) (for type `date` only) 176 | * Added [popover mode](https://talkhabi.github.io/vue-persian-datetime-picker/guide/popover.html) 177 | * Close picker with `escape` key, fixes [#141](https://github.com/talkhabi/vue-persian-datetime-picker/issues/141) 178 | 179 | ### 2.5.0 (2021-01-30) 180 | * Added [range option](https://talkhabi.github.io/vue-persian-datetime-picker/guide/range.html) (for type `date` only) 181 | * Fixed [#136](https://github.com/talkhabi/vue-persian-datetime-picker/issues/136) 182 | 183 | ### 2.4.1 (2020-11-13) 184 | * Fixed [#131](https://github.com/talkhabi/vue-persian-datetime-picker/issues/131) 185 | * Fixed [#128](https://github.com/talkhabi/vue-persian-datetime-picker/issues/128) 186 | 187 | ### 2.4.0 (2020-10-17) 188 | * Added `compat-time` to display time on the front page (in datetime mode) 189 | * Fixed clear value [#125](https://github.com/talkhabi/vue-persian-datetime-picker/issues/125) 190 | * Fixed disabling in datetime mode [#123](https://github.com/talkhabi/vue-persian-datetime-picker/issues/123) 191 | * Fixed timezone in the first and second half of the year [#108](https://github.com/talkhabi/vue-persian-datetime-picker/issues/108) 192 | 193 | ### 2.3.0 (2020-08-30) 194 | * Added [`convert-numbers`](https://github.com/talkhabi/vue-persian-datetime-picker/pull/117) prop that converts numbers to persian in fa locale: `` 195 | 196 | ### 2.2.0 (2020-08-02) 197 | * [Custom localization](https://talkhabi.github.io/vue-persian-datetime-picker/guide/localization.html) is now supported 198 | * Added some [slots](https://talkhabi.github.io/vue-persian-datetime-picker/guide/slots.html) 199 | 200 | ### 2.1.6 (2020-07-13) 201 | * Fixed `locale-config` prop [#116](https://github.com/talkhabi/vue-persian-datetime-picker/issues/116) 202 | * Added `show-now-btn` prop [#109](https://github.com/talkhabi/vue-persian-datetime-picker/issues/109) 203 | 204 | ### 2.1.5 (2020-04-19) 205 | * Fixed wrong date in Safari browsers 206 | * Fixed scroll to element (year section) 207 | 208 | ### 2.1.4 (2020-01-30) 209 | * Fixed initial locale value 210 | 211 | ### 2.1.2 (2019-09-08) 212 | * Fixed jumpMinute and roundMinute in datetime mode 213 | * Fixed time animation effect 214 | 215 | ### 2.1.1 (2019-09-07) 216 | * Fixed JumpMinute and hour change 217 | 218 | ### 2.1.0 (2019-08-19) 219 | * Added "year-month" type ``. fixed [#70](https://github.com/talkhabi/vue-persian-datetime-picker/issues/70) 220 | * Timezone support ``. fixed [#33](https://github.com/talkhabi/vue-persian-datetime-picker/issues/33) 221 | 222 | ### 2.0.2 (2019-07-17) 223 | * Flipped month change buttons in rtl direction 224 | * Added label to locales. fixed [#67](https://github.com/talkhabi/vue-persian-datetime-picker/issues/67) 225 | * Added display format to each locale. fixed [#68](https://github.com/talkhabi/vue-persian-datetime-picker/issues/68) 226 | * Fixed wrong date on date-time picker mode 227 | 228 | ### 2.0.1 (2019-05-11) 229 | 230 | * Fixed [#53](https://github.com/talkhabi/vue-persian-datetime-picker/issues/53) 231 | * Show the next step on submit button click, instead of submitting immediately [#41](https://github.com/talkhabi/vue-persian-datetime-picker/issues/41) 232 | 233 | ### 2.0.0 (2019-01-16) 234 | 235 | * Added gregorian support `` 236 | * Added locale configuration support 237 | * Improved css transitions 238 | * Fixed min-max in time picker 239 | 240 | ### 1.1.7 (2018-12-31) 241 | 242 | * Fixed [critical error](https://github.com/talkhabi/vue-persian-datetime-picker/issues/39) 243 | 244 | ### 1.1.6 (2018-12-29) 245 | 246 | * Added jumpMinute and roundMinute to time-picker `` 247 | * Added clear button `` 248 | * Added inline mode `` 249 | * Fixed `tab` key press problem 250 | * Fixed responsive mode 251 | * Fixed watching to `min-max` changes 252 | 253 | ### 1.1.5 (2018-09-01) 254 | 255 | * Added emit on open 256 | * Added feature to highlight items and dates 257 | 258 | ### 1.1.4 (2018-08-12) 259 | 260 | * Added label for display 261 | * Added feature to disable some dates 262 | * Added feature to disable or enable the datepicker 263 | * Icons and css styles optimization 264 | * Load component via script tag 265 | 266 | 267 | ### 1.1.3 (2018-05-22) 268 | 269 | * Fixed "min-date" bug 270 | 271 | ### 1.1.2 (2018-05-12) 272 | 273 | * Updated "moment-jalaali" version to 0.7.3 274 | * Fixed "display-format" when is editable 275 | * Reset "view" value 276 | 277 | ### 1.1.1 (2018-05-03) 278 | 279 | * Added "append-to" 280 | * Added "display format" 281 | 282 | ### 1.1.0 (2018-05-01) 283 | 284 | * Added default settings feature 285 | 286 | 287 | ### 1.0.9 (2017-12-25) 288 | 289 | * Clear input value 290 | * Fixed editable input bug 291 | * Added "Initial value" 292 | * Package keywords 293 | * Fixed css class name 294 | * Fixed some other bugs 295 | 296 | ### 1.0.7 (2017-12-14) 297 | 298 | * Avoid submitting form 299 | * Auto submit on wrapper click 300 | 301 | ### 1.0.5 (2017-12-04) 302 | 303 | * Fixed default value for "value" 304 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [['@vue/cli-plugin-babel/preset', { useBuiltIns: false }]] 3 | } 4 | -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "pluginsFile": "tests/e2e/plugins/index.js" 3 | } 4 | -------------------------------------------------------------------------------- /docs.deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # abort on errors 4 | set -e 5 | 6 | # build 7 | npm run docs:build 8 | 9 | # navigate into the build output directory 10 | cd docs/.vuepress/dist 11 | 12 | 13 | git init 14 | git add -A 15 | git commit -m 'docs: deploy' 16 | 17 | git push -f https://github.com/talkhabi/vue-persian-datetime-picker.git master:gh-pages 18 | 19 | cd - 20 | -------------------------------------------------------------------------------- /docs/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | const { description } = require('../../package') 2 | 3 | module.exports = { 4 | base: '/vue-persian-datetime-picker/', 5 | 6 | /** 7 | * Ref:https://v1.vuepress.vuejs.org/config/#title 8 | */ 9 | title: 'Vue persian datetime picker', 10 | /** 11 | * Ref:https://v1.vuepress.vuejs.org/config/#description 12 | */ 13 | description: description, 14 | 15 | /** 16 | * Extra tags to be injected to the page HTML `` 17 | * 18 | * ref:https://v1.vuepress.vuejs.org/config/#head 19 | */ 20 | head: [ 21 | ['meta', { name: 'theme-color', content: '#417df4' }], 22 | ['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }], 23 | ['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }] 24 | ], 25 | 26 | /** 27 | * Theme configuration, here is the default theme configuration for VuePress. 28 | * 29 | * ref:https://v1.vuepress.vuejs.org/theme/default-theme-config.html 30 | */ 31 | themeConfig: { 32 | repo: '', 33 | editLinks: false, 34 | docsDir: '', 35 | editLinkText: '', 36 | lastUpdated: false, 37 | nav: [ 38 | { 39 | text: 'Guide', 40 | link: '/guide/', 41 | }, 42 | { 43 | text: 'Config', 44 | link: '/config/' 45 | }, 46 | { 47 | text: 'Examples', 48 | link: '/guide/formatting.html' 49 | }, 50 | { 51 | text: 'Github', 52 | link: 'https://github.com/talkhabi/vue-persian-datetime-picker' 53 | } 54 | ], 55 | sidebar: { 56 | '/guide/': [ 57 | { 58 | title: 'Guide', 59 | collapsable: false, 60 | children: [ 61 | '', 62 | 'installation', 63 | 'examples', 64 | 'recommend-config', 65 | ] 66 | }, 67 | { 68 | title: 'Examples', 69 | collapsable: true, 70 | children: [ 71 | 'formatting', 72 | 'min-max', 73 | 'initial-value', 74 | 'different-input-format', 75 | 'view', 76 | 'simple-mode', 77 | 'popover', 78 | 'range', 79 | 'multiple', 80 | 'editable', 81 | 'custom-input', 82 | 'input-settings', 83 | 'auto-submit', 84 | 'theme', 85 | 'append-to', 86 | 'disabling', 87 | 'label', 88 | 'highlight', 89 | 'localization', 90 | 'timezone', 91 | 'datetime-picker', 92 | 'time-picker', 93 | 'use-router', 94 | ] 95 | }, 96 | { 97 | title: 'Slots - Events', 98 | collapsable: true, 99 | children: [ 100 | 'slots', 101 | 'events', 102 | ] 103 | }, 104 | ], 105 | } 106 | }, 107 | 108 | /** 109 | * Apply plugins,ref:https://v1.vuepress.vuejs.org/zh/plugin/ 110 | */ 111 | plugins: [ 112 | '@vuepress/plugin-back-to-top', 113 | '@vuepress/plugin-medium-zoom', 114 | ] 115 | } 116 | -------------------------------------------------------------------------------- /docs/.vuepress/enhanceApp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Client app enhancement file. 3 | * 4 | * https://v1.vuepress.vuejs.org/guide/basic-config.html#app-level-enhancements 5 | */ 6 | import VuePersianDatetimePicker from '../../src/VuePersianDatetimePicker' 7 | 8 | export default ({ 9 | Vue, // the version of Vue being used in the VuePress app 10 | options, // the options for the root Vue instance 11 | router, // the router instance for the app 12 | siteData // site metadata 13 | }) => { 14 | // ...apply enhancements for the site. 15 | 16 | Vue.component('date-picker', VuePersianDatetimePicker) 17 | } 18 | -------------------------------------------------------------------------------- /docs/.vuepress/public/fonts/IRANSansWeb.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talkhabi/vue-persian-datetime-picker/5794f06ce909abe5de605b1f1b7a4384387a0a7d/docs/.vuepress/public/fonts/IRANSansWeb.eot -------------------------------------------------------------------------------- /docs/.vuepress/public/fonts/IRANSansWeb.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talkhabi/vue-persian-datetime-picker/5794f06ce909abe5de605b1f1b7a4384387a0a7d/docs/.vuepress/public/fonts/IRANSansWeb.ttf -------------------------------------------------------------------------------- /docs/.vuepress/public/fonts/IRANSansWeb.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talkhabi/vue-persian-datetime-picker/5794f06ce909abe5de605b1f1b7a4384387a0a7d/docs/.vuepress/public/fonts/IRANSansWeb.woff -------------------------------------------------------------------------------- /docs/.vuepress/public/fonts/IRANSansWeb.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talkhabi/vue-persian-datetime-picker/5794f06ce909abe5de605b1f1b7a4384387a0a7d/docs/.vuepress/public/fonts/IRANSansWeb.woff2 -------------------------------------------------------------------------------- /docs/.vuepress/public/fonts/IRANSansWeb_Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talkhabi/vue-persian-datetime-picker/5794f06ce909abe5de605b1f1b7a4384387a0a7d/docs/.vuepress/public/fonts/IRANSansWeb_Bold.eot -------------------------------------------------------------------------------- /docs/.vuepress/public/fonts/IRANSansWeb_Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talkhabi/vue-persian-datetime-picker/5794f06ce909abe5de605b1f1b7a4384387a0a7d/docs/.vuepress/public/fonts/IRANSansWeb_Bold.ttf -------------------------------------------------------------------------------- /docs/.vuepress/public/fonts/IRANSansWeb_Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talkhabi/vue-persian-datetime-picker/5794f06ce909abe5de605b1f1b7a4384387a0a7d/docs/.vuepress/public/fonts/IRANSansWeb_Bold.woff -------------------------------------------------------------------------------- /docs/.vuepress/public/fonts/IRANSansWeb_Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talkhabi/vue-persian-datetime-picker/5794f06ce909abe5de605b1f1b7a4384387a0a7d/docs/.vuepress/public/fonts/IRANSansWeb_Bold.woff2 -------------------------------------------------------------------------------- /docs/.vuepress/public/fonts/IRANSansWeb_Light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talkhabi/vue-persian-datetime-picker/5794f06ce909abe5de605b1f1b7a4384387a0a7d/docs/.vuepress/public/fonts/IRANSansWeb_Light.eot -------------------------------------------------------------------------------- /docs/.vuepress/public/fonts/IRANSansWeb_Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talkhabi/vue-persian-datetime-picker/5794f06ce909abe5de605b1f1b7a4384387a0a7d/docs/.vuepress/public/fonts/IRANSansWeb_Light.ttf -------------------------------------------------------------------------------- /docs/.vuepress/public/fonts/IRANSansWeb_Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talkhabi/vue-persian-datetime-picker/5794f06ce909abe5de605b1f1b7a4384387a0a7d/docs/.vuepress/public/fonts/IRANSansWeb_Light.woff -------------------------------------------------------------------------------- /docs/.vuepress/public/fonts/IRANSansWeb_Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talkhabi/vue-persian-datetime-picker/5794f06ce909abe5de605b1f1b7a4384387a0a7d/docs/.vuepress/public/fonts/IRANSansWeb_Light.woff2 -------------------------------------------------------------------------------- /docs/.vuepress/public/fonts/IRANSans_Medium.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talkhabi/vue-persian-datetime-picker/5794f06ce909abe5de605b1f1b7a4384387a0a7d/docs/.vuepress/public/fonts/IRANSans_Medium.eot -------------------------------------------------------------------------------- /docs/.vuepress/public/vue-persian-datetime-picker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talkhabi/vue-persian-datetime-picker/5794f06ce909abe5de605b1f1b7a4384387a0a7d/docs/.vuepress/public/vue-persian-datetime-picker.png -------------------------------------------------------------------------------- /docs/.vuepress/styles/index.styl: -------------------------------------------------------------------------------- 1 | /** 2 | * Custom Styles here. 3 | * 4 | * ref:https://v1.vuepress.vuejs.org/config/#index-styl 5 | */ 6 | 7 | $fonts-path = '/vue-persian-datetime-picker/fonts/' 8 | 9 | @font-face 10 | font-family IRANSans 11 | font-style normal 12 | font-weight bold 13 | font-display fallback 14 | src url($fonts-path + 'IRANSansWeb_Bold.eot') 15 | src url($fonts-path + 'IRANSansWeb_Bold.eot?#iefix') format('embedded-opentype'), 16 | url($fonts-path + 'IRANSansWeb_Bold.woff2') format('woff2'), 17 | url($fonts-path + 'IRANSansWeb_Bold.woff') format('woff'), 18 | url($fonts-path + 'IRANSansWeb_Bold.ttf') format('truetype') 19 | 20 | @font-face 21 | font-family IRANSans 22 | font-style normal 23 | font-weight normal 24 | font-display fallback 25 | src url($fonts-path + 'IRANSansWeb.eot') 26 | src url($fonts-path + 'IRANSansWeb.eot?#iefix') format('embedded-opentype'), 27 | url($fonts-path + 'IRANSansWeb.woff2') format('woff2'), 28 | url($fonts-path + 'IRANSansWeb.woff') format('woff'), 29 | url($fonts-path + 'IRANSansWeb.ttf') format('truetype') 30 | 31 | @font-face 32 | font-family IRANSans 33 | font-style normal 34 | font-weight 300 35 | font-display fallback 36 | src url($fonts-path + 'IRANSansWeb_Light.eot') 37 | src url($fonts-path + 'IRANSansWeb_Light.eot?#iefix') format('embedded-opentype'), 38 | url($fonts-path + 'IRANSansWeb_Light.woff2') format('woff2'), 39 | url($fonts-path + 'IRANSansWeb_Light.woff') format('woff'), 40 | url($fonts-path + 'IRANSansWeb_Light.ttf') format('truetype') 41 | 42 | .vpd-container 43 | font-family IRANSans, sans-serif 44 | font-size 13px 45 | button 46 | font-family inherit 47 | 48 | .vpd-input-group 49 | input 50 | border-left none 51 | border-right solid 1px #dadada 52 | border-radius 0 53 | label svg + span 54 | margin-right 0 !important 55 | margin-left 4px 56 | .vpd-clear-btn 57 | left auto 58 | right 0 59 | 60 | .vpd-main 61 | max-width 260px 62 | margin 10px 0 30px 63 | display inline-block 64 | 65 | .home .hero img 66 | max-height 486px!important 67 | 68 | .form-control 69 | border solid 1px #dadada 70 | background white 71 | line-height 30px 72 | padding 0 15px 73 | vertical-align middle 74 | &:disabled 75 | background-color #e9ecef 76 | -------------------------------------------------------------------------------- /docs/.vuepress/styles/palette.styl: -------------------------------------------------------------------------------- 1 | /** 2 | * Custom palette here. 3 | * 4 | * ref:https://v1.vuepress.vuejs.org/zh/config/#palette-styl 5 | */ 6 | 7 | $accentColor = #417df4 8 | $textColor = #2c3e50 9 | $borderColor = #eaecef 10 | $codeBgColor = #282c34 11 | -------------------------------------------------------------------------------- /docs/config/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar: auto 3 | --- 4 | 5 | # Config 6 | 7 | ## value 8 | 9 | Default input value 10 | - Type: `String` | `Array` | `Number` | `Date` 11 | - Default: `''` 12 | - Example: `1396/08/01 22:45` | `2017/07/07 20:45` | `1500838200` | `20:45` | `new Date()` 13 | 14 | 15 | ## initialValue 16 | 17 | Initial value of picker (if value is empty) 18 | - Type: `String` | `Number` 19 | - Default: `'''` 20 | - Example: `1396/08/01 22:45` | `2017/07/07 20:45` | `1500838200` | `20:45` 21 | 22 | 23 | ## inputFormat 24 | 25 | Format for `value` 26 | - Type: `String` 27 | - Default: `''` 28 | - Example: `jYYYY/jMM/jDD HH:mm` | `YYYY/MM/DD HH:mm` | `x` | `HH:mm` 29 | - See [moment-jalaali](https://github.com/jalaali/moment-jalaali) 30 | 31 | 32 | ## displayFormat 33 | 34 | Format only to display the date in the field 35 | - Type: `String` 36 | - Default: `''` 37 | - Example: `jYYYY/jMM/jDD HH:mm` | `YYYY/MM/DD HH:mm` | `x` | `HH:mm` 38 | - See [moment-jalaali](https://github.com/jalaali/moment-jalaali) 39 | 40 | 41 | ## format 42 | 43 | Format for input and output value 44 | - Type: `String` 45 | - Default: `''` 46 | - Example: `jYYYY/jMM/jDD HH:mm` | `YYYY/MM/DD HH:mm` | `x` | `date` | `HH:mm` 47 | - If empty, it will be built according to the type of picker: 48 | * --- `time`: `HH:mm` 49 | * --- `datetime`: `jYYYY/jMM/jDD HH:mm` 50 | * --- `date`: `jYYYY/jMM/jDD` 51 | * --- `year`: `jYYYY` 52 | * --- `month`: `jMM` 53 | - See [moment-jalaali](https://github.com/jalaali/moment-jalaali) 54 | 55 | 56 | ## view 57 | 58 | Step to view on startup 59 | - Type: `String` 60 | - Default: `day` 61 | - Accepted: `day` | `month` | `year` | `time` 62 | 63 | 64 | ## type 65 | 66 | The picker type 67 | - Type: `String` 68 | - Default: `date` 69 | - Accepted: `date` | `datetime` | `year` | `month` | `time` 70 | 71 | 72 | ## min 73 | 74 | The minimum of selectable period 75 | - Based on `inputFormat` or `format` 76 | - Type: `String` 77 | - Default: `''` 78 | - Example: `1396/08/01 22:45` | `22:45` 79 | 80 | 81 | ## max 82 | 83 | The maximum of selectable period 84 | - Type: `String` 85 | - Default: `''` 86 | - Example: `1396/08/01 22:45` | `22:45` 87 | 88 | 89 | ## editable 90 | 91 | Editable input or just readonly 92 | - Type: `Boolean` 93 | - Default: `false` 94 | 95 | 96 | ## element 97 | Deprecated, Use [customInput](/config/#custominput) 98 | 99 | Sometimes you don't want to use picker default input, 100 | so you can use our own input element with `id` attribute 101 | - Type: `String` 102 | - Default: `undefined` 103 | - Example: `the-id-of-custom-input` 104 | 105 | 106 | ## customInput 107 | New version of `element` 108 | use this instead of `element`, this custom input does not need v-model, and it will be automatically updated 109 | also supports `display-format` 110 | - Type: String (DOMString containing a selector list) 111 | - Default: `undefined` 112 | - Example: `.my-custom-input` 113 | - Version: `>= 2.10.0` 114 | 115 | 116 | ## name 117 | 118 | The form input name (when you are not using `element`) 119 | - Type: `String` 120 | - Default: `undefined` 121 | - Example: `my-input-name` 122 | 123 | 124 | ## inputClass 125 | 126 | The form input className (when you are not using `element`) 127 | - Type: `String` 128 | - Default: `form-control` 129 | - Example: `my-custom-class` 130 | 131 | 132 | ## placeholder 133 | 134 | The form input placeholder (when you are not using `element`) 135 | - Type: `String` 136 | - Default: `''` 137 | - Example: `select date` 138 | 139 | 140 | ## altName 141 | 142 | Hidden input element name 143 | - Type: `String` 144 | - Default: `''` 145 | - Example: `birthday` 146 | - If empty, the hidden input will not be created 147 | 148 | 149 | ## altFormat 150 | 151 | Format for hidden input 152 | - Type: `String` 153 | - Default: `''` 154 | - Example: `YYYY-MM-DD HH:mm:ss [GMT]ZZ` 155 | - If empty, it will be built according to the type of picker: 156 | * --- `time`: `HH:mm:ss [GMT]ZZ` 157 | * --- `datetime`: `YYYY-MM-DD HH:mm:ss [GMT]ZZ` 158 | * --- `date`: `YYYY-MM-DD` 159 | * --- `year`: `YYYY` 160 | * --- `month`: `MM` 161 | - See [moment-jalaali](https://github.com/jalaali/moment-jalaali) 162 | 163 | 164 | ## show 165 | 166 | Show or hide the picker 167 | - Type: `Boolean` 168 | - Default: `false` 169 | 170 | 171 | ## color 172 | 173 | Primary color of picker 174 | - Type: `String` 175 | - Default: `#417df4` 176 | - Example: `red` | `#417df4` 177 | 178 | 179 | ## autoSubmit 180 | 181 | Auto submit and hide picker After a date is selected 182 | - Type: `Boolean` 183 | - Default: `false` 184 | 185 | 186 | ## wrapperSubmit 187 | 188 | Auto submit when clicking the wrapper 189 | - Type: `Boolean` 190 | - Default: `fals` 191 | 192 | 193 | ## appendTo 194 | 195 | Place to append picker 196 | - Type: `String` 197 | - Default: `null` 198 | - Example: `#app` | `#my-container` 199 | - If you want to append picker to another container like `body`, 200 | pass the container as `append-to="body"`. 201 | 202 | 203 | ## disabled 204 | 205 | Disable or enable the datepicker 206 | - Type: `Boolean` 207 | - Default: `false` 208 | 209 | 210 | ## disable 211 | 212 | Disabling some dates or items 213 | - Type: `Array` | `String` | `Function` | `RegExp` 214 | - Default: `undefined` 215 | - Example: `['1397/02/02', '1390/10/10']` | `1397/05/20` | `/1397\/05\/(.*)/` 216 | 217 | 218 | ## label 219 | 220 | The input label 221 | - Type: `String` 222 | - Default: `''` 223 | - Example: `date` | `birthday` 224 | 225 | 226 | ## highlight 227 | 228 | Highlighting items 229 | - Type: `Function` 230 | - Default: `null` 231 | - This option only accepts function that must return an object of attributes. 232 | 233 | 234 | ## jumpMinute 235 | 236 | Change minutes by step 237 | - Type: `Number` 238 | - Default: `1` 239 | - Example: `1` | `5` | `15` 240 | 241 | 242 | ## roundMinute 243 | 244 | Round minutes when jumpMinute is greater than 1 245 | - Type: `Boolean` 246 | - Default: `false` 247 | - Example: when `jumpMinute = 15`, will result: `13:00`, `13:15`, `13:30`, `13:45` ... 248 | 249 | 250 | ## clearable 251 | 252 | Show clear button 253 | - Type: `Boolean` 254 | - Default: `false` 255 | 256 | 257 | ## inline 258 | 259 | Inline mode 260 | - Type: `Boolean` 261 | - Default: `false` 262 | 263 | 264 | ## locale 265 | 266 | Locales config (`fa` for jalali and `en` for gregorian) 267 | - Type: `String` 268 | - Default: `fa` 269 | - Accepted: `fa` | `en` | `fa,en` | `en,fa` 270 | - If you need other locales like `ar` or `fr`... see [localization](/guide/localization.html) 271 | 272 | 273 | ## localeConfig 274 | 275 | Locales configuration 276 | - Type: `Object` 277 | - Default: `{}` 278 | - Example: see [localization](/guide/localization.html) 279 | 280 | 281 | ## timezone 282 | 283 | Timezone configuration 284 | - Type: `String` | `Boolean` | `Function` 285 | - Default: `fase` 286 | - Example: `true` | `false` | `+03:30` | `+04:30` 287 | 288 | 289 | ## showNowBtn 290 | 291 | Show or hide `now` button 292 | - Type: `Boolean` 293 | - Default: `true` 294 | 295 | 296 | ## convertNumbers 297 | 298 | Convert to locale numbers or not 299 | - Type: `Boolean` 300 | - Default: `false` 301 | 302 | 303 | ## compactTime 304 | 305 | Display the time on the front page 306 | - Type: `Boolean` 307 | - Default: `false` 308 | 309 | 310 | ## range 311 | 312 | Enable or disable range mode 313 | - Type: `Boolean` | `` | `` 314 | - Default: `false` 315 | 316 | 317 | ## multiple 318 | 319 | Enable or disable multiple mode 320 | - Type: `Boolean` 321 | - Default: `false` 322 | 323 | 324 | ## popover 325 | 326 | Enable or disable popover mode 327 | - Type: `Boolean` | `String` | `Object` 328 | - Default: `false` 329 | - Accepted: 330 | * `true` | `false` 331 | * `top` | `bottom` | `right` | `left` 332 | * `top-left` | `top-right` | `bottom-right` | `bottom-left` 333 | * `{ offsetX: Number, offsetY: Number }` 334 | * `{ placement: String, offsetX: Number, offsetY: Number }` 335 | 336 | 337 | ## useRouter 338 | 339 | If you want to change route address in open/close action, then enable this option. 340 | - Type: `Boolean` | `String` 341 | - Default: `false` 342 | - Example: `true` | `false` | `foo` 343 | 344 | 345 | ## simple 346 | 347 | Enable or disable simple mode 348 | - Type: `Boolean` 349 | - Default: `false` 350 | 351 | 352 | ## inputAttrs 353 | 354 | Additional attributes for input element 355 | - Type: `Object` 356 | - Default: `null` 357 | - Example: `{ 'data-foo': 'bar' }` 358 | -------------------------------------------------------------------------------- /docs/guide/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | # Vue persian datetime picker 4 | A [vuejs 2](https://vuejs.org/) plugin to select jalali/gregorian date and time 5 | 6 | ```vue {2,8} 7 | 10 | 19 | ``` 20 | 21 | 22 | 23 | 24 | 25 | 34 | 35 | ::: tip vue-3 support 36 | If you are using [vuejs 3](https://v3.vuejs.org/) please refer to [vue3-persian-datetime-picker](https://github.com/talkhabi/vue3-persian-datetime-picker) 37 | ::: 38 | -------------------------------------------------------------------------------- /docs/guide/append-to.md: -------------------------------------------------------------------------------- 1 | # Append to container 2 | 3 | - With this option you can specify where to append the picker 4 | 5 | ::: warning 6 | This will also work in `inline` mode! 7 | So be careful when using these options together. 8 | ::: 9 | 10 | ```vue 11 | 12 | ``` 13 | 14 | 15 | 16 | 17 | 18 | ```vue 19 | 20 | 21 | 22 | ``` 23 | -------------------------------------------------------------------------------- /docs/guide/auto-submit.md: -------------------------------------------------------------------------------- 1 | # Auto submit 2 | 3 | ```vue 4 | 5 | ``` 6 | 7 | 8 | 9 | 10 | ```vue 11 | 12 | ``` 13 | 14 | 15 | 16 | 17 | ```vue 18 | 19 | ``` 20 | 21 | 22 | 23 | 24 | 25 | ```vue 26 | 27 | ``` 28 | 29 | 30 | 31 | 32 | - Wrapper submit 33 | 34 | ```vue 35 | 36 | ``` 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /docs/guide/custom-input.md: -------------------------------------------------------------------------------- 1 | # Custom input 2 | 3 | - #### Using `custom-input` prop 4 | ```vue 5 | 10 | date = {{ date }} 11 | 12 | 18 | ``` 19 | ```js 20 | export default { 21 | data() { 22 | return { 23 | date: '' 24 | } 25 | } 26 | } 27 | ``` 28 | 29 | 41 | 42 | 47 | 48 | date = {{ date }} 49 | 50 | 51 | 57 | 58 | 59 | 60 | - #### Method B: Using `element` prop (deprecated) 61 | 62 | :::details show example 63 | ```vue 64 | 71 | 72 | 77 | ``` 78 | ```js 79 | export default { 80 | data() { 81 | return { 82 | date: '' 83 | } 84 | } 85 | } 86 | ``` 87 | 88 | 95 | 96 | 97 | 102 | 103 | 104 | ::: 105 | 106 | 107 | ::: danger 108 | `element` does not support `display-format`. 109 | 110 | please use `custom-input`. 111 | ::: 112 | 113 | 114 | ### Custom editable input 115 | ```vue 116 | 119 | 120 | 125 | 126 | 135 | ``` 136 | ```js 137 | export default { 138 | data() { 139 | return{ 140 | date: '', 141 | show: false 142 | } 143 | } 144 | } 145 | ``` 146 | 147 |
148 | 151 | 152 | 158 | 159 | date = {{ date2 }} 160 | 161 | 162 | 171 | 172 | 173 |
174 | 175 | -------------------------------------------------------------------------------- /docs/guide/datetime-picker.md: -------------------------------------------------------------------------------- 1 | # Datetime picker 2 | 3 | - Simple datetime picker 4 | ```vue 5 | 6 | ``` 7 | 8 | 9 | 10 | 11 | 12 | 13 | - Compact mode 14 | ```vue 15 | 20 | ``` 21 | 22 | 27 | 28 | 29 | - Datetime minimum and maximum 30 | 31 | ```vue 32 | 38 | ``` 39 | 40 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /docs/guide/different-input-format.md: -------------------------------------------------------------------------------- 1 | # Different input and output format 2 | 3 | ```vue 4 | 10 | ``` 11 | 12 | 18 | 19 | 20 | ```js 21 | /** 22 | * Different input and output format 23 | * 1: set input by value="..." 24 | * 2: set input format by input-format="..." 25 | * 3: set output format by format="..." 26 | * 4: get output by @input="date=$event" 27 | */ 28 | "$event" 29 | type: String 30 | returns formatted datetime 31 | ``` 32 | 33 | ```vue 34 | 40 | 46 | ``` 47 | -------------------------------------------------------------------------------- /docs/guide/disabling.md: -------------------------------------------------------------------------------- 1 | # Disabling 2 | 3 | ## Disable datepicker 4 | 5 | ```vue 6 | 7 | ``` 8 | 9 | 27 | 28 | 32 | 33 | `disabled = {{ disabled }}` 34 | 35 | 36 | 37 | 38 | 39 | 40 | ## Disable dates 41 | - String 42 | ```vue 43 | 44 | ``` 45 | 46 | 47 | 48 | 49 | - Day name 50 | ```vue 51 | 52 | ``` 53 | 54 | 55 | 56 | 57 | - Array of strings 58 | ```vue 59 | 60 | ``` 61 | 62 | 63 | 64 | 65 | - Regular expression 66 | ```vue 67 | 68 | ``` 69 | 70 | 71 | 72 | 73 | - Array of strings and RegExp 74 | ```vue 75 | 76 | ``` 77 | 78 | 79 | 80 | 81 | - Function 82 | ```vue 83 | 84 | 97 | ``` 98 | 99 | 100 | 101 | 102 | 103 | - Datetime examples 104 | ```vue 105 | String 106 | 107 | 108 | Array of strings 109 | 110 | 111 | Regular Expressions 112 | 113 | 114 | Array of strings and RegExp 115 | 116 | ``` 117 | 118 | - Time examples 119 | ```vue 120 | String 121 | 122 | 123 | Array of strings 124 | 125 | 126 | Regular Expressions 127 | 128 | 129 | Array of strings and RegExp 130 | 131 | ``` 132 | -------------------------------------------------------------------------------- /docs/guide/editable.md: -------------------------------------------------------------------------------- 1 | # Editable input 2 | 3 | ```vue 4 | 10 | ``` 11 | ```css 12 | input.is-editable { 13 | direction: ltr; 14 | text-align: left; 15 | } 16 | ``` 17 | 18 | 23 | 24 | -------------------------------------------------------------------------------- /docs/guide/events.md: -------------------------------------------------------------------------------- 1 | # Available events 2 | 3 |
4 |
5 | 6 | - `@input` event 7 | ```vue 8 | 13 | date = {{ date }} 14 | ``` 15 | 16 | 28 | 29 | 30 | 36 | 37 | 38 | date = {{ date }} 39 | 40 |
41 |
42 | 43 | - `@change` event 44 | ```vue 45 | 50 | dateMoment = {{ dateMoment.format('dddd jDD jMMMM jYYYY') }} 51 | date = {{ date }} 52 | ``` 53 | ```js 54 | import moment from 'moment-jalaali'; 55 | export default { 56 | data() { 57 | return { 58 | date: '1396/05/03', 59 | dateMoment: moment('1396/05/03', 'jYYYY/jMM/jDD'), 60 | } 61 | } 62 | } 63 | ``` 64 | 65 | 66 | 72 | 73 | 74 | dateMoment = {{ dateMoment.format('dddd jDD jMMMM jYYYY') }} 75 |
76 | date = {{ date2 }} 77 | 78 |
79 |
80 | 81 | - `@open` | `@close` events 82 | ```vue 83 | 84 | ``` 85 | ```js 86 | export default { 87 | methods: { 88 | onOpen(picker) { 89 | console.log('The datepicker is open'); 90 | }, 91 | onClose(picker) { 92 | console.log('The datepicker was closed'); 93 | } 94 | } 95 | } 96 | ``` 97 | 98 |
99 |
100 | 101 | - `@localeChange` event 102 | ```vue 103 | 104 | ``` 105 | ```js 106 | export default { 107 | methods: { 108 | onLocaleChange(localeData) { 109 | console.log('locale-change', localeData); 110 | } 111 | } 112 | } 113 | ``` 114 | 115 |
116 |
117 | 118 | - `@next-month` | `@prev-month` events 119 | ```vue 120 | 121 | ``` 122 | ```js 123 | export default { 124 | methods: { 125 | onNextMonth(date) { 126 | console.log('next-month', date); 127 | }, 128 | onPrevMonth(date) { 129 | console.log('prev-month', date); 130 | } 131 | } 132 | } 133 | ``` 134 | 135 |
136 |
137 | 138 | - `@year-change` | `@month-change` events 139 | ```vue 140 | 141 | ``` 142 | ```js 143 | export default { 144 | methods: { 145 | onYearChange(year) { 146 | console.log('year-change', year); 147 | }, 148 | onMonthChange(month) { 149 | console.log('month-change', month); 150 | } 151 | } 152 | } 153 | ``` 154 | -------------------------------------------------------------------------------- /docs/guide/examples.md: -------------------------------------------------------------------------------- 1 | # Basic examples 2 | 3 | ```vue 4 | 5 | ``` 6 | 7 | 8 | 9 | 10 | 11 | ```vue 12 | 13 | ``` 14 | 15 | 16 | 17 | 18 | 19 | ```vue 20 | 21 | ``` 22 | 23 | 24 | 25 | 26 | 27 | ```vue 28 | 29 | ``` 30 | 31 | 32 | 33 | 34 | 35 | ```vue 36 | 37 | ``` 38 | 39 | 40 | 41 | 42 | 43 | ```vue 44 | 45 | ``` 46 | 47 | 48 | 49 | 50 | 51 | ```vue 52 | 53 | ``` 54 | 55 | 56 | 57 | 58 | 59 | ```vue 60 | 61 | ``` 62 | 63 | 64 | 65 | 66 | ```vue 67 | 68 | ``` 69 | 70 | 71 | 72 | 73 | ```vue 74 | 75 | ``` 76 | 77 | 78 | 79 | 80 | ```vue 81 | 82 | ``` 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /docs/guide/formatting.md: -------------------------------------------------------------------------------- 1 | # Formatting 2 | 3 | ```vue 4 | 8 | ``` 9 | 10 | 11 | 12 | 13 | 14 | 15 | ::: details Click to view more info 16 | ```js 17 | "format" 18 | type: String 19 | default: 20 | HH:mm //for time 21 | jYYYY/jMM/jDD HH:mm //for datetime 22 | jYYYY/jMM/jDD //for date 23 | jYYYY //for year 24 | jMM //for month 25 | example: 26 | jYY-jMM-jDD //00-07-24 27 | jYY/jMM/jDD //00/07/24 28 | jYY jMMMM jDD //00 مهر 24 29 | jYYYY jMMMM jDD //1400 مهر 24 30 | dddd jDD jMMMM jYYYY //شنبه 24 مهر 1400 31 | dddd jDD jMMMM jYYYY ساعت HH:mm //شنبه 24 مهر 1400 ساعت 00:54 32 | ``` 33 | ::: 34 | 35 | - Also see [moment](https://momentjs.com/docs/#/displaying/format/) and [moment-jalaali](https://github.com/jalaali/moment-jalaali) 36 | 37 | ## Format only for display 38 | 39 | ```vue 40 | 41 | 42 | 43 | 44 | 49 | ``` 50 | 51 | ```js 52 | export default { 53 | data() { 54 | return { 55 | user: { 56 | name: '', 57 | tel: '', 58 | birthday: '', 59 | } 60 | } 61 | } 62 | } 63 | ``` 64 | 65 | 66 | 67 | 68 | 69 |
70 | 71 | 72 | 73 | 74 | 75 |
76 | 77 | 78 | 83 | 84 | 85 | - `user = {{ user }}` 86 | 87 | 100 | -------------------------------------------------------------------------------- /docs/guide/highlight.md: -------------------------------------------------------------------------------- 1 | # Highlight dates 2 | 3 | - How to highlight some dates: 4 | 5 | ```vue 6 | 7 | 25 | ``` 26 | 27 | ```scss 28 | .highlighted-1 { 29 | > span { 30 | color: #EF6C00 !important; 31 | } 32 | .vpd-day-effect { 33 | background-color: #f9e1be !important; 34 | border: solid 1px #EF6C00; 35 | } 36 | // for simple mode: 37 | &.vpd-addon-list-item { 38 | color: #ef6c00 !important; 39 | &.vpd-selected { 40 | background-color: #f9e1be; 41 | } 42 | } 43 | } 44 | .highlighted-2 { 45 | color: #E91E63; 46 | .vpd-day-effect { 47 | background-color: #E91E63 !important; 48 | } 49 | // for simple mode: 50 | &.vpd-addon-list-item { 51 | color: #e91e63 !important; 52 | &.vpd-selected { 53 | background-color: #f1d4de; 54 | } 55 | } 56 | } 57 | ``` 58 | 59 | 77 | 106 | 107 | 108 | 109 | 110 | 111 | - Example for `time` 112 | ```vue 113 | 114 | ``` 115 | ```js 116 | export default { 117 | methods: { 118 | highlight(formatted, dateMoment, checkingFor) { 119 | if (dateMoment.hour() === dateMoment.minutes()) 120 | return { 121 | style: { color: '#00BCD4 !important' } 122 | }; 123 | return {} 124 | } 125 | } 126 | } 127 | ``` 128 | 129 | - Example for `month` 130 | ```vue 131 | 132 | ``` 133 | ```js 134 | export default { 135 | methods: { 136 | highlight(formatted, dateMoment, checkingFor) { 137 | if (formatted === '05') 138 | return { 139 | style: { color: 'red !important' }, 140 | class: 'highlighted', 141 | } 142 | return {} 143 | } 144 | } 145 | } 146 | ``` 147 | 148 | - Example for `year` 149 | ```vue 150 | 151 | ``` 152 | ```js 153 | export default { 154 | methods: { 155 | highlight(formatted, dateMoment, checkingFor) { 156 | if (checkingFor === 'year' && formatted === '1396') 157 | return { 158 | style: { color: 'red !important' }, 159 | class: 'highlighted', 160 | 'data-info': '1396', 161 | }; 162 | return {} 163 | } 164 | } 165 | } 166 | ``` 167 | -------------------------------------------------------------------------------- /docs/guide/initial-value.md: -------------------------------------------------------------------------------- 1 | # Initial value 2 | 3 | ```vue 4 | 8 | ``` 9 | ```js 10 | data() { 11 | return { 12 | date: '1396/05/03' 13 | } 14 | } 15 | ``` 16 | 17 | 18 | 22 | 23 | 24 | ::: details View more 25 | ```js 26 | /** 27 | * Show datepicker with initial value 28 | * v-bind:value="..." 29 | * or v-model="..." 30 | */ 31 | "value" 32 | type: String 33 | default: Null 34 | example: 20:20 | 1396/08/08 20:20 | 1396/08/08 35 | ``` 36 | 37 | ```vue 38 | 39 | 40 | 41 | 42 | ``` 43 | ::: 44 | 45 | ## Startup initial value 46 | - With initial value for start if `value` is empty 47 | 48 | ```vue 49 | 50 | ``` 51 | 52 | 53 | 54 | 55 | 56 | ```vue 57 | 58 | ``` 59 | 60 | 61 | 62 | 63 | 64 | ```vue 65 | 66 | ``` 67 | 68 | 69 | 70 | 71 | 72 | ```vue 73 | 74 | ``` 75 | 76 | 77 | 78 | 79 | 80 | ```vue 81 | 82 | ``` 83 | 84 | 85 | 86 | 87 | 88 | ```js 89 | data() { 90 | return { 91 | date: '', 92 | time: '', 93 | datetime: '', 94 | year: '', 95 | month: '', 96 | } 97 | } 98 | ``` 99 | -------------------------------------------------------------------------------- /docs/guide/input-settings.md: -------------------------------------------------------------------------------- 1 | # Input settings 2 | 3 | - Set `name`, `placeholder`, `class` or other attributes for input. 4 | 5 | ```vue 6 | 15 | 16 | output: 17 | 18 | 19 | 27 | 28 | ``` 29 | 30 | 31 | 39 | 40 | 41 | ## Alt field 42 | 43 | ::: warning NOTE 44 | An alternate field will only be created when you enter the `alt-name`. 45 | 46 | `` 47 | ::: 48 | 49 | ```vue 50 | 55 | output: 56 | 57 | 58 | 59 | 60 | 61 | ``` 62 | 63 | 67 | 68 | -------------------------------------------------------------------------------- /docs/guide/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | ## Install in a browser (umd) 4 | ```html 5 | 6 | 7 | 8 | 9 |
10 | 11 |
12 | 23 | ``` 24 | 25 | ## Install with npm 26 | ```bash 27 | npm install vue-persian-datetime-picker --save 28 | ``` 29 | 30 | Configuration for moment to ignore loading locales 31 | ```js 32 | // webpack.config.js: 33 | module.exports.plugins = [ 34 | //... 35 | new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/) 36 | //... 37 | ] 38 | 39 | // vue.config.js: 40 | module.exports = { 41 | //.. 42 | configureWebpack: { 43 | plugins: [new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)] 44 | }, 45 | //... 46 | } 47 | ``` 48 | 49 | ### Usage 50 | ```js 51 | // main.js 52 | //... 53 | import VuePersianDatetimePicker from 'vue-persian-datetime-picker'; 54 | Vue.component('date-picker', VuePersianDatetimePicker); 55 | //... 56 | ``` 57 | Or in component 58 | ```html 59 | 64 | 65 | 78 | ``` 79 | 80 | 81 | ## Global setup 82 | ```js 83 | // main.js 84 | import VuePersianDatetimePicker from 'vue-persian-datetime-picker'; 85 | Vue.use(VuePersianDatetimePicker, { 86 | name: 'custom-date-picker', 87 | props: { 88 | format: 'YYYY-MM-DD HH:mm', 89 | displayFormat: 'jYYYY-jMM-jDD HH:mm', 90 | editable: false, 91 | inputClass: 'form-control my-custom-class-name', 92 | placeholder: 'Please select a date', 93 | altFormat: 'YYYY-MM-DD HH:mm', 94 | color: '#00acc1', 95 | autoSubmit: true, 96 | //... 97 | //... And whatever you want to set as default. 98 | //... 99 | } 100 | }); 101 | ``` 102 | Then use in component 103 | ```html 104 | 105 | ``` 106 | -------------------------------------------------------------------------------- /docs/guide/label.md: -------------------------------------------------------------------------------- 1 | # Label 2 | 3 | 13 | 14 | ```vue 15 | 16 | 17 |
18 | « 19 | END 20 | » 21 |
22 |
23 | ``` 24 | ```js 25 | export default { 26 | data() { 27 | return { 28 | start: '', 29 | end: '', 30 | } 31 | } 32 | } 33 | ``` 34 | 35 | 36 | 37 | 38 |
39 | 40 | 41 | 42 |
43 | « 44 | END 45 | » 46 |
47 |
48 |
49 | -------------------------------------------------------------------------------- /docs/guide/localization.md: -------------------------------------------------------------------------------- 1 | # Localization 2 | 3 | ## Gregorian datepicker 4 | ```vue 5 | 6 | ``` 7 | 8 | 9 | 10 | 11 | ```vue 12 | 13 | ``` 14 | 15 | 16 | 17 | 18 | ```vue 19 | 32 | ``` 33 | 34 | 47 | 48 | 49 | ::: details View more 50 | ```vue 51 | "locale" 52 | type: String 53 | default: "fa" 54 | example: "fa" | "en" | "en,fa" | "fa,en" // ordering is important! 55 | /*******************************/ 56 | "localeConfig" 57 | type: Object 58 | default: {} 59 | example: 60 | 89 | ``` 90 | ::: 91 | 92 | 93 | ## Custom locale 94 | ```vue 95 | 100 | ``` 101 | ```js 102 | import moment from 'moment' 103 | import fr from 'moment/locale/fr' 104 | import ar from 'moment/locale/ar-sa' 105 | import zh from 'moment/locale/zh-cn' 106 | 107 | moment.updateLocale('fr', fr) 108 | moment.updateLocale('zh-cn', zh) 109 | moment.updateLocale('ar-sa', { ...ar, postformat: str => str }) 110 | 111 | export default { 112 | data() { 113 | return { 114 | localeConfig: { 115 | fa: { 116 | displayFormat: 'jYYYY/jMM/jDD', 117 | lang: { label: 'FA' } 118 | }, 119 | 'ar-sa': { 120 | dow: 0, 121 | dir: 'rtl', 122 | displayFormat: vm => { 123 | // vm.type = date | time | datetime | year | month | yearmonth 124 | switch (vm.type) { 125 | case 'date': 126 | return 'YYYY/MM/DD' 127 | case 'datetime': 128 | return 'YYYY/MM/DD HH:mm' 129 | case 'year': 130 | return 'YYYY' 131 | case 'month': 132 | return 'MM' 133 | case 'yearmonth': 134 | return 'YY/MM' 135 | case 'time': 136 | return 'HH:mm' 137 | } 138 | }, 139 | lang: { 140 | label: 'AR', 141 | submit: 'اختيار', 142 | cancel: 'إلغاء', 143 | now: 'الآن', 144 | nextMonth: 'الشهر القادم', 145 | prevMonth: 'الشهر الماضي' 146 | } 147 | }, 148 | fr: { 149 | dow: 0, 150 | dir: 'ltr', 151 | displayFormat: vm => { 152 | return vm.type === 'datetime' ? 'YYYY/MM/DD HH:mm' : 'YYYY/MM/DD' 153 | }, 154 | lang: { 155 | label: 'FR', 156 | submit: 'Sélection', 157 | cancel: 'Annuler', 158 | now: 'Maintenant', 159 | nextMonth: 'le mois prochain', 160 | prevMonth: 'le mois précédent' 161 | } 162 | }, 163 | 'zh-cn': { 164 | dow: 0, 165 | dir: 'ltr', 166 | displayFormat: vm => { 167 | return vm.type === 'datetime' ? 'YYYY/MM/DD HH:mm' : 'YYYY/MM/DD' 168 | }, 169 | lang: { 170 | label: 'ZH-CN', 171 | submit: '選拔', 172 | cancel: '取消', 173 | now: '現在', 174 | nextMonth: '下個月', 175 | prevMonth: '上個月' 176 | } 177 | } 178 | } 179 | } 180 | } 181 | } 182 | ``` 183 | 184 | 266 | 267 | 268 | 273 | 274 | -------------------------------------------------------------------------------- /docs/guide/min-max.md: -------------------------------------------------------------------------------- 1 | # Minimum and maximum 2 | 3 | ```vue 4 | 9 | ``` 10 | 11 | 12 | 16 | 17 | 18 | ```js 19 | /** 20 | * Limit datetime picker minimum and maximum 21 | */ 22 | "min" 23 | type: String 24 | default: Null 25 | example: 26 | 20:20 //for time 27 | 1396/08/08 20:20 //for datetime 28 | 1396/08/08 //for date 29 | /*******************************/ 30 | "max" 31 | type: String 32 | default: Null 33 | example: same as "min" 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/guide/multiple.md: -------------------------------------------------------------------------------- 1 | # Multiple select 2 | 3 | ::: warning NOTE: 4 | Multiple selection feature is only supported for `date` type. 5 | ::: 6 | 7 | ```vue 8 | 15 | ``` 16 | ```js 17 | export default { 18 | data() { 19 | return { 20 | dates: [] 21 | } 22 | } 23 | } 24 | 25 | ``` 26 | 27 | 36 | 37 | 44 | 45 | 46 | `dates = {{ dates }}` 47 | -------------------------------------------------------------------------------- /docs/guide/popover.md: -------------------------------------------------------------------------------- 1 | # Popover mode 2 | 3 | ```vue 4 | 5 | ``` 6 | 7 | 8 | 9 | 10 | 11 | ```vue 12 | 13 | ``` 14 | 15 | 16 | 17 | 18 | ```vue 19 | 27 | ``` 28 | 29 | 30 | 31 | 32 | 33 | accepted: 34 | 35 | `true` | `false` 36 | 37 | `top` | `bottom` | `right` | `left` 38 | 39 | `top-left` | `top-right` | `bottom-right` | `bottom-left` 40 | 41 | `{ offsetX: Number, offsetY: Number }` 42 | 43 | `{ placement: String, offsetX: Number, offsetY: Number }` 44 | 45 | -------------------------------------------------------------------------------- /docs/guide/range.md: -------------------------------------------------------------------------------- 1 | # Range select 2 | 3 | ::: warning NOTE: 4 | Range selection feature is only supported for `date` type. 5 | ::: 6 | 7 | ```vue 8 | 15 | ``` 16 | 26 | 27 | 35 | 36 | 37 | `range = {{ range }}` 38 | 39 | ::: tip 40 | Sometimes you need to range starts from 00:00 AM and ends in 23:59 PM, 41 | 42 | for example: `start = "2021-02-08 00:00", end = "2021-02-11 23:59"` 43 | 44 | so just set format according to the following config: 45 | ::: 46 | 47 | ```vue 48 | 59 | ``` 60 | ```js 61 | data() { 62 | return { 63 | range: [] 64 | } 65 | } 66 | ``` 67 | 68 | 69 | 80 | 81 | 82 | `range = {{ range2 }}` 83 | -------------------------------------------------------------------------------- /docs/guide/recommend-config.md: -------------------------------------------------------------------------------- 1 | # Recommend configuration 2 | 3 | #### The recommended configuration for registration and usage: 4 | 5 | ```js 6 | // main.js 7 | 8 | import Vue from 'vue' 9 | import VuePersianDatetimePicker from 'vue-persian-datetime-picker' 10 | 11 | Vue.use(VuePersianDatetimePicker, { 12 | name: 'date-picker', 13 | props: { 14 | format: 'YYYY-MM-DD HH:mm:ss', 15 | displayFormat: 'jYYYY-jMM-jDD HH:mm', // if you need jalali dates 16 | color: '#00acc1', 17 | autoSubmit: true, 18 | clearable: true, 19 | } 20 | }) 21 | ``` 22 | 23 | #### Advanced setup example: 24 | 25 | ```js 26 | // plugins/date-picker.js 27 | 28 | import Vue from 'vue' 29 | import moment from 'moment' 30 | import { cloneDeep as clone } from 'lodash' 31 | import VuePersianDatetimePicker from 'vue-persian-datetime-picker' 32 | 33 | const highlight = function highlight(formatted, dateMoment) { 34 | let attributes = {} 35 | if (dateMoment.format('YYYY-MM-DD') === moment().format('YYYY-MM-DD')) { 36 | attributes['class'] = 'is-today' 37 | } 38 | return attributes 39 | } 40 | 41 | let locale = process.env.VUE_APP_I18N_LOCALE || 'en' // the localization in your project env file (en,fa,ar...) 42 | let localeConfig = () => ({ 43 | ar: { 44 | dow: 0, 45 | dir: 'rtl', 46 | displayFormat: vm => { 47 | switch (vm.type) { 48 | case 'date': 49 | return 'YYYY/MM/DD' 50 | case 'datetime': 51 | return 'YYYY/MM/DD HH:mm' 52 | case 'year': 53 | return 'YYYY' 54 | case 'month': 55 | return 'MM' 56 | case 'yearmonth': 57 | return 'YY/MM' 58 | case 'time': 59 | return 'HH:mm' 60 | } 61 | }, 62 | lang: { 63 | label: 'AR', 64 | submit: 'اختيار', 65 | cancel: 'إلغاء', 66 | now: 'الآن', 67 | nextMonth: 'الشهر القادم', 68 | prevMonth: 'الشهر الماضي' 69 | } 70 | }, 71 | en: { 72 | dow: 0, 73 | dir: 'ltr', 74 | displayFormat: vm => { 75 | switch (vm.type) { 76 | case 'date': 77 | return 'YYYY/MM/DD' 78 | case 'datetime': 79 | return 'YYYY/MM/DD HH:mm' 80 | case 'year': 81 | return 'YYYY' 82 | case 'month': 83 | return 'MM' 84 | case 'yearmonth': 85 | return 'YY/MM' 86 | case 'time': 87 | return 'HH:mm' 88 | } 89 | } 90 | } 91 | }) 92 | 93 | export const datePickerConfig = { 94 | name: 'date-picker', 95 | props: { 96 | locale, 97 | highlight, 98 | localeConfig, 99 | clearable: true, 100 | autoSubmit: true, 101 | color: '#00acc1', 102 | format: 'YYYY-MM-DD', 103 | displayFormat: 'jDD jMMMM jYYYY' 104 | } 105 | } 106 | 107 | export const dateTimePickerConfig = { 108 | name: 'date-time-picker', 109 | props: { 110 | locale, 111 | highlight, 112 | localeConfig, 113 | clearable: true, 114 | autoSubmit: true, 115 | color: '#00acc1', 116 | type: 'datetime', 117 | format: 'YYYY-MM-DD HH:mm', 118 | displayFormat: 'jYYYY-jMM-jDD HH:mm' 119 | } 120 | } 121 | 122 | export const localize = function(localeName) { 123 | locale = localeName 124 | } 125 | 126 | Vue.use(clone(VuePersianDatetimePicker), datePickerConfig) 127 | Vue.use(clone(VuePersianDatetimePicker), dateTimePickerConfig) 128 | ``` 129 | -------------------------------------------------------------------------------- /docs/guide/simple-mode.md: -------------------------------------------------------------------------------- 1 | # Simple mode 2 | 3 | ```vue 4 | 5 | ``` 6 | 7 | 8 | 9 | 10 | 11 | ```vue 12 | 13 | ``` 14 | 15 | 16 | 17 | 18 | 19 | ```vue 20 | 21 | ``` 22 | 23 | 24 | 25 | 26 | 27 | ```vue 28 | 29 | ``` 30 | 31 | 32 | 33 | 34 | 35 | ```vue 36 | 37 | ``` 38 | 39 | 40 | 41 | 42 | 43 | ```vue 44 | 45 | ``` 46 | 47 | 48 | 49 | 50 | 51 | ```vue 52 | 53 | ``` 54 | 55 | 56 | 57 | 58 | 59 | ```vue 60 | 61 | ``` 62 | 63 | 64 | 65 | 66 | 67 | ```vue 68 | 69 | ``` 70 | 71 | 72 | 73 | 74 | 75 | ```vue 76 | 77 | ``` 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /docs/guide/slots.md: -------------------------------------------------------------------------------- 1 | # Available slots 2 | 3 | ```vue 4 | 5 | 6 | 7 | 10 | 11 | 12 | 15 | 16 | 17 | 20 | 21 | 22 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 41 | 42 | 43 | 47 | 48 | 49 | 52 | 53 | 54 | 57 | 58 | 59 | 62 | 63 | 64 | 67 | 68 | 69 | 78 | 79 | 80 | 88 | 89 | 90 | 98 | 99 | ``` 100 | -------------------------------------------------------------------------------- /docs/guide/theme.md: -------------------------------------------------------------------------------- 1 | # Theme and color 2 | 3 | ```vue 4 | 5 | ``` 6 | 7 | 8 | 9 | 10 | 11 | ```vue 12 | 13 | 14 | ``` 15 | 16 | 17 | 18 | 19 | 20 | ```vue 21 | 22 | ``` 23 | 24 | 25 | 26 | 27 | 28 | ```vue 29 | 30 | ``` 31 | 32 | 33 | 34 | 35 | 36 | ```vue 37 | 38 | ``` 39 | 40 | 41 | 42 | 43 | 44 | ```vue 45 | 46 | ``` 47 | 48 | 49 | 50 | 51 | 52 | ```vue 53 | 54 | ``` 55 | 56 | 57 | 58 | 59 | 60 | ```vue 61 | 62 | ``` 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /docs/guide/time-picker.md: -------------------------------------------------------------------------------- 1 | # Time picker 2 | 3 | - Simple time picker 4 | ```vue 5 | 6 | ``` 7 | 8 | 9 | 10 | 11 | - Time minimum and maximum 12 | ```vue 13 | 19 | ``` 20 | 21 | 26 | 27 | 28 | - Advance time picker 29 | ```vue 30 | 36 | ``` 37 | 38 | 43 | 44 | -------------------------------------------------------------------------------- /docs/guide/timezone.md: -------------------------------------------------------------------------------- 1 | # Timezone 2 | 3 | ```vue 4 | 11 | UTC: {{ datetime }} 12 | ``` 13 | 14 | ```js 15 | import moment from 'moment-jalaali' 16 | export default { 17 | data() { 18 | return { 19 | // moment().utc().format('YYYY-MM-DD HH:mm') 20 | datetime: '2021-10-21 20:26' 21 | } 22 | } 23 | } 24 | ``` 25 | 26 | 36 | 37 | 38 | 46 | 47 | 48 | UTC: {{ datetime }} 49 | 50 | 51 | -------------------------------------------------------------------------------- /docs/guide/use-router.md: -------------------------------------------------------------------------------- 1 | # Using vue-router 2 | 3 | ::: tip 4 | This option helps users to close the picker without changing the screen 5 | when pressing the back button. 6 | ::: 7 | 8 | ```vue 9 | 10 | ``` 11 | 12 | 13 | 14 | 15 | ```vue 16 | 17 | ``` 18 | 19 | 20 | 21 | 22 | ```vue 23 | 24 | ``` 25 | 26 | 27 | 28 | 29 | 30 | ```vue 31 | example => example.com/home?vpd-75454=active 32 | example => example.com/home?vpd-foo=active 33 | example => example.com/home?vpd-bar=active 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/guide/view.md: -------------------------------------------------------------------------------- 1 | # View at startup 2 | - Show a specific section at startup 3 | 4 | ```vue 5 | 6 | ``` 7 | 8 | 9 | 10 | 11 | 12 | ```js 13 | /** 14 | * Show 'year', 'month', 'day', or 'time' at startup 15 | */ 16 | "view" 17 | type: String 18 | default: day 19 | example: year | month | day | time 20 | ``` 21 | 22 | ```vue 23 | 24 | 25 | 26 | ``` 27 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | heroImage: /vue-persian-datetime-picker.png 4 | tagline: A vue plugin to select jalali/gregorian date and time 5 | actionText: Quick Start → 6 | actionLink: /guide/ 7 | features: 8 | - title: Jalali | Gregorian 9 | details: Fully localizable datepicker 10 | - title: Single | Range | Multiple 11 | details: Single, range and multiple modes are supported 12 | - title: Material | Simple 13 | details: You can set theme to be material or simple mode 14 | 15 | footer: MIT Licensed | Copyright © 2017-present Mohammad Talkhabi 16 | --- 17 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'vue-persian-datetime-picker' 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-persian-datetime-picker", 3 | "description": "A vue plugin to select jalali date and time", 4 | "version": "2.10.4", 5 | "private": false, 6 | "author": "Mohammad Talkhabi", 7 | "license": "MIT", 8 | "main": "dist/vue-persian-datetime-picker.common.js", 9 | "keywords": [ 10 | "vuejs", 11 | "persian", 12 | "jalali", 13 | "datepicker", 14 | "timepicker", 15 | "datetime picker", 16 | "vue datepicker", 17 | "persian datepicker", 18 | "jalali datepicker", 19 | "shamsi datepicker" 20 | ], 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/talkhabi/vue-persian-datetime-picker" 24 | }, 25 | "files": [ 26 | "src", 27 | "dist/*.js", 28 | "index.d.ts" 29 | ], 30 | "scripts": { 31 | "serve": "vuepress dev docs", 32 | "build": "npm run export-common & npm run export-umd", 33 | "test:unit": "vue-cli-service test:unit", 34 | "test:e2e": "vue-cli-service test:e2e", 35 | "lint": "vue-cli-service lint", 36 | "docs:dev": "vuepress dev docs", 37 | "docs:build": "vuepress build docs", 38 | "docs:deploy": "docs.deploy.sh", 39 | "export-common": "vue-cli-service build --target lib --name VuePersianDatetimePicker --filename vue-persian-datetime-picker src/VuePersianDatetimePicker.vue --formats commonjs --no-clean", 40 | "export-umd": "vue-cli-service build --target lib --name VuePersianDatetimePicker --filename vue-persian-datetime-picker src/VuePersianDatetimePicker.vue --formats umd,umd-min --no-clean" 41 | }, 42 | "dependencies": { 43 | "moment-jalaali": "^0.9.2" 44 | }, 45 | "devDependencies": { 46 | "@vue/cli-plugin-babel": "~4.5.0", 47 | "@vue/cli-plugin-e2e-cypress": "~4.5.0", 48 | "@vue/cli-plugin-eslint": "~4.5.0", 49 | "@vue/cli-plugin-unit-jest": "~4.5.0", 50 | "@vue/cli-service": "~4.5.0", 51 | "@vue/eslint-config-prettier": "^6.0.0", 52 | "@vue/test-utils": "^1.0.3", 53 | "babel-eslint": "^10.1.0", 54 | "core-js": "^3.6.5", 55 | "eslint": "^6.7.2", 56 | "eslint-plugin-prettier": "^3.3.1", 57 | "eslint-plugin-vue": "^7.5.0", 58 | "node-sass": "^4.12.0", 59 | "prettier": "^1.19.1", 60 | "sass-loader": "^8.0.2", 61 | "vue": "2.6.11", 62 | "vue-template-compiler": "2.6.11", 63 | "vue-server-renderer": "2.6.11", 64 | "vuepress": "^1.8.2" 65 | }, 66 | "eslintConfig": { 67 | "root": true, 68 | "env": { 69 | "node": true 70 | }, 71 | "extends": [ 72 | "plugin:vue/essential", 73 | "plugin:vue/strongly-recommended", 74 | "plugin:vue/recommended", 75 | "@vue/prettier" 76 | ], 77 | "parserOptions": { 78 | "parser": "babel-eslint" 79 | }, 80 | "rules": { 81 | "vue/html-self-closing": [ 82 | "warn", 83 | { 84 | "html": { 85 | "void": "always", 86 | "normal": "always", 87 | "component": "always" 88 | }, 89 | "svg": "always", 90 | "math": "always" 91 | } 92 | ] 93 | }, 94 | "overrides": [ 95 | { 96 | "files": [ 97 | "**/__tests__/*.{j,t}s?(x)", 98 | "**/tests/unit/**/*.spec.{j,t}s?(x)" 99 | ], 100 | "env": { 101 | "jest": true 102 | } 103 | } 104 | ] 105 | }, 106 | "prettier": { 107 | "semi": false, 108 | "singleQuote": true, 109 | "endOfLine": "lf", 110 | "tabWidth": 2 111 | }, 112 | "browserslist": [ 113 | "> 1%", 114 | "last 2 versions", 115 | "not dead" 116 | ], 117 | "jest": { 118 | "preset": "@vue/cli-plugin-unit-jest" 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/assets/scss/_transitions.scss: -------------------------------------------------------------------------------- 1 | .fade-enter-active, .fade-leave-active { 2 | transition: opacity .5s 3 | } 4 | 5 | .fade-enter, .fade-leave-active { 6 | opacity: 0 7 | } 8 | 9 | .vpd-fade-scale-enter-active, 10 | .vpd-fade-scale-leave-active { 11 | transition: opacity .5s 12 | } 13 | 14 | .vpd-fade-scale-enter, 15 | .vpd-fade-scale-leave-active { 16 | opacity: 0; 17 | 18 | .#{$prefix}content { 19 | transform: scale(0.7, 0.7); 20 | opacity: 0; 21 | } 22 | } 23 | 24 | .slideX-enter-active, 25 | .slideX-leave-active { 26 | position: absolute; 27 | top: 0; 28 | bottom: 0; 29 | right: 0; 30 | left: 0; 31 | opacity: 1; 32 | transform: translateX(0); 33 | transition: all .3s ease-out; 34 | } 35 | 36 | .slideX-leave-to, .slideX-enter { 37 | opacity: 0; 38 | } 39 | 40 | .direction-next { 41 | .slideX-leave-to { 42 | transform: translateX(-100%); 43 | } 44 | 45 | .slideX-enter { 46 | transform: translateX(100%); 47 | } 48 | } 49 | 50 | .direction-prev { 51 | .slideX-leave-to { 52 | transform: translateX(100%); 53 | } 54 | 55 | .slideX-enter { 56 | transform: translateX(-100%); 57 | } 58 | } 59 | 60 | .slideY-enter-active, 61 | .slideY-leave-active { 62 | position: absolute; 63 | top: 0; 64 | bottom: 0; 65 | right: 0; 66 | left: 0; 67 | opacity: 1; 68 | transform: translateY(0); 69 | transition: all .3s ease-in-out; 70 | } 71 | 72 | .slideY-leave-to, .slideY-enter { 73 | opacity: 0; 74 | } 75 | 76 | .direction-next { 77 | .slideY-leave-to { 78 | transform: translateY(100%); 79 | } 80 | 81 | .slideY-enter { 82 | transform: translateY(-100%); 83 | } 84 | } 85 | 86 | .direction-prev { 87 | .slideY-leave-to { 88 | transform: translateY(-100%); 89 | } 90 | 91 | .slideY-enter { 92 | transform: translateY(100%); 93 | } 94 | } 95 | 96 | .fade-transition { 97 | opacity: 1; 98 | transition: all 0.3s ease; 99 | } 100 | 101 | .fade-enter, .fade-leave { 102 | opacity: 0; 103 | } 104 | 105 | .fast-updating { 106 | .slideY-enter-active, 107 | .slideY-leave-active { 108 | transition: all 170ms ease-in-out; 109 | } 110 | 111 | .direction-next { 112 | .slideY-leave-to { 113 | transform: translateY(45%); 114 | } 115 | 116 | .slideY-enter { 117 | transform: translateY(-5%); 118 | } 119 | } 120 | 121 | .direction-prev { 122 | .slideY-leave-to { 123 | transform: translateY(-45%); 124 | } 125 | 126 | .slideY-enter { 127 | transform: translateY(5%); 128 | } 129 | } 130 | } 131 | 132 | .#{$prefix}dir-rtl { 133 | .direction-next { 134 | .slideX-leave-to { 135 | transform: translateX(100%); 136 | } 137 | 138 | .slideX-enter { 139 | transform: translateX(-100%); 140 | } 141 | } 142 | 143 | .direction-prev { 144 | .slideX-leave-to { 145 | transform: translateX(-100%); 146 | } 147 | 148 | .slideX-enter { 149 | transform: translateX(100%); 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/assets/scss/_variables.scss: -------------------------------------------------------------------------------- 1 | $prefix: vpd-; //shorted to reduce the css size 2 | $width: 316px; 3 | 4 | $day-size: 40px; 5 | $month-label-height: 50px; 6 | -------------------------------------------------------------------------------- /src/assets/scss/style.scss: -------------------------------------------------------------------------------- 1 | @import 'variables'; 2 | @import 'transitions'; 3 | 4 | .#{$prefix}main *, 5 | .#{$prefix}wrapper * { 6 | box-sizing: border-box; 7 | } 8 | 9 | .#{$prefix}clearfix { 10 | &:after, &:before { 11 | content: " "; 12 | display: table; 13 | } 14 | 15 | &:after { 16 | clear: both; 17 | } 18 | } 19 | 20 | .#{$prefix}input-group { 21 | display: flex; 22 | position: relative; 23 | 24 | input { 25 | flex-grow: 1; 26 | border: 1px solid #dadada; 27 | border-right: none; 28 | border-top-right-radius: 0; 29 | border-bottom-right-radius: 0; 30 | line-height: 30px; 31 | padding: 0 10px; 32 | 33 | &:not(.#{$prefix}is-editable) { 34 | cursor: pointer; 35 | } 36 | } 37 | 38 | label { 39 | color: white; 40 | white-space: nowrap; 41 | 42 | svg + span { 43 | display: inline-block; 44 | margin-right: 4px; 45 | vertical-align: middle; 46 | } 47 | } 48 | 49 | &.#{$prefix}disabled { 50 | input, label { 51 | cursor: default; 52 | } 53 | } 54 | 55 | .#{$prefix}clear-btn { 56 | position: absolute; 57 | left: 0; 58 | top: 0; 59 | line-height: 32px; 60 | width: 30px; 61 | cursor: pointer; 62 | text-align: center; 63 | font-style: normal; 64 | font-family: monospace, sans-serif; 65 | opacity: 0.4; 66 | 67 | &:hover { 68 | opacity: 0.7; 69 | } 70 | } 71 | } 72 | 73 | .#{$prefix}icon-btn { 74 | cursor: pointer; 75 | padding: 0 10px; 76 | display: flex; 77 | justify-content: center; 78 | align-items: center; 79 | } 80 | 81 | .#{$prefix}wrapper { 82 | position: fixed; 83 | top: 0; 84 | left: 0; 85 | right: 0; 86 | bottom: 0; 87 | width: 100%; 88 | height: 100%; 89 | background-color: rgba(0, 0, 0, 0.5); 90 | z-index: 9999; 91 | } 92 | 93 | .#{$prefix}container { 94 | position: absolute; 95 | top: 50%; 96 | left: 50%; 97 | transform: translate(-50%, -50%); 98 | user-select: none; 99 | -webkit-user-select: none; 100 | -moz-user-select: none; 101 | -ms-user-select: none; 102 | } 103 | 104 | .#{$prefix}content { 105 | opacity: 1; 106 | transition: all .4s cubic-bezier(0.25, 0.1, 0.17, 1.84); 107 | text-align: right; 108 | direction: rtl; 109 | width: $width; 110 | background-color: #ffffff; 111 | box-shadow: 5px 22px 95px -14px rgba(0, 0, 0, 1); 112 | cursor: default; 113 | } 114 | 115 | .#{$prefix}header { 116 | color: #ffffff; 117 | padding: 10px 20px; 118 | position: relative; 119 | } 120 | 121 | .#{$prefix}body { 122 | position: relative; 123 | } 124 | 125 | .#{$prefix}year-label { 126 | margin-bottom: 2px; 127 | position: relative; 128 | height: 24px; 129 | opacity: 0.7; 130 | overflow: hidden; 131 | cursor: pointer; 132 | font-size: 16px; 133 | 134 | > span { 135 | display: inline-block; 136 | padding: 0 10px; 137 | line-height: 22px; 138 | height: 22px; 139 | border-width: 1px; 140 | border-style: solid; 141 | border-radius: 2px; 142 | transition: all 0.1s ease-out; 143 | 144 | &:not(:hover) { 145 | border-color: transparent !important; 146 | color: inherit !important; 147 | } 148 | } 149 | } 150 | 151 | .#{$prefix}date { 152 | position: relative; 153 | font-size: 28px; 154 | line-height: 40px; 155 | height: 40px; 156 | overflow: hidden; 157 | 158 | span { 159 | display: block; 160 | height: inherit; 161 | line-height: inherit; 162 | } 163 | } 164 | 165 | .#{$prefix}week { 166 | font-size: 12px; 167 | padding: 0 14px; 168 | line-height: 20px; 169 | color: #b9b9b9; 170 | margin-bottom: 10px; 171 | height: 20px; 172 | } 173 | 174 | .#{$prefix}weekday { 175 | float: right; 176 | width: $day-size; 177 | text-align: center; 178 | } 179 | 180 | .#{$prefix}days { 181 | padding: 0 18px; 182 | position: relative; 183 | overflow: hidden; 184 | transition: height 300ms cubic-bezier(0.75, 0.02, 0.27, 0.99); 185 | } 186 | 187 | .#{$prefix}day { 188 | width: $day-size; 189 | height: $day-size; 190 | float: right; 191 | line-height: $day-size; 192 | position: relative; 193 | 194 | &:not(.#{$prefix}empty) { 195 | cursor: pointer; 196 | transition: color 450ms ease; 197 | text-align: center; 198 | } 199 | 200 | &[disabled] { 201 | cursor: default; 202 | color: darken(#ffffff, 20%); 203 | 204 | .#{$prefix}day-effect { 205 | background-color: transparent; 206 | } 207 | 208 | .#{$prefix}day-text { 209 | color: darken(#ffffff, 20%); 210 | } 211 | } 212 | 213 | &:not([disabled]) { 214 | &:hover { 215 | color: #ffffff; 216 | 217 | .#{$prefix}day-effect { 218 | transform: scale(1); 219 | opacity: 0.6; 220 | } 221 | } 222 | 223 | &.#{$prefix}selected { 224 | color: #ffffff; 225 | 226 | .#{$prefix}day-effect { 227 | transform: scale(1); 228 | opacity: 1; 229 | } 230 | } 231 | 232 | &.#{$prefix}range-between { 233 | color: #ffffff; 234 | .#{$prefix}day-effect { 235 | transform: scale(0.75); 236 | opacity: 1; 237 | } 238 | } 239 | 240 | &.#{$prefix}range-hover { 241 | color: #ffffff; 242 | .#{$prefix}day-effect { 243 | transform: scale(0.7); 244 | opacity: 0.6; 245 | } 246 | } 247 | } 248 | } 249 | 250 | .#{$prefix}day-effect { 251 | position: absolute; 252 | width: $day-size - 4px; 253 | height: $day-size - 4px; 254 | border-radius: 50%; 255 | top: 2px; 256 | left: 2px; 257 | transform: scale(0); 258 | opacity: 0; 259 | transition: all 450ms ease; 260 | } 261 | 262 | .#{$prefix}day-text { 263 | position: relative; 264 | } 265 | 266 | .#{$prefix}controls { 267 | position: relative; 268 | z-index: 2; 269 | height: $month-label-height; 270 | line-height: $month-label-height; 271 | text-align: center; 272 | 273 | button { 274 | position: relative; 275 | background-color: transparent; 276 | border: none; 277 | user-select: none; 278 | outline: none; 279 | cursor: pointer; 280 | 281 | &[disabled] { 282 | opacity: 0.3; 283 | cursor: default; 284 | } 285 | } 286 | } 287 | 288 | .#{$prefix}next, 289 | .#{$prefix}prev { 290 | width: $month-label-height; 291 | height: $month-label-height; 292 | line-height: $month-label-height; 293 | } 294 | 295 | .#{$prefix}next { 296 | float: right; 297 | } 298 | 299 | .#{$prefix}prev { 300 | float: left; 301 | } 302 | 303 | .#{$prefix}arrow { 304 | width: 11px; 305 | height: 11px; 306 | } 307 | 308 | .#{$prefix}month { 309 | position: relative; 310 | overflow: hidden; 311 | } 312 | 313 | .#{$prefix}month-label { 314 | position: absolute; 315 | top: 0; 316 | left: $month-label-height; 317 | right: $month-label-height; 318 | overflow: hidden; 319 | width: 95px; 320 | margin-left: auto; 321 | margin-right: auto; 322 | line-height: $month-label-height; 323 | height: $month-label-height; 324 | text-align: center; 325 | cursor: pointer; 326 | 327 | > span { 328 | display: inline-block; 329 | padding: 0 5px; 330 | line-height: 26px; 331 | height: 26px; 332 | border-width: 1px; 333 | border-style: solid; 334 | border-radius: 2px; 335 | transition: all 0.1s ease-out; 336 | white-space: nowrap; 337 | 338 | &:not(:hover) { 339 | border-color: transparent !important; 340 | color: inherit !important; 341 | } 342 | } 343 | } 344 | 345 | .#{$prefix}actions { 346 | text-align: right; 347 | padding: 8px; 348 | 349 | button { 350 | border: none; 351 | background-color: transparent; 352 | display: inline-block; 353 | cursor: pointer; 354 | outline: none; 355 | font-size: 14px; 356 | text-transform: uppercase; 357 | min-width: 88px; 358 | text-align: center; 359 | -webkit-appearance: none; 360 | line-height: 36px; 361 | height: 36px; 362 | transition: all 0.3s ease; 363 | 364 | &:hover { 365 | background-color: darken(#ffffff, 5%); 366 | } 367 | 368 | &[disabled] { 369 | opacity: 0.6; 370 | cursor: default; 371 | } 372 | } 373 | } 374 | 375 | .#{$prefix}addon-list-content { 376 | direction: rtl; 377 | } 378 | 379 | .#{$prefix}addon-list-item { 380 | width: (1/3)*100%; 381 | text-align: center; 382 | font-size: 14px; 383 | height: 44px; 384 | line-height: 36px; 385 | transition: all 0.3s ease; 386 | color: #8a8a8a; 387 | cursor: pointer; 388 | float: right; 389 | border: solid 4px #fff; 390 | 391 | &.#{$prefix}selected, &:hover { 392 | background-color: #f9f9f9; 393 | } 394 | 395 | &.#{$prefix}selected { 396 | font-weight: bold; 397 | background-color: #f5f5f5; 398 | } 399 | } 400 | 401 | .#{$prefix}addon-list { 402 | width: 100%; 403 | background-color: #ffffff; 404 | position: absolute; 405 | z-index: 2; 406 | overflow: auto; 407 | top: 0; 408 | bottom: 52px; 409 | border-bottom: solid 1px #eee; 410 | direction: ltr; 411 | 412 | &.#{$prefix}can-close { 413 | padding-top: 30px; 414 | } 415 | } 416 | 417 | .#{$prefix}month-list { 418 | padding-top: 15px; 419 | 420 | .#{$prefix}addon-list-item { 421 | height: 54px; 422 | line-height: 46px; 423 | 424 | // &.#{$prefix}selected, &:hover {} 425 | } 426 | } 427 | 428 | .#{$prefix}addon-list-item[disabled] { 429 | opacity: 0.3; 430 | cursor: default !important; 431 | background-color: transparent !important; 432 | } 433 | 434 | .#{$prefix}close-addon { 435 | position: absolute; 436 | top: 4px; 437 | left: 4px; 438 | z-index: 2; 439 | width: 30px; 440 | height: 30px; 441 | line-height: 30px; 442 | color: #444; 443 | font-family: sans-serif; 444 | text-align: center; 445 | cursor: pointer; 446 | background-color: rgba(0, 0, 0, 0.1); 447 | } 448 | 449 | .#{$prefix}time { 450 | user-select: none; 451 | -moz-user-select: none; 452 | -webkit-user-select: none; 453 | 454 | .#{$prefix}time-h, 455 | .#{$prefix}time-m { 456 | position: relative; 457 | margin-top: 70px; 458 | float: left; 459 | width: 50%; 460 | height: 100%; 461 | text-align: center; 462 | color: #a2a2a2; 463 | 464 | .#{$prefix}counter { 465 | font-size: 90px; 466 | height: 100px; 467 | line-height: 100px; 468 | overflow: hidden; 469 | position: relative; 470 | direction: ltr; 471 | transition: opacity 0.3s ease-in-out; 472 | } 473 | 474 | .#{$prefix}counter-item { 475 | height: inherit; 476 | width: 51px; 477 | display: inline-block; 478 | vertical-align: text-top; 479 | position: relative; 480 | } 481 | } 482 | 483 | .#{$prefix}time-h:after { 484 | position: absolute; 485 | top: 50%; 486 | right: 0; 487 | content: ':'; 488 | font-size: 70px; 489 | transform: translate(50%, -50%); 490 | transition: inherit; 491 | } 492 | 493 | .#{$prefix}up-arrow-btn, 494 | .#{$prefix}down-arrow-btn { 495 | display: block; 496 | cursor: pointer; 497 | outline: none; 498 | height: 34px; 499 | 500 | &:active svg path { 501 | } 502 | } 503 | 504 | &.#{$prefix}disabled .#{$prefix}counter-item { 505 | opacity: 0.5; 506 | } 507 | } 508 | 509 | .#{$prefix}time-column { 510 | .#{$prefix}counter { 511 | position: relative; 512 | input { 513 | position: absolute; 514 | z-index: 5; 515 | border: none; 516 | background-color: transparent; 517 | top: 0; 518 | left: 0; 519 | width: 100%; 520 | height: 100%; 521 | text-align: center; 522 | outline: none; 523 | color: inherit; 524 | font-size: inherit; 525 | line-height: inherit; 526 | font-family: inherit; 527 | opacity: 0; 528 | padding: 5% 0 0 0; 529 | } 530 | input:focus { 531 | opacity: 1; 532 | &:not(.is-empty) ~ div { 533 | opacity: 0; 534 | } 535 | } 536 | } 537 | } 538 | 539 | .#{$prefix}prev-step { 540 | position: absolute; 541 | top: 0; 542 | left: 0; 543 | width: 30px; 544 | height: 30px; 545 | text-align: center; 546 | padding: 9px; 547 | cursor: pointer; 548 | 549 | &:hover { 550 | background-color: rgba(0, 0, 0, 0.2); 551 | } 552 | } 553 | 554 | [data-type="time"] { 555 | .#{$prefix}time { 556 | .#{$prefix}time-m, 557 | .#{$prefix}time-h { 558 | margin-top: 40px; 559 | } 560 | } 561 | } 562 | 563 | .#{$prefix}is-inline { 564 | position: static; 565 | background-color: transparent; 566 | height: auto; 567 | width: auto; 568 | display: block; 569 | margin-top: 2px; 570 | 571 | .#{$prefix}container { 572 | position: static; 573 | transform: none; 574 | } 575 | 576 | .#{$prefix}content { 577 | box-shadow: 0 0 1px -1px black; 578 | } 579 | } 580 | 581 | .#{$prefix}wrapper.#{$prefix}is-popover { 582 | position: fixed; 583 | width: 100px; 584 | height: 0; 585 | z-index: 500; 586 | right: unset; 587 | bottom: unset; 588 | .#{$prefix}container { 589 | position: absolute; 590 | transform: none; 591 | top: unset; 592 | left: unset; 593 | z-index: 1; 594 | } 595 | .#{$prefix}content { 596 | transition: none; 597 | box-shadow: 0 3px 8px rgba(black, 0.4); 598 | } 599 | &[data-placement="top-left"] { 600 | .#{$prefix}container { 601 | bottom: 0; 602 | right: 0; 603 | } 604 | .#{$prefix}content { 605 | box-shadow: 0 -3px 8px rgba(black, 0.4); 606 | } 607 | } 608 | &[data-placement="top-right"] { 609 | .#{$prefix}container { 610 | bottom: 0; 611 | left: 0; 612 | } 613 | .#{$prefix}content { 614 | box-shadow: 0 -3px 8px rgba(black, 0.4); 615 | } 616 | } 617 | &[data-placement="bottom-left"] { 618 | .#{$prefix}container { 619 | top: 0; 620 | right: 0; 621 | } 622 | } 623 | &[data-placement="bottom-right"] { 624 | .#{$prefix}container { 625 | top: 0; 626 | left: 0; 627 | } 628 | } 629 | } 630 | 631 | .#{$prefix}no-footer { 632 | .#{$prefix}addon-list { 633 | bottom: 0; 634 | border-bottom: none; 635 | } 636 | } 637 | 638 | .#{$prefix}wrapper { 639 | &[data-type="datetime"] { 640 | &.#{$prefix}compact-time { 641 | .#{$prefix}time { 642 | margin-top: 10px; 643 | display: block !important; 644 | position: relative; 645 | border-top: solid 1px #eee; 646 | overflow: hidden; 647 | .#{$prefix}addon-list-content { 648 | display: flex; 649 | justify-content: center; 650 | direction: ltr; 651 | } 652 | .#{$prefix}counter-item { 653 | width: 18px; 654 | } 655 | .#{$prefix}counter { 656 | height: 30px; 657 | font-size: 20px; 658 | line-height: 34px; 659 | width: 40px; 660 | } 661 | .#{$prefix}up-arrow-btn, 662 | .#{$prefix}down-arrow-btn { 663 | position: absolute; 664 | top: 0; 665 | height: 26px; 666 | width: 26px; 667 | margin: 2px; 668 | border-radius: 50%; 669 | display: flex; 670 | justify-content: center; 671 | align-items: center; 672 | background-color: rgba(black, 0.05); 673 | } 674 | .#{$prefix}time-m { 675 | margin-top: 0; 676 | float: none; 677 | width: 100px; 678 | .#{$prefix}up-arrow-btn { 679 | right: 30px; 680 | } 681 | .#{$prefix}down-arrow-btn { 682 | right: 0; 683 | } 684 | } 685 | .#{$prefix}time-h { 686 | margin-top: 0; 687 | float: none; 688 | width: 100px; 689 | &:after { 690 | font-size: 20px; 691 | } 692 | .#{$prefix}counter { 693 | margin-left: auto; 694 | } 695 | .#{$prefix}up-arrow-btn { 696 | left: 0; 697 | } 698 | .#{$prefix}down-arrow-btn { 699 | left: 30px; 700 | } 701 | } 702 | svg { 703 | height: 10px; 704 | } 705 | } 706 | } 707 | 708 | .#{$prefix}date { 709 | font-size: 22px; 710 | } 711 | } 712 | 713 | &.#{$prefix}is-range { 714 | .#{$prefix}date { 715 | font-size: 16px; 716 | } 717 | } 718 | &.#{$prefix}is-multiple { 719 | .#{$prefix}date { 720 | font-size: 14px; 721 | white-space: normal; 722 | line-height: 20px; 723 | } 724 | } 725 | } 726 | 727 | @media screen and (max-height: 460px) { 728 | .#{$prefix}wrapper { 729 | overflow: auto; 730 | text-align: center; 731 | 732 | .#{$prefix}container { 733 | position: relative; 734 | top: 0; 735 | left: 0; 736 | transform: none; 737 | display: inline-block; 738 | margin: 20px auto; 739 | } 740 | } 741 | } 742 | 743 | .#{$prefix}locales { 744 | list-style-type: none; 745 | padding: 0; 746 | margin: 0; 747 | position: absolute; 748 | left: 14px; 749 | top: 12px; 750 | line-height: 24px; 751 | text-align: center; 752 | opacity: 0.7; 753 | 754 | li { 755 | cursor: pointer; 756 | } 757 | } 758 | 759 | // ======================= 760 | // Simple Mode 761 | // ======================= 762 | .#{$prefix}simple-body { 763 | .#{$prefix}header { 764 | display: flex; 765 | padding: 10px; 766 | justify-content: space-between; 767 | } 768 | .#{$prefix}locales { 769 | position: static; 770 | padding: 0 5px; 771 | line-height: unset; 772 | } 773 | } 774 | 775 | .#{$prefix}simple-content { 776 | display: flex; 777 | justify-content: center; 778 | height: 240px; 779 | direction: ltr; 780 | 781 | .#{$prefix}column { 782 | height: 100%; 783 | flex-grow: 1; 784 | display: flex; 785 | flex-flow: column; 786 | padding: 5px; 787 | 788 | .#{$prefix}column-header { 789 | text-align: center; 790 | font-weight: bold; 791 | color: black; 792 | height: 30px; 793 | line-height: 30px; 794 | } 795 | 796 | .#{$prefix}column-content { 797 | flex-grow: 1; 798 | height: 100%; 799 | overflow: auto; 800 | padding-right: 4px; 801 | position: relative; 802 | &:before, 803 | &:after { 804 | display: block; 805 | height: 80px; 806 | content: ''; 807 | } 808 | 809 | @media screen and (min-width: 640px) { 810 | &::-webkit-scrollbar { 811 | width: 8px; 812 | } 813 | &::-webkit-scrollbar-track { 814 | background: rgba(black, 0.05); 815 | border-radius: 5px; 816 | } 817 | &::-webkit-scrollbar-thumb { 818 | background: rgba(black, 0.2); 819 | border-radius: 5px; 820 | } 821 | &::-webkit-scrollbar-thumb:hover { 822 | background: rgba(black, 0.5); 823 | } 824 | } 825 | } 826 | } 827 | 828 | .#{$prefix}addon-list-item { 829 | width: 100%; 830 | float: none !important; 831 | line-height: 42px; 832 | height: 44px; 833 | border: solid 1px white; 834 | } 835 | 836 | .#{$prefix}range-first { 837 | border-radius: 40px 40px 0 0; 838 | } 839 | 840 | .#{$prefix}range-between { 841 | font-weight: bold; 842 | background-color: #f5f5f5; 843 | } 844 | 845 | .#{$prefix}range-last { 846 | border-radius: 0 0 40px 40px; 847 | &.#{$prefix}range-first { 848 | border-radius: 40px; 849 | } 850 | } 851 | 852 | .#{$prefix}column.#{$prefix}is-mounted { 853 | .#{$prefix}selected:not(.#{$prefix}range-first):not(.#{$prefix}range-last):not([disabled]) { 854 | position: sticky; 855 | top: 0; 856 | bottom: 0; 857 | } 858 | } 859 | } 860 | 861 | .#{$prefix}is-multiple { 862 | .#{$prefix}simple-content { 863 | .#{$prefix}column { 864 | .#{$prefix}selected { 865 | position: static !important; 866 | } 867 | } 868 | } 869 | } 870 | 871 | 872 | .#{$prefix}dir-ltr { 873 | .#{$prefix}content, 874 | .#{$prefix}actions, 875 | .#{$prefix}addon-list-content { 876 | direction: ltr; 877 | } 878 | 879 | .#{$prefix}content, 880 | .#{$prefix}actions { 881 | text-align: left; 882 | font-family: sans-serif; 883 | } 884 | 885 | .#{$prefix}month-label { 886 | font-size: 90% 887 | } 888 | 889 | .#{$prefix}addon-list-item, 890 | .#{$prefix}weekday, 891 | .#{$prefix}day { 892 | float: left; 893 | } 894 | 895 | .#{$prefix}locales { 896 | left: auto; 897 | right: 14px; 898 | } 899 | } 900 | 901 | .#{$prefix}dir-rtl { 902 | .#{$prefix}next, 903 | .#{$prefix}prev { 904 | transform: rotateY(180deg); 905 | } 906 | 907 | .#{$prefix}next { 908 | float: left 909 | } 910 | 911 | .#{$prefix}prev { 912 | float: right 913 | } 914 | } 915 | -------------------------------------------------------------------------------- /src/components/Arrow.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 37 | -------------------------------------------------------------------------------- /src/components/Btn.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 57 | -------------------------------------------------------------------------------- /src/components/CalendarIcon.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 33 | -------------------------------------------------------------------------------- /src/components/EditIcon.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 26 | -------------------------------------------------------------------------------- /src/components/LocaleChange.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 30 | -------------------------------------------------------------------------------- /src/components/TimeIcon.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 25 | -------------------------------------------------------------------------------- /src/components/simple/SimpleMode.vue: -------------------------------------------------------------------------------- 1 | 127 | 128 | 192 | -------------------------------------------------------------------------------- /src/components/simple/SimpleModeColumn.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 63 | -------------------------------------------------------------------------------- /src/components/time/TimeColumn.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 140 | -------------------------------------------------------------------------------- /src/components/time/TimeSection.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 110 | -------------------------------------------------------------------------------- /src/modules/core.js: -------------------------------------------------------------------------------- 1 | /*global getYear*/ 2 | /*eslint no-undef: ["error", { "typeof": true }] */ 3 | 4 | import momentBase from 'moment' 5 | import moment from 'moment-jalaali' 6 | import fa from './moment.locale.fa' 7 | import utils from './utils' 8 | 9 | moment.updateLocale('en', { 10 | weekdaysMin: 'S_M_T_W_T_F_S'.split('_') 11 | }) 12 | moment.updateLocale('fa', fa) 13 | moment.loadPersian({ dialect: 'persian-modern' }) 14 | moment.daysInMonth = function(year, month) { 15 | return moment({ year, month }).daysInMonth() 16 | } 17 | 18 | //===================================== 19 | // CONFIG 20 | //===================================== 21 | const localMethods = { 22 | fa: { 23 | daysInMonth: 'jDaysInMonth', 24 | year: 'jYear', 25 | month: 'jMonth', 26 | date: 'jDate', 27 | day: 'day' 28 | }, 29 | en: { 30 | daysInMonth: 'daysInMonth', 31 | year: 'year', 32 | month: 'month', 33 | date: 'date', 34 | day: 'day' 35 | } 36 | } 37 | const localesConfig = { 38 | fa: { 39 | dow: 6, 40 | dir: 'rtl', 41 | displayFormat: null, 42 | lang: { 43 | label: 'شمسی', 44 | submit: 'تایید', 45 | cancel: 'انصراف', 46 | now: 'اکنون', 47 | nextMonth: 'ماه بعد', 48 | prevMonth: 'ماه قبل', 49 | year: 'سال', 50 | month: 'ماه', 51 | day: 'روز' 52 | } 53 | }, 54 | en: { 55 | dow: 0, 56 | dir: 'ltr', 57 | displayFormat: null, 58 | lang: { 59 | label: 'میلادی', 60 | submit: 'Select', 61 | cancel: 'Cancel', 62 | now: 'Now', 63 | nextMonth: 'Next month', 64 | prevMonth: 'Previous month', 65 | year: 'Year', 66 | month: 'Month', 67 | day: 'Day' 68 | } 69 | } 70 | } 71 | 72 | const Core = function(defaultLocaleName, defaultOptions) { 73 | 'use strict' 74 | 75 | const Instance = { 76 | moment: moment, 77 | momentBase: momentBase, 78 | locale: { name: 'fa', config: {} }, 79 | localesConfig: {}, 80 | setLocalesConfig: null, 81 | changeLocale: null, 82 | getWeekArray: null, 83 | getYearsList: null, 84 | getMonthsList: null 85 | } 86 | 87 | //===================================== 88 | // METHODS 89 | //===================================== 90 | let xDaysInMonth 91 | 92 | Instance.changeLocale = function changeLocale( 93 | localeName = 'fa', 94 | options = {} 95 | ) { 96 | let locale = this.locale 97 | let config = utils.clone(localesConfig[localeName] || localesConfig.en) 98 | let methods = localMethods[localeName] || localMethods.en 99 | 100 | options = options[localeName] || {} 101 | if (!localesConfig[localeName]) 102 | options = utils.extend(true, {}, utils.clone(localesConfig.en), options) 103 | locale.name = localeName 104 | locale.config = utils.extend(true, config, options) 105 | 106 | xDaysInMonth = moment[methods.daysInMonth] 107 | 108 | function addMethods(date) { 109 | if (date === undefined) return 110 | 111 | const nameInLocale = name => { 112 | if (locale.name !== 'fa') name = name.replace(/j/g, '') 113 | return name 114 | } 115 | 116 | date.xYear = moment.fn[methods.year] 117 | date.xMonth = moment.fn[methods.month] 118 | date.xDate = moment.fn[methods.date] 119 | 120 | date.xFormat = function(format) { 121 | return this.format(nameInLocale(format)) 122 | } 123 | date.xStartOf = function(value) { 124 | return this.startOf(methods[value]) 125 | } 126 | date.xEndOf = function(value) { 127 | return this.endOf(methods[value]) 128 | } 129 | date.xAdd = function(amount, key) { 130 | return this.add(amount, methods[key]) 131 | } 132 | date.xDaysInMonth = function() { 133 | return xDaysInMonth(this.xYear(), this.xMonth()) 134 | } 135 | date.clone = function() { 136 | return Instance.moment(this.toDate()) 137 | } 138 | } 139 | 140 | this.moment = function() { 141 | let date = moment.apply(null, arguments) 142 | date.locale(locale.name) 143 | addMethods(date) 144 | return date 145 | } 146 | } 147 | 148 | Instance.setLocalesConfig = function(config) { 149 | let defaults = utils.clone(localesConfig) 150 | for (let key in config) { 151 | if (config.hasOwnProperty(key) && defaults[key] === undefined) 152 | defaults[key] = utils.extend( 153 | true, 154 | {}, 155 | utils.clone(defaults.en), 156 | { lang: { label: key } }, 157 | config[key] 158 | ) 159 | } 160 | this.localesConfig = utils.extend(true, defaults, config) 161 | } 162 | 163 | Instance.getWeekArray = function getWeekArray(date) { 164 | function addWeek(weekArray, week) { 165 | let emptyDays = 7 - week.length 166 | 167 | for (let i = 0; i < emptyDays; ++i) { 168 | week[weekArray.length ? 'push' : 'unshift'](null) 169 | } 170 | 171 | weekArray.push(week) 172 | } 173 | 174 | date.set({ h: 12, m: 0 }) 175 | let daysInMonth = xDaysInMonth(date.xYear(), date.xMonth()) 176 | let day = date.clone().xDate(1) 177 | let dayArray = [day.toDate()] 178 | 179 | for (let i = 2; i <= daysInMonth; i++) { 180 | dayArray.push(day.xAdd(1, 'day').toDate()) 181 | } 182 | 183 | let weekArray = [] 184 | let week = [] 185 | 186 | dayArray.forEach(day => { 187 | if (week.length > 0 && day.getDay() === this.locale.config.dow) { 188 | addWeek(weekArray, week) 189 | week = [] 190 | } 191 | 192 | week.push(day) 193 | 194 | if (dayArray.indexOf(day) === dayArray.length - 1) { 195 | addWeek(weekArray, week) 196 | } 197 | }) 198 | 199 | return weekArray 200 | } 201 | 202 | Instance.getYearsList = function getYearsList(from, to, range = false, date) { 203 | let years = [] 204 | if (range) { 205 | let year = getYear(date) 206 | from = year - range 207 | to = year + range 208 | } 209 | for (let i = from; i <= to; i++) { 210 | years.push(i) 211 | } 212 | return years 213 | } 214 | 215 | Instance.getMonthsList = function getMonthsList(minDate, maxDate, date) { 216 | let list = [], 217 | min = minDate ? minDate.clone().xStartOf('month') : -Infinity, 218 | max = maxDate ? maxDate.clone().xEndOf('month') : Infinity 219 | for (let i = 0; i < 12; i++) { 220 | let month = date.clone().xMonth(i) 221 | let start = month.clone().xStartOf('month') 222 | 223 | let end = month.clone().xEndOf('month') 224 | 225 | month.disabled = start < min || end > max 226 | list.push(month) 227 | } 228 | return list 229 | } 230 | 231 | Instance.changeLocale(defaultLocaleName, defaultOptions) 232 | 233 | return Instance 234 | } 235 | 236 | export default Core 237 | 238 | export { localesConfig } 239 | -------------------------------------------------------------------------------- /src/modules/mixins.js: -------------------------------------------------------------------------------- 1 | import { clone } from './utils' 2 | 3 | /** 4 | * Model Mixin 5 | */ 6 | export const modelMixin = { 7 | model: { 8 | prop: 'value', 9 | event: 'input' 10 | }, 11 | props: { 12 | value: { type: [String, Number, Array, Object, Boolean], default: '' } 13 | }, 14 | data() { 15 | return { 16 | selfValue: '' 17 | } 18 | }, 19 | watch: { 20 | value: { 21 | handler(val) { 22 | if (this.selfValue !== val) this.selfValue = val 23 | }, 24 | immediate: true, 25 | deep: true 26 | }, 27 | selfValue(val) { 28 | if (val !== this.value) this.$emit('input', val) 29 | } 30 | } 31 | } 32 | 33 | /** 34 | * Change route when visible changes 35 | * @type Object 36 | */ 37 | export const popupRouteChanger = { 38 | data() { 39 | return { 40 | routerQueryName: null 41 | } 42 | }, 43 | mounted() { 44 | this.initRouter() 45 | }, 46 | methods: { 47 | initRouter() { 48 | const isSet = prop => prop || typeof prop === 'string' 49 | const useRouter = this.useRouter 50 | 51 | if (!isSet(useRouter) || this.isPopover || this.inline || !this.$router) 52 | return 53 | 54 | this.$watch('visible', this.onVisibleChange) 55 | this.$watch(() => this.$route.query, this.onRouteChange, { 56 | deep: true, 57 | immediate: true 58 | }) 59 | 60 | this.routerQueryName = 61 | typeof useRouter === 'string' && useRouter ? useRouter : this.id 62 | }, 63 | onVisibleChange(visible) { 64 | let currentRoute = this.$route 65 | let query = clone(currentRoute.query || {}) 66 | if (visible) { 67 | query[this.routerQueryName] = 'active' 68 | this.$router.push({ query }) 69 | } else if (query[this.routerQueryName]) { 70 | this.$router.back() 71 | } 72 | }, 73 | onRouteChange() { 74 | let visible = !!this.$route.query[this.routerQueryName] 75 | if (visible && this.disabled) return 76 | this.visible = visible 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/modules/moment.locale.fa.js: -------------------------------------------------------------------------------- 1 | //! moment.js locale configuration 2 | 3 | var symbolMap = { 4 | '1': '۱', 5 | '2': '۲', 6 | '3': '۳', 7 | '4': '۴', 8 | '5': '۵', 9 | '6': '۶', 10 | '7': '۷', 11 | '8': '۸', 12 | '9': '۹', 13 | '0': '۰' 14 | }, 15 | numberMap = { 16 | '۱': '1', 17 | '۲': '2', 18 | '۳': '3', 19 | '۴': '4', 20 | '۵': '5', 21 | '۶': '6', 22 | '۷': '7', 23 | '۸': '8', 24 | '۹': '9', 25 | '۰': '0' 26 | } 27 | 28 | export default { 29 | months: 'ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر'.split( 30 | '_' 31 | ), 32 | monthsShort: 'ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر'.split( 33 | '_' 34 | ), 35 | weekdays: 'یک\u200cشنبه_دوشنبه_سه\u200cشنبه_چهارشنبه_پنج\u200cشنبه_جمعه_شنبه'.split( 36 | '_' 37 | ), 38 | weekdaysShort: 'یک\u200cشنبه_دوشنبه_سه\u200cشنبه_چهارشنبه_پنج\u200cشنبه_جمعه_شنبه'.split( 39 | '_' 40 | ), 41 | weekdaysMin: 'ی_د_س_چ_پ_ج_ش'.split('_'), 42 | weekdaysParseExact: true, 43 | longDateFormat: { 44 | LT: 'HH:mm', 45 | LTS: 'HH:mm:ss', 46 | L: 'DD/MM/YYYY', 47 | LL: 'D MMMM YYYY', 48 | LLL: 'D MMMM YYYY HH:mm', 49 | LLLL: 'dddd, D MMMM YYYY HH:mm' 50 | }, 51 | meridiemParse: /قبل از ظهر|بعد از ظهر/, 52 | isPM: function(input) { 53 | return /بعد از ظهر/.test(input) 54 | }, 55 | meridiem: function(hour) { 56 | if (hour < 12) { 57 | return 'قبل از ظهر' 58 | } else { 59 | return 'بعد از ظهر' 60 | } 61 | }, 62 | calendar: { 63 | sameDay: '[امروز ساعت] LT', 64 | nextDay: '[فردا ساعت] LT', 65 | nextWeek: 'dddd [ساعت] LT', 66 | lastDay: '[دیروز ساعت] LT', 67 | lastWeek: 'dddd [پیش] [ساعت] LT', 68 | sameElse: 'L' 69 | }, 70 | relativeTime: { 71 | future: 'در %s', 72 | past: '%s پیش', 73 | s: 'چند ثانیه', 74 | ss: 'ثانیه d%', 75 | m: 'یک دقیقه', 76 | mm: '%d دقیقه', 77 | h: 'یک ساعت', 78 | hh: '%d ساعت', 79 | d: 'یک روز', 80 | dd: '%d روز', 81 | M: 'یک ماه', 82 | MM: '%d ماه', 83 | y: 'یک سال', 84 | yy: '%d سال' 85 | }, 86 | preparse: function(string) { 87 | return string 88 | .replace(/[۰-۹]/g, function(match) { 89 | return numberMap[match] 90 | }) 91 | .replace(/،/g, ',') 92 | }, 93 | postformat: function(string) { 94 | return string 95 | .replace(/\d/g, function(match) { 96 | return symbolMap[match] 97 | }) 98 | .replace(/,/g, '،') 99 | }, 100 | dayOfMonthOrdinalParse: /\d{1,2}م/, 101 | ordinal: '%dم', 102 | week: { 103 | dow: 6, // Saturday is the first day of the week. 104 | doy: 12 // The week that contains Jan 1st is the first week of the year. 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/modules/popover-util.js: -------------------------------------------------------------------------------- 1 | const popover = { 2 | shouldAppendPicker(appendTo, isPopover) { 3 | return appendTo || isPopover 4 | }, 5 | 6 | appendChild(parent, child) { 7 | try { 8 | let container = document.querySelector(parent || 'body') 9 | container.appendChild(child) 10 | } catch (er) { 11 | document.body.appendChild(child) 12 | console.warn(`Cannot append picker to "${parent}"!`) 13 | } 14 | }, 15 | 16 | removeChild(element) { 17 | if (!element) return 18 | try { 19 | if (element.$el) element = element.$el 20 | if (element.parentNode) element.parentNode.removeChild(element) 21 | } catch (e) { 22 | console.warn("can't remove child", e) 23 | } 24 | }, 25 | 26 | setPickerPosition(pickerWrapperEl, containerEl, inputWrapperEl, options) { 27 | if (!inputWrapperEl || !pickerWrapperEl) return 28 | 29 | let { placement = '', offsetX = 0, offsetY = 0 } = options 30 | let dataPlacement = '' 31 | let isOnTop = /top/.test(placement) 32 | let isOnLeft = /left/.test(placement) 33 | let isOnRight = /right/.test(placement) 34 | let isOnBottom = /bottom/.test(placement) 35 | 36 | pickerWrapperEl.style.width = inputWrapperEl.offsetWidth + 'px' 37 | pickerWrapperEl.style.top = '0px' 38 | pickerWrapperEl.style.left = '0px' 39 | 40 | const inputWrapperHeight = inputWrapperEl.offsetHeight 41 | const inputWrapperRect = inputWrapperEl.getBoundingClientRect() 42 | const top = inputWrapperRect.top + inputWrapperHeight 43 | const distanceY = 0 44 | 45 | if (!isOnTop && !isOnBottom) 46 | isOnTop = top + containerEl.offsetHeight > window.innerHeight 47 | 48 | if (!isOnLeft && !isOnRight) 49 | isOnLeft = inputWrapperRect.right > containerEl.offsetWidth 50 | 51 | dataPlacement += isOnTop ? 'top' : 'bottom' 52 | dataPlacement += isOnLeft ? '-left' : '-right' 53 | 54 | if (isOnTop) { 55 | pickerWrapperEl.style.top = 56 | inputWrapperRect.top - distanceY - offsetY + 'px' 57 | } else { 58 | pickerWrapperEl.style.top = top + distanceY + offsetY + 'px' 59 | } 60 | 61 | offsetX *= isOnRight ? 1 : -1 62 | pickerWrapperEl.setAttribute('data-placement', dataPlacement) 63 | pickerWrapperEl.style.left = inputWrapperRect.left + offsetX + 'px' 64 | } 65 | } 66 | 67 | export default popover 68 | -------------------------------------------------------------------------------- /src/modules/utils.js: -------------------------------------------------------------------------------- 1 | let toString = Object.prototype.toString, 2 | hasOwnProperty = Object.prototype.hasOwnProperty 3 | const tools = { 4 | isFunction: function(obj) { 5 | return toString.call(obj) === '[object Function]' 6 | }, 7 | isArray: function(obj) { 8 | return toString.call(obj) === '[object Array]' 9 | }, 10 | isPlainObject: function(obj) { 11 | // Must be an Object. 12 | // Because of IE, we also have to check the presence of the constructor property. 13 | // Make sure that DOM nodes and window objects don't pass through, as well 14 | if ( 15 | !obj || 16 | toString.call(obj) !== '[object Object]' || 17 | obj.nodeType || 18 | obj.setInterval 19 | ) { 20 | return false 21 | } 22 | 23 | // Not own constructor property must be Object 24 | if ( 25 | obj.constructor && 26 | !hasOwnProperty.call(obj, 'constructor') && 27 | !hasOwnProperty.call(obj.constructor.prototype, 'isPrototypeOf') 28 | ) { 29 | return false 30 | } 31 | 32 | // Own properties are enumerated firstly, so to speed up, 33 | // if last one is own, then all properties are own. 34 | 35 | var key 36 | for (key in obj); 37 | 38 | return key === undefined || hasOwnProperty.call(obj, key) 39 | } 40 | } 41 | 42 | /* 43 | * jQuery extend function 44 | * https://gist.github.com/bentsai/3150936 45 | */ 46 | const extend = function() { 47 | var options, 48 | name, 49 | src, 50 | copy, 51 | copyIsArray, 52 | clone, 53 | target = arguments[0] || {}, 54 | i = 1, 55 | length = arguments.length, 56 | deep = false 57 | 58 | // Handle a deep copy situation 59 | if (typeof target === 'boolean') { 60 | deep = target 61 | target = arguments[1] || {} 62 | // skip the boolean and the target 63 | i = 2 64 | } 65 | 66 | // Handle case when target is a string or something (possible in deep copy) 67 | if (typeof target !== 'object' && !tools.isFunction(target)) { 68 | target = {} 69 | } 70 | 71 | // extend jQuery itself if only one argument is passed 72 | if (length === i) { 73 | target = this 74 | --i 75 | } 76 | 77 | for (; i < length; i++) { 78 | // Only deal with non-null/undefined values 79 | if ((options = arguments[i]) !== null) { 80 | // Extend the base object 81 | for (name in options) { 82 | src = target[name] 83 | copy = options[name] 84 | 85 | // Prevent never-ending loop 86 | if (target === copy) { 87 | continue 88 | } 89 | 90 | // Recurse if we're merging plain objects or arrays 91 | if ( 92 | deep && 93 | copy && 94 | (tools.isPlainObject(copy) || (copyIsArray = tools.isArray(copy))) 95 | ) { 96 | if (copyIsArray) { 97 | copyIsArray = false 98 | clone = src && tools.isArray(src) ? src : [] 99 | } else { 100 | clone = src && tools.isPlainObject(src) ? src : {} 101 | } 102 | 103 | // Never move original objects, clone them 104 | target[name] = extend(deep, clone, copy) 105 | 106 | // Don't bring in undefined values 107 | } else if (copy !== undefined) { 108 | target[name] = copy 109 | } 110 | } 111 | } 112 | } 113 | 114 | // Return the modified object 115 | return target 116 | } 117 | 118 | /** 119 | * Simple helper for clone an Array of dates (in moment) 120 | * @param arr Array 121 | * @returns Array 122 | */ 123 | export const cloneDates = arr => arr.map(d => d.clone()) 124 | 125 | /** 126 | * Check if two dates are on the same day 127 | * @param a Moment date 128 | * @param b Moment date 129 | * @returns {boolean} 130 | */ 131 | export const isSameDay = (a, b) => { 132 | a = a.clone().set({ h: 12, m: 0 }) 133 | return Math.abs(a.diff(b, 'hours')) < 20 134 | } 135 | 136 | /** 137 | * full clone using JSON.stringify 138 | * @param obj 139 | * @returns {any} 140 | */ 141 | export const clone = obj => JSON.parse(JSON.stringify(obj)) 142 | 143 | /** 144 | * https://stackoverflow.com/a/51029299/3183699 145 | * @param element 146 | * @param duration 147 | * @param callback 148 | */ 149 | export const scrollIntoCenter = function(element, duration = 200, callback) { 150 | const parent = element.parentNode 151 | let startingTop = parent.scrollTop 152 | let parentCenter = parent.offsetHeight / 2 153 | let elementCenter = element.offsetHeight / 2 154 | let distance = element.offsetTop - startingTop - parentCenter + elementCenter 155 | let start 156 | const done = () => { 157 | if (typeof callback === 'function') { 158 | callback() 159 | } 160 | } 161 | 162 | if (!duration) { 163 | parent.scrollTo(0, startingTop + distance) 164 | done() 165 | return 166 | } 167 | 168 | window.requestAnimationFrame(function step(timestamp) { 169 | if (!start) start = timestamp 170 | let time = timestamp - start 171 | let percent = Math.min(time / duration, 1) 172 | parent.scrollTo(0, startingTop + distance * percent) 173 | 174 | // Proceed with animation as long as we wanted it to. 175 | if (time < duration) { 176 | window.requestAnimationFrame(step) 177 | } else { 178 | done() 179 | } 180 | }) 181 | } 182 | 183 | export const addEventListener = function(el, type, handler) { 184 | if (typeof el === 'string') el = document.querySelector(el) 185 | if (!el) throw new Error('Cant find custom element: ' + el) 186 | if (el.addEventListener) el.addEventListener(type, handler, true) 187 | else el.attachEvent('on' + type, handler, true) 188 | } 189 | 190 | export const addLiveEvent = function(selector, event, callback, context) { 191 | addEventListener(context || document, event, function(e) { 192 | if (e.target.closest(selector)) callback.call(e.target, e) 193 | }) 194 | } 195 | 196 | export default { extend, clone } 197 | -------------------------------------------------------------------------------- /tests/e2e/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: ['cypress'], 3 | env: { 4 | mocha: true, 5 | 'cypress/globals': true 6 | }, 7 | rules: { 8 | strict: 'off' 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/e2e/plugins/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable arrow-body-style */ 2 | // https://docs.cypress.io/guides/guides/plugins-guide.html 3 | 4 | // if you need a custom webpack configuration you can uncomment the following import 5 | // and then use the `file:preprocessor` event 6 | // as explained in the cypress docs 7 | // https://docs.cypress.io/api/plugins/preprocessors-api.html#Examples 8 | 9 | // /* eslint-disable import/no-extraneous-dependencies, global-require */ 10 | // const webpack = require('@cypress/webpack-preprocessor') 11 | 12 | module.exports = (on, config) => { 13 | // on('file:preprocessor', webpack({ 14 | // webpackOptions: require('@vue/cli-service/webpack.config'), 15 | // watchOptions: {} 16 | // })) 17 | 18 | return Object.assign({}, config, { 19 | fixturesFolder: 'tests/e2e/fixtures', 20 | integrationFolder: 'tests/e2e/specs', 21 | screenshotsFolder: 'tests/e2e/screenshots', 22 | videosFolder: 'tests/e2e/videos', 23 | supportFile: 'tests/e2e/support/index.js' 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /tests/e2e/specs/test.js: -------------------------------------------------------------------------------- 1 | // https://docs.cypress.io/api/introduction/api.html 2 | 3 | describe('My First Test', () => { 4 | it('Visits the app root url', () => { 5 | cy.visit('/') 6 | cy.contains('h1', 'Welcome to Your Vue.js App') 7 | }) 8 | }) 9 | -------------------------------------------------------------------------------- /tests/e2e/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add("login", (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This is will overwrite an existing command -- 25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /tests/e2e/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /tests/unit/example.spec.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import HelloWorld from '@/components/HelloWorld.vue' 3 | 4 | describe('HelloWorld.vue', () => { 5 | it('renders props.msg when passed', () => { 6 | const msg = 'new message' 7 | const wrapper = shallowMount(HelloWorld, { 8 | propsData: { msg } 9 | }) 10 | expect(wrapper.text()).toMatch(msg) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | 3 | module.exports = { 4 | publicPath: process.env.VUE_APP_PUBLIC_PATH, 5 | lintOnSave: false, 6 | filenameHashing: true, 7 | productionSourceMap: false, 8 | 9 | configureWebpack: { 10 | plugins: [new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)] 11 | }, 12 | 13 | css: { 14 | sourceMap: process.env.NODE_ENV !== 'production', 15 | extract: false 16 | } 17 | } 18 | 19 | if (process.env.npm_lifecycle_event === 'export-common') { 20 | module.exports.configureWebpack.externals = { 21 | moment: 'moment', 22 | 'moment-jalaali': 'moment-jalaali' 23 | } 24 | module.exports.configureWebpack.devtool = '' 25 | } 26 | 27 | if (process.env.npm_lifecycle_event === 'export-umd') { 28 | module.exports.configureWebpack.externals = { 29 | moment: 'moment', 30 | 'moment-jalaali': 'moment' 31 | } 32 | module.exports.configureWebpack.devtool = '' 33 | } 34 | --------------------------------------------------------------------------------