🧹 Chore/move v2 components to components dir (#6261)

* Removed old root UI components.

* Fixed import path

* Moved first v2 components to new components dir.

* Changed icon to not import from root, since it somehow creates a recursive import

* Removed irrelevant todo

* Added docs for new components

* Fixed type errors

* Update packages/ui/components/index.ts

Removing as per comment

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
This commit is contained in:
Jeroen Reumkensandsean-brydon authored and GitHub committed 2023-01-05 12:04:28 +00:00
1 parent aaac491dc7
commit 0cbaba73e4
47 files changed
+301 -449

No files matched your search

+1 -3
View File
@@ -11,9 +11,7 @@ import { APP_NAME, COMPANY_NAME, SUPPORT_MAIL_ADDRESS } from "@calcom/lib/consta
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import { App as AppType } from "@calcom/types/App";
import { Button, Icon, Shell, showToast, SkeletonButton, SkeletonText } from "@calcom/ui";
import HeadSeo from "@components/seo/head-seo";
import { Button, Icon, Shell, showToast, SkeletonButton, SkeletonText, HeadSeo } from "@calcom/ui";
const Component = ({
name,
@@ -28,7 +28,7 @@ import { getRecurringFreq } from "@calcom/lib/recurringStrings";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import { detectBrowserTimeFormat, setIs24hClockInLocalStorage, TimeFormat } from "@calcom/lib/timeFormat";
import { trpc } from "@calcom/trpc/react";
import { Icon } from "@calcom/ui";
import { Icon, HeadSeo } from "@calcom/ui";
import { timeZone as localStorageTimeZone } from "@lib/clock";
import useRouterQuery from "@lib/hooks/useRouterQuery";
@@ -37,7 +37,6 @@ import Gates, { Gate, GateState } from "@components/Gates";
import AvailableTimes from "@components/booking/AvailableTimes";
import BookingDescription from "@components/booking/BookingDescription";
import TimeOptions from "@components/booking/TimeOptions";
import { HeadSeo } from "@components/seo/head-seo";
import PoweredByCal from "@components/ui/PoweredByCal";
import type { AvailabilityPageProps } from "../../../pages/[user]/[type]";
+1 -3
View File
@@ -1,11 +1,9 @@
import classNames from "classnames";
import { APP_NAME, LOGO } from "@calcom/lib/constants";
import { Credits, HeadSeo } from "@calcom/ui";
import Loader from "@components/Loader";
import { HeadSeo } from "@components/seo/head-seo";
import Credits from "../../../../packages/ui/v2/core/Credits";
interface Props {
title: string;
+1 -3
View File
@@ -4,12 +4,10 @@ import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import { COMPANY_NAME, DEVELOPER_DOCS, DOCS_URL, JOIN_SLACK, WEBSITE_URL } from "@calcom/lib/constants";
import { Icon } from "@calcom/ui";
import { Icon, HeadSeo } from "@calcom/ui";
import { useLocale } from "@lib/hooks/useLocale";
import { HeadSeo } from "@components/seo/head-seo";
import { ssgInit } from "@server/lib/ssg";
export default function Custom404() {
+1 -2
View File
@@ -27,7 +27,7 @@ import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calco
import prisma from "@calcom/prisma";
import { baseEventTypeSelect } from "@calcom/prisma/selects";
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import { Icon } from "@calcom/ui";
import { Icon, HeadSeo } from "@calcom/ui";
import { inferSSRProps } from "@lib/types/inferSSRProps";
import { EmbedProps } from "@lib/withEmbedSsr";
@@ -37,7 +37,6 @@ import { AvatarSSR } from "@components/ui/AvatarSSR";
import { ssrInit } from "@server/lib/ssr";
const HeadSeo = dynamic(() => import("@components/seo/head-seo"));
export default function User(props: inferSSRProps<typeof getServerSideProps> & EmbedProps) {
const { users, profile, eventTypes, isDynamicGroup, dynamicNames, dynamicUsernames, isSingleUser } = props;
const [user] = users; //To be used when we only have a single user, not dynamic group
+1 -2
View File
@@ -35,14 +35,13 @@ import { localStorage } from "@calcom/lib/webstorage";
import prisma, { baseUserSelect } from "@calcom/prisma";
import { Prisma } from "@calcom/prisma/client";
import { customInputSchema, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import { Button, EmailInput, Icon } from "@calcom/ui";
import { Button, EmailInput, Icon, HeadSeo } from "@calcom/ui";
import { timeZone } from "@lib/clock";
import { inferSSRProps } from "@lib/types/inferSSRProps";
import CancelBooking from "@components/booking/CancelBooking";
import EventReservationSchema from "@components/schemas/EventReservationSchema";
import { HeadSeo } from "@components/seo/head-seo";
import { ssrInit } from "@server/lib/ssr";
+1 -2
View File
@@ -8,8 +8,7 @@ import { isSAMLLoginEnabled } from "@calcom/features/ee/sso/lib/saml";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import { inferSSRProps } from "@calcom/types/inferSSRProps";
import { Alert, Button, EmailField, PasswordField, TextField } from "@calcom/ui";
import { HeadSeo } from "@calcom/web/components/seo/head-seo";
import { Alert, Button, EmailField, PasswordField, TextField, HeadSeo } from "@calcom/ui";
import { asStringOrNull } from "@calcom/web/lib/asStringOrNull";
import { WEBAPP_URL } from "@calcom/web/lib/config/constants";
import prisma from "@calcom/web/lib/prisma";
+1 -2
View File
@@ -12,12 +12,11 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
import useTheme from "@calcom/lib/hooks/useTheme";
import { getTeamWithMembers } from "@calcom/lib/server/queries/teams";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import { Avatar, Button, Icon } from "@calcom/ui";
import { Avatar, Button, Icon, HeadSeo } from "@calcom/ui";
import { useToggleQuery } from "@lib/hooks/useToggleQuery";
import { inferSSRProps } from "@lib/types/inferSSRProps";
import { HeadSeo } from "@components/seo/head-seo";
import Team from "@components/team/screens/Team";
import AvatarGroup from "@components/ui/AvatarGroup";
+1 -3
View File
@@ -4,12 +4,10 @@ import dayjs from "@calcom/dayjs";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { detectBrowserTimeFormat } from "@calcom/lib/timeFormat";
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
import { Button, Icon } from "@calcom/ui";
import { Button, Icon, HeadSeo } from "@calcom/ui";
import { inferSSRProps } from "@lib/types/inferSSRProps";
import { HeadSeo } from "@components/seo/head-seo";
export default function MeetingUnavailable(props: inferSSRProps<typeof getServerSideProps>) {
const { t } = useLocale();
@@ -5,9 +5,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
import { detectBrowserTimeFormat } from "@calcom/lib/timeFormat";
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
import type { inferSSRProps } from "@calcom/types/inferSSRProps";
import { Button, Icon } from "@calcom/ui";
import { HeadSeo } from "@components/seo/head-seo";
import { Button, Icon, HeadSeo } from "@calcom/ui";
export default function MeetingNotStarted(props: inferSSRProps<typeof getServerSideProps>) {
const { t } = useLocale();
+1 -3
View File
@@ -1,7 +1,5 @@
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Button, Icon } from "@calcom/ui";
import { HeadSeo } from "@components/seo/head-seo";
import { Button, Icon, HeadSeo } from "@calcom/ui";
export default function NoMeetingFound() {
const { t } = useLocale();
@@ -13,8 +13,8 @@ import {
AppUser,
} from "@calcom/types/AppGetServerSideProps";
import {
Alert,
Button,
Banner,
Badge,
ButtonGroup,
Dialog,
@@ -362,14 +362,14 @@ function SingleForm({ form, appUrl, Page }: SingleFormComponentProps) {
</Button>
</div>
{!form._count?.responses && (
<Banner
className="mt-6"
variant="neutral"
title="No Responses yet"
description="Wait for some time for responses to be collected. You can go and submit the form yourself as well."
Icon={Icon.FiInfo}
onDismiss={() => console.log("dismissed")}
/>
<>
<Alert
className="mt-6"
severity="neutral"
title="No responses yet"
message="Wait for some time for responses to be collected. You can go and submit the form yourself as well."
/>
</>
)}
</div>
<div className="w-full rounded-md border border-gray-200 p-8">
@@ -2,13 +2,18 @@ import { CheckCircleIcon, ExclamationIcon, InformationCircleIcon, XCircleIcon }
import classNames from "classnames";
import { ReactNode } from "react";
import { Icon } from "../icon";
export interface AlertProps {
title?: ReactNode;
// @TODO: Message should be children, more flexible?
message?: ReactNode;
// @TODO: Provide action buttons so style is always the same.
actions?: ReactNode;
className?: string;
iconClassName?: string;
severity: "success" | "warning" | "error" | "info";
// @TODO: Success and info shouldn't exist as per design?
severity: "success" | "warning" | "error" | "info" | "neutral";
}
export function Alert(props: AlertProps) {
const { severity, iconClassName } = props;
@@ -21,7 +26,8 @@ export function Alert(props: AlertProps) {
severity === "error" && "border-red-900 bg-red-50 text-red-800",
severity === "warning" && "border-yellow-700 bg-yellow-50 text-yellow-700",
severity === "info" && "border-sky-700 bg-sky-50 text-sky-700",
severity === "success" && "bg-gray-900 text-white"
severity === "success" && "bg-gray-900 text-white",
severity === "neutral" && "border-none bg-gray-100"
)}>
<div className="relative flex flex-col md:flex-row">
<div className="flex-shrink-0">
@@ -40,6 +46,9 @@ export function Alert(props: AlertProps) {
aria-hidden="true"
/>
)}
{severity === "neutral" && (
<Icon.FiInfo className={classNames("h-5 w-5 text-gray-800", iconClassName)} aria-hidden="true" />
)}
{severity === "success" && (
<CheckCircleIcon
className={classNames("h-5 w-5 text-gray-400", iconClassName)}
@@ -51,6 +60,7 @@ export function Alert(props: AlertProps) {
<h3 className="text-sm font-medium">{props.title}</h3>
<div className="text-sm">{props.message}</div>
</div>
{/* @TODO: Shouldn't be absolute. This makes it harder to give margin etc. */}
{props.actions && <div className="absolute top-1 right-1 text-sm md:relative">{props.actions}</div>}
</div>
</div>
@@ -0,0 +1,55 @@
import { Canvas, Meta, Story, ArgsTable } from '@storybook/addon-docs';
import { Examples, Example, Note, Title,CustomArgsTable,VariantsTable,VariantRow } from '@calcom/storybook/components'
import { Icon } from "@calcom/ui";
import {Alert} from './Alert';
<Meta title="UI/Alert" component={Alert} />
<Title title="Alert" suffix="Brief" subtitle="Version 2.0 — Last Update: 04 jan 2023"/>
## Definition
Alerts provide time-based feedback to the user after taking an action.
## Structure
Each alert has a state to represent neutral, positive or negative responses.
<CustomArgsTable of={Alert} />
<Examples title="Alert style">
<Example title="Error">
<Alert severity="error" title="Summarise what happened" message="Describe what can be done about it here." />
</Example>
<Example title="Warning">
<Alert severity="warning" title="Summarise what happened" message="Describe what can be done about it here." />
</Example>
<Example title="With actions">
<Alert
severity="warning"
title="Summarise what happened"
message="Describe what can be done about it here."
actions={<><button>Cancel me</button></>}
/>
</Example>
</Examples>
<Title offset title="Alert" suffix="Variants" />
<Canvas>
<Story
name="Alert">
<VariantsTable titles={['Default']} columnMinWidth={150}>
<VariantRow variant="Success">
<Alert severity="success" title="Summarise what happened" message="Describe what can be done about it here." />
</VariantRow>
<VariantRow variant="Warning">
<Alert severity="warning" title="Summarise what happened" message="Describe what can be done about it here." />
</VariantRow>
<VariantRow variant="Error">
<Alert severity="error" title="Summarise what happened" message="Describe what can be done about it here." />
</VariantRow>
<VariantRow variant="Neutral">
<Alert severity="neutral" title="Summarise what happened" message="Describe what can be done about it here." />
</VariantRow>
</VariantsTable>
</Story>
</Canvas>
+2
View File
@@ -0,0 +1,2 @@
export { Alert } from "./Alert";
export type { AlertProps } from "./Alert";
+1 -1
View File
@@ -8,7 +8,7 @@ import type { AppFrontendPayload as App } from "@calcom/types/App";
import type { CredentialFrontendPayload as Credential } from "@calcom/types/Credential";
import { Icon } from "@calcom/ui";
import EmptyScreen from "../../v2/core/EmptyScreen";
import { EmptyScreen } from "../empty-screen";
import { AppCard } from "./AppCard";
export function useShouldShowArrows() {
+1
View File
@@ -2,5 +2,6 @@ export { useShouldShowArrows, AllApps } from "./AllApps";
export { AppCard } from "./AppCard";
export { Slider } from "./Slider";
export { SkeletonLoader as AppSkeletonLoader } from "./SkeletonLoader";
export { SkeletonLoader } from "./SkeletonLoader";
export { TrendingAppsSlider } from "./TrendingAppsSlider";
export { AppStoreCategories } from "./Categories";
File renamed without changes.
@@ -0,0 +1,22 @@
import { Canvas, Meta, Story, ArgsTable } from '@storybook/addon-docs';
import { Examples, Example, Note, Title,CustomArgsTable,VariantsTable,VariantRow } from '@calcom/storybook/components'
import { Icon } from "@calcom/ui";
import Credits from './Credits';
<Meta title="UI/Credits" component={Credits} />
<Title title="Credits" suffix="Brief" subtitle="Version 2.0 — Last Update: 05 jan 2023"/>
## Definition
Shows the current copy right as well as app version name.
<Canvas>
<Story
name="Credits">
<VariantsTable titles={[]} columnMinWidth={150}>
<VariantRow variant="Default">
<Credits />
</VariantRow>
</VariantsTable>
</Story>
</Canvas>
+1
View File
@@ -0,0 +1 @@
export { default as Credits } from "./Credits";
@@ -1,6 +1,11 @@
import { classNames } from "@calcom/lib";
export default function VerticalDivider({ className, ...props }: JSX.IntrinsicElements["svg"]) {
export function Divider({ className, ...props }: JSX.IntrinsicElements["hr"]) {
className = classNames(className, "gray-200");
return <hr className={className} {...props} />;
}
export function VerticalDivider({ className, ...props }: JSX.IntrinsicElements["svg"]) {
className = classNames(className, "mx-3");
return (
<svg
@@ -0,0 +1,33 @@
import { Canvas, Meta, Story, ArgsTable } from '@storybook/addon-docs';
import { Examples, Example, Note, Title,CustomArgsTable,VariantsTable,VariantRow } from '@calcom/storybook/components'
import { Icon } from "@calcom/ui";
import {Divider, VerticalDivider} from './Divider';
<Meta title="UI/Divider" component={Divider} />
<Title title="Divider" suffix="Brief" subtitle="Version 2.0 — Last Update: 05 jan 2023"/>
## Definition
Shows the current copy right as well as app version name.
<Canvas>
<Story
name="Divider">
<VariantsTable titles={[]} columnMinWidth={150}>
<VariantRow variant="Horizontal">
<div className="dark:text-white">
Text above
<Divider />
Text below
</div>
</VariantRow>
<VariantRow variant="Vertical">
<div className="flex dark:text-white">
Text left
<VerticalDivider />
Text right
</div>
</VariantRow>
</VariantsTable>
</Story>
</Canvas>
+1
View File
@@ -0,0 +1 @@
export { Divider, VerticalDivider } from "./Divider";
@@ -5,7 +5,7 @@ import { SVGComponent } from "@calcom/types/SVGComponent";
import { Button } from "../../components/button";
export default function EmptyScreen({
export function EmptyScreen({
Icon,
headline,
description,
@@ -0,0 +1,28 @@
import { Canvas, Meta, Story, ArgsTable } from '@storybook/addon-docs';
import { Examples, Example, Note, Title,CustomArgsTable,VariantsTable,VariantRow } from '@calcom/storybook/components'
import { Icon } from "@calcom/ui";
import { EmptyScreen } from './EmptyScreen';
<Meta title="UI/EmptyScreen" component={EmptyScreen} />
<Title title="EmptyScreen" suffix="Brief" subtitle="Version 2.0 — Last Update: 05 jan 2023"/>
## Definition
An empty state is where no info/ apps are added to a page with typically content. There must be a text that prompts users to learn more about why they see this state if necessary.
The CTA button can prompt the user to add the info/ apps needed for the page.
<Canvas>
<Story
name="EmptyScreen">
<VariantsTable titles={[]} columnMinWidth={150}>
<VariantRow variant="Default">
<EmptyScreen
Icon={Icon.FiCalendar}
headline="Empty state header"
description="Do consectetur qui ex deserunt do est veniam commodo. Eiusmod eiusmod dolore nostrud pariatur mollit sit commodo. Learn More."
buttonText="Button text"
/>
</VariantRow>
</VariantsTable>
</Story>
</Canvas>
@@ -0,0 +1 @@
export { EmptyScreen } from "./EmptyScreen";
+2
View File
@@ -17,3 +17,5 @@ export {
} from "./inputs/Input";
export { Label } from "./inputs/Label";
export { Select, SelectField, SelectWithValidation, getReactSelectProps } from "./select";
export { TimezoneSelect } from "./timezone-select";
export type { ITimezone, ITimezoneOption } from "./timezone-select";
@@ -6,9 +6,9 @@ import BaseSelect, {
Props as SelectProps,
} from "react-timezone-select";
import { getReactSelectProps } from "../../components/form";
import { getReactSelectProps } from "../select";
function TimezoneSelect({ className, components, ...props }: SelectProps) {
export function TimezoneSelect({ className, components, ...props }: SelectProps) {
const reactSelectProps = useMemo(() => {
return getReactSelectProps({ className, components: components || {} });
}, [className, components]);
@@ -25,5 +25,4 @@ function TimezoneSelect({ className, components, ...props }: SelectProps) {
);
}
export default TimezoneSelect;
export type { ITimezone, ITimezoneOption };
@@ -0,0 +1,2 @@
export { TimezoneSelect } from "./TimezoneSelect";
export type { ITimezone, ITimezoneOption } from "./TimezoneSelect";
@@ -10,10 +10,9 @@ import {
} from "@calcom/lib/OgImages";
import { getBrowserInfo } from "@calcom/lib/browser/browser.utils";
import { APP_NAME } from "@calcom/lib/constants";
import { seoConfig, getSeoImage } from "@calcom/lib/next-seo.config";
import { truncateOnWord } from "@calcom/lib/text";
import { getSeoImage, seoConfig } from "@lib/config/next-seo.config";
export type HeadSeoProps = {
title: string;
description: string;
+1
View File
@@ -0,0 +1 @@
export { HeadSeo } from "./HeadSeo";
+11
View File
@@ -26,11 +26,14 @@ export {
Select,
SelectField,
SelectWithValidation,
TimezoneSelect,
} from "./form";
export type { ITimezone, ITimezoneOption } from "./form";
export {
AllApps,
AppCard,
AppSkeletonLoader,
SkeletonLoader,
Slider,
TrendingAppsSlider,
useShouldShowArrows,
@@ -44,4 +47,12 @@ export type { ActionType } from "./table/TableActions";
export { Icon } from "./icon";
export { ErrorBoundary } from "./errorBoundary";
export { Logo } from "./logo";
export { Alert } from "./alert";
export type { AlertProps } from "./alert";
export { Credits } from "./credits";
export { Divider, VerticalDivider } from "./divider";
export { EmptyScreen } from "./empty-screen";
export { List, ListItem, ListItemText, ListItemTitle, ListLinkItem } from "./list";
export type { ListItemProps, ListProps } from "./list";
export { HeadSeo } from "./head-seo";
export { Skeleton, SkeletonAvatar, SkeletonButton, SkeletonContainer, SkeletonText } from "./skeleton";
+42
View File
@@ -5,6 +5,8 @@ import classNames from "@calcom/lib/classNames";
export type ListProps = {
roundContainer?: boolean;
// @TODO: Do we still need this? Coming from old v2 component. Prefer to delete it :)
noBorderTreatment?: boolean;
} & JSX.IntrinsicElements["ul"];
export function List(props: ListProps) {
@@ -15,6 +17,7 @@ export function List(props: ListProps) {
"-mx-4 rounded-sm sm:mx-0 sm:overflow-hidden ",
// Add rounded top and bottome if roundContainer is true
props.roundContainer && "[&>*:first-child]:rounded-t-md [&>*:last-child]:rounded-b-md ",
!props.noBorderTreatment && "divide-y divide-neutral-200 rounded-md border border-l border-r ",
props.className
)}>
{props.children}
@@ -56,6 +59,45 @@ export function ListItem(props: ListItemProps) {
);
}
export type ListLinkItemProps = {
href: string;
heading: string;
subHeading: string;
disabled?: boolean;
actions?: JSX.Element;
} & JSX.IntrinsicElements["li"];
export function ListLinkItem(props: ListLinkItemProps) {
const { href, heading = "", children, disabled = false, actions = <div /> } = props;
let subHeading = props.subHeading;
if (!subHeading) {
subHeading = "";
}
return (
<li
className={classNames(
"group flex w-full items-center justify-between p-5 hover:bg-neutral-50",
disabled ? "hover:bg-white" : ""
)}>
<Link passHref href={href}>
<a
className={classNames(
"flex-grow truncate text-sm",
disabled ? "pointer-events-none cursor-not-allowed opacity-30" : ""
)}>
<h1 className="text-sm font-semibold leading-none">{heading}</h1>
<h2 className="min-h-4 mt-2 text-sm font-normal leading-none">
{subHeading.substring(0, 100)}
{subHeading.length > 100 && "..."}
</h2>
<div className="mt-2">{children}</div>
</a>
</Link>
{actions}
</li>
);
}
export function ListItemTitle<TComponent extends keyof JSX.IntrinsicElements = "span">(
props: JSX.IntrinsicElements[TComponent] & { component?: TComponent }
) {
+1 -1
View File
@@ -1,2 +1,2 @@
export { List, ListItem, ListItemText, ListItemTitle } from "./List";
export { List, ListItem, ListItemText, ListItemTitle, ListLinkItem } from "./List";
export type { ListItemProps, ListProps } from "./List";
@@ -0,0 +1,22 @@
import { Canvas, Meta, Story, ArgsTable } from '@storybook/addon-docs';
import { Examples, Example, Note, Title,CustomArgsTable,VariantsTable,VariantRow } from '@calcom/storybook/components'
import { Icon } from "@calcom/ui";
import { List, ListItem } from './List';
<Meta title="UI/List" component={List} />
<Title title="List" suffix="Brief" subtitle="Version 2.0 — Last Update: 05 jan 2023"/>
## Definition
Sums it up nicely.
<Canvas>
<Story
name="List">
<VariantsTable titles={[]} columnMinWidth={150}>
<VariantRow variant="Default">
TODO!
</VariantRow>
</VariantsTable>
</Story>
</Canvas>
+1 -1
View File
@@ -6,7 +6,7 @@ import classNames from "@calcom/lib/classNames";
import { getErrorFromUnknown } from "@calcom/lib/errors";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Alert } from "../v2/core/Alert";
import { Alert } from "../components/alert";
import { showToast } from "../v2/core/notifications";
type InputProps = Omit<JSX.IntrinsicElements["input"], "name"> & { name: string };
+29 -21
View File
@@ -8,10 +8,14 @@ export {
Button,
ButtonGroup,
Checkbox,
Credits,
Divider,
EmailField,
EmailInput,
EmptyScreen,
FieldsetLegend,
Form,
HeadSeo,
HintsOrErrors,
Input,
InputField,
@@ -19,6 +23,11 @@ export {
InputFieldWithSelect,
InputLeading,
Label,
List,
ListItem,
ListItemText,
ListItemTitle,
ListLinkItem,
PasswordField,
TextArea,
TextAreaField,
@@ -29,6 +38,8 @@ export {
SelectField,
SelectWithValidation,
TableActions,
TimezoneSelect,
VerticalDivider,
Skeleton,
SkeletonAvatar,
SkeletonText,
@@ -37,16 +48,29 @@ export {
DropdownActions,
Icon,
ErrorBoundary,
Alert,
TrendingAppsSlider,
AppCard,
AllApps,
AppSkeletonLoader,
SkeletonLoader,
AppStoreCategories,
Slider,
useShouldShowArrows,
} from "./components";
export type { ActionType } from "./components";
export type { AvatarProps, BadgeProps, ButtonBaseProps, ButtonProps, TopBannerProps } from "./components";
export type {
ActionType,
AlertProps,
AvatarProps,
BadgeProps,
ButtonBaseProps,
ButtonProps,
ITimezone,
ITimezoneOption,
ListItemProps,
ListProps,
TopBannerProps,
} from "./components";
export { default as CheckboxField } from "./components/form/checkbox/Checkbox";
/** ⬇️ TODO - Move these to components */
export { default as AddressInput } from "./form/AddressInputLazy";
@@ -54,21 +78,10 @@ export { default as PhoneInput } from "./form/PhoneInputLazy";
export { UnstyledSelect } from "./form/Select";
export { default as Loader } from "./v2/core/Loader";
export { default as TimezoneChangeDialog } from "./TimezoneChangeDialog";
export {
Alert,
EmptyScreen,
HorizontalTabs,
SettingsToggle,
showToast,
Swatch,
Switch,
Card,
VerticalTabs,
} from "./v2";
export type { AlertProps } from "./v2";
export { Segment, SegmentOption } from "./v2/core";
export { HorizontalTabs, SettingsToggle, showToast, Swatch, Switch, Card, VerticalTabs } from "./v2";
export { default as Shell, ShellMain, MobileNavigationMoreItems, ShellSubHeading } from "./v2/core/Shell";
export { default as Banner } from "./v2/core/banner";
export { default as ColorPicker } from "./v2/core/colorpicker";
export { default as ConfirmationDialogContent } from "./v2/core/ConfirmationDialogContent";
export {
@@ -80,7 +93,6 @@ export {
DialogTrigger,
} from "./v2/core/Dialog";
export type { DialogProps } from "./v2/core/Dialog";
export { default as Divider } from "./v2/core/Divider";
export {
Dropdown,
DropdownItem,
@@ -106,14 +118,10 @@ export { default as InstalledAppsLayout } from "./v2/core/layouts/InstalledAppsL
export { default as SettingsLayout, getLayout as getSettingsLayout } from "./v2/core/layouts/SettingsLayout";
export { default as WizardLayout, getLayout as getWizardLayout } from "./v2/core/layouts/WizardLayout";
export { default as LinkIconButton } from "./v2/core/LinkIconButton";
export { List, ListItem, ListItemText, ListItemTitle, ListLinkItem } from "./v2/core/List";
export { default as MeetingTimeInTimezones } from "./v2/core/MeetingTimeInTimezones";
export { default as Meta, MetaProvider } from "./v2/core/Meta";
export { StepCard } from "./v2/core/StepCard";
export { default as Stepper } from "./v2/core/Stepper";
export { Steps } from "./v2/core/Steps";
export { default as TimezoneSelect } from "./v2/core/TimezoneSelect";
export type { ITimezone, ITimezoneOption } from "./v2/core/TimezoneSelect";
export { Tooltip } from "./v2/core/Tooltip";
export { default as VerticalDivider } from "./v2/core/VerticalDivider";
export { default as WizardForm } from "./v2/core/WizardForm";
-6
View File
@@ -1,6 +0,0 @@
import { classNames } from "@calcom/lib";
export default function Divider({ className, ...props }: JSX.IntrinsicElements["hr"]) {
className = classNames(className, "gray-200");
return <hr className={className} {...props} />;
}
-117
View File
@@ -1,117 +0,0 @@
import Link from "next/link";
import { createElement } from "react";
import classNames from "@calcom/lib/classNames";
export function List(props: JSX.IntrinsicElements["ul"] & { noBorderTreatment?: true }) {
const { noBorderTreatment, ...rest } = props;
return (
<ul
{...rest}
className={classNames(
"sm:mx-0 sm:overflow-hidden",
!noBorderTreatment && "divide-y divide-neutral-200 rounded-md border border-l border-r ",
props.className
)}>
{props.children}
</ul>
);
}
export type ListItemProps = { expanded?: boolean } & ({ href?: never } & JSX.IntrinsicElements["li"]);
export type ListLinkItemProps = {
href: string;
heading: string;
subHeading: string;
disabled?: boolean;
actions?: JSX.Element;
} & JSX.IntrinsicElements["li"];
export function ListItem(props: ListItemProps) {
const { href, expanded, ...passThroughProps } = props;
const elementType = href ? "a" : "li";
const element = createElement(
elementType,
{
...passThroughProps,
className: classNames(
"items-center bg-white min-w-0 flex-1 flex border-gray-200",
expanded ? "my-2 border" : "border -mb-px last:mb-0",
props.className,
(props.onClick || href) && "hover:bg-neutral-50"
),
},
props.children
);
return href ? (
<Link passHref href={href}>
{element}
</Link>
) : (
element
);
}
export function ListLinkItem(props: ListLinkItemProps) {
const { href, heading = "", children, disabled = false, actions = <div /> } = props;
let subHeading = props.subHeading;
if (!subHeading) {
subHeading = "";
}
return (
<li
className={classNames(
"group flex w-full items-center justify-between p-5 hover:bg-neutral-50",
disabled ? "hover:bg-white" : ""
)}>
<Link passHref href={href}>
<a
className={classNames(
"flex-grow truncate text-sm",
disabled ? "pointer-events-none cursor-not-allowed opacity-30" : ""
)}>
<h1 className="text-sm font-semibold leading-none">{heading}</h1>
<h2 className="min-h-4 mt-2 text-sm font-normal leading-none">
{subHeading.substring(0, 100)}
{subHeading.length > 100 && "..."}
</h2>
<div className="mt-2">{children}</div>
</a>
</Link>
{actions}
</li>
);
}
export function ListItemTitle<TComponent extends keyof JSX.IntrinsicElements = "span">(
props: JSX.IntrinsicElements[TComponent] & { component?: TComponent }
) {
const { component = "span", ...passThroughProps } = props;
return createElement(
component,
{
...passThroughProps,
className: classNames("text-sm font-medium text-neutral-900 truncate", props.className),
},
props.children
);
}
export function ListItemText<TComponent extends keyof JSX.IntrinsicElements = "span">(
props: JSX.IntrinsicElements[TComponent] & { component?: TComponent }
) {
const { component = "span", ...passThroughProps } = props;
return createElement(
component,
{
...passThroughProps,
className: classNames("text-sm text-gray-500 truncate", props.className),
},
props.children
);
}
-29
View File
@@ -1,29 +0,0 @@
import { ReactNode } from "react";
import { Badge, BadgeProps } from "../../components/badge";
type Props = {
title: string;
description?: string;
badgeText?: string;
badgeVariant?: BadgeProps["variant"];
infoIcon?: string;
rightAlignedComponent?: ReactNode;
};
function PageHeader({ title, description, rightAlignedComponent, badgeText, badgeVariant }: Props) {
return (
<div className="flex items-center">
<div className="mr-4 flex-col">
<div className="flex w-full items-center space-x-2 rtl:space-x-reverse">
<h1 className="font-cal text-xl font-semibold text-black">{title}</h1>
{badgeText && badgeVariant && <Badge variant={badgeVariant}>{badgeText}</Badge>}
</div>
<h2 className="text-sm text-gray-600">{description}</h2>
</div>
<div className="ml-auto ">{rightAlignedComponent && rightAlignedComponent}</div>
</div>
);
}
export default PageHeader;
-11
View File
@@ -1,11 +0,0 @@
import React, { Fragment, ReactNode } from "react";
import ReactDOM from "react-dom";
export const Portal = (props: { children: React.ReactNode }) => {
const portalContainer = document.getElementById("Portal");
// This should never be the case as it lives in _document.tsx
if (!portalContainer) return null;
return ReactDOM.createPortal(<Fragment>{props.children}</Fragment>, portalContainer);
};
-36
View File
@@ -1,36 +0,0 @@
import * as RadioGroup from "@radix-ui/react-radio-group";
import React from "react";
import { Label } from "../../components/form";
type SegmentProps = {
label: string;
children: React.ReactNode;
} & RadioGroup.RadioGroupProps;
export function Segment({ label, children, ...rest }: SegmentProps) {
return (
<div>
<div className="inline-flex flex-col">
{label && <Label>{label}</Label>}
<RadioGroup.Root
className="flex space-x-1 rounded-md border-gray-200 bg-white p-[2px] text-sm font-medium leading-5 "
{...rest}>
{children}
</RadioGroup.Root>
</div>
</div>
);
}
type SegmentOptionProps = { value: string; children: React.ReactNode } & RadioGroup.RadioGroupItemProps;
export function SegmentOption({ value, children, ...rest }: SegmentOptionProps) {
return (
<RadioGroup.Item
value={value}
className="radix-disabled:opacity-40 radix-state-checked:bg-gray-200 radix-state-checked:hover:bg-gray-200 rounded-[4px] py-[6px] px-3 hover:bg-gray-100"
{...rest}>
{children}
</RadioGroup.Item>
);
}
+2 -2
View File
@@ -35,11 +35,11 @@ import {
TimezoneChangeDialog,
} from "../..";
import { Logo, ErrorBoundary } from "../../components";
import { Credits } from "../../components/credits";
import { HeadSeo } from "../../components/head-seo";
import { Icon } from "../../components/icon";
import { SkeletonText } from "../../components/skeleton";
import Credits from "./Credits";
import { KBarContent, KBarRoot, KBarTrigger } from "./Kbar";
import HeadSeo from "./head-seo";
/* TODO: Migate this */
-65
View File
@@ -1,65 +0,0 @@
import { MouseEvent, useState } from "react";
import classNames from "@calcom/lib/classNames";
import { SVGComponent } from "@calcom/types/SVGComponent";
const stylesByVariant = {
neutral: { background: "bg-gray-100 ", text: "!text-gray-800", hover: "hover:!bg-gray-200" },
warning: { background: "bg-orange-100 ", text: "!text-orange-800", hover: "hover:!bg-orange-200" },
error: { background: "bg-red-100 ", text: "!text-red-800", hover: "hover:!bg-red-200" },
};
export type BannerProps = {
title: string;
description?: string;
variant: keyof typeof stylesByVariant;
errorMessage?: string;
Icon?: SVGComponent;
onDismiss: (event: MouseEvent<HTMLElement, globalThis.MouseEvent>) => void;
onAction?: (event: MouseEvent<HTMLElement, globalThis.MouseEvent>) => void;
actionText?: string;
} & JSX.IntrinsicElements["div"];
const Banner = (props: BannerProps) => {
const { variant, errorMessage, title, description, className, Icon, ...rest } = props;
const [show, setShow] = useState(true);
if (!show) {
return null;
}
return (
<div
className={classNames(
"flex items-center rounded-md px-4 py-4",
stylesByVariant[variant].background,
stylesByVariant[variant].text,
className
)}
{...rest}>
<div className={classNames("flex flex-col text-sm")}>
<div className="mb-2">{Icon && <Icon className="h-4 w-4" />}</div>
<div className="flex flex-col space-y-2">
<h1 className="font-semibold leading-none">{title}</h1>
{description && <h2 className="font-normal leading-4">{description}</h2>}
{props.variant === "error" && <p className="ml-4 pt-2 font-mono text-xs">{errorMessage}</p>}
</div>
<div className="mt-4 flex w-full items-center gap-4 self-start text-sm font-medium">
{props.actionText && (
<button className="text-sm font-medium leading-4" onClick={() => props.onAction}>
Action
</button>
)}
<button
className="text-sm font-medium leading-4"
onClick={(e) => {
setShow(false);
props.onDismiss(e);
}}>
Dismiss
</button>
</div>
</div>
</div>
);
};
export default Banner;
+1 -1
View File
@@ -4,7 +4,7 @@ import { classNames } from "@calcom/lib";
import { Icon } from "../../..";
import { Badge, BadgeProps } from "../../../components";
import Divider from "../Divider";
import { Divider } from "../../../components/divider";
type Action = { check: () => boolean; fn: () => void };
export default function FormCard({
-102
View File
@@ -1,102 +0,0 @@
import merge from "lodash/merge";
import { NextSeo, NextSeoProps } from "next-seo";
import { constructAppImage, constructGenericImage, constructMeetingImage } from "@calcom/lib/OgImages";
import { getBrowserInfo } from "@calcom/lib/browser/browser.utils";
import { APP_NAME } from "@calcom/lib/constants";
import { seoConfig, getSeoImage, HeadSeoProps } from "@calcom/lib/next-seo.config";
import { truncateOnWord } from "@calcom/lib/text";
/**
* Build full seo tags from title, desc, canonical and url
*/
const buildSeoMeta = (pageProps: {
title: string;
description: string;
image: string;
siteName?: string;
url?: string;
canonical?: string;
}): NextSeoProps => {
const { title, description, image, canonical, siteName = seoConfig.headSeo.siteName } = pageProps;
return {
title: title,
canonical: canonical,
openGraph: {
site_name: siteName,
type: "website",
title: title,
description: description,
images: [
{
url: image,
},
],
},
additionalMetaTags: [
{
property: "name",
content: title,
},
{
property: "description",
content: description,
},
{
name: "description",
content: description,
},
{
property: "image",
content: image,
},
],
};
};
export const HeadSeo = (props: HeadSeoProps): JSX.Element => {
const defaultUrl = getBrowserInfo()?.url;
const { title, description, siteName, canonical = defaultUrl, nextSeoProps = {}, app, meeting } = props;
const image = getSeoImage("ogImage") + constructGenericImage({ title, description });
const truncatedDescription = truncateOnWord(description, 158);
const pageTitle = title + " | " + APP_NAME;
let seoObject = buildSeoMeta({
title: pageTitle,
image,
description: truncatedDescription,
canonical,
siteName,
});
if (meeting) {
const pageImage = getSeoImage("ogImage") + constructMeetingImage(meeting);
seoObject = buildSeoMeta({
title: pageTitle,
description: truncatedDescription,
image: pageImage,
canonical,
siteName,
});
}
if (app) {
const pageImage =
getSeoImage("ogImage") + constructAppImage({ ...app, description: truncatedDescription });
seoObject = buildSeoMeta({
title: pageTitle,
description: truncatedDescription,
image: pageImage,
canonical,
siteName,
});
}
const seoProps: NextSeoProps = merge(nextSeoProps, seoObject);
return <NextSeo {...seoProps} />;
};
export default HeadSeo;
-10
View File
@@ -1,6 +1,3 @@
export { Alert } from "./Alert";
export type { AlertProps } from "./Alert";
export { default as Banner } from "./banner";
export { Card } from "./Card";
export type { BaseCardProps } from "./Card";
export { default as ColorPicker } from "./colorpicker";
@@ -22,11 +19,8 @@ export {
DropdownMenuTrigger,
DropdownMenuTriggerItem,
} from "./Dropdown";
export { default as EmptyScreen } from "./EmptyScreen";
export { Checkbox, DatePicker, FormStep, Radio, RadioGroup } from "./form";
export { default as LinkIconButton } from "./LinkIconButton";
export { List, ListItem } from "./List";
export type { ListItemProps } from "./List";
export { default as Loader } from "./Loader";
export { default as MeetingTimeInTimezones } from "./MeetingTimeInTimezones";
export type { HorizontalTabItemProps } from "./navigation/tabs/HorizontalTabItem";
@@ -34,14 +28,10 @@ export { default as HorizontalTabs, HorizontalTabItem } from "./navigation/tabs/
export type { VerticalTabItemProps } from "./navigation/tabs/VerticalTabItem";
export { default as VerticalTabs, VerticalTabItem } from "./navigation/tabs/VerticalTabs";
export { default as showToast } from "./notifications";
export { default as PageHeader } from "./PageHeader";
export { Portal } from "./Portal";
export { Segment, SegmentOption } from "./Segment";
export { default as SettingsToggle } from "./SettingsToggle";
export { default as Shell } from "./Shell";
export { default as Stepper } from "./Stepper";
export { default as Swatch } from "./Swatch";
export { default as Switch } from "./Switch";
export { default as Tooltip } from "./Tooltip";
export { default as VerticalDivider } from "./VerticalDivider";
export { default as WizardForm } from "./WizardForm";