goodteditor-ui 1.0.26 → 1.0.27

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 (116) hide show
  1. package/.eslintrc.js +7 -7
  2. package/.prettierrc +14 -14
  3. package/README.md +35 -35
  4. package/babel.config.js +5 -5
  5. package/index.js +53 -53
  6. package/jsconfig.json +13 -13
  7. package/package.json +67 -67
  8. package/src/App.vue +36 -36
  9. package/src/components/ui/Avatar.md +68 -68
  10. package/src/components/ui/Avatar.vue +180 -180
  11. package/src/components/ui/Badge.md +20 -20
  12. package/src/components/ui/Badge.vue +75 -75
  13. package/src/components/ui/Collapse.md +90 -90
  14. package/src/components/ui/Collapse.vue +86 -86
  15. package/src/components/ui/ColorPicker/Alpha.vue +114 -114
  16. package/src/components/ui/ColorPicker/Colors.vue +117 -117
  17. package/src/components/ui/ColorPicker/Hue.vue +113 -113
  18. package/src/components/ui/ColorPicker/Preview.vue +55 -55
  19. package/src/components/ui/ColorPicker/Saturation.vue +123 -123
  20. package/src/components/ui/ColorPicker/mixin.js +105 -105
  21. package/src/components/ui/ColorPicker.md +17 -17
  22. package/src/components/ui/ColorPicker.vue +314 -314
  23. package/src/components/ui/Datalist.md +41 -41
  24. package/src/components/ui/Datalist.vue +164 -164
  25. package/src/components/ui/DatePicker.md +168 -168
  26. package/src/components/ui/DatePicker.vue +527 -527
  27. package/src/components/ui/FileSelector.md +105 -105
  28. package/src/components/ui/FileSelector.vue +82 -82
  29. package/src/components/ui/Grid.md +130 -130
  30. package/src/components/ui/Grid.vue +92 -92
  31. package/src/components/ui/Image.md +59 -59
  32. package/src/components/ui/Image.vue +57 -57
  33. package/src/components/ui/InputAutocomplete.md +115 -115
  34. package/src/components/ui/InputAutocomplete.vue +349 -349
  35. package/src/components/ui/InputColorPicker.md +51 -51
  36. package/src/components/ui/InputColorPicker.vue +151 -151
  37. package/src/components/ui/InputDatePicker.md +121 -121
  38. package/src/components/ui/InputDatePicker.vue +326 -326
  39. package/src/components/ui/InputTags.md +51 -51
  40. package/src/components/ui/InputTags.vue +184 -184
  41. package/src/components/ui/InputTimePicker.md +25 -25
  42. package/src/components/ui/InputTimePicker.vue +253 -253
  43. package/src/components/ui/InputUnits.md +20 -20
  44. package/src/components/ui/InputUnits.vue +257 -257
  45. package/src/components/ui/Lazy.md +37 -37
  46. package/src/components/ui/Lazy.vue +92 -92
  47. package/src/components/ui/Pagination.md +74 -74
  48. package/src/components/ui/Pagination.vue +138 -138
  49. package/src/components/ui/Paginator.md +34 -34
  50. package/src/components/ui/Paginator.vue +83 -83
  51. package/src/components/ui/Popover.md +34 -34
  52. package/src/components/ui/Popover.vue +274 -274
  53. package/src/components/ui/Popup.md +59 -59
  54. package/src/components/ui/Popup.vue +150 -150
  55. package/src/components/ui/ResponsiveContainer.md +58 -58
  56. package/src/components/ui/ResponsiveContainer.vue +99 -99
  57. package/src/components/ui/Select.md +187 -187
  58. package/src/components/ui/Select.vue +421 -421
  59. package/src/components/ui/TimePicker.md +50 -50
  60. package/src/components/ui/TimePicker.vue +252 -252
  61. package/src/components/ui/Tooltip.md +54 -54
  62. package/src/components/ui/Tooltip.vue +113 -113
  63. package/src/components/ui/WysiwygEditor/WysiwygEditor.d.ts +128 -128
  64. package/src/components/ui/WysiwygEditor/constants.js +273 -273
  65. package/src/components/ui/WysiwygEditor/extensions/blockquote.js +15 -15
  66. package/src/components/ui/WysiwygEditor/extensions/bold.js +15 -15
  67. package/src/components/ui/WysiwygEditor/extensions/bullet-list.js +15 -15
  68. package/src/components/ui/WysiwygEditor/extensions/code-block.js +13 -13
  69. package/src/components/ui/WysiwygEditor/extensions/code.js +13 -13
  70. package/src/components/ui/WysiwygEditor/extensions/font-size.js +34 -34
  71. package/src/components/ui/WysiwygEditor/extensions/formatting.js +14 -14
  72. package/src/components/ui/WysiwygEditor/extensions/heading.js +13 -13
  73. package/src/components/ui/WysiwygEditor/extensions/horizontal-rule.js +15 -15
  74. package/src/components/ui/WysiwygEditor/extensions/image.js +37 -33
  75. package/src/components/ui/WysiwygEditor/extensions/index.d.ts +32 -32
  76. package/src/components/ui/WysiwygEditor/extensions/index.js +32 -32
  77. package/src/components/ui/WysiwygEditor/extensions/italic.js +15 -15
  78. package/src/components/ui/WysiwygEditor/extensions/link.js +16 -16
  79. package/src/components/ui/WysiwygEditor/extensions/list-item.js +15 -15
  80. package/src/components/ui/WysiwygEditor/extensions/ordered-list.js +15 -15
  81. package/src/components/ui/WysiwygEditor/extensions/paragraph.js +23 -23
  82. package/src/components/ui/WysiwygEditor/extensions/strike.js +15 -15
  83. package/src/components/ui/WysiwygEditor/extensions/table-cell.js +13 -13
  84. package/src/components/ui/WysiwygEditor/extensions/table-header.js +15 -15
  85. package/src/components/ui/WysiwygEditor/extensions/table-row.js +15 -15
  86. package/src/components/ui/WysiwygEditor/extensions/table.js +29 -29
  87. package/src/components/ui/WysiwygEditor/extensions/text-align.js +6 -6
  88. package/src/components/ui/WysiwygEditor/extensions/text-style.js +12 -12
  89. package/src/components/ui/WysiwygEditor/extensions/underline.js +15 -15
  90. package/src/components/ui/WysiwygEditor/index.d.ts +4 -4
  91. package/src/components/ui/WysiwygEditor/index.js +4 -4
  92. package/src/components/ui/WysiwygEditor/renders/Button.vue +28 -28
  93. package/src/components/ui/WysiwygEditor/renders/ColorPicker.vue +41 -41
  94. package/src/components/ui/WysiwygEditor/renders/Image.vue +200 -162
  95. package/src/components/ui/WysiwygEditor/renders/InputAuto.vue +34 -34
  96. package/src/components/ui/WysiwygEditor/renders/InputUnits.vue +37 -37
  97. package/src/components/ui/WysiwygEditor/renders/Link.vue +82 -82
  98. package/src/components/ui/WysiwygEditor/renders/Select.vue +47 -47
  99. package/src/components/ui/WysiwygEditor/renders/ToolbarPopover.vue +49 -49
  100. package/src/components/ui/WysiwygEditor/renders/components/Popover.vue +15 -0
  101. package/src/components/ui/WysiwygEditor/renders/components/WithPopover.vue +35 -35
  102. package/src/components/ui/WysiwygEditor/renders/index.d.ts +8 -8
  103. package/src/components/ui/WysiwygEditor/renders/index.js +8 -8
  104. package/src/components/ui/WysiwygEditor/renders/mixins/RenderMixin.js +39 -39
  105. package/src/components/ui/WysiwygEditor/renders/mixins/index.js +1 -1
  106. package/src/components/ui/WysiwygEditor/tools-and-commands.js +709 -702
  107. package/src/components/ui/WysiwygEditor/utils.js +72 -72
  108. package/src/components/ui/WysiwygEditor.md +18 -18
  109. package/src/components/ui/WysiwygEditor.vue +266 -266
  110. package/src/components/ui/utils/FormComponent.js +107 -107
  111. package/src/components/ui/utils/Helpers.js +84 -84
  112. package/src/components/ui/utils/WithPopover.js +81 -81
  113. package/src/main.js +8 -8
  114. package/styleguide.config.js +37 -37
  115. package/vue.config.js +8 -8
  116. package/dist/js.png +0 -0
