FullCalendar - Half day in dayGridMonth - javascript

I have a calendar with FullCalendar that allows me to post my absences.
For some absences, they are only half a day (morning or afternoon), and for others, they can last several days but end at 12:00.
The goal is therefore in the monthly view to display the events without filling a whole cell when it is not necessary:
So I have my list of events that contain extendedProps, letting me know if I have to correct their width to take into account the half-days.
For these, I manage to modify their width, thanks to this function:
eventRender: function (info) {
// HALF DAY
var half = info.event.extendedProps.halfDay;
if (half == true) {
// console.log(info);
var elementStyle = info.el;
jQuery(elementStyle).css('width', 50 + "px");
}
And I have :
So that's good, but I need to improve the function to take into account the total size of the width and perform a calculation that will reduce it. Because here I assign it the value '50' in width.
For example :
An event that lasts 2.5 days: a calculation that determines the width to occupy on the calendar in the monthly view so that it does not take the place of 3 days but 2.5 days. Taking into account of course the size of the screen.
And for an event that lasts only half a day, make sure that it only fills half of a box as in the image, but with a better calculation.
The problem is that I do not find a way to do that. I tell myself that I should already get the width of a cell according to the size of the screen and that I do the math then, but I have trouble
EDIT:
Example: I have an event that lasts just one day. So I give it the option 'allDay' => true.
We see on the picture on the day 'Sat 02/11' that it is directly set for the whole day. It's OK!
Now, if I have an event that should last 2.5 days. It would be necessary that on this sight, during two days, it is marked in the line 'all the day', but that in the last day, it is not in 'all the day' but that one put him well in the schedules . But if I set the option 'allDay' of the event to true, then it will appear in the box 'all day' for 3 days!
And if I take away the option 'allDay', then in the weekly view, it will fill a whole day, as seen in the picture in the day of 'dim 03/11'. And it's really not aesthetic

Related

Mobile Like Time Picker For React Web

I am trying to make an Alarm clock UI using react and I am stuck at this component where users can scroll or swipe through hours, minutes etc.. I tried some methods but failed.
I tried on scroll, on wheel, but my problem is I just can't get the accurate value which user sees(like the 03:30 PM).
I don't want you to help me with code, just need to know how to approach this.
I would approach this problem like this
let's address the challenges here
Scrolling to the desired points only
Looped scrolling
Auto pick the Value - without clicking basically.
Scrolling to the desired points only
This concept is called snap scrolling, you can use some library for that avoid writing its logic by yourself because then you will end up handling a lots of edge cases.
Looped scrolling
You need to handle this using basic JS logic you can provide some extra buffer elements at the end and at the starting refer to this example
https://codepen.io/lemmin/pen/bqNBpK
window.onscroll = function () {
// Horizontal Scroll.
var y = document.body.getBoundingClientRect().top;
page.scrollLeft = -y;
// Looping Scroll.
var diff = window.scrollY - dummy_x;
if (diff > 0) {
window.scrollTo(0, diff);
}
else if (window.scrollY == 0) {
window.scrollTo(0, dummy_x);
}
}
Auto pick the Value
one very basic approach could be, you can get the scroll offset on change of scroll, and as you have the height of every entity you can get the element that is in focus(or highlighted to the user).
Below is one more approach, you can create a selector div then you can check for the overlap on-scroll-stop whichever element is within this div, you can get its value.
personally, I think the first approach will be simpler and more stable. I have seen people using both types of approaches.
let me know if could help with anything else.

Work Day Calendar - Appending 3 DIVs Together Dynamically via JS - Trying to Flush Final DIV to the Right

working on a simple work day calendar for my bootcamp. 95% there - all of the logic works well, but i am having an issue getting the save button to properly float all the way to the right and flush out the DIV with no errant space between the button and the right edge of the block.
here's the github repository for this project:
https://github.com/infiniteoo/homework_week_05_third_party_apis
here's a live example:
https://infiniteoo.github.io/homework_week_05_third_party_apis/
here's the relevant code pertaining to this issue for quicker review:
timeDiv.text(finalHour + amPM);
timeDiv.addClass('time-div');
// 2nd column: events (big/wide)
let descriptionDiv = $("<div>");
let textAreaForDiv = $("<textarea>");
textAreaForDiv.attr('id', 'textarea' + hour);
descriptionDiv.append(textAreaForDiv);
descriptionDiv.addClass("description");
descriptionDiv.css("width", "80%");
// creates floppy disk icon for save button
let saveIcon = $('<i>');
saveIcon.addClass("fa fa-save");
// 3rd column: save button
let saveDiv = $("<div>");
saveDiv.addClass("saveBtn ");
saveDiv.attr('id', hour);
// add icon to save button
saveDiv.append(saveIcon);
// append all three individual blocks to the bigger div
timeBlock.append(timeDiv, descriptionDiv, saveDiv);
timeBlock.addClass("time-block row");
if (currentHour > hour) {
// if the hour has passed, make the background grey
timeBlock.addClass("past");
} else if (currentHour < hour) {
// if the hour happens in the future, make the background green
timeBlock.addClass("future");
textAreaForDiv.attr("placeholder", "Enter a task to complete this hour...");
} else {
// make the background red
timeBlock.addClass("present");
textAreaForDiv.attr("placeholder", "Enter a task to complete this hour...");
}
// add completed time block to the main container
$("#main-contain").append(timeBlock);
now, if i wanted to 'cheat' and put some padding-left on the save button, that fixes it, but the website breaks when you view it at a smaller width, and these projects need to be responsive.
i'm sure there's something small i'm missing, and quite honestly if you could rather give me a HINT versus the answer, i would prefer it. if you do that, i promise i will return to the thread and post the working answer and praise you with glory.
can anyone please point me in the right direction?
thanks so much for your help!
You are using flex on the row element. This will by default put child elements bang up against each other to the left.
You can get them spaced out along the row in various ways. I think the one you want is space-between this will divide up any spare space evenly and put it between the elements, with the left most element hard against the left and the rightmost one hard against the right (which is where you want the save button to be). So, add this:
.row {
justify-content: space-between
}

Improving iScroll performance on a large table

I'm updating a table header and its first column positions programatically based on how the user scrolls around to keep them aligned.
The issue I'm experiencing is that as soon as my data sets gets big enough, the scrolling gets more and more choppy/less smooth.
The relevant code is at the very bottom of the fiddle:
iScroll.on('scroll', function(){
var pos = $('#scroller').position();
$('#pos').text('pos.left=' + pos.left + ' pos.top=' + pos.top);
// code to hold first row and first column
$('#scroller th:nth-child(1)').css({top: (-pos.top), left: (-pos.left), position:'relative'});
$('#scroller th:nth-child(n+1)').css({top: (-pos.top), position:'relative'});
// this seems to be the most expensive operation:
$('#scroller td:nth-child(1)').css({left: (-pos.left), position:'relative'});
});
I know that this can be written a lot more efficent by caching the elements and so on. For example, I have tried saving the elements in to an array and updating their position in a more "vanilla" fashion:
headerElements[i].style.left = left + 'px'; // etc...
No matter how fast I make the callback, I'm still not happy about the result. Do you have any suggestions?
https://jsfiddle.net/0qv1kjac/16/
Just use ClusterizeJS! It can handle hundreds of thousands of rows and was built exactly for this purpose.
How does it work, you ask?
The main idea is not to pollute DOM with all used tags. Instead of that - it splits the list to clusters, then shows elements for current scroll position and adds extra rows to top and bottom of the list to emulate full height of table so that browser shows scrollbar as for full list
To be able to handle big amounts of data you need data virtualization. It has some restrictions, though.
First you need to decide the size of a view port. Let's say you want to render 10 items in a row and 20 items in column. It would be 10x20 items then. In you fiddle it's div with id wrapper.
Then you need to know total amount of data you have. From your fiddle it would be 100x100 items. And, also you need to know height and width of a item (cell). Let's take 40x120 (in px).
So div#wrapper is a view port, it should have fixed sized like 10x20 items. Then you need to set up correct width and height for table. The height of table would be equal to total amount of data in column including head by item height. Width for table would be total amount of items in single row by item width.
Once you set up these, div#wrapper will receive horizontal and vertical scrolls. Now you able to scroll left and bottom, but it will be just empty space. However this empty space is able to hold exact amount of data you have.
Then you need to take scroll data left and top (position), which comes in pixels and normalize it to amount of items, so you could know not how many pixels you've scrolled, but how many items you've scrolled(or rows if we scroll from top to bottom).
It could be done by division of pixels scrolled on item height. For example, you scrolled to left by 80px, that's 2 items. It means these items should be invisible because you've scrolled past them. So you know that you scrolled past 2 items, and you know that you should see 10 items in a row. That means you take your data array which has data for row with 100 items, and slice it like this:
var visibleItems = rowData.slice(itemsScrolled, itemsScrolled + 10);
It will give you items which should be visible in viewport at current scroll position. Once you have these items you need to construct html and append it to table.
Also on each scroll event you need to set top and left position for tbody and thead so they would move with scroll, otherwise you will have your data, but it will be at (0; 0) inside a viewport.
Anyway, code speaks thousand of words, so here's the fiddle: https://jsfiddle.net/Ldfjrg81/9/
Note, that this approach requires heights and widths to be precise, otherwise it will work incorrectly. Also if you have items of different sizes, this also should be taken into consideration, so better if you have fixed and equal sizes of items. In jsfiddle, I commented out the code which forces first column to stay in place, but you can render it separately.
It's a good solution to stick to some library as suggested in comments, since it handles a lot of cases for you.
You can make rendering even faster if use react.js or vue.js
This won't be the answer your are looking for but here's my 2 cents anyway.
Javascript animation (especially given the amount that the DOM has to render) will never be as smooth as you want it. Even if you could get it smooth on your machine, chances are that it will vary drastically on other peoples (Older PC's, Browsers etc).
I would see 2 options if I were to tackle this myself.
Go old school and add a horizontal and vertical scrollbar. I know it's not a pretty solution but it would work well.
Only render a certain amount of rows and discard those off screen. This could be a bit complicated but in essence you would render say 10 rows. Once the user scrolls to a point where the 11th should be there, render that one and remove the 1st. You would pop them in and out as needed.
In terms of the actual JS (you mentioned putting elements in to an array), that isn't going to help. The actual choppyness is due to the browser needing to render that many elements in the first place.
You're experiencing choppy / non-smooth scrolling because the scroll event fires at a very high pace.
And every time it fires you're adjusting the position of many elements: this is expensive and furthermore until the browser has completed the repaint it's unresponsive (here the choppy scrolling).
I see two options:
Option number one: display only the visible subset of the whole data set (this has been already suggested in another answer so I won't go futher)
Option number two (easier)
First, let animations on left and top css changes occurr via transitions. This is more efficient, is non-blocking and often let the browser take advantage of the gpu
Then instead of repeteadly adjust left and top, do it once a while; for example 0.5 seconds. This is done by the function ScrollWorker() (see code below) that recalls itself via a setTimeout().
Finally use the callback invoked by the scroll event to keep the #scroller position (stored in a variable) updated.
// Position of the `#scroller` element
// (I used two globals that may pollute the global namespace
// that piece of code is just for explanation purpose)
var oldPosition,
newPosition;
// Use transition to perform animations
// You may set this in the stylesheet
$('th').css( { 'transition': 'left 0.5s, top 0.5s' } );
$('td').css( { 'transition': 'left 0.5s, top 0.5s' } );
// Save the initial position
newPosition = $('#scroller').position();
oldPosition = $('#scroller').position();
// Upon scroll just set the position value
iScroll.on('scroll', function() {
newPosition = $('#scroller').position();
} );
// Start the scroll worker
ScrollWorker();
function ScrollWorker() {
// Adjust the layout if position changed (your original code)
if( newPosition.left != oldPosition.left || newPosition.top != oldPosition.top ) {
$('#scroller th:nth-child(1)').css({top: (-newPosition.top), left: (-newPosition.left), position:'relative'});
$('#scroller th:nth-child(n+1)').css({top: (-newPosition.top), position:'relative'});
$('#scroller td:nth-child(1)').css({left: (-newPosition.left), position:'relative'});
// Update the stored position
oldPosition.left = newPosition.left;
oldPosition.top = newPosition.top;
// Let animation complete then check again
// You may adjust the timer value
// The timer value must be higher or equal the transition time
setTimeout( ScrollWorker, 500 );
} else {
// No changes
// Check again after just 0.1secs
setTimeout( ScrollWorker, 100 );
}
}
Here is the Fiddle
I set the Worker pace and the transition time to 0.5 secs. You may adjust the value with higher or lower timing, eventually in a dinamic way based on the number of elements in the table.
Yes! Here are some improvements to the code from your JS Fiddle. You can view my edits at: https://jsfiddle.net/briankueck/u63maywa/
Some suggested improvements are:
Switching position:relative values in the JS layer to position:fixed in the CSS layer.
Shortening the jQuery DOM chains, so that the code doesn't start at the root element & walk all the way through the dom with each $ lookup. The scroller is now the root element. Everything uses .find() off of that element, which creates shorter trees & jQuery can traverse those branches faster.
Moving the logging code out of the DOM & into the console.log. I've added a debugging switch to disable it, as you're looking for the fastest scrolling on the table. If it runs fast enough for you, then you can always re-enable it to see it in the JSFiddle. If you really need to see that on the iPhone, then it can be added into the DOM. Although, it's probably not necessary to see the left & top position values in the iPhone.
Remove all extraneous $ values, which aren't mapped to the jQuery object. Something like $scroller gets confusing with $, as the latter is the jQuery library, but the former isn't.
Switching to ES6 syntax, by using let instead of var will make your code look more modern.
There is a new left calculation in the <th> tag, which you'll want to look at.
The iScroll event listener has been cleaned up. With position:fixed, the top <th> tags only need to have the top property applied to them. The left <td> tags only need to have the left property applied to them. The corner <th> needs to have both the top & left property applied to it.
Remove everything that's unnecessary, like the extraneous HTML tags which were used for logging purposes.
If you really want to go more vanilla, change out the .css() methods for the actual .style.left= -pos.left + 'px'; and .style.top= -pos.top + 'px'; properties in the JS code.
Try using a diff tool like WinMerge or Beyond Compare to compare the code from your version to what's in my edits, so that you can easily see the differences.
Hopefully, this will make the scrolling smoother, as the scroll event doesn't have to process anything that it doesn't need to do... like 5 full DOM traversing look-ups, rather than 3 short-tree searches.
Enjoy! :)
HTML:
<body>
<div id="wrapper">
<table id="scroller">
<thead>
</thead>
<tbody>
</tbody>
</table>
</div>
</body>
CSS:
/* ... only the relevant bits ... */
thead th {
background-color: #99a;
min-width: 120px;
height: 32px;
border: 1px solid #222;
position: fixed; /* New */
z-index: 9;
}
thead th:nth-child(1) {/*first cell in the header*/
border-left: 1px solid #222; /* New: Border fix */
border-right: 2px solid #222; /* New: Border fix */
position: fixed; /* New */
display: block; /*seperates the first cell in the header from the header*/
background-color: #88b;
z-index: 10;
}
JS:
// main code
let debug = false;
$(function(){
let scroller = $('#scroller');
let top = $('<tr/>');
for (var i = 0; i < 100; i++) {
let left = (i === 0) ? 0 : 1;
top.append('<th style="left:' + ((123*i)+left) + 'px;">'+ Math.random().toString(36).substring(7) +'</th>');
}
scroller.find('thead').append(top);
for (let i = 0; i < 100; i++) {
let row = $('<tr/>');
for (let j = 0; j < 100; j++) {
row.append('<td>'+ Math.random().toString(36).substring(7) +'</td>');
}
scroller.find('tbody').append(row);
}
if (debug) console.log('initialize iscroll');
let iScroll = null;
try {
iScroll = new IScroll('#wrapper', {
interactiveScrollbars: true,
scrollbars: true,
scrollX: true,
probeType: 3,
useTransition:false,
bounce:false
});
} catch(e) {
if (debug) console.error(e.name + ":" + e.message + "\n" + e.stack);
}
if (debug) console.log('initialized');
iScroll.on('scroll', function(){
let pos = scroller.position();
if (debug) console.log('pos.left=' + pos.left + ' pos.top=' + pos.top);
// code to hold first row and first column
scroller.find('th').css({top:-pos.top}); // Top Row
scroller.find('th:nth-child(1)').css({left:-pos.left}); // Corner
scroller.find('td:nth-child(1)').css({left:-pos.left}); // 1st Left Column
});
});
Is it necessary that you create your own scroller? Why don't you just style the data in HTML/CSS and just use the overflow attribute? JavaScript needs work on it's ability to adjust framerates. I was using your jFiddle earlier and it worked just fine with the native overflow handler.
Found this in the manual. Probably not what you wanna hear but it's the way it is:
IScroll is a class that needs to be initiated for each scrolling area. There's no limit to the number of iScrolls you can have in each page if not that imposed by the device CPU/Memory.
Try to keep the DOM as simple as possible. iScroll uses the hardware compositing layer but there's a limit to the elements the hardware can handle.
The reason the performance degradation is happening is that your scroll event handler is firing again and again and again instead of waiting for a reasonable and imperceptible interval.
The screenshot shows what happened when I tracked how many times the event handler fired, while scrolling for just a few seconds. The computationally-heavy event handler was fired over 600 times!!! This is more than 60 times per second!!!
It may seem counter-intuitive, but reducing the frequency that the table is updated will vastly increase perceived response times. If your user scrolls for fraction of a second, about 150 milliseconds, and the table is updated ten times, freezing the display during the scrolling, the net result is far worse than if the table were updated only three times and moved fluidly rather than freezing. It is just wasted processor burn to update more times than the browser can handle without freezing.
So, how do you make an event handler that fires at a maximum frequency, for example 25 times per second, even it is triggered much more often, like 100 times per second?
The naive way of doing it is to run a setInterval event. That is better, but horribly inefficient as well. There is a better way of doing it, by setting a delayed event handler, and clearing it on subsequent invocations before setting it again, until the minimum time interval has passed. This way it only runs no more often than at the maximum desired frequency. This is one major case for why the ``clearInterval'' method was invented.
Here is live working code:
https://jsfiddle.net/pgjvf7pb/7/
Note: when refreshing continuously like this, the header column may appear out of position.
I advise to do the update only when the scrolling has paused for about 25ms or so, rather than continuously. This way, it appears to the user that the header column is dynamically calculated as well as being fixed in place, because it appears instantly after scrolling rather than seeming to scroll with the data.
https://jsfiddle.net/5vcqv7nq/2/
The logic is like this:
variables outside your event handler
// stores the scrolling operation for a tiny delay to prevent redundancy
var fresh;
// stores time of last scrolling refresh
var lastfresh = new Date();
operations inside your event handler
// clears redundant scrolling operations before they are applied
if (fresh) clearTimeout(fresh);
var x = function() {
// stores new time of scrolling refresh
lastfresh = new Date();
// perform scrolling update operations here...
};
// refresh instantly if it is more than 50ms out of date
if (new Date() - lastfresh > 50) x();
// otherwise, pause for half of that time to avoid wasted runs
else fresh = setTimeout(x, 25);
Demo: https://jsfiddle.net/pgjvf7pb/7/
Once again, I recommend that you remove the line of code that refreshes the data instantly, and the else condition after that, and simply use one line
fresh = setTimeout(x, 25);
This will appear to instantly calculate the header column the moment any scrolling is finished, and saves even more operations. My second link to JS Fiddle shows what this looks like, here: https://jsfiddle.net/5vcqv7nq/2/

Check Position During Each Click and Add Class

I want the class .disabled to be added to the left and/or right controls (.tab-left, .tab-right) if the first or last tab is showing so a user can see that they have reached the end and cannot click any further.
Right now I something like this to prevent the user from going past the end.
if (tabs are at the end) {
return;
}
This works for users not being able to click past the end, but if I add the class before returning, the problem is the .disabled class won't be added until the tabs have reached the end and the user clicks again.
if (tabs are at the end) {
$('.tab-right').addClass('disabled');
return;
}
I need the disabled class to be added when the last tab is showing, not when the user trys to click past the end.
Here's a link to the js fiddle: http://jsfiddle.net/uue6pgcx/
One option you could try is to enable/disable the right and left buttons once the animation is complete.
$ul.filter(':not(:animated)').animate({
"left": dir + liWidth
}, {
complete: function () {
// Calculate the number of items in the container (without left and right navigation buttons).
var lisContainer = Math.round(($container.width() - $left.outerWidth() - $right.outerWidth()) / liWidth);
// Disable right button when list is moved to the left a number of items
// such as the remaining number of them is less or equal than the number
// of items that fit in the container.
$right.toggleClass('disabled', $li.length + $ul.position().left / liWidth <= lisContainer);
// Disable left button when list is in the origin.
$left.toggleClass('disabled', $ul.position().left === 0);
}
});
Disclaimer: According to jQuery outerWidth additional notes, The number returned by dimensions-related APIs, including .outerWidth(), may be fractional in some cases. Code should not assume it is an integer.. So lets hope Math.round will suffice to get the proper number.
Maybe there is a better way to calculate if the right button must be disabled/enabled instead of relying on the number of items that fit in the container.
Here it is your code with the above modification:
http://jsfiddle.net/0Lsepxeu/

PHP Dynamic Gallery Pagination (with a twist)

well i am displaying a dynamic gallery using PHP... what i want is to instead of showing a lot of thumbnails on one page i rather do paging.. you know the number pages and next and all..
well its an old thing but i don't know how to do this..
HOW TO DO IT ?
but hold your horses HERES THE TWIST
i am showing thumbnails in DIVS which i set in line so lets say if someone open the page in 1024x768 i get 3 thumbnails in a row and and there are 6 rows so they make 18 thumnails on the whole..
but if someone open the page with their resolution lets say 1600 x 1200 i get 5 thumnails in a row so 18 thumbnails would make 3 rows.. lets say i want to keep the rows 6 and pop up the thumbnails to 30... HOW TO RIDE A CODE TO DO THAT
p.s. best of luck with my crazy thangs....
The basic technique is this: You know, or else calculate, how many images you're displaying per page. So when you do your query, that's your LIMIT. Also, you're going to need an offset - how many results you need to skip over. If you were showing 8 results per page and you wanted to show the 3rd page, your offset would be (3 - 1) * 8, or 16.
Now, your interesting challenge sounds like your display: You're showing different amounts of thumbnails based on the display. So you will need to calculate how big of a display you want, probably with javascript, and then make an ajax request to query for the amount of results you want and the offset, based on the page you're showing.
Granted that somehow you manage to get a hold on the user's resolution and that you will be able to compute the total number of thumbnails in a DIV, then it's no magic to tell PHP to go with 30 thumbnails instead.
define('ROW_TOTAL', 6);
define('MIN_THUMBS', 3);
define('THUMB_WIDTH', 320);
// Get the clients resolution (i.e. via ECMA and a cookie)
$thumbs = isset($_COOKIE['res_width']) ?
(int) (intval($_COOKIE['res_width'], 10) / THUMB_WIDTH) : MIN_THUMBS;
// Get the page via the client's request
$page = isset($_GET['page']) ?
intval($_GET['page'], 10) : 0;
$query = sprintf(
'SELECT thumb_url' .
' FROM images' .
' WHERE set_id = %u'.
' LIMIT %u OFFSET %u',
$set_id,
$thumbs,
$thumbs * $page
);
// Feth the records and process them in a loop
// I'll leave that up to you
Good luck,
aefxx
Pagination can be handled in PHP. But you can't do all of what you mentioned in pure PHP. Some AJAX/Javascript will be involved, specifically in auto-detection of browser resolution. You do have to consider clients with browsers that have portrait and landscape dimensions, including small screens (mobile phones 240x240 or 320x320).
you can find screen resolution by java script
document.write(screen.width+'x'+screen.height);
this line will print the screen resolution. so can go for further step as-usual by if condition
if($screen = 1024x768) { execute query
} else { execute query }
I hope This will help you..

Categories