Skip to content

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
empty
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

NameDescriptionTypeDefault
employeesArray of employee data to display in the calendar, including schedule information.Array[]
initialDateThe initial date to display in the calendar. The calendar will show the week containing this date.DateCurrent date
loadingControls whether to show a loading indicator, typically used during data fetching.booleanfalse
companyOptionsArray of options for the company filter dropdown.Array<{text: string, value: string}>[]
departmentOptionsArray of options for the department filter dropdown.Array<{text: string, value: string}>[]
branchOptionsArray of options for the branch filter dropdown.Array<{text: string, value: string}>[]
emptyStateConfiguration for the empty state displayed when there are no employees to show.ObjectDefault empty state

Events

NameDescriptionParameters
loadMoreEmitted 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.-
onCellClickEmitted when a calendar cell is clicked. The parameter contains the employeeId, date, and shift information for the clicked cell.(data: SelectedShift)
update:firstLastDayOfWeekEmitted 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:sortEmitted when the sort order changes by clicking the sort icon. Value will be 'asc', 'desc', or '' (empty string for no sorting).(value: string)
update:searchEmitted when the search term changes. Used for v-model binding.(value: string)
update:selectedCellEmitted when a cell is selected. Used for v-model binding of the selectedCell prop.(data: SelectedShift)
update:selectedCompanyEmitted when the company filter selection changes. Used for v-model binding.(value: string)
update:selectedDepartmentEmitted when the department filter selection changes. Used for v-model binding.(value: string)
update:selectedBranchEmitted when the branch filter selection changes. Used for v-model binding.(value: string)
onClickEmptyButtonEmitted when the empty state button is clicked. Can be used to trigger actions like adding a new employee.-

Slots

NameDescriptionExample
filterSlot for customizing the filter section above the calendar. Can be used to add custom filters or controls.
<template #filter>
  <div>Custom Filter Content</div>
</template>
loadingSlot for customizing the loading state displayed during data fetching.
<template #loading>
  <div>loading</div>
</template>
empty-stateSlot for customizing the empty state shown when there are no employees to display. By default, uses the SprEmptyState component.
<template #empty-state>
  <div>No employees found</div>
</template>

Product Uses

Sprout HR