buefy 0.9.12 → 0.9.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/dist/buefy.css +1 -1
  2. package/dist/buefy.esm.js +106 -104
  3. package/dist/buefy.esm.min.js +2 -2
  4. package/dist/buefy.js +106 -104
  5. package/dist/buefy.min.css +1 -1
  6. package/dist/buefy.min.js +2 -2
  7. package/dist/cjs/autocomplete.js +4 -4
  8. package/dist/cjs/button.js +2 -2
  9. package/dist/cjs/carousel.js +5 -5
  10. package/dist/cjs/{chunk-47e1b22b.js → chunk-114191ae.js} +2 -2
  11. package/dist/cjs/{chunk-db3fa73a.js → chunk-2062216d.js} +1 -1
  12. package/dist/cjs/{chunk-d9a33552.js → chunk-2911aa4b.js} +45 -45
  13. package/dist/cjs/{chunk-de7a7f98.js → chunk-2ae50815.js} +3 -3
  14. package/dist/cjs/{chunk-01268607.js → chunk-2c7de785.js} +0 -0
  15. package/dist/cjs/{chunk-30d55827.js → chunk-30670fac.js} +2 -2
  16. package/dist/cjs/{chunk-c3495010.js → chunk-34949503.js} +0 -0
  17. package/dist/cjs/{chunk-2dc027c9.js → chunk-3cc5d9a6.js} +0 -0
  18. package/dist/cjs/{chunk-a14ca5bf.js → chunk-61023b09.js} +1 -1
  19. package/dist/cjs/{chunk-c5a552d4.js → chunk-6cb902f8.js} +0 -0
  20. package/dist/cjs/{chunk-7b13241d.js → chunk-7da0c017.js} +4 -2
  21. package/dist/cjs/{chunk-bd1feb6c.js → chunk-9e4cf4c5.js} +0 -0
  22. package/dist/cjs/{chunk-ea7e1e7e.js → chunk-a11294f9.js} +1 -1
  23. package/dist/cjs/{chunk-3aaecc36.js → chunk-c6fbc7b4.js} +1 -1
  24. package/dist/cjs/{chunk-dcf024d2.js → chunk-c7b2aa4b.js} +0 -0
  25. package/dist/cjs/{chunk-40697b2f.js → chunk-d0f8ea39.js} +6 -6
  26. package/dist/cjs/{chunk-c00639be.js → chunk-d120e215.js} +0 -0
  27. package/dist/cjs/{chunk-cb3ee6f4.js → chunk-d54e40f6.js} +0 -0
  28. package/dist/cjs/{chunk-adaa5792.js → chunk-f5106717.js} +2 -2
  29. package/dist/cjs/{chunk-66710d19.js → chunk-fe2f57ee.js} +1 -1
  30. package/dist/cjs/{chunk-344f6275.js → chunk-fefd7b77.js} +0 -0
  31. package/dist/cjs/clockpicker.js +7 -7
  32. package/dist/cjs/datepicker.js +8 -8
  33. package/dist/cjs/datetimepicker.js +10 -10
  34. package/dist/cjs/dialog.js +10 -10
  35. package/dist/cjs/dropdown.js +2 -2
  36. package/dist/cjs/field.js +1 -1
  37. package/dist/cjs/helpers.js +50 -50
  38. package/dist/cjs/icon.js +1 -1
  39. package/dist/cjs/image.js +1 -1
  40. package/dist/cjs/index.js +21 -21
  41. package/dist/cjs/input.js +3 -3
  42. package/dist/cjs/loading.js +1 -1
  43. package/dist/cjs/menu.js +1 -1
  44. package/dist/cjs/message.js +2 -2
  45. package/dist/cjs/modal.js +1 -1
  46. package/dist/cjs/notification.js +3 -3
  47. package/dist/cjs/numberinput.js +6 -6
  48. package/dist/cjs/pagination.js +2 -2
  49. package/dist/cjs/progress.js +1 -1
  50. package/dist/cjs/rate.js +1 -1
  51. package/dist/cjs/select.js +3 -3
  52. package/dist/cjs/slider.js +1 -1
  53. package/dist/cjs/snackbar.js +1 -1
  54. package/dist/cjs/steps.js +4 -4
  55. package/dist/cjs/table.js +7 -7
  56. package/dist/cjs/tabs.js +4 -4
  57. package/dist/cjs/taginput.js +4 -4
  58. package/dist/cjs/timepicker.js +9 -9
  59. package/dist/cjs/toast.js +1 -1
  60. package/dist/cjs/tooltip.js +1 -1
  61. package/dist/cjs/upload.js +1 -1
  62. package/dist/components/autocomplete/index.js +5 -5
  63. package/dist/components/autocomplete/index.min.js +1 -1
  64. package/dist/components/breadcrumb/index.js +1 -1
  65. package/dist/components/breadcrumb/index.min.js +1 -1
  66. package/dist/components/button/index.js +3 -3
  67. package/dist/components/button/index.min.js +1 -1
  68. package/dist/components/carousel/index.js +26 -26
  69. package/dist/components/carousel/index.min.js +1 -1
  70. package/dist/components/checkbox/index.js +1 -1
  71. package/dist/components/checkbox/index.min.js +1 -1
  72. package/dist/components/clockpicker/index.js +22 -20
  73. package/dist/components/clockpicker/index.min.js +2 -2
  74. package/dist/components/collapse/index.js +1 -1
  75. package/dist/components/collapse/index.min.js +1 -1
  76. package/dist/components/datepicker/index.js +71 -69
  77. package/dist/components/datepicker/index.min.js +2 -2
  78. package/dist/components/datetimepicker/index.js +71 -69
  79. package/dist/components/datetimepicker/index.min.js +2 -2
  80. package/dist/components/dialog/index.js +10 -10
  81. package/dist/components/dialog/index.min.js +1 -1
  82. package/dist/components/dropdown/index.js +6 -6
  83. package/dist/components/dropdown/index.min.js +1 -1
  84. package/dist/components/field/index.js +5 -3
  85. package/dist/components/field/index.min.js +2 -2
  86. package/dist/components/icon/index.js +3 -3
  87. package/dist/components/icon/index.min.js +1 -1
  88. package/dist/components/image/index.js +4 -4
  89. package/dist/components/image/index.min.js +1 -1
  90. package/dist/components/input/index.js +3 -3
  91. package/dist/components/input/index.min.js +1 -1
  92. package/dist/components/loading/index.js +3 -3
  93. package/dist/components/loading/index.min.js +1 -1
  94. package/dist/components/menu/index.js +3 -3
  95. package/dist/components/menu/index.min.js +1 -1
  96. package/dist/components/message/index.js +3 -3
  97. package/dist/components/message/index.min.js +1 -1
  98. package/dist/components/modal/index.js +3 -3
  99. package/dist/components/modal/index.min.js +1 -1
  100. package/dist/components/navbar/index.js +1 -1
  101. package/dist/components/navbar/index.min.js +1 -1
  102. package/dist/components/notification/index.js +3 -3
  103. package/dist/components/notification/index.min.js +1 -1
  104. package/dist/components/numberinput/index.js +6 -6
  105. package/dist/components/numberinput/index.min.js +1 -1
  106. package/dist/components/pagination/index.js +3 -3
  107. package/dist/components/pagination/index.min.js +1 -1
  108. package/dist/components/progress/index.js +6 -6
  109. package/dist/components/progress/index.min.js +1 -1
  110. package/dist/components/radio/index.js +1 -1
  111. package/dist/components/radio/index.min.js +1 -1
  112. package/dist/components/rate/index.js +3 -3
  113. package/dist/components/rate/index.min.js +1 -1
  114. package/dist/components/select/index.js +3 -3
  115. package/dist/components/select/index.min.js +1 -1
  116. package/dist/components/sidebar/index.js +1 -1
  117. package/dist/components/sidebar/index.min.js +1 -1
  118. package/dist/components/skeleton/index.js +1 -1
  119. package/dist/components/skeleton/index.min.js +1 -1
  120. package/dist/components/slider/index.js +7 -7
  121. package/dist/components/slider/index.min.js +1 -1
  122. package/dist/components/snackbar/index.js +3 -3
  123. package/dist/components/snackbar/index.min.js +1 -1
  124. package/dist/components/steps/index.js +14 -14
  125. package/dist/components/steps/index.min.js +1 -1
  126. package/dist/components/switch/index.js +1 -1
  127. package/dist/components/switch/index.min.js +1 -1
  128. package/dist/components/table/index.js +10 -10
  129. package/dist/components/table/index.min.js +1 -1
  130. package/dist/components/tabs/index.js +14 -14
  131. package/dist/components/tabs/index.min.js +1 -1
  132. package/dist/components/tag/index.js +1 -1
  133. package/dist/components/tag/index.min.js +1 -1
  134. package/dist/components/taginput/index.js +5 -5
  135. package/dist/components/taginput/index.min.js +1 -1
  136. package/dist/components/timepicker/index.js +22 -20
  137. package/dist/components/timepicker/index.min.js +2 -2
  138. package/dist/components/toast/index.js +3 -3
  139. package/dist/components/toast/index.min.js +1 -1
  140. package/dist/components/tooltip/index.js +1 -1
  141. package/dist/components/tooltip/index.min.js +1 -1
  142. package/dist/components/upload/index.js +1 -1
  143. package/dist/components/upload/index.min.js +1 -1
  144. package/dist/esm/autocomplete.js +5 -5
  145. package/dist/esm/button.js +3 -3
  146. package/dist/esm/carousel.js +5 -5
  147. package/dist/esm/{chunk-3dbb54cd.js → chunk-18e8b067.js} +1 -1
  148. package/dist/esm/{chunk-7d9db167.js → chunk-21fc0948.js} +6 -6
  149. package/dist/esm/{chunk-a146937d.js → chunk-22e9f916.js} +45 -45
  150. package/dist/esm/{chunk-18fcd4ac.js → chunk-29ca0df8.js} +0 -0
  151. package/dist/esm/{chunk-affd6df1.js → chunk-3773c62d.js} +0 -0
  152. package/dist/esm/{chunk-7ee6c15c.js → chunk-4b67a181.js} +1 -1
  153. package/dist/esm/{chunk-f97f6a90.js → chunk-6019fd7a.js} +2 -2
  154. package/dist/esm/{chunk-d9794c38.js → chunk-71a547bc.js} +0 -0
  155. package/dist/esm/{chunk-161dac72.js → chunk-75a5af93.js} +0 -0
  156. package/dist/esm/{chunk-4f046bae.js → chunk-799e084d.js} +0 -0
  157. package/dist/esm/{chunk-6b53dd42.js → chunk-83eb0d37.js} +2 -2
  158. package/dist/esm/{chunk-80ec133b.js → chunk-8d0f95b8.js} +3 -3
  159. package/dist/esm/{chunk-50188ced.js → chunk-9f7f7441.js} +0 -0
  160. package/dist/esm/{chunk-3098b0e9.js → chunk-ae8ab23a.js} +2 -2
  161. package/dist/esm/{chunk-0f30042f.js → chunk-b07e3182.js} +4 -2
  162. package/dist/esm/{chunk-a64bdef6.js → chunk-b0c0c6b0.js} +0 -0
  163. package/dist/esm/{chunk-cdfb7fda.js → chunk-c9c58d0c.js} +0 -0
  164. package/dist/esm/{chunk-f6b30428.js → chunk-d7f92d97.js} +1 -1
  165. package/dist/esm/{chunk-9957ac1a.js → chunk-d92f0cd9.js} +0 -0
  166. package/dist/esm/{chunk-72ad8d50.js → chunk-e7c9b2cb.js} +1 -1
  167. package/dist/esm/{chunk-8b279c1f.js → chunk-ece062a7.js} +1 -1
  168. package/dist/esm/clockpicker.js +7 -7
  169. package/dist/esm/datepicker.js +9 -9
  170. package/dist/esm/datetimepicker.js +10 -10
  171. package/dist/esm/dialog.js +10 -10
  172. package/dist/esm/dropdown.js +3 -3
  173. package/dist/esm/field.js +2 -2
  174. package/dist/esm/helpers.js +50 -50
  175. package/dist/esm/icon.js +2 -2
  176. package/dist/esm/image.js +2 -2
  177. package/dist/esm/index.js +21 -21
  178. package/dist/esm/input.js +4 -4
  179. package/dist/esm/loading.js +2 -2
  180. package/dist/esm/menu.js +1 -1
  181. package/dist/esm/message.js +2 -2
  182. package/dist/esm/modal.js +2 -2
  183. package/dist/esm/notification.js +3 -3
  184. package/dist/esm/numberinput.js +6 -6
  185. package/dist/esm/pagination.js +3 -3
  186. package/dist/esm/progress.js +1 -1
  187. package/dist/esm/rate.js +1 -1
  188. package/dist/esm/select.js +4 -4
  189. package/dist/esm/slider.js +1 -1
  190. package/dist/esm/snackbar.js +1 -1
  191. package/dist/esm/steps.js +4 -4
  192. package/dist/esm/table.js +7 -7
  193. package/dist/esm/tabs.js +4 -4
  194. package/dist/esm/taginput.js +4 -4
  195. package/dist/esm/timepicker.js +10 -10
  196. package/dist/esm/toast.js +1 -1
  197. package/dist/esm/tooltip.js +2 -2
  198. package/dist/esm/upload.js +1 -1
  199. package/package.json +171 -171
  200. package/src/components/breadcrumb/BreadcrumbItem.spec.js +25 -25
  201. package/src/components/carousel/CarouselList.vue +313 -313
  202. package/src/components/carousel/__snapshots__/CarouselList.spec.js.snap +156 -156
  203. package/src/components/datepicker/Datepicker.vue +843 -843
  204. package/src/components/dialog/Dialog.vue +256 -256
  205. package/src/components/field/Field.vue +3 -3
  206. package/src/components/image/Image.vue +309 -309
  207. package/src/components/numberinput/Numberinput.vue +309 -309
  208. package/src/utils/helpers.js +291 -291
