Recurring Event Add & Edit

As seen in the previous example, an add/edit form can be created with custom and base event fields. While the CRUD example features delete with undo, this example shows off how to build a fully customizable recurrence editor right in the add/edit screen.

Above some common presets, like Daily, Weekly on X day, Monthly on Xth, Annually on the same day, Every Weekday a fully custom recurrence editor is implemented.

Copy & paste or delve into the code and understand how to generate recurrence objects or RRULES as seen in this live rule builder.

Get started with this example

Demo

Loading the live demo

Preview in full screen

Add a recurrence editor to a Scheduler add/edit dialog — with daily, weekly, monthly, and custom presets plus an RRULE builder.

Code

<script setup>
import {
  formatDate,
  MbscButton,
  MbscDatepicker,
  MbscEventcalendar,
  MbscInput,
  MbscPopup,
  MbscRadio,
  MbscRadioGroup,
  MbscSegmented,
  MbscSegmentedGroup,
  MbscSelect,
  MbscSwitch,
  MbscTextarea,
  setOptions,
  updateRecurringEvent 
} from '@mobiscroll/vue'
import { onMounted, ref } from 'vue'

setOptions({
  locale:  ,
  theme: ' ',
  themeVariant: ' '
})

const days = [
  {
    name: 'Sunday',
    short: 'SU',
    checked: true
  },
  {
    name: 'Monday',
    short: 'MO',
    checked: false
  },
  {
    name: 'Tuesday',
    short: 'TU',
    checked: false
  },
  {
    name: 'Wednesday',
    short: 'WE',
    checked: false
  },
  {
    name: 'Thursday',
    short: 'TH',
    checked: false
  },
  {
    name: 'Friday',
    short: 'FR',
    checked: false
  },
  {
    name: 'Saturday',
    short: 'SA',
    checked: false
  }
]
const ordinalList = { 1: 'first', 2: 'second', 3: 'third', 4: 'fourth' }
const popupEventTitle = ref('')
const popupEventDescription = ref('')
const popupEventAllDay = ref(true)
const popupEventDates = ref(null)
const calendarSelectedDate = ref(new Date())
const myEvents = ref([
  {
    id: 1,
    start: '10:00',
    end: '12:00',
    title: 'Take vitamins',
    color: '#67ab0d',
    recurring: { repeat: 'daily', weekDays: 'MO,TU,WE,TH,FR,SA,SU' }
  },
  {
    id: 2,
    start: '18:00',
    end: '20:00',
    recurring: { repeat: 'weekly', weekDays: 'MO' },
    title: 'Football training',
    color: '#fd966a'
  },
  {
    id: 3,
    start: '17:00',
    end: '18:00',
    recurring: { repeat: 'monthly', day: 10 },
    title: 'Rent payment',
    color: '#3ea39e'
  },
  {
    id: 4,
    recurring: { repeat: 'yearly', day: 6, month: 6 },
    title: 'Dexter BD',
    allDay: true,
    color: '#d00f0f'
  }
])
const myView = {
  scheduler: { type: 'week' }
}
const repeatData = ref([
  {
    value: 'norepeat',
    text: 'Does not repeat'
  },
  {
    value: 'daily',
    text: 'Daily'
  },
  {
    value: 'weekly',
    text: 'Weekly'
  },
  {
    value: 'monthly',
    text: 'Monthly'
  },
  {
    value: 'monthly-pos',
    text: 'Monthly'
  },
  {
    value: 'yearly',
    text: 'Yearly'
  },
  {
    value: 'yearly-pos',
    text: 'Yearly'
  },
  {
    value: 'weekday',
    text: 'Every weekday (Monday to Friday)'
  },
  {
    value: 'custom',
    text: 'Custom'
  }
])
const selectedRepeat = ref('norepeat')
const selectResponsive = {
  xsmall: {
    touchUi: true
  },
  small: {
    touchUi: false
  }
}
const responsivePopup = {
  medium: {
    display: 'anchored',
    width: 510,
    fullScreen: false,
    touchUi: false
  }
}
const weekDays = ref(['SU'])
const showCustomRepeat = ref(false)
const repeatType = ref('daily')
const repeatNr = ref(1)
const monthlyDays = ref([1])
const monthlyDay = ref(1)
const yearlyDays = ref([1])
const yearlyDay = ref(1)
const months = [
  {
    value: 1,
    text: 'January'
  },
  {
    value: 2,
    text: 'February'
  },
  {
    value: 3,
    text: 'March'
  },
  {
    value: 4,
    text: 'April'
  },
  {
    value: 5,
    text: 'May'
  },
  {
    value: 6,
    text: 'June'
  },
  {
    value: 7,
    text: 'July'
  },
  {
    value: 8,
    text: 'August'
  },
  {
    value: 9,
    text: 'September'
  },
  {
    value: 10,
    text: 'October'
  },
  {
    value: 11,
    text: 'November'
  },
  {
    value: 12,
    text: 'December'
  }
]
const selectedMonth = ref(1)
const untilDate = ref(null)
const occurrences = ref(10)
const condition = ref('never')
const tempEvent = ref({})
const recurringEditMode = ref('current')
const newEvent = ref({})
const originalRecurringEvent = ref({})
const eventOccurrence = ref({})
const isEdit = ref(false)
const editFromPopup = ref(false)
const recurringDelete = ref(false)
const recurringActionText = ref('Edit')
const isPopupOpen = ref(false)
const popupHeaderText = ref('')
const isRecurringEditOpen = ref(false)
const popupAnchor = ref(null)
const startRef = ref(null)
const endRef = ref(null)
const popupAddButtons = [
  'cancel',
  {
    handler: () => {
      saveEvent()
    },
    keyCode: 'enter',
    text: 'Add',
    cssClass: 'mbsc-popup-button-primary'
  }
]
const popupEditButtons = [
  'cancel',
  {
    handler: () => {
      if (Object.keys(originalRecurringEvent.value).length !== 0) {
        editFromPopup.value = true
        recurringActionText.value = 'Edit'
        recurringDelete.value = false
        isRecurringEditOpen.value = true
      } else {
        saveEvent()
      }
    },
    keyCode: 'enter',
    text: 'Save',
    cssClass: 'mbsc-popup-button-primary'
  }
]
const datePickerControls = ['date']
const datePickerResponsive = {
  medium: {
    controls: ['calendar'],
    touchUi: false
  }
}
const datetimePickerControls = ['datetime']
const datetimePickerResponsive = {
  medium: {
    controls: ['calendar', 'time'],
    touchUi: false
  }
}
const popupButtons = ref([])
const recurringEditButtons = [
  'cancel',
  {
    text: 'Ok',
    keyCode: 'enter',
    handler: () => {
      if (recurringDelete.value) {
        deleteRecurringEvent()
      } else {
        if (editFromPopup.value) {
          setTempEvent()
        }

        if (recurringEditMode.value === 'current') {
          delete tempEvent.value.id
          delete tempEvent.value.recurring
          delete tempEvent.value.recurringException
        }

        const events = updateRecurringEvent(
          originalRecurringEvent.value,
          eventOccurrence.value,
          editFromPopup.value ? null : newEvent.value,
          editFromPopup.value ? tempEvent.value : null,
          recurringEditMode.value
        )

        // Update event
        let newEventList = [...myEvents.value]
        const index = newEventList.findIndex((x) => x.id === events.updatedEvent.id)
        newEventList[index] = events.updatedEvent

        // Add new event
        if (events.newEvent) {
          newEventList = [...newEventList, events.newEvent]
        }

        myEvents.value = newEventList

        isPopupOpen.value = false
        isRecurringEditOpen.value = false
      }
    },
    cssClass: 'mbsc-popup-button-primary'
  }
]

