<form class="js-select-appointment-form">
    <select class="select" id="select-cc3d" name="select-cc3d">
  <option class="select__hint" disabled="disabled" selected="selected" value="">Auswählen</option>
  <option value="1-08:00/11:00">Mo, 8:00-11:00 Uhr</option>
  <option value="1-11:00/14:00">Mo, 11:00-14:00 Uhr</option>
  <option value="1-14:00/18:00">Mo, 14:00-18:00 Uhr</option>
  <option value="2-08:00/11:00">Di 8:00-11:00 Uhr</option>
  <option value="2-11:00/14:00">Di, 11:00-14:00 Uhr</option>
  <option value="2-14:00/18:00">Di, 14:00-18:00 Uhr</option>
  <option value="3-08:00/11:00">Mi, 8:00-11:00 Uhr</option>
  <option value="3-11:00/14:00">Mi, 11:00-14:00 Uhr</option>
  <option value="3-14:00/18:00">Mi, 14:00-18:00 Uhr</option>
  <option value="4-08:00/11:00">Do, 8:00-11:00 Uhr</option>
  <option value="4-11:00/14:00">Do, 11:00-14:00 Uhr</option>
  <option value="4-14:00/18:00">Do, 14:00-18:00 Uhr</option>
  <option value="5-14:00/16:00">Fr, 14:00-16:00 Uhr</option>
</select>
</form>
-
  //- Prepare attr object
  settings = settings || {};
  settings.attr = settings.attr || {};
  settings.attr.class = classList(settings.attr.class);

  if (!formSelectorWorkaround) {
    settings.attr.class.push('js-select-appointment');
  }

//- Render atom
if formSelectorWorkaround
  form.js-select-appointment-form
    != include('@select', settings)
