first commit
This commit is contained in:
commit
eb2f504652
32490 changed files with 5731109 additions and 0 deletions
21
node_modules/@vuepic/vue-datepicker/LICENSE
generated
vendored
Normal file
21
node_modules/@vuepic/vue-datepicker/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021-present Vuepic
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
95
node_modules/@vuepic/vue-datepicker/README.md
generated
vendored
Normal file
95
node_modules/@vuepic/vue-datepicker/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
## @vuepic/vue-datepicker
|
||||
|
||||
|
||||
### The most complete datepicker solution for Vue 3
|
||||
|
||||
[](https://github.com/Vuepic/vue-datepicker/blob/main/LICENSE) [](https://www.npmjs.com/package/@vuepic/vue-datepicker)  [](https://github.com/Vuepic/vue-datepicker/issues)  
|
||||
[](https://sonarcloud.io/summary/new_code?id=Vuepic_vue-datepicker)
|
||||
|
||||
|
||||
|
||||
|
||||
[DOCUMENTATION](https://vue3datepicker.com/)
|
||||
|
||||
|
||||
[StackBlitz Playground](https://stackblitz.com/edit/vuepic-vue-datepicker?file=src%2Fcomponents%2FPlayground.vue)
|
||||
|
||||
## Features
|
||||
|
||||
- Single date picker
|
||||
- Range date picker
|
||||
- Time picker
|
||||
- Month picker
|
||||
- Year picker
|
||||
- Week picker
|
||||
- Multiple dates select
|
||||
- Multiple calendars
|
||||
- Text input
|
||||
- UTC support
|
||||
- Timezones
|
||||
- Locale support
|
||||
- Week numbers
|
||||
- Custom `v-model`
|
||||
- Dark and light theme
|
||||
- SSR support
|
||||
- Highly configurable
|
||||
- Accessible
|
||||
- Included type definitions
|
||||
|
||||
## Install
|
||||
|
||||
```shell
|
||||
yarn add @vuepic/vue-datepicker
|
||||
|
||||
# or
|
||||
|
||||
npm install @vuepic/vue-datepicker
|
||||
```
|
||||
|
||||
Import and register component
|
||||
|
||||
**Global**
|
||||
|
||||
```js
|
||||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
|
||||
import Datepicker from '@vuepic/vue-datepicker';
|
||||
import '@vuepic/vue-datepicker/dist/main.css';
|
||||
|
||||
const app = createApp(App);
|
||||
app.component('Datepicker', Datepicker);
|
||||
```
|
||||
|
||||
**Local**
|
||||
|
||||
```vue
|
||||
<script>
|
||||
import Datepicker from '@vuepic/vue-datepicker';
|
||||
import '@vuepic/vue-datepicker/dist/main.css';
|
||||
|
||||
export default {
|
||||
components: { Datepicker }
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## Supporting the project
|
||||
|
||||
As you may know, maintaining an open-source project is a very time-consuming job. Your support is very appreciated ❤️
|
||||
|
||||
Please ⭐️ this repository if you like the component!
|
||||
|
||||
You can also make a financial contribution via sponsoring this project or one time donation. [Become a sponsor](https://github.com/sponsors/Vuepic)
|
||||
|
||||
Special thanks to our sponsors 🙏
|
||||
|
||||
<a href="https://hapio.io/" target="_blank">
|
||||
<img src="https://avatars.githubusercontent.com/u/99868704?s=200&v=4" width="80" alt="Hapio">
|
||||
</a>
|
||||
|
||||
## License
|
||||
|
||||
Copyright © 2021-present [Vuepic](https://github.com/Vuepic)
|
||||
|
||||
[MIT](https://github.com/Vuepic/vue-datepicker/blob/master/LICENSE)
|
||||
1
node_modules/@vuepic/vue-datepicker/dist/main.css
generated
vendored
Normal file
1
node_modules/@vuepic/vue-datepicker/dist/main.css
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
node_modules/@vuepic/vue-datepicker/dist/main.css.map
generated
vendored
Normal file
1
node_modules/@vuepic/vue-datepicker/dist/main.css.map
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sourceRoot":"","sources":["../src/VueDatePicker/style/components/_DatepickerInput.scss","../src/VueDatePicker/style/_variables.scss","../src/VueDatePicker/style/components/_DatepickerMenu.scss","../src/VueDatePicker/style/components/_Calendar.scss","../src/VueDatePicker/style/components/_MonthYearInput.scss","../src/VueDatePicker/style/components/_SelectionGrid.scss","../src/VueDatePicker/style/components/_TimeInput.scss","../src/VueDatePicker/style/components/_ActionRow.scss","../src/VueDatePicker/style/main.scss"],"names":[],"mappings":"AAAA,gBACE,kBACA,WACA,iBAEA,sBACE,0CACA,aAIJ,WACE,4CACA,cCVkB,IDWlB,YCbgB,mHDchB,wCACA,aACA,iEACA,WACA,UCUc,KDTd,mBACA,QCHkB,SDIlB,2BACA,sBAEA,wBACE,WAGF,iBACE,0CAIJ,eACE,0BAGF,iBACE,0CAGF,cACE,oCAEA,2BACE,oCAIJ,iBACE,qBACA,MCvBc,KDwBd,OCxBc,KDyBd,eACA,UC1Bc,KD2Bd,mBACA,QCvCkB,SDwClB,2BACA,uBAGF,gBACE,eACA,kBACA,QACA,OACA,2BACA,2BAGF,gBACE,kBACA,QACA,QACA,2BACA,eACA,2BAGF,oBACE,aC9DuB,KDiEzB,iBACE,2CACA,qCAEA,uBACE,qCAIJ,mBACE,0CACA,oCAEA,yBACE,oCElGJ,UACE,kBACA,sCACA,kBACA,UDgBmB,MCfnB,YDJgB,mHCKhB,UDuBc,KCtBd,iBACA,6CACA,sBAEA,iBACE,sBAGF,kBACE,sBAGF,gBACE,6CACA,aAIJ,gBACE,cAGF,sCACE,kBACA,MACA,OACA,QACA,SACA,UAGF,mBAGE,gCACA,mBAGF,mBAGE,yBACA,eAGF,eACE,SACA,SACA,YACA,WACA,4CACA,kBACA,kDACA,iDACA,8CAGF,kBACE,SACA,YACA,YACA,WACA,4CACA,kBACA,mDACA,oDACA,6CAGF,cACE,kBACA,cAGF,gBACE,yCACA,8BACA,cACA,iBACA,cDnFkB,ICoFlB,UD1Dc,KC2Dd,eACA,yBAGF,mBACE,YACA,8CAGF,kBACE,YACA,8CAGF,mBACE,YACA,6CAGF,kBACE,YAEA,wBACE,uCACA,eAIJ,0BACE,aCrHF,mBACE,aACA,uBACA,mBACA,sBACA,YFJgB,mHEKhB,OAGF,qBACE,kBACA,aACA,uBACA,mBACA,2BACA,mBACA,iBAGF,0BACE,kBACA,YACA,OFPc,KEQd,QFPiB,IEQjB,MFTc,KEUd,sBAGF,kBACE,aACA,uBACA,mBACA,OFVe,MEajB,mBACE,kBACA,YACA,sBACA,2BAGF,cACE,kBAGF,0BACE,gDACA,QFzBiC,ME4BnC,gBACE,aACA,mBACA,kBACA,uBACA,cFrDkB,IEsDlB,OF1Cc,KE2Cd,QF1CiB,IE2CjB,MF5Cc,KE6Cd,+BACA,sBACA,kBAGF,wEACE,6BACA,0BAGF,kEACE,4BACA,yBAGF,iDACE,mCACA,mCAGF,oDACE,8CACA,iDAGF,4EACE,iCACA,iCAGF,iBACE,gCAGF,mBACE,gCACA,mBAqCF,mBACE,iCACA,gBACA,2CACA,8CAGF,wBACE,mCACA,mCACA,gBACA,6CACA,gDAGF,WACE,yCAGF,cACE,gCACA,kBAGF,qBACE,gBACA,8CACA,iDAGF,2BAIE,+CAGF,yBAIE,gDAGF,+BACE,WACA,WACA,kCAGF,mBACE,YFhK0B,KEmK5B,iCACE,WACA,wCACA,kBACA,SAGF,gBACE,UACA,kBACA,SACA,2BAKF,iBACE,WACA,OAKF,oBACE,kBACA,cFjNkB,IEkNlB,yCACA,YACA,wCACA,cACA,sBACA,eAGF,qBACE,mBAGF,kBACE,aACA,mBACA,qBACA,2BAGF,kBACE,WACA,UACA,kBACA,sCACA,2BACA,iBAGF,qBACE,SACA,SACA,WACA,UACA,yCACA,kBACA,8CACA,+CACA,6CAGF,uBACE,kBACA,WAGF,0CACE,kBACE,uBAIJ,oBACE,2CCzQF,oBACE,aACA,mBACA,OHS0B,KGR1B,2BACA,sBAGF,eACE,aACA,mBACA,uBACA,eACA,YACA,MHD+B,KGE/B,2BACA,kBACA,kBAEA,mBACE,OHNqB,KGOrB,MHPqB,KGUvB,qBACE,iCACA,iCAIJ,sDACE,oCACA,oCACA,mBAWF,uBACE,UACA,kBACA,eACA,OHpC0B,KGqC1B,aACA,mBACA,uBACA,cHjDkB,IGkDlB,sBAEA,6BACE,iCACA,iCAIJ,qBACE,aACA,WC/DF,aACE,kBACA,WACA,YACA,sCACA,MACA,OACA,+BACA,cACA,YJRgB,mHIShB,2BACA,sBAGF,gDACE,2CACA,iDAGF,0CACE,UACA,iDAGF,gDACE,4CACA,mBAGF,mBACE,YACA,aAGF,oBACE,aAGF,qBACE,cAGF,uBACE,sBACA,gBAGF,mCACE,YAGF,iBACE,UACA,sBACA,aACA,iBACA,kBACA,eACA,eACA,WACA,mBAGF,wCACE,OAGF,iBACE,sBACA,UACA,QJ7CwB,II8CxB,mBAGF,sBACE,eAGF,yBACE,eACA,cJ7EkB,II8ElB,kBACA,mCACA,mCAGF,kBACE,eACA,cJrFkB,IIsFlB,kBAEA,wBACE,iCACA,iCAIJ,qBACE,iCACA,iCAGF,oBACE,gBACA,SACA,gBAGF,wBACE,UACA,sBAGF,2BACE,mBACA,oCAEA,iCACE,oCAIJ,kCACE,mBACA,4CAEA,wCACE,4CAIJ,yBACE,aACA,WACA,mBACA,8BACA,OJzHc,KKfhB,gBACE,WACA,aACA,mBACA,uBACA,iBACA,YLLgB,mHKMhB,2BAGF,kBACE,eAGF,8BACE,eAGF,kBACE,eAGF,8BACE,cAGF,cACE,ULImB,KKHnB,kBACA,aACA,mBACA,uBACA,sBAGF,kBACE,eACA,2BACA,cLnCkB,IKoClB,aACA,mBACA,uBACA,cAEA,wBACE,iCACA,iCAIJ,oBACE,YACA,SACA,OL3B6B,KK4B7B,ML5B6B,KK6B7B,aACA,mBACA,uBACA,eACA,kBACA,2BACA,sBAEA,wBACE,OLtC2B,KKuC3B,MLvC2B,KK0C7B,0BACE,iCACA,8BAIJ,gEACE,oCACA,oCACA,mBAWF,kBACE,mCACA,mCACA,YACA,QL3EmB,KK4EnB,cL1FkB,IK2FlB,eC9FF,gBACE,aACA,mBACA,WACA,QNamB,KMZnB,sBACA,2BACA,sCAEA,oBACE,ONIqB,KMHrB,WAIJ,uBACE,UACA,2BACA,UNYsB,MMTxB,oBACE,UACA,iBAGF,YACE,iBACA,eACA,QNR2B,QMS3B,cN3BkB,IM4BlB,oBACA,mBAGF,YACE,8BAGF,qBACE,uCACA,mBAGF,YACE,gCCpCF,gBACE,+BACA,sBACA,0BACA,4BACA,+BACA,4BACA,qCACA,8BACA,8BACA,2BACA,gCACA,iCACA,6BACA,kCACA,oCACA,+BACA,4BACA,qCACA,yBACA,2BACA,2BACA,4BACA,0CAGF,iBACE,4BACA,yBACA,0BACA,+BACA,+BACA,4BACA,qCACA,iCACA,8BACA,wBACA,6BACA,iCACA,6BACA,oCACA,+BACA,4BACA,qCACA,yBACA,2BACA,2BACA,4BACA,kCACA,4CAGF,UACE,YP7DgB,mHO8DhB,iBACA,sBAGF,aACE,eAIF,UACE,oBACA,kBAIF,YACE,WACA,kBACA,2BACA,sCACA,eACA,aACA,mBACA,qBACA,uBACA,QPvEmB,KOwEnB,sBACA,OP/EkB,KOiFlB,kBACE,iCACA,iCAGF,gBACE,OPpFqB,KOqFrB,WAIJ,mBACE,0BPrGkB,IOsGlB,2BPtGkB,IOyGpB,kBACE,aAGF,6BACE,sBACA,kBAGF,cACE,kBAGF,gHAIE,4BAGF,0BACE,UACA,2BAGF,wBACE,UACA,4BAGF,0BACE,UACA,4BAGF,wBACE,UACA,2BAGF,sKAME,4BAGF,gDAEE,UACA,2BAGF,gDAEE,UACA,4BAGF,2BACE,UAGF,0DAEE,4BAGF,yBACE","file":"main.css"}
|
||||
2
node_modules/@vuepic/vue-datepicker/dist/vue-datepicker.iife.js
generated
vendored
Normal file
2
node_modules/@vuepic/vue-datepicker/dist/vue-datepicker.iife.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
3652
node_modules/@vuepic/vue-datepicker/dist/vue-datepicker.js
generated
vendored
Normal file
3652
node_modules/@vuepic/vue-datepicker/dist/vue-datepicker.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
node_modules/@vuepic/vue-datepicker/dist/vue-datepicker.umd.cjs
generated
vendored
Normal file
1
node_modules/@vuepic/vue-datepicker/dist/vue-datepicker.umd.cjs
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
269
node_modules/@vuepic/vue-datepicker/index.d.ts
generated
vendored
Normal file
269
node_modules/@vuepic/vue-datepicker/index.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
import { Locale } from 'date-fns';
|
||||
import { ComponentOptionsMixin, ComputedOptions, DefineComponent, MethodOptions } from 'vue';
|
||||
|
||||
export type EmitEvents =
|
||||
| 'update:model-value'
|
||||
| 'text-submit'
|
||||
| 'closed'
|
||||
| 'cleared'
|
||||
| 'open'
|
||||
| 'focus'
|
||||
| 'blur'
|
||||
| 'internal-model-change'
|
||||
| 'recalculate-position'
|
||||
| 'flow-step'
|
||||
| 'update-month-year'
|
||||
| 'invalid-select'
|
||||
| 'tooltip-open'
|
||||
| 'tooltip-close'
|
||||
| 'invalid-fixed-range';
|
||||
|
||||
export interface VueDatePicker {
|
||||
uid?: string;
|
||||
name?: string;
|
||||
is24?: boolean;
|
||||
enableTimePicker?: boolean;
|
||||
range?: boolean;
|
||||
multiCalendars?: boolean | number | string;
|
||||
multiCalendarsSolo?: boolean;
|
||||
multiStatic?: boolean;
|
||||
modelValue?:
|
||||
| Date
|
||||
| Date[]
|
||||
| string
|
||||
| string[]
|
||||
| number
|
||||
| number[]
|
||||
| {
|
||||
hours: number | string;
|
||||
minutes: number | string;
|
||||
seconds?: number | string;
|
||||
}
|
||||
| {
|
||||
hours: number | string;
|
||||
minutes: number | string;
|
||||
seconds?: number | string;
|
||||
}[]
|
||||
| { month: number | string; year: number | string }
|
||||
| { month: number | string; year: number | string }[]
|
||||
| null;
|
||||
locale?: string;
|
||||
position?: 'left' | 'center' | 'right';
|
||||
dark?: boolean;
|
||||
placeholder?: string;
|
||||
weekNumbers?: boolean;
|
||||
hoursIncrement?: number | string;
|
||||
hoursGridIncrement?: number | string;
|
||||
secondsGridIncrement?: number | string;
|
||||
minutesGridIncrement?: number | string;
|
||||
minutesIncrement?: number | string;
|
||||
secondsIncrement?: number | string;
|
||||
minDate?: Date | string;
|
||||
maxDate?: Date | string;
|
||||
minTime?: { hours?: number | string; minutes?: number | string; seconds?: number | string };
|
||||
maxTime?: { hours?: number | string; minutes?: number | string; seconds?: number | string };
|
||||
weekStart?: '0' | '1' | '2' | '3' | '4' | '5' | '6' | 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||
disabled?: boolean;
|
||||
readonly?: boolean;
|
||||
required?: boolean;
|
||||
format?:
|
||||
| string
|
||||
| ((
|
||||
date:
|
||||
| Date
|
||||
| Date[]
|
||||
| {
|
||||
hours: number | string;
|
||||
minutes: number | string;
|
||||
seconds?: number | string;
|
||||
}
|
||||
| {
|
||||
hours: number | string;
|
||||
minutes: number | string;
|
||||
seconds?: number | string;
|
||||
}[]
|
||||
| {
|
||||
month: number | string;
|
||||
year: number | string;
|
||||
},
|
||||
) => string);
|
||||
previewFormat?:
|
||||
| string
|
||||
| ((
|
||||
date:
|
||||
| Date
|
||||
| Date[]
|
||||
| {
|
||||
hours: number | string;
|
||||
minutes: number | string;
|
||||
seconds?: number | string;
|
||||
}
|
||||
| {
|
||||
hours: number | string;
|
||||
minutes: number | string;
|
||||
seconds?: number | string;
|
||||
}[]
|
||||
| {
|
||||
month: number | string;
|
||||
year: number | string;
|
||||
},
|
||||
) => string);
|
||||
inputClassName?: string;
|
||||
menuClassName?: string;
|
||||
calendarClassName?: string;
|
||||
calendarCellClassName?: string;
|
||||
hideInputIcon?: boolean;
|
||||
state?: boolean;
|
||||
clearable?: boolean;
|
||||
closeOnScroll?: boolean;
|
||||
autoApply?: boolean;
|
||||
filters?: {
|
||||
months?: number[];
|
||||
years?: number[];
|
||||
times?: { hours?: number[]; minutes?: number[]; seconds?: number[] };
|
||||
};
|
||||
disableMonthYearSelect?: boolean;
|
||||
yearRange?: number[];
|
||||
disabledDates?: Date[] | string[] | ((date: Date) => boolean);
|
||||
inline?: boolean;
|
||||
inlineWithInput?: boolean;
|
||||
selectText?: string;
|
||||
cancelText?: string;
|
||||
weekNumName?: string;
|
||||
autoPosition?: boolean;
|
||||
monthPicker?: boolean;
|
||||
timePicker?: boolean;
|
||||
closeOnAutoApply?: boolean;
|
||||
textInput?: boolean;
|
||||
textInputOptions?: {
|
||||
enterSubmit?: boolean;
|
||||
tabSubmit?: boolean;
|
||||
openMenu?: boolean;
|
||||
rangeSeparator?: string;
|
||||
format?: string | string[] | ((value: string) => Date | null);
|
||||
};
|
||||
teleport?: string | HTMLElement;
|
||||
monthNameFormat?: 'long' | 'short';
|
||||
startDate?: string | Date;
|
||||
startTime?:
|
||||
| {
|
||||
hours?: number | string;
|
||||
minutes?: number | string;
|
||||
seconds?: number | string;
|
||||
}
|
||||
| {
|
||||
hours?: number | string;
|
||||
minutes?: number | string;
|
||||
seconds?: number | string;
|
||||
}[];
|
||||
monthYearComponent?: DefineComponent;
|
||||
timePickerComponent?: DefineComponent;
|
||||
actionRowComponent?: DefineComponent;
|
||||
customProps?: Record<string, unknown>;
|
||||
hideOffsetDates?: boolean;
|
||||
autoRange?: number | string;
|
||||
noToday?: boolean;
|
||||
noHoursOverlay?: boolean;
|
||||
noMinutesOverlay?: boolean;
|
||||
noSecondsOverlay?: boolean;
|
||||
altPosition?: boolean | ((el: HTMLElement | undefined) => { top: string; left: string; transform: string });
|
||||
disabledWeekDays?: number[] | string[];
|
||||
allowedDates?: string[] | Date[];
|
||||
showNowButton?: boolean;
|
||||
nowButtonLabel?: string;
|
||||
partialRange?: boolean;
|
||||
monthChangeOnScroll?: boolean | 'inverse';
|
||||
markers?: {
|
||||
date: Date | string;
|
||||
type?: 'dot' | 'line';
|
||||
tooltip?: { text?: string; html?: string; color?: string }[];
|
||||
color?: string;
|
||||
}[];
|
||||
transitions?:
|
||||
| boolean
|
||||
| { open?: string; close?: string; next?: string; previous?: string; vNext?: string; vPrevious?: string };
|
||||
modeHeight?: string | number;
|
||||
enableSeconds?: boolean;
|
||||
escClose?: boolean;
|
||||
spaceConfirm?: boolean;
|
||||
monthChangeOnArrows?: boolean;
|
||||
formatLocale?: Locale;
|
||||
autocomplete?: string;
|
||||
multiDates?: boolean;
|
||||
presetRanges?: { label: string; range: Date[] | string[]; style?: Record<string, string>; slot?: string }[];
|
||||
flow?: ('month' | 'year' | 'calendar' | 'time' | 'minutes' | 'hours' | 'seconds')[];
|
||||
preventMinMaxNavigation?: boolean;
|
||||
minRange?: number | string;
|
||||
maxRange?: number | string;
|
||||
fixedStart?: boolean;
|
||||
fixedEnd?: boolean;
|
||||
utc?: boolean | 'preserve';
|
||||
multiDatesLimit?: number | string;
|
||||
reverseYears?: boolean;
|
||||
keepActionRow?: boolean;
|
||||
weekPicker?: boolean;
|
||||
noSwipe?: boolean;
|
||||
vertical?: boolean;
|
||||
ariaLabels?: {
|
||||
toggleOverlay?: string;
|
||||
menu?: string;
|
||||
input?: string;
|
||||
calendarWrap?: string;
|
||||
calendarDays?: string;
|
||||
openTimePicker?: string;
|
||||
closeTimePicker?: string;
|
||||
incrementValue?: (type: 'hours' | 'minutes' | 'seconds') => string;
|
||||
decrementValue?: (type: 'hours' | 'minutes' | 'seconds') => string;
|
||||
openTpOverlay?: (type: 'hours' | 'minutes' | 'seconds') => string;
|
||||
amPmButton?: string;
|
||||
openYearsOverlay?: string;
|
||||
openMonthsOverlay?: string;
|
||||
nextMonth?: string;
|
||||
prevMonth?: string;
|
||||
day?: ({ value }: { value: Date }) => string;
|
||||
};
|
||||
arrowNavigation?: boolean;
|
||||
yearPicker?: boolean;
|
||||
disableTimeRangeValidation?: boolean;
|
||||
dayNames?: ((lang: string, weekStart: number) => string[]) | string[];
|
||||
modelType?: 'timestamp' | 'format' | string;
|
||||
modelAuto?: boolean;
|
||||
highlight?: Date[] | string[] | number[] | ((date: Date[]) => boolean);
|
||||
highlightWeekDays?: number[];
|
||||
highlightDisabledDays?: boolean;
|
||||
offset?: string | number;
|
||||
teleportCenter?: boolean;
|
||||
ignoreTimeValidation?: boolean;
|
||||
dayClass?: (date: Date) => string;
|
||||
hideNavigation?: ('month' | 'year' | 'calendar' | 'time' | 'minutes' | 'hours' | 'seconds')[];
|
||||
onClickOutside?: (validate: () => boolean) => void;
|
||||
noDisabledRange?: boolean;
|
||||
sixWeeks?: boolean;
|
||||
timezone?: string;
|
||||
}
|
||||
|
||||
export interface PublicMethods extends MethodOptions {
|
||||
selectDate: () => void;
|
||||
closeMenu: () => void;
|
||||
openMenu: () => void;
|
||||
clearValue: () => void;
|
||||
onScroll: () => void;
|
||||
updateInternalModelValue: (value: Date | Date[]) => void;
|
||||
setMonthYear: (value: { month?: number | string; year?: number | string }) => void;
|
||||
}
|
||||
|
||||
declare const _default: DefineComponent<
|
||||
VueDatePicker,
|
||||
{},
|
||||
{},
|
||||
ComputedOptions,
|
||||
PublicMethods,
|
||||
ComponentOptionsMixin,
|
||||
ComponentOptionsMixin,
|
||||
EmitEvents[],
|
||||
EmitEvents,
|
||||
VueDatePicker
|
||||
>;
|
||||
|
||||
export default _default;
|
||||
119
node_modules/@vuepic/vue-datepicker/package.json
generated
vendored
Normal file
119
node_modules/@vuepic/vue-datepicker/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
{
|
||||
"name": "@vuepic/vue-datepicker",
|
||||
"version": "3.6.8",
|
||||
"description": "Datepicker component for Vue 3",
|
||||
"author": "Vuepic",
|
||||
"private": false,
|
||||
"license": "MIT",
|
||||
"homepage": "https://vue3datepicker.com",
|
||||
"types": "index.d.ts",
|
||||
"type": "module",
|
||||
"main": "dist/vue-datepicker.umd.cjs",
|
||||
"module": "dist/vue-datepicker.js",
|
||||
"browser": "dist/vue-datepicker.js",
|
||||
"unpkg": "dist/vue-datepicker.iife.js",
|
||||
"style": "dist/main.css",
|
||||
"sass": "src/VueDatePicker/style/main.scss",
|
||||
"files": [
|
||||
"dist/*",
|
||||
"src/**/*",
|
||||
"index.d.ts",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/vue-datepicker.js",
|
||||
"require": "./dist/vue-datepicker.umd.cjs"
|
||||
},
|
||||
"./dist/main.css": {
|
||||
"import": "./dist/main.css",
|
||||
"require": "./dist/main.css"
|
||||
},
|
||||
"./src/VueDatePicker/style/main.scss": {
|
||||
"import": "./src/VueDatePicker/style/main.scss",
|
||||
"require": "./src/VueDatePicker/style/main.scss"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "run-s clean build:lib build:css",
|
||||
"clean": "rimraf ./dist",
|
||||
"build:lib": "run-s typecheck build:es build:browser",
|
||||
"typecheck": "vue-tsc --noEmit -p tsconfig.type-check.json --composite false",
|
||||
"build:es": "cross-env NODE_ENV=production vite build --mode production",
|
||||
"build:browser": "cross-env NODE_ENV=production vite -f iife build --mode production",
|
||||
"build:css": "cross-env NODE_ENV=production node_modules/.bin/sass src/VueDatePicker/style/main.scss dist/main.css --style compressed",
|
||||
"test": "cross-env NODE_ENV=test vitest --environment jsdom",
|
||||
"test:coverage": "cross-env NODE_ENV=test vitest --environment jsdom run --coverage",
|
||||
"lint": "run-s lint:style lint:lib",
|
||||
"lint:style": "stylelint --fix \"src/**/*.scss\"",
|
||||
"lint:lib": "eslint --fix \"src/**\" --ignore-pattern \"src/**/*.scss\""
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rushstack/eslint-patch": "^1.2.0",
|
||||
"@types/jsdom": "^21.1.0",
|
||||
"@types/node": "^18.13.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.51.0",
|
||||
"@typescript-eslint/parser": "^5.51.0",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"@vitest/coverage-c8": "^0.28.4",
|
||||
"@vue/eslint-config-prettier": "^7.0.0",
|
||||
"@vue/eslint-config-typescript": "^11.0.2",
|
||||
"@vue/test-utils": "^2.2.10",
|
||||
"@vue/tsconfig": "^0.1.3",
|
||||
"c8": "^7.12.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.33.0",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-vue": "^9.9.0",
|
||||
"jsdom": "^21.1.0",
|
||||
"minimist": "^1.2.7",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss": "^8.4.21",
|
||||
"prettier": "^2.8.4",
|
||||
"rimraf": "^4.1.2",
|
||||
"sass": "^1.58.0",
|
||||
"sass-loader": "^13.2.0",
|
||||
"stylelint": "^14.16.1",
|
||||
"stylelint-config-standard-scss": "^6.1.0",
|
||||
"terser": "^5.16.3",
|
||||
"typescript": "^4.9.5",
|
||||
"vite": "^4.1.1",
|
||||
"vitest": "^0.28.4",
|
||||
"vue": "^3.2.47",
|
||||
"vue-tsc": "^1.0.24"
|
||||
},
|
||||
"dependencies": {
|
||||
"date-fns": "^2.29.3",
|
||||
"date-fns-tz": "^1.3.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": ">=3.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Vuepic/vue-datepicker.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/Vuepic/vue-datepicker/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"vue",
|
||||
"vue-datepicker",
|
||||
"datepicker",
|
||||
"date",
|
||||
"vue3-datepicker",
|
||||
"datetimepicker",
|
||||
"daterangepicker"
|
||||
],
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not dead"
|
||||
]
|
||||
}
|
||||
373
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/VueDatePicker.vue
generated
vendored
Normal file
373
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/VueDatePicker.vue
generated
vendored
Normal file
|
|
@ -0,0 +1,373 @@
|
|||
<template>
|
||||
<div :class="wrapperClass">
|
||||
<DatepickerInput
|
||||
ref="inputRef"
|
||||
:is-menu-open="isOpen"
|
||||
v-model:input-value="inputValue"
|
||||
v-bind="$props"
|
||||
@clear="clearValue"
|
||||
@open="openMenu"
|
||||
@set-input-date="setInputDate"
|
||||
@set-empty-date="emitModelValue"
|
||||
@select-date="selectDate"
|
||||
@toggle="toggleMenu"
|
||||
@close="closeMenu"
|
||||
@focus="handleInputFocus"
|
||||
@blur="handleBlur"
|
||||
>
|
||||
<template v-for="(slot, i) in inputSlots" #[slot]="args" :key="i">
|
||||
<slot :name="slot" v-bind="args" />
|
||||
</template>
|
||||
</DatepickerInput>
|
||||
<teleport :to="teleport" :disabled="inline" v-if="isOpen">
|
||||
<DatepickerMenu
|
||||
v-if="isOpen"
|
||||
ref="dpMenuRef"
|
||||
:class="theme"
|
||||
:style="menuPosition"
|
||||
:open-on-top="openOnTop"
|
||||
v-bind="$props"
|
||||
v-model:internal-model-value="internalModelValue"
|
||||
@close-picker="closeMenu"
|
||||
@select-date="selectDate"
|
||||
@auto-apply="autoApplyValue"
|
||||
@time-update="timeUpdate"
|
||||
@flow-step="$emit('flow-step', $event)"
|
||||
@update-month-year="$emit('update-month-year', $event)"
|
||||
@invalid-select="$emit('invalid-select', internalModelValue)"
|
||||
@invalid-fixed-range="$emit('invalid-fixed-range', $event)"
|
||||
@recalculate-position="setMenuPosition"
|
||||
@tooltip-open="$emit('tooltip-open', $event)"
|
||||
@tooltip-close="$emit('tooltip-close', $event)"
|
||||
>
|
||||
<template v-for="(slot, i) in slotList" #[slot]="args" :key="i">
|
||||
<slot :name="slot" v-bind="{ ...args }" />
|
||||
</template>
|
||||
</DatepickerMenu>
|
||||
</teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, nextTick, onMounted, onUnmounted, ref, toRef, useSlots, watch } from 'vue';
|
||||
|
||||
import DatepickerInput from '@/components/DatepickerInput.vue';
|
||||
import DatepickerMenu from '@/components/DatepickerMenu.vue';
|
||||
|
||||
import {
|
||||
useExternalInternalMapper,
|
||||
usePosition,
|
||||
mapSlots,
|
||||
useArrowNavigation,
|
||||
useState,
|
||||
useUtils,
|
||||
} from '@/components/composables';
|
||||
import { onClickOutside } from '@/directives/clickOutside';
|
||||
import { AllProps } from '@/utils/props';
|
||||
import { getNumVal } from '@/utils/util';
|
||||
|
||||
import type { DynamicClass, MonthYearOpt, DatepickerMenuRef } from '@/interfaces';
|
||||
|
||||
const emit = defineEmits([
|
||||
'update:model-value',
|
||||
'text-submit',
|
||||
'closed',
|
||||
'cleared',
|
||||
'open',
|
||||
'focus',
|
||||
'blur',
|
||||
'internal-model-change',
|
||||
'recalculate-position',
|
||||
'flow-step',
|
||||
'update-month-year',
|
||||
'invalid-select',
|
||||
'invalid-fixed-range',
|
||||
'tooltip-open',
|
||||
'tooltip-close',
|
||||
]);
|
||||
const props = defineProps({
|
||||
...AllProps,
|
||||
});
|
||||
const slots = useSlots();
|
||||
const isOpen = ref(false);
|
||||
const modelValueRef = toRef(props, 'modelValue');
|
||||
const timezoneRef = toRef(props, 'timezone');
|
||||
const dpMenuRef = ref<DatepickerMenuRef | null>(null);
|
||||
const inputRef = ref(null);
|
||||
const isInputFocused = ref(false);
|
||||
|
||||
const { setMenuFocused, setShiftKey } = useState();
|
||||
const { clearArrowNav } = useArrowNavigation();
|
||||
const { validateDate, isValidTime, defaults } = useUtils(props);
|
||||
|
||||
onMounted(() => {
|
||||
parseExternalModelValue(props.modelValue);
|
||||
if (!props.inline) {
|
||||
window.addEventListener('scroll', onScroll);
|
||||
window.addEventListener('resize', onResize);
|
||||
}
|
||||
|
||||
if (props.inline) {
|
||||
isOpen.value = true;
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (!props.inline) {
|
||||
window.removeEventListener('scroll', onScroll);
|
||||
window.removeEventListener('resize', onResize);
|
||||
}
|
||||
});
|
||||
|
||||
const slotList = mapSlots(slots, 'all', props.presetRanges);
|
||||
const inputSlots = mapSlots(slots, 'input');
|
||||
|
||||
watch(
|
||||
[modelValueRef, timezoneRef],
|
||||
() => {
|
||||
parseExternalModelValue(modelValueRef.value);
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
const { openOnTop, menuPosition, setMenuPosition, setInitialPosition } = usePosition(
|
||||
dpMenuRef,
|
||||
inputRef,
|
||||
emit,
|
||||
props,
|
||||
);
|
||||
|
||||
const {
|
||||
inputValue,
|
||||
internalModelValue,
|
||||
parseExternalModelValue,
|
||||
emitModelValue,
|
||||
formatInputValue,
|
||||
checkBeforeEmit,
|
||||
} = useExternalInternalMapper(emit, props, isInputFocused);
|
||||
|
||||
const wrapperClass = computed(
|
||||
(): DynamicClass => ({
|
||||
dp__main: true,
|
||||
dp__theme_dark: props.dark,
|
||||
dp__theme_light: !props.dark,
|
||||
dp__flex_display: props.inline,
|
||||
dp__flex_display_with_input: props.inlineWithInput,
|
||||
}),
|
||||
);
|
||||
|
||||
const theme = computed(() => (props.dark ? 'dp__theme_dark' : 'dp__theme_light'));
|
||||
|
||||
/**
|
||||
* Event listener for 'scroll'
|
||||
* Depending on the props, it can close the menu or set correct position
|
||||
*/
|
||||
const onScroll = (): void => {
|
||||
if (isOpen.value) {
|
||||
if (props.closeOnScroll) {
|
||||
closeMenu();
|
||||
} else if (props.autoPosition) {
|
||||
setMenuPosition();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Event listener for 'resize'
|
||||
* Since the menu is absolutely positioned, on window resize, correct positioning
|
||||
*/
|
||||
const onResize = (): void => {
|
||||
if (isOpen.value) {
|
||||
setMenuPosition();
|
||||
}
|
||||
};
|
||||
|
||||
const openMenu = (): void => {
|
||||
if (!props.disabled && !props.readonly) {
|
||||
setInitialPosition();
|
||||
isOpen.value = true;
|
||||
nextTick().then(() => {
|
||||
setMenuPosition();
|
||||
if (isOpen.value) {
|
||||
emit('open');
|
||||
}
|
||||
});
|
||||
|
||||
if (!isOpen.value) {
|
||||
clearInternalValues();
|
||||
}
|
||||
|
||||
parseExternalModelValue(props.modelValue);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* When x button is pressed on input, it will call this function that will emit null
|
||||
* for the modelValue and clear internally stored data
|
||||
*/
|
||||
const clearValue = (): void => {
|
||||
inputValue.value = '';
|
||||
clearInternalValues();
|
||||
emit('update:model-value', null);
|
||||
emit('cleared');
|
||||
closeMenu();
|
||||
};
|
||||
|
||||
const validateBeforeEmit = () => {
|
||||
const date = internalModelValue.value;
|
||||
if (!date) return true;
|
||||
if (!Array.isArray(date) && validateDate(date)) return true;
|
||||
if (Array.isArray(date)) {
|
||||
if (date.length === 2 && validateDate(date[0]) && validateDate(date[1])) {
|
||||
return true;
|
||||
}
|
||||
return validateDate(date[0]);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when select button is clicked, emit update for the modelValue
|
||||
*/
|
||||
const selectDate = (): void => {
|
||||
if (checkBeforeEmit() && validateBeforeEmit()) {
|
||||
emitModelValue();
|
||||
closeMenu();
|
||||
} else {
|
||||
emit('invalid-select', internalModelValue.value);
|
||||
}
|
||||
};
|
||||
|
||||
const emitOnAutoApply = (ignoreClose: boolean): void => {
|
||||
emitModelValue();
|
||||
if (props.closeOnAutoApply && !ignoreClose) {
|
||||
closeMenu();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* When value is selected it will emit an event that will call this function
|
||||
* ignoreClose is passed when time is picked or month and year, since they update the value and for
|
||||
* the user experience it should not close the menu
|
||||
*/
|
||||
const autoApplyValue = (ignoreClose = false): void => {
|
||||
if (props.autoApply) {
|
||||
const isTimeValid = isValidTime(internalModelValue.value);
|
||||
|
||||
if (isTimeValid && validateBeforeEmit()) {
|
||||
if (props.range && Array.isArray(internalModelValue.value)) {
|
||||
if (props.partialRange || internalModelValue.value.length === 2) {
|
||||
emitOnAutoApply(ignoreClose);
|
||||
}
|
||||
} else {
|
||||
emitOnAutoApply(ignoreClose);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears the internally stored values. This is different from clearValue since it does not emit v-model
|
||||
* update, just clears internal data
|
||||
*/
|
||||
const clearInternalValues = (): void => {
|
||||
if (!props.textInput) {
|
||||
internalModelValue.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Closes the menu and clears the internal data
|
||||
*/
|
||||
const closeMenu = (): void => {
|
||||
if (!props.inline) {
|
||||
if (isOpen.value) {
|
||||
isOpen.value = false;
|
||||
setMenuFocused(false);
|
||||
setShiftKey(false);
|
||||
clearArrowNav();
|
||||
emit('closed');
|
||||
setInitialPosition();
|
||||
if (inputValue.value) {
|
||||
parseExternalModelValue(modelValueRef.value);
|
||||
}
|
||||
}
|
||||
clearInternalValues();
|
||||
if (inputRef.value) {
|
||||
(inputRef.value as { focusInput: () => void }).focusInput();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const setInputDate = (date: Date | Date[] | null, submit?: boolean): void => {
|
||||
if (!date) {
|
||||
internalModelValue.value = null;
|
||||
return;
|
||||
}
|
||||
internalModelValue.value = date;
|
||||
if (submit) {
|
||||
selectDate();
|
||||
emit('text-submit');
|
||||
}
|
||||
};
|
||||
|
||||
const timeUpdate = (): void => {
|
||||
if (props.autoApply && isValidTime(internalModelValue.value)) {
|
||||
emitModelValue();
|
||||
}
|
||||
};
|
||||
|
||||
const toggleMenu = (): void => {
|
||||
if (isOpen.value) return closeMenu();
|
||||
return openMenu();
|
||||
};
|
||||
|
||||
const updateInternalModelValue = (value: Date | Date[]): void => {
|
||||
internalModelValue.value = value;
|
||||
};
|
||||
|
||||
const shouldFormatInputValue = computed(() => props.textInput && defaults.value.textInputOptions.format);
|
||||
|
||||
const handleInputFocus = () => {
|
||||
if (shouldFormatInputValue.value) {
|
||||
isInputFocused.value = true;
|
||||
formatInputValue();
|
||||
}
|
||||
|
||||
emit('focus');
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
if (shouldFormatInputValue.value) {
|
||||
isInputFocused.value = false;
|
||||
formatInputValue();
|
||||
}
|
||||
emit('blur');
|
||||
};
|
||||
|
||||
const setMonthYear = (value: MonthYearOpt) => {
|
||||
if (dpMenuRef.value) {
|
||||
dpMenuRef.value.updateMonthYear(0, {
|
||||
month: getNumVal(value.month),
|
||||
year: getNumVal(value.year),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onClickOutside(
|
||||
dpMenuRef,
|
||||
inputRef,
|
||||
props.onClickOutside ? () => props.onClickOutside(validateBeforeEmit) : closeMenu,
|
||||
);
|
||||
|
||||
defineExpose({
|
||||
closeMenu,
|
||||
selectDate,
|
||||
clearValue,
|
||||
openMenu,
|
||||
onScroll,
|
||||
formatInputValue, // exposed for testing purposes
|
||||
updateInternalModelValue, // modify internal modelValue
|
||||
setMonthYear,
|
||||
});
|
||||
</script>
|
||||
173
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/ActionRow.vue
generated
vendored
Normal file
173
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/ActionRow.vue
generated
vendored
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
<template>
|
||||
<div class="dp__action_row" :style="calendarWidth ? { width: `${calendarWidth}px` } : {}">
|
||||
<template v-if="$slots['action-row']">
|
||||
<slot
|
||||
name="action-row"
|
||||
v-bind="{
|
||||
internalModelValue,
|
||||
disabled,
|
||||
selectDate: () => $emit('select-date'),
|
||||
closePicker: () => $emit('close-picker'),
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="dp__selection_preview">
|
||||
<slot name="action-preview" v-if="$slots['action-preview']" :value="internalModelValue" />
|
||||
<template v-if="!$slots['action-preview']">
|
||||
<template v-if="!Array.isArray(previewValue)">{{ previewValue }}</template>
|
||||
<template v-if="Array.isArray(previewValue)">
|
||||
<div v-for="(preview, i) in previewValue" :key="i">{{ preview }}</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
<div class="dp__action_buttons">
|
||||
<slot name="action-select" v-if="$slots['action-select']" :value="internalModelValue" />
|
||||
<template v-if="!$slots['action-select']">
|
||||
<span
|
||||
v-if="!inline"
|
||||
class="dp__action dp__cancel"
|
||||
ref="cancelButtonRef"
|
||||
tabindex="0"
|
||||
@click="$emit('close-picker')"
|
||||
@keydown.enter="$emit('close-picker')"
|
||||
@keydown.space="$emit('close-picker')"
|
||||
>{{ cancelText }}</span
|
||||
>
|
||||
<span
|
||||
:class="selectClass"
|
||||
tabindex="0"
|
||||
@keydown.enter="selectDate"
|
||||
@keydown.space="selectDate"
|
||||
@click="selectDate"
|
||||
data-test="select-button"
|
||||
ref="selectButtonRef"
|
||||
>{{ selectText }}</span
|
||||
>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
|
||||
import { convertType, unrefElement } from '@/utils/util';
|
||||
import { useArrowNavigation, useUtils } from '@/components/composables';
|
||||
import { AllProps } from '@/utils/props';
|
||||
import { getDate, isDateAfter, isDateBefore } from '@/utils/date-utils';
|
||||
|
||||
import type { PropType } from 'vue';
|
||||
import type { InternalModuleValue } from '@/interfaces';
|
||||
|
||||
const emit = defineEmits(['close-picker', 'select-date', 'invalid-select']);
|
||||
|
||||
const props = defineProps({
|
||||
calendarWidth: { type: Number as PropType<number>, default: 0 },
|
||||
menuMount: { type: Boolean as PropType<boolean>, default: false },
|
||||
internalModelValue: { type: [Date, Array] as PropType<InternalModuleValue>, default: null },
|
||||
...AllProps,
|
||||
});
|
||||
|
||||
const { formatDate, isValidTime, defaults } = useUtils(props);
|
||||
const { buildMatrix } = useArrowNavigation();
|
||||
|
||||
const cancelButtonRef = ref(null);
|
||||
const selectButtonRef = ref(null);
|
||||
|
||||
onMounted(() => {
|
||||
if (props.arrowNavigation) {
|
||||
buildMatrix([unrefElement(cancelButtonRef), unrefElement(selectButtonRef)] as HTMLElement[], 'actionRow');
|
||||
}
|
||||
});
|
||||
|
||||
const validDateRange = computed(() => {
|
||||
return props.range && !props.partialRange && props.internalModelValue
|
||||
? (props.internalModelValue as Date[]).length === 2
|
||||
: true;
|
||||
});
|
||||
|
||||
const disabled = computed(() => !isTimeValid.value || !isMonthValid.value || !validDateRange.value);
|
||||
|
||||
const selectClass = computed(() => ({
|
||||
dp__action: true,
|
||||
dp__select: true,
|
||||
dp__action_disabled: disabled.value,
|
||||
}));
|
||||
|
||||
const isTimeValid = computed((): boolean => {
|
||||
if (!props.enableTimePicker || props.ignoreTimeValidation) return true;
|
||||
return isValidTime(props.internalModelValue);
|
||||
});
|
||||
|
||||
const isMonthValid = computed((): boolean => {
|
||||
if (!props.monthPicker) return true;
|
||||
return isMonthWithinRange(props.internalModelValue as Date);
|
||||
});
|
||||
|
||||
const handleCustomPreviewFormat = () => {
|
||||
const formatFn = defaults.value.previewFormat as (val: Date | Date[]) => string | string[];
|
||||
|
||||
if (props.timePicker) return formatFn(convertType(props.internalModelValue));
|
||||
|
||||
if (props.monthPicker) return formatFn(convertType(props.internalModelValue as Date));
|
||||
|
||||
return formatFn(convertType(props.internalModelValue));
|
||||
};
|
||||
|
||||
const formatRangeDate = () => {
|
||||
const dates = props.internalModelValue as Date[];
|
||||
if (defaults.value.multiCalendars > 0) {
|
||||
return `${formatDate(dates[0])} - ${formatDate(dates[1])}`;
|
||||
}
|
||||
return [formatDate(dates[0]), formatDate(dates[1])];
|
||||
};
|
||||
|
||||
const previewValue = computed((): string | string[] => {
|
||||
if (!props.internalModelValue || !props.menuMount) return '';
|
||||
if (typeof defaults.value.previewFormat === 'string') {
|
||||
if (Array.isArray(props.internalModelValue)) {
|
||||
if (props.internalModelValue.length === 2 && props.internalModelValue[1]) {
|
||||
return formatRangeDate();
|
||||
}
|
||||
if (props.multiDates) {
|
||||
return props.internalModelValue.map((date) => `${formatDate(date)}`);
|
||||
}
|
||||
if (props.modelAuto) {
|
||||
return `${formatDate(props.internalModelValue[0])}`;
|
||||
}
|
||||
return `${formatDate(props.internalModelValue[0])} -`;
|
||||
}
|
||||
return formatDate(props.internalModelValue);
|
||||
}
|
||||
return handleCustomPreviewFormat();
|
||||
});
|
||||
|
||||
const isMonthWithinRange = (date: Date | string): boolean => {
|
||||
if (!props.monthPicker) return true;
|
||||
let valid = true;
|
||||
if (props.minDate && props.maxDate) {
|
||||
return (
|
||||
isDateAfter(getDate(date), getDate(props.minDate)) &&
|
||||
isDateBefore(getDate(date), getDate(props.maxDate))
|
||||
);
|
||||
}
|
||||
if (props.minDate) {
|
||||
valid = isDateAfter(getDate(date), getDate(props.minDate));
|
||||
}
|
||||
if (props.maxDate) {
|
||||
valid = isDateBefore(getDate(date), getDate(props.maxDate));
|
||||
}
|
||||
|
||||
return valid;
|
||||
};
|
||||
|
||||
const selectDate = (): void => {
|
||||
if (isTimeValid.value && isMonthValid.value && validDateRange.value) {
|
||||
emit('select-date');
|
||||
} else {
|
||||
emit('invalid-select');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
296
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/Calendar.vue
generated
vendored
Normal file
296
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/Calendar.vue
generated
vendored
Normal file
|
|
@ -0,0 +1,296 @@
|
|||
<template>
|
||||
<div :class="calendarParentClass">
|
||||
<div :style="contentWrapStyle">
|
||||
<div
|
||||
ref="calendarWrapRef"
|
||||
v-if="!specificMode"
|
||||
:class="calendarWrapClass"
|
||||
role="grid"
|
||||
:aria-label="defaults.ariaLabels?.calendarWrap"
|
||||
>
|
||||
<div class="dp__calendar_header" role="row">
|
||||
<div class="dp__calendar_header_item" role="gridcell" v-if="weekNumbers">
|
||||
{{ weekNumName }}
|
||||
</div>
|
||||
<div
|
||||
class="dp__calendar_header_item"
|
||||
role="gridcell"
|
||||
v-for="(dayVal, i) in weekDays"
|
||||
:key="i"
|
||||
data-test="calendar-header"
|
||||
>
|
||||
<slot v-if="$slots['calendar-header']" name="calendar-header" :day="dayVal" :index="i" />
|
||||
<template v-if="!$slots['calendar-header']">
|
||||
{{ dayVal }}
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dp__calendar_header_separator"></div>
|
||||
<transition :name="transitionName" :css="!!transitions">
|
||||
<div
|
||||
class="dp__calendar"
|
||||
role="grid"
|
||||
:aria-label="defaults.ariaLabels?.calendarDays"
|
||||
v-if="showCalendar"
|
||||
>
|
||||
<div class="dp__calendar_row" role="row" v-for="(week, weekInd) in mappedDates" :key="weekInd">
|
||||
<div role="gridcell" v-if="weekNumbers" class="dp__calendar_item dp__week_num">
|
||||
<div class="dp__cell_inner">
|
||||
{{ getWeekNum(week.days) }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="(dayVal, dayInd) in week.days"
|
||||
role="gridcell"
|
||||
class="dp__calendar_item"
|
||||
:ref="(el) => assignDayRef(el, weekInd, dayInd)"
|
||||
:key="dayInd + weekInd"
|
||||
:aria-selected="
|
||||
dayVal.classData.dp__active_date ||
|
||||
dayVal.classData.dp__range_start ||
|
||||
dayVal.classData.dp__range_start
|
||||
"
|
||||
:aria-disabled="dayVal.classData.dp__cell_disabled"
|
||||
:aria-label="defaults.ariaLabels?.day?.(dayVal)"
|
||||
tabindex="0"
|
||||
:data-test="dayVal.value"
|
||||
@click.stop.prevent="$emit('select-date', dayVal)"
|
||||
@keydown.enter="$emit('select-date', dayVal)"
|
||||
@keydown.space="$emit('handle-space', dayVal)"
|
||||
@mouseenter="onMouseOver(dayVal, weekInd, dayInd)"
|
||||
@mouseleave="onMouseLeave(dayVal)"
|
||||
>
|
||||
<div class="dp__cell_inner" :class="dayVal.classData">
|
||||
<slot
|
||||
name="day"
|
||||
v-if="$slots.day && showDay(dayVal)"
|
||||
:day="+dayVal.text"
|
||||
:date="dayVal.value"
|
||||
></slot>
|
||||
<template v-if="!$slots.day"> {{ dayVal.text }} </template>
|
||||
<div
|
||||
v-if="dayVal.marker && showDay(dayVal)"
|
||||
:class="markerClass(dayVal.marker)"
|
||||
:style="dayVal.marker.color ? { backgroundColor: dayVal.marker.color } : {}"
|
||||
></div>
|
||||
<div
|
||||
class="dp__marker_tooltip"
|
||||
v-if="dateMatch(dayVal.value)"
|
||||
:style="markerTooltipStyle"
|
||||
>
|
||||
<div class="dp__tooltip_content" @click.stop v-if="dayVal.marker?.tooltip">
|
||||
<div
|
||||
v-for="(tooltip, i) in dayVal.marker.tooltip"
|
||||
:key="i"
|
||||
class="dp__tooltip_text"
|
||||
>
|
||||
<slot
|
||||
name="marker-tooltip"
|
||||
v-if="$slots['marker-tooltip']"
|
||||
:tooltip="tooltip"
|
||||
:day="dayVal.value"
|
||||
></slot>
|
||||
<template v-if="!$slots['marker-tooltip']">
|
||||
<div
|
||||
class="dp__tooltip_mark"
|
||||
:style="tooltip.color ? { backgroundColor: tooltip.color } : {}"
|
||||
></div>
|
||||
<div>{{ tooltip.text }}</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="dp__arrow_bottom_tp"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, nextTick, onMounted, ref } from 'vue';
|
||||
|
||||
import { getDayNames, getDefaultMarker, unrefElement } from '@/utils/util';
|
||||
import { useArrowNavigation, useUtils } from '@/components/composables';
|
||||
import { AllProps } from '@/utils/props';
|
||||
import { getDate, isDateAfter, isDateEqual, resetDateTime } from '@/utils/date-utils';
|
||||
|
||||
import type { PropType, UnwrapRef } from 'vue';
|
||||
import type { DynamicClass, ICalendarDate, ICalendarDay, IMarker } from '@/interfaces';
|
||||
|
||||
const emit = defineEmits([
|
||||
'select-date',
|
||||
'set-hover-date',
|
||||
'handle-scroll',
|
||||
'mount',
|
||||
'handle-swipe',
|
||||
'handle-space',
|
||||
'tooltip-open',
|
||||
'tooltip-close',
|
||||
]);
|
||||
|
||||
const props = defineProps({
|
||||
mappedDates: { type: Array as PropType<ICalendarDate[]>, default: () => [] },
|
||||
getWeekNum: {
|
||||
type: Function as PropType<(dates: UnwrapRef<ICalendarDay[]>) => string | number>,
|
||||
default: () => '',
|
||||
},
|
||||
specificMode: { type: Boolean as PropType<boolean>, default: false },
|
||||
instance: { type: Number as PropType<number>, default: 0 },
|
||||
month: { type: Number as PropType<number>, default: 0 },
|
||||
year: { type: Number as PropType<number>, default: 0 },
|
||||
...AllProps,
|
||||
});
|
||||
|
||||
const { buildMultiLevelMatrix } = useArrowNavigation();
|
||||
const { setDateMonthOrYear, defaults } = useUtils(props);
|
||||
|
||||
const showMakerTooltip = ref<Date | null>(null);
|
||||
const markerTooltipStyle = ref({ bottom: '', left: '', transform: '' });
|
||||
const dayRefs = ref<HTMLElement[][]>([]);
|
||||
const calendarWrapRef = ref<HTMLElement | null>(null);
|
||||
const showCalendar = ref(true);
|
||||
const transitionName = ref('');
|
||||
const touch = ref({ startX: 0, endX: 0, startY: 0, endY: 0 });
|
||||
|
||||
const weekDays = computed(() => {
|
||||
if (props.dayNames) {
|
||||
return Array.isArray(props.dayNames) ? props.dayNames : props.dayNames(props.locale, +props.weekStart);
|
||||
}
|
||||
return getDayNames(props.locale, +props.weekStart);
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
emit('mount', { cmp: 'calendar', refs: dayRefs });
|
||||
if (!props.noSwipe) {
|
||||
if (calendarWrapRef.value) {
|
||||
calendarWrapRef.value.addEventListener('touchstart', onTouchStart, { passive: false });
|
||||
calendarWrapRef.value.addEventListener('touchend', onTouchEnd, { passive: false });
|
||||
calendarWrapRef.value.addEventListener('touchmove', onTouchMove, { passive: false });
|
||||
}
|
||||
}
|
||||
if (props.monthChangeOnScroll && calendarWrapRef.value) {
|
||||
calendarWrapRef.value.addEventListener('wheel', onScroll, { passive: false });
|
||||
}
|
||||
});
|
||||
|
||||
const getTransitionName = (isNext: boolean) => {
|
||||
if (isNext) return props.vertical ? 'vNext' : 'next';
|
||||
return props.vertical ? 'vPrevious' : 'previous';
|
||||
};
|
||||
|
||||
const triggerTransition = (month: number, year: number): void => {
|
||||
if (props.transitions) {
|
||||
const newDate = resetDateTime(setDateMonthOrYear(getDate(), props.month, props.year));
|
||||
transitionName.value = isDateAfter(resetDateTime(setDateMonthOrYear(getDate(), month, year)), newDate)
|
||||
? defaults.value.transitions[getTransitionName(true)]
|
||||
: defaults.value.transitions[getTransitionName(false)];
|
||||
showCalendar.value = false;
|
||||
nextTick(() => {
|
||||
showCalendar.value = true;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Class object for calendar wrapper
|
||||
const calendarWrapClass = computed(
|
||||
(): DynamicClass => ({
|
||||
dp__calendar_wrap: true,
|
||||
[props.calendarClassName]: !!props.calendarClassName,
|
||||
}),
|
||||
);
|
||||
|
||||
const markerClass = computed((): ((marker: IMarker) => DynamicClass) => (marker) => {
|
||||
const defaultMarker = getDefaultMarker(marker);
|
||||
return {
|
||||
dp__marker_dot: defaultMarker.type === 'dot',
|
||||
dp__marker_line: defaultMarker.type === 'line',
|
||||
};
|
||||
});
|
||||
|
||||
const dateMatch = computed((): ((day: Date) => boolean) => (day) => isDateEqual(day, showMakerTooltip.value));
|
||||
|
||||
const calendarParentClass = computed(() => ({
|
||||
dp__calendar: true,
|
||||
dp__calendar_next: defaults.value.multiCalendars > 0 && props.instance !== 0,
|
||||
}));
|
||||
|
||||
const showDay = computed(() => (day: ICalendarDay) => props.hideOffsetDates ? day.current : true);
|
||||
|
||||
const contentWrapStyle = computed(() => (props.specificMode ? { height: `${props.modeHeight}px` } : undefined));
|
||||
|
||||
const onMouseOver = (day: UnwrapRef<ICalendarDay>, weekInd: number, dayInd: number): void => {
|
||||
emit('set-hover-date', day);
|
||||
if (day.marker?.tooltip?.length) {
|
||||
const el = unrefElement(dayRefs.value[weekInd][dayInd]);
|
||||
if (el) {
|
||||
const { width, height } = el.getBoundingClientRect();
|
||||
|
||||
markerTooltipStyle.value = {
|
||||
bottom: `${height}px`,
|
||||
left: `${width / 2}px`,
|
||||
transform: `translateX(-50%)`,
|
||||
};
|
||||
showMakerTooltip.value = day.value;
|
||||
emit('tooltip-open', day.value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseLeave = (day: UnwrapRef<ICalendarDay>): void => {
|
||||
if (showMakerTooltip.value) {
|
||||
showMakerTooltip.value = null;
|
||||
emit('tooltip-close', day.value);
|
||||
}
|
||||
};
|
||||
|
||||
const onTouchStart = (ev: TouchEvent): void => {
|
||||
touch.value.startX = ev.changedTouches[0].screenX;
|
||||
touch.value.startY = ev.changedTouches[0].screenY;
|
||||
};
|
||||
|
||||
const onTouchEnd = (ev: TouchEvent): void => {
|
||||
touch.value.endX = ev.changedTouches[0].screenX;
|
||||
touch.value.endY = ev.changedTouches[0].screenY;
|
||||
handleTouch();
|
||||
};
|
||||
|
||||
const onTouchMove = (ev: TouchEvent): void => {
|
||||
if (props.vertical && !props.inline) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
const handleTouch = () => {
|
||||
const property = props.vertical ? 'Y' : 'X';
|
||||
if (Math.abs(touch.value[`start${property}`] - touch.value[`end${property}`]) > 10) {
|
||||
emit('handle-swipe', touch.value[`start${property}`] > touch.value[`end${property}`] ? 'right' : 'left');
|
||||
}
|
||||
};
|
||||
|
||||
const assignDayRef = (el: any, weekInd: number, dayInd: number) => {
|
||||
if (el) {
|
||||
if (Array.isArray(dayRefs.value[weekInd])) {
|
||||
dayRefs.value[weekInd][dayInd] = el;
|
||||
} else {
|
||||
dayRefs.value[weekInd] = [el];
|
||||
}
|
||||
}
|
||||
if (props.arrowNavigation) {
|
||||
buildMultiLevelMatrix(dayRefs.value, 'calendar');
|
||||
}
|
||||
};
|
||||
|
||||
const onScroll = (ev: WheelEvent) => {
|
||||
if (props.monthChangeOnScroll) {
|
||||
ev.preventDefault();
|
||||
emit('handle-scroll', ev);
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({ triggerTransition });
|
||||
</script>
|
||||
238
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/DatepickerInput.vue
generated
vendored
Normal file
238
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/DatepickerInput.vue
generated
vendored
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
<template>
|
||||
<div
|
||||
@click="handleOpen"
|
||||
:aria-label="defaults.ariaLabels?.input"
|
||||
role="textbox"
|
||||
aria-multiline="false"
|
||||
:aria-disabled="disabled"
|
||||
:aria-readonly="readonly"
|
||||
>
|
||||
<slot v-if="$slots.trigger && !$slots['dp-input'] && !inline" name="trigger" />
|
||||
<div v-if="!$slots.trigger && (!inline || inlineWithInput)" class="dp__input_wrap">
|
||||
<slot
|
||||
v-if="$slots['dp-input'] && !$slots.trigger && !inline"
|
||||
name="dp-input"
|
||||
:value="inputValue"
|
||||
:on-input="handleInput"
|
||||
:on-enter="handleEnter"
|
||||
:on-tab="handleTab"
|
||||
:on-clear="onClear"
|
||||
/>
|
||||
<input
|
||||
v-if="!$slots['dp-input']"
|
||||
ref="inputRef"
|
||||
:id="uid ? `dp-input-${uid}` : undefined"
|
||||
:name="name"
|
||||
:class="inputClass"
|
||||
:inputmode="textInput ? 'text' : 'none'"
|
||||
:placeholder="placeholder"
|
||||
:disabled="disabled"
|
||||
:readonly="readonly"
|
||||
:required="required"
|
||||
:value="inputValue"
|
||||
:autocomplete="autocomplete"
|
||||
@input="handleInput"
|
||||
@keydown.enter="handleOpen"
|
||||
@keydown.tab="handleTab"
|
||||
@blur="handleBlur"
|
||||
@focus="handleFocus"
|
||||
@keypress="handleKeyPress"
|
||||
/>
|
||||
<span class="dp__input_icon" v-if="$slots['input-icon'] && !hideInputIcon"><slot name="input-icon" /></span>
|
||||
<CalendarIcon
|
||||
v-if="!$slots['input-icon'] && !hideInputIcon && !$slots['dp-input']"
|
||||
class="dp__input_icon dp__input_icons"
|
||||
/>
|
||||
<span
|
||||
class="dp__clear_icon"
|
||||
v-if="$slots['clear-icon'] && inputValue && clearable && !disabled && !readonly"
|
||||
><slot name="clear-icon" :clear="onClear"
|
||||
/></span>
|
||||
<CancelIcon
|
||||
v-if="clearable && !$slots['clear-icon'] && inputValue && !disabled && !readonly"
|
||||
class="dp__clear_icon dp__input_icons"
|
||||
data-test="clear-icon"
|
||||
@click.stop.prevent="onClear"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { CalendarIcon, CancelIcon } from '@/components/Icons';
|
||||
|
||||
import { parseFreeInput } from '@/utils/date-utils';
|
||||
import { useUtils } from '@/components/composables';
|
||||
import { AllProps } from '@/utils/props';
|
||||
|
||||
import type { PropType } from 'vue';
|
||||
import type { DynamicClass } from '@/interfaces';
|
||||
|
||||
const emit = defineEmits([
|
||||
'clear',
|
||||
'open',
|
||||
'update:input-value',
|
||||
'set-input-date',
|
||||
'close',
|
||||
'select-date',
|
||||
'set-empty-date',
|
||||
'toggle',
|
||||
'focus-prev',
|
||||
'focus',
|
||||
'blur',
|
||||
]);
|
||||
|
||||
const props = defineProps({
|
||||
isMenuOpen: { type: Boolean as PropType<boolean>, default: false },
|
||||
inputValue: { type: String as PropType<string>, default: '' },
|
||||
...AllProps,
|
||||
});
|
||||
|
||||
const { getDefaultPattern, isValidDate, defaults } = useUtils(props);
|
||||
|
||||
const parsedDate = ref();
|
||||
const inputRef = ref<HTMLElement | null>(null);
|
||||
const isFocused = ref(false);
|
||||
|
||||
const inputClass = computed(
|
||||
(): DynamicClass => ({
|
||||
dp__pointer: !props.disabled && !props.readonly && !props.textInput,
|
||||
dp__disabled: props.disabled,
|
||||
dp__input_readonly: !props.textInput,
|
||||
dp__input: true,
|
||||
dp__input_icon_pad: !props.hideInputIcon,
|
||||
dp__input_valid: props.state,
|
||||
dp__input_invalid: props.state === false,
|
||||
dp__input_focus: isFocused.value || props.isMenuOpen,
|
||||
dp__input_reg: !props.textInput,
|
||||
[props.inputClassName]: !!props.inputClassName,
|
||||
}),
|
||||
);
|
||||
|
||||
const handleOnEmptyInput = () => {
|
||||
emit('set-input-date', null);
|
||||
if (props.autoApply) {
|
||||
emit('set-empty-date');
|
||||
parsedDate.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
const parser = (value: string): Date | null => {
|
||||
return parseFreeInput(value, defaults.value.textInputOptions?.format || getDefaultPattern());
|
||||
};
|
||||
|
||||
const handleRangeTextInput = (value: string) => {
|
||||
const { rangeSeparator } = defaults.value.textInputOptions;
|
||||
const [dateOne, dateTwo] = value.split(`${rangeSeparator}`);
|
||||
|
||||
if (dateOne) {
|
||||
const parsedDateOne = parser(dateOne.trim());
|
||||
const parsedDateTwo = dateTwo ? parser(dateTwo.trim()) : null;
|
||||
const parsedArr = parsedDateOne && parsedDateTwo ? [parsedDateOne, parsedDateTwo] : [parsedDateOne];
|
||||
parsedDate.value = parsedDateOne ? parsedArr : null;
|
||||
}
|
||||
};
|
||||
|
||||
const parseInput = (value: string) => {
|
||||
if (props.range) {
|
||||
handleRangeTextInput(value);
|
||||
} else {
|
||||
if (props.multiDates) {
|
||||
const dates = value.split(`;`);
|
||||
parsedDate.value = dates.map((val) => parser(val.trim())).filter((val) => val);
|
||||
} else {
|
||||
parsedDate.value = parser(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleInput = (event: Event): void => {
|
||||
const { value } = event.target as HTMLInputElement;
|
||||
|
||||
if (value !== '') {
|
||||
if (defaults.value.textInputOptions?.openMenu && !props.isMenuOpen) {
|
||||
emit('open');
|
||||
}
|
||||
parseInput(value);
|
||||
|
||||
emit('set-input-date', parsedDate.value);
|
||||
} else {
|
||||
handleOnEmptyInput();
|
||||
}
|
||||
emit('update:input-value', value);
|
||||
};
|
||||
|
||||
const handleEnter = (): void => {
|
||||
if (defaults.value.textInputOptions?.enterSubmit && isValidDate(parsedDate.value) && props.inputValue !== '') {
|
||||
emit('set-input-date', parsedDate.value, true);
|
||||
parsedDate.value = null;
|
||||
} else if (defaults.value.textInputOptions?.enterSubmit && props.inputValue === '') {
|
||||
parsedDate.value = null;
|
||||
emit('clear');
|
||||
}
|
||||
};
|
||||
|
||||
const handleTab = (): void => {
|
||||
if (defaults.value.textInputOptions?.tabSubmit && isValidDate(parsedDate.value) && props.inputValue !== '') {
|
||||
emit('set-input-date', parsedDate.value, true);
|
||||
parsedDate.value = null;
|
||||
} else if (defaults.value.textInputOptions?.tabSubmit && props.inputValue === '') {
|
||||
parsedDate.value = null;
|
||||
emit('clear');
|
||||
}
|
||||
};
|
||||
|
||||
const handleFocus = (): void => {
|
||||
isFocused.value = true;
|
||||
emit('focus');
|
||||
};
|
||||
|
||||
const handleOpen = (ev: KeyboardEvent | MouseEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopImmediatePropagation();
|
||||
ev.stopPropagation();
|
||||
if (props.textInput && defaults.value.textInputOptions?.openMenu && !props.inlineWithInput) {
|
||||
if (!props.isMenuOpen) {
|
||||
emit('open');
|
||||
} else if (defaults.value.textInputOptions.enterSubmit) {
|
||||
emit('select-date');
|
||||
}
|
||||
} else if (!props.textInput) {
|
||||
emit('toggle');
|
||||
}
|
||||
};
|
||||
|
||||
const handleBlur = (): void => {
|
||||
isFocused.value = false;
|
||||
if (!props.isMenuOpen) {
|
||||
emit('blur');
|
||||
}
|
||||
if (props.autoApply && props.textInput && parsedDate.value) {
|
||||
emit('set-input-date', parsedDate.value);
|
||||
emit('select-date');
|
||||
parsedDate.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
const onClear = () => {
|
||||
emit('clear');
|
||||
};
|
||||
|
||||
const handleKeyPress = (ev: KeyboardEvent): boolean | void => {
|
||||
if (!props.textInput) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
const focusInput = () => {
|
||||
if (inputRef.value) {
|
||||
inputRef.value.focus({ preventScroll: true });
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
focusInput,
|
||||
});
|
||||
</script>
|
||||
497
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/DatepickerMenu.vue
generated
vendored
Normal file
497
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/DatepickerMenu.vue
generated
vendored
Normal file
|
|
@ -0,0 +1,497 @@
|
|||
<template>
|
||||
<transition appear :name="defaults.transitions?.menuAppear" mode="out-in" :css="!!transitions">
|
||||
<div
|
||||
:id="uid ? `dp-menu-${uid}` : undefined"
|
||||
tabindex="0"
|
||||
ref="dpMenuRef"
|
||||
role="dialog"
|
||||
:class="dpMenuClass"
|
||||
@mouseleave="clearHoverDate"
|
||||
@click="handleDpMenuClick"
|
||||
@keydown.esc="handleEsc"
|
||||
@keydown.left.prevent="handleArrowKey('left')"
|
||||
@keydown.up.prevent="handleArrowKey('up')"
|
||||
@keydown.down.prevent="handleArrowKey('down')"
|
||||
@keydown.right.prevent="handleArrowKey('right')"
|
||||
@keydown="checkShiftKey"
|
||||
>
|
||||
<div :class="disabledReadonlyOverlay" v-if="(disabled || readonly) && inline"></div>
|
||||
<div :class="arrowClass" v-if="!inline && !teleportCenter"></div>
|
||||
<div
|
||||
:class="{
|
||||
dp__menu_content_wrapper:
|
||||
presetRanges?.length || !!$slots['left-sidebar'] || !!$slots['right-sidebar'],
|
||||
}"
|
||||
>
|
||||
<div class="dp__sidebar_left" v-if="$slots['left-sidebar']">
|
||||
<slot name="left-sidebar" />
|
||||
</div>
|
||||
<div class="dp__preset_ranges" v-if="presetRanges?.length">
|
||||
<div
|
||||
v-for="(preset, i) in presetRanges"
|
||||
:key="i"
|
||||
:style="preset.style || {}"
|
||||
class="dp__preset_range"
|
||||
@click="presetDateRange(preset.range, !!preset.slot)"
|
||||
>
|
||||
<template v-if="preset.slot">
|
||||
<slot
|
||||
:name="preset.slot"
|
||||
:preset-date-range="presetDateRange"
|
||||
:label="preset.label"
|
||||
:range="preset.range"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ preset.label }}
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dp__instance_calendar" ref="calendarWrapperRef" role="document">
|
||||
<div :class="menuCalendarClassWrapper">
|
||||
<div v-for="(instance, i) in calendarAmm" :key="instance" :class="calendarInstanceClassWrapper">
|
||||
<component
|
||||
:is="monthYearComponent ? monthYearComponent : MonthYearPicker"
|
||||
:ref="
|
||||
(el: any) => {
|
||||
if (el) monthYearPickerRefs[i] = el;
|
||||
}
|
||||
"
|
||||
v-if="!disableMonthYearSelect && !timePicker"
|
||||
:months="months"
|
||||
:years="years"
|
||||
:month="month(instance)"
|
||||
:year="year(instance)"
|
||||
:instance="instance"
|
||||
:internal-model-value="internalModelValue"
|
||||
v-bind="$props"
|
||||
@mount="childMount('monthYearInput')"
|
||||
@reset-flow="resetFlow"
|
||||
@update-month-year="updateMonthYear(instance, $event)"
|
||||
@month-year-select="monthYearSelect"
|
||||
@overlay-closed="focusMenu"
|
||||
>
|
||||
<template v-for="(slot, j) in monthYearSlots" #[slot]="args" :key="j">
|
||||
<slot :name="slot" v-bind="args" />
|
||||
</template>
|
||||
</component>
|
||||
<Calendar
|
||||
:ref="
|
||||
(el: any) => {
|
||||
if (el) calendarRefs[i] = el;
|
||||
}
|
||||
"
|
||||
:specific-mode="specificMode"
|
||||
:get-week-num="getWeekNum"
|
||||
:instance="instance"
|
||||
:mapped-dates="mappedDates(instance)"
|
||||
:month="month(instance)"
|
||||
:year="year(instance)"
|
||||
v-bind="$props"
|
||||
v-model:flow-step="flowStep"
|
||||
@select-date="selectDate($event, !isFirstInstance(instance))"
|
||||
@handle-space="handleSpace($event, !isFirstInstance(instance))"
|
||||
@set-hover-date="setHoverDate($event)"
|
||||
@handle-scroll="handleScroll($event, instance)"
|
||||
@handle-swipe="handleSwipe($event, instance)"
|
||||
@mount="childMount('calendar')"
|
||||
@reset-flow="resetFlow"
|
||||
@tooltip-open="$emit('tooltip-open', $event)"
|
||||
@tooltip-close="$emit('tooltip-close', $event)"
|
||||
>
|
||||
<template v-for="(slot, j) in calendarSlots" #[slot]="args" :key="j">
|
||||
<slot :name="slot" v-bind="{ ...args }" />
|
||||
</template>
|
||||
</Calendar>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<template v-if="$slots['time-picker']">
|
||||
<slot name="time-picker" v-bind="{ time, updateTime }" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<component
|
||||
v-if="enableTimePicker && !monthPicker && !weekPicker"
|
||||
:is="timePickerComponent ? timePickerComponent : TimePickerCmp"
|
||||
ref="timePickerRef"
|
||||
:hours="time.hours"
|
||||
:minutes="time.minutes"
|
||||
:seconds="time.seconds"
|
||||
:internal-model-value="internalModelValue"
|
||||
v-bind="$props"
|
||||
@mount="childMount('timePicker')"
|
||||
@update:hours="updateTime($event)"
|
||||
@update:minutes="updateTime($event, false)"
|
||||
@update:seconds="updateTime($event, false, true)"
|
||||
@reset-flow="resetFlow"
|
||||
@overlay-closed="focusMenu"
|
||||
>
|
||||
<template v-for="(slot, i) in timePickerSlots" #[slot]="args" :key="i">
|
||||
<slot :name="slot" v-bind="args" />
|
||||
</template>
|
||||
</component>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dp__sidebar_right" v-if="$slots['right-sidebar']">
|
||||
<slot name="right-sidebar" />
|
||||
</div>
|
||||
<div class="dp__now_wrap" v-if="showNowButton">
|
||||
<slot name="now-button" v-if="$slots['now-button']" :select-current-date="selectCurrentDate" />
|
||||
<button
|
||||
v-if="!$slots['now-button']"
|
||||
type="button"
|
||||
role="button"
|
||||
class="dp__now_button"
|
||||
@click="selectCurrentDate"
|
||||
>
|
||||
{{ nowButtonLabel }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<component
|
||||
v-if="!autoApply || keepActionRow"
|
||||
:is="actionRowComponent ? actionRowComponent : ActionRow"
|
||||
:menu-mount="menuMount"
|
||||
:calendar-width="calendarWidth"
|
||||
:internal-model-value="internalModelValue"
|
||||
v-bind="$props"
|
||||
@close-picker="$emit('close-picker')"
|
||||
@select-date="$emit('select-date')"
|
||||
@invalid-select="$emit('invalid-select')"
|
||||
>
|
||||
<template v-for="(slot, i) in actionSlots" #[slot]="args" :key="i">
|
||||
<slot :name="slot" v-bind="{ ...args }" />
|
||||
</template>
|
||||
</component>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, onUnmounted, reactive, ref, useSlots, watch } from 'vue';
|
||||
|
||||
import ActionRow from '@/components/ActionRow.vue';
|
||||
import Calendar from '@/components/Calendar.vue';
|
||||
import MonthYearPicker from '@/components/MonthYearPicker/MonthYearPicker.vue';
|
||||
import TimePickerCmp from '@/components/TimePicker/TimePicker.vue';
|
||||
|
||||
import { useCalendar, mapSlots, useArrowNavigation, useState, useUtils } from '@/components/composables';
|
||||
import { getMonths, getYears, unrefElement } from '@/utils/util';
|
||||
import { useCalendarClass } from '@/components/composables/calendar-class';
|
||||
import { AllProps } from '@/utils/props';
|
||||
|
||||
import type {
|
||||
CalendarRef,
|
||||
DynamicClass,
|
||||
Flow,
|
||||
ICalendarDate,
|
||||
IDefaultSelect,
|
||||
MenuChildCmp,
|
||||
MonthYearPickerRef,
|
||||
TimePickerRef,
|
||||
ICalendarDay,
|
||||
InternalModuleValue,
|
||||
} from '@/interfaces';
|
||||
import type { ComputedRef, PropType, Ref, UnwrapRef } from 'vue';
|
||||
|
||||
const emit = defineEmits([
|
||||
'close-picker',
|
||||
'select-date',
|
||||
'auto-apply',
|
||||
'time-update',
|
||||
'flow-step',
|
||||
'update-month-year',
|
||||
'invalid-select',
|
||||
'update:internal-model-value',
|
||||
'recalculate-position',
|
||||
'invalid-fixed-range',
|
||||
'tooltip-open',
|
||||
'tooltip-close',
|
||||
]);
|
||||
const props = defineProps({
|
||||
openOnTop: { type: Boolean as PropType<boolean>, default: false },
|
||||
internalModelValue: { type: [Date, Array] as PropType<InternalModuleValue>, default: null },
|
||||
...AllProps,
|
||||
});
|
||||
|
||||
const { setMenuFocused, setShiftKey, control } = useState();
|
||||
const { getCalendarDays, defaults } = useUtils(props);
|
||||
const slots = useSlots();
|
||||
|
||||
const calendarWrapperRef = ref(null);
|
||||
const childrenMounted = reactive({
|
||||
timePicker: !!(!props.enableTimePicker || props.timePicker || props.monthPicker),
|
||||
monthYearInput: !!props.timePicker,
|
||||
calendar: false,
|
||||
});
|
||||
const monthYearPickerRefs = ref<MonthYearPickerRef[]>([]);
|
||||
const calendarRefs = ref<CalendarRef[]>([]);
|
||||
const timePickerRef = ref<TimePickerRef | null>(null);
|
||||
const dpMenuRef = ref(null);
|
||||
const calendarWidth = ref(0);
|
||||
const menuMount = ref(false);
|
||||
const flowStep = ref(0);
|
||||
|
||||
onMounted(() => {
|
||||
menuMount.value = true;
|
||||
if (!props.presetRanges?.length && !slots['left-sidebar'] && !slots['right-sidebar']) {
|
||||
getCalendarWidth();
|
||||
}
|
||||
|
||||
const menu = unrefElement(dpMenuRef);
|
||||
if (menu && !props.textInput && !props.inline) {
|
||||
setMenuFocused(true);
|
||||
focusMenu();
|
||||
}
|
||||
if (menu) {
|
||||
const stopDefault = (event: Event) => {
|
||||
if (!props.monthYearComponent && !props.timePickerComponent && !Object.keys(slots).length) {
|
||||
event.preventDefault();
|
||||
}
|
||||
event.stopImmediatePropagation();
|
||||
event.stopPropagation();
|
||||
};
|
||||
menu.addEventListener('pointerdown', stopDefault);
|
||||
menu.addEventListener('mousedown', stopDefault);
|
||||
}
|
||||
window.addEventListener('resize', getCalendarWidth);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', getCalendarWidth);
|
||||
});
|
||||
|
||||
const { arrowRight, arrowLeft, arrowDown, arrowUp } = useArrowNavigation();
|
||||
|
||||
const triggerCalendarTransition = (instance?: number): void => {
|
||||
if (instance || instance === 0) {
|
||||
(calendarRefs.value[instance] as unknown as CalendarRef).triggerTransition(
|
||||
month.value(instance),
|
||||
year.value(instance),
|
||||
);
|
||||
} else {
|
||||
calendarRefs.value.forEach((refVal, i) =>
|
||||
(refVal as unknown as CalendarRef).triggerTransition(month.value(i), year.value(i)),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const focusMenu = (): void => {
|
||||
const menu = unrefElement(dpMenuRef);
|
||||
if (menu) {
|
||||
menu.focus({ preventScroll: true });
|
||||
}
|
||||
};
|
||||
|
||||
const updateFlowStep = (): void => {
|
||||
if (props.flow?.length && flowStep.value !== -1) {
|
||||
flowStep.value += 1;
|
||||
emit('flow-step', flowStep.value);
|
||||
handleFlow();
|
||||
}
|
||||
};
|
||||
|
||||
const resetFlow = (): void => {
|
||||
flowStep.value = -1;
|
||||
};
|
||||
|
||||
const {
|
||||
calendars,
|
||||
modelValue,
|
||||
month,
|
||||
year,
|
||||
time,
|
||||
updateTime,
|
||||
updateMonthYear,
|
||||
selectDate,
|
||||
getWeekNum,
|
||||
monthYearSelect,
|
||||
handleScroll,
|
||||
handleArrow,
|
||||
handleSwipe,
|
||||
getMarker,
|
||||
selectCurrentDate,
|
||||
presetDateRange,
|
||||
} = useCalendar(props, emit, updateFlowStep, triggerCalendarTransition, flowStep);
|
||||
|
||||
const { setHoverDate, clearHoverDate, getDayClassData } = useCalendarClass(modelValue, props);
|
||||
|
||||
watch(
|
||||
calendars,
|
||||
() => {
|
||||
if (props.openOnTop) {
|
||||
setTimeout(() => {
|
||||
emit('recalculate-position');
|
||||
}, 0);
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
const calendarSlots = mapSlots(slots, 'calendar');
|
||||
const actionSlots = mapSlots(slots, 'action');
|
||||
const timePickerSlots = mapSlots(slots, 'timePicker');
|
||||
const monthYearSlots = mapSlots(slots, 'monthYear');
|
||||
|
||||
const arrowClass = computed(() => (!props.openOnTop ? 'dp__arrow_top' : 'dp__arrow_bottom'));
|
||||
|
||||
// Generate array of years depending on provided range that will be available for picker
|
||||
const years = computed((): IDefaultSelect[] => {
|
||||
return getYears(props.yearRange);
|
||||
});
|
||||
|
||||
// Get generated months
|
||||
const months = computed((): IDefaultSelect[] => {
|
||||
return getMonths(props.locale, props.monthNameFormat);
|
||||
});
|
||||
|
||||
const getCalendarWidth = (): void => {
|
||||
const el = unrefElement(calendarWrapperRef);
|
||||
if (el) {
|
||||
calendarWidth.value = el.getBoundingClientRect().width;
|
||||
}
|
||||
};
|
||||
|
||||
// Get dates for the currently selected month and year
|
||||
const dates = computed(() => (instance: number) => getCalendarDays(month.value(instance), year.value(instance)));
|
||||
|
||||
const calendarAmm = computed((): number[] =>
|
||||
defaults.value.multiCalendars > 0 && props.range ? [...Array(defaults.value.multiCalendars).keys()] : [0],
|
||||
);
|
||||
|
||||
const isFirstInstance = computed(
|
||||
(): ((instance: number) => boolean) =>
|
||||
(instance): boolean =>
|
||||
instance === 1,
|
||||
);
|
||||
|
||||
// If datepicker is using only month or time picker
|
||||
const specificMode = computed((): boolean => props.monthPicker || props.timePicker || props.yearPicker);
|
||||
|
||||
const menuCalendarClassWrapper = computed(
|
||||
(): DynamicClass => ({
|
||||
dp__flex_display: defaults.value.multiCalendars > 0,
|
||||
}),
|
||||
);
|
||||
|
||||
const calendarInstanceClassWrapper = computed(() => ({
|
||||
dp__instance_calendar: defaults.value.multiCalendars > 0,
|
||||
}));
|
||||
|
||||
const disabledReadonlyOverlay = computed(() => ({
|
||||
dp__menu_disabled: props.disabled,
|
||||
dp__menu_readonly: props.readonly,
|
||||
}));
|
||||
/**
|
||||
* Array of the dates from which calendar is built.
|
||||
* It also sets classes depending on picker modes, active dates, today, v-model.
|
||||
*/
|
||||
const mappedDates = computed(
|
||||
() =>
|
||||
(instance: number): ICalendarDate[] =>
|
||||
mapDates(dates, instance),
|
||||
);
|
||||
|
||||
const dpMenuClass = computed(
|
||||
(): DynamicClass => ({
|
||||
dp__menu: true,
|
||||
dp__menu_index: !props.inline,
|
||||
dp__relative: props.inline,
|
||||
[props.menuClassName]: !!props.menuClassName,
|
||||
}),
|
||||
);
|
||||
|
||||
// Map days data that will be displayed in the calendar component
|
||||
const mapDates = (dates: ComputedRef<(instance: number) => ICalendarDate[]>, instance: number): ICalendarDate[] => {
|
||||
return dates.value(instance).map((date) => {
|
||||
return {
|
||||
...date,
|
||||
days: date.days.map((calendarDay) => {
|
||||
calendarDay.marker = getMarker(calendarDay);
|
||||
calendarDay.classData = getDayClassData(calendarDay);
|
||||
return calendarDay;
|
||||
}),
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const handleDpMenuClick = (e: Event) => {
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
};
|
||||
|
||||
const handleEsc = (): void => {
|
||||
if (props.escClose) {
|
||||
emit('close-picker');
|
||||
}
|
||||
};
|
||||
|
||||
const handleSpace = (day: UnwrapRef<ICalendarDay>, isNext = false): void => {
|
||||
selectDate(day, isNext);
|
||||
if (props.spaceConfirm) {
|
||||
emit('select-date');
|
||||
}
|
||||
};
|
||||
|
||||
const childMount = (cmp: MenuChildCmp): void => {
|
||||
if (props.flow?.length) {
|
||||
childrenMounted[cmp] = true;
|
||||
|
||||
if (!Object.keys(childrenMounted).filter((key) => !childrenMounted[key as MenuChildCmp]).length) {
|
||||
handleFlow();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleFlowStep = (step: Flow, refVal: Ref, fn: string, multi: boolean, ...args: Array<boolean | string>) => {
|
||||
if (props.flow[flowStep.value] === step) {
|
||||
const cmpRef = multi ? refVal.value[0] : refVal.value;
|
||||
if (cmpRef) {
|
||||
cmpRef[fn](...args);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleFlow = (): void => {
|
||||
handleFlowStep('month', monthYearPickerRefs, 'toggleMonthPicker', true, true);
|
||||
handleFlowStep('year', monthYearPickerRefs, 'toggleYearPicker', true, true);
|
||||
handleFlowStep('calendar', timePickerRef, 'toggleTimePicker', false, false, true);
|
||||
handleFlowStep('time', timePickerRef, 'toggleTimePicker', false, true, true);
|
||||
|
||||
const flowValue = props.flow[flowStep.value];
|
||||
if (flowValue === 'hours' || flowValue === 'minutes' || flowValue === 'seconds') {
|
||||
handleFlowStep(flowValue, timePickerRef, 'toggleTimePicker', false, true, true, flowValue);
|
||||
}
|
||||
};
|
||||
|
||||
const handleArrowKey = (arrow: 'up' | 'down' | 'left' | 'right'): void => {
|
||||
if (props.arrowNavigation) {
|
||||
if (arrow === 'up') return arrowUp();
|
||||
if (arrow === 'down') return arrowDown();
|
||||
if (arrow === 'left') return arrowLeft();
|
||||
if (arrow === 'right') return arrowRight();
|
||||
} else {
|
||||
if (arrow === 'left' || arrow === 'up') {
|
||||
handleArrow('left', 0, arrow === 'up');
|
||||
} else {
|
||||
handleArrow('right', 0, arrow === 'down');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const checkShiftKey = (ev: KeyboardEvent) => {
|
||||
setShiftKey(ev.shiftKey);
|
||||
if (!props.disableMonthYearSelect && ev.code === 'Tab') {
|
||||
if ((ev.target as HTMLElement).classList.contains('dp__menu') && control.value.shiftKeyInMenu) {
|
||||
ev.preventDefault();
|
||||
ev.stopImmediatePropagation();
|
||||
emit('close-picker');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
updateMonthYear,
|
||||
});
|
||||
</script>
|
||||
18
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/Icons/CalendarIcon.vue
generated
vendored
Normal file
18
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/Icons/CalendarIcon.vue
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<template>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" class="dp__icon">
|
||||
<path
|
||||
d="M29.333 8c0-2.208-1.792-4-4-4h-18.667c-2.208 0-4 1.792-4 4v18.667c0 2.208 1.792 4 4 4h18.667c2.208 0 4-1.792 4-4v-18.667zM26.667 8v18.667c0 0.736-0.597 1.333-1.333 1.333 0 0-18.667 0-18.667 0-0.736 0-1.333-0.597-1.333-1.333 0 0 0-18.667 0-18.667 0-0.736 0.597-1.333 1.333-1.333 0 0 18.667 0 18.667 0 0.736 0 1.333 0.597 1.333 1.333z"
|
||||
></path>
|
||||
<path
|
||||
d="M20 2.667v5.333c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v-5.333c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333z"
|
||||
></path>
|
||||
<path
|
||||
d="M9.333 2.667v5.333c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v-5.333c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333z"
|
||||
></path>
|
||||
<path
|
||||
d="M4 14.667h24c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333h-24c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333z"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup></script>
|
||||
12
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/Icons/CancelIcon.vue
generated
vendored
Normal file
12
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/Icons/CancelIcon.vue
generated
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<template>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" class="dp__icon">
|
||||
<path
|
||||
d="M23.057 7.057l-16 16c-0.52 0.52-0.52 1.365 0 1.885s1.365 0.52 1.885 0l16-16c0.52-0.52 0.52-1.365 0-1.885s-1.365-0.52-1.885 0z"
|
||||
></path>
|
||||
<path
|
||||
d="M7.057 8.943l16 16c0.52 0.52 1.365 0.52 1.885 0s0.52-1.365 0-1.885l-16-16c-0.52-0.52-1.365-0.52-1.885 0s-0.52 1.365 0 1.885z"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup></script>
|
||||
9
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/Icons/ChevronDownIcon.vue
generated
vendored
Normal file
9
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/Icons/ChevronDownIcon.vue
generated
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<template>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" class="dp__icon">
|
||||
<path
|
||||
d="M7.057 12.943l8 8c0.521 0.521 1.365 0.521 1.885 0l8-8c0.52-0.52 0.52-1.365 0-1.885s-1.365-0.52-1.885 0l-7.057 7.057c0 0-7.057-7.057-7.057-7.057-0.52-0.52-1.365-0.52-1.885 0s-0.52 1.365 0 1.885z"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup></script>
|
||||
9
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/Icons/ChevronLeftIcon.vue
generated
vendored
Normal file
9
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/Icons/ChevronLeftIcon.vue
generated
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<template>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" class="dp__icon">
|
||||
<path
|
||||
d="M20.943 23.057l-7.057-7.057c0 0 7.057-7.057 7.057-7.057 0.52-0.52 0.52-1.365 0-1.885s-1.365-0.52-1.885 0l-8 8c-0.521 0.521-0.521 1.365 0 1.885l8 8c0.52 0.52 1.365 0.52 1.885 0s0.52-1.365 0-1.885z"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup></script>
|
||||
9
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/Icons/ChevronRightIcon.vue
generated
vendored
Normal file
9
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/Icons/ChevronRightIcon.vue
generated
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<template>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" class="dp__icon">
|
||||
<path
|
||||
d="M12.943 24.943l8-8c0.521-0.521 0.521-1.365 0-1.885l-8-8c-0.52-0.52-1.365-0.52-1.885 0s-0.52 1.365 0 1.885l7.057 7.057c0 0-7.057 7.057-7.057 7.057-0.52 0.52-0.52 1.365 0 1.885s1.365 0.52 1.885 0z"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup></script>
|
||||
9
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/Icons/ChevronUpIcon.vue
generated
vendored
Normal file
9
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/Icons/ChevronUpIcon.vue
generated
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<template>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" class="dp__icon">
|
||||
<path
|
||||
d="M24.943 19.057l-8-8c-0.521-0.521-1.365-0.521-1.885 0l-8 8c-0.52 0.52-0.52 1.365 0 1.885s1.365 0.52 1.885 0l7.057-7.057c0 0 7.057 7.057 7.057 7.057 0.52 0.52 1.365 0.52 1.885 0s0.52-1.365 0-1.885z"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup></script>
|
||||
12
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/Icons/ClockIcon.vue
generated
vendored
Normal file
12
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/Icons/ClockIcon.vue
generated
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<template>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" class="dp__icon">
|
||||
<path
|
||||
d="M16 1.333c-8.095 0-14.667 6.572-14.667 14.667s6.572 14.667 14.667 14.667c8.095 0 14.667-6.572 14.667-14.667s-6.572-14.667-14.667-14.667zM16 4c6.623 0 12 5.377 12 12s-5.377 12-12 12c-6.623 0-12-5.377-12-12s5.377-12 12-12z"
|
||||
></path>
|
||||
<path
|
||||
d="M14.667 8v8c0 0.505 0.285 0.967 0.737 1.193l5.333 2.667c0.658 0.329 1.46 0.062 1.789-0.596s0.062-1.46-0.596-1.789l-4.596-2.298c0 0 0-7.176 0-7.176 0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333z"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup></script>
|
||||
7
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/Icons/index.ts
generated
vendored
Normal file
7
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/Icons/index.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
export { default as CalendarIcon } from './CalendarIcon.vue';
|
||||
export { default as CancelIcon } from './CancelIcon.vue';
|
||||
export { default as ChevronLeftIcon } from './ChevronLeftIcon.vue';
|
||||
export { default as ChevronRightIcon } from './ChevronRightIcon.vue';
|
||||
export { default as ClockIcon } from './ClockIcon.vue';
|
||||
export { default as ChevronUpIcon } from './ChevronUpIcon.vue';
|
||||
export { default as ChevronDownIcon } from './ChevronDownIcon.vue';
|
||||
32
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/MonthYearPicker/ActionIcon.vue
generated
vendored
Normal file
32
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/MonthYearPicker/ActionIcon.vue
generated
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<template>
|
||||
<div
|
||||
class="dp__month_year_col_nav"
|
||||
@click="$emit('activate')"
|
||||
@keydown.enter="$emit('activate')"
|
||||
@keydown.space="$emit('activate')"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
:aria-label="ariaLabel"
|
||||
:aria-disabled="disabled"
|
||||
ref="elRef"
|
||||
>
|
||||
<div class="dp__inner_nav" :class="{ dp__inner_nav_disabled: disabled }">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import type { PropType } from 'vue';
|
||||
|
||||
const emit = defineEmits(['activate', 'set-ref']);
|
||||
defineProps({
|
||||
ariaLabel: { type: String as PropType<string>, default: '' },
|
||||
disabled: { type: Boolean as PropType<boolean>, default: false },
|
||||
});
|
||||
const elRef = ref(null);
|
||||
|
||||
onMounted(() => emit('set-ref', elRef));
|
||||
</script>
|
||||
428
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/MonthYearPicker/MonthYearPicker.vue
generated
vendored
Normal file
428
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/MonthYearPicker/MonthYearPicker.vue
generated
vendored
Normal file
|
|
@ -0,0 +1,428 @@
|
|||
<template>
|
||||
<div class="dp__month_year_row">
|
||||
<template v-if="$slots['month-year']">
|
||||
<slot
|
||||
name="month-year"
|
||||
v-bind="{ month, year, months, years, updateMonthYear, handleMonthYearChange, instance }"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="!monthPicker && !yearPicker">
|
||||
<ActionIcon
|
||||
v-if="showLeftIcon && !vertical"
|
||||
:aria-label="defaults.ariaLabels?.prevMonth"
|
||||
:disabled="isDisabled(false)"
|
||||
@activate="handleMonthYearChange(false)"
|
||||
@set-ref="setElRefs($event, 0)"
|
||||
>
|
||||
<slot name="arrow-left" v-if="$slots['arrow-left']" />
|
||||
<ChevronLeftIcon v-if="!$slots['arrow-left']" />
|
||||
</ActionIcon>
|
||||
<div class="dp__month_year_wrap">
|
||||
<RegularPicker
|
||||
type="month"
|
||||
slot-name="month-overlay-val"
|
||||
overlay-slot="overlay-month"
|
||||
:aria-label="defaults.ariaLabels?.openMonthsOverlay"
|
||||
v-model="monthModelBind"
|
||||
v-bind="childProps('month')"
|
||||
@toggle="toggleMonthPicker"
|
||||
@set-ref="setElRefs($event, 1)"
|
||||
>
|
||||
<slot v-if="$slots.month" name="month" v-bind="getMonthDisplayVal" />
|
||||
<template v-if="!$slots.month">{{ getMonthDisplayVal.text }}</template>
|
||||
<template #calendar-icon v-if="$slots['calendar-icon']">
|
||||
<slot name="calendar-icon" />
|
||||
</template>
|
||||
<template v-if="$slots['month-overlay-value']" #month-overlay-val="{ item }: any">
|
||||
<slot name="month-overlay-value" :text="item.text" :value="item.value" />
|
||||
</template>
|
||||
<template v-if="$slots['month-overlay']" #overlay-month>
|
||||
<slot name="month-overlay" v-bind="overlaySlotProps('month')" />
|
||||
</template>
|
||||
<template v-if="$slots['month-overlay-header']" #overlay-month-header>
|
||||
<slot name="month-overlay-header" :toggle="toggleMonthPicker" />
|
||||
</template>
|
||||
</RegularPicker>
|
||||
<RegularPicker
|
||||
type="year"
|
||||
slot-name="year-overlay-val"
|
||||
overlay-slot="overlay-year"
|
||||
:aria-label="defaults.ariaLabels?.openYearsOverlay"
|
||||
v-model="yearModelBind"
|
||||
v-bind="childProps('year')"
|
||||
@toggle="toggleYearPicker"
|
||||
@set-ref="setElRefs($event, 2)"
|
||||
>
|
||||
<slot v-if="$slots.year" name="year" :year="year" />
|
||||
<template v-if="!$slots.year">{{ year }}</template>
|
||||
<template #calendar-icon v-if="$slots['calendar-icon']">
|
||||
<slot name="calendar-icon" />
|
||||
</template>
|
||||
<template v-if="$slots['year-overlay-value']" #year-overlay-val="{ item }: any">
|
||||
<slot name="year-overlay-value" :text="item.text" :value="item.value" />
|
||||
</template>
|
||||
<template v-if="$slots['year-overlay']" #overlay-year>
|
||||
<slot name="year-overlay" v-bind="overlaySlotProps('year')" />
|
||||
</template>
|
||||
<template v-if="$slots['year-overlay-header']" #overlay-year-header>
|
||||
<slot name="year-overlay-header" :toggle="toggleYearPicker" />
|
||||
</template>
|
||||
</RegularPicker>
|
||||
</div>
|
||||
<ActionIcon
|
||||
v-if="showLeftIcon && vertical"
|
||||
:aria-label="defaults.ariaLabels?.prevMonth"
|
||||
:disabled="isDisabled(false)"
|
||||
@activate="handleMonthYearChange(false)"
|
||||
>
|
||||
<slot name="arrow-up" v-if="$slots['arrow-up']" />
|
||||
<ChevronUpIcon v-if="!$slots['arrow-up']" />
|
||||
</ActionIcon>
|
||||
<ActionIcon
|
||||
v-if="showRightIcon"
|
||||
ref="rightIcon"
|
||||
:disabled="isDisabled(true)"
|
||||
:aria-label="defaults.ariaLabels?.nextMonth"
|
||||
@activate="handleMonthYearChange(true)"
|
||||
@set-ref="setElRefs($event, 3)"
|
||||
>
|
||||
<slot
|
||||
:name="vertical ? 'arrow-down' : 'arrow-right'"
|
||||
v-if="$slots[vertical ? 'arrow-down' : 'arrow-right']"
|
||||
/>
|
||||
<component
|
||||
:is="vertical ? ChevronDownIcon : ChevronRightIcon"
|
||||
v-if="!$slots[vertical ? 'arrow-down' : 'arrow-right']"
|
||||
/>
|
||||
</ActionIcon>
|
||||
</template>
|
||||
<template v-if="monthPicker">
|
||||
<SelectionGrid
|
||||
v-bind="childProps('month')"
|
||||
:skip-active="range"
|
||||
:year="year"
|
||||
:multi-model-value="multiModelValue"
|
||||
month-picker
|
||||
v-model="monthModelBind"
|
||||
@toggle="toggleMonthPicker"
|
||||
@selected="$emit('overlay-closed')"
|
||||
>
|
||||
<template v-if="$slots['month-overlay-value']" #item="{ item }">
|
||||
<slot name="month-overlay-value" :text="item.text" :value="item.value" />
|
||||
</template>
|
||||
<template #header>
|
||||
<div class="dp__month_picker_header">
|
||||
<div
|
||||
class="dp__month_year_col_nav"
|
||||
tabindex="0"
|
||||
ref="mpPrevIconRef"
|
||||
@click="handleYear(false)"
|
||||
@keydown.enter="handleYear(false)"
|
||||
>
|
||||
<div
|
||||
class="dp__inner_nav"
|
||||
:class="{ dp__inner_nav_disabled: isDisabled(false) }"
|
||||
role="button"
|
||||
:aria-label="defaults.ariaLabels?.prevMonth"
|
||||
>
|
||||
<slot name="arrow-left" v-if="$slots['arrow-left']" />
|
||||
<ChevronLeftIcon v-if="!$slots['arrow-left']" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="dp__pointer"
|
||||
role="button"
|
||||
ref="mpYearButtonRef"
|
||||
:aria-label="defaults.ariaLabels?.openYearsOverlay"
|
||||
tabindex="0"
|
||||
@click="() => toggleYearPicker(false)"
|
||||
@keydown.enter="() => toggleYearPicker(false)"
|
||||
>
|
||||
<slot v-if="$slots.year" name="year" :year="year" />
|
||||
<template v-if="!$slots.year">{{ year }}</template>
|
||||
</div>
|
||||
<div
|
||||
class="dp__month_year_col_nav"
|
||||
tabindex="0"
|
||||
ref="mpNextIconRef"
|
||||
@click="handleYear(true)"
|
||||
@keydown.enter="handleYear(true)"
|
||||
>
|
||||
<div
|
||||
class="dp__inner_nav"
|
||||
:class="{ dp__inner_nav_disabled: isDisabled(true) }"
|
||||
role="button"
|
||||
:aria-label="defaults.ariaLabels?.nextMonth"
|
||||
>
|
||||
<slot name="arrow-right" v-if="$slots['arrow-right']" />
|
||||
<ChevronRightIcon v-if="!$slots['arrow-right']" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<transition :name="transitionName(showYearPicker)" :css="showTransition">
|
||||
<SelectionGrid
|
||||
v-if="showYearPicker"
|
||||
v-bind="childProps('year')"
|
||||
v-model="yearModelBind"
|
||||
@toggle="toggleYearPicker"
|
||||
@selected="$emit('overlay-closed')"
|
||||
><template #button-icon>
|
||||
<slot name="calendar-icon" v-if="$slots['calendar-icon']" />
|
||||
<CalendarIcon v-if="!$slots['calendar-icon']" />
|
||||
</template>
|
||||
<template v-if="$slots['year-overlay-value']" #item="{ item }">
|
||||
<slot name="year-overlay-value" :text="item.text" :value="item.value" />
|
||||
</template>
|
||||
</SelectionGrid>
|
||||
</transition>
|
||||
</template>
|
||||
</SelectionGrid>
|
||||
</template>
|
||||
<template v-if="yearPicker">
|
||||
<SelectionGrid
|
||||
v-bind="childProps('year')"
|
||||
v-model="yearModelBind"
|
||||
:multi-model-value="multiModelValue"
|
||||
:skip-active="range"
|
||||
skip-button-ref
|
||||
year-picker
|
||||
@toggle="toggleYearPicker"
|
||||
@selected="$emit('overlay-closed')"
|
||||
>
|
||||
<template v-if="$slots['year-overlay-value']" #item="{ item }">
|
||||
<slot name="year-overlay-value" :text="item.text" :value="item.value" />
|
||||
</template>
|
||||
</SelectionGrid>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { getMonth, getYear } from 'date-fns';
|
||||
|
||||
import {
|
||||
ChevronLeftIcon,
|
||||
ChevronRightIcon,
|
||||
CalendarIcon,
|
||||
ChevronDownIcon,
|
||||
ChevronUpIcon,
|
||||
} from '@/components/Icons';
|
||||
import ActionIcon from '@/components/MonthYearPicker/ActionIcon.vue';
|
||||
import RegularPicker from '@/components/MonthYearPicker/RegularPicker.vue';
|
||||
import SelectionGrid from '@/components/SelectionGrid.vue';
|
||||
|
||||
import { useMontYearPick, useTransitions, useArrowNavigation, useUtils } from '@/components/composables';
|
||||
import { unrefElement } from '@/utils/util';
|
||||
import { AllProps } from '@/utils/props';
|
||||
|
||||
import type { PropType } from 'vue';
|
||||
import type { IDefaultSelect, InternalModuleValue } from '@/interfaces';
|
||||
import { getDate } from '@/utils/date-utils';
|
||||
|
||||
const emit = defineEmits(['update-month-year', 'month-year-select', 'mount', 'reset-flow', 'overlay-closed']);
|
||||
const props = defineProps({
|
||||
month: { type: Number as PropType<number>, default: 0 },
|
||||
year: { type: Number as PropType<number>, default: 0 },
|
||||
instance: { type: Number as PropType<number>, default: 0 },
|
||||
years: { type: Array as PropType<IDefaultSelect[]>, default: () => [] },
|
||||
months: { type: Array as PropType<IDefaultSelect[]>, default: () => [] },
|
||||
internalModelValue: { type: [Date, Array] as PropType<InternalModuleValue>, default: null },
|
||||
...AllProps,
|
||||
});
|
||||
const { defaults } = useUtils(props);
|
||||
const { transitionName, showTransition } = useTransitions(defaults.value.transitions);
|
||||
const { buildMatrix } = useArrowNavigation();
|
||||
const { handleMonthYearChange, isDisabled, updateMonthYear } = useMontYearPick(props, emit);
|
||||
|
||||
const showMonthPicker = ref(false);
|
||||
const showYearPicker = ref(false);
|
||||
const elementRefs = ref<Array<HTMLElement | null>>([null, null, null, null]);
|
||||
const mpPrevIconRef = ref<HTMLElement | null>(null);
|
||||
const mpYearButtonRef = ref<HTMLElement | null>(null);
|
||||
const mpNextIconRef = ref<HTMLElement | null>(null);
|
||||
|
||||
onMounted(() => {
|
||||
emit('mount');
|
||||
});
|
||||
|
||||
const bindOptions = (type: 'month' | 'year') => ({
|
||||
get: () => props[type],
|
||||
set: (value: number) => {
|
||||
const otherType = type === 'month' ? 'year' : 'month';
|
||||
emit('update-month-year', { [type]: value, [otherType]: props[otherType] });
|
||||
emit('month-year-select', type === 'year');
|
||||
if (type === 'month') {
|
||||
toggleMonthPicker(true);
|
||||
} else {
|
||||
toggleYearPicker(true);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const monthModelBind = computed(bindOptions('month'));
|
||||
const yearModelBind = computed(bindOptions('year'));
|
||||
|
||||
const isMonthDisabled = (date: Date | string): boolean => {
|
||||
const year = getYear(getDate(date));
|
||||
return props.year === year;
|
||||
};
|
||||
|
||||
const getDisabledMonths = computed(() => {
|
||||
if (props.monthPicker) {
|
||||
if (Array.isArray(props.disabledDates)) {
|
||||
return props.disabledDates
|
||||
.map((date) => getDate(date))
|
||||
.filter((date) => isMonthDisabled(date))
|
||||
.map((date) => getMonth(date));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
const childProps = computed(() => (type: 'month' | 'year') => {
|
||||
const isMonth = type === 'month';
|
||||
return {
|
||||
showSelectionGrid: (isMonth ? showMonthPicker : showYearPicker).value,
|
||||
items: (isMonth ? groupedMonths : groupedYears).value,
|
||||
disabledValues: defaults.value.filters[isMonth ? 'months' : 'years'].concat(getDisabledMonths.value),
|
||||
minValue: (isMonth ? minMonth : minYear).value,
|
||||
maxValue: (isMonth ? maxMonth : maxYear).value,
|
||||
headerRefs:
|
||||
isMonth && props.monthPicker ? [mpPrevIconRef.value, mpYearButtonRef.value, mpNextIconRef.value] : [],
|
||||
escClose: props.escClose,
|
||||
transitions: defaults.value.transitions,
|
||||
ariaLabels: defaults.value.ariaLabels,
|
||||
textInput: props.textInput,
|
||||
autoApply: props.autoApply,
|
||||
arrowNavigation: props.arrowNavigation,
|
||||
hideNavigation: props.hideNavigation,
|
||||
};
|
||||
});
|
||||
|
||||
const overlaySlotProps = computed(() => (type: 'month' | 'year') => ({
|
||||
month: props.month,
|
||||
year: props.year,
|
||||
items: type === 'month' ? props.months : props.years,
|
||||
instance: props.instance,
|
||||
updateMonthYear,
|
||||
toggle: type === 'month' ? toggleMonthPicker : toggleYearPicker,
|
||||
}));
|
||||
|
||||
const minYear = computed(() => (props.minDate ? getYear(getDate(props.minDate)) : null));
|
||||
const maxYear = computed(() => (props.maxDate ? getYear(getDate(props.maxDate)) : null));
|
||||
|
||||
const minMonth = computed(() => {
|
||||
if (props.minDate && minYear.value) {
|
||||
if (minYear.value > props.year) return 12;
|
||||
if (minYear.value === props.year) return getMonth(getDate(props.minDate));
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
const maxMonth = computed(() => {
|
||||
if (props.maxDate && maxYear.value) {
|
||||
if (maxYear.value < props.year) return -1;
|
||||
if (maxYear.value === props.year) return getMonth(getDate(props.maxDate));
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
const multiModelValue = computed(() => {
|
||||
return props.range && props.internalModelValue && (props.monthPicker || props.yearPicker)
|
||||
? (props.internalModelValue as Date[])
|
||||
: [];
|
||||
});
|
||||
|
||||
// @SONAR_START@
|
||||
const reverseList = (list: IDefaultSelect[]) => list.reverse();
|
||||
// @SONAR_STOP@
|
||||
|
||||
const getGroupedList = (items: IDefaultSelect[], reverse = false): IDefaultSelect[][] => {
|
||||
const list = [];
|
||||
const setList = (listItems: IDefaultSelect[]) => {
|
||||
return reverse ? reverseList(listItems) : listItems;
|
||||
};
|
||||
for (let i = 0; i < items.length; i += 3) {
|
||||
const listItems = [items[i], items[i + 1], items[i + 2]];
|
||||
list.push(setList(listItems));
|
||||
}
|
||||
return reverse ? list.reverse() : list;
|
||||
};
|
||||
|
||||
const getMonthDisplayVal = computed((): IDefaultSelect => {
|
||||
const month = props.months.find((month) => month.value === props.month);
|
||||
if (month) return month;
|
||||
return { text: '', value: 0 };
|
||||
});
|
||||
|
||||
const groupedMonths = computed((): IDefaultSelect[][] => {
|
||||
return getGroupedList(props.months);
|
||||
});
|
||||
|
||||
const groupedYears = computed((): IDefaultSelect[][] => {
|
||||
return getGroupedList(props.years, props.reverseYears);
|
||||
});
|
||||
|
||||
const showLeftIcon = computed(() => {
|
||||
if (defaults.value.multiCalendars) {
|
||||
return !props.multiCalendarsSolo ? props.instance === 0 : true;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
const showRightIcon = computed((): boolean => {
|
||||
if (defaults.value.multiCalendars) {
|
||||
return !props.multiCalendarsSolo ? props.instance === defaults.value.multiCalendars - 1 : true;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
const toggleMonthPicker = (flow = false): void => {
|
||||
checkFlow(flow);
|
||||
showMonthPicker.value = !showMonthPicker.value;
|
||||
if (!showMonthPicker.value) {
|
||||
emit('overlay-closed');
|
||||
}
|
||||
};
|
||||
|
||||
const toggleYearPicker = (flow = false): void => {
|
||||
checkFlow(flow);
|
||||
showYearPicker.value = !showYearPicker.value;
|
||||
if (!showYearPicker.value) {
|
||||
emit('overlay-closed');
|
||||
}
|
||||
};
|
||||
|
||||
const checkFlow = (flow: boolean): void => {
|
||||
if (!flow) {
|
||||
emit('reset-flow');
|
||||
}
|
||||
};
|
||||
|
||||
const handleYear = (increment = false): void => {
|
||||
if (!isDisabled.value(increment)) {
|
||||
emit('update-month-year', {
|
||||
year: increment ? props.year + 1 : props.year - 1,
|
||||
month: props.month,
|
||||
fromNav: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const setElRefs = (el: HTMLElement, i: number): void => {
|
||||
if (props.arrowNavigation) {
|
||||
elementRefs.value[i] = unrefElement(el);
|
||||
buildMatrix(elementRefs.value, 'monthYear');
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
toggleMonthPicker,
|
||||
toggleYearPicker,
|
||||
});
|
||||
</script>
|
||||
89
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/MonthYearPicker/RegularPicker.vue
generated
vendored
Normal file
89
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/MonthYearPicker/RegularPicker.vue
generated
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
<template>
|
||||
<div
|
||||
class="dp__month_year_select"
|
||||
@click="$emit('toggle')"
|
||||
@keydown.enter="$emit('toggle')"
|
||||
@keydown.space="$emit('toggle')"
|
||||
role="button"
|
||||
:aria-label="ariaLabel"
|
||||
tabindex="0"
|
||||
ref="elRef"
|
||||
>
|
||||
<slot name="default" />
|
||||
</div>
|
||||
<transition :name="transitionName(showSelectionGrid)" :css="showTransition">
|
||||
<SelectionGrid
|
||||
v-if="showSelectionGrid"
|
||||
v-bind="{
|
||||
modelValue,
|
||||
items,
|
||||
disabledValues,
|
||||
minValue,
|
||||
maxValue,
|
||||
escClose,
|
||||
type,
|
||||
arrowNavigation,
|
||||
textInput,
|
||||
autoApply,
|
||||
ariaLabels,
|
||||
hideNavigation,
|
||||
}"
|
||||
:header-refs="[]"
|
||||
@update:model-value="$emit('update:model-value', $event)"
|
||||
@toggle="$emit('toggle')"
|
||||
>
|
||||
<template #button-icon>
|
||||
<slot name="calendar-icon" v-if="$slots['calendar-icon']" />
|
||||
<CalendarIcon v-if="!$slots['calendar-icon']" />
|
||||
</template>
|
||||
<template v-if="$slots[slotName]" #item="{ item }">
|
||||
<slot :name="slotName" :item="item" />
|
||||
</template>
|
||||
<template v-if="$slots[overlaySlot]" #overlay>
|
||||
<slot :name="overlaySlot" />
|
||||
</template>
|
||||
<template v-if="$slots[`${overlaySlot}-header`]" #header>
|
||||
<slot :name="`${overlaySlot}-header`" />
|
||||
</template>
|
||||
</SelectionGrid>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import SelectionGrid from '@/components/SelectionGrid.vue';
|
||||
import { CalendarIcon } from '@/components/Icons';
|
||||
import { useTransitions } from '@/components/composables';
|
||||
|
||||
import type { PropType } from 'vue';
|
||||
import type { IDefaultSelect, Transition, AriaLabels, Flow } from '@/interfaces';
|
||||
|
||||
const emit = defineEmits(['update:model-value', 'toggle', 'set-ref']);
|
||||
const props = defineProps({
|
||||
ariaLabel: { type: String as PropType<string>, default: '' },
|
||||
showSelectionGrid: { type: Boolean as PropType<boolean>, default: false },
|
||||
modelValue: { type: Number as PropType<number>, default: null },
|
||||
items: { type: Array as PropType<IDefaultSelect[][]>, default: () => [] },
|
||||
disabledValues: { type: Array as PropType<number[]>, default: () => [] },
|
||||
minValue: { type: Number as PropType<number | null>, default: null },
|
||||
maxValue: { type: Number as PropType<number | null>, default: null },
|
||||
slotName: { type: String as PropType<string>, default: '' },
|
||||
overlaySlot: { type: String as PropType<string>, default: '' },
|
||||
headerRefs: { type: Array as PropType<(HTMLElement | null)[]>, default: () => [] },
|
||||
escClose: { type: Boolean as PropType<boolean>, default: true },
|
||||
type: { type: String as PropType<'month' | 'year'>, default: null },
|
||||
transitions: { type: [Object, Boolean] as PropType<Transition>, default: false },
|
||||
arrowNavigation: { type: Boolean as PropType<boolean>, default: false },
|
||||
autoApply: { type: Boolean as PropType<boolean>, default: false },
|
||||
textInput: { type: Boolean as PropType<boolean>, default: false },
|
||||
ariaLabels: { type: Object as PropType<AriaLabels>, default: () => ({}) },
|
||||
hideNavigation: { type: Array as PropType<Flow[]>, default: () => [] },
|
||||
});
|
||||
|
||||
const { transitionName, showTransition } = useTransitions(props.transitions);
|
||||
|
||||
const elRef = ref(null);
|
||||
|
||||
onMounted(() => emit('set-ref', elRef));
|
||||
</script>
|
||||
304
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/SelectionGrid.vue
generated
vendored
Normal file
304
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/SelectionGrid.vue
generated
vendored
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
<template>
|
||||
<div ref="gridWrapRef" :class="dpOverlayClass" role="dialog" tabindex="0" @keydown.esc="handleEsc">
|
||||
<div :class="containerClass" ref="containerRef" role="grid" :style="{ height: `${containerHeight}px` }">
|
||||
<div class="dp__selection_grid_header"><slot name="header"></slot></div>
|
||||
<template v-if="$slots.overlay">
|
||||
<slot name="overlay" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="dp__overlay_row" v-for="(row, i) in mappedItems" :key="i" role="row">
|
||||
<div
|
||||
v-for="(col, ind) in row"
|
||||
role="gridcell"
|
||||
:class="cellClassName"
|
||||
:key="col.value"
|
||||
:aria-selected="col.value === modelValue && !disabledValues.includes(col.value)"
|
||||
:aria-disabled="col.className.dp__overlay_cell_disabled"
|
||||
:ref="(el) => assignRef(el, col, i, ind)"
|
||||
tabindex="0"
|
||||
:data-test="col.text"
|
||||
@click="onClick(col.value)"
|
||||
@keydown.enter="onClick(col.value)"
|
||||
@keydown.space="onClick(col.value)"
|
||||
@mouseover="hoverValue = col.value"
|
||||
>
|
||||
<div :class="col.className">
|
||||
<slot v-if="$slots.item" name="item" :item="col" />
|
||||
<template v-if="!$slots.item">{{ col.text }}</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div
|
||||
v-if="$slots['button-icon']"
|
||||
v-show="!hideNavigationButtons(type)"
|
||||
role="button"
|
||||
:aria-label="ariaLabels?.toggleOverlay"
|
||||
:class="actionButtonClass"
|
||||
tabindex="0"
|
||||
ref="toggleButton"
|
||||
@click="toggle"
|
||||
@keydown.enter="toggle"
|
||||
>
|
||||
<slot name="button-icon" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, nextTick, onBeforeUpdate, onMounted, onUnmounted, ref } from 'vue';
|
||||
import { setMonth, setYear } from 'date-fns';
|
||||
|
||||
import { unrefElement, convertType } from '@/utils/util';
|
||||
import { useArrowNavigation, useUtils } from '@/components/composables';
|
||||
import { isDateBetween, isDateEqual } from '@/utils/date-utils';
|
||||
|
||||
import type { PropType } from 'vue';
|
||||
import type { IDefaultSelect, DynamicClass, Flow, AriaLabels } from '@/interfaces';
|
||||
import type { AllPropsType } from '@/utils/props';
|
||||
|
||||
const { setSelectionGrid, buildMultiLevelMatrix, setMonthPicker } = useArrowNavigation();
|
||||
|
||||
const emit = defineEmits(['update:model-value', 'selected', 'toggle', 'reset-flow']);
|
||||
|
||||
const props = defineProps({
|
||||
items: { type: Array as PropType<IDefaultSelect[][]>, default: () => [] },
|
||||
modelValue: { type: [String, Number] as PropType<string | number>, default: null },
|
||||
multiModelValue: { type: Array as PropType<Date[]>, default: () => [] },
|
||||
disabledValues: { type: Array as PropType<number[]>, default: () => [] },
|
||||
minValue: { type: [Number, String] as PropType<number | string | null>, default: null },
|
||||
maxValue: { type: [Number, String] as PropType<number | string | null>, default: null },
|
||||
year: { type: Number as PropType<number>, default: 0 },
|
||||
skipActive: { type: Boolean as PropType<boolean>, default: false },
|
||||
headerRefs: { type: Array as PropType<(HTMLElement | null)[]>, default: () => [] },
|
||||
skipButtonRef: { type: Boolean as PropType<boolean>, default: false },
|
||||
monthPicker: { type: Boolean as PropType<boolean>, default: false },
|
||||
yearPicker: { type: Boolean as PropType<boolean>, default: false },
|
||||
escClose: { type: Boolean as PropType<boolean>, default: true },
|
||||
type: { type: String as PropType<Flow>, default: null },
|
||||
arrowNavigation: { type: Boolean as PropType<boolean>, default: false },
|
||||
autoApply: { type: Boolean as PropType<boolean>, default: false },
|
||||
textInput: { type: Boolean as PropType<boolean>, default: false },
|
||||
ariaLabels: { type: Object as PropType<AriaLabels>, default: () => ({}) },
|
||||
hideNavigation: { type: Array as PropType<Flow[]>, default: () => [] },
|
||||
});
|
||||
|
||||
const { hideNavigationButtons } = useUtils(props as unknown as AllPropsType);
|
||||
|
||||
const scrollable = ref(false);
|
||||
const selectionActiveRef = ref<HTMLElement | null>(null);
|
||||
const gridWrapRef = ref(null);
|
||||
const elementRefs = ref<Array<HTMLElement | null>[]>([]);
|
||||
const hoverValue = ref();
|
||||
const toggleButton = ref<HTMLElement | null>(null);
|
||||
const containerHeight = ref(0);
|
||||
const containerRef = ref<HTMLElement | null>(null);
|
||||
|
||||
onBeforeUpdate(() => {
|
||||
selectionActiveRef.value = null;
|
||||
});
|
||||
|
||||
/**
|
||||
* On mounted hook, set the scroll position, if any to a selected value when opening overlay
|
||||
*/
|
||||
onMounted(() => {
|
||||
nextTick().then(() => setScrollPosition());
|
||||
focusGrid();
|
||||
handleArrowNav(true);
|
||||
});
|
||||
|
||||
onUnmounted(() => handleArrowNav(false));
|
||||
|
||||
const handleArrowNav = (value: boolean): void => {
|
||||
if (props.arrowNavigation) {
|
||||
if (props.headerRefs?.length) {
|
||||
setMonthPicker(value);
|
||||
} else {
|
||||
setSelectionGrid(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const focusGrid = (): void => {
|
||||
const elm = unrefElement(gridWrapRef);
|
||||
if (elm) {
|
||||
if (!props.textInput) {
|
||||
elm.focus({ preventScroll: true });
|
||||
}
|
||||
scrollable.value = elm.clientHeight < elm.scrollHeight;
|
||||
}
|
||||
};
|
||||
|
||||
// Dynamic class for the overlay
|
||||
const dpOverlayClass = computed(
|
||||
(): DynamicClass => ({
|
||||
dp__overlay: true,
|
||||
}),
|
||||
);
|
||||
|
||||
const cellClassName = computed(() => ({
|
||||
dp__overlay_col: true,
|
||||
}));
|
||||
|
||||
const isActive = (itemVal: IDefaultSelect) => {
|
||||
if (props.skipActive) return false;
|
||||
return itemVal.value === props.modelValue;
|
||||
};
|
||||
/**
|
||||
* Simple map for building a grid, just add dynamic classes for each cell
|
||||
*/
|
||||
const mappedItems = computed(() => {
|
||||
return props.items.map((item) => {
|
||||
return item
|
||||
.filter((exists) => exists)
|
||||
.map((itemVal) => {
|
||||
const disabled =
|
||||
props.disabledValues.some((val) => val === itemVal.value) || checkMinMaxValue(itemVal.value);
|
||||
const active = props.multiModelValue?.length
|
||||
? props.multiModelValue?.some((value) =>
|
||||
isDateEqual(
|
||||
value,
|
||||
setYear(
|
||||
props.monthPicker ? setMonth(new Date(), itemVal.value) : new Date(),
|
||||
props.monthPicker ? props.year : itemVal.value,
|
||||
),
|
||||
),
|
||||
)
|
||||
: isActive(itemVal);
|
||||
|
||||
return {
|
||||
...itemVal,
|
||||
className: {
|
||||
dp__overlay_cell_active: active,
|
||||
dp__overlay_cell: !active,
|
||||
dp__overlay_cell_disabled: disabled,
|
||||
dp__overlay_cell_active_disabled: disabled && active,
|
||||
dp__overlay_cell_pad: true,
|
||||
dp__cell_in_between: props.multiModelValue?.length ? rangeActive(itemVal.value) : false,
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Dynamic class for action button
|
||||
*/
|
||||
const actionButtonClass = computed(
|
||||
(): DynamicClass => ({
|
||||
dp__button: true,
|
||||
dp__overlay_action: true,
|
||||
dp__over_action_scroll: scrollable.value,
|
||||
dp__button_bottom: props.autoApply,
|
||||
}),
|
||||
);
|
||||
|
||||
const containerClass = computed(() => ({
|
||||
dp__overlay_container: true,
|
||||
dp__container_flex: props.items?.length <= 6,
|
||||
dp__container_block: props.items?.length > 6,
|
||||
}));
|
||||
|
||||
/**
|
||||
* Check if value is within min-max range
|
||||
*/
|
||||
const checkMinMaxValue = (value: number | string): boolean => {
|
||||
const hasMax = props.maxValue || props.maxValue === 0;
|
||||
const hasMin = props.minValue || props.minValue === 0;
|
||||
if (!hasMax && !hasMin) return false;
|
||||
|
||||
if (hasMax && hasMin) {
|
||||
return +value > +props.maxValue || +value < +props.minValue;
|
||||
}
|
||||
if (hasMax) {
|
||||
return +value > +props.maxValue;
|
||||
}
|
||||
|
||||
if (hasMin) {
|
||||
return +value < +props.minValue;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set scroll position in overlay based on active selection
|
||||
*/
|
||||
const setScrollPosition = (): void => {
|
||||
const el = unrefElement(selectionActiveRef);
|
||||
const parent = unrefElement(gridWrapRef);
|
||||
const btn = unrefElement(toggleButton);
|
||||
const container = unrefElement(containerRef);
|
||||
const toggleBtnHeight = btn ? btn.getBoundingClientRect().height : 0;
|
||||
if (parent) {
|
||||
containerHeight.value = parent.getBoundingClientRect().height - toggleBtnHeight;
|
||||
}
|
||||
if (el && container) {
|
||||
container.scrollTop =
|
||||
el.offsetTop -
|
||||
container.offsetTop -
|
||||
(containerHeight.value / 2 - el.getBoundingClientRect().height) -
|
||||
toggleBtnHeight;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle click on cell, if value is enabled (not in filters), emit value back to parent
|
||||
*/
|
||||
const onClick = (val: string | number): void => {
|
||||
if (!props.disabledValues.some((value) => value === val) && !checkMinMaxValue(val)) {
|
||||
emit('update:model-value', val);
|
||||
emit('selected');
|
||||
}
|
||||
};
|
||||
|
||||
const rangeActive = (value: number): boolean => {
|
||||
const yearValue = props.monthPicker ? props.year : value;
|
||||
return isDateBetween(
|
||||
props.multiModelValue,
|
||||
setYear(
|
||||
props.monthPicker ? setMonth(new Date(), hoverValue.value || 0) : new Date(),
|
||||
props.monthPicker ? yearValue : hoverValue.value || yearValue,
|
||||
),
|
||||
setYear(props.monthPicker ? setMonth(new Date(), value) : new Date(), yearValue),
|
||||
);
|
||||
};
|
||||
|
||||
const toggle = () => {
|
||||
emit('toggle');
|
||||
emit('reset-flow');
|
||||
};
|
||||
|
||||
const handleEsc = () => {
|
||||
if (props.escClose) {
|
||||
toggle();
|
||||
}
|
||||
};
|
||||
|
||||
const assignRef = (el: any, col: IDefaultSelect, rowInd: number, colInd: number): void => {
|
||||
if (el) {
|
||||
if (col.value === +props.modelValue && !props.disabledValues.includes(col.value)) {
|
||||
selectionActiveRef.value = el;
|
||||
}
|
||||
if (props.arrowNavigation) {
|
||||
if (Array.isArray(elementRefs.value[rowInd])) {
|
||||
elementRefs.value[rowInd][colInd] = el;
|
||||
} else {
|
||||
elementRefs.value[rowInd] = [el];
|
||||
}
|
||||
buildMatrix();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const buildMatrix = () => {
|
||||
const refs = props.headerRefs?.length
|
||||
? [props.headerRefs].concat(elementRefs.value)
|
||||
: elementRefs.value.concat([props.skipButtonRef ? [] : [toggleButton.value]]);
|
||||
|
||||
buildMultiLevelMatrix(convertType(refs), props.headerRefs?.length ? 'monthPicker' : 'selectionGrid');
|
||||
};
|
||||
|
||||
defineExpose({ focusGrid });
|
||||
</script>
|
||||
328
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/TimePicker/TimeInput.vue
generated
vendored
Normal file
328
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/TimePicker/TimeInput.vue
generated
vendored
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
<template>
|
||||
<div class="dp__time_input" v-if="!disabled">
|
||||
<div v-for="(timeInput, i) in timeInputs" :key="i" :class="timeColClass">
|
||||
<template v-if="timeInput.separator"> : </template>
|
||||
<template v-else>
|
||||
<div
|
||||
:class="{
|
||||
dp__inc_dec_button: true,
|
||||
dp__inc_dec_button_disabled: disabledArrowUpBtn(timeInput.type),
|
||||
}"
|
||||
role="button"
|
||||
data-test="time-inc-btn"
|
||||
:aria-label="defaults.ariaLabels?.incrementValue(timeInput.type)"
|
||||
tabindex="0"
|
||||
@keydown.enter="handleTimeValue(timeInput.type)"
|
||||
@keydown.space="handleTimeValue(timeInput.type)"
|
||||
@click="handleTimeValue(timeInput.type)"
|
||||
:ref="(el) => assignRefs(el, i, 0)"
|
||||
>
|
||||
<slot name="arrow-up" v-if="$slots['arrow-up']" />
|
||||
<ChevronUpIcon v-if="!$slots['arrow-up']" />
|
||||
</div>
|
||||
<div
|
||||
role="button"
|
||||
:aria-label="defaults.ariaLabels?.openTpOverlay(timeInput.type)"
|
||||
:class="checkOverlayDisabled(timeInput.type) ? '' : 'dp__time_display'"
|
||||
tabindex="0"
|
||||
:data-test="`${timeInput.type}-toggle-overlay-btn`"
|
||||
@keydown.enter="toggleOverlay(timeInput.type)"
|
||||
@keydown.space="toggleOverlay(timeInput.type)"
|
||||
@click="toggleOverlay(timeInput.type)"
|
||||
:ref="(el) => assignRefs(el, i, 1)"
|
||||
>
|
||||
<slot
|
||||
v-if="$slots[timeInput.type]"
|
||||
:name="timeInput.type"
|
||||
:text="timeValueDisplay(timeInput.type).text"
|
||||
:value="timeValueDisplay(timeInput.type).value"
|
||||
/>
|
||||
<template v-if="!$slots[timeInput.type]">{{ timeValueDisplay(timeInput.type).text }}</template>
|
||||
</div>
|
||||
<div
|
||||
:class="{
|
||||
dp__inc_dec_button: true,
|
||||
dp__inc_dec_button_disabled: disabledArrowDownBtn(timeInput.type),
|
||||
}"
|
||||
role="button"
|
||||
data-test="time-dec-btn"
|
||||
:aria-label="defaults.ariaLabels?.decrementValue(timeInput.type)"
|
||||
tabindex="0"
|
||||
@keydown.enter="handleTimeValue(timeInput.type, false)"
|
||||
@keydown.space="handleTimeValue(timeInput.type, false)"
|
||||
@click="handleTimeValue(timeInput.type, false)"
|
||||
:ref="(el) => assignRefs(el, i, 2)"
|
||||
>
|
||||
<slot name="arrow-down" v-if="$slots['arrow-down']" />
|
||||
<ChevronDownIcon v-if="!$slots['arrow-down']" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div v-if="!is24">
|
||||
<slot name="am-pm-button" v-if="$slots['am-pm-button']" :toggle="setAmPm" :value="amPm"></slot>
|
||||
<button
|
||||
ref="amPmButton"
|
||||
type="button"
|
||||
v-if="!$slots['am-pm-button']"
|
||||
class="dp__pm_am_button"
|
||||
role="button"
|
||||
:aria-label="defaults.ariaLabels?.amPmButton"
|
||||
tabindex="0"
|
||||
@click="setAmPm"
|
||||
@keydown.enter.prevent="setAmPm"
|
||||
@keydown.space.prevent="setAmPm"
|
||||
>
|
||||
{{ amPm }}
|
||||
</button>
|
||||
</div>
|
||||
<transition
|
||||
v-for="(timeInput, i) in timeInputOverlays"
|
||||
:key="i"
|
||||
:name="transitionName(overlays[timeInput.type])"
|
||||
:css="showTransition"
|
||||
>
|
||||
<SelectionGrid
|
||||
v-if="overlays[timeInput.type]"
|
||||
:items="getGridItems(timeInput.type)"
|
||||
:disabled-values="defaults.filters.times[timeInput.type].concat(disabledInGrid(timeInput.type))"
|
||||
:esc-close="escClose"
|
||||
:aria-labels="defaults.ariaLabels"
|
||||
:hide-navigation="hideNavigation"
|
||||
@update:model-value="handleTimeFromOverlay(timeInput.type, $event)"
|
||||
@selected="toggleOverlay(timeInput.type)"
|
||||
@toggle="toggleOverlay(timeInput.type)"
|
||||
@reset-flow="$emit('reset-flow')"
|
||||
:type="timeInput.type"
|
||||
>
|
||||
<template #button-icon>
|
||||
<slot name="clock-icon" v-if="$slots['clock-icon']" />
|
||||
<ClockIcon v-if="!$slots['clock-icon']" />
|
||||
</template>
|
||||
<template v-if="$slots[`${timeInput.type}-overlay-value`]" #item="{ item }">
|
||||
<slot :name="`${timeInput.type}-overlay-value`" :text="item.text" :value="item.value" />
|
||||
</template>
|
||||
</SelectionGrid>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, reactive, ref } from 'vue';
|
||||
|
||||
import { add, getHours, getMinutes, getSeconds, set, sub } from 'date-fns';
|
||||
|
||||
import { ChevronUpIcon, ChevronDownIcon, ClockIcon } from '@/components/Icons';
|
||||
import SelectionGrid from '@/components/SelectionGrid.vue';
|
||||
import { useTransitions, useArrowNavigation, useUtils } from '@/components/composables';
|
||||
import { AllProps } from '@/utils/props';
|
||||
import { getArrayInArray, hoursToAmPmHours } from '@/utils/util';
|
||||
|
||||
import type { PropType } from 'vue';
|
||||
import type { Duration } from 'date-fns';
|
||||
import type { DynamicClass, IDefaultSelect, TimeType, TimeOverlayCheck } from '@/interfaces';
|
||||
import { getDate } from '@/utils/date-utils';
|
||||
|
||||
const emit = defineEmits([
|
||||
'set-hours',
|
||||
'set-minutes',
|
||||
'update:hours',
|
||||
'update:minutes',
|
||||
'update:seconds',
|
||||
'reset-flow',
|
||||
'mounted',
|
||||
'overlay-closed',
|
||||
]);
|
||||
const props = defineProps({
|
||||
hours: { type: Number as PropType<number>, default: 0 },
|
||||
minutes: { type: Number as PropType<number>, default: 0 },
|
||||
seconds: { type: Number as PropType<number>, default: 0 },
|
||||
closeTimePickerBtn: { type: Object as PropType<HTMLElement | null>, default: null },
|
||||
order: { type: Number as PropType<number>, default: 0 },
|
||||
...AllProps,
|
||||
});
|
||||
|
||||
const { setTimePickerElements, setTimePickerBackRef } = useArrowNavigation();
|
||||
const { defaults } = useUtils(props);
|
||||
|
||||
const { transitionName, showTransition } = useTransitions(defaults.value.transitions);
|
||||
|
||||
const overlays = reactive({
|
||||
hours: false,
|
||||
minutes: false,
|
||||
seconds: false,
|
||||
});
|
||||
const amPm = ref('AM');
|
||||
const amPmButton = ref<HTMLElement | null>(null);
|
||||
const elementRefs = ref<HTMLElement[][]>([]);
|
||||
|
||||
onMounted(() => {
|
||||
emit('mounted');
|
||||
});
|
||||
|
||||
const disabledArrowUpBtn = computed(() => (type: TimeType) => {
|
||||
return !!(
|
||||
props.maxTime &&
|
||||
props.maxTime[type] &&
|
||||
props.maxTime[type] < props[type] + +props[`${type}Increment`]
|
||||
);
|
||||
});
|
||||
|
||||
const disabledArrowDownBtn = computed(() => (type: TimeType) => {
|
||||
return !!(
|
||||
props.minTime &&
|
||||
props.minTime[type] &&
|
||||
props.minTime[type] > props[type] - +props[`${type}Increment`]
|
||||
);
|
||||
});
|
||||
|
||||
const addTime = (initial: Record<string, number>, toAdd: Duration) => add(set(getDate(), initial), toAdd);
|
||||
|
||||
const subTime = (initial: Record<string, number>, toSub: Duration) => sub(set(getDate(), initial), toSub);
|
||||
|
||||
const timeColClass = computed(
|
||||
(): DynamicClass => ({
|
||||
dp__time_col: true,
|
||||
dp__time_col_reg: !props.enableSeconds && props.is24,
|
||||
dp__time_col_reg_with_button: !props.enableSeconds && !props.is24,
|
||||
dp__time_col_sec: props.enableSeconds && props.is24,
|
||||
dp__time_col_sec_with_button: props.enableSeconds && !props.is24,
|
||||
}),
|
||||
);
|
||||
|
||||
const timeInputs = computed((): { type: TimeType; separator?: boolean }[] => {
|
||||
const inputs = [{ type: 'hours' }, { type: '', separator: true }, { type: 'minutes' }];
|
||||
return (props.enableSeconds ? inputs.concat([{ type: '', separator: true }, { type: 'seconds' }]) : inputs) as {
|
||||
type: TimeType;
|
||||
}[];
|
||||
});
|
||||
|
||||
const timeInputOverlays = computed(() => timeInputs.value.filter((input) => !input.separator));
|
||||
|
||||
const timeValueDisplay = computed(() => (type: TimeType) => {
|
||||
if (type === 'hours') {
|
||||
const hour = convert24ToAmPm(props.hours);
|
||||
return { text: hour < 10 ? `0${hour}` : `${hour}`, value: hour };
|
||||
}
|
||||
return { text: props[type] < 10 ? `0${props[type]}` : `${props[type]}`, value: props[type] };
|
||||
});
|
||||
|
||||
const getGridItems = (type: TimeType): IDefaultSelect[][] => {
|
||||
const timeRange = props.is24 ? 24 : 12;
|
||||
const max = type === 'hours' ? timeRange : 60;
|
||||
const increment = +props[`${type}GridIncrement`];
|
||||
const min = type === 'hours' && !props.is24 ? increment : 0;
|
||||
|
||||
const generatedArray: IDefaultSelect[] = [];
|
||||
|
||||
for (let i = min; i < max; i += increment) {
|
||||
generatedArray.push({ value: i, text: i < 10 ? `0${i}` : `${i}` });
|
||||
}
|
||||
|
||||
if (type === 'hours' && !props.is24) {
|
||||
generatedArray.push({ value: 0, text: '12' });
|
||||
}
|
||||
|
||||
return getArrayInArray(generatedArray);
|
||||
};
|
||||
|
||||
const checkMinMax = (val: number, type: TimeType): boolean => {
|
||||
const minValue = props.minTime && props.minTime[type];
|
||||
const maxValue = props.maxTime && props.maxTime[type];
|
||||
if (minValue && maxValue) return val < minValue || val > maxValue;
|
||||
if (minValue) return val < minValue;
|
||||
if (maxValue) return val > maxValue;
|
||||
return false;
|
||||
};
|
||||
|
||||
const disabledInGrid = computed(() => (type: TimeType) => {
|
||||
const times = getGridItems(type)
|
||||
.flat()
|
||||
.map((item) => item.value);
|
||||
|
||||
return times.filter((item) => checkMinMax(item, type));
|
||||
});
|
||||
|
||||
const checkOverlayDisabled = (type: TimeType): boolean => {
|
||||
return props[`no${type[0].toUpperCase() + type.slice(1)}Overlay` as TimeOverlayCheck];
|
||||
};
|
||||
|
||||
const toggleOverlay = (type: TimeType): void => {
|
||||
if (!checkOverlayDisabled(type)) {
|
||||
overlays[type] = !overlays[type];
|
||||
if (!overlays[type]) {
|
||||
emit('overlay-closed');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getTimeGetter = (type: TimeType) => {
|
||||
if (type === 'hours') return getHours;
|
||||
if (type === 'minutes') return getMinutes;
|
||||
return getSeconds;
|
||||
};
|
||||
|
||||
const handleTimeValue = (type: TimeType, inc = true): void => {
|
||||
const addOrSub = inc ? addTime : subTime;
|
||||
const disabled = inc ? disabledArrowUpBtn.value(type) : disabledArrowDownBtn.value(type);
|
||||
if (!disabled) {
|
||||
emit(
|
||||
`update:${type}`,
|
||||
getTimeGetter(type)(addOrSub({ [type]: +props[type] }, { [type]: +props[`${type}Increment`] })),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const convert24ToAmPm = (time: number): number => {
|
||||
if (props.is24) {
|
||||
return time;
|
||||
}
|
||||
if (time >= 12) {
|
||||
amPm.value = 'PM';
|
||||
} else {
|
||||
amPm.value = 'AM';
|
||||
}
|
||||
return hoursToAmPmHours(time);
|
||||
};
|
||||
|
||||
const setAmPm = () => {
|
||||
if (amPm.value === 'PM') {
|
||||
amPm.value = 'AM';
|
||||
emit('update:hours', props.hours - 12);
|
||||
} else {
|
||||
amPm.value = 'PM';
|
||||
emit('update:hours', props.hours + 12);
|
||||
}
|
||||
};
|
||||
|
||||
const openChildCmp = (child: TimeType): void => {
|
||||
overlays[child] = true;
|
||||
};
|
||||
|
||||
const assignRefs = (el: any, col: number, pos: number): void => {
|
||||
if (el && props.arrowNavigation) {
|
||||
if (Array.isArray(elementRefs.value[col])) {
|
||||
elementRefs.value[col][pos] = el;
|
||||
} else {
|
||||
elementRefs.value[col] = [el];
|
||||
}
|
||||
const matrix = elementRefs.value.reduce(
|
||||
(col, row) => row.map((_, i) => [...(col[i] || []), row[i]]),
|
||||
[] as HTMLElement[][],
|
||||
);
|
||||
setTimePickerBackRef(props.closeTimePickerBtn);
|
||||
if (amPmButton.value) {
|
||||
matrix[1] = matrix[1].concat(amPmButton.value);
|
||||
}
|
||||
setTimePickerElements(matrix, props.order as 0 | 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTimeFromOverlay = (type: TimeType, value: number): void => {
|
||||
if (type === 'hours' && !props.is24) {
|
||||
return emit(`update:${type}`, amPm.value === 'PM' ? value + 12 : value);
|
||||
}
|
||||
return emit(`update:${type}`, value);
|
||||
};
|
||||
|
||||
defineExpose({ openChildCmp });
|
||||
</script>
|
||||
213
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/TimePicker/TimePicker.vue
generated
vendored
Normal file
213
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/TimePicker/TimePicker.vue
generated
vendored
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
<template>
|
||||
<div>
|
||||
<div
|
||||
v-if="!timePicker"
|
||||
v-show="!hideNavigationButtons('time')"
|
||||
:class="toggleButtonClass"
|
||||
role="button"
|
||||
:aria-label="defaults.ariaLabels?.openTimePicker"
|
||||
tabindex="0"
|
||||
data-test="open-time-picker-btn"
|
||||
ref="openTimePickerBtn"
|
||||
@keydown.enter="toggleTimePicker(true)"
|
||||
@keydown.space="toggleTimePicker(true)"
|
||||
@click="toggleTimePicker(true)"
|
||||
>
|
||||
<slot name="clock-icon" v-if="$slots['clock-icon']" />
|
||||
<ClockIcon v-if="!$slots['clock-icon']" />
|
||||
</div>
|
||||
<transition :name="transitionName(showTimePicker)" :css="showTransition">
|
||||
<div v-if="showTimePicker || timePicker" class="dp__overlay" ref="overlayRef" tabindex="0">
|
||||
<div class="dp__overlay_container dp__container_flex dp__time_picker_overlay_container">
|
||||
<slot
|
||||
name="time-picker-overlay"
|
||||
v-if="$slots['time-picker-overlay']"
|
||||
:hours="hours"
|
||||
:minutes="minutes"
|
||||
:seconds="seconds"
|
||||
:set-hours="updateHours"
|
||||
:set-minutes="updateMinutes"
|
||||
:set-seconds="updateSeconds"
|
||||
></slot>
|
||||
<template v-if="!$slots['time-picker-overlay']">
|
||||
<div class="dp__overlay_row">
|
||||
<TimeInput
|
||||
v-for="(tInput, index) in timeInputs"
|
||||
:key="index"
|
||||
v-show="index === 0 ? true : shouldShowRangedInput"
|
||||
v-bind="{
|
||||
...$props,
|
||||
order: index,
|
||||
hours: tInput.hours,
|
||||
minutes: tInput.minutes,
|
||||
seconds: tInput.seconds,
|
||||
closeTimePickerBtn,
|
||||
disabled: index === 0 ? fixedStart : fixedEnd,
|
||||
}"
|
||||
ref="timeInputRefs"
|
||||
@update:hours="updateHours(getEvent($event, index, 'hours'))"
|
||||
@update:minutes="updateMinutes(getEvent($event, index, 'minutes'))"
|
||||
@update:seconds="updateSeconds(getEvent($event, index, 'seconds'))"
|
||||
@mounted="focusOverlay"
|
||||
@overlay-closed="focusOverlay"
|
||||
>
|
||||
<template v-for="(slot, i) in timeInputSlots" #[slot]="args" :key="i">
|
||||
<slot :name="slot" v-bind="args" />
|
||||
</template>
|
||||
</TimeInput>
|
||||
</div>
|
||||
</template>
|
||||
<div
|
||||
v-if="!timePicker"
|
||||
v-show="!hideNavigationButtons('time')"
|
||||
ref="closeTimePickerBtn"
|
||||
:class="toggleButtonClass"
|
||||
role="button"
|
||||
:aria-label="defaults.ariaLabels?.closeTimePicker"
|
||||
tabindex="0"
|
||||
@keydown.enter="toggleTimePicker(false)"
|
||||
@keydown.space="toggleTimePicker(false)"
|
||||
@click="toggleTimePicker(false)"
|
||||
>
|
||||
<slot name="calendar-icon" v-if="$slots['calendar-icon']" />
|
||||
<CalendarIcon v-if="!$slots['calendar-icon']" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, nextTick, onMounted, ref, useSlots } from 'vue';
|
||||
|
||||
import { ClockIcon, CalendarIcon } from '@/components/Icons';
|
||||
import TimeInput from '@/components/TimePicker/TimeInput.vue';
|
||||
|
||||
import { isModelAuto, unrefElement } from '@/utils/util';
|
||||
import { mapSlots, useTransitions, useArrowNavigation, useUtils } from '@/components/composables';
|
||||
import { AllProps } from '@/utils/props';
|
||||
|
||||
import type { PropType } from 'vue';
|
||||
import type { TimeInputRef, InternalModuleValue } from '@/interfaces';
|
||||
|
||||
const emit = defineEmits([
|
||||
'update:hours',
|
||||
'update:minutes',
|
||||
'update:seconds',
|
||||
'mount',
|
||||
'reset-flow',
|
||||
'overlay-closed',
|
||||
]);
|
||||
|
||||
const props = defineProps({
|
||||
hours: { type: [Number, Array] as PropType<number | number[]>, default: 0 },
|
||||
minutes: { type: [Number, Array] as PropType<number | number[]>, default: 0 },
|
||||
seconds: { type: [Number, Array] as PropType<number | number[]>, default: 0 },
|
||||
internalModelValue: { type: [Date, Array] as PropType<InternalModuleValue>, default: null },
|
||||
...AllProps,
|
||||
});
|
||||
|
||||
const { buildMatrix, setTimePicker } = useArrowNavigation();
|
||||
const slots = useSlots();
|
||||
const { hideNavigationButtons, defaults } = useUtils(props);
|
||||
|
||||
const { transitionName, showTransition } = useTransitions(defaults.value.transitions);
|
||||
const openTimePickerBtn = ref(null);
|
||||
const closeTimePickerBtn = ref(null);
|
||||
const timeInputRefs = ref<TimeInputRef[]>([]);
|
||||
const overlayRef = ref<HTMLElement | null>(null);
|
||||
|
||||
onMounted(() => {
|
||||
emit('mount');
|
||||
if (!props.timePicker && props.arrowNavigation) {
|
||||
buildMatrix([unrefElement(openTimePickerBtn.value) as HTMLElement], 'time');
|
||||
} else {
|
||||
setTimePicker(true, props.timePicker);
|
||||
}
|
||||
});
|
||||
|
||||
const shouldShowRangedInput = computed(() => {
|
||||
if (props.range && props.modelAuto) return isModelAuto(props.internalModelValue);
|
||||
return true;
|
||||
});
|
||||
|
||||
const showTimePicker = ref(false);
|
||||
|
||||
const getTimeInput = (i: number) => {
|
||||
return {
|
||||
hours: Array.isArray(props.hours) ? props.hours[i] : props.hours,
|
||||
minutes: Array.isArray(props.minutes) ? props.minutes[i] : props.minutes,
|
||||
seconds: Array.isArray(props.seconds) ? props.seconds[i] : props.seconds,
|
||||
};
|
||||
};
|
||||
|
||||
const timeInputs = computed((): { hours: number; minutes: number; seconds: number }[] => {
|
||||
const arr = [];
|
||||
if (props.range) {
|
||||
for (let i = 0; i < 2; i++) {
|
||||
arr.push(getTimeInput(i));
|
||||
}
|
||||
} else {
|
||||
arr.push(getTimeInput(0));
|
||||
}
|
||||
return arr as { hours: number; minutes: number; seconds: number }[];
|
||||
});
|
||||
|
||||
const toggleTimePicker = (show: boolean, flow = false, childOpen = ''): void => {
|
||||
if (!flow) {
|
||||
emit('reset-flow');
|
||||
}
|
||||
showTimePicker.value = show;
|
||||
|
||||
if (props.arrowNavigation) {
|
||||
setTimePicker(show);
|
||||
if (!show) {
|
||||
emit('overlay-closed');
|
||||
}
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
if (childOpen !== '' && timeInputRefs.value[0]) {
|
||||
timeInputRefs.value[0].openChildCmp(childOpen);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const toggleButtonClass = computed(() => ({
|
||||
dp__button: true,
|
||||
dp__button_bottom: props.autoApply,
|
||||
}));
|
||||
|
||||
const timeInputSlots = mapSlots(slots, 'timePicker');
|
||||
|
||||
const getEvent = (event: number, index: number, property: 'hours' | 'minutes' | 'seconds') => {
|
||||
if (!props.range) {
|
||||
return event;
|
||||
}
|
||||
if (index === 0) {
|
||||
return [event, timeInputs.value[1][property]];
|
||||
}
|
||||
return [timeInputs.value[0][property], event];
|
||||
};
|
||||
|
||||
const updateHours = (hours: number | number[]): void => {
|
||||
emit('update:hours', hours);
|
||||
};
|
||||
|
||||
const updateMinutes = (minutes: number | number[]): void => {
|
||||
emit('update:minutes', minutes);
|
||||
};
|
||||
|
||||
const updateSeconds = (seconds: number | number[]): void => {
|
||||
emit('update:seconds', seconds);
|
||||
};
|
||||
|
||||
const focusOverlay = () => {
|
||||
if (overlayRef.value && props.arrowNavigation) {
|
||||
overlayRef.value.focus({ preventScroll: true });
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({ toggleTimePicker });
|
||||
</script>
|
||||
190
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/composables/arrow-navigate.ts
generated
vendored
Normal file
190
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/composables/arrow-navigate.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
import { computed, reactive, ref } from 'vue';
|
||||
|
||||
type ElRefs = Array<HTMLElement | null>;
|
||||
|
||||
const refSets = reactive({
|
||||
monthYear: [] as ElRefs,
|
||||
calendar: [] as ElRefs[],
|
||||
time: [] as ElRefs,
|
||||
actionRow: [] as ElRefs,
|
||||
selectionGrid: [] as ElRefs[],
|
||||
timePicker: {
|
||||
'0': [] as ElRefs[],
|
||||
'1': [] as ElRefs[],
|
||||
},
|
||||
monthPicker: [] as ElRefs[],
|
||||
});
|
||||
const timePickerBackRef = ref<HTMLElement | null>(null);
|
||||
const isSelectionGrid = ref(false);
|
||||
const isTimePicker = ref(false);
|
||||
const isMonthPicker = ref(false);
|
||||
const isTimePickerMode = ref(false);
|
||||
|
||||
const selectedIndex = ref(0);
|
||||
const activeRow = ref(0);
|
||||
|
||||
export const useArrowNavigation = () => {
|
||||
const matrix = computed((): ElRefs[] => {
|
||||
if (isSelectionGrid.value) return [...refSets.selectionGrid, refSets.actionRow].filter((set) => set.length);
|
||||
if (isTimePicker.value) {
|
||||
return [
|
||||
...refSets.timePicker[0],
|
||||
...refSets.timePicker[1],
|
||||
isTimePickerMode.value ? [] : [timePickerBackRef.value],
|
||||
refSets.actionRow,
|
||||
].filter((set) => set.length);
|
||||
}
|
||||
if (isMonthPicker.value) return [...refSets.monthPicker, refSets.actionRow];
|
||||
return [refSets.monthYear, ...refSets.calendar, refSets.time, refSets.actionRow].filter((set) => set.length);
|
||||
});
|
||||
|
||||
// Handles left and right arrow
|
||||
const handleSelectionIndexX = (increment?: boolean): void => {
|
||||
selectedIndex.value = increment ? selectedIndex.value + 1 : selectedIndex.value - 1;
|
||||
let el = null;
|
||||
if (matrix.value[activeRow.value]) {
|
||||
el = matrix.value[activeRow.value][selectedIndex.value];
|
||||
}
|
||||
|
||||
if (!el) {
|
||||
selectedIndex.value = increment ? selectedIndex.value - 1 : selectedIndex.value + 1;
|
||||
}
|
||||
};
|
||||
|
||||
// Handles up and down arrow
|
||||
const handleSelectionIndexY = (increment?: boolean): void => {
|
||||
if ((activeRow.value === 0 && !increment) || (activeRow.value === matrix.value.length && increment)) return;
|
||||
activeRow.value = increment ? activeRow.value + 1 : activeRow.value - 1;
|
||||
const el = matrix.value[activeRow.value];
|
||||
|
||||
if (!el) {
|
||||
activeRow.value = increment ? activeRow.value - 1 : activeRow.value + 1;
|
||||
} else {
|
||||
if (
|
||||
matrix.value[activeRow.value] &&
|
||||
!matrix.value[activeRow.value][selectedIndex.value] &&
|
||||
selectedIndex.value !== 0
|
||||
) {
|
||||
selectedIndex.value = matrix.value[activeRow.value].length - 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleElFocus = (increment: boolean): void => {
|
||||
let el = null;
|
||||
if (matrix.value[activeRow.value]) {
|
||||
el = matrix.value[activeRow.value][selectedIndex.value];
|
||||
}
|
||||
if (el) {
|
||||
el.focus({ preventScroll: !isSelectionGrid.value });
|
||||
} else {
|
||||
selectedIndex.value = increment ? selectedIndex.value - 1 : selectedIndex.value + 1;
|
||||
}
|
||||
};
|
||||
|
||||
const arrowRight = (): void => {
|
||||
handleSelectionIndexX(true);
|
||||
handleElFocus(true);
|
||||
};
|
||||
|
||||
const arrowLeft = (): void => {
|
||||
handleSelectionIndexX(false);
|
||||
handleElFocus(false);
|
||||
};
|
||||
|
||||
const arrowUp = (): void => {
|
||||
handleSelectionIndexY(false);
|
||||
handleElFocus(true);
|
||||
};
|
||||
|
||||
const arrowDown = (): void => {
|
||||
handleSelectionIndexY(true);
|
||||
handleElFocus(true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add values per page, holds the ref values of the focusable elements
|
||||
* Build top to bottom
|
||||
*/
|
||||
const buildMatrix = (elements: Array<HTMLElement | null>, set: 'monthYear' | 'time' | 'actionRow'): void => {
|
||||
refSets[set] = elements;
|
||||
};
|
||||
|
||||
const buildMultiLevelMatrix = (
|
||||
elements: HTMLElement[][],
|
||||
set: 'calendar' | 'selectionGrid' | 'monthPicker',
|
||||
): void => {
|
||||
refSets[set] = elements;
|
||||
};
|
||||
|
||||
const resetNavigation = (): void => {
|
||||
selectedIndex.value = 0;
|
||||
activeRow.value = 0;
|
||||
};
|
||||
|
||||
const setMonthPicker = (value: boolean): void => {
|
||||
isMonthPicker.value = value;
|
||||
resetNavigation();
|
||||
};
|
||||
|
||||
/**
|
||||
* For selection grid, things are handled per grid
|
||||
*/
|
||||
const setSelectionGrid = (value: boolean): void => {
|
||||
isSelectionGrid.value = value;
|
||||
resetNavigation();
|
||||
if (!value) {
|
||||
refSets.selectionGrid = [];
|
||||
}
|
||||
};
|
||||
|
||||
const setTimePicker = (value: boolean, mode = false) => {
|
||||
isTimePicker.value = value;
|
||||
isTimePickerMode.value = mode;
|
||||
resetNavigation();
|
||||
if (!value) {
|
||||
refSets.timePicker[0] = [];
|
||||
refSets.timePicker[1] = [];
|
||||
}
|
||||
};
|
||||
|
||||
const setTimePickerElements = (elements: HTMLElement[][], order: 0 | 1 = 0): void => {
|
||||
refSets.timePicker[order] = elements;
|
||||
};
|
||||
|
||||
const setTimePickerBackRef = (el: HTMLElement | null): void => {
|
||||
timePickerBackRef.value = el;
|
||||
};
|
||||
|
||||
const clearArrowNav = (): void => {
|
||||
refSets.monthYear = [];
|
||||
refSets.calendar = [];
|
||||
refSets.time = [];
|
||||
refSets.actionRow = [];
|
||||
refSets.selectionGrid = [];
|
||||
refSets.timePicker[0] = [];
|
||||
refSets.timePicker[1] = [];
|
||||
isSelectionGrid.value = false;
|
||||
isTimePicker.value = false;
|
||||
isTimePickerMode.value = false;
|
||||
isMonthPicker.value = false;
|
||||
resetNavigation();
|
||||
timePickerBackRef.value = null;
|
||||
};
|
||||
|
||||
return {
|
||||
buildMatrix,
|
||||
buildMultiLevelMatrix,
|
||||
setTimePickerBackRef,
|
||||
setSelectionGrid,
|
||||
setTimePicker,
|
||||
setTimePickerElements,
|
||||
arrowRight,
|
||||
arrowLeft,
|
||||
arrowUp,
|
||||
arrowDown,
|
||||
clearArrowNav,
|
||||
setMonthPicker,
|
||||
refSets, // exposed for testing
|
||||
};
|
||||
};
|
||||
305
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/composables/calendar-class.ts
generated
vendored
Normal file
305
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/composables/calendar-class.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
import { ref } from 'vue';
|
||||
import { addDays } from 'date-fns';
|
||||
|
||||
import { useUtils } from '@/components/composables';
|
||||
import { isModelAuto } from '@/utils/util';
|
||||
import { isDateAfter, isDateBefore, isDateBetween, isDateEqual, getDate } from '@/utils/date-utils';
|
||||
|
||||
import type { UnwrapRef, WritableComputedRef } from 'vue';
|
||||
import type { ExtendedProps, ICalendarDay, InternalModuleValue } from '@/interfaces';
|
||||
|
||||
export const useCalendarClass = (modelValue: WritableComputedRef<InternalModuleValue>, props: ExtendedProps) => {
|
||||
const { isDisabled, matchDate, getWeekFromDate } = useUtils(props);
|
||||
// Track hovered date
|
||||
const hoveredDate = ref<Date | null>(null);
|
||||
// Today date
|
||||
const today = ref<Date>(getDate());
|
||||
|
||||
/**
|
||||
* When using range picker keep track of hovered value in the calendar
|
||||
*/
|
||||
const setHoverDate = (day: UnwrapRef<ICalendarDay>): void => {
|
||||
if (!day.current && props.hideOffsetDates) {
|
||||
return;
|
||||
}
|
||||
hoveredDate.value = day.value;
|
||||
};
|
||||
|
||||
// When mouse leaves the menu clear the hover date data
|
||||
const clearHoverDate = (): void => {
|
||||
hoveredDate.value = null;
|
||||
};
|
||||
|
||||
const checkRangeDirection = (isStart: boolean): boolean => {
|
||||
if (Array.isArray(modelValue.value) && props.range && modelValue.value[0] && hoveredDate.value) {
|
||||
return isStart
|
||||
? isDateAfter(hoveredDate.value, modelValue.value[0])
|
||||
: isDateBefore(hoveredDate.value, modelValue.value[0]);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const checkDateEqual = (day: ICalendarDay, isStart: boolean) => {
|
||||
const startDateCompare = () => {
|
||||
if (!modelValue.value) return null;
|
||||
return isStart ? (modelValue.value as Date[])[0] || null : (modelValue.value as Date[])[1];
|
||||
};
|
||||
const dateToCompare = modelValue.value && Array.isArray(modelValue.value) ? startDateCompare() : null;
|
||||
return isDateEqual(getDate(day.value), dateToCompare);
|
||||
};
|
||||
|
||||
const checkDateBefore = (isStart: boolean) => {
|
||||
const dateToCompare = Array.isArray(modelValue.value) ? modelValue.value[0] : null;
|
||||
return isStart ? !isDateBefore(hoveredDate.value || null, dateToCompare) : true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check when to add a proper active start/end date class on range picker
|
||||
*/
|
||||
const rangeActiveStartEnd = (day: UnwrapRef<ICalendarDay>, isStart = true): boolean => {
|
||||
if ((props.range || props.weekPicker) && Array.isArray(modelValue.value)) {
|
||||
if (props.hideOffsetDates && !day.current) return false;
|
||||
return isDateEqual(getDate(day.value), modelValue.value[isStart ? 0 : 1]);
|
||||
}
|
||||
|
||||
if (props.range) {
|
||||
return (
|
||||
(checkDateEqual(day, isStart) && checkDateBefore(isStart)) ||
|
||||
(isDateEqual(day.value, Array.isArray(modelValue.value) ? modelValue.value[0] : null) &&
|
||||
checkRangeDirection(isStart))
|
||||
);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// If the range mode is used, checks for the end value of hovered date
|
||||
const isHoverDateStartEnd = (dateIsHovered: boolean, calendarDay: ICalendarDay, start?: boolean): boolean => {
|
||||
if (Array.isArray(modelValue.value) && modelValue.value[0] && modelValue.value.length === 1) {
|
||||
if (dateIsHovered) {
|
||||
return false;
|
||||
}
|
||||
return start
|
||||
? isDateAfter(modelValue.value[0], calendarDay.value)
|
||||
: isDateBefore(modelValue.value[0], calendarDay.value);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if some date is active, in case of range, it will have two dates
|
||||
*/
|
||||
const isActiveDate = (calendarDay: ICalendarDay): boolean => {
|
||||
if (!modelValue.value) return false;
|
||||
if (props.hideOffsetDates && !calendarDay.current) return false;
|
||||
if (!props.range) {
|
||||
if (props.multiDates && Array.isArray(modelValue.value)) {
|
||||
return modelValue.value.some((dateVal) => isDateEqual(dateVal, calendarDay.value));
|
||||
}
|
||||
return isDateEqual(calendarDay.value, modelValue.value ? (modelValue.value as Date) : today.value);
|
||||
}
|
||||
if (props.modelAuto && Array.isArray(modelValue.value)) {
|
||||
return isDateEqual(calendarDay.value, modelValue.value[0] ? modelValue.value[0] : today.value);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if range ends on the given day
|
||||
*/
|
||||
const isHoverRangeEnd = (day: UnwrapRef<ICalendarDay>): boolean => {
|
||||
if (props.autoRange || props.weekPicker) {
|
||||
if (hoveredDate.value) {
|
||||
if (props.hideOffsetDates && !day.current) return false;
|
||||
const rangeEnd = addDays(hoveredDate.value, +props.autoRange);
|
||||
const range = getWeekFromDate(getDate(hoveredDate.value));
|
||||
return props.weekPicker
|
||||
? isDateEqual(range[1], getDate(day.value))
|
||||
: isDateEqual(rangeEnd, getDate(day.value));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if date in auto range preview is in between
|
||||
*/
|
||||
const isAutoRangeInBetween = (day: UnwrapRef<ICalendarDay>): boolean => {
|
||||
if (props.autoRange || props.weekPicker) {
|
||||
if (hoveredDate.value) {
|
||||
const rangeEnd = addDays(hoveredDate.value, +props.autoRange);
|
||||
if (props.hideOffsetDates && !day.current) return false;
|
||||
const range = getWeekFromDate(getDate(hoveredDate.value));
|
||||
return props.weekPicker
|
||||
? isDateAfter(day.value, range[0]) && isDateBefore(day.value, range[1])
|
||||
: isDateAfter(day.value, hoveredDate.value) && isDateBefore(day.value, rangeEnd);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const isAutoRangeStart = (day: UnwrapRef<ICalendarDay>): boolean => {
|
||||
if (props.autoRange || props.weekPicker) {
|
||||
if (hoveredDate.value) {
|
||||
if (props.hideOffsetDates && !day.current) return false;
|
||||
const range = getWeekFromDate(getDate(hoveredDate.value));
|
||||
return props.weekPicker ? isDateEqual(range[0], day.value) : isDateEqual(hoveredDate.value, day.value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* If range mode used, this will check if the calendar day is between 2 active dates
|
||||
*/
|
||||
const rangeActive = (calendarDay: ICalendarDay): boolean => {
|
||||
return isDateBetween(modelValue.value as Date[], hoveredDate.value, calendarDay.value);
|
||||
};
|
||||
|
||||
const isSingleInModelAuto = (): boolean => {
|
||||
if (props.modelAuto && Array.isArray(props.internalModelValue)) {
|
||||
return !!props.internalModelValue[0];
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const isModelAutoActive = () => {
|
||||
if (props.modelAuto) return isModelAuto(props.internalModelValue);
|
||||
return true;
|
||||
};
|
||||
|
||||
// Check if date is hovered, for single picker mode
|
||||
const dateHover = (day: ICalendarDay) => {
|
||||
if ((Array.isArray(modelValue.value) && modelValue.value.length) || props.weekPicker) return false;
|
||||
const isInRange = props.range ? !rangeActiveStartEnd(day) && !rangeActiveStartEnd(day, false) : true;
|
||||
|
||||
return !isDisabled(day.value) && !isActiveDate(day) && !(!day.current && props.hideOffsetDates) && isInRange;
|
||||
};
|
||||
|
||||
// Check if the date is selected
|
||||
const isActive = (day: ICalendarDay) => {
|
||||
if (props.range) {
|
||||
return props.modelAuto ? isSingleInModelAuto() && isActiveDate(day) : false;
|
||||
}
|
||||
return isActiveDate(day);
|
||||
};
|
||||
|
||||
// Check if the date should be highlighted
|
||||
const highlighted = (day: ICalendarDay) => {
|
||||
if (props.highlight) {
|
||||
return matchDate(day.value, props.highlight);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// If enabled, checks the days that are also highlighted to not be marked as disabled
|
||||
const disableHighlight = (day: ICalendarDay) => isDisabled(day.value) && props.highlightDisabledDays === false;
|
||||
|
||||
// Check if the given week day should be highlighted
|
||||
const highlightedWeekDay = (day: ICalendarDay) => {
|
||||
return props.highlightWeekDays && props.highlightWeekDays.includes(day.value.getDay());
|
||||
};
|
||||
|
||||
const isBetween = (day: ICalendarDay) => {
|
||||
if (
|
||||
(props.range || props.weekPicker) &&
|
||||
(props.multiCalendars > 0 ? day.current : true) &&
|
||||
isModelAutoActive() &&
|
||||
!(!day.current && props.hideOffsetDates) &&
|
||||
!isActiveDate(day)
|
||||
) {
|
||||
return rangeActive(day);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Common classes to be checked for any mode
|
||||
const sharedClasses = (day: ICalendarDay): Record<string, boolean> => {
|
||||
return {
|
||||
dp__cell_offset: !day.current,
|
||||
dp__pointer: !props.disabled && !(!day.current && props.hideOffsetDates) && !isDisabled(day.value),
|
||||
dp__cell_disabled: isDisabled(day.value),
|
||||
dp__cell_highlight:
|
||||
!disableHighlight(day) && (highlighted(day) || highlightedWeekDay(day)) && !isActive(day),
|
||||
dp__cell_highlight_active:
|
||||
!disableHighlight(day) && (highlighted(day) || highlightedWeekDay(day)) && isActive(day),
|
||||
dp__today: !props.noToday && isDateEqual(day.value, today.value) && day.current,
|
||||
};
|
||||
};
|
||||
|
||||
// Get set of classes for the single date picker
|
||||
const singleDateClasses = (day: ICalendarDay): Record<string, boolean> => {
|
||||
return {
|
||||
dp__active_date: isActive(day),
|
||||
dp__date_hover: dateHover(day),
|
||||
};
|
||||
};
|
||||
|
||||
// Get set of classes for the week picker
|
||||
const weekPickerClasses = (day: ICalendarDay): Record<string, boolean> => {
|
||||
return {
|
||||
...rangeDateClasses(day),
|
||||
...autoRangeClasses(day),
|
||||
dp__range_between_week: isBetween(day) && props.weekPicker,
|
||||
};
|
||||
};
|
||||
|
||||
// Get set of classes for the range
|
||||
const rangeDateClasses = (day: ICalendarDay): Record<string, boolean> => {
|
||||
return {
|
||||
dp__range_start:
|
||||
props.multiCalendars > 0
|
||||
? day.current && rangeActiveStartEnd(day) && isModelAutoActive()
|
||||
: rangeActiveStartEnd(day) && isModelAutoActive(),
|
||||
dp__range_end:
|
||||
props.multiCalendars > 0
|
||||
? day.current && rangeActiveStartEnd(day, false) && isModelAutoActive()
|
||||
: rangeActiveStartEnd(day, false) && isModelAutoActive(),
|
||||
dp__range_between: isBetween(day) && !props.weekPicker,
|
||||
dp__date_hover_start: isHoverDateStartEnd(dateHover(day), day, true),
|
||||
dp__date_hover_end: isHoverDateStartEnd(dateHover(day), day, false),
|
||||
};
|
||||
};
|
||||
|
||||
// Get set of classes for auto range
|
||||
const autoRangeClasses = (day: ICalendarDay): Record<string, boolean> => {
|
||||
return {
|
||||
...rangeDateClasses(day),
|
||||
dp__cell_auto_range: isAutoRangeInBetween(day),
|
||||
dp__cell_auto_range_start: isAutoRangeStart(day),
|
||||
dp__cell_auto_range_end: isHoverRangeEnd(day),
|
||||
};
|
||||
};
|
||||
|
||||
// Return specific set of classes depending on the config, since we don't need to check for all
|
||||
const getModeClasses = (day: ICalendarDay) => {
|
||||
if (props.range) {
|
||||
if (props.autoRange) return autoRangeClasses(day);
|
||||
if (props.modelAuto) return { ...singleDateClasses(day), ...rangeDateClasses(day) };
|
||||
return rangeDateClasses(day);
|
||||
}
|
||||
if (props.weekPicker) {
|
||||
return weekPickerClasses(day);
|
||||
}
|
||||
|
||||
return singleDateClasses(day);
|
||||
};
|
||||
|
||||
// Get needed classes
|
||||
const getDayClassData = (day: ICalendarDay): Record<string, boolean> => {
|
||||
return {
|
||||
...sharedClasses(day),
|
||||
...getModeClasses(day),
|
||||
[props.dayClass ? props.dayClass(day.value) : '']: true,
|
||||
[props.calendarCellClassName]: !!props.calendarCellClassName,
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
setHoverDate,
|
||||
clearHoverDate,
|
||||
getDayClassData,
|
||||
};
|
||||
};
|
||||
741
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/composables/calendar.ts
generated
vendored
Normal file
741
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/composables/calendar.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,741 @@
|
|||
import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue';
|
||||
import {
|
||||
add,
|
||||
addDays,
|
||||
addMonths,
|
||||
differenceInCalendarDays,
|
||||
eachDayOfInterval,
|
||||
getHours,
|
||||
getISOWeek,
|
||||
getMinutes,
|
||||
getMonth,
|
||||
getSeconds,
|
||||
getYear,
|
||||
isAfter,
|
||||
isBefore,
|
||||
set,
|
||||
setMilliseconds,
|
||||
subMonths,
|
||||
} from 'date-fns';
|
||||
|
||||
import { isModelValueRange, isNumberArray, isRange } from '@/utils/type-guard';
|
||||
import { useUtils } from '@/components/composables/utils';
|
||||
import { isDateAfter, isDateBefore, isDateEqual, getDate, setDateTime } from '@/utils/date-utils';
|
||||
|
||||
import type {
|
||||
ExtendedProps,
|
||||
ICalendarData,
|
||||
ICalendarDay,
|
||||
IMarker,
|
||||
InternalModuleValue,
|
||||
TimeType,
|
||||
VueEmit,
|
||||
} from '@/interfaces';
|
||||
import type { UnwrapRef, Ref } from 'vue';
|
||||
|
||||
// @SONAR_START@
|
||||
export const useCalendar = (
|
||||
props: ExtendedProps,
|
||||
emit: VueEmit,
|
||||
updateFlow: () => void,
|
||||
triggerCalendarTransition: (inst?: number) => void,
|
||||
flowStep: Ref<number>,
|
||||
) => {
|
||||
// @SONAR_STOP@
|
||||
|
||||
const {
|
||||
getDefaultStartTime,
|
||||
isDisabled,
|
||||
sanitizeDate,
|
||||
getWeekFromDate,
|
||||
setDateMonthOrYear,
|
||||
validateMonthYearInRange,
|
||||
defaults,
|
||||
} = useUtils(props);
|
||||
|
||||
// internal model value is updated from this computed property
|
||||
const modelValue = computed({
|
||||
get: (): InternalModuleValue => {
|
||||
return props.internalModelValue;
|
||||
},
|
||||
set: (value: InternalModuleValue): void => {
|
||||
if (!props.readonly && !props.disabled) {
|
||||
emit('update:internal-model-value', value);
|
||||
}
|
||||
},
|
||||
});
|
||||
const tempRange = ref<Date[]>([]);
|
||||
watch(modelValue, () => {
|
||||
if (!props.multiCalendars) {
|
||||
mapInternalModuleValues();
|
||||
}
|
||||
});
|
||||
|
||||
// Calendar data per instance
|
||||
const calendars = ref<ICalendarData[]>([{ month: getMonth(getDate()), year: getYear(getDate()) }]);
|
||||
|
||||
// Time values
|
||||
const time = reactive({
|
||||
hours: props.range ? [getHours(getDate()), getHours(getDate())] : getHours(getDate()),
|
||||
minutes: props.range ? [getMinutes(getDate()), getMinutes(getDate())] : getMinutes(getDate()),
|
||||
seconds: props.range ? [0, 0] : 0,
|
||||
});
|
||||
|
||||
// Get month based on the calendar instance
|
||||
const month = computed(
|
||||
() =>
|
||||
(instance: number): number =>
|
||||
calendars.value[instance] ? calendars.value[instance].month : 0,
|
||||
);
|
||||
|
||||
// Get year based on the calendar instance
|
||||
const year = computed(
|
||||
() =>
|
||||
(instance: number): number =>
|
||||
calendars.value[instance] ? calendars.value[instance].year : 0,
|
||||
);
|
||||
|
||||
const isFlowLastStep = computed(() => {
|
||||
if (props.flow && props.flow.length) {
|
||||
return flowStep.value === props.flow.length;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// Any update for month or year value will go through this function
|
||||
const setCalendarMonthYear = (instance: number, month: number | null, year: number | null): void => {
|
||||
if (!calendars.value[instance]) {
|
||||
calendars.value[instance] = { month: 0, year: 0 };
|
||||
}
|
||||
calendars.value[instance].month = month === null ? calendars.value[instance]?.month : month;
|
||||
calendars.value[instance].year = year === null ? calendars.value[instance]?.year : year;
|
||||
};
|
||||
|
||||
// Any time modification will go through this function
|
||||
const setTime = (property: TimeType, value: number | number[]): void => {
|
||||
time[property] = value;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (!modelValue.value) {
|
||||
if (props.startDate) {
|
||||
setCalendarMonthYear(0, getMonth(getDate(props.startDate)), getYear(getDate(props.startDate)));
|
||||
|
||||
if (defaults.value.multiCalendars) {
|
||||
autoChangeMultiCalendars(0);
|
||||
}
|
||||
}
|
||||
if (defaults.value.startTime) {
|
||||
assignStartTime();
|
||||
}
|
||||
}
|
||||
mapInternalModuleValues(true);
|
||||
});
|
||||
|
||||
/**
|
||||
* Values for times, month and year are managed separately, here we map those values from passed v-model
|
||||
*/
|
||||
const mapInternalModuleValues = (fromMount = false): void => {
|
||||
if (modelValue.value) {
|
||||
if (Array.isArray(modelValue.value)) {
|
||||
tempRange.value = modelValue.value;
|
||||
return assignExistingModelValueArr(fromMount);
|
||||
}
|
||||
return assignSingleValue(modelValue.value);
|
||||
}
|
||||
// On initial empty picker, note: range values are not set on month and year
|
||||
if (props.timePicker) return assignTimePicker();
|
||||
if (props.monthPicker && !props.range) return assignMonthPicker();
|
||||
if (props.yearPicker && !props.range) return assignYearPicker();
|
||||
if (defaults.value.multiCalendars && fromMount && !props.startDate)
|
||||
return assignMonthAndYear(getDate(), fromMount);
|
||||
};
|
||||
|
||||
// Assign month and year values per date
|
||||
const assignMonthAndYear = (date: Date, fromMount = false): void => {
|
||||
if (!defaults.value.multiCalendars || !props.multiStatic || fromMount) {
|
||||
setCalendarMonthYear(0, getMonth(date), getYear(date));
|
||||
}
|
||||
if (defaults.value.multiCalendars) {
|
||||
for (let i = 1; i < defaults.value.multiCalendars; i++) {
|
||||
const prevDate = set(getDate(), { month: month.value(i - 1), year: year.value(i - 1) });
|
||||
const nextMonth = add(prevDate, { months: 1 });
|
||||
calendars.value[i] = { month: getMonth(nextMonth), year: getYear(nextMonth) };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Assign singe value
|
||||
const assignSingleValue = (date: Date): void => {
|
||||
assignMonthAndYear(date);
|
||||
setTime('hours', getHours(date));
|
||||
setTime('minutes', getMinutes(date));
|
||||
setTime('seconds', getSeconds(date));
|
||||
};
|
||||
|
||||
// Assign range values
|
||||
const assignRangeValue = (dates: Date[], fromMount: boolean) => {
|
||||
assignMonthAndYear(dates[0], fromMount);
|
||||
const getValue = (mapper: (date: Date) => number, keys: TimeType): number[] => [
|
||||
mapper(dates[0]),
|
||||
dates[1] ? mapper(dates[1]) : (time[keys] as number[])[1],
|
||||
];
|
||||
setTime('hours', getValue(getHours, 'hours'));
|
||||
setTime('minutes', getValue(getMinutes, 'minutes'));
|
||||
setTime('seconds', getValue(getSeconds, 'seconds'));
|
||||
};
|
||||
|
||||
// Assign range values, or in case of multiDates, set
|
||||
const assignExistingMulti = (dates: Date[], fromMount: boolean) => {
|
||||
if ((props.range || props.weekPicker) && !props.multiDates) {
|
||||
return assignRangeValue(dates, fromMount);
|
||||
}
|
||||
|
||||
if (props.multiDates) {
|
||||
const lastEntry = dates[dates.length - 1];
|
||||
return assignSingleValue(lastEntry);
|
||||
}
|
||||
};
|
||||
|
||||
// Populate values based on model value when it is an array
|
||||
const assignExistingModelValueArr = (fromMount: boolean) => {
|
||||
const dates = modelValue.value as Date[];
|
||||
assignExistingMulti(dates, fromMount);
|
||||
if (defaults.value.multiCalendars && props.multiCalendarsSolo) {
|
||||
handleNextMonthYear();
|
||||
}
|
||||
};
|
||||
|
||||
// On initial empty time picker, assign value
|
||||
const assignTimePicker = () => {
|
||||
assignStartTime();
|
||||
if (!props.range) {
|
||||
modelValue.value = setDateTime(getDate(), time.hours as number, time.minutes as number, getSecondsValue());
|
||||
} else {
|
||||
const hours = time.hours as number[];
|
||||
const minutes = time.minutes as number[];
|
||||
modelValue.value = [
|
||||
setDateTime(getDate(), hours[0], minutes[0], getSecondsValue()),
|
||||
setDateTime(getDate(), hours[1], minutes[1], getSecondsValue(false)),
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
// On initial empty month picker assign value
|
||||
const assignMonthPicker = () => {
|
||||
modelValue.value = setDateMonthOrYear(getDate(), month.value(0), year.value(0));
|
||||
};
|
||||
|
||||
// On initial empty year picker, assign value
|
||||
const assignYearPicker = () => {
|
||||
modelValue.value = getDate();
|
||||
};
|
||||
|
||||
// Check if seconds are enabled, and return proper value
|
||||
const getSecondsValue = (getFirst = true): number => {
|
||||
if (props.enableSeconds) {
|
||||
if (Array.isArray(time.seconds)) {
|
||||
return getFirst ? time.seconds[0] : time.seconds[1];
|
||||
}
|
||||
return time.seconds;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* If start time is provided, assign data.
|
||||
* Note: data is sanitized parameters since prop value can be provided partially
|
||||
*/
|
||||
const assignStartTime = (): void => {
|
||||
const startTime = getDefaultStartTime();
|
||||
if (startTime) {
|
||||
const isMulti = Array.isArray(startTime);
|
||||
const hours = isMulti ? [+startTime[0].hours, +startTime[1].hours] : +startTime.hours;
|
||||
const minutes = isMulti ? [+startTime[0].minutes, +startTime[1].minutes] : +startTime.minutes;
|
||||
const seconds = isMulti ? [+startTime[0].seconds, +startTime[1].seconds] : +startTime.seconds;
|
||||
|
||||
setTime('hours', hours);
|
||||
setTime('minutes', minutes);
|
||||
if (props.enableSeconds) {
|
||||
setTime('seconds', seconds);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Get last date in the multi dates arr
|
||||
const multiDatesLast = (): Date | null => {
|
||||
if (Array.isArray(modelValue.value) && modelValue.value.length) {
|
||||
return modelValue.value[modelValue.value.length - 1];
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extracted method to map month and year
|
||||
*/
|
||||
|
||||
const handleNextMonthYear = (): void => {
|
||||
if (Array.isArray(modelValue.value) && modelValue.value.length === 2) {
|
||||
const date = getDate(
|
||||
getDate(modelValue.value[1] ? modelValue.value[1] : addMonths(modelValue.value[0], 1)),
|
||||
);
|
||||
const [firstMonth, firstYear] = [getMonth(modelValue.value[0]), getYear(modelValue.value[0])];
|
||||
const [secondMonth, secondYear] = [getMonth(modelValue.value[1]), getYear(modelValue.value[1])];
|
||||
|
||||
if (
|
||||
(firstMonth !== secondMonth || (firstMonth === secondMonth && firstYear !== secondYear)) &&
|
||||
props.multiCalendarsSolo
|
||||
) {
|
||||
setCalendarMonthYear(1, getMonth(date), getYear(date));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Add one month to a given date
|
||||
const getNextMonthYear = (date: Date): { month: number; year: number } => {
|
||||
const newDate = addMonths(date, 1);
|
||||
return { month: getMonth(newDate), year: getYear(newDate) };
|
||||
};
|
||||
|
||||
/**
|
||||
* When using next calendar on auto range mode, adjust month and year for both calendars
|
||||
*/
|
||||
const handleNextCalendarAutoRange = (date: string | Date) => {
|
||||
const monthValue = getMonth(getDate(date));
|
||||
const yearValue = getYear(getDate(date));
|
||||
setCalendarMonthYear(0, monthValue, yearValue);
|
||||
if (defaults.value.multiCalendars > 0) {
|
||||
for (let i = 1; i < defaults.value.multiCalendars; i++) {
|
||||
const next = getNextMonthYear(
|
||||
set(getDate(date), { year: month.value(i - 1), month: year.value(i - 1) }),
|
||||
);
|
||||
setCalendarMonthYear(i, next.month, next.year);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleMultiDateSelect = (date: Date): void => {
|
||||
if (modelValue.value && Array.isArray(modelValue.value)) {
|
||||
if (modelValue.value.some((dateVal) => isDateEqual(date, dateVal))) {
|
||||
const value = modelValue.value.filter((dateVal) => !isDateEqual(dateVal, date));
|
||||
modelValue.value = !value.length ? null : value;
|
||||
} else {
|
||||
if (
|
||||
(props.multiDatesLimit && +props.multiDatesLimit > modelValue.value.length) ||
|
||||
!props.multiDatesLimit
|
||||
) {
|
||||
modelValue.value.push(date);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
modelValue.value = [date];
|
||||
}
|
||||
};
|
||||
|
||||
// Get dates between 2 dates
|
||||
const getDaysInBetween = (dateOne: Date, dateTwo: Date) => {
|
||||
// Check if selection is backwards
|
||||
const start = isDateAfter(dateOne, dateTwo) ? dateTwo : dateOne;
|
||||
const end = isDateAfter(dateTwo, dateOne) ? dateTwo : dateOne;
|
||||
return eachDayOfInterval({ start, end });
|
||||
};
|
||||
|
||||
// If min or max range is set, validate given range
|
||||
const checkMinMaxRange = (secondDate: Date): boolean => {
|
||||
if (Array.isArray(modelValue.value) && modelValue.value[0]) {
|
||||
const absoluteDiff = differenceInCalendarDays(secondDate, modelValue.value[0]);
|
||||
const daysInBetween = getDaysInBetween(modelValue.value[0], secondDate);
|
||||
const disabledDates =
|
||||
daysInBetween.length === 1 ? 0 : daysInBetween.filter((date) => isDisabled(date)).length;
|
||||
const diff = Math.abs(absoluteDiff) - disabledDates;
|
||||
if (props.minRange && props.maxRange) return diff >= +props.minRange && diff <= +props.maxRange;
|
||||
if (props.minRange) return diff >= +props.minRange;
|
||||
if (props.maxRange) return diff <= +props.maxRange;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// If the range with fixed start/end is set, update range when the date is selected
|
||||
const getRangeWithFixedDate = (date: Date): Date[] => {
|
||||
if (Array.isArray(modelValue.value) && modelValue.value.length === 2) {
|
||||
if (
|
||||
props.fixedStart &&
|
||||
(isDateAfter(date, modelValue.value[0]) || isDateEqual(date, modelValue.value[0]))
|
||||
) {
|
||||
return [modelValue.value[0], date];
|
||||
}
|
||||
if (props.fixedEnd && (isDateBefore(date, modelValue.value[1]) || isDateEqual(date, modelValue.value[1]))) {
|
||||
return [date, modelValue.value[1]];
|
||||
}
|
||||
emit('invalid-fixed-range', date);
|
||||
return modelValue.value;
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
const autoApply = (): void => {
|
||||
if (props.autoApply && isFlowLastStep.value) {
|
||||
emit('auto-apply');
|
||||
}
|
||||
};
|
||||
|
||||
const selectOnAutoApply = () => {
|
||||
if (props.autoApply) {
|
||||
emit('select-date');
|
||||
}
|
||||
};
|
||||
|
||||
// Check if there are disabled dates for a given range
|
||||
const isDateRangeAllowed = (range: Date[]): boolean => {
|
||||
const datesInBetween = eachDayOfInterval({ start: range[0], end: range[1] });
|
||||
return !datesInBetween.some((date) => isDisabled(date));
|
||||
};
|
||||
|
||||
// Called on selectDate when the week-picker mode is used
|
||||
const handleWeekPickerSelect = (day: ICalendarDay) => {
|
||||
modelValue.value = getWeekFromDate(getDate(day.value));
|
||||
return autoApply();
|
||||
};
|
||||
|
||||
// Called on selectDate when the regular single picker is used
|
||||
const handleSingleDateSelect = (day: ICalendarDay) => {
|
||||
const date = setDateTime(getDate(day.value), time.hours as number, time.minutes as number, getSecondsValue());
|
||||
if (props.multiDates) {
|
||||
handleMultiDateSelect(date);
|
||||
} else {
|
||||
modelValue.value = date;
|
||||
}
|
||||
updateFlow();
|
||||
autoApply();
|
||||
};
|
||||
|
||||
// Before range selecting, ensure that modelValue is properly set
|
||||
const presetTempRange = () => {
|
||||
tempRange.value = modelValue.value ? (modelValue.value as Date[]).slice() : [];
|
||||
if (tempRange.value.length === 2 && !(props.fixedStart || props.fixedEnd)) {
|
||||
tempRange.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
// Handles auto range selecting
|
||||
const handleAutoRange = (day: ICalendarDay, isNext: boolean) => {
|
||||
const autoRange = [getDate(day.value), addDays(getDate(day.value), +props.autoRange)];
|
||||
if (isDateRangeAllowed(autoRange)) {
|
||||
if (isNext) {
|
||||
handleNextCalendarAutoRange(day.value);
|
||||
}
|
||||
tempRange.value = autoRange;
|
||||
}
|
||||
};
|
||||
|
||||
// Handle range with fixed start/end
|
||||
const setFixedDateRange = (day: ICalendarDay) => {
|
||||
if (includesDisabled(day.value)) return;
|
||||
tempRange.value = getRangeWithFixedDate(getDate(day.value));
|
||||
};
|
||||
|
||||
const includesDisabled = (day: Date) => {
|
||||
if (!props.noDisabledRange) return false;
|
||||
const daysBetween = getDaysInBetween(tempRange.value[0], day);
|
||||
return daysBetween.some((date) => isDisabled(date));
|
||||
};
|
||||
|
||||
// Called on selectDate when range mode is used
|
||||
const handleRangeDatesSelect = (day: ICalendarDay, isNext: boolean) => {
|
||||
presetTempRange();
|
||||
if (props.autoRange) return handleAutoRange(day, isNext);
|
||||
if (props.fixedStart || props.fixedEnd) return setFixedDateRange(day);
|
||||
if (!tempRange.value[0]) {
|
||||
tempRange.value[0] = getDate(day.value);
|
||||
} else {
|
||||
if (checkMinMaxRange(getDate(day.value)) && !includesDisabled(day.value)) {
|
||||
if (isDateBefore(getDate(day.value), getDate(tempRange.value[0]))) {
|
||||
tempRange.value.unshift(getDate(day.value));
|
||||
} else {
|
||||
tempRange.value[1] = getDate(day.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Assign time to a temp range item
|
||||
const assignTime = (index: number) => {
|
||||
tempRange.value[index] = setDateTime(
|
||||
tempRange.value[index],
|
||||
(time.hours as number[])[index],
|
||||
(time.minutes as number[])[index],
|
||||
getSecondsValue(index !== 1),
|
||||
);
|
||||
};
|
||||
|
||||
// After range date is select, ensure that proper times are set and assign to modelValue
|
||||
const postRangeSelect = () => {
|
||||
if (tempRange.value.length) {
|
||||
if (tempRange.value[0] && !tempRange.value[1]) {
|
||||
assignTime(0);
|
||||
} else {
|
||||
assignTime(0);
|
||||
assignTime(1);
|
||||
updateFlow();
|
||||
}
|
||||
modelValue.value = tempRange.value.slice();
|
||||
|
||||
if (tempRange.value[0] && tempRange.value[1] && props.autoApply) {
|
||||
emit('auto-apply');
|
||||
}
|
||||
if (tempRange.value[0] && !tempRange.value[1] && props.modelAuto && props.autoApply) {
|
||||
emit('auto-apply');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when the date in the calendar is clicked
|
||||
* Do a necessary formatting and assign value to internal
|
||||
*/
|
||||
const selectDate = (day: UnwrapRef<ICalendarDay>, isNext = false): void => {
|
||||
if (isDisabled(day.value) || (!day.current && props.hideOffsetDates)) return;
|
||||
|
||||
if (props.weekPicker) return handleWeekPickerSelect(day);
|
||||
|
||||
if (!props.range) return handleSingleDateSelect(day);
|
||||
|
||||
if (isNumberArray(time.hours) && isNumberArray(time.minutes) && !props.multiDates) {
|
||||
handleRangeDatesSelect(day, isNext);
|
||||
postRangeSelect();
|
||||
}
|
||||
};
|
||||
|
||||
// Get week number if enabled
|
||||
const getWeekNum = (days: UnwrapRef<ICalendarDay[]>): string | number => {
|
||||
const firstCurrentData = days.find((day) => day.current);
|
||||
if (firstCurrentData) {
|
||||
return getISOWeek(firstCurrentData.value);
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
// In multi calendar mode, when the month/year is changed, sync all calendars
|
||||
const autoChangeMultiCalendars = (instance: number): void => {
|
||||
for (let i = instance - 1; i >= 0; i--) {
|
||||
const date = subMonths(set(getDate(), { month: month.value(i + 1), year: year.value(i + 1) }), 1);
|
||||
setCalendarMonthYear(i, getMonth(date), getYear(date));
|
||||
}
|
||||
for (let i = instance + 1; i <= defaults.value.multiCalendars - 1; i++) {
|
||||
const date = addMonths(set(getDate(), { month: month.value(i - 1), year: year.value(i - 1) }), 1);
|
||||
setCalendarMonthYear(i, getMonth(date), getYear(date));
|
||||
}
|
||||
};
|
||||
|
||||
// Helper function that will return a date with a set month and year for a given calendar instance
|
||||
const getMonthYearValue = (instance: number): Date => {
|
||||
return setDateMonthOrYear(getDate(), month.value(instance), year.value(instance));
|
||||
};
|
||||
|
||||
// Helper function that will return a date with a set time for a given calendar instance
|
||||
const getSetDateTime = (dateValue: Date): Date => {
|
||||
return setDateTime(dateValue, time.hours as number, time.minutes as number, getSecondsValue());
|
||||
};
|
||||
|
||||
// Handles selection of month/year
|
||||
const updateMonthYear = (instance: number, val: { month: number; year: number; fromNav?: boolean }): void => {
|
||||
const isValueChange = props.monthPicker
|
||||
? month.value(instance) !== val.month || !val.fromNav
|
||||
: year.value(instance) !== val.year;
|
||||
|
||||
setCalendarMonthYear(instance, val.month, val.year);
|
||||
|
||||
if (defaults.value.multiCalendars && !props.multiCalendarsSolo) {
|
||||
autoChangeMultiCalendars(instance);
|
||||
}
|
||||
|
||||
if (props.monthPicker || props.yearPicker) {
|
||||
if (props.range) {
|
||||
if (isValueChange) {
|
||||
let rangeDate = modelValue.value ? (modelValue.value as Date[]).slice() : [];
|
||||
if (rangeDate.length === 2 && rangeDate[1] !== null) {
|
||||
rangeDate = [];
|
||||
}
|
||||
if (!rangeDate.length) {
|
||||
rangeDate = [getMonthYearValue(instance)];
|
||||
} else {
|
||||
if (isDateBefore(getMonthYearValue(instance), rangeDate[0])) {
|
||||
rangeDate.unshift(getMonthYearValue(instance));
|
||||
} else {
|
||||
rangeDate[1] = getMonthYearValue(instance);
|
||||
}
|
||||
}
|
||||
modelValue.value = rangeDate;
|
||||
}
|
||||
} else {
|
||||
modelValue.value = getMonthYearValue(instance);
|
||||
}
|
||||
}
|
||||
emit('update-month-year', { instance, month: val.month, year: val.year });
|
||||
triggerCalendarTransition(props.multiCalendarsSolo ? instance : undefined);
|
||||
};
|
||||
|
||||
// Post month/year select, handles auto-apply and flow step, after the selection
|
||||
const monthYearSelect = async (isYear = false): Promise<void> => {
|
||||
if (props.autoApply && (props.monthPicker || props.yearPicker)) {
|
||||
await nextTick();
|
||||
const ignoreClose = props.monthPicker ? isYear : false;
|
||||
if (props.range) {
|
||||
emit('auto-apply', ignoreClose || !modelValue.value || (modelValue.value as Date[]).length === 1);
|
||||
} else {
|
||||
emit('auto-apply', ignoreClose);
|
||||
}
|
||||
}
|
||||
updateFlow();
|
||||
};
|
||||
|
||||
// Handle month change on scroll/arrow/swipe
|
||||
const autoChangeMonth = (increment: number, instance: number) => {
|
||||
const initialDate = set(getDate(), { month: month.value(instance), year: year.value(instance) });
|
||||
const date = increment < 0 ? addMonths(initialDate, 1) : subMonths(initialDate, 1);
|
||||
if (validateMonthYearInRange(getMonth(date), getYear(date), increment < 0, props.preventMinMaxNavigation)) {
|
||||
setCalendarMonthYear(instance, getMonth(date), getYear(date));
|
||||
if (defaults.value.multiCalendars && !props.multiCalendarsSolo) {
|
||||
autoChangeMultiCalendars(instance);
|
||||
}
|
||||
emit('update-month-year', { instance, month: getMonth(date), year: getYear(date) });
|
||||
triggerCalendarTransition();
|
||||
}
|
||||
};
|
||||
|
||||
// Same logic done twice with the time update, however do a range checks before applying are done
|
||||
const handleTimeUpdate = (dateValue: Date | Date[]): void => {
|
||||
if (
|
||||
isModelValueRange(dateValue) &&
|
||||
isModelValueRange(modelValue.value) &&
|
||||
isNumberArray(time.hours) &&
|
||||
isNumberArray(time.minutes)
|
||||
) {
|
||||
if (dateValue[0] && modelValue.value[0]) {
|
||||
modelValue.value[0] = setDateTime(dateValue[0], time.hours[0], time.minutes[0], getSecondsValue());
|
||||
}
|
||||
if (dateValue[1] && modelValue.value[1]) {
|
||||
modelValue.value[1] = setDateTime(dateValue[1], time.hours[1], time.minutes[1], getSecondsValue(false));
|
||||
}
|
||||
} else if (props.multiDates && Array.isArray(modelValue.value)) {
|
||||
modelValue.value[modelValue.value.length - 1] = getSetDateTime(dateValue as Date);
|
||||
} else if (!props.range && !isRange(dateValue)) {
|
||||
modelValue.value = getSetDateTime(dateValue);
|
||||
}
|
||||
emit('time-update');
|
||||
};
|
||||
|
||||
// Called on event when the time value is changed
|
||||
const updateTime = (value: number | number[], isHours = true, isSeconds = false) => {
|
||||
const hoursCp = isHours ? value : time.hours;
|
||||
const minutesCp = !isHours && !isSeconds ? value : time.minutes;
|
||||
const secondsCp = isSeconds ? value : time.seconds;
|
||||
if (
|
||||
props.range &&
|
||||
isRange(modelValue.value) &&
|
||||
isNumberArray(hoursCp) &&
|
||||
isNumberArray(minutesCp) &&
|
||||
isNumberArray(secondsCp) &&
|
||||
!props.disableTimeRangeValidation
|
||||
) {
|
||||
const setTime = (index: number) =>
|
||||
setDateTime((modelValue.value as Date[])[index], hoursCp[index], minutesCp[index], secondsCp[index]);
|
||||
|
||||
const resetMilliseconds = (index: number) => setMilliseconds((modelValue.value as Date[])[index], 0);
|
||||
if (
|
||||
isDateEqual(modelValue.value[0], modelValue.value[1]) &&
|
||||
(isAfter(setTime(0), resetMilliseconds(1)) || isBefore(setTime(1), resetMilliseconds(0)))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
setTime('hours', hoursCp);
|
||||
setTime('minutes', minutesCp);
|
||||
setTime('seconds', secondsCp);
|
||||
|
||||
if (modelValue.value) {
|
||||
if (props.multiDates) {
|
||||
const lastEntry = multiDatesLast();
|
||||
if (lastEntry) {
|
||||
handleTimeUpdate(lastEntry);
|
||||
}
|
||||
} else {
|
||||
handleTimeUpdate(modelValue.value);
|
||||
}
|
||||
} else if (props.timePicker) {
|
||||
handleTimeUpdate(props.range ? [getDate(), getDate()] : getDate());
|
||||
}
|
||||
updateFlow();
|
||||
};
|
||||
|
||||
// Handle mouse scroll
|
||||
const handleScroll = (event: WheelEvent, instance: number): void => {
|
||||
if (props.monthChangeOnScroll) {
|
||||
autoChangeMonth(props.monthChangeOnScroll !== 'inverse' ? -event.deltaY : event.deltaY, instance);
|
||||
}
|
||||
};
|
||||
|
||||
// Handle arrow key
|
||||
const handleArrow = (arrow: 'left' | 'right', instance: number, vertical = false): void => {
|
||||
if (props.monthChangeOnArrows && props.vertical === vertical) {
|
||||
handleSwipe(arrow, instance);
|
||||
}
|
||||
};
|
||||
|
||||
// Handle touch swipe
|
||||
const handleSwipe = (direction: 'left' | 'right', instance: number): void => {
|
||||
autoChangeMonth(direction === 'right' ? -1 : 1, instance);
|
||||
};
|
||||
|
||||
// Check if the calendar day has a marker
|
||||
const getMarker = (date: UnwrapRef<ICalendarDay>): IMarker | undefined =>
|
||||
props.markers.find((marker) => isDateEqual(sanitizeDate(date.value), sanitizeDate(marker.date)));
|
||||
|
||||
// Select current date on now button
|
||||
const selectCurrentDate = (): void => {
|
||||
if (!props.range) {
|
||||
modelValue.value = getDate();
|
||||
} else {
|
||||
if (modelValue.value && Array.isArray(modelValue.value) && modelValue.value[0]) {
|
||||
modelValue.value = isDateBefore(getDate(), modelValue.value[0])
|
||||
? [getDate(), modelValue.value[0]]
|
||||
: [modelValue.value[0], getDate()];
|
||||
} else {
|
||||
modelValue.value = [getDate()];
|
||||
}
|
||||
}
|
||||
selectOnAutoApply();
|
||||
};
|
||||
|
||||
// Called when the preset range is clicked
|
||||
const presetDateRange = (dates: Date[] | string[], hasSlot?: boolean): void => {
|
||||
if (hasSlot) return;
|
||||
if (dates.length && dates.length <= 2 && props.range) {
|
||||
modelValue.value = dates.map((date) => getDate(date));
|
||||
selectOnAutoApply();
|
||||
if (props.multiCalendars) {
|
||||
nextTick().then(() => mapInternalModuleValues(true));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
time,
|
||||
month,
|
||||
year,
|
||||
modelValue,
|
||||
calendars,
|
||||
monthYearSelect,
|
||||
isDisabled,
|
||||
updateTime,
|
||||
getWeekNum,
|
||||
selectDate,
|
||||
updateMonthYear,
|
||||
handleScroll,
|
||||
getMarker,
|
||||
handleArrow,
|
||||
handleSwipe,
|
||||
selectCurrentDate,
|
||||
presetDateRange,
|
||||
};
|
||||
};
|
||||
321
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/composables/external-internal-mapper.ts
generated
vendored
Normal file
321
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/composables/external-internal-mapper.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,321 @@
|
|||
import { ref, toRef, watch } from 'vue';
|
||||
import { format, getHours, getMinutes, getMonth, getSeconds, getYear, parse, setYear } from 'date-fns';
|
||||
|
||||
import { dateToUtc, getDate, setDateTime } from '@/utils/date-utils';
|
||||
import { convertType, errors } from '@/utils/util';
|
||||
import { useUtils } from '@/components/composables';
|
||||
|
||||
import type { ModelValue, VueEmit, TimeModel, MonthModel, ModelTypeConverted } from '@/interfaces';
|
||||
import type { AllPropsType } from '@/utils/props';
|
||||
import type { Ref } from 'vue';
|
||||
|
||||
/**
|
||||
* Handles values from external to internal and vise versa
|
||||
*/
|
||||
export const useExternalInternalMapper = (emit: VueEmit, props: AllPropsType, isInputFocused: Ref<boolean>) => {
|
||||
const internalModelValue = ref();
|
||||
const {
|
||||
getZonedToUtc,
|
||||
getZonedDate,
|
||||
formatDate,
|
||||
getDefaultPattern,
|
||||
checkRangeEnabled,
|
||||
checkPartialRangeValue,
|
||||
isValidDate,
|
||||
setDateMonthOrYear,
|
||||
defaults,
|
||||
} = useUtils(props);
|
||||
|
||||
const inputValue = ref('');
|
||||
const formatRef = toRef(props, 'format');
|
||||
|
||||
watch(internalModelValue, () => {
|
||||
emit('internal-model-change', internalModelValue.value);
|
||||
});
|
||||
|
||||
watch(formatRef, () => {
|
||||
formatInputValue();
|
||||
});
|
||||
|
||||
const getTimeVal = (date?: Date): TimeModel | ModelTypeConverted => {
|
||||
const dateValue = date || getDate();
|
||||
if (props.modelType) return toModelType(dateValue);
|
||||
return {
|
||||
hours: getHours(dateValue),
|
||||
minutes: getMinutes(dateValue),
|
||||
seconds: props.enableSeconds ? getSeconds(dateValue) : 0,
|
||||
};
|
||||
};
|
||||
|
||||
const getMonthVal = (date: Date): MonthModel | ModelTypeConverted => {
|
||||
if (props.modelType) return toModelType(date);
|
||||
return { month: getMonth(date), year: getYear(date) };
|
||||
};
|
||||
|
||||
const mapYearExternalToInternal = (value: number | number[]): Date | Date[] => {
|
||||
if (Array.isArray(value)) {
|
||||
return checkRangeEnabled(() => [
|
||||
setYear(getDate(), value[0]),
|
||||
value[1] ? setYear(getDate(), value[1]) : checkPartialRangeValue(),
|
||||
]);
|
||||
}
|
||||
return setYear(getDate(), +value);
|
||||
};
|
||||
|
||||
const convertCustomModeType = (value: unknown, defaultValue: Date): Date => {
|
||||
const shouldConvert = (typeof value === 'string' || typeof value === 'number') && props.modelType;
|
||||
if (shouldConvert) return parseModelType(value);
|
||||
return defaultValue;
|
||||
};
|
||||
|
||||
const mapTimeExternalToInternal = (value: TimeModel | TimeModel[]): Date | Date[] => {
|
||||
// Unlike in other modes, partial range is not supported here, since the time needs to be defined on both time inputs
|
||||
if (Array.isArray(value)) {
|
||||
return [
|
||||
convertCustomModeType(
|
||||
value[0],
|
||||
setDateTime(null, +value[0].hours, +value[0].minutes, value[0].seconds),
|
||||
),
|
||||
convertCustomModeType(
|
||||
value[1],
|
||||
setDateTime(null, +value[1].hours, +value[1].minutes, value[1].seconds),
|
||||
),
|
||||
];
|
||||
}
|
||||
return convertCustomModeType(value, setDateTime(null, value.hours, value.minutes, value.seconds));
|
||||
};
|
||||
|
||||
const mapMonthExternalToInternal = (value: MonthModel | MonthModel[]): Date | Date[] => {
|
||||
if (Array.isArray(value)) {
|
||||
return checkRangeEnabled(() => [
|
||||
convertCustomModeType(value[0], setDateMonthOrYear(null, +value[0].month, +value[0].year)),
|
||||
convertCustomModeType(
|
||||
value[1],
|
||||
value[1] ? setDateMonthOrYear(null, +value[1].month, +value[1].year) : checkPartialRangeValue(),
|
||||
),
|
||||
]);
|
||||
}
|
||||
return convertCustomModeType(value, setDateMonthOrYear(null, +value.month, +value.year));
|
||||
};
|
||||
|
||||
// Map external multi dates format to internal model value
|
||||
const mapMultiDateExternalToInternal = (value: Date[] | string[]) => {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((date) => parseModelType(date));
|
||||
}
|
||||
throw new Error(errors.dateArr('multi-dates'));
|
||||
};
|
||||
|
||||
// Map external week picker format to internal model value
|
||||
const mapWeekExternalToInternal = (value: Date[]) => {
|
||||
if (Array.isArray(value)) {
|
||||
return [getDate(value[0]), getDate(value[1])];
|
||||
}
|
||||
throw new Error(errors.dateArr('week-picker'));
|
||||
};
|
||||
|
||||
// Map external format to internal model value for range and single picker
|
||||
const mapDateExternalToInternal = (value: Date | Date[]) => {
|
||||
if (props.modelAuto) {
|
||||
if (Array.isArray(value)) return [parseModelType(value[0]), parseModelType(value[1])];
|
||||
// In case of auto-apply, if we add null, it will never select range
|
||||
return props.autoApply ? [parseModelType(value)] : [parseModelType(value), null];
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return checkRangeEnabled(() => [
|
||||
parseModelType(value[0]),
|
||||
value[1] ? parseModelType(value[1]) : checkPartialRangeValue(),
|
||||
]);
|
||||
}
|
||||
return parseModelType(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* When emitting value back to host, if partial date is enabled and second value is not selected
|
||||
* auto add 'null' value as second value
|
||||
*/
|
||||
const sanitizeModelValue = () => {
|
||||
if (Array.isArray(internalModelValue.value) && props.range && internalModelValue.value.length === 1) {
|
||||
internalModelValue.value.push(checkPartialRangeValue());
|
||||
}
|
||||
};
|
||||
|
||||
// Shared method, create mapped array of dates to emit
|
||||
const getRangeEmitValue = (): Date[] => {
|
||||
const modelValue = internalModelValue.value as Date[];
|
||||
return [
|
||||
toModelType(modelValue[0]),
|
||||
modelValue[1] ? toModelType(modelValue[1]) : checkPartialRangeValue(),
|
||||
] as Date[];
|
||||
};
|
||||
|
||||
// Get value for model-auto, single or array, internally it is always handled as an array
|
||||
const getModelAutoForExternal = () =>
|
||||
(internalModelValue.value as Date[])[1]
|
||||
? getRangeEmitValue()
|
||||
: toModelType(convertType(internalModelValue.value[0]));
|
||||
|
||||
// Map internal value for external
|
||||
const getMultiDatesForExternal = () =>
|
||||
((internalModelValue.value as Date[]) || []).map((date) => toModelType(date) as string);
|
||||
|
||||
// Parent internal to external function mapper that will return proper date format based on provided config
|
||||
const mapInternalDatesToExternal = () => {
|
||||
sanitizeModelValue();
|
||||
if (props.modelAuto) return getModelAutoForExternal();
|
||||
if (props.multiDates) return getMultiDatesForExternal();
|
||||
if (Array.isArray(internalModelValue.value)) {
|
||||
return checkRangeEnabled(() => getRangeEmitValue());
|
||||
}
|
||||
return toModelType(convertType(internalModelValue.value));
|
||||
};
|
||||
|
||||
const mapExternalToInternal = (value: ModelValue) => {
|
||||
if (!value) return null;
|
||||
if (props.timePicker) return mapTimeExternalToInternal(convertType(value));
|
||||
if (props.monthPicker) return mapMonthExternalToInternal(convertType(value));
|
||||
if (props.yearPicker) return mapYearExternalToInternal(convertType(value));
|
||||
if (props.multiDates) return mapMultiDateExternalToInternal(convertType(value));
|
||||
if (props.weekPicker) return mapWeekExternalToInternal(convertType(value));
|
||||
return mapDateExternalToInternal(convertType(value));
|
||||
};
|
||||
|
||||
/**
|
||||
* Map external values to dates that will be used internally by the datepicker
|
||||
* Also does the validation of the provided value, if invalid it will use null as a default or an empty value
|
||||
*/
|
||||
const parseExternalModelValue = (value: ModelValue): void => {
|
||||
const mappedDate = mapExternalToInternal(value);
|
||||
|
||||
if (isValidDate(convertType(mappedDate))) {
|
||||
internalModelValue.value = convertType(mappedDate);
|
||||
formatInputValue();
|
||||
} else {
|
||||
internalModelValue.value = null;
|
||||
inputValue.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
const formatRangeTextInput = () => {
|
||||
const formatter = (value: Date) => format(value, defaults.value.textInputOptions?.format as string);
|
||||
return `${formatter(internalModelValue.value[0])} ${defaults.value.textInputOptions?.rangeSeparator} ${
|
||||
internalModelValue.value[1] ? formatter(internalModelValue.value[1]) : ''
|
||||
}`;
|
||||
};
|
||||
|
||||
// If text input format is enabled, on input focus, set the text to the given format
|
||||
const formatForTextInput = () => {
|
||||
if (isInputFocused.value && internalModelValue.value) {
|
||||
if (Array.isArray(internalModelValue.value)) return formatRangeTextInput();
|
||||
return format(internalModelValue.value, defaults.value.textInputOptions?.format as string);
|
||||
}
|
||||
return formatDate(internalModelValue.value);
|
||||
};
|
||||
|
||||
// Get proper input value depending on the mode
|
||||
const getInputValue = (): string => {
|
||||
if (!internalModelValue.value) return '';
|
||||
if (props.multiDates) return (internalModelValue.value as Date[]).map((date) => formatDate(date)).join('; ');
|
||||
if (props.textInput && typeof defaults.value.textInputOptions?.format === 'string') return formatForTextInput();
|
||||
return formatDate(internalModelValue.value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Map the date value(s) to the human-readable text for the input field
|
||||
*/
|
||||
const formatInputValue = (): void => {
|
||||
if (!props.format || typeof props.format === 'string') {
|
||||
inputValue.value = getInputValue();
|
||||
} else {
|
||||
inputValue.value = props.format(internalModelValue.value as Date | Date[]);
|
||||
}
|
||||
};
|
||||
|
||||
const parseModelType = (value: string | number | Date): Date => {
|
||||
if (props.utc) {
|
||||
const toDate = new Date(value);
|
||||
return props.utc === 'preserve' ? new Date(toDate.getTime() + toDate.getTimezoneOffset() * 60000) : toDate;
|
||||
}
|
||||
if (props.modelType) {
|
||||
if (props.modelType === 'date' || props.modelType === 'timestamp') return getZonedDate(new Date(value));
|
||||
|
||||
if (props.modelType === 'format' && (typeof props.format === 'string' || !props.format))
|
||||
return parse(value as string, getDefaultPattern(), new Date());
|
||||
|
||||
return getZonedDate(parse(value as string, props.modelType, new Date()));
|
||||
}
|
||||
|
||||
return getZonedDate(new Date(value));
|
||||
};
|
||||
|
||||
const toModelType = (val: Date): string | number | Date => {
|
||||
if (props.utc) {
|
||||
return dateToUtc(val, props.utc === 'preserve');
|
||||
}
|
||||
if (props.modelType) {
|
||||
if (props.modelType === 'timestamp') return +getZonedToUtc(val);
|
||||
|
||||
if (props.modelType === 'format' && (typeof props.format === 'string' || !props.format))
|
||||
return formatDate(getZonedToUtc(val));
|
||||
|
||||
return formatDate(getZonedToUtc(val), props.modelType);
|
||||
}
|
||||
return getZonedToUtc(val);
|
||||
};
|
||||
|
||||
const emitValue = (value: ModelValue): void => {
|
||||
emit('update:model-value', value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Shared code between modes that support range and partial range
|
||||
* Maps the internal model-value into appropriate output
|
||||
*/
|
||||
const mapInternalToSpecificExternal = <T extends (val: Date) => ReturnType<T> | ReturnType<T>[]>(mapper: T) => {
|
||||
if (Array.isArray(internalModelValue.value)) {
|
||||
return [
|
||||
mapper(internalModelValue.value[0]),
|
||||
internalModelValue.value[1] ? mapper(internalModelValue.value[1]) : checkPartialRangeValue(),
|
||||
];
|
||||
}
|
||||
return mapper(convertType(internalModelValue.value));
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const modeEmitter = (mapper: any) => emitValue(convertType(mapInternalToSpecificExternal(mapper)));
|
||||
|
||||
/**
|
||||
* When date is selected, emit event to update modelValue on external,
|
||||
* and format input value
|
||||
*/
|
||||
const emitModelValue = (): void => {
|
||||
formatInputValue();
|
||||
|
||||
if (props.monthPicker) return modeEmitter(getMonthVal);
|
||||
if (props.timePicker) return modeEmitter(getTimeVal);
|
||||
if (props.yearPicker) return modeEmitter(getYear);
|
||||
if (props.weekPicker) return emitValue(internalModelValue.value);
|
||||
return emitValue(mapInternalDatesToExternal());
|
||||
};
|
||||
|
||||
// Check if there is any selection before emitting value, to prevent null setting
|
||||
const checkBeforeEmit = (): boolean => {
|
||||
if (internalModelValue.value) {
|
||||
if (props.range) {
|
||||
if (props.partialRange) return internalModelValue.value.length >= 1;
|
||||
return internalModelValue.value.length === 2;
|
||||
}
|
||||
return !!internalModelValue.value;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
return {
|
||||
inputValue,
|
||||
internalModelValue,
|
||||
checkBeforeEmit,
|
||||
parseExternalModelValue,
|
||||
formatInputValue,
|
||||
emitModelValue,
|
||||
};
|
||||
};
|
||||
9
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/composables/index.ts
generated
vendored
Normal file
9
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/composables/index.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
export * from './state';
|
||||
export * from './utils';
|
||||
export * from './arrow-navigate';
|
||||
export * from './calendar';
|
||||
export * from './external-internal-mapper';
|
||||
export * from './month-year';
|
||||
export * from './position';
|
||||
export * from './slots';
|
||||
export * from './transition';
|
||||
66
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/composables/month-year.ts
generated
vendored
Normal file
66
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/composables/month-year.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import { computed } from 'vue';
|
||||
import { addMonths, addYears, getMonth, getYear, set, subMonths, subYears } from 'date-fns';
|
||||
|
||||
import { useUtils } from '@/components/composables';
|
||||
|
||||
import type { ExtendedProps, VueEmit } from '@/interfaces';
|
||||
|
||||
export const useMontYearPick = (props: { month: number; year: number } & ExtendedProps, emit: VueEmit) => {
|
||||
const { validateMonthYearInRange, validateMaxDate, validateMinDate, defaults } = useUtils(props);
|
||||
|
||||
const recursiveMonthAdjust = (date: Date, increment: boolean): Date => {
|
||||
let monthDate = date;
|
||||
if (defaults.value.filters.months.includes(getMonth(monthDate))) {
|
||||
monthDate = increment ? addMonths(date, 1) : subMonths(date, 1);
|
||||
return recursiveMonthAdjust(monthDate, increment);
|
||||
}
|
||||
return monthDate;
|
||||
};
|
||||
|
||||
const recursiveYearAdjust = (date: Date, increment: boolean): Date => {
|
||||
let yearDate = date;
|
||||
if (defaults.value.filters.years.includes(getYear(yearDate))) {
|
||||
yearDate = increment ? addYears(date, 1) : subYears(date, 1);
|
||||
return recursiveYearAdjust(yearDate, increment);
|
||||
}
|
||||
return yearDate;
|
||||
};
|
||||
|
||||
const handleMonthYearChange = (isNext: boolean): void => {
|
||||
const initialDate = set(new Date(), { month: props.month, year: props.year });
|
||||
let date = isNext ? addMonths(initialDate, 1) : subMonths(initialDate, 1);
|
||||
|
||||
let month = getMonth(date);
|
||||
let year = getYear(date);
|
||||
|
||||
if (defaults.value.filters.months.includes(month)) {
|
||||
date = recursiveMonthAdjust(date, isNext);
|
||||
month = getMonth(date);
|
||||
year = getYear(date);
|
||||
}
|
||||
|
||||
if (defaults.value.filters.years.includes(year)) {
|
||||
date = recursiveYearAdjust(date, isNext);
|
||||
year = getYear(date);
|
||||
}
|
||||
if (validateMonthYearInRange(month, year, isNext, props.preventMinMaxNavigation)) {
|
||||
updateMonthYear(month, year);
|
||||
}
|
||||
};
|
||||
|
||||
const updateMonthYear = (month: number, year: number): void => {
|
||||
emit('update-month-year', { month, year });
|
||||
};
|
||||
|
||||
const isDisabled = computed(() => (next: boolean) => {
|
||||
if (!props.preventMinMaxNavigation) return false;
|
||||
if (next && !props.maxDate) return false;
|
||||
if (!next && !props.minDate) return false;
|
||||
const currentDate = set(new Date(), { month: props.month, year: props.year });
|
||||
const compareDate = next ? addMonths(currentDate, 1) : subMonths(currentDate, 1);
|
||||
const monthYear: [number, number] = [getMonth(compareDate), getYear(compareDate)];
|
||||
return next ? !validateMaxDate(...monthYear) : !validateMinDate(...monthYear);
|
||||
});
|
||||
|
||||
return { handleMonthYearChange, isDisabled, updateMonthYear };
|
||||
};
|
||||
201
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/composables/position.ts
generated
vendored
Normal file
201
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/composables/position.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
import { ref, toRef, watch } from 'vue';
|
||||
|
||||
import { OpenPosition } from '@/interfaces';
|
||||
import { unrefElement } from '@/utils/util';
|
||||
|
||||
import type { ComponentRef, VueEmit, CustomAltPosition } from '@/interfaces';
|
||||
import type { AllPropsType } from '@/utils/props';
|
||||
|
||||
/**
|
||||
* Extracted code from the main component, used for calculating the position of the menu
|
||||
*/
|
||||
export const usePosition = (menuRef: ComponentRef, inputRef: ComponentRef, emit: VueEmit, props: AllPropsType) => {
|
||||
const menuPosition = ref<{ top: string; left: string; transform: string; position?: string }>({
|
||||
top: '0',
|
||||
left: '0',
|
||||
transform: 'none',
|
||||
});
|
||||
const openOnTop = ref(false);
|
||||
const centered = toRef(props, 'teleportCenter');
|
||||
|
||||
watch(centered, () => {
|
||||
setMenuPosition();
|
||||
});
|
||||
|
||||
// Get correct offset of an element
|
||||
const getOffset = (el: HTMLElement): { top: number; left: number } => {
|
||||
const rect = el.getBoundingClientRect();
|
||||
return {
|
||||
left: rect.left + window.scrollX,
|
||||
top: rect.top + window.scrollY,
|
||||
};
|
||||
};
|
||||
|
||||
// Use alternative position calculation, for specific cases on nested relative elements
|
||||
const getOffsetAlt = (el: HTMLElement): { top: number; left: number } => {
|
||||
const rect = el.getBoundingClientRect();
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) {
|
||||
x += el.offsetLeft - el.scrollLeft;
|
||||
y = rect.top + el.scrollTop;
|
||||
el = el.offsetParent as HTMLElement;
|
||||
}
|
||||
return { top: y, left: x };
|
||||
};
|
||||
|
||||
const setPositionRight = (left: number, width: number): void => {
|
||||
menuPosition.value.left = `${left + width}px`;
|
||||
menuPosition.value.transform = `translateX(-100%)`;
|
||||
};
|
||||
|
||||
const setPositionLeft = (left: number): void => {
|
||||
menuPosition.value.left = `${left}px`;
|
||||
menuPosition.value.transform = `translateX(0)`;
|
||||
};
|
||||
|
||||
const setHorizontalPositioning = (left: number, width: number, initial = false): void => {
|
||||
if (props.position === OpenPosition.left) {
|
||||
setPositionLeft(left);
|
||||
}
|
||||
|
||||
if (props.position === OpenPosition.right) {
|
||||
setPositionRight(left, width);
|
||||
}
|
||||
|
||||
if (props.position === OpenPosition.center) {
|
||||
menuPosition.value.left = `${left + width / 2}px`;
|
||||
menuPosition.value.transform = initial ? `translate(-50%, -50%)` : `translateX(-50%)`;
|
||||
}
|
||||
};
|
||||
|
||||
const getInputPositions = (inputEl: HTMLElement) => {
|
||||
const { width, height } = inputEl.getBoundingClientRect();
|
||||
const { top, left } = props.altPosition ? getOffsetAlt(inputEl) : getOffset(inputEl);
|
||||
return { top, left, width, height };
|
||||
};
|
||||
|
||||
// On initial component map, set menu starting position to the middle of the input
|
||||
const setInitialPosition = () => {
|
||||
const inputEl = unrefElement(inputRef);
|
||||
if (inputEl) {
|
||||
const { top, left, width, height } = getInputPositions(inputEl);
|
||||
|
||||
menuPosition.value.top = `${top + height / 2}px`;
|
||||
menuPosition.value.transform = `translateY(-50%)`;
|
||||
setHorizontalPositioning(left, width, true);
|
||||
}
|
||||
};
|
||||
|
||||
const teleportCenter = () => {
|
||||
menuPosition.value.left = `50%`;
|
||||
menuPosition.value.top = `50%`;
|
||||
menuPosition.value.transform = `translate(-50%, -50%)`;
|
||||
menuPosition.value.position = `fixed`;
|
||||
};
|
||||
|
||||
const customAltPosition = () => {
|
||||
const el = unrefElement(inputRef);
|
||||
menuPosition.value = (props.altPosition as CustomAltPosition)(el);
|
||||
};
|
||||
|
||||
/**
|
||||
* Main call, when input is clicked, opens the menu on the first entry
|
||||
* Recalculate param is added when the position is recalculated on scroll or resize
|
||||
*/
|
||||
const setMenuPosition = (recalculate = true): void => {
|
||||
if (!props.inline) {
|
||||
if (centered.value) return teleportCenter();
|
||||
|
||||
if (props.altPosition && typeof props.altPosition !== 'boolean') return customAltPosition();
|
||||
|
||||
if (recalculate) {
|
||||
emit('recalculate-position');
|
||||
}
|
||||
return calculateMenuPosition();
|
||||
}
|
||||
};
|
||||
|
||||
const setLeftRightPosition = ({
|
||||
inputEl,
|
||||
menuEl,
|
||||
left,
|
||||
width,
|
||||
}: {
|
||||
inputEl: HTMLElement;
|
||||
menuEl: HTMLElement;
|
||||
left: number;
|
||||
width: number;
|
||||
}) => {
|
||||
if (window.screen.width > 768) {
|
||||
setHorizontalPositioning(left, width);
|
||||
}
|
||||
|
||||
autoLeftRight(inputEl, menuEl);
|
||||
};
|
||||
|
||||
// Set menu position bellow input
|
||||
const setBottomPosition = (inputEl: HTMLElement, menuEl: HTMLElement) => {
|
||||
const { top: offset, left, height, width } = getInputPositions(inputEl);
|
||||
|
||||
menuPosition.value.top = `${height + offset + +props.offset}px`;
|
||||
setLeftRightPosition({ inputEl, menuEl, left, width });
|
||||
|
||||
openOnTop.value = false;
|
||||
};
|
||||
|
||||
// Set menu position above the input
|
||||
const setTopPosition = (inputEl: HTMLElement, menuEl: HTMLElement) => {
|
||||
const { top: offset, left, width } = getInputPositions(inputEl);
|
||||
const { height } = menuEl.getBoundingClientRect();
|
||||
|
||||
menuPosition.value.top = `${offset - height - +props.offset}px`;
|
||||
setLeftRightPosition({ inputEl, menuEl, left, width });
|
||||
|
||||
openOnTop.value = true;
|
||||
};
|
||||
|
||||
// Set auto left-right if the menu is out of the screen
|
||||
const autoLeftRight = (inputEl: HTMLElement, menuEl: HTMLElement) => {
|
||||
if (props.autoPosition) {
|
||||
const { left, width } = getInputPositions(inputEl);
|
||||
const { left: menuLeft, right: menuRight } = menuEl.getBoundingClientRect();
|
||||
|
||||
if (menuLeft < 0) return setPositionLeft(left);
|
||||
if (menuRight > document.documentElement.clientWidth) return setPositionRight(left, width);
|
||||
}
|
||||
};
|
||||
|
||||
// If auto-position is enabled, perform calculation to fit menu on the screen
|
||||
const setAutoPosition = (inputEl: HTMLElement, menuEl: HTMLElement) => {
|
||||
const { height: menuHeight } = menuEl.getBoundingClientRect();
|
||||
const { top: inputTop, height: inputHeight } = inputEl.getBoundingClientRect();
|
||||
|
||||
const fullHeight = window.innerHeight;
|
||||
const spaceBottom = fullHeight - inputTop - inputHeight;
|
||||
const spaceTop = inputTop;
|
||||
|
||||
// If there is enough space at the bottom, open at the bottom
|
||||
if (menuHeight <= spaceBottom) return setBottomPosition(inputEl, menuEl);
|
||||
// If there is not enough space at the bottom but there is on top, set position on top
|
||||
if (menuHeight > spaceBottom && menuHeight <= spaceTop) return setTopPosition(inputEl, menuEl);
|
||||
// If we pass both check, it means there is not enough space above and bellow the input
|
||||
// Position where there is more space available
|
||||
if (spaceBottom >= spaceTop) return setBottomPosition(inputEl, menuEl);
|
||||
return setTopPosition(inputEl, menuEl);
|
||||
};
|
||||
|
||||
// Parent function that will perform check on which calculation function to invoke
|
||||
const calculateMenuPosition = () => {
|
||||
const inputEl = unrefElement(inputRef);
|
||||
const menuEl = unrefElement(menuRef);
|
||||
if (inputEl && menuEl) {
|
||||
if (props.autoPosition) {
|
||||
return setAutoPosition(inputEl, menuEl);
|
||||
}
|
||||
return setBottomPosition(inputEl, menuEl);
|
||||
}
|
||||
};
|
||||
|
||||
return { openOnTop, menuPosition, setMenuPosition, setInitialPosition };
|
||||
};
|
||||
73
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/composables/slots.ts
generated
vendored
Normal file
73
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/composables/slots.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import type { Slots } from 'vue';
|
||||
import type { PresetRange } from '@/interfaces';
|
||||
|
||||
export const slots = [
|
||||
{ name: 'clock-icon', use: ['time', 'calendar'] },
|
||||
{ name: 'arrow-left', use: ['month-year', 'calendar'] },
|
||||
{ name: 'arrow-right', use: ['month-year', 'calendar'] },
|
||||
{ name: 'arrow-up', use: ['time', 'calendar'] },
|
||||
{ name: 'arrow-down', use: ['time', 'calendar'] },
|
||||
{ name: 'calendar-icon', use: ['month-year', 'time', 'calendar'] },
|
||||
{ name: 'day', use: ['calendar'] },
|
||||
{ name: 'month-overlay-value', use: ['calendar', 'month-year'] },
|
||||
{ name: 'year-overlay-value', use: ['calendar', 'month-year'] },
|
||||
{ name: 'year-overlay', use: ['month-year'] },
|
||||
{ name: 'month-overlay', use: ['month-year'] },
|
||||
{ name: 'month-overlay-header', use: ['month-year'] },
|
||||
{ name: 'year-overlay-header', use: ['month-year'] },
|
||||
{ name: 'hours-overlay-value', use: ['calendar', 'time'] },
|
||||
{ name: 'minutes-overlay-value', use: ['calendar', 'time'] },
|
||||
{ name: 'seconds-overlay-value', use: ['calendar', 'time'] },
|
||||
{ name: 'hours', use: ['calendar', 'time'] },
|
||||
{ name: 'minutes', use: ['calendar', 'time'] },
|
||||
{ name: 'month', use: ['calendar', 'month-year'] },
|
||||
{ name: 'year', use: ['calendar', 'month-year'] },
|
||||
{ name: 'action-select', use: ['action'] },
|
||||
{ name: 'action-preview', use: ['action'] },
|
||||
{ name: 'calendar-header', use: ['calendar'] },
|
||||
{ name: 'marker-tooltip', use: ['calendar'] },
|
||||
{ name: 'now-button', use: [] },
|
||||
{ name: 'time-picker-overlay', use: ['calendar', 'time'] },
|
||||
{ name: 'am-pm-button', use: ['calendar', 'time'] },
|
||||
{ name: 'left-sidebar', use: ['menu'] },
|
||||
{ name: 'right-sidebar', use: ['menu'] },
|
||||
{ name: 'month-year', use: ['month-year'] },
|
||||
{ name: 'time-picker', use: ['menu'] },
|
||||
{ name: 'action-row', use: ['action'] },
|
||||
];
|
||||
|
||||
export const inputSlots = [{ name: 'trigger' }, { name: 'input-icon' }, { name: 'clear-icon' }, { name: 'dp-input' }];
|
||||
|
||||
const slotNames = {
|
||||
all: () => slots,
|
||||
monthYear: () => slots.filter((slot) => slot.use.includes('month-year')),
|
||||
input: () => inputSlots,
|
||||
timePicker: () => slots.filter((slot) => slot.use.includes('time')),
|
||||
action: () => slots.filter((slot) => slot.use.includes('action')),
|
||||
calendar: () => slots.filter((slot) => slot.use.includes('calendar')),
|
||||
menu: () => slots.filter((slot) => slot.use.includes('menu')),
|
||||
};
|
||||
|
||||
export const mapSlots = (
|
||||
slots: Slots,
|
||||
usage: 'all' | 'monthYear' | 'timePicker' | 'input' | 'action' | 'calendar' | 'menu',
|
||||
presetRanges?: PresetRange[],
|
||||
): string[] => {
|
||||
const toReturn: string[] = [];
|
||||
|
||||
slotNames[usage]().forEach((slot) => {
|
||||
if (slots[slot.name]) {
|
||||
toReturn.push(slot.name);
|
||||
}
|
||||
});
|
||||
|
||||
if (presetRanges && presetRanges.length) {
|
||||
presetRanges.forEach((preset) => {
|
||||
if (preset.slot) {
|
||||
toReturn.push(preset.slot);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
};
|
||||
25
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/composables/state.ts
generated
vendored
Normal file
25
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/composables/state.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { computed, reactive } from 'vue';
|
||||
|
||||
const state = reactive({
|
||||
menuFocused: false,
|
||||
shiftKeyInMenu: false,
|
||||
});
|
||||
|
||||
export const useState = () => {
|
||||
const setMenuFocused = (value: boolean): void => {
|
||||
state.menuFocused = value;
|
||||
};
|
||||
|
||||
const setShiftKey = (value: boolean): void => {
|
||||
if (state.shiftKeyInMenu === value) return;
|
||||
state.shiftKeyInMenu = value;
|
||||
};
|
||||
|
||||
const control = computed(() => ({ shiftKeyInMenu: state.shiftKeyInMenu, menuFocused: state.menuFocused }));
|
||||
|
||||
return {
|
||||
control,
|
||||
setMenuFocused,
|
||||
setShiftKey,
|
||||
};
|
||||
};
|
||||
14
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/composables/transition.ts
generated
vendored
Normal file
14
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/composables/transition.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { computed } from 'vue';
|
||||
|
||||
import type { Transition } from '@/interfaces';
|
||||
|
||||
export const useTransitions = (transitions: Transition | boolean) => {
|
||||
const transitionName = computed(() => (isOpen: boolean): string => {
|
||||
if (transitions && typeof transitions !== 'boolean') {
|
||||
return isOpen ? transitions.open : transitions.close;
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
return { transitionName, showTransition: !!transitions };
|
||||
};
|
||||
413
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/composables/utils.ts
generated
vendored
Normal file
413
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/components/composables/utils.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,413 @@
|
|||
import { computed } from 'vue';
|
||||
import {
|
||||
addDays,
|
||||
endOfWeek,
|
||||
format,
|
||||
getDay,
|
||||
getHours,
|
||||
getMinutes,
|
||||
getMonth,
|
||||
getSeconds,
|
||||
getYear,
|
||||
isAfter,
|
||||
isBefore,
|
||||
isEqual,
|
||||
isValid,
|
||||
parseISO,
|
||||
set,
|
||||
setMonth,
|
||||
setYear,
|
||||
startOfWeek,
|
||||
} from 'date-fns';
|
||||
import { utcToZonedTime, zonedTimeToUtc } from 'date-fns-tz/esm';
|
||||
import { errors, convertType } from '@/utils/util';
|
||||
|
||||
import type {
|
||||
DateValue,
|
||||
Flow,
|
||||
TimeModel,
|
||||
WeekStartNum,
|
||||
InternalModuleValue,
|
||||
ICalendarDate,
|
||||
ICalendarDay,
|
||||
} from '@/interfaces';
|
||||
import type { AllPropsType } from '@/utils/props';
|
||||
import {
|
||||
defaultAriaLabels,
|
||||
defaultMultiCalendars,
|
||||
defaultPreviewFormat,
|
||||
defaultTransitions,
|
||||
getDefaultTextInputOptions,
|
||||
getDefaultFilters,
|
||||
} from '@/utils/defaults';
|
||||
import { getDate, isDateAfter, isDateBefore, isDateEqual, resetDateTime } from '@/utils/date-utils';
|
||||
|
||||
// Instead of using everywhere new Date(), this is the central place for getting or parsing the date
|
||||
|
||||
export const useUtils = (props: AllPropsType) => {
|
||||
// Check for partial range enabled on null value as second item in the array
|
||||
const checkPartialRangeValue = (): Date => {
|
||||
if (props.partialRange) return null as unknown as Date;
|
||||
throw new Error(errors.prop('partial-range'));
|
||||
};
|
||||
|
||||
const defaults = computed(() => ({
|
||||
ariaLabels: defaultAriaLabels(props.ariaLabels),
|
||||
textInputOptions: Object.assign(getDefaultTextInputOptions(), props.textInputOptions),
|
||||
multiCalendars: defaultMultiCalendars(props.multiCalendars),
|
||||
previewFormat: defaultPreviewFormat(props.previewFormat, props.format, getDefaultPattern()),
|
||||
filters: getDefaultFilters(props.filters),
|
||||
transitions: defaultTransitions(props.transitions),
|
||||
startTime: getDefaultStartTime(),
|
||||
}));
|
||||
|
||||
// Execute some code depending on the range prop, if not, throw error
|
||||
const checkRangeEnabled = <T>(fn: () => T): T => {
|
||||
if (props.range) return fn();
|
||||
throw new Error(errors.prop('range'));
|
||||
};
|
||||
|
||||
// Shared method for time format
|
||||
const getTimeFormat = (): string => {
|
||||
const seconds = props.enableSeconds ? ':ss' : '';
|
||||
return props.is24 ? `HH:mm${seconds}` : `hh:mm${seconds} aa`;
|
||||
};
|
||||
|
||||
// Get default format pattern, returns user specified if defined first
|
||||
const getDefaultPattern = (): string => {
|
||||
if (props.format) return props.format as string;
|
||||
if (props.monthPicker) return 'MM/yyyy';
|
||||
if (props.timePicker) return getTimeFormat();
|
||||
if (props.weekPicker) return 'MM/dd/yyyy';
|
||||
if (props.yearPicker) return 'yyyy';
|
||||
return props.enableTimePicker ? `MM/dd/yyyy, ${getTimeFormat()}` : 'MM/dd/yyyy';
|
||||
};
|
||||
|
||||
// Format date to a human-readable output
|
||||
const formatDate = (value: Date | Date[], customPattern?: string) => {
|
||||
if (typeof props.format === 'function') return props.format(value);
|
||||
const pattern = customPattern || getDefaultPattern();
|
||||
const options = props.formatLocale ? { locale: props.formatLocale } : undefined;
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return `${format(value[0], pattern, options)} ${
|
||||
props.modelAuto && !value[1] ? '' : defaults.value.textInputOptions.rangeSeparator || '-'
|
||||
} ${value[1] ? format(value[1], pattern, options) : ''}`;
|
||||
}
|
||||
|
||||
return format(value, pattern, options);
|
||||
};
|
||||
|
||||
// Converts utc date to a timezone date
|
||||
const getZonedDate = (date: Date): Date => {
|
||||
return props.timezone ? utcToZonedTime(date, props.timezone) : date;
|
||||
};
|
||||
|
||||
// Convert zoned date back to utc date
|
||||
const getZonedToUtc = (date: Date): Date => {
|
||||
return props.timezone ? zonedTimeToUtc(date, props.timezone) : date;
|
||||
};
|
||||
|
||||
const hideNavigationButtons = computed(() => (key: Flow) => {
|
||||
return props.hideNavigation?.includes(key);
|
||||
});
|
||||
|
||||
const validateDate = (date: Date) => {
|
||||
const aboveMax = props.maxDate ? isDateAfter(getZonedDate(date), getZonedDate(getDate(props.maxDate))) : false;
|
||||
const bellowMin = props.minDate
|
||||
? isDateBefore(getZonedDate(date), getZonedDate(getDate(props.minDate)))
|
||||
: false;
|
||||
const inDisableArr = matchDate(date, props.disabledDates);
|
||||
const disabledMonths = defaults.value.filters.months.map((month) => +month);
|
||||
const inDisabledMonths = disabledMonths.includes(getMonth(date));
|
||||
const weekDayDisabled = props.disabledWeekDays.length
|
||||
? props.disabledWeekDays.some((day) => +day === getDay(date))
|
||||
: false;
|
||||
const notInSpecific = props.allowedDates.length
|
||||
? !props.allowedDates.some((dateVal) => isDateEqual(getZonedDate(getDate(dateVal)), getZonedDate(date)))
|
||||
: false;
|
||||
|
||||
const dateYear = getYear(date);
|
||||
|
||||
const outOfYearRange = dateYear < +props.yearRange[0] || dateYear > +props.yearRange[1];
|
||||
|
||||
return !(
|
||||
aboveMax ||
|
||||
bellowMin ||
|
||||
inDisableArr ||
|
||||
inDisabledMonths ||
|
||||
outOfYearRange ||
|
||||
weekDayDisabled ||
|
||||
notInSpecific
|
||||
);
|
||||
};
|
||||
|
||||
const assignDefaultTime = (obj: TimeModel): TimeModel => {
|
||||
const defaultTime = {
|
||||
hours: getHours(getDate()),
|
||||
minutes: getMinutes(getDate()),
|
||||
seconds: getSeconds(getDate()),
|
||||
};
|
||||
return Object.assign(defaultTime, obj);
|
||||
};
|
||||
|
||||
const getDefaultStartTime = (): TimeModel | TimeModel[] | null => {
|
||||
if (props.range) {
|
||||
if (props.startTime && Array.isArray(props.startTime)) {
|
||||
return [assignDefaultTime(props.startTime[0]), assignDefaultTime(props.startTime[1])];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return props.startTime && !Array.isArray(props.startTime) ? assignDefaultTime(props.startTime) : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if date is between max and min date, or if it is included in filters
|
||||
*/
|
||||
const isDisabled = (date: Date): boolean => {
|
||||
return !validateDate(date);
|
||||
};
|
||||
|
||||
const isValidDate = (value: Date | Date[] | null): boolean => {
|
||||
if (Array.isArray(value)) {
|
||||
return isValid(value[0]) && (value[1] ? isValid(value[1]) : true);
|
||||
}
|
||||
return value ? isValid(value) : false;
|
||||
};
|
||||
|
||||
const sanitizeDate = (date: Date | string): Date | string => {
|
||||
if (date instanceof Date) {
|
||||
return date;
|
||||
}
|
||||
return parseISO(date);
|
||||
};
|
||||
|
||||
const getWeekFromDate = (date: Date): [Date, Date] => {
|
||||
const start = startOfWeek(getZonedDate(date), { weekStartsOn: +props.weekStart as WeekStartNum });
|
||||
const end = endOfWeek(getZonedDate(date), { weekStartsOn: +props.weekStart as WeekStartNum });
|
||||
return [start, end];
|
||||
};
|
||||
|
||||
const matchDate = (date: Date, pattern: Date[] | string[] | number[] | ((date: Date) => boolean)): boolean => {
|
||||
if (Array.isArray(pattern)) {
|
||||
return pattern.some((includedDate) => isDateEqual(getZonedDate(getDate(includedDate)), getZonedDate(date)));
|
||||
}
|
||||
return pattern(date);
|
||||
};
|
||||
|
||||
const setDateMonthOrYear = (date: DateValue, month?: number | null, year?: number | null): Date => {
|
||||
let dateCopy = date ? getDate(date) : getDate();
|
||||
if (month || month === 0) {
|
||||
dateCopy = setMonth(dateCopy, month);
|
||||
}
|
||||
if (year) {
|
||||
dateCopy = setYear(dateCopy, year);
|
||||
}
|
||||
return dateCopy;
|
||||
};
|
||||
|
||||
// Returns a getDate object with a set of time from a provided date
|
||||
const setTimeValue = (date: Date): Date =>
|
||||
set(getDate(), { hours: getHours(date), minutes: getMinutes(date), seconds: getSeconds(date) });
|
||||
|
||||
const getMinMaxTime = (time: TimeModel): Date => {
|
||||
return set(getDate(), {
|
||||
hours: +time.hours || 0,
|
||||
minutes: +time.minutes || 0,
|
||||
seconds: +time.seconds || 0,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Depending on the time or full date validation, validate if the selected time is valid
|
||||
* Extracted logic from isValidTime fn
|
||||
*/
|
||||
const validateTime = (date: Date | null, dateToCompare: Date, compare: 'max' | 'min', full: boolean): boolean => {
|
||||
if (!date) return true;
|
||||
if (full) {
|
||||
const valid = compare === 'max' ? isBefore(date, dateToCompare) : isAfter(date, dateToCompare);
|
||||
const setOptions = { seconds: 0, milliseconds: 0 };
|
||||
return valid || isEqual(set(date, setOptions), set(dateToCompare, setOptions));
|
||||
}
|
||||
return compare === 'max'
|
||||
? date.getTime() <= dateToCompare.getTime()
|
||||
: date.getTime() >= dateToCompare.getTime();
|
||||
};
|
||||
|
||||
// Check if we need to do a time validation
|
||||
const skipTimeValidation = () => {
|
||||
return !props.enableTimePicker || props.monthPicker || props.yearPicker || props.ignoreTimeValidation;
|
||||
};
|
||||
|
||||
// Return set time for a given date or array of dates
|
||||
const getSetTimeValue = (date: Date | Date[]) => {
|
||||
if (Array.isArray(date)) {
|
||||
return [date[0] ? setTimeValue(date[0]) : null, date[1] ? setTimeValue(date[1]) : null];
|
||||
}
|
||||
return setTimeValue(date);
|
||||
};
|
||||
|
||||
// Extracted validation function that will check time based on maxTime or maxDate
|
||||
const checkTimeMax = (selectedDateTime: Date | Date[]) => {
|
||||
const max = props.maxTime ? getMinMaxTime(props.maxTime) : getDate(props.maxDate);
|
||||
if (Array.isArray(selectedDateTime)) {
|
||||
return (
|
||||
validateTime(selectedDateTime[0], max, 'max', !!props.maxDate) &&
|
||||
validateTime(selectedDateTime[1], max, 'max', !!props.maxDate)
|
||||
);
|
||||
}
|
||||
return validateTime(selectedDateTime, max, 'max', !!props.maxDate);
|
||||
};
|
||||
|
||||
// Extracted validation function that will check time based on minTime or minDate
|
||||
const checkTimeMin = (selectedDateTime: Date | Date[], isValid: boolean) => {
|
||||
const min = props.minTime ? getMinMaxTime(props.minTime) : getDate(props.minDate);
|
||||
if (Array.isArray(selectedDateTime)) {
|
||||
return (
|
||||
validateTime(selectedDateTime[0], min, 'min', !!props.minDate) &&
|
||||
validateTime(selectedDateTime[1], min, 'min', !!props.minDate) &&
|
||||
isValid
|
||||
);
|
||||
}
|
||||
return validateTime(selectedDateTime, min, 'min', !!props.minDate) && isValid;
|
||||
};
|
||||
|
||||
// Validate time
|
||||
const isValidTime = (date: InternalModuleValue): boolean => {
|
||||
let isValid = true;
|
||||
if (!date) return true;
|
||||
if (skipTimeValidation()) return true;
|
||||
|
||||
const selectedDateTime = !props.minDate && !props.maxDate ? getSetTimeValue(date) : date;
|
||||
|
||||
if (props.maxTime || props.maxDate) {
|
||||
isValid = checkTimeMax(convertType(selectedDateTime));
|
||||
}
|
||||
|
||||
if (props.minTime || props.minDate) {
|
||||
isValid = checkTimeMin(convertType(selectedDateTime), isValid);
|
||||
}
|
||||
return isValid;
|
||||
};
|
||||
|
||||
// Get 7 days from the provided start date, month is used to check whether the date is from the specified month or in the offset
|
||||
const getWeekDays = (startDay: Date, month: number): ICalendarDay[] => {
|
||||
const startDate = getDate(JSON.parse(JSON.stringify(startDay)));
|
||||
const dates = [];
|
||||
for (let i = 0; i < 7; i++) {
|
||||
const next = addDays(startDate, i);
|
||||
const isNext = getMonth(next) !== month;
|
||||
dates.push({
|
||||
text: props.hideOffsetDates && isNext ? '' : next.getDate(),
|
||||
value: next,
|
||||
current: !isNext,
|
||||
classData: {},
|
||||
});
|
||||
}
|
||||
return dates;
|
||||
};
|
||||
|
||||
// Get days for the calendar to be displayed in a table grouped by weeks
|
||||
const getCalendarDays = (month: number, year: number): ICalendarDate[] => {
|
||||
const weeks: ICalendarDate[] = [];
|
||||
const firstDate = getDate(getZonedDate(new Date(year, month)));
|
||||
const lastDate = getDate(getZonedDate(new Date(year, month + 1, 0)));
|
||||
|
||||
const firstDateInCalendar = startOfWeek(firstDate, { weekStartsOn: props.weekStart as WeekStartNum });
|
||||
|
||||
const addDaysToWeek = (date: Date) => {
|
||||
const days = getWeekDays(date, month);
|
||||
weeks.push({ days });
|
||||
if (
|
||||
!weeks[weeks.length - 1].days.some((day) =>
|
||||
isDateEqual(resetDateTime(day.value), resetDateTime(lastDate)),
|
||||
)
|
||||
) {
|
||||
const nextDate = addDays(date, 7);
|
||||
addDaysToWeek(nextDate);
|
||||
}
|
||||
};
|
||||
addDaysToWeek(firstDateInCalendar);
|
||||
|
||||
if (props.sixWeeks && weeks.length < 6) {
|
||||
const diff = 6 - weeks.length;
|
||||
for (let i = 1; i <= diff; i++) {
|
||||
const lastWeek = weeks[weeks.length - 1];
|
||||
const last = lastWeek.days[lastWeek.days.length - 1];
|
||||
const days = getWeekDays(addDays(last.value, 1), getMonth(firstDate));
|
||||
weeks.push({ days });
|
||||
}
|
||||
}
|
||||
|
||||
return weeks;
|
||||
};
|
||||
|
||||
const getDateForCompare = (date: Date | string, month: number, year: number): [Date, Date] => {
|
||||
return [set(getDate(date), { date: 1 }), set(getDate(), { month, year, date: 1 })];
|
||||
};
|
||||
|
||||
const validateMinDate = (month: number, year: number): boolean => {
|
||||
return (
|
||||
isDateBefore(...getDateForCompare(props.minDate, month, year)) ||
|
||||
isDateEqual(...getDateForCompare(props.minDate, month, year))
|
||||
);
|
||||
};
|
||||
|
||||
const validateMaxDate = (month: number, year: number): boolean => {
|
||||
return (
|
||||
isDateAfter(...getDateForCompare(props.maxDate, month, year)) ||
|
||||
isDateEqual(...getDateForCompare(props.maxDate, month, year))
|
||||
);
|
||||
};
|
||||
|
||||
const validateBothMinAndMax = (month: number, year: number, isNext: boolean) => {
|
||||
let valid = false;
|
||||
if (props.maxDate && isNext && validateMaxDate(month, year)) {
|
||||
valid = true;
|
||||
}
|
||||
if (props.minDate && !isNext && validateMinDate(month, year)) {
|
||||
valid = true;
|
||||
}
|
||||
return valid;
|
||||
};
|
||||
|
||||
const validateMonthYearInRange = (month: number, year: number, isNext: boolean, preventNav: boolean): boolean => {
|
||||
let valid = false;
|
||||
if (preventNav) {
|
||||
if (props.minDate && props.maxDate) {
|
||||
valid = validateBothMinAndMax(month, year, isNext);
|
||||
} else if (props.minDate && validateMinDate(month, year)) {
|
||||
valid = true;
|
||||
} else if (props.maxDate && validateMaxDate(month, year)) {
|
||||
valid = true;
|
||||
}
|
||||
} else {
|
||||
valid = true;
|
||||
}
|
||||
return valid;
|
||||
};
|
||||
|
||||
return {
|
||||
checkPartialRangeValue,
|
||||
checkRangeEnabled,
|
||||
getZonedDate,
|
||||
getZonedToUtc,
|
||||
formatDate,
|
||||
getDefaultPattern,
|
||||
validateDate,
|
||||
getDefaultStartTime,
|
||||
isDisabled,
|
||||
isValidDate,
|
||||
sanitizeDate,
|
||||
getWeekFromDate,
|
||||
matchDate,
|
||||
setDateMonthOrYear,
|
||||
isValidTime,
|
||||
getCalendarDays,
|
||||
validateMonthYearInRange,
|
||||
validateMaxDate,
|
||||
validateMinDate,
|
||||
defaults,
|
||||
hideNavigationButtons,
|
||||
};
|
||||
};
|
||||
79
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/directives/clickOutside.ts
generated
vendored
Normal file
79
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/directives/clickOutside.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// Following code is a port of @vueuse/core clickOutside hook
|
||||
import { unref, watch, getCurrentScope, onScopeDispose } from 'vue';
|
||||
|
||||
import type { Fn, MaybeElementRef, MaybeRef, OnClickOutsideEvents, OnClickOutsideOptions } from '@/interfaces';
|
||||
import { unrefElement } from '@/utils/util';
|
||||
|
||||
const defaultWindow = typeof window !== 'undefined' ? window : undefined;
|
||||
|
||||
const noop = () => {
|
||||
return;
|
||||
};
|
||||
|
||||
const tryOnScopeDispose = (fn: Fn): boolean => {
|
||||
if (getCurrentScope()) {
|
||||
onScopeDispose(fn);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const useEventListener = (
|
||||
target: MaybeRef<EventTarget> | undefined,
|
||||
event: string,
|
||||
listener: EventListener,
|
||||
options: Record<string, boolean>,
|
||||
): (() => void) => {
|
||||
if (!target) return noop;
|
||||
|
||||
let cleanup = noop;
|
||||
|
||||
const stopWatch = watch(
|
||||
() => unref(target),
|
||||
(el) => {
|
||||
cleanup();
|
||||
if (!el) return;
|
||||
|
||||
el.addEventListener(event, listener, options);
|
||||
|
||||
cleanup = () => {
|
||||
el.removeEventListener(event, listener, options);
|
||||
cleanup = noop;
|
||||
};
|
||||
},
|
||||
{ immediate: true, flush: 'post' },
|
||||
);
|
||||
|
||||
const stop = () => {
|
||||
stopWatch();
|
||||
cleanup();
|
||||
};
|
||||
|
||||
tryOnScopeDispose(stop);
|
||||
|
||||
return stop;
|
||||
};
|
||||
|
||||
export const onClickOutside = <E extends keyof OnClickOutsideEvents = 'pointerdown'>(
|
||||
target: MaybeElementRef,
|
||||
inputRef: MaybeElementRef,
|
||||
handler: (evt: OnClickOutsideEvents[E]) => void,
|
||||
options: OnClickOutsideOptions<E> = {},
|
||||
): (() => void) | undefined => {
|
||||
const { window = defaultWindow, event = 'pointerdown' } = options;
|
||||
|
||||
if (!window) return;
|
||||
|
||||
const listener = (event: OnClickOutsideEvents[E]) => {
|
||||
const el = unrefElement(target);
|
||||
const inputEl = unrefElement(inputRef);
|
||||
|
||||
if (!el || !inputEl) return;
|
||||
|
||||
if (el === event.target || event.composedPath().includes(el) || event.composedPath().includes(inputEl)) return;
|
||||
|
||||
handler(event);
|
||||
};
|
||||
|
||||
return useEventListener(window, event, listener as EventListener, { passive: true });
|
||||
};
|
||||
189
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/interfaces.ts
generated
vendored
Normal file
189
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/interfaces.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
import type { ComponentPublicInstance, Ref } from 'vue';
|
||||
import type { AllPropsType } from '@/utils/props';
|
||||
|
||||
export type DynamicClass = Record<string, boolean>;
|
||||
|
||||
export interface IDefaultSelect<T = number> {
|
||||
value: T;
|
||||
text: string;
|
||||
className?: DynamicClass;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type VueEmit = (event: any, ...args: any[]) => void;
|
||||
|
||||
export enum OpenPosition {
|
||||
center = 'center',
|
||||
left = 'left',
|
||||
right = 'right',
|
||||
}
|
||||
|
||||
export type IFormat = string | ((date: Date | Date[]) => string);
|
||||
|
||||
export type InternalModuleValue = Date | Date[] | null;
|
||||
|
||||
export interface DateFilter {
|
||||
months: number[];
|
||||
years: number[];
|
||||
times: { hours: number[]; minutes: number[]; seconds: number[] };
|
||||
}
|
||||
|
||||
export interface ICalendarDay {
|
||||
text: number | string;
|
||||
value: Date;
|
||||
current: boolean;
|
||||
classData: DynamicClass;
|
||||
marker?: IMarker | null;
|
||||
}
|
||||
|
||||
export interface ICalendarDate {
|
||||
days: ICalendarDay[];
|
||||
}
|
||||
|
||||
export interface TextInputOptions {
|
||||
enterSubmit: boolean;
|
||||
tabSubmit: boolean;
|
||||
openMenu: boolean;
|
||||
rangeSeparator: string;
|
||||
format?: string | string[] | ((value: string) => Date | null);
|
||||
}
|
||||
|
||||
export interface MonthModel {
|
||||
month: number | string;
|
||||
year: number | string;
|
||||
}
|
||||
|
||||
export interface TimeModel {
|
||||
hours: number | string;
|
||||
minutes: number | string;
|
||||
seconds: number | string;
|
||||
}
|
||||
|
||||
export type ModelValue =
|
||||
| Date
|
||||
| Date[]
|
||||
| string
|
||||
| string[]
|
||||
| TimeModel
|
||||
| TimeModel[]
|
||||
| MonthModel
|
||||
| MonthModel[]
|
||||
| number
|
||||
| number[]
|
||||
| null;
|
||||
|
||||
export type WeekStartNum = 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||
export type WeekStartStr = '0' | '1' | '2' | '3' | '4' | '5' | '6';
|
||||
|
||||
export type MaybeRef<T> = T | Ref<T>;
|
||||
export type Fn = () => void;
|
||||
|
||||
export interface ConfigurableWindow {
|
||||
window?: Window;
|
||||
}
|
||||
|
||||
export type MaybeElementRef = MaybeRef<HTMLElement | SVGElement | ComponentPublicInstance | undefined | null>;
|
||||
export type OnClickOutsideEvents = Pick<
|
||||
WindowEventMap,
|
||||
'click' | 'mousedown' | 'mouseup' | 'touchstart' | 'touchend' | 'pointerdown' | 'pointerup'
|
||||
>;
|
||||
|
||||
export interface OnClickOutsideOptions<E extends keyof OnClickOutsideEvents> extends ConfigurableWindow {
|
||||
event?: E;
|
||||
}
|
||||
|
||||
export interface IMarker {
|
||||
date: Date | string;
|
||||
type?: 'dot' | 'line';
|
||||
tooltip?: { text?: string; slot?: string; color?: string }[];
|
||||
color?: string;
|
||||
}
|
||||
|
||||
export interface Transition {
|
||||
menuAppear: string;
|
||||
open: string;
|
||||
close: string;
|
||||
next: string;
|
||||
previous: string;
|
||||
vNext: string;
|
||||
vPrevious: string;
|
||||
}
|
||||
|
||||
export type IDisableDates = (date: Date) => boolean;
|
||||
export type TimeType = 'hours' | 'minutes' | 'seconds';
|
||||
|
||||
export type CustomAltPosition = (el: HTMLElement | null) => { top: string; left: string; transform: string };
|
||||
export type AltPosition = boolean | CustomAltPosition;
|
||||
|
||||
export type PresetRange = { label: string; range: Date[] | string[]; style?: Record<string, string>; slot?: string };
|
||||
|
||||
export type Flow = 'month' | 'year' | 'calendar' | 'time' | 'minutes' | 'hours' | 'seconds';
|
||||
export type MenuChildCmp = 'timePicker' | 'monthYearInput' | 'calendar';
|
||||
|
||||
export interface MonthYearPickerRef {
|
||||
toggleMonthPicker: (auto: boolean) => void;
|
||||
toggleYearPicker: (auto: boolean) => void;
|
||||
}
|
||||
|
||||
export interface AriaLabels {
|
||||
toggleOverlay: string;
|
||||
menu: string;
|
||||
input: string;
|
||||
calendarWrap: string;
|
||||
calendarDays: string;
|
||||
openTimePicker: string;
|
||||
closeTimePicker: string;
|
||||
incrementValue: (type: TimeType) => string;
|
||||
decrementValue: (type: TimeType) => string;
|
||||
openTpOverlay: (type: TimeType) => string;
|
||||
amPmButton: string;
|
||||
openYearsOverlay: string;
|
||||
openMonthsOverlay: string;
|
||||
nextMonth: string;
|
||||
prevMonth: string;
|
||||
day: (dayVal: ICalendarDay) => string;
|
||||
}
|
||||
|
||||
export interface CalendarRef extends ComponentPublicInstance {
|
||||
triggerTransition: (m: number, year: number) => void;
|
||||
}
|
||||
|
||||
export interface TimePickerRef extends Element {
|
||||
toggleTimePicker: (show: boolean, flow: boolean, child?: TimeType) => void;
|
||||
}
|
||||
|
||||
export interface TimeInputRef extends Element {
|
||||
openChildCmp: (child: string) => void;
|
||||
}
|
||||
|
||||
export interface ICalendarData {
|
||||
month: number;
|
||||
year: number;
|
||||
}
|
||||
|
||||
export type ComponentRef = Ref<ComponentPublicInstance | HTMLElement | null>;
|
||||
|
||||
export type TimeOverlayCheck = 'noHoursOverlay' | 'noMinutesOverlay' | 'noSecondsOverlay';
|
||||
|
||||
export type ModelFormatType = 'timestamp' | 'format';
|
||||
|
||||
export type ModelType = ModelFormatType | string;
|
||||
|
||||
export type DateTimeSetter = number | string | null;
|
||||
|
||||
export type DateValue = Date | string | null;
|
||||
|
||||
export type ModelTypeConverted = string | number | Date;
|
||||
|
||||
export interface ExtendedProps extends AllPropsType {
|
||||
internalModelValue: InternalModuleValue;
|
||||
}
|
||||
|
||||
export interface MonthYearOpt {
|
||||
month?: number | string;
|
||||
year?: number | string;
|
||||
}
|
||||
|
||||
export type DatepickerMenuRef = ComponentPublicInstance<{
|
||||
updateMonthYear: (ins: number, val: { month: number | null; year: number | null }) => void;
|
||||
}>;
|
||||
32
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/style/_variables.scss
generated
vendored
Normal file
32
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/style/_variables.scss
generated
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// General
|
||||
$dp__font_family: -apple-system, blinkmacsystemfont, "Segoe UI", roboto, oxygen, ubuntu, cantarell, "Open Sans",
|
||||
"Helvetica Neue", sans-serif !default; // Font size for the menu
|
||||
$dp__border_radius: 4px !default; // Border radius everywhere
|
||||
$dp__cell_border_radius: $dp__border_radius !default; // Specific border radius for the calendar cell
|
||||
|
||||
// Transitions
|
||||
$dp__transition_length: 22px !default; // Passed to the translateX in animation
|
||||
$dp__transition_duration: 0.1s !default; // Transition duration
|
||||
|
||||
// Sizing
|
||||
$dp__button_height: 35px !default; // size for buttons in overlays
|
||||
$dp__month_year_row_height: 35px !default; // height of the month-year select row
|
||||
$dp__month_year_row_button_size: 25px !default; // Specific height for the next/previous buttons
|
||||
$dp__button_icon_height: 20px !default; // icon sizing in buttons
|
||||
$dp__cell_size: 35px !default; // width and height of calendar cell
|
||||
$dp__cell_padding: 5px !default; // padding in the cell
|
||||
$dp__common_padding: 10px !default;
|
||||
$dp__input_padding: 6px 12px !default; // padding in the input
|
||||
$dp__input_icon_padding: 35px !default; // Padding on the left side of the input if icon is present
|
||||
$dp__menu_min_width: 260px !default; // Adjust the min width of the menu
|
||||
$dp__action_buttons_padding: 2px 5px !default; // Adjust padding for the action buttons in action row
|
||||
$dp__row_margin: 5px 0 !default; // Adjust the spacing between rows in the calendar
|
||||
$dp__calendar_header_cell_padding: 0.5rem !default; // Adjust padding in calendar header cells
|
||||
$dp__two_calendars_spacing: 10px !default; // Space between two calendars if using two calendars
|
||||
$dp__overlay_col_padding: 3px !default; // Padding in the overlay column
|
||||
$dp__time_inc_dec_button_size: 32px !default; // Sizing for arrow buttons in the time picker
|
||||
|
||||
// Font sizes
|
||||
$dp__font_size: 1rem !default; // overall font-size
|
||||
$dp__preview_font_size: 0.8rem !default; // font size of the date preview in the action row
|
||||
$dp__time_font_size: 2rem !default; // font size in the time picker
|
||||
47
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/style/components/_ActionRow.scss
generated
vendored
Normal file
47
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/style/components/_ActionRow.scss
generated
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
.dp__action_row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: $dp__common_padding;
|
||||
box-sizing: border-box;
|
||||
color: var(--dp-text-color);
|
||||
background: var(--dp-background-color);
|
||||
|
||||
svg {
|
||||
height: $dp__button_icon_height;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.dp__selection_preview {
|
||||
width: 50%;
|
||||
color: var(--dp-text-color);
|
||||
font-size: $dp__preview_font_size;
|
||||
}
|
||||
|
||||
.dp__action_buttons {
|
||||
width: 50%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.dp__action {
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
padding: $dp__action_buttons_padding;
|
||||
border-radius: $dp__border_radius;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dp__select {
|
||||
color: var(--dp-success-color);
|
||||
}
|
||||
|
||||
.dp__action_disabled {
|
||||
color: var(--dp-success-color-disabled);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.dp__cancel {
|
||||
color: var(--dp-secondary-color);
|
||||
}
|
||||
267
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/style/components/_Calendar.scss
generated
vendored
Normal file
267
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/style/components/_Calendar.scss
generated
vendored
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
.dp__calendar_wrap {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
font-family: $dp__font_family;
|
||||
flex: 0;
|
||||
}
|
||||
|
||||
.dp__calendar_header {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--dp-text-color);
|
||||
white-space: nowrap;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dp__calendar_header_item {
|
||||
text-align: center;
|
||||
flex-grow: 1;
|
||||
height: $dp__cell_size;
|
||||
padding: $dp__cell_padding;
|
||||
width: $dp__cell_size;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.dp__calendar_row {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: $dp__row_margin;
|
||||
}
|
||||
|
||||
.dp__calendar_item {
|
||||
text-align: center;
|
||||
flex-grow: 1;
|
||||
box-sizing: border-box;
|
||||
color: var(--dp-text-color);
|
||||
}
|
||||
|
||||
.dp__calendar {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dp__calendar_header_cell {
|
||||
border-bottom: thin solid var(--dp-border-color);
|
||||
padding: $dp__calendar_header_cell_padding;
|
||||
}
|
||||
|
||||
.dp__cell_inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
border-radius: $dp__cell_border_radius;
|
||||
height: $dp__cell_size;
|
||||
padding: $dp__cell_padding;
|
||||
width: $dp__cell_size;
|
||||
border: 1px solid transparent;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
%__dp_range_border_radius_start {
|
||||
border-bottom-right-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
%__dp_range_border_radius_end {
|
||||
border-bottom-left-radius: 0;
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
|
||||
%__dp_active {
|
||||
background: var(--dp-primary-color);
|
||||
color: var(--dp-primary-text-color);
|
||||
}
|
||||
|
||||
%__dp_range_preview {
|
||||
border-top: 1px dashed var(--dp-primary-color);
|
||||
border-bottom: 1px dashed var(--dp-primary-color);
|
||||
}
|
||||
|
||||
%__hover_date {
|
||||
background: var(--dp-hover-color);
|
||||
color: var(--dp-hover-text-color);
|
||||
}
|
||||
|
||||
.dp__cell_offset {
|
||||
color: var(--dp-secondary-color);
|
||||
}
|
||||
|
||||
.dp__cell_disabled {
|
||||
color: var(--dp-secondary-color);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.dp__active_date {
|
||||
@extend %__dp_active;
|
||||
}
|
||||
|
||||
.dp__range_start {
|
||||
@extend %__dp_active;
|
||||
@extend %__dp_range_border_radius_start;
|
||||
}
|
||||
|
||||
.dp__range_end {
|
||||
@extend %__dp_active;
|
||||
@extend %__dp_range_border_radius_end;
|
||||
}
|
||||
|
||||
.dp__date_hover {
|
||||
&:hover {
|
||||
@extend %__hover_date;
|
||||
}
|
||||
}
|
||||
|
||||
.dp__date_hover_start {
|
||||
&:hover {
|
||||
@extend %__dp_range_border_radius_start;
|
||||
@extend %__hover_date;
|
||||
}
|
||||
}
|
||||
|
||||
.dp__date_hover_end {
|
||||
&:hover {
|
||||
@extend %__dp_range_border_radius_end;
|
||||
@extend %__hover_date;
|
||||
}
|
||||
}
|
||||
|
||||
.dp__range_between {
|
||||
background: var(--dp-hover-color);
|
||||
border-radius: 0;
|
||||
border-top: 1px solid var(--dp-hover-color);
|
||||
border-bottom: 1px solid var(--dp-hover-color);
|
||||
}
|
||||
|
||||
.dp__range_between_week {
|
||||
background: var(--dp-primary-color);
|
||||
color: var(--dp-primary-text-color);
|
||||
border-radius: 0;
|
||||
border-top: 1px solid var(--dp-primary-color);
|
||||
border-bottom: 1px solid var(--dp-primary-color);
|
||||
}
|
||||
|
||||
.dp__today {
|
||||
border: 1px solid var(--dp-primary-color);
|
||||
}
|
||||
|
||||
.dp__week_num {
|
||||
color: var(--dp-secondary-color);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dp__cell_auto_range {
|
||||
border-radius: 0;
|
||||
border-top: 1px dashed var(--dp-primary-color);
|
||||
border-bottom: 1px dashed var(--dp-primary-color);
|
||||
}
|
||||
|
||||
.dp__cell_auto_range_start {
|
||||
@extend %__dp_range_border_radius_start;
|
||||
@extend %__dp_range_preview;
|
||||
|
||||
border-left: 1px dashed var(--dp-primary-color);
|
||||
}
|
||||
|
||||
.dp__cell_auto_range_end {
|
||||
@extend %__dp_range_border_radius_end;
|
||||
@extend %__dp_range_preview;
|
||||
|
||||
border-right: 1px dashed var(--dp-primary-color);
|
||||
}
|
||||
|
||||
.dp__calendar_header_separator {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: var(--dp-border-color);
|
||||
}
|
||||
|
||||
.dp__calendar_next {
|
||||
margin-left: $dp__two_calendars_spacing;
|
||||
}
|
||||
|
||||
%__dp_marker {
|
||||
height: 5px;
|
||||
background-color: var(--dp-marker-color);
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.dp__marker_dot {
|
||||
width: 5px;
|
||||
border-radius: 50%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
@extend %__dp_marker;
|
||||
}
|
||||
|
||||
.dp__marker_line {
|
||||
width: 100%;
|
||||
left: 0;
|
||||
|
||||
@extend %__dp_marker;
|
||||
}
|
||||
|
||||
.dp__marker_tooltip {
|
||||
position: absolute;
|
||||
border-radius: $dp__border_radius;
|
||||
background-color: var(--dp-tooltip-color);
|
||||
padding: 5px;
|
||||
border: 1px solid var(--dp-border-color);
|
||||
z-index: 99999;
|
||||
box-sizing: border-box;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.dp__tooltip_content {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dp__tooltip_text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-flow: row nowrap;
|
||||
color: var(--dp-text-color);
|
||||
}
|
||||
|
||||
.dp__tooltip_mark {
|
||||
height: 5px;
|
||||
width: 5px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--dp-text-color);
|
||||
color: var(--dp-text-color);
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.dp__arrow_bottom_tp {
|
||||
left: 50%;
|
||||
bottom: 0;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
background-color: var(--dp-tooltip-color);
|
||||
position: absolute;
|
||||
border-right: 1px solid var(--dp-border-color);
|
||||
border-bottom: 1px solid var(--dp-border-color);
|
||||
transform: translate(-50%, 50%) rotate(45deg);
|
||||
}
|
||||
|
||||
.dp__instance_calendar {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
.dp__flex_display {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.dp__cell_highlight {
|
||||
background-color: var(--dp-highlight-color);
|
||||
}
|
||||
101
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/style/components/_DatepickerInput.scss
generated
vendored
Normal file
101
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/style/components/_DatepickerInput.scss
generated
vendored
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
.dp__input_wrap {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
box-sizing: unset;
|
||||
|
||||
&:focus {
|
||||
border-color: var(--dp-border-color-hover);
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.dp__input {
|
||||
background-color: var(--dp-background-color);
|
||||
border-radius: $dp__border_radius;
|
||||
font-family: $dp__font_family;
|
||||
border: 1px solid var(--dp-border-color);
|
||||
outline: none;
|
||||
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
width: 100%;
|
||||
font-size: $dp__font_size;
|
||||
line-height: $dp__font_size * 1.5;
|
||||
padding: $dp__input_padding;
|
||||
color: var(--dp-text-color);
|
||||
box-sizing: border-box;
|
||||
|
||||
&::placeholder {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: var(--dp-border-color-hover);
|
||||
}
|
||||
}
|
||||
|
||||
.dp__input_reg {
|
||||
caret-color: transparent;
|
||||
}
|
||||
|
||||
.dp__input_focus {
|
||||
border-color: var(--dp-border-color-hover);
|
||||
}
|
||||
|
||||
.dp__disabled {
|
||||
background: var(--dp-disabled-color);
|
||||
|
||||
&::placeholder {
|
||||
color: var(--dp-disabled-color-text);
|
||||
}
|
||||
}
|
||||
|
||||
.dp__input_icons {
|
||||
display: inline-block;
|
||||
width: $dp__font_size;
|
||||
height: $dp__font_size;
|
||||
stroke-width: 0;
|
||||
font-size: $dp__font_size;
|
||||
line-height: $dp__font_size * 1.5;
|
||||
padding: $dp__input_padding;
|
||||
color: var(--dp-icon-color);
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.dp__input_icon {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
transform: translateY(-50%);
|
||||
color: var(--dp-icon-color);
|
||||
}
|
||||
|
||||
.dp__clear_icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
transform: translateY(-50%);
|
||||
cursor: pointer;
|
||||
color: var(--dp-icon-color);
|
||||
}
|
||||
|
||||
.dp__input_icon_pad {
|
||||
padding-left: $dp__input_icon_padding;
|
||||
}
|
||||
|
||||
.dp__input_valid {
|
||||
box-shadow: 0 0 $dp__border_radius var(--dp-success-color);
|
||||
border-color: var(--dp-success-color);
|
||||
|
||||
&:hover {
|
||||
border-color: var(--dp-success-color);
|
||||
}
|
||||
}
|
||||
|
||||
.dp__input_invalid {
|
||||
box-shadow: 0 0 $dp__border_radius var(--dp-danger-color);
|
||||
border-color: var(--dp-danger-color);
|
||||
|
||||
&:hover {
|
||||
border-color: var(--dp-danger-color);
|
||||
}
|
||||
}
|
||||
119
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/style/components/_DatepickerMenu.scss
generated
vendored
Normal file
119
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/style/components/_DatepickerMenu.scss
generated
vendored
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
.dp__menu {
|
||||
position: absolute;
|
||||
background: var(--dp-background-color);
|
||||
border-radius: $dp__border_radius;
|
||||
min-width: $dp__menu_min_width;
|
||||
font-family: $dp__font_family;
|
||||
font-size: $dp__font_size;
|
||||
user-select: none;
|
||||
border: 1px solid var(--dp-menu-border-color);
|
||||
box-sizing: border-box;
|
||||
|
||||
&::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
&::before {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border: 1px solid var(--dp-menu-border-color);
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.dp__menu_index {
|
||||
z-index: 99999;
|
||||
}
|
||||
|
||||
%__dp_menu_readonly_disabled {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.dp__menu_disabled {
|
||||
@extend %__dp_menu_readonly_disabled;
|
||||
|
||||
background: rgb(255 255 255 / 50%);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.dp__menu_readonly {
|
||||
@extend %__dp_menu_readonly_disabled;
|
||||
|
||||
background: transparent;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.dp__arrow_top {
|
||||
left: 50%;
|
||||
top: -1px;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
background-color: var(--dp-background-color);
|
||||
position: absolute;
|
||||
border-left: 1px solid var(--dp-menu-border-color);
|
||||
border-top: 1px solid var(--dp-menu-border-color);
|
||||
transform: translate(-50%, -50%) rotate(45deg);
|
||||
}
|
||||
|
||||
.dp__arrow_bottom {
|
||||
left: 50%;
|
||||
bottom: -1px;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
background-color: var(--dp-background-color);
|
||||
position: absolute;
|
||||
border-right: 1px solid var(--dp-menu-border-color);
|
||||
border-bottom: 1px solid var(--dp-menu-border-color);
|
||||
transform: translate(-50%, 50%) rotate(45deg);
|
||||
}
|
||||
|
||||
.dp__now_wrap {
|
||||
text-align: center;
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
.dp__now_button {
|
||||
border: 1px solid var(--dp-primary-color);
|
||||
color: var(--dp-primary-color);
|
||||
padding: 0 4px;
|
||||
font-weight: bold;
|
||||
border-radius: $dp__border_radius;
|
||||
font-size: $dp__font_size;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.dp__preset_ranges {
|
||||
padding: 5px;
|
||||
border-right: 1px solid var(--dp-border-color);
|
||||
}
|
||||
|
||||
.dp__sidebar_left {
|
||||
padding: 5px;
|
||||
border-right: 1px solid var(--dp-border-color);
|
||||
}
|
||||
|
||||
.dp__sidebar_right {
|
||||
padding: 5px;
|
||||
border-left: 1px solid var(--dp-border-color);
|
||||
}
|
||||
|
||||
.dp__preset_range {
|
||||
padding: 5px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--dp-hover-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.dp__menu_content_wrapper {
|
||||
display: flex;
|
||||
}
|
||||
65
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/style/components/_MonthYearInput.scss
generated
vendored
Normal file
65
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/style/components/_MonthYearInput.scss
generated
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
.dp__month_year_row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: $dp__month_year_row_height;
|
||||
color: var(--dp-text-color);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.dp__inner_nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
height: $dp__month_year_row_button_size;
|
||||
width: $dp__month_year_row_button_size;
|
||||
color: var(--dp-icon-color);
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
|
||||
svg {
|
||||
height: $dp__button_icon_height;
|
||||
width: $dp__button_icon_height;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--dp-hover-color);
|
||||
color: var(--dp-hover-icon-color);
|
||||
}
|
||||
}
|
||||
|
||||
%__dp_inner_nav_disabled {
|
||||
background: var(--dp-disabled-color);
|
||||
color: var(--dp-disabled-color-text);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.dp__inner_nav_disabled {
|
||||
@extend %__dp_inner_nav_disabled;
|
||||
|
||||
&:hover {
|
||||
@extend %__dp_inner_nav_disabled;
|
||||
}
|
||||
}
|
||||
|
||||
.dp__month_year_select {
|
||||
width: 50%;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
height: $dp__month_year_row_height;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: $dp__border_radius;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:hover {
|
||||
background: var(--dp-hover-color);
|
||||
color: var(--dp-hover-text-color);
|
||||
}
|
||||
}
|
||||
|
||||
.dp__month_year_wrap {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
138
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/style/components/_SelectionGrid.scss
generated
vendored
Normal file
138
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/style/components/_SelectionGrid.scss
generated
vendored
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
.dp__overlay {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--dp-background-color);
|
||||
top: 0;
|
||||
left: 0;
|
||||
transition: opacity 1s ease-out;
|
||||
z-index: 99999;
|
||||
font-family: $dp__font_family;
|
||||
color: var(--dp-text-color);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.dp__overlay_container::-webkit-scrollbar-track {
|
||||
box-shadow: var(--dp-scroll-bar-background);
|
||||
background-color: var(--dp-scroll-bar-background);
|
||||
}
|
||||
|
||||
.dp__overlay_container::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
background-color: var(--dp-scroll-bar-background);
|
||||
}
|
||||
|
||||
.dp__overlay_container::-webkit-scrollbar-thumb {
|
||||
background-color: var(--dp-scroll-bar-color);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.dp__overlay:focus {
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.dp__container_flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.dp__container_block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dp__overlay_container {
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.dp__time_picker_overlay_container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.dp__overlay_row {
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
flex-wrap: wrap;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dp__overlay_container > .dp__overlay_row {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.dp__overlay_col {
|
||||
box-sizing: border-box;
|
||||
width: 33%;
|
||||
padding: $dp__overlay_col_padding;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dp__overlay_cell_pad {
|
||||
padding: $dp__common_padding 0;
|
||||
}
|
||||
|
||||
.dp__overlay_cell_active {
|
||||
cursor: pointer;
|
||||
border-radius: $dp__border_radius;
|
||||
text-align: center;
|
||||
background: var(--dp-primary-color);
|
||||
color: var(--dp-primary-text-color);
|
||||
}
|
||||
|
||||
.dp__overlay_cell {
|
||||
cursor: pointer;
|
||||
border-radius: $dp__border_radius;
|
||||
text-align: center;
|
||||
|
||||
&:hover {
|
||||
background: var(--dp-hover-color);
|
||||
color: var(--dp-hover-text-color);
|
||||
}
|
||||
}
|
||||
|
||||
.dp__cell_in_between {
|
||||
background: var(--dp-hover-color);
|
||||
color: var(--dp-hover-text-color);
|
||||
}
|
||||
|
||||
.dp__overlay_action {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.dp__over_action_scroll {
|
||||
right: 5px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.dp__overlay_cell_disabled {
|
||||
cursor: not-allowed;
|
||||
background: var(--dp-disabled-color);
|
||||
|
||||
&:hover {
|
||||
background: var(--dp-disabled-color);
|
||||
}
|
||||
}
|
||||
|
||||
.dp__overlay_cell_active_disabled {
|
||||
cursor: not-allowed;
|
||||
background: var(--dp-primary-disabled-color);
|
||||
|
||||
&:hover {
|
||||
background: var(--dp-primary-disabled-color);
|
||||
}
|
||||
}
|
||||
|
||||
.dp__month_picker_header {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: $dp__cell_size;
|
||||
}
|
||||
96
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/style/components/_TimeInput.scss
generated
vendored
Normal file
96
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/style/components/_TimeInput.scss
generated
vendored
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
.dp__time_input {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
user-select: none;
|
||||
font-family: $dp__font_family;
|
||||
color: var(--dp-text-color);
|
||||
}
|
||||
|
||||
.dp__time_col_reg {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.dp__time_col_reg_with_button {
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.dp__time_col_sec {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.dp__time_col_sec_with_button {
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.dp__time_col {
|
||||
font-size: $dp__time_font_size;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.dp__time_display {
|
||||
cursor: pointer;
|
||||
color: var(--dp-text-color);
|
||||
border-radius: $dp__border_radius;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 3px;
|
||||
|
||||
&:hover {
|
||||
background: var(--dp-hover-color);
|
||||
color: var(--dp-hover-text-color);
|
||||
}
|
||||
}
|
||||
|
||||
.dp__inc_dec_button {
|
||||
padding: 5px;
|
||||
margin: 0;
|
||||
height: $dp__time_inc_dec_button_size;
|
||||
width: $dp__time_inc_dec_button_size;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
color: var(--dp-icon-color);
|
||||
box-sizing: border-box;
|
||||
|
||||
svg {
|
||||
height: $dp__time_inc_dec_button_size;
|
||||
width: $dp__time_inc_dec_button_size;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--dp-hover-color);
|
||||
color: var(--dp-primary-color);
|
||||
}
|
||||
}
|
||||
|
||||
%__dp_time_arrow_disabled {
|
||||
background: var(--dp-disabled-color);
|
||||
color: var(--dp-disabled-color-text);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.dp__inc_dec_button_disabled {
|
||||
@extend %__dp_time_arrow_disabled;
|
||||
|
||||
&:hover {
|
||||
@extend %__dp_time_arrow_disabled;
|
||||
}
|
||||
}
|
||||
|
||||
.dp__pm_am_button {
|
||||
background: var(--dp-primary-color);
|
||||
color: var(--dp-primary-text-color);
|
||||
border: none;
|
||||
padding: $dp__common_padding;
|
||||
border-radius: $dp__border_radius;
|
||||
cursor: pointer;
|
||||
}
|
||||
181
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/style/main.scss
generated
vendored
Normal file
181
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/style/main.scss
generated
vendored
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
@import "variables";
|
||||
@import "components/DatepickerInput";
|
||||
@import "components/DatepickerMenu";
|
||||
@import "components/Calendar";
|
||||
@import "components/MonthYearInput";
|
||||
@import "components/SelectionGrid";
|
||||
@import "components/TimeInput";
|
||||
@import "components/ActionRow";
|
||||
|
||||
.dp__theme_dark {
|
||||
--dp-background-color: #212121;
|
||||
--dp-text-color: #fff;
|
||||
--dp-hover-color: #484848;
|
||||
--dp-hover-text-color: #fff;
|
||||
--dp-hover-icon-color: #959595;
|
||||
--dp-primary-color: #005cb2;
|
||||
--dp-primary-disabled-color: #61a8ea;
|
||||
--dp-primary-text-color: #fff;
|
||||
--dp-secondary-color: #a9a9a9;
|
||||
--dp-border-color: #2d2d2d;
|
||||
--dp-menu-border-color: #2d2d2d;
|
||||
--dp-border-color-hover: #aaaeb7;
|
||||
--dp-disabled-color: #737373;
|
||||
--dp-disabled-color-text: #d0d0d0;
|
||||
--dp-scroll-bar-background: #212121;
|
||||
--dp-scroll-bar-color: #484848;
|
||||
--dp-success-color: #00701a;
|
||||
--dp-success-color-disabled: #428f59;
|
||||
--dp-icon-color: #959595;
|
||||
--dp-danger-color: #e53935;
|
||||
--dp-marker-color: #e53935;
|
||||
--dp-tooltip-color: #3e3e3e;
|
||||
--dp-highlight-color: rgb(0 92 178 / 20%);
|
||||
}
|
||||
|
||||
.dp__theme_light {
|
||||
--dp-background-color: #fff;
|
||||
--dp-text-color: #212121;
|
||||
--dp-hover-color: #f3f3f3;
|
||||
--dp-hover-text-color: #212121;
|
||||
--dp-hover-icon-color: #959595;
|
||||
--dp-primary-color: #1976d2;
|
||||
--dp-primary-disabled-color: #6bacea;
|
||||
--dp-primary-text-color: #f8f5f5;
|
||||
--dp-secondary-color: #c0c4cc;
|
||||
--dp-border-color: #ddd;
|
||||
--dp-menu-border-color: #ddd;
|
||||
--dp-border-color-hover: #aaaeb7;
|
||||
--dp-disabled-color: #f6f6f6;
|
||||
--dp-scroll-bar-background: #f3f3f3;
|
||||
--dp-scroll-bar-color: #959595;
|
||||
--dp-success-color: #76d275;
|
||||
--dp-success-color-disabled: #a3d9b1;
|
||||
--dp-icon-color: #959595;
|
||||
--dp-danger-color: #ff6f60;
|
||||
--dp-marker-color: #ff6f60;
|
||||
--dp-tooltip-color: #fafafa;
|
||||
--dp-disabled-color-text: #8e8e8e;
|
||||
--dp-highlight-color: rgb(25 118 210 / 10%);
|
||||
}
|
||||
|
||||
.dp__main {
|
||||
font-family: $dp__font_family;
|
||||
user-select: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.dp__pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
// Allow control of color property on icons
|
||||
.dp__icon {
|
||||
stroke: currentcolor;
|
||||
fill: currentcolor;
|
||||
}
|
||||
|
||||
// Div style that is displayed on the bottom of the menu for switching layouts
|
||||
.dp__button {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: var(--dp-icon-color);
|
||||
background: var(--dp-background-color);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
padding: $dp__common_padding;
|
||||
box-sizing: border-box;
|
||||
height: $dp__button_height;
|
||||
|
||||
&:hover {
|
||||
background: var(--dp-hover-color);
|
||||
color: var(--dp-hover-icon-color);
|
||||
}
|
||||
|
||||
svg {
|
||||
height: $dp__button_icon_height;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.dp__button_bottom {
|
||||
border-bottom-left-radius: $dp__border_radius;
|
||||
border-bottom-right-radius: $dp__border_radius;
|
||||
}
|
||||
|
||||
.dp__flex_display {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.dp__flex_display_with_input {
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.dp__relative {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.calendar-next-enter-active,
|
||||
.calendar-next-leave-active,
|
||||
.calendar-prev-enter-active,
|
||||
.calendar-prev-leave-active {
|
||||
transition: all $dp__transition_duration ease-out;
|
||||
}
|
||||
|
||||
.calendar-next-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateX($dp__transition_length);
|
||||
}
|
||||
|
||||
.calendar-next-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(-$dp__transition_length);
|
||||
}
|
||||
|
||||
.calendar-prev-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateX(-$dp__transition_length);
|
||||
}
|
||||
|
||||
.calendar-prev-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX($dp__transition_length);
|
||||
}
|
||||
|
||||
.dp-menu-appear-enter-active,
|
||||
.dp-menu-appear-leave-active,
|
||||
.dp-slide-up-enter-active,
|
||||
.dp-slide-up-leave-active,
|
||||
.dp-slide-down-enter-active,
|
||||
.dp-slide-down-leave-active {
|
||||
transition: all 0.1s ease-out;
|
||||
}
|
||||
|
||||
.dp-slide-down-leave-to,
|
||||
.dp-slide-up-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateY($dp__transition_length);
|
||||
}
|
||||
|
||||
.dp-slide-down-enter-from,
|
||||
.dp-slide-up-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-$dp__transition_length);
|
||||
}
|
||||
|
||||
.dp-menu-appear-enter-from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.dp-menu-appear-enter-active,
|
||||
.dp-menu-appear-leave-active {
|
||||
transition: opacity 0.1s ease;
|
||||
}
|
||||
|
||||
.dp-menu-appear-leave-to {
|
||||
opacity: 1;
|
||||
}
|
||||
134
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/utils/date-utils.ts
generated
vendored
Normal file
134
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/utils/date-utils.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
import {
|
||||
parse,
|
||||
isDate,
|
||||
isValid,
|
||||
setHours,
|
||||
setMinutes,
|
||||
setSeconds,
|
||||
setMilliseconds,
|
||||
isBefore,
|
||||
isEqual,
|
||||
isAfter,
|
||||
} from 'date-fns';
|
||||
import type { DateTimeSetter, DateValue } from '@/interfaces';
|
||||
|
||||
const parseTextToDate = (value: string, pattern: string): Date | null => {
|
||||
const parsedDate = parse(value, pattern.slice(0, value.length), new Date());
|
||||
if (isValid(parsedDate) && isDate(parsedDate)) {
|
||||
return parsedDate;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const parseFreeInput = (
|
||||
value: string,
|
||||
pattern: string | string[] | ((value: string) => Date | null),
|
||||
): Date | null => {
|
||||
if (typeof pattern === 'string') {
|
||||
return parseTextToDate(value, pattern);
|
||||
}
|
||||
|
||||
if (Array.isArray(pattern)) {
|
||||
let parsedDate = null;
|
||||
for (const textVal of pattern) {
|
||||
parsedDate = parseTextToDate(value, textVal);
|
||||
if (parsedDate) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return parsedDate;
|
||||
}
|
||||
|
||||
if (typeof pattern === 'function') {
|
||||
return pattern(value);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const getDate = (value?: Date | string | number) => (value ? new Date(value) : new Date());
|
||||
|
||||
export const dateToUtc = (date: Date, preserve: boolean) => {
|
||||
if (preserve) {
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||
const day = date.getDate().toString().padStart(2, '0');
|
||||
const hours = date.getHours().toString().padStart(2, '0');
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0');
|
||||
|
||||
return `${date.getFullYear()}-${month}-${day}T${hours}:${minutes}:00.000Z`;
|
||||
}
|
||||
const utcDate = Date.UTC(
|
||||
date.getUTCFullYear(),
|
||||
date.getUTCMonth(),
|
||||
date.getUTCDate(),
|
||||
date.getUTCHours(),
|
||||
date.getUTCMinutes(),
|
||||
date.getUTCSeconds(),
|
||||
);
|
||||
|
||||
return new Date(utcDate).toISOString();
|
||||
};
|
||||
|
||||
// Reset date time
|
||||
export const resetDateTime = (value: Date | string): Date => {
|
||||
let dateParse = getDate(JSON.parse(JSON.stringify(value)));
|
||||
dateParse = setHours(dateParse, 0);
|
||||
dateParse = setMinutes(dateParse, 0);
|
||||
dateParse = setSeconds(dateParse, 0);
|
||||
dateParse = setMilliseconds(dateParse, 0);
|
||||
|
||||
return dateParse;
|
||||
};
|
||||
|
||||
export const setDateTime = (
|
||||
date: Date | null,
|
||||
hours?: DateTimeSetter,
|
||||
minutes?: DateTimeSetter,
|
||||
seconds?: DateTimeSetter,
|
||||
): Date => {
|
||||
let dateCopy = date ? getDate(date) : getDate();
|
||||
if (hours || hours === 0) {
|
||||
dateCopy = setHours(dateCopy, +hours);
|
||||
}
|
||||
if (minutes || minutes === 0) {
|
||||
dateCopy = setMinutes(dateCopy, +minutes);
|
||||
}
|
||||
if (seconds || seconds === 0) {
|
||||
dateCopy = setSeconds(dateCopy, +seconds);
|
||||
}
|
||||
return setMilliseconds(dateCopy, 0);
|
||||
};
|
||||
|
||||
export const isDateBefore = (date: DateValue, dateToCompare: DateValue): boolean => {
|
||||
if (!date || !dateToCompare) {
|
||||
return false;
|
||||
}
|
||||
return isBefore(resetDateTime(date), resetDateTime(dateToCompare));
|
||||
};
|
||||
|
||||
export const isDateEqual = (date: DateValue, dateToCompare: DateValue): boolean => {
|
||||
if (!date || !dateToCompare) {
|
||||
return false;
|
||||
}
|
||||
return isEqual(resetDateTime(date), resetDateTime(dateToCompare));
|
||||
};
|
||||
|
||||
export const isDateAfter = (date: DateValue, dateToCompare: DateValue): boolean => {
|
||||
if (!date || !dateToCompare) {
|
||||
return false;
|
||||
}
|
||||
return isAfter(resetDateTime(date), resetDateTime(dateToCompare));
|
||||
};
|
||||
|
||||
export const isDateBetween = (range: Date[], hoverDate: Date | null, dateToCheck: Date): boolean => {
|
||||
if (range && range[0] && range[1]) {
|
||||
return isDateAfter(dateToCheck, range[0]) && isDateBefore(dateToCheck, range[1]);
|
||||
}
|
||||
if (range && range[0] && hoverDate) {
|
||||
return (
|
||||
(isDateAfter(dateToCheck, range[0]) && isDateBefore(dateToCheck, hoverDate)) ||
|
||||
(isDateBefore(dateToCheck, range[0]) && isDateAfter(dateToCheck, hoverDate))
|
||||
);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
79
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/utils/defaults.ts
generated
vendored
Normal file
79
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/utils/defaults.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
import type { AriaLabels, IFormat, Transition, TextInputOptions, DateFilter } from '@/interfaces';
|
||||
|
||||
export const mergeDefaultTransitions = (conf: Partial<Transition>): Transition =>
|
||||
Object.assign(
|
||||
{
|
||||
menuAppear: 'dp-menu-appear',
|
||||
open: 'dp-slide-down',
|
||||
close: 'dp-slide-up',
|
||||
next: 'calendar-next',
|
||||
previous: 'calendar-prev',
|
||||
vNext: 'dp-slide-up',
|
||||
vPrevious: 'dp-slide-down',
|
||||
},
|
||||
conf,
|
||||
);
|
||||
|
||||
export const defaultAriaLabels = (labels: Partial<AriaLabels>): AriaLabels => {
|
||||
return Object.assign(
|
||||
{
|
||||
toggleOverlay: 'Toggle overlay',
|
||||
menu: 'Datepicker menu',
|
||||
input: 'Datepicker input',
|
||||
calendarWrap: 'Calendar wrapper',
|
||||
calendarDays: 'Calendar days',
|
||||
openTimePicker: 'Open time picker',
|
||||
closeTimePicker: 'Close time Picker',
|
||||
incrementValue: (type: string) => `Increment ${type}`,
|
||||
decrementValue: (type: string) => `Decrement ${type}`,
|
||||
openTpOverlay: (type: string) => `Open ${type} overlay`,
|
||||
amPmButton: 'Switch AM/PM mode',
|
||||
openYearsOverlay: 'Open years overlay',
|
||||
openMonthsOverlay: 'Open months overlay',
|
||||
nextMonth: 'Next month',
|
||||
prevMonth: 'Previous month',
|
||||
day: () => '',
|
||||
},
|
||||
labels,
|
||||
);
|
||||
};
|
||||
|
||||
export const defaultMultiCalendars = (multiCalendars: boolean | string | number | null): number => {
|
||||
if (multiCalendars === null) return 0;
|
||||
if (typeof multiCalendars === 'boolean') return multiCalendars ? 2 : 0;
|
||||
return +multiCalendars >= 2 ? +multiCalendars : 2;
|
||||
};
|
||||
|
||||
export const defaultPreviewFormat = (
|
||||
previewFormat: IFormat,
|
||||
format: IFormat,
|
||||
defaultPattern: string | ((val: Date) => string),
|
||||
): IFormat => {
|
||||
if (!previewFormat) {
|
||||
return typeof defaultPattern === 'string' ? defaultPattern : format;
|
||||
}
|
||||
return previewFormat;
|
||||
};
|
||||
|
||||
export const defaultTransitions = (transitions: boolean | Partial<Transition>): Transition => {
|
||||
if (typeof transitions === 'boolean') {
|
||||
return transitions ? mergeDefaultTransitions({}) : (false as unknown as Transition);
|
||||
}
|
||||
return mergeDefaultTransitions(transitions);
|
||||
};
|
||||
|
||||
/**
|
||||
* Default options to merge with user provided ones
|
||||
*/
|
||||
export const getDefaultTextInputOptions = (): TextInputOptions => ({
|
||||
enterSubmit: true,
|
||||
tabSubmit: true,
|
||||
openMenu: true,
|
||||
rangeSeparator: ' - ',
|
||||
});
|
||||
|
||||
/**
|
||||
* Default filters to merge with user provided values
|
||||
*/
|
||||
export const getDefaultFilters = (filters: Partial<DateFilter>): DateFilter =>
|
||||
Object.assign({ months: [], years: [], times: { hours: [], minutes: [], seconds: [] } }, filters);
|
||||
160
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/utils/props.ts
generated
vendored
Normal file
160
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/utils/props.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
import type { Locale } from 'date-fns';
|
||||
|
||||
import type { DefineComponent, ExtractPropTypes, PropType } from 'vue';
|
||||
|
||||
import type {
|
||||
AltPosition,
|
||||
AriaLabels,
|
||||
Flow,
|
||||
DateFilter,
|
||||
IDisableDates,
|
||||
IFormat,
|
||||
IMarker,
|
||||
TextInputOptions,
|
||||
TimeModel,
|
||||
Transition,
|
||||
ModelType,
|
||||
ModelValue,
|
||||
OpenPosition,
|
||||
PresetRange,
|
||||
WeekStartNum,
|
||||
WeekStartStr,
|
||||
} from '@/interfaces';
|
||||
|
||||
export const AllProps = {
|
||||
multiCalendars: { type: [Boolean, Number, String] as PropType<boolean | number | string>, default: null },
|
||||
modelValue: { type: [String, Date, Array, Object, Number] as PropType<ModelValue>, default: null },
|
||||
modelType: { type: String as PropType<ModelType>, default: null },
|
||||
position: { type: String as PropType<OpenPosition>, default: 'center' },
|
||||
dark: { type: Boolean as PropType<boolean>, default: false },
|
||||
format: {
|
||||
type: [String, Function] as PropType<IFormat>,
|
||||
default: () => null,
|
||||
},
|
||||
closeOnScroll: { type: Boolean as PropType<boolean>, default: false },
|
||||
autoPosition: { type: Boolean as PropType<boolean>, default: true },
|
||||
closeOnAutoApply: { type: Boolean as PropType<boolean>, default: true },
|
||||
teleport: { type: [String, Object] as PropType<string | HTMLElement>, default: 'body' },
|
||||
altPosition: { type: [Boolean, Function] as PropType<AltPosition>, default: false },
|
||||
transitions: { type: [Boolean, Object] as PropType<boolean | Partial<Transition>>, default: true },
|
||||
formatLocale: { type: Object as PropType<Locale>, default: null },
|
||||
utc: { type: [Boolean, String] as PropType<boolean | 'preserve'>, default: false },
|
||||
ariaLabels: { type: Object as PropType<Partial<AriaLabels>>, default: () => ({}) },
|
||||
offset: { type: [Number, String] as PropType<number | string>, default: 10 },
|
||||
hideNavigation: { type: Array as PropType<Flow[]>, default: () => [] },
|
||||
timezone: { type: String as PropType<string>, default: null },
|
||||
vertical: { type: Boolean as PropType<boolean>, default: false },
|
||||
disableMonthYearSelect: { type: Boolean as PropType<boolean>, default: false },
|
||||
menuClassName: { type: String as PropType<string>, default: null },
|
||||
dayClass: { type: Function as PropType<(date: Date) => string>, default: null },
|
||||
yearRange: { type: Array as PropType<number[]>, default: () => [1900, 2100] },
|
||||
multiCalendarsSolo: { type: Boolean as PropType<boolean>, default: false },
|
||||
calendarCellClassName: { type: String as PropType<string>, default: null },
|
||||
enableTimePicker: { type: Boolean as PropType<boolean>, default: true },
|
||||
autoApply: { type: Boolean as PropType<boolean>, default: false },
|
||||
disabledDates: { type: [Array, Function] as PropType<Date[] | string[] | IDisableDates>, default: () => [] },
|
||||
monthNameFormat: { type: String as PropType<'long' | 'short'>, default: 'short' },
|
||||
startDate: { type: [Date, String] as PropType<string | Date>, default: null },
|
||||
startTime: { type: [Object, Array] as PropType<TimeModel | TimeModel[] | null>, default: null },
|
||||
monthYearComponent: { type: Object as PropType<DefineComponent>, default: null },
|
||||
timePickerComponent: { type: Object as PropType<DefineComponent>, default: null },
|
||||
actionRowComponent: { type: Object as PropType<DefineComponent>, default: null },
|
||||
hideOffsetDates: { type: Boolean as PropType<boolean>, default: false },
|
||||
autoRange: { type: [Number, String] as PropType<number | string>, default: null },
|
||||
noToday: { type: Boolean as PropType<boolean>, default: false },
|
||||
disabledWeekDays: { type: Array as PropType<string[] | number[]>, default: () => [] },
|
||||
allowedDates: { type: Array as PropType<string[] | Date[]>, default: () => [] },
|
||||
showNowButton: { type: Boolean as PropType<boolean>, default: false },
|
||||
nowButtonLabel: { type: String as PropType<string>, default: 'Now' },
|
||||
markers: { type: Array as PropType<IMarker[]>, default: () => [] },
|
||||
modeHeight: { type: [Number, String] as PropType<number | string>, default: 255 },
|
||||
escClose: { type: Boolean as PropType<boolean>, default: true },
|
||||
spaceConfirm: { type: Boolean as PropType<boolean>, default: true },
|
||||
monthChangeOnArrows: { type: Boolean as PropType<boolean>, default: true },
|
||||
presetRanges: { type: Array as PropType<PresetRange[]>, default: () => [] },
|
||||
flow: { type: Array as PropType<Flow[]>, default: () => [] },
|
||||
preventMinMaxNavigation: { type: Boolean as PropType<boolean>, default: false },
|
||||
minRange: { type: [Number, String] as PropType<number | string>, default: null },
|
||||
maxRange: { type: [Number, String] as PropType<number | string>, default: null },
|
||||
multiDatesLimit: { type: [Number, String] as PropType<number | string>, default: null },
|
||||
reverseYears: { type: Boolean as PropType<boolean>, default: false },
|
||||
keepActionRow: { type: Boolean as PropType<boolean>, default: false },
|
||||
weekPicker: { type: Boolean as PropType<boolean>, default: false },
|
||||
filters: { type: Object as PropType<DateFilter>, default: () => ({}) },
|
||||
arrowNavigation: { type: Boolean as PropType<boolean>, default: false },
|
||||
multiStatic: { type: Boolean as PropType<boolean>, default: true },
|
||||
disableTimeRangeValidation: { type: Boolean as PropType<boolean>, default: false },
|
||||
highlight: {
|
||||
type: [Array, Function] as PropType<Date[] | string[] | number[] | ((date: Date) => boolean)>,
|
||||
default: null,
|
||||
},
|
||||
highlightWeekDays: {
|
||||
type: Array as PropType<number[]>,
|
||||
default: null,
|
||||
},
|
||||
highlightDisabledDays: { type: Boolean as PropType<boolean>, default: false },
|
||||
teleportCenter: { type: Boolean as PropType<boolean>, default: false },
|
||||
locale: { type: String as PropType<string>, default: 'en-Us' },
|
||||
weekNumName: { type: String as PropType<string>, default: 'W' },
|
||||
weekStart: { type: [Number, String] as PropType<WeekStartNum | WeekStartStr>, default: 1 },
|
||||
weekNumbers: { type: Boolean as PropType<boolean>, default: false },
|
||||
calendarClassName: { type: String as PropType<string>, default: null },
|
||||
noSwipe: { type: Boolean as PropType<boolean>, default: false },
|
||||
monthChangeOnScroll: { type: [Boolean, String] as PropType<boolean | 'inverse'>, default: true },
|
||||
dayNames: {
|
||||
type: [Function, Array] as PropType<((lang: string, weekStart: number) => string[]) | string[]>,
|
||||
default: null,
|
||||
},
|
||||
monthPicker: { type: Boolean as PropType<boolean>, default: false },
|
||||
customProps: { type: Object as PropType<Record<string, unknown>>, default: null },
|
||||
yearPicker: { type: Boolean as PropType<boolean>, default: false },
|
||||
modelAuto: { type: Boolean as PropType<boolean>, default: false },
|
||||
selectText: { type: String as PropType<string>, default: 'Select' },
|
||||
cancelText: { type: String as PropType<string>, default: 'Cancel' },
|
||||
previewFormat: {
|
||||
type: [String, Function] as PropType<IFormat>,
|
||||
default: () => '',
|
||||
},
|
||||
multiDates: { type: Boolean as PropType<boolean>, default: false },
|
||||
partialRange: { type: Boolean as PropType<boolean>, default: true },
|
||||
ignoreTimeValidation: { type: Boolean as PropType<boolean>, default: false },
|
||||
minDate: { type: [Date, String] as PropType<Date | string>, default: null },
|
||||
maxDate: { type: [Date, String] as PropType<Date | string>, default: null },
|
||||
minTime: { type: Object as PropType<TimeModel>, default: null },
|
||||
maxTime: { type: Object as PropType<TimeModel>, default: null },
|
||||
name: { type: String as PropType<string>, default: null },
|
||||
placeholder: { type: String as PropType<string>, default: '' },
|
||||
hideInputIcon: { type: Boolean as PropType<boolean>, default: false },
|
||||
clearable: { type: Boolean as PropType<boolean>, default: true },
|
||||
state: { type: Boolean as PropType<boolean>, default: null },
|
||||
required: { type: Boolean as PropType<boolean>, default: false },
|
||||
autocomplete: { type: String as PropType<string>, default: 'off' },
|
||||
inputClassName: { type: String as PropType<string>, default: null },
|
||||
inlineWithInput: { type: Boolean as PropType<boolean>, default: false },
|
||||
textInputOptions: { type: Object as PropType<Partial<TextInputOptions>>, default: () => null },
|
||||
fixedStart: { type: Boolean as PropType<boolean>, default: false },
|
||||
fixedEnd: { type: Boolean as PropType<boolean>, default: false },
|
||||
timePicker: { type: Boolean as PropType<boolean>, default: false },
|
||||
enableSeconds: { type: Boolean as PropType<boolean>, default: false },
|
||||
is24: { type: Boolean as PropType<boolean>, default: true },
|
||||
noHoursOverlay: { type: Boolean as PropType<boolean>, default: false },
|
||||
noMinutesOverlay: { type: Boolean as PropType<boolean>, default: false },
|
||||
noSecondsOverlay: { type: Boolean as PropType<boolean>, default: false },
|
||||
hoursGridIncrement: { type: [String, Number] as PropType<string | number>, default: 1 },
|
||||
minutesGridIncrement: { type: [String, Number] as PropType<string | number>, default: 5 },
|
||||
secondsGridIncrement: { type: [String, Number] as PropType<string | number>, default: 5 },
|
||||
hoursIncrement: { type: [Number, String] as PropType<number | string>, default: 1 },
|
||||
minutesIncrement: { type: [Number, String] as PropType<number | string>, default: 1 },
|
||||
secondsIncrement: { type: [Number, String] as PropType<number | string>, default: 1 },
|
||||
range: { type: Boolean as PropType<boolean>, default: false },
|
||||
uid: { type: String as PropType<string>, default: null },
|
||||
disabled: { type: Boolean as PropType<boolean>, default: false },
|
||||
readonly: { type: Boolean as PropType<boolean>, default: false },
|
||||
inline: { type: Boolean as PropType<boolean>, default: false },
|
||||
textInput: { type: Boolean as PropType<boolean>, default: false },
|
||||
onClickOutside: { type: Function as PropType<(validate: () => boolean) => void>, default: null },
|
||||
noDisabledRange: { type: Boolean as PropType<boolean>, default: false },
|
||||
sixWeeks: { type: Boolean as PropType<boolean>, default: false },
|
||||
};
|
||||
|
||||
export type AllPropsType = ExtractPropTypes<typeof AllProps>;
|
||||
19
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/utils/type-guard.ts
generated
vendored
Normal file
19
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/utils/type-guard.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import type { ModelValue } from '@/interfaces';
|
||||
|
||||
/**
|
||||
* =========================================================================================================================
|
||||
* Provide type checks when there is a parameter with multiple types, since ts doesn't know on which type we are working on
|
||||
* =========================================================================================================================
|
||||
*/
|
||||
|
||||
export const isModelValueRange = (value: ModelValue): value is Date[] => {
|
||||
return Array.isArray(value);
|
||||
};
|
||||
|
||||
export const isNumberArray = (value: number | number[]): value is number[] => {
|
||||
return Array.isArray(value);
|
||||
};
|
||||
|
||||
export const isRange = (value: ModelValue): value is Date[] => {
|
||||
return Array.isArray(value) && value.length === 2;
|
||||
};
|
||||
98
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/utils/util.ts
generated
vendored
Normal file
98
node_modules/@vuepic/vue-datepicker/src/VueDatePicker/utils/util.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
import { unref } from 'vue';
|
||||
|
||||
import type { IDefaultSelect, IMarker, MaybeElementRef, ModelValue } from '@/interfaces';
|
||||
import type { ComponentPublicInstance } from 'vue';
|
||||
|
||||
export const getArrayInArray = <T>(list: T[], increment = 3): T[][] => {
|
||||
const items = [];
|
||||
for (let i = 0; i < list.length; i += increment) {
|
||||
items.push([list[i], list[i + 1], list[i + 2]]);
|
||||
}
|
||||
|
||||
return items;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate week day names based on locale and in order specified in week start
|
||||
*/
|
||||
export const getDayNames = (locale: string, weekStart: number): string[] => {
|
||||
// Get list in order from sun ... sat
|
||||
const days = [1, 2, 3, 4, 5, 6, 7].map((day) => {
|
||||
return new Intl.DateTimeFormat(locale, { weekday: 'short', timeZone: 'UTC' })
|
||||
.format(new Date(`2017-01-0${day}T00:00:00+00:00`))
|
||||
.slice(0, 2);
|
||||
});
|
||||
|
||||
// Get days that are in order before specified week start
|
||||
const beforeWeekStart = days.slice(0, weekStart);
|
||||
// Get days that are in order after specified week start
|
||||
const afterWeekStart = days.slice(weekStart + 1, days.length);
|
||||
|
||||
// return them in correct order
|
||||
return [days[weekStart]].concat(...afterWeekStart).concat(...beforeWeekStart);
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate array of years for selection display
|
||||
*/
|
||||
export const getYears = (yearRange: number[] | string[]): IDefaultSelect[] => {
|
||||
const years: IDefaultSelect[] = [];
|
||||
for (let year = +yearRange[0]; year <= +yearRange[1]; year++) {
|
||||
years.push({ value: +year, text: `${year}` });
|
||||
}
|
||||
return years;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate month names based on locale for selection display
|
||||
*/
|
||||
export const getMonths = (locale: string, format: 'long' | 'short'): IDefaultSelect[] => {
|
||||
const formatter = new Intl.DateTimeFormat(locale, { month: format, timeZone: 'UTC' });
|
||||
const months = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map((month) => {
|
||||
const mm = month < 10 ? `0${month}` : month;
|
||||
return new Date(`2017-${mm}-01T00:00:00+00:00`);
|
||||
});
|
||||
return months.map((date, i) => ({
|
||||
text: formatter.format(date),
|
||||
value: i,
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Since internally watched values are in 24h mode, this will get am-pm value from set hour
|
||||
*/
|
||||
export const hoursToAmPmHours = (index: number): number => {
|
||||
const hoursValues = [12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
||||
|
||||
return hoursValues[index];
|
||||
};
|
||||
|
||||
export const unrefElement = (elRef: MaybeElementRef): HTMLElement | null => {
|
||||
const plain = unref(elRef);
|
||||
if (!(plain as ComponentPublicInstance)?.$el) return plain as HTMLElement | null;
|
||||
return (plain as ComponentPublicInstance)?.$el;
|
||||
};
|
||||
|
||||
export const getDefaultMarker = (marker: IMarker): IMarker => Object.assign({ type: 'dot' }, marker);
|
||||
|
||||
export const isModelAuto = (modelValue: ModelValue): boolean => {
|
||||
if (Array.isArray(modelValue)) {
|
||||
return !!modelValue[0] && !!modelValue[1];
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export const errors = {
|
||||
prop: (name: string): string => `"${name}" prop must be enabled!`,
|
||||
dateArr: (name: string) => `You need to use array as "model-value" binding in order to support "${name}"`,
|
||||
};
|
||||
|
||||
export const convertType = <T>(val: any): T => {
|
||||
return val as unknown as T;
|
||||
};
|
||||
|
||||
export const getNumVal = (num?: string | number | null): number | null => {
|
||||
if (num === 0) return num;
|
||||
if (!num || isNaN(+num)) return null;
|
||||
return +num;
|
||||
};
|
||||
13
node_modules/@vuepic/vue-datepicker/src/entry.esm.ts
generated
vendored
Normal file
13
node_modules/@vuepic/vue-datepicker/src/entry.esm.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import component from './VueDatePicker/VueDatePicker.vue';
|
||||
import type { App, Plugin } from 'vue';
|
||||
|
||||
type InstallableComponent = typeof component & { install: Exclude<Plugin['install'], undefined> };
|
||||
|
||||
export default /*#__PURE__*/ ((): InstallableComponent => {
|
||||
const installable = component as unknown as InstallableComponent;
|
||||
|
||||
installable.install = (app: App) => {
|
||||
app.component('Vue3DatePicker', installable);
|
||||
};
|
||||
return installable;
|
||||
})();
|
||||
7
node_modules/@vuepic/vue-datepicker/src/entry.ts
generated
vendored
Normal file
7
node_modules/@vuepic/vue-datepicker/src/entry.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import component, * as namedExports from './entry.esm';
|
||||
|
||||
Object.entries(namedExports).forEach(([exportName, exported]) => {
|
||||
if (exportName !== 'default') component[exportName] = exported;
|
||||
});
|
||||
|
||||
export default component;
|
||||
Loading…
Add table
Add a link
Reference in a new issue