vueless 1.5.1-beta.2 → 1.5.1-beta.4
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.
- package/index.d.ts +9 -1
- package/index.ts +9 -1
- package/modules.d.ts +1 -0
- package/package.json +1 -1
- package/plugin-vite.js +5 -0
- package/types.ts +5 -0
- package/ui.form-calendar/UCalendar.vue +2 -0
- package/ui.form-calendar/UCalendarDayView.vue +18 -0
- package/ui.form-calendar/storybook/stories.ts +26 -0
- package/ui.form-calendar/tests/UCalendar.test.ts +13 -0
- package/ui.form-calendar/tests/UCalendarDayView.test.ts +33 -0
- package/ui.form-calendar/types.ts +6 -0
- package/ui.form-date-picker/UDatePicker.vue +2 -0
- package/ui.form-date-picker/storybook/stories.ts +29 -0
- package/ui.form-date-picker/tests/UDatePicker.test.ts +13 -0
- package/ui.form-date-picker/types.ts +5 -0
- package/ui.form-date-picker-range/UDatePickerRange.vue +63 -8
- package/ui.form-date-picker-range/UDatePickerRangePeriodMenu.vue +26 -29
- package/ui.form-date-picker-range/config.ts +17 -2
- package/ui.form-date-picker-range/storybook/stories.ts +77 -0
- package/ui.form-date-picker-range/tests/UDatePickerRange.test.ts +203 -1
- package/ui.form-date-picker-range/types.ts +14 -1
- package/utils/node/helper.d.ts +1 -0
- package/utils/node/helper.js +13 -1
- package/utils/tests/theme.test.ts +124 -0
- package/utils/theme.ts +46 -48
package/index.d.ts
CHANGED
|
@@ -27,7 +27,15 @@ export {
|
|
|
27
27
|
} from "./utils/ui";
|
|
28
28
|
export { addToRequestQueue, removeFromRequestQueue } from "./utils/requestQueue";
|
|
29
29
|
export { isMac, isPWA, isIOS, isAndroid, isMobileApp, isWindows } from "./utils/platform";
|
|
30
|
-
export {
|
|
30
|
+
export {
|
|
31
|
+
getTheme,
|
|
32
|
+
setTheme,
|
|
33
|
+
resetTheme,
|
|
34
|
+
getThemeCookieName,
|
|
35
|
+
normalizeThemeConfig,
|
|
36
|
+
cssVar,
|
|
37
|
+
setRootCSSVariables,
|
|
38
|
+
} from "./utils/theme";
|
|
31
39
|
export { getArgs, getArgTypes, getSlotNames, getSlotsFragment, getSource, getDocsDescription } from "./utils/storybook";
|
|
32
40
|
/* adapters */
|
|
33
41
|
export { default as defaultEnLocale } from "./adapter.locale/locales/en";
|
package/index.ts
CHANGED
|
@@ -33,7 +33,15 @@ export {
|
|
|
33
33
|
} from "./utils/ui";
|
|
34
34
|
export { addToRequestQueue, removeFromRequestQueue } from "./utils/requestQueue";
|
|
35
35
|
export { isMac, isPWA, isIOS, isAndroid, isMobileApp, isWindows } from "./utils/platform";
|
|
36
|
-
export {
|
|
36
|
+
export {
|
|
37
|
+
getTheme,
|
|
38
|
+
setTheme,
|
|
39
|
+
resetTheme,
|
|
40
|
+
getThemeCookieName,
|
|
41
|
+
normalizeThemeConfig,
|
|
42
|
+
cssVar,
|
|
43
|
+
setRootCSSVariables,
|
|
44
|
+
} from "./utils/theme";
|
|
37
45
|
export { getArgs, getArgTypes, getSlotNames, getSlotsFragment, getSource, getDocsDescription } from "./utils/storybook";
|
|
38
46
|
/* adapters */
|
|
39
47
|
export { default as defaultEnLocale } from "./adapter.locale/locales/en";
|
package/modules.d.ts
CHANGED
package/package.json
CHANGED
package/plugin-vite.js
CHANGED
|
@@ -24,6 +24,7 @@ import { overrideComponents, restoreComponents } from "./utils/node/componentOve
|
|
|
24
24
|
import {
|
|
25
25
|
getNuxtDirs,
|
|
26
26
|
getVueDirs,
|
|
27
|
+
getAppName,
|
|
27
28
|
getVuelessAppDirs,
|
|
28
29
|
cacheMergedConfigs,
|
|
29
30
|
autoImportUserConfigs,
|
|
@@ -74,6 +75,9 @@ export const Vueless = function (options = {}) {
|
|
|
74
75
|
|
|
75
76
|
const vuelessSrcDir = isInternalEnv ? VUELESS_LOCAL_DIR : VUELESS_PACKAGE_DIR;
|
|
76
77
|
|
|
78
|
+
/* app name for theme cookie prefixing */
|
|
79
|
+
const appName = getAppName();
|
|
80
|
+
|
|
77
81
|
// Cache to store previous icon content for change detection
|
|
78
82
|
const iconContentCache = new Map();
|
|
79
83
|
|
|
@@ -109,6 +113,7 @@ export const Vueless = function (options = {}) {
|
|
|
109
113
|
define: {
|
|
110
114
|
"process.env": {},
|
|
111
115
|
__VUELESS_DEV__: process.argv.includes("--watch") || process.argv.includes("-w"),
|
|
116
|
+
__VUELESS_APP_NAME__: JSON.stringify(appName),
|
|
112
117
|
},
|
|
113
118
|
optimizeDeps: {
|
|
114
119
|
include: isInternalEnv
|
package/types.ts
CHANGED
|
@@ -183,6 +183,11 @@ export interface Config extends ThemeConfig {
|
|
|
183
183
|
*/
|
|
184
184
|
directives?: Partial<Directives>;
|
|
185
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Theme cookie name prefix (defaults to the app `package.json` name, `""` disables prefixing).
|
|
188
|
+
*/
|
|
189
|
+
cookiePrefix?: string;
|
|
190
|
+
|
|
186
191
|
/**
|
|
187
192
|
* Tailwind CSS theme config.
|
|
188
193
|
* https://tailwindcss.com/docs/theme
|
|
@@ -54,6 +54,7 @@ const props = withDefaults(defineProps<Props<TModelValue>>(), {
|
|
|
54
54
|
modelValue: undefined,
|
|
55
55
|
minDate: undefined,
|
|
56
56
|
maxDate: undefined,
|
|
57
|
+
dayClasses: undefined,
|
|
57
58
|
});
|
|
58
59
|
|
|
59
60
|
const emit = defineEmits([
|
|
@@ -918,6 +919,7 @@ const {
|
|
|
918
919
|
:date-format="actualDateFormat"
|
|
919
920
|
:locale="locale"
|
|
920
921
|
:config="config"
|
|
922
|
+
:day-classes="dayClasses"
|
|
921
923
|
:is-arrow-key-dirty="isArrowKeyDirty"
|
|
922
924
|
:data-test="getDataTest('day-view')"
|
|
923
925
|
@input="onInputDate"
|
|
@@ -232,6 +232,10 @@ function onMouseleaveDayView() {
|
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
+
function getDayCustomClasses(day: Date) {
|
|
236
|
+
return props.dayClasses?.(day);
|
|
237
|
+
}
|
|
238
|
+
|
|
235
239
|
/**
|
|
236
240
|
* Get element / nested component attributes for each config token ✨
|
|
237
241
|
* Applies: `class`, `config`, redefined default `props` and dev `vl-...` attributes.
|
|
@@ -279,6 +283,7 @@ defineExpose({
|
|
|
279
283
|
size="md"
|
|
280
284
|
square
|
|
281
285
|
v-bind="selectedDayAttrs"
|
|
286
|
+
:class="getDayCustomClasses(day)"
|
|
282
287
|
:disabled="dateIsOutOfRange(day, minDate, maxDate, locale, dateFormat)"
|
|
283
288
|
:label="formatDate(day, 'j', locale)"
|
|
284
289
|
@mousedown.prevent.capture
|
|
@@ -294,6 +299,7 @@ defineExpose({
|
|
|
294
299
|
size="md"
|
|
295
300
|
square
|
|
296
301
|
v-bind="selectedDayAttrs"
|
|
302
|
+
:class="getDayCustomClasses(day)"
|
|
297
303
|
:disabled="dateIsOutOfRange(day, minDate, maxDate, locale, dateFormat)"
|
|
298
304
|
:label="formatDate(day, 'j', locale)"
|
|
299
305
|
@mousedown.prevent.capture
|
|
@@ -311,6 +317,7 @@ defineExpose({
|
|
|
311
317
|
size="md"
|
|
312
318
|
square
|
|
313
319
|
v-bind="firstDayInRangeAttrs"
|
|
320
|
+
:class="getDayCustomClasses(day)"
|
|
314
321
|
:disabled="dateIsOutOfRange(day, minDate, maxDate, locale, dateFormat)"
|
|
315
322
|
:label="formatDate(day, 'j', locale)"
|
|
316
323
|
@mousedown.prevent.capture
|
|
@@ -328,6 +335,7 @@ defineExpose({
|
|
|
328
335
|
size="md"
|
|
329
336
|
square
|
|
330
337
|
v-bind="lastDayInRangeAttrs"
|
|
338
|
+
:class="getDayCustomClasses(day)"
|
|
331
339
|
:disabled="dateIsOutOfRange(day, minDate, maxDate, locale, dateFormat)"
|
|
332
340
|
:label="formatDate(day, 'j', locale)"
|
|
333
341
|
@mousedown.prevent.capture
|
|
@@ -343,6 +351,7 @@ defineExpose({
|
|
|
343
351
|
size="md"
|
|
344
352
|
square
|
|
345
353
|
v-bind="anotherMonthFirstDayInRangeAttrs"
|
|
354
|
+
:class="getDayCustomClasses(day)"
|
|
346
355
|
:disabled="dateIsOutOfRange(day, minDate, maxDate, locale, dateFormat)"
|
|
347
356
|
:label="formatDate(day, 'j', locale)"
|
|
348
357
|
@mousedown.prevent.capture
|
|
@@ -358,6 +367,7 @@ defineExpose({
|
|
|
358
367
|
size="md"
|
|
359
368
|
square
|
|
360
369
|
v-bind="anotherMonthLastDayInRangeAttrs"
|
|
370
|
+
:class="getDayCustomClasses(day)"
|
|
361
371
|
:disabled="dateIsOutOfRange(day, minDate, maxDate, locale, dateFormat)"
|
|
362
372
|
:label="formatDate(day, 'j', locale)"
|
|
363
373
|
@mousedown.prevent.capture
|
|
@@ -373,6 +383,7 @@ defineExpose({
|
|
|
373
383
|
size="md"
|
|
374
384
|
square
|
|
375
385
|
v-bind="lastDayInRangeAttrs"
|
|
386
|
+
:class="getDayCustomClasses(day)"
|
|
376
387
|
:disabled="dateIsOutOfRange(day, minDate, maxDate, locale, dateFormat)"
|
|
377
388
|
:label="formatDate(day, 'j', locale)"
|
|
378
389
|
@mousedown.prevent.capture
|
|
@@ -391,6 +402,7 @@ defineExpose({
|
|
|
391
402
|
size="md"
|
|
392
403
|
square
|
|
393
404
|
v-bind="anotherMonthDayInRangeAttrs"
|
|
405
|
+
:class="getDayCustomClasses(day)"
|
|
394
406
|
:disabled="dateIsOutOfRange(day, minDate, maxDate, locale, dateFormat)"
|
|
395
407
|
:label="formatDate(day, 'j', locale)"
|
|
396
408
|
@mousedown.prevent.capture
|
|
@@ -409,6 +421,7 @@ defineExpose({
|
|
|
409
421
|
size="md"
|
|
410
422
|
square
|
|
411
423
|
v-bind="currentDayInRangeAttrs"
|
|
424
|
+
:class="getDayCustomClasses(day)"
|
|
412
425
|
:disabled="dateIsOutOfRange(day, minDate, maxDate, locale, dateFormat)"
|
|
413
426
|
:label="formatDate(day, 'j', locale)"
|
|
414
427
|
@mousedown.prevent.capture
|
|
@@ -424,6 +437,7 @@ defineExpose({
|
|
|
424
437
|
size="md"
|
|
425
438
|
square
|
|
426
439
|
v-bind="dayInRangeAttrs"
|
|
440
|
+
:class="getDayCustomClasses(day)"
|
|
427
441
|
:disabled="dateIsOutOfRange(day, minDate, maxDate, locale, dateFormat)"
|
|
428
442
|
:label="formatDate(day, 'j', locale)"
|
|
429
443
|
@mousedown.prevent.capture
|
|
@@ -439,6 +453,7 @@ defineExpose({
|
|
|
439
453
|
size="md"
|
|
440
454
|
square
|
|
441
455
|
v-bind="currentDayAttrs"
|
|
456
|
+
:class="getDayCustomClasses(day)"
|
|
442
457
|
:disabled="dateIsOutOfRange(day, minDate, maxDate, locale, dateFormat)"
|
|
443
458
|
:label="formatDate(day, 'j', locale)"
|
|
444
459
|
@mousedown.prevent.capture
|
|
@@ -454,6 +469,7 @@ defineExpose({
|
|
|
454
469
|
size="md"
|
|
455
470
|
square
|
|
456
471
|
v-bind="activeDayAttrs"
|
|
472
|
+
:class="getDayCustomClasses(day)"
|
|
457
473
|
:disabled="dateIsOutOfRange(day, minDate, maxDate, locale, dateFormat)"
|
|
458
474
|
:label="formatDate(day, 'j', locale)"
|
|
459
475
|
@mousedown.prevent.capture
|
|
@@ -469,6 +485,7 @@ defineExpose({
|
|
|
469
485
|
size="md"
|
|
470
486
|
square
|
|
471
487
|
v-bind="anotherMonthDayAttrs"
|
|
488
|
+
:class="getDayCustomClasses(day)"
|
|
472
489
|
:disabled="dateIsOutOfRange(day, minDate, maxDate, locale, dateFormat)"
|
|
473
490
|
:label="formatDate(day, 'j', locale)"
|
|
474
491
|
@mousedown.prevent.capture
|
|
@@ -484,6 +501,7 @@ defineExpose({
|
|
|
484
501
|
size="md"
|
|
485
502
|
square
|
|
486
503
|
v-bind="dayAttrs"
|
|
504
|
+
:class="getDayCustomClasses(day)"
|
|
487
505
|
:disabled="dateIsOutOfRange(day, minDate, maxDate, locale, dateFormat)"
|
|
488
506
|
:label="formatDate(day, 'j', locale)"
|
|
489
507
|
@mousedown.prevent.capture
|
|
@@ -147,3 +147,29 @@ MinMax.parameters = {
|
|
|
147
147
|
},
|
|
148
148
|
},
|
|
149
149
|
};
|
|
150
|
+
|
|
151
|
+
export const DayClasses = DefaultTemplate.bind({});
|
|
152
|
+
DayClasses.args = {
|
|
153
|
+
dayClasses: (day: Date) => {
|
|
154
|
+
return [5, 12, 19].includes(day.getDate()) ? "bg-success" : "";
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
DayClasses.parameters = {
|
|
158
|
+
docs: {
|
|
159
|
+
description: {
|
|
160
|
+
story:
|
|
161
|
+
// eslint-disable-next-line vue/max-len
|
|
162
|
+
"Use the `dayClasses` prop to apply extra CSS classes to individual day cells, e.g. to highlight dates by status.",
|
|
163
|
+
},
|
|
164
|
+
source: {
|
|
165
|
+
code: `
|
|
166
|
+
<UCalendar
|
|
167
|
+
v-model="args.modelValue"
|
|
168
|
+
:day-classes="(day) => ([5, 12, 19].includes(day.getDate()) ? 'bg-success' : '')"
|
|
169
|
+
/>
|
|
170
|
+
|
|
171
|
+
<UText color="neutral" class="mt-4">{{ args.modelValue }}</UText>
|
|
172
|
+
`,
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
};
|
|
@@ -295,6 +295,19 @@ describe("UCalendar.vue", () => {
|
|
|
295
295
|
expect(updatedValue.to).toMatch(dateFormatRegex);
|
|
296
296
|
});
|
|
297
297
|
|
|
298
|
+
it("Day Classes – passes dayClasses to UCalendarDayView", () => {
|
|
299
|
+
const dayClasses = (day: Date) => (day.getDate() === 15 ? "bg-mint-highlight" : "");
|
|
300
|
+
|
|
301
|
+
const component = mount(UCalendar, {
|
|
302
|
+
props: {
|
|
303
|
+
modelValue: null,
|
|
304
|
+
dayClasses,
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
expect(component.findComponent(DayView).props("dayClasses")).toBe(dayClasses);
|
|
309
|
+
});
|
|
310
|
+
|
|
298
311
|
it("Tabindex – sets tabindex attribute on wrapper", () => {
|
|
299
312
|
const tabindex = 5;
|
|
300
313
|
|
|
@@ -386,6 +386,39 @@ describe("UCalendarDayView.vue", () => {
|
|
|
386
386
|
});
|
|
387
387
|
});
|
|
388
388
|
|
|
389
|
+
describe("Day Classes Prop", () => {
|
|
390
|
+
it("applies function-based dayClasses only to the matching day", () => {
|
|
391
|
+
const component = mount(UCalendarDayView, {
|
|
392
|
+
props: {
|
|
393
|
+
...defaultProps,
|
|
394
|
+
selectedDate: null,
|
|
395
|
+
dayClasses: (day: Date) => (day.getDate() === 10 ? "bg-mint-highlight" : ""),
|
|
396
|
+
},
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
const dayButtons = component.findAll('[vl-key="day"]');
|
|
400
|
+
const targetDay = dayButtons.find((button) => button.text() === "10");
|
|
401
|
+
const otherDay = dayButtons.find((button) => button.text() === "12");
|
|
402
|
+
|
|
403
|
+
expect(targetDay?.classes()).toContain("bg-mint-highlight");
|
|
404
|
+
expect(otherDay?.classes()).not.toContain("bg-mint-highlight");
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it("renders regular day cells without error when dayClasses prop is not provided", () => {
|
|
408
|
+
const component = mount(UCalendarDayView, {
|
|
409
|
+
props: {
|
|
410
|
+
...defaultProps,
|
|
411
|
+
selectedDate: null,
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
const regularDay = component.find('[vl-key="day"]');
|
|
416
|
+
|
|
417
|
+
expect(regularDay.exists()).toBe(true);
|
|
418
|
+
expect(regularDay.classes()).not.toContain("bg-mint-highlight");
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
|
|
389
422
|
describe("Exposed Properties", () => {
|
|
390
423
|
it("exposes days computed property", () => {
|
|
391
424
|
const component = mount(UCalendarDayView, {
|
|
@@ -72,6 +72,11 @@ export interface Props<TModelValue extends DateValue> {
|
|
|
72
72
|
*/
|
|
73
73
|
maxDate?: Date | string;
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Extra CSS classes to apply to each day cell, on top of the built-in state classes.
|
|
77
|
+
*/
|
|
78
|
+
dayClasses?: (day: Date) => string;
|
|
79
|
+
|
|
75
80
|
/**
|
|
76
81
|
* Component config object.
|
|
77
82
|
*/
|
|
@@ -93,5 +98,6 @@ export interface UCalendarViewProps {
|
|
|
93
98
|
isArrowKeyDirty: boolean;
|
|
94
99
|
maxDate?: Date | string;
|
|
95
100
|
minDate?: Date | string;
|
|
101
|
+
dayClasses?: (day: Date) => string;
|
|
96
102
|
config: Config;
|
|
97
103
|
}
|
|
@@ -34,6 +34,7 @@ const props = withDefaults(defineProps<Props<TModelValue>>(), {
|
|
|
34
34
|
modelValue: undefined,
|
|
35
35
|
minDate: undefined,
|
|
36
36
|
maxDate: undefined,
|
|
37
|
+
dayClasses: undefined,
|
|
37
38
|
placeholder: "",
|
|
38
39
|
label: "",
|
|
39
40
|
});
|
|
@@ -364,6 +365,7 @@ watchEffect(() => {
|
|
|
364
365
|
:user-date-time-format="userDateTimeFormat"
|
|
365
366
|
:max-date="maxDate"
|
|
366
367
|
:min-date="minDate"
|
|
368
|
+
:day-classes="dayClasses"
|
|
367
369
|
v-bind="datepickerCalendarAttrs"
|
|
368
370
|
:data-test="getDataTest('calendar')"
|
|
369
371
|
@keydown.esc="deactivate"
|
|
@@ -454,3 +454,32 @@ Slots.parameters = {
|
|
|
454
454
|
},
|
|
455
455
|
},
|
|
456
456
|
};
|
|
457
|
+
|
|
458
|
+
export const DayClasses = DefaultTemplate.bind({});
|
|
459
|
+
DayClasses.args = {
|
|
460
|
+
modelValue: dateValue,
|
|
461
|
+
dayClasses: (day: Date) => {
|
|
462
|
+
return [5, 12, 19].includes(day.getDate()) ? "bg-success" : "";
|
|
463
|
+
},
|
|
464
|
+
};
|
|
465
|
+
DayClasses.parameters = {
|
|
466
|
+
docs: {
|
|
467
|
+
description: {
|
|
468
|
+
story:
|
|
469
|
+
// eslint-disable-next-line vue/max-len
|
|
470
|
+
"Use the `dayClasses` prop to apply extra CSS classes to individual day cells, e.g. to highlight dates by status.",
|
|
471
|
+
},
|
|
472
|
+
source: {
|
|
473
|
+
code: `
|
|
474
|
+
<UDatePicker
|
|
475
|
+
v-model="args.modelValue"
|
|
476
|
+
label="Select a date"
|
|
477
|
+
:day-classes="(day) => ([5, 12, 19].includes(day.getDate()) ? 'bg-success' : '')"
|
|
478
|
+
class="max-w-96"
|
|
479
|
+
/>
|
|
480
|
+
|
|
481
|
+
<UText color="neutral" class="mt-4">{{ args.modelValue }}</UText>
|
|
482
|
+
`,
|
|
483
|
+
},
|
|
484
|
+
},
|
|
485
|
+
};
|
|
@@ -284,6 +284,19 @@ describe("UDatePicker.vue", () => {
|
|
|
284
284
|
expect(component.findComponent({ name: "UCalendar" }).props("maxDate")).toBe(maxDate);
|
|
285
285
|
});
|
|
286
286
|
|
|
287
|
+
it("Day Classes – passes dayClasses to UCalendar", () => {
|
|
288
|
+
const dayClasses = (day: Date) => (day.getDate() === 15 ? "bg-mint-highlight" : "");
|
|
289
|
+
|
|
290
|
+
const component = mount(UDatePicker, {
|
|
291
|
+
props: {
|
|
292
|
+
modelValue: new Date(),
|
|
293
|
+
dayClasses,
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
expect(component.findComponent({ name: "UCalendar" }).props("dayClasses")).toBe(dayClasses);
|
|
298
|
+
});
|
|
299
|
+
|
|
287
300
|
it("Id – sets id attribute on input", () => {
|
|
288
301
|
const idValue = "date-picker-id";
|
|
289
302
|
|
|
@@ -103,6 +103,11 @@ export interface Props<TModelValue> {
|
|
|
103
103
|
*/
|
|
104
104
|
rightIcon?: string;
|
|
105
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Extra CSS classes to apply to each day cell, on top of the built-in state classes.
|
|
108
|
+
*/
|
|
109
|
+
dayClasses?: (day: Date) => string;
|
|
110
|
+
|
|
106
111
|
/**
|
|
107
112
|
* Unique element id.
|
|
108
113
|
*/
|
|
@@ -73,9 +73,12 @@ defineOptions({ inheritAttrs: false });
|
|
|
73
73
|
const props = withDefaults(defineProps<Props<TModelValue>>(), {
|
|
74
74
|
...getDefaults<Props<TModelValue>, Config>(defaultConfig, COMPONENT_NAME),
|
|
75
75
|
customRangeButton: () => ({ range: { from: null, to: null } }),
|
|
76
|
+
periods: () =>
|
|
77
|
+
getDefaults<Props<TModelValue>, Config>(defaultConfig, COMPONENT_NAME).periods || [],
|
|
76
78
|
modelValue: undefined,
|
|
77
79
|
minDate: undefined,
|
|
78
80
|
maxDate: undefined,
|
|
81
|
+
dayClasses: undefined,
|
|
79
82
|
placeholder: "",
|
|
80
83
|
label: "",
|
|
81
84
|
});
|
|
@@ -129,7 +132,21 @@ const activeDate: Ref<string | Date | null> = ref(
|
|
|
129
132
|
? parseDate<SortedLocale>(props.modelValue.from, props.dateFormat, locale.value)
|
|
130
133
|
: new Date(),
|
|
131
134
|
);
|
|
132
|
-
|
|
135
|
+
let isInvalidPeriodsWarned = false;
|
|
136
|
+
const allowedPeriods = computed(() => getAllowedPeriods());
|
|
137
|
+
const isPeriodSwitchShown = computed(() => {
|
|
138
|
+
const hasCustomRange = Boolean(
|
|
139
|
+
props.customRangeButton.range.from && props.customRangeButton.range.to,
|
|
140
|
+
);
|
|
141
|
+
const periodButtonCount = allowedPeriods.value.filter((type) => type !== Period.Custom).length;
|
|
142
|
+
|
|
143
|
+
return periodButtonCount + Number(hasCustomRange) > 1;
|
|
144
|
+
});
|
|
145
|
+
const rawPeriod = ref(Period.OwnRange);
|
|
146
|
+
const period = computed({
|
|
147
|
+
get: () => getAllowedPeriod(rawPeriod.value),
|
|
148
|
+
set: (value: Period) => (rawPeriod.value = value),
|
|
149
|
+
});
|
|
133
150
|
const rangeStart = ref("");
|
|
134
151
|
const rangeEnd = ref("");
|
|
135
152
|
const inputRangeFromError = ref("");
|
|
@@ -319,6 +336,29 @@ function deactivate() {
|
|
|
319
336
|
isShownMenu.value = false;
|
|
320
337
|
}
|
|
321
338
|
|
|
339
|
+
function getAllowedPeriods(): readonly Period[] {
|
|
340
|
+
const allPeriods = Object.values(Period);
|
|
341
|
+
const validPeriods = allPeriods.filter((type) => props.periods.includes(type));
|
|
342
|
+
|
|
343
|
+
if (validPeriods.length) return validPeriods;
|
|
344
|
+
|
|
345
|
+
if (import.meta.env.DEV && !isInvalidPeriodsWarned) {
|
|
346
|
+
// eslint-disable-next-line no-console
|
|
347
|
+
console.warn(`[${COMPONENT_NAME}] No valid "periods" provided, falling back to all periods.`);
|
|
348
|
+
isInvalidPeriodsWarned = true;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return allPeriods;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function getAllowedPeriod(detectedPeriod: Period): Period {
|
|
355
|
+
if (detectedPeriod === Period.Custom || allowedPeriods.value.includes(detectedPeriod)) {
|
|
356
|
+
return detectedPeriod;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return allowedPeriods.value.includes(Period.OwnRange) ? Period.OwnRange : allowedPeriods.value[0];
|
|
360
|
+
}
|
|
361
|
+
|
|
322
362
|
setDefaultPeriodForButton();
|
|
323
363
|
|
|
324
364
|
function setDefaultPeriodForButton() {
|
|
@@ -404,16 +444,12 @@ function shiftRangeNext(to: Date, from: Date, daysDifference: number) {
|
|
|
404
444
|
return;
|
|
405
445
|
}
|
|
406
446
|
|
|
407
|
-
let nextDate = periodDateList.value.find(
|
|
408
|
-
(item) => localValue.value.to && item.endRange > localValue.value.to,
|
|
409
|
-
);
|
|
447
|
+
let nextDate = periodDateList.value.find((item) => item.startRange > from);
|
|
410
448
|
|
|
411
449
|
if (!nextDate) {
|
|
412
450
|
onClickShiftDatesList(ShiftAction.Next);
|
|
413
451
|
|
|
414
|
-
nextDate = periodDateList.value.find(
|
|
415
|
-
(item) => localValue.value.to && item.endRange > localValue.value.to,
|
|
416
|
-
);
|
|
452
|
+
nextDate = periodDateList.value.find((item) => item.startRange > from);
|
|
417
453
|
}
|
|
418
454
|
|
|
419
455
|
if (nextDate && isDatePeriodOutOfRange(nextDate)) return;
|
|
@@ -468,8 +504,19 @@ function shiftRangePrev(to: Date, from: Date, daysDifference: number) {
|
|
|
468
504
|
}
|
|
469
505
|
}
|
|
470
506
|
|
|
507
|
+
function getPeriodDateList(date: Date): DatePeriodRange[] {
|
|
508
|
+
if (isPeriod.value.week) return getWeekDateList(date, locale.value.months.shorthand);
|
|
509
|
+
if (isPeriod.value.month) return getMonthsDateList(date, locale.value.months.longhand);
|
|
510
|
+
if (isPeriod.value.quarter) return getQuartersDateList(date, locale.value.quarter);
|
|
511
|
+
if (isPeriod.value.year) return getYearDateList(date);
|
|
512
|
+
|
|
513
|
+
return [];
|
|
514
|
+
}
|
|
515
|
+
|
|
471
516
|
function onClickShiftRange(action: ShiftActions) {
|
|
472
|
-
|
|
517
|
+
const isLeavingCustom = isPeriod.value.custom;
|
|
518
|
+
|
|
519
|
+
if (isLeavingCustom) {
|
|
473
520
|
period.value = Period.OwnRange;
|
|
474
521
|
}
|
|
475
522
|
|
|
@@ -487,6 +534,10 @@ function onClickShiftRange(action: ShiftActions) {
|
|
|
487
534
|
if (!from) return;
|
|
488
535
|
to = to || addDays(from, 1);
|
|
489
536
|
|
|
537
|
+
if (isLeavingCustom && !isPeriod.value.ownRange) {
|
|
538
|
+
periodDateList.value = getPeriodDateList(from);
|
|
539
|
+
}
|
|
540
|
+
|
|
490
541
|
const daysDifference = Math.ceil(Math.abs(getDatesDifference(from, to)) / millisecondsPerDay);
|
|
491
542
|
|
|
492
543
|
action === ShiftAction.Next
|
|
@@ -568,6 +619,7 @@ const mutatedProps = computed(() => ({
|
|
|
568
619
|
quarter: isPeriod.value.quarter,
|
|
569
620
|
year: isPeriod.value.year,
|
|
570
621
|
customRangeButtonDescription: Boolean(props.customRangeButton.description),
|
|
622
|
+
periodSwitch: isPeriodSwitchShown.value,
|
|
571
623
|
}));
|
|
572
624
|
|
|
573
625
|
const {
|
|
@@ -730,6 +782,8 @@ watchEffect(() => {
|
|
|
730
782
|
v-model:period="period"
|
|
731
783
|
:config="config"
|
|
732
784
|
:is-period="isPeriod"
|
|
785
|
+
:allowed-periods="allowedPeriods"
|
|
786
|
+
:is-period-switch-shown="isPeriodSwitchShown"
|
|
733
787
|
:custom-range-button="customRangeButton"
|
|
734
788
|
:locale="locale"
|
|
735
789
|
:date-format="dateFormat"
|
|
@@ -781,6 +835,7 @@ watchEffect(() => {
|
|
|
781
835
|
:min-date="minDate"
|
|
782
836
|
:max-date="maxDate"
|
|
783
837
|
:date-format="dateFormat"
|
|
838
|
+
:day-classes="dayClasses"
|
|
784
839
|
v-bind="datepickerCalendarAttrs as KeyAttrsWithConfig<UCalendarConfig>"
|
|
785
840
|
range
|
|
786
841
|
:data-test="getDataTest('calendar')"
|
|
@@ -36,12 +36,20 @@ const isDatePeriodOutOfRange = inject<IsDatePeriodOutOfRange | null>(
|
|
|
36
36
|
null,
|
|
37
37
|
);
|
|
38
38
|
|
|
39
|
-
const periods = computed(() =>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
const periods = computed(() =>
|
|
40
|
+
[
|
|
41
|
+
{ name: Period.Week, title: props.locale.week },
|
|
42
|
+
{ name: Period.Month, title: props.locale.month },
|
|
43
|
+
{ name: Period.Quarter, title: props.locale.quarter },
|
|
44
|
+
{ name: Period.Year, title: props.locale.year },
|
|
45
|
+
].filter(({ name }) => props.allowedPeriods.includes(name)),
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const isOwnRangeAllowed = computed(() => props.allowedPeriods.includes(Period.OwnRange));
|
|
49
|
+
|
|
50
|
+
const hasCustomRange = computed(() =>
|
|
51
|
+
Boolean(props.customRangeButton.range.from && props.customRangeButton.range.to),
|
|
52
|
+
);
|
|
45
53
|
|
|
46
54
|
const rangeSwitchTitle = computed(() => {
|
|
47
55
|
if (props.isPeriod.month || props.isPeriod.quarter) {
|
|
@@ -148,7 +156,11 @@ function getDatePeriodState(date: DatePeriodRange) {
|
|
|
148
156
|
</script>
|
|
149
157
|
|
|
150
158
|
<template>
|
|
151
|
-
<div
|
|
159
|
+
<div
|
|
160
|
+
v-if="isPeriodSwitchShown && periods.length"
|
|
161
|
+
v-bind="attrs.periodRowAttrs.value"
|
|
162
|
+
:data-test="getDataTest()"
|
|
163
|
+
>
|
|
152
164
|
<template v-for="periodButton in periods" :key="periodButton.name">
|
|
153
165
|
<UButton
|
|
154
166
|
v-if="periodButton.name !== period"
|
|
@@ -176,27 +188,12 @@ function getDatePeriodState(date: DatePeriodRange) {
|
|
|
176
188
|
</template>
|
|
177
189
|
</div>
|
|
178
190
|
|
|
179
|
-
<div
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
size="xs"
|
|
184
|
-
color="grayscale"
|
|
185
|
-
variant="soft"
|
|
186
|
-
:data-test="getDataTest('custom-range')"
|
|
187
|
-
v-bind="attrs.customRangeButtonAttrs.value"
|
|
188
|
-
@click="onClickCustomRangeButton"
|
|
189
|
-
>
|
|
190
|
-
{{ customRangeButton.label }}
|
|
191
|
-
<span
|
|
192
|
-
v-if="customRangeButton.description"
|
|
193
|
-
v-bind="attrs.customRangeDescriptionAttrs.value"
|
|
194
|
-
v-text="customRangeButton.description"
|
|
195
|
-
/>
|
|
196
|
-
</UButton>
|
|
197
|
-
|
|
191
|
+
<div
|
|
192
|
+
v-if="isPeriodSwitchShown && (hasCustomRange || isOwnRangeAllowed)"
|
|
193
|
+
v-bind="attrs.periodRowAttrs.value"
|
|
194
|
+
>
|
|
198
195
|
<UButton
|
|
199
|
-
v-if="
|
|
196
|
+
v-if="hasCustomRange"
|
|
200
197
|
square
|
|
201
198
|
size="xs"
|
|
202
199
|
color="grayscale"
|
|
@@ -214,7 +211,7 @@ function getDatePeriodState(date: DatePeriodRange) {
|
|
|
214
211
|
</UButton>
|
|
215
212
|
|
|
216
213
|
<UButton
|
|
217
|
-
v-if="Period.OwnRange !== period"
|
|
214
|
+
v-if="isOwnRangeAllowed && Period.OwnRange !== period"
|
|
218
215
|
square
|
|
219
216
|
size="xs"
|
|
220
217
|
color="grayscale"
|
|
@@ -227,7 +224,7 @@ function getDatePeriodState(date: DatePeriodRange) {
|
|
|
227
224
|
/>
|
|
228
225
|
|
|
229
226
|
<UButton
|
|
230
|
-
v-else
|
|
227
|
+
v-else-if="isOwnRangeAllowed"
|
|
231
228
|
square
|
|
232
229
|
size="xs"
|
|
233
230
|
color="grayscale"
|