I finally "vibe-coded" the perfect custom Date Picker using AI (No plugins needed!)

Like many of you, I’ve never been completely satisfied with Bubble’s native date picker or the available plugins. They always feel a bit clunky, hard to style perfectly, or don’t fit the premium feel I want for my UI.

I decided to see if I could “vibe-code” my way into building a custom one from scratch using Google Gemini (3.1 Pro). After a few hours of tweaking the HTML and CSS back and forth with the AI, it actually worked perfectly.

I wanted to share my approach here in case anyone else wants to break free from the standard date pickers.

image

How it works:

The entire date picker is an all-in-one HTML script utilizing the Flatpickr library. It starts as a sleek, blank input bar. When clicked, it doesn’t just expand—it generates a true custom floating group that breaks out of the element boundaries to display the calendar, so it never messes with your page layout.

Features I had the AI build into it:

  • A Quick Select sidebar (Last 7 Days, This Month, etc.).

  • Bottom action buttons (Yesterday, Today, Tomorrow, Apply, Clear).

  • A custom “All dates up to this date” toggle that acts as an epoch/beginning-of-time filter for database searches.

  • Two-way URL Parameter Sync: The script is programmed to automatically read the selected dates from a URL parameter on page load, and it outputs a clean start_date,end_date string when the user makes a new selection.

    ?dueDateRange=2026-02-17T00%3A00%3A00.000Z%2C2026-03-19T00%3A00%3A00.000Z

How to set it up in your app:

You only need 1 HTML Element and the Toolbox Plugin (specifically the Javascript to Bubble element).

Step 1: The HTML Element Put an HTML element on the page where you want the date picker to live. Set min-width: 0 and max-height: 48px (My code is styled for a 48px height to match my inputs, but you can adjust this). Crucial setting: Make sure “Display as an iFrame” is UNCHECKED in the Bubble layout settings so the calendar can float freely over your other groups! Paste the script into this element.

Step 2: Javascript to Bubble Add a JavascripttoBubble element to the page. Name the function bubble_fn_dateRange (this is what my script is hardcoded to look for) and make sure “Trigger Event” is checked.

Step 3: The Workflow (Push & Pull) When the user clicks “Apply Dates” (or any of the quick-action buttons), the HTML script instantly passes the start_date,end_date string to the Javascript to Bubble element and closes the calendar. In my app, I set up a workflow for “When JavascripttoBubble event triggers” to push those dates directly into my URL as a parameter to filter my repeating groups. When the page reloads, the HTML script natively reads that URL parameter and populates the calendar with those dates!

Make it your own (Vibe Coding):

This specific code might not be 100% perfect for your app’s aesthetic or logic, but that’s the beauty of this. You can take my code, paste it into Gemini or ChatGPT, and just “vibe-code” the rest. For example, you could ask the AI:

  • “Change the blue colors to #YOURCOLOR, and make the border radius 10px.”

  • “Instead of reading the initial dates from a URL parameter, rewrite it so I can drop Bubble Dynamic Data directly into the script to set the start/end dates.”

  • “Change the Javascript output format so it sends two separate Unix timestamps instead of a single string.”

It’s the perfect foundation for anyone who wants a “premium” feel without the limitations of a standard plugin.

HTML Element:

Nothing changes in appearance except for the script pasted in (script below)

JavaScript to Bubble Element

(You’ll need to add the Toolbox Plugin)

zoomed in:

The workflow:

The script:

The script is too long so i'll post in a reply in this thread...

PS, if you have questions on how to make this work, simply ask Gemini or Chat GPT, and they’ll walk you through it. Thats how I learned.

4 Likes

