[HOW-TO] Create a custom Calendar using repeating group

If you have two date objects (date1 and date2) that represent the start and end of an event, those define a date range (lets call it eventrange1). If you have a third date (let’s call it calendar_date), the following expression is true if calendar_date is within the event range:

eventrange1 contains point calendar_date

Date ranges are not a native JavaScript data type, they are specific to Bubble, but are conceptually used in other applications and libraries. In Bubble, a date range is constructed using the ← range → operator like so:

date1 <- range -> date2

This returns an object of date range type. You can either construct date ranges and store them in your database as that type, or you can (USUALLY) construct them on-the-fly as above. So we could also write the first expression as:

date1 <- range -> date2 contains point calendar_date

If you add a Range field (of type date range) to your “Event” data type, you can do range-based constraints and comparisons like this:

Of course this implies that when you construct your Event objects, you (somewhere along the way) set their Range field. As an example:

If you don’t have ranges pre-constructed, you can’t do rangewise comparisons server-side (in a constraint). However, you can do them client side using the “Advanced” :filtered condition. Like this:

^^^ Keep in mind that this is all done client side, so what’s happening is all Events are being downloaded to the browser and then being iterated over by the :filter.

2 Likes

Hi @gf_wolfer! How did you go about making the calendar responsive? I have a nice calendar in place, with events showing and pretty much all features that the Full Calendar provides but I haven’t been able to make it responsive and not mess up the lay-out of the table.

Can you indeed post the steps?

Much obliged!

1 Like

No problem, I’ll have to look back into what I did, will post something soon

1 Like

Hi @gf_wolfer! Still interested to know what you did to make the calendar resposive :wink:

1 Like

Wow, this is awesome! Thanks so much for posting! This has inspired me to try building a complimentary “by week” view version, (which I suspect is far less challenging than the month) but I seem to be stuck on the javascript bit. Here’s what I got…

  1. Create a repeating group of 7 columns (days) with 1 row
  2. Nest a vertical repeating group inside it that just has 1 column and 48 rows. (24hrs.*2=48 half-hour time slots).
  3. Javascript… crap.

Does anyone know if this is the best approach or if it can be done?

Thanks!

Mostly the Responsive part has to do with ensuring your elements, especially the Day of the week text elements will scale on a smaller screen. Hard to describe, but you can check out (or simply copy) the responsive calendar from this app :point_down:

Hope this helps!

1 Like

Hi all, hi @codurly,

Thanks for the code - but it doesn’t seem to work for months where the first starts on a Sunday (like September 2019 and December 2019).

The first for loop that pushes the days onto the D array evaluates to empty on those days:

for(i=a.getDay()-2;0<=i;i–)D.push(new Date(t,e-1,n-i));