@@ -1,843 +1,843 @@
1
- <template>
2
- <div
3
- class="datepicker control"
4
- :class="[size, {'is-expanded': expanded}]"
5
- >
6
- <b-dropdown
7
- v-if="!isMobile || inline"
8
- ref="dropdown"
9
- :position="position"
10
- :disabled="disabled"
11
- :inline="inline"
12
- :mobile-modal="mobileModal"
13
- :trap-focus="trapFocus"
14
- :aria-role="ariaRole"
15
- :aria-modal="!inline"
16
- :append-to-body="appendToBody"
17
- append-to-body-copy-parent
18
- @active-change="onActiveChange">
19
- <template #trigger v-if="!inline">
20
- <slot name="trigger">
21
- <b-input
22
- ref="input"
23
- autocomplete="off"
24
- :value="formattedValue"
25
- :placeholder="placeholder"
26
- :size="size"
27
- :icon="icon"
28
- :icon-right="iconRight"
29
- :icon-right-clickable="iconRightClickable"
30
- :icon-pack="iconPack"
31
- :rounded="rounded"
32
- :loading="loading"
33
- :disabled="disabled"
34
- :readonly="!editable"
35
- v-bind="$attrs"
36
- :use-html5-validation="false"
37
- @click.native="onInputClick"
38
- @icon-right-click="$emit('icon-right-click')"
39
- @keyup.native.enter="togglePicker(true)"
40
- @change.native="onChange($event.target.value)"
41
- @focus="handleOnFocus" />
42
- </slot>
43
- </template>
44
-
45
- <b-dropdown-item
46
- :disabled="disabled"
47
- :focusable="focusable"
48
- custom
49
- :class="{'dropdown-horizonal-timepicker': horizontalTimePicker}">
50
- <div>
51
- <header class="datepicker-header">
52
- <template v-if="$slots.header !== undefined && $slots.header.length">
53
- <slot name="header" />
54
- </template>
55
- <div
56
- v-else
57
- class="pagination field is-centered"
58
- :class="size">
59
- <a
60
- v-show="!showPrev && !disabled"
61
- class="pagination-previous"
62
- role="button"
63
- href="#"
64
- :disabled="disabled"
65
- :aria-label="ariaPreviousLabel"
66
- @click.prevent="prev"
67
- @keydown.enter.prevent="prev"
68
- @keydown.space.prevent="prev">
69
-
70
- <b-icon
71
- :icon="iconPrev"
72
- :pack="iconPack"
73
- both
74
- type="is-primary is-clickable"/>
75
- </a>
76
- <a
77
- v-show="!showNext && !disabled"
78
- class="pagination-next"
79
- role="button"
80
- href="#"
81
- :disabled="disabled"
82
- :aria-label="ariaNextLabel"
83
- @click.prevent="next"
84
- @keydown.enter.prevent="next"
85
- @keydown.space.prevent="next">
86
-
87
- <b-icon
88
- :icon="iconNext"
89
- :pack="iconPack"
90
- both
91
- type="is-primary is-clickable"/>
92
- </a>
93
- <div class="pagination-list">
94
- <b-field>
95
- <b-select
96
- v-if="!isTypeMonth"
97
- v-model="focusedDateData.month"
98
- :disabled="disabled"
99
- :size="size">
100
- <option
101
- v-for="month in listOfMonths"
102
- :value="month.index"
103
- :key="month.name"
104
- :disabled="month.disabled">
105
- {{ month.name }}
106
- </option>
107
- </b-select>
108
- <b-select
109
- v-model="focusedDateData.year"
110
- :disabled="disabled"
111
- :size="size">
112
- <option
113
- v-for="year in listOfYears"
114
- :value="year"
115
- :key="year">
116
- {{ year }}
117
- </option>
118
- </b-select>
119
- </b-field>
120
- </div>
121
- </div>
122
- </header>
123
- <div
124
- v-if="!isTypeMonth"
125
- class="datepicker-content"
126
- :class="{'content-horizonal-timepicker': horizontalTimePicker}">
127
- <b-datepicker-table
128
- v-model="computedValue"
129
- :day-names="newDayNames"
130
- :month-names="newMonthNames"
131
- :first-day-of-week="firstDayOfWeek"
132
- :rules-for-first-week="rulesForFirstWeek"
133
- :min-date="minDate"
134
- :max-date="maxDate"
135
- :focused="focusedDateData"
136
- :disabled="disabled"
137
- :unselectable-dates="unselectableDates"
138
- :unselectable-days-of-week="unselectableDaysOfWeek"
139
- :selectable-dates="selectableDates"
140
- :events="events"
141
- :indicators="indicators"
142
- :date-creator="dateCreator"
143
- :type-month="isTypeMonth"
144
- :nearby-month-days="nearbyMonthDays"
145
- :nearby-selectable-month-days="nearbySelectableMonthDays"
146
- :show-week-number="showWeekNumber"
147
- :week-number-clickable="weekNumberClickable"
148
- :range="range"
149
- :multiple="multiple"
150
- @range-start="date => $emit('range-start', date)"
151
- @range-end="date => $emit('range-end', date)"
152
- @close="togglePicker(false)"
153
- @update:focused="focusedDateData = $event" />
154
- </div>
155
- <div v-else>
156
- <b-datepicker-month
157
- v-model="computedValue"
158
- :month-names="newMonthNames"
159
- :min-date="minDate"
160
- :max-date="maxDate"
161
- :focused="focusedDateData"
162
- :disabled="disabled"
163
- :unselectable-dates="unselectableDates"
164
- :unselectable-days-of-week="unselectableDaysOfWeek"
165
- :selectable-dates="selectableDates"
166
- :events="events"
167
- :indicators="indicators"
168
- :date-creator="dateCreator"
169
- :range="range"
170
- :multiple="multiple"
171
- @range-start="date => $emit('range-start', date)"
172
- @range-end="date => $emit('range-end', date)"
173
- @close="togglePicker(false)"
174
- @change-focus="changeFocus"
175
- @update:focused="focusedDateData = $event" />
176
- </div>
177
- </div>
178
-
179
- <footer
180
- v-if="$slots.default !== undefined && $slots.default.length"
181
- class="datepicker-footer"
182
- :class="{'footer-horizontal-timepicker': horizontalTimePicker}">
183
- <slot/>
184
- </footer>
185
- </b-dropdown-item>
186
- </b-dropdown>
187
-
188
- <b-input
189
- v-else
190
- ref="input"
191
- :type="!isTypeMonth ? 'date' : 'month'"
192
- autocomplete="off"
193
- :value="formatNative(computedValue)"
194
- :placeholder="placeholder"
195
- :size="size"
196
- :icon="icon"
197
- :icon-pack="iconPack"
198
- :rounded="rounded"
199
- :loading="loading"
200
- :max="formatNative(maxDate)"
201
- :min="formatNative(minDate)"
202
- :disabled="disabled"
203
- :readonly="false"
204
- v-bind="$attrs"
205
- :use-html5-validation="false"
206
- @change.native="onChangeNativePicker"
207
- @focus="onFocus"
208
- @blur="onBlur"/>
209
- </div>
210
- </template>
211
-
212
- <script>
213
- import FormElementMixin from '../../utils/FormElementMixin'
214
- import { isMobile, getMonthNames, getWeekdayNames, matchWithGroups } from '../../utils/helpers'
215
- import config from '../../utils/config'
216
-
217
- import Dropdown from '../dropdown/Dropdown'
218
- import DropdownItem from '../dropdown/DropdownItem'
219
- import Input from '../input/Input'
220
- import Field from '../field/Field'
221
- import Select from '../select/Select'
222
- import Icon from '../icon/Icon'
223
-
224
- import DatepickerTable from './DatepickerTable'
225
- import DatepickerMonth from './DatepickerMonth'
226
-
227
- const defaultDateFormatter = (date, vm) => {
228
- const targetDates = Array.isArray(date) ? date : [date]
229
- const dates = targetDates.map((date) => {
230
- const d = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 12)
231
- return !vm.isTypeMonth ? vm.dtf.format(d) : vm.dtfMonth.format(d)
232
- })
233
- return !vm.multiple ? dates.join(' - ') : dates.join(', ')
234
- }
235
-
236
- const defaultDateParser = (date, vm) => {
237
- if (vm.dtf.formatToParts && typeof vm.dtf.formatToParts === 'function') {
238
- const formatRegex = (vm.isTypeMonth ? vm.dtfMonth : vm.dtf)
239
- .formatToParts(new Date(2000, 11, 25)).map((part) => {
240
- if (part.type === 'literal') {
241
- return part.value
242
- }
243
- return `((?!=<${part.type}>)\\d+)`
244
- }).join('')
245
- const dateGroups = matchWithGroups(formatRegex, date)
246
-
247
- // We do a simple validation for the group.
248
- // If it is not valid, it will fallback to Date.parse below
249
- if (
250
- dateGroups.year &&
251
- dateGroups.year.length === 4 &&
252
- dateGroups.month &&
253
- dateGroups.month <= 12
254
- ) {
255
- if (vm.isTypeMonth) return new Date(dateGroups.year, dateGroups.month - 1)
256
- else if (dateGroups.day && dateGroups.day <= 31) {
257
- return new Date(dateGroups.year, dateGroups.month - 1, dateGroups.day, 12)
258
- }
259
- }
260
- }
261
- // Fallback if formatToParts is not supported or if we were not able to parse a valid date
262
- if (!vm.isTypeMonth) return new Date(Date.parse(date))
263
- if (date) {
264
- const s = date.split('/')
265
- const year = s[0].length === 4 ? s[0] : s[1]
266
- const month = s[0].length === 2 ? s[0] : s[1]
267
- if (year && month) {
268
- return new Date(parseInt(year, 10), parseInt(month - 1, 10), 1, 0, 0, 0, 0)
269
- }
270
- }
271
- return null
272
- }
273
-
274
- export default {
275
- name: 'BDatepicker',
276
- components: {
277
- [DatepickerTable.name]: DatepickerTable,
278
- [DatepickerMonth.name]: DatepickerMonth,
279
- [Input.name]: Input,
280
- [Field.name]: Field,
281
- [Select.name]: Select,
282
- [Icon.name]: Icon,
283
- [Dropdown.name]: Dropdown,
284
- [DropdownItem.name]: DropdownItem
285
- },
286
- mixins: [FormElementMixin],
287
- inheritAttrs: false,
288
- provide() {
289
- return {
290
- $datepicker: this
291
- }
292
- },
293
- props: {
294
- value: {
295
- type: [Date, Array]
296
- },
297
- dayNames: {
298
- type: Array,
299
- default: () => {
300
- if (!Array.isArray(config.defaultDayNames)) {
301
- return undefined
302
- }
303
- return config.defaultDayNames
304
- }
305
- },
306
- monthNames: {
307
- type: Array,
308
- default: () => {
309
- if (!Array.isArray(config.defaultMonthNames)) {
310
- return undefined
311
- }
312
- return config.defaultMonthNames
313
- }
314
- },
315
- firstDayOfWeek: {
316
- type: Number,
317
- default: () => {
318
- if (typeof config.defaultFirstDayOfWeek === 'number') {
319
- return config.defaultFirstDayOfWeek
320
- } else {
321
- return 0
322
- }
323
- }
324
- },
325
- inline: Boolean,
326
- minDate: Date,
327
- maxDate: Date,
328
- focusedDate: Date,
329
- placeholder: String,
330
- editable: Boolean,
331
- disabled: Boolean,
332
- horizontalTimePicker: Boolean,
333
- unselectableDates: [Array, Function],
334
- unselectableDaysOfWeek: {
335
- type: Array,
336
- default: () => config.defaultUnselectableDaysOfWeek
337
- },
338
- selectableDates: [Array, Function],
339
- dateFormatter: {
340
- type: Function,
341
- default: (date, vm) => {
342
- if (typeof config.defaultDateFormatter === 'function') {
343
- return config.defaultDateFormatter(date)
344
- } else {
345
- return defaultDateFormatter(date, vm)
346
- }
347
- }
348
- },
349
- dateParser: {
350
- type: Function,
351
- default: (date, vm) => {
352
- if (typeof config.defaultDateParser === 'function') {
353
- return config.defaultDateParser(date)
354
- } else {
355
- return defaultDateParser(date, vm)
356
- }
357
- }
358
- },
359
- dateCreator: {
360
- type: Function,
361
- default: () => {
362
- if (typeof config.defaultDateCreator === 'function') {
363
- return config.defaultDateCreator()
364
- } else {
365
- return new Date()
366
- }
367
- }
368
- },
369
- mobileNative: {
370
- type: Boolean,
371
- default: () => config.defaultDatepickerMobileNative
372
- },
373
- position: String,
374
- iconRight: String,
375
- iconRightClickable: Boolean,
376
- events: Array,
377
- indicators: {
378
- type: String,
379
- default: 'dots'
380
- },
381
- openOnFocus: Boolean,
382
- iconPrev: {
383
- type: String,
384
- default: () => config.defaultIconPrev
385
- },
386
- iconNext: {
387
- type: String,
388
- default: () => config.defaultIconNext
389
- },
390
- yearsRange: {
391
- type: Array,
392
- default: () => config.defaultDatepickerYearsRange
393
- },
394
- type: {
395
- type: String,
396
- validator: (value) => {
397
- return [
398
- 'month'
399
- ].indexOf(value) >= 0
400
- }
401
- },
402
- nearbyMonthDays: {
403
- type: Boolean,
404
- default: () => config.defaultDatepickerNearbyMonthDays
405
- },
406
- nearbySelectableMonthDays: {
407
- type: Boolean,
408
- default: () => config.defaultDatepickerNearbySelectableMonthDays
409
- },
410
- showWeekNumber: {
411
- type: Boolean,
412
- default: () => config.defaultDatepickerShowWeekNumber
413
- },
414
- weekNumberClickable: {
415
- type: Boolean,
416
- default: () => config.defaultDatepickerWeekNumberClickable
417
- },
418
- rulesForFirstWeek: {
419
- type: Number,
420
- default: () => 4
421
- },
422
- range: {
423
- type: Boolean,
424
- default: false
425
- },
426
- closeOnClick: {
427
- type: Boolean,
428
- default: true
429
- },
430
- multiple: {
431
- type: Boolean,
432
- default: false
433
- },
434
- mobileModal: {
435
- type: Boolean,
436
- default: () => config.defaultDatepickerMobileModal
437
- },
438
- focusable: {
439
- type: Boolean,
440
- default: true
441
- },
442
- trapFocus: {
443
- type: Boolean,
444
- default: () => config.defaultTrapFocus
445
- },
446
- appendToBody: Boolean,
447
- ariaNextLabel: String,
448
- ariaPreviousLabel: String
449
- },
450
- data() {
451
- const focusedDate = (Array.isArray(this.value) ? this.value[0] : (this.value)) ||
452
- this.focusedDate || this.dateCreator()
453
-
454
- if (!this.value && this.maxDate && this.maxDate.getFullYear() < focusedDate.getFullYear()) {
455
- focusedDate.setFullYear(this.maxDate.getFullYear())
456
- }
457
-
458
- return {
459
- dateSelected: this.value,
460
- focusedDateData: {
461
- day: focusedDate.getDate(),
462
- month: focusedDate.getMonth(),
463
- year: focusedDate.getFullYear()
464
- },
465
- _elementRef: 'input',
466
- _isDatepicker: true
467
- }
468
- },
469
- computed: {
470
- computedValue: {
471
- get() {
472
- return this.dateSelected
473
- },
474
- set(value) {
475
- this.updateInternalState(value)
476
- if (!this.multiple) this.togglePicker(false)
477
- this.$emit('input', value)
478
- if (this.useHtml5Validation) {
479
- this.$nextTick(() => {
480
- this.checkHtml5Validity()
481
- })
482
- }
483
- }
484
- },
485
- formattedValue() {
486
- return this.formatValue(this.computedValue)
487
- },
488
- localeOptions() {
489
- return new Intl.DateTimeFormat(this.locale, {
490
- year: 'numeric',
491
- month: 'numeric'
492
- }).resolvedOptions()
493
- },
494
- dtf() {
495
- return new Intl.DateTimeFormat(this.locale)
496
- },
497
- dtfMonth() {
498
- return new Intl.DateTimeFormat(this.locale, {
499
- year: this.localeOptions.year || 'numeric',
500
- month: this.localeOptions.month || '2-digit'
501
- })
502
- },
503
- newMonthNames() {
504
- if (Array.isArray(this.monthNames)) {
505
- return this.monthNames
506
- }
507
- return getMonthNames(this.locale)
508
- },
509
- newDayNames() {
510
- if (Array.isArray(this.dayNames)) {
511
- return this.dayNames
512
- }
513
- return getWeekdayNames(this.locale)
514
- },
515
- listOfMonths() {
516
- let minMonth = 0
517
- let maxMonth = 12
518
- if (this.minDate && this.focusedDateData.year === this.minDate.getFullYear()) {
519
- minMonth = this.minDate.getMonth()
520
- }
521
- if (this.maxDate && this.focusedDateData.year === this.maxDate.getFullYear()) {
522
- maxMonth = this.maxDate.getMonth()
523
- }
524
- return this.newMonthNames.map((name, index) => {
525
- return {
526
- name: name,
527
- index: index,
528
- disabled: index < minMonth || index > maxMonth
529
- }
530
- })
531
- },
532
- /*
533
- * Returns an array of years for the year dropdown. If earliest/latest
534
- * dates are set by props, range of years will fall within those dates.
535
- */
536
- listOfYears() {
537
- let latestYear = this.focusedDateData.year + this.yearsRange[1]
538
- if (this.maxDate && this.maxDate.getFullYear() < latestYear) {
539
- latestYear = Math.max(this.maxDate.getFullYear(), this.focusedDateData.year)
540
- }
541
-
542
- let earliestYear = this.focusedDateData.year + this.yearsRange[0]
543
- if (this.minDate && this.minDate.getFullYear() > earliestYear) {
544
- earliestYear = Math.min(this.minDate.getFullYear(), this.focusedDateData.year)
545
- }
546
-
547
- const arrayOfYears = []
548
- for (let i = earliestYear; i <= latestYear; i++) {
549
- arrayOfYears.push(i)
550
- }
551
-
552
- return arrayOfYears.reverse()
553
- },
554
-
555
- showPrev() {
556
- if (!this.minDate) return false
557
- if (this.isTypeMonth) {
558
- return this.focusedDateData.year <= this.minDate.getFullYear()
559
- }
560
- const dateToCheck = new Date(this.focusedDateData.year, this.focusedDateData.month)
561
- const date = new Date(this.minDate.getFullYear(), this.minDate.getMonth())
562
- return (dateToCheck <= date)
563
- },
564
-
565
- showNext() {
566
- if (!this.maxDate) return false
567
- if (this.isTypeMonth) {
568
- return this.focusedDateData.year >= this.maxDate.getFullYear()
569
- }
570
- const dateToCheck = new Date(this.focusedDateData.year, this.focusedDateData.month)
571
- const date = new Date(this.maxDate.getFullYear(), this.maxDate.getMonth())
572
- return (dateToCheck >= date)
573
- },
574
-
575
- isMobile() {
576
- return this.mobileNative && isMobile.any()
577
- },
578
-
579
- isTypeMonth() {
580
- return this.type === 'month'
581
- },
582
-
583
- ariaRole() {
584
- if (!this.inline) {
585
- return 'dialog'
586
- }
587
- }
588
- },
589
- watch: {
590
- /**
591
- * When v-model is changed:
592
- * 1. Update internal value.
593
- * 2. If it's invalid, validate again.
594
- */
595
- value(value) {
596
- this.updateInternalState(value)
597
- if (!this.multiple) this.togglePicker(false)
598
- },
599
-
600
- focusedDate(value) {
601
- if (value) {
602
- this.focusedDateData = {
603
- day: value.getDate(),
604
- month: value.getMonth(),
605
- year: value.getFullYear()
606
- }
607
- }
608
- },
609
-
610
- /*
611
- * Emit input event on month and/or year change
612
- */
613
- 'focusedDateData.month'(value) {
614
- this.$emit('change-month', value)
615
- },
616
- 'focusedDateData.year'(value) {
617
- this.$emit('change-year', value)
618
- }
619
- },
620
- methods: {
621
- /*
622
- * Parse string into date
623
- */
624
- onChange(value) {
625
- const date = this.dateParser(value, this)
626
- if (date && (!isNaN(date) ||
627
- (Array.isArray(date) && date.length === 2 && !isNaN(date[0]) && !isNaN(date[1])))) {
628
- this.computedValue = date
629
- } else {
630
- // Force refresh input value when not valid date
631
- this.computedValue = null
632
- if (this.$refs.input) {
633
- this.$refs.input.newValue = this.computedValue
634
- }
635
- }
636
- },
637
-
638
- /*
639
- * Format date into string
640
- */
641
- formatValue(value) {
642
- if (Array.isArray(value)) {
643
- const isArrayWithValidDates = Array.isArray(value) && value.every((v) => !isNaN(v))
644
- return isArrayWithValidDates ? this.dateFormatter([...value], this) : null
645
- }
646
- return (value && !isNaN(value)) ? this.dateFormatter(value, this) : null
647
- },
648
-
649
- /*
650
- * Either decrement month by 1 if not January or decrement year by 1
651
- * and set month to 11 (December) or decrement year when 'month'
652
- */
653
- prev() {
654
- if (this.disabled) return
655
-
656
- if (this.isTypeMonth) {
657
- this.focusedDateData.year -= 1
658
- } else {
659
- if (this.focusedDateData.month > 0) {
660
- this.focusedDateData.month -= 1
661
- } else {
662
- this.focusedDateData.month = 11
663
- this.focusedDateData.year -= 1
664
- }
665
- }
666
- },
667
-
668
- /*
669
- * Either increment month by 1 if not December or increment year by 1
670
- * and set month to 0 (January) or increment year when 'month'
671
- */
672
- next() {
673
- if (this.disabled) return
674
-
675
- if (this.isTypeMonth) {
676
- this.focusedDateData.year += 1
677
- } else {
678
- if (this.focusedDateData.month < 11) {
679
- this.focusedDateData.month += 1
680
- } else {
681
- this.focusedDateData.month = 0
682
- this.focusedDateData.year += 1
683
- }
684
- }
685
- },
686
-
687
- formatNative(value) {
688
- return this.isTypeMonth
689
- ? this.formatYYYYMM(value) : this.formatYYYYMMDD(value)
690
- },
691
-
692
- /*
693
- * Format date into string 'YYYY-MM-DD'
694
- */
695
- formatYYYYMMDD(value) {
696
- const date = new Date(value)
697
- if (value && !isNaN(date)) {
698
- const year = date.getFullYear()
699
- const month = date.getMonth() + 1
700
- const day = date.getDate()
701
- return year + '-' +
702
- ((month < 10 ? '0' : '') + month) + '-' +
703
- ((day < 10 ? '0' : '') + day)
704
- }
705
- return ''
706
- },
707
-
708
- /*
709
- * Format date into string 'YYYY-MM'
710
- */
711
- formatYYYYMM(value) {
712
- const date = new Date(value)
713
- if (value && !isNaN(date)) {
714
- const year = date.getFullYear()
715
- const month = date.getMonth() + 1
716
- return year + '-' +
717
- ((month < 10 ? '0' : '') + month)
718
- }
719
- return ''
720
- },
721
-
722
- /*
723
- * Parse date from string
724
- */
725
- onChangeNativePicker(event) {
726
- const date = event.target.value
727
- const s = date ? date.split('-') : []
728
- if (s.length === 3) {
729
- const year = parseInt(s[0], 10)
730
- const month = parseInt(s[1]) - 1
731
- const day = parseInt(s[2])
732
- this.computedValue = new Date(year, month, day)
733
- } else {
734
- this.computedValue = null
735
- }
736
- },
737
- updateInternalState(value) {
738
- if (this.dateSelected === value) return
739
- const isArray = Array.isArray(value)
740
- const currentDate = isArray
741
- ? (!value.length ? this.dateCreator() : value[value.length - 1])
742
- : (!value ? this.dateCreator() : value)
743
- if (!isArray ||
744
- (isArray && this.dateSelected && value.length > this.dateSelected.length)) {
745
- this.focusedDateData = {
746
- day: currentDate.getDate(),
747
- month: currentDate.getMonth(),
748
- year: currentDate.getFullYear()
749
- }
750
- }
751
- this.dateSelected = value
752
- },
753
-
754
- /*
755
- * Toggle datepicker
756
- */
757
- togglePicker(active) {
758
- if (this.$refs.dropdown) {
759
- const isActive = typeof active === 'boolean'
760
- ? active
761
- : !this.$refs.dropdown.isActive
762
- if (isActive) {
763
- this.$refs.dropdown.isActive = isActive
764
- } else if (this.closeOnClick) {
765
- this.$refs.dropdown.isActive = isActive
766
- }
767
- }
768
- },
769
-
770
- /*
771
- * Call default onFocus method and show datepicker
772
- */
773
- handleOnFocus(event) {
774
- this.onFocus(event)
775
- if (this.openOnFocus) {
776
- this.togglePicker(true)
777
- }
778
- },
779
-
780
- /*
781
- * Toggle dropdown
782
- */
783
- toggle() {
784
- if (this.mobileNative && this.isMobile) {
785
- const input = this.$refs.input.$refs.input
786
- input.focus()
787
- input.click()
788
- return
789
- }
790
- this.$refs.dropdown.toggle()
791
- },
792
-
793
- /*
794
- * Avoid dropdown toggle when is already visible
795
- */
796
- onInputClick(event) {
797
- if (this.$refs.dropdown.isActive) {
798
- event.stopPropagation()
799
- }
800
- },
801
-
802
- /**
803
- * Keypress event that is bound to the document.
804
- */
805
- keyPress({ key }) {
806
- if (this.$refs.dropdown && this.$refs.dropdown.isActive && (key === 'Escape' || key === 'Esc')) {
807
- this.togglePicker(false)
808
- }
809
- },
810
-
811
- /**
812
- * Emit 'blur' event on dropdown is not active (closed)
813
- */
814
- onActiveChange(value) {
815
- if (!value) {
816
- this.onBlur()
817
- }
818
- /*
819
- * Emit 'active-change' when on dropdown active state change
820
- */
821
- this.$emit('active-change', value)
822
- },
823
-
824
- changeFocus(day) {
825
- this.focusedDateData = {
826
- day: day.getDate(),
827
- month: day.getMonth(),
828
- year: day.getFullYear()
829
- }
830
- }
831
- },
832
- created() {
833
- if (typeof window !== 'undefined') {
834
- document.addEventListener('keyup', this.keyPress)
835
- }
836
- },
837
- beforeDestroy() {
838
- if (typeof window !== 'undefined') {
839
- document.removeEventListener('keyup', this.keyPress)
840
- }
841
- }
842
- }
843
- </script>
1
+ <template>
2
+ <div
3
+ class="datepicker control"
4
+ :class="[size, {'is-expanded': expanded}]"
5
+ >
6
+ <b-dropdown
7
+ v-if="!isMobile || inline"
8
+ ref="dropdown"
9
+ :position="position"
10
+ :disabled="disabled"
11
+ :inline="inline"
12
+ :mobile-modal="mobileModal"
13
+ :trap-focus="trapFocus"
14
+ :aria-role="ariaRole"
15
+ :aria-modal="!inline"
16
+ :append-to-body="appendToBody"
17
+ append-to-body-copy-parent
18
+ @active-change="onActiveChange">
19
+ <template #trigger v-if="!inline">
20
+ <slot name="trigger">
21
+ <b-input
22
+ ref="input"
23
+ autocomplete="off"
24
+ :value="formattedValue"
25
+ :placeholder="placeholder"
26
+ :size="size"
27
+ :icon="icon"
28
+ :icon-right="iconRight"
29
+ :icon-right-clickable="iconRightClickable"
30
+ :icon-pack="iconPack"
31
+ :rounded="rounded"
32
+ :loading="loading"
33
+ :disabled="disabled"
34
+ :readonly="!editable"
35
+ v-bind="$attrs"
36
+ :use-html5-validation="false"
37
+ @click.native="onInputClick"
38
+ @icon-right-click="$emit('icon-right-click')"
39
+ @keyup.native.enter="togglePicker(true)"
40
+ @change.native="onChange($event.target.value)"
41
+ @focus="handleOnFocus" />
42
+ </slot>
43
+ </template>
44
+
45
+ <b-dropdown-item
46
+ :disabled="disabled"
47
+ :focusable="focusable"
48
+ custom
49
+ :class="{'dropdown-horizonal-timepicker': horizontalTimePicker}">
50
+ <div>
51
+ <header class="datepicker-header">
52
+ <template v-if="$slots.header !== undefined && $slots.header.length">
53
+ <slot name="header" />
54
+ </template>
55
+ <div
56
+ v-else
57
+ class="pagination field is-centered"
58
+ :class="size">
59
+ <a
60
+ v-show="!showPrev && !disabled"
61
+ class="pagination-previous"
62
+ role="button"
63
+ href="#"
64
+ :disabled="disabled"
65
+ :aria-label="ariaPreviousLabel"
66
+ @click.prevent="prev"
67
+ @keydown.enter.prevent="prev"
68
+ @keydown.space.prevent="prev">
69
+
70
+ <b-icon
71
+ :icon="iconPrev"
72
+ :pack="iconPack"
73
+ both
74
+ type="is-primary is-clickable"/>
75
+ </a>
76
+ <a
77
+ v-show="!showNext && !disabled"
78
+ class="pagination-next"
79
+ role="button"
80
+ href="#"
81
+ :disabled="disabled"
82
+ :aria-label="ariaNextLabel"
83
+ @click.prevent="next"
84
+ @keydown.enter.prevent="next"
85
+ @keydown.space.prevent="next">
86
+
87
+ <b-icon
88
+ :icon="iconNext"
89
+ :pack="iconPack"
90
+ both
91
+ type="is-primary is-clickable"/>
92
+ </a>
93
+ <div class="pagination-list">
94
+ <b-field>
95
+ <b-select
96
+ v-if="!isTypeMonth"
97
+ v-model="focusedDateData.month"
98
+ :disabled="disabled"
99
+ :size="size">
100
+ <option
101
+ v-for="month in listOfMonths"
102
+ :value="month.index"
103
+ :key="month.name"
104
+ :disabled="month.disabled">
105
+ {{ month.name }}
106
+ </option>
107
+ </b-select>
108
+ <b-select
109
+ v-model="focusedDateData.year"
110
+ :disabled="disabled"
111
+ :size="size">
112
+ <option
113
+ v-for="year in listOfYears"
114
+ :value="year"
115
+ :key="year">
116
+ {{ year }}
117
+ </option>
118
+ </b-select>
119
+ </b-field>
120
+ </div>
121
+ </div>
122
+ </header>
123
+ <div
124
+ v-if="!isTypeMonth"
125
+ class="datepicker-content"
126
+ :class="{'content-horizonal-timepicker': horizontalTimePicker}">
127
+ <b-datepicker-table
128
+ v-model="computedValue"
129
+ :day-names="newDayNames"
130
+ :month-names="newMonthNames"
131
+ :first-day-of-week="firstDayOfWeek"
132
+ :rules-for-first-week="rulesForFirstWeek"
133
+ :min-date="minDate"
134
+ :max-date="maxDate"
135
+ :focused="focusedDateData"
136
+ :disabled="disabled"
137
+ :unselectable-dates="unselectableDates"
138
+ :unselectable-days-of-week="unselectableDaysOfWeek"
139
+ :selectable-dates="selectableDates"
140
+ :events="events"
141
+ :indicators="indicators"
142
+ :date-creator="dateCreator"
143
+ :type-month="isTypeMonth"
144
+ :nearby-month-days="nearbyMonthDays"
145
+ :nearby-selectable-month-days="nearbySelectableMonthDays"
146
+ :show-week-number="showWeekNumber"
147
+ :week-number-clickable="weekNumberClickable"
148
+ :range="range"
149
+ :multiple="multiple"
150
+ @range-start="date => $emit('range-start', date)"
151
+ @range-end="date => $emit('range-end', date)"
152
+ @close="togglePicker(false)"
153
+ @update:focused="focusedDateData = $event" />
154
+ </div>
155
+ <div v-else>
156
+ <b-datepicker-month
157
+ v-model="computedValue"
158
+ :month-names="newMonthNames"
159
+ :min-date="minDate"
160
+ :max-date="maxDate"
161
+ :focused="focusedDateData"
162
+ :disabled="disabled"
163
+ :unselectable-dates="unselectableDates"
164
+ :unselectable-days-of-week="unselectableDaysOfWeek"
165
+ :selectable-dates="selectableDates"
166
+ :events="events"
167
+ :indicators="indicators"
168
+ :date-creator="dateCreator"
169
+ :range="range"
170
+ :multiple="multiple"
171
+ @range-start="date => $emit('range-start', date)"
172
+ @range-end="date => $emit('range-end', date)"
173
+ @close="togglePicker(false)"
174
+ @change-focus="changeFocus"
175
+ @update:focused="focusedDateData = $event" />
176
+ </div>
177
+ </div>
178
+
179
+ <footer
180
+ v-if="$slots.default !== undefined && $slots.default.length"
181
+ class="datepicker-footer"
182
+ :class="{'footer-horizontal-timepicker': horizontalTimePicker}">
183
+ <slot/>
184
+ </footer>
185
+ </b-dropdown-item>
186
+ </b-dropdown>
187
+
188
+ <b-input
189
+ v-else
190
+ ref="input"
191
+ :type="!isTypeMonth ? 'date' : 'month'"
192
+ autocomplete="off"
193
+ :value="formatNative(computedValue)"
194
+ :placeholder="placeholder"
195
+ :size="size"
196
+ :icon="icon"
197
+ :icon-pack="iconPack"
198
+ :rounded="rounded"
199
+ :loading="loading"
200
+ :max="formatNative(maxDate)"
201
+ :min="formatNative(minDate)"
202
+ :disabled="disabled"
203
+ :readonly="false"
204
+ v-bind="$attrs"
205
+ :use-html5-validation="false"
206
+ @change.native="onChangeNativePicker"
207
+ @focus="onFocus"
208
+ @blur="onBlur"/>
209
+ </div>
210
+ </template>
211
+
212
+ <script>
213
+ import FormElementMixin from '../../utils/FormElementMixin'
214
+ import { isMobile, getMonthNames, getWeekdayNames, matchWithGroups } from '../../utils/helpers'
215
+ import config from '../../utils/config'
216
+
217
+ import Dropdown from '../dropdown/Dropdown'
218
+ import DropdownItem from '../dropdown/DropdownItem'
219
+ import Input from '../input/Input'
220
+ import Field from '../field/Field'
221
+ import Select from '../select/Select'
222
+ import Icon from '../icon/Icon'
223
+
224
+ import DatepickerTable from './DatepickerTable'
225
+ import DatepickerMonth from './DatepickerMonth'
226
+
227
+ const defaultDateFormatter = (date, vm) => {
228
+ const targetDates = Array.isArray(date) ? date : [date]
229
+ const dates = targetDates.map((date) => {
230
+ const d = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 12)
231
+ return !vm.isTypeMonth ? vm.dtf.format(d) : vm.dtfMonth.format(d)
232
+ })
233
+ return !vm.multiple ? dates.join(' - ') : dates.join(', ')
234
+ }
235
+
236
+ const defaultDateParser = (date, vm) => {
237
+ if (vm.dtf.formatToParts && typeof vm.dtf.formatToParts === 'function') {
238
+ const formatRegex = (vm.isTypeMonth ? vm.dtfMonth : vm.dtf)
239
+ .formatToParts(new Date(2000, 11, 25)).map((part) => {
240
+ if (part.type === 'literal') {
241
+ return part.value
242
+ }
243
+ return `((?!=<${part.type}>)\\d+)`
244
+ }).join('')
245
+ const dateGroups = matchWithGroups(formatRegex, date)
246
+
247
+ // We do a simple validation for the group.
248
+ // If it is not valid, it will fallback to Date.parse below
249
+ if (
250
+ dateGroups.year &&
251
+ dateGroups.year.length === 4 &&
252
+ dateGroups.month &&
253
+ dateGroups.month <= 12
254
+ ) {
255
+ if (vm.isTypeMonth) return new Date(dateGroups.year, dateGroups.month - 1)
256
+ else if (dateGroups.day && dateGroups.day <= 31) {
257
+ return new Date(dateGroups.year, dateGroups.month - 1, dateGroups.day, 12)
258
+ }
259
+ }
260
+ }
261
+ // Fallback if formatToParts is not supported or if we were not able to parse a valid date
262
+ if (!vm.isTypeMonth) return new Date(Date.parse(date))
263
+ if (date) {
264
+ const s = date.split('/')
265
+ const year = s[0].length === 4 ? s[0] : s[1]
266
+ const month = s[0].length === 2 ? s[0] : s[1]
267
+ if (year && month) {
268
+ return new Date(parseInt(year, 10), parseInt(month - 1, 10), 1, 0, 0, 0, 0)
269
+ }
270
+ }
271
+ return null
272
+ }
273
+
274
+ export default {
275
+ name: 'BDatepicker',
276
+ components: {
277
+ [DatepickerTable.name]: DatepickerTable,
278
+ [DatepickerMonth.name]: DatepickerMonth,
279
+ [Input.name]: Input,
280
+ [Field.name]: Field,
281
+ [Select.name]: Select,
282
+ [Icon.name]: Icon,
283
+ [Dropdown.name]: Dropdown,
284
+ [DropdownItem.name]: DropdownItem
285
+ },
286
+ mixins: [FormElementMixin],
287
+ inheritAttrs: false,
288
+ provide() {
289
+ return {
290
+ $datepicker: this
291
+ }
292
+ },
293
+ props: {
294
+ value: {
295
+ type: [Date, Array]
296
+ },
297
+ dayNames: {
298
+ type: Array,
299
+ default: () => {
300
+ if (!Array.isArray(config.defaultDayNames)) {
301
+ return undefined
302
+ }
303
+ return config.defaultDayNames
304
+ }
305
+ },
306
+ monthNames: {
307
+ type: Array,
308
+ default: () => {
309
+ if (!Array.isArray(config.defaultMonthNames)) {
310
+ return undefined
311
+ }
312
+ return config.defaultMonthNames
313
+ }
314
+ },
315
+ firstDayOfWeek: {
316
+ type: Number,
317
+ default: () => {
318
+ if (typeof config.defaultFirstDayOfWeek === 'number') {
319
+ return config.defaultFirstDayOfWeek
320
+ } else {
321
+ return 0
322
+ }
323
+ }
324
+ },
325
+ inline: Boolean,
326
+ minDate: Date,
327
+ maxDate: Date,
328
+ focusedDate: Date,
329
+ placeholder: String,
330
+ editable: Boolean,
331
+ disabled: Boolean,
332
+ horizontalTimePicker: Boolean,
333
+ unselectableDates: [Array, Function],
334
+ unselectableDaysOfWeek: {
335
+ type: Array,
336
+ default: () => config.defaultUnselectableDaysOfWeek
337
+ },
338
+ selectableDates: [Array, Function],
339
+ dateFormatter: {
340
+ type: Function,
341
+ default: (date, vm) => {
342
+ if (typeof config.defaultDateFormatter === 'function') {
343
+ return config.defaultDateFormatter(date)
344
+ } else {
345
+ return defaultDateFormatter(date, vm)
346
+ }
347
+ }
348
+ },
349
+ dateParser: {
350
+ type: Function,
351
+ default: (date, vm) => {
352
+ if (typeof config.defaultDateParser === 'function') {
353
+ return config.defaultDateParser(date)
354
+ } else {
355
+ return defaultDateParser(date, vm)
356
+ }
357
+ }
358
+ },
359
+ dateCreator: {
360
+ type: Function,
361
+ default: () => {
362
+ if (typeof config.defaultDateCreator === 'function') {
363
+ return config.defaultDateCreator()
364
+ } else {
365
+ return new Date()
366
+ }
367
+ }
368
+ },
369
+ mobileNative: {
370
+ type: Boolean,
371
+ default: () => config.defaultDatepickerMobileNative
372
+ },
373
+ position: String,
374
+ iconRight: String,
375
+ iconRightClickable: Boolean,
376
+ events: Array,
377
+ indicators: {
378
+ type: String,
379
+ default: 'dots'
380
+ },
381
+ openOnFocus: Boolean,
382
+ iconPrev: {
383
+ type: String,
384
+ default: () => config.defaultIconPrev
385
+ },
386
+ iconNext: {
387
+ type: String,
388
+ default: () => config.defaultIconNext
389
+ },
390
+ yearsRange: {
391
+ type: Array,
392
+ default: () => config.defaultDatepickerYearsRange
393
+ },
394
+ type: {
395
+ type: String,
396
+ validator: (value) => {
397
+ return [
398
+ 'month'
399
+ ].indexOf(value) >= 0
400
+ }
401
+ },
402
+ nearbyMonthDays: {
403
+ type: Boolean,
404
+ default: () => config.defaultDatepickerNearbyMonthDays
405
+ },
406
+ nearbySelectableMonthDays: {
407
+ type: Boolean,
408
+ default: () => config.defaultDatepickerNearbySelectableMonthDays
409
+ },
410
+ showWeekNumber: {
411
+ type: Boolean,
412
+ default: () => config.defaultDatepickerShowWeekNumber
413
+ },
414
+ weekNumberClickable: {
415
+ type: Boolean,
416
+ default: () => config.defaultDatepickerWeekNumberClickable
417
+ },
418
+ rulesForFirstWeek: {
419
+ type: Number,
420
+ default: () => 4
421
+ },
422
+ range: {
423
+ type: Boolean,
424
+ default: false
425
+ },
426
+ closeOnClick: {
427
+ type: Boolean,
428
+ default: true
429
+ },
430
+ multiple: {
431
+ type: Boolean,
432
+ default: false
433
+ },
434
+ mobileModal: {
435
+ type: Boolean,
436
+ default: () => config.defaultDatepickerMobileModal
437
+ },
438
+ focusable: {
439
+ type: Boolean,
440
+ default: true
441
+ },
442
+ trapFocus: {
443
+ type: Boolean,
444
+ default: () => config.defaultTrapFocus
445
+ },
446
+ appendToBody: Boolean,
447
+ ariaNextLabel: String,
448
+ ariaPreviousLabel: String
449
+ },
450
+ data() {
451
+ const focusedDate = (Array.isArray(this.value) ? this.value[0] : (this.value)) ||
452
+ this.focusedDate || this.dateCreator()
453
+
454
+ if (!this.value && this.maxDate && this.maxDate.getFullYear() < focusedDate.getFullYear()) {
455
+ focusedDate.setFullYear(this.maxDate.getFullYear())
456
+ }
457
+
458
+ return {
459
+ dateSelected: this.value,
460
+ focusedDateData: {
461
+ day: focusedDate.getDate(),
462
+ month: focusedDate.getMonth(),
463
+ year: focusedDate.getFullYear()
464
+ },
465
+ _elementRef: 'input',
466
+ _isDatepicker: true
467
+ }
468
+ },
469
+ computed: {
470
+ computedValue: {
471
+ get() {
472
+ return this.dateSelected
473
+ },
474
+ set(value) {
475
+ this.updateInternalState(value)
476
+ if (!this.multiple) this.togglePicker(false)
477
+ this.$emit('input', value)
478
+ if (this.useHtml5Validation) {
479
+ this.$nextTick(() => {
480
+ this.checkHtml5Validity()
481
+ })
482
+ }
483
+ }
484
+ },
485
+ formattedValue() {
486
+ return this.formatValue(this.computedValue)
487
+ },
488
+ localeOptions() {
489
+ return new Intl.DateTimeFormat(this.locale, {
490
+ year: 'numeric',
491
+ month: 'numeric'
492
+ }).resolvedOptions()
493
+ },
494
+ dtf() {
495
+ return new Intl.DateTimeFormat(this.locale)
496
+ },
497
+ dtfMonth() {
498
+ return new Intl.DateTimeFormat(this.locale, {
499
+ year: this.localeOptions.year || 'numeric',
500
+ month: this.localeOptions.month || '2-digit'
501
+ })
502
+ },
503
+ newMonthNames() {
504
+ if (Array.isArray(this.monthNames)) {
505
+ return this.monthNames
506
+ }
507
+ return getMonthNames(this.locale)
508
+ },
509
+ newDayNames() {
510
+ if (Array.isArray(this.dayNames)) {
511
+ return this.dayNames
512
+ }
513
+ return getWeekdayNames(this.locale)
514
+ },
515
+ listOfMonths() {
516
+ let minMonth = 0
517
+ let maxMonth = 12
518
+ if (this.minDate && this.focusedDateData.year === this.minDate.getFullYear()) {
519
+ minMonth = this.minDate.getMonth()
520
+ }
521
+ if (this.maxDate && this.focusedDateData.year === this.maxDate.getFullYear()) {
522
+ maxMonth = this.maxDate.getMonth()
523
+ }
524
+ return this.newMonthNames.map((name, index) => {
525
+ return {
526
+ name: name,
527
+ index: index,
528
+ disabled: index < minMonth || index > maxMonth
529
+ }
530
+ })
531
+ },
532
+ /*
533
+ * Returns an array of years for the year dropdown. If earliest/latest
534
+ * dates are set by props, range of years will fall within those dates.
535
+ */
536
+ listOfYears() {
537
+ let latestYear = this.focusedDateData.year + this.yearsRange[1]
538
+ if (this.maxDate && this.maxDate.getFullYear() < latestYear) {
539
+ latestYear = Math.max(this.maxDate.getFullYear(), this.focusedDateData.year)
540
+ }
541
+
542
+ let earliestYear = this.focusedDateData.year + this.yearsRange[0]
543
+ if (this.minDate && this.minDate.getFullYear() > earliestYear) {
544
+ earliestYear = Math.min(this.minDate.getFullYear(), this.focusedDateData.year)
545
+ }
546
+
547
+ const arrayOfYears = []
548
+ for (let i = earliestYear; i <= latestYear; i++) {
549
+ arrayOfYears.push(i)
550
+ }
551
+
552
+ return arrayOfYears.reverse()
553
+ },
554
+
555
+ showPrev() {
556
+ if (!this.minDate) return false
557
+ if (this.isTypeMonth) {
558
+ return this.focusedDateData.year <= this.minDate.getFullYear()
559
+ }
560
+ const dateToCheck = new Date(this.focusedDateData.year, this.focusedDateData.month)
561
+ const date = new Date(this.minDate.getFullYear(), this.minDate.getMonth())
562
+ return (dateToCheck <= date)
563
+ },
564
+
565
+ showNext() {
566
+ if (!this.maxDate) return false
567
+ if (this.isTypeMonth) {
568
+ return this.focusedDateData.year >= this.maxDate.getFullYear()
569
+ }
570
+ const dateToCheck = new Date(this.focusedDateData.year, this.focusedDateData.month)
571
+ const date = new Date(this.maxDate.getFullYear(), this.maxDate.getMonth())
572
+ return (dateToCheck >= date)
573
+ },
574
+
575
+ isMobile() {
576
+ return this.mobileNative && isMobile.any()
577
+ },
578
+
579
+ isTypeMonth() {
580
+ return this.type === 'month'
581
+ },
582
+
583
+ ariaRole() {
584
+ if (!this.inline) {
585
+ return 'dialog'
586
+ }
587
+ }
588
+ },
589
+ watch: {
590
+ /**
591
+ * When v-model is changed:
592
+ * 1. Update internal value.
593
+ * 2. If it's invalid, validate again.
594
+ */
595
+ value(value) {
596
+ this.updateInternalState(value)
597
+ if (!this.multiple) this.togglePicker(false)
598
+ },
599
+
600
+ focusedDate(value) {
601
+ if (value) {
602
+ this.focusedDateData = {
603
+ day: value.getDate(),
604
+ month: value.getMonth(),
605
+ year: value.getFullYear()
606
+ }
607
+ }
608
+ },
609
+
610
+ /*
611
+ * Emit input event on month and/or year change
612
+ */
613
+ 'focusedDateData.month'(value) {
614
+ this.$emit('change-month', value)
615
+ },
616
+ 'focusedDateData.year'(value) {
617
+ this.$emit('change-year', value)
618
+ }
619
+ },
620
+ methods: {
621
+ /*
622
+ * Parse string into date
623
+ */
624
+ onChange(value) {
625
+ const date = this.dateParser(value, this)
626
+ if (date && (!isNaN(date) ||
627
+ (Array.isArray(date) && date.length === 2 && !isNaN(date[0]) && !isNaN(date[1])))) {
628
+ this.computedValue = date
629
+ } else {
630
+ // Force refresh input value when not valid date
631
+ this.computedValue = null
632
+ if (this.$refs.input) {
633
+ this.$refs.input.newValue = this.computedValue
634
+ }
635
+ }
636
+ },
637
+
638
+ /*
639
+ * Format date into string
640
+ */
641
+ formatValue(value) {
642
+ if (Array.isArray(value)) {
643
+ const isArrayWithValidDates = Array.isArray(value) && value.every((v) => !isNaN(v))
644
+ return isArrayWithValidDates ? this.dateFormatter([...value], this) : null
645
+ }
646
+ return (value && !isNaN(value)) ? this.dateFormatter(value, this) : null
647
+ },
648
+
649
+ /*
650
+ * Either decrement month by 1 if not January or decrement year by 1
651
+ * and set month to 11 (December) or decrement year when 'month'
652
+ */
653
+ prev() {
654
+ if (this.disabled) return
655
+
656
+ if (this.isTypeMonth) {
657
+ this.focusedDateData.year -= 1
658
+ } else {
659
+ if (this.focusedDateData.month > 0) {
660
+ this.focusedDateData.month -= 1
661
+ } else {
662
+ this.focusedDateData.month = 11
663
+ this.focusedDateData.year -= 1
664
+ }
665
+ }
666
+ },
667
+
668
+ /*
669
+ * Either increment month by 1 if not December or increment year by 1
670
+ * and set month to 0 (January) or increment year when 'month'
671
+ */
672
+ next() {
673
+ if (this.disabled) return
674
+
675
+ if (this.isTypeMonth) {
676
+ this.focusedDateData.year += 1
677
+ } else {
678
+ if (this.focusedDateData.month < 11) {
679
+ this.focusedDateData.month += 1
680
+ } else {
681
+ this.focusedDateData.month = 0
682
+ this.focusedDateData.year += 1
683
+ }
684
+ }
685
+ },
686
+
687
+ formatNative(value) {
688
+ return this.isTypeMonth
689
+ ? this.formatYYYYMM(value) : this.formatYYYYMMDD(value)
690
+ },
691
+
692
+ /*
693
+ * Format date into string 'YYYY-MM-DD'
694
+ */
695
+ formatYYYYMMDD(value) {
696
+ const date = new Date(value)
697
+ if (value && !isNaN(date)) {
698
+ const year = date.getFullYear()
699
+ const month = date.getMonth() + 1
700
+ const day = date.getDate()
701
+ return year + '-' +
702
+ ((month < 10 ? '0' : '') + month) + '-' +
703
+ ((day < 10 ? '0' : '') + day)
704
+ }
705
+ return ''
706
+ },
707
+
708
+ /*
709
+ * Format date into string 'YYYY-MM'
710
+ */
711
+ formatYYYYMM(value) {
712
+ const date = new Date(value)
713
+ if (value && !isNaN(date)) {
714
+ const year = date.getFullYear()
715
+ const month = date.getMonth() + 1
716
+ return year + '-' +
717
+ ((month < 10 ? '0' : '') + month)
718
+ }
719
+ return ''
720
+ },
721
+
722
+ /*
723
+ * Parse date from string
724
+ */
725
+ onChangeNativePicker(event) {
726
+ const date = event.target.value
727
+ const s = date ? date.split('-') : []
728
+ if (s.length === 3) {
729
+ const year = parseInt(s[0], 10)
730
+ const month = parseInt(s[1]) - 1
731
+ const day = parseInt(s[2])
732
+ this.computedValue = new Date(year, month, day)
733
+ } else {
734
+ this.computedValue = null
735
+ }
736
+ },
737
+ updateInternalState(value) {
738
+ if (this.dateSelected === value) return
739
+ const isArray = Array.isArray(value)
740
+ const currentDate = isArray
741
+ ? (!value.length ? this.dateCreator() : value[value.length - 1])
742
+ : (!value ? this.dateCreator() : value)
743
+ if (!isArray ||
744
+ (isArray && this.dateSelected && value.length > this.dateSelected.length)) {
745
+ this.focusedDateData = {
746
+ day: currentDate.getDate(),
747
+ month: currentDate.getMonth(),
748
+ year: currentDate.getFullYear()
749
+ }
750
+ }
751
+ this.dateSelected = value
752
+ },
753
+
754
+ /*
755
+ * Toggle datepicker
756
+ */
757
+ togglePicker(active) {
758
+ if (this.$refs.dropdown) {
759
+ const isActive = typeof active === 'boolean'
760
+ ? active
761
+ : !this.$refs.dropdown.isActive
762
+ if (isActive) {
763
+ this.$refs.dropdown.isActive = isActive
764
+ } else if (this.closeOnClick) {
765
+ this.$refs.dropdown.isActive = isActive
766
+ }
767
+ }
768
+ },
769
+
770
+ /*
771
+ * Call default onFocus method and show datepicker
772
+ */
773
+ handleOnFocus(event) {
774
+ this.onFocus(event)
775
+ if (this.openOnFocus) {
776
+ this.togglePicker(true)
777
+ }
778
+ },
779
+
780
+ /*
781
+ * Toggle dropdown
782
+ */
783
+ toggle() {
784
+ if (this.mobileNative && this.isMobile) {
785
+ const input = this.$refs.input.$refs.input
786
+ input.focus()
787
+ input.click()
788
+ return
789
+ }
790
+ this.$refs.dropdown.toggle()
791
+ },
792
+
793
+ /*
794
+ * Avoid dropdown toggle when is already visible
795
+ */
796
+ onInputClick(event) {
797
+ if (this.$refs.dropdown.isActive) {
798
+ event.stopPropagation()
799
+ }
800
+ },
801
+
802
+ /**
803
+ * Keypress event that is bound to the document.
804
+ */
805
+ keyPress({ key }) {
806
+ if (this.$refs.dropdown && this.$refs.dropdown.isActive && (key === 'Escape' || key === 'Esc')) {
807
+ this.togglePicker(false)
808
+ }
809
+ },
810
+
811
+ /**
812
+ * Emit 'blur' event on dropdown is not active (closed)
813
+ */
814
+ onActiveChange(value) {
815
+ if (!value) {
816
+ this.onBlur()
817
+ }
818
+ /*
819
+ * Emit 'active-change' when on dropdown active state change
820
+ */
821
+ this.$emit('active-change', value)
822
+ },
823
+
824
+ changeFocus(day) {
825
+ this.focusedDateData = {
826
+ day: day.getDate(),
827
+ month: day.getMonth(),
828
+ year: day.getFullYear()
829
+ }
830
+ }
831
+ },
832
+ created() {
833
+ if (typeof window !== 'undefined') {
834
+ document.addEventListener('keyup', this.keyPress)
835
+ }
836
+ },
837
+ beforeDestroy() {
838
+ if (typeof window !== 'undefined') {
839
+ document.removeEventListener('keyup', this.keyPress)
840
+ }
841
+ }
842
+ }
843
+ </script>