mymth / vanillajs-datepicker

A vanilla JavaScript remake of bootstrap-datepicker for Bulma and other CSS frameworks
MIT License
719 stars 147 forks source link

[feature] [request] Support for bootstrap's dark mode #159

Open ferminc opened 10 months ago

ferminc commented 10 months ago

Since v5.3 bootstrap now includes a dark color mode, this is what the datepicker looks like when active, it would be great to have support for it

Screenshot (32)

BSarmady commented 9 months ago

I changed the as following, until it worked for me, you'll probably need to change a little bit more since there are still hardcoded colors in css files.

For my modifications, I used bootstrap css variables to replace hardcoded colors; However, since this component does not exactly follow bootstrap css rules (like button hover, selection, focus) the colors are kind of different.

I also added {buttonClass: 'btn btn-secondary'} to options to make header and today buttons look a little more appealing

.datepicker{ width:-moz-min-content; width:min-content; }
.datepicker:not(.active){ display:none; }
.datepicker-dropdown{ position:absolute; z-index:1000; padding-top:4px; }
.datepicker-dropdown.datepicker-orient-top{ padding-top:0; padding-bottom:4px; }
.datepicker-picker{ display:flex; flex-direction:column; border-radius:0.375rem; background-color:var(--bs-secondary-bg); }
.datepicker-dropdown .datepicker-picker{ box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15); }
.datepicker-main{ flex:auto; padding:2px; }
.datepicker-footer{ box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.1); background-color:var(--bs-secondary-bg); }
.datepicker-title{ box-shadow:inset 0 -1px 1px rgba(0, 0, 0, 0.1); background-color:var(--bs-secondary-bg); padding:0.375rem 0.75rem; text-align:center; font-weight:700; }
.datepicker-controls{ display:flex; }
.datepicker-header .datepicker-controls{ padding:2px 2px 0; }
.datepicker-controls .btn{ /*border-color:var(--bs-secondary-color);*/ background-color:var(--bs-btn-bg); }
.datepicker-controls .btn:hover{ border-color:var(--bs-btn-hover-border-color); background-color:var(--bs-btn-hover-bg); color:var(--bs-btn-hover-color); }
.datepicker-controls .btn:focus{ border-color:var(--bs-btn-hover-border-color); box-shadow:var(--bs-btn-focus-box-shadow); background-color:var(--bs-btn-hover-bg); color:var(--bs-btn-hover-color); }
.datepicker-controls .btn:disabled{ border-color:var(--bs-btn-disabled-border-color); background-color:var(--bs-btn-disabled-bg); color:var(--bs-btn-disabled-color); opacity:var(--bs-btn-disabled-opacity); }
.datepicker-controls .btn:not(:disabled):active{ border-color:#d4dae0; background-color:var(--bs-secondary-color); color:var(--bs-btn-color); }
.datepicker-controls .btn:not(:disabled):active:focus{ box-shadow:0 0 0 0.25rem rgba(211, 212, 213, 0.5); }
.datepicker-header .datepicker-controls .btn{ border-color:transparent; font-weight:bold; }
.datepicker-footer .datepicker-controls .btn{ flex:auto; margin:calc(0.375rem - 1px) 0.375rem; border-radius:0.25rem; font-size:0.875rem; }
.datepicker-controls .view-switch{ flex:auto; }
.datepicker-controls .prev-button, .datepicker-controls .next-button{ padding-right:0.375rem; padding-left:0.375rem; flex:0 0 14.2857142857%; }
.datepicker-controls .prev-button.disabled, .datepicker-controls .next-button.disabled{ visibility:hidden; }
.datepicker-view, .datepicker-grid{ display:flex; }
.datepicker-view{ align-items:stretch; width:15.75rem; }
.datepicker-grid{ flex-wrap:wrap; flex:auto; }
.datepicker .days{ display:flex; flex-direction:column; flex:auto; }
.datepicker .days-of-week{ display:flex; }
.datepicker .week-numbers{ display:flex; flex-direction:column; flex:0 0 9.6774193548%; }
.datepicker .weeks{ display:flex; flex-direction:column; align-items:stretch; flex:auto; }
.datepicker span{ display:flex; align-items:center; justify-content:center; border-radius:0.375rem; cursor:default; -webkit-touch-callout:none; -webkit-user-select:none; -moz-user-select:none; user-select:none; }
.datepicker .dow{ height:1.5rem; font-size:0.9375rem; font-weight:700; }
.datepicker .week{ flex:auto; color:#dee2e6; font-size:0.875rem; }
.datepicker-cell, .datepicker .days .dow{ flex-basis:14.2857142857%; }
.datepicker-cell{ height:2.25rem; }
.datepicker-cell:not(.day){ flex-basis:25%; height:4.5rem; }
.datepicker-cell:not(.disabled):hover{ background-color:var(--bs-primary-bg-subtle); cursor:pointer; }
.datepicker-cell.focused:not(.selected){ background-color:var(--bs-secondary-bg); }
.datepicker-cell.selected, .datepicker-cell.selected:hover{ background-color:var(--bs-primary); color:#ffffff; font-weight:600; }
.datepicker-cell.disabled{ color:rgba(33, 37, 41, 0.5); }
.datepicker-cell.prev:not(.disabled), .datepicker-cell.next:not(.disabled){ color:#6c757d; }
.datepicker-cell.prev.selected, .datepicker-cell.next.selected{ color:#e6e6e6; }
.datepicker-cell.highlighted:not(.selected):not(.range):not(.today){ border-radius:0; background-color:var(--bs-secondary-bg); }
.datepicker-cell.highlighted:not(.selected):not(.range):not(.today):not(.disabled):hover{ background-color:var(--bs-primary-bg-subtle); }
.datepicker-cell.highlighted:not(.selected):not(.range):not(.today).focused{ background-color:var(--bs-secondary-bg); }
.datepicker-cell.today:not(.selected){ background-color:var(--bs-primary-bg-subtle); }
.datepicker-cell.today:not(.selected):not(.disabled){ color:var(--bs-primary-text-emphasis); }
.datepicker-cell.today.focused:not(.selected){ background-color:var(--bs-primary-bg-subtle); }
.datepicker-cell.range-end:not(.selected), .datepicker-cell.range-start:not(.selected){ background-color:#6c757d; color:#ffffff; }
.datepicker-cell.range-end.focused:not(.selected), .datepicker-cell.range-start.focused:not(.selected){ background-color:#69727a; }
.datepicker-cell.range-start:not(.range-end){ border-radius:0.375rem 0 0 0.375rem; }
.datepicker-cell.range-end:not(.range-start){ border-radius:0 0.375rem 0.375rem 0; }
.datepicker-cell.range{ border-radius:0; background-color:#e9ecef; }
.datepicker-cell.range:not(.disabled):not(.focused):not(.today):hover{ background-color:var(--bs-primary-bg-subtle); }
.datepicker-cell.range.disabled{ color:#ced4db; }
.datepicker-cell.range.focused{ background-color:var(--bs-secondary-color); }
.datepicker-input.in-edit{ border-color:#73acfe; }
.datepicker-input.in-edit:focus, .datepicker-input.in-edit:active{ box-shadow:0 0 0.25em 0.25em rgba(115, 172, 254, 0.2); }
fercomunello commented 8 months ago

@BSarmady Thanks for the CSS, currently I'm using Bootstrap v.5.3 so I changed the styles a little bit.

import {Datepicker} from "vanillajs-datepicker";

const {locales}: any = Datepicker;

let inputGroups: HTMLElement[] = [];

document.querySelectorAll(`input[data-component="date-picker"]`)
    .forEach(input => {
        new Datepicker(<HTMLElement> input, {
                autohide: true,
                language: locales['pt-BR'] ? 'pt-BR' : 'en',
                buttonClass: 'btn btn-dark',
                clearButton: true,
                todayButton: true,
                todayButtonMode: 1
            }
        )
       if (input.parentElement?.classList.contains('input-group')) {
           inputGroups.push(<HTMLElement>input.parentElement);
       }
    })
;

for (let i = inputGroups.length - 1; i >= 0; i--){
    const span = inputGroups[i].querySelector('span.input-group-text') as HTMLElement;
    const input: HTMLInputElement | null =
        nextOrPreviousSiblingElement(span, 'input');
    if (input) {
        span?.addEventListener('click',
            () => input.dispatchEvent(new Event('focus')));
    }
    inputGroups.pop();
}
.datepicker {
    width: min-content;

    &:not(.active) {
        display: none;
    }

    .days {
        display: flex;
        flex-direction: column;
        flex: auto;
    }

    .days-of-week {
        display: flex;
    }

    .week-numbers {
        display: flex;
        flex-direction: column;
        flex: 0 0 9.6774193548%;
    }

    .weeks {
        display: flex;
        flex-direction: column;
        align-items: stretch;
        flex: auto;
    }

    span {
        display: flex;
        align-items: center;
        justify-content: center;
        border-radius: 0.375rem;
        cursor: default;
        user-select: none;
    }

    .dow {
        height: 1.5rem;
        font-size: 0.9375rem;
        font-weight: 700;
    }

    .week {
        flex: auto;
        color: #dee2e6;
        font-size: 0.875rem;
    }
}

.datepicker-dropdown {
    position: absolute;
    z-index: var(--z-index-date-picker);
    padding-top: 4px;

    &.datepicker-orient-top {
        padding-top: 0;
        padding-bottom: 4px;
    }

    .datepicker-picker {
        box-shadow: 0 0.5rem 1rem 4px rgba(0, 0, 0, 0.15);
    }
}

.datepicker-picker {
    display: flex;
    flex-direction: column;
    border-radius: 0.375rem;
    background-color: var(--bs-secondary-bg);
}

.datepicker-main {
    flex: auto;
    padding: 2px;
}

.datepicker-footer {
    box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
    background-color: var(--bs-secondary-bg);

    .datepicker-controls {
        .btn {
            flex: auto;
            margin: calc(0.375rem - 1px) 0.375rem;
            border-radius: 0.25rem;
            font-size: 0.875rem;
            font-weight: bold;
        }
    }
}

.datepicker-title {
    box-shadow: inset 0 -1px 1px rgba(0, 0, 0, 0.1);
    background-color: var(--bs-secondary-bg);
    padding: 0.375rem 0.75rem;
    text-align: center;
    font-weight: 700;
}

.datepicker-controls {
    display: flex;
    gap: 4px;

    .btn {
        background-color: var(--bs-btn-bg);

        &:hover {
            border-color: var(--bs-btn-hover-border-color);
            background-color: var(--bs-btn-hover-bg);
            color: var(--bs-btn-hover-color);
        }

        &:focus {
            border-color: var(--bs-btn-hover-border-color);
            box-shadow: var(--bs-btn-focus-box-shadow);
            background-color: var(--bs-btn-hover-bg);
            color: var(--bs-btn-hover-color);
        }

        &:disabled {
            border-color: var(--bs-btn-disabled-border-color);
            background-color: var(--bs-btn-disabled-bg);
            color: var(--bs-btn-disabled-color);
            opacity: var(--bs-btn-disabled-opacity);
        }

        &:not(:disabled):active {
            background-color: var(--bs-btn-hover-bg);
            color: var(--bs-btn-color);

            &:focus {
                box-shadow: 0 0 0 0.25rem rgba(211, 212, 213, 0.5);
            }
        }
    }

    .view-switch {
        flex: auto;
    }
}

.datepicker-header {
    .datepicker-controls {
        padding: 2px 2px 0;

        .btn {
            border-color: transparent;
            font-weight: bold;
        }
    }
}

.datepicker-controls .prev-button,
.datepicker-controls .next-button {
    padding-right: 0.375rem;
    padding-left: 0.375rem;
    flex: 0 0 14.2857142857%;
}

.datepicker-controls .prev-button.disabled,
.datepicker-controls .next-button.disabled {
    visibility: hidden;
}

.datepicker-view, .datepicker-grid {
    display: flex;
}

.datepicker-view {
    align-items: stretch;
    width: 15.75rem;
}

.datepicker-grid {
    flex-wrap: wrap;
    flex: auto;
}

.datepicker-cell, .datepicker .days .dow {
    flex-basis: 14.2857142857%;
}

.datepicker-cell {
    height: 2.25rem;

    &:not(.day) {
        flex-basis: 25%;
        height: 4.5rem;
    }

    &:not(.disabled):hover {
        cursor: pointer;
        [data-bs-theme=auto] &,
        [data-bs-theme=light] & {
            background-color: #dedede;
        }

        [data-bs-theme=dark] & {
            background: var(--bs-tertiary-bg);
        }
    }

    &.focused {
        &:not(.selected) {
            background-color: var(--bs-secondary-bg);
        }
    }

    &.disabled {
        color: rgba(33, 37, 41, 0.5);
    }

    &.highlighted {
        &:not(.selected):not(.range):not(.today) {
            border-radius: 0;
            background-color: var(--bs-secondary-bg);
        }

        &:not(.selected):not(.range):not(.today):not(.disabled):hover {
            background-color: var(--bs-primary-bg-subtle);
        }

        &:not(.selected):not(.range):not(.today).focused {
            background-color: var(--bs-secondary-bg);
        }
    }

    &.today {
        &:not(.selected) {
            background-color: var(--bs-primary-bg-subtle);
        }

        &:not(.selected):not(.disabled) {
            color: var(--bs-primary-text-emphasis);
        }

        &.focused {
            &:not(.selected) {
                background-color: var(--bs-primary-bg-subtle);
            }
        }
    }

    &.range-start {
        &:not(.range-end) {
            border-radius: 0.375rem 0 0 0.375rem;
        }
    }

    &.range-end {
        &:not(.range-start) {
            border-radius: 0 0.375rem 0.375rem 0;
        }
    }

    &.range {
        border-radius: 0;
        background-color: #e9ecef;

        &:not(.disabled):not(.focused):not(.today):hover {
            background-color: var(--bs-primary-bg-subtle);
        }

        &.disabled {
            color: #ced4db;
        }

        &.focused {
            background-color: var(--bs-secondary-color);
        }
    }
}

.datepicker-cell.selected,
.datepicker-cell.selected:hover {
    background-color: var(--bs-primary);
    color: #ffffff;
    font-weight: 600;
}

.datepicker-cell.prev:not(.disabled),
.datepicker-cell.next:not(.disabled) {
    color: #6c757d;
}

.datepicker-cell.prev.selected,
.datepicker-cell.next.selected {
    color: #e6e6e6;
}

.datepicker-cell.range-end:not(.selected),
.datepicker-cell.range-start:not(.selected) {
    background-color: #6c757d;
    color: #ffffff;
}

.datepicker-cell.range-end.focused:not(.selected),
.datepicker-cell.range-start.focused:not(.selected) {
    background-color: #69727a;
}

.datepicker-input {
    &.in-edit {
        border-color: #73acfe;
    }
}

.datepicker-input.in-edit:focus,
.datepicker-input.in-edit:active {
    box-shadow: 0 0 0.25em 0.25em rgba(115, 172, 254, 0.2);
}
BSarmady commented 8 months ago

@fercomunello, your CSS looks better, however I think you have missed a few of the colors for background and box shadow. I think there is a box shadow variable in 5.3.1 too which can be replaced.

For JavaScript I would go with subtle variants of colors or probably secondary instead of dark so change in theme by user does not break the look. Note that not all developers are using default bootstrap colors, in one of my applications by customer request we changed primary color to pink and secondary to chocolate brown

With subtle it look like this in dark mode and light mode

image

image

fercomunello commented 8 months ago

@BSarmady You're right. In my case it's fine because I don't customized Bootstrap 5 a lot, I see.. :thinking:

image image