From 944ea92cefebd1e22abebb161338d768f60469a7 Mon Sep 17 00:00:00 2001 From: romankko2 Date: Mon, 28 Nov 2022 12:40:23 +0100 Subject: [PATCH] vue-cal --- .../app@vendor/vue-cal/all-day-bar.vue | 47 + app/src/plugins/app@vendor/vue-cal/cell.vue | 615 ++++++ app/src/plugins/app@vendor/vue-cal/event.vue | 426 +++++ app/src/plugins/app@vendor/vue-cal/header.vue | 284 +++ .../plugins/app@vendor/vue-cal/i18n/ar.json | 15 + .../plugins/app@vendor/vue-cal/i18n/bg.json | 15 + .../plugins/app@vendor/vue-cal/i18n/bn.json | 15 + .../plugins/app@vendor/vue-cal/i18n/bs.json | 15 + .../plugins/app@vendor/vue-cal/i18n/ca.json | 16 + .../plugins/app@vendor/vue-cal/i18n/cs.json | 15 + .../plugins/app@vendor/vue-cal/i18n/da.json | 15 + .../plugins/app@vendor/vue-cal/i18n/de.json | 15 + .../plugins/app@vendor/vue-cal/i18n/el.json | 18 + .../plugins/app@vendor/vue-cal/i18n/en.json | 15 + .../plugins/app@vendor/vue-cal/i18n/es.json | 15 + .../plugins/app@vendor/vue-cal/i18n/et.json | 15 + .../plugins/app@vendor/vue-cal/i18n/fa.json | 15 + .../plugins/app@vendor/vue-cal/i18n/fr.json | 15 + .../plugins/app@vendor/vue-cal/i18n/he.json | 15 + .../plugins/app@vendor/vue-cal/i18n/hr.json | 15 + .../plugins/app@vendor/vue-cal/i18n/hu.json | 15 + .../plugins/app@vendor/vue-cal/i18n/id.json | 15 + .../plugins/app@vendor/vue-cal/i18n/is.json | 15 + .../plugins/app@vendor/vue-cal/i18n/it.json | 15 + .../plugins/app@vendor/vue-cal/i18n/ja.json | 15 + .../plugins/app@vendor/vue-cal/i18n/ka.json | 15 + .../plugins/app@vendor/vue-cal/i18n/ko.json | 15 + .../plugins/app@vendor/vue-cal/i18n/lt.json | 15 + .../plugins/app@vendor/vue-cal/i18n/mn.json | 15 + .../plugins/app@vendor/vue-cal/i18n/nl.json | 15 + .../plugins/app@vendor/vue-cal/i18n/no.json | 15 + .../plugins/app@vendor/vue-cal/i18n/pl.json | 15 + .../app@vendor/vue-cal/i18n/pt-br.json | 15 + .../plugins/app@vendor/vue-cal/i18n/ro.json | 15 + .../plugins/app@vendor/vue-cal/i18n/ru.json | 16 + .../plugins/app@vendor/vue-cal/i18n/sk.json | 15 + .../plugins/app@vendor/vue-cal/i18n/sl.json | 15 + .../plugins/app@vendor/vue-cal/i18n/sq.json | 16 + .../plugins/app@vendor/vue-cal/i18n/sr.json | 15 + .../plugins/app@vendor/vue-cal/i18n/sv.json | 15 + .../plugins/app@vendor/vue-cal/i18n/tr.json | 15 + .../plugins/app@vendor/vue-cal/i18n/uk.json | 16 + .../plugins/app@vendor/vue-cal/i18n/vi.json | 16 + .../app@vendor/vue-cal/i18n/zh-cn.json | 16 + .../app@vendor/vue-cal/i18n/zh-hk.json | 16 + app/src/plugins/app@vendor/vue-cal/index.ts | 10 + app/src/plugins/app@vendor/vue-cal/index.vue | 1683 +++++++++++++++++ .../vue-cal/modules/drag-and-drop.js | 344 ++++ .../app@vendor/vue-cal/scss/_base.scss | 76 + .../app@vendor/vue-cal/scss/_variables.scss | 16 + .../app@vendor/vue-cal/scss/index.scss | 2 + .../plugins/app@vendor/vue-cal/styles.scss | 323 ++++ .../plugins/app@vendor/vue-cal/utils/cell.js | 94 + .../plugins/app@vendor/vue-cal/utils/date.js | 376 ++++ .../plugins/app@vendor/vue-cal/utils/event.js | 426 +++++ .../app@vendor/vue-cal/weekdays-headings.vue | 188 ++ 56 files changed, 5535 insertions(+) create mode 100644 app/src/plugins/app@vendor/vue-cal/all-day-bar.vue create mode 100644 app/src/plugins/app@vendor/vue-cal/cell.vue create mode 100644 app/src/plugins/app@vendor/vue-cal/event.vue create mode 100644 app/src/plugins/app@vendor/vue-cal/header.vue create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/ar.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/bg.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/bn.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/bs.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/ca.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/cs.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/da.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/de.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/el.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/en.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/es.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/et.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/fa.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/fr.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/he.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/hr.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/hu.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/id.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/is.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/it.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/ja.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/ka.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/ko.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/lt.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/mn.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/nl.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/no.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/pl.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/pt-br.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/ro.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/ru.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/sk.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/sl.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/sq.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/sr.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/sv.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/tr.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/uk.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/vi.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/zh-cn.json create mode 100644 app/src/plugins/app@vendor/vue-cal/i18n/zh-hk.json create mode 100644 app/src/plugins/app@vendor/vue-cal/index.ts create mode 100644 app/src/plugins/app@vendor/vue-cal/index.vue create mode 100644 app/src/plugins/app@vendor/vue-cal/modules/drag-and-drop.js create mode 100644 app/src/plugins/app@vendor/vue-cal/scss/_base.scss create mode 100644 app/src/plugins/app@vendor/vue-cal/scss/_variables.scss create mode 100644 app/src/plugins/app@vendor/vue-cal/scss/index.scss create mode 100644 app/src/plugins/app@vendor/vue-cal/styles.scss create mode 100644 app/src/plugins/app@vendor/vue-cal/utils/cell.js create mode 100644 app/src/plugins/app@vendor/vue-cal/utils/date.js create mode 100644 app/src/plugins/app@vendor/vue-cal/utils/event.js create mode 100644 app/src/plugins/app@vendor/vue-cal/weekdays-headings.vue diff --git a/app/src/plugins/app@vendor/vue-cal/all-day-bar.vue b/app/src/plugins/app@vendor/vue-cal/all-day-bar.vue new file mode 100644 index 0000000..f95b3d3 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/all-day-bar.vue @@ -0,0 +1,47 @@ + + + diff --git a/app/src/plugins/app@vendor/vue-cal/cell.vue b/app/src/plugins/app@vendor/vue-cal/cell.vue new file mode 100644 index 0000000..2a09a87 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/cell.vue @@ -0,0 +1,615 @@ + + + + + diff --git a/app/src/plugins/app@vendor/vue-cal/event.vue b/app/src/plugins/app@vendor/vue-cal/event.vue new file mode 100644 index 0000000..df8ddd5 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/event.vue @@ -0,0 +1,426 @@ + + + + + diff --git a/app/src/plugins/app@vendor/vue-cal/header.vue b/app/src/plugins/app@vendor/vue-cal/header.vue new file mode 100644 index 0000000..94a1e26 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/header.vue @@ -0,0 +1,284 @@ + + + + + diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/ar.json b/app/src/plugins/app@vendor/vue-cal/i18n/ar.json new file mode 100644 index 0000000..37d0255 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/ar.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["الإثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت", "الأحد"], + "months": ["يناير" , "فبراير" , "مارس" , "أبريل" , "مايو" , "يونيو" , "يوليو" , "أغسطس" , "سبتمبر" , "أكتوبر" , "نوفمبر" , " ديسمبر"], + "years": "سنوات", + "year": "سنة", + "month": "شهر", + "week": "أسبوع", + "day": "يوم", + "today": "اليوم", + "noEvent": "لا حدث", + "allDay": "طوال اليوم", + "deleteEvent": "حذف", + "createEvent": "إنشاء حدث", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/bg.json b/app/src/plugins/app@vendor/vue-cal/i18n/bg.json new file mode 100644 index 0000000..c865a39 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/bg.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Понеделник", "Вторник", "Сряда", "Четвъртък", "Петък", "Събота", "Неделя"], + "months": ["Януари", "Февруари", "Март", "Април", "Май", "Юни", "Юли", "Август", "Септември", "Октомври", "Ноември", "Декември"], + "years": "Години", + "year": "Година", + "month": "Месец", + "week": "Седмица", + "day": "Ден", + "today": "Днес", + "noEvent": "Няма събития", + "allDay": "Цял ден", + "deleteEvent": "Изтрий", + "createEvent": "Създай събитие", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/bn.json b/app/src/plugins/app@vendor/vue-cal/i18n/bn.json new file mode 100644 index 0000000..ef63695 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/bn.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["সোম", "মঙ্গল", "বুধ", "বৃহস্পতি", "শুক্র" , "শনি" , "রবি"], + "months": ["জানুয়ারি" , "ফেব্ুয়ারী" , "মার্চ", "এপ্রিল" , "মে" , "জুন" , "জুলাই" , "অগাস্ট" , "সেপ্টেম্বর" , "অক্টোবর", "নভেম্বর" , "ডিসেম্বর"], + "years": "বছর", + "year": "বছর", + "month": "মাস", + "week": "সপ্তাহ", + "day": "দিন", + "today": "আজ", + "noEvent": "কার্যসূচী", + "allDay": "সারাদিন", + "deleteEvent": "মুছুন", + "createEvent": "কার্যসূচী তৈরি করুন", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/bs.json b/app/src/plugins/app@vendor/vue-cal/i18n/bs.json new file mode 100644 index 0000000..199e864 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/bs.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Ponedjeljak", "Utorak", "Srijeda", "Četvrtak", "Petak", "Subota", "Nedjelja"], + "months": ["Januar", "Februar", "Mart", "April", "Maj", "Jun", "Jul", "Avgust", "Septembar", "Oktobar", "Novembar", "Decembar"], + "years": "Godine", + "year": "Godina", + "month": "Mjesec", + "week": "Sedmica", + "day": "Dan", + "today": "Danas", + "noEvent": "Nema događaja", + "allDay": "Cijeli dan", + "deleteEvent": "Obriši", + "createEvent": "Kreiraj događaj", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/ca.json b/app/src/plugins/app@vendor/vue-cal/i18n/ca.json new file mode 100644 index 0000000..614ecb6 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/ca.json @@ -0,0 +1,16 @@ +{ + "weekDays": ["Dilluns", "Dimarts", "Dimecres", "Dijous", "Divendres", "Dissabte", "Diumenge"], + "weekDaysShort": ["Dl", "Dt", "Dc", "Dj", "Dv", "Ds", "Dg"], + "months": ["Gener", "Febrer", "Març", "Abril", "Maig", "Juny", "Juliol", "Agost", "Setembre", "Octubre", "Novembre", "Desembre"], + "years": "Anys", + "year": "Any", + "month": "Mes", + "week": "Setmana", + "day": "Dia", + "today": "Avui", + "noEvent": "No hi ha esdeveniments", + "allDay": "Tot el dia", + "deleteEvent": "Eliminar", + "createEvent": "Crear un esdeveniment", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/cs.json b/app/src/plugins/app@vendor/vue-cal/i18n/cs.json new file mode 100644 index 0000000..f236ddf --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/cs.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Pondělí", "Úterý", "Středa", "Čtvrtek", "Pátek", "Sobota", "Neděle"], + "months": ["Leden", "Únor", "Březen", "Duben", "Květen", "Červen", "Červenec", "Srpen", "Září", "Říjen", "Listopad", "Prosinec"], + "years": "Roky", + "year": "Rok", + "month": "Měsíc", + "week": "Týden", + "day": "Den", + "today": "Dnes", + "noEvent": "Bez událostí", + "allDay": "Celý den", + "deleteEvent": "Odstranit", + "createEvent": "Vytvořit událost", + "dateFormat": "dddd D. MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/da.json b/app/src/plugins/app@vendor/vue-cal/i18n/da.json new file mode 100644 index 0000000..14782a7 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/da.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Mandag", "Tirsdag", "Onsdag", "Torsdag", "Fredag", "Lørdag", "Søndag"], + "months": ["Januar", "Februar", "Marts", "April", "Maj", "Juni", "Juli", "August", "September", "Oktober", "November", "December"], + "years": "År (flertal)", + "year": "År", + "month": "Måned", + "week": "Uge", + "day": "Dag", + "today": "I dag", + "noEvent": "Ingen begivenhed", + "allDay": "Hele dagen", + "deleteEvent": "Slet", + "createEvent": "Opret et event", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/de.json b/app/src/plugins/app@vendor/vue-cal/i18n/de.json new file mode 100644 index 0000000..5b4c57a --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/de.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"], + "months": ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"], + "years": "Jahre", + "year": "Jahr", + "month": "Monat", + "week": "Woche", + "day": "Tag", + "today": "Heute", + "noEvent": "Keine Events", + "allDay": "Ganztägig", + "deleteEvent": "Löschen", + "createEvent": "Event erstellen", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/el.json b/app/src/plugins/app@vendor/vue-cal/i18n/el.json new file mode 100644 index 0000000..5549253 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/el.json @@ -0,0 +1,18 @@ +{ + "weekDays": ["Δευτέρα", "Τρίτη", "Τετάρτη", "Πέμπτη", "Παρασκευή", "Σάββατο", "Κυριακή"], + "months": ["Ιανουάριος", "Φεβρουάριος", "Μάρτιος", "Απρίλιος", "Μάιος", "Ιούνιος", "Ιούλιος", "Αύγουστος", "Σεπτέμβριος", "Οκτώβριος", "Νοέμβριος", "Δεκέμβριος"], + "monthsGenitive": ["Ιανουαρίου", "Φεβρουαρίου", "Μαρτίου", "Απριλίου", "Μαΐου", "Ιουνίου", "Ιουλίου", "Αυγούστου", "Σεπτεμβρίου", "Οκτωβρίου", "Νοεμβρίου", "Δεκεμβρίου"], + "years": "Έτη", + "year": "Έτος", + "month": "Μήνα", + "week": "Εβδομάδα", + "day": "Ημέρα", + "today": "Σήμερα", + "noEvent": "Κανένα συμβάν", + "allDay": "Ημερήσιο συμβάν", + "deleteEvent": "Διαγραφή", + "createEvent": "Δημιουργία συμβάντος", + "dateFormat": "dddd D MMMMG YYYY", + "am": "π.μ.", + "pm": "μ.μ." +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/en.json b/app/src/plugins/app@vendor/vue-cal/i18n/en.json new file mode 100644 index 0000000..da4dbbc --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/en.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], + "months": ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], + "years": "Years", + "year": "Year", + "month": "Month", + "week": "Week", + "day": "Day", + "today": "Today", + "noEvent": "No Event", + "allDay": "All day", + "deleteEvent": "Delete", + "createEvent": "Create an event", + "dateFormat": "dddd MMMM D{S}, YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/es.json b/app/src/plugins/app@vendor/vue-cal/i18n/es.json new file mode 100644 index 0000000..97ac462 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/es.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado", "Domingo"], + "months": ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"], + "years": "Años", + "year": "Año", + "month": "Mes", + "week": "Semana", + "day": "Día", + "today": "Hoy", + "noEvent": "No hay evento", + "allDay": "Todo el día", + "deleteEvent": "Borrar", + "createEvent": "Crear un evento", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/et.json b/app/src/plugins/app@vendor/vue-cal/i18n/et.json new file mode 100644 index 0000000..ce81667 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/et.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Esmaspäev", "Teisipäev", "Kolmapäev", "Neljapäev", "Reede", "Laupäev", "Pühapäev"], + "months": ["Jaanuar", "Veebruar", "Märts", "Aprill", "Mai", "Juuni", "Juuli", "August", "September", "Oktoober", "November", "Detsember"], + "years": "Aastad", + "year": "Aasta", + "month": "Kuu", + "week": "Nädal", + "day": "Päev", + "today": "Täna", + "noEvent": "Sündmus puudub", + "allDay": "Terve päev", + "deleteEvent": "Kustuta", + "createEvent": "Loo sündmus", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/fa.json b/app/src/plugins/app@vendor/vue-cal/i18n/fa.json new file mode 100644 index 0000000..c32eb8b --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/fa.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["دوشنبه", "سه شنبه", "چهار شنبه", "پنج شنبه", "جمعه", "شنبه", "یک شنبه"], + "months": ["ژانویه", "فوریه", "مارس", "آوریل", "می", "ژوئن", "ژوئیه", "اوت", "سپتامبر", "اکتبر", "نوامبر", "دسامبر"], + "years": "سالها", + "year": "سال", + "month": "ماه", + "week": "هفته", + "day": "روز", + "today": "امروز", + "noEvent": "رویدادی نیست", + "allDay": "تمام روز", + "deleteEvent": "حذف", + "createEvent": "ایجاد یک رویداد", + "dateFormat": "dddd D MMMM YYYY" +} \ No newline at end of file diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/fr.json b/app/src/plugins/app@vendor/vue-cal/i18n/fr.json new file mode 100644 index 0000000..2dc28dd --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/fr.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche"], + "months": ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"], + "years": "Années", + "year": "Année", + "month": "Mois", + "week": "Semaine", + "day": "Jour", + "today": "Aujourd'hui", + "noEvent": "Aucun événement", + "allDay": "Jour entier", + "deleteEvent": "Supprimer", + "createEvent": "Créer un événement", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/he.json b/app/src/plugins/app@vendor/vue-cal/i18n/he.json new file mode 100644 index 0000000..84bcc03 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/he.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["שני", "שלישי", "רביעי", "חמישי", "שישי", "שבת", "ראשון"], + "months": ["ינואר", "פברואר", "מרץ", "אפריל", "מאי", "יוני", "יולי", "אוגוסט", "ספטמבר", "אוקטובר", "נובמבר", "דצמבר"], + "years": "שנים", + "year": "שנה", + "month": "חודש", + "week": "שבוע", + "day": "יום", + "today": "היום", + "noEvent": "אין אירועים", + "allDay": "כל היום", + "deleteEvent": "מחיקה", + "createEvent": "צור אירוע", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/hr.json b/app/src/plugins/app@vendor/vue-cal/i18n/hr.json new file mode 100644 index 0000000..4e74d16 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/hr.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Ponedjeljak", "Utorak", "Srijeda", "Četvrtak", "Petak", "Subota", "Nedjelja"], + "months": ["Siječanj", "Veljača", "Ožujak", "Travanj", "Svibanj", "Lipanj", "Srpanj", "Kolovoz", "Rujan", "Listopad", "Studeni", "Prosinac"], + "years": "Godine", + "year": "Godina", + "month": "Mjesec", + "week": "Tjedan", + "day": "Dan", + "today": "Današnji dan", + "noEvent": "Nema događaja", + "allDay": "Cijeli dan", + "deleteEvent": "Obriši", + "createEvent": "Kreiraj događaj", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/hu.json b/app/src/plugins/app@vendor/vue-cal/i18n/hu.json new file mode 100644 index 0000000..f6029c6 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/hu.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Hétfo", "Kedd", "Szerda", "Csütörtök", "Péntek", "Szombat", "Vasárnap"], + "months": ["Január", "Február", "Március", "Április", "Május", "Június", "Július", "Augusztus", "Szeptember", "Október", "November", "December"], + "years": "Évek", + "year": "Év", + "month": "Hónap", + "week": "Hét", + "day": "Nap", + "today": "Mai nap", + "noEvent": "Nincs esemény", + "allDay": "Egész nap", + "deleteEvent": "Esemény törlese", + "createEvent": "Esemény létrehozása", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/id.json b/app/src/plugins/app@vendor/vue-cal/i18n/id.json new file mode 100644 index 0000000..cacc4ea --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/id.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu", "Minggu"], + "months": ["Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember"], + "years": "Tahunan", + "year": "Tahun", + "month": "Bulan", + "week": "Minggu", + "day": "Hari", + "today": "Hari Ini", + "noEvent": "Tidak Ada Kegiatan", + "allDay": "Sepanjang Hari", + "deleteEvent": "Hapus", + "createEvent": "Tambah Kegiatan", + "dateFormat": "dddd, D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/is.json b/app/src/plugins/app@vendor/vue-cal/i18n/is.json new file mode 100644 index 0000000..085cb13 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/is.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Mánudagur", "Þriðjudagur", "Miðvikudagur", "Fimmtudagur", "Föstudagur", "Laugardagur", "Sunnudagur"], + "months": ["Janúar", "Febrúar", "Mars", "Apríl", "Maí", "Júní", "Júlí", "Ágúst", "September", "Október", "Nóvember", "Desember"], + "years": "Ár", + "year": "Ár", + "month": "Mánuður", + "week": "Vika", + "day": "Dagur", + "today": "Í dag", + "noEvent": "Enginn atburður", + "allDay": "Allan daginn", + "deleteEvent": "Eyða", + "createEvent": "Búðu til viðburð", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/it.json b/app/src/plugins/app@vendor/vue-cal/i18n/it.json new file mode 100644 index 0000000..94f612d --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/it.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Lunedì", "Martedì", "Mercoledì", "Giovedì", "Venerdì", "Sabato", "Domenica"], + "months": ["Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre"], + "years": "Anni", + "year": "Anno", + "month": "Mese", + "week": "Settimana", + "day": "Giorno", + "today": "Oggi", + "noEvent": "Nessun evento", + "allDay": "Tutto il giorno", + "deleteEvent": "Cancella", + "createEvent": "Crea evento", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/ja.json b/app/src/plugins/app@vendor/vue-cal/i18n/ja.json new file mode 100644 index 0000000..dd9808c --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/ja.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["月", "火", "水", "木", "金", "土", "日"], + "months": ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"], + "years": "年", + "year": "今年", + "month": "月", + "week": "週", + "day": "日", + "today": "今日", + "noEvent": "イベントなし", + "allDay": "終日", + "deleteEvent": "削除", + "createEvent": "イベント作成", + "dateFormat": "YYYY年 MMMM D日 (dddd)" + } diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/ka.json b/app/src/plugins/app@vendor/vue-cal/i18n/ka.json new file mode 100644 index 0000000..10bfd41 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/ka.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["ორშაბათი", "სამშაბათი", "ოთხშაბათი", "ხუთშაბათი", "პარასკევი", "შაბათი", "კვირა"], + "months": ["იანვარი", "თებერვალი", "მარტი", "აპრილი", "მაისი", "ივნისი", "ივლისი", "აგვისტო", "სექტემბერი", "ოქტომბერი", "ნოემბერი", "დეკემბერი"], + "years": "წლები", + "year": "წელი", + "month": "თვე", + "week": "კვირა", + "day": "დღე", + "today": "დღეს", + "noEvent": "ღონისძიება არ არის", + "allDay": "მთელი დღე", + "deleteEvent": "წაშლა", + "createEvent": "შექმენით ღონისძიება", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/ko.json b/app/src/plugins/app@vendor/vue-cal/i18n/ko.json new file mode 100644 index 0000000..7a9f186 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/ko.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["월", "화", "수", "목", "금", "토", "일"], + "months": ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"], + "years": "년도", + "year": "연간", + "month": "월간", + "week": "주간", + "day": "일간", + "today": "오늘", + "noEvent": "일정 없음", + "allDay": "하루 종일", + "deleteEvent": "삭제", + "createEvent": "일정 추가", + "dateFormat": "YYYY년 MMMM D일 dddd요일" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/lt.json b/app/src/plugins/app@vendor/vue-cal/i18n/lt.json new file mode 100644 index 0000000..0c1e4f2 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/lt.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Pirmadienis", "Antradienis", "Trečiadienis", "Ketvirtadienis", "Penktadienis", "Šeštadienis", "Sekmadienis"], + "months": ["Sausis", "Vasaris", "Kovas", "Balandis", "Gegužė", "Birželis", "Liepa", "Rugpjūtis", "Rugsėjis", "Spalis", "Lapkritis", "Gruodis"], + "years": "Metų pasirinkimas", + "year": "Metai", + "month": "Mėnesis", + "week": "Savaitė", + "day": "Diena", + "today": "Šiandien", + "noEvent": "Jokių įvykių", + "allDay": "Visa diena", + "deleteEvent": "Ištrinti", + "createEvent": "Sukurti įvykį", + "dateFormat": "dddd, D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/mn.json b/app/src/plugins/app@vendor/vue-cal/i18n/mn.json new file mode 100644 index 0000000..602975e --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/mn.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Даваа", "Мягмар", "Лхавга", "Пүрэв", "Баасан", "Бямба", "Ням"], + "months": ["1-р сар", "2-р сар", "3-р сар", "4-р сар", "5-р сар", "6-р сар", "7-р сар", "8-р сар", "9-р сар", "10-р сар", "11-р сар", "12-р сар"], + "years": "Жилүүд", + "year": "Жил", + "month": "Сар", + "week": "Долоо хоног", + "day": "Өдөр", + "today": "Өнөөдөр", + "noEvent": "Тэмдэглэлгүй", + "allDay": "Бүх өдөр", + "deleteEvent": "Устгах", + "createEvent": "Шинэ тэмдэглэл", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/nl.json b/app/src/plugins/app@vendor/vue-cal/i18n/nl.json new file mode 100644 index 0000000..26ed121 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/nl.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Maandag", "Dinsdag", "Woensdag", "Donderdag", "Vrijdag", "Zaterdag", "Zondag"], + "months": ["Januari", "Februari", "Maart", "April", "Mei", "Juni", "Juli", "Augustus", "September", "Oktober", "November", "December"], + "years": "Jaren", + "year": "Jaar", + "month": "Maand", + "week": "Week", + "day": "Dag", + "today": "Vandaag", + "noEvent": "Geen afspraken", + "allDay": "Hele dag", + "deleteEvent": "Verwijderen", + "createEvent": "Nieuwe afspraak aanmaken", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/no.json b/app/src/plugins/app@vendor/vue-cal/i18n/no.json new file mode 100644 index 0000000..6fa8a5e --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/no.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Mandag", "Tirsdag", "Onsdag", "Torsdag", "Fredag", "Lørdag", "Søndag"], + "months": ["Januar", "Februar", "Mars", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Desember"], + "years": "Velg år", + "year": "År", + "month": "Måned", + "week": "Uke", + "day": "Dag", + "today": "Idag", + "noEvent": "Ingen hendelse", + "allDay": "Hele dagen", + "deleteEvent": "Ta bort", + "createEvent": "Ny hendelse", + "dateFormat": "dddd, D. MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/pl.json b/app/src/plugins/app@vendor/vue-cal/i18n/pl.json new file mode 100644 index 0000000..e128b95 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/pl.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Poniedziałek", "Wtorek", "Środa", "Czwartek", "Piątek", "Sobota", "Niedziela"], + "months": ["Styczeń", "Luty", "Marzec", "Kwiecień", "Maj", "Czerwiec", "Lipiec", "Sierpień", "Wrzesień", "Październik", "Listopad", "Grudzień"], + "years": "Lata", + "year": "Rok", + "month": "Miesiąc", + "week": "Tydzień", + "day": "Dzień", + "today": "Dzisiaj", + "noEvent": "Brak wydarzeń", + "allDay": "Cały dzień", + "deleteEvent": "Usuń", + "createEvent": "Utwórz wydarzenie", + "dateFormat": "dddd, D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/pt-br.json b/app/src/plugins/app@vendor/vue-cal/i18n/pt-br.json new file mode 100644 index 0000000..99b5daf --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/pt-br.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Segunda-feira", "Terça-feira", "Quarta-feira", "Quinta-feira", "Sexta-feira", "Sábado", "Domingo"], + "months": ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"], + "years": "Anos", + "year": "Ano", + "month": "Mês", + "week": "Semana", + "day": "Dia", + "today": "Hoje", + "noEvent": "Sem eventos", + "allDay": "Dia inteiro", + "deleteEvent": "Remover", + "createEvent": "Criar um evento", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/ro.json b/app/src/plugins/app@vendor/vue-cal/i18n/ro.json new file mode 100644 index 0000000..f0e7224 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/ro.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Luni", "Marți", "Miercuri", "Joi", "Vineri", "Sâmbăta", "Duminică"], + "months": ["Ianuarie", "Februarie", "Martie", "Aprilie", "Mai", "Iunie", "Iulie", "August", "Septembrie", "Octombrie", "Noiembrie", "Decembrie"], + "years": "Ani", + "year": "An", + "month": "Lună", + "week": "Săptămână", + "day": "Zi", + "today": "Azi", + "noEvent": "Nici o interacțiune", + "allDay": "Toată ziua", + "deleteEvent": "Șterge", + "createEvent": "Adaugă un eveniment", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/ru.json b/app/src/plugins/app@vendor/vue-cal/i18n/ru.json new file mode 100644 index 0000000..c296690 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/ru.json @@ -0,0 +1,16 @@ +{ + "weekDays": ["Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота", "Воскресенье"], + "weekDaysShort": ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"], + "months": ["Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"], + "years": "Годы", + "year": "Год", + "month": "Месяц", + "week": "Неделя", + "day": "День", + "today": "Сегодня", + "noEvent": "Нет событий", + "allDay": "Весь день", + "deleteEvent": "Удалить", + "createEvent": "Создать событие", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/sk.json b/app/src/plugins/app@vendor/vue-cal/i18n/sk.json new file mode 100644 index 0000000..5189b8b --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/sk.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Pondelok", "Utorok", "Streda", "Štvrtok", "Piatok", "Sobota", "Nedeľa"], + "months": ["Január", "Február", "Marec", "Apríl", "Máj", "Jún", "Júl", "August", "September", "Október", "November", "December"], + "years": "Roky", + "year": "Rok", + "month": "Mesiac", + "week": "Týždeň", + "day": "Deň", + "today": "Dnes", + "noEvent": "Bez udalosti", + "allDay": "Celý deň", + "deleteEvent": "Odstrániť", + "createEvent": "Vytvoriť udalosť", + "dateFormat": "dddd D. MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/sl.json b/app/src/plugins/app@vendor/vue-cal/i18n/sl.json new file mode 100644 index 0000000..01c3b66 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/sl.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Ponedeljek", "Torek", "Sreda", "Četrtek", "Petek", "Sobota", "Nedelja"], + "months": ["Januar", "Februar", "Marec", "April", "Maj", "Junij", "Julij", "Avgust", "September", "Oktober", "November", "December"], + "years": "Leta", + "year": "Leto", + "month": "Mesec", + "week": "Teden", + "day": "Dan", + "today": "Danes", + "noEvent": "Ni dogodkov", + "allDay": "Cel dan", + "deleteEvent": "Odstrani", + "createEvent": "Ustvari dogodek", + "dateFormat": "dddd MMMM D, YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/sq.json b/app/src/plugins/app@vendor/vue-cal/i18n/sq.json new file mode 100644 index 0000000..6f5ba3b --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/sq.json @@ -0,0 +1,16 @@ +{ + "weekDays": ["E Hënë","E Martë","E Mërkurë","E Enjte","E Premte","E Shtunë","E Diel"], + "weekDaysShort": ["Hë","Ma","Mr","Enj","Pr","Sh","D"], + "months": ["Janar","Shkurt","Mars","Prill","Maj","Qershor","Korrik","Gusht","Shtator","Tetor","Nëntor","Dhjetor"], + "years": "Vitet", + "year": "Viti", + "month": "Muaji", + "week": "Java", + "day": "Dita", + "today": "Sot", + "noEvent": "Nuk ka event", + "allDay": "Tërë ditën", + "deleteEvent": "Fshijë", + "createEvent": "Krijo një event", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/sr.json b/app/src/plugins/app@vendor/vue-cal/i18n/sr.json new file mode 100644 index 0000000..98133f5 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/sr.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Ponedeljak", "Utorak", "Sreda", "Četvrtak", "Petak", "Subota", "Nedelja"], + "months": ["Januar", "Februar", "Mart", "April", "Maj", "Jun", "Jul", "Avgust", "Septembar", "Oktobar", "Novembar", "Decembar"], + "years": "Godine", + "year": "Godina", + "month": "Mesec", + "week": "Sedmica", + "day": "Dan", + "today": "Danas", + "noEvent": "Nema događaja", + "allDay": "Celi dan", + "deleteEvent": "Obriši", + "createEvent": "Kreiraj događaj", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/sv.json b/app/src/plugins/app@vendor/vue-cal/i18n/sv.json new file mode 100644 index 0000000..0fa2e05 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/sv.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Måndag", "Tisdag", "Onsdag", "Torsdag", "Fredag", "Lördag", "Söndag"], + "months": ["Januari", "Februari", "Mars", "April", "Maj", "Juni", "Juli", "Augusti", "September", "Oktober", "November", "December"], + "years": "År", + "year": "År", + "month": "Månad", + "week": "Vecka", + "day": "Dag", + "today": "Idag", + "noEvent": "Ingen händelse", + "allDay": "Heldag", + "deleteEvent": "Ta bort", + "createEvent": "Skapa händelse", + "dateFormat": "dddd den D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/tr.json b/app/src/plugins/app@vendor/vue-cal/i18n/tr.json new file mode 100644 index 0000000..dcadb7a --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/tr.json @@ -0,0 +1,15 @@ +{ + "weekDays": ["Pazartesi", "Salı", "Çarşamba", "Perşembe", "Cuma", "Cumartesi", "Pazar"], + "months": ["Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık"], + "years": "Yıllar", + "year": "Yıl", + "month": "Ay", + "week": "Hafta", + "day": "Gün", + "today": "Bugün", + "noEvent": "Etkinlik Yok", + "allDay": "Tüm gün", + "deleteEvent": "Sil", + "createEvent": "Etkinlik ekle", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/uk.json b/app/src/plugins/app@vendor/vue-cal/i18n/uk.json new file mode 100644 index 0000000..3a8ff45 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/uk.json @@ -0,0 +1,16 @@ +{ + "weekDays": ["Понеділок", "Вівторок", "Середа", "Четвер", "П'ятниця", "Субота", "Неділя"], + "months": ["Січень", "Лютий", "Березень", "Квітень", "Травень", "Червень", "Липень", "Серпень", "Вересень", "Жовтень", "Листопад", "Грудень"], + "weekDaysShort": ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Нд"], + "years": "Роки", + "year": "Рік", + "month": "Місяць", + "week": "Тиждень", + "day": "День", + "today": "Сьогодні", + "noEvent": "Немає подій", + "allDay": "Весь день", + "deleteEvent": "Видалити", + "createEvent": "Створити подію", + "dateFormat": "dddd D MMMM YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/vi.json b/app/src/plugins/app@vendor/vue-cal/i18n/vi.json new file mode 100644 index 0000000..4b52805 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/vi.json @@ -0,0 +1,16 @@ +{ + "weekDays": ["Thứ hai", "Thứ ba", "Thứ tư", "Thứ năm", "Thứ sáu", "Thứ bảy", "Chủ nhật"], + "weekDaysShort": ["T2", "T3", "T4", "T5", "T6", "T7", "CN"], + "months": ["Tháng 1", "Tháng 2", "Tháng 3", "Tháng 4", "Tháng 5", "Tháng 6", "Tháng 7", "Tháng 8", "Tháng 9", "Tháng 10", "Tháng 11", "Tháng 12"], + "years": "Năm", + "year": "Năm nay", + "month": "Tháng", + "week": "Tuần", + "day": "Ngày", + "today": "Hôm nay", + "noEvent": "NKhông có Event", + "allDay": "Cả ngày", + "deleteEvent": "Xóa", + "createEvent": "Tạo event", + "dateFormat": "dddd MMMM D YYYY" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/zh-cn.json b/app/src/plugins/app@vendor/vue-cal/i18n/zh-cn.json new file mode 100644 index 0000000..1bbc0d9 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/zh-cn.json @@ -0,0 +1,16 @@ +{ + "weekDays": ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"], + "weekDaysShort": ["一", "二", "三", "四", "五", "六", "日"], + "months": ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"], + "years": "年", + "year": "本年", + "month": "月", + "week": "周", + "day": "日", + "today": "今日", + "noEvent": "暂无活动", + "allDay": "整天", + "deleteEvent": "删除", + "createEvent": "新建活动", + "dateFormat": "YYYY MMMM D dddd" +} diff --git a/app/src/plugins/app@vendor/vue-cal/i18n/zh-hk.json b/app/src/plugins/app@vendor/vue-cal/i18n/zh-hk.json new file mode 100644 index 0000000..74c99e9 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/i18n/zh-hk.json @@ -0,0 +1,16 @@ +{ + "weekDays": ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"], + "weekDaysShort": ["一", "二", "三", "四", "五", "六", "日"], + "months": ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"], + "years": "年", + "year": "本年", + "month": "月", + "week": "周", + "day": "日", + "today": "今日", + "noEvent": "暫無活動", + "allDay": "整天", + "deleteEvent": "刪除", + "createEvent": "新建活動", + "dateFormat": "YYYY MMMM D dddd" +} diff --git a/app/src/plugins/app@vendor/vue-cal/index.ts b/app/src/plugins/app@vendor/vue-cal/index.ts new file mode 100644 index 0000000..a7185b5 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/index.ts @@ -0,0 +1,10 @@ +import { App } from 'vue' +import VueCal from './index.vue' + + +export default { + install: (app: App) => { + + app.component('Vue-cal', VueCal) + } +} \ No newline at end of file diff --git a/app/src/plugins/app@vendor/vue-cal/index.vue b/app/src/plugins/app@vendor/vue-cal/index.vue new file mode 100644 index 0000000..303bd12 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/index.vue @@ -0,0 +1,1683 @@ + + + diff --git a/app/src/plugins/app@vendor/vue-cal/modules/drag-and-drop.js b/app/src/plugins/app@vendor/vue-cal/modules/drag-and-drop.js new file mode 100644 index 0000000..d6340ab --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/modules/drag-and-drop.js @@ -0,0 +1,344 @@ +/** + * Events drag and drop module. + * + * Note: Waiting for VS Code to support JavaScript private fields. + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Class_fields + * Meantime keep `_` for private. + */ + +const holdOverTimeout = 800 // How long we should hold over an element before it reacts. +let changeViewTimeout = null +let pressPrevOrNextInterval = null +let viewBeforeDrag = { id: null, date: null } // To go back if cancelling. +let viewChanged = false +let cancelViewChange = true +let dragOverCell = { el: null, cell: null, timeout: null } +const dragging = { + _eid: null, + fromVueCal: null, + toVueCal: null +} + +export const DragAndDrop = class { + _vuecal + + constructor (vuecal) { + this._vuecal = vuecal + } + + /** + * When click and drag an event the cursor can be anywhere in the event, + * when dropping the event, we need to subtract the cursor position in the event. + * + * @param {Object} e The associated DOM event. + */ + _getEventStart (e) { + const { timeStep, timeCellHeight, timeFrom, utils } = this._vuecal + let { y } = utils.cell.getPosition(e) + y -= e.dataTransfer.getData('cursor-grab-at') * 1 + return Math.round(y * timeStep / parseInt(timeCellHeight) + timeFrom) + } + + /** + * On drop, update the event start and end date directly into the event. + * + * @param {Object} e The associated DOM event. + * @param {Object} event The event being dragged. + * @param {Object} transferData The transfer data from the HTML5 dragging event. + * @param {Date} cellDate The hovered cell starting date. + */ + _updateEventStartEnd (e, event, transferData, cellDate) { + // If no duration calculate it from event.endTimeMinutes - event.startTimeMinutes + // before we modify the start and end. + const eventDuration = transferData.duration * 1 || (event.endTimeMinutes - event.startTimeMinutes) + + // Force the start of the event at previous midnight minimum. + let startTimeMinutes = Math.max(this._getEventStart(e), 0) + + // On drop, snap to time every X minutes if the option is on. + if (this._vuecal.snapToTime) { + const plusHalfSnapTime = (startTimeMinutes + this._vuecal.snapToTime / 2) + startTimeMinutes = plusHalfSnapTime - (plusHalfSnapTime % this._vuecal.snapToTime) + } + + event.startTimeMinutes = startTimeMinutes + event.start = new Date(new Date(cellDate).setMinutes(startTimeMinutes)) + // Force the end of the event at next midnight maximum. + event.endTimeMinutes = Math.min(startTimeMinutes + eventDuration, 24 * 60) + event.end = new Date(new Date(cellDate).setMinutes(event.endTimeMinutes)) + } + + /** + * On event drag start, only possible if editableEvent is true. + * /!\ This is using the native HTML5 drag & drop, not supported on touch devices. + * + * @param {Object} e The associated DOM event. + * @param {Object} event The event being dragged. + */ + eventDragStart (e, event) { + // Cancel the drag if event has draggable set to false and trying to drag a text selection. + if (e.target.nodeType === 3) return e.preventDefault() + + e.dataTransfer.dropEffect = 'move' + // Transfer the event's data to the receiver (when successfully drag & dropping out of Vue Cal). + // Notice: in Firefox the drag is prevented if there is no dataTransfer.setData(). + e.dataTransfer.setData('event', JSON.stringify(event)) + // When click and drag an event the cursor can be anywhere in the event, + // when later dropping the event, we need to subtract the cursor position in the event. + e.dataTransfer.setData('cursor-grab-at', e.offsetY) // In pixels. + + const { clickHoldAnEvent } = this._vuecal.domEvents + // Cancel any delete on dragStart (if held for too long). Don't drag an event with a visible delete button. + setTimeout(() => { + clickHoldAnEvent._eid = null + clearTimeout(clickHoldAnEvent.timeoutId) + event.deleting = false + }, 0) + + this._vuecal.domEvents.dragAnEvent._eid = event._eid // Only for vuecal dragging-event class. + dragging._eid = event._eid + dragging.fromVueCal = this._vuecal._.uid + event.dragging = true + // Controls the CSS class of the static event that remains while a copy is being dragged. + // Thanks to this class, the event being dragged can have a different style. + setTimeout(() => (event.draggingStatic = true), 0) + + viewChanged = false + viewBeforeDrag = { id: this._vuecal.view.id, date: this._vuecal.view.startDate } + + cancelViewChange = true // Re-init the cancel view: should cancel unless a cell received the event. + } + + /** + * On event drag end, when releasing the event. + * + * @param {Object} event The event being dragged. + */ + eventDragEnd (event) { + this._vuecal.domEvents.dragAnEvent._eid = null + dragging._eid = null + event.dragging = false + event.draggingStatic = false + + // If an event is dragged from a Vue Cal instance and dropped in a different one, remove the + // event from the first one. + const { fromVueCal, toVueCal } = dragging + if (toVueCal && fromVueCal !== toVueCal) this._vuecal.utils.event.deleteAnEvent(event) + dragging.fromVueCal = null + dragging.toVueCal = null + + // When dropping the event, cancel view change if no cell received the event (in cellDragDrop). + if (viewChanged && cancelViewChange && viewBeforeDrag.id) this._vuecal.switchView(viewBeforeDrag.id, viewBeforeDrag.date, true) + } + + /** + * On cell/split enter with a dragging event. + * Highlight the cell, and if on `years`, `year`, `month` view, + * set a timer to go deeper on drag hold over this cell. + * + * @param {Object} e The associated DOM event. + * @param {Object} cell The cell component's $data. + * @param {Date} cellDate The hovered cell starting date. + */ + cellDragEnter (e, cell, cellDate) { + const target = e.currentTarget + + // Cancel dragEnter event if hovering a child. + if (e.currentTarget.contains(e.relatedTarget)) return + if (target === dragOverCell.el || !target.className.includes('vuecal__cell-content')) return false + + // Un-highlight the previous cell. + if (dragOverCell.el) dragOverCell.cell.highlighted = false + + dragOverCell = { el: target, cell, timeout: clearTimeout(dragOverCell.timeout) } + cell.highlighted = true + + // On `years`, `year` & `month` views, go to narrower view on drag and hold. + if (['years', 'year', 'month'].includes(this._vuecal.view.id)) { + dragOverCell.timeout = setTimeout(() => this._vuecal.switchToNarrowerView(cellDate), 2000) + } + } + + /** + * On cell/split drag over, highlight the cell being hovered, + * Useful when starting to drag event on the same cell/split it's in. + * Warning: This is fired repeatedly as long as you stay over this cell/split. + * + * @param {Object} e The associated DOM event. + * @param {Object} cell The cell component's $data. + * @param {Date} cellDate The hovered cell starting date. + * @param {Number|String} split The optional split being hovered if any. + */ + cellDragOver (e, cell, cellDate, split) { + e.preventDefault() + cell.highlighted = true + if (split || split === 0) cell.highlightedSplit = split + } + + /** + * When event drag leaves a cell/split. + * Remove the cell/split highlighted state. + * Warning: cell dragleave event happens AFTER another cell dragenter! + * + * @param {Object} e The associated DOM event. + * @param {Object} cell The cell component's $data. + */ + cellDragLeave (e, cell) { + e.preventDefault() + + if (e.currentTarget.contains(e.relatedTarget)) return + + cell.highlightedSplit = false + + // Only cancel the timer if leaving the current cell to no other one. + // If leaving this cell to enter another, a cancel is done in cellDragEnter, + // and a new timer is started. + if (dragOverCell.cell === cell) { + clearTimeout(dragOverCell.timeout) + dragOverCell = { el: null, cell: null, timeout: null } + cell.highlighted = false + } + } + + /** + * On successful event drop into a cell/split. + * Change the event start and end time and remove the event dragging state + * and cell/split highlighted state. + * + * @param {Object} e The associated DOM event. + * @param {Object} cell The cell component's $data. + * @param {Date} cellDate The hovered cell starting date. + * @param {Number|String} split The optional split being dropped into, if any. + */ + cellDragDrop (e, cell, cellDate, split) { + // Needed to prevent navigation to the text set in dataTransfer from eventDragStart(). + e.preventDefault() + + clearTimeout(dragOverCell.timeout) + dragOverCell = { el: null, cell: null, timeout: null } + + const transferData = JSON.parse(e.dataTransfer.getData('event') || '{}') + let event, addToView + + // If the event is not coming from this Vue Cal it means that we are accepting a new event. + // So create the event in this Vue Cal. + if (dragging.fromVueCal !== this._vuecal._.uid) { + // Removing the _eid is mandatory! It prevents the event to be duplicated when drag and + // dropping to another calendar then back to the original place. + const { _eid, start, end, duration, ...cleanTransferData } = transferData + // Note: createAnEvent adds the event to the view. + event = this._vuecal.utils.event.createAnEvent(cellDate, duration, { ...cleanTransferData, split }) + } + else { + // Find the dragged event from its _eid in the view or mutableEvents array. + // If dragging the event to another day, the event is not in the view array but in the + // mutableEvents one. + event = this._vuecal.view.events.find(evt => evt._eid === dragging._eid) + + if (!event) { + event = this._vuecal.mutableEvents.find(evt => evt._eid === dragging._eid) + addToView = !!event + } + + // Case where events are fetched from the backend and removed from the array when not in the view. + // So it won't be found in the mutableEvents array. + if (!event) { + const duration = transferData.endTimeMinutes - transferData.startTimeMinutes + // Pass exactly the same event as it was before the view change (same _eid as well) except dates. + const { start, end, ...cleanTransferData } = transferData + event = this._vuecal.utils.event.createAnEvent(cellDate, duration, { ...cleanTransferData, split }) + // Note: createAnEvent adds the event to the view. + } + } + + const { start: oldDate, split: oldSplit } = event + this._updateEventStartEnd(e, event, transferData, cellDate) + + // Only add the event to view after the start and end are modified otherwise + // it would be filtered out from the function if not in range. + if (addToView) this._vuecal.addEventsToView([event]) + + event.dragging = false + if (split || split === 0) event.split = split + + cell.highlighted = false + cell.highlightedSplit = null + cancelViewChange = false + dragging.toVueCal = this._vuecal._.uid + + // Emit `event-drop` & `event-change` events and return the updated event. + const params = { + event: this._vuecal.cleanupEvent(event), + oldDate, + newDate: event.start, + ...((split || split === 0) && { oldSplit, newSplit: split }), + originalEvent: this._vuecal.cleanupEvent(transferData), + external: !dragging.fromVueCal // If external event, not coming from any Vue Cal. + } + this._vuecal.$emit('event-drop', params) + this._vuecal.$emit('event-change', { event: params.event, originalEvent: params.originalEvent }) + + // Sometimes the event dragend does not trigger (?!), so manually trigger it if it didn't. + setTimeout(() => { + if (dragging._eid) this.eventDragEnd(event) + }, 300) + } + + /** + * On drag enter on a view button or on today, prev & next buttons. + * Sets a highlighted state on the hovered button, and go to requested view. + * + * @param {Object} e The associated DOM event. + * @param {String} id The id of the header element being hovered. One of: + * previous, next, today, years, year, month, week, day. + * @param {Object} headerData The header component's $data. + */ + viewSelectorDragEnter (e, id, headerData) { + if (e.currentTarget.contains(e.relatedTarget)) return + + headerData.highlightedControl = id + clearTimeout(changeViewTimeout) + changeViewTimeout = setTimeout(() => { + if (['previous', 'next'].includes(id)) { + this._vuecal[id]() + // Keep pressing on previous or next button until user goes away. + clearInterval(pressPrevOrNextInterval) + pressPrevOrNextInterval = setInterval(this._vuecal[id], holdOverTimeout) + } + else if (id === 'today') { + clearInterval(pressPrevOrNextInterval) + let viewId + if (this._vuecal.view.id.includes('year')) { + // If hovering today from a year or years view go to narrower view from month view. + viewId = this._vuecal.enabledViews.filter(view => !view.includes('year'))[0] + } + this._vuecal.switchView(viewId || this._vuecal.view.id, new Date(new Date().setHours(0, 0, 0, 0)), true) + } + else this._vuecal.switchView(id, null, true) + viewChanged = true + }, holdOverTimeout) + } + + /** + * On drag leave on a view button or on today, prev & next buttons. + * Removes the highlighted state on the hovered button, and cancel the timer to + * go to the requested view. + * + * @param {Object} e The associated DOM event. + * @param {String} id The id of the header element being hovered. One of: + * previous, next, today, years, year, month, week, day. + * @param {Object} headerData The header component's $data. + */ + viewSelectorDragLeave (e, id, headerData) { + if (e.currentTarget.contains(e.relatedTarget)) return + + // Only cancel the timer if leaving the current nav button to no other one. + // If leaving this nav button to enter another, a cancel is done in viewSelectorDragEnter, + // and a new timer is started. + if (headerData.highlightedControl === id) { + headerData.highlightedControl = null + if (changeViewTimeout) changeViewTimeout = clearTimeout(changeViewTimeout) + if (pressPrevOrNextInterval) pressPrevOrNextInterval = clearInterval(pressPrevOrNextInterval) + } + } +} diff --git a/app/src/plugins/app@vendor/vue-cal/scss/_base.scss b/app/src/plugins/app@vendor/vue-cal/scss/_base.scss new file mode 100644 index 0000000..f1bfe40 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/scss/_base.scss @@ -0,0 +1,76 @@ + +// Global structure. +// -------------------------------------------------------- +* {margin: 0;padding: 0;} + +html {scroll-behavior: smooth;} + +#app {overflow-x: hidden;} + +.w-app { + font: 15px 'Avenir', Helvetica, Arial, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + background-color: #fff !important; + color: #2c3e50; + margin-top: 8em; + padding-top: 12em; +} + +.page-container { + width: 100%; + max-width: $page-max-width; + margin-left: auto; + margin-right: auto; +} + +// Shared classes & styles. +// -------------------------------------------------------- +a { + text-decoration: none; + color: $primary; + + &[name] { + position: relative; + top: -4em; + display: block; + } +} + +.scrollable { + height: 250px; + overflow-y: scroll; + padding-right: 1.8em; +} + +.footer { + font-size: 0.9em; + font-style: italic; + margin: 3em 0 3.5em; + + .w-icon { + font-size: 1.2em; + + &.heart {transition: 1s ease-out;cursor: pointer;} + &.heart:hover {animation: pulse 1.8s ease-out infinite;} + } +} + +@keyframes pulse { + 0%, 20%, 30%, 35%, 45%, 100% {transform: scale(1);} + 25%, 40% {transform: scale(1.3);} +} + +// Media queries. +// -------------------------------------------------------- +@media screen and (max-width: ($page-max-width + (2 * $container-padding-md))) { + :root {--container-padding: #{$container-padding-md};} + .page-container { + padding-left: var(--container-padding); + padding-right: var(--container-padding); + } +} + +@media screen and (max-width: (500 + (2 * $container-padding-sm))) { + :root {--container-padding: #{$container-padding-sm};} +} diff --git a/app/src/plugins/app@vendor/vue-cal/scss/_variables.scss b/app/src/plugins/app@vendor/vue-cal/scss/_variables.scss new file mode 100644 index 0000000..95dfca6 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/scss/_variables.scss @@ -0,0 +1,16 @@ +$primary: #42b983; +$page-max-width: 1200px; +$container-padding-md: 20px; +$container-padding-sm: 12px; + +@mixin backdrop-blur($blur: 4px, $fallback-color: rgba(255, 255, 255, 0.9), $blur-color: rgba(255, 255, 255, 0.6)) { + background-color: $fallback-color; // Slightly transparent fallback by default. + + // If backdrop support: very transparent and blurred. + @supports ((-webkit-backdrop-filter: none) or (backdrop-filter: none)) { + & { + background-color: $blur-color; + backdrop-filter: blur($blur); + } + } +} diff --git a/app/src/plugins/app@vendor/vue-cal/scss/index.scss b/app/src/plugins/app@vendor/vue-cal/scss/index.scss new file mode 100644 index 0000000..81f5aa4 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/scss/index.scss @@ -0,0 +1,2 @@ +@import './variables'; +@import './base'; diff --git a/app/src/plugins/app@vendor/vue-cal/styles.scss b/app/src/plugins/app@vendor/vue-cal/styles.scss new file mode 100644 index 0000000..3ff86cf --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/styles.scss @@ -0,0 +1,323 @@ +$time-column-width: 3em; +$week-numbers-width: 3em; +$time-column-width-12: 4em; // twelve-hour clock shows am/pm. +$weekdays-headings-height: 2.8em; +$weekdays-headings-height-with-splits: 3.4em; +$splits-headers-height: 1.5em; + +.vuecal { + height: 100%; + box-shadow: 0 0 0 1px inset rgba(0, 0, 0, 0.08); + + // Disable user selection everywhere except in events. + * {user-select: none;} + // On touch device, disable everywhere except the event title if editable. + &--has-touch *:not(.vuecal__event-title--edit) { + -webkit-touch-callout: none; + user-select: none; + } + + // General. + //==================================// + .clickable {cursor: pointer;} + + &--resizing-event, &--drag-creating-event {cursor: ns-resize;} + &--dragging-event { + cursor: move; + cursor: -webkit-grabbing; + cursor: grabbing; + } + + .dragging-helper { + position: absolute; + width: 60px; + height: 40px; + background: rgba(138, 190, 230, 0.8); + border: 1px solid #61a9e0; + z-index: 10; + // transition: 0.2s transform; + } + + &--xsmall {font-size: 0.9em;} + + &__flex { + display: flex; + flex-direction: row; + + &[column] { + flex-direction: column; + flex: 1 1 auto; + } + + &[grow] { + flex: 1 1 auto; + width: 100%; + } + + &[wrap] { + flex-wrap: wrap; + } + } + + // Calendar header. + //==================================// + &__split-days-headers { + &.slide-fade--right-leave-active {display: none;} + + .vuecal--week-numbers.vuecal--month-view & {margin-left: $week-numbers-width;} + .vuecal--day-view:not(.vuecal--overflow-x) & {margin-left: $time-column-width;height: 2.2em;} + .vuecal--day-view.vuecal--twelve-hour:not(.vuecal--overflow-x) & {margin-left: $time-column-width-12;} + + .day-split-header { + display: flex; + flex-grow: 1; + flex-basis: 0; // Evenly distribute. + justify-content: center; + align-items: center; + height: 100%; + } + + .vuecal--day-view.vuecal--overflow-x.vuecal--sticky-split-labels .day-split-header {height: $splits-headers-height;} + } + + // Calendar body. + //==================================// + &__body { + position: relative; + overflow: hidden; + } + + &__all-day { + min-height: 1.7em; + margin-bottom: -1px; + flex-shrink: 0; + + &-text { + width: $time-column-width; + box-sizing: border-box; + color: #999; + padding-right: 2px; + display: flex; + flex-shrink: 0; + align-items: center; + justify-content: flex-end; + border-bottom: 1px solid #ddd; + hyphens: auto; + + span { + font-size: 0.85em; + text-align: right; + line-height: 1.1; + } + } + .vuecal--twelve-hour & > span {width: $time-column-width-12;} + } + + &__bg { + overflow: auto; + overflow-x: hidden; // Prevent horizontal scroll bar while transitioning. + -webkit-overflow-scrolling: touch; + min-height: 60px; + position: relative; + width: 100%; + // margin-bottom: 1px; + } + + &--no-time &__bg { + display: flex; + flex: 1 1 auto; + overflow: auto; + } + + &__week-numbers { + width: $week-numbers-width; + flex-shrink: 0 !important; + + .vuecal__week-number-cell { + opacity: 0.4; + font-size: 0.9em; + align-items: center; + justify-items: center; + justify-content: center; + } + } + + &__scrollbar-check { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow: scroll; + visibility: hidden; + z-index: -1; + + & div {height: 120%;} + } + + &__time-column { + width: $time-column-width; + height: 100%; + flex-shrink: 0; + + .vuecal--twelve-hour & { + width: $time-column-width-12; + font-size: 0.9em; + } + + .vuecal--overflow-x.vuecal--week-view & { + margin-top: $weekdays-headings-height; + box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3); + } + + .vuecal--overflow-x.vuecal--week-view.vuecal--sticky-split-labels & { + margin-top: $weekdays-headings-height-with-splits; + } + .vuecal--overflow-x.vuecal--day-view.vuecal--sticky-split-labels & {margin-top: $splits-headers-height;} + + .vuecal__time-cell { + color: #999; + text-align: right; + padding-right: 2px; + font-size: 0.9em; + } + + .vuecal__time-cell-line:before { + content: ''; + position: absolute; + left: 0; + right: 0; + border-top: 1px solid rgba(196, 196, 196, 0.3); + } + } + + // Calendar cells. + //==================================// + &__cells { + margin: 0 1px 1px 0; + .vuecal--overflow-x.vuecal--day-view & {margin: 0;} + + .vuecal--events-on-month-view.vuecal--short-events & {width: 99.9%;} + + .vuecal--overflow-x.vuecal--day-view &, + .vuecal--overflow-x.vuecal--week-view & { + flex-wrap: nowrap; + overflow: auto; + } + } +} + +// Transitions. +//==================================// +.slide-fade--left-enter-active, .slide-fade--left-leave-active, +.slide-fade--right-enter-active, .slide-fade--right-leave-active { + transition: 0.25s ease-out; +} + +.slide-fade--left-enter-from, +.slide-fade--right-leave-to { + transform: translateX(-15px); + opacity: 0; +} + +.slide-fade--left-leave-to, +.slide-fade--right-enter-from { + transform: translateX(15px); + opacity: 0; +} + +.slide-fade--left-leave-active, +.slide-fade--right-leave-active {position: absolute !important;height: 100%;} +.vuecal__title-bar .slide-fade--left-leave-active, +.vuecal__title-bar .slide-fade--right-leave-active {left: 0;right: 0;height: auto;} +.vuecal__heading .slide-fade--left-leave-active, +.vuecal__heading .slide-fade--right-leave-active {display: flex;align-items: center;} + +// Themes. +//==================================// +.vuecal--green-theme { + .vuecal__menu, .vuecal__cell-events-count {background-color: #42b983;color: #fff;} + .vuecal__title-bar {background-color: #e4f5ef;} + .vuecal__cell--today, .vuecal__cell--current {background-color: rgba(240, 240, 255, 0.4);} + &:not(.vuecal--day-view) .vuecal__cell--selected {background-color: rgba(235, 255, 245, 0.4);} + .vuecal__cell--selected:before {border-color: rgba(66, 185, 131, 0.5);} + .vuecal__cell--highlighted:not(.vuecal__cell--has-splits), + .vuecal__cell-split--highlighted {background-color: rgba(195, 255, 225, 0.5);} + .vuecal__arrow--highlighted, + .vuecal__today-btn--highlighted, + .vuecal__view-btn--highlighted {background-color: rgba(136, 236, 191, 0.25);} +} + +.vuecal--blue-theme { + .vuecal__menu, .vuecal__cell-events-count {background-color: rgba(66, 163, 185, 0.8);color: #fff;} + .vuecal__title-bar {background-color: rgba(0, 165, 188, 0.3);} + .vuecal__cell--today, .vuecal__cell--current {background-color: rgba(240, 240, 255, 0.4);} + &:not(.vuecal--day-view) .vuecal__cell--selected {background-color: rgba(235, 253, 255, 0.4);} + .vuecal__cell--selected:before {border-color: rgba(115, 191, 204, 0.5);} + .vuecal__cell--highlighted:not(.vuecal__cell--has-splits), + .vuecal__cell-split--highlighted {background-color: rgba(0, 165, 188, 0.06);} + .vuecal__arrow--highlighted, + .vuecal__today-btn--highlighted, + .vuecal__view-btn--highlighted {background-color: rgba(66, 163, 185, 0.2);} +} + +// Rounded cells. +.vuecal--rounded-theme { + .vuecal__weekdays-headings {border: none;} + .vuecal__cell, .vuecal__cell:before {background: none;border: none;} + .vuecal__cell--out-of-scope {opacity: 0.4;} + .vuecal__cell-content { + width: 30px; + height: 30px; + flex-grow: 0; + border: 1px solid transparent; + border-radius: 30px; + color: #333; + } + &.vuecal--day-view .vuecal__cell-content {width: auto;background: none;} + &.vuecal--year-view .vuecal__cell {width: 33.33%;} + &.vuecal--year-view .vuecal__cell-content {width: 85px;} + &.vuecal--years-view .vuecal__cell-content {width: 52px;} + .vuecal__cell {background-color: transparent !important;} + + &.vuecal--green-theme { + &:not(.vuecal--day-view) .vuecal__cell-content {background-color: #f1faf7;} + &:not(.vuecal--day-view) .vuecal__cell--today .vuecal__cell-content {background-color: #42b983;color: #fff;} + .vuecal--day-view .vuecal__cell--today:before {background-color: rgba(66, 185, 131, 0.05);} + &:not(.vuecal--day-view) .vuecal__cell--selected .vuecal__cell-content {border-color: #42b983;} + .vuecal__cell--highlighted:not(.vuecal__cell--has-splits), + .vuecal__cell-split--highlighted {background-color: rgba(195, 255, 225, 0.5);} + } + + &.vuecal--blue-theme { + &:not(.vuecal--day-view) .vuecal__cell-content {background-color: rgba(100, 182, 255, 0.2);} + &:not(.vuecal--day-view) .vuecal__cell--today .vuecal__cell-content {background-color: #8fb7e4;color: #fff;} + .vuecal--day-view .vuecal__cell--today:before {background-color: rgba(143, 183, 228, 0.1);} + &:not(.vuecal--day-view) .vuecal__cell--selected .vuecal__cell-content {border-color: #61a9e0;} + .vuecal__cell--highlighted:not(.vuecal__cell--has-splits), + .vuecal__cell-split--highlighted {background-color: rgba(0, 165, 188, 0.06);} + } +} + +.vuecal--date-picker { + .vuecal__title-bar {font-size: 1.2em;} + .vuecal__heading {height: 2.2em;font-weight: 500;opacity: 0.4;} + .vuecal__weekdays-headings {border: none;} + .vuecal__body {margin-left: 1px;} + .vuecal__cell, .vuecal__cell:before {background: none;border: none;} + .vuecal__cell-content { + height: 26px; + flex-grow: 0; + border: 1px solid transparent; + border-radius: 25px; + transition: background-color 0.2s cubic-bezier(0.39, 0.58, 0.57, 1); + } + &.vuecal--years-view .vuecal__cell-content {flex: 0;padding: 0 4px;height: 24px;} + &.vuecal--year-view .vuecal__cell-content {flex: 0;padding: 0 15px;} + &.vuecal--month-view .vuecal__cell-content {width: 26px;} + + &:not(.vuecal--day-view) .vuecal__cell-content:hover {background-color: rgba(0, 0, 0, 0.1);} + &:not(.vuecal--day-view) .vuecal__cell--selected .vuecal__cell-content {background-color: #42b982;color: #fff;} + &:not(.vuecal--day-view) .vuecal__cell--current .vuecal__cell-content, + &:not(.vuecal--day-view) .vuecal__cell--today .vuecal__cell-content {border-color: #42b982;} +} diff --git a/app/src/plugins/app@vendor/vue-cal/utils/cell.js b/app/src/plugins/app@vendor/vue-cal/utils/cell.js new file mode 100644 index 0000000..ff3f679 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/utils/cell.js @@ -0,0 +1,94 @@ +/** + * Cell Utils. + * + * Waiting for VS Code to support JavaScript private fields. + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Class_fields + * Meantime keep `_` for private. + */ + +const minutesInADay = 24 * 60 + +export default class CellUtils { + _vuecal = null + + constructor (vuecal) { + this._vuecal = vuecal + } + + /** + * Select a cell and go to narrower view on double click or single click according to vuecalProps option. + * + * @param {Boolean} force Force switching to narrower view. + * @param {Date} date The selected cell date at the exact time where it was clicked (through cursor coords). + * @param {Integer} split The selected cell split if any. + */ + selectCell = (force = false, date, split) => { + // Cell-click event returns a date and time at cursor position. + this._vuecal.$emit('cell-click', split ? { date, split } : date) + + // Switch to narrower view. + if (this._vuecal.clickToNavigate || force) this._vuecal.switchToNarrowerView() + + // Handle double click manually for touch devices. + else if (this._vuecal.dblclickToNavigate && 'ontouchstart' in window) { + this._vuecal.domEvents.dblTapACell.taps++ + + setTimeout(() => (this._vuecal.domEvents.dblTapACell.taps = 0), this._vuecal.domEvents.dblTapACell.timeout) + + if (this._vuecal.domEvents.dblTapACell.taps >= 2) { + this._vuecal.domEvents.dblTapACell.taps = 0 + this._vuecal.switchToNarrowerView() + this._vuecal.$emit('cell-dblclick', split ? { date, split } : date) + } + } + } + + /** + * Select a cell and go to narrower view on enter. + * + * @param {Boolean} force Force switching to narrower view. + * @param {Date} date The selected cell date at the exact time where it was clicked (through cursor coords). + * @param {Integer} split The selected cell split if any. + */ + keyPressEnterCell = (date, split) => { + // Cell-key-press-enter event returns a date and time at cursor position. + this._vuecal.$emit('cell-keypress-enter', split ? { date, split } : date) + + // Switch to narrower view. + this._vuecal.switchToNarrowerView() + } + + /** + * Get the coordinates of the mouse cursor from the cells wrapper referential (`ref="cells"`). + * + * @todo Cache bounding box & update it on resize. + * @param {Object} e the native DOM event object. + * @return {Object} containing { x: {Number}, y: {Number} } + */ + getPosition = e => { + const { left, top } = this._vuecal.cellsEl.getBoundingClientRect() + const { clientX, clientY } = 'ontouchstart' in window && e.touches ? e.touches[0] : e + return { x: clientX - left, y: clientY - top } + } + + /** + * Get the number of minutes from the top to the mouse cursor. + * Returns a constrained time between 0 and 24 * 60. + * + * @param {Object} e the native DOM event object. + * @return {Object} containing { minutes: {Number}, cursorCoords: { x: {Number}, y: {Number} } } + */ + minutesAtCursor = e => { + let minutes = 0 + let cursorCoords = { x: 0, y: 0 } + const { timeStep, timeCellHeight, timeFrom } = this._vuecal.$props + + if (typeof e === 'number') minutes = e + else if (typeof e === 'object') { + cursorCoords = this.getPosition(e) + minutes = Math.round(cursorCoords.y * timeStep / parseInt(timeCellHeight) + timeFrom) + } + + return { minutes: Math.max(Math.min(minutes, minutesInADay), 0), cursorCoords } + } +} diff --git a/app/src/plugins/app@vendor/vue-cal/utils/date.js b/app/src/plugins/app@vendor/vue-cal/utils/date.js new file mode 100644 index 0000000..e6d7a89 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/utils/date.js @@ -0,0 +1,376 @@ +/** + * Date Utils & prototypes. + * + * Waiting for VS Code to support JavaScript private fields. + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Class_fields + * Meantime keep `_` for private. + */ + +let now, todayDate, todayF, self +let _dateObject = {} +let _timeObject = {} + +export default class DateUtils { + texts = {} + + constructor (texts, noPrototypes = false) { + self = this // For use in Date prototypes. + this._texts = texts + + // Add prototypes ASAP - only once. + if (!noPrototypes && Date && !Date.prototype.addDays) this._initDatePrototypes() + + // @todo: This would be nicer, but how to set Date.noPrototypes ASAP only if user wants? + // if (Date.noPrototypes) delete Date.noPrototypes + // else this._initDatePrototypes() + } + + _initDatePrototypes () { + /* eslint-disable no-extend-native */ + Date.prototype.addDays = function (days) { return self.addDays(this, days) } + Date.prototype.subtractDays = function (days) { return self.subtractDays(this, days) } + Date.prototype.addHours = function (hours) { return self.addHours(this, hours) } + Date.prototype.subtractHours = function (hours) { return self.subtractHours(this, hours) } + Date.prototype.addMinutes = function (minutes) { return self.addMinutes(this, minutes) } + Date.prototype.subtractMinutes = function (minutes) { return self.subtractMinutes(this, minutes) } + Date.prototype.getWeek = function () { return self.getWeek(this) } + Date.prototype.isToday = function () { return self.isToday(this) } + Date.prototype.isLeapYear = function () { return self.isLeapYear(this) } + Date.prototype.format = function (format = 'YYYY-MM-DD') { return self.formatDate(this, format) } + Date.prototype.formatTime = function (format = 'HH:mm') { return self.formatTime(this, format) } + /* eslint-enable no-extend-native */ + } + + removePrototypes () { + delete Date.prototype.addDays + delete Date.prototype.subtractDays + delete Date.prototype.addHours + delete Date.prototype.subtractHours + delete Date.prototype.addMinutes + delete Date.prototype.subtractMinutes + delete Date.prototype.getWeek + delete Date.prototype.isToday + delete Date.prototype.isLeapYear + delete Date.prototype.format + delete Date.prototype.formatTime + } + + updateTexts (texts) { + this._texts = texts + } + + // Cache Today's date (to a maximum) for better isToday() performances. Formatted without leading 0. + // We still need to update Today's date when Today changes without page refresh. + _todayFormatted () { + if (todayDate !== (new Date()).getDate()) { + now = new Date() + todayDate = now.getDate() + todayF = `${now.getFullYear()}-${now.getMonth()}-${now.getDate()}` + } + + return todayF + } + + // UTILITIES. + // ==================================================================== + addDays (date, days) { + const d = new Date(date.valueOf()) + d.setDate(d.getDate() + days) + return d + } + + subtractDays (date, days) { + const d = new Date(date.valueOf()) + d.setDate(d.getDate() - days) + return d + } + + addHours (date, hours) { + const d = new Date(date.valueOf()) + d.setHours(d.getHours() + hours) + return d + } + + subtractHours (date, hours) { + const d = new Date(date.valueOf()) + d.setHours(d.getHours() - hours) + return d + } + + addMinutes (date, minutes) { + const d = new Date(date.valueOf()) + d.setMinutes(d.getMinutes() + minutes) + return d + } + + subtractMinutes (date, minutes) { + const d = new Date(date.valueOf()) + d.setMinutes(d.getMinutes() - minutes) + return d + } + + getWeek (date) { + const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())) + const dayNum = d.getUTCDay() || 7 + d.setUTCDate(d.getUTCDate() + 4 - dayNum) + const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1)) + return Math.ceil((((d - yearStart) / 86400000) + 1) / 7) + } + + isToday (date) { + return `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}` === this._todayFormatted() + } + + isLeapYear (date) { + const year = date.getFullYear() + return !(year % 400) || (year % 100 && !(year % 4)) + } + + // Returns today if it's FirstDayOfWeek (Monday or Sunday) or previous FirstDayOfWeek otherwise. + getPreviousFirstDayOfWeek (date = null, weekStartsOnSunday) { + const prevFirstDayOfWeek = (date && new Date(date.valueOf())) || new Date() + const dayModifier = weekStartsOnSunday ? 7 : 6 + prevFirstDayOfWeek.setDate(prevFirstDayOfWeek.getDate() - (prevFirstDayOfWeek.getDay() + dayModifier) % 7) + return prevFirstDayOfWeek + } + + /** + * Converts a string to a Javascript Date object. If a Date object is passed, return it as is. + * + * @param {String | Date} date the string to convert to Date. + * @return {Date} the equivalent Javascript Date object. + */ + stringToDate (date) { + if (date instanceof Date) return date + // Regexp way is less performant: https://jsperf.com/string-to-date-regexp-vs-new-date + // const [, y, m, d, h = 0, min = 0] = date.match(/(\d{4})-(\d{2})-(\d{2})(?: (\d{2}):(\d{2}))?/) + // return new Date(y, parseInt(m) - 1, d, h, min) + + if (date.length === 10) date += ' 00:00' + return new Date(date.replace(/-/g, '/')) // replace '-' with '/' for Safari. + } + + /** + * Simply takes a Date and returns the associated time in minutes (sum of hours + minutes). + * + * @param {Date} date the JavaScript Date to extract minutes from. + * @return {Number} the number of minutes (total of hours plus minutes). + */ + dateToMinutes = date => date.getHours() * 60 + date.getMinutes() + + /** + * Count the number of days this date range spans onto. + * E.g. countDays(2019-11-02 18:00, 2019-11-03 02:00) = 2 + * + * @param {String | Date} start the start date + * @param {String | Date} end the end date + * @return {Integer} The number of days this date range involves + */ + countDays (start, end) { + // replace '-' with '/' for Safari. + if (typeof start === 'string') start = start.replace(/-/g, '/') + if (typeof end === 'string') end = end.replace(/-/g, '/') + + // Set start & end at midnight then compare the delta. Don't modify the original dates. + start = (new Date(start)).setHours(0, 0, 0, 0) + // Set end at midnight plus 1 sec, so Math.ceil will round it up to a full day. + end = (new Date(end)).setHours(0, 0, 1, 0) + + // Remove the potential daylight saving delta. + const timezoneDiffMs = (new Date(end).getTimezoneOffset() - new Date(start).getTimezoneOffset()) * 60 * 1000 + return Math.ceil((end - start - timezoneDiffMs) / (24 * 3600 * 1000)) + } + + /** + * Take 2 dates and check if within the same time step (useful in overlapping events). + * + * @return {Boolean} `true` if their time is included in the same time step, + * this means these 2 dates are very close. + */ + datesInSameTimeStep (date1, date2, timeStep) { + return Math.abs(date1.getTime() - date2.getTime()) <= timeStep * 60 * 1000 + } + // ==================================================================== + + // FORMATTERS. + // ==================================================================== + /** + * Formats a date/time to the given format and returns the formatted string. + * + * @param {Date} date a JavaScript Date object to format. + * @param {String} format the wanted format. + * @param {Object} texts Optional: the localized texts object to override the vue-cal one in this._texts. + * This becomes useful when showing multiple instances with different languages, + * like in the documentation page. + * @return {String} the formatted date. + */ + formatDate (date, format = 'YYYY-MM-DD', texts = null) { + if (!texts) texts = this._texts + if (!format) format = 'YYYY-MM-DD' // Allows passing null for default format. + if (format === 'YYYY-MM-DD') return this.formatDateLite(date) + + // Reinit the date and time object on each function call. + _dateObject = {} + _timeObject = {} + + // Each keyword is a function to load the dateObject or timeObject on demand: no wasted resource. + const dateObj = { + YYYY: () => this._hydrateDateObject(date, texts).YYYY, + YY: () => this._hydrateDateObject(date, texts).YY(), + M: () => this._hydrateDateObject(date, texts).M, + MM: () => this._hydrateDateObject(date, texts).MM(), + MMM: () => this._hydrateDateObject(date, texts).MMM(), + MMMM: () => this._hydrateDateObject(date, texts).MMMM(), + MMMMG: () => this._hydrateDateObject(date, texts).MMMMG(), + D: () => this._hydrateDateObject(date, texts).D, + DD: () => this._hydrateDateObject(date, texts).DD(), + S: () => this._hydrateDateObject(date, texts).S(), + d: () => this._hydrateDateObject(date, texts).d, + dd: () => this._hydrateDateObject(date, texts).dd(), + ddd: () => this._hydrateDateObject(date, texts).ddd(), + dddd: () => this._hydrateDateObject(date, texts).dddd(), + HH: () => this._hydrateTimeObject(date, texts).HH, + H: () => this._hydrateTimeObject(date, texts).H, + hh: () => this._hydrateTimeObject(date, texts).hh, + h: () => this._hydrateTimeObject(date, texts).h, + am: () => this._hydrateTimeObject(date, texts).am, + AM: () => this._hydrateTimeObject(date, texts).AM, + mm: () => this._hydrateTimeObject(date, texts).mm, + m: () => this._hydrateTimeObject(date, texts).m + } + + return format.replace(/(\{[a-zA-Z]+\}|[a-zA-Z]+)/g, (m, contents) => { + const result = dateObj[contents.replace(/\{|\}/g, '')] + return result !== undefined ? result() : contents + }) + } + + // More performant function to convert a Date to `YYYY-MM-DD` formatted string only. + formatDateLite (date) { + const m = date.getMonth() + 1 + const d = date.getDate() + return `${date.getFullYear()}-${m < 10 ? '0' : ''}${m}-${d < 10 ? '0' : ''}${d}` + } + + /** + * Formats a time (from Date or number of mins) to the given format and returns the formatted string. + * + * @param {Date | Number} date a JavaScript Date object or a time in minutes. + * @param {String} format the wanted format. + * @param {Object} texts Optional: the localized texts object to override the vue-cal one in this._texts. + * This becomes useful when showing multiple instances with different languages, + * like in the documentation page. + * @param {Boolean} round if time is 23:59:59, rounds up to 24:00 for formatting only. + * @return {String} the formatted time. + */ + formatTime (date, format = 'HH:mm', texts = null, round = false) { + let shouldRound = false + if (round) { + const [h, m, s] = [date.getHours(), date.getMinutes(), date.getSeconds()] + if ((h + m + s) === (23 + 59 + 59)) shouldRound = true + } + + if (date instanceof Date && format === 'HH:mm') return shouldRound ? '24:00' : this.formatTimeLite(date) + + _timeObject = {} // Reinit the time object on each function call. + if (!texts) texts = this._texts + const timeObj = this._hydrateTimeObject(date, texts) + + const formatted = format.replace(/(\{[a-zA-Z]+\}|[a-zA-Z]+)/g, (m, contents) => { + const result = timeObj[contents.replace(/\{|\}/g, '')] + return result !== undefined ? result : contents + }) + + // Round 23:59:59 to 24:00. For 12-hour format there is nothing to replace: as both are 12am. + // Also don't return `24:00` straight away as the user format may be different. + return shouldRound ? formatted.replace('23:59', '24:00') : formatted + } + + /** + * Formats a time to 'HH:mm' from a Date and returns the formatted string. + * + * @param {Date} date a JavaScript Date object to format. + * @return {String} the formatted time. + */ + formatTimeLite (date) { + const h = date.getHours() + const m = date.getMinutes() + return `${(h < 10 ? '0' : '') + h}:${(m < 10 ? '0' : '') + m}` + } + + _nth (d) { + if (d > 3 && d < 21) return 'th' + switch (d % 10) { + case 1: return 'st' + case 2: return 'nd' + case 3: return 'rd' + default: return 'th' + } + } + + _hydrateDateObject (date, texts) { + if (_dateObject.D) return _dateObject + + const YYYY = date.getFullYear() + const M = date.getMonth() + 1 + const D = date.getDate() + const day = date.getDay() // Day of the week. + const dayNumber = (day - 1 + 7) % 7 // Day of the week. 0 to 6 with 6 = Sunday. + // Some of this props are functions, to only calculate on demand. + _dateObject = { + // Year. + YYYY, // 2019. + YY: () => YYYY.toString().substring(2), // 19. + + // Month. + M, // 1 to 12. + MM: () => (M < 10 ? '0' : '') + M, // 01 to 12. + MMM: () => texts.months[M - 1].substring(0, 3), // Jan to Dec. + MMMM: () => texts.months[M - 1], // January to December. + MMMMG: () => (texts.monthsGenitive || texts.months)[M - 1], // January to December in genitive form (Greek...) + + // Day. + D, // 1 to 31. + DD: () => (D < 10 ? '0' : '') + D, // 01 to 31. + S: () => this._nth(D), // st, nd, rd, th. + + // Day of the week. + d: dayNumber + 1, // 1 to 7 with 7 = Sunday. + dd: () => texts.weekDays[dayNumber][0], // M to S. + ddd: () => texts.weekDays[dayNumber].substr(0, 3), // Mon to Sun. + dddd: () => texts.weekDays[dayNumber] // Monday to Sunday. + } + + return _dateObject + } + + _hydrateTimeObject (date, texts) { + if (_timeObject.am) return _timeObject + + let H, m + if (date instanceof Date) { + H = date.getHours() + m = date.getMinutes() + } + else { + H = Math.floor(date / 60) + m = Math.floor(date % 60) + } + + const h = H % 12 ? H % 12 : 12 + const am = (texts || { am: 'am', pm: 'pm' })[H === 24 || H < 12 ? 'am' : 'pm'] + _timeObject = { + H, + h, + HH: (H < 10 ? '0' : '') + H, + hh: (h < 10 ? '0' : '') + h, + am, + AM: am.toUpperCase(), + m, + mm: (m < 10 ? '0' : '') + m + } + + return _timeObject + } + // ==================================================================== +} diff --git a/app/src/plugins/app@vendor/vue-cal/utils/event.js b/app/src/plugins/app@vendor/vue-cal/utils/event.js new file mode 100644 index 0000000..c6d752c --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/utils/event.js @@ -0,0 +1,426 @@ +/** + * Events Utils. + * + * Waiting for VS Code to support JavaScript private fields. + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Class_fields + * Meantime keep `_` for private. + */ + +const defaultEventDuration = 2 // In hours. +const minutesInADay = 24 * 60 // Don't do the maths every time. + +let ud + +// Event overlaps: only for the current view, recreated on view change. +let _cellOverlaps, _comparisonArray + +// This is an approximation, it will not work with DLS time. +// const approxDayMilliseconds = minutesInADay * 60 * 1000 +// This is an approximate minimum we can get in a year. Purposely stay below 365 but close. +// const minYearMilliseconds = 364 * approxDayMilliseconds // Don't do the maths every time. + +export default class EventUtils { + _vuecal = null + + eventDefaults = { + _eid: null, + start: '', // Externally given formatted date & time or Date object. + startTimeMinutes: 0, + end: '', // Externally given formatted date & time or Date object. + endTimeMinutes: 0, + title: '', + content: '', + background: false, + allDay: false, + segments: null, + repeat: null, + daysCount: 1, + deletable: true, + deleting: false, + titleEditable: true, + resizable: true, + resizing: false, + draggable: true, + dragging: false, + draggingStatic: false, // Controls the CSS class of the static clone while dragging. + focused: false, + class: '' + } + + constructor (vuecal, dateUtils) { + this._vuecal = vuecal + ud = dateUtils + } + + /** + * Create an event at the given date and time, and allow overriding + * event attributes through the eventOptions object. + * + * @param {Date | String} dateTime The date and time of the new event start. + * @param {Number} duration the event duration in minutes. + * @param {Object} eventOptions some options to override the `eventDefaults` - optional. + */ + createAnEvent (dateTime, duration, eventOptions) { + if (typeof dateTime === 'string') dateTime = ud.stringToDate(dateTime) + if (!(dateTime instanceof Date)) return false + + const startTimeMinutes = ud.dateToMinutes(dateTime) + duration = duration * 1 || defaultEventDuration * 60 + const endTimeMinutes = startTimeMinutes + duration + const end = ud.addMinutes(new Date(dateTime), duration) + + // Automatically add the required endTimeMinutes when passing an end. + if (eventOptions.end) { + if (typeof eventOptions.end === 'string') eventOptions.end = ud.stringToDate(eventOptions.end) + eventOptions.endTimeMinutes = ud.dateToMinutes(eventOptions.end) + } + + const event = { + ...this.eventDefaults, + _eid: `${this._vuecal._.uid}_${this._vuecal.eventIdIncrement++}`, + start: dateTime, + startTimeMinutes, + end, + endTimeMinutes, + segments: null, + ...eventOptions + } + + // If the onEventCreate() function is given as a parameter to vue-cal: + // 1. give it access to the created event & the deleteAnEvent() function. + // 2. Prevent creation of the event if this function returns false. + if (typeof this._vuecal.onEventCreate === 'function') { + if (!this._vuecal.onEventCreate(event, () => this.deleteAnEvent(event))) return + } + + // Check if event is a multiple day event and update days count. + if (event.startDateF !== event.endDateF) { + event.daysCount = ud.countDays(event.start, event.end) + } + + // Add event to the mutableEvents array. + this._vuecal.mutableEvents.push(event) + + // Add the new event to the current view. + // The event may have been edited on the fly to become a multiple-day event, + // the method addEventsToView makes sure the segments are created. + this._vuecal.addEventsToView([event]) + + this._vuecal.emitWithEvent('event-create', event) + this._vuecal.$emit('event-change', { event: this._vuecal.cleanupEvent(event), originalEvent: null }) + + return event + } + + /** + * Add an event segment (= day) to a multiple-day event. + * + * @param {Object} e the multiple-day event to add segment in. + */ + addEventSegment (e) { + // If event was previously single-day, event.segments = null, + // so first create the first segment (first day). + if (!e.segments) { + e.segments = {} + e.segments[ud.formatDateLite(e.start)] = { + start: e.start, + startTimeMinutes: e.startTimeMinutes, + endTimeMinutes: minutesInADay, + isFirstDay: true, + isLastDay: false + } + } + + // Modify the last segment - which will not stay the last one after this function. + const previousSegment = e.segments[ud.formatDateLite(e.end)] + // previousSegment might not exist when dragging too fast, prevent errors. + if (previousSegment) { + previousSegment.isLastDay = false + previousSegment.endTimeMinutes = minutesInADay + } + else { + // @todo: when moving fast might lose the previousSegment. + // Trying to update it would then result in an error, but do nothing would create a visual bug. + } + + // Create the new last segment. + const start = ud.addDays(e.end, 1) + const formattedDate = ud.formatDateLite(start) + start.setHours(0, 0, 0, 0) + e.segments[formattedDate] = { + start, + startTimeMinutes: 0, + endTimeMinutes: e.endTimeMinutes, + isFirstDay: false, + isLastDay: true + } + + e.end = ud.addMinutes(start, e.endTimeMinutes) + e.daysCount = Object.keys(e.segments).length + + return formattedDate + } + + /** + * Remove an event segment (= day) from a multiple-day event. + * + * @param {Object} e the multiple-day event to remove segments from. + */ + removeEventSegment (e) { + let segmentsCount = Object.keys(e.segments).length + if (segmentsCount <= 1) return ud.formatDateLite(e.end) + + // Remove the last segment. + delete e.segments[ud.formatDateLite(e.end)] + segmentsCount-- + + const end = ud.subtractDays(e.end, 1) + const formattedDate = ud.formatDateLite(end) + const previousSegment = e.segments[formattedDate] + + // If no more segments, reset the segments attribute to null. + if (!segmentsCount) e.segments = null + + // previousSegment might not exist when dragging too fast, prevent errors. + else if (previousSegment) { + // Modify the new last segment. + previousSegment.isLastDay = true + previousSegment.endTimeMinutes = e.endTimeMinutes + } + else { + // @todo: when moving fast might lose the previousSegment. + // Trying to update it would then result in an error, but do nothing would create a visual bug. + } + + e.daysCount = segmentsCount || 1 + e.end = end + + return formattedDate + } + + /** + * Create 1 segment per day of the given event, but only within the current view. + * (It won't create segments for all the days in view that are not in the event!) + * + * An event segment is a piece of event per day that contains more day-specific data. + * + * @param {Object} e the multiple-day event to create segments for. + * @param {Date} viewStartDate the starting date of the view. + * @param {Date} viewEndDate the ending date of the view. + */ + createEventSegments (e, viewStartDate, viewEndDate) { + const viewStartTimestamp = viewStartDate.getTime() + const viewEndTimestamp = viewEndDate.getTime() + let eventStart = e.start.getTime() + let eventEnd = e.end.getTime() + let repeatedEventStartFound = false + let timestamp, end, eventStartAtMidnight + + // @todo: I don't think we still need that: + // Removing 1 sec when ending at 00:00, so that we don't create a segment for nothing on last day. + if (!e.end.getHours() && !e.end.getMinutes()) eventEnd -= 1000 + + e.segments = {} + + // The goal is to create 1 segment per day in the event, but only within the current view. + if (!e.repeat) { // Simple case first. + timestamp = Math.max(viewStartTimestamp, eventStart) + end = Math.min(viewEndTimestamp, eventEnd) + } + else { + // Start at the beginning of the range, and end at soonest between `repeat.until` if any or range end. + // This range will most likely be too large (e.g. whole week) and we need to narrow it + // down in the while loop below. + // We must not create unused segments, it would break the render or result in weird behaviors. + timestamp = viewStartTimestamp + end = Math.min( + viewEndTimestamp, + e.repeat.until ? ud.stringToDate(e.repeat.until).getTime() : viewEndTimestamp + ) + } + + while (timestamp <= end) { + let createSegment = false + // Be careful not to simply add 24 hours! + // In case of DLS, that would cause the event to never end and browser to hang. + // So use `addDays(1)` instead. + const nextMidnight = ud.addDays(new Date(timestamp), 1).setHours(0, 0, 0, 0) + let isFirstDay, isLastDay, start, formattedDate + + if (e.repeat) { + const tmpDate = new Date(timestamp) + const tmpDateFormatted = ud.formatDateLite(tmpDate) + // If the current day in loop is a known date of the repeated event (in `e.occurrences`), + // then we found the first day of the event repetition, now update the `eventStart` and + // the end of the loop at current day + event days count. + if (repeatedEventStartFound || (e.occurrences && e.occurrences[tmpDateFormatted])) { + if (!repeatedEventStartFound) { + eventStart = e.occurrences[tmpDateFormatted].start + eventStartAtMidnight = new Date(eventStart).setHours(0, 0, 0, 0) + eventEnd = e.occurrences[tmpDateFormatted].end + } + repeatedEventStartFound = true + createSegment = true + } + + isFirstDay = timestamp === eventStartAtMidnight + isLastDay = tmpDateFormatted === ud.formatDateLite(new Date(eventEnd)) + start = new Date(isFirstDay ? eventStart : timestamp) + formattedDate = ud.formatDateLite(start) + // We want to find any potential other repetition of event in same range. + if (isLastDay) repeatedEventStartFound = false + } + else { + createSegment = true + isFirstDay = timestamp === eventStart + isLastDay = end === eventEnd && nextMidnight > end + start = isFirstDay ? e.start : new Date(timestamp) + formattedDate = ud.formatDateLite(isFirstDay ? e.start : start) + } + + if (createSegment) { + e.segments[formattedDate] = { + start, + startTimeMinutes: isFirstDay ? e.startTimeMinutes : 0, + endTimeMinutes: isLastDay ? e.endTimeMinutes : minutesInADay, + isFirstDay, + isLastDay + } + } + + timestamp = nextMidnight + } + + return e + } + + /** + * Delete an event. + * + * @param {Object} event the calendar event to delete. + */ + deleteAnEvent (event) { + this._vuecal.emitWithEvent('event-delete', event) + + // Delete the event globally. + this._vuecal.mutableEvents = this._vuecal.mutableEvents.filter(e => e._eid !== event._eid) + // Delete the event from the current view. + // checkCellOverlappingEvents() will be re-run automatically from the cell computed events. + this._vuecal.view.events = this._vuecal.view.events.filter(e => e._eid !== event._eid) + } + + // EVENT OVERLAPS. + // =================================================================== + // Will recalculate all the overlaps of the current cell OR split. + // cellEvents will contain only the current split events if in a split. + checkCellOverlappingEvents (cellEvents, options) { + _comparisonArray = cellEvents.slice(0) + _cellOverlaps = {} + + // Can't filter background events before calling this function otherwise + // when an event is changed to background it would not update its previous overlaps. + cellEvents.forEach(e => { + // For performance, never compare the current event in the next loops. + // The array is smaller and smaller as we loop. + _comparisonArray.shift() + + if (!_cellOverlaps[e._eid]) _cellOverlaps[e._eid] = { overlaps: [], start: e.start, position: 0 } + _cellOverlaps[e._eid].position = 0 + + _comparisonArray.forEach(e2 => { + if (!_cellOverlaps[e2._eid]) _cellOverlaps[e2._eid] = { overlaps: [], start: e2.start, position: 0 } + + const eventIsInRange = this.eventInRange(e2, e.start, e.end) + const eventsInSameTimeStep = options.overlapsPerTimeStep ? ud.datesInSameTimeStep(e.start, e2.start, options.timeStep) : 1 + // Add to the overlaps array if overlapping. + if (!e.background && !e.allDay && !e2.background && !e2.allDay && eventIsInRange && eventsInSameTimeStep) { + _cellOverlaps[e._eid].overlaps.push(e2._eid) + _cellOverlaps[e._eid].overlaps = [...new Set(_cellOverlaps[e._eid].overlaps)] // Dedupe, most performant way. + + _cellOverlaps[e2._eid].overlaps.push(e._eid) + _cellOverlaps[e2._eid].overlaps = [...new Set(_cellOverlaps[e2._eid].overlaps)] // Dedupe, most performant way. + _cellOverlaps[e2._eid].position++ + } + // Remove from the overlaps array if not overlapping or if 1 of the 2 events is background or all-day long. + else { + let pos1, pos2 + if ((pos1 = (_cellOverlaps[e._eid] || { overlaps: [] }).overlaps.indexOf(e2._eid)) > -1) _cellOverlaps[e._eid].overlaps.splice(pos1, 1) + if ((pos2 = (_cellOverlaps[e2._eid] || { overlaps: [] }).overlaps.indexOf(e._eid)) > -1) _cellOverlaps[e2._eid].overlaps.splice(pos2, 1) + _cellOverlaps[e2._eid].position-- + } + }) + }) + + // Overlaps streak is the longest horizontal set of simultaneous events. + // This is determining the width of events in a streak. + // e.g. 3 overlapping events in a cell: + // ___ ___ + // | 1 | |_2_| 1 overlaps 2 & 3; 2 & 3 don't overlap; + // | | ___ => streak = 2; each width = 50% not 33%. + // |___| |_3_| + let longestStreak = 0 + for (const id in _cellOverlaps) { + const item = _cellOverlaps[id] + + // Calculate the position of each event in current streak (determines the CSS left property). + const overlapsRow = item.overlaps.map(id2 => ({ id: id2, start: _cellOverlaps[id2].start })) + overlapsRow.push({ id, start: item.start }) + overlapsRow.sort((a, b) => a.start < b.start ? -1 : (a.start > b.start ? 1 : (a.id > b.id ? -1 : 1))) + item.position = overlapsRow.findIndex(e => e.id === id) + + longestStreak = Math.max(this.getOverlapsStreak(item, _cellOverlaps), longestStreak) + } + + return [_cellOverlaps, longestStreak] + } + + /** + * Overlaps streak is the longest horizontal set of simultaneous events. + * This is determining the width of each events in this streak. + * E.g. 3 overlapping events [1, 2, 3]; 1 overlaps 2 & 3; 2 & 3 don't overlap; + * => streak = 2; each width = 50% not 33%. + * + * @param {Object} event The current event we are checking among all the events of the current cell. + * @param {Object} cellOverlaps An indexed array of all the events overlaps for the current cell. + * @return {Number} The number of simultaneous event for this event. + */ + getOverlapsStreak (event, cellOverlaps = {}) { + let streak = event.overlaps.length + 1 + let removeFromStreak = [] + event.overlaps.forEach(id => { + if (!removeFromStreak.includes(id)) { + const overlapsWithoutSelf = event.overlaps.filter(id2 => id2 !== id) + overlapsWithoutSelf.forEach(id3 => { + if (!cellOverlaps[id3].overlaps.includes(id)) removeFromStreak.push(id3) + }) + } + }) + + removeFromStreak = [...new Set(removeFromStreak)] // Dedupe, most performant way. + streak -= removeFromStreak.length + return streak + } + + /** + * Tells whether an event is in a given date range, even partially. + * + * @param {Object} event The event to test. + * @param {Date} start The start of range date object. + * @param {Date} end The end of range date object. + * @return {Boolean} true if in range, even partially. + */ + eventInRange (event, start, end) { + // Check if all-day or timeless event (if date but no time there won't be a `:` in event.start). + if (event.allDay || !this._vuecal.time) { + // Get the date and discard the time if any, then check it's within the date range. + const startTimestamp = new Date(event.start).setHours(0, 0, 0, 0) + const endTimestamp = new Date(event.end).setHours(23, 59, 0, 0) + return (endTimestamp >= new Date(start).setHours(0, 0, 0, 0) && + startTimestamp <= new Date(end).setHours(0, 0, 0, 0)) + } + + const startTimestamp = event.start.getTime() + const endTimestamp = event.end.getTime() + return startTimestamp < end.getTime() && endTimestamp > start.getTime() + } +} diff --git a/app/src/plugins/app@vendor/vue-cal/weekdays-headings.vue b/app/src/plugins/app@vendor/vue-cal/weekdays-headings.vue new file mode 100644 index 0000000..2db2834 --- /dev/null +++ b/app/src/plugins/app@vendor/vue-cal/weekdays-headings.vue @@ -0,0 +1,188 @@ + + + + +