Embed Miscellaneous Improvements and Fixes (#2499)
|
@ -9,8 +9,8 @@ The Embed allows your website visitors to book a meeting with you directly from
|
||||||
## Install on any website
|
## Install on any website
|
||||||
|
|
||||||
- _Step-1._ Install the Vanilla JS Snippet
|
- _Step-1._ Install the Vanilla JS Snippet
|
||||||
|
```html
|
||||||
```javascript
|
<script>
|
||||||
(function (C, A, L) {
|
(function (C, A, L) {
|
||||||
let p = function (a, ar) {
|
let p = function (a, ar) {
|
||||||
a.q.push(ar);
|
a.q.push(ar);
|
||||||
|
@ -38,14 +38,10 @@ The Embed allows your website visitors to book a meeting with you directly from
|
||||||
}
|
}
|
||||||
p(cal, ar);
|
p(cal, ar);
|
||||||
};
|
};
|
||||||
})(window, "https://cal.com/embed.js", "init");
|
})(window, "https://cal.com/embed.js", "init");
|
||||||
```
|
Cal("init")
|
||||||
|
</script>
|
||||||
- _Step-2_. Initialize it
|
```
|
||||||
|
|
||||||
```javascript
|
|
||||||
Cal("init")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Install with a Framework
|
## Install with a Framework
|
||||||
|
|
||||||
|
@ -72,18 +68,20 @@ Show the embed inline inside a container element. It would take the width and he
|
||||||
<details>
|
<details>
|
||||||
<summary>_Vanilla JS_</summary>
|
<summary>_Vanilla JS_</summary>
|
||||||
|
|
||||||
```javascript
|
```html
|
||||||
Cal("inline", {
|
<script>
|
||||||
elementOrSelector: "Your Embed Container Selector Path", // You can also provide an element directly
|
Cal("inline", {
|
||||||
calLink: "jane", // The link that you want to embed. It would open https://cal.com/jane in embed
|
elementOrSelector: "Your Embed Container Selector Path", // You can also provide an element directly
|
||||||
config: {
|
calLink: "jane", // The link that you want to embed. It would open https://cal.com/jane in embed
|
||||||
name: "John Doe", // Prefill Name
|
config: {
|
||||||
email: "johndoe@gmail.com", // Prefill Email
|
name: "John Doe", // Prefill Name
|
||||||
notes: "Test Meeting", // Prefill Notes
|
email: "johndoe@gmail.com", // Prefill Email
|
||||||
guests: ["janedoe@gmail.com", "test@gmail.com"], // Prefill Guests
|
notes: "Test Meeting", // Prefill Notes
|
||||||
theme: "dark", // "dark" or "light" theme
|
guests: ["janedoe@gmail.com", "test@gmail.com"], // Prefill Guests
|
||||||
},
|
theme: "dark", // "dark" or "light" theme
|
||||||
});
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
@ -146,8 +144,10 @@ Consider an instruction as a function with that name and that would be called wi
|
||||||
|
|
||||||
Appends embed inline as the child of the element.
|
Appends embed inline as the child of the element.
|
||||||
|
|
||||||
```javascript
|
```html
|
||||||
|
<script>
|
||||||
Cal("inline", { elementOrSelector, calLink });
|
Cal("inline", { elementOrSelector, calLink });
|
||||||
|
</script>
|
||||||
````
|
````
|
||||||
|
|
||||||
- `elementOrSelector` - Give it either a valid CSS selector or an HTMLElement instance directly
|
- `elementOrSelector` - Give it either a valid CSS selector or an HTMLElement instance directly
|
||||||
|
@ -158,8 +158,10 @@ Cal("inline", { elementOrSelector, calLink });
|
||||||
|
|
||||||
Configure UI for embed. Make it look part of your webpage.
|
Configure UI for embed. Make it look part of your webpage.
|
||||||
|
|
||||||
```javascript
|
```html
|
||||||
|
<script>
|
||||||
Cal("inline", { styles });
|
Cal("inline", { styles });
|
||||||
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
- `styles` - It supports styling for `body` and `eventTypeListItem`. Right now we support just background on these two.
|
- `styles` - It supports styling for `body` and `eventTypeListItem`. Right now we support just background on these two.
|
||||||
|
@ -170,15 +172,18 @@ Usage:
|
||||||
|
|
||||||
If you want to open cal link on some action. Make it pop open instantly by preloading it.
|
If you want to open cal link on some action. Make it pop open instantly by preloading it.
|
||||||
|
|
||||||
```javascript
|
```html
|
||||||
|
<script>
|
||||||
Cal("preload", { calLink });
|
Cal("preload", { calLink });
|
||||||
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
- `calLink` - Cal Link that you want to embed e.g. john. Just give the username. No need to give the full URL [https://cal.com/john]()
|
- `calLink` - Cal Link that you want to embed e.g. john. Just give the username. No need to give the full URL [https://cal.com/john]()
|
||||||
|
|
||||||
## Actions
|
## Actions
|
||||||
You can listen to an action that occurs in embedded cal link as follows. You can think of them as DOM events. We are avoiding the term "events" to not confuse it with Cal Events.
|
You can listen to an action that occurs in embedded cal link as follows. You can think of them as DOM events. We are avoiding the term "events" to not confuse it with Cal Events.
|
||||||
```javascript
|
```html
|
||||||
|
<script>
|
||||||
Cal("on", {
|
Cal("on", {
|
||||||
action: "ANY_ACTION_NAME",
|
action: "ANY_ACTION_NAME",
|
||||||
callback: (e)=>{
|
callback: (e)=>{
|
||||||
|
@ -188,6 +193,7 @@ Cal("on", {
|
||||||
const {data, type, namespace} = e.detail;
|
const {data, type, namespace} = e.detail;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
Following are the list of supported actions.
|
Following are the list of supported actions.
|
||||||
|
|
|
@ -18,7 +18,14 @@ import { useRouter } from "next/router";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { FormattedNumber, IntlProvider } from "react-intl";
|
import { FormattedNumber, IntlProvider } from "react-intl";
|
||||||
|
|
||||||
import { useEmbedStyles, useIsEmbed, useIsBackgroundTransparent, sdkActionManager } from "@calcom/embed-core";
|
import {
|
||||||
|
useEmbedStyles,
|
||||||
|
useIsEmbed,
|
||||||
|
useIsBackgroundTransparent,
|
||||||
|
sdkActionManager,
|
||||||
|
useEmbedType,
|
||||||
|
useEmbedNonStylesConfig,
|
||||||
|
} from "@calcom/embed-core";
|
||||||
import classNames from "@calcom/lib/classNames";
|
import classNames from "@calcom/lib/classNames";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
|
|
||||||
|
@ -56,6 +63,8 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, previousPage
|
||||||
const { t, i18n } = useLocale();
|
const { t, i18n } = useLocale();
|
||||||
const { contracts } = useContracts();
|
const { contracts } = useContracts();
|
||||||
const availabilityDatePickerEmbedStyles = useEmbedStyles("availabilityDatePicker");
|
const availabilityDatePickerEmbedStyles = useEmbedStyles("availabilityDatePicker");
|
||||||
|
const shouldAlignCentrallyInEmbed = useEmbedNonStylesConfig("align") !== "left";
|
||||||
|
const shouldAlignCentrally = !isEmbed || shouldAlignCentrallyInEmbed;
|
||||||
let isBackgroundTransparent = useIsBackgroundTransparent();
|
let isBackgroundTransparent = useIsBackgroundTransparent();
|
||||||
useExposePlanGlobally(plan);
|
useExposePlanGlobally(plan);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -146,18 +155,19 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, previousPage
|
||||||
<CustomBranding lightVal={profile.brandColor} darkVal={profile.darkBrandColor} />
|
<CustomBranding lightVal={profile.brandColor} darkVal={profile.darkBrandColor} />
|
||||||
<div>
|
<div>
|
||||||
<main
|
<main
|
||||||
className={
|
className={classNames(
|
||||||
|
shouldAlignCentrally ? "mx-auto" : "",
|
||||||
isEmbed
|
isEmbed
|
||||||
? classNames("m-auto", selectedDate ? "max-w-5xl" : "max-w-3xl")
|
? classNames(selectedDate ? "max-w-5xl" : "max-w-3xl")
|
||||||
: "transition-max-width mx-auto my-0 duration-500 ease-in-out md:my-24 " +
|
: "transition-max-width mx-auto my-0 duration-500 ease-in-out md:my-24 " +
|
||||||
(selectedDate ? "max-w-5xl" : "max-w-3xl")
|
(selectedDate ? "max-w-5xl" : "max-w-3xl")
|
||||||
}>
|
)}>
|
||||||
{isReady && (
|
{isReady && (
|
||||||
<div
|
<div
|
||||||
style={availabilityDatePickerEmbedStyles}
|
style={availabilityDatePickerEmbedStyles}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
isBackgroundTransparent ? "" : "bg-white dark:bg-gray-800 sm:dark:border-gray-600",
|
isBackgroundTransparent ? "" : "bg-white dark:bg-gray-800 sm:dark:border-gray-600",
|
||||||
"border-bookinglightest rounded-sm md:border",
|
"border-bookinglightest rounded-md md:border",
|
||||||
isEmbed ? "mx-auto" : selectedDate ? "max-w-5xl" : "max-w-3xl"
|
isEmbed ? "mx-auto" : selectedDate ? "max-w-5xl" : "max-w-3xl"
|
||||||
)}>
|
)}>
|
||||||
{/* mobile: details */}
|
{/* mobile: details */}
|
||||||
|
|
|
@ -18,7 +18,13 @@ import { FormattedNumber, IntlProvider } from "react-intl";
|
||||||
import { ReactMultiEmail } from "react-multi-email";
|
import { ReactMultiEmail } from "react-multi-email";
|
||||||
import { useMutation } from "react-query";
|
import { useMutation } from "react-query";
|
||||||
|
|
||||||
import { useIsEmbed, useIsBackgroundTransparent } from "@calcom/embed-core";
|
import {
|
||||||
|
useIsEmbed,
|
||||||
|
useEmbedStyles,
|
||||||
|
useIsBackgroundTransparent,
|
||||||
|
useEmbedType,
|
||||||
|
useEmbedNonStylesConfig,
|
||||||
|
} from "@calcom/embed-core";
|
||||||
import classNames from "@calcom/lib/classNames";
|
import classNames from "@calcom/lib/classNames";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { HttpError } from "@calcom/lib/http-error";
|
import { HttpError } from "@calcom/lib/http-error";
|
||||||
|
@ -71,6 +77,8 @@ const BookingPage = ({
|
||||||
}: BookingPageProps) => {
|
}: BookingPageProps) => {
|
||||||
const { t, i18n } = useLocale();
|
const { t, i18n } = useLocale();
|
||||||
const isEmbed = useIsEmbed();
|
const isEmbed = useIsEmbed();
|
||||||
|
const shouldAlignCentrallyInEmbed = useEmbedNonStylesConfig("align") !== "left";
|
||||||
|
const shouldAlignCentrally = !isEmbed || shouldAlignCentrallyInEmbed;
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { contracts } = useContracts();
|
const { contracts } = useContracts();
|
||||||
const { data: session } = useSession();
|
const { data: session } = useSession();
|
||||||
|
@ -298,16 +306,17 @@ const BookingPage = ({
|
||||||
<CustomBranding lightVal={profile.brandColor} darkVal={profile.darkBrandColor} />
|
<CustomBranding lightVal={profile.brandColor} darkVal={profile.darkBrandColor} />
|
||||||
<main
|
<main
|
||||||
className={classNames(
|
className={classNames(
|
||||||
isEmbed ? "mx-auto" : "mx-auto my-0 rounded-sm sm:my-24",
|
shouldAlignCentrally ? "mx-auto" : "",
|
||||||
"max-w-3xl sm:border sm:dark:border-gray-600"
|
isEmbed ? "" : "sm:my-24",
|
||||||
|
"my-0 max-w-3xl "
|
||||||
)}>
|
)}>
|
||||||
{isReady && (
|
{isReady && (
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"overflow-hidden",
|
"main overflow-hidden",
|
||||||
isEmbed ? "" : "border border-gray-200",
|
isEmbed ? "" : "border border-gray-200",
|
||||||
isBackgroundTransparent ? "" : "bg-white dark:border-0 dark:bg-gray-800",
|
isBackgroundTransparent ? "" : "dark:border-1 bg-white dark:bg-gray-800",
|
||||||
"sm:rounded-sm"
|
"rounded-md sm:border sm:dark:border-gray-600"
|
||||||
)}>
|
)}>
|
||||||
<div className="px-4 py-5 sm:flex sm:p-4">
|
<div className="px-4 py-5 sm:flex sm:p-4">
|
||||||
<div className="sm:w-1/2 sm:border-r sm:dark:border-gray-700">
|
<div className="sm:w-1/2 sm:border-r sm:dark:border-gray-700">
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { CreditCardIcon } from "@heroicons/react/solid";
|
import { CreditCardIcon } from "@heroicons/react/solid";
|
||||||
import { Elements } from "@stripe/react-stripe-js";
|
import { Elements } from "@stripe/react-stripe-js";
|
||||||
|
import classNames from "classnames";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import timezone from "dayjs/plugin/timezone";
|
import timezone from "dayjs/plugin/timezone";
|
||||||
import toArray from "dayjs/plugin/toArray";
|
import toArray from "dayjs/plugin/toArray";
|
||||||
|
@ -8,6 +9,7 @@ import Head from "next/head";
|
||||||
import React, { FC, useEffect, useState } from "react";
|
import React, { FC, useEffect, useState } from "react";
|
||||||
import { FormattedNumber, IntlProvider } from "react-intl";
|
import { FormattedNumber, IntlProvider } from "react-intl";
|
||||||
|
|
||||||
|
import { sdkActionManager, useIsEmbed } from "@calcom/embed-core";
|
||||||
import getStripe from "@calcom/stripe/client";
|
import getStripe from "@calcom/stripe/client";
|
||||||
import PaymentComponent from "@ee/components/stripe/Payment";
|
import PaymentComponent from "@ee/components/stripe/Payment";
|
||||||
import { PaymentPageProps } from "@ee/pages/payment/[uid]";
|
import { PaymentPageProps } from "@ee/pages/payment/[uid]";
|
||||||
|
@ -26,16 +28,33 @@ const PaymentPage: FC<PaymentPageProps> = (props) => {
|
||||||
const [is24h, setIs24h] = useState(isBrowserLocale24h());
|
const [is24h, setIs24h] = useState(isBrowserLocale24h());
|
||||||
const [date, setDate] = useState(dayjs.utc(props.booking.startTime));
|
const [date, setDate] = useState(dayjs.utc(props.booking.startTime));
|
||||||
const { isReady, Theme } = useTheme(props.profile.theme);
|
const { isReady, Theme } = useTheme(props.profile.theme);
|
||||||
|
const isEmbed = useIsEmbed();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
let embedIframeWidth = 0;
|
||||||
setDate(date.tz(localStorage.getItem("timeOption.preferredTimeZone") || dayjs.tz.guess()));
|
setDate(date.tz(localStorage.getItem("timeOption.preferredTimeZone") || dayjs.tz.guess()));
|
||||||
setIs24h(!!localStorage.getItem("timeOption.is24hClock"));
|
setIs24h(!!localStorage.getItem("timeOption.is24hClock"));
|
||||||
}, []);
|
if (isEmbed) {
|
||||||
|
requestAnimationFrame(function fixStripeIframe() {
|
||||||
|
// HACK: Look for stripe iframe and center position it just above the embed content
|
||||||
|
const stripeIframeWrapper = document.querySelector(
|
||||||
|
'iframe[src*="https://js.stripe.com/v3/authorize-with-url-inner"]'
|
||||||
|
)?.parentElement;
|
||||||
|
if (stripeIframeWrapper) {
|
||||||
|
stripeIframeWrapper.style.margin = "0 auto";
|
||||||
|
stripeIframeWrapper.style.width = embedIframeWidth + "px";
|
||||||
|
}
|
||||||
|
requestAnimationFrame(fixStripeIframe);
|
||||||
|
});
|
||||||
|
sdkActionManager?.on("__dimensionChanged", (e) => {
|
||||||
|
embedIframeWidth = e.detail.data.iframeWidth as number;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [isEmbed]);
|
||||||
|
|
||||||
const eventName = props.booking.title;
|
const eventName = props.booking.title;
|
||||||
|
|
||||||
return isReady ? (
|
return isReady ? (
|
||||||
<div className="h-screen bg-neutral-50 dark:bg-neutral-900">
|
<div className="h-screen">
|
||||||
<Theme />
|
<Theme />
|
||||||
<Head>
|
<Head>
|
||||||
<title>
|
<title>
|
||||||
|
@ -51,7 +70,10 @@ const PaymentPage: FC<PaymentPageProps> = (props) => {
|
||||||
​
|
​
|
||||||
</span>
|
</span>
|
||||||
<div
|
<div
|
||||||
className="inline-block transform overflow-hidden rounded-sm border border-neutral-200 bg-white px-8 pt-5 pb-4 text-left align-bottom transition-all dark:border-neutral-700 dark:bg-gray-800 sm:my-8 sm:w-full sm:max-w-lg sm:py-6 sm:align-middle"
|
className={classNames(
|
||||||
|
"main inline-block transform overflow-hidden rounded-lg border border-neutral-200 bg-white px-8 pt-5 pb-4 text-left align-bottom transition-all dark:border-neutral-700 dark:bg-gray-800 sm:w-full sm:max-w-lg sm:py-6 sm:align-middle",
|
||||||
|
isEmbed ? "" : "sm:my-8"
|
||||||
|
)}
|
||||||
role="dialog"
|
role="dialog"
|
||||||
aria-modal="true"
|
aria-modal="true"
|
||||||
aria-labelledby="modal-headline">
|
aria-labelledby="modal-headline">
|
||||||
|
|
|
@ -18,6 +18,8 @@ function applyThemeAndAddListener(theme: string) {
|
||||||
document.documentElement.classList.remove("dark");
|
document.documentElement.classList.remove("dark");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
document.documentElement.classList.remove("dark");
|
||||||
|
document.documentElement.classList.remove("light");
|
||||||
document.documentElement.classList.add(theme);
|
document.documentElement.classList.add(theme);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -33,15 +35,16 @@ export default function useTheme(theme?: Maybe<string>) {
|
||||||
const embedTheme = useEmbedTheme();
|
const embedTheme = useEmbedTheme();
|
||||||
// Embed UI configuration takes more precedence over App Configuration
|
// Embed UI configuration takes more precedence over App Configuration
|
||||||
theme = embedTheme || theme;
|
theme = embedTheme || theme;
|
||||||
|
const [_theme, setTheme] = useState<Maybe<string>>(null);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// TODO: isReady doesn't seem required now. This is also impacting PSI Score for pages which are using isReady.
|
// TODO: isReady doesn't seem required now. This is also impacting PSI Score for pages which are using isReady.
|
||||||
setIsReady(true);
|
setIsReady(true);
|
||||||
|
setTheme(theme);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
function Theme() {
|
function Theme() {
|
||||||
const code = applyThemeAndAddListener.toString();
|
const code = applyThemeAndAddListener.toString();
|
||||||
const themeStr = theme ? `"${theme}"` : null;
|
const themeStr = _theme ? `"${_theme}"` : null;
|
||||||
return (
|
return (
|
||||||
<Head>
|
<Head>
|
||||||
<script dangerouslySetInnerHTML={{ __html: `(${code})(${themeStr})` }}></script>
|
<script dangerouslySetInnerHTML={{ __html: `(${code})(${themeStr})` }}></script>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { ArrowRightIcon } from "@heroicons/react/outline";
|
import { ArrowRightIcon } from "@heroicons/react/outline";
|
||||||
import { BadgeCheckIcon } from "@heroicons/react/solid";
|
import { BadgeCheckIcon } from "@heroicons/react/solid";
|
||||||
import { UserPlan } from "@prisma/client";
|
import { UserPlan } from "@prisma/client";
|
||||||
|
import classNames from "classnames";
|
||||||
import { GetServerSidePropsContext } from "next";
|
import { GetServerSidePropsContext } from "next";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
@ -9,9 +10,10 @@ import React, { useEffect, useState } from "react";
|
||||||
import { Toaster } from "react-hot-toast";
|
import { Toaster } from "react-hot-toast";
|
||||||
import { JSONObject } from "superjson/dist/types";
|
import { JSONObject } from "superjson/dist/types";
|
||||||
|
|
||||||
import { sdkActionManager, useEmbedStyles, useIsEmbed } from "@calcom/embed-core";
|
import { sdkActionManager, useEmbedNonStylesConfig, useEmbedStyles, useIsEmbed } from "@calcom/embed-core";
|
||||||
import defaultEvents, {
|
import defaultEvents, {
|
||||||
getDynamicEventDescription,
|
getDynamicEventDescription,
|
||||||
|
getGroupName,
|
||||||
getUsernameList,
|
getUsernameList,
|
||||||
getUsernameSlugLink,
|
getUsernameSlugLink,
|
||||||
} from "@calcom/lib/defaultEvents";
|
} from "@calcom/lib/defaultEvents";
|
||||||
|
@ -23,6 +25,7 @@ import prisma from "@lib/prisma";
|
||||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@lib/telemetry";
|
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@lib/telemetry";
|
||||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||||
|
|
||||||
|
import CustomBranding from "@components/CustomBranding";
|
||||||
import AvatarGroup from "@components/ui/AvatarGroup";
|
import AvatarGroup from "@components/ui/AvatarGroup";
|
||||||
import { AvatarSSR } from "@components/ui/AvatarSSR";
|
import { AvatarSSR } from "@components/ui/AvatarSSR";
|
||||||
|
|
||||||
|
@ -37,7 +40,7 @@ interface EvtsToVerify {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function User(props: inferSSRProps<typeof getServerSideProps>) {
|
export default function User(props: inferSSRProps<typeof getServerSideProps>) {
|
||||||
const { users } = props;
|
const { users, profile } = props;
|
||||||
const [user] = users; //To be used when we only have a single user, not dynamic group
|
const [user] = users; //To be used when we only have a single user, not dynamic group
|
||||||
const { Theme } = useTheme(user.theme);
|
const { Theme } = useTheme(user.theme);
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
|
@ -102,13 +105,15 @@ export default function User(props: inferSSRProps<typeof getServerSideProps>) {
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
|
const isEmbed = useIsEmbed();
|
||||||
const eventTypeListItemEmbedStyles = useEmbedStyles("eventTypeListItem");
|
const eventTypeListItemEmbedStyles = useEmbedStyles("eventTypeListItem");
|
||||||
|
const shouldAlignCentrallyInEmbed = useEmbedNonStylesConfig("align") !== "left";
|
||||||
|
const shouldAlignCentrally = !isEmbed || shouldAlignCentrallyInEmbed;
|
||||||
const query = { ...router.query };
|
const query = { ...router.query };
|
||||||
delete query.user; // So it doesn't display in the Link (and make tests fail)
|
delete query.user; // So it doesn't display in the Link (and make tests fail)
|
||||||
useExposePlanGlobally("PRO");
|
useExposePlanGlobally("PRO");
|
||||||
const nameOrUsername = user.name || user.username || "";
|
const nameOrUsername = user.name || user.username || "";
|
||||||
const [evtsToVerify, setEvtsToVerify] = useState<EvtsToVerify>({});
|
const [evtsToVerify, setEvtsToVerify] = useState<EvtsToVerify>({});
|
||||||
const isEmbed = useIsEmbed();
|
|
||||||
const telemetry = useTelemetry();
|
const telemetry = useTelemetry();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -128,8 +133,17 @@ export default function User(props: inferSSRProps<typeof getServerSideProps>) {
|
||||||
username={isDynamicGroup ? dynamicUsernames.join(", ") : (user.username as string) || ""}
|
username={isDynamicGroup ? dynamicUsernames.join(", ") : (user.username as string) || ""}
|
||||||
// avatar={user.avatar || undefined}
|
// avatar={user.avatar || undefined}
|
||||||
/>
|
/>
|
||||||
<div className={"h-screen dark:bg-neutral-900" + isEmbed ? " bg:white m-auto max-w-3xl" : ""}>
|
<CustomBranding lightVal={profile.brandColor} darkVal={profile.darkBrandColor} />
|
||||||
<main className="mx-auto max-w-3xl px-4 py-24">
|
|
||||||
|
<div className={classNames(shouldAlignCentrally ? "mx-auto" : "", isEmbed ? "max-w-3xl" : "")}>
|
||||||
|
<main
|
||||||
|
className={classNames(
|
||||||
|
shouldAlignCentrally ? "mx-auto" : "",
|
||||||
|
isEmbed
|
||||||
|
? " border-bookinglightest rounded-md border bg-white dark:bg-neutral-900 sm:dark:border-gray-600"
|
||||||
|
: "",
|
||||||
|
"max-w-3xl py-24 px-4"
|
||||||
|
)}>
|
||||||
{isSingleUser && ( // When we deal with a single user, not dynamic group
|
{isSingleUser && ( // When we deal with a single user, not dynamic group
|
||||||
<div className="mb-8 text-center">
|
<div className="mb-8 text-center">
|
||||||
<AvatarSSR user={user} className="mx-auto mb-4 h-24 w-24" alt={nameOrUsername}></AvatarSSR>
|
<AvatarSSR user={user} className="mx-auto mb-4 h-24 w-24" alt={nameOrUsername}></AvatarSSR>
|
||||||
|
@ -284,6 +298,8 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
||||||
email: true,
|
email: true,
|
||||||
name: true,
|
name: true,
|
||||||
bio: true,
|
bio: true,
|
||||||
|
brandColor: true,
|
||||||
|
darkBrandColor: true,
|
||||||
avatar: true,
|
avatar: true,
|
||||||
theme: true,
|
theme: true,
|
||||||
plan: true,
|
plan: true,
|
||||||
|
@ -298,10 +314,36 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
||||||
notFound: true,
|
notFound: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const isDynamicGroup = users.length > 1;
|
const isDynamicGroup = users.length > 1;
|
||||||
|
|
||||||
|
const dynamicNames = isDynamicGroup
|
||||||
|
? users.map((user) => {
|
||||||
|
return user.name || "";
|
||||||
|
})
|
||||||
|
: [];
|
||||||
const [user] = users; //to be used when dealing with single user, not dynamic group
|
const [user] = users; //to be used when dealing with single user, not dynamic group
|
||||||
|
|
||||||
|
const profile = isDynamicGroup
|
||||||
|
? {
|
||||||
|
name: getGroupName(dynamicNames),
|
||||||
|
image: null,
|
||||||
|
theme: null,
|
||||||
|
weekStart: "Sunday",
|
||||||
|
brandColor: "",
|
||||||
|
darkBrandColor: "",
|
||||||
|
allowDynamicBooking: users.some((user) => {
|
||||||
|
return !user.allowDynamicBooking;
|
||||||
|
})
|
||||||
|
? false
|
||||||
|
: true,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
name: user.name || user.username,
|
||||||
|
image: user.avatar,
|
||||||
|
theme: user.theme,
|
||||||
|
brandColor: user.brandColor,
|
||||||
|
darkBrandColor: user.darkBrandColor,
|
||||||
|
};
|
||||||
const usersIds = users.map((user) => user.id);
|
const usersIds = users.map((user) => user.id);
|
||||||
const credentials = await prisma.credential.findMany({
|
const credentials = await prisma.credential.findMany({
|
||||||
where: {
|
where: {
|
||||||
|
@ -337,6 +379,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
users,
|
users,
|
||||||
|
profile,
|
||||||
user: {
|
user: {
|
||||||
emailMd5: crypto.createHash("md5").update(user.email).digest("hex"),
|
emailMd5: crypto.createHash("md5").update(user.email).digest("hex"),
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,7 +5,7 @@ type Props = Record<string, unknown> & DocumentProps;
|
||||||
class MyDocument extends Document<Props> {
|
class MyDocument extends Document<Props> {
|
||||||
static async getInitialProps(ctx: DocumentContext) {
|
static async getInitialProps(ctx: DocumentContext) {
|
||||||
const initialProps = await Document.getInitialProps(ctx);
|
const initialProps = await Document.getInitialProps(ctx);
|
||||||
const isEmbed = ctx.req?.url?.includes("embed");
|
const isEmbed = ctx.req?.url?.includes("embed=");
|
||||||
return { ...initialProps, isEmbed };
|
return { ...initialProps, isEmbed };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,9 @@ class MyDocument extends Document<Props> {
|
||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
{/* Keep the embed hidden till parent initializes and gives it the appropriate styles */}
|
{/* Keep the embed hidden till parent initializes and gives it the appropriate styles */}
|
||||||
<body className="bg-gray-100 dark:bg-neutral-900" style={props.isEmbed ? { display: "none" } : {}}>
|
<body
|
||||||
|
className={props.isEmbed ? "bg-transparent" : "bg-gray-100 dark:bg-neutral-900"}
|
||||||
|
style={props.isEmbed ? { display: "none" } : {}}>
|
||||||
<Main />
|
<Main />
|
||||||
<NextScript />
|
<NextScript />
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -12,7 +12,12 @@ import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useEffect, useState, useRef } from "react";
|
import { useEffect, useState, useRef } from "react";
|
||||||
|
|
||||||
import { useIsEmbed, useEmbedStyles, useIsBackgroundTransparent } from "@calcom/embed-core";
|
import {
|
||||||
|
useIsEmbed,
|
||||||
|
useEmbedStyles,
|
||||||
|
useIsBackgroundTransparent,
|
||||||
|
useEmbedNonStylesConfig,
|
||||||
|
} from "@calcom/embed-core";
|
||||||
import { sdkActionManager } from "@calcom/embed-core";
|
import { sdkActionManager } from "@calcom/embed-core";
|
||||||
import { getDefaultEvent } from "@calcom/lib/defaultEvents";
|
import { getDefaultEvent } from "@calcom/lib/defaultEvents";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
|
@ -88,7 +93,7 @@ function RedirectionToast({ url }: { url: string }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="relative inset-x-0 top-0 z-[60] pb-2 sm:fixed sm:top-2 sm:pb-5">
|
<div className="relative z-[60] pb-2 sm:pb-5">
|
||||||
<div className="mx-auto w-full sm:max-w-7xl sm:px-2 lg:px-8">
|
<div className="mx-auto w-full sm:max-w-7xl sm:px-2 lg:px-8">
|
||||||
<div className="border border-green-600 bg-green-500 p-2 sm:p-3">
|
<div className="border border-green-600 bg-green-500 p-2 sm:p-3">
|
||||||
<div className="flex flex-wrap items-center justify-between">
|
<div className="flex flex-wrap items-center justify-between">
|
||||||
|
@ -142,6 +147,9 @@ export default function Success(props: inferSSRProps<typeof getServerSideProps>)
|
||||||
|
|
||||||
const isBackgroundTransparent = useIsBackgroundTransparent();
|
const isBackgroundTransparent = useIsBackgroundTransparent();
|
||||||
const isEmbed = useIsEmbed();
|
const isEmbed = useIsEmbed();
|
||||||
|
const shouldAlignCentrallyInEmbed = useEmbedNonStylesConfig("align") !== "left";
|
||||||
|
const shouldAlignCentrally = !isEmbed || shouldAlignCentrallyInEmbed;
|
||||||
|
|
||||||
const attendeeName = typeof name === "string" ? name : "Nameless";
|
const attendeeName = typeof name === "string" ? name : "Nameless";
|
||||||
|
|
||||||
const eventNameObject = {
|
const eventNameObject = {
|
||||||
|
@ -214,19 +222,22 @@ export default function Success(props: inferSSRProps<typeof getServerSideProps>)
|
||||||
description={needsConfirmation ? t("booking_submitted") : t("booking_confirmed")}
|
description={needsConfirmation ? t("booking_submitted") : t("booking_confirmed")}
|
||||||
/>
|
/>
|
||||||
<CustomBranding lightVal={props.profile.brandColor} darkVal={props.profile.darkBrandColor} />
|
<CustomBranding lightVal={props.profile.brandColor} darkVal={props.profile.darkBrandColor} />
|
||||||
<main className={classNames("mx-auto", isEmbed ? "" : "max-w-3xl py-24")}>
|
<main className={classNames(shouldAlignCentrally ? "mx-auto" : "", isEmbed ? "" : "max-w-3xl")}>
|
||||||
<div className={classNames("overflow-y-auto", isEmbed ? "" : "fixed inset-0 z-50 ")}>
|
<div className={classNames("overflow-y-auto", isEmbed ? "" : "z-50 ")}>
|
||||||
{isSuccessRedirectAvailable(eventType) && eventType.successRedirectUrl ? (
|
{isSuccessRedirectAvailable(eventType) && eventType.successRedirectUrl ? (
|
||||||
<RedirectionToast url={eventType.successRedirectUrl}></RedirectionToast>
|
<RedirectionToast url={eventType.successRedirectUrl}></RedirectionToast>
|
||||||
) : null}{" "}
|
) : null}{" "}
|
||||||
<div className="flex min-h-screen items-end justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
<div
|
||||||
|
className={classNames(
|
||||||
|
shouldAlignCentrally ? "text-center" : "",
|
||||||
|
"flex items-end justify-center px-4 pt-4 pb-20 sm:block sm:p-0"
|
||||||
|
)}>
|
||||||
<div
|
<div
|
||||||
className={classNames("my-4 transition-opacity sm:my-0", isEmbed ? "" : "fixed inset-0")}
|
className={classNames("my-4 transition-opacity sm:my-0", isEmbed ? "" : " inset-0")}
|
||||||
aria-hidden="true">
|
aria-hidden="true">
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"inline-block transform overflow-hidden rounded-sm",
|
"inline-block transform overflow-hidden rounded-md border sm:my-8 sm:max-w-lg",
|
||||||
isEmbed ? "" : "border sm:my-8 sm:max-w-lg ",
|
|
||||||
isBackgroundTransparent ? "" : "bg-white dark:border-neutral-700 dark:bg-gray-800",
|
isBackgroundTransparent ? "" : "bg-white dark:border-neutral-700 dark:bg-gray-800",
|
||||||
"px-8 pt-5 pb-4 text-left align-bottom transition-all sm:w-full sm:py-6 sm:align-middle"
|
"px-8 pt-5 pb-4 text-left align-bottom transition-all sm:w-full sm:py-6 sm:align-middle"
|
||||||
)}
|
)}
|
||||||
|
@ -404,7 +415,7 @@ export default function Success(props: inferSSRProps<typeof getServerSideProps>)
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{userIsOwner && (
|
{userIsOwner && !isEmbed && (
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Link href="/bookings">
|
<Link href="/bookings">
|
||||||
<a className="flex items-center text-black dark:text-white">
|
<a className="flex items-center text-black dark:text-white">
|
||||||
|
|
|
@ -86,7 +86,7 @@ function TeamPage({ team }: TeamPageProps) {
|
||||||
<div>
|
<div>
|
||||||
<Theme />
|
<Theme />
|
||||||
<HeadSeo title={teamName} description={teamName} />
|
<HeadSeo title={teamName} description={teamName} />
|
||||||
<div className="px-4 pt-24 pb-12">
|
<div className="rounded-md bg-white px-4 pt-24 pb-12 dark:bg-gray-800 md:border">
|
||||||
<div className="max-w-96 mx-auto mb-8 text-center">
|
<div className="max-w-96 mx-auto mb-8 text-center">
|
||||||
<Avatar
|
<Avatar
|
||||||
alt={teamName}
|
alt={teamName}
|
||||||
|
|
|
@ -33,6 +33,8 @@
|
||||||
"start": "turbo run start --scope=\"@calcom/web\"",
|
"start": "turbo run start --scope=\"@calcom/web\"",
|
||||||
"test": "turbo run test",
|
"test": "turbo run test",
|
||||||
"test-playwright": "yarn playwright test --config=tests/config/playwright.config.ts",
|
"test-playwright": "yarn playwright test --config=tests/config/playwright.config.ts",
|
||||||
|
"embed-tests-quick": "turbo run embed-tests-quick",
|
||||||
|
"embed-tests": "turbo run embed-tests",
|
||||||
"test-e2e": "turbo run test-e2e --concurrency=1",
|
"test-e2e": "turbo run test-e2e --concurrency=1",
|
||||||
"type-check": "turbo run type-check"
|
"type-check": "turbo run type-check"
|
||||||
},
|
},
|
||||||
|
|
1
packages/embeds/embed-core/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
src/tailwind.generated.css
|
|
@ -9,7 +9,7 @@ You can also see various example usages [here](https://github.com/calcom/cal.com
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
Run the following command and then you can test the embed in the automatically opened page `http://localhost:3002`
|
Run the following command and then you can test the embed in the automatically opened page `http://localhost:3100`
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
yarn dev
|
yarn dev
|
||||||
|
@ -38,22 +38,21 @@ Make `dist/embed.umd.js` servable on URL <http://cal.com/embed.js>
|
||||||
## Known Bugs and Upcoming Improvements
|
## Known Bugs and Upcoming Improvements
|
||||||
|
|
||||||
- Unsupported Browsers and versions. Documenting them and gracefully handling that.
|
- Unsupported Browsers and versions. Documenting them and gracefully handling that.
|
||||||
|
- Need to create a booking Shell so that common changes for embed can be applied there.
|
||||||
|
|
||||||
- Accessibility and UI/UX Issues
|
- Accessibility and UI/UX Issues
|
||||||
- let user choose the loader for ModalBox
|
- let user choose the loader for ModalBox
|
||||||
- If website owner links the booking page directly for an event, should the user be able to go to events-listing page using back button ?
|
- If website owner links the booking page directly for an event, should the user be able to go to events-listing page using back button ?
|
||||||
- Let user specify both dark and light theme colors. Right now the colors specified are for light theme.
|
- 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.
|
- Transparent support is not properly done for team links
|
||||||
- Try opening in portrait mode and then go to landscape mode.
|
- Maybe don't set border radius in inline mode or give option to configure border radius.
|
||||||
- In inline mode, due to changing height of iframe, the content goes beyond the fold. Automatic scroll needs to be implemented.
|
|
||||||
- On Availability page, when selecting date, width doesn't increase. max-width is there but because of strict width restriction with iframe, it doesn't allow it to expand.
|
|
||||||
|
|
||||||
- Branding
|
- Branding
|
||||||
- Powered by Cal.com and 'Try it for free'. Should they be shown only for FREE account.
|
- Powered by Cal.com and 'Try it for free'. Should they be shown only for FREE account.
|
||||||
- Branding at the bottom has been removed for UI improvements, need to see where to add it.
|
- Branding at the bottom has been removed for UI improvements, need to see where to add it.
|
||||||
|
|
||||||
- API
|
- API
|
||||||
- Allow loader color customization using UI command itself too.
|
- Allow loader color customization using UI command itself too. Right now it's possible using CSS only.
|
||||||
|
|
||||||
- Automation Tests
|
- Automation Tests
|
||||||
- Run automation tests in CI
|
- Run automation tests in CI
|
||||||
|
@ -71,8 +70,6 @@ Make `dist/embed.umd.js` servable on URL <http://cal.com/embed.js>
|
||||||
- Need to reduce the number of colors on booking page, so that UI configuration is simpler
|
- Need to reduce the number of colors on booking page, so that UI configuration is simpler
|
||||||
|
|
||||||
- Dev Experience/Ease of Installation
|
- Dev Experience/Ease of Installation
|
||||||
- Improved Demo
|
|
||||||
- Seeding might be done for team event so that such an example is also available readily in index.html
|
|
||||||
- Do we need a one liner(like `window.dataLayer.push`) to inform SDK of something even if snippet is not yet on the page but would be there e.g. through GTM it would come late on the page ?
|
- Do we need a one liner(like `window.dataLayer.push`) to inform SDK of something even if snippet is not yet on the page but would be there e.g. through GTM it would come late on the page ?
|
||||||
|
|
||||||
- Might be better to pass all configuration using a single base64encoded query param to booking page.
|
- Might be better to pass all configuration using a single base64encoded query param to booking page.
|
||||||
|
@ -81,7 +78,7 @@ Make `dist/embed.umd.js` servable on URL <http://cal.com/embed.js>
|
||||||
- Custom written Tailwind CSS is sent multiple times for different custom elements.
|
- Custom written Tailwind CSS is sent multiple times for different custom elements.
|
||||||
|
|
||||||
- Embed Code Generator
|
- Embed Code Generator
|
||||||
|
- Option to disable redirect banner and let parent handle redirect.
|
||||||
- Release Issues
|
- Release Issues
|
||||||
- Compatibility Issue - When embed-iframe.js is updated in such a way that it is not compatible with embed.js, doing a release might break the embed for some time. e.g. iframeReady event let's say get's changed to something else
|
- Compatibility Issue - When embed-iframe.js is updated in such a way that it is not compatible with embed.js, doing a release might break the embed for some time. e.g. iframeReady event let's say get's changed to something else
|
||||||
- Best Case scenario - App and Website goes live at the same time. A website using embed loads the same updated and thus compatible versions of embed.js and embed-iframe.js
|
- Best Case scenario - App and Website goes live at the same time. A website using embed loads the same updated and thus compatible versions of embed.js and embed-iframe.js
|
||||||
|
|
|
@ -5,6 +5,15 @@
|
||||||
if (!location.search.includes("nonResponsive")) {
|
if (!location.search.includes("nonResponsive")) {
|
||||||
document.write('<meta name="viewport" content="width=device-width"/>');
|
document.write('<meta name="viewport" content="width=device-width"/>');
|
||||||
}
|
}
|
||||||
|
(()=> {
|
||||||
|
const url = new URL(document.URL);
|
||||||
|
// Only run the example specified by only=, avoids distraction and faster to test.
|
||||||
|
const only = url.searchParams.get("only");
|
||||||
|
const namespace = only ? only.replace("ns:",""): null
|
||||||
|
if (namespace) {
|
||||||
|
location.hash="#cal-booking-place-" + namespace + "-iframe"
|
||||||
|
}
|
||||||
|
})()
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
(function (C, A, L) {
|
(function (C, A, L) {
|
||||||
|
@ -34,7 +43,7 @@
|
||||||
}
|
}
|
||||||
p(cal, ar);
|
p(cal, ar);
|
||||||
};
|
};
|
||||||
})(window, "//localhost:3002/dist/embed.umd.js", "init");
|
})(window, "//localhost:3100/dist/embed.umd.js", "init");
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -55,8 +64,7 @@
|
||||||
color: green;
|
color: green;
|
||||||
}
|
}
|
||||||
* {
|
* {
|
||||||
--cal-brand-border-color: blue;
|
--cal-brand-color: gray;
|
||||||
--cal-brand-background-color: blue;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
@ -64,7 +72,7 @@
|
||||||
<h3>This page has a non responsive version accessible <a href="?nonResponsive">here</a></h3>
|
<h3>This page has a non responsive version accessible <a href="?nonResponsive">here</a></h3>
|
||||||
<h3>Pre-render test page available at <a href="?only=prerender-test">here</a></h3>
|
<h3>Pre-render test page available at <a href="?only=prerender-test">here</a></h3>
|
||||||
<div>
|
<div>
|
||||||
<button data-cal-namespace="prerendertestLightTheme" data-cal-link="free?light&popup">Book with Free User[Light Theme]</button>
|
<button data-cal-namespace="prerendertestLightTheme" data-cal-config='{"theme":"light"}' data-cal-link="free?light&popup">Book with Free User[Light Theme]</button>
|
||||||
<div>
|
<div>
|
||||||
<i
|
<i
|
||||||
>Corresponding Cal Link is being preloaded. Assuming that it would take you some time to click this
|
>Corresponding Cal Link is being preloaded. Assuming that it would take you some time to click this
|
||||||
|
@ -74,9 +82,12 @@
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<h2>Other Popup Examples</h2>
|
<h2>Other Popup Examples</h2>
|
||||||
<button data-cal-namespace="popupDarkTheme" data-cal-config='{"theme":"dark"}' data-cal-link="free?dark&popup">Book with Free User[Dark Theme]</button>
|
<button data-cal-namespace="popupAutoTheme" data-cal-link="free">Book with Free User[Auto Theme]</button>
|
||||||
<button data-cal-namespace="popupTeamLinkLightTheme" data-cal-config='{"theme":"light"}' data-cal-link="team/test-team?team&light&popup">Book with Test Team[Light Theme]</button>
|
<button data-cal-namespace="popupDarkTheme" data-cal-config='{"theme":"dark"}' data-cal-link="free">Book with Free User[Dark Theme]</button>
|
||||||
<button data-cal-namespace="popupTeamLinkDarkTheme" data-cal-config='{"theme":"dark"}' data-cal-link="team/test-team?team&dark&popup">Book with Test Team[Dark Theme]</button>
|
<button data-cal-namespace="popupTeamLinkLightTheme" data-cal-config='{"theme":"light"}' data-cal-link="team/seeded-team/collective-seeded-team-event">Book with Test Team[Light Theme]</button>
|
||||||
|
<button data-cal-namespace="popupTeamLinkDarkTheme" data-cal-config='{"theme":"dark"}' data-cal-link="team/seeded-team/collective-seeded-team-event">Book with Test Team[Dark Theme]</button>
|
||||||
|
<button data-cal-namespace="popupTeamLinksList" data-cal-link="team/seeded-team/">See Team Links [Auto Theme]</button>
|
||||||
|
<button data-cal-namespace="popupPaidEvent" data-cal-link="pro/paid">Book Paid Event [Auto Theme]</button>
|
||||||
<div>
|
<div>
|
||||||
<h2>Embed for Pages behind authentication</h2>
|
<h2>Embed for Pages behind authentication</h2>
|
||||||
<button data-cal-namespace="upcomingBookings" data-cal-config='{"theme":"dark"}' data-cal-link="bookings/upcoming">Show Upcoming Bookings</button>
|
<button data-cal-namespace="upcomingBookings" data-cal-config='{"theme":"dark"}' data-cal-link="bookings/upcoming">Show Upcoming Bookings</button>
|
||||||
|
@ -85,18 +96,14 @@
|
||||||
<div id="namespaces-test">
|
<div id="namespaces-test">
|
||||||
<div class="debug" id="cal-booking-place-default">
|
<div class="debug" id="cal-booking-place-default">
|
||||||
<h2>
|
<h2>
|
||||||
Default Namespace(Cal)<i>[Dark Theme][inline][Guests(janedoe@gmail.com and test@gmail.com)]</i>
|
Default Namespace(Cal)<i>[Dark Theme][inline][Guests(janedoe@example.com and test@example.com)]</i>
|
||||||
</h2>
|
</h2>
|
||||||
<div>
|
<div>
|
||||||
<i><a href="?only=ns:default">Test in Zen Mode</a></i>
|
<i><a href="?only=ns:default">Test in Zen Mode</a></i>
|
||||||
</div>
|
</div>
|
||||||
<i class="last-action"> You would see last Booking page action in my place </i>
|
<i class="last-action"> You would see last Booking page action in my place </i>
|
||||||
<div >
|
<div >
|
||||||
<div>
|
<div class="place" style="width:50%;"></div>
|
||||||
if you render booking embed in me, I would not let it be more than 30vh in height. So you would
|
|
||||||
have to scroll to see the entire content
|
|
||||||
</div>
|
|
||||||
<div class="place" style="width:50%; max-height: 30vh; overflow: scroll"></div>
|
|
||||||
<div class="loader" id="cal-booking-loader-">Loading .....</div>
|
<div class="loader" id="cal-booking-loader-">Loading .....</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -109,7 +116,7 @@
|
||||||
<i>You would see last Booking page action in my place</i>
|
<i>You would see last Booking page action in my place</i>
|
||||||
</i>
|
</i>
|
||||||
<div class="place">
|
<div class="place">
|
||||||
<div>If you render booking embed in me, I won't restrict you. The entire page is yours.</div>
|
<div>If you render booking embed in me, I won't restrict you. The entire page is yours. Content is by default aligned center</div>
|
||||||
<button
|
<button
|
||||||
onclick="(function () {Cal.ns.second('ui', {styles:{eventTypeListItem:{backgroundColor:'blue'}}})})()">
|
onclick="(function () {Cal.ns.second('ui', {styles:{eventTypeListItem:{backgroundColor:'blue'}}})})()">
|
||||||
Change <code>eventTypeListItem</code> bg color
|
Change <code>eventTypeListItem</code> bg color
|
||||||
|
@ -117,6 +124,21 @@
|
||||||
<button onclick="(function () {Cal.ns.second('ui', {styles:{body:{background:'red'}}})})()">
|
<button onclick="(function () {Cal.ns.second('ui', {styles:{body:{background:'red'}}})})()">
|
||||||
Change <code>body</code> bg color
|
Change <code>body</code> bg color
|
||||||
</button>
|
</button>
|
||||||
|
<button onclick="(function () {Cal.ns.second('ui', {styles:{align:'left'}})})()">
|
||||||
|
Align left
|
||||||
|
</button>
|
||||||
|
<button onclick="(function () {Cal.ns.second('ui', {styles:{align:'center'}})})()">
|
||||||
|
Align Center
|
||||||
|
</button>
|
||||||
|
<button onclick="(function () {Cal.ns.second('ui', {styles:{enabledDateButton: {
|
||||||
|
backgroundColor: '#D3D3D3',
|
||||||
|
},
|
||||||
|
disabledDateButton: {
|
||||||
|
backgroundColor: 'lightslategray',
|
||||||
|
},}})})()">
|
||||||
|
Change Date Button Color
|
||||||
|
</button>
|
||||||
|
|
||||||
<div class="loader" id="cal-booking-loader-second">Loading .....</div>
|
<div class="loader" id="cal-booking-loader-second">Loading .....</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -136,7 +158,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="debug" id="cal-booking-place-fourth">
|
<div class="debug" id="cal-booking-place-fourth">
|
||||||
<h2>Namespace "fourth"(Cal.ns.fourth)[Team Event Test][inline]</h2>
|
<h2>Namespace "fourth"(Cal.ns.fourth)[Team Event Test][inline taking entire width]</h2>
|
||||||
<div>
|
<div>
|
||||||
<i><a href="?only=ns:fourth">Test in Zen Mode</a></i>
|
<i><a href="?only=ns:fourth">Test in Zen Mode</a></i>
|
||||||
</div>
|
</div>
|
||||||
|
@ -150,6 +172,23 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="debug" id="cal-booking-place-fifth">
|
||||||
|
<h2>Namespace "fifth"(Cal.ns.fifth)[Team Event Test][inline along with some content]</h2>
|
||||||
|
<div>
|
||||||
|
<i><a href="?only=ns:fifth">Test in Zen Mode</a></i>
|
||||||
|
</div>
|
||||||
|
<i class="last-action">
|
||||||
|
<i>You would see last Booking page action in my place</i>
|
||||||
|
</i>
|
||||||
|
<div style="display:flex;align-items: center;">
|
||||||
|
<h2 style="width: 30%">
|
||||||
|
On the right side you can book a team meeting =>
|
||||||
|
</h2>
|
||||||
|
<div style="width: 70%" class="place">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const callback = function (e) {
|
const callback = function (e) {
|
||||||
const detail = e.detail;
|
const detail = e.detail;
|
||||||
|
@ -176,10 +215,14 @@
|
||||||
elementOrSelector: "#cal-booking-place-default .place",
|
elementOrSelector: "#cal-booking-place-default .place",
|
||||||
calLink: "pro?case=1",
|
calLink: "pro?case=1",
|
||||||
config: {
|
config: {
|
||||||
name: "John Doe",
|
__autoScroll:true,
|
||||||
|
iframeAttrs: {
|
||||||
|
id: "cal-booking-place-default-iframe"
|
||||||
|
},
|
||||||
|
name: "John",
|
||||||
email: "johndoe@gmail.com",
|
email: "johndoe@gmail.com",
|
||||||
notes: "Test Meeting",
|
notes: "Test Meeting",
|
||||||
guests: ["janedoe@gmail.com", "test@gmail.com"],
|
guests: ["janedoe@example.com", "test@example.com"],
|
||||||
theme: "dark",
|
theme: "dark",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -202,25 +245,11 @@
|
||||||
{
|
{
|
||||||
elementOrSelector: "#cal-booking-place-second .place",
|
elementOrSelector: "#cal-booking-place-second .place",
|
||||||
calLink: "pro?case=2",
|
calLink: "pro?case=2",
|
||||||
},
|
config: {
|
||||||
],
|
iframeAttrs: {
|
||||||
[
|
id: "cal-booking-place-second-iframe"
|
||||||
"ui",
|
|
||||||
{
|
|
||||||
styles: {
|
|
||||||
body: {
|
|
||||||
background: "white",
|
|
||||||
},
|
},
|
||||||
eventTypeListItem: {
|
}
|
||||||
backgroundColor: "#D3D3D3",
|
|
||||||
},
|
|
||||||
enabledDateButton: {
|
|
||||||
backgroundColor: "#D3D3D3",
|
|
||||||
},
|
|
||||||
disabledDateButton: {
|
|
||||||
backgroundColor: "lightslategray",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -243,6 +272,11 @@
|
||||||
{
|
{
|
||||||
elementOrSelector: "#cal-booking-place-third .place",
|
elementOrSelector: "#cal-booking-place-third .place",
|
||||||
calLink: "pro/30min",
|
calLink: "pro/30min",
|
||||||
|
config: {
|
||||||
|
iframeAttrs: {
|
||||||
|
id: "cal-booking-place-third-iframe"
|
||||||
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
@ -280,7 +314,12 @@
|
||||||
"inline",
|
"inline",
|
||||||
{
|
{
|
||||||
elementOrSelector: "#cal-booking-place-fourth .place",
|
elementOrSelector: "#cal-booking-place-fourth .place",
|
||||||
calLink: "team/test-team",
|
calLink: "team/seeded-team",
|
||||||
|
config: {
|
||||||
|
iframeAttrs: {
|
||||||
|
id: "cal-booking-place-fourth-iframe"
|
||||||
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
@ -307,7 +346,30 @@
|
||||||
callback,
|
callback,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (!only || only === "ns:fifth") {
|
||||||
|
Cal("init", "fifth", {
|
||||||
|
debug: 1,
|
||||||
|
origin: "http://localhost:3000",
|
||||||
|
});
|
||||||
|
Cal.ns.fifth(
|
||||||
|
[
|
||||||
|
"inline",
|
||||||
|
{
|
||||||
|
elementOrSelector: "#cal-booking-place-fifth .place",
|
||||||
|
calLink: "team/seeded-team/collective-seeded-team-event",
|
||||||
|
config: {
|
||||||
|
iframeAttrs: {
|
||||||
|
id: "cal-booking-place-fifth-iframe"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
Cal.ns.fifth("on", {
|
||||||
|
action: "*",
|
||||||
|
callback,
|
||||||
|
});
|
||||||
|
}
|
||||||
if (!only || only === "prerender-test") {
|
if (!only || only === "prerender-test") {
|
||||||
Cal('init', 'prerendertestLightTheme', {
|
Cal('init', 'prerendertestLightTheme', {
|
||||||
debug: 1,
|
debug: 1,
|
||||||
|
@ -321,6 +383,10 @@
|
||||||
debug: 1,
|
debug: 1,
|
||||||
origin: "http://localhost:3000",
|
origin: "http://localhost:3000",
|
||||||
})
|
})
|
||||||
|
Cal('init', 'popupAutoTheme', {
|
||||||
|
debug: 1,
|
||||||
|
origin: "http://localhost:3000",
|
||||||
|
})
|
||||||
Cal('init', 'popupTeamLinkLightTheme', {
|
Cal('init', 'popupTeamLinkLightTheme', {
|
||||||
debug: 1,
|
debug: 1,
|
||||||
origin: "http://localhost:3000",
|
origin: "http://localhost:3000",
|
||||||
|
@ -330,7 +396,17 @@
|
||||||
origin: "http://localhost:3000",
|
origin: "http://localhost:3000",
|
||||||
})
|
})
|
||||||
|
|
||||||
Cal('init', 'upcomingBookings', {
|
Cal('init', 'popupTeamLinkDarkTheme', {
|
||||||
|
debug: 1,
|
||||||
|
origin: "http://localhost:3000",
|
||||||
|
})
|
||||||
|
|
||||||
|
Cal('init', 'popupTeamLinksList', {
|
||||||
|
debug: 1,
|
||||||
|
origin: "http://localhost:3000",
|
||||||
|
})
|
||||||
|
|
||||||
|
Cal('init', 'popupPaidEvent', {
|
||||||
debug: 1,
|
debug: 1,
|
||||||
origin: "http://localhost:3000",
|
origin: "http://localhost:3000",
|
||||||
})
|
})
|
||||||
|
|
|
@ -7,13 +7,30 @@
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"build:cal": "NEXT_PUBLIC_WEBSITE_URL='https://cal.com' yarn build",
|
"build:cal": "NEXT_PUBLIC_WEBSITE_URL='https://cal.com' yarn build",
|
||||||
"vite": "vite",
|
"vite": "vite",
|
||||||
"dev": "run-p 'build --watch' 'vite --port 3002 --strict-port --open'",
|
"tailwind": "yarn tailwindcss -i ./src/styles.css -o ./src/tailwind.generated.css --watch",
|
||||||
|
"buildWatchAndServer": "run-p 'build --watch' 'vite --port 3100 --strict-port --open'",
|
||||||
|
"dev": "run-p 'tailwind' 'buildWatchAndServer'",
|
||||||
"type-check": "tsc --pretty --noEmit",
|
"type-check": "tsc --pretty --noEmit",
|
||||||
"lint": "eslint --ext .ts,.js src",
|
"lint": "eslint --ext .ts,.js src",
|
||||||
"test-playwright": "yarn playwright test --config=playwright/config/playwright.config.ts"
|
"embed-tests": "yarn playwright test --config=playwright/config/playwright.config.ts",
|
||||||
|
"embed-tests-quick": "QUICK=true yarn embed-tests"
|
||||||
|
},
|
||||||
|
"postcss": {
|
||||||
|
"map": false,
|
||||||
|
"plugins": {
|
||||||
|
"tailwindcss": {},
|
||||||
|
"autoprefixer": {}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vite": "^2.8.6",
|
"autoprefixer": "^10.4.4",
|
||||||
"eslint": "^8.10.0"
|
"eslint": "^8.10.0",
|
||||||
|
"postcss": "^8.4.12",
|
||||||
|
"vite": "^2.8.6"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tailwindcss": "^3.0.24",
|
||||||
|
"tsc": "^2.0.4",
|
||||||
|
"typescript": "^4.6.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,10 @@ import * as path from "path";
|
||||||
|
|
||||||
const outputDir = path.join("../results");
|
const outputDir = path.join("../results");
|
||||||
const testDir = path.join("../tests");
|
const testDir = path.join("../tests");
|
||||||
|
const quickMode = process.env.QUICK === "true";
|
||||||
const config: PlaywrightTestConfig = {
|
const config: PlaywrightTestConfig = {
|
||||||
forbidOnly: !!process.env.CI,
|
forbidOnly: !!process.env.CI,
|
||||||
retries: 1,
|
retries: quickMode ? 0 : 1,
|
||||||
workers: 1,
|
workers: 1,
|
||||||
timeout: 60_000,
|
timeout: 60_000,
|
||||||
reporter: [
|
reporter: [
|
||||||
|
@ -19,15 +19,21 @@ const config: PlaywrightTestConfig = {
|
||||||
],
|
],
|
||||||
globalSetup: require.resolve("./globalSetup"),
|
globalSetup: require.resolve("./globalSetup"),
|
||||||
outputDir,
|
outputDir,
|
||||||
|
expect: {
|
||||||
|
toMatchSnapshot: {
|
||||||
|
// Opacity transitions can cause small differences
|
||||||
|
maxDiffPixels: 50,
|
||||||
|
},
|
||||||
|
},
|
||||||
webServer: {
|
webServer: {
|
||||||
// Start App Server manually - Can't be handled here. See https://github.com/microsoft/playwright/issues/8206
|
// Start App Server manually - Can't be handled here. See https://github.com/microsoft/playwright/issues/8206
|
||||||
command: "yarn workspace @calcom/embed-core dev",
|
command: "yarn workspace @calcom/embed-core dev",
|
||||||
port: 3002,
|
port: 3100,
|
||||||
timeout: 60_000,
|
timeout: 60_000,
|
||||||
reuseExistingServer: !process.env.CI,
|
reuseExistingServer: !process.env.CI,
|
||||||
},
|
},
|
||||||
use: {
|
use: {
|
||||||
baseURL: "http://localhost:3002",
|
baseURL: "http://localhost:3100",
|
||||||
locale: "en-US",
|
locale: "en-US",
|
||||||
trace: "retain-on-failure",
|
trace: "retain-on-failure",
|
||||||
headless: !!process.env.CI || !!process.env.PLAYWRIGHT_HEADLESS,
|
headless: !!process.env.CI || !!process.env.PLAYWRIGHT_HEADLESS,
|
||||||
|
@ -38,16 +44,20 @@ const config: PlaywrightTestConfig = {
|
||||||
testDir,
|
testDir,
|
||||||
use: { ...devices["Desktop Chrome"] },
|
use: { ...devices["Desktop Chrome"] },
|
||||||
},
|
},
|
||||||
{
|
quickMode
|
||||||
name: "firefox",
|
? {}
|
||||||
testDir,
|
: {
|
||||||
use: { ...devices["Desktop Firefox"] },
|
name: "firefox",
|
||||||
},
|
testDir,
|
||||||
{
|
use: { ...devices["Desktop Firefox"] },
|
||||||
name: "webkit",
|
},
|
||||||
testDir,
|
quickMode
|
||||||
use: { ...devices["Desktop Safari"] },
|
? {}
|
||||||
},
|
: {
|
||||||
|
name: "webkit",
|
||||||
|
testDir,
|
||||||
|
use: { ...devices["Desktop Safari"] },
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
export type ExpectedUrlDetails = {
|
export type ExpectedUrlDetails = {
|
||||||
|
@ -123,9 +133,16 @@ expect.extend({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const iframeReadyEventDetail = await getActionFiredDetails({
|
const iframeReadyEventDetail = await new Promise(async (resolve) => {
|
||||||
calNamespace,
|
setInterval(async () => {
|
||||||
actionType: "__iframeReady",
|
const iframeReadyEventDetail = await getActionFiredDetails({
|
||||||
|
calNamespace,
|
||||||
|
actionType: "linkReady",
|
||||||
|
});
|
||||||
|
if (iframeReadyEventDetail) {
|
||||||
|
resolve(iframeReadyEventDetail);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!iframeReadyEventDetail) {
|
if (!iframeReadyEventDetail) {
|
||||||
|
|
|
@ -11,18 +11,26 @@ export const test = base.extend<Fixtures>({
|
||||||
({ calNamespace }: { calNamespace: string }) => {
|
({ calNamespace }: { calNamespace: string }) => {
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
window.eventsFiredStoreForPlaywright = window.eventsFiredStoreForPlaywright || {};
|
window.eventsFiredStoreForPlaywright = window.eventsFiredStoreForPlaywright || {};
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", function tryAddingListener() {
|
||||||
if (parent !== window) {
|
if (parent !== window) {
|
||||||
// Firefox seems to execute this snippet for iframe as well. Avoid that. It must be executed only for parent frame.
|
// Firefox seems to execute this snippet for iframe as well. Avoid that. It must be executed only for parent frame.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log("PlaywrightTest:", "Adding listener for __iframeReady");
|
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
let api = window.Cal;
|
let api = window.Cal;
|
||||||
|
|
||||||
|
if (!api) {
|
||||||
|
setTimeout(tryAddingListener, 500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (calNamespace) {
|
if (calNamespace) {
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
api = window.Cal.ns[calNamespace];
|
api = window.Cal.ns[calNamespace];
|
||||||
}
|
}
|
||||||
|
console.log("PlaywrightTest:", "Adding listener for __iframeReady");
|
||||||
|
if (!api) {
|
||||||
|
throw new Error(`namespace "${calNamespace}" not found`);
|
||||||
|
}
|
||||||
api("on", {
|
api("on", {
|
||||||
action: "*",
|
action: "*",
|
||||||
callback: (e: any) => {
|
callback: (e: any) => {
|
||||||
|
@ -41,13 +49,15 @@ export const test = base.extend<Fixtures>({
|
||||||
},
|
},
|
||||||
getActionFiredDetails: async ({ page }, use) => {
|
getActionFiredDetails: async ({ page }, use) => {
|
||||||
await use(async ({ calNamespace, actionType }) => {
|
await use(async ({ calNamespace, actionType }) => {
|
||||||
return await page.evaluate(
|
if (!page.isClosed()) {
|
||||||
({ actionType, calNamespace }) => {
|
return await page.evaluate(
|
||||||
//@ts-ignore
|
({ actionType, calNamespace }) => {
|
||||||
return window.eventsFiredStoreForPlaywright[`${actionType}-${calNamespace}`];
|
//@ts-ignore
|
||||||
},
|
return window.eventsFiredStoreForPlaywright[`${actionType}-${calNamespace}`];
|
||||||
{ actionType, calNamespace }
|
},
|
||||||
);
|
{ actionType, calNamespace }
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +1,35 @@
|
||||||
import { Page, test } from "@playwright/test";
|
import { Page, Frame, test, expect } from "@playwright/test";
|
||||||
|
|
||||||
|
import prisma from "@lib/prisma";
|
||||||
|
|
||||||
export function todo(title: string) {
|
export function todo(title: string) {
|
||||||
test.skip(title, () => {});
|
test.skip(title, () => {});
|
||||||
}
|
}
|
||||||
|
export const deleteAllBookingsByEmail = async (email: string) =>
|
||||||
|
await prisma.booking.deleteMany({
|
||||||
|
where: {
|
||||||
|
attendees: {
|
||||||
|
some: {
|
||||||
|
email: email,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getBooking = async (bookingId: string) => {
|
||||||
|
const booking = await prisma.booking.findUnique({
|
||||||
|
where: {
|
||||||
|
uid: bookingId,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
attendees: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!booking) {
|
||||||
|
throw new Error("Booking not found");
|
||||||
|
}
|
||||||
|
return booking;
|
||||||
|
};
|
||||||
|
|
||||||
export const getEmbedIframe = async ({ page, pathname }: { page: Page; pathname: string }) => {
|
export const getEmbedIframe = async ({ page, pathname }: { page: Page; pathname: string }) => {
|
||||||
// FIXME: Need to wait for the iframe to be properly added to shadow dom. There should be a no time boundation way to do it.
|
// FIXME: Need to wait for the iframe to be properly added to shadow dom. There should be a no time boundation way to do it.
|
||||||
|
@ -19,3 +46,48 @@ export const getEmbedIframe = async ({ page, pathname }: { page: Page; pathname:
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function selectFirstAvailableTimeSlotNextMonth(frame: Frame, page: Page) {
|
||||||
|
await frame.click('[data-testid="incrementMonth"]');
|
||||||
|
// @TODO: Find a better way to make test wait for full month change render to end
|
||||||
|
// so it can click up on the right day, also when resolve remove other todos
|
||||||
|
// Waiting for full month increment
|
||||||
|
await frame.waitForTimeout(1000);
|
||||||
|
expect(await page.screenshot()).toMatchSnapshot("availability-page-2.png");
|
||||||
|
// TODO: Find out why the first day is always booked on tests
|
||||||
|
await frame.locator('[data-testid="day"][data-disabled="false"]').nth(1).click();
|
||||||
|
await frame.click('[data-testid="time"]');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function bookFirstEvent(username: string, frame: Frame, page: Page) {
|
||||||
|
// Click first event type
|
||||||
|
await frame.click('[data-testid="event-type-link"]');
|
||||||
|
await frame.waitForNavigation({
|
||||||
|
url(url) {
|
||||||
|
return !!url.pathname.match(new RegExp(`/${username}/.*$`));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(await page.screenshot()).toMatchSnapshot("availability-page-1.png");
|
||||||
|
await selectFirstAvailableTimeSlotNextMonth(frame, page);
|
||||||
|
await frame.waitForNavigation({
|
||||||
|
url(url) {
|
||||||
|
return url.pathname.includes(`/${username}/book`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(await page.screenshot()).toMatchSnapshot("booking-page.png");
|
||||||
|
// --- fill form
|
||||||
|
await frame.fill('[name="name"]', "Embed User");
|
||||||
|
await frame.fill('[name="email"]', "embed-user@example.com");
|
||||||
|
await frame.press('[name="email"]', "Enter");
|
||||||
|
const response = await page.waitForResponse("**/api/book/event");
|
||||||
|
const responseObj = await response.json();
|
||||||
|
const bookingId = responseObj.uid;
|
||||||
|
// Make sure we're navigated to the success page
|
||||||
|
await frame.waitForNavigation({
|
||||||
|
url(url) {
|
||||||
|
return url.pathname.endsWith("/success");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(await page.screenshot()).toMatchSnapshot("success-page.png");
|
||||||
|
return bookingId;
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
import { expect } from "@playwright/test";
|
import { expect } from "@playwright/test";
|
||||||
|
|
||||||
import { test } from "../fixtures/fixtures";
|
import { test } from "../fixtures/fixtures";
|
||||||
import { todo, getEmbedIframe } from "../lib/testUtils";
|
import { todo, getEmbedIframe, bookFirstEvent, getBooking, deleteAllBookingsByEmail } from "../lib/testUtils";
|
||||||
|
|
||||||
|
test("should open embed iframe on click - Configured with light theme", async ({
|
||||||
|
page,
|
||||||
|
addEmbedListeners,
|
||||||
|
getActionFiredDetails,
|
||||||
|
}) => {
|
||||||
|
await deleteAllBookingsByEmail("embed-user@example.com");
|
||||||
|
|
||||||
test("should open embed iframe on click", async ({ page, addEmbedListeners, getActionFiredDetails }) => {
|
|
||||||
const calNamespace = "prerendertestLightTheme";
|
const calNamespace = "prerendertestLightTheme";
|
||||||
await addEmbedListeners(calNamespace);
|
await addEmbedListeners(calNamespace);
|
||||||
await page.goto("/?only=prerender-test");
|
await page.goto("/?only=prerender-test");
|
||||||
|
@ -17,6 +23,15 @@ test("should open embed iframe on click", async ({ page, addEmbedListeners, getA
|
||||||
expect(embedIframe).toBeEmbedCalLink(calNamespace, getActionFiredDetails, {
|
expect(embedIframe).toBeEmbedCalLink(calNamespace, getActionFiredDetails, {
|
||||||
pathname: "/free",
|
pathname: "/free",
|
||||||
});
|
});
|
||||||
|
expect(await page.screenshot()).toMatchSnapshot("event-types-list.png");
|
||||||
|
if (!embedIframe) {
|
||||||
|
throw new Error("Embed iframe not found");
|
||||||
|
}
|
||||||
|
const bookingId = await bookFirstEvent("free", embedIframe, page);
|
||||||
|
const booking = await getBooking(bookingId);
|
||||||
|
|
||||||
|
expect(booking.attendees.length).toBe(1);
|
||||||
|
await deleteAllBookingsByEmail("embed-user@example.com");
|
||||||
});
|
});
|
||||||
|
|
||||||
todo("Floating Button Test with Dark Theme");
|
todo("Floating Button Test with Dark Theme");
|
||||||
|
|
After Width: | Height: | Size: 216 KiB |
After Width: | Height: | Size: 171 KiB |
After Width: | Height: | Size: 987 KiB |
After Width: | Height: | Size: 220 KiB |
After Width: | Height: | Size: 183 KiB |
After Width: | Height: | Size: 996 KiB |
After Width: | Height: | Size: 234 KiB |
After Width: | Height: | Size: 226 KiB |
After Width: | Height: | Size: 1.2 MiB |
After Width: | Height: | Size: 224 KiB |
After Width: | Height: | Size: 213 KiB |
After Width: | Height: | Size: 1.2 MiB |
After Width: | Height: | Size: 265 KiB |
After Width: | Height: | Size: 225 KiB |
After Width: | Height: | Size: 1.2 MiB |
|
@ -1,13 +1,14 @@
|
||||||
import { expect, Frame } from "@playwright/test";
|
import { expect, Frame } from "@playwright/test";
|
||||||
|
|
||||||
import { test } from "../fixtures/fixtures";
|
import { test } from "../fixtures/fixtures";
|
||||||
import { todo, getEmbedIframe } from "../lib/testUtils";
|
import { todo, getEmbedIframe, bookFirstEvent, deleteAllBookingsByEmail } from "../lib/testUtils";
|
||||||
|
|
||||||
test("Inline Iframe - Configured with Dark Theme", async ({
|
test("Inline Iframe - Configured with Dark Theme", async ({
|
||||||
page,
|
page,
|
||||||
getActionFiredDetails,
|
getActionFiredDetails,
|
||||||
addEmbedListeners,
|
addEmbedListeners,
|
||||||
}) => {
|
}) => {
|
||||||
|
await deleteAllBookingsByEmail("embed-user@example.com");
|
||||||
await addEmbedListeners("");
|
await addEmbedListeners("");
|
||||||
await page.goto("/?only=ns:default");
|
await page.goto("/?only=ns:default");
|
||||||
const embedIframe = await getEmbedIframe({ page, pathname: "/pro" });
|
const embedIframe = await getEmbedIframe({ page, pathname: "/pro" });
|
||||||
|
@ -17,6 +18,12 @@ test("Inline Iframe - Configured with Dark Theme", async ({
|
||||||
theme: "dark",
|
theme: "dark",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
expect(await page.screenshot()).toMatchSnapshot("event-types-list.png");
|
||||||
|
if (!embedIframe) {
|
||||||
|
throw new Error("Embed iframe not found");
|
||||||
|
}
|
||||||
|
await bookFirstEvent("pro", embedIframe, page);
|
||||||
|
await deleteAllBookingsByEmail("embed-user@example.com");
|
||||||
});
|
});
|
||||||
|
|
||||||
todo(
|
todo(
|
||||||
|
|
After Width: | Height: | Size: 266 KiB |
After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 1.8 MiB |
After Width: | Height: | Size: 271 KiB |
After Width: | Height: | Size: 158 KiB |
After Width: | Height: | Size: 1.8 MiB |
After Width: | Height: | Size: 287 KiB |
After Width: | Height: | Size: 89 KiB |
After Width: | Height: | Size: 1.9 MiB |
After Width: | Height: | Size: 287 KiB |
After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 2.8 MiB |
After Width: | Height: | Size: 329 KiB |
After Width: | Height: | Size: 152 KiB |
After Width: | Height: | Size: 2.4 MiB |
|
@ -1,33 +0,0 @@
|
||||||
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 class="mr-3 flex items-center justify-center">
|
|
||||||
<svg
|
|
||||||
class="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 class="font-semibold leading-5 antialiased">Book my Cal</div>
|
|
||||||
</button>`;
|
|
||||||
this.attachShadow({ mode: "open" });
|
|
||||||
|
|
||||||
this.shadowRoot!.innerHTML = buttonHtml;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { CalWindow } from "@calcom/embed-snippet";
|
||||||
|
|
||||||
|
import floatingButtonHtml from "./FloatingButtonHtml";
|
||||||
|
|
||||||
|
export class FloatingButton extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
const buttonHtml = `<style>${(window as CalWindow).Cal!.__css}</style> ${floatingButtonHtml}`;
|
||||||
|
this.attachShadow({ mode: "open" });
|
||||||
|
this.shadowRoot!.innerHTML = buttonHtml;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
const html = `<button class="fixed bottom-4 right-4 flex h-16 origin-center bg-red-50 transform cursor-pointer items-center
|
||||||
|
rounded-full py-4 px-6 text-base outline-none drop-shadow-md transition focus:outline-none fo
|
||||||
|
cus: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 class="mr-3 flex items-center justify-center">
|
||||||
|
<svg
|
||||||
|
class="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 class="font-semibold leading-5 antialiased">Book my Cal</div>
|
||||||
|
</button>`;
|
||||||
|
|
||||||
|
export default html;
|
|
@ -1,5 +1,7 @@
|
||||||
import loaderCss from "./loader.css";
|
import { CalWindow } from "@calcom/embed-snippet";
|
||||||
import tailwindCss from "./tailwind.css";
|
|
||||||
|
import loaderCss from "../loader.css";
|
||||||
|
import inlineHtml from "./inlineHtml";
|
||||||
|
|
||||||
export class Inline extends HTMLElement {
|
export class Inline extends HTMLElement {
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
|
@ -14,14 +16,8 @@ export class Inline extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.attachShadow({ mode: "open" });
|
this.attachShadow({ mode: "open" });
|
||||||
this.shadowRoot!.innerHTML = `
|
this.shadowRoot!.innerHTML = `<style>${
|
||||||
<style> ${tailwindCss}${loaderCss}</style>
|
(window as CalWindow).Cal!.__css
|
||||||
<div id="loader" style="left:0;right:0" class="absolute z-highest flex h-screen w-full items-center">
|
}</style><style>${loaderCss}</style>${inlineHtml}`;
|
||||||
<div class="loader border-brand dark:border-darkmodebrand">
|
|
||||||
<span class="loader-inner bg-brand dark:bg-darkmodebrand"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<slot></slot>
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
7
packages/embeds/embed-core/src/Inline/inlineHtml.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
const html = `<div id="loader" style="top:calc(50% - 30px); left:calc(50% - 30px)" class="absolute z-highest">
|
||||||
|
<div class="loader border-brand dark:border-darkmodebrand">
|
||||||
|
<span class="loader-inner bg-brand dark:bg-darkmodebrand"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<slot></slot>`;
|
||||||
|
export default html;
|
|
@ -1,126 +0,0 @@
|
||||||
import loaderCss from "./loader.css";
|
|
||||||
import tailwindCss from "./tailwind.css";
|
|
||||||
|
|
||||||
export class ModalBox extends HTMLElement {
|
|
||||||
static htmlOverflow: string;
|
|
||||||
//@ts-ignore
|
|
||||||
static get observedAttributes() {
|
|
||||||
return ["state"];
|
|
||||||
}
|
|
||||||
|
|
||||||
show(show: boolean) {
|
|
||||||
// We can't make it display none as that takes iframe width and height calculations to 0
|
|
||||||
(this.shadowRoot!.host as unknown as any).style.visibility = show ? "visible" : "hidden";
|
|
||||||
}
|
|
||||||
|
|
||||||
close() {
|
|
||||||
this.show(false);
|
|
||||||
document.body.style.overflow = ModalBox.htmlOverflow;
|
|
||||||
}
|
|
||||||
|
|
||||||
attributeChangedCallback(name: string, oldValue: string, newValue: string) {
|
|
||||||
if (name !== "state") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newValue == "loaded") {
|
|
||||||
(this.shadowRoot!.querySelector("#loader")! as HTMLElement).style.display = "none";
|
|
||||||
} else if (newValue === "started") {
|
|
||||||
this.show(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
const closeEl = this.shadowRoot!.querySelector(".close") as HTMLElement;
|
|
||||||
|
|
||||||
this.shadowRoot!.host.addEventListener("click", (e) => {
|
|
||||||
this.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
closeEl.onclick = () => {
|
|
||||||
this.close();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
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> ${tailwindCss}
|
|
||||||
.backdrop {
|
|
||||||
position:fixed;
|
|
||||||
width:100%;
|
|
||||||
height:100%;
|
|
||||||
top:0;
|
|
||||||
left:0;
|
|
||||||
z-index:99999999;
|
|
||||||
display:block;
|
|
||||||
background-color:rgb(5,5,5, 0.8)
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (min-width:600px) {
|
|
||||||
.modal-box {
|
|
||||||
margin:0 auto;
|
|
||||||
margin-top:20px;
|
|
||||||
margin-bottom:20px;
|
|
||||||
position:absolute;
|
|
||||||
width:50%;
|
|
||||||
top:50%;
|
|
||||||
left:50%;
|
|
||||||
transform: translateY(-50%) translateX(-50%);
|
|
||||||
overflow: scroll;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width:600px) {
|
|
||||||
.modal-box {
|
|
||||||
width: 100%;
|
|
||||||
height: 80%;
|
|
||||||
position:fixed;
|
|
||||||
top:50px;
|
|
||||||
left:0;
|
|
||||||
right: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
position: relative;
|
|
||||||
float:right;
|
|
||||||
top: 10px;
|
|
||||||
}
|
|
||||||
.close {
|
|
||||||
font-size: 30px;
|
|
||||||
left: -20px;
|
|
||||||
position: relative;
|
|
||||||
color:white;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.loader {
|
|
||||||
--cal-brand-border-color: white;
|
|
||||||
--cal-brand-background-color: white;
|
|
||||||
}
|
|
||||||
${loaderCss}
|
|
||||||
</style>
|
|
||||||
<div class="backdrop">
|
|
||||||
<div class="header">
|
|
||||||
<span class="close">×</span>
|
|
||||||
</div>
|
|
||||||
<div class="modal-box">
|
|
||||||
<div class="body">
|
|
||||||
<div id="loader" class="absolute z-highest flex h-screen w-full items-center">
|
|
||||||
<div class="loader border-brand dark:border-darkmodebrand">
|
|
||||||
<span class="loader-inner bg-brand dark:bg-darkmodebrand"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
this.attachShadow({ mode: "open" });
|
|
||||||
ModalBox.htmlOverflow = document.body.style.overflow;
|
|
||||||
document.body.style.overflow = "hidden";
|
|
||||||
this.shadowRoot!.innerHTML = modalHtml;
|
|
||||||
}
|
|
||||||
}
|
|
71
packages/embeds/embed-core/src/ModalBox/ModalBox.ts
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
import { CalWindow } from "@calcom/embed-snippet";
|
||||||
|
|
||||||
|
import loaderCss from "../loader.css";
|
||||||
|
import modalBoxHtml from "./ModalBoxHtml";
|
||||||
|
|
||||||
|
export class ModalBox extends HTMLElement {
|
||||||
|
static htmlOverflow: string;
|
||||||
|
//@ts-ignore
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ["state"];
|
||||||
|
}
|
||||||
|
|
||||||
|
show(show: boolean) {
|
||||||
|
// We can't make it display none as that takes iframe width and height calculations to 0
|
||||||
|
(this.shadowRoot!.host as unknown as any).style.visibility = show ? "visible" : "hidden";
|
||||||
|
if (!show) {
|
||||||
|
document.body.style.overflow = ModalBox.htmlOverflow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.show(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name: string, oldValue: string, newValue: string) {
|
||||||
|
if (name !== "state") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newValue == "loaded") {
|
||||||
|
(this.shadowRoot!.querySelector("#loader")! as HTMLElement).style.display = "none";
|
||||||
|
} else if (newValue === "started") {
|
||||||
|
this.show(true);
|
||||||
|
} else if (newValue == "closed") {
|
||||||
|
this.show(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
const closeEl = this.shadowRoot!.querySelector(".close") as HTMLElement;
|
||||||
|
document.addEventListener(
|
||||||
|
"keydown",
|
||||||
|
(e) => {
|
||||||
|
if (e.key === "Escape") {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
once: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.shadowRoot!.host.addEventListener("click", (e) => {
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
closeEl.onclick = () => {
|
||||||
|
this.close();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
const modalHtml = `<style>${
|
||||||
|
(window as CalWindow).Cal!.__css
|
||||||
|
}</style><style>${loaderCss}</style>${modalBoxHtml}`;
|
||||||
|
this.attachShadow({ mode: "open" });
|
||||||
|
ModalBox.htmlOverflow = document.body.style.overflow;
|
||||||
|
document.body.style.overflow = "hidden";
|
||||||
|
this.shadowRoot!.innerHTML = modalHtml;
|
||||||
|
}
|
||||||
|
}
|
72
packages/embeds/embed-core/src/ModalBox/ModalBoxHtml.ts
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
const html = `<style>
|
||||||
|
.my-backdrop {
|
||||||
|
position:fixed;
|
||||||
|
width:100%;
|
||||||
|
height:100%;
|
||||||
|
top:0;
|
||||||
|
left:0;
|
||||||
|
z-index:99999999;
|
||||||
|
display:block;
|
||||||
|
background-color:rgb(5,5,5, 0.8)
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (min-width:600px) {
|
||||||
|
.modal-box {
|
||||||
|
margin:0 auto;
|
||||||
|
margin-top:20px;
|
||||||
|
margin-bottom:20px;
|
||||||
|
position:absolute;
|
||||||
|
width:100%;
|
||||||
|
top:50%;
|
||||||
|
left:50%;
|
||||||
|
transform: translateY(-50%) translateX(-50%);
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width:600px) {
|
||||||
|
.modal-box {
|
||||||
|
width: 100%;
|
||||||
|
height: 80%;
|
||||||
|
position:fixed;
|
||||||
|
top:50px;
|
||||||
|
left:0;
|
||||||
|
right: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
position: relative;
|
||||||
|
float:right;
|
||||||
|
top: 10px;
|
||||||
|
}
|
||||||
|
.close {
|
||||||
|
font-size: 30px;
|
||||||
|
left: -20px;
|
||||||
|
position: relative;
|
||||||
|
color:white;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
/*Modal background is black only, so hardcode white */
|
||||||
|
.loader {
|
||||||
|
--cal-brand-color:white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class="my-backdrop">
|
||||||
|
<div class="header">
|
||||||
|
<span class="close">×</span>
|
||||||
|
</div>
|
||||||
|
<div class="modal-box">
|
||||||
|
<div class="body">
|
||||||
|
<div id="loader" class="z-[999999999999] absolute flex w-full items-center">
|
||||||
|
<div class="loader modal-loader border-brand dark:border-darkmodebrand">
|
||||||
|
<span class="loader-inner bg-brand dark:bg-darkmodebrand"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
export default html;
|
|
@ -12,6 +12,7 @@ const embedStore = {
|
||||||
// Store all embed styles here so that as and when new elements are mounted, styles can be applied to it.
|
// Store all embed styles here so that as and when new elements are mounted, styles can be applied to it.
|
||||||
styles: {},
|
styles: {},
|
||||||
namespace: null,
|
namespace: null,
|
||||||
|
embedType: undefined,
|
||||||
theme: null,
|
theme: null,
|
||||||
// Store all React State setters here.
|
// Store all React State setters here.
|
||||||
reactStylesStateSetters: {},
|
reactStylesStateSetters: {},
|
||||||
|
@ -21,6 +22,7 @@ const embedStore = {
|
||||||
styles: UiConfig["styles"];
|
styles: UiConfig["styles"];
|
||||||
namespace: string | null;
|
namespace: string | null;
|
||||||
theme: string | null;
|
theme: string | null;
|
||||||
|
embedType: undefined | null | string;
|
||||||
reactStylesStateSetters: any;
|
reactStylesStateSetters: any;
|
||||||
parentInformedAboutContentHeight: boolean;
|
parentInformedAboutContentHeight: boolean;
|
||||||
windowLoadEventFired: boolean;
|
windowLoadEventFired: boolean;
|
||||||
|
@ -84,7 +86,9 @@ interface EmbedStyles {
|
||||||
disabledDateButton?: Pick<CSSProperties, "background" | "color" | "backgroundColor">;
|
disabledDateButton?: Pick<CSSProperties, "background" | "color" | "backgroundColor">;
|
||||||
availabilityDatePicker?: Pick<CSSProperties, "background" | "color" | "backgroundColor">;
|
availabilityDatePicker?: Pick<CSSProperties, "background" | "color" | "backgroundColor">;
|
||||||
}
|
}
|
||||||
interface EmbedStylesBranding {
|
interface EmbedNonStylesConfig {
|
||||||
|
/** Default would be center */
|
||||||
|
align: "left";
|
||||||
branding?: {
|
branding?: {
|
||||||
brandColor?: string;
|
brandColor?: string;
|
||||||
lightColor?: string;
|
lightColor?: string;
|
||||||
|
@ -97,7 +101,7 @@ interface EmbedStylesBranding {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReactEmbedStylesSetter = React.Dispatch<React.SetStateAction<EmbedStyles | EmbedStylesBranding>>;
|
type ReactEmbedStylesSetter = React.Dispatch<React.SetStateAction<EmbedStyles | EmbedNonStylesConfig>>;
|
||||||
|
|
||||||
const setEmbedStyles = (stylesConfig: UiConfig["styles"]) => {
|
const setEmbedStyles = (stylesConfig: UiConfig["styles"]) => {
|
||||||
embedStore.styles = stylesConfig;
|
embedStore.styles = stylesConfig;
|
||||||
|
@ -111,14 +115,14 @@ const setEmbedStyles = (stylesConfig: UiConfig["styles"]) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const registerNewSetter = (elementName: keyof EmbedStyles | keyof EmbedStylesBranding, setStyles: any) => {
|
const registerNewSetter = (elementName: keyof EmbedStyles | keyof EmbedNonStylesConfig, setStyles: any) => {
|
||||||
embedStore.reactStylesStateSetters[elementName] = setStyles;
|
embedStore.reactStylesStateSetters[elementName] = setStyles;
|
||||||
// It's possible that 'ui' instruction has already been processed and the registration happened due to some action by the user in iframe.
|
// It's possible that 'ui' instruction has already been processed and the registration happened due to some action by the user in iframe.
|
||||||
// So, we should call the setter immediately with available embedStyles
|
// So, we should call the setter immediately with available embedStyles
|
||||||
setStyles(embedStore.styles);
|
setStyles(embedStore.styles);
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeFromEmbedStylesSetterMap = (elementName: keyof EmbedStyles | keyof EmbedStylesBranding) => {
|
const removeFromEmbedStylesSetterMap = (elementName: keyof EmbedStyles | keyof EmbedNonStylesConfig) => {
|
||||||
delete embedStore.reactStylesStateSetters[elementName];
|
delete embedStore.reactStylesStateSetters[elementName];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -128,6 +132,12 @@ function isValidNamespace(ns: string | null | undefined) {
|
||||||
|
|
||||||
export const useEmbedTheme = () => {
|
export const useEmbedTheme = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
useEffect(() => {
|
||||||
|
router.events.on("routeChangeComplete", () => {
|
||||||
|
sdkActionManager?.fire("__routeChanged", {});
|
||||||
|
});
|
||||||
|
}, [router.events]);
|
||||||
|
|
||||||
if (embedStore.theme) {
|
if (embedStore.theme) {
|
||||||
return embedStore.theme;
|
return embedStore.theme;
|
||||||
}
|
}
|
||||||
|
@ -151,8 +161,8 @@ export const useEmbedStyles = (elementName: keyof EmbedStyles) => {
|
||||||
return styles[elementName] || {};
|
return styles[elementName] || {};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useEmbedBranding = (elementName: keyof EmbedStylesBranding) => {
|
export const useEmbedNonStylesConfig = (elementName: keyof EmbedNonStylesConfig) => {
|
||||||
const [styles, setStyles] = useState({} as EmbedStylesBranding);
|
const [styles, setStyles] = useState({} as EmbedNonStylesConfig);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
registerNewSetter(elementName, setStyles);
|
registerNewSetter(elementName, setStyles);
|
||||||
|
@ -171,7 +181,7 @@ export const useIsBackgroundTransparent = () => {
|
||||||
// TODO: Background should be read as ui.background and not ui.body.background
|
// TODO: Background should be read as ui.background and not ui.body.background
|
||||||
const bodyEmbedStyles = useEmbedStyles("body");
|
const bodyEmbedStyles = useEmbedStyles("body");
|
||||||
|
|
||||||
if (bodyEmbedStyles?.background === "transparent") {
|
if (bodyEmbedStyles.background === "transparent") {
|
||||||
isBackgroundTransparent = true;
|
isBackgroundTransparent = true;
|
||||||
}
|
}
|
||||||
return isBackgroundTransparent;
|
return isBackgroundTransparent;
|
||||||
|
@ -179,8 +189,8 @@ export const useIsBackgroundTransparent = () => {
|
||||||
|
|
||||||
export const useBrandColors = () => {
|
export const useBrandColors = () => {
|
||||||
// TODO: Branding shouldn't be part of ui.styles. It should exist as ui.branding.
|
// TODO: Branding shouldn't be part of ui.styles. It should exist as ui.branding.
|
||||||
const brandingColors = useEmbedBranding("branding");
|
const brandingColors = useEmbedNonStylesConfig("branding") as EmbedNonStylesConfig["branding"];
|
||||||
return brandingColors;
|
return brandingColors || {};
|
||||||
};
|
};
|
||||||
|
|
||||||
function getNamespace() {
|
function getNamespace() {
|
||||||
|
@ -196,6 +206,17 @@ function getNamespace() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getEmbedType() {
|
||||||
|
if (embedStore.embedType) {
|
||||||
|
return embedStore.embedType;
|
||||||
|
}
|
||||||
|
if (isBrowser) {
|
||||||
|
const url = new URL(document.URL);
|
||||||
|
const embedType = (embedStore.embedType = url.searchParams.get("embedType"));
|
||||||
|
return embedType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const isEmbed = () => {
|
const isEmbed = () => {
|
||||||
const namespace = getNamespace();
|
const namespace = getNamespace();
|
||||||
const _isValidNamespace = isValidNamespace(namespace);
|
const _isValidNamespace = isValidNamespace(namespace);
|
||||||
|
@ -218,6 +239,14 @@ export const useIsEmbed = () => {
|
||||||
return _isEmbed;
|
return _isEmbed;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useEmbedType = () => {
|
||||||
|
const [state, setState] = useState<string | null | undefined>(null);
|
||||||
|
useEffect(() => {
|
||||||
|
setState(getEmbedType());
|
||||||
|
}, []);
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
|
||||||
function unhideBody() {
|
function unhideBody() {
|
||||||
document.body.style.display = "block";
|
document.body.style.display = "block";
|
||||||
}
|
}
|
||||||
|
@ -300,9 +329,13 @@ function keepParentInformedAboutDimensionChanges() {
|
||||||
embedStore.windowLoadEventFired = true;
|
embedStore.windowLoadEventFired = true;
|
||||||
// Use the dimensions of main element as in most places there is max-width restriction on it and we just want to show the main content.
|
// Use the dimensions of main element as in most places there is max-width restriction on it and we just want to show the main content.
|
||||||
// It avoids the unwanted padding outside main tag.
|
// It avoids the unwanted padding outside main tag.
|
||||||
const mainElement = document.getElementsByTagName("main")[0] || document.documentElement;
|
const mainElement =
|
||||||
|
(document.getElementsByClassName("main")[0] as HTMLElement) ||
|
||||||
|
document.getElementsByTagName("main")[0] ||
|
||||||
|
document.documentElement;
|
||||||
const documentScrollHeight = document.documentElement.scrollHeight;
|
const documentScrollHeight = document.documentElement.scrollHeight;
|
||||||
const documentScrollWidth = document.documentElement.scrollWidth;
|
const documentScrollWidth = document.documentElement.scrollWidth;
|
||||||
|
|
||||||
const contentHeight = mainElement.offsetHeight;
|
const contentHeight = mainElement.offsetHeight;
|
||||||
const contentWidth = mainElement.offsetWidth;
|
const contentWidth = mainElement.offsetWidth;
|
||||||
|
|
||||||
|
@ -331,10 +364,6 @@ function keepParentInformedAboutDimensionChanges() {
|
||||||
// Parent Counterpart would change the dimension of iframe and thus page's dimension would be impacted which is recursive.
|
// Parent Counterpart would change the dimension of iframe and thus page's dimension would be impacted which is recursive.
|
||||||
// It should stop ideally by reaching a hiddenHeight value of 0.
|
// It should stop ideally by reaching a hiddenHeight value of 0.
|
||||||
// FIXME: If 0 can't be reached we need to just abandon our quest for perfect iframe and let scroll be there. Such case can be logged in the wild and fixed later on.
|
// FIXME: If 0 can't be reached we need to just abandon our quest for perfect iframe and let scroll be there. Such case can be logged in the wild and fixed later on.
|
||||||
if (numDimensionChanges > 50) {
|
|
||||||
console.warn("Too many dimension changes detected.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
runAsap(informAboutScroll);
|
runAsap(informAboutScroll);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -361,10 +390,16 @@ if (isBrowser) {
|
||||||
// Because on cal-iframe we set explicty width to make it look inline and part of page, there is never space available for content to automatically expand
|
// Because on cal-iframe we set explicty width to make it look inline and part of page, there is never space available for content to automatically expand
|
||||||
// This is a HACK to quickly tell iframe to go full width and let iframe content adapt to that and set new width.
|
// This is a HACK to quickly tell iframe to go full width and let iframe content adapt to that and set new width.
|
||||||
sdkActionManager?.on("__refreshWidth", () => {
|
sdkActionManager?.on("__refreshWidth", () => {
|
||||||
sdkActionManager?.fire("__dimensionChanged", {
|
// sdkActionManager?.fire("__dimensionChanged", {
|
||||||
iframeWidth: 100,
|
// iframeWidth: 100,
|
||||||
__unit: "%",
|
// __unit: "%",
|
||||||
});
|
// });
|
||||||
|
// runAsap(() => {
|
||||||
|
// sdkActionManager?.fire("__dimensionChanged", {
|
||||||
|
// iframeWidth: 100,
|
||||||
|
// __unit: "%",
|
||||||
|
// });
|
||||||
|
// });
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener("message", (e) => {
|
window.addEventListener("message", (e) => {
|
||||||
|
@ -378,6 +413,19 @@ if (isBrowser) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.addEventListener("click", (e) => {
|
||||||
|
if (!e.target) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const mainElement =
|
||||||
|
(document.getElementsByClassName("main")[0] as HTMLElement) ||
|
||||||
|
document.getElementsByTagName("main")[0] ||
|
||||||
|
document.documentElement;
|
||||||
|
if ((e.target as HTMLElement).contains(mainElement)) {
|
||||||
|
sdkActionManager?.fire("__closeIframe", {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (!pageStatus || pageStatus == "200") {
|
if (!pageStatus || pageStatus == "200") {
|
||||||
keepParentInformedAboutDimensionChanges();
|
keepParentInformedAboutDimensionChanges();
|
||||||
sdkActionManager?.fire("__iframeReady", {});
|
sdkActionManager?.fire("__iframeReady", {});
|
||||||
|
|
|
@ -4,4 +4,6 @@
|
||||||
.cal-embed {
|
.cal-embed {
|
||||||
border: 0px;
|
border: 0px;
|
||||||
min-height: 300px;
|
min-height: 300px;
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,29 @@
|
||||||
import type { CalWindow } from "@calcom/embed-snippet";
|
import type { CalWindow } from "@calcom/embed-snippet";
|
||||||
|
|
||||||
import { FloatingButton } from "./FloatingButton";
|
import { FloatingButton } from "./FloatingButton/FloatingButton";
|
||||||
import { ModalBox } from "./ModalBox";
|
import { Inline } from "./Inline/inline";
|
||||||
|
import { ModalBox } from "./ModalBox/ModalBox";
|
||||||
import { methods, UiConfig } from "./embed-iframe";
|
import { methods, UiConfig } from "./embed-iframe";
|
||||||
import css from "./embed.css";
|
import css from "./embed.css";
|
||||||
import { Inline } from "./inline";
|
|
||||||
import { SdkActionManager } from "./sdk-action-manager";
|
import { SdkActionManager } from "./sdk-action-manager";
|
||||||
|
import allCss from "./tailwind.generated.css";
|
||||||
|
|
||||||
|
customElements.define("cal-modal-box", ModalBox);
|
||||||
|
customElements.define("cal-floating-button", FloatingButton);
|
||||||
|
customElements.define("cal-inline", Inline);
|
||||||
|
|
||||||
declare module "*.css";
|
declare module "*.css";
|
||||||
|
|
||||||
type Namespace = string;
|
type Namespace = string;
|
||||||
type Config = {
|
type Config = {
|
||||||
origin: string;
|
origin: string;
|
||||||
debug: 1;
|
debug?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const globalCal = (window as CalWindow).Cal;
|
const globalCal = (window as CalWindow).Cal;
|
||||||
|
|
||||||
if (!globalCal || !globalCal.q) {
|
if (!globalCal || !globalCal.q) {
|
||||||
throw new Error("Cal is not defined. This shouldn't happen");
|
throw new Error("Cal is not defined. This shouldn't happen");
|
||||||
}
|
}
|
||||||
|
globalCal.__css = allCss;
|
||||||
document.head.appendChild(document.createElement("style")).innerHTML = css;
|
document.head.appendChild(document.createElement("style")).innerHTML = css;
|
||||||
|
|
||||||
function log(...args: any[]) {
|
function log(...args: any[]) {
|
||||||
|
@ -75,7 +78,7 @@ export type InstructionQueue = Instruction[];
|
||||||
export class Cal {
|
export class Cal {
|
||||||
iframe?: HTMLIFrameElement;
|
iframe?: HTMLIFrameElement;
|
||||||
|
|
||||||
__config: any;
|
__config: Config;
|
||||||
|
|
||||||
modalBox!: Element;
|
modalBox!: Element;
|
||||||
|
|
||||||
|
@ -96,7 +99,7 @@ export class Cal {
|
||||||
return {
|
return {
|
||||||
...config,
|
...config,
|
||||||
// guests is better for API but Booking Page accepts guest. So do the mapping
|
// guests is better for API but Booking Page accepts guest. So do the mapping
|
||||||
guest: config.guests ?? "",
|
guest: config.guests ?? undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,27 +144,35 @@ export class Cal {
|
||||||
queryObject = {},
|
queryObject = {},
|
||||||
}: {
|
}: {
|
||||||
calLink: string;
|
calLink: string;
|
||||||
queryObject?: Record<string, string | string[]>;
|
queryObject?: Record<string, string | string[] | Record<string, string>>;
|
||||||
}) {
|
}) {
|
||||||
const iframe = (this.iframe = document.createElement("iframe"));
|
const iframe = (this.iframe = document.createElement("iframe"));
|
||||||
iframe.className = "cal-embed";
|
iframe.className = "cal-embed";
|
||||||
iframe.name = "cal-embed";
|
iframe.name = "cal-embed";
|
||||||
const config = this.getConfig();
|
const config = this.getConfig();
|
||||||
|
const { iframeAttrs, ...restQueryObject } = queryObject;
|
||||||
|
|
||||||
|
if (iframeAttrs && typeof iframeAttrs !== "string" && !(iframeAttrs instanceof Array)) {
|
||||||
|
iframe.setAttribute("id", iframeAttrs.id);
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare searchParams from config
|
// Prepare searchParams from config
|
||||||
const searchParams = new URLSearchParams();
|
const searchParams = new URLSearchParams();
|
||||||
for (const [key, value] of Object.entries(queryObject)) {
|
for (const [key, value] of Object.entries(restQueryObject)) {
|
||||||
|
if (value === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (value instanceof Array) {
|
if (value instanceof Array) {
|
||||||
value.forEach((val) => searchParams.append(key, val));
|
value.forEach((val) => searchParams.append(key, val));
|
||||||
} else {
|
} else {
|
||||||
searchParams.set(key, value);
|
searchParams.set(key, value as string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const urlInstance = new URL(`${config.origin}/${calLink}`);
|
const urlInstance = new URL(`${config.origin}/${calLink}`);
|
||||||
urlInstance.searchParams.set("embed", this.namespace);
|
urlInstance.searchParams.set("embed", this.namespace);
|
||||||
if (config.debug) {
|
if (config.debug) {
|
||||||
urlInstance.searchParams.set("debug", config.debug);
|
urlInstance.searchParams.set("debug", "" + config.debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge searchParams from config onto the URL which might have query params already
|
// Merge searchParams from config onto the URL which might have query params already
|
||||||
|
@ -219,6 +230,16 @@ export class Cal {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
config = config || {};
|
||||||
|
|
||||||
|
// Keeping auto-scroll disabled for two reasons:
|
||||||
|
// - If user scrolls the content to an appropriate position, it again resets it to default position which might not be for the liking of the user
|
||||||
|
// - Sometimes, the position can be wrong(e.g. if there is a fixed position header on top coming above the iframe content).
|
||||||
|
// Best solution might be to autoscroll only if the iframe is not fully visible, detection of full visibility might be tough
|
||||||
|
|
||||||
|
// We need to keep in mind that autoscroll is meant to solve the problem when on a certain view(which is availability page right now), the height goes too high and then suddenly it becomes normal
|
||||||
|
(config as unknown as any).__autoScroll = !!(config as unknown as any).__autoScroll;
|
||||||
|
config.embedType = "inline";
|
||||||
const iframe = this.createIframe({ calLink, queryObject: Cal.getQueryObject(config) });
|
const iframe = this.createIframe({ calLink, queryObject: Cal.getQueryObject(config) });
|
||||||
iframe.style.height = "100%";
|
iframe.style.height = "100%";
|
||||||
iframe.style.width = "100%";
|
iframe.style.width = "100%";
|
||||||
|
@ -230,8 +251,9 @@ export class Cal {
|
||||||
throw new Error("Element not found");
|
throw new Error("Element not found");
|
||||||
}
|
}
|
||||||
const template = document.createElement("template");
|
const template = document.createElement("template");
|
||||||
template.innerHTML = `<cal-inline style="max-height:inherit;height:inherit;min-height:inherit;display:block;position:relative"></cal-inline>`;
|
template.innerHTML = `<cal-inline style="max-height:inherit;height:inherit;min-height:inherit;display:flex;position:relative;flex-wrap:wrap"></cal-inline>`;
|
||||||
this.inlineEl = template.content.children[0];
|
this.inlineEl = template.content.children[0];
|
||||||
|
(this.inlineEl as unknown as any).__CalAutoScroll = config.__autoScroll;
|
||||||
this.inlineEl.appendChild(iframe);
|
this.inlineEl.appendChild(iframe);
|
||||||
element.appendChild(template.content);
|
element.appendChild(template.content);
|
||||||
}
|
}
|
||||||
|
@ -247,7 +269,7 @@ export class Cal {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const template = document.createElement("template");
|
const template = document.createElement("template");
|
||||||
template.innerHTML = `<cal-floating-button data-cal-namespace=${this.namespace} data-cal-link=${calLink}></cal-floating-button>`;
|
template.innerHTML = `<cal-floating-button data-cal-namespace="${this.namespace}" data-cal-link="${calLink}"></cal-floating-button>`;
|
||||||
document.body.appendChild(template.content);
|
document.body.appendChild(template.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,6 +279,7 @@ export class Cal {
|
||||||
existingModalEl.setAttribute("state", "started");
|
existingModalEl.setAttribute("state", "started");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
config.embedType = "modal";
|
||||||
const iframe = this.createIframe({ calLink, queryObject: Cal.getQueryObject(config) });
|
const iframe = this.createIframe({ calLink, queryObject: Cal.getQueryObject(config) });
|
||||||
iframe.style.borderRadius = "8px";
|
iframe.style.borderRadius = "8px";
|
||||||
|
|
||||||
|
@ -264,8 +287,12 @@ export class Cal {
|
||||||
iframe.style.width = "100%";
|
iframe.style.width = "100%";
|
||||||
const template = document.createElement("template");
|
const template = document.createElement("template");
|
||||||
template.innerHTML = `<cal-modal-box uid="${uid}"></cal-modal-box>`;
|
template.innerHTML = `<cal-modal-box uid="${uid}"></cal-modal-box>`;
|
||||||
|
|
||||||
this.modalBox = template.content.children[0];
|
this.modalBox = template.content.children[0];
|
||||||
this.modalBox.appendChild(iframe);
|
this.modalBox.appendChild(iframe);
|
||||||
|
this.actionManager.on("__closeIframe", () => {
|
||||||
|
this.modalBox.setAttribute("state", "closed");
|
||||||
|
});
|
||||||
document.body.appendChild(template.content);
|
document.body.appendChild(template.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,7 +375,7 @@ export class Cal {
|
||||||
constructor(namespace: string, q: InstructionQueue) {
|
constructor(namespace: string, q: InstructionQueue) {
|
||||||
this.__config = {
|
this.__config = {
|
||||||
// Keep cal.com hardcoded till the time embed.js deployment to cal.com/embed.js is automated. This is to prevent accidentally pushing of localhost domain to production
|
// Keep cal.com hardcoded till the time embed.js deployment to cal.com/embed.js is automated. This is to prevent accidentally pushing of localhost domain to production
|
||||||
origin: /*import.meta.env.NEXT_PUBLIC_WEBSITE_URL || */ "https://cal.com",
|
origin: /*import.meta.env.NEXT_PUBLIC_WEBSITE_URL || */ "https://app.cal.com",
|
||||||
};
|
};
|
||||||
this.namespace = namespace;
|
this.namespace = namespace;
|
||||||
this.actionManager = new SdkActionManager(namespace);
|
this.actionManager = new SdkActionManager(namespace);
|
||||||
|
@ -377,9 +404,9 @@ export class Cal {
|
||||||
iframe.style.height = data.iframeHeight + unit;
|
iframe.style.height = data.iframeHeight + unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.iframeWidth) {
|
// if (data.iframeWidth) {
|
||||||
iframe.style.width = data.iframeWidth + unit;
|
// iframe.style.width = data.iframeWidth + unit;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (this.modalBox) {
|
if (this.modalBox) {
|
||||||
// It ensures that if the iframe is so tall that it can't fit in the parent window without scroll. Then force the scroll by restricting the max-height to innerHeight
|
// It ensures that if the iframe is so tall that it can't fit in the parent window without scroll. Then force the scroll by restricting the max-height to innerHeight
|
||||||
|
@ -399,6 +426,13 @@ export class Cal {
|
||||||
this.doInIframe({ method, arg });
|
this.doInIframe({ method, arg });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.actionManager.on("__routeChanged", () => {
|
||||||
|
if (this.inlineEl && (this.inlineEl as unknown as any).__CalAutoScroll) {
|
||||||
|
this.inlineEl.scrollIntoView();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.actionManager.on("linkReady", (e) => {
|
this.actionManager.on("linkReady", (e) => {
|
||||||
this.modalBox?.setAttribute("state", "loaded");
|
this.modalBox?.setAttribute("state", "loaded");
|
||||||
this.inlineEl?.setAttribute("loading", "done");
|
this.inlineEl?.setAttribute("loading", "done");
|
||||||
|
@ -455,13 +489,12 @@ document.addEventListener("click", (e) => {
|
||||||
if (namespace) {
|
if (namespace) {
|
||||||
api = globalCal.ns![namespace];
|
api = globalCal.ns![namespace];
|
||||||
}
|
}
|
||||||
|
if (!api) {
|
||||||
|
throw new Error(`Namespace ${namespace} isn't defined`);
|
||||||
|
}
|
||||||
api("modal", {
|
api("modal", {
|
||||||
calLink: path,
|
calLink: path,
|
||||||
config,
|
config,
|
||||||
uid: modalUniqueId,
|
uid: modalUniqueId,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
customElements.define("cal-modal-box", ModalBox);
|
|
||||||
customElements.define("cal-floating-button", FloatingButton);
|
|
||||||
customElements.define("cal-inline", Inline);
|
|
||||||
|
|
|
@ -53,10 +53,13 @@
|
||||||
display: block;
|
display: block;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
margin: 60px auto;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
border-width: 4px;
|
border-width: 4px;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
-webkit-animation: loader 2s infinite ease;
|
-webkit-animation: loader 2s infinite ease;
|
||||||
animation: loader 2s infinite ease;
|
animation: loader 2s infinite ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loader.modal-loader {
|
||||||
|
margin: 60px auto;
|
||||||
|
}
|
22
packages/embeds/embed-core/src/styles.css
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Cal Sans';
|
||||||
|
src: url("https://cal.com/cal.ttf");
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
font-family: 'Cal Sans';
|
||||||
|
font-weight: normal;
|
||||||
|
letter-spacing: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body, :host {
|
||||||
|
font-family: ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji
|
||||||
|
}
|
|
@ -1,203 +0,0 @@
|
||||||
* {
|
|
||||||
-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));
|
|
||||||
}
|
|
||||||
|
|
||||||
.justify-center {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.items-center {
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.antialiased {
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
|
|
||||||
.leading-5 {
|
|
||||||
line-height: 1.25rem;
|
|
||||||
}
|
|
||||||
.w-7 {
|
|
||||||
width: 1.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.h-7 {
|
|
||||||
height: 1.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.font-semibold {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
.flex {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.antialiased {
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
|
|
||||||
.leading-5 {
|
|
||||||
line-height: 1.25rem;
|
|
||||||
}
|
|
||||||
.font-semibold {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
.mr-3 {
|
|
||||||
margin-right: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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:var(--cal-brand-border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-brand {
|
|
||||||
background-color: var(--cal-brand-background-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
.relative {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
17
packages/embeds/embed-core/tailwind.config.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
const base = require("@calcom/config/tailwind-preset");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
...base,
|
||||||
|
content: ["**/*Html.ts"],
|
||||||
|
theme: {
|
||||||
|
...base.theme,
|
||||||
|
extend: {
|
||||||
|
...base.theme.extend,
|
||||||
|
colors: {
|
||||||
|
...base.theme.extend.colors,
|
||||||
|
// Set default as black
|
||||||
|
brand: "var(--cal-brand-color, black)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
|
@ -2,6 +2,9 @@
|
||||||
"extends": "@calcom/tsconfig/base.json",
|
"extends": "@calcom/tsconfig/base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"module": "esnext",
|
"module": "esnext",
|
||||||
|
"paths": {
|
||||||
|
"@lib/*": ["../../../apps/web/lib/*"]
|
||||||
|
},
|
||||||
"moduleResolution": "Node",
|
"moduleResolution": "Node",
|
||||||
"baseUrl": "."
|
"baseUrl": "."
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,7 +2,6 @@ require("dotenv").config({ path: "../../../.env" });
|
||||||
|
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const { defineConfig } = require("vite");
|
const { defineConfig } = require("vite");
|
||||||
|
|
||||||
module.exports = defineConfig({
|
module.exports = defineConfig({
|
||||||
envPrefix: "NEXT_PUBLIC_",
|
envPrefix: "NEXT_PUBLIC_",
|
||||||
build: {
|
build: {
|
||||||
|
|
2
packages/embeds/embed-react/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.turbo
|
||||||
|
dist
|
|
@ -2,4 +2,13 @@
|
||||||
|
|
||||||
Embed Cal Link as a React Component
|
Embed Cal Link as a React Component
|
||||||
|
|
||||||
To know how to use it, follow the steps at <https://docs.cal.com/integrations/embed>
|
To know how to use it, follow the steps at <https://docs.cal.com/integrations/embed>
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
- Playwright tests.
|
||||||
|
- Need to what these tests should be as embed-core already have tests. We probably just need to verify that embed-core API is called appropriately.
|
||||||
|
- It would probably be better if Playwright tests exist at one place for all embeds.
|
||||||
|
- Distribution
|
||||||
|
- It would be better DX to serve the unbuilt version with JSX, instead of built version with React.createElement calls. But because of WebPack loaders not running on node_modules automatically, it doesn't work automatically.
|
||||||
|
- Right now if a typescript project uses the package, VSCode takes the user to .d.ts files instead of the functions definitions. How to solve it ?
|
|
@ -3,18 +3,42 @@
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"description": "Embed Cal Link as a React Component",
|
"description": "Embed Cal Link as a React Component",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --port=3003 --open",
|
"dev": "vite --port=3101 --open",
|
||||||
"build": "vite build",
|
"tsc": "tsc",
|
||||||
|
"build": "vite build && yarn tsc --emitDeclarationOnly --declarationDir dist",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
|
"prepare": "yarn build",
|
||||||
"type-check": "tsc --pretty --noEmit",
|
"type-check": "tsc --pretty --noEmit",
|
||||||
"lint": "eslint --ext .ts,.js,.tsx,.jsx ./src"
|
"lint": "eslint --ext .ts,.js,.tsx,.jsx ./src",
|
||||||
|
"embed-tests": "yarn playwright test --config=./playwright/config/playwright.config.ts",
|
||||||
|
"embed-tests-quick": "QUICK=true yarn embed-tests"
|
||||||
},
|
},
|
||||||
"main": "src/Cal.tsx",
|
"main": "./dist/Cal.umd.js",
|
||||||
"dependencies": {
|
"module": "./dist/Cal.es.js",
|
||||||
"@calcom/embed-snippet": "^1.0.0"
|
"types": "./dist/src/index.d.ts",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^17.0.0",
|
||||||
|
"react-dom": "^17.0.0"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"import": "./dist/Cal.es.js",
|
||||||
|
"require": "./dist/Cal.umd.js"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vite": "^2.8.6",
|
"@calcom/embed-snippet": "^1.0.0",
|
||||||
"eslint": "^8.10.0"
|
"@types/react": "^17.0.0",
|
||||||
|
"@types/react-dom": "^17.0.0",
|
||||||
|
"@vitejs/plugin-react": "^1.3.0",
|
||||||
|
"eslint": "^8.10.0",
|
||||||
|
"vite": "^2.9.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"playwright": "^1.21.1",
|
||||||
|
"typescript": "^4.6.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { PlaywrightTestConfig, devices } from "@playwright/test";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
//TODO: Move the common config to embed-playwright-config and let core and react use the base. Along with config there would be base fixtures and expect custom matchers as well.
|
||||||
|
import baseConfig from "@calcom/embed-core/playwright/config/playwright.config";
|
||||||
|
|
||||||
|
const testDir = path.join("../tests");
|
||||||
|
|
||||||
|
const projects = baseConfig.projects?.map((project) => {
|
||||||
|
if (!project.name) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...project,
|
||||||
|
testDir,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const config: PlaywrightTestConfig = {
|
||||||
|
...baseConfig,
|
||||||
|
webServer: {
|
||||||
|
// Start App Server manually - Can't be handled here. See https://github.com/microsoft/playwright/issues/8206
|
||||||
|
command: "yarn workspace @calcom/embed-react dev",
|
||||||
|
port: 3101,
|
||||||
|
timeout: 60_000,
|
||||||
|
reuseExistingServer: true,
|
||||||
|
},
|
||||||
|
use: {
|
||||||
|
...baseConfig.use,
|
||||||
|
baseURL: "http://localhost:3101",
|
||||||
|
},
|
||||||
|
projects,
|
||||||
|
};
|
||||||
|
export default config;
|
18
packages/embeds/embed-react/playwright/tests/basic.test.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { expect } from "@playwright/test";
|
||||||
|
|
||||||
|
import { test } from "@calcom/embed-core/playwright/fixtures/fixtures";
|
||||||
|
import { getEmbedIframe } from "@calcom/embed-core/playwright/lib/testUtils";
|
||||||
|
|
||||||
|
test("Inline Usage Snapshot", async ({ page, getActionFiredDetails, addEmbedListeners }) => {
|
||||||
|
//TODO: Do it with page.goto automatically
|
||||||
|
await addEmbedListeners("");
|
||||||
|
await page.goto("/");
|
||||||
|
const embedIframe = await getEmbedIframe({ page, pathname: "/pro" });
|
||||||
|
expect(embedIframe).toBeEmbedCalLink("", getActionFiredDetails, {
|
||||||
|
pathname: "/pro",
|
||||||
|
searchParams: {
|
||||||
|
theme: "dark",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(await page.screenshot()).toMatchSnapshot("react-component-inline.png");
|
||||||
|
});
|
After Width: | Height: | Size: 57 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 126 KiB |
|
@ -13,12 +13,17 @@ export default function Cal({
|
||||||
config?: any;
|
config?: any;
|
||||||
embedJsUrl?: string;
|
embedJsUrl?: string;
|
||||||
}) {
|
}) {
|
||||||
|
if (!calLink) {
|
||||||
|
throw new Error("calLink is required");
|
||||||
|
}
|
||||||
|
const initializedRef = useRef(false);
|
||||||
const Cal = useEmbed(embedJsUrl);
|
const Cal = useEmbed(embedJsUrl);
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!Cal) {
|
if (!Cal || initializedRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
initializedRef.current = true;
|
||||||
const element = ref.current;
|
const element = ref.current;
|
||||||
let initConfig = {};
|
let initConfig = {};
|
||||||
if (calOrigin) {
|
if (calOrigin) {
|
||||||
|
@ -30,9 +35,6 @@ export default function Cal({
|
||||||
calLink,
|
calLink,
|
||||||
config,
|
config,
|
||||||
});
|
});
|
||||||
return () => {
|
|
||||||
element?.querySelector(".cal-embed")?.remove();
|
|
||||||
};
|
|
||||||
}, [Cal, calLink, config, calOrigin]);
|
}, [Cal, calLink, config, calOrigin]);
|
||||||
|
|
||||||
if (!Cal) {
|
if (!Cal) {
|
||||||
|
|
3
packages/embeds/embed-react/src/index.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import Cal from "./Cal";
|
||||||
|
|
||||||
|
export default Cal;
|
|
@ -1,8 +1,15 @@
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { useState } from "react";
|
||||||
import ReactDom from "react-dom";
|
import ReactDom from "react-dom";
|
||||||
|
|
||||||
import Cal from "@calcom/embed-react";
|
import Cal from "./src/index";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
const [loaded, setLoaded] = useState(false);
|
||||||
|
useEffect(() => {
|
||||||
|
// Simulate state change causing config object to change, causing rerender of Cal
|
||||||
|
setTimeout(setLoaded.bind(true), 1000);
|
||||||
|
}, []);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h1>
|
<h1>
|
||||||
|
@ -10,7 +17,7 @@ function App() {
|
||||||
</h1>
|
</h1>
|
||||||
<Cal
|
<Cal
|
||||||
calOrigin="http://localhost:3000"
|
calOrigin="http://localhost:3000"
|
||||||
embedJsUrl="//localhost:3002/dist/embed.umd.js"
|
embedJsUrl="//localhost:3100/dist/embed.umd.js"
|
||||||
calLink="pro"
|
calLink="pro"
|
||||||
config={{
|
config={{
|
||||||
name: "John Doe",
|
name: "John Doe",
|
||||||
|
|
|
@ -4,8 +4,12 @@
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "Node",
|
"moduleResolution": "Node",
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"jsx": "preserve"
|
"declaration": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"paths": {
|
||||||
|
"@lib/*": ["../../../apps/web/lib/*"]
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"include": ["."],
|
"include": ["."],
|
||||||
"exclude": ["dist", "build", "node_modules"]
|
"exclude": ["dist", "build", "node_modules", "test-cal.tsx"]
|
||||||
}
|
}
|
||||||
|
|
28
packages/embeds/embed-react/vite.config.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
|
import path from "path";
|
||||||
|
import { defineConfig } from "vite";
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
build: {
|
||||||
|
lib: {
|
||||||
|
entry: path.resolve(__dirname, "src/index.ts"),
|
||||||
|
name: "Cal",
|
||||||
|
fileName: (format) => `Cal.${format}.js`,
|
||||||
|
},
|
||||||
|
rollupOptions: {
|
||||||
|
// make sure to externalize deps that shouldn't be bundled
|
||||||
|
// into your library
|
||||||
|
external: ["react", "react-dom"],
|
||||||
|
output: {
|
||||||
|
// Provide global variables to use in the UMD build
|
||||||
|
// for externalized deps
|
||||||
|
globals: {
|
||||||
|
react: "React",
|
||||||
|
"react-dom": "ReactDOM",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -2,7 +2,7 @@
|
||||||
* As we want to keep control on the size of this snippet but we want some portion of it to be still readable.
|
* As we want to keep control on the size of this snippet but we want some portion of it to be still readable.
|
||||||
* So, write the code that you need directly but keep it short.
|
* So, write the code that you need directly but keep it short.
|
||||||
*/
|
*/
|
||||||
import { Cal as CalClass, Instruction, InstructionQueue } from "@calcom/embed-core/src/embed";
|
import type { Cal as CalClass, InstructionQueue } from "@calcom/embed-core/src/embed";
|
||||||
|
|
||||||
export interface GlobalCal {
|
export interface GlobalCal {
|
||||||
(methodName: string, arg?: any): void;
|
(methodName: string, arg?: any): void;
|
||||||
|
@ -13,6 +13,7 @@ export interface GlobalCal {
|
||||||
/** If user registers multiple namespaces, those are available here */
|
/** If user registers multiple namespaces, those are available here */
|
||||||
ns?: Record<string, GlobalCal>;
|
ns?: Record<string, GlobalCal>;
|
||||||
instance?: CalClass;
|
instance?: CalClass;
|
||||||
|
__css?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CalWindow extends Window {
|
export interface CalWindow extends Window {
|
||||||
|
|
|
@ -122,6 +122,12 @@
|
||||||
},
|
},
|
||||||
"postinstall": {},
|
"postinstall": {},
|
||||||
"start": {},
|
"start": {},
|
||||||
|
"embed-tests": {
|
||||||
|
"cache": false
|
||||||
|
},
|
||||||
|
"embed-tests-quick": {
|
||||||
|
"cache": false
|
||||||
|
},
|
||||||
"test": {
|
"test": {
|
||||||
"dependsOn": ["^test"]
|
"dependsOn": ["^test"]
|
||||||
},
|
},
|
||||||
|
|
326
yarn.lock
|
@ -117,7 +117,7 @@
|
||||||
json5 "^2.1.2"
|
json5 "^2.1.2"
|
||||||
semver "^6.3.0"
|
semver "^6.3.0"
|
||||||
|
|
||||||
"@babel/core@^7.7.2", "@babel/core@^7.8.0":
|
"@babel/core@^7.17.9", "@babel/core@^7.7.2", "@babel/core@^7.8.0":
|
||||||
version "7.17.9"
|
version "7.17.9"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.9.tgz#6bae81a06d95f4d0dec5bb9d74bbc1f58babdcfe"
|
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.9.tgz#6bae81a06d95f4d0dec5bb9d74bbc1f58babdcfe"
|
||||||
integrity sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw==
|
integrity sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw==
|
||||||
|
@ -626,7 +626,7 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-plugin-utils" "^7.8.0"
|
"@babel/helper-plugin-utils" "^7.8.0"
|
||||||
|
|
||||||
"@babel/plugin-syntax-jsx@^7.12.13":
|
"@babel/plugin-syntax-jsx@^7.12.13", "@babel/plugin-syntax-jsx@^7.16.7":
|
||||||
version "7.16.7"
|
version "7.16.7"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz#50b6571d13f764266a113d77c82b4a6508bbe665"
|
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz#50b6571d13f764266a113d77c82b4a6508bbe665"
|
||||||
integrity sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==
|
integrity sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==
|
||||||
|
@ -891,6 +891,38 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-plugin-utils" "^7.16.7"
|
"@babel/helper-plugin-utils" "^7.16.7"
|
||||||
|
|
||||||
|
"@babel/plugin-transform-react-jsx-development@^7.16.7":
|
||||||
|
version "7.16.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz#43a00724a3ed2557ed3f276a01a929e6686ac7b8"
|
||||||
|
integrity sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A==
|
||||||
|
dependencies:
|
||||||
|
"@babel/plugin-transform-react-jsx" "^7.16.7"
|
||||||
|
|
||||||
|
"@babel/plugin-transform-react-jsx-self@^7.16.7":
|
||||||
|
version "7.16.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.16.7.tgz#f432ad0cba14c4a1faf44f0076c69e42a4d4479e"
|
||||||
|
integrity sha512-oe5VuWs7J9ilH3BCCApGoYjHoSO48vkjX2CbA5bFVhIuO2HKxA3vyF7rleA4o6/4rTDbk6r8hBW7Ul8E+UZrpA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-plugin-utils" "^7.16.7"
|
||||||
|
|
||||||
|
"@babel/plugin-transform-react-jsx-source@^7.16.7":
|
||||||
|
version "7.16.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.16.7.tgz#1879c3f23629d287cc6186a6c683154509ec70c0"
|
||||||
|
integrity sha512-rONFiQz9vgbsnaMtQlZCjIRwhJvlrPET8TabIUK2hzlXw9B9s2Ieaxte1SCOOXMbWRHodbKixNf3BLcWVOQ8Bw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-plugin-utils" "^7.16.7"
|
||||||
|
|
||||||
|
"@babel/plugin-transform-react-jsx@^7.16.7", "@babel/plugin-transform-react-jsx@^7.17.3":
|
||||||
|
version "7.17.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz#eac1565da176ccb1a715dae0b4609858808008c1"
|
||||||
|
integrity sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-annotate-as-pure" "^7.16.7"
|
||||||
|
"@babel/helper-module-imports" "^7.16.7"
|
||||||
|
"@babel/helper-plugin-utils" "^7.16.7"
|
||||||
|
"@babel/plugin-syntax-jsx" "^7.16.7"
|
||||||
|
"@babel/types" "^7.17.0"
|
||||||
|
|
||||||
"@babel/plugin-transform-regenerator@^7.16.7":
|
"@babel/plugin-transform-regenerator@^7.16.7":
|
||||||
version "7.17.9"
|
version "7.17.9"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.17.9.tgz#0a33c3a61cf47f45ed3232903683a0afd2d3460c"
|
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.17.9.tgz#0a33c3a61cf47f45ed3232903683a0afd2d3460c"
|
||||||
|
@ -1337,7 +1369,7 @@
|
||||||
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46"
|
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46"
|
||||||
integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
|
integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
|
||||||
|
|
||||||
"@eslint/eslintrc@^1.2.0", "@eslint/eslintrc@^1.2.1":
|
"@eslint/eslintrc@^1.2.1":
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.1.tgz#8b5e1c49f4077235516bc9ec7d41378c0f69b8c6"
|
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.1.tgz#8b5e1c49f4077235516bc9ec7d41378c0f69b8c6"
|
||||||
integrity sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==
|
integrity sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==
|
||||||
|
@ -1619,7 +1651,7 @@
|
||||||
resolved "https://registry.yarnpkg.com/@glidejs/glide/-/glide-3.5.2.tgz#7012c5920ecf202bbda44d8526fc979984b6dd54"
|
resolved "https://registry.yarnpkg.com/@glidejs/glide/-/glide-3.5.2.tgz#7012c5920ecf202bbda44d8526fc979984b6dd54"
|
||||||
integrity sha512-7jGciNJ2bQ4eZLSNlSZ+VAyW63kALf420CvkEpK4lEsUfWJq9odqimci0YCiyNyMUFB+pWHwLYyNc57dijYsCg==
|
integrity sha512-7jGciNJ2bQ4eZLSNlSZ+VAyW63kALf420CvkEpK4lEsUfWJq9odqimci0YCiyNyMUFB+pWHwLYyNc57dijYsCg==
|
||||||
|
|
||||||
"@headlessui/react@^1.4.1", "@headlessui/react@^1.5.0":
|
"@headlessui/react@^1.4.1":
|
||||||
version "1.5.0"
|
version "1.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.5.0.tgz#483b44ba2c8b8d4391e1d2c863898d7dd0cc0296"
|
resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.5.0.tgz#483b44ba2c8b8d4391e1d2c863898d7dd0cc0296"
|
||||||
integrity sha512-aaRnYxBb3MU2FNJf3Ut9RMTUqqU3as0aI1lQhgo2n9Fa67wRu14iOGqx93xB+uMNVfNwZ5B3y/Ndm7qZGuFeMQ==
|
integrity sha512-aaRnYxBb3MU2FNJf3Ut9RMTUqqU3as0aI1lQhgo2n9Fa67wRu14iOGqx93xB+uMNVfNwZ5B3y/Ndm7qZGuFeMQ==
|
||||||
|
@ -2434,11 +2466,6 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
webpack-bundle-analyzer "4.3.0"
|
webpack-bundle-analyzer "4.3.0"
|
||||||
|
|
||||||
"@next/env@12.1.0":
|
|
||||||
version "12.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@next/env/-/env-12.1.0.tgz#73713399399b34aa5a01771fb73272b55b22c314"
|
|
||||||
integrity sha512-nrIgY6t17FQ9xxwH3jj0a6EOiQ/WDHUos35Hghtr+SWN/ntHIQ7UpuvSi0vaLzZVHQWaDupKI+liO5vANcDeTQ==
|
|
||||||
|
|
||||||
"@next/env@12.1.4":
|
"@next/env@12.1.4":
|
||||||
version "12.1.4"
|
version "12.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@next/env/-/env-12.1.4.tgz#5af629b43075281ecd7f87938802b7cf5b67e94b"
|
resolved "https://registry.yarnpkg.com/@next/env/-/env-12.1.4.tgz#5af629b43075281ecd7f87938802b7cf5b67e94b"
|
||||||
|
@ -2466,11 +2493,6 @@
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.1.5.tgz#36729ab3dfd7743e82cfe536b43254dcb146620c"
|
resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.1.5.tgz#36729ab3dfd7743e82cfe536b43254dcb146620c"
|
||||||
integrity sha512-SKnGTdYcoN04Y2DvE0/Y7/MjkA+ltsmbuH/y/hR7Ob7tsj+8ZdOYuk+YvW1B8dY20nDPHP58XgDTSm2nA8BzzA==
|
integrity sha512-SKnGTdYcoN04Y2DvE0/Y7/MjkA+ltsmbuH/y/hR7Ob7tsj+8ZdOYuk+YvW1B8dY20nDPHP58XgDTSm2nA8BzzA==
|
||||||
|
|
||||||
"@next/swc-android-arm64@12.1.0":
|
|
||||||
version "12.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.1.0.tgz#865ba3a9afc204ff2bdeea49dd64d58705007a39"
|
|
||||||
integrity sha512-/280MLdZe0W03stA69iL+v6I+J1ascrQ6FrXBlXGCsGzrfMaGr7fskMa0T5AhQIVQD4nA/46QQWxG//DYuFBcA==
|
|
||||||
|
|
||||||
"@next/swc-android-arm64@12.1.4":
|
"@next/swc-android-arm64@12.1.4":
|
||||||
version "12.1.4"
|
version "12.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.1.4.tgz#f320d60639e19ecffa1f9034829f2d95502a9a51"
|
resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.1.4.tgz#f320d60639e19ecffa1f9034829f2d95502a9a51"
|
||||||
|
@ -2481,11 +2503,6 @@
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.1.5.tgz#52578f552305c92d0b9b81d603c9643fb71e0835"
|
resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.1.5.tgz#52578f552305c92d0b9b81d603c9643fb71e0835"
|
||||||
integrity sha512-YXiqgQ/9Rxg1dXp6brXbeQM1JDx9SwUY/36JiE+36FXqYEmDYbxld9qkX6GEzkc5rbwJ+RCitargnzEtwGW0mw==
|
integrity sha512-YXiqgQ/9Rxg1dXp6brXbeQM1JDx9SwUY/36JiE+36FXqYEmDYbxld9qkX6GEzkc5rbwJ+RCitargnzEtwGW0mw==
|
||||||
|
|
||||||
"@next/swc-darwin-arm64@12.1.0":
|
|
||||||
version "12.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.0.tgz#08e8b411b8accd095009ed12efbc2f1d4d547135"
|
|
||||||
integrity sha512-R8vcXE2/iONJ1Unf5Ptqjk6LRW3bggH+8drNkkzH4FLEQkHtELhvcmJwkXcuipyQCsIakldAXhRbZmm3YN1vXg==
|
|
||||||
|
|
||||||
"@next/swc-darwin-arm64@12.1.4":
|
"@next/swc-darwin-arm64@12.1.4":
|
||||||
version "12.1.4"
|
version "12.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.4.tgz#fd578278312613eddcf3aee26910100509941b63"
|
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.4.tgz#fd578278312613eddcf3aee26910100509941b63"
|
||||||
|
@ -2496,11 +2513,6 @@
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.5.tgz#3d5b53211484c72074f4975ba0ec2b1107db300e"
|
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.5.tgz#3d5b53211484c72074f4975ba0ec2b1107db300e"
|
||||||
integrity sha512-y8mhldb/WFZ6lFeowkGfi0cO/lBdiBqDk4T4LZLvCpoQp4Or/NzUN6P5NzBQZ5/b4oUHM/wQICEM+1wKA4qIVw==
|
integrity sha512-y8mhldb/WFZ6lFeowkGfi0cO/lBdiBqDk4T4LZLvCpoQp4Or/NzUN6P5NzBQZ5/b4oUHM/wQICEM+1wKA4qIVw==
|
||||||
|
|
||||||
"@next/swc-darwin-x64@12.1.0":
|
|
||||||
version "12.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.0.tgz#fcd684497a76e8feaca88db3c394480ff0b007cd"
|
|
||||||
integrity sha512-ieAz0/J0PhmbZBB8+EA/JGdhRHBogF8BWaeqR7hwveb6SYEIJaDNQy0I+ZN8gF8hLj63bEDxJAs/cEhdnTq+ug==
|
|
||||||
|
|
||||||
"@next/swc-darwin-x64@12.1.4":
|
"@next/swc-darwin-x64@12.1.4":
|
||||||
version "12.1.4"
|
version "12.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.4.tgz#ace5f80d8c8348efe194f6d7074c6213c52b3944"
|
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.4.tgz#ace5f80d8c8348efe194f6d7074c6213c52b3944"
|
||||||
|
@ -2511,11 +2523,6 @@
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.5.tgz#adcabb732d226453777c0d37d58eaff9328b66fd"
|
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.5.tgz#adcabb732d226453777c0d37d58eaff9328b66fd"
|
||||||
integrity sha512-wqJ3X7WQdTwSGi0kIDEmzw34QHISRIQ5uvC+VXmsIlCPFcMA+zM5723uh8NfuKGquDMiEMS31a83QgkuHMYbwQ==
|
integrity sha512-wqJ3X7WQdTwSGi0kIDEmzw34QHISRIQ5uvC+VXmsIlCPFcMA+zM5723uh8NfuKGquDMiEMS31a83QgkuHMYbwQ==
|
||||||
|
|
||||||
"@next/swc-linux-arm-gnueabihf@12.1.0":
|
|
||||||
version "12.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.0.tgz#9ec6380a27938a5799aaa6035c205b3c478468a7"
|
|
||||||
integrity sha512-njUd9hpl6o6A5d08dC0cKAgXKCzm5fFtgGe6i0eko8IAdtAPbtHxtpre3VeSxdZvuGFh+hb0REySQP9T1ttkog==
|
|
||||||
|
|
||||||
"@next/swc-linux-arm-gnueabihf@12.1.4":
|
"@next/swc-linux-arm-gnueabihf@12.1.4":
|
||||||
version "12.1.4"
|
version "12.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.4.tgz#2bf2c83863635f19c71c226a2df936e001cce29c"
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.4.tgz#2bf2c83863635f19c71c226a2df936e001cce29c"
|
||||||
|
@ -2526,11 +2533,6 @@
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.5.tgz#82a7cde67482b756bc65fbebf1dfa8a782074e93"
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.5.tgz#82a7cde67482b756bc65fbebf1dfa8a782074e93"
|
||||||
integrity sha512-WnhdM5duONMvt2CncAl+9pim0wBxDS2lHoo7ub/o/i1bRbs11UTzosKzEXVaTDCUkCX2c32lIDi1WcN2ZPkcdw==
|
integrity sha512-WnhdM5duONMvt2CncAl+9pim0wBxDS2lHoo7ub/o/i1bRbs11UTzosKzEXVaTDCUkCX2c32lIDi1WcN2ZPkcdw==
|
||||||
|
|
||||||
"@next/swc-linux-arm64-gnu@12.1.0":
|
|
||||||
version "12.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.0.tgz#7f4196dff1049cea479607c75b81033ae2dbd093"
|
|
||||||
integrity sha512-OqangJLkRxVxMhDtcb7Qn1xjzFA3s50EIxY7mljbSCLybU+sByPaWAHY4px97ieOlr2y4S0xdPKkQ3BCAwyo6Q==
|
|
||||||
|
|
||||||
"@next/swc-linux-arm64-gnu@12.1.4":
|
"@next/swc-linux-arm64-gnu@12.1.4":
|
||||||
version "12.1.4"
|
version "12.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.4.tgz#d577190f641c9b4b463719dd6b8953b6ba9be8d9"
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.4.tgz#d577190f641c9b4b463719dd6b8953b6ba9be8d9"
|
||||||
|
@ -2541,11 +2543,6 @@
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.5.tgz#f82ca014504950aab751e81f467492e9be0bad5d"
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.5.tgz#f82ca014504950aab751e81f467492e9be0bad5d"
|
||||||
integrity sha512-Jq2H68yQ4bLUhR/XQnbw3LDW0GMQn355qx6rU36BthDLeGue7YV7MqNPa8GKvrpPocEMW77nWx/1yI6w6J07gw==
|
integrity sha512-Jq2H68yQ4bLUhR/XQnbw3LDW0GMQn355qx6rU36BthDLeGue7YV7MqNPa8GKvrpPocEMW77nWx/1yI6w6J07gw==
|
||||||
|
|
||||||
"@next/swc-linux-arm64-musl@12.1.0":
|
|
||||||
version "12.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.0.tgz#b445f767569cdc2dddee785ca495e1a88c025566"
|
|
||||||
integrity sha512-hB8cLSt4GdmOpcwRe2UzI5UWn6HHO/vLkr5OTuNvCJ5xGDwpPXelVkYW/0+C3g5axbDW2Tym4S+MQCkkH9QfWA==
|
|
||||||
|
|
||||||
"@next/swc-linux-arm64-musl@12.1.4":
|
"@next/swc-linux-arm64-musl@12.1.4":
|
||||||
version "12.1.4"
|
version "12.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.4.tgz#e70ffe70393d8f9242deecdb282ce5a8fd588b14"
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.4.tgz#e70ffe70393d8f9242deecdb282ce5a8fd588b14"
|
||||||
|
@ -2556,11 +2553,6 @@
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.5.tgz#f811ec9f4b12a978426c284c95ab2f515ddf7f9e"
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.5.tgz#f811ec9f4b12a978426c284c95ab2f515ddf7f9e"
|
||||||
integrity sha512-KgPjwdbhDqXI7ghNN8V/WAiLquc9Ebe8KBrNNEL0NQr+yd9CyKJ6KqjayVkmX+hbHzbyvbui/5wh/p3CZQ9xcQ==
|
integrity sha512-KgPjwdbhDqXI7ghNN8V/WAiLquc9Ebe8KBrNNEL0NQr+yd9CyKJ6KqjayVkmX+hbHzbyvbui/5wh/p3CZQ9xcQ==
|
||||||
|
|
||||||
"@next/swc-linux-x64-gnu@12.1.0":
|
|
||||||
version "12.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.0.tgz#67610e9be4fbc987de7535f1bcb17e45fe12f90e"
|
|
||||||
integrity sha512-OKO4R/digvrVuweSw/uBM4nSdyzsBV5EwkUeeG4KVpkIZEe64ZwRpnFB65bC6hGwxIBnTv5NMSnJ+0K/WmG78A==
|
|
||||||
|
|
||||||
"@next/swc-linux-x64-gnu@12.1.4":
|
"@next/swc-linux-x64-gnu@12.1.4":
|
||||||
version "12.1.4"
|
version "12.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.4.tgz#91498a130387fb1961902f2bee55863f8e910cff"
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.4.tgz#91498a130387fb1961902f2bee55863f8e910cff"
|
||||||
|
@ -2571,11 +2563,6 @@
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.5.tgz#d44857257e6d20dc841998951d584ab1f25772c3"
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.5.tgz#d44857257e6d20dc841998951d584ab1f25772c3"
|
||||||
integrity sha512-O2ErUTvCJ6DkNTSr9pbu1n3tcqykqE/ebty1rwClzIYdOgpB3T2MfEPP+K7GhUR87wmN/hlihO9ch7qpVFDGKw==
|
integrity sha512-O2ErUTvCJ6DkNTSr9pbu1n3tcqykqE/ebty1rwClzIYdOgpB3T2MfEPP+K7GhUR87wmN/hlihO9ch7qpVFDGKw==
|
||||||
|
|
||||||
"@next/swc-linux-x64-musl@12.1.0":
|
|
||||||
version "12.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.0.tgz#ea19a23db08a9f2e34ac30401f774cf7d1669d31"
|
|
||||||
integrity sha512-JohhgAHZvOD3rQY7tlp7NlmvtvYHBYgY0x5ZCecUT6eCCcl9lv6iV3nfu82ErkxNk1H893fqH0FUpznZ/H3pSw==
|
|
||||||
|
|
||||||
"@next/swc-linux-x64-musl@12.1.4":
|
"@next/swc-linux-x64-musl@12.1.4":
|
||||||
version "12.1.4"
|
version "12.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.4.tgz#78057b03c148c121553d41521ad38f6c732762ff"
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.4.tgz#78057b03c148c121553d41521ad38f6c732762ff"
|
||||||
|
@ -2586,11 +2573,6 @@
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.5.tgz#3cc523abadc9a2a6de680593aff06e71cc29ecef"
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.5.tgz#3cc523abadc9a2a6de680593aff06e71cc29ecef"
|
||||||
integrity sha512-1eIlZmlO/VRjxxzUBcVosf54AFU3ltAzHi+BJA+9U/lPxCYIsT+R4uO3QksRzRjKWhVQMRjEnlXyyq5SKJm7BA==
|
integrity sha512-1eIlZmlO/VRjxxzUBcVosf54AFU3ltAzHi+BJA+9U/lPxCYIsT+R4uO3QksRzRjKWhVQMRjEnlXyyq5SKJm7BA==
|
||||||
|
|
||||||
"@next/swc-win32-arm64-msvc@12.1.0":
|
|
||||||
version "12.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.0.tgz#eadf054fc412085659b98e145435bbba200b5283"
|
|
||||||
integrity sha512-T/3gIE6QEfKIJ4dmJk75v9hhNiYZhQYAoYm4iVo1TgcsuaKLFa+zMPh4056AHiG6n9tn2UQ1CFE8EoybEsqsSw==
|
|
||||||
|
|
||||||
"@next/swc-win32-arm64-msvc@12.1.4":
|
"@next/swc-win32-arm64-msvc@12.1.4":
|
||||||
version "12.1.4"
|
version "12.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.4.tgz#05bbaabacac23b8edf6caa99eb86b17550a09051"
|
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.4.tgz#05bbaabacac23b8edf6caa99eb86b17550a09051"
|
||||||
|
@ -2601,11 +2583,6 @@
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.5.tgz#c62232d869f1f9b22e8f24e4e7f05307c20f30ca"
|
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.5.tgz#c62232d869f1f9b22e8f24e4e7f05307c20f30ca"
|
||||||
integrity sha512-oromsfokbEuVb0CBLLE7R9qX3KGXucZpsojLpzUh1QJjuy1QkrPJncwr8xmWQnwgtQ6ecMWXgXPB+qtvizT9Tw==
|
integrity sha512-oromsfokbEuVb0CBLLE7R9qX3KGXucZpsojLpzUh1QJjuy1QkrPJncwr8xmWQnwgtQ6ecMWXgXPB+qtvizT9Tw==
|
||||||
|
|
||||||
"@next/swc-win32-ia32-msvc@12.1.0":
|
|
||||||
version "12.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.0.tgz#68faeae10c89f698bf9d28759172b74c9c21bda1"
|
|
||||||
integrity sha512-iwnKgHJdqhIW19H9PRPM9j55V6RdcOo6rX+5imx832BCWzkDbyomWnlzBfr6ByUYfhohb8QuH4hSGEikpPqI0Q==
|
|
||||||
|
|
||||||
"@next/swc-win32-ia32-msvc@12.1.4":
|
"@next/swc-win32-ia32-msvc@12.1.4":
|
||||||
version "12.1.4"
|
version "12.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.4.tgz#8fd2fb48f04a2802e51fc320878bf6b411c1c866"
|
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.4.tgz#8fd2fb48f04a2802e51fc320878bf6b411c1c866"
|
||||||
|
@ -2616,11 +2593,6 @@
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.5.tgz#2bd9b28a9ba730d12a493e7d9d18e150fe89d496"
|
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.5.tgz#2bd9b28a9ba730d12a493e7d9d18e150fe89d496"
|
||||||
integrity sha512-a/51L5KzBpeZSW9LbekMo3I3Cwul+V+QKwbEIMA+Qwb2qrlcn1L9h3lt8cHqNTFt2y72ce6aTwDTw1lyi5oIRA==
|
integrity sha512-a/51L5KzBpeZSW9LbekMo3I3Cwul+V+QKwbEIMA+Qwb2qrlcn1L9h3lt8cHqNTFt2y72ce6aTwDTw1lyi5oIRA==
|
||||||
|
|
||||||
"@next/swc-win32-x64-msvc@12.1.0":
|
|
||||||
version "12.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.0.tgz#d27e7e76c87a460a4da99c5bfdb1618dcd6cd064"
|
|
||||||
integrity sha512-aBvcbMwuanDH4EMrL2TthNJy+4nP59Bimn8egqv6GHMVj0a44cU6Au4PjOhLNqEh9l+IpRGBqMTzec94UdC5xg==
|
|
||||||
|
|
||||||
"@next/swc-win32-x64-msvc@12.1.4":
|
"@next/swc-win32-x64-msvc@12.1.4":
|
||||||
version "12.1.4"
|
version "12.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.4.tgz#a72ed44c9b1f850986a30fe36c59e01f8a79b5f3"
|
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.4.tgz#a72ed44c9b1f850986a30fe36c59e01f8a79b5f3"
|
||||||
|
@ -3385,6 +3357,14 @@
|
||||||
estree-walker "^1.0.1"
|
estree-walker "^1.0.1"
|
||||||
picomatch "^2.2.2"
|
picomatch "^2.2.2"
|
||||||
|
|
||||||
|
"@rollup/pluginutils@^4.2.0":
|
||||||
|
version "4.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d"
|
||||||
|
integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==
|
||||||
|
dependencies:
|
||||||
|
estree-walker "^2.0.1"
|
||||||
|
picomatch "^2.2.2"
|
||||||
|
|
||||||
"@rushstack/eslint-patch@1.0.8":
|
"@rushstack/eslint-patch@1.0.8":
|
||||||
version "1.0.8"
|
version "1.0.8"
|
||||||
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.0.8.tgz#be3e914e84eacf16dbebd311c0d0b44aa1174c64"
|
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.0.8.tgz#be3e914e84eacf16dbebd311c0d0b44aa1174c64"
|
||||||
|
@ -4017,11 +3997,6 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.9.1.tgz#0611b37db4246c937feef529ddcc018cf8e35708"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.9.1.tgz#0611b37db4246c937feef529ddcc018cf8e35708"
|
||||||
integrity sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==
|
integrity sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==
|
||||||
|
|
||||||
"@types/node@17.0.21":
|
|
||||||
version "17.0.21"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.21.tgz#864b987c0c68d07b4345845c3e63b75edd143644"
|
|
||||||
integrity sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==
|
|
||||||
|
|
||||||
"@types/node@^12.12.6":
|
"@types/node@^12.12.6":
|
||||||
version "12.20.47"
|
version "12.20.47"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.47.tgz#ca9237d51f2a2557419688511dab1c8daf475188"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.47.tgz#ca9237d51f2a2557419688511dab1c8daf475188"
|
||||||
|
@ -4087,6 +4062,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
|
|
||||||
|
"@types/react-dom@^17.0.0":
|
||||||
|
version "17.0.15"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.15.tgz#f2c8efde11521a4b7991e076cb9c70ba3bb0d156"
|
||||||
|
integrity sha512-Tr9VU9DvNoHDWlmecmcsE5ZZiUkYx+nKBzum4Oxe1K0yJVyBlfbq7H3eXjxXqJczBKqPGq3EgfTru4MgKb9+Yw==
|
||||||
|
dependencies:
|
||||||
|
"@types/react" "^17"
|
||||||
|
|
||||||
"@types/react-phone-number-input@^3.0.13":
|
"@types/react-phone-number-input@^3.0.13":
|
||||||
version "3.0.13"
|
version "3.0.13"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react-phone-number-input/-/react-phone-number-input-3.0.13.tgz#4eb7dcd278dcf9eb2a8d2ce2cb304657cbf1b4e5"
|
resolved "https://registry.yarnpkg.com/@types/react-phone-number-input/-/react-phone-number-input-3.0.13.tgz#4eb7dcd278dcf9eb2a8d2ce2cb304657cbf1b4e5"
|
||||||
|
@ -4143,10 +4125,10 @@
|
||||||
"@types/scheduler" "*"
|
"@types/scheduler" "*"
|
||||||
csstype "^3.0.2"
|
csstype "^3.0.2"
|
||||||
|
|
||||||
"@types/react@17.0.40":
|
"@types/react@^17", "@types/react@^17.0.0":
|
||||||
version "17.0.40"
|
version "17.0.44"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.40.tgz#dc010cee6254d5239a138083f3799a16638e6bad"
|
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.44.tgz#c3714bd34dd551ab20b8015d9d0dbec812a51ec7"
|
||||||
integrity sha512-UrXhD/JyLH+W70nNSufXqMZNuUD2cXHu6UjCllC6pmOQgBX4SGXOH8fjRka0O0Ee0HrFxapDD8Bwn81Kmiz6jQ==
|
integrity sha512-Ye0nlw09GeMp2Suh8qoOv0odfgCoowfM/9MG6WeRD60Gq9wS90bdkdRtYbRkNhXOpG4H+YXGvj4wOWhAC0LJ1g==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/prop-types" "*"
|
"@types/prop-types" "*"
|
||||||
"@types/scheduler" "*"
|
"@types/scheduler" "*"
|
||||||
|
@ -4383,6 +4365,20 @@
|
||||||
clsx "^1.1.1"
|
clsx "^1.1.1"
|
||||||
next-transpile-modules "^8.0.0"
|
next-transpile-modules "^8.0.0"
|
||||||
|
|
||||||
|
"@vitejs/plugin-react@^1.3.0":
|
||||||
|
version "1.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-1.3.1.tgz#bf008adf33e713215cd4a6b94a75146dd6891975"
|
||||||
|
integrity sha512-qQS8Y2fZCjo5YmDUplEXl3yn+aueiwxB7BaoQ4nWYJYR+Ai8NXPVLlkLobVMs5+DeyFyg9Lrz6zCzdX1opcvyw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/core" "^7.17.9"
|
||||||
|
"@babel/plugin-transform-react-jsx" "^7.17.3"
|
||||||
|
"@babel/plugin-transform-react-jsx-development" "^7.16.7"
|
||||||
|
"@babel/plugin-transform-react-jsx-self" "^7.16.7"
|
||||||
|
"@babel/plugin-transform-react-jsx-source" "^7.16.7"
|
||||||
|
"@rollup/pluginutils" "^4.2.0"
|
||||||
|
react-refresh "^0.12.0"
|
||||||
|
resolve "^1.22.0"
|
||||||
|
|
||||||
"@wojtekmaj/date-utils@^1.0.2", "@wojtekmaj/date-utils@^1.0.3":
|
"@wojtekmaj/date-utils@^1.0.2", "@wojtekmaj/date-utils@^1.0.3":
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@wojtekmaj/date-utils/-/date-utils-1.0.3.tgz#2dcfd92881425c5923e429c2aec86fb3609032a1"
|
resolved "https://registry.yarnpkg.com/@wojtekmaj/date-utils/-/date-utils-1.0.3.tgz#2dcfd92881425c5923e429c2aec86fb3609032a1"
|
||||||
|
@ -4798,7 +4794,7 @@ autolinker@^3.11.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib "^2.3.0"
|
tslib "^2.3.0"
|
||||||
|
|
||||||
autoprefixer@^10.3.4, autoprefixer@^10.4.0, autoprefixer@^10.4.2:
|
autoprefixer@^10.3.4, autoprefixer@^10.4.0, autoprefixer@^10.4.4:
|
||||||
version "10.4.4"
|
version "10.4.4"
|
||||||
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.4.tgz#3e85a245b32da876a893d3ac2ea19f01e7ea5a1e"
|
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.4.tgz#3e85a245b32da876a893d3ac2ea19f01e7ea5a1e"
|
||||||
integrity sha512-Tm8JxsB286VweiZ5F0anmbyGiNI3v3wGv3mz9W+cxEDYB/6jbnj6GM9H9mK3wIL8ftgl+C07Lcwb8PG5PCCPzA==
|
integrity sha512-Tm8JxsB286VweiZ5F0anmbyGiNI3v3wGv3mz9W+cxEDYB/6jbnj6GM9H9mK3wIL8ftgl+C07Lcwb8PG5PCCPzA==
|
||||||
|
@ -5627,11 +5623,6 @@ chardet@^0.7.0:
|
||||||
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
||||||
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
|
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
|
||||||
|
|
||||||
chart.js@^3.7.1:
|
|
||||||
version "3.7.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-3.7.1.tgz#0516f690c6a8680c6c707e31a4c1807a6f400ada"
|
|
||||||
integrity sha512-8knRegQLFnPQAheZV8MjxIXc5gQEfDFD897BJgv/klO/vtIyFFmgMXrNfgrXpbTr/XbTturxRgxIXx/Y+ASJBA==
|
|
||||||
|
|
||||||
chokidar@^3.5.3:
|
chokidar@^3.5.3:
|
||||||
version "3.5.3"
|
version "3.5.3"
|
||||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
|
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
|
||||||
|
@ -7175,47 +7166,6 @@ eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0:
|
||||||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
|
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
|
||||||
integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
|
integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
|
||||||
|
|
||||||
eslint@8.10.0:
|
|
||||||
version "8.10.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.10.0.tgz#931be395eb60f900c01658b278e05b6dae47199d"
|
|
||||||
integrity sha512-tcI1D9lfVec+R4LE1mNDnzoJ/f71Kl/9Cv4nG47jOueCMBrCCKYXr4AUVS7go6mWYGFD4+EoN6+eXSrEbRzXVw==
|
|
||||||
dependencies:
|
|
||||||
"@eslint/eslintrc" "^1.2.0"
|
|
||||||
"@humanwhocodes/config-array" "^0.9.2"
|
|
||||||
ajv "^6.10.0"
|
|
||||||
chalk "^4.0.0"
|
|
||||||
cross-spawn "^7.0.2"
|
|
||||||
debug "^4.3.2"
|
|
||||||
doctrine "^3.0.0"
|
|
||||||
escape-string-regexp "^4.0.0"
|
|
||||||
eslint-scope "^7.1.1"
|
|
||||||
eslint-utils "^3.0.0"
|
|
||||||
eslint-visitor-keys "^3.3.0"
|
|
||||||
espree "^9.3.1"
|
|
||||||
esquery "^1.4.0"
|
|
||||||
esutils "^2.0.2"
|
|
||||||
fast-deep-equal "^3.1.3"
|
|
||||||
file-entry-cache "^6.0.1"
|
|
||||||
functional-red-black-tree "^1.0.1"
|
|
||||||
glob-parent "^6.0.1"
|
|
||||||
globals "^13.6.0"
|
|
||||||
ignore "^5.2.0"
|
|
||||||
import-fresh "^3.0.0"
|
|
||||||
imurmurhash "^0.1.4"
|
|
||||||
is-glob "^4.0.0"
|
|
||||||
js-yaml "^4.1.0"
|
|
||||||
json-stable-stringify-without-jsonify "^1.0.1"
|
|
||||||
levn "^0.4.1"
|
|
||||||
lodash.merge "^4.6.2"
|
|
||||||
minimatch "^3.0.4"
|
|
||||||
natural-compare "^1.4.0"
|
|
||||||
optionator "^0.9.1"
|
|
||||||
regexpp "^3.2.0"
|
|
||||||
strip-ansi "^6.0.1"
|
|
||||||
strip-json-comments "^3.1.0"
|
|
||||||
text-table "^0.2.0"
|
|
||||||
v8-compile-cache "^2.0.3"
|
|
||||||
|
|
||||||
eslint@^8.10.0, eslint@^8.11.0:
|
eslint@^8.10.0, eslint@^8.11.0:
|
||||||
version "8.11.0"
|
version "8.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.11.0.tgz#88b91cfba1356fc10bb9eb592958457dfe09fb37"
|
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.11.0.tgz#88b91cfba1356fc10bb9eb592958457dfe09fb37"
|
||||||
|
@ -12312,29 +12262,6 @@ next-validations@^0.1.11:
|
||||||
resolved "https://registry.yarnpkg.com/next-validations/-/next-validations-0.1.11.tgz#fcc62dea5be8f9793d410de175f96e3fc1dac54d"
|
resolved "https://registry.yarnpkg.com/next-validations/-/next-validations-0.1.11.tgz#fcc62dea5be8f9793d410de175f96e3fc1dac54d"
|
||||||
integrity sha512-rdyRgZ3f3jwhLigdi9MC5R74BvRpB3cewa8LVnMHDiDRnSThvX0CdZ5KHK4t/SgrIGaVXiXOQ59KtvBqjcm5pA==
|
integrity sha512-rdyRgZ3f3jwhLigdi9MC5R74BvRpB3cewa8LVnMHDiDRnSThvX0CdZ5KHK4t/SgrIGaVXiXOQ59KtvBqjcm5pA==
|
||||||
|
|
||||||
next@12.1.0:
|
|
||||||
version "12.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/next/-/next-12.1.0.tgz#c33d753b644be92fc58e06e5a214f143da61dd5d"
|
|
||||||
integrity sha512-s885kWvnIlxsUFHq9UGyIyLiuD0G3BUC/xrH0CEnH5lHEWkwQcHOORgbDF0hbrW9vr/7am4ETfX4A7M6DjrE7Q==
|
|
||||||
dependencies:
|
|
||||||
"@next/env" "12.1.0"
|
|
||||||
caniuse-lite "^1.0.30001283"
|
|
||||||
postcss "8.4.5"
|
|
||||||
styled-jsx "5.0.0"
|
|
||||||
use-subscription "1.5.1"
|
|
||||||
optionalDependencies:
|
|
||||||
"@next/swc-android-arm64" "12.1.0"
|
|
||||||
"@next/swc-darwin-arm64" "12.1.0"
|
|
||||||
"@next/swc-darwin-x64" "12.1.0"
|
|
||||||
"@next/swc-linux-arm-gnueabihf" "12.1.0"
|
|
||||||
"@next/swc-linux-arm64-gnu" "12.1.0"
|
|
||||||
"@next/swc-linux-arm64-musl" "12.1.0"
|
|
||||||
"@next/swc-linux-x64-gnu" "12.1.0"
|
|
||||||
"@next/swc-linux-x64-musl" "12.1.0"
|
|
||||||
"@next/swc-win32-arm64-msvc" "12.1.0"
|
|
||||||
"@next/swc-win32-ia32-msvc" "12.1.0"
|
|
||||||
"@next/swc-win32-x64-msvc" "12.1.0"
|
|
||||||
|
|
||||||
next@12.1.4, next@^12.1.0:
|
next@12.1.4, next@^12.1.0:
|
||||||
version "12.1.4"
|
version "12.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/next/-/next-12.1.4.tgz#597a9bdec7aec778b442c4f6d41afd2c64a54b23"
|
resolved "https://registry.yarnpkg.com/next/-/next-12.1.4.tgz#597a9bdec7aec778b442c4f6d41afd2c64a54b23"
|
||||||
|
@ -12622,6 +12549,11 @@ object-hash@^2.0.1, object-hash@^2.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5"
|
resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5"
|
||||||
integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==
|
integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==
|
||||||
|
|
||||||
|
object-hash@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9"
|
||||||
|
integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==
|
||||||
|
|
||||||
object-inspect@^1.12.0, object-inspect@^1.9.0:
|
object-inspect@^1.12.0, object-inspect@^1.9.0:
|
||||||
version "1.12.0"
|
version "1.12.0"
|
||||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0"
|
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0"
|
||||||
|
@ -13374,6 +13306,37 @@ playwright-core@1.20.2:
|
||||||
yauzl "2.10.0"
|
yauzl "2.10.0"
|
||||||
yazl "2.5.1"
|
yazl "2.5.1"
|
||||||
|
|
||||||
|
playwright-core@1.21.1:
|
||||||
|
version "1.21.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.21.1.tgz#2757be7921576f047c0a622194dc45f4e1962e17"
|
||||||
|
integrity sha512-SbK5dEsai9ZUKlxcinqegorBq4GnftXd4/GfW+pLsdQIQWrLCM/JNh6YQ2Rf2enVykXCejtoXW8L5vJXBBVSJQ==
|
||||||
|
dependencies:
|
||||||
|
colors "1.4.0"
|
||||||
|
commander "8.3.0"
|
||||||
|
debug "4.3.3"
|
||||||
|
extract-zip "2.0.1"
|
||||||
|
https-proxy-agent "5.0.0"
|
||||||
|
jpeg-js "0.4.3"
|
||||||
|
mime "3.0.0"
|
||||||
|
pixelmatch "5.2.1"
|
||||||
|
pngjs "6.0.0"
|
||||||
|
progress "2.0.3"
|
||||||
|
proper-lockfile "4.1.2"
|
||||||
|
proxy-from-env "1.1.0"
|
||||||
|
rimraf "3.0.2"
|
||||||
|
socks-proxy-agent "6.1.1"
|
||||||
|
stack-utils "2.0.5"
|
||||||
|
ws "8.4.2"
|
||||||
|
yauzl "2.10.0"
|
||||||
|
yazl "2.5.1"
|
||||||
|
|
||||||
|
playwright@^1.21.1:
|
||||||
|
version "1.21.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.21.1.tgz#62bdefc0e8baba192d93d8daf0c0eb9213869d76"
|
||||||
|
integrity sha512-Of0h1XAvsqK1XfHVZ8sL2PjJVoQUu9gTmmMTtLS7MEyWMRD0kn8myeI90xj1ncJhUysQxGboH64S5v+lL2USrg==
|
||||||
|
dependencies:
|
||||||
|
playwright-core "1.21.1"
|
||||||
|
|
||||||
pngjs@6.0.0:
|
pngjs@6.0.0:
|
||||||
version "6.0.0"
|
version "6.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-6.0.0.tgz#ca9e5d2aa48db0228a52c419c3308e87720da821"
|
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-6.0.0.tgz#ca9e5d2aa48db0228a52c419c3308e87720da821"
|
||||||
|
@ -13406,7 +13369,7 @@ postcss-js@^4.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
camelcase-css "^2.0.1"
|
camelcase-css "^2.0.1"
|
||||||
|
|
||||||
postcss-load-config@^3.1.0:
|
postcss-load-config@^3.1.0, postcss-load-config@^3.1.4:
|
||||||
version "3.1.4"
|
version "3.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855"
|
resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855"
|
||||||
integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==
|
integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==
|
||||||
|
@ -13421,7 +13384,7 @@ postcss-nested@5.0.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
postcss-selector-parser "^6.0.6"
|
postcss-selector-parser "^6.0.6"
|
||||||
|
|
||||||
postcss-selector-parser@^6.0.6, postcss-selector-parser@^6.0.9:
|
postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.6, postcss-selector-parser@^6.0.9:
|
||||||
version "6.0.10"
|
version "6.0.10"
|
||||||
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d"
|
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d"
|
||||||
integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==
|
integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==
|
||||||
|
@ -13443,7 +13406,7 @@ postcss@8.4.5:
|
||||||
picocolors "^1.0.0"
|
picocolors "^1.0.0"
|
||||||
source-map-js "^1.0.1"
|
source-map-js "^1.0.1"
|
||||||
|
|
||||||
postcss@^8.3.6, postcss@^8.4.12, postcss@^8.4.4, postcss@^8.4.6, postcss@^8.4.8:
|
postcss@^8.3.6, postcss@^8.4.12, postcss@^8.4.4, postcss@^8.4.6:
|
||||||
version "8.4.12"
|
version "8.4.12"
|
||||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.12.tgz#1e7de78733b28970fa4743f7da6f3763648b1905"
|
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.12.tgz#1e7de78733b28970fa4743f7da6f3763648b1905"
|
||||||
integrity sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==
|
integrity sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==
|
||||||
|
@ -13868,11 +13831,6 @@ react-calendar@^3.3.1:
|
||||||
merge-class-names "^1.1.1"
|
merge-class-names "^1.1.1"
|
||||||
prop-types "^15.6.0"
|
prop-types "^15.6.0"
|
||||||
|
|
||||||
react-chartjs-2@^4.0.1:
|
|
||||||
version "4.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-chartjs-2/-/react-chartjs-2-4.1.0.tgz#2a123df16d3a987c54eb4e810ed766d3c03adf8d"
|
|
||||||
integrity sha512-AsUihxEp8Jm1oBhbEovE+w50m9PVNhz1sfwEIT4hZduRC0m14gHWHd0cUaxkFDb8HNkdMIGzsNlmVqKiOpU74g==
|
|
||||||
|
|
||||||
react-colorful@^5.5.1:
|
react-colorful@^5.5.1:
|
||||||
version "5.5.1"
|
version "5.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.5.1.tgz#29d9c4e496f2ca784dd2bb5053a3a4340cfaf784"
|
resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.5.1.tgz#29d9c4e496f2ca784dd2bb5053a3a4340cfaf784"
|
||||||
|
@ -14068,6 +14026,11 @@ react-redux@^7.2.4:
|
||||||
prop-types "^15.7.2"
|
prop-types "^15.7.2"
|
||||||
react-is "^17.0.2"
|
react-is "^17.0.2"
|
||||||
|
|
||||||
|
react-refresh@^0.12.0:
|
||||||
|
version "0.12.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.12.0.tgz#28ac0a2c30ef2bb3433d5fd0621e69a6d774c3a4"
|
||||||
|
integrity sha512-suLIhrU2IHKL5JEKR/fAwJv7bbeq4kJ+pJopf77jHwuR+HmJS/HbrPIGsTBUVfw7tXPOmYv7UJ7PCaN49e8x4A==
|
||||||
|
|
||||||
react-remove-scroll-bar@^2.1.0:
|
react-remove-scroll-bar@^2.1.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.2.0.tgz#d4d545a7df024f75d67e151499a6ab5ac97c8cdd"
|
resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.2.0.tgz#d4d545a7df024f75d67e151499a6ab5ac97c8cdd"
|
||||||
|
@ -15543,11 +15506,6 @@ style-to-object@^0.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
inline-style-parser "0.1.1"
|
inline-style-parser "0.1.1"
|
||||||
|
|
||||||
styled-jsx@5.0.0:
|
|
||||||
version "5.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.0.tgz#816b4b92e07b1786c6b7111821750e0ba4d26e77"
|
|
||||||
integrity sha512-qUqsWoBquEdERe10EW8vLp3jT25s/ssG1/qX5gZ4wu15OZpmSMFI2v+fWlRhLfykA5rFtlJ1ME8A8pm/peV4WA==
|
|
||||||
|
|
||||||
styled-jsx@5.0.1:
|
styled-jsx@5.0.1:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.1.tgz#78fecbbad2bf95ce6cd981a08918ce4696f5fc80"
|
resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.1.tgz#78fecbbad2bf95ce6cd981a08918ce4696f5fc80"
|
||||||
|
@ -15712,11 +15670,6 @@ swarm-js@^0.1.40:
|
||||||
tar "^4.0.2"
|
tar "^4.0.2"
|
||||||
xhr-request "^1.0.1"
|
xhr-request "^1.0.1"
|
||||||
|
|
||||||
swr@^1.2.2:
|
|
||||||
version "1.3.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/swr/-/swr-1.3.0.tgz#c6531866a35b4db37b38b72c45a63171faf9f4e8"
|
|
||||||
integrity sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw==
|
|
||||||
|
|
||||||
symbol-tree@^3.2.4:
|
symbol-tree@^3.2.4:
|
||||||
version "3.2.4"
|
version "3.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
|
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
|
||||||
|
@ -15749,6 +15702,33 @@ tailwindcss@^3.0.23:
|
||||||
quick-lru "^5.1.1"
|
quick-lru "^5.1.1"
|
||||||
resolve "^1.22.0"
|
resolve "^1.22.0"
|
||||||
|
|
||||||
|
tailwindcss@^3.0.24:
|
||||||
|
version "3.0.24"
|
||||||
|
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.0.24.tgz#22e31e801a44a78a1d9a81ecc52e13b69d85704d"
|
||||||
|
integrity sha512-H3uMmZNWzG6aqmg9q07ZIRNIawoiEcNFKDfL+YzOPuPsXuDXxJxB9icqzLgdzKNwjG3SAro2h9SYav8ewXNgig==
|
||||||
|
dependencies:
|
||||||
|
arg "^5.0.1"
|
||||||
|
chokidar "^3.5.3"
|
||||||
|
color-name "^1.1.4"
|
||||||
|
detective "^5.2.0"
|
||||||
|
didyoumean "^1.2.2"
|
||||||
|
dlv "^1.1.3"
|
||||||
|
fast-glob "^3.2.11"
|
||||||
|
glob-parent "^6.0.2"
|
||||||
|
is-glob "^4.0.3"
|
||||||
|
lilconfig "^2.0.5"
|
||||||
|
normalize-path "^3.0.0"
|
||||||
|
object-hash "^3.0.0"
|
||||||
|
picocolors "^1.0.0"
|
||||||
|
postcss "^8.4.12"
|
||||||
|
postcss-js "^4.0.0"
|
||||||
|
postcss-load-config "^3.1.4"
|
||||||
|
postcss-nested "5.0.6"
|
||||||
|
postcss-selector-parser "^6.0.10"
|
||||||
|
postcss-value-parser "^4.2.0"
|
||||||
|
quick-lru "^5.1.1"
|
||||||
|
resolve "^1.22.0"
|
||||||
|
|
||||||
tapable@^2.2.0:
|
tapable@^2.2.0:
|
||||||
version "2.2.1"
|
version "2.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"
|
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"
|
||||||
|
@ -16074,6 +16054,11 @@ ts-node@^10.6.0:
|
||||||
v8-compile-cache-lib "^3.0.0"
|
v8-compile-cache-lib "^3.0.0"
|
||||||
yn "3.1.1"
|
yn "3.1.1"
|
||||||
|
|
||||||
|
tsc@^2.0.4:
|
||||||
|
version "2.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/tsc/-/tsc-2.0.4.tgz#5f6499146abea5dca4420b451fa4f2f9345238f5"
|
||||||
|
integrity sha512-fzoSieZI5KKJVBYGvwbVZs/J5za84f2lSTLPYf6AGiIf43tZ3GNrI1QzTLcjtyDDP4aLxd46RTZq1nQxe7+k5Q==
|
||||||
|
|
||||||
tsconfig-paths@^3.11.0, tsconfig-paths@^3.12.0, tsconfig-paths@^3.9.0:
|
tsconfig-paths@^3.11.0, tsconfig-paths@^3.12.0, tsconfig-paths@^3.9.0:
|
||||||
version "3.14.1"
|
version "3.14.1"
|
||||||
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a"
|
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a"
|
||||||
|
@ -16631,13 +16616,6 @@ use-sidecar@^1.0.1:
|
||||||
detect-node-es "^1.1.0"
|
detect-node-es "^1.1.0"
|
||||||
tslib "^1.9.3"
|
tslib "^1.9.3"
|
||||||
|
|
||||||
use-subscription@1.5.1:
|
|
||||||
version "1.5.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.5.1.tgz#73501107f02fad84c6dd57965beb0b75c68c42d1"
|
|
||||||
integrity sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA==
|
|
||||||
dependencies:
|
|
||||||
object-assign "^4.1.1"
|
|
||||||
|
|
||||||
use@^3.1.0:
|
use@^3.1.0:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
||||||
|
@ -16831,6 +16809,18 @@ vite@^2.8.6:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.3.2"
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
|
vite@^2.9.5:
|
||||||
|
version "2.9.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.5.tgz#08ef37ac7a6d879c96f328b791732c9a00ea25ea"
|
||||||
|
integrity sha512-dvMN64X2YEQgSXF1lYabKXw3BbN6e+BL67+P3Vy4MacnY+UzT1AfkHiioFSi9+uiDUiaDy7Ax/LQqivk6orilg==
|
||||||
|
dependencies:
|
||||||
|
esbuild "^0.14.27"
|
||||||
|
postcss "^8.4.12"
|
||||||
|
resolve "^1.22.0"
|
||||||
|
rollup "^2.59.0"
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
void-elements@3.1.0:
|
void-elements@3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
|
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
|
||||||
|
|