function getWeekDayNum(date) {
  const year = date.getFullYear()
  const month = date.getMonth()
  const firstDayOfMonth = new Date(year, month, 1)
  const lastDayOfMonth = new Date(year, month + 1, 0)
  let count = 0

  for (let d = firstDayOfMonth; d < lastDayOfMonth; d.setDate(d.getDate() + 7)) {
    if (date >= d) {
      count++
    } else {
      break
    }
  }

  return Math.max(1, count)
}

function deleteRecurringEvent() {
  let currentExceptions = []
  let followingExceptions = []
  switch (recurringEditMode.value) {
    case 'current':
    default:
      currentExceptions = tempEvent.value.recurringException || []
      currentExceptions = [...currentExceptions, tempEvent.value.start]
      originalRecurringEvent.value.recurringException = currentExceptions
      myEvents.value = [...myEvents.value]
      break
    case 'following':
      followingExceptions = tempEvent.value.recurringException || []
      followingExceptions = [...followingExceptions, tempEvent.value.start]
      originalRecurringEvent.value.recurringException = followingExceptions
      originalRecurringEvent.value.recurring.until = tempEvent.value.start
      myEvents.value = [...myEvents.value]
      break
    case 'all':
      myEvents.value = myEvents.value.filter((item) => item.id !== tempEvent.value.id)
      break
  }
  isPopupOpen.value = false
  isRecurringEditOpen.value = false
}

