feat: v2 embed (#4477)
* Add support for custom tabNameKey and use embedTabName as the key for embed to avoid conflict with event-types tabName property * Fix tests * Code cleanup * feat: v2 embed * fix: button black default, reuse horitzontalTabs v2 * fix: remove comment, remove linkProps from NavTabs v2 * fix: height: 98% to avoid overflow * fix: add embed to event type detail page * fix: add also tabNames embed-code embed-react * fix: add tabNames w empty divs * Update Embed component as per V2 Co-authored-by: Hariom Balhara <hariombalhara@gmail.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: Agusti Fernandez Pardo <git@agusti.me> Co-authored-by: Alex van Andel <me@alexvanandel.com>
This commit is contained in:
parent
35c2f9046a
commit
a3462657db
|
@ -5,15 +5,14 @@ import { createRef, forwardRef, MutableRefObject, RefObject, useRef, useState }
|
|||
import { components, ControlProps } from "react-select";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { Dialog, DialogClose, DialogContent } from "@calcom/ui/Dialog";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import { InputLeading, Label, TextArea, TextField } from "@calcom/ui/form/fields";
|
||||
import { HorizontalTabs, showToast } from "@calcom/ui/v2";
|
||||
import { Button, Switch } from "@calcom/ui/v2";
|
||||
|
||||
import { EMBED_LIB_URL, WEBAPP_URL } from "@lib/config/constants";
|
||||
|
||||
import NavTabs from "@components/NavTabs";
|
||||
import ColorPicker from "@components/ui/colorpicker";
|
||||
import Select from "@components/ui/form/Select";
|
||||
|
||||
|
@ -38,7 +37,7 @@ type PreviewState = {
|
|||
brandColor: string;
|
||||
};
|
||||
};
|
||||
const queryParamsForDialog = ["embedType", "tabName", "embedUrl"];
|
||||
const queryParamsForDialog = ["embedType", "embedTabName", "embedUrl"];
|
||||
|
||||
const getDimension = (dimension: string) => {
|
||||
if (dimension.match(/^\d+$/)) {
|
||||
|
@ -453,7 +452,7 @@ const embeds: {
|
|||
const tabs = [
|
||||
{
|
||||
name: "HTML",
|
||||
tabName: "embed-code",
|
||||
embedTabName: "embed-code",
|
||||
icon: Icon.FiCode,
|
||||
type: "code",
|
||||
Component: forwardRef<
|
||||
|
@ -504,7 +503,7 @@ ${getEmbedTypeSpecificString({ embedFramework: "HTML", embedType, calLink, previ
|
|||
},
|
||||
{
|
||||
name: "React",
|
||||
tabName: "embed-react",
|
||||
embedTabName: "embed-react",
|
||||
icon: Icon.FiCode,
|
||||
type: "code",
|
||||
Component: forwardRef<
|
||||
|
@ -544,7 +543,7 @@ ${getEmbedTypeSpecificString({ embedFramework: "react", embedType, calLink, prev
|
|||
},
|
||||
{
|
||||
name: "Preview",
|
||||
tabName: "embed-preview",
|
||||
embedTabName: "embed-preview",
|
||||
icon: Icon.FiEye,
|
||||
type: "iframe",
|
||||
Component: forwardRef<
|
||||
|
@ -561,7 +560,7 @@ ${getEmbedTypeSpecificString({ embedFramework: "react", embedType, calLink, prev
|
|||
<iframe
|
||||
ref={ref as typeof ref & MutableRefObject<HTMLIFrameElement>}
|
||||
data-testid="embed-preview"
|
||||
className="border-1 h-[75vh] border"
|
||||
className="border-1 h-[100vh] border"
|
||||
width="100%"
|
||||
height="100%"
|
||||
src={`${WEBAPP_URL}/embed/preview.html?embedType=${embedType}&calLink=${calLink}`}
|
||||
|
@ -678,8 +677,8 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
|||
};
|
||||
|
||||
// Use embed-code as default tab
|
||||
if (!router.query.tabName) {
|
||||
router.query.tabName = "embed-code";
|
||||
if (!router.query.embedTabName) {
|
||||
router.query.embedTabName = "embed-code";
|
||||
router.push({
|
||||
query: {
|
||||
...router.query,
|
||||
|
@ -780,7 +779,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
|||
];
|
||||
|
||||
return (
|
||||
<DialogContent size="xl">
|
||||
<DialogContent size="lg">
|
||||
<div className="flex">
|
||||
<div className="flex w-1/3 flex-col bg-white p-6">
|
||||
<h3 className="mb-2 flex text-xl font-bold leading-6 text-gray-900" id="modal-title">
|
||||
|
@ -788,7 +787,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
|||
onClick={() => {
|
||||
const newQuery = { ...router.query };
|
||||
delete newQuery.embedType;
|
||||
delete newQuery.tabName;
|
||||
delete newQuery.embedTabName;
|
||||
router.push({
|
||||
query: {
|
||||
...newQuery,
|
||||
|
@ -871,9 +870,9 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
|||
<div
|
||||
className={classNames(
|
||||
"mt-4 items-center justify-between",
|
||||
embedType === "floating-popup" ? "flex" : "hidden"
|
||||
embedType === "floating-popup" ? "" : "hidden"
|
||||
)}>
|
||||
<div className="text-sm">Button Text</div>
|
||||
<div className="mb-2 text-sm">Button Text</div>
|
||||
{/* Default Values should come from preview iframe */}
|
||||
<TextField
|
||||
name="buttonText"
|
||||
|
@ -895,10 +894,9 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
|||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
"mt-4 flex items-center justify-between",
|
||||
embedType === "floating-popup" ? "flex" : "hidden"
|
||||
"mt-4 flex items-center justify-start",
|
||||
embedType === "floating-popup" ? "space-x-2" : "hidden"
|
||||
)}>
|
||||
<div className="text-sm">Display Calendar Icon Button</div>
|
||||
<Switch
|
||||
defaultChecked={true}
|
||||
onCheckedChange={(checked) => {
|
||||
|
@ -913,13 +911,14 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
|||
});
|
||||
}}
|
||||
/>
|
||||
<div className="text-sm">Display Calendar Icon Button</div>
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
"mt-4 flex items-center justify-between",
|
||||
embedType === "floating-popup" ? "flex" : "hidden"
|
||||
"mt-4 items-center justify-between",
|
||||
embedType === "floating-popup" ? "" : "hidden"
|
||||
)}>
|
||||
<div>Position of Button</div>
|
||||
<div className="mb-2">Position of Button</div>
|
||||
<Select
|
||||
onChange={(position) => {
|
||||
setPreviewState((previewState) => {
|
||||
|
@ -936,13 +935,9 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
|||
options={FloatingPopupPositionOptions}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
"mt-4 flex items-center justify-between",
|
||||
embedType === "floating-popup" ? "flex" : "hidden"
|
||||
)}>
|
||||
<div className={classNames("mt-4", embedType === "floating-popup" ? "" : "hidden")}>
|
||||
<div>Button Color</div>
|
||||
<div className="w-36">
|
||||
<div className="w-full">
|
||||
<ColorPicker
|
||||
defaultValue="#000000"
|
||||
onChange={(color) => {
|
||||
|
@ -959,13 +954,9 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
"mt-4 flex items-center justify-between",
|
||||
embedType === "floating-popup" ? "flex" : "hidden"
|
||||
)}>
|
||||
<div className={classNames("mt-4", embedType === "floating-popup" ? "" : "hidden")}>
|
||||
<div>Text Color</div>
|
||||
<div className="w-36">
|
||||
<div className="w-full">
|
||||
<ColorPicker
|
||||
defaultValue="#000000"
|
||||
onChange={(color) => {
|
||||
|
@ -1000,10 +991,10 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
|||
</CollapsibleTrigger>
|
||||
<CollapsibleContent>
|
||||
<div className="mt-6 text-sm">
|
||||
<Label className="flex items-center justify-between">
|
||||
<div>Theme</div>
|
||||
<Label className="">
|
||||
<div className="mb-2">Theme</div>
|
||||
<Select
|
||||
className="w-36"
|
||||
className="w-full"
|
||||
defaultValue={ThemeOptions[0]}
|
||||
components={{
|
||||
Control: ThemeSelectControl,
|
||||
|
@ -1030,9 +1021,9 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
|||
// { name: "highlightColor", title: "Highlight Color" },
|
||||
// { name: "medianColor", title: "Median Color" },
|
||||
].map((palette) => (
|
||||
<Label key={palette.name} className="flex items-center justify-between">
|
||||
<div>{palette.title}</div>
|
||||
<div className="w-36">
|
||||
<Label key={palette.name} className="pb-4">
|
||||
<div className="mb-2 pt-2">{palette.title}</div>
|
||||
<div className="w-full">
|
||||
<ColorPicker
|
||||
defaultValue="#000000"
|
||||
onChange={(color) => {
|
||||
|
@ -1049,33 +1040,33 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
|||
</Collapsible>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-2/3 bg-gray-50 p-6">
|
||||
<NavTabs data-testid="embed-tabs" tabs={tabs} linkProps={{ shallow: true }} />
|
||||
<div className="flex w-2/3 flex-col p-6">
|
||||
<HorizontalTabs tabNameKey="embedTabName" data-testid="embed-tabs" tabs={tabs} />
|
||||
{tabs.map((tab) => {
|
||||
return (
|
||||
<div
|
||||
key={tab.tabName}
|
||||
className={classNames(router.query.tabName === tab.tabName ? "block" : "hidden")}>
|
||||
<div>
|
||||
<div className={classNames(tab.type === "code" ? "h-[75vh]" : "")}>
|
||||
{tab.type === "code" ? (
|
||||
<tab.Component
|
||||
embedType={embedType}
|
||||
calLink={calLink}
|
||||
previewState={previewState}
|
||||
ref={refOfEmbedCodesRefs.current[tab.name]}
|
||||
/>
|
||||
) : (
|
||||
<tab.Component
|
||||
embedType={embedType}
|
||||
calLink={calLink}
|
||||
previewState={previewState}
|
||||
ref={iframeRef}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className={router.query.tabName == "embed-preview" ? "block" : "hidden"} />
|
||||
key={tab.embedTabName}
|
||||
className={classNames(
|
||||
router.query.embedTabName === tab.embedTabName ? "flex flex-grow flex-col" : "hidden"
|
||||
)}>
|
||||
<div className="flex h-[55vh] flex-grow flex-col">
|
||||
{tab.type === "code" ? (
|
||||
<tab.Component
|
||||
embedType={embedType}
|
||||
calLink={calLink}
|
||||
previewState={previewState}
|
||||
ref={refOfEmbedCodesRefs.current[tab.name]}
|
||||
/>
|
||||
) : (
|
||||
<tab.Component
|
||||
embedType={embedType}
|
||||
calLink={calLink}
|
||||
previewState={previewState}
|
||||
ref={iframeRef}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className={router.query.embedTabName == "embed-preview" ? "block" : "hidden"} />
|
||||
<div className="mt-8 flex flex-row-reverse gap-x-2">
|
||||
{tab.type === "code" ? (
|
||||
<Button
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
HorizontalTabs,
|
||||
Switch,
|
||||
Label,
|
||||
HorizontalTabItemProps,
|
||||
} from "@calcom/ui/v2";
|
||||
import { Dialog } from "@calcom/ui/v2/core/Dialog";
|
||||
import Dropdown, {
|
||||
|
@ -32,6 +33,7 @@ import Shell from "@calcom/ui/v2/core/Shell";
|
|||
import VerticalDivider from "@calcom/ui/v2/core/VerticalDivider";
|
||||
|
||||
import { ClientSuspense } from "@components/ClientSuspense";
|
||||
import { EmbedButton, EmbedDialog } from "@components/Embed";
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode;
|
||||
|
@ -80,7 +82,7 @@ function EventTypeSingleLayout({
|
|||
|
||||
// Define tab navigation here
|
||||
const EventTypeTabs = useMemo(() => {
|
||||
const navigation = [
|
||||
const navigation: (VerticalTabItemProps & HorizontalTabItemProps)[] = [
|
||||
{
|
||||
name: "event_setup_tab_title",
|
||||
tabName: "setup",
|
||||
|
@ -123,7 +125,7 @@ function EventTypeSingleLayout({
|
|||
icon: Icon.FiZap,
|
||||
info: `${enabledWorkflowsNumber} ${t("active")}`,
|
||||
},
|
||||
] as VerticalTabItemProps[];
|
||||
];
|
||||
|
||||
// If there is a team put this navigation item within the tabs
|
||||
if (team)
|
||||
|
@ -152,6 +154,8 @@ function EventTypeSingleLayout({
|
|||
eventType.slug
|
||||
}`;
|
||||
|
||||
const embedLink = `${team ? `team/${team.slug}` : eventType.users[0].username}/${eventType.slug}`;
|
||||
|
||||
return (
|
||||
<Shell
|
||||
backPath="/event-types"
|
||||
|
@ -199,8 +203,12 @@ function EventTypeSingleLayout({
|
|||
showToast("Link copied!", "success");
|
||||
}}
|
||||
/>
|
||||
{/* TODO: Implement embed here @hariom */}
|
||||
{/* <Button color="secondary" size="icon" StartIcon={Icon.FiCode} /> */}
|
||||
<EmbedButton
|
||||
embedUrl={encodeURIComponent(embedLink)}
|
||||
StartIcon={Icon.FiCode}
|
||||
color="secondary"
|
||||
size="icon"
|
||||
/>
|
||||
<Button
|
||||
color="secondary"
|
||||
size="icon"
|
||||
|
@ -256,7 +264,7 @@ function EventTypeSingleLayout({
|
|||
<VerticalTabs tabs={EventTypeTabs} sticky />
|
||||
</div>
|
||||
<div className="p-2 md:mx-0 md:p-0 xl:hidden">
|
||||
<HorizontalTabs tabs={EventTypeTabs} />
|
||||
<HorizontalTabs tabNameKey="tabName" tabs={EventTypeTabs} />
|
||||
</div>
|
||||
<div className="w-full ltr:mr-2 rtl:ml-2">
|
||||
<div
|
||||
|
@ -283,6 +291,7 @@ function EventTypeSingleLayout({
|
|||
{t("delete_event_type_description") as string}
|
||||
</ConfirmationDialogContent>
|
||||
</Dialog>
|
||||
<EmbedDialog />
|
||||
</Shell>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
import autoAnimate from "@formkit/auto-animate";
|
||||
import { EventTypeCustomInput, PeriodType, Prisma, SchedulingType } from "@prisma/client";
|
||||
import { GetServerSidePropsContext } from "next";
|
||||
|
|
|
@ -60,7 +60,7 @@ async function expectToBeNavigatingToEmbedCodeAndPreviewDialog(
|
|||
url.searchParams.get("dialog") === "embed" &&
|
||||
url.searchParams.get("embedUrl") === embedUrl &&
|
||||
url.searchParams.get("embedType") === embedType &&
|
||||
url.searchParams.get("tabName") === "embed-code"
|
||||
url.searchParams.get("embedTabName") === "embed-code"
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
</head>
|
||||
<script type="module" src="./src/preview.ts"></script>
|
||||
<body>
|
||||
<div id="my-embed" style="width: 100%; height: 100%; overflow: scroll"></div>
|
||||
<div id="my-embed" style="width: 100%; height: 90%; overflow: scroll"></div>
|
||||
<script type="module">
|
||||
|
||||
</script>
|
||||
|
|
|
@ -252,10 +252,6 @@ export const methods = {
|
|||
ui: function style(uiConfig: UiConfig) {
|
||||
// TODO: Create automatic logger for all methods. Useful for debugging.
|
||||
log("Method: ui called", uiConfig);
|
||||
if (window.CalComPlan && window.CalComPlan !== "PRO") {
|
||||
log(`Upgrade to PRO for "ui" instruction to work`, window.CalComPlan);
|
||||
return;
|
||||
}
|
||||
const stylesConfig = uiConfig.styles;
|
||||
|
||||
// In case where parent gives instructions before CalComPlan is set.
|
||||
|
|
|
@ -276,8 +276,8 @@ export class Cal {
|
|||
hideButtonIcon = false,
|
||||
attributes,
|
||||
buttonPosition = "bottom-right",
|
||||
buttonColor = "rgb(255, 202, 0)",
|
||||
buttonTextColor = "rgb(20, 30, 47)",
|
||||
buttonColor = "rgb(0, 0, 0)",
|
||||
buttonTextColor = "rgb(255, 255, 255)",
|
||||
}: {
|
||||
calLink: string;
|
||||
buttonText?: string;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import React, { ComponentProps } from "react";
|
||||
|
||||
import HorizontalTabs from "@calcom/ui/v2/core/navigation/tabs/HorizontalTabs";
|
||||
import type { VerticalTabItemProps } from "@calcom/ui/v2/core/navigation/tabs/VerticalTabItem";
|
||||
|
||||
import Shell from "../Shell";
|
||||
import { HorizontalTabItemProps } from "../navigation/tabs/HorizontalTabItem";
|
||||
|
||||
const tabs: VerticalTabItemProps[] = [
|
||||
const tabs: HorizontalTabItemProps[] = [
|
||||
{
|
||||
name: "app_store",
|
||||
href: "/apps",
|
||||
|
|
|
@ -45,10 +45,10 @@ export default function BookingLayout({
|
|||
<Shell {...rest}>
|
||||
<div className="flex flex-col sm:space-x-2 xl:flex-row">
|
||||
<div className="hidden xl:block">
|
||||
<VerticalTabs tabs={tabs} sticky />
|
||||
<VerticalTabs tabNameKey="tabName" tabs={tabs} sticky />
|
||||
</div>
|
||||
<div className="block xl:hidden">
|
||||
<HorizontalTabs tabs={tabs} />
|
||||
<HorizontalTabs tabNameKey="tabName" tabs={tabs} />
|
||||
</div>
|
||||
<main className="w-full max-w-6xl">{children}</main>
|
||||
</div>
|
||||
|
|
|
@ -6,34 +6,40 @@ import { MouseEventHandler } from "react";
|
|||
import classNames from "@calcom/lib/classNames";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
|
||||
export type HorizontalTabItemProps = {
|
||||
export type HorizontalTabItemProps<T extends string = "tabName"> = {
|
||||
name: string;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
} & (
|
||||
| {
|
||||
/** If you want to change query param tabName as per current tab */
|
||||
href: string;
|
||||
tabName?: never;
|
||||
}
|
||||
| {
|
||||
| ({
|
||||
href?: never;
|
||||
/** If you want to change the path as per current tab */
|
||||
tabName: string;
|
||||
}
|
||||
} & Partial<Record<T, string>>)
|
||||
);
|
||||
|
||||
const HorizontalTabItem = ({ name, href, tabName, ...props }: HorizontalTabItemProps) => {
|
||||
const HorizontalTabItem = function <T extends string>({
|
||||
name,
|
||||
href,
|
||||
tabNameKey,
|
||||
...props
|
||||
}: HorizontalTabItemProps<T> & {
|
||||
tabNameKey?: T;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const { t } = useLocale();
|
||||
let newHref = "";
|
||||
let isCurrent;
|
||||
const _tabNameKey = tabNameKey || "tabName";
|
||||
const tabName = props[tabNameKey as keyof typeof props];
|
||||
|
||||
if (href) {
|
||||
newHref = href;
|
||||
isCurrent = router.asPath === href;
|
||||
} else if (tabName) {
|
||||
newHref = "";
|
||||
isCurrent = router.query.tabName === tabName;
|
||||
isCurrent = router.query[_tabNameKey] === tabName;
|
||||
}
|
||||
|
||||
const onClick: MouseEventHandler = tabName
|
||||
|
@ -42,7 +48,7 @@ const HorizontalTabItem = ({ name, href, tabName, ...props }: HorizontalTabItemP
|
|||
router.push({
|
||||
query: {
|
||||
...router.query,
|
||||
tabName,
|
||||
[_tabNameKey]: tabName,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { FC } from "react";
|
||||
|
||||
import HorizontalTabItem, { HorizontalTabItemProps } from "./HorizontalTabItem";
|
||||
|
||||
export { HorizontalTabItem };
|
||||
|
||||
export interface NavTabProps {
|
||||
tabs: HorizontalTabItemProps[];
|
||||
export interface NavTabProps<T extends string> {
|
||||
tabs: HorizontalTabItemProps<T>[];
|
||||
tabNameKey?: T;
|
||||
}
|
||||
|
||||
const HorizontalTabs: FC<NavTabProps> = ({ tabs, ...props }) => {
|
||||
const HorizontalTabs = function <T extends string>({ tabs, tabNameKey, ...props }: NavTabProps<T>) {
|
||||
const _tabNameKey = tabNameKey || "tabName";
|
||||
return (
|
||||
<div className="-mx-6 mb-2 w-[calc(100%+40px)]">
|
||||
<nav
|
||||
|
@ -16,7 +16,7 @@ const HorizontalTabs: FC<NavTabProps> = ({ tabs, ...props }) => {
|
|||
aria-label="Tabs"
|
||||
{...props}>
|
||||
{tabs.map((tab, idx) => (
|
||||
<HorizontalTabItem {...tab} key={idx} />
|
||||
<HorizontalTabItem tabNameKey={_tabNameKey} {...tab} key={idx} />
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import noop from "lodash/noop";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { FC, Fragment, MouseEventHandler } from "react";
|
||||
import { Fragment, MouseEventHandler } from "react";
|
||||
|
||||
// import { ChevronRight } from "react-feather";
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
|
@ -9,12 +9,12 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
|||
import { SVGComponent } from "@calcom/types/SVGComponent";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
|
||||
export type VerticalTabItemProps = {
|
||||
export type VerticalTabItemProps<T extends string = "tabName"> = {
|
||||
name: string;
|
||||
info?: string;
|
||||
icon?: SVGComponent;
|
||||
disabled?: boolean;
|
||||
children?: VerticalTabItemProps[];
|
||||
children?: VerticalTabItemProps<T>[];
|
||||
textClassNames?: string;
|
||||
className?: string;
|
||||
isChild?: boolean;
|
||||
|
@ -26,32 +26,34 @@ export type VerticalTabItemProps = {
|
|||
href: string;
|
||||
tabName?: never;
|
||||
}
|
||||
| {
|
||||
| ({
|
||||
href?: never;
|
||||
/** If you want to change the path as per current tab */
|
||||
tabName: string;
|
||||
}
|
||||
} & Partial<Record<T, string>>)
|
||||
);
|
||||
|
||||
const VerticalTabItem: FC<VerticalTabItemProps> = ({
|
||||
const VerticalTabItem = function <T extends string>({
|
||||
name,
|
||||
href,
|
||||
tabName,
|
||||
tabNameKey,
|
||||
info,
|
||||
isChild,
|
||||
disableChevron,
|
||||
...props
|
||||
}) => {
|
||||
}: VerticalTabItemProps<T> & {
|
||||
tabNameKey?: T;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const { t } = useLocale();
|
||||
let newHref = "";
|
||||
let isCurrent;
|
||||
const tabName = props[tabNameKey as keyof typeof props] as string;
|
||||
const _tabNameKey = tabNameKey || "tabName";
|
||||
if (href) {
|
||||
newHref = href;
|
||||
isCurrent = router.asPath === href;
|
||||
} else if (tabName) {
|
||||
newHref = "";
|
||||
isCurrent = router.query.tabName === tabName;
|
||||
isCurrent = router.query[_tabNameKey] === tabName;
|
||||
}
|
||||
|
||||
const onClick: MouseEventHandler = tabName
|
||||
|
@ -60,7 +62,7 @@ const VerticalTabItem: FC<VerticalTabItemProps> = ({
|
|||
router.push({
|
||||
query: {
|
||||
...router.query,
|
||||
tabName,
|
||||
[_tabNameKey]: tabName,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -83,6 +85,8 @@ const VerticalTabItem: FC<VerticalTabItemProps> = ({
|
|||
)}
|
||||
aria-current={isCurrent ? "page" : undefined}>
|
||||
{props.icon && (
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
<props.icon className="mr-[10px] h-[16px] w-[16px] self-start stroke-[2px] md:mt-0" />
|
||||
)}
|
||||
<div>
|
||||
|
@ -101,7 +105,7 @@ const VerticalTabItem: FC<VerticalTabItemProps> = ({
|
|||
</a>
|
||||
</Link>
|
||||
{props.children?.map((child) => (
|
||||
<VerticalTabItem key={child.name} {...child} isChild />
|
||||
<VerticalTabItem tabNameKey={tabNameKey} key={child.name} {...child} isChild />
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
|
|
|
@ -1,19 +1,26 @@
|
|||
import { FC } from "react";
|
||||
|
||||
import { classNames } from "@calcom/lib";
|
||||
|
||||
import VerticalTabItem, { VerticalTabItemProps } from "./VerticalTabItem";
|
||||
|
||||
export { VerticalTabItem };
|
||||
|
||||
export interface NavTabProps {
|
||||
tabs: VerticalTabItemProps[];
|
||||
export interface NavTabProps<T extends string> {
|
||||
tabs: VerticalTabItemProps<T>[];
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
sticky?: boolean;
|
||||
tabNameKey?: T;
|
||||
}
|
||||
|
||||
const NavTabs: FC<NavTabProps> = ({ tabs, className = "", sticky, ...props }) => {
|
||||
const NavTabs = function <T extends string>({
|
||||
tabs,
|
||||
tabNameKey,
|
||||
className = "",
|
||||
sticky,
|
||||
...props
|
||||
}: NavTabProps<T>) {
|
||||
const _tabNameKey = tabNameKey || "tabName";
|
||||
|
||||
return (
|
||||
<nav
|
||||
className={classNames(
|
||||
|
@ -26,7 +33,7 @@ const NavTabs: FC<NavTabProps> = ({ tabs, className = "", sticky, ...props }) =>
|
|||
{sticky && <div className="pt-6" />}
|
||||
{props.children}
|
||||
{tabs.map((tab, idx) => (
|
||||
<VerticalTabItem {...tab} key={idx} />
|
||||
<VerticalTabItem tabNameKey={_tabNameKey} {...tab} key={idx} />
|
||||
))}
|
||||
</nav>
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue
Block a user