@@ -1,527 +1,527 @@
1
- <template>
2
- <div class="ui-date-picker">
3
- <div class="panel d-inline-flex flex-col scroll-hide">
4
- <div class="row row-collapse row-vgap-none flex-nowrap">
5
- <div class="col col-auto" v-if="$scopedSlots.left">
6
- <!--
7
- @slot Custom left content
8
- -->
9
- <slot name="left"></slot>
10
- </div>
11
- <div class="col col-auto">
12
- <!--
13
- @slot Custom header content
14
- @binding {Function} prevYear select previous year
15
- @binding {Function} prevMonth select previous month
16
- @binding {Function} nextYear select next year
17
- @binding {Function} nextMonth select next month
18
- @binding {Function} setMonth set month by index (0 = january)
19
- @binding {Function} setYear set year (4-digit)
20
- @binding {Function} setToday set today's date
21
- @binding {Number} month current month index (0 = january)
22
- @binding {String} monthName current month name
23
- @binding {Number} year current year (4-digit)
24
- -->
25
- <slot
26
- name="header"
27
- v-bind="{
28
- prevYear,
29
- prevMonth,
30
- nextYear,
31
- nextMonth,
32
- setMonth,
33
- setYear,
34
- setToday,
35
- month,
36
- monthName: monthNames[month],
37
- year,
38
- }"
39
- >
40
- <div
41
- class="ui-date-picker-header text-small mar-bot-3 bg-primary color-white pad-3"
42
- >
43
- <div class="flex-shrink nobr">
44
- <div class="icon cursor-pointer" @click="prevYear">
45
- <i class="mdi mdi-chevron-double-left"></i>
46
- </div>
47
- <div class="icon cursor-pointer" @click="prevMonth">
48
- <i class="mdi mdi-chevron-left"></i>
49
- </div>
50
- </div>
51
- <div class="flex-grow flex-h-center d-flex">
52
- <span>{{ monthNames[month] }}</span>
53
- <input class="color-inherit" type="number" v-model.number="year" />
54
- </div>
55
- <div class="flex-shrink nobr">
56
- <div class="icon cursor-pointer" @click="nextMonth">
57
- <i class="mdi mdi-chevron-right"></i>
58
- </div>
59
- <div class="icon cursor-pointer" @click="nextYear">
60
- <i class="mdi mdi-chevron-double-right"></i>
61
- </div>
62
- </div>
63
- </div>
64
- </slot>
65
- <div class="ui-date-picker-grid pad-3">
66
- <div
67
- class="ui-date-picker-day-cell text-small"
68
- v-for="dayIndex of dayNamesIndexes"
69
- :key="`day-${dayIndex}`"
70
- >
71
- <!--
72
- @slot Custom day content
73
- @binding {Number} dayIndex day index (0 = sunday)
74
- @binding {String} dayName day name
75
- @binding {Boolean} isWeekend whether a weekend day
76
- -->
77
- <slot
78
- name="cell-day"
79
- v-bind="{
80
- dayIndex,
81
- dayName: dayNames[dayIndex],
82
- isWeekend: isWeekendDay(dayIndex),
83
- }"
84
- >
85
- <div class="color-grey-dark">
86
- {{ dayNames[dayIndex] }}
87
- </div>
88
- </slot>
89
- </div>
90
- <div
91
- class="ui-date-picker-date-cell text-small"
92
- v-for="n in currentMonthDates"
93
- :key="`${n.getMonth()}-${n.getDate()}`"
94
- >
95
- <!--
96
- @slot Custom date content
97
- @binding {Date} date date
98
- @binding {Date} dateStart range start date
99
- @binding {Date} dateEnd range end date
100
- @binding {Number} month current view month
101
- @binding {Number} year current view year
102
- @binding {Boolean} isCurrentMonthDate is date in current month view
103
- @binding {Boolean} isDisabledDate is date disabled
104
- @binding {Boolean} isInRange is date in range
105
- @binding {Boolean} isRangeStart is date = range start
106
- @binding {Boolean} isRangeEnd is date = range end
107
- @binding {Boolean} isSelected is date selected
108
- @binding {Boolean} isToday is date today
109
- @binding {Boolean} isWeekend is date a weekend day
110
- @binding {Function} setDate set date
111
- -->
112
- <slot
113
- name="cell-date"
114
- v-bind="{
115
- date: n,
116
- dateStart,
117
- dateEnd,
118
- month,
119
- year,
120
- isCurrentMonthDate: isCurrentMonthDate(n),
121
- isDisabledDate: isDisabledDate(n),
122
- isInRange: isRangeDate(n),
123
- isRangeStart: isRangeStartDate(n),
124
- isRangeEnd: isRangeEndDate(n),
125
- isSelected: isSelectedDate(n),
126
- isToday: isTodayDate(n),
127
- isWeekend: isWeekendDate(n),
128
- setDate,
129
- }"
130
- >
131
- <div
132
- class="btn btn-icon btn-small"
133
- :class="{
134
- 'btn-primary': isSelectedDate(n) || isRangeDate(n),
135
- 'radius-right-none':
136
- isRangeStartDate(n) && !isRangeEndDate(n) && dateEnd,
137
- 'radius-left-none':
138
- !isRangeStartDate(n) && isRangeEndDate(n),
139
- 'radius-none':
140
- isRangeDate(n) &&
141
- !isRangeStartDate(n) &&
142
- !isRangeEndDate(n),
143
- 'disabled radius-none': isDisabledDate(n),
144
- }"
145
- @click="setDate(n)"
146
- >
147
- <template v-if="isSelectedDate(n) || isRangeDate(n)">
148
- <div>{{ n.getDate() }}</div>
149
- </template>
150
- <template v-else-if="isTodayDate(n)">
151
- <div class="color-primary">{{ n.getDate() }}</div>
152
- </template>
153
- <template v-else-if="!isCurrentMonthDate(n)">
154
- <div class="text-muted">{{ n.getDate() }}</div>
155
- </template>
156
- <template v-else>
157
- <div class="color-body">{{ n.getDate() }}</div>
158
- </template>
159
- </div>
160
- </slot>
161
- </div>
162
- </div>
163
- </div>
164
- <div class="col col-auto" v-if="$scopedSlots.right">
165
- <!--
166
- @slot Custom right content
167
- -->
168
- <slot name="right"></slot>
169
- </div>
170
- </div>
171
- </div>
172
- </div>
173
- </template>
174
- <style lang="less" scoped>
175
- .ui-date-picker {
176
- display: inline-flex;
177
- color: var(--color-body);
178
- &-header {
179
- display: flex;
180
- align-items: center;
181
- input {
182
- width: 3em;
183
- border: none;
184
- background: transparent;
185
- outline: none;
186
- text-align: center;
187
- margin: 0;
188
- padding: 0;
189
- outline: none;
190
- -moz-appearance: textfield;
191
- &::-webkit-outer-spin-button,
192
- &::-webkit-inner-spin-button {
193
- -webkit-appearance: none;
194
- margin: 0;
195
- }
196
- }
197
- }
198
- &-grid {
199
- display: grid;
200
- grid-template-columns: repeat(7, 1fr);
201
- grid-auto-rows: 1fr;
202
- row-gap: 1px;
203
- }
204
- &-day-cell,
205
- &-date-cell {
206
- position: relative;
207
- display: flex;
208
- align-items: center;
209
- justify-content: center;
210
- }
211
- .flex-nowrap {
212
- flex-wrap: nowrap;
213
- }
214
- .radius-left-none {
215
- border-top-left-radius: 0;
216
- border-bottom-left-radius: 0;
217
- }
218
- .radius-right-none {
219
- border-top-right-radius: 0;
220
- border-bottom-right-radius: 0;
221
- }
222
- }
223
- </style>
224
- <script>
225
- import { isDateValid } from './utils/Helpers';
226
-
227
- export default {
228
- props: {
229
- /**
230
- * @model
231
- */
232
- value: {
233
- type: [Date, Array],
234
- default: null,
235
- validation(val) {
236
- if (Array.isArray(val)) {
237
- return val.find(el => !this.isValid(el)) == null;
238
- } else {
239
- return this.isValid(val);
240
- }
241
- },
242
- },
243
- /**
244
- * Min date (inclusive)
245
- */
246
- min: {
247
- type: Date,
248
- default: null,
249
- },
250
- /**
251
- * Max date (inclusive)
252
- */
253
- max: {
254
- type: Date,
255
- default: null,
256
- },
257
- /**
258
- * Allowed date filter function(date:Date):Boolean
259
- */
260
- allowed: {
261
- type: Function,
262
- default: () => true,
263
- },
264
- /**
265
- * Range mode
266
- */
267
- range: {
268
- type: Boolean,
269
- default: false,
270
- },
271
- /**
272
- * Day names starting from 'Sunday'
273
- */
274
- dayNames: {
275
- type: Array,
276
- default() {
277
- return ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'];
278
- },
279
- },
280
- /**
281
- * Weekend day indexes (0 = Sunday; 6 = Saturday)
282
- */
283
- weekendDays: {
284
- type: Array,
285
- default() {
286
- return [0, 6];
287
- },
288
- },
289
- /**
290
- * Month names starting from 'January'
291
- */
292
- monthNames: {
293
- type: Array,
294
- default() {
295
- return [
296
- 'Январь',
297
- 'Февраль',
298
- 'Март',
299
- 'Апрель',
300
- 'Май',
301
- 'Июнь',
302
- 'Июль',
303
- 'Август',
304
- 'Сентябрь',
305
- 'Октябрь',
306
- 'Ноябрь',
307
- 'Декабрь',
308
- ];
309
- },
310
- },
311
- /**
312
- * Which day is the first day of the week (0 = Sunday; 6 = Saturday)
313
- */
314
- dayOfWeek: {
315
- type: Number,
316
- default: 1,
317
- validation(val) {
318
- return val >= 0 && val < 7;
319
- },
320
- },
321
- },
322
- data() {
323
- return {
324
- date: null,
325
- dateStart: null,
326
- dateEnd: null,
327
- month: null,
328
- year: null,
329
- syncMonthYear: true,
330
- };
331
- },
332
- computed: {
333
- dayNamesIndexes() {
334
- let a = [];
335
- for (let i = this.dayOfWeek; i < 7 + this.dayOfWeek; ++i) {
336
- a.push(i >= 7 ? i - 7 : i);
337
- }
338
- return a;
339
- },
340
- firstDayOfMonth() {
341
- let n = new Date(this.year, this.month, 1).getDay() - this.dayOfWeek;
342
- return n < 0 ? 7 + n : n;
343
- },
344
- lastDayOfMonth() {
345
- let n = new Date(this.year, this.month + 1, 0).getDay() - this.dayOfWeek;
346
- return n < 0 ? 7 + n : n;
347
- },
348
- daysInMonth() {
349
- return new Date(this.year, this.month + 1, 0).getDate();
350
- },
351
- currentMonthDates() {
352
- let dates = [...new Array(this.daysInMonth)].map(
353
- (v, i) => new Date(this.year, this.month, i + 1)
354
- );
355
- return [...this.prevMonthDates, ...dates, ...this.nextMonthDates];
356
- },
357
- prevMonthDates() {
358
- let n = this.firstDayOfMonth;
359
- let pmd = new Date(this.year, this.month, 0).getDate();
360
- return [...new Array(n)]
361
- .map((v, i) => new Date(this.year, this.month - 1, pmd - i))
362
- .reverse();
363
- },
364
- nextMonthDates() {
365
- let n = 6 - this.lastDayOfMonth;
366
- return [...new Array(n)].map((v, i) => new Date(this.year, this.month + 1, i + 1));
367
- },
368
- },
369
- watch: {
370
- value: {
371
- handler(val) {
372
- let d = new Date();
373
- if (this.range && Array.isArray(val)) {
374
- let [start, end] = val;
375
- this.dateStart = start || null;
376
- this.dateEnd = end || null;
377
- d = start || d;
378
- } else {
379
- this.date = val;
380
- d = val || d;
381
- }
382
- if (this.syncMonthYear) {
383
- this.month = d.getMonth();
384
- this.year = d.getFullYear();
385
- }
386
- this.syncMonthYear = true;
387
- },
388
- immediate: true,
389
- },
390
- range() {
391
- this.date = null;
392
- this.dateStart = null;
393
- this.dateEnd = null;
394
- this._triggerChange();
395
- },
396
- },
397
- methods: {
398
- isValid(date) {
399
- return isDateValid(date);
400
- },
401
- prevMonth() {
402
- let n = this.month - 1;
403
- this.month = n < 0 ? 11 : n;
404
- this.year -= n < 0 ? 1 : 0;
405
- },
406
- prevYear() {
407
- this.year = this.year - 1;
408
- },
409
- nextMonth() {
410
- let n = this.month + 1;
411
- this.month = n > 11 ? 0 : n;
412
- this.year += n > 11 ? 1 : 0;
413
- },
414
- nextYear() {
415
- this.year = this.year + 1;
416
- },
417
- setYear(n) {
418
- this.year = n;
419
- },
420
- setMonth(n) {
421
- this.month = n;
422
- },
423
- setDate(date) {
424
- let d = this.isValid(date) ? date : new Date(this.year, this.month, date);
425
-
426
- if (this.range) {
427
- if (!this.dateStart || (this.dateStart && this.dateEnd)) {
428
- this.dateStart = d;
429
- this.dateEnd = null;
430
- } else {
431
- if (d < this.dateStart) {
432
- this.dateEnd = this.dateStart;
433
- this.dateStart = d;
434
- } else if (d > this.dateStart) {
435
- this.dateEnd = d;
436
- } else {
437
- return;
438
- }
439
- this._triggerChange();
440
- }
441
- } else {
442
- this.date = d;
443
- this._triggerChange();
444
- }
445
- /**
446
- * Set date event
447
- * @property {Date} date
448
- */
449
- this.$emit('set-date', d);
450
- },
451
- setToday() {
452
- this.date = new Date();
453
- this.month = this.date.getMonth();
454
- this.year = this.date.getFullYear();
455
- this._triggerChange();
456
- /**
457
- * Set date event
458
- * @property {Date} date
459
- */
460
- this.$emit('set-date', this.date);
461
- },
462
- isCurrentMonthDate(date) {
463
- if (this.isValid(date)) {
464
- return this.month == date.getMonth();
465
- }
466
- return true;
467
- },
468
- isDisabledDate(date) {
469
- let d = this.isValid(date) ? date : new Date(this.year, this.month, date);
470
- let { min, max, allowed } = this;
471
- return (min && d < min) || (max && d > max) || (allowed && !allowed(d));
472
- },
473
- isRangeStartDate(date) {
474
- let d = this.isValid(date) ? date : new Date(this.year, this.month, date);
475
- return this.dateStart && +d == +this.dateStart;
476
- },
477
- isRangeEndDate(date) {
478
- let d = this.isValid(date) ? date : new Date(this.year, this.month, date);
479
- return this.dateEnd && +d == +this.dateEnd;
480
- },
481
- isRangeDate(date) {
482
- let d = this.isValid(date) ? date : new Date(this.year, this.month, date);
483
- if (this.dateStart && +d == +this.dateStart) {
484
- return true;
485
- }
486
- return this.dateStart && this.dateEnd && d >= this.dateStart && d <= this.dateEnd;
487
- },
488
- isSelectedDate(date) {
489
- let d = this.isValid(date) ? date : new Date(this.year, this.month, date);
490
- return this.date && +this.date == +date;
491
- },
492
- isWeekendDate(date) {
493
- let d = this.isValid(date) ? date : new Date(this.year, this.month, date);
494
- return this.weekendDays.includes(d.getDay());
495
- },
496
- isTodayDate(date) {
497
- let d = this.isValid(date) ? date : new Date(this.year, this.month, date);
498
- let now = new Date();
499
- return (
500
- now.getFullYear() === d.getFullYear() &&
501
- now.getMonth() === d.getMonth() &&
502
- now.getDate() === d.getDate()
503
- );
504
- },
505
- isWeekendDay(day) {
506
- return this.weekendDays.includes(day);
507
- },
508
- _triggerChange() {
509
- let val = this.date;
510
- if (this.range) {
511
- val = this.dateStart && this.dateEnd ? [this.dateStart, this.dateEnd] : [];
512
- }
513
- this.syncMonthYear = false;
514
- /**
515
- * Date change event
516
- * @property {Date} date
517
- */
518
- this.$emit('input', val);
519
- /**
520
- * Date change event
521
- * @property {Date} date
522
- */
523
- this.$emit('change', val);
524
- },
525
- },
526
- };
527
- </script>
1
+ <template>
2
+ <div class="ui-date-picker">
3
+ <div class="panel d-inline-flex flex-col scroll-hide">
4
+ <div class="row row-collapse row-vgap-none flex-nowrap">
5
+ <div class="col col-auto" v-if="$scopedSlots.left">
6
+ <!--
7
+ @slot Custom left content
8
+ -->
9
+ <slot name="left"></slot>
10
+ </div>
11
+ <div class="col col-auto">
12
+ <!--
13
+ @slot Custom header content
14
+ @binding {Function} prevYear select previous year
15
+ @binding {Function} prevMonth select previous month
16
+ @binding {Function} nextYear select next year
17
+ @binding {Function} nextMonth select next month
18
+ @binding {Function} setMonth set month by index (0 = january)
19
+ @binding {Function} setYear set year (4-digit)
20
+ @binding {Function} setToday set today's date
21
+ @binding {Number} month current month index (0 = january)
22
+ @binding {String} monthName current month name
23
+ @binding {Number} year current year (4-digit)
24
+ -->
25
+ <slot
26
+ name="header"
27
+ v-bind="{
28
+ prevYear,
29
+ prevMonth,
30
+ nextYear,
31
+ nextMonth,
32
+ setMonth,
33
+ setYear,
34
+ setToday,
35
+ month,
36
+ monthName: monthNames[month],
37
+ year,
38
+ }"
39
+ >
40
+ <div
41
+ class="ui-date-picker-header text-small mar-bot-3 bg-primary color-white pad-3"
42
+ >
43
+ <div class="flex-shrink nobr">
44
+ <div class="icon cursor-pointer" @click="prevYear">
45
+ <i class="mdi mdi-chevron-double-left"></i>
46
+ </div>
47
+ <div class="icon cursor-pointer" @click="prevMonth">
48
+ <i class="mdi mdi-chevron-left"></i>
49
+ </div>
50
+ </div>
51
+ <div class="flex-grow flex-h-center d-flex">
52
+ <span>{{ monthNames[month] }}</span>
53
+ <input class="color-inherit" type="number" v-model.number="year" />
54
+ </div>
55
+ <div class="flex-shrink nobr">
56
+ <div class="icon cursor-pointer" @click="nextMonth">
57
+ <i class="mdi mdi-chevron-right"></i>
58
+ </div>
59
+ <div class="icon cursor-pointer" @click="nextYear">
60
+ <i class="mdi mdi-chevron-double-right"></i>
61
+ </div>
62
+ </div>
63
+ </div>
64
+ </slot>
65
+ <div class="ui-date-picker-grid pad-3">
66
+ <div
67
+ class="ui-date-picker-day-cell text-small"
68
+ v-for="dayIndex of dayNamesIndexes"
69
+ :key="`day-${dayIndex}`"
70
+ >
71
+ <!--
72
+ @slot Custom day content
73
+ @binding {Number} dayIndex day index (0 = sunday)
74
+ @binding {String} dayName day name
75
+ @binding {Boolean} isWeekend whether a weekend day
76
+ -->
77
+ <slot
78
+ name="cell-day"
79
+ v-bind="{
80
+ dayIndex,
81
+ dayName: dayNames[dayIndex],
82
+ isWeekend: isWeekendDay(dayIndex),
83
+ }"
84
+ >
85
+ <div class="color-grey-dark">
86
+ {{ dayNames[dayIndex] }}
87
+ </div>
88
+ </slot>
89
+ </div>
90
+ <div
91
+ class="ui-date-picker-date-cell text-small"
92
+ v-for="n in currentMonthDates"
93
+ :key="`${n.getMonth()}-${n.getDate()}`"
94
+ >
95
+ <!--
96
+ @slot Custom date content
97
+ @binding {Date} date date
98
+ @binding {Date} dateStart range start date
99
+ @binding {Date} dateEnd range end date
100
+ @binding {Number} month current view month
101
+ @binding {Number} year current view year
102
+ @binding {Boolean} isCurrentMonthDate is date in current month view
103
+ @binding {Boolean} isDisabledDate is date disabled
104
+ @binding {Boolean} isInRange is date in range
105
+ @binding {Boolean} isRangeStart is date = range start
106
+ @binding {Boolean} isRangeEnd is date = range end
107
+ @binding {Boolean} isSelected is date selected
108
+ @binding {Boolean} isToday is date today
109
+ @binding {Boolean} isWeekend is date a weekend day
110
+ @binding {Function} setDate set date
111
+ -->
112
+ <slot
113
+ name="cell-date"
114
+ v-bind="{
115
+ date: n,
116
+ dateStart,
117
+ dateEnd,
118
+ month,
119
+ year,
120
+ isCurrentMonthDate: isCurrentMonthDate(n),
121
+ isDisabledDate: isDisabledDate(n),
122
+ isInRange: isRangeDate(n),
123
+ isRangeStart: isRangeStartDate(n),
124
+ isRangeEnd: isRangeEndDate(n),
125
+ isSelected: isSelectedDate(n),
126
+ isToday: isTodayDate(n),
127
+ isWeekend: isWeekendDate(n),
128
+ setDate,
129
+ }"
130
+ >
131
+ <div
132
+ class="btn btn-icon btn-small"
133
+ :class="{
134
+ 'btn-primary': isSelectedDate(n) || isRangeDate(n),
135
+ 'radius-right-none':
136
+ isRangeStartDate(n) && !isRangeEndDate(n) && dateEnd,
137
+ 'radius-left-none':
138
+ !isRangeStartDate(n) && isRangeEndDate(n),
139
+ 'radius-none':
140
+ isRangeDate(n) &&
141
+ !isRangeStartDate(n) &&
142
+ !isRangeEndDate(n),
143
+ 'disabled radius-none': isDisabledDate(n),
144
+ }"
145
+ @click="setDate(n)"
146
+ >
147
+ <template v-if="isSelectedDate(n) || isRangeDate(n)">
148
+ <div>{{ n.getDate() }}</div>
149
+ </template>
150
+ <template v-else-if="isTodayDate(n)">
151
+ <div class="color-primary">{{ n.getDate() }}</div>
152
+ </template>
153
+ <template v-else-if="!isCurrentMonthDate(n)">
154
+ <div class="text-muted">{{ n.getDate() }}</div>
155
+ </template>
156
+ <template v-else>
157
+ <div class="color-body">{{ n.getDate() }}</div>
158
+ </template>
159
+ </div>
160
+ </slot>
161
+ </div>
162
+ </div>
163
+ </div>
164
+ <div class="col col-auto" v-if="$scopedSlots.right">
165
+ <!--
166
+ @slot Custom right content
167
+ -->
168
+ <slot name="right"></slot>
169
+ </div>
170
+ </div>
171
+ </div>
172
+ </div>
173
+ </template>
174
+ <style lang="less" scoped>
175
+ .ui-date-picker {
176
+ display: inline-flex;
177
+ color: var(--color-body);
178
+ &-header {
179
+ display: flex;
180
+ align-items: center;
181
+ input {
182
+ width: 3em;
183
+ border: none;
184
+ background: transparent;
185
+ outline: none;
186
+ text-align: center;
187
+ margin: 0;
188
+ padding: 0;
189
+ outline: none;
190
+ -moz-appearance: textfield;
191
+ &::-webkit-outer-spin-button,
192
+ &::-webkit-inner-spin-button {
193
+ -webkit-appearance: none;
194
+ margin: 0;
195
+ }
196
+ }
197
+ }
198
+ &-grid {
199
+ display: grid;
200
+ grid-template-columns: repeat(7, 1fr);
201
+ grid-auto-rows: 1fr;
202
+ row-gap: 1px;
203
+ }
204
+ &-day-cell,
205
+ &-date-cell {
206
+ position: relative;
207
+ display: flex;
208
+ align-items: center;
209
+ justify-content: center;
210
+ }
211
+ .flex-nowrap {
212
+ flex-wrap: nowrap;
213
+ }
214
+ .radius-left-none {
215
+ border-top-left-radius: 0;
216
+ border-bottom-left-radius: 0;
217
+ }
218
+ .radius-right-none {
219
+ border-top-right-radius: 0;
220
+ border-bottom-right-radius: 0;
221
+ }
222
+ }
223
+ </style>
224
+ <script>
225
+ import { isDateValid } from './utils/Helpers';
226
+
227
+ export default {
228
+ props: {
229
+ /**
230
+ * @model
231
+ */
232
+ value: {
233
+ type: [Date, Array],
234
+ default: null,
235
+ validation(val) {
236
+ if (Array.isArray(val)) {
237
+ return val.find(el => !this.isValid(el)) == null;
238
+ } else {
239
+ return this.isValid(val);
240
+ }
241
+ },
242
+ },
243
+ /**
244
+ * Min date (inclusive)
245
+ */
246
+ min: {
247
+ type: Date,
248
+ default: null,
249
+ },
250
+ /**
251
+ * Max date (inclusive)
252
+ */
253
+ max: {
254
+ type: Date,
255
+ default: null,
256
+ },
257
+ /**
258
+ * Allowed date filter function(date:Date):Boolean
259
+ */
260
+ allowed: {
261
+ type: Function,
262
+ default: () => true,
263
+ },
264
+ /**
265
+ * Range mode
266
+ */
267
+ range: {
268
+ type: Boolean,
269
+ default: false,
270
+ },
271
+ /**
272
+ * Day names starting from 'Sunday'
273
+ */
274
+ dayNames: {
275
+ type: Array,
276
+ default() {
277
+ return ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'];
278
+ },
279
+ },
280
+ /**
281
+ * Weekend day indexes (0 = Sunday; 6 = Saturday)
282
+ */
283
+ weekendDays: {
284
+ type: Array,
285
+ default() {
286
+ return [0, 6];
287
+ },
288
+ },
289
+ /**
290
+ * Month names starting from 'January'
291
+ */
292
+ monthNames: {
293
+ type: Array,
294
+ default() {
295
+ return [
296
+ 'Январь',
297
+ 'Февраль',
298
+ 'Март',
299
+ 'Апрель',
300
+ 'Май',
301
+ 'Июнь',
302
+ 'Июль',
303
+ 'Август',
304
+ 'Сентябрь',
305
+ 'Октябрь',
306
+ 'Ноябрь',
307
+ 'Декабрь',
308
+ ];
309
+ },
310
+ },
311
+ /**
312
+ * Which day is the first day of the week (0 = Sunday; 6 = Saturday)
313
+ */
314
+ dayOfWeek: {
315
+ type: Number,
316
+ default: 1,
317
+ validation(val) {
318
+ return val >= 0 && val < 7;
319
+ },
320
+ },
321
+ },
322
+ data() {
323
+ return {
324
+ date: null,
325
+ dateStart: null,
326
+ dateEnd: null,
327
+ month: null,
328
+ year: null,
329
+ syncMonthYear: true,
330
+ };
331
+ },
332
+ computed: {
333
+ dayNamesIndexes() {
334
+ let a = [];
335
+ for (let i = this.dayOfWeek; i < 7 + this.dayOfWeek; ++i) {
336
+ a.push(i >= 7 ? i - 7 : i);
337
+ }
338
+ return a;
339
+ },
340
+ firstDayOfMonth() {
341
+ let n = new Date(this.year, this.month, 1).getDay() - this.dayOfWeek;
342
+ return n < 0 ? 7 + n : n;
343
+ },
344
+ lastDayOfMonth() {
345
+ let n = new Date(this.year, this.month + 1, 0).getDay() - this.dayOfWeek;
346
+ return n < 0 ? 7 + n : n;
347
+ },
348
+ daysInMonth() {
349
+ return new Date(this.year, this.month + 1, 0).getDate();
350
+ },
351
+ currentMonthDates() {
352
+ let dates = [...new Array(this.daysInMonth)].map(
353
+ (v, i) => new Date(this.year, this.month, i + 1)
354
+ );
355
+ return [...this.prevMonthDates, ...dates, ...this.nextMonthDates];
356
+ },
357
+ prevMonthDates() {
358
+ let n = this.firstDayOfMonth;
359
+ let pmd = new Date(this.year, this.month, 0).getDate();
360
+ return [...new Array(n)]
361
+ .map((v, i) => new Date(this.year, this.month - 1, pmd - i))
362
+ .reverse();
363
+ },
364
+ nextMonthDates() {
365
+ let n = 6 - this.lastDayOfMonth;
366
+ return [...new Array(n)].map((v, i) => new Date(this.year, this.month + 1, i + 1));
367
+ },
368
+ },
369
+ watch: {
370
+ value: {
371
+ handler(val) {
372
+ let d = new Date();
373
+ if (this.range && Array.isArray(val)) {
374
+ let [start, end] = val;
375
+ this.dateStart = start || null;
376
+ this.dateEnd = end || null;
377
+ d = start || d;
378
+ } else {
379
+ this.date = val;
380
+ d = val || d;
381
+ }
382
+ if (this.syncMonthYear) {
383
+ this.month = d.getMonth();
384
+ this.year = d.getFullYear();
385
+ }
386
+ this.syncMonthYear = true;
387
+ },
388
+ immediate: true,
389
+ },
390
+ range() {
391
+ this.date = null;
392
+ this.dateStart = null;
393
+ this.dateEnd = null;
394
+ this._triggerChange();
395
+ },
396
+ },
397
+ methods: {
398
+ isValid(date) {
399
+ return isDateValid(date);
400
+ },
401
+ prevMonth() {
402
+ let n = this.month - 1;
403
+ this.month = n < 0 ? 11 : n;
404
+ this.year -= n < 0 ? 1 : 0;
405
+ },
406
+ prevYear() {
407
+ this.year = this.year - 1;
408
+ },
409
+ nextMonth() {
410
+ let n = this.month + 1;
411
+ this.month = n > 11 ? 0 : n;
412
+ this.year += n > 11 ? 1 : 0;
413
+ },
414
+ nextYear() {
415
+ this.year = this.year + 1;
416
+ },
417
+ setYear(n) {
418
+ this.year = n;
419
+ },
420
+ setMonth(n) {
421
+ this.month = n;
422
+ },
423
+ setDate(date) {
424
+ let d = this.isValid(date) ? date : new Date(this.year, this.month, date);
425
+
426
+ if (this.range) {
427
+ if (!this.dateStart || (this.dateStart && this.dateEnd)) {
428
+ this.dateStart = d;
429
+ this.dateEnd = null;
430
+ } else {
431
+ if (d < this.dateStart) {
432
+ this.dateEnd = this.dateStart;
433
+ this.dateStart = d;
434
+ } else if (d > this.dateStart) {
435
+ this.dateEnd = d;
436
+ } else {
437
+ return;
438
+ }
439
+ this._triggerChange();
440
+ }
441
+ } else {
442
+ this.date = d;
443
+ this._triggerChange();
444
+ }
445
+ /**
446
+ * Set date event
447
+ * @property {Date} date
448
+ */
449
+ this.$emit('set-date', d);
450
+ },
451
+ setToday() {
452
+ this.date = new Date();
453
+ this.month = this.date.getMonth();
454
+ this.year = this.date.getFullYear();
455
+ this._triggerChange();
456
+ /**
457
+ * Set date event
458
+ * @property {Date} date
459
+ */
460
+ this.$emit('set-date', this.date);
461
+ },
462
+ isCurrentMonthDate(date) {
463
+ if (this.isValid(date)) {
464
+ return this.month == date.getMonth();
465
+ }
466
+ return true;
467
+ },
468
+ isDisabledDate(date) {
469
+ let d = this.isValid(date) ? date : new Date(this.year, this.month, date);
470
+ let { min, max, allowed } = this;
471
+ return (min && d < min) || (max && d > max) || (allowed && !allowed(d));
472
+ },
473
+ isRangeStartDate(date) {
474
+ let d = this.isValid(date) ? date : new Date(this.year, this.month, date);
475
+ return this.dateStart && +d == +this.dateStart;
476
+ },
477
+ isRangeEndDate(date) {
478
+ let d = this.isValid(date) ? date : new Date(this.year, this.month, date);
479
+ return this.dateEnd && +d == +this.dateEnd;
480
+ },
481
+ isRangeDate(date) {
482
+ let d = this.isValid(date) ? date : new Date(this.year, this.month, date);
483
+ if (this.dateStart && +d == +this.dateStart) {
484
+ return true;
485
+ }
486
+ return this.dateStart && this.dateEnd && d >= this.dateStart && d <= this.dateEnd;
487
+ },
488
+ isSelectedDate(date) {
489
+ let d = this.isValid(date) ? date : new Date(this.year, this.month, date);
490
+ return this.date && +this.date == +date;
491
+ },
492
+ isWeekendDate(date) {
493
+ let d = this.isValid(date) ? date : new Date(this.year, this.month, date);
494
+ return this.weekendDays.includes(d.getDay());
495
+ },
496
+ isTodayDate(date) {
497
+ let d = this.isValid(date) ? date : new Date(this.year, this.month, date);
498
+ let now = new Date();
499
+ return (
500
+ now.getFullYear() === d.getFullYear() &&
501
+ now.getMonth() === d.getMonth() &&
502
+ now.getDate() === d.getDate()
503
+ );
504
+ },
505
+ isWeekendDay(day) {
506
+ return this.weekendDays.includes(day);
507
+ },
508
+ _triggerChange() {
509
+ let val = this.date;
510
+ if (this.range) {
511
+ val = this.dateStart && this.dateEnd ? [this.dateStart, this.dateEnd] : [];
512
+ }
513
+ this.syncMonthYear = false;
514
+ /**
515
+ * Date change event
516
+ * @property {Date} date
517
+ */
518
+ this.$emit('input', val);
519
+ /**
520
+ * Date change event
521
+ * @property {Date} date
522
+ */
523
+ this.$emit('change', val);
524
+ },
525
+ },
526
+ };
527
+ </script>