function loadPopupForm(event) {
  const startDate = new Date(event.start)
  popupEventTitle.value = event.title
  popupEventDescription.value = event['description']
  popupEventDates.value = [startDate, event.end]
  untilDate.value = formatDate(
    'YYYY-MM-DD',
    new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate() + 1)
  )
  popupEventAllDay.value = event.allDay || false
  updateCustomForm()
}

function getCustomRule() {
  let recurringRule
  const d = editFromPopup.value ? popupEventDates.value[0] : new Date(tempEvent.value.start)
  const weekday = d.getDay()
  const monthday = d.getDate()
  const weekNr = getWeekDayNum(d)

  if (editFromPopup.value && tempEvent.value.start && tempEvent.value.recurring) {
    switch (selectedRepeat.value) {
      default:
      case 'daily':
        recurringRule = {
          repeat: 'daily'
        }
        break
      case 'weekly':
        recurringRule = {
          repeat: 'weekly',
          weekDays: days[weekday].short
        }
        break
      case 'monthly':
        recurringRule = {
          repeat: 'monthly',
          day: monthday
        }
        break
      case 'monthly-pos':
        recurringRule = {
          repeat: 'monthly',
          weekDays: days[weekday].short,
          pos: weekNr
        }
        break
      case 'yearly':
        recurringRule = {
          repeat: 'yearly',
          day: monthday,
          month: d.getMonth() + 1
        }
        break
      case 'yearly-pos':
        recurringRule = {
          repeat: 'yearly',
          month: d.getMonth() + 1,
          weekDays: days[weekday].short,
          pos: weekNr
        }
        break
    }
  } else {
    switch (selectedRepeat.value) {
      case 'daily':
        recurringRule = { repeat: 'daily' }
        break
      case 'weekly':
        recurringRule = { repeat: 'weekly', weekDays: days[d.getDay()].short }
        break
      case 'monthly':
        recurringRule = { repeat: 'monthly', day: d.getDate() }
        break
      case 'monthly-pos':
        recurringRule = { repeat: 'monthly', weekDays: days[weekday].short, pos: weekNr }
        break
      case 'yearly':
        recurringRule = { repeat: 'yearly', month: d.getMonth() + 1 }
        break
      case 'yearly-pos':
        recurringRule = {
          repeat: 'yearly',
          month: d.getMonth() + 1,
          weekDays: days[weekday].short,
          pos: weekNr
        }
        break
      case 'weekday':
        recurringRule = { repeat: 'weekly', weekDays: 'MO,TU,WE,TH,FR' }
        break
      case 'custom':
      case 'custom-value':
        recurringRule = {
          repeat: repeatType.value,
          interval: repeatNr.value
        }
        switch (repeatType.value) {
          case 'weekly':
            recurringRule.weekDays = weekDays.value.join(',')
            break
          case 'monthly':
            recurringRule.day = monthlyDay.value
            break
          case 'yearly':
            recurringRule.day = yearlyDay.value
            recurringRule.month = selectedMonth.value
            break
        }

        switch (condition.value) {
          case 'until':
            recurringRule.until = untilDate.value
            break
          case 'count':
            recurringRule.count = occurrences.value
            break
        }
        break
    }
  }
  return recurringRule
}

