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

처리조건 :

1. 기본 로드될 때 이미 예약되어 선택 불가능한 날짜들 17, 18일, 24,25일 비활성화처리


2. 체크인 날짜 선택시 그 이후 기예약 시작일 찾아서 거기까지만 예약 가능하도록 나머지 일자 비활성화 처리



3. 체크아웃 날짜 선택시 처음에 로드했던 예약 불가능한 날짜들 17, 18일, 24,25일 비활성화처리 (단, 기예약 시작일과 현재 선택하는 체크아웃 날짜는 같을 수 있도록 처리 보완 필요, 추가 이미지 확인)





Page Load


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-17', '2025-01-18', '2025-01-24', '2025-01-25', '2025-01-26'];
window.checkInCheckOutDateRangePicker.setDisabledDates(testDates);


$(document).on('daterangepicker-reset', function(event, data) {
    apex.item('P6_MAX').setValue(null);

    window.checkInCheckOutDateRangePicker.setMaxDate(null);

    const testDates = ['2025-01-17', '2025-01-18', '2025-01-24', '2025-01-25', '2025-01-26'];
    window.checkInCheckOutDateRangePicker.setDisabledDates(testDates);
});

// 참고 : 예약된 날짜 데이터 가져오기
/*
apex.server.process(
   'GET_RESERVED_DATES',
   {},
   {
       success: function(data) {
           // data는 ['2024-01-01', '2024-01-02', '2024-01-03'] 형식으로 받음
           window.checkInCheckOutDateRangePicker.setDisabledDates(data);
           window.eventStartEndDateRangePicker.setDisabledDates(data);  // 두 번째 데이트피커에도 적용
       },
       error: function(jqXHR, textStatus, errorThrown) {
           apex.message.showErrors([{
               type: "error",
               location: ["page"],
               message: "Failed to load reserved dates",
               unsafe: false
           }]);
       }
   }
);
*/


P6_CHECKIN 선택시(변경시), Max 예약일 구한 후 :

PL/SQL


:P6_MAX := '2025-01-17'; --to_date('2025-01-17', 'YYYY-MM-DD');


JS 


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


P6_CHECKOUT 선택시(변경시)


window.checkInCheckOutDateRangePicker.setMaxDate(null);

//나중에 종료일 선택 날짜와 P6_MAX와 같으면 비활성화 날짜 리스트에서 제외, 예를 들어 '2025-01-17' 종료일 선택일 경우
const testDates = ['2025-01-18', '2025-01-24', '2025-01-25', '2025-01-26'];
window.checkInCheckOutDateRangePicker.setDisabledDates(testDates);


P6_CHECKIN_CHECKOUT_PICKER :



Reset_CheckIn_CheckOut 버튼 (SID 및 DA 로 셋팅) :




CSS : 


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);
}

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));
}

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);                        
}

a-date-picker.date-range-picker .a-DatePicker-calendar td {
    height: 44px !important;
    position: relative;
}

a-date-picker.date-range-picker .a-DatePicker-calendar td > span {
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    width: 44px !important;
    height: 44px !important;
    margin: auto !important;
    border-radius: 50% !important;
    position: relative;
    z-index: 2;
}

a-date-picker.date-range-picker .a-DatePicker-calendar td.dateRangeStart {
    background: none;
    position: relative;
}

a-date-picker.date-range-picker .a-DatePicker-calendar td.dateRangeStart:not(.dateRangeSingleDay)::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    right: 0;
    height: 44px;
    transform: translateY(-22px);
    background-color: #F7F7F7;
    z-index: 1;
}

a-date-picker.date-range-picker .a-DatePicker-calendar td.dateRangeStart > span {
    background-color: #222222 !important;
    color: #FFFFFF !important;
}

a-date-picker.date-range-picker .a-DatePicker-calendar td.dateRangeMiddle {
    background: none;
    position: relative;
}

a-date-picker.date-range-picker .a-DatePicker-calendar td.dateRangeMiddle::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 0;
    right: 0;
    height: 44px;
    transform: translateY(-22px);
    background-color: #F7F7F7;
    z-index: 1;
}

a-date-picker.date-range-picker .a-DatePicker-calendar td.dateRangeEnd {
    background: none;
    position: relative;
}

a-date-picker.date-range-picker .a-DatePicker-calendar td.dateRangeEnd::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 0;
    right: 50%;
    height: 44px;
    transform: translateY(-22px);
    background-color: #F7F7F7;
    z-index: 1;
}

a-date-picker.date-range-picker .a-DatePicker-calendar td.dateRangeEnd > span {
    background-color: #222222 !important;
    color: #FFFFFF !important;
}

a-date-picker.date-range-picker .a-DatePicker-calendar td.dateRangeSingleDay > span {
    background-color: #222222 !important;
    color: #FFFFFF !important;
}

a-date-picker.date-range-picker .a-DatePicker-calendar td.is-disabled > span {
    text-decoration: line-through;
}

a-date-picker.date-range-picker .a-DatePicker-calendar td.is-selected > span {
    background-color: #222222 !important;
    color: #FFFFFF !important;
}


