Calendar Component
The Calendar
component is a comprehensive and customizable weekly calendar designed for employee scheduling and management. It provides a full-featured interface for viewing and interacting with employee schedules, including support for:
- Weekly date navigation with intuitive controls
- Employee listing with profile information and work hour tracking
- Multiple shift display for each day with detailed information
- Support for rest days and custom shift types
- Infinite scrolling for large employee lists
- Empty state customization
- Loading states and skeleton loaders
- Responsive design with proper handling of overflows
Usage
Basic Example
Aug 2025
Employee Name | 24 SUN | 25 MON | 26 TUE | 27 WED | 28 THU | 29 FRI | 30 SAT |
---|---|---|---|---|---|---|---|
TW Theresa Webb Senior UX Researcher 40/48 HRS | |||||||
KM Kathryn Murphy Interaction Designer 35/48 HRS | |||||||
KM Kathryn Murphy Interaction Designer 35/48 HRS | |||||||
KM Kathryn Murphy Interaction Designer 35/48 HRS | |||||||
No List Found |
vue
<template>
<SprCalendar
v-model:search="searchEmployee"
v-model:selected-cell="selectedCell"
v-model:selected-company="selectedCompany"
v-model:selected-department="selectedDepartment"
v-model:selected-branch="selectedBranch"
:employees="employees"
:initial-date="initialDate"
:company-options="companyOptions"
:department-options="departmentOptions"
:branch-options="branchOptions"
/>
</template>
<script setup lang="ts">
import SprCalendar from '@/components/calendar/calendar.vue';
import { ref } from 'vue';
const initialDate = new Date();
const searchEmployee = ref();
const selectedCompany = ref('');
const selectedDepartment = ref('');
const selectedBranch = ref('');
const selectedCell = ref({
employeeId: '',
date: '',
schedule: null,
});
const employees = [
{
id: 1,
name: 'Theresa Webb',
position: 'Senior UX Researcher',
avatar: '',
highlight: true,
hoursWorked: 40,
hoursTarget: 48,
schedule: {
'2025-05-05': [{ type: 'restday' }],
'2025-05-06': [{ startTime: '09:00AM', endTime: '06:00PM', location: 'Office A', type: 'Standard Day Shift' }],
'2025-05-07': [{ startTime: '09:00AM', endTime: '06:00PM', location: 'Office A', type: 'Standard Day Shift' }],
'2025-05-13': [{ startTime: '09:00AM', endTime: '06:00PM', location: 'Office A', type: 'Standard Day Shift' }],
// ...other dates
},
},
{
id: 2,
name: 'Kathryn Murphy',
position: 'Interaction Designer',
avatar: '',
highlight: true,
hoursWorked: 35,
hoursTarget: 48,
schedule: {
'2025-05-05': [{ type: 'restday' }],
'2025-05-08': [{ startTime: '10:00AM', endTime: '07:00PM', location: 'Office B', type: 'Morning Shift' }],
'2025-05-10': [{ startTime: '10:00AM', endTime: '07:00PM', location: 'Office B', type: 'Morning Shift' }],
'2025-05-13': [
{ startTime: '10:00AM', endTime: '06:00PM', location: 'Office a', type: 'Morning Shift' },
{ startTime: '10:00AM', endTime: '07:00PM', location: 'Office B', type: 'Morning Shift' },
{ startTime: '10:00AM', endTime: '08:00PM', location: 'Office c', type: 'Morning Shift' },
{ startTime: '10:00AM', endTime: '09:00PM', location: 'Office d', type: 'Morning Shift' },
],
// ...other dates
},
},
{
id: 3,
name: 'Kathryn Murphy',
position: 'Interaction Designer',
avatar: '',
highlight: true,
hoursWorked: 35,
hoursTarget: 48,
schedule: {
'2025-05-01': [{ type: 'restday' }],
'2025-05-02': [{ startTime: '10:00AM', endTime: '07:00PM', location: 'Office B', type: 'Morning Shift' }],
// ...other dates
},
},
{
id: 4,
name: 'Kathryn Murphy',
position: 'Interaction Designer',
avatar: '',
highlight: true,
hoursWorked: 35,
hoursTarget: 48,
schedule: {
'2025-05-12': [{ type: 'restday' }],
'2025-05-15': [{ startTime: '10:00AM', endTime: '07:00PM', location: 'Office B', type: 'Morning Shift' }],
// ...other dates
},
},
];
const companyOptions = [
{ text: 'All Companies', value: 'all' },
{ text: 'Company A', value: 'company-a' },
{ text: 'Company B', value: 'company-b' },
];
const departmentOptions = [
{ text: 'All Departments', value: 'all' },
{ text: 'Design', value: 'design' },
{ text: 'Development', value: 'development' },
];
const branchOptions = [
{ text: 'All Branches', value: 'all' },
{ text: 'Branch A', value: 'branch-a' },
{ text: 'Branch B', value: 'branch-b' },
];
</script>
Features
Infinite Scroll
The calendar supports infinite scrolling for loading more employee data:
- Automatically triggers when scrolling near the bottom (50px threshold)
- Emits
loadMore
event when more data should be loaded - Maintains smooth scrolling experience with proper spacing
Example of handling infinite scroll:
vue
<template>
<SprCalendar @load-more="handleLoadMore" :employees="employees" :loading="isLoading" />
</template>
<script setup>
const handleLoadMore = async () => {
isLoading.value = true;
// Load more employees here
await loadMoreEmployees();
isLoading.value = false;
};
</script>
API Reference
Props
Name | Description | Type | Default |
---|---|---|---|
employees | Array of employee data to display in the calendar, including schedule information. | Array | [] |
initialDate | The initial date to display in the calendar. The calendar will show the week containing this date. | Date | Current date |
loading | Controls whether to show a loading indicator, typically used during data fetching. | boolean | false |
companyOptions | Array of options for the company filter dropdown. | Array<{text: string, value: string}> | [] |
departmentOptions | Array of options for the department filter dropdown. | Array<{text: string, value: string}> | [] |
branchOptions | Array of options for the branch filter dropdown. | Array<{text: string, value: string}> | [] |
emptyState | Configuration for the empty state displayed when there are no employees to show. | Object | Default empty state |
Events
Name | Description | Parameters |
---|---|---|
loadMore | Emitted when the user scrolls near the bottom of the calendar (within 50px), indicating that more employee data should be loaded. Used for implementing infinite scroll. | - |
onCellClick | Emitted when a calendar cell is clicked. The parameter contains the employeeId, date, and shift information for the clicked cell. | (data: SelectedShift) |
update:firstLastDayOfWeek | Emitted when the visible week range changes, either through navigation or initialization. The dates are formatted as 'YYYY-MM-DD'. | (range: { firstDay: string, lastDay: string }) |
update:sort | Emitted when the sort order changes by clicking the sort icon. Value will be 'asc', 'desc', or '' (empty string for no sorting). | (value: string) |
update:search | Emitted when the search term changes. Used for v-model binding. | (value: string) |
update:selectedCell | Emitted when a cell is selected. Used for v-model binding of the selectedCell prop. | (data: SelectedShift) |
update:selectedCompany | Emitted when the company filter selection changes. Used for v-model binding. | (value: string) |
update:selectedDepartment | Emitted when the department filter selection changes. Used for v-model binding. | (value: string) |
update:selectedBranch | Emitted when the branch filter selection changes. Used for v-model binding. | (value: string) |
onClickEmptyButton | Emitted when the empty state button is clicked. Can be used to trigger actions like adding a new employee. | - |
Slots
Name | Description | Example |
---|---|---|
filter | Slot for customizing the filter section above the calendar. Can be used to add custom filters or controls. |
|
loading | Slot for customizing the loading state displayed during data fetching. |
|
empty-state | Slot for customizing the empty state shown when there are no employees to display. By default, uses the SprEmptyState component. |
|