Floating widget support

use-embed-on-website
Hariom Balhara 2022-04-11 17:26:26 +05:30
parent 6da1ded096
commit 638dd4e67e
8 changed files with 224 additions and 33 deletions

View File

@ -136,7 +136,7 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, previousPage
<main
className={
isEmbed
? ""
? classNames("m-auto", selectedDate ? "max-w-5xl" : "max-w-3xl")
: "transition-max-width mx-auto my-0 duration-500 ease-in-out md:my-24 " +
(selectedDate ? "max-w-5xl" : "max-w-3xl")
}>

View File

@ -43,6 +43,8 @@ Make `dist/embed.umd.js` servable on URL <http://cal.com/embed.js>
- Let user specify both dark and light theme colors. Right now the colors specified are for light theme.
- Embed doesn't adapt to screen size without page refresh.
- Try opening in portrait mode and then go to landscape mode.
- In inline mode, due to changing height of iframe, the content goes beyond the fold. Automatic scroll needs to be implemented.
- Loader should also be there in inline mode.
- Branding
- Powered by Cal.com and 'Try it for free'. Should they be shown only for FREE account.

View File

@ -329,6 +329,16 @@
debug: 1,
origin: "http://localhost:3000",
})
Cal("init", "floatingButton", {
debug: 1,
origin: "http://localhost:3000",
});
if (!only || only == "ns:floatingButton") {
Cal.ns.floatingButton("floatingButton", {
calLink: "pro"
})
}
</script>
<script></script>
</body>

View File

@ -0,0 +1,33 @@
import tailwindCss from "./tailwind.css";
export class FloatingButton extends HTMLElement {
constructor() {
super();
const buttonHtml = `
<style>
${tailwindCss}
</style>
<button
class="fixed bottom-4 right-4 flex h-16 origin-center transform cursor-pointer items-center rounded-full py-4 px-6 text-base outline-none drop-shadow-md transition transition-all focus:outline-none focus:ring-4 focus:ring-gray-600 focus:ring-opacity-50 active:scale-95 md:bottom-6 md:right-10"
style="background-color: rgb(255, 202, 0); color: rgb(20, 30, 47); z-index: 10001">
<div className="mr-3 flex items-center justify-center">
<svg
className="h-7 w-7"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
</svg>
</div>
<div className="font-semibold leading-5 antialiased">Book a time</div>
</button>`;
this.attachShadow({ mode: "open" });
this.shadowRoot!.innerHTML = buttonHtml;
}
}

View File

@ -1,3 +1,5 @@
import tailwindCss from "./tailwind.css";
export class ModalBox extends HTMLElement {
static htmlOverflow: string;
//@ts-ignore
@ -32,37 +34,7 @@ export class ModalBox extends HTMLElement {
super();
//FIXME: this styling goes as is as it's a JS string. That's a lot of unnecessary whitespaces over the wire.
const modalHtml = `
<style>
.bg-gray-50 {
--tw-bg-opacity: 1;
background-color: rgb(248 248 248 / var(--tw-bg-opacity));
}
.items-center {
align-items: center;
}
.w-full {
width: 100%;
}
.h-screen {
height: 100%;
}
.flex {
display: flex;
}
.z-highest {
z-index: 500000000;
}
.absolute {
position: absolute;
}
.border-brand {
border-color: white;
}
.bg-brand {
background-color: white;
}
<style> ${tailwindCss}
.backdrop {
position:fixed;
width:100%;
@ -73,6 +45,7 @@ export class ModalBox extends HTMLElement {
display:block;
background-color:rgb(5,5,5, 0.8)
}
@media only screen and (min-width:600px) {
.modal-box {
margin:0 auto;

View File

@ -1,5 +1,6 @@
import type { CalWindow } from "@calcom/embed-snippet";
import { FloatingButton } from "./FloatingButton";
import { ModalBox } from "./ModalBox";
import { methods, UiConfig } from "./embed-iframe";
import css from "./embed.css";
@ -228,6 +229,21 @@ export class Cal {
element.appendChild(iframe);
}
floatingButton({ calLink }: { calLink: string }) {
validate(arguments[0], {
required: true,
props: {
calLink: {
required: true,
type: "string",
},
},
});
const template = document.createElement("template");
template.innerHTML = `<cal-floating-button data-cal-namespace=${this.namespace} data-cal-link=${calLink}></cal-floating-button>`;
document.body.appendChild(template.content);
}
modal({ calLink, config = {} }: { calLink: string; config?: Record<string, string> }) {
const iframe = this.createIframe({ calLink, queryObject: Cal.getQueryObject(config) });
iframe.style.height = "100%";
@ -415,3 +431,4 @@ document.addEventListener("click", (e) => {
});
customElements.define("cal-modal-box", ModalBox);
customElements.define("cal-floating-button", FloatingButton);

View File

@ -0,0 +1,152 @@
* {
-tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
}
.bg-gray-50 {
--tw-bg-opacity: 1;
background-color: rgb(248 248 248 / var(--tw-bg-opacity));
}
.items-center {
align-items: center;
}
.w-full {
width: 100%;
}
.h-screen {
height: 100%;
}
.flex {
display: flex;
}
.z-highest {
z-index: 500000000;
}
.absolute {
position: absolute;
}
.border-brand {
border-color: white;
}
.bg-brand {
background-color: white;
}
@media (min-width: 768px) {
.md\:right-10 {
right: 2.5rem;
}
.md\:bottom-6 {
bottom: 1.5rem;
}
}
.transition-all {
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.transition {
transition-property: color, background-color, border-color, fill, stroke, opacity, box-shadow, transform,
filter, -webkit-text-decoration-color, -webkit-backdrop-filter;
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity,
box-shadow, transform, filter, backdrop-filter;
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity,
box-shadow, transform, filter, backdrop-filter, -webkit-text-decoration-color, -webkit-backdrop-filter;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.drop-shadow-md {
--tw-drop-shadow: drop-shadow(0 4px 3px rgb(0 0 0 / 0.07)) drop-shadow(0 2px 2px rgb(0 0 0 / 0.06));
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate)
var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
}
.outline-none {
outline: 2px solid transparent;
outline-offset: 2px;
}
.text-base {
font-size: 1rem;
line-height: 1.5rem;
}
.py-4 {
padding-top: 1rem;
padding-bottom: 1rem;
}
.px-6 {
padding-left: 1.5rem;
padding-right: 1.5rem;
}
.rounded-full {
border-radius: 9999px;
}
.items-center {
align-items: center;
}
.cursor-pointer {
cursor: pointer;
}
.transform {
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate))
skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.origin-center {
transform-origin: center;
}
.h-16 {
height: 4rem;
}
.flex {
display: flex;
}
.right-4 {
right: 1rem;
}
.bottom-4 {
bottom: 1rem;
}
.fixed {
position: fixed;
}

View File

@ -19,16 +19,20 @@ export default function Cal({
if (!Cal) {
return;
}
const element = ref.current;
let initConfig = {};
if (calOrigin) {
(initConfig as any).origin = calOrigin;
}
Cal("init", initConfig);
Cal("inline", {
elementOrSelector: ref.current,
elementOrSelector: element,
calLink,
config,
});
return () => {
element?.querySelector(".cal-embed")?.remove();
};
}, [Cal, calLink, config, calOrigin]);
if (!Cal) {