import { ArrowLeftIcon, ChevronRightIcon, CodeIcon, EyeIcon, SunIcon } from "@heroicons/react/solid"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/react-collapsible"; import classNames from "classnames"; import { useRouter } from "next/router"; import { createRef, forwardRef, MutableRefObject, RefObject, useRef, useState } from "react"; import { components, ControlProps } from "react-select"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import showToast from "@calcom/lib/notification"; import { EventType } from "@calcom/prisma/client"; import { Button, Switch } from "@calcom/ui"; import { Dialog, DialogClose, DialogContent } from "@calcom/ui/Dialog"; import { InputLeading, Label, TextArea, TextField } from "@calcom/ui/form/fields"; import { EMBED_LIB_URL, WEBAPP_URL } from "@lib/config/constants"; import { trpc } from "@lib/trpc"; import NavTabs from "@components/NavTabs"; import ColorPicker from "@components/ui/colorpicker"; import Select from "@components/ui/form/Select"; type EmbedType = "inline" | "floating-popup" | "element-click"; type EmbedFramework = "react" | "HTML"; const enum Theme { auto = "auto", light = "light", dark = "dark", } type PreviewState = { inline: { width: string; height: string; }; theme: Theme; floatingPopup: Record; elementClick: Record; palette: { brandColor: string; }; }; const queryParamsForDialog = ["embedType", "tabName", "eventTypeId"]; const getDimension = (dimension: string) => { if (dimension.match(/^\d+$/)) { dimension = `${dimension}%`; } return dimension; }; /** * It allows us to show code with certain reusable blocks indented according to the block variable placement * So, if you add a variable ${abc} with indentation of 4 spaces, it will automatically indent all newlines in `abc` with the same indent before constructing the final string * `A${var}C` with var = "B" -> partsWithoutBlock=['A','C'] blocksOrVariables=['B'] */ const code = (partsWithoutBlock: TemplateStringsArray, ...blocksOrVariables: string[]) => { const constructedCode: string[] = []; for (let i = 0; i < partsWithoutBlock.length; i++) { const partWithoutBlock = partsWithoutBlock[i]; // blocksOrVariables length would always be 1 less than partsWithoutBlock // So, last item should be concatenated as is. if (i >= blocksOrVariables.length) { constructedCode.push(partWithoutBlock); continue; } const block = blocksOrVariables[i]; const indentedBlock: string[] = []; let indent = ""; block.split("\n").forEach((line) => { indentedBlock.push(line); }); // non-null assertion is okay because we know that we are referencing last element. // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const indentationMatch = partWithoutBlock .split("\n") .at(-1)! .match(/(^[\t ]*).*$/); if (indentationMatch) { indent = indentationMatch[1]; } constructedCode.push(partWithoutBlock + indentedBlock.join("\n" + indent)); } return constructedCode.join(""); }; const getInstructionString = ({ apiName, instructionName, instructionArg, }: { apiName: string; instructionName: string; instructionArg: Record; }) => { return `${apiName}("${instructionName}", ${JSON.stringify(instructionArg)});`; }; const getEmbedUIInstructionString = ({ apiName, theme, brandColor, }: { apiName: string; theme?: string; brandColor: string; }) => { theme = theme !== "auto" ? theme : undefined; return getInstructionString({ apiName, instructionName: "ui", instructionArg: { theme, styles: { branding: { brandColor, }, }, }, }); }; // eslint-disable-next-line @typescript-eslint/no-explicit-any const Codes: Record string>> = { react: { inline: ({ calLink, uiInstructionCode, previewState, }: { calLink: string; uiInstructionCode: string; previewState: PreviewState; }) => { const width = getDimension(previewState.inline.width); const height = getDimension(previewState.inline.height); return code` import Cal, { getCalApi } from "@calcom/embed-react"; function MyComponent() { useEffect(()=>{ (async function () { const cal = await getCalApi(); ${uiInstructionCode} })(); }, []) return ; };`; }, "floating-popup": ({ floatingButtonArg, uiInstructionCode, }: { floatingButtonArg: string; uiInstructionCode: string; }) => { return code` import Cal, { getCalApi } from "@calcom/embed-react"; function MyComponent() { useEffect(()=>{ (async function () { const cal = await getCalApi(); Cal("floatingButton", ${floatingButtonArg}); ${uiInstructionCode} })(); }, []) };`; }, "element-click": ({ calLink, uiInstructionCode }: { calLink: string; uiInstructionCode: string }) => { return code` import Cal, { getCalApi } from "@calcom/embed-react"; function MyComponent() { useEffect(()=>{ (async function () { const cal = await getCalApi(); ${uiInstructionCode} })(); }, []) return