{% extends "component-demo-base.html" %} {% set TAG_NAME="x-calendar" %} {% set COMPONENT_NAME="calendar" %} {% block demoContent %}
Provides a simple calendar UI widget.
Note that when the API works with datestrings, while it's best to pass in "YYYY-MM-DD" ISO strings to the calendar, the <x-calendar>
will also accept any string parseable by Date.parse()
. However, be aware that browser implementations of Date.parse()
may differ slightly.
<x-calendar></x-calendar>
To add a simple set of navigation controls to the calendar, simply add the controls
attribute to the <x-calendar>
<x-calendar controls></x-calendar>
view
While calendars default to focusing on the current day, by setting a date in the view
attribute of the <x-calendar>
, users can specify the initial viewport of the calendar.
<x-calendar view="2013-06-09" controls></x-calendar>
chosen/.chosen
While the user can directly select a date on the calendar, the calendar can also be initialized with a starting chosen date.
The chosen
attribute is also accessible with the the .chosen
property.
<x-calendar chosen="2012-05-17" controls></x-calendar>
// assume calendar is already defined to be the
// <x-calendar> DOM node
calendar.chosen = "2013-05-17";
// ...or...
calendar.chosen = "May 17, 2013";
// ...or...
// remember that Date() months start at 0, not 1
calendar.chosen = new Date(2013, 4, 17);
multiple
While the calendar defaults to only allowing one chosen date at a time, when the multiple
attribute is applied, multiple dates can be chosen at once. (Try dragging the cursor around to paint over dates.)
In addition, this allows the chosen
attribute to take more than one date. To do so, pass in a JSON string for an array whose elements are either: strings signifying single dates, or two-element arrays of strings signifying the start and end of a date range.
Important note: Because these must be JSON strings, make sure that all quoted strings in the attribute string are using double quotes, not single quotes!
If assigning to the calendar's .chosen
property instead, the user can provide the actual array instead of a JSON string, and Date
objects may be used instead of strings.
The multiple
attribute is also accessible with the the .multiple
property.
<x-calendar multiple controls
chosen='[["2013-06-04", "2013-06-10"], "2013-06-19", ["2013-06-29","2013-07-02"]]'>
</x-calendar>
// assume calendar is already defined to be the
// <x-calendar> DOM node
calendar.chosen = '[["2013-06-04", "2013-06-10"], "2013-06-19", ["2013-06-29","2013-07-02"]]';
// ...or...
calendar.chosen = [["2013-06-04", "2013-06-10"], "2013-06-19", ["2013-06-29","2013-07-02"]];
// ...or...
// remember that Date() months start at 0, not 1
calendar.chosen = [[new Date(2013, 5, 4), new Date(2013, 5, 10)],
new Date(2013, 5, 19),
[new Date(2013, 5, 29), new Date(2013, 6, 2)]
];
notoggle
By default, the <calendar>
allows the user to select dates by clicking individual days.
However, if the notoggle
attribute is provided, the calendar disables choosing dates through the interface. (This does not prevent developers from programmatically changing the chosen dates.)
This prevents datetoggleon
and datetoggleoff
events from being triggered normally through the interface, but datetap
events are unaffected. (See events demo for details on these events)
<x-calendar notoggle controls></x-calendar>
span
By default, the calendar only shows one month at a time, but by providing a number to the span
attribute, the calendar can display as many months as the user needs.
The span
attribute is also accessible with the the .span
property.
<x-calendar controls span=2></x-calendar>
first-weekday-num/.firstWeekdayNum
By default, the calendar uses Sunday as the first day of the week. However, in many regions, this may not be the case, so the <x-calendar>
API provides the first-weekday-num
attribute to change which day starts the week.
The number passed to first-weekday-num
should be a number between 0 to 6, where 0=Sunday, 1=Monday, etc.
The first-weekday-num
attribute is also accessible with the the .firstWeekdayNum
property.
<x-calendar controls first-weekday-num=1></x-calendar>
.labels
While the calendar defaults to using English labels, these labels can be edited by the user by editing the .labels
property of the calendar.
When changing labels, the given data should be a JS object that can contain any of the following key:value pairs:
{ "prev": a string to display on the previous-month navigation button, "next": a string to display on the next-month navigation button, "months": an array of 12 strings, where the first string corresponds to January, the second to February, etc, all the way up to December., "weekdays": an array of 7 strings, where the first string corresponds to Sunday, the second to Monday, etc, all the way up to Saturday. }
If the new data given does not have any of these keys, that corresponding particular label will remain unchanged.
For example, we can use this to provide a French-language calendar.
<x-calendar controls first-weekday-num=1></x-calendar>
var frenchCal = document.querySelector("x-calendar[lang=fr]");
frenchCal.labels = {
prev: "<<",
next: ">>",
months: ["janvier", "f\u00E9vrier", "mars", "avril", "mai",
"juin", "juillet", "ao\u00FBt", "septembre", "octobre",
"novembre", "d\u00E9cembre"],
weekdays: ["dim", "lun", "mar", "mer", "jeu", "ven", "sam"]
};
To provide API hooks for developers, the <x-calendar>
fires several different events as it is interacted with:
datetoggleon
events are fired when a date is first chosen/turned on. This event also provides the following information in e.detail
:
{ 'date': the Date object corresponding to the toggled date, 'iso': the ISO-formatted string representing the toggled date }
datetoggleoff
events are similar to datetoggleoff
, but are fired when a date is unchosen/turned off. This event also provides the following information in e.detail
:
{ 'date': the Date object corresponding to the toggled date, 'iso': the ISO-formatted string representing the toggled date }
datetap
events are fired when the user taps a day without dragging/painting over other dates. The event also receives the following custom datamap in e.detail
:
{ 'date': the Date object corresponding to the tapped date, 'iso': the ISO-formatted string representing the tapped date }
prevmonth
and nextmonth
events are fired whenever the user uses the calendar's navigation controls to move back or forward a month, respectively.<x-calendar controls multiple></x-calendar>
Due to the number of elements in the calendar, utilize any of the following CSS selectors to customize the calendar's appearance:
x-calendar
.x-calendar .month-label
.
x-calendar .month
.
x-calendar
.x-calendar .week
.x-calendar .day
.x-calendar .weekday-labels
.x-calendar .weekday-label
.x-calendar .day.today
.x-calendar .day.badmonth
x-calendar .day.chosen
x-calendar .prev
and x-calendar .next
.x-calendar[active]
selector.
x-calendar[active] .day[active]
<x-calendar id="custom-style-cal" controls></x-calendar>
#custom-style-cal{top
background-color: lightsteelblue;
}
#custom-style-cal .month-label{
background-color: rgba(255,255,255,.3);
}
/* use height and line-height to
* modify the height of weeks */
#custom-style-cal .week{
height: 1.9em;
line-height: 1.9em;
}
#custom-style-cal .day{
font-size: .85em;
border-radius: 50%;
border: 1px solid #fff;
box-shadow: inset 1px 1px 4px grey;
}
/* use :not selectors to let default colors
* fall through in those cases */
#custom-style-cal .day:not(:hover):not(.chosen){
background-color: #fff;
}
#custom-style-cal .day.today{
border-color: limegreen;
}
.customRenderFn
In some cases, a developer may want additional ability to style the calendar dynamically beyond the default behavior. To help faciltate this, the x-calendar
can take an additional callback function through the .customRenderFn
property to call on each displayed day whenever the calendar is rerendered.
The callback function will be called with three parameters: the day's DOM element, the JavaScript Date object corresponding to the day, and the ISO-formatted string version of the date.
Important note: Because this callback function is intended to be run whenever the calendar rerenders, the function should not do anything to cause a rerender of the calendar. In other words, avoid modifying the calendar's attributes directly.
As a simple example, this can be used to selectively apply styles based on date information.
<x-calendar id="custom-render-simple" controls></x-calendar>
*[dance-time]{
background-image: url("grounds_keeping_it_real_s1.gif");
color: white;
text-shadow: 1px 1px 4px grey;
}
var cal = document.getElementById("custom-render-simple");
cal.customRenderFn = function(dayEl, date, iso){
// add selector to every 5th day in a month
if(date.getDate() % 5 === 0){
dayEl.setAttribute("dance-time", true);
}
else{
dayEl.removeAttribute("dance-time");
}
};
This is a simple example of how to use the <x-calendar>
component's API to create a simple widget that can store and retrieve day-specific event data.
<figure id="scheduler-demo">
<x-calendar controls notoggle></x-calendar>
<span>Selected Date: <span id="scheduler-date">None</span></span>
<textarea id="scheduler-info"></textarea>
<button id="scheduler-save" disabled>Update info</button>
</figure>
#scheduler-demo > *:not(x-calendar){
margin: 0 auto;
display: block;
}
/* give the current day a sunken look */
#scheduler-demo > x-calendar .scheduler-current{
box-shadow: inset 1px 1px 4px grey;
background-color: rgba(65,105,225,0.5);
}
/* days with data have a border */
#scheduler-demo > x-calendar [scheduler-has-info]{
border-color: royalblue;
border-style: dashed;
}
/* days with data also have an icon */
#scheduler-demo > x-calendar [scheduler-has-info]:after{
content: "";
position: absolute;
background-color: royalblue;
right: 0%;
bottom: 0%;
width: 25%;
height: 25%;
border-radius: 50%;
-webkit-transform: translate(50%, 50%);
-moz-transform: translate(50%, 50%);
-o-transform: translate(50%, 50%);
transform: translate(50%, 50%);
}
/** set up custom event handling demo using provided API hooks **/
// the dictionary of iso datestring keys mapped to event data
var DATE_INFO = {};
// the currently displayed date
var CURR_ISO = null;
// aliases for DOM manipulation
var eventsStage = document.getElementById("scheduler-demo");
var eventsCal = eventsStage.querySelector("x-calendar");
var eventsDateHeader = document.getElementById("scheduler-date");
var eventsInfo = document.getElementById("scheduler-info");
var eventsSaveButton = document.getElementById("scheduler-save");
// define a .customRenderFn that provides extra styling
// information for day elements with stored data
eventsCal.customRenderFn = function(dayEl, date, isoStr){
if(isoStr === CURR_ISO){
xtag.addClass(dayEl, "scheduler-current");
}
else{
xtag.removeClass(dayEl, "scheduler-current");
}
if(isoStr in DATE_INFO){
dayEl.setAttribute("scheduler-has-info", true);
}
else{
dayEl.removeAttribute("scheduler-has-info");
}
}
// respond to calendar taps
eventsCal.addEventListener("datetap", function(e){
// grab date info from event as both a Date and a string
var date = e.detail.date;
var dateStr = e.detail.iso;
// get dictionary content for this date
var content = (dateStr && dateStr in DATE_INFO) ?
DATE_INFO[dateStr] : "";
// set up text area
eventsInfo.value = content;
eventsInfo.disabled = !dateStr;
eventsSaveButton.disabled = !dateStr;
// remember currently shown date
CURR_ISO = dateStr;
eventsDateHeader.textContent = (dateStr) ? dateStr : "None";
// programmatically toggle date object with .toggleDateOn
eventsCal.toggleDateOn(date);
// forces rerender of calendar
eventsCal.render();
});
// save button listener; simply adds textarea value to data
eventsSaveButton.addEventListener("click", function(e){
if(CURR_ISO){
if(eventsInfo.value){
DATE_INFO[CURR_ISO] = eventsInfo.value;
}
else{
delete DATE_INFO[CURR_ISO];
}
}
eventsCal.render();
});