Add appointment management with a calendar
This commit is contained in:
20
lib/fullcalendar/list/LICENSE.txt
Normal file
20
lib/fullcalendar/list/LICENSE.txt
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2019 Adam Shaw
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
8
lib/fullcalendar/list/README.md
Normal file
8
lib/fullcalendar/list/README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
# FullCalendar List View Plugin
|
||||
|
||||
View your events as a bulleted list
|
||||
|
||||
[View the docs »](https://fullcalendar.io/docs/list-view)
|
||||
|
||||
This package was created from the [FullCalendar monorepo »](https://github.com/fullcalendar/fullcalendar)
|
||||
118
lib/fullcalendar/list/main.css
Normal file
118
lib/fullcalendar/list/main.css
Normal file
@@ -0,0 +1,118 @@
|
||||
/* List View
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
/* possibly reusable */
|
||||
.fc-event-dot {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
/* view wrapper */
|
||||
.fc-rtl .fc-list-view {
|
||||
direction: rtl;
|
||||
/* unlike core views, leverage browser RTL */
|
||||
}
|
||||
|
||||
.fc-list-view {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
/* table resets */
|
||||
.fc .fc-list-table {
|
||||
table-layout: auto;
|
||||
/* for shrinkwrapping cell content */
|
||||
}
|
||||
|
||||
.fc-list-table td {
|
||||
border-width: 1px 0 0;
|
||||
padding: 8px 14px;
|
||||
}
|
||||
|
||||
.fc-list-table tr:first-child td {
|
||||
border-top-width: 0;
|
||||
}
|
||||
|
||||
/* day headings with the list */
|
||||
.fc-list-heading {
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
.fc-list-heading td {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.fc-ltr .fc-list-heading-main {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.fc-ltr .fc-list-heading-alt {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.fc-rtl .fc-list-heading-main {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.fc-rtl .fc-list-heading-alt {
|
||||
float: left;
|
||||
}
|
||||
|
||||
/* event list items */
|
||||
.fc-list-item.fc-has-url {
|
||||
cursor: pointer;
|
||||
/* whole row will be clickable */
|
||||
}
|
||||
|
||||
.fc-list-item-marker,
|
||||
.fc-list-item-time {
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* make the dot closer to the event title */
|
||||
.fc-ltr .fc-list-item-marker {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.fc-rtl .fc-list-item-marker {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.fc-list-item-title a {
|
||||
/* every event title cell has an <a> tag */
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.fc-list-item-title a[href]:hover {
|
||||
/* hover effect only on titles with hrefs */
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* message when no events */
|
||||
.fc-list-empty-wrap2 {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.fc-list-empty-wrap1 {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: table;
|
||||
}
|
||||
|
||||
.fc-list-empty {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fc-unthemed .fc-list-empty {
|
||||
/* theme will provide own background */
|
||||
background-color: #eee;
|
||||
}
|
||||
36
lib/fullcalendar/list/main.d.ts
vendored
Normal file
36
lib/fullcalendar/list/main.d.ts
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// Generated by dts-bundle v0.7.3-fork.1
|
||||
// Dependencies for this module:
|
||||
// ../../../../../@fullcalendar/core
|
||||
|
||||
declare module '@fullcalendar/list' {
|
||||
import ListView from '@fullcalendar/list/ListView';
|
||||
export { ListView };
|
||||
const _default: import("@fullcalendar/core").PluginDef;
|
||||
export default _default;
|
||||
}
|
||||
|
||||
declare module '@fullcalendar/list/ListView' {
|
||||
import { View, ViewProps, ScrollComponent, DateMarker, DateRange, ComponentContext, EventUiHash, EventRenderRange, EventStore, Seg, ViewSpec } from '@fullcalendar/core';
|
||||
export { ListView as default, ListView };
|
||||
class ListView extends View {
|
||||
scroller: ScrollComponent;
|
||||
contentEl: HTMLElement;
|
||||
dayDates: DateMarker[];
|
||||
constructor(viewSpec: ViewSpec, parentEl: HTMLElement);
|
||||
firstContext(context: ComponentContext): void;
|
||||
render(props: ViewProps, context: ComponentContext): void;
|
||||
destroy(): void;
|
||||
_renderSkeleton(context: ComponentContext): void;
|
||||
_unrenderSkeleton(): void;
|
||||
updateSize(isResize: any, viewHeight: any, isAuto: any): void;
|
||||
computeScrollerHeight(viewHeight: any): number;
|
||||
_eventStoreToSegs(eventStore: EventStore, eventUiBases: EventUiHash, dayRanges: DateRange[]): Seg[];
|
||||
eventRangesToSegs(eventRanges: EventRenderRange[], dayRanges: DateRange[]): any[];
|
||||
eventRangeToSegs(eventRange: EventRenderRange, dayRanges: DateRange[]): any[];
|
||||
renderEmptyMessage(): void;
|
||||
renderSegList(allSegs: any): void;
|
||||
groupSegsByDay(segs: any): any[];
|
||||
buildDayHeaderRow(dayDate: any): HTMLTableRowElement;
|
||||
}
|
||||
}
|
||||
|
||||
348
lib/fullcalendar/list/main.esm.js
Normal file
348
lib/fullcalendar/list/main.esm.js
Normal file
@@ -0,0 +1,348 @@
|
||||
/*!
|
||||
FullCalendar List View Plugin v4.4.2
|
||||
Docs & License: https://fullcalendar.io/
|
||||
(c) 2019 Adam Shaw
|
||||
*/
|
||||
|
||||
import { getAllDayHtml, isMultiDayRange, htmlEscape, FgEventRenderer, memoize, memoizeRendering, ScrollComponent, subtractInnerElHeight, sliceEventStore, intersectRanges, htmlToElement, createFormatter, createElement, buildGotoAnchorHtml, View, startOfDay, addDays, createPlugin } from '@fullcalendar/core';
|
||||
|
||||
/*! *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
***************************************************************************** */
|
||||
/* global Reflect, Promise */
|
||||
|
||||
var extendStatics = function(d, b) {
|
||||
extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
||||
return extendStatics(d, b);
|
||||
};
|
||||
|
||||
function __extends(d, b) {
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
}
|
||||
|
||||
var ListEventRenderer = /** @class */ (function (_super) {
|
||||
__extends(ListEventRenderer, _super);
|
||||
function ListEventRenderer(listView) {
|
||||
var _this = _super.call(this) || this;
|
||||
_this.listView = listView;
|
||||
return _this;
|
||||
}
|
||||
ListEventRenderer.prototype.attachSegs = function (segs) {
|
||||
if (!segs.length) {
|
||||
this.listView.renderEmptyMessage();
|
||||
}
|
||||
else {
|
||||
this.listView.renderSegList(segs);
|
||||
}
|
||||
};
|
||||
ListEventRenderer.prototype.detachSegs = function () {
|
||||
};
|
||||
// generates the HTML for a single event row
|
||||
ListEventRenderer.prototype.renderSegHtml = function (seg) {
|
||||
var _a = this.context, theme = _a.theme, options = _a.options;
|
||||
var eventRange = seg.eventRange;
|
||||
var eventDef = eventRange.def;
|
||||
var eventInstance = eventRange.instance;
|
||||
var eventUi = eventRange.ui;
|
||||
var url = eventDef.url;
|
||||
var classes = ['fc-list-item'].concat(eventUi.classNames);
|
||||
var bgColor = eventUi.backgroundColor;
|
||||
var timeHtml;
|
||||
if (eventDef.allDay) {
|
||||
timeHtml = getAllDayHtml(options);
|
||||
}
|
||||
else if (isMultiDayRange(eventRange.range)) {
|
||||
if (seg.isStart) {
|
||||
timeHtml = htmlEscape(this._getTimeText(eventInstance.range.start, seg.end, false // allDay
|
||||
));
|
||||
}
|
||||
else if (seg.isEnd) {
|
||||
timeHtml = htmlEscape(this._getTimeText(seg.start, eventInstance.range.end, false // allDay
|
||||
));
|
||||
}
|
||||
else { // inner segment that lasts the whole day
|
||||
timeHtml = getAllDayHtml(options);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Display the normal time text for the *event's* times
|
||||
timeHtml = htmlEscape(this.getTimeText(eventRange));
|
||||
}
|
||||
if (url) {
|
||||
classes.push('fc-has-url');
|
||||
}
|
||||
return '<tr class="' + classes.join(' ') + '">' +
|
||||
(this.displayEventTime ?
|
||||
'<td class="fc-list-item-time ' + theme.getClass('widgetContent') + '">' +
|
||||
(timeHtml || '') +
|
||||
'</td>' :
|
||||
'') +
|
||||
'<td class="fc-list-item-marker ' + theme.getClass('widgetContent') + '">' +
|
||||
'<span class="fc-event-dot"' +
|
||||
(bgColor ?
|
||||
' style="background-color:' + bgColor + '"' :
|
||||
'') +
|
||||
'></span>' +
|
||||
'</td>' +
|
||||
'<td class="fc-list-item-title ' + theme.getClass('widgetContent') + '">' +
|
||||
'<a' + (url ? ' href="' + htmlEscape(url) + '"' : '') + '>' +
|
||||
htmlEscape(eventDef.title || '') +
|
||||
'</a>' +
|
||||
'</td>' +
|
||||
'</tr>';
|
||||
};
|
||||
// like "4:00am"
|
||||
ListEventRenderer.prototype.computeEventTimeFormat = function () {
|
||||
return {
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
meridiem: 'short'
|
||||
};
|
||||
};
|
||||
return ListEventRenderer;
|
||||
}(FgEventRenderer));
|
||||
|
||||
/*
|
||||
Responsible for the scroller, and forwarding event-related actions into the "grid".
|
||||
*/
|
||||
var ListView = /** @class */ (function (_super) {
|
||||
__extends(ListView, _super);
|
||||
function ListView(viewSpec, parentEl) {
|
||||
var _this = _super.call(this, viewSpec, parentEl) || this;
|
||||
_this.computeDateVars = memoize(computeDateVars);
|
||||
_this.eventStoreToSegs = memoize(_this._eventStoreToSegs);
|
||||
_this.renderSkeleton = memoizeRendering(_this._renderSkeleton, _this._unrenderSkeleton);
|
||||
var eventRenderer = _this.eventRenderer = new ListEventRenderer(_this);
|
||||
_this.renderContent = memoizeRendering(eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer), [_this.renderSkeleton]);
|
||||
return _this;
|
||||
}
|
||||
ListView.prototype.firstContext = function (context) {
|
||||
context.calendar.registerInteractiveComponent(this, {
|
||||
el: this.el
|
||||
// TODO: make aware that it doesn't do Hits
|
||||
});
|
||||
};
|
||||
ListView.prototype.render = function (props, context) {
|
||||
_super.prototype.render.call(this, props, context);
|
||||
var _a = this.computeDateVars(props.dateProfile), dayDates = _a.dayDates, dayRanges = _a.dayRanges;
|
||||
this.dayDates = dayDates;
|
||||
this.renderSkeleton(context);
|
||||
this.renderContent(context, this.eventStoreToSegs(props.eventStore, props.eventUiBases, dayRanges));
|
||||
};
|
||||
ListView.prototype.destroy = function () {
|
||||
_super.prototype.destroy.call(this);
|
||||
this.renderSkeleton.unrender();
|
||||
this.renderContent.unrender();
|
||||
this.context.calendar.unregisterInteractiveComponent(this);
|
||||
};
|
||||
ListView.prototype._renderSkeleton = function (context) {
|
||||
var theme = context.theme;
|
||||
this.el.classList.add('fc-list-view');
|
||||
var listViewClassNames = (theme.getClass('listView') || '').split(' '); // wish we didn't have to do this
|
||||
for (var _i = 0, listViewClassNames_1 = listViewClassNames; _i < listViewClassNames_1.length; _i++) {
|
||||
var listViewClassName = listViewClassNames_1[_i];
|
||||
if (listViewClassName) { // in case input was empty string
|
||||
this.el.classList.add(listViewClassName);
|
||||
}
|
||||
}
|
||||
this.scroller = new ScrollComponent('hidden', // overflow x
|
||||
'auto' // overflow y
|
||||
);
|
||||
this.el.appendChild(this.scroller.el);
|
||||
this.contentEl = this.scroller.el; // shortcut
|
||||
};
|
||||
ListView.prototype._unrenderSkeleton = function () {
|
||||
// TODO: remove classNames
|
||||
this.scroller.destroy(); // will remove the Grid too
|
||||
};
|
||||
ListView.prototype.updateSize = function (isResize, viewHeight, isAuto) {
|
||||
_super.prototype.updateSize.call(this, isResize, viewHeight, isAuto);
|
||||
this.eventRenderer.computeSizes(isResize);
|
||||
this.eventRenderer.assignSizes(isResize);
|
||||
this.scroller.clear(); // sets height to 'auto' and clears overflow
|
||||
if (!isAuto) {
|
||||
this.scroller.setHeight(this.computeScrollerHeight(viewHeight));
|
||||
}
|
||||
};
|
||||
ListView.prototype.computeScrollerHeight = function (viewHeight) {
|
||||
return viewHeight -
|
||||
subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller
|
||||
};
|
||||
ListView.prototype._eventStoreToSegs = function (eventStore, eventUiBases, dayRanges) {
|
||||
return this.eventRangesToSegs(sliceEventStore(eventStore, eventUiBases, this.props.dateProfile.activeRange, this.context.nextDayThreshold).fg, dayRanges);
|
||||
};
|
||||
ListView.prototype.eventRangesToSegs = function (eventRanges, dayRanges) {
|
||||
var segs = [];
|
||||
for (var _i = 0, eventRanges_1 = eventRanges; _i < eventRanges_1.length; _i++) {
|
||||
var eventRange = eventRanges_1[_i];
|
||||
segs.push.apply(segs, this.eventRangeToSegs(eventRange, dayRanges));
|
||||
}
|
||||
return segs;
|
||||
};
|
||||
ListView.prototype.eventRangeToSegs = function (eventRange, dayRanges) {
|
||||
var _a = this.context, dateEnv = _a.dateEnv, nextDayThreshold = _a.nextDayThreshold;
|
||||
var range = eventRange.range;
|
||||
var allDay = eventRange.def.allDay;
|
||||
var dayIndex;
|
||||
var segRange;
|
||||
var seg;
|
||||
var segs = [];
|
||||
for (dayIndex = 0; dayIndex < dayRanges.length; dayIndex++) {
|
||||
segRange = intersectRanges(range, dayRanges[dayIndex]);
|
||||
if (segRange) {
|
||||
seg = {
|
||||
component: this,
|
||||
eventRange: eventRange,
|
||||
start: segRange.start,
|
||||
end: segRange.end,
|
||||
isStart: eventRange.isStart && segRange.start.valueOf() === range.start.valueOf(),
|
||||
isEnd: eventRange.isEnd && segRange.end.valueOf() === range.end.valueOf(),
|
||||
dayIndex: dayIndex
|
||||
};
|
||||
segs.push(seg);
|
||||
// detect when range won't go fully into the next day,
|
||||
// and mutate the latest seg to the be the end.
|
||||
if (!seg.isEnd && !allDay &&
|
||||
dayIndex + 1 < dayRanges.length &&
|
||||
range.end <
|
||||
dateEnv.add(dayRanges[dayIndex + 1].start, nextDayThreshold)) {
|
||||
seg.end = range.end;
|
||||
seg.isEnd = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return segs;
|
||||
};
|
||||
ListView.prototype.renderEmptyMessage = function () {
|
||||
this.contentEl.innerHTML =
|
||||
'<div class="fc-list-empty-wrap2">' + // TODO: try less wraps
|
||||
'<div class="fc-list-empty-wrap1">' +
|
||||
'<div class="fc-list-empty">' +
|
||||
htmlEscape(this.context.options.noEventsMessage) +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
};
|
||||
// called by ListEventRenderer
|
||||
ListView.prototype.renderSegList = function (allSegs) {
|
||||
var theme = this.context.theme;
|
||||
var segsByDay = this.groupSegsByDay(allSegs); // sparse array
|
||||
var dayIndex;
|
||||
var daySegs;
|
||||
var i;
|
||||
var tableEl = htmlToElement('<table class="fc-list-table ' + theme.getClass('tableList') + '"><tbody></tbody></table>');
|
||||
var tbodyEl = tableEl.querySelector('tbody');
|
||||
for (dayIndex = 0; dayIndex < segsByDay.length; dayIndex++) {
|
||||
daySegs = segsByDay[dayIndex];
|
||||
if (daySegs) { // sparse array, so might be undefined
|
||||
// append a day header
|
||||
tbodyEl.appendChild(this.buildDayHeaderRow(this.dayDates[dayIndex]));
|
||||
daySegs = this.eventRenderer.sortEventSegs(daySegs);
|
||||
for (i = 0; i < daySegs.length; i++) {
|
||||
tbodyEl.appendChild(daySegs[i].el); // append event row
|
||||
}
|
||||
}
|
||||
}
|
||||
this.contentEl.innerHTML = '';
|
||||
this.contentEl.appendChild(tableEl);
|
||||
};
|
||||
// Returns a sparse array of arrays, segs grouped by their dayIndex
|
||||
ListView.prototype.groupSegsByDay = function (segs) {
|
||||
var segsByDay = []; // sparse array
|
||||
var i;
|
||||
var seg;
|
||||
for (i = 0; i < segs.length; i++) {
|
||||
seg = segs[i];
|
||||
(segsByDay[seg.dayIndex] || (segsByDay[seg.dayIndex] = []))
|
||||
.push(seg);
|
||||
}
|
||||
return segsByDay;
|
||||
};
|
||||
// generates the HTML for the day headers that live amongst the event rows
|
||||
ListView.prototype.buildDayHeaderRow = function (dayDate) {
|
||||
var _a = this.context, theme = _a.theme, dateEnv = _a.dateEnv, options = _a.options;
|
||||
var mainFormat = createFormatter(options.listDayFormat); // TODO: cache
|
||||
var altFormat = createFormatter(options.listDayAltFormat); // TODO: cache
|
||||
return createElement('tr', {
|
||||
className: 'fc-list-heading',
|
||||
'data-date': dateEnv.formatIso(dayDate, { omitTime: true })
|
||||
}, '<td class="' + (theme.getClass('tableListHeading') ||
|
||||
theme.getClass('widgetHeader')) + '" colspan="3">' +
|
||||
(mainFormat ?
|
||||
buildGotoAnchorHtml(options, dateEnv, dayDate, { 'class': 'fc-list-heading-main' }, htmlEscape(dateEnv.format(dayDate, mainFormat)) // inner HTML
|
||||
) :
|
||||
'') +
|
||||
(altFormat ?
|
||||
buildGotoAnchorHtml(options, dateEnv, dayDate, { 'class': 'fc-list-heading-alt' }, htmlEscape(dateEnv.format(dayDate, altFormat)) // inner HTML
|
||||
) :
|
||||
'') +
|
||||
'</td>');
|
||||
};
|
||||
return ListView;
|
||||
}(View));
|
||||
ListView.prototype.fgSegSelector = '.fc-list-item'; // which elements accept event actions
|
||||
function computeDateVars(dateProfile) {
|
||||
var dayStart = startOfDay(dateProfile.renderRange.start);
|
||||
var viewEnd = dateProfile.renderRange.end;
|
||||
var dayDates = [];
|
||||
var dayRanges = [];
|
||||
while (dayStart < viewEnd) {
|
||||
dayDates.push(dayStart);
|
||||
dayRanges.push({
|
||||
start: dayStart,
|
||||
end: addDays(dayStart, 1)
|
||||
});
|
||||
dayStart = addDays(dayStart, 1);
|
||||
}
|
||||
return { dayDates: dayDates, dayRanges: dayRanges };
|
||||
}
|
||||
|
||||
var main = createPlugin({
|
||||
views: {
|
||||
list: {
|
||||
class: ListView,
|
||||
buttonTextKey: 'list',
|
||||
listDayFormat: { month: 'long', day: 'numeric', year: 'numeric' } // like "January 1, 2016"
|
||||
},
|
||||
listDay: {
|
||||
type: 'list',
|
||||
duration: { days: 1 },
|
||||
listDayFormat: { weekday: 'long' } // day-of-week is all we need. full date is probably in header
|
||||
},
|
||||
listWeek: {
|
||||
type: 'list',
|
||||
duration: { weeks: 1 },
|
||||
listDayFormat: { weekday: 'long' },
|
||||
listDayAltFormat: { month: 'long', day: 'numeric', year: 'numeric' }
|
||||
},
|
||||
listMonth: {
|
||||
type: 'list',
|
||||
duration: { month: 1 },
|
||||
listDayAltFormat: { weekday: 'long' } // day-of-week is nice-to-have
|
||||
},
|
||||
listYear: {
|
||||
type: 'list',
|
||||
duration: { year: 1 },
|
||||
listDayAltFormat: { weekday: 'long' } // day-of-week is nice-to-have
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default main;
|
||||
export { ListView };
|
||||
356
lib/fullcalendar/list/main.js
Normal file
356
lib/fullcalendar/list/main.js
Normal file
@@ -0,0 +1,356 @@
|
||||
/*!
|
||||
FullCalendar List View Plugin v4.4.2
|
||||
Docs & License: https://fullcalendar.io/
|
||||
(c) 2019 Adam Shaw
|
||||
*/
|
||||
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fullcalendar/core')) :
|
||||
typeof define === 'function' && define.amd ? define(['exports', '@fullcalendar/core'], factory) :
|
||||
(global = global || self, factory(global.FullCalendarList = {}, global.FullCalendar));
|
||||
}(this, function (exports, core) { 'use strict';
|
||||
|
||||
/*! *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
***************************************************************************** */
|
||||
/* global Reflect, Promise */
|
||||
|
||||
var extendStatics = function(d, b) {
|
||||
extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
||||
return extendStatics(d, b);
|
||||
};
|
||||
|
||||
function __extends(d, b) {
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
}
|
||||
|
||||
var ListEventRenderer = /** @class */ (function (_super) {
|
||||
__extends(ListEventRenderer, _super);
|
||||
function ListEventRenderer(listView) {
|
||||
var _this = _super.call(this) || this;
|
||||
_this.listView = listView;
|
||||
return _this;
|
||||
}
|
||||
ListEventRenderer.prototype.attachSegs = function (segs) {
|
||||
if (!segs.length) {
|
||||
this.listView.renderEmptyMessage();
|
||||
}
|
||||
else {
|
||||
this.listView.renderSegList(segs);
|
||||
}
|
||||
};
|
||||
ListEventRenderer.prototype.detachSegs = function () {
|
||||
};
|
||||
// generates the HTML for a single event row
|
||||
ListEventRenderer.prototype.renderSegHtml = function (seg) {
|
||||
var _a = this.context, theme = _a.theme, options = _a.options;
|
||||
var eventRange = seg.eventRange;
|
||||
var eventDef = eventRange.def;
|
||||
var eventInstance = eventRange.instance;
|
||||
var eventUi = eventRange.ui;
|
||||
var url = eventDef.url;
|
||||
var classes = ['fc-list-item'].concat(eventUi.classNames);
|
||||
var bgColor = eventUi.backgroundColor;
|
||||
var timeHtml;
|
||||
if (eventDef.allDay) {
|
||||
timeHtml = core.getAllDayHtml(options);
|
||||
}
|
||||
else if (core.isMultiDayRange(eventRange.range)) {
|
||||
if (seg.isStart) {
|
||||
timeHtml = core.htmlEscape(this._getTimeText(eventInstance.range.start, seg.end, false // allDay
|
||||
));
|
||||
}
|
||||
else if (seg.isEnd) {
|
||||
timeHtml = core.htmlEscape(this._getTimeText(seg.start, eventInstance.range.end, false // allDay
|
||||
));
|
||||
}
|
||||
else { // inner segment that lasts the whole day
|
||||
timeHtml = core.getAllDayHtml(options);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Display the normal time text for the *event's* times
|
||||
timeHtml = core.htmlEscape(this.getTimeText(eventRange));
|
||||
}
|
||||
if (url) {
|
||||
classes.push('fc-has-url');
|
||||
}
|
||||
return '<tr class="' + classes.join(' ') + '">' +
|
||||
(this.displayEventTime ?
|
||||
'<td class="fc-list-item-time ' + theme.getClass('widgetContent') + '">' +
|
||||
(timeHtml || '') +
|
||||
'</td>' :
|
||||
'') +
|
||||
'<td class="fc-list-item-marker ' + theme.getClass('widgetContent') + '">' +
|
||||
'<span class="fc-event-dot"' +
|
||||
(bgColor ?
|
||||
' style="background-color:' + bgColor + '"' :
|
||||
'') +
|
||||
'></span>' +
|
||||
'</td>' +
|
||||
'<td class="fc-list-item-title ' + theme.getClass('widgetContent') + '">' +
|
||||
'<a' + (url ? ' href="' + core.htmlEscape(url) + '"' : '') + '>' +
|
||||
core.htmlEscape(eventDef.title || '') +
|
||||
'</a>' +
|
||||
'</td>' +
|
||||
'</tr>';
|
||||
};
|
||||
// like "4:00am"
|
||||
ListEventRenderer.prototype.computeEventTimeFormat = function () {
|
||||
return {
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
meridiem: 'short'
|
||||
};
|
||||
};
|
||||
return ListEventRenderer;
|
||||
}(core.FgEventRenderer));
|
||||
|
||||
/*
|
||||
Responsible for the scroller, and forwarding event-related actions into the "grid".
|
||||
*/
|
||||
var ListView = /** @class */ (function (_super) {
|
||||
__extends(ListView, _super);
|
||||
function ListView(viewSpec, parentEl) {
|
||||
var _this = _super.call(this, viewSpec, parentEl) || this;
|
||||
_this.computeDateVars = core.memoize(computeDateVars);
|
||||
_this.eventStoreToSegs = core.memoize(_this._eventStoreToSegs);
|
||||
_this.renderSkeleton = core.memoizeRendering(_this._renderSkeleton, _this._unrenderSkeleton);
|
||||
var eventRenderer = _this.eventRenderer = new ListEventRenderer(_this);
|
||||
_this.renderContent = core.memoizeRendering(eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer), [_this.renderSkeleton]);
|
||||
return _this;
|
||||
}
|
||||
ListView.prototype.firstContext = function (context) {
|
||||
context.calendar.registerInteractiveComponent(this, {
|
||||
el: this.el
|
||||
// TODO: make aware that it doesn't do Hits
|
||||
});
|
||||
};
|
||||
ListView.prototype.render = function (props, context) {
|
||||
_super.prototype.render.call(this, props, context);
|
||||
var _a = this.computeDateVars(props.dateProfile), dayDates = _a.dayDates, dayRanges = _a.dayRanges;
|
||||
this.dayDates = dayDates;
|
||||
this.renderSkeleton(context);
|
||||
this.renderContent(context, this.eventStoreToSegs(props.eventStore, props.eventUiBases, dayRanges));
|
||||
};
|
||||
ListView.prototype.destroy = function () {
|
||||
_super.prototype.destroy.call(this);
|
||||
this.renderSkeleton.unrender();
|
||||
this.renderContent.unrender();
|
||||
this.context.calendar.unregisterInteractiveComponent(this);
|
||||
};
|
||||
ListView.prototype._renderSkeleton = function (context) {
|
||||
var theme = context.theme;
|
||||
this.el.classList.add('fc-list-view');
|
||||
var listViewClassNames = (theme.getClass('listView') || '').split(' '); // wish we didn't have to do this
|
||||
for (var _i = 0, listViewClassNames_1 = listViewClassNames; _i < listViewClassNames_1.length; _i++) {
|
||||
var listViewClassName = listViewClassNames_1[_i];
|
||||
if (listViewClassName) { // in case input was empty string
|
||||
this.el.classList.add(listViewClassName);
|
||||
}
|
||||
}
|
||||
this.scroller = new core.ScrollComponent('hidden', // overflow x
|
||||
'auto' // overflow y
|
||||
);
|
||||
this.el.appendChild(this.scroller.el);
|
||||
this.contentEl = this.scroller.el; // shortcut
|
||||
};
|
||||
ListView.prototype._unrenderSkeleton = function () {
|
||||
// TODO: remove classNames
|
||||
this.scroller.destroy(); // will remove the Grid too
|
||||
};
|
||||
ListView.prototype.updateSize = function (isResize, viewHeight, isAuto) {
|
||||
_super.prototype.updateSize.call(this, isResize, viewHeight, isAuto);
|
||||
this.eventRenderer.computeSizes(isResize);
|
||||
this.eventRenderer.assignSizes(isResize);
|
||||
this.scroller.clear(); // sets height to 'auto' and clears overflow
|
||||
if (!isAuto) {
|
||||
this.scroller.setHeight(this.computeScrollerHeight(viewHeight));
|
||||
}
|
||||
};
|
||||
ListView.prototype.computeScrollerHeight = function (viewHeight) {
|
||||
return viewHeight -
|
||||
core.subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller
|
||||
};
|
||||
ListView.prototype._eventStoreToSegs = function (eventStore, eventUiBases, dayRanges) {
|
||||
return this.eventRangesToSegs(core.sliceEventStore(eventStore, eventUiBases, this.props.dateProfile.activeRange, this.context.nextDayThreshold).fg, dayRanges);
|
||||
};
|
||||
ListView.prototype.eventRangesToSegs = function (eventRanges, dayRanges) {
|
||||
var segs = [];
|
||||
for (var _i = 0, eventRanges_1 = eventRanges; _i < eventRanges_1.length; _i++) {
|
||||
var eventRange = eventRanges_1[_i];
|
||||
segs.push.apply(segs, this.eventRangeToSegs(eventRange, dayRanges));
|
||||
}
|
||||
return segs;
|
||||
};
|
||||
ListView.prototype.eventRangeToSegs = function (eventRange, dayRanges) {
|
||||
var _a = this.context, dateEnv = _a.dateEnv, nextDayThreshold = _a.nextDayThreshold;
|
||||
var range = eventRange.range;
|
||||
var allDay = eventRange.def.allDay;
|
||||
var dayIndex;
|
||||
var segRange;
|
||||
var seg;
|
||||
var segs = [];
|
||||
for (dayIndex = 0; dayIndex < dayRanges.length; dayIndex++) {
|
||||
segRange = core.intersectRanges(range, dayRanges[dayIndex]);
|
||||
if (segRange) {
|
||||
seg = {
|
||||
component: this,
|
||||
eventRange: eventRange,
|
||||
start: segRange.start,
|
||||
end: segRange.end,
|
||||
isStart: eventRange.isStart && segRange.start.valueOf() === range.start.valueOf(),
|
||||
isEnd: eventRange.isEnd && segRange.end.valueOf() === range.end.valueOf(),
|
||||
dayIndex: dayIndex
|
||||
};
|
||||
segs.push(seg);
|
||||
// detect when range won't go fully into the next day,
|
||||
// and mutate the latest seg to the be the end.
|
||||
if (!seg.isEnd && !allDay &&
|
||||
dayIndex + 1 < dayRanges.length &&
|
||||
range.end <
|
||||
dateEnv.add(dayRanges[dayIndex + 1].start, nextDayThreshold)) {
|
||||
seg.end = range.end;
|
||||
seg.isEnd = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return segs;
|
||||
};
|
||||
ListView.prototype.renderEmptyMessage = function () {
|
||||
this.contentEl.innerHTML =
|
||||
'<div class="fc-list-empty-wrap2">' + // TODO: try less wraps
|
||||
'<div class="fc-list-empty-wrap1">' +
|
||||
'<div class="fc-list-empty">' +
|
||||
core.htmlEscape(this.context.options.noEventsMessage) +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
};
|
||||
// called by ListEventRenderer
|
||||
ListView.prototype.renderSegList = function (allSegs) {
|
||||
var theme = this.context.theme;
|
||||
var segsByDay = this.groupSegsByDay(allSegs); // sparse array
|
||||
var dayIndex;
|
||||
var daySegs;
|
||||
var i;
|
||||
var tableEl = core.htmlToElement('<table class="fc-list-table ' + theme.getClass('tableList') + '"><tbody></tbody></table>');
|
||||
var tbodyEl = tableEl.querySelector('tbody');
|
||||
for (dayIndex = 0; dayIndex < segsByDay.length; dayIndex++) {
|
||||
daySegs = segsByDay[dayIndex];
|
||||
if (daySegs) { // sparse array, so might be undefined
|
||||
// append a day header
|
||||
tbodyEl.appendChild(this.buildDayHeaderRow(this.dayDates[dayIndex]));
|
||||
daySegs = this.eventRenderer.sortEventSegs(daySegs);
|
||||
for (i = 0; i < daySegs.length; i++) {
|
||||
tbodyEl.appendChild(daySegs[i].el); // append event row
|
||||
}
|
||||
}
|
||||
}
|
||||
this.contentEl.innerHTML = '';
|
||||
this.contentEl.appendChild(tableEl);
|
||||
};
|
||||
// Returns a sparse array of arrays, segs grouped by their dayIndex
|
||||
ListView.prototype.groupSegsByDay = function (segs) {
|
||||
var segsByDay = []; // sparse array
|
||||
var i;
|
||||
var seg;
|
||||
for (i = 0; i < segs.length; i++) {
|
||||
seg = segs[i];
|
||||
(segsByDay[seg.dayIndex] || (segsByDay[seg.dayIndex] = []))
|
||||
.push(seg);
|
||||
}
|
||||
return segsByDay;
|
||||
};
|
||||
// generates the HTML for the day headers that live amongst the event rows
|
||||
ListView.prototype.buildDayHeaderRow = function (dayDate) {
|
||||
var _a = this.context, theme = _a.theme, dateEnv = _a.dateEnv, options = _a.options;
|
||||
var mainFormat = core.createFormatter(options.listDayFormat); // TODO: cache
|
||||
var altFormat = core.createFormatter(options.listDayAltFormat); // TODO: cache
|
||||
return core.createElement('tr', {
|
||||
className: 'fc-list-heading',
|
||||
'data-date': dateEnv.formatIso(dayDate, { omitTime: true })
|
||||
}, '<td class="' + (theme.getClass('tableListHeading') ||
|
||||
theme.getClass('widgetHeader')) + '" colspan="3">' +
|
||||
(mainFormat ?
|
||||
core.buildGotoAnchorHtml(options, dateEnv, dayDate, { 'class': 'fc-list-heading-main' }, core.htmlEscape(dateEnv.format(dayDate, mainFormat)) // inner HTML
|
||||
) :
|
||||
'') +
|
||||
(altFormat ?
|
||||
core.buildGotoAnchorHtml(options, dateEnv, dayDate, { 'class': 'fc-list-heading-alt' }, core.htmlEscape(dateEnv.format(dayDate, altFormat)) // inner HTML
|
||||
) :
|
||||
'') +
|
||||
'</td>');
|
||||
};
|
||||
return ListView;
|
||||
}(core.View));
|
||||
ListView.prototype.fgSegSelector = '.fc-list-item'; // which elements accept event actions
|
||||
function computeDateVars(dateProfile) {
|
||||
var dayStart = core.startOfDay(dateProfile.renderRange.start);
|
||||
var viewEnd = dateProfile.renderRange.end;
|
||||
var dayDates = [];
|
||||
var dayRanges = [];
|
||||
while (dayStart < viewEnd) {
|
||||
dayDates.push(dayStart);
|
||||
dayRanges.push({
|
||||
start: dayStart,
|
||||
end: core.addDays(dayStart, 1)
|
||||
});
|
||||
dayStart = core.addDays(dayStart, 1);
|
||||
}
|
||||
return { dayDates: dayDates, dayRanges: dayRanges };
|
||||
}
|
||||
|
||||
var main = core.createPlugin({
|
||||
views: {
|
||||
list: {
|
||||
class: ListView,
|
||||
buttonTextKey: 'list',
|
||||
listDayFormat: { month: 'long', day: 'numeric', year: 'numeric' } // like "January 1, 2016"
|
||||
},
|
||||
listDay: {
|
||||
type: 'list',
|
||||
duration: { days: 1 },
|
||||
listDayFormat: { weekday: 'long' } // day-of-week is all we need. full date is probably in header
|
||||
},
|
||||
listWeek: {
|
||||
type: 'list',
|
||||
duration: { weeks: 1 },
|
||||
listDayFormat: { weekday: 'long' },
|
||||
listDayAltFormat: { month: 'long', day: 'numeric', year: 'numeric' }
|
||||
},
|
||||
listMonth: {
|
||||
type: 'list',
|
||||
duration: { month: 1 },
|
||||
listDayAltFormat: { weekday: 'long' } // day-of-week is nice-to-have
|
||||
},
|
||||
listYear: {
|
||||
type: 'list',
|
||||
duration: { year: 1 },
|
||||
listDayAltFormat: { weekday: 'long' } // day-of-week is nice-to-have
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
exports.ListView = ListView;
|
||||
exports.default = main;
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
}));
|
||||
1
lib/fullcalendar/list/main.min.css
vendored
Normal file
1
lib/fullcalendar/list/main.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.fc-event-dot{display:inline-block;width:10px;height:10px;border-radius:5px}.fc-rtl .fc-list-view{direction:rtl}.fc-list-view{border-width:1px;border-style:solid}.fc .fc-list-table{table-layout:auto}.fc-list-table td{border-width:1px 0 0;padding:8px 14px}.fc-list-table tr:first-child td{border-top-width:0}.fc-list-heading{border-bottom-width:1px}.fc-list-heading td{font-weight:700}.fc-ltr .fc-list-heading-main{float:left}.fc-ltr .fc-list-heading-alt,.fc-rtl .fc-list-heading-main{float:right}.fc-rtl .fc-list-heading-alt{float:left}.fc-list-item.fc-has-url{cursor:pointer}.fc-list-item-marker,.fc-list-item-time{white-space:nowrap;width:1px}.fc-ltr .fc-list-item-marker{padding-right:0}.fc-rtl .fc-list-item-marker{padding-left:0}.fc-list-item-title a{text-decoration:none;color:inherit}.fc-list-item-title a[href]:hover{text-decoration:underline}.fc-list-empty-wrap2{position:absolute;top:0;left:0;right:0;bottom:0}.fc-list-empty-wrap1{width:100%;height:100%;display:table}.fc-list-empty{display:table-cell;vertical-align:middle;text-align:center}.fc-unthemed .fc-list-empty{background-color:#eee}
|
||||
6
lib/fullcalendar/list/main.min.js
vendored
Normal file
6
lib/fullcalendar/list/main.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
33
lib/fullcalendar/list/package.json
Normal file
33
lib/fullcalendar/list/package.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "@fullcalendar/list",
|
||||
"version": "4.4.2",
|
||||
"title": "FullCalendar List View Plugin",
|
||||
"description": "View your events as a bulleted list",
|
||||
"keywords": [
|
||||
"calendar",
|
||||
"event",
|
||||
"full-sized"
|
||||
],
|
||||
"homepage": "https://fullcalendar.io/",
|
||||
"docs": "https://fullcalendar.io/docs/list-view",
|
||||
"bugs": "https://fullcalendar.io/reporting-bugs",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fullcalendar/fullcalendar.git",
|
||||
"homepage": "https://github.com/fullcalendar/fullcalendar"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": {
|
||||
"name": "Adam Shaw",
|
||||
"email": "arshaw@arshaw.com",
|
||||
"url": "http://arshaw.com/"
|
||||
},
|
||||
"copyright": "2019 Adam Shaw",
|
||||
"peerDependencies": {
|
||||
"@fullcalendar/core": "~4.4.0"
|
||||
},
|
||||
"main": "main.js",
|
||||
"module": "main.esm.js",
|
||||
"unpkg": "main.min.js",
|
||||
"types": "main.d.ts"
|
||||
}
|
||||
Reference in New Issue
Block a user