React.js continuous scroll - javascript

I have a component (for a visual view):
The current way I render this is like so:
const today = new Date()
const minimumDate = new Date(new Date().setDate(today.getDate() - 14))
const maximumDate = new Date(new Date().setDate(today.getDate() + 14))
const currentDate = minimumDate;
const dateRange = [];
while(currentDate <= maximumDate){
const newDate = new Date(currentDate); newDate.setHours(0,0,0,0)
dateRange.push(newDate)
currentDate.setDate(currentDate.getDate() + 1)
}
const [itemDays, setItemDays] = useState(dateRange)
itemDays.map((date, i) => <CalendarDay key ={i} workout={{}} date={date}/>)
What i want is to render a set amount of days on the calendar, having the entire calendar would cause massive performace hits. The above code does this for +-14 days and the current day. What I need is to convert the scoll bar length into some for of index which corresponds to the dates in itemDays so that when it gets below or above a certain index I can then load the next or previous day continuously.
The trouble is, every method ive tried falls flat. Ive tried these methods which are not accurat enough:
Getting the centre element and try to determine where we are in the array
Using the scroll position and the itemDays length to generate an index (The math never checks out, could be to do with the way scrollLeft works)
Each method was either too far from being a feasible solution or would require working with a bunch of edge cases. I would also like to try to snap the scroll to the centre element which I cant do without getting some form on index like the above mentioned.

Related

Algorithm to select new element from array everyday (Wordle)

Like Wordle, I’m trying to implement an algorithm to select a new element from an array everyday.
My current algorithm is as:
function getRandomElement(): string {
const epochMS = new Date(2022, 0).valueOf();
const now = Date.now();
const msInDay = 86400000;
const daysSinceEpochIndex = Math.floor((now - epochMS) / msInDay);
return array[daysSinceEpochIndex % array.length];
}
The issue I’m facing is that while it work, epochMS has a different time zone than now due to daylight savings. This means it selects a new element from the array an hour after it should. How do I account for time zone differences and make it select a new element every 24 hours (i.e., at midnight) no matter where in the world the user is? Thank you.

alternative way to show certain icons according to date Using IF statement

Hi I have this working function (have some problems) but I wonder if I can improve on it o change the way of how it works
Explanation:
this function takes text Content of a div compare it to a current day at hand then it does the if on it and show certain icons according if the condition is met or not.
Example
if today is 29.2 the text content of the div is 29 and I want to show the icons only if the day is old so if the date is like future or tomorrow I want the icons to be hidden … the problem that I am facing btw is this logic works until the month switch to new month and the problem is that the new month numbers are like 1.2.3 small numbers so the icons show up because the if looks at bigger than the current day not smaller ..
any idea how to keep them hidden
protentional complex solution
making a hidden span inside every div which will show the day and the month and then make the logic filter according to month/day rather then only day!! too complex right do you have any more simple idea ?
to do this job.
//hide Task controle (archive Trash) if day is older than today
function hideIcons() {
// Extermal lets !!! already defined Trash and Archive Array style too !! FIX!!!
let divs = document.getElementsByClassName('day-number');
let trash = document.getElementsByClassName('fa-trash-alt');
let archive = document.getElementsByClassName('fa-archive');
let today = new Date();
let s = today.getDate()
for (let i = 0; i < divs.length; i++) {
(() => {
if (divs[i].textContent >= s) {
trash[i].style.display = 'none';
archive[i].style.display = 'none';
};
})(i);
}
};
hideIcons();
<span class="day-number"></span>

Best way to compare the elements of an array of objects

So I have an array that consists of some number of objects that have both StartTime and Duration member variables. The startTime variable is a string in the format "14:20" and the Duration variable is a number, representing a number of minutes.
Can you guys recommend the best logic for going through the array and combining overlapping elements. For example, if the array had two objects, with the first one having a startTime of "00:00" & duration of 60 and the second object having a startTime of "08:00" & duration of 120 they would be left as separate elements BUT if the second object had a start time of "00:30" and a duration of 120, the two would be combined into one object with a startTime of "00:00" and a duration of 150.
I have been stuck on the general logic of this for some time, because I can't figure how to handle the case when two blocks are combined, but the new combined block creates a new overlap that must be handled. Am I just thinking about this wrong?! Usually I'm good with this sort of thing but am seriously struggling here.
I would do this by creating two trees, one indexed by StartTime and the other indexed by EndTime (StartTime + Duration). Search both trees for elements that overlapped. Once the search was complete any found elements would be removed from both trees and the single new element (StartTime = minStartTime(searchResult), EndTime = maxEndTime(searchResult)) would be inserted into both trees.
Uses a little extra storage but I believe this makes your overlap problem trivial.
(Tree implementation left as an exercise :)
expanding on #gavgrif...
i recommend something along these lines.
create new array field ['start'] in minutes from midnight (14:20 -> 188)
sort array by 'start' field
loop through array looking for overlapping items to merge into segments. where overlapping is that start of this new item is not greater than the segment end.
php example:
$segment = array('start' => -1, 'end' => -1); // force new segment on init
$segments = array();
foreach ($items as $item) {
if ($item['start'] > $segment['end']) {
// add current segment (ignore initial fake segment) to segments
if ($segment['start'] > -1) {
// close out current segment
$segments[] = $segment;
}
// start new segment
$segment = $item;
} else {
// increment segment end if its later than current segment end
$segment['end'] = max($segment['end'], $item['end'];
}
}
// close out final segment
$segments[] = $segment;

Javascript Google Chart Issues

I am having issues creating a line chart for my website. Take a look at this link:
http://jsfiddle.net/asgallant/XdncE/
This works fine when I just have 12 arrays (1 entire year) in my inputData variable. However, when I try adding multiple years,
var inputData = [[1990,5335293],[1990,5309932],[1990,5327306],[1990,5354168],
[1990,5394006],[1990,5448990],[1990,5474112],[1990,5446876],[1990,5382558],
[1990,5410053], [1990,5399647],[1990,5386422],[1991,2780189],[1991,2785247],
[1991,2812202],[1991,2815125],[1991,2827592],[1991,2869426],[1991,2862056],
[1991,2822597],[1991,2806516],[1991,2815310],[1991,2806339],[1991,2792384]] ;
my graph completely messes up because the year intervals (haxis) become way off. Why is that? Do you guys know how to fix it?
You have to correct month calculation from
var d = new Date(inputData[i][0], i, 1);
to
var d = new Date(inputData[i][0], i%12, 1);

If elements are touching, split their width in half?

I'm working on a Javascript/jQuery calendar which includes a month view and a day view. Clicking the days will change the date, which will update the date variables in the day view.
The day view is split up into half hour segments from midnight to 11:00 PM. Clicking on any half hour <tr> (the day view is a table) will create an event between that time clicked and an hour in the future, as well as append a div on top of the calendar, spanning the range of time and positioned at the correct starting point (each pixel is a minute...)
There is a problem, however. If you create an "event" between a certain time span where there is already one in place, they overlap. This is the default behavior, obviously, but what I would like to happen is that if an event is created between a range of dates that is already occupied by an event, they align side by side so that they're not overlapping.
This resembles the behavior seen in the iCal app for mac:
Now my first thought to achieve such a goal was to use collision detection, but all the jQuery plugins for this are bloated or require the elements to be draggable.
Then I thought there might be a way in CSS to do this, where if two elements are overlapping, they split the width evenly.
Then I thought that's ridiculously far fetched, so I'm wondering how I can achieve this as easily as possible.
I'll post the full code in a jsFiddle, but for the most important function would be insertEvent which looks like this:
function insertEvent(start, end){
var end_minutes = new Date(end).getMinutes();
var end_border = new Date(new Date(end).setMinutes(end_minutes + 2));
//$(".day_date").html(start + "<br />" + end);
var diff = Math.abs(end_border - new Date(start));
var minutes = Math.floor((diff/1000)/60);
var start_element = $("td").find("[data-date='" + start + "']");
var offset = start_element.offset().top - $(".second").offset().top;
var this_element = $("<div class='event' style='height:" + minutes + "px;margin-top:" + offset + "px;'></div>");
$(".right").prepend(this_element);
}
This takes two parameters in the javascript new Date() format, one for the start date and one for the end date.
The fiddle: http://jsfiddle.net/charlescarver/HwdwL/
One of the the problems I see with your approach is that there isn't a structure to the storage of the data. I've built a calendar in Javascript before and it's not easy work. First, make sure you have some kind of abstraction for the calendar event. Something like:
function CalendarEvent(startDateTime, endDateTime) {
this.startDateTime = startDateTime;
this.endDateTime = endDateTime;
}
CalendarEvent.prototype.start = function() {
return this.startDateTime.getTime();
};
CalendarEvent.prototype.end = function() {
return this.endDateTime.getTime();
};
CalendarEvent.new = function(startDateTime, endDateTime) {
// This is a little factory method. It prevents calendar events
// from having end times that fall before the start time.
// USE THIS TO INSTANTIATE A NEW CALENDAR EVENT
if(endDateTime.getTime() < startDateTime.getTime()) {
throw new Error("End time falls before start time");
}
return new CalendarEvent(startDateTime, endDateTime);
};
CalendarEvent.compare = function(eventOne, eventTwo) {
// this is a class method to compare two events
// If used with sort it will sort by startDateTime
return eventOne.start() - eventTwo.start();
};
// ... add any other methods you need
Next you're going to want to sort the calendar events. I would sort by start time. Then once it is sorted you can actually re-render everything when changes are made. As long as you sort correctly, determining if a calendar event collides is as simple as this:
CalendarEvent.prototype.intersects = function(otherEvent) {
// If the other event starts after this one ends
// then they don't intersect
if(otherEvent.start() > this.end()) {
return false;
}
// If the other event ends before this one starts
// then they don't intersect
if(otherEvent.end() < this.start()) {
return false;
}
// Everything else is true
return true;
};
Because the data is sorted you know that if two or more calendar events intersect they will have to share the space. Granted, you must think about a few things when you divide the space. Do you want a naive implementation where you just share the space equally from left to right (left having the earliest start time). If so your visual representation could look like this if it had 4 events that shared a space (each block is an event):
However if your events have strange shapes they might cause your calendar to look strange. Consider the following:
In this instance event 2 takes up a lot of vertical space and all the space underneath event 1 is unused. Maybe for a better UX you don't want that kind of thing to happen. If so you should design your rendering algorithm accordingly. Just remember that it is probably easiest to re-render on every change that you encounter, but it's all about how you store the data. If you do not store the data in some kind of structure that is easily traversed then you won't be able to do this kind of thing.
But to complete the answer to your question, here is a fairly naive example. I haven't tested it so this is a pretty big assumption of it working. It is not entirely complete you will have to edit the rendering for yourself. This is merely to give you an idea of how to get it to work. It could definitely look prettier:
function renderCalendarEvents(calendarEvents) {
// Sort the calendar events (assuming calendarEvents is an array)
var sortedEvents = calendarEvents.sort(CalendarEvent.compare);
var index = 0;
// renderEvents is an anonymous function that will be called every time
// you need to render an event
// it returns it's columnDivisor.
var renderEvent = function(position) {
var currentEvent = sortedEvents[index];
var nextEvent = sortedEvents[index + 1];
// The default column divisor is determined by
// the current x-position + 1
var columnDivisor = position + 1;
// Increment before any recursion
index += 1;
// Check if nextEvent even exists
if(nextEvent) {
// If the nextEvent intersects with the current event
// then recurse
if(currentEvent.intersects(nextEvent)) {
// We need to tell the next event that it starts at the
// column position that is immediately +1 to the current event
columnDivisor = renderEvent(position + 1);
}
}
// placeEvent() is some function you can call to actually place
// the calendar event element on the page
// The position is the x-position of the current event
// The columnDivisor is a count of the amount of events sharing this column
placeEvent(currentEvent, position, columnDivisor);
return columnDivisor;
};
while(true) {
// render events until we're done
renderEvent(0);
if(index >= sortedEvents.length) {
break;
}
}
}
Essentially the idea with this particular algorithm is that if the nextEvent on the list exists and that event intersects with the currentEvent then we need to split the width of the currentEvent. It keeps on recursing until it finds no more intersections then it makes it's way back up the chain of recursive calls. I skipped the actual DOM manipulation logic because really the hard part is determining how much you need to split the actual column in order to get these events to fit. So hopefully this all makes a little bit of sense.
EDIT:
To be much more clear, in order to add this to your existing code I would replace your insertEvent function with something like this. I don't write all of the logic for you so you'll have to do some of your own writing. But that's half the fun :-).
function insertEvent(start, end) {
var newEvent = Calendar.new(start, end);
// you'll have to store the array somewhere.
// i'm just assuming some kind of global right now
eventsArray.push(newEvent);
// You'll want to destroy any event elements
destroyCurrentEventElements();
// Now run the rendering function
renderCalendarEvents(eventsArray);
}

Categories

Resources