mirror of https://github.com/nextcloud/calendar
basic fullcalendar integration
Signed-off-by: Georg Ehrke <developer@georgehrke.com>
This commit is contained in:
parent
b4dd09f9c6
commit
6126cb4f59
|
@ -1,66 +1,6 @@
|
|||
/**
|
||||
* Calendar App
|
||||
*
|
||||
* @author Raghu Nayyar
|
||||
* @author Georg Ehrke
|
||||
* @copyright 2016 Raghu Nayyar <hey@raghunayyar.com>
|
||||
* @copyright 2016 Georg Ehrke <oc.list@georgehrke.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
.app {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.modal-content #app-sidebar {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#fullcalendar table {
|
||||
white-space: inherit !important;
|
||||
}
|
||||
|
||||
.fc-state-highlight.fc-day-number,
|
||||
#fullcalendar tbody tr,
|
||||
#fullcalendar tbody tr:hover,
|
||||
#fullcalendar tbody tr:focus {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
#popover-container,
|
||||
#importpopover-container {
|
||||
max-height: 0 !important;
|
||||
max-width: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
#fullcalendar .fc-axis,
|
||||
#fullcalendar .fc-day-header {
|
||||
font-size: 100%;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
#fullcalendar td.fc-day.fc-sat,
|
||||
#fullcalendar td.fc-day.fc-sun {
|
||||
background-color: var(--color-background-darker);
|
||||
}
|
||||
|
||||
@import 'calendarlist.scss';
|
||||
@import 'confirmation.scss';
|
||||
|
||||
@import 'datepicker.scss';
|
||||
@import 'eventdialog.scss';
|
||||
@import 'fullcalendar.scss';
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
/**
|
||||
* Calendar App
|
||||
*
|
||||
* @copyright 2016 Raghu Nayyar <hey@raghunayyar.com>
|
||||
* @copyright 2018 Georg Ehrke <oc.list@georgehrke.com>
|
||||
* @copyright 2017 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @author Raghu Nayyar
|
||||
* @author Georg Ehrke
|
||||
* @copyright 2016 Raghu Nayyar <hey@raghunayyar.com>
|
||||
* @copyright 2016 Georg Ehrke <oc.list@georgehrke.com>
|
||||
* @copyright 2017 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
* @author John Molakvoæ
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
|
|
|
@ -334,3 +334,16 @@ button.delete:focus {
|
|||
.dropdown-menu li a:hover {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.modal-content #app-sidebar {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#popover-container,
|
||||
#importpopover-container {
|
||||
max-height: 0 !important;
|
||||
max-width: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,32 @@
|
|||
/**
|
||||
* Calendar App
|
||||
*
|
||||
* @copyright 2018 Georg Ehrke <oc.list@georgehrke.com>
|
||||
*
|
||||
* @author Georg Ehrke
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#app-content {
|
||||
|
||||
padding-top: 50px;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Fullcalendar modifications */
|
||||
|
||||
|
@ -68,3 +97,29 @@
|
|||
.fc-unthemed td.fc-today {
|
||||
background: #fcf8e3 !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#fullcalendar .fc-axis,
|
||||
#fullcalendar .fc-day-header {
|
||||
font-size: 100%;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
#fullcalendar td.fc-day.fc-sat,
|
||||
#fullcalendar td.fc-day.fc-sun {
|
||||
background-color: nc-darken($color-main-background, 3%);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#fullcalendar table {
|
||||
white-space: inherit !important;
|
||||
}
|
||||
|
||||
.fc-state-highlight.fc-day-number,
|
||||
#fullcalendar tbody tr,
|
||||
#fullcalendar tbody tr:hover,
|
||||
#fullcalendar tbody tr:focus {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* Calendar App
|
||||
*
|
||||
* @copyright 2018 Georg Ehrke <oc.list@georgehrke.com>
|
||||
*
|
||||
* @author Georg Ehrke
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
|
@ -4610,7 +4610,7 @@
|
|||
},
|
||||
"fecha": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "http://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz",
|
||||
"integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg=="
|
||||
},
|
||||
"figures": {
|
||||
|
@ -7081,6 +7081,11 @@
|
|||
"verror": "1.10.0"
|
||||
}
|
||||
},
|
||||
"jstz": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jstz/-/jstz-2.1.1.tgz",
|
||||
"integrity": "sha512-8hfl5RD6P7rEeIbzStBz3h4f+BQHfq/ABtoU6gXKQv5OcZhnmrIpG7e1pYaZ8hS9e0mp+bxUj08fnDUbKctYyA=="
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
|
||||
|
@ -7749,18 +7754,11 @@
|
|||
"integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y="
|
||||
},
|
||||
"moment-timezone": {
|
||||
"version": "0.5.21",
|
||||
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.21.tgz",
|
||||
"integrity": "sha512-j96bAh4otsgj3lKydm3K7kdtA3iKf2m6MY2iSYCzCm5a1zmHo1g+aK3068dDEeocLZQIS9kU8bsdQHLqEvgW0A==",
|
||||
"version": "0.5.23",
|
||||
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.23.tgz",
|
||||
"integrity": "sha512-WHFH85DkCfiNMDX5D3X7hpNH3/PUhjTGcD0U1SgfBGZxJ3qUmJh5FdvaFjcClxOvB3rzdfj4oRffbI38jEnC1w==",
|
||||
"requires": {
|
||||
"moment": ">= 2.9.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"moment": {
|
||||
"version": "2.22.2",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz",
|
||||
"integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y="
|
||||
}
|
||||
}
|
||||
},
|
||||
"move-concurrently": {
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
"debounce": "^1.2.0",
|
||||
"fullcalendar": "^4.0.0-alpha.2",
|
||||
"ical.js": "^1.2.2",
|
||||
"jstz": "^2.1.1",
|
||||
"moment": "^2.22.2",
|
||||
"nextcloud-axios": "^0.1.2",
|
||||
"nextcloud-vue": "^0.4.3",
|
||||
|
|
|
@ -25,10 +25,114 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { Calendar } from 'fullcalendar'
|
||||
import '../../node_modules/fullcalendar/dist/fullcalendar.css'
|
||||
import debounce from 'debounce'
|
||||
|
||||
import '../fullcalendar/timeZoneImpl'
|
||||
|
||||
export default {
|
||||
name: 'FullCalendar',
|
||||
mounted: () => {
|
||||
props: {
|
||||
// single events used for new event, etc.
|
||||
events: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
}
|
||||
},
|
||||
// event sources for calendars
|
||||
eventSources: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
}
|
||||
},
|
||||
// config options
|
||||
config: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaultConfig: {
|
||||
// dayNames: [],
|
||||
// dayNamesShort: [],
|
||||
defaultView: 'month',
|
||||
editable: true,
|
||||
firstDay: null,
|
||||
forceEventDuration: true,
|
||||
header: false,
|
||||
// locale: null,
|
||||
// monthNames: [],
|
||||
// monthNamesShort: [],
|
||||
slotDuration: '00:15:00',
|
||||
nowIndicator: true,
|
||||
weekNumbers: false, // TODO is this the default in view controller?
|
||||
weekends: true,
|
||||
eventSources: this.eventSources,
|
||||
timeZone: 'America/New_York',
|
||||
timeZoneImpl: 'vtimezone-timezone',
|
||||
},
|
||||
calendar: null,
|
||||
currentDate: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
events: {
|
||||
deep: true,
|
||||
handler(newValue, oldValue) {
|
||||
|
||||
}
|
||||
},
|
||||
eventSources: {
|
||||
deep: true,
|
||||
handler(newEventSources, oldEventSources) {
|
||||
const toAdd = newEventSources.filter((es) => oldEventSources.find((oes) => es.id === oes.id) === undefined)
|
||||
const toRemove = oldEventSources.filter((oes) => newEventSources.find((es) => es.id === oes.id) === undefined)
|
||||
|
||||
toAdd.forEach((es) => {
|
||||
this.calendar.addEventSource(es)
|
||||
})
|
||||
toRemove.forEach((es) => {
|
||||
this.calendar.getEventSourceById(es.id).remove()
|
||||
})
|
||||
}
|
||||
},
|
||||
config: {
|
||||
deep: true,
|
||||
handler(newValue, oldValue) {
|
||||
|
||||
}
|
||||
},
|
||||
'$route'({ params }) {
|
||||
if (params.view !== this.calendar.getView().type) {
|
||||
this.calendar.changeView(params.view)
|
||||
|
||||
}
|
||||
|
||||
if (params.firstday !== this.currentDate) {
|
||||
this.calendar.gotoDate(params.firstday)
|
||||
this.currentDate = params.firstday
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted: function() {
|
||||
window.addEventListener('resize', debounce(() => {
|
||||
const windowHeight = window.innerHeight
|
||||
const headerHeight = document.getElementById('header').clientHeight
|
||||
|
||||
this.calendar.setOption('height', windowHeight - headerHeight)
|
||||
}, 500))
|
||||
|
||||
const windowHeight = window.innerHeight
|
||||
const headerHeight = document.getElementById('header').clientHeight
|
||||
const height = windowHeight - headerHeight
|
||||
|
||||
this.calendar = new Calendar(this.$el,
|
||||
Object.assign({}, { height }, this.defaultConfig, this.config))
|
||||
this.calendar.render()
|
||||
},
|
||||
beforeDestroy: () => {
|
||||
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2018 Georg Ehrke
|
||||
*
|
||||
* @author Georg Ehrke <oc.list@georgehrke.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
import { NamedTimeZoneImpl, registerNamedTimeZoneImpl } from 'fullcalendar'
|
||||
import { getTimezone } from '../services/timezoneDataProviderService'
|
||||
import ICAL from 'ical.js'
|
||||
|
||||
/**
|
||||
* Our own Fullcalendar Timezone implementation based on the VTimezones we ship
|
||||
*/
|
||||
class VTimezoneNamedTimezone extends NamedTimeZoneImpl {
|
||||
|
||||
/**
|
||||
* gets UTC offset for given date of this timezone
|
||||
*
|
||||
* @param {Number[]} date an array that mirrors the parameters from new Date()
|
||||
* @returns {Number} offset in minutes
|
||||
*/
|
||||
offsetForArray([year, month, day, hour, minute, second]) {
|
||||
const timezone = getTimezone(this.name)
|
||||
month += 1
|
||||
const time = new ICAL.Time({ year, month, day, hour, minute, second, isDate: false })
|
||||
|
||||
// TODO - all these operations require complex RRULE expansion for DST / Standard
|
||||
// this function is called dozens of dozens of times, result should probably be cached
|
||||
|
||||
console.debug(timezone.utcOffset(time) / 60)
|
||||
return timezone.utcOffset(time) / 60
|
||||
}
|
||||
|
||||
/**
|
||||
* returns parameters for Date object in this timezone based on given timestamp
|
||||
*
|
||||
* @param {Number[]} ms Timestamp in milliseconds
|
||||
* @returns {Number[]}
|
||||
*/
|
||||
timestampToArray(ms) {
|
||||
const timezone = getTimezone(this.name)
|
||||
const time = ICAL.Time.fromData({ year: 1970, month: 1, day: 1, hour: 0, minute: 0, second: 0 }) // just create a dummy object because fromUnixTime is not exposed on ICAL.Time
|
||||
time.fromUnixTime(Math.floor(ms / 1000))
|
||||
const local = time.convertToZone(timezone)
|
||||
|
||||
// TODO - all these operations require complex RRULE expansion for DST / Standard
|
||||
// this function is called dozens of dozens of times, result should probably be cached
|
||||
|
||||
console.debug([local.year, local.month - 1, local.day, local.hour, local.minute, local.second, 0])
|
||||
return [local.year, local.month - 1, local.day, local.hour, local.minute, local.second, 0]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
registerNamedTimeZoneImpl('vtimezone-timezone', VTimezoneNamedTimezone)
|
|
@ -1,7 +1,9 @@
|
|||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
import Calendar from './views/Calendar'
|
||||
import Edit from './views/Edit'
|
||||
import EditSimple from './views/EditSimple'
|
||||
import EditSidebar from './views/EditSidebar'
|
||||
|
||||
import { dateFactory, getYYYYMMDDFromDate } from './services/date.js'
|
||||
|
||||
Vue.use(Router)
|
||||
|
@ -13,32 +15,60 @@ const router = new Router({
|
|||
{
|
||||
path: '/',
|
||||
name: 'Root',
|
||||
component: Calendar,
|
||||
redirect: {
|
||||
name: 'View',
|
||||
name: 'CalendarView',
|
||||
params: {
|
||||
view: oca_calendar.initialView,
|
||||
firstday: getYYYYMMDDFromDate(dateFactory())
|
||||
},
|
||||
}
|
||||
},
|
||||
// {
|
||||
// // This route can be used in order to link to events without knowing it's date
|
||||
// path: '/edit/:object',
|
||||
// name: 'EditNoDateNoRecurrenceId',
|
||||
// redirect: {
|
||||
// name: 'Edit',
|
||||
//
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// // This route can be used in order to link to events without knowing it's date
|
||||
// path: '/edit/:object/:recurrenceId',
|
||||
// name: 'EditNoDate',
|
||||
// redirect: {
|
||||
// name: 'Edit',
|
||||
//
|
||||
// }
|
||||
// },
|
||||
{
|
||||
path: '/:view/:firstday',
|
||||
component: Calendar,
|
||||
name: 'CalendarView',
|
||||
children: [
|
||||
// {
|
||||
// path: '',
|
||||
// name: 'CalendarView',
|
||||
// },
|
||||
{
|
||||
path: '/',
|
||||
name: 'View',
|
||||
component: Calendar,
|
||||
path: '/:view/:firstday/edit/popover/:object/:recurrenceId',
|
||||
name: 'EditPopoverView',
|
||||
component: EditSimple,
|
||||
},
|
||||
{
|
||||
path: '/edit/:mode/:object/:recurrenceId',
|
||||
name: 'Edit',
|
||||
component: Edit,
|
||||
path: '/:view/:firstday/edit/sidebar/:object/:recurrenceId',
|
||||
name: 'EditSidebarView',
|
||||
component: EditSidebar,
|
||||
},
|
||||
{
|
||||
path: '/new/:mode/:dtstart/:dtend',
|
||||
name: 'Edit',
|
||||
component: Edit,
|
||||
path: '/:view/:firstday/new/popover/:dtstart/:dtend',
|
||||
name: 'NewPopoverView',
|
||||
component: EditSimple,
|
||||
},
|
||||
{
|
||||
path: '/:view/:firstday/new/sidebar/:dtstart/:dtend',
|
||||
name: 'NewSidebarView',
|
||||
component: EditSidebar,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -26,12 +26,19 @@ const colors = []
|
|||
/**
|
||||
* get the appropriate text color to be used on top of an rgb value
|
||||
*
|
||||
* @param {Number} red decimal value for red
|
||||
* @param {Number|String} red decimal value for red
|
||||
* @param {Number} green decimal value for green
|
||||
* @param {Number} blue decimal value for blue
|
||||
* @returns {string}
|
||||
*/
|
||||
export function generateTextColorFromRGB(red, green, blue) {
|
||||
if (typeof red === 'string') {
|
||||
const { r, g, b } = extractRGBFromHexString(red)
|
||||
red = r
|
||||
green = g
|
||||
blue = b
|
||||
}
|
||||
|
||||
const brightness = (((red * 299) + (green * 587) + (blue * 114)) / 1000)
|
||||
return (brightness > 130) ? '#000000' : '#FAFAFA'
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2018 Georg Ehrke
|
||||
*
|
||||
* @author Georg Ehrke <oc.list@georgehrke.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} calendar calendar object from Vuex store
|
||||
* @returns {Function}
|
||||
*/
|
||||
export default function(calendar) {
|
||||
return ({ start, startStr, end, endStr, timeZone }, callback) => {
|
||||
console.debug(calendar)
|
||||
console.debug(start)
|
||||
console.debug(startStr)
|
||||
console.debug(end)
|
||||
console.debug(endStr)
|
||||
console.debug(timeZone)
|
||||
console.debug(callback)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2018 Georg Ehrke
|
||||
*
|
||||
* @author Georg Ehrke <oc.list@georgehrke.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
import tzData from '../../timezones/zones.json'
|
||||
import ICAL from 'ical.js'
|
||||
|
||||
console.debug(`The calendar app is using version ${tzData.version} of the timezone database`)
|
||||
|
||||
/**
|
||||
* Checks whether the given timezone is a so-called Olsen Timezone
|
||||
* @see https://en.wikipedia.org/wiki/Tz_database
|
||||
*
|
||||
* @param {String} tzName name of timezone
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isOlsonTimezone(tzName) {
|
||||
const hasSlash = tzName.indexOf('/') !== -1
|
||||
const hasSpace = tzName.indexOf(' ') !== -1
|
||||
const startsWithETC = tzName.startsWith('Etc')
|
||||
const startsWithUS = tzName.startsWith('US/')
|
||||
|
||||
return hasSlash && !hasSpace && !startsWithETC && !startsWithUS
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if timezone is just an alias
|
||||
*
|
||||
* @param {String} tzName name of timezone
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isAlias(tzName) {
|
||||
return tzData.aliases.hasOwnProperty(tzName)
|
||||
}
|
||||
|
||||
/**
|
||||
* gets timezone object for given name
|
||||
*
|
||||
* @param {String} tzName name of timezone
|
||||
* @returns {icaltimezone}
|
||||
*/
|
||||
export function getTimezone(tzName) {
|
||||
if (isAlias(tzName)) {
|
||||
return getTimezone(tzData.aliases[tzName])
|
||||
}
|
||||
|
||||
// GMT maps to UTC, so only check UTC
|
||||
if (tzName === 'UTC') {
|
||||
return ICAL.TimezoneService.get('UTC')
|
||||
} else if (tzName === 'floating') {
|
||||
return ICAL.Timezone.localTimezone
|
||||
}
|
||||
|
||||
if (!tzData.zones.hasOwnProperty(tzName)) {
|
||||
throw new Error('Unknown timezone')
|
||||
}
|
||||
|
||||
const ics = tzData.zones[tzName].ics
|
||||
const jCal = ICAL.parse(ics)
|
||||
const components = new ICAL.Component(jCal)
|
||||
if (components.name === 'vtimezone') {
|
||||
return new ICAL.Timezone(components)
|
||||
} else {
|
||||
return new ICAL.Timezone(components.getFirstSubcomponent('vtimezone'))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* lists all timezones available (without aliases)
|
||||
*
|
||||
* @returns {String[]}
|
||||
*/
|
||||
export function listAllTimezones() {
|
||||
const olsonAliases = []
|
||||
tzData.aliases.forEach((value, key) => {
|
||||
if (isOlsonTimezone(key)) {
|
||||
olsonAliases.push(key)
|
||||
}
|
||||
})
|
||||
|
||||
const timezones = Object.keys(tzData.zones).concat(olsonAliases)
|
||||
timezones.sort()
|
||||
|
||||
return timezones
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2018 Georg Ehrke
|
||||
*
|
||||
* @author Georg Ehrke <oc.list@georgehrke.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
import jstz from 'jstz';
|
||||
|
||||
/**
|
||||
* Returns the current timezone of the user
|
||||
*
|
||||
* @returns {String} Current timezone of user
|
||||
*/
|
||||
export default () => jstz.determine().name
|
|
@ -1,39 +1,44 @@
|
|||
<template>
|
||||
<div class="view">
|
||||
<div id="app-content">
|
||||
<!-- Full calendar -->
|
||||
<full-calendar />
|
||||
<full-calendar :events="events" :event-sources="eventSources" :config="config" />
|
||||
<!-- Edit modal -->
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
<script>
|
||||
import FullCalendar from '../components/FullCalendar.vue'
|
||||
import '../../node_modules/fullcalendar/dist/fullcalendar.css'
|
||||
import { generateTextColorFromRGB } from '../services/colorService'
|
||||
import fullCalendarEventService from '../services/fullCalendarEventService'
|
||||
|
||||
export default {
|
||||
name: 'Calendar',
|
||||
components: {
|
||||
FullCalendar
|
||||
},
|
||||
// props: {
|
||||
// view: {
|
||||
// type: String,
|
||||
// required: true,
|
||||
// },
|
||||
// firstday: {
|
||||
// type: String,
|
||||
// required: true,
|
||||
// }
|
||||
// },
|
||||
data() {
|
||||
return {
|
||||
calendar: null
|
||||
computed: {
|
||||
events() {
|
||||
return []
|
||||
},
|
||||
eventSources() {
|
||||
console.debug(this.$store.getters.enabledCalendars)
|
||||
return this.$store.getters.enabledCalendars.map((enabledCalendar) => ({
|
||||
id: enabledCalendar.id,
|
||||
// coloring
|
||||
backgroundColor: enabledCalendar.color,
|
||||
borderColor: enabledCalendar.color,
|
||||
textColor: generateTextColorFromRGB(enabledCalendar.color),
|
||||
// html foo
|
||||
className: enabledCalendar.id,
|
||||
editable: !enabledCalendar.readOnly,
|
||||
|
||||
events: fullCalendarEventService(enabledCalendar),
|
||||
}))
|
||||
},
|
||||
config() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// this.calendar = new Calendar(this.$refs.fullcalendar)
|
||||
// this.calendar.render()
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<template>
|
||||
<div class="editor">
|
||||
<div id="app-sidebar">
|
||||
FOO VAR
|
||||
<!-- title -->
|
||||
<!-- timepicker -->
|
||||
<!-- details -->
|
||||
|
@ -15,6 +16,6 @@
|
|||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'Edit',
|
||||
name: 'EditSidebar',
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,12 @@
|
|||
<template>
|
||||
<div class="editor">
|
||||
<!-- title -->
|
||||
<!-- timepicker -->
|
||||
<!-- details -->
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'EditSimple',
|
||||
}
|
||||
</script>
|
Loading…
Reference in New Issue