function setTempEvent() {
  tempEvent.value.title = popupEventTitle.value
  tempEvent.value.description = popupEventDescription.value
  tempEvent.value.start = popupEventDates.value[0]
  tempEvent.value.end = popupEventDates.value[1]
  tempEvent.value.allDay = popupEventAllDay.value
  tempEvent.value.recurring = getCustomRule()
}

function saveEvent() {
  setTempEvent()

  if (isEdit.value) {
    // Update the event in the list
    myEvents.value = [...myEvents.value]
    // Here you can update the event in your storage as well
    // ...
  } else {
    // Add the new event to the list
    myEvents.value = [...myEvents.value, tempEvent.value]
    // Here you can add the event to your storage as well
    // ...
  }
  // Navigate the calendar
  navigateTo()
  // Close the popup
  isPopupOpen.value = false
}

function deleteEvent(event) {
  myEvents.value = myEvents.value.filter((item) => item.id !== event.id)
}

// Set custom values to default
function resetCustomValues() {
  repeatType.value = 'daily'
  repeatNr.value = 1
  condition.value = 'never'
  occurrences.value = 10
  selectedMonth.value = 1
  monthlyDay.value = 1
  yearlyDay.value = 1
  weekDays.value = ['SU']
  selectedRepeat.value = 'norepeat'
  showCustomRepeat.value = false
  repeatData.value = repeatData.value.filter((item) => item.value !== 'custom-value')
}

function updateCustomForm() {
  const d = new Date(tempEvent.value.start)
  const weekday = d.getDay()
  const monthday = d.getDate()
  const newData = [...repeatData.value]
  const weekNr = getWeekDayNum(d)

  // Update select texts by selected date
  for (const item of newData) {
    switch (item.value) {
      case 'weekly':
        item.text = 'Weekly on ' + days[weekday].name
        break
      case 'monthly':
        item.text = 'Monthly on day ' + monthday
        break
      case 'yearly':
        item.text = 'Annually on ' + months[d.getMonth()].text + ' ' + monthday
        break
      case 'monthly-pos':
        item.text = 'Monthly on the ' + ordinalList[weekNr] + ' ' + days[weekday].name
        break
      case 'yearly-pos':
        item.text =
          'Annually on the ' +
          ordinalList[weekNr] +
          ' ' +
          days[weekday].name +
          ' of ' +
          months[d.getMonth()].text
        break
      default:
    }
  }

  repeatData.value = newData

  const rec = tempEvent.value.recurring

  if (rec) {
    repeatType.value = rec.repeat
    weekDays.value = rec.repeat === 'weekly' ? rec.weekDays.split(',') : ['SU']
    if (rec.interval) {
      // Set custom text
      let customText = ''

      repeatNr.value = rec.interval

      switch (rec.repeat) {
        case 'daily':
          customText = repeatNr.value > 1 ? 'Every ' + repeatNr.value + ' days' : 'Daily'
          break
        case 'weekly':
          customText = repeatNr.value > 1 ? 'Every ' + repeatNr.value + ' weeks' : 'Weekly'
          customText += ' on ' + rec.weekDays
          break
        case 'monthly':
          monthlyDay.value = rec.day
          customText = repeatNr.value > 1 ? 'Every ' + repeatNr.value + ' months' : 'Monthly'
          customText += ' on day ' + monthlyDay.value
          break
        case 'yearly':
          yearlyDay.value = rec.day
          selectedMonth.value = rec.month
          customText = repeatNr.value > 1 ? 'Every ' + repeatNr.value + ' years' : 'Annualy'
          customText += ' on ' + months[selectedMonth.value - 1].text + ' ' + yearlyDay.value
          break
      }

      if (rec.until) {
        condition.value = 'until'
        untilDate.value = rec.until
        customText += ' until ' + formatDate('MMMM D, YYYY', new Date(untilDate.value))
      } else if (rec.count) {
        condition.value = 'count'
        occurrences.value = rec.count
        customText += ', ' + occurrences.value + ' times'
      } else {
        condition.value = 'never'
      }

      // Add custom value
      repeatData.value = [...repeatData.value, { value: 'custom-value', text: customText }]
      // Set custom value
      selectedRepeat.value = 'custom-value'
    } else if (rec.weekDays === 'MO,TU,WE,TH,FR') {
      selectedRepeat.value = 'weekday'
    } else {
      selectedRepeat.value = rec.repeat
    }
  } else {
    resetCustomValues()
  }
  showCustomRepeat.value =
    selectedRepeat.value === 'custom' || selectedRepeat.value === 'custom-value'
}

// Popuplate data for months
function populateMonthDays(month, type) {
  const day30 = [2, 4, 6, 9, 11]
  const newValues = []

  for (let i = 1; i <= 31; i++) {
    if (!(i === 31 && day30.includes(month)) && !(i === 30 && month === 2)) {
      newValues.push(i)
    }
  }

  if (type === 'monthly') {
    monthlyDays.value = newValues
    monthlyDay.value = 1
  } else {
    yearlyDays.value = newValues
    yearlyDay.value = 1
  }
}

function monthChange(ev) {
  selectedMonth.value = ev.value
  yearlyDay.value = 1
  populateMonthDays(ev.value, 'yearly')
}

function navigateTo() {
  const rec = tempEvent.value.recurring
  const d = new Date(tempEvent.value.start)
  let nextYear = 0

  // Navigate the calendar to the correct view
  if (rec && rec.repeat === 'yearly') {
    if (d.getMonth() + 1 > +rec.month && d.getDay() > +rec.day) {
      nextYear = 1
    }
    calendarSelectedDate.value = new Date(d.getFullYear() + nextYear, rec.month - 1, rec.day)
  } else {
    calendarSelectedDate.value = d
  }
}

function handleEventClick(args) {
  const event = args.event
  isEdit.value = true
  tempEvent.value = event

  // Recurring event
  if (event.recurring) {
    originalRecurringEvent.value = event.original
    eventOccurrence.value = { ...event }

    // Fill popup form with event data
    loadPopupForm(event)
  } else {
    originalRecurringEvent.value = {}
    // Fill popup form with event data
    loadPopupForm(event)
  }

  // Set popup options
  popupHeaderText.value = 'Edit event'
  popupButtons.value = popupEditButtons
  popupAnchor.value = args.domEvent.currentTarget
  // Open the popup
  isPopupOpen.value = true
}

function handleEventUpdate(args) {
  const event = args.event
  if (event.recurring) {
    originalRecurringEvent.value = args.oldEvent
    // We need this on delete
    tempEvent.value = event
    eventOccurrence.value = args.oldEventOccurrence
    if (args.domEvent.keyCode === 46) {
      recurringActionText.value = 'Delete'
      recurringDelete.value = true
      isRecurringEditOpen.value = true
    } else {
      recurringActionText.value = 'Edit'
      recurringDelete.value = false
      isRecurringEditOpen.value = true
    }
    return false
  } else {
    return true
  }
}

function handleEventCreate(args) {
  const originEvent = args.originEvent
  if (originEvent && originEvent.recurring) {
    newEvent.value = args.event
    return false
  } else {
    return true
  }
}

function handleEventCreated(args) {
  isEdit.value = false
  tempEvent.value = args.event
  // Fill popup form with event data
  loadPopupForm(args.event)
  // Set popup options
  popupHeaderText.value = 'New Event'
  popupButtons.value = popupAddButtons
  popupAnchor.value = args.target
  resetCustomValues()
  // Open the popup
  isPopupOpen.value = true
}

function handleEventDeleted(args) {
  deleteEvent(args.event)
}

function handleEventUpdated() {
  // Here you can update the event in your storage as well, after drag & drop or resize
  // ...
}

function handleDelete() {
  if (tempEvent.value.recurring) {
    recurringActionText.value = 'Delete'
    recurringDelete.value = true
    isRecurringEditOpen.value = true
  } else {
    deleteEvent(tempEvent.value)
    isPopupOpen.value = false
  }
}

function handleSelectedDateChange(args) {
  calendarSelectedDate.value = args.date
}

onMounted(() => {
  populateMonthDays(1, 'monthly')
  populateMonthDays(1, 'yearly')
})
</script>