else
  != include('@select', settings)
{
  "settings": {
    "hasPleaseSelectOption": true,
    "options": [
      {
        "text": "Mo, 8:00-11:00 Uhr",
        "value": "1-08:00/11:00"
      },
      {
        "text": "Mo, 11:00-14:00 Uhr",
        "value": "1-11:00/14:00"
      },
      {
        "text": "Mo, 14:00-18:00 Uhr",
        "value": "1-14:00/18:00"
      },
      {
        "text": "Di 8:00-11:00 Uhr",
        "value": "2-08:00/11:00"
      },
      {
        "text": "Di, 11:00-14:00 Uhr",
        "value": "2-11:00/14:00"
      },
      {
        "text": "Di, 14:00-18:00 Uhr",
        "value": "2-14:00/18:00"
      },
      {
        "text": "Mi, 8:00-11:00 Uhr",
        "value": "3-08:00/11:00"
      },
      {
        "text": "Mi, 11:00-14:00 Uhr",
        "value": "3-11:00/14:00"
      },
      {
        "text": "Mi, 14:00-18:00 Uhr",
        "value": "3-14:00/18:00"
      },
      {
        "text": "Do, 8:00-11:00 Uhr",
        "value": "4-08:00/11:00"
      },
      {
        "text": "Do, 11:00-14:00 Uhr",
        "value": "4-11:00/14:00"
      },
      {
        "text": "Do, 14:00-18:00 Uhr",
        "value": "4-14:00/18:00"
      },
      {
        "text": "Fr, 14:00-16:00 Uhr",
        "value": "5-14:00/16:00"
      }
    ]
  },
  "formSelectorWorkaround": true
}
  • Content:
    import setIsoDay from 'date-fns/set_iso_day';
    import addWeeks from 'date-fns/add_weeks';
    import setHours from 'date-fns/set_hours';
    import addHours from 'date-fns/add_hours';
    import setMinutes from 'date-fns/set_minutes';
    import compareDesc from 'date-fns/compare_desc';
    
    /**
     * Time slot string
     *
     * example: '4-08:00/11:00'
     * format: {weekDay}-{timeStart}/{timeEnd}
     * format date-fns: E-HH:mm/HH:mm
     * format PHP 5.1: N-H:i/H:i
     * weekDay: 1=monday, 7=saturday
     */
    export const TIME_SLOT_REGEX = /^([1-7])[-](0[0-9]|1[0-9]|2[0-3])[:]([0-5][0-9])[/](0[0-9]|1[0-9]|2[0-3])[:]([0-5][0-9])$/;
    
    export function parseTimeSlot(timeSlotString) {
      if (TIME_SLOT_REGEX.test(timeSlotString)) {
        const [
          ,
          day,
          startHours,
          startMinutes,
          endHours,
          endMinutes,
        ] = TIME_SLOT_REGEX.exec(timeSlotString);
    
        return {
          day: Number(day),
          startHours: Number(startHours),
          startMinutes: Number(startMinutes),
          endHours: Number(endHours),
          endMinutes: Number(endMinutes),
        };
      }
    
      return null;
    }
    
    export function getAppointment({ timeSlot, weekOffset, bookDate }) {
      const startDate = addWeeks(
        setIsoDay(
          setHours(
            setMinutes(bookDate, timeSlot.startMinutes),
            timeSlot.startHours,
          ),
          timeSlot.day,
        ),
        weekOffset,
      );
    
      const endDate = addWeeks(
        setIsoDay(
          setHours(
            setMinutes(bookDate, timeSlot.endMinutes),
            timeSlot.endHours,
          ),
          timeSlot.day,
        ),
        weekOffset,
      );
    
      return {
        startDate,
        endDate,
      };
    }
    
    function getNextAppointments(params, weekOffset = 0, prevAppointments = []) {
      const {
        timeSlots,
        count,
        hoursAhead,
        bookDate,
      } = params;
    
      const appointments = [...prevAppointments];
    
      const timeSlotCount = timeSlots.length;
      const aheadBookDate = addHours(bookDate, hoursAhead);
    
      for (let i = 0; i < timeSlotCount; i += 1) {
        const timeSlot = timeSlots[i];
    
        const appointment = getAppointment({
          timeSlot,
          weekOffset,
          bookDate,
        });
    
        const isAppointmentAvailable = compareDesc(aheadBookDate, appointment.startDate) >= 0;
    
        if (isAppointmentAvailable) {
          appointments.push(appointment);
        }
    
        if (appointments.length >= count) {
          break;
        }
      }
    
      /**
       * Repeat search in the next week only if:
       *
       * (1) we have not found the required number of appointments
       * AND
       * (2) we have not reached the recursion limit (two weeks with empty results)
       */
      if (appointments.length < count && weekOffset <= count) {
        return getNextAppointments(params, weekOffset + 1, appointments);
      }
    
      return appointments;
    }
    
    export default function getAppointments(
      {
        timeSlotStrings,
        count,
        hoursAhead,
      },
      bookDate = new Date(),
    ) {
      const timeSlots = timeSlotStrings.reduce(
        (prevTimeSlots, timeSlotString) => {
          const timeSlot = parseTimeSlot(timeSlotString);
    
          if (!timeSlot) {
            return prevTimeSlots;
          }
    
          return [
            ...prevTimeSlots,
            timeSlot,
          ];
        },
        [],
      );
    
      if (!timeSlots.length) {
        return null;
      }
    
      return getNextAppointments({
        timeSlots,
        count,
        hoursAhead,
        bookDate,
      });
    }
    
  • URL: /components/raw/select-appointment/get-appointments.js
  • Filesystem Path: src/components/atoms/select-appointment/get-appointments.js
  • Size: 3.2 KB
  • Content:
    import h from 'hyperscript';
    import format from 'date-fns/format';
    import getIsoDay from 'date-fns/get_iso_day';
    
    import getAppointments from './get-appointments';
    
    const DAY_NAMES = [
      'Mo',
      'Di',
      'Mi',
      'Do',
      'Fr',
      'Sa',
      'So',
    ];
    
    function formatAppointment({ startDate, endDate }) {
      const dayName = DAY_NAMES[getIsoDay(startDate) - 1];
    
      return {
        value: `${format(startDate, 'YYYY-MM-DD-HH:mm')}/${format(endDate, 'HH:mm')}`,
        display: `${dayName}, ${format(startDate, 'D.M.YYYY, HH:mm')}-${format(endDate, 'HH:mm')} Uhr`,
      };
    }
    
    function initField($el) {
      const $options = $el.querySelectorAll('option:not([value=""]):not([disabled])');
      const timeSlotStrings = $options.map($option => $option.value);
    
      const appointments = getAppointments(
        {
          timeSlotStrings,
          count: 2,
          hoursAhead: 1,
        },
        new Date(),
      );
    
      if (appointments === null) {
        return;
      }
    
      const $fragment = document.createDocumentFragment();
    
      appointments.forEach((appointment) => {
        const { value, display } = formatAppointment(appointment);
    
        $fragment.appendChild(
          h('option', { value }, display),
        );
      });
    
      $el.appendChild($fragment);
      $options.forEach($option => $option.remove());
    }
    
    // Intialize via class on the select element.
    document.querySelectorAll('.js-select-appointment').forEach(initField);
    
    // Intialize via class on the parent form element.
    // This is a hacky workaround for cases where select attributes cannot be customized.
    document.querySelectorAll('.js-select-appointment-form select').forEach(initField);
    
  • URL: /components/raw/select-appointment/select-appointment.js
  • Filesystem Path: src/components/atoms/select-appointment/select-appointment.js
  • Size: 1.6 KB

There are no notes for this item.