leongersen / noUiSlider

noUiSlider is a lightweight, ARIA-accessible JavaScript range slider with multi-touch and keyboard support. It is fully GPU animated: no reflows, so it is fast; even on older devices. It also fits wonderfully in responsive designs and has no dependencies.
https://refreshless.com/nouislider/
MIT License
5.66k stars 659 forks source link

Ideas for date slider displaying years for distant dates, months for recent dates? #1171

Closed chrisjdixon closed 2 years ago

chrisjdixon commented 2 years ago

We've got a particular need for a slider to display both recent dates / months and also years in distant history. This could be accomplished with date functionality shown on the examples page, but specific dates many years ago aren't useful and bring undesired complexity. We'd also want non linear functionality, as the difference between the last few months is far more important than the difference between a few months 20 years ago, and if things didn't scale that difference would be too fiddly.

We're thinking maybe increments of single years until Date().getFullYear() - 1 and then "Jan 2020", "Feb 2020", ... , "Oct 2021", "Nov 2021". Any ideas how we might be able to get noUiSlider to do this?

leongersen commented 2 years ago

To build on the example:

dateSlider.noUiSlider.on('update', function (values, handle) {
    dateValues[handle].innerHTML = formatter.format(new Date(+values[handle]));
});

You could compare the date to the current date, and base your formatting off of that:

const currentDate = ...// 

dateSlider.noUiSlider.on('update', function (values, handle) {
    const selectedDate = new Date(+values[handle]);
    let formatted = formatter.format(selectedDate);

    if (selectedDate is longer than x days ago) { // or compared to currentDate
        formatted = someOtherFormatted.format(selectedDate);
    }

    dateValues[handle].innerHTML = formatted;
});
chrisjdixon commented 2 years ago

OK great, thank you! I'm new to JS and don't know how to fully build out "selectedDate is longer than x days ago", so would you mind if I asked a few silly questions? I've already spent an hour and I'm stuck 😛

I want to change the formatter if the first slider value's year precedes Date().getFullYear() - 1, but I unexpectedly can't get the year of the first value through variations of values[0].getFullYear() / Number() and similar... I feel like an idiot.

var dateValues = [
    document.getElementById('event-start'),
    document.getElementById('event-end')
];

var monthFormatter = new Intl.DateTimeFormat('en-GB', {
    year:"numeric", month:"short"
});

var yearFormatter = new Intl.DateTimeFormat('en-GB', {
    year:"numeric"
});

dateSlider.noUiSlider.on('update', function (values, handle) {
    if (values[0].Date.getFullYear() - 1 < new Date().getFullYear() - 1) 
    {console.log('year formatting happens here');
    dateValues[handle].innerHTML = yearFormatter.format(new Date(+values[handle]));}
    else {console.log('month formatting happens here');
    dateValues[handle].innerHTML = monthFormatter.format(new Date(+values[handle]));};
});

Can you see what I'm doing wrong? I also want to format dates separately, if that's relevant.

Thank you very much for the help,

leongersen commented 2 years ago

Where did you get values[0].Date from? Converting the slider value to a date happens as new Date(+values[handle]);. You can call getFullYear on that.

chrisjdixon commented 2 years ago

OK great! We've now got this nicely:

dateSlider.noUiSlider.on('update', function (values, handle) {
    if (new Date(+values[handle]).getFullYear() < new Date().getFullYear() - 1) 
    {dateValues[handle].innerHTML = yearFormatter.format(new Date(+values[handle]));}
    else {dateValues[handle].innerHTML = monthFormatter.format(new Date(+values[handle]));};
});

Our only remaining front end issue are the tooltips. There are formatting options such as wNumb({decimals: 1}) but we're unsure if tooltips can be made with the same function as above. Can they? Is there a way to simply use the formatted values we've already worked out?

Thanks again,

leongersen commented 2 years ago

Sure. You factor out your update event in to a method:

function format (value) {
    var date = new Date(+value);

    if (date.getFullYear() < new Date().getFullYear() - 1) {
        return yearFormatter.format(date);
    }

    return monthFormatter.format(date);
}

Then call it in the update event and passing it as the format option for the tooltips:

dateSlider.noUiSlider.on('update', function (values, handle) {
    dateValues[handle].innerHTML = format(values[handle]);
});
tooltips: {
    to: format
}
chrisjdixon commented 2 years ago

OK looks good! But we can't get tooltips: { to: format } to work. The tooltips docs suggest we need to also include , from: ... in that part (argument?), and one of your SO answers from a few years ago suggests that should be , from: Number.

Yet in any variation we try, eg. tooltips: {to: format}, tooltips: {to: format, from: Number, to: format, from: Number} we get Uncaught Error: noUiSlider (14.6.4): must pass a formatter for all handles. Is there something we're missing?

update: your solution works fine with only one handle, but we don't understand why this won't work for multiple.

leongersen commented 2 years ago

You can resolve those issues by updating to the latest version of noUiSlider.

In the release you are currently working with, you'd pass an array to the tooltips option: tooltips: [{to: format, from: Number}, {to: format, from: Number}].

chrisjdixon commented 2 years ago

OK, cool. Another super dumb question but there are tons of files I don't understand in those downloads but I can't find "nouislider.js" or "nouislider.css". It seems in previous versions they were saved in the "distribute" dir... do you know where I could find them for the latest version?

leongersen commented 2 years ago

How are you downloading the package?

chrisjdixon commented 2 years ago

This link from noUiSlider's GitHub releases page.

leongersen commented 2 years ago

Okay, gotcha. I stopped committing the distributed files upon release as of v15, so they're no longer in the zip when downloading the source. I have yet to add a nice alternative for npm installs (see #1148).

You can grab these for now: https://refreshless.com/nouislider/dist/nouislider.js?v=1550 https://refreshless.com/nouislider/dist/nouislider.css?v=1550

github-actions[bot] commented 2 years ago

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.