20241229 - 에어비앤비와 유사한 기능으로 변경 Date Picker

Version 1.0.1 - 09-FEB-2023 - Steve Muench

Version 1.0.2 - 29-DEC-2024


    - 기존 예약일 선택 못 하도록 로드 시점 disable
    - 첫번째 예약 가능 날짜 선택 시 이후 최초 예약일 전일(-1)까지 선택할 수 있는 MAX로 설정


처음에는 2025년 1월 1일, 2일, 3일만 선택 불가능 (이미 예약된 일자들)


12월 9일을 선택하게 되면 연속적으로 예약할 수 있는 날짜의 MAX는 12월 31일임, 그 이후 날짜는 비활성화 (1월 1일부터 예약이 있으므로)



페이지 로드시 JS


// Setup the config for CheckIn/CheckOut Date Range Picker
window.checkInCheckOutDateRangePicker = new DateRangePicker({
   picker: {
       name: "P6_CHECKIN_CHECKOUT_PICKER",
       format: "YYYY-MM-DD",
       allowSingleDay: false
   },
   start: {
       name: "P6_CHECKIN",  
       label: "Check In"
   },
   end: {
       name: "P6_CHECKOUT",
       label:"Check Out"
   },
   reset: {
       id:"Reset_CheckIn_CheckOut"
   }
});

const testDates = ['2025-01-01', '2025-01-02', '2025-01-03'];
window.checkInCheckOutDateRangePicker.setDisabledDates(testDates);


첫번째 날짜(예약시작일) 선택 후 서버에서 예약 시작일 이후 최초의 예약된 날짜를 가져와서 반영


const maxDate = apex.item('P6_MAX').getValue();
window.checkInCheckOutDateRangePicker.setMaxDate(maxDate);



dateRangePicker.css


/* Hide the current day indicator*/
a-date-picker.date-range-picker .a-DatePicker-calendar td.is-current {
    --a-datepicker-calendar-day-current-background-color:
        var(--a-datepicker-calendar-background-color);
    --a-datepicker-calendar-day-current-text-color:
        var(--a-datepicker-calendar-day-text-color);
    --a-datepicker-calendar-day-current-border-color:
        var(--a-datepicker-calendar-background-color);
}

/* Make the current and selected day use a regular font, not bold */
a-date-picker.date-range-picker .a-DatePicker-calendar td.is-current > span,
a-date-picker.date-range-picker .a-DatePicker-calendar td.is-selected > span {
  font-weight: var(--a-datepicker-calendar-day-font-weight,
                   var(--a-base-font-weight-semibold,500));
}

/* Round the corners of the start and end dates */
a-date-picker.date-range-picker .a-DatePicker-calendar td.is-current.dateRangeStart > span,
a-date-picker.date-range-picker .a-DatePicker-calendar td.is-current.dateRangeEnd > span {
   border-radius: var(--a-datepicker-calendar-day-border-radius);
}

a-date-picker.date-range-picker .a-DatePicker-calendar td.dateRangeStart,
a-date-picker.date-range-picker .a-DatePicker-calendar td.dateRangeEnd,
a-date-picker.date-range-picker .a-DatePicker-calendar td.dateRangeSingleDay {
   --a-datepicker-calendar-day-border-radius:
       calc(var(--a-datepicker-calendar-day-font-size, 1rem) * 2);
   --a-datepicker-calendar-day-text-color:
       var(--a-palette-primary-contrast);                        
}

/* Style the day at the start of the date range */

a-date-picker.date-range-picker .a-DatePicker-calendar td.dateRangeStart > span {
   border-start-end-radius: 0 !important;
   border-end-end-radius: 0 !important;
   margin-inline: calc(0px - var(--a-datepicker-calendar-day-spacing, 2px));
   inline-size: unset;
   min-inline-size: calc(var(--a-datepicker-calendar-day-font-size, 1rem) * 2);
}

/* Style the days in the middle of the date range */

a-date-picker.date-range-picker .a-DatePicker-calendar td.dateRangeMiddle {
   --a-datepicker-calendar-day-background-color:
       var(--a-datepicker-calendar-day-hover-background-color,
           var(--a-gv-row-hover-background-color));
   --a-datepicker-calendar-day-text-color:
             var(--a-palette-primary-contrast);            
   --a-datepicker-calendar-day-border-color: transparent;
   position: relative;
}

a-date-picker.date-range-picker .a-DatePicker-calendar td.dateRangeMiddle:before {
 content: "";
 background-color: var(--a-datepicker-calendar-day-selected-background-color,
                       var(--a-datepicker-calendar-day-current-background-color,
                           var(--a-palette-primary)));
 opacity: 1; /* Use value like 0.5 for middle days to be slightly different */
 inline-size: 100%;
 position: absolute;
 z-index: 0;
 left: 0;
 block-size: calc(var(--a-datepicker-calendar-day-font-size, 1rem) * 2);
 top: 50%;
 transform: translateY(-50%);
}

a-date-picker.date-range-picker .a-DatePicker-calendar td.dateRangeMiddle > span {
   border-radius: 0;
   inline-size: unset;
   position: relative;
   z-index: 1;
   background-color: transparent;
}

/* Style the day at the end of the date range */

a-date-picker.date-range-picker .a-DatePicker-calendar td.dateRangeEnd  {
   --a-datepicker-calendar-day-background-color:
       var(--a-datepicker-calendar-day-selected-background-color,
           var(--a-datepicker-calendar-day-current-background-color,
              var(--a-palette-primary)));
   --a-datepicker-calendar-day-text-color:
       var(--a-datepicker-calendar-day-selected-text-color,
          var(--a-datepicker-calendar-day-current-text-color,
             var(--a-palette-primary-contrast)));
   --a-datepicker-calendar-day-border-color:
       var(--a-datepicker-calendar-day-selected-border-color,
           var(--a-datepicker-calendar-day-current-border-color,
               var(--a-palette-primary)));
}

a-date-picker.date-range-picker .a-DatePicker-calendar td.dateRangeEnd > span {
   border-end-start-radius: 0 !important;
   border-start-start-radius: 0 !important;
   margin-inline: calc(0px - var(--a-datepicker-calendar-day-spacing, 2px));
   inline-size: unset;
   min-inline-size: calc(var(--a-datepicker-calendar-day-font-size, 1rem) * 2);
}

a-date-picker.date-range-picker .a-DatePicker-calendar td.dateRangeSingleDay > span {
   margin-inline: calc(0px - var(--a-datepicker-calendar-day-spacing, 2px));
   inline-size: unset;
   min-inline-size: calc(var(--a-datepicker-calendar-day-font-size, 1rem) * 2);
}

/* Add this single style to your existing CSS */
a-date-picker.date-range-picker .a-DatePicker-calendar td.is-disabled > span {
   text-decoration: line-through;
}


dateRangePicker.js