<template>
  <MbscEventcalendar
    :view="myView"
    :data="myEvents"
    clickToCreate="double"
    :dragToCreate="true"
    :dragToMove="true"
    :dragToResize="true"
    :selectedDate="calendarSelectedDate"
    @selected-date-change="handleSelectedDateChange"
    @event-click="handleEventClick"
    @event-update="handleEventUpdate"
    @event-create="handleEventCreate"
    @event-created="handleEventCreated"
    @event-deleted="handleEventDeleted"
    @event-updated="handleEventUpdated"
  />
  <MbscPopup
    cssClass="md-recurring-event-editor-popup"
    display="bottom"
    :contentPadding="false"
    :fullScreen="true"
    :scrollLock="false"
    :height="500"
    :headerText="popupHeaderText"
    :anchor="popupAnchor"
    :buttons="popupButtons"
    :isOpen="isPopupOpen"
    :responsive="responsivePopup"
    @close="isPopupOpen = false"
  >
    <div class="mbsc-form-group">
      <MbscInput label="Title" v-model="popupEventTitle" />
      <MbscTextarea label="Description" v-model="popupEventDescription" />
    </div>
    <div class="mbsc-form-group">
      <MbscSwitch label="All-day" v-model="popupEventAllDay" />
      <MbscInput ref="startRef" label="Starts" />
      <MbscInput ref="endRef" label="Ends" />
      <MbscDatepicker
        v-model="popupEventDates"
        select="range"
        :touchUi="true"
        :startInput="startRef"
        :endInput="endRef"
        :showRangeLabels="false"
        :controls="popupEventAllDay ? datePickerControls : datetimePickerControls"
        :responsive="popupEventAllDay ? datePickerResponsive : datetimePickerResponsive"
      />
      <MbscSelect
        v-model="selectedRepeat"
        label="Repeats"
        :data="repeatData"
        :responsive="selectResponsive"
      />
    </div>
    <div class="mbsc-form-group">
      <div v-if="selectedRepeat === 'custom' || selectedRepeat === 'custom-value'">
        <div>
          <MbscSegmentedGroup v-model="repeatType">
            <MbscSegmented value="daily">Daily</MbscSegmented>
            <MbscSegmented value="weekly">Weekly</MbscSegmented>
            <MbscSegmented value="monthly">Monthly</MbscSegmented>
            <MbscSegmented value="yearly">Yearly</MbscSegmented>
          </MbscSegmentedGroup>

          <div class="md-recurrence-options">
            <span>Repeat every</span>
            <span class="md-recurrence-input md-recurrence-input-nr">
              <MbscInput :min="1" v-model="repeatNr" inputStyle="outline" />
            </span>
            <span v-if="repeatType === 'daily'">days</span>
            <span v-if="repeatType === 'weekly'">weeks</span>
            <span v-if="repeatType === 'monthly'">
              month(s) on day
              <span class="md-recurrence-input md-recurrence-input-nr">
                <MbscSelect
                  :data="monthlyDays"
                  v-model="monthlyDay"
                  className="md-recurrence-input"
                  inputStyle="outline"
                />
              </span>
            </span>
            <span v-if="repeatType === 'yearly'">
              year(s) <br />on day
              <span class="md-recurrence-input md-recurrence-input-nr">
                <MbscSelect
                  :data="yearlyDays"
                  v-model="yearlyDay"
                  className="md-recurrence-input"
                  inputStyle="outline"
                />
              </span>
              <span>of</span>
              <span class="md-recurrence-input">
                <MbscSelect
                  :data="months"
                  v-model="selectedMonth"
                  className="md-recurrence-input"
                  inputStyle="outline"
                  @change="monthChange"
                />
              </span>
            </span>

            <p v-if="repeatType === 'daily'" class="md-recurrence-desc">
              The event will be repeated every day or every x days, depending on the value
            </p>
            <p v-if="repeatType === 'weekly'" class="md-recurrence-desc">
              The event will be repeated every x weeks on specific weekdays
            </p>
            <p v-if="repeatType === 'monthly'" class="md-recurrence-desc">
              The event will be repeated every x month on specific day of the month
            </p>
            <p v-if="repeatType === 'yearly'" class="md-recurrence-desc">
              The event will be repeated every x years on specific day of a specific month
            </p>
          </div>

          <MbscSegmentedGroup v-model="weekDays" v-if="repeatType === 'weekly'" select="multiple">
            <MbscSegmented value="SU">Sun</MbscSegmented>
            <MbscSegmented value="MO">Mon</MbscSegmented>
            <MbscSegmented value="TU">Tue</MbscSegmented>
            <MbscSegmented value="WE">Wed</MbscSegmented>
            <MbscSegmented value="TH">Thu</MbscSegmented>
            <MbscSegmented value="FR">Fri</MbscSegmented>
            <MbscSegmented value="SA">Sat</MbscSegmented>
          </MbscSegmentedGroup>

          <div class="md-recurrence-ends">Ends</div>

          <div class="mbsc-form-group">
            <MbscRadioGroup v-model="condition">
              <MbscRadio
                label="Never"
                position="start"
                description="The event will repeat indefinitely"
                value="never"
              />
              <MbscRadio value="until" position="start" @click="condition = 'until'">
                Until
                <span class="md-recurrence-input">
                  <MbscDatepicker
                    :controls="['calendar']"
                    display="anchored"
                    :touchUi="false"
                    dateFormat="YYYY-MM-DD"
                    returnFormat="iso8601"
                    v-model="untilDate"
                    inputStyle="outline"
                  />
                </span>
                <span class="mbsc-description">
                  The event will run until it reaches a specific date
                </span>
              </MbscRadio>
              <MbscRadio value="count" position="start">
                After
                <span class="md-recurrence-input md-recurrence-input-nr">
                  <MbscInput
                    inputStyle="outline"
                    v-model="occurrences"
                    @click="condition = 'count'"
                  />
                  occurrences
                  <span class="mbsc-description">
                    The event will repeat until it reaches a certain amount of occurrences
                  </span>
                </span>
              </MbscRadio>
            </MbscRadioGroup>
          </div>
        </div>
      </div>
      <div v-if="isEdit" class="mbsc-button-group">
        <MbscButton
          className="mbsc-button-block"
          color="danger"
          variant="outline"
          @click="handleDelete"
        >
          Delete event
        </MbscButton>
      </div>
    </div>
  </MbscPopup>
  <MbscPopup
    display="bottom"
    :contentPadding="false"
    :buttons="recurringEditButtons"
    :isOpen="isRecurringEditOpen"
    @close="isRecurringEditOpen = false"
  >
    <div class="mbsc-form-group">
      <div class="mbsc-form-group-title">{{ recurringActionText }} recurring event</div>
      <MbscRadioGroup v-model="recurringEditMode">
        <MbscRadio label="This event only" value="current" />
        <MbscRadio label="This and following events" value="following" />
        <MbscRadio label="All events" value="all" />
      </MbscRadioGroup>
    </div>
  </MbscPopup>
