npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

vue-use-calendar

v0.0.2

Published

Calendar & dates composables for Vue 3.

Downloads

856

Readme

Vue use-calendar

A vue 3 composable to create any kind of calendar!

  • Open source
  • SSR compliant
  • Fully typed with Typescript
  • Full customize your calendar style without thinking about the logic
  • Extendable
  • Uses date-fns functions internally for lightweight and consistent Dates operations

This can be used to hold the dates logic for your calendar components.

>>> See the DEMO <<<

Install it

# npm
npm install vue-use-calendar

# yarn
yarn add vue-use-calendar

Basic example

// Considering today is the 15th of March 2022
const { useMontlyCalendar } = useCalendar()
const {
  nextMonth,
  prevMonth,
  currentMonthAndYear,
  currentMonth,
  selectedDates,
  listeners,
} = useMonthlyCalendar({ fullWeeks: false, infinite: true });

/*
  currentMonthAndYear === { year: 2022, month: 2 }
  currentMonth === { month: 2, year: 2022, days: [...], index: 24266 }
  selectedDates === []
*/ 

// Go to next month
nextMonth()

/*
  currentMonthAndYear === { year: 2022, month: 3 }
  currentMonth === { month: 3, year: 2022, days: [...], index: 24267 }
  selectedDates === []
*/

listeners.selectSingle(currentMonth.days[3])

/*
  selectedDates === [{ date: <4th March 2020>, isSelected: true, ... }]
*/

The composables

The entry point of the library is use-calendar.

use-calendar

import { isSameDay } from 'date-fns';
import { es } from 'date-fns/locale';

const pricesByDay = [
      { price: 55, date: '2022-05-12' },
]

const { useMontlyCalendar } = useCalendar({
      startOn: new Date(2025, 5, 1),
      minDate: '2025-05-12',
      maxDate: new Date(2025, 5, 18),
      disabled: [new Date(2025, 5, 15)],
      firstDayOfWeek: 1, // Monday
      locale: es, // Spanish
      preSelection: [new Date(2025, 5, 13)],
      factory: (calendarDate) => {
            // Extends the generated date by adding the associated price
            const correspondingPrice = pricesByDay.find(({ date }) => isSameDay(date, calendarDate.date));
            const customDate: CustomDate = {
                  ...calendarDate,
                  price: correspondingPrice?.price || 0,
            };
            return customDate;
      }
})

const { currentMonth } = useMontlyCalendar()

Parameters

| name | type | optional | default | description | |------|------|----------|---------|-------------| | startOn | string \| Date | true | undefined | The date to initiate the calendar on | | minDate | string \| Date | true | undefined | The minimum selectionable date. All dates before will be disabled | | maxDate | string \| Date | true | undefined | The maximum selectionable date. All dates after will be disabled | | disabled | Array\<string \| Date> | true | [] | A date or array of date to disable | | firstDayOfWeek | 0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 | true | 0 | Tells on which day the week starts. 0 being Sunday. | | locale | date-fns's Locale | true | undefined | The locale object to use for translating weekdays.Import like import { fr } from 'date-fns/locale';. See date-fns | | preSelection | Array\<Date> \| Date | true | [] | A date or array of date to be preselected on calendar generation | | factory | (date: ICalendarDate) => \<extends ICalendarDate\> | true | undefined | A custom factory function to extend the default ICalendarDate objects. See [exemple](TODO example link) |

Outputs

The composable returns the following sub-composables

| name | description | |------|-------------| | useMonthlyCalendar | Generate a calendar where days are grouped by months | | useWeeklyCalendar | Generate a calendar where days are grouped by weeks | | useWeekdays | Gets the list of weekdays, translated and formatted |

use-monthly-calendar

const { useMontlyCalendar } = useCalendar()

const { currentMonth } = useMonthlyCalendar({ infinite: false, fullWeeks: false })

Parameters

| name | type | optional | default | description | |------|------|----------|---------|-------------| | infinite | boolean | true | true | Indicates if navigating throught the calendar should generate new months on the fly | | fullWeeks | boolean | true | true | Indicates if each month should display the "other month" days to complete each week |

Outputs

