design-system-next 2.16.3 → 2.17.5
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/dist/design-system-next.es.js +8149 -7277
- package/dist/design-system-next.es.js.gz +0 -0
- package/dist/design-system-next.umd.js +13 -13
- package/dist/design-system-next.umd.js.gz +0 -0
- package/dist/main.css +1 -1
- package/dist/main.css.gz +0 -0
- package/dist/package.json.d.ts +1 -1
- package/package.json +1 -1
- package/src/assets/styles/tailwind.css +20 -0
- package/src/components/attribute-filter/attribute-filter.ts +5 -1
- package/src/components/attribute-filter/attribute-filter.vue +3 -3
- package/src/components/badge/badge.ts +1 -1
- package/src/components/badge/use-badge.ts +1 -0
- package/src/components/calendar/calendar.ts +1 -1
- package/src/components/calendar/calendar.vue +41 -18
- package/src/components/calendar/use-calendar.ts +13 -6
- package/src/components/calendar-cell/calendar-cell.ts +4 -0
- package/src/components/calendar-cell/use-calendar-cell.ts +21 -2
- package/src/components/card/card.ts +5 -0
- package/src/components/card/use-card.ts +15 -3
- package/src/components/date-picker/date-picker.ts +23 -0
- package/src/components/date-picker/reusable-calendar/reusable-calendar.ts +121 -0
- package/src/components/date-picker/reusable-calendar/reusable-calendar.vue +192 -0
- package/src/components/date-picker/reusable-calendar/use-reusable-calendar.ts +366 -0
- package/src/components/date-picker/tabs/DatePickerCalendarTab.vue +321 -0
- package/src/components/date-picker/tabs/DatePickerMonthTab.vue +60 -0
- package/src/components/date-picker/tabs/DatePickerYearTab.vue +114 -0
- package/src/components/icon/icon.ts +36 -0
- package/src/components/icon/icon.vue +12 -0
- package/src/components/icon/use-icon.ts +67 -0
- package/src/components/lozenge/lozenge.ts +1 -1
- package/src/components/popper/popper.ts +12 -0
- package/src/components/popper/popper.vue +36 -0
- package/src/components/popper/use-popper.ts +16 -0
|
@@ -21,6 +21,7 @@ export const useBadge = (props: BadgePropTypes) => {
|
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
const variantClasses = classNames({
|
|
24
|
+
'spr-background-color-surface spr-text-color-base': variant.value === 'neutral',
|
|
24
25
|
'spr-background-color-danger-base spr-text-color-inverted-strong': variant.value === 'danger',
|
|
25
26
|
'spr-background-color-disabled spr-text-color-on-fill-disabled': variant.value === 'disabled',
|
|
26
27
|
'spr-background-color-information-base spr-text-color-inverted-strong': variant.value === 'information',
|
|
@@ -124,7 +124,7 @@
|
|
|
124
124
|
class="spr-w-full"
|
|
125
125
|
>
|
|
126
126
|
<div
|
|
127
|
-
v-if="schedule.type === 'restday'"
|
|
127
|
+
v-if="schedule.type === 'restday' || schedule.type === 'exempted'"
|
|
128
128
|
class="spr-flex spr-flex-col spr-items-center spr-justify-start"
|
|
129
129
|
@click="
|
|
130
130
|
onCellClick({
|
|
@@ -134,7 +134,7 @@
|
|
|
134
134
|
})
|
|
135
135
|
"
|
|
136
136
|
>
|
|
137
|
-
<spr-calendar-cell type="restday" fullwidth />
|
|
137
|
+
<spr-calendar-cell :type="schedule.type === 'restday' ? 'restday' : 'exempt'" fullwidth />
|
|
138
138
|
</div>
|
|
139
139
|
<div
|
|
140
140
|
v-else
|
|
@@ -159,21 +159,41 @@
|
|
|
159
159
|
</div>
|
|
160
160
|
</section>
|
|
161
161
|
|
|
162
|
-
<section v-if="
|
|
163
|
-
<
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
162
|
+
<section v-if="showCustomSlot(index, employee.id)">
|
|
163
|
+
<slot
|
|
164
|
+
name="cell"
|
|
165
|
+
:details="{
|
|
166
|
+
employeeId: employee.id,
|
|
167
|
+
date: formatDate(date, dateFormat),
|
|
168
|
+
shift: employee.schedule[formatDate(date, dateFormat)],
|
|
169
|
+
}"
|
|
170
|
+
/>
|
|
171
|
+
</section>
|
|
172
|
+
|
|
173
|
+
<section v-if="showCopyShift(index, employee.id)">
|
|
174
|
+
<slot
|
|
175
|
+
name="copy"
|
|
176
|
+
:copy="{
|
|
177
|
+
employeeId: employee.id,
|
|
178
|
+
date: formatDate(date, dateFormat),
|
|
179
|
+
shift: employee.schedule[formatDate(date, dateFormat)],
|
|
180
|
+
}"
|
|
171
181
|
>
|
|
172
|
-
<
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
182
|
+
<spr-calendar-cell
|
|
183
|
+
:view-only="false"
|
|
184
|
+
custom-border-size="1"
|
|
185
|
+
custom-color="#FFFFFF"
|
|
186
|
+
fullwidth
|
|
187
|
+
@on-click="
|
|
188
|
+
onCellClick({ employeeId: details.employeeId, date: details.date, shift: details.shift })
|
|
189
|
+
"
|
|
190
|
+
>
|
|
191
|
+
<div class="spr-flex spr-w-full spr-items-center spr-justify-center spr-gap-size-spacing-3xs">
|
|
192
|
+
<Icon icon="ph:copy-light" />
|
|
193
|
+
<div class="spr-label-xs-medium">Copy</div>
|
|
194
|
+
</div>
|
|
195
|
+
</spr-calendar-cell>
|
|
196
|
+
</slot>
|
|
177
197
|
</section>
|
|
178
198
|
</td>
|
|
179
199
|
</tr>
|
|
@@ -257,6 +277,7 @@
|
|
|
257
277
|
</template>
|
|
258
278
|
|
|
259
279
|
<script setup lang="ts">
|
|
280
|
+
import { useSlots } from 'vue';
|
|
260
281
|
import { Icon } from '@iconify/vue';
|
|
261
282
|
import SprButton from '@/components/button/button.vue';
|
|
262
283
|
import SprAvatar from '@/components/avatar/avatar.vue';
|
|
@@ -269,6 +290,7 @@ import { calendarPropTypes, calendarEmitTypes } from './calendar';
|
|
|
269
290
|
const props = defineProps(calendarPropTypes);
|
|
270
291
|
import { useCalendar } from './use-calendar';
|
|
271
292
|
const emit = defineEmits(calendarEmitTypes);
|
|
293
|
+
const slots = useSlots();
|
|
272
294
|
|
|
273
295
|
const {
|
|
274
296
|
// State
|
|
@@ -287,7 +309,8 @@ const {
|
|
|
287
309
|
goToToday,
|
|
288
310
|
onCellClick,
|
|
289
311
|
handleHover,
|
|
290
|
-
|
|
312
|
+
showCopyShift,
|
|
291
313
|
handleSorting,
|
|
292
|
-
|
|
314
|
+
showCustomSlot,
|
|
315
|
+
} = useCalendar(props, emit, slots);
|
|
293
316
|
</script>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { computed, SetupContext, toRefs, ref, watch } from 'vue';
|
|
1
|
+
import { computed, SetupContext, toRefs, ref, watch, Slots } from 'vue';
|
|
2
2
|
import dayjs from 'dayjs';
|
|
3
3
|
import isBetween from 'dayjs/plugin/isBetween';
|
|
4
4
|
import classNames from 'classnames';
|
|
@@ -9,8 +9,8 @@ dayjs.extend(isBetween);
|
|
|
9
9
|
|
|
10
10
|
import type { CalendarPropTypes, CalendarEmitTypes, SelectedShift } from './calendar';
|
|
11
11
|
|
|
12
|
-
export const useCalendar = (props: CalendarPropTypes, emit: SetupContext<CalendarEmitTypes>['emit']) => {
|
|
13
|
-
const { initialDate,
|
|
12
|
+
export const useCalendar = (props: CalendarPropTypes, emit: SetupContext<CalendarEmitTypes>['emit'], slots: Slots) => {
|
|
13
|
+
const { initialDate, hideCopyButton } = toRefs(props);
|
|
14
14
|
|
|
15
15
|
const state = {
|
|
16
16
|
dateFormat: ref('YYYY-MM-DD'),
|
|
@@ -108,12 +108,18 @@ export const useCalendar = (props: CalendarPropTypes, emit: SetupContext<Calenda
|
|
|
108
108
|
state.employeeId.value = employeeId;
|
|
109
109
|
};
|
|
110
110
|
|
|
111
|
-
const
|
|
111
|
+
const showCopyShift = (index: number, employeeId: number) => {
|
|
112
112
|
return (
|
|
113
113
|
state.hoveredCell.value === index &&
|
|
114
114
|
state.isHover.value &&
|
|
115
115
|
state.employeeId.value === employeeId &&
|
|
116
|
-
!
|
|
116
|
+
!hideCopyButton.value
|
|
117
|
+
);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const showCustomSlot = (index: number, employeeId: number) => {
|
|
121
|
+
return (
|
|
122
|
+
state.hoveredCell.value === index && state.isHover.value && state.employeeId.value === employeeId && slots.cell
|
|
117
123
|
);
|
|
118
124
|
};
|
|
119
125
|
|
|
@@ -191,8 +197,9 @@ export const useCalendar = (props: CalendarPropTypes, emit: SetupContext<Calenda
|
|
|
191
197
|
goToToday,
|
|
192
198
|
onCellClick,
|
|
193
199
|
handleHover,
|
|
194
|
-
|
|
200
|
+
showCopyShift,
|
|
195
201
|
handleSorting,
|
|
202
|
+
showCustomSlot,
|
|
196
203
|
|
|
197
204
|
...state,
|
|
198
205
|
};
|
|
@@ -4,8 +4,19 @@ import classNames from 'classnames';
|
|
|
4
4
|
import type { CalendarCellPropTypes, CalendarCellEmitTypes } from './calendar-cell';
|
|
5
5
|
|
|
6
6
|
export const useCalendarCell = (props: CalendarCellPropTypes, emit: SetupContext<CalendarCellEmitTypes>['emit']) => {
|
|
7
|
-
const {
|
|
8
|
-
|
|
7
|
+
const {
|
|
8
|
+
title,
|
|
9
|
+
description,
|
|
10
|
+
type,
|
|
11
|
+
status,
|
|
12
|
+
subDescription,
|
|
13
|
+
icon,
|
|
14
|
+
fullwidth,
|
|
15
|
+
viewOnly,
|
|
16
|
+
loading,
|
|
17
|
+
customColor,
|
|
18
|
+
customBorderSize,
|
|
19
|
+
} = toRefs(props);
|
|
9
20
|
const offlineStatus = ['restday', 'vacation', 'holiday', 'exempt', 'sick', 'emergency'];
|
|
10
21
|
const shiftLabels: Record<string, string> = {
|
|
11
22
|
standard: 'Standard Day Shift',
|
|
@@ -68,6 +79,12 @@ export const useCalendarCell = (props: CalendarCellPropTypes, emit: SetupContext
|
|
|
68
79
|
|
|
69
80
|
const getCustomColorStyles = computed(() => {
|
|
70
81
|
if (!customColor.value || !customColor.value.startsWith('#')) return {};
|
|
82
|
+
if (customColor.value === '#FFFFFF') {
|
|
83
|
+
return {
|
|
84
|
+
borderColor: '#B8C1C0',
|
|
85
|
+
backgroundColor: customColor.value,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
71
88
|
|
|
72
89
|
const opacity = '20'; // 20 in hex = 12.5% opacity
|
|
73
90
|
|
|
@@ -80,11 +97,13 @@ export const useCalendarCell = (props: CalendarCellPropTypes, emit: SetupContext
|
|
|
80
97
|
const getCalendarCellClassess = computed(() => {
|
|
81
98
|
const calendarCellWrapper = classNames(
|
|
82
99
|
'spr-flex spr-items-center spr-p-size-spacing-3xs spr-gap-size-spacing-3xs spr-relative spr-rounded-lg spr-border-2 spr-transition-all sm:spr-flex-col spr-overflow-hidden',
|
|
100
|
+
|
|
83
101
|
{
|
|
84
102
|
'spr-w-full': fullwidth.value,
|
|
85
103
|
'spr-max-w-[217px]': !fullwidth.value,
|
|
86
104
|
'hover:spr-drop-shadow-sm spr-cursor-pointer': !viewOnly.value,
|
|
87
105
|
'spr-h-[80px] spr-skeletal-loader': loading.value,
|
|
106
|
+
[`spr-border-[${customBorderSize.value}px]`]: customBorderSize.value,
|
|
88
107
|
},
|
|
89
108
|
);
|
|
90
109
|
|
|
@@ -12,6 +12,7 @@ export const cardPropTypes = {
|
|
|
12
12
|
tone: {
|
|
13
13
|
type: String as PropType<(typeof TONES)[number] | undefined>,
|
|
14
14
|
validator: (val: string | undefined) => !val || TONES.includes(val as (typeof TONES)[number]),
|
|
15
|
+
default: 'plain',
|
|
15
16
|
},
|
|
16
17
|
title: {
|
|
17
18
|
type: String,
|
|
@@ -59,6 +60,10 @@ export const cardPropTypes = {
|
|
|
59
60
|
type: Boolean,
|
|
60
61
|
default: false,
|
|
61
62
|
},
|
|
63
|
+
customBorderSize: {
|
|
64
|
+
type: String,
|
|
65
|
+
default: null,
|
|
66
|
+
},
|
|
62
67
|
};
|
|
63
68
|
|
|
64
69
|
export type CardPropTypes = ExtractPropTypes<typeof cardPropTypes>;
|
|
@@ -12,8 +12,17 @@ interface CardClasses {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export const useCard = (props: CardPropTypes, slots: Slots) => {
|
|
15
|
-
const {
|
|
16
|
-
|
|
15
|
+
const {
|
|
16
|
+
tone,
|
|
17
|
+
title,
|
|
18
|
+
headerIcon,
|
|
19
|
+
borderRadiusSize,
|
|
20
|
+
hasCollapsible,
|
|
21
|
+
isCollapsibleOpen,
|
|
22
|
+
hasContentPadding,
|
|
23
|
+
flexbox,
|
|
24
|
+
customBorderSize,
|
|
25
|
+
} = toRefs(props);
|
|
17
26
|
|
|
18
27
|
const cardClasses: ComputedRef<CardClasses> = computed(() => {
|
|
19
28
|
const toneValue = tone?.value;
|
|
@@ -21,7 +30,7 @@ export const useCard = (props: CardPropTypes, slots: Slots) => {
|
|
|
21
30
|
const baseClasses = classNames(`spr-border-solid`, {
|
|
22
31
|
// Tones
|
|
23
32
|
'spr-background-color-base': !toneValue,
|
|
24
|
-
'spr-
|
|
33
|
+
'spr-background-color': toneValue === 'plain',
|
|
25
34
|
'spr-background-color-surface': toneValue === 'neutral',
|
|
26
35
|
'spr-background-color-success-weak': toneValue === 'success',
|
|
27
36
|
'spr-background-color-information-weak': toneValue === 'information',
|
|
@@ -47,6 +56,9 @@ export const useCard = (props: CardPropTypes, slots: Slots) => {
|
|
|
47
56
|
'spr-rounded-border-radius-md': borderRadiusSize.value === 'xs',
|
|
48
57
|
'spr-rounded-border-radius-lg': borderRadiusSize.value === 'lg',
|
|
49
58
|
'spr-rounded-border-radius-xl': borderRadiusSize.value === 'xl' || !borderRadiusSize.value,
|
|
59
|
+
|
|
60
|
+
[`spr-border-[${customBorderSize.value}px]`]: customBorderSize.value,
|
|
61
|
+
'spr-border-none': customBorderSize.value === '0',
|
|
50
62
|
});
|
|
51
63
|
|
|
52
64
|
const headerClasses = classNames(`spr-flex spr-items-center transition-all duration-300 ease-in-out`, {
|
|
@@ -162,3 +162,26 @@ export const datePickerEmitTypes = {
|
|
|
162
162
|
|
|
163
163
|
export type DatePickerPropTypes = ExtractPropTypes<typeof datePickerPropTypes>;
|
|
164
164
|
export type DatePickerEmitTypes = typeof datePickerEmitTypes;
|
|
165
|
+
|
|
166
|
+
// Tab component interfaces
|
|
167
|
+
export interface TabComponentProps {
|
|
168
|
+
selectedMonth?: number;
|
|
169
|
+
selectedYear?: number;
|
|
170
|
+
selectedDay?: number;
|
|
171
|
+
minMaxYear: MinMaxYearType;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export interface CalendarTabEmits {
|
|
175
|
+
'update:date': (day: { date: Date; inactive: boolean }) => void;
|
|
176
|
+
'update:month': (month: number) => void;
|
|
177
|
+
'update:year': (year: number) => void;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export interface MonthTabEmits {
|
|
181
|
+
'update:month': (month: number) => void;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface YearTabEmits {
|
|
185
|
+
'update:year': (year: number) => void;
|
|
186
|
+
'update:currentPage': (page: number) => void;
|
|
187
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { PropType, ExtractPropTypes } from 'vue';
|
|
2
|
+
|
|
3
|
+
// Helper function for prop types
|
|
4
|
+
export const definePropType = <T>(val: unknown): PropType<T> => val as PropType<T>;
|
|
5
|
+
|
|
6
|
+
// Define constants for prop validation
|
|
7
|
+
export const DATE_PICKER_MODES = ['full', 'month-year', 'year-only'] as const;
|
|
8
|
+
|
|
9
|
+
// Define types
|
|
10
|
+
export interface MinMaxYearType {
|
|
11
|
+
min: number;
|
|
12
|
+
max: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface DisabledDatesType {
|
|
16
|
+
to: string;
|
|
17
|
+
from: string;
|
|
18
|
+
pastDates: boolean | string;
|
|
19
|
+
futureDates: boolean | string;
|
|
20
|
+
selectedDates: Array<string>;
|
|
21
|
+
weekends: boolean;
|
|
22
|
+
weekdays: boolean;
|
|
23
|
+
selectedDays: Array<string>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type RestDayType = 'su' | 'mo' | 'tu' | 'we' | 'th' | 'fr' | 'sa';
|
|
27
|
+
export type DatePickerMode = typeof DATE_PICKER_MODES[number];
|
|
28
|
+
|
|
29
|
+
// Define props with JSDoc comments for documentation
|
|
30
|
+
export const reusableCalendarPropTypes = {
|
|
31
|
+
/**
|
|
32
|
+
* @description The selected date value (v-model)
|
|
33
|
+
*/
|
|
34
|
+
modelValue: {
|
|
35
|
+
type: String,
|
|
36
|
+
default: '',
|
|
37
|
+
},
|
|
38
|
+
/**
|
|
39
|
+
* @description Pre-selected month (0-indexed)
|
|
40
|
+
*/
|
|
41
|
+
selectedMonth: {
|
|
42
|
+
type: Number,
|
|
43
|
+
default: undefined,
|
|
44
|
+
},
|
|
45
|
+
/**
|
|
46
|
+
* @description Pre-selected year
|
|
47
|
+
*/
|
|
48
|
+
selectedYear: {
|
|
49
|
+
type: Number,
|
|
50
|
+
default: undefined,
|
|
51
|
+
},
|
|
52
|
+
/**
|
|
53
|
+
* @description Pre-selected day
|
|
54
|
+
*/
|
|
55
|
+
selectedDay: {
|
|
56
|
+
type: Number,
|
|
57
|
+
default: undefined,
|
|
58
|
+
},
|
|
59
|
+
/**
|
|
60
|
+
* @description Year range constraints
|
|
61
|
+
*/
|
|
62
|
+
minMaxYear: {
|
|
63
|
+
type: Object as PropType<MinMaxYearType>,
|
|
64
|
+
default: () => ({ min: 1900, max: new Date().getFullYear() }),
|
|
65
|
+
},
|
|
66
|
+
/**
|
|
67
|
+
* @description Days to mark as rest days
|
|
68
|
+
*/
|
|
69
|
+
restDays: {
|
|
70
|
+
type: Array as PropType<RestDayType[]>,
|
|
71
|
+
default: () => [],
|
|
72
|
+
},
|
|
73
|
+
/**
|
|
74
|
+
* @description Date restrictions
|
|
75
|
+
*/
|
|
76
|
+
disabledDates: {
|
|
77
|
+
type: Object as PropType<DisabledDatesType>,
|
|
78
|
+
default: undefined,
|
|
79
|
+
},
|
|
80
|
+
/**
|
|
81
|
+
* @description Disable the calendar
|
|
82
|
+
*/
|
|
83
|
+
disabled: {
|
|
84
|
+
type: Boolean,
|
|
85
|
+
default: false,
|
|
86
|
+
},
|
|
87
|
+
/**
|
|
88
|
+
* @description Make the calendar read-only
|
|
89
|
+
*/
|
|
90
|
+
readonly: {
|
|
91
|
+
type: Boolean,
|
|
92
|
+
default: false,
|
|
93
|
+
},
|
|
94
|
+
/**
|
|
95
|
+
* @description Calendar mode: 'full', 'month-year', 'year-only'
|
|
96
|
+
*/
|
|
97
|
+
mode: {
|
|
98
|
+
type: String as PropType<DatePickerMode>,
|
|
99
|
+
validator: (value: DatePickerMode) => DATE_PICKER_MODES.includes(value),
|
|
100
|
+
default: 'full',
|
|
101
|
+
},
|
|
102
|
+
/**
|
|
103
|
+
* @description Date format for v-model
|
|
104
|
+
*/
|
|
105
|
+
format: {
|
|
106
|
+
type: String,
|
|
107
|
+
default: 'MM-DD-YYYY',
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Define emits with type validation
|
|
112
|
+
export const reusableCalendarEmitTypes = {
|
|
113
|
+
'update:modelValue': (value: string): value is string => typeof value === 'string',
|
|
114
|
+
'update:month': (month: number): month is number => typeof month === 'number',
|
|
115
|
+
'update:year': (year: number): year is number => typeof year === 'number',
|
|
116
|
+
'update:day': (day: number): day is number => typeof day === 'number',
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// Export types for use in other files
|
|
120
|
+
export type ReusableCalendarPropTypes = ExtractPropTypes<typeof reusableCalendarPropTypes>;
|
|
121
|
+
export type ReusableCalendarEmitTypes = typeof reusableCalendarEmitTypes;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
ref="reusableCalendarRef"
|
|
4
|
+
:class="reusableCalendarClasses"
|
|
5
|
+
>
|
|
6
|
+
<div
|
|
7
|
+
:class="[
|
|
8
|
+
'spr-flex spr-justify-between spr-gap-2 spr-px-4 spr-py-3',
|
|
9
|
+
'spr-border spr-border-x-0 spr-border-b spr-border-t-0 spr-border-solid spr-border-mushroom-200',
|
|
10
|
+
]"
|
|
11
|
+
>
|
|
12
|
+
<!-- Tabs -->
|
|
13
|
+
<div class="spr-flex spr-gap-1">
|
|
14
|
+
<spr-button
|
|
15
|
+
v-if="showMonthInput"
|
|
16
|
+
:class="getTabClasses('tab-months')"
|
|
17
|
+
variant="secondary"
|
|
18
|
+
size="small"
|
|
19
|
+
@click="handleTabClick('tab-months')"
|
|
20
|
+
>
|
|
21
|
+
{{ getMonthObject('monthValue', calendarTabPageData.selectedMonth)?.fullText }}
|
|
22
|
+
</spr-button>
|
|
23
|
+
<spr-button
|
|
24
|
+
v-if="showYearInput"
|
|
25
|
+
:class="getTabClasses('tab-years')"
|
|
26
|
+
variant="secondary"
|
|
27
|
+
size="small"
|
|
28
|
+
@click="handleTabClick('tab-years')"
|
|
29
|
+
>
|
|
30
|
+
{{ calendarTabPageData.selectedYear }}
|
|
31
|
+
</spr-button>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<!-- Next & Previous Buttons -->
|
|
35
|
+
<div v-if="currentTab === 'tab-calendar'" class="spr-flex spr-gap-1">
|
|
36
|
+
<spr-button
|
|
37
|
+
class="spr-cursor-pointer"
|
|
38
|
+
variant="secondary"
|
|
39
|
+
size="small"
|
|
40
|
+
:disabled="calendarTabIsMinMonth"
|
|
41
|
+
@click="calendarTabPrevMonth"
|
|
42
|
+
>
|
|
43
|
+
<Icon icon="ph:caret-left" />
|
|
44
|
+
</spr-button>
|
|
45
|
+
<spr-button
|
|
46
|
+
class="spr-cursor-pointer"
|
|
47
|
+
variant="secondary"
|
|
48
|
+
size="small"
|
|
49
|
+
:disabled="calendarTabIsMaxMonth"
|
|
50
|
+
@click="calendarTabNextMonth"
|
|
51
|
+
>
|
|
52
|
+
<Icon icon="ph:caret-right" />
|
|
53
|
+
</spr-button>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<div v-if="currentTab === 'tab-years'" class="spr-flex spr-gap-1">
|
|
57
|
+
<spr-button
|
|
58
|
+
class="spr-cursor-pointer"
|
|
59
|
+
variant="secondary"
|
|
60
|
+
size="small"
|
|
61
|
+
:disabled="yearTabIsPreviousButtonDisabled"
|
|
62
|
+
@click="yearTabGoToPreviousPage"
|
|
63
|
+
>
|
|
64
|
+
<Icon icon="ph:caret-left" />
|
|
65
|
+
</spr-button>
|
|
66
|
+
<spr-button
|
|
67
|
+
class="spr-cursor-pointer"
|
|
68
|
+
variant="secondary"
|
|
69
|
+
size="small"
|
|
70
|
+
:disabled="yearTabIsNextButtonDisabled"
|
|
71
|
+
@click="yearTabGoToNextPage"
|
|
72
|
+
>
|
|
73
|
+
<Icon icon="ph:caret-right" />
|
|
74
|
+
</spr-button>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<div class="spr-px-4 spr-pb-4 spr-pt-2">
|
|
79
|
+
<!-- Calendar Tab -->
|
|
80
|
+
<DatePickerCalendarTab
|
|
81
|
+
v-if="currentTab === 'tab-calendar' && showDateInput"
|
|
82
|
+
:calendar-days="calendarDays"
|
|
83
|
+
:selected-month="selectedMonthComputed"
|
|
84
|
+
:selected-year="selectedYearComputed"
|
|
85
|
+
:selected-day="selectedDayComputed"
|
|
86
|
+
:min-max-year="minMaxYear"
|
|
87
|
+
:rest-days="restDays"
|
|
88
|
+
:disabled-dates="disabledDates"
|
|
89
|
+
:disabled="disabled"
|
|
90
|
+
:readonly="readonly"
|
|
91
|
+
@update:date="handleCalendarDateUpdateWrapper as any"
|
|
92
|
+
@update:month="handleCalendarMonthUpdateWrapper as any"
|
|
93
|
+
@update:year="handleCalendarYearUpdateWrapper as any"
|
|
94
|
+
/>
|
|
95
|
+
|
|
96
|
+
<!-- Months Tab -->
|
|
97
|
+
<DatePickerMonthTab
|
|
98
|
+
v-if="currentTab === 'tab-months' && showMonthInput"
|
|
99
|
+
:selected-month="selectedMonthComputed"
|
|
100
|
+
:selected-year="selectedYearComputed"
|
|
101
|
+
:min-max-year="minMaxYear"
|
|
102
|
+
:disabled="disabled"
|
|
103
|
+
:readonly="readonly"
|
|
104
|
+
@update:month="handleMonthTabMonthUpdateWrapper as any"
|
|
105
|
+
/>
|
|
106
|
+
|
|
107
|
+
<!-- Years Tab -->
|
|
108
|
+
<DatePickerYearTab
|
|
109
|
+
v-if="currentTab === 'tab-years' && showYearInput"
|
|
110
|
+
:selected-month="selectedMonthComputed"
|
|
111
|
+
:selected-year="selectedYearComputed"
|
|
112
|
+
:min-max-year="minMaxYear"
|
|
113
|
+
:years-array="yearTabPageData.yearsArray"
|
|
114
|
+
:current-page="yearTabPageData.currentPage"
|
|
115
|
+
:items-per-page="yearTabPageData.itemsPerPage"
|
|
116
|
+
:disabled="disabled"
|
|
117
|
+
:readonly="readonly"
|
|
118
|
+
@update:year="handleYearTabYearUpdateWrapper as any"
|
|
119
|
+
@update:current-page="handleYearTabCurrentPageUpdateWrapper as any"
|
|
120
|
+
/>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
</template>
|
|
124
|
+
|
|
125
|
+
<script lang="ts" setup>
|
|
126
|
+
import { computed, ref } from 'vue';
|
|
127
|
+
import { Icon } from '@iconify/vue';
|
|
128
|
+
import classNames from 'classnames';
|
|
129
|
+
|
|
130
|
+
import SprButton from '@/components/button/button.vue';
|
|
131
|
+
import DatePickerCalendarTab from '../tabs/DatePickerCalendarTab.vue';
|
|
132
|
+
import DatePickerMonthTab from '../tabs/DatePickerMonthTab.vue';
|
|
133
|
+
import DatePickerYearTab from '../tabs/DatePickerYearTab.vue';
|
|
134
|
+
|
|
135
|
+
import { useReusableCalendar } from './use-reusable-calendar';
|
|
136
|
+
import { reusableCalendarEmitTypes, reusableCalendarPropTypes } from './reusable-calendar';
|
|
137
|
+
|
|
138
|
+
const props = defineProps(reusableCalendarPropTypes);
|
|
139
|
+
const emit = defineEmits(reusableCalendarEmitTypes);
|
|
140
|
+
|
|
141
|
+
const reusableCalendarRef = ref<HTMLElement | null>(null);
|
|
142
|
+
|
|
143
|
+
// Use the composable for all logic
|
|
144
|
+
const {
|
|
145
|
+
// State
|
|
146
|
+
currentTab,
|
|
147
|
+
calendarTabPageData,
|
|
148
|
+
yearTabPageData,
|
|
149
|
+
|
|
150
|
+
// Computed properties
|
|
151
|
+
showMonthInput,
|
|
152
|
+
showDateInput,
|
|
153
|
+
showYearInput,
|
|
154
|
+
calendarDays,
|
|
155
|
+
selectedMonthComputed,
|
|
156
|
+
selectedYearComputed,
|
|
157
|
+
selectedDayComputed,
|
|
158
|
+
calendarTabIsMinMonth,
|
|
159
|
+
calendarTabIsMaxMonth,
|
|
160
|
+
yearTabIsPreviousButtonDisabled,
|
|
161
|
+
yearTabIsNextButtonDisabled,
|
|
162
|
+
|
|
163
|
+
// Functions
|
|
164
|
+
getTabClasses,
|
|
165
|
+
handleTabClick,
|
|
166
|
+
getMonthObject,
|
|
167
|
+
calendarTabPrevMonth,
|
|
168
|
+
calendarTabNextMonth,
|
|
169
|
+
yearTabGoToPreviousPage,
|
|
170
|
+
yearTabGoToNextPage,
|
|
171
|
+
|
|
172
|
+
// Event handlers
|
|
173
|
+
handleCalendarDateUpdateWrapper,
|
|
174
|
+
handleCalendarMonthUpdateWrapper,
|
|
175
|
+
handleCalendarYearUpdateWrapper,
|
|
176
|
+
handleMonthTabMonthUpdateWrapper,
|
|
177
|
+
handleYearTabYearUpdateWrapper,
|
|
178
|
+
handleYearTabCurrentPageUpdateWrapper,
|
|
179
|
+
} = useReusableCalendar(props, emit);
|
|
180
|
+
|
|
181
|
+
// Compute CSS classes using classNames utility
|
|
182
|
+
const reusableCalendarClasses = computed(() => {
|
|
183
|
+
return classNames(
|
|
184
|
+
'reusable-calendar-container spr-bg-white spr-rounded-lg spr-shadow-lg spr-border spr-border-solid spr-border-mushroom-200 min-w-[320px]',
|
|
185
|
+
{
|
|
186
|
+
'spr-disabled': props.disabled,
|
|
187
|
+
'spr-readonly': props.readonly,
|
|
188
|
+
}
|
|
189
|
+
);
|
|
190
|
+
});
|
|
191
|
+
</script>
|
|
192
|
+
|