diff --git a/README.md b/README.md index 1bbf8333..4d022e51 100644 --- a/README.md +++ b/README.md @@ -11,13 +11,13 @@

- + - + - +

@@ -27,13 +27,13 @@ Social - + - + - +

@@ -44,13 +44,13 @@ It's an UI Kit that can be used on every [React](https://reactjs.org) and [React Native](https://reactnative.dev/) project as a complete UI library with optimized and customizable components. -See [Kitchen Documentation](http://docs.onruntime.com/kitchen) for all details. +See [Kitchen Documentation](https://kitchen.tonightpass.com/docs) for all details. This library use [Styled Component](https://styled-components.com/) as components styling and theme provider. ## Getting Started -Visit /kitchen to get started with Kitchen and see the full documentation. +Visit /kitchen to get started with Kitchen and see the full documentation. ## Community @@ -68,6 +68,7 @@ Please see our [contributing rules](https://docs.onruntime.com/contributing/intr - Antoine Kingue ([@antoinekm](https://github.com/antoinekm)) - Jérémy Baudrin ([@jerembdn](https://github.com/jerembdn)) +- Younès Bessa ([@younesbessa](https://github.com/younesbessa)) ## License diff --git a/examples/next-typescript/pages/index.tsx b/examples/next-typescript/pages/index.tsx index 01f56e37..7a3976df 100644 --- a/examples/next-typescript/pages/index.tsx +++ b/examples/next-typescript/pages/index.tsx @@ -20,6 +20,7 @@ const IndexPage: NextPage = () => { Text Icon Note + Skeleton ); }; diff --git a/examples/next-typescript/pages/skeleton.tsx b/examples/next-typescript/pages/skeleton.tsx new file mode 100644 index 00000000..2626e6d4 --- /dev/null +++ b/examples/next-typescript/pages/skeleton.tsx @@ -0,0 +1,117 @@ +import { Skeleton, Container, Button } from "@tonightpass/kitchen"; +import { NextPage } from "next"; +import React from "react"; +import styled from "styled-components"; + +const SkeletonPage: NextPage = () => { + return ( + <> + +

default with set width

+ +
+ +

default with box height

+ +
+ +

wrapping children

+ + Hidden by skeleton + + + + + +
+ +

wrapping children with fixed size

+ + {null} + + + + Not hidden by Skeleton + +
+ +

normal

+ +
+ +

round

+ +
+ +

square

+ +
+ +

no animation

+ + {null} + +
+ + ); +}; + +const TestButton = styled(Button)``; + +export default SkeletonPage; diff --git a/packages/kitchen/src/components/Skeleton/index.tsx b/packages/kitchen/src/components/Skeleton/index.tsx new file mode 100644 index 00000000..f7ea60d1 --- /dev/null +++ b/packages/kitchen/src/components/Skeleton/index.tsx @@ -0,0 +1,159 @@ +import React from "react"; +import styled from "styled-components"; +import { KitchenComponent } from "../../types"; +import isString from "../../utils/isString"; + +export type SkeletonProps = KitchenComponent & { + /** + * The skeleton's appearance (if it's shown or not). + */ + show?: boolean; + + /** + * The skeleton's width. + */ + width?: number | string; + + /** + * The skeleton's height. + */ + height?: number | string; + + /** + * The skeleton's box height. + */ + boxHeight?: number; + + /** + * The skeleton's mode (animated or not). + */ + animated?: boolean; + + /** + * The skeleton's shape. + */ + shape?: "normal" | "square" | "round"; + + /** + * The skeleton's children. + */ + children?: React.ReactNode; + + /** + * The skeleton's key. + */ + [key: string]: any; +}; + +const Skeleton = styled( + ({ + as: Component = "span", + children, + width, + height, + show = true, + animated = true, + ...props + }: SkeletonProps) => { + if (!show && !children) return <>; + if (!show && children) return children as JSX.Element; + return ( + + {children} + + ); + } +)` +Skeleton.displayName = "Skeleton"; +${({ children, show = true, width, height, shape, theme, animated = true }) => + children + ? ` + position: relative; + display: block; + width: ${width ? "100%" : "auto"}; + max-width: ${ + width ? (isString(width) ? width : `${width}px`) : "unset" + }; + min-height: ${ + height ? (isString(height) ? height : `${height}px`) : "auto" + }; + ${ + show + ? ` + ::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 100; + user-select: none; + cursor: default; + border-radius: ${ + shape === "square" ? "0" : shape === "round" ? "99999px" : "8px" + }; + background-image: linear-gradient( + 270deg, + ${theme.colors.layout.darkest}, + ${theme.colors.layout.dark}, + ${theme.colors.layout.dark}, + ${theme.colors.layout.darkest} + ); + background-size: 400% 100%; + animation: ${ + animated ? "skeleton 8s ease-in-out infinite" : "none" + }; + } + ` + : "" + } + ` + : ` + display: block; + width: 100%; + user-select: none; + cursor: default; + max-width: ${ + width ? (isString(width) ? width : `${width}px`) : "24px" + }; + min-height: ${ + height ? (isString(height) ? height : `${height}px`) : "24px" + }; + border-radius: ${ + shape === "square" ? "0" : shape === "round" ? "99999px" : "8px" + }; + background-size: 400% 100%; + background-image: linear-gradient( + 270deg, + ${theme.colors.layout.darkest}, + ${theme.colors.layout.dark}, + ${theme.colors.layout.dark}, + ${theme.colors.layout.darkest} + ); + animation: ${animated ? "skeleton 8s ease-in-out infinite" : "none"}; + `} + + ${({ boxHeight, height }) => + boxHeight && + Number.isFinite(height) && + boxHeight - ((height as number) || 24) > 0 && + `margin-bottom: ${boxHeight - ((height as number) || 24)}px`}; + + @keyframes skeleton { + 0% { + background-position: 200% 0; + } + to { + background-position: -200% 0; + } + } +`; + +export default Skeleton; diff --git a/packages/kitchen/src/index.ts b/packages/kitchen/src/index.ts index 9296c464..11732ec0 100644 --- a/packages/kitchen/src/index.ts +++ b/packages/kitchen/src/index.ts @@ -12,6 +12,7 @@ import UnorderedList from "./components/UnorderedList"; import ListItem from "./components/ListItem"; import Icon from "./components/Icon"; import InlineCode from "./components/InlineCode"; +import Skeleton from "./components/Skeleton"; import Spinner from "./components/Spinner"; import Text from "./components/Text"; @@ -27,6 +28,7 @@ export { OrderedList, ListItem, UnorderedList, + Skeleton, Spinner, Text, }; diff --git a/packages/kitchen/src/utils/isString.ts b/packages/kitchen/src/utils/isString.ts new file mode 100644 index 00000000..204cfa0f --- /dev/null +++ b/packages/kitchen/src/utils/isString.ts @@ -0,0 +1,8 @@ +/** + * isString is a utility function that checks if a value is a string. + */ +const isString = (x: any) => { + return Object.prototype.toString.call(x) === "[object String]"; +}; + +export default isString;