From d61238c8322d82e7a9058edf7f893ae023717acc Mon Sep 17 00:00:00 2001
From: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Date: Tue, 26 Apr 2022 15:11:02 +0100
Subject: [PATCH] Loader Components (#2616)

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
---
 apps/web/components/apps/SkeletonLoader.tsx   | 10 +++---
 .../availability/SkeletonLoader.tsx           |  8 +++--
 .../web/components/booking/SkeletonLoader.tsx | 12 ++++---
 .../components/eventtype/SkeletonLoader.tsx   | 22 ++++++------
 packages/ui/index.tsx                         |  1 +
 packages/ui/skeleton/index.tsx                | 34 +++++++++++++++++++
 6 files changed, 65 insertions(+), 22 deletions(-)
 create mode 100644 packages/ui/skeleton/index.tsx

diff --git a/apps/web/components/apps/SkeletonLoader.tsx b/apps/web/components/apps/SkeletonLoader.tsx
index 8dc31def..a4ee7d04 100644
--- a/apps/web/components/apps/SkeletonLoader.tsx
+++ b/apps/web/components/apps/SkeletonLoader.tsx
@@ -1,5 +1,7 @@
 import React from "react";
 
+import { SkeletonAvatar, SkeletonText } from "@calcom/ui";
+
 import { ShellSubHeading } from "@components/Shell";
 
 function SkeletonLoader() {
@@ -22,16 +24,16 @@ function SkeletonItem() {
     <li className="group flex w-full items-center justify-between p-3">
       <div className="flex-grow truncate text-sm">
         <div className="flex justify-start space-x-2">
-          <div className="h-10 w-10 rounded-lg bg-gray-100"></div>
+          <SkeletonText width="10" height="10"></SkeletonText>
           <div className="space-y-2">
-            <div className="h-4 w-32 rounded-md bg-gray-100"></div>
-            <div className="h-4 w-16 rounded-md bg-gray-100"></div>
+            <SkeletonText height="4" width="32"></SkeletonText>
+            <SkeletonText height="4" width="16"></SkeletonText>
           </div>
         </div>
       </div>
       <div className="mt-4 hidden flex-shrink-0 sm:mt-0 sm:ml-5 lg:flex">
         <div className="flex justify-between space-x-2 rtl:space-x-reverse">
-          <div className="h-11 w-32 rounded-md bg-gray-100"></div>
+          <SkeletonText width="32" height="11"></SkeletonText>
         </div>
       </div>
     </li>
diff --git a/apps/web/components/availability/SkeletonLoader.tsx b/apps/web/components/availability/SkeletonLoader.tsx
index 2ba945b8..2c12aa37 100644
--- a/apps/web/components/availability/SkeletonLoader.tsx
+++ b/apps/web/components/availability/SkeletonLoader.tsx
@@ -1,5 +1,7 @@
 import React from "react";
 
+import { SkeletonText } from "@calcom/ui";
+
 function SkeletonLoader() {
   return (
     <ul className="animate-pulse divide-y divide-neutral-200 border border-gray-200 bg-white sm:mx-0 sm:overflow-hidden">
@@ -17,13 +19,13 @@ function SkeletonItem() {
     <li className="group flex w-full items-center justify-between px-2 py-[23px] sm:px-6">
       <div className="flex-grow truncate text-sm">
         <div className="flex flex-col space-y-2">
-          <div className="h-4 w-32 rounded-md bg-gray-100"></div>
-          <div className="h-2 w-32 rounded-md bg-gray-100"></div>
+          <SkeletonText width="32" height="4"></SkeletonText>
+          <SkeletonText width="32" height="2"></SkeletonText>
         </div>
       </div>
       <div className="mt-4 hidden flex-shrink-0 sm:mt-0 sm:ml-5 lg:flex">
         <div className="flex justify-between space-x-2 rtl:space-x-reverse">
-          <div className="h-6 w-12 rounded-md bg-gray-100"></div>
+          <SkeletonText width="12" height="6"></SkeletonText>
         </div>
       </div>
     </li>
diff --git a/apps/web/components/booking/SkeletonLoader.tsx b/apps/web/components/booking/SkeletonLoader.tsx
index e152b4d1..2e50ad0e 100644
--- a/apps/web/components/booking/SkeletonLoader.tsx
+++ b/apps/web/components/booking/SkeletonLoader.tsx
@@ -1,5 +1,7 @@
 import React from "react";
 
+import { SkeletonText } from "@calcom/ui";
+
 import BookingsShell from "@components/BookingsShell";
 
 function SkeletonLoader() {
@@ -20,16 +22,16 @@ function SkeletonItem() {
       <div className="flex-grow truncate text-sm">
         <div className="flex">
           <div className="flex flex-col space-y-2">
-            <div className="h-5 w-32 rounded-md bg-gray-100"></div>
-            <div className="h-4 w-16 rounded-md bg-gray-100"></div>
+            <SkeletonText width="32" height="5" />
+            <SkeletonText width="16" height="4" />
           </div>
-          <div className="ml-4 h-5 w-24 rounded-md bg-gray-100"></div>
+          <SkeletonText width="24" height="5" className="ml-4" />
         </div>
       </div>
       <div className="mt-4 hidden flex-shrink-0 sm:mt-0 sm:ml-5 lg:flex">
         <div className="flex justify-between space-x-2 rtl:space-x-reverse">
-          <div className="h-6 w-16 rounded-md bg-gray-100"></div>
-          <div className="h-6 w-32 rounded-md bg-gray-100"></div>
+          <SkeletonText width="16" height="6" />
+          <SkeletonText width="32" height="6" />
         </div>
       </div>
     </li>
diff --git a/apps/web/components/eventtype/SkeletonLoader.tsx b/apps/web/components/eventtype/SkeletonLoader.tsx
index 8960352b..2397c698 100644
--- a/apps/web/components/eventtype/SkeletonLoader.tsx
+++ b/apps/web/components/eventtype/SkeletonLoader.tsx
@@ -2,22 +2,24 @@ import { LinkIcon } from "@heroicons/react/outline";
 import { ClockIcon, DotsHorizontalIcon, ExternalLinkIcon, UserIcon } from "@heroicons/react/solid";
 import React from "react";
 
+import { SkeletonAvatar, SkeletonContainer, SkeletonText } from "@calcom/ui";
+
 function SkeletonLoader() {
   return (
-    <div className="animate-pulse">
+    <SkeletonContainer>
       <div className="mb-4 flex items-center">
-        <div className="mt-1 h-8 w-8 rounded-full bg-gray-200 ltr:mr-2 rtl:ml-2" />
+        <SkeletonAvatar width="8" height="8"></SkeletonAvatar>
         <div className="space-y-1">
-          <div className="h-4 w-16 rounded-md bg-gray-200"></div>
-          <div className="h-4 w-24 rounded-md bg-gray-200"></div>
+          <SkeletonText height="4" width="16"></SkeletonText>
+          <SkeletonText height="4" width="24"></SkeletonText>
         </div>
       </div>
-      <ul className="animate-pulse divide-y divide-neutral-200 border border-gray-200 bg-white sm:mx-0 sm:overflow-hidden">
+      <ul className="divide-y divide-neutral-200 border border-gray-200 bg-white sm:mx-0 sm:overflow-hidden">
         <SkeletonItem />
         <SkeletonItem />
         <SkeletonItem />
       </ul>
-    </div>
+    </SkeletonContainer>
   );
 }
 
@@ -28,17 +30,17 @@ function SkeletonItem() {
     <li className="group flex w-full items-center justify-between px-4 py-4 sm:px-6">
       <div className="flex-grow truncate text-sm">
         <div>
-          <div className="h-5 w-32 rounded-md bg-gray-100"></div>
+          <SkeletonText width="32" height="5"></SkeletonText>
         </div>
-        <div className="text-neutral-500 dark:text-white">
+        <div className="">
           <ul className="mt-2 flex space-x-4 rtl:space-x-reverse ">
             <li className="flex items-center whitespace-nowrap">
               <ClockIcon className="mt-0.5 mr-1.5 inline h-4 w-4 text-gray-200"></ClockIcon>
-              <div className="h-4 w-12 rounded-md bg-gray-100"></div>
+              <SkeletonText width="12" height="4"></SkeletonText>
             </li>
             <li className="flex items-center whitespace-nowrap">
               <UserIcon className="mt-0.5 mr-1.5 inline h-4 w-4 text-gray-200"></UserIcon>
-              <div className="h-4 w-16 rounded-md bg-gray-100"></div>
+              <SkeletonText width="16" height="4"></SkeletonText>
             </li>
           </ul>
         </div>
diff --git a/packages/ui/index.tsx b/packages/ui/index.tsx
index 30b1364d..3290c411 100644
--- a/packages/ui/index.tsx
+++ b/packages/ui/index.tsx
@@ -1,3 +1,4 @@
 export { default as Button } from "./Button";
 export { default as EmptyScreen } from "./EmptyScreen";
 export { default as Switch } from "./Switch";
+export * from "./skeleton";
diff --git a/packages/ui/skeleton/index.tsx b/packages/ui/skeleton/index.tsx
new file mode 100644
index 00000000..ddc48712
--- /dev/null
+++ b/packages/ui/skeleton/index.tsx
@@ -0,0 +1,34 @@
+import classNames from "@calcom/lib/classNames";
+
+type SkeletonBaseProps = {
+  width: string;
+  height: string;
+  className?: string;
+};
+
+interface AvatarProps extends SkeletonBaseProps {
+  // Limit this cause we don't use avatars bigger than thi
+  width: "2" | "3" | "4" | "5" | "6" | "8";
+  height: "2" | "3" | "4" | "5" | "6" | "8";
+}
+
+interface SkeletonContainer {
+  as?: keyof JSX.IntrinsicElements;
+}
+
+const SkeletonAvatar: React.FC<AvatarProps> = ({ width, height }) => {
+  return (
+    <div className={`mt-1 rounded-full bg-gray-200 ltr:mr-2 rtl:ml-2 w-${width} h-${height} ${classNames}`} />
+  );
+};
+
+const SkeletonText: React.FC<SkeletonBaseProps> = ({ width, height }) => {
+  return <div className={`rounded-md bg-gray-200  w-${width} h-${height} ${classNames}`} />;
+};
+
+const SkeletonContainer: React.FC<SkeletonContainer> = ({ children, as }) => {
+  const Component = as || "div";
+  return <Component className="animate-pulse">{children}</Component>;
+};
+
+export { SkeletonAvatar, SkeletonText, SkeletonContainer };