/*--------------------------------------------------------------------
    Customized 2024-12-29
    - 기존 예약일 선택 못 하도록 로드 시점 disable
    - 첫번째 예약 가능 날짜 선택 시 이후 최초 예약일 전일(-1)까지 선택할 수 있는 MAX로 설정
\_____________________________________________________________________*/
window.DateRangePicker = class DateRangePicker {
    constructor(pConfig) {
        this.#config = pConfig;
        this.#disabledDates = new Set();
        this.#maxDate = null;
       
        this.#pickerItem().element.addClass("date-range-picker");
        this.#assignInitialValueFromStartDate();
        this.#assignDayFormatter();
        this.#pickerItem().element.on("change", () => {
            this.#onChanged();
        });
        document.getElementById(this.#resetId()).addEventListener(
            "click", () => {
            this.#reset();
        });
    }

    // Private fields
    #config;
    #disabledDates;
    #maxDate;

    // Public method to set disabled dates
    setDisabledDates(dates) {
        this.#disabledDates = new Set(dates);
        this.#pickerItem().refresh();
    }

    setMaxDate(date) {
        this.#maxDate = date;
        this.#pickerItem().refresh();
    }

    #pickerItem() {
        return apex.items[this.#config.picker.name];
    }

    #pickerFormat() {
        return this.#config.picker.format;
    }

    #pickerSingleDay() {
        return this.#config.picker.allowSingleDay;
    }

    #startItem() {
        return apex.items[this.#config.start.name];
    }

    #startItemLabel() {
        return this.#config.start.label;
    }

    #endItem() {
        return apex.items[this.#config.end.name];
    }

    #resetId() {
        return this.#config.reset.id;
    }

    #endItemLabel() {
        return this.#config.end.label;
    }

    #assignInitialValueFromStartDate() {
        const startItemValue = this.#startItem().getValue();
        if (startItemValue) {
            this.#pickerItem().setValue(startItemValue,null,true);
        }
    }

    #assignDayFormatter() {
        const pickerItem = this.#pickerItem();
        const startItemLabel = this.#startItemLabel();
        const endItemLabel = this.#endItemLabel();
        const pickerFormat = this.#pickerFormat();
        const startItem = this.#startItem();
        const endItem = this.#endItem();
        const getDisabledDates = () => this.#disabledDates;
        const getMaxDate = () => this.#maxDate;

        pickerItem.dayFormatter = function(iso8860DateString) {
            const disabledDates = getDisabledDates();
            const maxDate = getMaxDate();
           
            const currentDate = apex.date.parse(iso8860DateString, "YYYY-MM-DD");
            const isDisabledByDate = disabledDates.has(iso8860DateString);
            const isAfterMax = maxDate ?
                apex.date.isAfter(currentDate, apex.date.parse(maxDate, pickerFormat)) :
                false;
           
            const isDisabled = isDisabledByDate || isAfterMax;
           
            const startDateValue = startItem.getValue();
            const startDate = startDateValue ? apex.date.parse(startDateValue, pickerFormat) : null;
            const endDateValue = endItem.getValue();
            const endDate = endDateValue ? apex.date.parse(endDateValue, pickerFormat) : null;
           
            var tooltipText = "";
            var dateRangeClass = "";

            if (!startDateValue) {
                tooltipText = "Choose " + (startItemLabel ? startItemLabel : "Start") + " Date";
            } else {
                if (apex.date.isSame(startDate, currentDate)) {
                    tooltipText = startItemLabel;
                } else {
                    if (!endDateValue) {
                        tooltipText = "Choose " + (endItemLabel ? endItemLabel : "End") + " Date";
                    } else {
                        if (apex.date.isSame(endDate, currentDate)) {
                            tooltipText = endItemLabel;
                        }
                    }
                }
            }

            if (startDateValue) {
                if (apex.date.isSame(currentDate, startDate)) {
                    if (endDateValue && apex.date.isSame(currentDate, endDate)) {
                        dateRangeClass = "dateRangeSingleDay";
                    } else {
                        dateRangeClass = "dateRangeStart";
                    }
                } else if (endDateValue) {
                    if (apex.date.isSame(currentDate, endDate)) {
                        dateRangeClass = "dateRangeEnd";
                    } else if (apex.date.isBetween(currentDate, startDate, endDate)) {
                        dateRangeClass = "dateRangeMiddle";
                    }
                }
            }

            return {
                disabled: isDisabled,
                class: dateRangeClass,
                tooltip: isDisabled ? 'Not Available' : tooltipText
            };
        };

        pickerItem.refresh();
    }

    #onChanged() {
        const startItem       = this.#startItem();
        const endItem         = this.#endItem();
        const pickerItem      = this.#pickerItem();
        const pickerFormat    = this.#pickerFormat();
        const pickerSingleDay = this.#pickerSingleDay();

        if (!pickerItem.getValue()) {
            this.#reset();
        }
        else {
            if (startItem.getValue() === "") {
                startItem.setValue(pickerItem.getValue());
            }
            else {
                const datepicked = pickerItem.getNativeValue();
                const startDate  = apex.date.parse(startItem.getValue(),
                                                pickerFormat);
                if (apex.date.isBefore(datepicked,startDate)) {
                    startItem.setValue(pickerItem.getValue());
                }
                else if ((pickerSingleDay &&
                            apex.date.isSame(datepicked,startDate)) ||
                            apex.date.isAfter(datepicked,startDate)) {
                    endItem.setValue(pickerItem.getValue());
                }
            }
            pickerItem.setValue(startItem.getValue(), null, true);
            pickerItem.refresh();
        }
    }

    #reset(){
        const startItem = this.#startItem();
        const endItem = this.#endItem();
        const pickerItem = this.#pickerItem();
       
        startItem.setValue(null,null,true);
        endItem.setValue(null,null,true);
        pickerItem.setValue(null,null,true);
        pickerItem.refresh();
    }
};




참고

https://diveintoapex.com/2023/02/07/designer-collab-for-date-ranges/

댓글 없음:

댓글 쓰기

20250202 - IG 다운로드 버튼 바로 보이기

JS initialization Code : function (config) {     var $ = apex.jQuery,         toolbarData = $.apex.interactiveGrid.copyDefaultToolbar(),  ...