[CAL-1095] Embed modal (floating pop up button) - UI/layout/spacing issues (#8217)
* [CAL-1095] Embed modal (floating pop up button) - UI/layout/spacing issues * requested changes --------- Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com> Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: Keith Williams <keithwillcode@gmail.com>tweak/eslint-disable-exhaustive-deps^2
parent
5672a721d9
commit
4f466fd95d
|
@ -1,5 +1,6 @@
|
||||||
import { Collapsible, CollapsibleContent } from "@radix-ui/react-collapsible";
|
import { Collapsible, CollapsibleContent } from "@radix-ui/react-collapsible";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
import type { TFunction } from "next-i18next";
|
||||||
import type { NextRouter } from "next/router";
|
import type { NextRouter } from "next/router";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import type { MutableRefObject, RefObject } from "react";
|
import type { MutableRefObject, RefObject } from "react";
|
||||||
|
@ -21,11 +22,10 @@ import {
|
||||||
TextArea,
|
TextArea,
|
||||||
TextField,
|
TextField,
|
||||||
ColorPicker,
|
ColorPicker,
|
||||||
|
Select,
|
||||||
} from "@calcom/ui";
|
} from "@calcom/ui";
|
||||||
import { Code, Trello, Sun, ArrowLeft } from "@calcom/ui/components/icon";
|
import { Code, Trello, Sun, ArrowLeft } from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
import Select from "@components/ui/form/Select";
|
|
||||||
|
|
||||||
type EmbedType = "inline" | "floating-popup" | "element-click";
|
type EmbedType = "inline" | "floating-popup" | "element-click";
|
||||||
type EmbedFramework = "react" | "HTML";
|
type EmbedFramework = "react" | "HTML";
|
||||||
|
|
||||||
|
@ -305,15 +305,12 @@ const getEmbedTypeSpecificString = ({
|
||||||
return "";
|
return "";
|
||||||
};
|
};
|
||||||
|
|
||||||
const embeds: {
|
const embeds = (t: TFunction) =>
|
||||||
illustration: React.ReactElement;
|
(() => {
|
||||||
title: string;
|
return [
|
||||||
subtitle: string;
|
|
||||||
type: EmbedType;
|
|
||||||
}[] = [
|
|
||||||
{
|
{
|
||||||
title: "Inline Embed",
|
title: t("inline_embed"),
|
||||||
subtitle: "Loads your event type directly inline with your other website content.",
|
subtitle: t("load_inline_content"),
|
||||||
type: "inline",
|
type: "inline",
|
||||||
illustration: (
|
illustration: (
|
||||||
<svg
|
<svg
|
||||||
|
@ -385,8 +382,8 @@ const embeds: {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Floating pop-up button",
|
title: t("floating_pop_up_button"),
|
||||||
subtitle: "Adds a floating button on your site that launches Cal in a dialog.",
|
subtitle: t("floating_button_trigger_modal"),
|
||||||
type: "floating-popup",
|
type: "floating-popup",
|
||||||
illustration: (
|
illustration: (
|
||||||
<svg
|
<svg
|
||||||
|
@ -411,8 +408,8 @@ const embeds: {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Pop up via element click",
|
title: t("pop_up_element_click"),
|
||||||
subtitle: "Open your Cal dialog when someone clicks an element.",
|
subtitle: t("open_dialog_with_element_click"),
|
||||||
type: "element-click",
|
type: "element-click",
|
||||||
illustration: (
|
illustration: (
|
||||||
<svg
|
<svg
|
||||||
|
@ -489,6 +486,8 @@ const embeds: {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
})();
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{
|
{
|
||||||
name: "HTML",
|
name: "HTML",
|
||||||
|
@ -534,11 +533,7 @@ ${getEmbedTypeSpecificString({ embedFramework: "HTML", embedType, calLink, previ
|
||||||
<!-- Cal ${embedType} embed code ends -->`
|
<!-- Cal ${embedType} embed code ends -->`
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<p className="text-subtle hidden text-sm">
|
<p className="text-subtle hidden text-sm">{t("need_help_embedding")}</p>
|
||||||
{t(
|
|
||||||
"Need help? See our guides for embedding Cal on Wix, Squarespace, or WordPress, check our common questions, or explore advanced embed options."
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
@ -622,7 +617,7 @@ Cal("init", {origin:"${WEBAPP_URL}"});
|
||||||
const ThemeSelectControl = ({ children, ...props }: ControlProps<{ value: Theme; label: string }, false>) => {
|
const ThemeSelectControl = ({ children, ...props }: ControlProps<{ value: Theme; label: string }, false>) => {
|
||||||
return (
|
return (
|
||||||
<components.Control {...props}>
|
<components.Control {...props}>
|
||||||
<Sun className="text-subtle ml-2 h-4 w-4" />
|
<Sun className="text-subtle mr-2 h-4 w-4" />
|
||||||
{children}
|
{children}
|
||||||
</components.Control>
|
</components.Control>
|
||||||
);
|
);
|
||||||
|
@ -642,7 +637,7 @@ const ChooseEmbedTypesDialogContent = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="items-start space-y-2 md:flex md:space-y-0">
|
<div className="items-start space-y-2 md:flex md:space-y-0">
|
||||||
{embeds.map((embed, index) => (
|
{embeds(t).map((embed, index) => (
|
||||||
<button
|
<button
|
||||||
className="hover:bg-subtle bg-muted w-full rounded-md border border-transparent p-6 text-left hover:rounded-md ltr:mr-4 ltr:last:mr-0 rtl:ml-4 rtl:last:ml-0 lg:w-1/3"
|
className="hover:bg-subtle bg-muted w-full rounded-md border border-transparent p-6 text-left hover:rounded-md ltr:mr-4 ltr:last:mr-0 rtl:ml-4 rtl:last:ml-0 lg:w-1/3"
|
||||||
key={index}
|
key={index}
|
||||||
|
@ -692,7 +687,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
||||||
});
|
});
|
||||||
|
|
||||||
const refOfEmbedCodesRefs = useRef(embedCodeRefs);
|
const refOfEmbedCodesRefs = useRef(embedCodeRefs);
|
||||||
const embed = embeds.find((embed) => embed.type === embedType);
|
const embed = embeds(t).find((embed) => embed.type === embedType);
|
||||||
|
|
||||||
const [isEmbedCustomizationOpen, setIsEmbedCustomizationOpen] = useState(true);
|
const [isEmbedCustomizationOpen, setIsEmbedCustomizationOpen] = useState(true);
|
||||||
const [isBookingCustomizationOpen, setIsBookingCustomizationOpen] = useState(true);
|
const [isBookingCustomizationOpen, setIsBookingCustomizationOpen] = useState(true);
|
||||||
|
@ -806,11 +801,11 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
||||||
const FloatingPopupPositionOptions = [
|
const FloatingPopupPositionOptions = [
|
||||||
{
|
{
|
||||||
value: "bottom-right",
|
value: "bottom-right",
|
||||||
label: "Bottom Right",
|
label: "Bottom right",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "bottom-left",
|
value: "bottom-left",
|
||||||
label: "Bottom Left",
|
label: "Bottom left",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -834,7 +829,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
||||||
</button>
|
</button>
|
||||||
{embed.title}
|
{embed.title}
|
||||||
</h3>
|
</h3>
|
||||||
<h4 className="text-emphasis mb-6 text-sm font-normal">{embed.subtitle}</h4>
|
<h4 className="text-subtle mb-6 text-sm font-normal">{embed.subtitle}</h4>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className={classNames("font-medium", embedType === "element-click" ? "hidden" : "")}>
|
<div className={classNames("font-medium", embedType === "element-click" ? "hidden" : "")}>
|
||||||
<Collapsible
|
<Collapsible
|
||||||
|
@ -844,7 +839,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
||||||
<div className={classNames(embedType === "inline" ? "block" : "hidden")}>
|
<div className={classNames(embedType === "inline" ? "block" : "hidden")}>
|
||||||
{/*TODO: Add Auto/Fixed toggle from Figma */}
|
{/*TODO: Add Auto/Fixed toggle from Figma */}
|
||||||
<div className="text-default mb-[9px] text-sm">Window sizing</div>
|
<div className="text-default mb-[9px] text-sm">Window sizing</div>
|
||||||
<div className="justify-left flex items-center !font-normal">
|
<div className="justify-left mb-6 flex items-center !font-normal ">
|
||||||
<div className="mr-[9px]">
|
<div className="mr-[9px]">
|
||||||
<TextField
|
<TextField
|
||||||
labelProps={{ className: "hidden" }}
|
labelProps={{ className: "hidden" }}
|
||||||
|
@ -892,10 +887,10 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"mt-4 items-center justify-between",
|
"items-center justify-between",
|
||||||
embedType === "floating-popup" ? "text-emphasis" : "hidden"
|
embedType === "floating-popup" ? "text-emphasis" : "hidden"
|
||||||
)}>
|
)}>
|
||||||
<div className="mb-2 text-sm">Button Text</div>
|
<div className="mb-2 text-sm">Button text</div>
|
||||||
{/* Default Values should come from preview iframe */}
|
{/* Default Values should come from preview iframe */}
|
||||||
<TextField
|
<TextField
|
||||||
labelProps={{ className: "hidden" }}
|
labelProps={{ className: "hidden" }}
|
||||||
|
@ -910,7 +905,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
defaultValue="Book my Cal"
|
defaultValue={t("book_my_cal")}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -935,14 +930,14 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="text-default text-sm">Display Calendar Icon Button</div>
|
<div className="text-default my-2 text-sm">Display calendar icon</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"mt-4 items-center justify-between",
|
"mt-4 items-center justify-between",
|
||||||
embedType === "floating-popup" ? "text-emphasis" : "hidden"
|
embedType === "floating-popup" ? "text-emphasis" : "hidden"
|
||||||
)}>
|
)}>
|
||||||
<div className="mb-2">Position of Button</div>
|
<div className="mb-2">Position of button</div>
|
||||||
<Select
|
<Select
|
||||||
onChange={(position) => {
|
onChange={(position) => {
|
||||||
setPreviewState((previewState) => {
|
setPreviewState((previewState) => {
|
||||||
|
@ -959,14 +954,12 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
||||||
options={FloatingPopupPositionOptions}
|
options={FloatingPopupPositionOptions}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className="mt-3 flex flex-col xl:flex-row xl:justify-between">
|
||||||
className={classNames(
|
<div className={classNames("mt-4", embedType === "floating-popup" ? "" : "hidden")}>
|
||||||
"mt-4",
|
<div className="whitespace-nowrap">Button color</div>
|
||||||
embedType === "floating-popup" ? "text-emphasis" : "hidden"
|
<div className="mt-2 w-40 xl:mt-0 xl:w-full">
|
||||||
)}>
|
|
||||||
<div>Button Color</div>
|
|
||||||
<div className="w-full">
|
|
||||||
<ColorPicker
|
<ColorPicker
|
||||||
|
className="w-[130px]"
|
||||||
popoverAlign="start"
|
popoverAlign="start"
|
||||||
container={dialogContentRef?.current ?? undefined}
|
container={dialogContentRef?.current ?? undefined}
|
||||||
defaultValue="#000000"
|
defaultValue="#000000"
|
||||||
|
@ -984,14 +977,11 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className={classNames("mt-4", embedType === "floating-popup" ? "" : "hidden")}>
|
||||||
className={classNames(
|
<div className="whitespace-nowrap">Text color</div>
|
||||||
"mt-4",
|
<div className="mt-2 mb-6 w-40 xl:mt-0 xl:w-full">
|
||||||
embedType === "floating-popup" ? "text-emphasis" : "hidden"
|
|
||||||
)}>
|
|
||||||
<div>Text Color</div>
|
|
||||||
<div className="w-full">
|
|
||||||
<ColorPicker
|
<ColorPicker
|
||||||
|
className="w-[130px]"
|
||||||
popoverAlign="start"
|
popoverAlign="start"
|
||||||
container={dialogContentRef?.current ?? undefined}
|
container={dialogContentRef?.current ?? undefined}
|
||||||
defaultValue="#000000"
|
defaultValue="#000000"
|
||||||
|
@ -1009,6 +999,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</CollapsibleContent>
|
</CollapsibleContent>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1017,7 +1008,30 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
||||||
open={isBookingCustomizationOpen}
|
open={isBookingCustomizationOpen}
|
||||||
onOpenChange={() => setIsBookingCustomizationOpen((val) => !val)}>
|
onOpenChange={() => setIsBookingCustomizationOpen((val) => !val)}>
|
||||||
<CollapsibleContent>
|
<CollapsibleContent>
|
||||||
<div className="mt-6 text-sm">
|
<div className="text-sm">
|
||||||
|
<Label className="mb-6">
|
||||||
|
<div className="mb-2">Theme</div>
|
||||||
|
<Select
|
||||||
|
className="w-full"
|
||||||
|
defaultValue={ThemeOptions[0]}
|
||||||
|
components={{
|
||||||
|
Control: ThemeSelectControl,
|
||||||
|
IndicatorSeparator: () => null,
|
||||||
|
}}
|
||||||
|
onChange={(option) => {
|
||||||
|
if (!option) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setPreviewState((previewState) => {
|
||||||
|
return {
|
||||||
|
...previewState,
|
||||||
|
theme: option.value,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
options={ThemeOptions}
|
||||||
|
/>
|
||||||
|
</Label>
|
||||||
<div className="mb-6 flex items-center justify-start space-x-2 rtl:space-x-reverse">
|
<div className="mb-6 flex items-center justify-start space-x-2 rtl:space-x-reverse">
|
||||||
<Switch
|
<Switch
|
||||||
checked={previewState.hideEventTypeDetails}
|
checked={previewState.hideEventTypeDetails}
|
||||||
|
@ -1056,29 +1070,6 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
||||||
</div>
|
</div>
|
||||||
</Label>
|
</Label>
|
||||||
))}
|
))}
|
||||||
<Label>
|
|
||||||
<div className="mb-2">Theme</div>
|
|
||||||
<Select
|
|
||||||
className="w-full"
|
|
||||||
defaultValue={ThemeOptions[0]}
|
|
||||||
components={{
|
|
||||||
Control: ThemeSelectControl,
|
|
||||||
IndicatorSeparator: () => null,
|
|
||||||
}}
|
|
||||||
onChange={(option) => {
|
|
||||||
if (!option) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setPreviewState((previewState) => {
|
|
||||||
return {
|
|
||||||
...previewState,
|
|
||||||
theme: option.value,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
options={ThemeOptions}
|
|
||||||
/>
|
|
||||||
</Label>
|
|
||||||
</div>
|
</div>
|
||||||
</CollapsibleContent>
|
</CollapsibleContent>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
|
|
|
@ -1800,5 +1800,13 @@
|
||||||
"payment_app_commission": "Require payment ({{paymentFeePercentage}}% + {{fee, currency}} commission per transaction)",
|
"payment_app_commission": "Require payment ({{paymentFeePercentage}}% + {{fee, currency}} commission per transaction)",
|
||||||
"email_invite_team": "{{email}} has been invited",
|
"email_invite_team": "{{email}} has been invited",
|
||||||
"error_collecting_card": "Error collecting card",
|
"error_collecting_card": "Error collecting card",
|
||||||
"image_size_limit_exceed": "Uploaded image shouldn't exceed 5mb size limit"
|
"image_size_limit_exceed": "Uploaded image shouldn't exceed 5mb size limit",
|
||||||
|
"inline_embed": "Inline Embed",
|
||||||
|
"load_inline_content": "Loads your event type directly inline with your other website content.",
|
||||||
|
"floating_pop_up_button": "Floating pop-up button",
|
||||||
|
"floating_button_trigger_modal": "Puts a floating button on your site that triggers a modal with your event type.",
|
||||||
|
"pop_up_element_click": "Pop up via element click",
|
||||||
|
"open_dialog_with_element_click": "Open your Cal dialog when someone clicks an element.",
|
||||||
|
"need_help_embedding": "Need help? See our guides for embedding Cal on Wix, Squarespace, or WordPress, check our common questions, or explore advanced embed options.",
|
||||||
|
"book_my_cal": "Book my Cal"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import * as Popover from "@radix-ui/react-popover";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { HexColorInput, HexColorPicker } from "react-colorful";
|
import { HexColorInput, HexColorPicker } from "react-colorful";
|
||||||
|
|
||||||
|
import cx from "@calcom/lib/classNames";
|
||||||
import { fallBackHex, isValidHexCode } from "@calcom/lib/getBrandColours";
|
import { fallBackHex, isValidHexCode } from "@calcom/lib/getBrandColours";
|
||||||
|
|
||||||
export type ColorPickerProps = {
|
export type ColorPickerProps = {
|
||||||
|
@ -9,6 +10,7 @@ export type ColorPickerProps = {
|
||||||
onChange: (text: string) => void;
|
onChange: (text: string) => void;
|
||||||
container?: HTMLElement;
|
container?: HTMLElement;
|
||||||
popoverAlign?: React.ComponentProps<typeof Popover.Content>["align"];
|
popoverAlign?: React.ComponentProps<typeof Popover.Content>["align"];
|
||||||
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ColorPicker = (props: ColorPickerProps) => {
|
const ColorPicker = (props: ColorPickerProps) => {
|
||||||
|
@ -44,7 +46,10 @@ const ColorPicker = (props: ColorPickerProps) => {
|
||||||
</Popover.Root>
|
</Popover.Root>
|
||||||
|
|
||||||
<HexColorInput
|
<HexColorInput
|
||||||
className="border-default text-default bg-default block h-full w-full border px-3 py-2 ltr:rounded-r-md rtl:rounded-l-md sm:text-sm"
|
className={cx(
|
||||||
|
"border-default text-default bg-default block h-full w-full border px-3 py-2 ltr:rounded-r-md rtl:rounded-l-md sm:text-sm",
|
||||||
|
props.className
|
||||||
|
)}
|
||||||
color={color}
|
color={color}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
setColor(val);
|
setColor(val);
|
||||||
|
|
Loading…
Reference in New Issue