For September 2019 for instance (September 1st 2019).getDay = 0 therefore the loop doesn’t run in stead of running 6 times (as it should to add the August-days before the month starts (eg. 26,27,28,29,30 and 31).

The code does work for months starting on Monday through Saturday as for:

  • getDay = 1 (Monday): the loop doesn’t run - but the month starts on Monday so nothing needs to prepend the days.
  • getDay = 2 (Tuesday): the loop runs 1 time - which is correct
    . . .
  • getDay = 6 (Saturday): the loop runs 5 times - which is correct
    but for getDay = 0 (Sunday) the loop doesn’t run but needs to prepend 6 days!

my fix:
add an if-case for when getDay evaluates to 0.

Fixed code:

function getDaysInMonth(e,t) {
        var a=new Date(t,e,1),D=[],n=new Date(t,e,0).getDate(),b=a.getDay();
        if(b == 0) b=7;
	for(i=b-2;0<=i;i--)D.push(new Date(t,e-1,n-i));
	for(;a.getMonth()===e;)D.push(new Date(a)),a.setDate(a.getDate()+1);
	var g=new Date(t,e+1,1);
	for(i=D[D.length-1].getDay();6!=i;i++)D.push(new Date(g)),g.setDate(g.getDate()+1);
	for(i=D.length;42!=i;i++)D.push(new Date(g)),g.setDate(g.getDate()+1);
	return D;
}

Tried and tested :wink:

5 Likes

Hi @gf_wolfer,

Perfect! Thank you very much!

Hi,

I carefully read the whole topic and didn’t find a reference to the calendar use once it is built: how do you then display events inside the calendar (how can we place colored stripes that match each event datarange)?

@ryan8 do you please have a link to this tutorial you mentioned?

Thanks!

This calendar is A-MAZING! Thank you so much!!

Quick question: how would I add the functionality to click a button to return to the current date? If it’s March 2019 and I’ve scrolled to December 2019, I’d like to not have to scroll several months to get back to the current date. Thank you!!

Just set the state monthOffset to 0.

1 Like

Where is this done? I created a button for ‘Today’ and set that as the workflow, but nothing happens. Does something need to be updated in the javascript?

This is a simple workflow of a Custom State. Check out our Template for the Workflow:

Editor: https://bubble.io/page?type=page&name=calendar&id=topshelf-elements&tab=tabs-1

Preview mode: https://topshelf-elements.bubbleapps.io/version-test/calendar

1 Like

Wow!
Just wanted to give a :+1:t2::+1:t2::+1:t2: on this!
Took me 5 minutes to get much better UI with this calendar.
Used a small version of the month view to create a customized date picker!
This is a great addition to bubble’s resources!
Thanks a lot @codurly !!!

1 Like

Hey @recrutbox ,

did you have any luck with displaying colored stripes for each event created?

Did you ever get a reply back?

No I did not. I have not attempted this portion of my app yet either as I expect it to cause me great issues.

Evolution…

1 Like

How did you create that?

@wfarrell73, what you’re looking at there is a new type of Bubble element that I call Calendar Grid. It uses the same ideas as the custom calendar technique, but performs MUCH faster and adds a HUGE amount of functionality that takes weeks to build in Bubble, if you DIY. Here’s how it came to be…

I had built a bunch of app features around the custom calendar-via-a-repeating-group concept. In doing so, I learned a lot about date picking, range picking and date presentation/manipulation. (I already had an idea about a new approach to date range picking, which at the time was entirely novel, but has been adopted other places since.)

Range presentation and picking using the custom-calendar-via-RG technique is actually very difficult to implement (especially if one’s blocked ranges are coming from an API, which can lead to what I’ve called “the API ghetto” problem).

Because Bubble makes it difficult to do some types of iteration, it at first appears impossible to build an interactive range-picker in vanilla Bubble (even with Toolbox). But it can be done (see my GRUPZ.com project – https://grupz.com/vacation-rental-booking).

Unfortunately, at a certain level of complexity, the custom RG solution becomes very slow – both in terms of loading and in terms of responsiveness. (While it’s acceptably performant on desktop systems, it does not do well on lower end mobile devices – e.g., iPhone 6 is entirely unacceptable.)

The issue is that the only way to build this in Bubble is to make the calendar an insanely complicated state machine. (Rather than telling each “cell” – the dates in the calendar – how they should appear, you literally have to have each cell figure out how it should look based on a couple of dozen conditional evaluations – most of which involve search and/or filter operations.) This becomes very slow, very quickly as there are 42 of these little state machines doing this over and over.

SO, I recently started converting my Bubble version (which you might consider a prototype) into a plug-in element, coded in JavaScript with minimal reliance on external libraries (except for moment/moment-timezone, which is indispensable for projects like this).

The result is an element with a LOT of options, but that makes it easy to:

  1. Create a visually-pleasing, responsive calendar that
  2. Can also act as a simple calendar, interactive date picker, multi-date picker or date range picker
  3. Is timezone-aware
  4. Automatically detects locale and calendar/date presentation preferences (and adjusts itself to match)
  5. Is responsive to hover events
  6. Exports selected ranges as both date ranges AND the individual dates that compose the range (this is literally, actually impossible in vanilla Bubble, and only barely achievable in mildly modified Bubble) … AND…
  7. A bunch of other stuff…

And, yet, it loads nearly instantaneously and performance is nearly instantaneous, even on lower-end mobile devices.

Basically, it’s a specialized type of “repeating group” – designed not to display just ANY sort of list, but to generate, display and work with one very specialized type of list. And it’s designed FOR Bubble programmers BY (arguably the best) Bubble programmer(s)… So its gozintas and gozoutas make sense to the Bubble user.

Coming soon as a paid plugin.

5 Likes