Floating widget support
parent
6da1ded096
commit
638dd4e67e
|
@ -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")
|
||||
}>
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue