Simple Vue component to show a month-grid calendar with events

Overview

VueSimpleCalendar

Introduction

vue-simple-calendar is a flexible, themeable, lightweight calendar component for Vue that supports multi-day scheduled items.

This branch is for version 6, not yet released. This new version will drop IE11 support, be migrated to Vue 3 and Vite, and will add TypeScript support. Version 5 is available via npm.

Demo

Here is a live demo page and the repo for it: https://www.tallent.us/vue-simple-calendar/ https://github.com/richardtallent/vue-simple-calendar-sample

Table of Contents

Features

There are other great calendar components out there, but most are either intended to be used as date pickers or had way too many features for me. I wanted something that would simply show a month as a grid and show scheduled activities (including multi-day items) on that grid. While the component goes beyond that simple use case, that is still the core focus.

  • Shows a traditional month-grid calendar. Also supports weeks, years, or multiple weeks, months, or years.
  • Can show scheduled "items," including multi-day items, with an optional time of day.
  • If there are too many items to see in a week, you can scroll to see the others.
  • If there are too many weeks to see in the calendar component, you can scroll to see the others.
  • Optional support for dragging and dropping items between dates.
  • Automatic localization / internationalization (can be overridden).
  • Lightweight!
  • Flexbox layout (look ma, no tables!).
  • No external dependencies (Moment, JQuery, etc.).
  • Various user events (clicks, drags, etc.) are emitted by the calendar component.
  • Weeks can start on any day of the week (defaults to Sunday).
  • Easy to customize the theme (using CSS) to integrate with your own site
  • Easy to customize using Vue slots
  • Date range selection (programmatic or via user drag-select)

What this component does not try to do:

  • There will be no "agenda" view (time-of-day grid). Adding this view would require too much additional complexity.
  • There is no interface for managing calendar items. This is far too use-specific.
  • There is no built-in AJAX mechanism. This is also far too use-specific.
  • Only the Gregorian calendar is supported (7-day weeks, etc.).
  • It is not yet possible to "resize" items to cover more or fewer days. This may be added in the future.

Browser compatibility

As of version 6, compatibility is limited to relatively modern browsers (i.e., not IE11). Please note that this component is designed first for desktop web applications, not mobile, so while the component may operate on a mobile device, the limited resolution may not allow for much in the way of useful functionality. Also, drag and drop only works on desktop browsers -- the drag events on touch devices are very different, I haven't had time to dig into them yet.

If your browser doesn't support Intl, the month names and weekday names will be blank and the calendar will have a nointl class. Use CSS content to provide the appropriate month and weekday names for the languages you support. For example:

.calendar.nointl.locale-en.m01 .monthName::after { content: 'January'; }

Installation and Usage

(This assumes you already have a web application set up for using Vue. If you're starting a new project, look up the documentation for the Vue CLI, which will allow you to initialize a new project with webpack, etc.)

Install the component using npm:

npm i --save vue-simple-calendar

In your application, you will need to:

  • import the component and register it with Vue
  • import the default theme or any other theme you want to use (CSS)
  • create the calendar-view component
  • create the calendar-view-header component as a child of the calendar-view if you want a header for the calendar
  • wire up the properties and events

Tips:

  • The component will fill its parent's height and width, so be sure the parent has a minimum height that is appropriate for the number of weeks and average items per week being shown.
  • The default calendar header emits an input event when a user clicks a button in the header to move the calendar backward or forward through time. The event's argument is the new date to be shown. You have to handle this event and pass the date back to the calendar to change the view.
  • To minimize the impact of an ancestor element's layout on the calendar's functionality, it is recommended that the parent of the <calendar-view> component only contain the component, and that the parent have the following styles (#71):
display: flex;
flex-direction: column;
flex-grow: 1;

Here's a minimal application example for an empty calendar, styled with the default theme and the US holidays theme:

<template>
	<div id="app">
		<h1>My Calendar</h1>
		<calendar-view
			:show-date="showDate"
			class="theme-default holiday-us-traditional holiday-us-official">
			<template #header="{ headerProps }">
				<calendar-view-header
					:header-props="headerProps"
					@input="setShowDate" />
			</template>
		</calendar-view>
	</div>
</template>
<script>
	import { CalendarView, CalendarViewHeader } from "vue-simple-calendar"
	import from "vue-simple-calendar/dist/style.css"
	// The next two lines are optional themes
	import from "vue-simple-calendar/static/css/default.css"
	import from "vue-simple-calendar/static/css/holidays-us.css"

	export default {
		name: 'app',
		data: function() {
			return { showDate: new Date() }
		},
		components: {
			CalendarView,
			CalendarViewHeader,
		},
		methods: {
			setShowDate(d) {
				this.showDate = d;
			},
		}
	}
</script>
<style>
	#app {
		font-family: 'Avenir', Helvetica, Arial, sans-serif;
		color: #2c3e50;
		height: 67vh;
		width: 90vw;
		margin-left: auto;
		margin-right: auto;
	}
</style>

Props

The following properties are supported, by functional area:

Grid Display

  • showDate - The period to show by default. Defaults to today's date (user local time). Any time component is ignored.
  • displayPeriodUom - The period type to show. By default this is month, i.e., it shows a calendar in month-sized chunks. Other allowed values are year and week.
  • displayPeriodCount - The number of periods to show within the view. For example, if displayPeriodUom is week and displayPeriodCount is 2, the view will show a two-week period.
  • startingDayOfWeek - The day of the week that starts each week. Defaults to 0 (Sunday), valid range is 0-6. Common non-default values would be 1 (Monday) for Europe or 6 (Saturday) for much of the Middle East.
  • dateClasses - Optional object, where the key is a date in ISO form (e.g., "2018-04-15") and the value is a string or array of additional CSS classes that should be applied to the main element for that date. This could be useful for dynamically highlighting selected dates, holidays, blocked-off dates, etc. NOTE: For an example of how to use this property and associated styling, please refer to the demo app, which uses this prop to put a dagger emoji on the "ides" of the current month (the 13th or 15th, depending on the month) and some other icons on the 21st.
  • periodChangedCallback - Optional function to be called calendar updates initially and any time thereafter where the date range shown on the calendar changes. This is intended to allow your application to, say, query a back-end server to update the items property based on the date range visible in the calendar. When your function is called, it is passed an object as the argument, with four keys: periodStart / periodEnd (the dates that fall within the range of the months being shown) and displayFirstDate / displayLastDate (the dates shown on the calendar, including those that fall outside the period). See CHANGELOG for details on why I'm using a functional property rather than emitting an event.
  • displayWeekNumbers: Adds a column for each week to show the "week number." By default, this appears to the left of the days and contains the calendar week number. The position can moved to the right using CSS. See the weekNumber slot for more details.

Grid Selection

  • enableDateSelection - If true, the user can "drag" their cursor across dates to select a date range. When the user starts dragging, a date-selection-start is emitted. As the user drags into other dates, date-selection events are emitted. When the user stop dragging, a date-selection-finish event is emitted. All of these events sent a three-element array payload. The first two elements are the new date range selected, the third is the original DOM drag event. Note that this represents what the user selected -- if you want to highlight those dates, you'll need to pass the dates back as selectionStart and selectionEnd props. This allows you to modify the selection highlights as needed (for example, performing some action (such as adding a new event, like Google Calendar) and clearing it when that action is complete).
  • selectionStart - the start of the date range you want to select. This date is decorated with the selectionStart class.
  • selectionEnd - the end of the date range you want to select. This date is decorated with the selectionEnd class.

Note: Each date between selectionStart and selectionEnd (including them) has the aria-selected attribute. This is used in the default CSS theme to highlight these in yellow).

Header Display

  • locale - The BCP 47 language tag used to determine the month and day names. Defaults to the user's browser language setting.
  • monthNameFormat - The format to use for the month names. Possible values are numeric, 2-digit, narrow, short, or long, and the default is long.
  • weekdayNameFormat - The format to use for the names of the days of the week. Possible values are narrow, short, or long, and the default is short.
  • disablePast - If true, prevents the user from navigating to previous periods. Default is false. (Note: since this is a Boolean value, you should use v-bind on the attribute.)
  • disableFuture - If true, prevents the user from navigating to future periods. Default is false. (Note: since this is a Boolean value, you should use v-bind on the attribute.)
  • currentPeriodLabel - Optional label for the "Today" button (the button in the header to return to the current period). If blank, this will show the current date period (i.e., the period where today's date would fall). If this has the special value icons, it will display an icon, where the icon depends on whether the current date period is in the past, is the displayed period, or is in the future. The default icons for this are , -, and , respectively. If you use any other string, the button will show the literal value you provide.
  • currentPeriodLabelIcons - Optional replacement for the above three icons. Pass this as a three-character string.

Items (Scheduled events to show on the calendar)

  • items - An array of items to show on the calendar. See Calendar Item Properties below for more details.
  • enableDragDrop - If true, items are draggable, and dragging and dropping them emits events you can catch and respond to. Default is false. (Note: since this is a Boolean value, you should use v-bind on the attribute.)
  • showTimes - If true, shows the start and/or end time of an item beside the item title. Midnight is not shown, a midnight time is assumed to indicate an all-day or indeterminate time. (If you want to show midnight, use 00:00:01 and don't choose to show seconds.) The default is false.
  • timeFormatOptions - This takes an object containing Intl.DateTimeFormat options to be used to format the item times. The locale setting is automatically used. This option is ignored for browsers that don't support Intl (they will see the 24-hour, zero-padded time).
  • doEmitItemMouseEvents - Optional, default is false. If true, emits item-mouseenter and item-mouseleave events when the mouse hovers over a calendar item. In most cases, styling the isHovered class is enough to handle hover interactions with a calendar item. However, if you want to, say, show a tooltip or menu when a user hovers over a calendar item, you may need access to the real-time mouse DOM events. Be sure that your use of these events doesn't conflict with the user's ability to click, drag, read, or otherwise interact with the calendar items. NOTE: if you use slots for your calendar items, this property is ignored. (#136)
  • itemTop - Optional string of a CSS height to be used as the baseline for where items are positioned relative the top of the week. By default, this is 1.4em, the height of the standard cv-day-number element.
  • itemContentHeight - Optional CSS string of the total height of your items, not including borders. The default is 1.4em (1.0 from the font, 0.2 * 2 from the padding.). You would only set this if you're overriding the item height. This doesn't actually change the item height, it is only used to position the items below one another.
  • itemBorderHeight - Optional CSS string of the sum of your items' top and bottom borders. The default is 2px. You would only set this if you're overriding the item's top and/or bottom border width. This doesn't change the borders, it is only used to position the items below one another.

Tips for Vue component properties:

  • Remember to use kebab-case when specifying these properties using attributes on the calendar-view element (e.g., <calendar-view month-name-format="long">:
  • Remember to use binding (: prefix) for properties that should be a Boolean, number, array, or otherwise interpreted with JavaScript (e.g., <calendar-view :show-times="true">). You can omit the : for string properties with literal values.

Calendar Item Properties

Each item shown on the calendar can have the following properties. id and startDate are required, and title is strongly recommended.

  • id - A unique identifier for the item. This is required and must be unique.
  • startDate - The date the item starts on the calendar. This must be either passed as a JavaScript date object or as a string following an ISO-like form of "yyyy-mm-dd HH:MM:SS" (time is optional, and within time, minutes and seconds are both optional).
  • endDate - The date the item ends on the calendar. Defaults to the same date as startDate. This must be either passed as a JavaScript date object, or as a string following an ISO-like form of yyyy-mm-dd HH:MM:SS (time is optional, and within time, minutes and seconds are both optional).
  • title - The name of the item shown on the calendar. Defaults to "Untitled".
  • url - The URL associated with the item. The component has no built-in action associated with this, but it does add a "hasUrl" class to the item. To "follow" the URL, you'll need to listen for the click-item event and take the appropriate action.
  • classes - An array of strings with any additional CSS classes you wish to assign to the item.
  • style - A string with any additional CSS styles you wish to apply to the item.

Events

(Note: below, calendarItem refers to the normalized version of the calendar item involved in the activity. For more information, see the "item" slot below.)

You can create handlers for the following Vue events to add custom functionality (with their payload array):

Clicks

  • @click-date([date, calendarItems, windowEvent]): fires when the user clicks a date
  • @click-item([calendarItem, windowEvent]): fires when the user clicks a calendar item

Date selection (day drag and drop)

  • @date-selection-start([startDate, endDate, windowEvent]): fires when the user starts drag-selecting dates.
  • @date-selection([startDate, endDate, windowEvent]): fires as the user moves across dates while drag-selecting.
  • @date-selection-finish([startDate, endDate, windowEvent]): fires when the user stops drag-selecting dates (i.e., they release the mouse button)

Calendar Item Hover

  • @item-mouseenter([calendarItem, windowEvent]): optional (controlled by doEmitItemMouseEvents prop), fires when the user's pointer hovers over a calendar item.
  • @item-mouseleave([calendarItem, windowEvent]): optional (controlled by doEmitItemMouseEvents prop), fires when the user's pointer leaves a calendar item.

Calendar Item Drag and Drop

  • @drag-start([calendarItem, windowEvent]): fires when the user starts dragging a calendar item
  • @drag-enter-date([calendarItem, date, windowEvent]): fires when a calendar item is dragged over a date
  • @drag-leave-date([calendarItem, date, windowEvent]): fires when a calendar item is dragged out of a date without dropping it there
  • @drag-over-date([calendarItem, date, windowEvent]): fires multiple times as a calendar item is hovered over a date
  • @drop-on-date([calendarItem, date, windowEvent]): fires when a calendar item is dropped on a date

Slots

header

This named slot contains the component you want to use as the calendar's header. Generally, this would show the current date range, and allow the user to navigate back and forth through time. If you don't provide a component for this slot, there will be no header, just the calendar grid. This component comes with a nice default header component, CalendarViewHeader, which you can either use directly, or use as a template for creating your own. (Prior to 4.0, if you didn't provide a component, a default header would be shown. That is no longer the case for reasons explained in the CHANGELOG.)

<calendar-view :show-date="myShowDate">
	<calendar-view-header slot="header" slot-scope="{ headerProps }" :header-props="headerProps" @input="setMyShowDate" />
</calendar-view>

The parent calendar-view passes a property called headerProps to the header component. This property includes all of these values (basically, anything you would normally need to render a calendar header):

  • periodStart: the first date of the displayPeriodUom containing showDate
  • periodEnd: the last date of the displayPeriodUom containing showDate (the displayPeriodCount setting impacts this)
  • previousYear: one year before periodStart
  • previousPeriod: one displayPeriodUom before periodStart (regardless of the displayPeriodCount setting)
  • nextPeriod: one displayPeriodUom after periodStart (regardless of the displayPeriodCount setting)
  • previousFullPeriod: one displayPeriodUom before periodStart (takes the displayPeriodCount setting into consideration)
  • nextFullPeriod: one displayPeriodUom after periodStart (takes the displayPeriodCount setting into consideration)
  • nextYear: one year after periodStart
  • currentPeriod: the date at the beginning of the displayPeriodUom containing today's date (user local time)
  • currentPeriodLabel: the computed label (using the logic described for the property of the same name on the calendar component above) of the period containing today's date.
  • displayLocale: the user's locale setting
  • displayFirstDate: the first date shown in the calendar (may differ from periodStart--e.g., if periodStart is June 1, 2018, displayFirstDate will be May 27, 2018)
  • displayLastDate: the last date shown in the calendar (ditto)
  • monthNames: an array of the formatted names of the months to use based on the locale and month format settings
  • fixedItems: a copy of the calendar items, normalized to all have start/end dates, "Untitled" if there is no title, etc.

Since CalendarView has some logic around whether the user should be able to navigate to the past or the future, some of these dates will be null if the corresponding action is disabled.

The developer implementing her own header simply needs to create a header component that, like the default header component:

  • Receives this information and displays it with appropriate UI elements (suggested property name: headerProps)
  • Emits an event when the user wants to change the calendar period (suggested event name: input)

Note above that both previousPeriod and previousFullPeriod are provided, as well as nextPeriod and nextFullPeriod. The reason for this distinction is to allow the developer to decide how the calendar's Previous and Next buttons should move through time if displayPeriodCount is greater than 1. My personal preference is to move forward and backward by one week / month, allowing the user to pinpoint the exact date window they wish to see. But in some applications (a "quarterly" calendar, for example, or a calendar with set two-week periods), it may be better for the buttons to jump backward and forward based on the displayPeriodCount setting. This gives the developer both options -- just choose which interaction you want to use, and use those dates to set the new showDate.

dayHeader

This optional named slot replaces the default div.day elements that appear in the column headers for each day of the week. If all you need to do is change how the names are shown, it's probably better to override the locale and/or weekdayNameFormat property. This slot is intended for situations where you need to override the markup within each header cell. For example, if you want each day of the week to be clickable.

This slot passes two scoped variables: index, 0-7, and label, the text it would have used in the header based on the current locale and weekdayNameFormat.

dayContent

This optional named slot allows you to provide your own contents within the date cell. The day of the month is rendered in a separate (sibling) element with the class cv-day-number, so you should use CSS to hide this class if you want your slot to be the only content in the cell. Note that items are rendered above the individual date cells, so your slot content will appear below any items on that day.

This slot passes one scoped variable: day, the date associated with the cell.

item

This optional named slot replaces the div.item for each item (not just the contents of the items element, the entire element). Use this if you want to override entirely how items are rendered. For example, on a small mobile device, you may want to show just a thin stripe, dots, or icons to indicate items, without titles or times. This slot passes three scoped variables:

  • value: the normalized calendar item
  • weekStartDate: the date of the first day of the week being rendered
  • top: the CSS top value that you should apply to the style of your item element so it appears in the proper place. Assumes standard metrics for items, so if you have your own metrics, you'll need to compute and apply the top position yourself using the itemow value passed in the item.

Note that item is a version of the calendar item normalized to be shown on that week's row, it's not the bare item pulled from the items prop. This customized version parses and defaults the startDate and endDate, defaults missing id to a random number, defaults a blank title to "Untitled", and adds a number of classes values based on the position and role of the item as shown for that week (whether it continues from the previous week, etc.). The original item is passed back as item.originalItem.

weekNumber

This optional named slot replaces the content shown in the "week number" column. By default, this shows the week number of that week within its year (given your chosen startingDayOfWeek, and where week "1" is the week that contains January 1). However, using the weekNumber slot, you can use this column to display anything -- a number, icons, text, etc. This slow passes three scoped variables:

  • weekStart: The date that begins the week
  • numberInYear: The calendar week number (i.e., the number that would have been displayed by default)
  • numberInPeriod: The number of the week within the period (the month, unless you've overridden the period shown)

Using CSS, you can define your own width, colors, etc., and you can move the column to display after the days rather than before.

Note that this column "belongs" to the week, so if the week scrolls due to the number of items that week, so will the contents of this column.

Customizing the Look and Feel

In addition to slots, this component is designed to allow for significant customization of the look and feel solely through CSS. Here's the structure of the markup generated by the component (combined with the default header component). Each line represents a Vue SLOT (all caps) or an HTML element (first word on the line). Indentation represents the hierarchy. Each word after the first word is a class applied to the element. Elements or classes in (parenthesis) are conditional. Loops (i.e., v-for) are shown in [brackets].

Note that the items are not child nodes of the days, they are children of the week and positioned above the days. This allows items to span multiple days.

div cv-wrapper locale-X yYYYY mMM (past|future) period-X periodCount-X wrap-item-title-on-hover
	HEADER
		div cv-header
			div cv-header-nav
				button previousYear
				button previousPeriod
				button nextPeriod
				button nextYear
				button currentPeriod
			div periodLabel
				div startMonth
				div startDay
				div startYear
				div endMonth
				div endDay
				div endYear
	div cv-header-days
		div cv-header-day dowX [x7]
	div cv-weeks
		div cv-week weekX wsYYYY-MM-DD [x # weeks in visible period]
			(div cv-weeknumber)
				span
			dv-weekdays
				div cv-day dowX dYYYY-MM-DD dMM-DD dDD wmX (past|today|future|last|outsideOfMonth|lastInstance|selectionStart|selectionEnd) [x 7]
					div cv-day-number
					DAYCONTENT
				ITEM
					div cv-item offsetX spanX (continued|toBeContinued|hasUrl|hasItems) [x # of items]
						span startTime (hasEndTime)
						span endTime (hasStartTime)

where:

Calendar classes

locale-X

Two locale classes are added--one for the user's full locale, the other for just the first two letters (the language), both in lowercase. You could use this information to hide or show specific holidays, or to localize text using CSS content. Example:

locale-en locale-en-us

yYYYY

The full year of the starting period of the current view.

mMM

The month (01-12) of the starting period of the current view.

(future|past)

When the period shown does not include today's date (local time), one of these classes is added. By default, this shows and hides and determines the label for the button that returns the view to the current period.

period-X

The current displayPeriodUom value (year, month, or week).

periodCount-X

The current displayPeriodCount value (a number, usually 1).

Week classes

weekX

Each week is numbered, starting with the first week of the visible period. (This is not the same as the value in the optional "week number" column.)

wsYYYY-MM-DD

Each week also has a class representing the date of the Sunday starting that week. This could be used to style entire weeks that have some special importance.

wrap-item-title-on-hover

If an item title is truncated, this enables an optional behavior that will wrap the item to show the entire title when the user hovers over it.

Day classes

dowX

This class is for the day of the week, ranging from 0 to 6. This allows you to easily style certain days of the week (say, weekend days) differently from other days. The same class is also added to the weekday headers.

dYYYY-MM-DD / dMM-DD / dDD

Each day in the grid is given three special classes -- one for the month and day (01/01-12/31), one for the day (01-31), and one for the entire ISO 8601 date. This allows easy styling of holidays and other special days using CSS alone (rather than using calendar items).

The demo calendar has some examples of using these to add holiday emoji beside the day numbers. Example:

d2017-05-23 d05-23 d23

instanceX

The instance of the weekday within the day's month. For example, the class instance1 is added to the first Sunday, Monday, etc. of the month. Note that since this is relative to the day, not the "main" month being shown, days visible from the previous or next month will also have these classes, relative to their own month.

This makes it easy to style, say, the first and third Friday of each month. The demo app has some custom styles using this feature to add emoji to Labor Day and Thanksgiving.

lastInstance

Added to the last instance of a particular day of the week within its month (as above, relative to the day in question, which means the last day of the month previous, if in view, will also have this class. The example application uses this to show an emoji for Memorial Day (the last Monday of May in the US).

outsideOfMonth

This class is added to each day falling outside of the month being shown. By default, these days are greyed out.

past

This class is added to days before the current date (local time).

today

This class is added to the current date (local time), if visible on the current view.

future

This class is added to days after the current date (local time).

last

This class is added to the last day of its month.

selectionStart

This class is added to the day specified by the selectionStart prop.

selectionEnd

This class is added to the day specified by the selectionEnd prop.

Calendar Item classes

offsetX

This class on an item represents the day of the week when the item starts on this week. If an item spans more than one week, the offset for the second week, etc. would be offset0 (Sunday).

spanX

This class on an item represents the width of the item display that week, in days. For example, if an item spans from a Thursday to the next Wednesday, it would have span3 on the first week and span4 on the second week.

continued

This is added to an item when it is continuing from a previous week. By default, this turns off the rounded corners on the left side of the item box and adds a grey right-arrow before the title.

toBeContinued

This is added to an item when it is spills over into the following week. By default, this turns off the rounded corners on the right side of the item box and adds a grey right-arrow after the title.

hasUrl

This is added to an item when it has a url attribute (i.e., it is a hyperlink). By default, this is used to add a hovering underscore to item titles that are hyperlinked.

isHovered

This is added for all item elements whose id matches the id of the item being hovered. (This allows proper hover styling--when items wrap to more than one week, they are represented by more than one element, so a standard :hover selector will only select the element being hovered, not the entire item.) Note that there is no default styling for this, it is solely provided so you can choose to style hovered items if you wish.

I'm open to other suggestions, provided they are easily calculated and there's some reasonable use case for having them.

startTime / endTime

These classes are applied to the start and end time of an item, respectively.

Plans

  • Keep it simple, not a kitchen-sink control.
  • Better docs.
  • Add optional external stylesheets (keep scoped styling to the basics).
  • Add a "starts-on-Monday" mode.
  • Add support for showing item times
  • Possibly add a "week" view (no time of day scaling, just 7 taller boxes).
  • Possibly add modes for a set number of weeks, multiple months, or even a full year.
  • Extract date manipulation methods to a separate plugin.
  • Material theme (would love help with this)
  • Apple Calendar theme (would love help with this)
  • Outlook Calendar theme (would love help with this)
  • I'm not 100% happy with the Intl time format options, especially to show time ranges compactly. Considering a custom formatter or the ability to pass a formatter function as a property.
  • Rename the primary CSS classes (calendar-view, day, week, etc.) to depend far less on cascades, making it easier to customize the theme (breaking change for themes, targeted for 3.0.0).

Contributions

  • PRs and issues are welcome!
  • Please use the same code style -- there are linter configs included for styles, plain JavaScript, and Vue components.
  • Use of Prettier is recommended.
  • Please keep each PR related to a single fix or feature, and ideally start an Issue so we can discuss the details rather than just lobbing a PR over the fence with no prior communication. :)
  • Please play nice with others.
  • Any PRs will be accepted as having been released under the same license (MIT) as this component.

Inspiration

This project was inspired by Monthly.js, a JQuery-based control I've contributed to. Unfortunately, I wasn't able to port the code and still do things the Vue / Vanilla JS way, but I did borrow some of the concepts from that component.

FAQ

(Ok, not really frequently-asked, but just random stuff.)

Why Vue?

Because Vue is awesome. I've been using it since early 2017 in production, and I've migrated my applications from ExtJS, JQuery, and Riot to Vue components. If you prefer React or Angular, it should be reasonably easy to port it. If you do, please let me know, maybe we can coordinate on feature upgrades!

Can you add feature "X"?

Maybe. Depends if it fits the core functionality of viewing a calendar grid. I don't want to create something that replicates all possible calendar views, and definitely don't want to add functionality for creating or editing calendar items (that should be handled by the application/component hosting the view).

Why not use moment.js?

Moment.js is great, but I would only need a tiny fraction of its capabilities, and for simplicity, I wanted to not have any dependencies (other than Vue of course).

Why is the style so "plain"?

The baseline style (what you get with no external CSS files imported) is intended to be as bare as possible while still providing full functionality and legibility. The idea here is to make integrating this component into your own theme as easy as possible by inheriting what it can from your parent application and minimizing the places where you may need to override its choices.

The default theme stylesheet builds on this baseline to provide a restrained, clean, simple theme for the calendar, and is useful if you don't intend to create your own theme. You can include it from static/css/default.css. The sample app uses this stylesheet.

A third stylesheet, static/css/holidays-us.css, shows how simple it is to use CSS to style specific days using CSS selectors (it adds emoji icons for various holidays).

What styles can I not override?

  • If you change the cv-day-number height, you'll need to set the itemTop property so the first item is positioned below the day numbers.
  • If you change the cv-item height, you'll need to set the itemContentHeight and/or itemBorderHeight properties, so each item is positioned below the previous item. Every item must still have the same height.
  • The calendar bases all metrics other than borders (which are in pixels for Reasons) on em units, so if you find the font size is not ideal, it's better to change the calendar's parent font size and let the calendar scale accordingly than to try to manually adjust each element within the calendar. It's generally a good web design practice to limit the number of font sizes in use, which is why everything in the calendar (other than the default header label) uses the same relative font size by default (1.0em).
  • The z-index of the weeks and items are managed using style declarations, they ensure that items for one week don't overlap the next week.

Build Setup

# install dependencies
npm install

# build for production with minification
npm run bundle
Comments
  • Error: WebPack

    Error: WebPack

    Hello, I try to use your calendar but I get:

    ERROR in ./node_modules/babel-loader/lib?{"cacheDirectory":true,"presets":[["env",{"modules":false,"targets":{"browsers":["> 2%"],"uglify":true}}]],"plugins":["transform-object-rest-spread",["transform-runtime",{"polyfill":false,"helpers":false}]]}!./node_modules/vue-loader/lib/selector.js?type=script&index=0&bustCache!./node_modules/vue-simple-calendar/src/components/calendar-month.vue Module build failed: Error: Couldn't find preset "stage-2" relative to directory "C:\\Users\\umignon\\projects\\capability\\node_modules\\vue-simple-calendar" at C:\Users\umignon\projects\capability\node_modules\babel-core\lib\transformation\file\options\option-manager.js:293:19 at Array.map (<anonymous>) at OptionManager.resolvePresets (C:\Users\umignon\projects\capability\node_modules\babel-core\lib\transformation\file\options\option-manager.js:275:20) at OptionManager.mergePresets (C:\Users\umignon\projects\capability\node_modules\babel-core\lib\transformation\file\options\option-manager.js:264:10) at OptionManager.mergeOptions (C:\Users\umignon\projects\capability\node_modules\babel-core\lib\transformation\file\options\option-manager.js:249:14) at OptionManager.init (C:\Users\umignon\projects\capability\node_modules\babel-core\lib\transformation\file\options\option-manager.js:368:12) at File.initOptions (C:\Users\umignon\projects\capability\node_modules\babel-core\lib\transformation\file\index.js:212:65) at new File (C:\Users\umignon\projects\capability\node_modules\babel-core\lib\transformation\file\index.js:135:24) at Pipeline.transform (C:\Users\umignon\projects\capability\node_modules\babel-core\lib\transformation\pipeline.js:46:16) at transpile (C:\Users\umignon\projects\capability\node_modules\babel-loader\lib\index.js:50:20) at C:\Users\umignon\projects\capability\node_modules\babel-loader\lib\fs-cache.js:118:18 at ReadFileContext.callback (C:\Users\umignon\projects\capability\node_modules\babel-loader\lib\fs-cache.js:31:21) at FSReqWrap.readFileAfterOpen [as oncomplete] (fs.js:421:13) @ ./node_modules/vue-simple-calendar/src/components/calendar-month.vue 8:21-364 @ ./node_modules/babel-loader/lib?{"cacheDirectory":true,"presets":[["env",{"modules":false,"targets":{"browsers":["> 2%"],"uglify":true}}]],"plugins":["transform-object-rest-spread",["transform-runtime",{"polyfill":false,"helpers":false}]]}!./node_modules/vue-loader/lib/selector.js?type=script&index=0&bustCache!./resources/assets/js/components/fullCalendar.vue @ ./resources/assets/js/components/fullCalendar.vue @ ./resources/assets/js/vue.js @ ./resources/assets/js/app.js @ multi ./resources/assets/js/app.js ./resources/assets/sass/app.scss npm ERR! code ELIFECYCLE npm ERR! errno 2 npm ERR! @ development:cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js` npm ERR! Exit status 2 npm ERR! npm ERR! Failed at the @ development script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

    npm ERR! A complete log of this run can be found in: npm ERR! C:\Users\umignon\AppData\Roaming\npm-cache_logs\2017-12-15T14_48_05_818Z-debug.log error Command failed with exit code 2. info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.`

    here is my app.vue:

    <template>
            <calendar-month
                    :show-date="showDate"
                    @setShowDate="setShowDate"
                    class="holiday-us-traditional holiday-us-official"
            />
    </template>
    <script>
        import CalendarMonth from 'vue-simple-calendar/src/components/calendar-month'
        require("vue-simple-calendar/static/css/default.css")
        require("vue-simple-calendar/static/css/holidays-us.css")
    
        export default {
            data: function() {
                return { showDate: new Date() }
            },
            components: {
                CalendarMonth
            },
            methods: {
                setShowDate(d) {
                    this.showDate = d;
                },
            }
        }
    </script>
    <style>
        #app {
            font-family: 'Avenir', Helvetica, Arial, sans-serif;
            color: #2c3e50;
            height: 67vh;
            width: 90vw;
            margin-left: auto;
            margin-right: auto;
        }
    </style>
    

    here is my app.js:

    import Vue from 'vue';
    import app from './components/app.vue';
    
    new Vue({
        el: '#app',
        components: {
            app
        }
    });
    

    Thanks for your replys !

    opened by ghost 10
  • Make the word

    Make the word "Today" in the header a prop

    I'm looking into a multi-lingual use of the calendar, and it seems that "Today" in the header is hardcoded, so this is the only string that does not change when locale is changed. Could this be a prop that can then easily be targeted ?

    feedback requested 
    opened by martinval 9
  • SSR compatibility

    SSR compatibility

    I'd like to get this plugin working with SSR. So far I've been using no-ssr to avoid errors on the server, but I'd like this to work on the server too.

    This is a feature request, I'll be glad to help if needed.

    enhancement help wanted 
    opened by BorjaRafols 9
  • Edge, Initial Render Doesn't Show All Cells

    Edge, Initial Render Doesn't Show All Cells

    I'm using Edge 41.16299.726.0, but users may have experienced the issue on other versions. This may be more of an Edge bug, but I've only seen this behavior in this component.

    On load, some of the days don't show - their events do, and it appears their cells are in the DOM, but graphically they're missing. Doing anything to trigger a re-render causes everything to render correctly. I normally tell the rare Edge user to just hit the Next month button then the Previous month button (i.e. go to the next month then back), which resolves it, but resizing the viewport or even mousing over the elements in the inspector will both resolve it as well (since mousing over it in the inspector will highlight the moused-over control).

    I see the behavior in the demo, and other users have seen the issue as well.

    Normal view, some days not rendered, but all events rendered. day-not-rendered

    Same as above but after a refresh with the inspector open. Note that opening the inspector will resize the viewport and trigger a re-render, making the issue look resolved. Refresh the page after opening the inspector to see the behavior while the inspector is open. day-missing-inspector

    Same as above, but mousing over d23 - note the whole week popped in, but not the other "missing" week. day-visible-inspector

    browser bug 
    opened by pplusawork 8
  • Multiple Vue instances causing problems

    Multiple Vue instances causing problems

    Hello, and thank you for releasing this to the community!

    I've run into a problem where importing this into my app is importing a 2nd instance of Vue and causing conflicts. (A similar issue is described here: https://github.com/euvl/vue-js-modal/issues/21)

    I believe this can be resolved by simplifying your bundle.js to avoid registering the components on a Vue instance, and simply exporting the components, leaving it to end users to register the components with their app's instance of Vue.

    For now I've been able to work around this by importing like so:

    import CalendarView from 'vue-simple-calendar/src/components/CalendarView';
    import CalendarViewHeader from 'vue-simple-calendar/src/components/CalendarViewHeader';
    
    question 
    opened by jmkenz 7
  • Bug when using item specific classes

    Bug when using item specific classes

    In your sample you specify a specific color for an item :

     {
    	id: "e6",
    	startDate: thisMonth(29),
    	title: "Same day 2",
    	classes: "orange",
    }
    

    but the item is shown in default color. Inspecting the created HTML I see that the reason is a wrong class name.

    <div draggable="true" aria-grabbed="false" class="o r a n g e offset1 span1 cv-item" 
    

    I have one more please : Please make ICalendarItem and IHeaderProps items available for Typescript (include them in main.d.ts).

    Anyhow - Vue Simple Calendar is exactly what I need !!

    opened by ChristianRiedl 6
  • Is it possible to use VueSimpleCalendar with SSR?

    Is it possible to use VueSimpleCalendar with SSR?

    I'm using Nuxt v2.15.8. Please, maybe anybody use this plugin with Nuxt? Or could someone tell me how to correctly connect CalendarView to the page? When switching from page to page by nuxt-links, everything works correctly. But when I reload the page with Calendar I got error with message "document is not defined"?

    Снимок экрана 2021-10-31 в 12 18 04

    How can I solve this problem?

    not-a-bug 
    opened by amkatyshev 6
  • RTL support

    RTL support

    Hello Thanks for great calendar.

    I was using this for long time. However, I would like to implement RTL version of your plugin. However, using RTL webpack plugin I am able to render dates in RTL but events are still rendered according to LTR. Please let me know if you are planning to add RTL support. If you have something for me or suggestion I may give PR for this(If i am experieced enough to undestand your code).

    Big Thanks :blush:

    enhancement feedback requested 
    opened by jd-solanki 6
  • Drag and drop events from outside the calendar

    Drag and drop events from outside the calendar

    If would be great if this package could (or does?) support events external to the calendar to be dragged and dropped onto the calendar, mock-up below:

    image

    I fiddled around with this concept for a couple hours but every time I dragged an external event over, the last event that I had dragged and dropped that was already on the calendar would be the event that was placed, instead of the external event.

    Note that the following events were emitted:

    • 'drag-enter-date'
    • 'drag-over-date'
    • 'drop-on-date'

    'drag-start' was not emitted, which makes sense since it's not part of the slotted v-for that generates the events. I'm guessing it's the blocker since the "currentDragEvent" is not being updated/set due to that.

    Looking through past issues, I didn't see this come up but I imagine it would be a useful feature. Would love to hear any thoughts. And happy to provide any additional info on any of the above.

    enhancement 
    opened by TheQuail13 6
  • Week view: events on first day of week are not shown

    Week view: events on first day of week are not shown

    Hello,

    I'm currently encountering a strange bug. Events on the first day of the week are not shown, if the showDate property is set. If it's not set all events are shown correctly. If showDate is set and the date is changed (e.g. go to next week and then back) it works too.

    I created a sandbox to test the behaviour:

    https://codesandbox.io/s/0p88mx5n1v

    With showDate property set (event not shown): image

    Without showDate property (correct behaviour): image

    bug 
    opened by MrSnoozles 6
  • How do I customize header?

    How do I customize header?

    Hi, I'm new to Vue; I've read about using slot and could use it in simple cases. However, in Simple Calendar, I cannot customize header without re-implementing most of it's functionalities like next period, previous period, label (changing structures, adding icons... but buttons won't work). I can't simply pass this.$refs.calendar.headerProps to header slot either. How do I make use of all the functions of Simple Calendar when using header slot?

    not-a-bug documentation 
    opened by kiennguyen1101 6
  • How can we get the selected object from the array?

    How can we get the selected object from the array?

    When I call the related method, all the data comes, but I only want to get the selected object from the array. I just couldn't do it.

    // Component
    <calendar-view :show-date="showDate" :items="items"
             @click-item="sendDataMeeting(items)"
               class="theme-default holiday-us-traditional holiday-us-official">
              <template #header="{ headerProps }">
                            <calendar-view-header :header-props="headerProps" @input="setShowDate" />
                        </template>
                    </calendar-view>
    
    
    // Methods
    
    // Call methods
            sendDataMeeting(items) {
                this.$emit("sendDataMeet", items);
                console.log(items);
            },
    // Data Items
    listMetting() {
    
                axios.get(// this is url)
                    .then((response) => {
       
                        this.items = response.data.dataMeeting.map((meet) => {
                            return {
                                id: meet.id,
                                startDate: meet.meetingDate,
                                endDate : meet.meetingDateFinish,
                                title: meet.meetingTitle'
                            };
                        });
    
                    })
    
            },
    
    
    opened by serkankuyu 0
  • Clicking & URL issues

    Clicking & URL issues

    Clicking does absolutely nothing on items. here is what I have:

    <calendar-view
                    :show-date="showDate"
                    :items="items"
                    @click-item="onClickItem"
                    @click-date="onClickItem"
                    class="theme-default">
                    <template #header="{ headerProps }">
                        <calendar-view-header
                            :header-props="headerProps"
                            @input="setShowDate" />
                    </template>
    </calendar-view>`
    

    here is my methods where I retrieve the items and set them along with the click. Everything is being ignored:

    onClickItem(e){
                alert('')
    },
            async getMonthOrders(){
                let req, res;
                this.ready = false;
                req = await fetch(`${this.$store.state.api}/selectorder?lcCustno=${this.$root.getCustomerNumber()}&lclocation=${this.$route.query.lclocation}&ldfirstdate=${this.$root.moment(this.showDate).format('MM/DD/YYYY')}`);
                res = await req.json();
                this.orders = res;
                this.items = this.orders.map((order,i)=>{
                    return {
                        id:`e${i}`,
                        startDate:order['document date'].date,
                        title:`View #${order['document no_']}`,
                        url:'https://google.com',
                    };
                });
            },
    
    needs-repo 
    opened by cpgb85 1
  • Support drag/drop on mobile devices

    Support drag/drop on mobile devices

    The drag and drop functionality currently does not support touch devices. I haven't looked into whether this is easy enough to do in vanilla JS or if adding a dependency to deal with various platform differences will be more practical.

    May want to support "ghost" positioning during the drag event (i.e., being able to see the other events "open up" the place where the current event would be positioned).

    enhancement help wanted 
    opened by richardtallent 1
  • Prettier optional theme

    Prettier optional theme

    Theming ideas:

    • Keep the default (no theme) CSS as basic as possible.
    • Add a new optional theme that has more pizazz -- holiday background colors, prettier header, etc. Perhaps inspired by Material
    • Add more flexibility to slot or have external class control over the buttons in the header, so calling apps can easily style the navigation to match the feel of the rest of their application.
    enhancement help wanted 
    opened by richardtallent 2
Releases(v6.0.4)
  • v6.0.4(Feb 27, 2022)

  • v6.0.3(Nov 20, 2021)

  • v6.0.2(Nov 3, 2021)

  • v5.0.0(Sep 4, 2020)

  • v4.4.0(May 25, 2020)

    • Fix events showing incorrectly during the week of a DST change in the UK
    • Update dependencies
    • Clean up to use arrow functions for some functions
    • Now passes second argument $event (the native click event) to the onClickDay handler
    • Pass drag/drop events on days back to calling app even if the dragged item is not one of the calendar's items. - This allows developers to drag in elements from other controls, or to handle drag/drop if they use scoped slots for items.
    Source code(tar.gz)
    Source code(zip)
  • 4.2.2(May 2, 2019)

  • 4.2.1(Jan 26, 2019)

  • 4.2.0(Dec 19, 2018)

  • 4.1.0(Oct 5, 2018)

  • 4.0.2(Aug 26, 2018)

    This version brings an important breaking change (headers are now always implemented using a named slot, not just when you want to override the default header), ensuring that header slots are a first-class feature and that custom headers can have full feature parity with the default header.

    This also bundles a few bug fixes over the last few months as I was waiting on vue-cli version 3.0 to arrive (which this component now uses -- yay for vue ui!). I've also enhanced the data passed to headers, and added a new, consistent way for the calendar component to inform the application when the visible date range has changed (so the application can pull events, etc.).

    Source code(tar.gz)
    Source code(zip)
  • 3.0.2(May 17, 2018)

  • 3.0.0(May 6, 2018)

  • 2.2.0(Mar 20, 2018)

  • 2.0(Jan 1, 2018)

    New features include:

    • Dates passed as strings are interpreted using browser local time, not UTC, which prevents the event from showing up on an unexpected date.
    • Optional display of start and/or end times of events, with options for formatting
    • Ability to view more than one month at a time
    • Week view (including multi-week)
    • Year view (including, but not necessarily sanely, multi-year support)
    • New named slot for event
    • All slots now pass back useful properties the caller can bring into their scope
    • The main grid is scrollable if it is too tall for the component
    • Each week is scrollable if its events are too tall for the week's row in the component

    There are some breaking changes, so be sure to read the full documentation before upgrading. They should be mostly minor unless you heavily customized the CSS.

    Source code(tar.gz)
    Source code(zip)
  • 1.7.1(Dec 16, 2017)

  • 1.7.0(Dec 16, 2017)

  • 1.6(Nov 12, 2017)

  • 1.3(May 21, 2017)

Owner
Richard Tallent
Full-stack senior dev, environmental consultant, artist. ❤️ Vue, .NET Core, Excel, MSSQL, genealogy, science, and solving interesting problems.
Richard Tallent
A full 12-Month view calendar made by vue.js.

A full 12-Month view calendar made by vue.js.

null 90 Mar 23, 2021
A simple events calendar for Vue2, no dependencies except Vue2.

vue-event-calendar A simple events calendar for Vue2, no dependencies except Vue2. responsive & mobile first. Live Demo Here 中文文档 Requirements vue: ^2

Geoff Zhu 633 Nov 11, 2022
Simple-calendar-app - Simple Vue calendar (date-picker)

Simple vue calendar (date-picker) Vue Moment.js Installation git clone npm i npm

Dmitry Chinenov 3 Oct 31, 2022
vue calendar fullCalendar. no jquery required. Schedule events management

##vue-fullcalendar Works for Vue2 now. This is a fullCalendar component based on vue.js . No Jquery or fullCalendar.js required. Currently, It only su

Sunny Wang 1.5k Dec 18, 2022
Convenient Vue wrapper for the add to calendar button snippet, which lets you reliably create beautiful buttons, where people can add events to their calendars.

The Add to Calendar Button - optimized for Vue 3 This is a wrapper repository for the popular Add to Calendar Button, making it even more convenient,

Add to Calendar 3 Nov 9, 2022
Contra - Combines GitHub and GitLab contributions calendar to create a single calendar

Welcome to Contra ?? Combines GitHub and GitLab contributions calendar to create

Ahmet Korkmaz 10 Dec 29, 2022
A simple infinite calendar component in Vue 2

vue-infinite-calendar A simple infinite calendar component in Vue 2 Build Setup # install dependencies npm install # serve with hot reload at localho

Rares S 15 Feb 28, 2022
A simple vue calendar component. Base for further development and styling.

vue-simple-calendar A simple vue calendar component with minimal css. A base for further development/styling. DEMO Dependencies date-fns, lodash-es In

Roman Ranniew 6 Jun 15, 2022
A easy-to-use component to show somebody's schedule list to help interview arrangement or visit arrangement.

vue-schedule-board A easy-to-use component to show somebody's schedule list to help interview arrangement or visit arrangement. 中文 To get started, che

jinfang 17 Nov 3, 2022
The smallest Vue.js events handler.

Vuemit Vuemit is a tiny library to work with events on Vue.js. Its aim is to keeping simple the fact of sharing information between any number of comp

Gustavo Ocanto 145 Dec 23, 2022
Simple and clean calendar written in Vue.js

Vuelendar Simple and clean calendar written in Vue.js. Check out full Vuelendar's documentation here. Features Select single date Select range of date

The Codest 76 Oct 5, 2022
A simple and sweet vue.js calendar

vue-sweet-calendar A simple and sweet vue.js calendar Features Showing Multiple-Events Fully customizable How to install npm install vue-sweet-calenda

Mahdi Aryayi 35 Oct 5, 2022
A Simple Calendar With Vue.js

vue_calendar Project setup npm install Compiles and hot-reloads for development npm run serve Compiles and minifies for production npm run build 0.

Роман 5 Aug 25, 2022
A vue component for lunar calendar.

vue-lunar-calendar A vue component for lunar calendar. Uses Moment.js for date operations. This is the Korean lunar calendar. It is different from Chi

Kim WooHyun 70 Aug 20, 2022
A calendar component for Vue.js

calendar Chinese This is a calendar component based on vue.js . support custom content. No dependencies. Currently, It only supports month view. You c

Kylin 47 Aug 16, 2022
vue 2.x calendar component

vue2-calendar vue 2 calendar, datepicker component which supported lunar or date event Live Demo >> This project is not only a vue component, but also

Terry Cai 485 Dec 18, 2022
Vue.js Functional Calendar | Component/Package

Vue Functional Calendar Modern calendar and datepicker module for Vue.js Demo Demo: https://y3jnxov469.codesandbox.io/ Lightweight, high-performance c

Manuk 425 Dec 27, 2022
📅 A feature-rich calendar component, support multiple modes and gesture sliding. For vue 3.0+

?? A calendar component for vue3.0. Support gesture sliding, range selection, according to the week switch...

飞翔的荷兰人 434 Jan 3, 2023
a horizontal calendar component for Vue.js

a horizontal calendar component for Vue.js

jacques 37 Aug 10, 2022