| name | type | description | |------|------|-------------| | days | ComputedRef<Array<C>> | An array of all days currently generated in the calendar. Excludes copied days so the array is sorted by day without duplicates | | selectedDates | Array<Date> | Reactive array of current dates selection. | | listeners | Listeners<C> | Object of methods for changing dates states (see ##listeners) | | currentMonthAndYear | ShallowReactive<{ month: number; year: number }>; | Reactive object containing the current month and year displayed. Can be mutated. | | currentMonth | ComputedRef<Month<C>>; | The current month displayed. Contains data about this month and the array of days it includes. | | months | ShallowReactive<Month<C>[]>; | An array of all the months generated in the calendar. | | nextMonth | () => void; | Method to navigate to the next month | | prevMonth | () => void; | Method to navigate to the previous month | | nextMonthEnabled | ComputedRef<boolean>; | A computed boolean that indicates if it is allowed to go to the next month | | prevMonthEnabled | ComputedRef<boolean>; | A computed boolean that indicates if it is allowed to go to the previous month |

use-weekly-calendar

const { useWeeklyCalendar } = useCalendar()

const { currentWeek } = useWeeklyCalendar({ infinite: false })

Parameters

| name | type | optional | default | description | |------|------|----------|---------|-------------| | infinite | boolean | true | true | Indicates if navigating throught the calendar should generate new weeks on the fly |

Outputs

| name | type | description | |------|------|-------------| | days | ComputedRef<Array<C>> | An array of all days currently generated in the calendar. Excludes copied days so the array is sorted by day without duplicates | | selectedDates | Array<Date> | Reactive array of current dates selection. | | listeners | Listeners<C> | Object of methods for changing dates states (see ##listeners). | | weeks | Array<Week<C>>; | A reactive array of all generated weeks in the calendar. | | currentWeekIndex | Ref<number>; | The current week index displayed. | | currentWeek | ComputedRef<Week<C>>; | The current week wrapper. Contains data about this week and the array of days it includes. | | nextWeek | () => void; | Method to navigate to the next week | | prevWeek | () => void; | Method to navigate to the previous week | | nextWeekEnabled | ComputedRef<boolean>; | A computed boolean that indicates if it is allowed to go to the next week | | prevWeekEnabled | ComputedRef<boolean>; | A computed boolean that indicates if it is allowed to go to the previous week |

use-weekdays

const { useWeekdays } = useCalendar()

// [Mon, Tue, Wed, ..., Sun]
const weekDays = useWeekdays('iii')

Parameters

| name | type | optional | default | description | |------|------|----------|---------|-------------| | weekdayFormat | 'i' \| 'io' \| 'ii' \| 'iii' \| 'iiii' \| 'iiiii' \| 'iiiiii' | true | 'iiiii' | The format to use while translating the weekdays. See date-fns ISO day of week |

Outputs

| name | reactive | description | |------|----------|-------------| | | false | The array of week days, translated and formatted. |

Listeners

The listeners object returned by the sub composables contains the following methods:

selectSingle

Call this method to select a single date. It will unselect all currently selected dates.

const { useMontlyCalendar } = useCalendar()
const { currentMonth, listeners: { selectSingle } } = useMonthlyCalendar()

/*
         March 2022     
 Su  Mo  Tu  We  Th  Fr  Sa
          1   2   3   4   5
  6   7   8   9  10  11  12
 13  14  15  16  17  18  19
 20  21  22  23  24  25  26
 27  28  29  30  31 
*/

selectSingle(currentMonth.days[10])

/*
         March 2022     
 Su  Mo  Tu  We  Th  Fr  Sa
          1   2   3   4   5
  6   7   8   9  10 [11] 12
 13  14  15  16  17  18  19
 20  21  22  23  24  25  26
 27  28  29  30  31 
*/

selectSingle(currentMonth.days[20])

/*
Selecting another date resets all previously selected dates
         March 2022     
 Su  Mo  Tu  We  Th  Fr  Sa
          1   2   3   4   5
  6   7   8   9  10  11  12
 13  14  15  16  17  18  19
 20 [21] 22  23  24  25  26
 27  28  29  30  31 
*/

selectRange

Use this method to select a range of two dates.

Selecting a third date will remove the previous selected dates.

Selecting an already selected date will unselect it. It won't unselect any other date, only the one in parameter.

const { useMontlyCalendar } = useCalendar()
const { currentMonth, listeners: { selectRange } } = useMonthlyCalendar()

/*
         March 2022     
 Su  Mo  Tu  We  Th  Fr  Sa
          1   2   3   4   5
  6   7   8   9  10  11  12
 13  14  15  16  17  18  19
 20  21  22  23  24  25  26
 27  28  29  30  31 
*/

selectRange(currentMonth.days[10])
selectRange(currentMonth.days[20])

/*
The 11th and the 21st are marked as `selected`
         March 2022     
 Su  Mo  Tu  We  Th  Fr  Sa
          1   2   3   4   5
  6   7   8   9  10 [11] 12
 13  14  15  16  17  18  19
 20 [21] 22  23  24  25  26
 27  28  29  30  31 
*/

selectRange(currentMonth.days[27])

/*
A third date is selected. All previously selected date are reset and we select the new date only (the 28th here)
         March 2022     
 Su  Mo  Tu  We  Th  Fr  Sa
          1   2   3   4   5
  6   7   8   9  10  11  12
 13  14  15  16  17  18  19
 20  21  22  23  24  25  26
 27 [28] 29  30  31 
*/

selectRange(currentMonth.days[27])

/*
Selecting an already selected date reset its state
         March 2022     
 Su  Mo  Tu  We  Th  Fr  Sa
          1   2   3   4   5
  6   7   8   9  10  11  12
 13  14  15  16  17  18  19
 20  21  22  23  24  25  26
 27  28  29  30  31 
*/

selectMultiple

Select multiple dates without limitation.

Selecting an already selected date will unselect it.

const { useMontlyCalendar } = useCalendar()
const { currentMonth, listeners: { selectMultiple } } = useMonthlyCalendar()

/*
         March 2022     
 Su  Mo  Tu  We  Th  Fr  Sa
          1   2   3   4   5
  6   7   8   9  10  11  12
 13  14  15  16  17  18  19
 20  21  22  23  24  25  26
 27  28  29  30  31 
*/

selectMultiple(currentMonth.days[10])
selectMultiple(currentMonth.days[20])

/*
The 11th and the 21st are marked as `selected`
         March 2022     
 Su  Mo  Tu  We  Th  Fr  Sa
          1   2   3   4   5
  6   7   8   9  10 [11] 12
 13  14  15  16  17  18  19
 20 [21] 22  23  24  25  26
 27  28  29  30  31 
*/

selectMultiple(currentMonth.days[27])

/*
A third date is selected. It just adds it to the selected date without wiping out the others
         March 2022     
 Su  Mo  Tu  We  Th  Fr  Sa
          1   2   3   4   5
  6   7   8   9  10 [11] 12
 13  14  15  16  17  18  19
 20 [21] 22  23  24  25  26
 27 [28] 29  30  31 
*/

selectMultiple(currentMonth.days[27])

/*
Selecting an already selected date reset its state
         March 2022     
 Su  Mo  Tu  We  Th  Fr  Sa
          1   2   3   4   5
  6   7   8   9  10 [11] 12
 13  14  15  16  17  18  19
 20 [21] 22  23  24  25  26
 27  28  29  30  31 
*/

hoverMultiple

Set the hover status on all dates between the first selected date and the one passed in parameter. It can be used to style the dates in between two selected dates while you hover them.

const { useMontlyCalendar } = useCalendar()
const { currentMonth, listeners: { hoverMultiple } } = useMonthlyCalendar()

/*
         March 2022     
 Su  Mo  Tu  We  Th  Fr  Sa
          1   2   3   4   5
  6   7   8   9  10  11  12
 13  14  15  16  17  18  19
 20  21  22  23  24  25  26
 27  28  29  30  31 
*/

hoverMultiple(currentMonth.days[10])
hoverMultiple(currentMonth.days[20])

/*
All the days between the 11th and 21st are marked as `hovered`
         March 2022     
 Su  Mo  Tu  We  Th  Fr  Sa
          1   2   3   4   5
  6   7   8   9  10 [11  12
 13  14  15  16  17  18  19
 20  21] 22  23  24  25  26
 27  28  29  30  31 
*/

resetHover

Reset the hover state to false for all dates.

const { useMontlyCalendar } = useCalendar()
const { currentMonth, listeners: { hoverMultiple, resetHover } } = useMonthlyCalendar()

hoverMultiple(currentMonth.days[10])
hoverMultiple(currentMonth.days[20])

/*
All the days between the 11th and 21st are marked as `hovered`
         March 2022     
 Su  Mo  Tu  We  Th  Fr  Sa
          1   2   3   4   5
  6   7   8   9  10 [11  12
 13  14  15  16  17  18  19
 20  21] 22  23  24  25  26
 27  28  29  30  31 
*/

resetHover()

/*
All days marked as `hovered` are reset to normal state
         March 2022     
 Su  Mo  Tu  We  Th  Fr  Sa
          1   2   3   4   5
  6   7   8   9  10  11  12
 13  14  15  16  17  18  19
 20  21  22  23  24  25  26
 27  28  29  30  31 
*/

How does it work?

Each date is represented by a custom object called CalendarDate.

CalendarDate object

| Property | Type | Description | |-----------------|-----------------|------------------| | date | Date | The real javascript Date object | | isToday | boolean | True if is the same day as today | | isWeekend | boolean | True if is saturday or sunday | | otherMonth | boolean | True if the date is included | | disabled | Ref<boolean> | Reactive boolean if the date is disabled | | isSelected | Ref<boolean> | Reactive boolean if the date is selected | | isBetween | Ref<boolean> | Reactive boolean if the date is between two selected dates | | isHovered | Ref<boolean> | Reactive boolean if the date is currently hovered | | monthYearIndex | number | The date's month index. See What's the monthYear index? | | dayId | ${year}-${month}-${day} | A string representing a uniq id for this day. In the form ${year}-${month}-${day}. Mainly used internally. | | _copied | boolean | True if the day is a copy from another day. See ## Linked dates |

Extending the CalendarDate objects

You can provide an optional custom function in the useCalendar composable to add extra properties to your date objects.

// Imagine you have this kind of array somewhere in your app / components
const pricesByDay = [
      { price: 55, date: '2022-05-12' },
]

// If you want the calendar to use this object, you can provide a function to make the mapping yourself.

const { useMontlyCalendar } = useCalendar({
      factory: (calendarDate) => {
            // Find the price object related to the generated date
            const correspondingPrice = pricesByDay.find(({ date }) => isSameDay(date, calendarDate.date));
            // Creates a custom date object, with an additionnal `price` property.
            const customDate: CustomDate = {
                  ...calendarDate,
                  price: correspondingPrice?.price || 0,
            };
            // Return the new object date created from the original one + the price
            return customDate;
      }
})

It's a function that takes as a parameter a CalendarDate object generated by the composable. Anytime the composable generates a new date, it will trigger this function.

You must return an object containing the same properties as the original, eventually with extra ones.

Careful with this function! If you don't return the complete original object, it may break the composables. Be sure that you don't touch the existing properties of the CalendarDate object!

What's the monthYear index?

What we call the "monthYear" index is not a real concept. We use this index to identify every month with a number having the following properties:

  • All monthYearIndex is uniq for a given month in a year.
  • Two consecutives months should have a consecutive number (also true between "december year 1" and "january year 2")

The formula to compute it is as simple as : <month index> + <year> * 12

Example:

2020/08/18 => (08 - 1) + 2020 * 12 => 24247
2020/12/25 => (12 - 1) + 2020 * 12 => 24251
2021/01/21 => (01 - 1) + 2021 * 12 => 24252

This is internally used to easily navigate thought months (and years) with an classical index as an iterator.

Linked dates

The composable use a mechanism to link two identical dates so that when the state of one changes, the other is updated too.

For instance, when using fullWeeks prop on the use-monthly-calendar composable, you will have duplicates dates across your months. Month 1 will show some dates from Month 2, because they are part of its last week. On Month 2, these dates are the first dates of the month. But it also shows the last dates from Month 1.

      March 2022            April 2022       
 Su Mo Tu We Th Fr Sa  Su Mo Tu We Th  Fr Sa  
        1  2  3  4  5  [27 28 29 30 31] 1  2  
  6  7  8  9 10 11 12   3  4  5  6  7   8  9  
 13 14 15 16 17 18 19  10 11 12 13 14  15 16  
 20 21 22 23 24 25 26  17 18 19 20 21  22 23  
 27 28 29 30 31 [1 2]  24 25 26 27 28  29 30 

In the calendars above, the dates between squared brackets are copies from the dates in the original month. There are linked.

So when the date 30 in "March 2022" is selected, so is the [30th] in "April 2022".

Technical note: A date linked to another is marked by the internal attribute _copied

Contributing

Run the project

You can fork and clone the project on github.

Then install the dependencies:

yarn install

Start the example on localhost:3000:

yarn example

Submit changes

If you're willing to participate in the development of this library, you are warmly welcome!

Here are the steps to follow:

  1. Fork the repository on github
  2. Clone your fork (or update your remote target branch to your fork)
  3. Open a branch, name it according the changes you are planning on doing.
  4. Submit a PullRequest.

I'll review it as soon as possible! 👐

Todos

  • Try a LinkedList implementation to simplify the code (without class instance for SSR)
  • avoid updating currentMonthAndYear if infinite === false
  • use date-fns locale's weekStartOn by default
  • weekly calendar selection
  • move otherMonth and monthYearIndex attribute of the default CalendarDate object. Should be a custom one for month-calendar only.