Here is the script:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
<style>
:root{--primary:#34495E;--primary-hover:#2c3e50;--primary-light:#7A91A7;--all-dates-color:#0284c7;--all-dates-light:#7dd3fc;--bg-color:#f3f4f6;--surface:#ffffff;--text-main:#1f2937;--text-muted:#4b5563;--border-color:#e2e8f0;--input-border:#BDBDBD;--placeholder:#A9A9A9}html,body{font-family:'Segoe UI',Roboto,Helvetica,Arial,sans-serif;margin:0!important;padding:0!important;width:100%;height:100%;background:transparent!important}.widget-container{width:100%;height:100%;display:flex;flex-direction:column;position:relative;margin:0!important;padding:0!important}.trigger-bar{width:100%;height:100%;display:flex;align-items:center;cursor:pointer;box-sizing:border-box;margin:0!important}.trigger-bar.empty{background-color:#fff!important;border:2px solid var(--input-border);border-radius:5px;min-height:40px;padding:0 12px!important}.trigger-bar.filled{background-color:transparent!important;border:none;padding:0!important}.empty-content{display:flex;align-items:center;gap:8px;color:var(--placeholder);font-size:14px;font-weight:500;width:100%}.filled-content{display:none;align-items:center;gap:12px;width:100%;padding:0;margin:0}.filled-content-arrow{color:var(--input-border);font-size:18px}.cal-icon{width:44px;height:48px;border:2px solid var(--input-border);border-radius:6px;display:flex;flex-direction:column;background:#fff;box-sizing:border-box;overflow:hidden}.cal-month{height:20px;background:#f3f4f6;border-bottom:1px solid var(--input-border);font-size:11px;font-weight:800;text-align:center;line-height:20px;color:#1f2937;letter-spacing:.5px}.cal-day{height:24px;font-size:18px;font-weight:900;text-align:center;line-height:24px;color:var(--primary)}.custom-date-picker{z-index:9999999!important;background-color:var(--surface);border-radius:16px;box-shadow:0 10px 40px rgba(0,0,0,.15);display:flex;flex-direction:row;overflow:hidden;min-width:800px;border:1px solid var(--border-color);font-family:'Segoe UI',Roboto,Helvetica,Arial,sans-serif;opacity:0;visibility:hidden;pointer-events:none;position:absolute;top:-9999px;transition:opacity .15s ease}.custom-date-picker.show{opacity:1;visibility:visible;pointer-events:auto}.quick-buttons{background-color:#f8fafc;padding:20px 12px;display:flex;flex-direction:column;align-items:stretch;gap:6px;border-right:1px solid var(--border-color);width:max-content;min-width:0;box-sizing:border-box}.quick-buttons h3{margin:0 0 12px 0;font-size:14px;color:var(--text-muted);text-transform:uppercase;letter-spacing:.5px;padding:0 4px}.btn-quick{background:0 0;border:1px solid transparent;padding:8px 12px;border-radius:5px;text-align:left;cursor:pointer;font-size:14px;color:var(--text-main);transition:all .2s ease;width:100%;box-sizing:border-box}.btn-quick:hover{background-color:#e2e8f0;color:var(--primary);border-color:var(--input-border)}.btn-quick.active{background-color:var(--primary);color:#fff;border-color:var(--primary);box-shadow:0 2px 4px rgba(52,73,94,.3)}.divider{height:1px;background-color:var(--border-color);width:100%;margin:16px 0}.btn-clear{color:#ef4444;font-weight:600;font-size:12px;border:1px solid transparent;border-radius:8px;cursor:pointer;transition:all .2s ease;width:100%;height:42px;display:flex;align-items:center;justify-content:center;box-sizing:border-box;background:0 0;text-transform:uppercase;letter-spacing:.5px;padding:0}.btn-clear:hover{background-color:#fef2f2;color:#dc2626;border-color:#fca5a5}.main-content{padding:20px;display:flex;flex-direction:column;align-items:center;flex-grow:1}.flatpickr-calendar.inline{box-shadow:none;border:none}.flatpickr-current-month .numInputWrapper{margin-left:15px!important}.flatpickr-current-month .flatpickr-monthDropdown-months{margin-right:5px}.custom-date-picker:not(.mode-all-dates) .flatpickr-day.selected{background:var(--primary)!important;border-color:var(--primary)!important;color:#fff!important}.custom-date-picker:not(.mode-all-dates) .flatpickr-day:hover:not(.selected),.custom-date-picker:not(.mode-all-dates) .flatpickr-day.startRange:not(.selected),.custom-date-picker:not(.mode-all-dates) .flatpickr-day.endRange:not(.selected){background:var(--primary-light)!important;border-color:var(--primary-light)!important;color:#fff!important}.custom-date-picker:not(.mode-all-dates) .flatpickr-day.inRange{background:#e2e8f0!important;border-color:#e2e8f0!important;color:#1f2937!important;box-shadow:-5px 0 0 #e2e8f0,5px 0 0 #e2e8f0!important;border-radius:0!important}.custom-date-picker:not(.mode-all-dates) .flatpickr-day.inRange:hover{background:var(--primary-light)!important;border-color:var(--primary-light)!important;color:#fff!important;box-shadow:-5px 0 0 var(--primary-light),5px 0 0 var(--primary-light)!important}.custom-date-picker:not(.mode-all-dates) .flatpickr-day.startRange,.custom-date-picker:not(.mode-all-dates) .flatpickr-day.endRange{border-radius:50px!important;box-shadow:none!important}.custom-date-picker:not(.mode-all-dates) .flatpickr-calendar:has(.endRange:not(.startRange)) .flatpickr-day.startRange{border-radius:50px 0 0 50px!important;box-shadow:5px 0 0 #e2e8f0!important}.custom-date-picker:not(.mode-all-dates) .flatpickr-calendar:has(.endRange:not(.startRange)) .flatpickr-day.endRange{border-radius:0 50px 50px 0!important;box-shadow:-5px 0 0 #e2e8f0!important}.custom-date-picker:not(.mode-all-dates) .flatpickr-day.inRange:nth-child(7n){box-shadow:-5px 0 0 #e2e8f0!important}.custom-date-picker:not(.mode-all-dates) .flatpickr-calendar:has(.endRange:not(.startRange)) .flatpickr-day.startRange:nth-child(7n){box-shadow:none!important;border-radius:50px 0 0 50px!important}.custom-date-picker:not(.mode-all-dates) .flatpickr-day.inRange:nth-child(7n+1){box-shadow:5px 0 0 #e2e8f0!important}.custom-date-picker:not(.mode-all-dates) .flatpickr-calendar:has(.endRange:not(.startRange)) .flatpickr-day.endRange:nth-child(7n+1){box-shadow:none!important;border-radius:0 50px 50px 0!important}.custom-date-picker:not(.mode-all-dates) .flatpickr-day.inRange:hover:nth-child(7n){box-shadow:-5px 0 0 var(--primary-light)!important}.custom-date-picker:not(.mode-all-dates) .flatpickr-day.inRange:hover:nth-child(7n+1){box-shadow:5px 0 0 var(--primary-light)!important}.custom-date-picker:not(.mode-all-dates) .flatpickr-day.startRange.endRange{box-shadow:none!important;border-radius:50px!important}.custom-date-picker.mode-all-dates .flatpickr-day.inRange{background:0 0!important;box-shadow:none!important;border-color:transparent!important;color:var(--text-main)!important}.custom-date-picker.mode-all-dates .flatpickr-day.selected,.custom-date-picker.mode-all-dates .flatpickr-day.endRange{background:var(--all-dates-color)!important;border-color:var(--all-dates-color)!important;color:#fff!important;border-radius:50px!important;box-shadow:none!important}.custom-date-picker.mode-all-dates .flatpickr-day:hover:not(.selected){background:var(--all-dates-light)!important;border-color:var(--all-dates-light)!important;color:#fff!important;border-radius:50px!important;box-shadow:none!important}.custom-date-picker.mode-all-dates .flatpickr-day.startRange{background:0 0!important;border-color:transparent!important;color:var(--text-main)!important;box-shadow:none!important}.bottom-section{width:100%;margin-top:auto;display:flex;flex-direction:column}.selection-info-row{display:flex;flex-direction:row;align-items:center;width:100%;height:36px}.btn-all-dates{background-color:transparent;color:var(--primary);border:1px solid var(--border-color);padding:8px 12px;border-radius:6px;font-size:11px;font-weight:700;cursor:pointer;transition:all .2s ease;text-transform:uppercase;letter-spacing:.5px;white-space:nowrap;flex-shrink:0;display:block}.btn-all-dates:hover{background-color:#e2e8f0;border-color:var(--primary)}.btn-all-dates.active-all-dates{background-color:var(--all-dates-color);color:#fff;border-color:var(--all-dates-color);box-shadow:0 4px 10px rgba(2,132,199,.3)}.output-text-container{flex-grow:1;display:flex;justify-content:center}.output-text{color:var(--text-muted);font-size:14px;font-family:'Segoe UI',Roboto,Helvetica,sans-serif;font-weight:500;letter-spacing:.5px;text-align:center}.output-text span.bold{font-family:'Segoe UI',Roboto,Helvetica,sans-serif;font-weight:700;text-transform:uppercase;margin-right:8px;font-size:12px;color:var(--text-muted)}.action-row{display:flex;flex-direction:row;gap:12px;width:100%;justify-content:center}.btn-action{flex:1;background-color:transparent;color:var(--primary);border:1px solid var(--border-color);border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;transition:all .2s ease;text-transform:uppercase;letter-spacing:.5px;height:42px;display:flex;align-items:center;justify-content:center;padding:0}.btn-action.quick-bottom:hover{background-color:#e2e8f0;border-color:var(--primary)}.btn-action.quick-bottom.active-bottom{background-color:var(--primary);color:#fff;border-color:var(--primary);box-shadow:0 2px 4px rgba(52,73,94,.3)}.btn-apply-main{background-color:var(--primary);color:#fff;border:1px solid var(--primary);flex:1.5;transition:all .2s ease,transform .1s ease}.btn-apply-main:hover{background-color:var(--primary-hover);border-color:var(--primary-hover);transform:scale(1.02);box-shadow:0 6px 12px rgba(52,73,94,.2)}.action-row:has(.btn-action.quick-bottom:hover) .btn-apply-main{background-color:transparent!important;color:var(--text-muted)!important;border-color:var(--border-color)!important;transform:scale(1)!important;box-shadow:none!important}@media (max-width:850px){.custom-date-picker{flex-direction:column;min-width:100%}.quick-buttons{border-right:none;border-bottom:1px solid var(--border-color);flex-direction:row;flex-wrap:wrap;width:100%}.btn-quick{text-align:center;flex:1 1 30%}.selection-info-row{flex-direction:column;height:auto;gap:8px}.action-row{flex-wrap:wrap}.btn-action{flex:1 1 45%}.btn-apply-main{flex:1 1 100%}}
</style>

<div class="widget-container">
    <div id="main-trigger" class="trigger-bar empty" onclick="toggleCalendar()">
        <div id="empty-content" class="empty-content">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg>
            Select Date Range...
        </div>
        <div id="filled-content" class="filled-content">
            <span id="all-dates-text" style="display: none; font-size: 15px; font-weight: 700; color: var(--all-dates-color); letter-spacing: 0.5px;">ALL DATES</span>
            <div class="cal-icon" id="icon-container-start"><div class="cal-month" id="icon-month-start">FEB</div><div class="cal-day" id="icon-day-start">21</div></div>
            <span class="filled-content-arrow" id="icon-arrow">&rarr;</span>
            <div class="cal-icon" id="icon-container-end"><div class="cal-month" id="icon-month-end">FEB</div><div class="cal-day" id="icon-day-end">22</div></div>
        </div>
    </div>

    <div class="custom-date-picker" id="dropdown-calendar">
        <div class="quick-buttons">
            <h3>Quick Select</h3>
            <button class="btn-quick" onclick="setRange('last7', this)">Last 7 Days</button>
            <button class="btn-quick" onclick="setRange('next7', this)">Next 7 Days</button>
            <button class="btn-quick" onclick="setRange('last30', this)">Last 30 Days</button>
            <button class="btn-quick" onclick="setRange('next30', this)">Next 30 Days</button>
            <button class="btn-quick" onclick="setRange('thisMonth', this)">This Month</button>
            <button class="btn-quick" onclick="setRange('lastMonth', this)">Last Month</button>
            <button class="btn-quick" onclick="setRange('nextMonth', this)">Next Month</button>
            <div style="margin-top: auto; display: flex; flex-direction: column; width: 100%;"><div style="height: 36px;"></div><div class="divider"></div><button class="btn-clear" onclick="clearSelection()">Clear Dates</button></div>
        </div>

        <div class="main-content">
            <input type="text" id="date-picker" style="display: none;">
            <div class="bottom-section">
                <div class="selection-info-row">
                    <button class="btn-all-dates" id="btn-all-dates" onclick="toggleAllDatesMode()">All dates up to this date</button>
                    <div class="output-text-container"><div class="output-text"><span class="bold">SELECTED:</span><span id="result">Select a date to begin</span></div></div>
                </div>
                <div class="divider"></div>
                <div class="action-row">
                    <button class="btn-action quick-bottom" id="btn-yesterday" onclick="applyQuick('yesterday', this)">Yesterday</button>
                    <button class="btn-action quick-bottom" id="btn-today" onclick="applyQuick('today', this)">Today</button>
                    <button class="btn-action quick-bottom" id="btn-tomorrow" onclick="applyQuick('tomorrow', this)">Tomorrow</button>
                    <button class="btn-action btn-apply-main" onclick="applySelection()">Apply Dates</button>
                </div>
            </div>
        </div>
    </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
<script>
    let isQuickSelect = false, isAllDatesMode = false, isHandlingAllDates = false, currentStartFormatted = "", currentEndFormatted = "", activeStartVisual = null, activeEndVisual = null;
    window.fpInstance = null;
    const monthNames = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"];

    function formatForDisplay(d) { return `${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}-${d.getFullYear()}`; }
    function formatForUrl(d) { return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}T${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}:${String(d.getSeconds()).padStart(2,'0')}.000Z`; }

    window.toggleCalendar = function(forceClose = false) {
        const cal = document.getElementById('dropdown-calendar'), trigger = document.getElementById('main-trigger');
        if (forceClose || cal.classList.contains('show')) { cal.classList.remove('show'); cal.style.top = '-9999px'; }
        else {
            if (cal.parentNode !== document.body) document.body.appendChild(cal);
            cal.classList.add('show');
            const rect = trigger.getBoundingClientRect();
            cal.style.top = (rect.bottom + window.scrollY + 8) + 'px';
            cal.style.right = (document.documentElement.clientWidth - rect.right - window.scrollX) + 'px';
            cal.style.left = 'auto';
            if (window.fpInstance) window.fpInstance.redraw();
        }
    };

    document.addEventListener('click', e => {
        const cal = document.getElementById('dropdown-calendar'), trigger = document.getElementById('main-trigger');
        if (cal.classList.contains('show') && !cal.contains(e.target) && !trigger.contains(e.target)) toggleCalendar(true);
    });

    function updateTriggerBar() {
        const triggerBar = document.getElementById('main-trigger'), empty = document.getElementById('empty-content'), filled = document.getElementById('filled-content'), iconS = document.getElementById('icon-container-start'), iconE = document.getElementById('icon-container-end'), arrow = document.getElementById('icon-arrow'), text = document.getElementById('all-dates-text');
        if (!activeStartVisual || !activeEndVisual) { triggerBar.className = 'trigger-bar empty'; empty.style.display = 'flex'; filled.style.display = 'none'; return; }
        triggerBar.className = 'trigger-bar filled'; empty.style.display = 'none'; filled.style.display = 'flex';
        if (activeStartVisual.getFullYear() === 1970) { iconS.style.display = 'none'; text.style.display = 'block'; iconE.style.display = 'flex'; arrow.style.display = 'block'; document.getElementById('icon-month-end').innerText = monthNames[activeEndVisual.getMonth()]; document.getElementById('icon-day-end').innerText = activeEndVisual.getDate(); }
        else if (activeStartVisual.getTime() === activeEndVisual.getTime()) { iconS.style.display = 'flex'; text.style.display = 'none'; iconE.style.display = 'none'; arrow.style.display = 'none'; document.getElementById('icon-month-start').innerText = monthNames[activeStartVisual.getMonth()]; document.getElementById('icon-day-start').innerText = activeStartVisual.getDate(); }
        else { iconS.style.display = 'flex'; text.style.display = 'none'; iconE.style.display = 'flex'; arrow.style.display = 'block'; document.getElementById('icon-month-start').innerText = monthNames[activeStartVisual.getMonth()]; document.getElementById('icon-day-start').innerText = activeStartVisual.getDate(); document.getElementById('icon-month-end').innerText = monthNames[activeEndVisual.getMonth()]; document.getElementById('icon-day-end').innerText = activeEndVisual.getDate(); }
    }

    window.toggleAllDatesMode = function() {
        isAllDatesMode = !isAllDatesMode;
        const btn = document.getElementById('btn-all-dates'), cal = document.getElementById('dropdown-calendar');
        if (isAllDatesMode) { btn.classList.add('active-all-dates'); cal.classList.add('mode-all-dates'); if (window.fpInstance && window.fpInstance.selectedDates.length > 0) { let endD = window.fpInstance.selectedDates[window.fpInstance.selectedDates.length-1]; isHandlingAllDates = true; window.fpInstance.setDate([new Date(1970,0,1), endD], true); isHandlingAllDates = false; window.fpInstance.jumpToDate(endD); } }
        else { btn.classList.remove('active-all-dates'); cal.classList.remove('mode-all-dates'); if (window.fpInstance && window.fpInstance.selectedDates.length === 2) { let endD = window.fpInstance.selectedDates[1]; window.fpInstance.setDate([endD], true); window.fpInstance.jumpToDate(endD); } }
    };

    window.clearSelection = function() {
        if (window.fpInstance) window.fpInstance.clear();
        activeStartVisual = activeEndVisual = null; currentStartFormatted = currentEndFormatted = ""; isAllDatesMode = false;
        document.getElementById('btn-all-dates').classList.remove('active-all-dates'); document.getElementById('dropdown-calendar').classList.remove('mode-all-dates'); document.getElementById('btn-all-dates').style.display = 'block';
        document.getElementById('result').innerHTML = "Select a date to begin"; document.querySelectorAll('.btn-quick').forEach(b => b.classList.remove('active')); document.querySelectorAll('.quick-bottom').forEach(b => b.classList.remove('active-bottom'));
        updateTriggerBar(); if (typeof window.parent.bubble_fn_dateRange === 'function') window.parent.bubble_fn_dateRange(""); toggleCalendar(true);
    };

    window.applySelection = function() {
        const finalStr = `${currentStartFormatted},${currentEndFormatted}`;
        if (typeof window.parent.bubble_fn_dateRange === 'function') window.parent.bubble_fn_dateRange(finalStr);
        updateTriggerBar(); toggleCalendar(true);
    };

    window.applyQuick = function(type, el) { document.querySelectorAll('.quick-bottom').forEach(b => { if(b.id!=='btn-all-dates') b.classList.remove('active-bottom') }); if (el && el.id!=='btn-all-dates') el.classList.add('active-bottom'); setRange(type, null); applySelection(); };

    setTimeout(() => {
        window.fpInstance = flatpickr("#date-picker", {
            inline: true, mode: "range", showMonths: 2,
            onChange: function(selectedDates) {
                if (isHandlingAllDates) return;
                if (!isQuickSelect) { document.querySelectorAll('.btn-quick').forEach(b => b.classList.remove('active')); document.querySelectorAll('.quick-bottom').forEach(b => { if(b.id!=='btn-all-dates') b.classList.remove('active-bottom') }); }
                if (isAllDatesMode && selectedDates.length === 1 && selectedDates[0].getFullYear() !== 1970) { let v = new Date(window.fpInstance.currentYear, window.fpInstance.currentMonth, 1); isHandlingAllDates = true; window.fpInstance.setDate([new Date(1970,0,1), selectedDates[0]], true); isHandlingAllDates = false; window.fpInstance.jumpToDate(v); return; }
                if (selectedDates.length > 0) {
                    let start = new Date(selectedDates[0]), end = selectedDates.length === 2 ? new Date(selectedDates[1]) : new Date(selectedDates[0]);
                    activeStartVisual = new Date(start); activeEndVisual = new Date(end); end.setDate(end.getDate()+1);
                    currentStartFormatted = formatForUrl(start); currentEndFormatted = formatForUrl(end);
                    if (selectedDates.length === 2 && start.getFullYear() === 1970) document.getElementById('result').innerHTML = `All dates up to &nbsp; ${formatForDisplay(activeEndVisual)}`;
                    else if (selectedDates.length === 1 || activeStartVisual.getTime() === activeEndVisual.getTime()) document.getElementById('result').innerHTML = formatForDisplay(start);
                    else document.getElementById('result').innerHTML = `${formatForDisplay(start)} &nbsp;&rarr;&nbsp; ${formatForDisplay(activeEndVisual)}`;
                    const btnAll = document.getElementById('btn-all-dates'); btnAll.style.display = (selectedDates.length === 2 && start.getFullYear() !== 1970) ? 'none' : 'block';
                } else { document.getElementById('result').innerHTML = "Select a date to begin"; activeStartVisual = null; activeEndVisual = null; document.getElementById('btn-all-dates').style.display = 'block'; }
                isQuickSelect = false;
            }
        });
        document.addEventListener('mousemove', e => {
            const cal = document.getElementById('dropdown-calendar');
            if (cal && cal.classList.contains('show') && window.fpInstance && window.fpInstance.selectedDates.length === 1) {
                if (!e.target.closest('.flatpickr-days')) { const dc = window.fpInstance.daysContainer; const stuck = dc.querySelector('.endRange:not(.selected), .startRange:not(.selected), .inRange'); if (stuck) { const sel = dc.querySelector('.flatpickr-day.selected'); if (sel) sel.dispatchEvent(new MouseEvent('mouseover', { bubbles: true })); } }
            }
        });
        window.setRange = function(type, el) {
            isQuickSelect = true; if (el && el.classList.contains('btn-quick')) { document.querySelectorAll('.btn-quick').forEach(b => b.classList.remove('active')); document.querySelectorAll('.quick-bottom').forEach(b => { if(b.id!=='btn-all-dates') b.classList.remove('active-bottom') }); el.classList.add('active'); }
            const today = new Date(); today.setHours(0,0,0,0); let start = new Date(today), end = new Date(today), viewDate = null;
            if (type !== 'today' && type !== 'yesterday' && type !== 'tomorrow') { isAllDatesMode = false; document.getElementById('btn-all-dates').classList.remove('active-all-dates'); document.getElementById('dropdown-calendar').classList.remove('mode-all-dates'); }
            else if (isAllDatesMode) start = new Date(1970, 0, 1);
            switch(type) {
                case 'yesterday': if(!isAllDatesMode) start.setDate(today.getDate()-1); end.setDate(today.getDate()-1); break;
                case 'tomorrow': if(!isAllDatesMode) start.setDate(today.getDate()+1); end.setDate(today.getDate()+1); break;
                case 'last7': start.setDate(today.getDate()-6); break;
                case 'next7': start.setDate(today.getDate()+1); end.setDate(today.getDate()+7); break;
                case 'last30': start.setDate(today.getDate()-29); break;
                case 'next30': start.setDate(today.getDate()+1); end.setDate(today.getDate()+30); break;
                case 'thisMonth': start = new Date(today.getFullYear(), today.getMonth(), 1); end = new Date(today.getFullYear(), today.getMonth()+1, 0); break;
                case 'lastMonth': start = new Date(today.getFullYear(), today.getMonth()-1, 1); end = new Date(today.getFullYear(), today.getMonth(), 0); break;
                case 'nextMonth': start = new Date(today.getFullYear(), today.getMonth()+1, 1); end = new Date(today.getFullYear(), today.getMonth()+2, 0); viewDate = new Date(today.getFullYear(), today.getMonth(), 1); break;
            }
            window.fpInstance.setDate([start, end], true); window.fpInstance.jumpToDate(viewDate || end);
        };
        (function init() {
            let search = ""; try { search = window.parent.location.search; } catch(e) { search = window.location.search; }
            const params = new URLSearchParams(search), dueDate = params.get('dueDateRange');
            if (dueDate) {
                const dates = dueDate.split(',');
                if (dates.length === 2) {
                    let s = new Date(dates[0]), e = new Date(dates[1]); e.setDate(e.getDate()-1);
                    if (s.getFullYear() === 1970) { isAllDatesMode = true; document.getElementById('btn-all-dates').classList.add('active-all-dates'); document.getElementById('dropdown-calendar').classList.add('mode-all-dates'); }
                    window.fpInstance.setDate([s, e], true); window.fpInstance.jumpToDate(e); updateTriggerBar(); return;
                }
            } updateTriggerBar();
        })();
    }, 100);
</script>

Still you need plugins (Toolbox) :slight_smile:

Good job.

I’ve done this many times :grinning_face:

Posted about it a couple of times.

Sometimes a whole section…

one day, I tested a whole MVP in Bubble just using code and no native Bubble elements. The code was all dropped into 1 HTML element and looked like a mini novel. All using JS to Bubble. I did let Bubble handle the auth part. It all worked great.

If you’re doing components, you do need to watch out that the code you use doesn’t change other things in your native Bubble components. Had that happen a couple of times, but it’s an easy fix

2 Likes

Touché :grinning_face:

You do need to remember all plugins are just code…

if I get energetic one of these days, I’ll just code the toolbox plugin into the app and not need the plugin :grinning_face: