Last active
September 23, 2021 01:29
-
-
Save ewalk153/2e2cb914c76b4dd1e66015f6d4ea2e26 to your computer and use it in GitHub Desktop.
Add to calendar example with stimulus
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <html> | |
| <head> | |
| <script src="https://unpkg.com/stimulus/dist/stimulus.umd.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script> | |
| <link | |
| href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" | |
| rel="stylesheet" | |
| /> | |
| <script> | |
| class GcalLinkBuilder { | |
| // TODO convert this to take in date objects instead of strings | |
| constructor( | |
| title, | |
| location, | |
| details, | |
| startDate, | |
| endDate, | |
| isAllDay, | |
| crm, | |
| hasTimeZone | |
| ) { | |
| this.title = title; | |
| this.location = location; | |
| this.details = details; | |
| this.startDate = startDate; | |
| this.endDate = endDate; | |
| this.isAllDay = isAllDay; | |
| this.crm = crm; | |
| this.hasTimeZone = hasTimeZone; | |
| } | |
| // https://github.com/InteractionDesignFoundation/add-event-to-calendar-docs/blob/main/services/google.md | |
| url() { | |
| return `https://www.google.com/calendar/render?action=TEMPLATE&crm=${this.crm}&text=${this.title}&location=${this.location}&dates=${this.startDate}/${this.endDate}&details=${this.details}&sf=true&output=xml`; | |
| } | |
| } | |
| (() => { | |
| const application = Stimulus.Application.start(); | |
| application.register( | |
| "toggle", | |
| class extends Stimulus.Controller { | |
| elementToggle(element) { | |
| if (element.hasAttribute("hidden")) { | |
| element.removeAttribute("hidden"); | |
| } else { | |
| element.setAttribute("hidden", ""); | |
| } | |
| } | |
| toggle(event) { | |
| event.preventDefault(); | |
| const TARGETS = | |
| event.currentTarget.dataset.toggleTarget.split(","); | |
| TARGETS.forEach((target) => | |
| document | |
| .querySelectorAll(`[data-toggle-name="${target}"]`) | |
| .forEach((target) => this.elementToggle(target)) | |
| ); | |
| } | |
| } | |
| ); | |
| application.register( | |
| "gcal", | |
| class extends Stimulus.Controller { | |
| static get targets() { | |
| return [ | |
| "output", | |
| "title", | |
| "location", | |
| "startdate", | |
| "starttime", | |
| "enddate", | |
| "endtime", | |
| "description", | |
| "timezone", | |
| "allday", | |
| ]; | |
| } | |
| initialize() { | |
| this.defaultDates(); | |
| } | |
| pad(n, width, z) { | |
| z = z || "0"; | |
| n = n + ""; | |
| return n.length >= width | |
| ? n | |
| : new Array(width - n.length + 1).join(z) + n; | |
| } | |
| linkURL() { | |
| return new GcalLinkBuilder( | |
| this.title(), | |
| this.location(), | |
| this.details(), | |
| this.startDate(), | |
| this.endDate(), | |
| null, | |
| "BUSY", | |
| null | |
| ).url(); | |
| // return `https://www.google.com/calendar/render?action=TEMPLATE&crm=BUSY&text=${this.title()}&location=${this.location()}&dates=${this.startDate()}/${this.endDate()}&details=${this.details()}&sf=true&output=xml`; | |
| } | |
| // TODO replace date parsing with momentjs | |
| defaultDates() { | |
| const date = new Date(); | |
| const dateString = `${date.getFullYear()}-${this.pad( | |
| date.getMonth() + 1, | |
| 2, | |
| "0" | |
| )}-${this.pad(date.getDate(), 2, "0")}`; | |
| this.startdateTarget.value = dateString; | |
| this.enddateTarget.value = dateString; | |
| this.starttimeTarget.value = "12:00"; | |
| this.endtimeTarget.value = "13:00"; | |
| } | |
| isAllDay() { | |
| return this.alldayTarget.checked; | |
| } | |
| title() { | |
| return encodeURIComponent(this.titleTarget.value); | |
| } | |
| location() { | |
| return encodeURIComponent(this.locationTarget.value); | |
| } | |
| timezone() { | |
| return this.timezoneTarget.value; | |
| } | |
| details() { | |
| return encodeURIComponent(this.descriptionTarget.value); | |
| } | |
| // TODO replace date formatting with momentjs | |
| buildDatetimeString(dateField, timeField, timezoneField) { | |
| return encodeURIComponent( | |
| `${dateField.replaceAll("-", "")}T${timeField.replaceAll( | |
| ":", | |
| "" | |
| )}00` | |
| ); | |
| } | |
| // TODO replace date formatting with momentjs | |
| buildDateString(dateField) { | |
| return encodeURIComponent(`${dateField.replaceAll("-", "")}`); | |
| } | |
| // TODO replace date formatting with momentjs | |
| buildEndDateString(dateField) { | |
| const val = parseInt(dateField.replaceAll("-", ""), 10); | |
| return encodeURIComponent(`${val + 1}`); | |
| } | |
| startDate() { | |
| if (this.isAllDay()) { | |
| return this.buildDateString(this.startdateTarget.value); | |
| } | |
| return this.buildDatetimeString( | |
| this.startdateTarget.value, | |
| this.starttimeTarget.value, | |
| this.timezoneTarget.value | |
| ); | |
| } | |
| endDate() { | |
| if (this.isAllDay()) { | |
| return this.buildEndDateString(this.enddateTarget.value); | |
| } | |
| return this.buildDatetimeString( | |
| this.enddateTarget.value, | |
| this.endtimeTarget.value, | |
| this.timezoneTarget.value | |
| ); | |
| } | |
| link(event) { | |
| event.preventDefault(); | |
| this.outputTarget.value = this.linkURL(); | |
| this.outputTarget.select(); | |
| document.execCommand("copy"); | |
| } | |
| } | |
| ); | |
| })(); | |
| </script> | |
| </head> | |
| <body | |
| class=" | |
| rounded-t-lg | |
| overflow-hidden | |
| border-t border-l border-r border-gray-400 | |
| px-3 | |
| py-10 | |
| bg-gray-200 | |
| flex | |
| justify-center | |
| " | |
| > | |
| <div data-controller="gcal" class="w-full max-w-lg"> | |
| <form class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4"> | |
| <h1 | |
| class=" | |
| text-2xl | |
| font-bold | |
| leading-7 | |
| text-gray-900 | |
| " | |
| > | |
| Sharable Google Cal Link Builder | |
| </h1> | |
| <div class="mb-4"> | |
| <label class="block text-gray-700 text-sm font-bold mb-2" for="title"> | |
| Title | |
| </label> | |
| <input | |
| class=" | |
| shadow | |
| appearance-none | |
| border | |
| rounded | |
| w-full | |
| py-2 | |
| px-3 | |
| text-gray-700 | |
| leading-tight | |
| focus:outline-none focus:shadow-outline | |
| " | |
| id="title" | |
| type="text" | |
| name="title" | |
| data-gcal-target="title" | |
| /> | |
| </div> | |
| <div class="mb-4"> | |
| <label | |
| class="block text-gray-700 text-sm font-bold mb-2" | |
| for="location" | |
| > | |
| Location | |
| </label> | |
| <input | |
| id="location" | |
| class=" | |
| shadow | |
| appearance-none | |
| border | |
| rounded | |
| w-full | |
| py-2 | |
| px-3 | |
| text-gray-700 | |
| leading-tight | |
| focus:outline-none focus:shadow-outline | |
| " | |
| type="text" | |
| name="location" | |
| data-gcal-target="location" | |
| /> | |
| </div> | |
| <span data-controller="toggle"> | |
| <input | |
| type="checkbox" | |
| data-action="toggle#toggle" | |
| data-toggle-target="starttimeblock,endtimeblock,timezone" | |
| name="allday" | |
| id="allday" | |
| value="1" | |
| data-gcal-target="allday" | |
| /> | |
| <label for="allday" class="text-gray-700 text-sm font-bold mb-2" | |
| >all day?</label | |
| > | |
| <br /> | |
| <span data-toggle-name="timezone"> | |
| <div class="mb-4"> | |
| <label | |
| class="block text-gray-700 text-sm font-bold mb-2" | |
| for="timezone" | |
| > | |
| Timezone | |
| </label> | |
| <input | |
| type="text" | |
| name="timezone" | |
| id="timezone" | |
| class=" | |
| shadow | |
| appearance-none | |
| border | |
| rounded | |
| w-full | |
| py-2 | |
| px-3 | |
| text-gray-700 | |
| leading-tight | |
| focus:outline-none focus:shadow-outline | |
| " | |
| data-gcal-target="timezone" | |
| value="" | |
| /> | |
| (leave blank to exclude timezone) | |
| </div> | |
| </span> | |
| <div class="mb-4"> | |
| <label | |
| class="block text-gray-700 text-sm font-bold mb-2" | |
| for="startdate" | |
| > | |
| Start Date | |
| </label> | |
| <input | |
| type="date" | |
| id="startdate" | |
| name="startdate" | |
| class=" | |
| shadow | |
| appearance-none | |
| border | |
| rounded | |
| w-full | |
| py-2 | |
| px-3 | |
| text-gray-700 | |
| leading-tight | |
| focus:outline-none focus:shadow-outline | |
| " | |
| data-gcal-target="startdate" | |
| /> | |
| <span data-toggle-name="starttimeblock"> | |
| <input | |
| type="time" | |
| name="starttime" | |
| class=" | |
| shadow | |
| appearance-none | |
| border | |
| rounded | |
| w-full | |
| py-2 | |
| px-3 | |
| text-gray-700 | |
| leading-tight | |
| focus:outline-none focus:shadow-outline | |
| " | |
| data-gcal-target="starttime" | |
| /></span> | |
| </div> | |
| <div class="mb-4"> | |
| <label | |
| class="block text-gray-700 text-sm font-bold mb-2" | |
| for="enddate" | |
| > | |
| End Date | |
| </label> | |
| <input | |
| type="date" | |
| id="enddate" | |
| class=" | |
| shadow | |
| appearance-none | |
| border | |
| rounded | |
| w-full | |
| py-2 | |
| px-3 | |
| text-gray-700 | |
| leading-tight | |
| focus:outline-none focus:shadow-outline | |
| " | |
| name="enddate" | |
| data-gcal-target="enddate" | |
| /> | |
| <span data-toggle-name="endtimeblock"> | |
| <input | |
| type="time" | |
| class=" | |
| shadow | |
| appearance-none | |
| border | |
| rounded | |
| w-full | |
| py-2 | |
| px-3 | |
| text-gray-700 | |
| leading-tight | |
| focus:outline-none focus:shadow-outline | |
| " | |
| name="endtime" | |
| data-gcal-target="endtime" | |
| /></span> | |
| </div> | |
| </span> | |
| <div class="mb-4"> | |
| <label | |
| class="block text-gray-700 text-sm font-bold mb-2" | |
| for="description" | |
| > | |
| Description | |
| </label> | |
| <textarea | |
| id="description" | |
| class=" | |
| shadow | |
| appearance-none | |
| border | |
| w-full | |
| px-3 | |
| py-2 | |
| text-gray-700 | |
| border | |
| rounded | |
| focus:outline-none | |
| " | |
| name="description" | |
| data-gcal-target="description" | |
| ></textarea> | |
| </div> | |
| <br /> | |
| <button | |
| class=" | |
| bg-blue-500 | |
| hover:bg-blue-700 | |
| text-white | |
| font-bold | |
| py-2 | |
| px-4 | |
| rounded | |
| focus:outline-none focus:shadow-outline | |
| " | |
| data-action="click->gcal#link" | |
| > | |
| Generate Link | |
| </button> | |
| <br /><br /> | |
| <label class="block text-gray-700 text-sm font-bold mb-2" for="output"> | |
| Share this link | |
| </label> | |
| <input | |
| id="output" | |
| data-gcal-target="output" | |
| class=" | |
| shadow | |
| appearance-none | |
| border | |
| rounded | |
| w-full | |
| py-2 | |
| px-3 | |
| text-gray-700 | |
| leading-tight | |
| focus:outline-none focus:shadow-outline | |
| " | |
| /> | |
| </form> | |
| </div> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment