2022-09-21 16:01:47 +00:00
|
|
|
import React from "react";
|
|
|
|
|
2022-07-23 00:39:50 +00:00
|
|
|
import classNames from "@calcom/lib/classNames";
|
2022-09-21 16:01:47 +00:00
|
|
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
2022-07-23 00:39:50 +00:00
|
|
|
|
|
|
|
type SkeletonBaseProps = {
|
|
|
|
className?: string;
|
|
|
|
};
|
|
|
|
|
|
|
|
interface SkeletonContainer {
|
|
|
|
as?: keyof JSX.IntrinsicElements;
|
|
|
|
children?: React.ReactNode;
|
|
|
|
className?: string;
|
|
|
|
}
|
|
|
|
|
2022-09-09 08:22:39 +00:00
|
|
|
const SkeletonAvatar: React.FC<SkeletonBaseProps> = ({ className }) => {
|
2023-04-05 18:14:46 +00:00
|
|
|
return <div className={classNames(`bg-emphasis mt-1 rounded-full ltr:mr-2 rtl:ml-2`, className)} />;
|
2022-07-23 00:39:50 +00:00
|
|
|
};
|
|
|
|
|
2022-09-21 16:01:47 +00:00
|
|
|
type SkeletonProps<T> = {
|
|
|
|
as: keyof JSX.IntrinsicElements | React.FC;
|
|
|
|
className?: string;
|
|
|
|
children: React.ReactNode;
|
|
|
|
loading?: boolean;
|
|
|
|
waitForTranslation?: boolean;
|
|
|
|
loadingClassName?: string;
|
|
|
|
} & (T extends React.FC<infer P>
|
|
|
|
? P
|
|
|
|
: T extends keyof JSX.IntrinsicElements
|
|
|
|
? JSX.IntrinsicElements[T]
|
|
|
|
: never);
|
|
|
|
|
|
|
|
const Skeleton = <T extends keyof JSX.IntrinsicElements | React.FC>({
|
|
|
|
as,
|
|
|
|
className = "",
|
|
|
|
children,
|
|
|
|
loading = false,
|
|
|
|
/**
|
|
|
|
* Assumes that the text needs translation by default and wait for it.
|
|
|
|
*/
|
|
|
|
waitForTranslation = true,
|
|
|
|
/**
|
|
|
|
* Classes that you need only in loading state
|
|
|
|
*/
|
|
|
|
loadingClassName = "",
|
|
|
|
...rest
|
|
|
|
}: SkeletonProps<T>) => {
|
|
|
|
const { isLocaleReady } = useLocale();
|
|
|
|
loading = (waitForTranslation ? !isLocaleReady : false) || loading;
|
|
|
|
const Component = as;
|
|
|
|
return (
|
|
|
|
<Component
|
|
|
|
className={classNames(
|
|
|
|
loading
|
2023-05-31 12:30:09 +00:00
|
|
|
? classNames("font-size-0 bg-emphasis animate-pulse rounded-md text-transparent", loadingClassName)
|
2022-09-21 16:01:47 +00:00
|
|
|
: "",
|
|
|
|
className
|
|
|
|
)}
|
|
|
|
{...rest}>
|
|
|
|
{children}
|
|
|
|
</Component>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2023-06-05 09:24:16 +00:00
|
|
|
const SkeletonText: React.FC<SkeletonBaseProps & { invisible?: boolean; style?: React.CSSProperties }> = ({
|
2022-09-15 19:53:09 +00:00
|
|
|
className = "",
|
|
|
|
invisible = false,
|
2023-06-05 09:24:16 +00:00
|
|
|
style,
|
2022-09-15 19:53:09 +00:00
|
|
|
}) => {
|
2022-07-23 00:39:50 +00:00
|
|
|
return (
|
2022-09-08 00:38:37 +00:00
|
|
|
<span
|
2023-06-05 09:24:16 +00:00
|
|
|
style={style}
|
2022-07-23 00:39:50 +00:00
|
|
|
className={classNames(
|
2023-05-31 12:30:09 +00:00
|
|
|
`font-size-0 bg-emphasis inline-block animate-pulse rounded-md empty:before:inline-block empty:before:content-['']`,
|
2022-09-15 19:53:09 +00:00
|
|
|
className,
|
|
|
|
invisible ? "invisible" : ""
|
2022-07-23 00:39:50 +00:00
|
|
|
)}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2022-09-09 08:22:39 +00:00
|
|
|
const SkeletonButton: React.FC<SkeletonBaseProps> = ({ className }) => {
|
2022-07-23 00:39:50 +00:00
|
|
|
return (
|
|
|
|
<SkeletonContainer>
|
2023-04-05 18:14:46 +00:00
|
|
|
<div className={classNames(`bg-emphasis rounded-md`, className)} />
|
2022-07-23 00:39:50 +00:00
|
|
|
</SkeletonContainer>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
const SkeletonContainer: React.FC<SkeletonContainer> = ({ children, as, className }) => {
|
|
|
|
const Component = as || "div";
|
|
|
|
return <Component className={classNames("animate-pulse", className)}>{children}</Component>;
|
|
|
|
};
|
|
|
|
|
2022-09-21 16:01:47 +00:00
|
|
|
export { Skeleton, SkeletonAvatar, SkeletonText, SkeletonButton, SkeletonContainer };
|
2023-01-10 15:39:29 +00:00
|
|
|
export { default as Loader } from "./Loader";
|