This component is inspired from the meeting selector from doctolib with the power of Vuejs components.
- required: Vuejs >= 3.x
npm install vue-meeting-selector --save
yarn add vue-meeting-selector
Include the file in your app
import VueMeetingSelector from 'vue-meeting-selector';
import "vue-meeting-selector/dist/style.css";
import slotsGenerator from "vue-meeting-selector/src/helpers/slotsGenerator";
Issues and PR's are much appreciated. When you create a new PR please make it against the develop branch when adding new features and to the fix branch when fixing small issues instead of master.
<template>
<vue-meeting-selector
ref="meetingSelector"
class="meeting-selector"
v-model="meeting"
:date="date"
:loading="false"
multi
:meetings-days="meetingsDays"
@next-date="nextDate"
@previous-date="previousDate"
@update:modelValue="change"
/>
</template>
<script>
import { defineComponent, ref } from "vue";
import VueMeetingSelector from "vue-meeting-selector";
import "vue-meeting-selector/dist/style.css";
import slotsGenerator from "vue-meeting-selector/src/helpers/slotsGenerator";
export default defineComponent({
components: {
VueMeetingSelector,
},
setup() {
const meeting = ref([]);
const meetingsDays = ref([]);
const nbDaysToDisplay = ref(5);
const date = ref(new Date());
const initMeetingsDays = () => {
const start = {
hours: 8,
minutes: 0,
};
const end = {
hours: 16,
minutes: 0,
};
meetingsDays.value = slotsGenerator(
new Date(),
nbDaysToDisplay.value,
start,
end,
30
);
};
initMeetingsDays();
const meetingSelector = ref(null);
const up = () => meetingSelector.value.previousMeetings();
const down = () => meetingSelector.value.nextMeetings();
const nextDate = () => {
const start = {
hours: 8,
minutes: 0,
};
const end = {
hours: 16,
minutes: 0,
};
const d = new Date(date.value);
const newDate = new Date(d.setDate(d.getDate() + 7));
date.value = newDate;
meetingsDays.value = slotsGenerator(
newDate,
nbDaysToDisplay.value,
start,
end,
30
);
};
const previousDate = () => {
const start = {
hours: 8,
minutes: 0,
};
const end = {
hours: 16,
minutes: 0,
};
const d = new Date(date.value);
d.setDate(d.getDate() - 7);
const formatingDate = (dateToFormat) => {
const dateParsed = new Date(dateToFormat);
const day =
dateParsed.getDate() < 10
? `0${dateParsed.getDate()}`
: dateParsed.getDate();
const month =
dateParsed.getMonth() + 1 < 10
? `0${dateParsed.getMonth() + 1}`
: dateParsed.getMonth() + 1;
const year = dateParsed.getFullYear();
return `${year}-${month}-${day}`;
};
const newDate =
formatingDate(new Date()) >= formatingDate(d)
? new Date()
: new Date(d);
date.value = newDate;
meetingsDays.value = slotsGenerator(
newDate,
nbDaysToDisplay.value,
start,
end,
30
);
};
const change = () => {
console.log(meeting.value);
};
return {
meeting,
meetingsDays,
date,
meetingSelector,
up,
down,
nextDate,
previousDate,
change,
};
},
});
</script>
interface MeetingSlot {
date: Date | string;
[key: string]: any;
}
interface MeetingsDay {
date: Date | string;
slots: MeetingSlot[];
[key: string]: any;
}
interface ClassNames {
tabClass?: string,
tabPaginationleft?: string,
tabPaginationPreviousButton?: string,
tabPaginationRight?: string,
tabPaginationNextButton?: string,
tabPaginationUpButton?: string,
tabPaginationDownButton?: string,
tabDays?: string,
tabDayDisplay?: string,
tabMeetings?: string,
tabMeeting?: string,
tabMeetingButton?: string,
tabMeetingEmpty?: string,
tabLoading?: string,
}
// defaults value are available in src/defaults/calendarOptions.ts
interface CalendarOptions {
daysLabel: string[]; // Labels for days in title, start by sunday
monthsLabel: string[]; // labels for months in title, start by january
limit: number, // max nb meetings to display on a same column
spacing: number, // When clicking next, how many cells do you want to scroll
loadingLabel: string; // label to display when loading
disabledDate: Function; // function to disable left button (date is passed as params)
}
Params | Type |
---|---|
v-model | MeetingSlot | MeetingSlot[] |
date | Date | string |
meetingsDays | MeetingsDays[] |
calendarOptions | Object |
classNames | Object |
multi | boolean |
loading | boolean |
Name | Params |
---|---|
meeting-slot-selected | MeetingSlot | MeetingSlot[] |
meeting-slot-unselected | - |
change | MeetingSlot |
next-date | - |
previous-date | - |
To change head of every column, a meetings
(MeetingsDay) is passed as slot-scope.
<template
#header="{ meetings }">
<div>{{ meetings.date }}</div>
</template>
To change the previous/next button.
<template #button-previous>
<button
@click="previous">
previous
</button>
</template>
<template #button-next>
<button
@click="next">
next
</button>
</template>
To change up/down button to change hours of meetings, you will have to trigger methods with refs
<template #button-up="{ isDisabled }">
<button
:disabled="isDisabled"
@click="up">
up
</button>
</template>
<template
#button-down="{ isDisabled }">
<button
:disabled="isDisabled"
@click="down">
down
</button>
</template>
<script>
export default {
methods: {
up() {
this.$refs.meetingSelector.previousMeetings();
// (this.$refs.meetingSelector as Vue & { previousMeetings: () => void }).previousMeetings();
},
down() {
this.$refs.meetingSelector.nextMeetings();
// (this.$refs.meetingSelector as Vue & { nextMeetings: () => void }).nextMeetings();
},
},
};
</script>
To change the display of a meeting. (you will have to manually change the v-model) if the meeting don't have date, it's because the is no meeting. (you will have to handle a different display)
<template
#meeting="{ meeting }">
<div>{{ meeting.date }}</div>
</template>
To change the display of loading
<template
#loading>
Loading ...
</template>
npm install
npm run dev
npm run doc
npm run build:lib
npm run build:doc
npm run test:unit
npm run lint