feat(view): Introduce year grid view (with FC multiMonthYear plugin)

Closes #159

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2023-05-21 11:05:57 +02:00 committed by Anna Larch
parent f7c5189705
commit e4ab21072b
13 changed files with 83 additions and 14 deletions

View File

@ -31,9 +31,9 @@ return [
['name' => 'view#index', 'url' => '/new/{isAllDay}/{dtStart}/{dtEnd}', 'verb' => 'GET', 'postfix' => 'direct.new.timerange'],
['name' => 'view#index', 'url' => '/edit/{objectId}', 'verb' => 'GET', 'postfix' => 'direct.edit'],
['name' => 'view#index', 'url' => '/edit/{objectId}/{recurrenceId}', 'verb' => 'GET', 'postfix' => 'direct.edit.recurrenceId'],
['name' => 'view#index', 'url' => '/{view}/{timeRange}', 'verb' => 'GET', 'requirements' => ['view' => 'timeGridDay|timeGridWeek|dayGridMonth|listMonth'], 'postfix' => 'view.timerange'],
['name' => 'view#index', 'url' => '/{view}/{timeRange}/new/{mode}/{isAllDay}/{dtStart}/{dtEnd}', 'verb' => 'GET', 'requirements' => ['view' => 'timeGridDay|timeGridWeek|dayGridMonth|listMonth'], 'postfix' => 'view.timerange.new'],
['name' => 'view#index', 'url' => '/{view}/{timeRange}/edit/{mode}/{objectId}/{recurrenceId}', 'verb' => 'GET', 'requirements' => ['view' => 'timeGridDay|timeGridWeek|dayGridMonth|listMonth'], 'postfix' => 'view.timerange.edit'],
['name' => 'view#index', 'url' => '/{view}/{timeRange}', 'verb' => 'GET', 'requirements' => ['view' => 'timeGridDay|timeGridWeek|dayGridMonth|multiMonthYear|listMonth'], 'postfix' => 'view.timerange'],
['name' => 'view#index', 'url' => '/{view}/{timeRange}/new/{mode}/{isAllDay}/{dtStart}/{dtEnd}', 'verb' => 'GET', 'requirements' => ['view' => 'timeGridDay|timeGridWeek|dayGridMonth|multiMonthYear|listMonth'], 'postfix' => 'view.timerange.new'],
['name' => 'view#index', 'url' => '/{view}/{timeRange}/edit/{mode}/{objectId}/{recurrenceId}', 'verb' => 'GET', 'requirements' => ['view' => 'timeGridDay|timeGridWeek|dayGridMonth|multiMonthYear|listMonth'], 'postfix' => 'view.timerange.edit'],
['name' => 'view#getCalendarDotSvg', 'url' => '/public/getCalendarDotSvg/{color}.svg', 'verb' => 'GET'],
// Appointments
['name' => 'appointment#index', 'url' => '/appointments/{userId}', 'verb' => 'GET'],

View File

@ -105,7 +105,7 @@ class SettingsController extends Controller {
* @return JSONResponse
*/
private function setView(string $view):JSONResponse {
if (!\in_array($view, ['timeGridDay', 'timeGridWeek', 'dayGridMonth', 'listMonth'])) {
if (!\in_array($view, ['timeGridDay', 'timeGridWeek', 'dayGridMonth', 'multiMonthYear', 'listMonth'])) {
return new JSONResponse([], Http::STATUS_UNPROCESSABLE_ENTITY);
}

20
package-lock.json generated
View File

@ -13,6 +13,7 @@
"@fullcalendar/daygrid": "6.1.8",
"@fullcalendar/interaction": "6.1.8",
"@fullcalendar/list": "6.1.8",
"@fullcalendar/multimonth": "6.1.8",
"@fullcalendar/resource": "6.1.8",
"@fullcalendar/resource-timeline": "6.1.8",
"@fullcalendar/timegrid": "6.1.8",
@ -2074,6 +2075,17 @@
"@fullcalendar/core": "~6.1.8"
}
},
"node_modules/@fullcalendar/multimonth": {
"version": "6.1.8",
"resolved": "https://registry.npmjs.org/@fullcalendar/multimonth/-/multimonth-6.1.8.tgz",
"integrity": "sha512-3F0NlncQTfeE9x5ICxh/M9DaSdY6XjgM1NazY8k+d6ukd1jthHI7vs6j7tXJI9eGUKs3DNNEyzN/LoP06SIyKw==",
"dependencies": {
"@fullcalendar/daygrid": "~6.1.8"
},
"peerDependencies": {
"@fullcalendar/core": "~6.1.8"
}
},
"node_modules/@fullcalendar/premium-common": {
"version": "6.1.8",
"resolved": "https://registry.npmjs.org/@fullcalendar/premium-common/-/premium-common-6.1.8.tgz",
@ -19096,6 +19108,14 @@
"integrity": "sha512-10N0T/vCtId1cE3JGLpnbAivWVnaWCCkVO7wmbsyr5Y+I939kr/zq4BUNwBoP/xSFVVxx59FETh3iyA+MkV8Fw==",
"requires": {}
},
"@fullcalendar/multimonth": {
"version": "6.1.8",
"resolved": "https://registry.npmjs.org/@fullcalendar/multimonth/-/multimonth-6.1.8.tgz",
"integrity": "sha512-3F0NlncQTfeE9x5ICxh/M9DaSdY6XjgM1NazY8k+d6ukd1jthHI7vs6j7tXJI9eGUKs3DNNEyzN/LoP06SIyKw==",
"requires": {
"@fullcalendar/daygrid": "~6.1.8"
}
},
"@fullcalendar/premium-common": {
"version": "6.1.8",
"resolved": "https://registry.npmjs.org/@fullcalendar/premium-common/-/premium-common-6.1.8.tgz",

View File

@ -40,6 +40,7 @@
"@fullcalendar/daygrid": "6.1.8",
"@fullcalendar/interaction": "6.1.8",
"@fullcalendar/list": "6.1.8",
"@fullcalendar/multimonth": "6.1.8",
"@fullcalendar/resource": "6.1.8",
"@fullcalendar/resource-timeline": "6.1.8",
"@fullcalendar/timegrid": "6.1.8",

View File

@ -36,13 +36,14 @@
@click.stop.prevent="toggleDatepicker"
@mousedown.stop.prevent="doNothing"
@mouseup.stop.prevent="doNothing">
{{ selectedDate | formatDateRage(view, locale) }}
{{ selectedDate | formatDateRange(view, locale) }}
</NcButton>
<DatePicker ref="datepicker"
class="datepicker-button-section__datepicker"
:date="selectedDate"
:is-all-day="true"
:open.sync="isDatepickerOpen"
:type="view === 'multiMonthYear' ? 'year' : 'date'"
@change="navigateToDate" />
<NcButton v-shortkey="nextShortKeyConf"
:aria-label="nextLabel"
@ -64,7 +65,7 @@ import {
modifyDate,
} from '../../../utils/date.js'
import { mapState } from 'vuex'
import formatDateRage from '../../../filters/dateRangeFormat.js'
import formatDateRange from '../../../filters/dateRangeFormat.js'
import DatePicker from '../../Shared/DatePicker.vue'
import ChevronLeftIcon from 'vue-material-design-icons/ChevronLeft.vue'
import ChevronRightIcon from 'vue-material-design-icons/ChevronRight.vue'
@ -79,7 +80,7 @@ export default {
NcButton,
},
filters: {
formatDateRage,
formatDateRange,
},
data() {
return {
@ -107,6 +108,9 @@ export default {
case 'timeGridWeek':
return this.$t('calendar', 'Previous week')
case 'multiMonthYear':
return this.$t('calendar', 'Previous year')
case 'dayGridMonth':
default:
return this.$t('calendar', 'Previous month')
@ -126,6 +130,9 @@ export default {
case 'timeGridWeek':
return this.$t('calendar', 'Next week')
case 'multiMonthYear':
return this.$t('calendar', 'Next year')
case 'dayGridMonth':
default:
return this.$t('calendar', 'Next month')
@ -158,6 +165,12 @@ export default {
})
break
case 'multiMonthYear':
newDate = modifyDate(this.selectedDate, {
year: factor,
})
break
case 'dayGridMonth':
case 'listMonth':
default: {

View File

@ -47,6 +47,7 @@ import ViewGrid from 'vue-material-design-icons/ViewGrid.vue'
import ViewList from 'vue-material-design-icons/ViewList.vue'
import ViewModule from 'vue-material-design-icons/ViewModule.vue'
import ViewWeek from 'vue-material-design-icons/ViewWeek.vue'
import ViewComfy from 'vue-material-design-icons/ViewComfy.vue'
export default {
name: 'AppNavigationHeaderViewMenu',
@ -55,6 +56,7 @@ export default {
ActionButton,
ViewDay,
ViewGrid,
ViewComfy,
ViewList,
ViewModule,
ViewWeek,
@ -73,6 +75,10 @@ export default {
id: 'dayGridMonth',
icon: 'ViewModule',
label: this.$t('calendar', 'Month'),
}, {
id: 'multiMonthYear',
icon: 'ViewComfy',
label: this.$t('calendar', 'Year'),
}, {
id: 'listMonth',
icon: 'ViewList',
@ -87,8 +93,10 @@ export default {
timeGridWeek_Num: [2],
dayGridMonth: ['m'],
dayGridMonth_Num: [3],
multiMonthYear: ['y'],
multiMonthYear_Num: [4],
listMonth: ['l'],
listMonth_Num: [4],
listMonth_Num: [5],
}
},
defaultIcon() {

View File

@ -38,6 +38,11 @@
@click="view('dayGridMonth')">
{{ $t('calendar', 'Month') }}
</NcButton>
<NcButton :type="isYearViewSelected ? 'primary' : 'secondary'"
class="button"
@click="view('multiMonthYear')">
{{ $t('calendar', 'Year') }}
</NcButton>
<NcButton :class="isMonthListViewSelected ? 'primary' : 'secondary'"
class="button"
@click="view('listMonth')">
@ -64,6 +69,9 @@ export default {
isMonthViewSelected() {
return this.selectedView === 'dayGridMonth'
},
isYearViewSelected() {
return this.selectedView === 'multiMonthYear'
},
isMonthListViewSelected() {
return this.selectedView === 'listMonth'
},

View File

@ -91,7 +91,10 @@ export default {
keys: [['3'], ['m']],
label: t('calendar', 'Month view'),
}, {
keys: [['4'], ['l']],
keys: [['4'], ['y']],
label: t('calendar', 'Year view'),
}, {
keys: [['5'], ['l']],
label: t('calendar', 'List view'),
}],
}, {

View File

@ -33,6 +33,7 @@ import dayGridPlugin from '@fullcalendar/daygrid'
import interactionPlugin from '@fullcalendar/interaction'
import listPlugin from '@fullcalendar/list'
import timeGridPlugin from '@fullcalendar/timegrid'
import multiMonthPlugin from '@fullcalendar/multimonth'
// Import event sources
import eventSource from '../fullcalendar/eventSources/eventSource.js'
@ -164,6 +165,7 @@ export default {
interactionPlugin,
listPlugin,
timeGridPlugin,
multiMonthPlugin,
]
},
isEditable() {

View File

@ -26,7 +26,7 @@
:format="'YYYY-MM-DD HH:mm'"
:formatter="formatter"
:value="date"
:type="type"
:type="actualType"
:clearable="false"
:minute-step="5"
:disabled-date="disabledDate"
@ -142,6 +142,10 @@ export default {
type: Boolean,
default: false,
},
type: {
type: String,
default: 'datetime',
},
},
data() {
return {
@ -187,12 +191,12 @@ export default {
*
* @return {string}
*/
type() {
if (this.isAllDay) {
actualType() {
if (this.type === 'datetime' && this.isAllDay) {
return 'date'
}
return 'datetime'
return this.type
},
/**
* The earliest date a user is allowed to pick in the timezone

View File

@ -41,6 +41,9 @@ export default (value, view, locale) => {
year: moment(value).locale(locale).weekYear(),
})
case 'multiMonthYear':
return moment(value).locale(locale).format('YYYY')
case 'dayGridMonth':
case 'listMonth':
default:

View File

@ -40,6 +40,11 @@ const getDateFormattingConfig = () => {
...defaultConfig,
dayHeaderFormat: 'ddd',
},
multiMonthYear: {
...defaultConfig,
dayHeaderFormat: 'ddd',
multiMonthMaxColumns: 4,
},
timeGridDay: defaultConfig,
timeGridWeek: defaultConfig,
listMonth: {

View File

@ -120,13 +120,15 @@ export function getDateFromDateTimeValue(dateTimeValue) {
* @param {number} data.day Number of days to add
* @param {number} data.week Number of weeks to add
* @param {number} data.month Number of months to add
* @param data.year
* @return {Date}
*/
export function modifyDate(date, { day = 0, week = 0, month = 0 }) {
export function modifyDate(date, { day = 0, week = 0, month = 0, year = 0 }) {
date = new Date(date.getTime())
date.setDate(date.getDate() + day)
date.setDate(date.getDate() + week * 7)
date.setMonth(date.getMonth() + month)
date.setFullYear(date.getFullYear() + year)
return date
}