</template>

<style>
.md-recurring-event-editor-popup .md-recurrence-input .mbsc-textfield-wrapper {
  display: inline-block;
  margin: 3px;
  z-index: 3;
  line-height: normal;
}

.md-recurring-event-editor-popup .md-recurrence-input .mbsc-textfield {
  width: 140px;
  height: 32px;
}

.md-recurring-event-editor-popup .md-recurrence-input-nr .mbsc-textfield {
  width: 90px;
}

.md-recurring-event-editor-popup .md-recurrence-input .mbsc-select-icon {
  top: 50%;
  height: 1.5em;
  margin-top: -0.75em;
}

.md-recurrence-options {
  padding: 0 1em;
  margin: 1em 0;
}

.md-recurrence-ends {
  padding: 0 1em;
  margin: 1.25em 0 -1.25em 0;
}

.md-recurrence-desc {
  font-size: 12px;
  font-weight: normal;
  color: #555;
  margin: 1em 0;
}

.md-hide-elm {
  display: none;
}
</style>

Duplicate Session

You have signed in on a different machine. To keep everything in sync, you have been signed out of this session.

If you need to invite team members, manage seats and accounts you can do it from the licenses page.

Duplicate Session

We noticed you are logged in on a different machine and have closed your old session.

If you need to invite team members, manage seats and accounts you can do it from the licenses page.