JS :


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();
        });
    }

    #config;
    #disabledDates;
    #maxDate;

    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 if (endDateValue) {
                        dateRangeClass = "dateRangeStart";
                    } else {
                        dateRangeClass = "dateRangeSingleDay";
                    }
                } 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();

        if (!pickerItem.getValue()) {
            this.#reset();
            return;
        }

        const datepicked = pickerItem.getNativeValue();
        const startDateValue = startItem.getValue();
        const endDateValue = endItem.getValue();

        if (startDateValue === "" || (endDateValue !== "")) {
            startItem.setValue(pickerItem.getValue());
            endItem.setValue(null);
        } else {
            const startDate = apex.date.parse(startDateValue, pickerFormat);
            if (apex.date.isBefore(datepicked, startDate)) {
                startItem.setValue(pickerItem.getValue());
                endItem.setValue(null);
            } else if (!apex.date.isSame(datepicked, startDate)) {
                endItem.setValue(pickerItem.getValue());
            }
        }

        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.element.find('.dateRangeStart, .dateRangeEnd, .dateRangeSingleDay').removeClass('dateRangeStart dateRangeEnd dateRangeSingleDay');
        pickerItem.element.find('.is-selected').removeClass('is-selected');
       
        pickerItem.refresh();
       
        $(document).trigger('daterangepicker-reset', {
            picker: this.#config.picker.name,
            start: this.#config.start.name,
            end: this.#config.end.name
        });
    }
};



참고




20241229 - 버튼 포커스일때 테두리 점점점 제거

CSS


#SID_BTNINFO:focus {
   outline: 2px solid transparent;
   box-shadow: none;

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/

IR 커스텀 버튼 - 재설정 Reset




Execute JavaScript Code :


$(".a-IRR-button--remove[aria-label='필터 제거']").each(function() {
    $(this).click();
});





참고

https://oracleapexsolutions.blogspot.com/2017/09/interactive-reports-button-positions.html

20241016 - EBA_DEMO_WORKLIST 조회된 리전의 PK 값 읽어오기 (feat. Smart Filter 스마트 필터)

1. DB 패키지 생성


create or replace package "EBA_DEMO_WORKLIST" as

    type eba_demo_worklist_pk_t_row is record (
        id varchar2(1000)
    );
    type eba_demo_worklist_pk_t_tab is table of eba_demo_worklist_pk_t_row;

    function get_region_results_pk_values(
        p_page_id          in number,
        p_region_static_id in varchar2,
        p_pk_column_name   in varchar2 )
    return eba_demo_worklist_pk_t_tab pipelined;

end "EBA_DEMO_WORKLIST";
/



create or replace package body "EBA_DEMO_WORKLIST" as

    function get_region_results_pk_values(
        p_page_id          in number,
        p_region_static_id in varchar2,
        p_pk_column_name in varchar2 )  
    return eba_demo_worklist_pk_t_tab pipelined is
    l_region_id   number;
    l_context     apex_exec.t_context;
    type t_col_index is table of pls_integer index by varchar2(255);
    l_col_index t_col_index;
   
    ---------------------------------------------------------------------------
    procedure get_column_indexes( p_columns wwv_flow_t_varchar2 ) is
    begin
        for i in 1 .. p_columns.count loop
            l_col_index( p_columns( i ) ) := apex_exec.get_column_position(
                                                 p_context => l_context,
                                                 p_column_name => p_columns( i ) );
        end loop;
    end get_column_indexes;

begin
    -- 1. get the id of the region from its name
    select region_id
      into l_region_id
      from apex_application_page_regions
     where application_id = v('APP_ID')
       and page_id        = p_page_id
       and static_id      = p_region_static_id;
       
    -- 2. Get a cursor (apex_exec.t_context) for the current region data
    l_context := apex_region.open_query_context(
                     p_page_id      => p_page_id,
                     p_region_id    => l_region_id );
    get_column_indexes( wwv_flow_t_varchar2( p_pk_column_name) );
    while apex_exec.next_row( p_context => l_context ) loop
        pipe row( eba_demo_worklist_pk_t_row(
                      apex_exec.get_varchar2  ( p_context => l_context,
                                              p_column_idx => l_col_index( p_pk_column_name ))));
    end loop;
    apex_exec.close( l_context );
    return;

exception
    when no_data_needed then
        apex_exec.close( l_context );
        return;
    when others then
        apex_exec.close( l_context );
        raise;
end get_region_results_pk_values;

end "EBA_DEMO_WORKLIST";
/


2. 두 개의 리전 생성

1) 스마트 필터와 모든 다른 조건들이 연결이 되어 조회되는 리전 (SID > RESULTS) + 감추기

2) 위 리전에서 조회된 PK 값을 읽어서 조회할 최종 보여지게 되는 리전


where pomst.po_id in
(select to_number(id)
from eba_demo_worklist.get_region_results_pk_values(299,'RESULTS','PO_ID'))


3. 스마트 필터와 연결된 리전 감추기


.hidden {
   display: none;
}





참고

https://diveintoapex.com/2022/06/05/smartfiltering-multiple-regions/

https://blogs.oracle.com/apex/post/add-a-chart-to-your-faceted-search-page

20241016 - Ajax 호출이 SM에 대해 서버 오류 ORA-22905: 내포되지 않은 테이블 항목으로 부터 행을 가지고 올 수 없습니다을(를) 반환했습니다.

Region - Optimizer Hint : APEX$USE_NO_GROUPING_SETS



20250315 - 글로벌 변수 Global Variables

G_USER Specifies the currently logged in user. G_FLOW_ID Specifies the ID of the currently running application. G_FLOW_STEP_ID Specifi...