From be9e29cddb286c34a2857afff619a73cbf2a4a12 Mon Sep 17 00:00:00 2001 From: Ansh Goyal Date: Sat, 16 Mar 2024 11:08:40 +0530 Subject: [PATCH] feat: migrate logos, slack and roadmap components to typescript (#2763) --- components/Modal.tsx | 62 ++++++++++++++++ components/logos/Adidas.tsx | 60 ++++++++++++++++ components/logos/Axway.tsx | 45 ++++++++++++ components/logos/SAP.tsx | 31 ++++++++ components/logos/Salesforce.tsx | 23 ++++++ components/logos/Slack.tsx | 55 ++++++++++++++ components/roadmap/RoadmapColumn.tsx | 47 ++++++++++++ components/roadmap/RoadmapItem.tsx | 66 +++++++++++++++++ components/roadmap/RoadmapList.tsx | 56 +++++++++++++++ components/roadmap/RoadmapPill.tsx | 104 +++++++++++++++++++++++++++ components/slack/Message.tsx | 59 +++++++++++++++ components/slack/index.tsx | 84 ++++++++++++++++++++++ 12 files changed, 692 insertions(+) create mode 100644 components/Modal.tsx create mode 100644 components/logos/Adidas.tsx create mode 100644 components/logos/Axway.tsx create mode 100644 components/logos/SAP.tsx create mode 100644 components/logos/Salesforce.tsx create mode 100644 components/logos/Slack.tsx create mode 100644 components/roadmap/RoadmapColumn.tsx create mode 100644 components/roadmap/RoadmapItem.tsx create mode 100644 components/roadmap/RoadmapList.tsx create mode 100644 components/roadmap/RoadmapPill.tsx create mode 100644 components/slack/Message.tsx create mode 100644 components/slack/index.tsx diff --git a/components/Modal.tsx b/components/Modal.tsx new file mode 100644 index 00000000000..396c5daa48d --- /dev/null +++ b/components/Modal.tsx @@ -0,0 +1,62 @@ +import { useEffect, useRef } from 'react'; + +interface IModalProps { + title: string; + children: React.ReactNode; + onModalClose?: () => void; +} + +/** + * @description Modal component. + * @param {string} props.title - The title of the modal. + * @param {React.ReactNode} props.children - The content of the modal. + * @param {function} props.onModalClose=()=>{} - Function to handle modal close event. + */ +export default function Modal({ + title, + children, + onModalClose = () => {} +}: IModalProps) { + const modalRef = useRef(null); + + // Focus the modal when it mounts + useEffect(() => { + if (modalRef.current) modalRef.current.focus(); + }, []); + + /** + * @description Handles the backdrop click event. + * @param {React.MouseEvent} e - The event object. + */ + function backdropClickHandler(e: React.MouseEvent) { + if (modalRef.current && (modalRef.current === e.target || !modalRef.current.contains(e.target as Node))) { + onModalClose(); + } + } + + /** + * @description Handles the key up event. + * @param {React.KeyboardEvent} e - The event object. + */ + function onKeyUpHandler(e: React.KeyboardEvent) { + if (e.key === 'Escape') onModalClose(); + } + + return ( +
+
+
+

{title}

+ +
+
+ {children} +
+
+
+ ); +} diff --git a/components/logos/Adidas.tsx b/components/logos/Adidas.tsx new file mode 100644 index 00000000000..f9e8fd285a9 --- /dev/null +++ b/components/logos/Adidas.tsx @@ -0,0 +1,60 @@ +/** + * @description Logo for Adidas + * @param {string} className - used to style the svg + */ +export default function AdidasLogo({ className }: { className?: string }) { + return ( + + + + + + + + + + + + ); +} diff --git a/components/logos/Axway.tsx b/components/logos/Axway.tsx new file mode 100644 index 00000000000..8ef1650d74d --- /dev/null +++ b/components/logos/Axway.tsx @@ -0,0 +1,45 @@ +/** + * @description Logo for Axway + * @param {string} className - used to style the svg + */ +export default function AxwayLogo({ className }: { className?: string }) { + return ( + + + + + + + + + + ); +} diff --git a/components/logos/SAP.tsx b/components/logos/SAP.tsx new file mode 100644 index 00000000000..897a325d09a --- /dev/null +++ b/components/logos/SAP.tsx @@ -0,0 +1,31 @@ +import { useState } from 'react'; + +/** + * @description Logo for SAP + * @param {string} className - used to style the svg + */ +export default function SapLogo({ className }: { className?: string }) { + const [isHovered, setIsHovered] = useState(false); + + const handleMouseEnter = () => { + setIsHovered(true); + }; + const handleMouseLeave = () => { + setIsHovered(false); + }; + + return ( + + + + + + + + + + ); +} diff --git a/components/logos/Salesforce.tsx b/components/logos/Salesforce.tsx new file mode 100644 index 00000000000..e75247a186c --- /dev/null +++ b/components/logos/Salesforce.tsx @@ -0,0 +1,23 @@ +/** + * @description Logo for Salesforce + * @param {string} className - used to style the svg + */ +export default function SalesforceLogo({ className }: { className?: string }) { + return ( + + + + + + + + + + + + + + + + ); +} diff --git a/components/logos/Slack.tsx b/components/logos/Slack.tsx new file mode 100644 index 00000000000..603d817a774 --- /dev/null +++ b/components/logos/Slack.tsx @@ -0,0 +1,55 @@ +/** + * @description Logo for Slack + * @param {string} className - used to style the svg + */ +export default function SlackLogo({ className }: { className?: string }) { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/components/roadmap/RoadmapColumn.tsx b/components/roadmap/RoadmapColumn.tsx new file mode 100644 index 00000000000..9d879182618 --- /dev/null +++ b/components/roadmap/RoadmapColumn.tsx @@ -0,0 +1,47 @@ +import React from 'react'; + +import { HeadingLevel, HeadingTypeStyle } from '@/types/typography/Heading'; +import { ParagraphTypeStyle } from '@/types/typography/Paragraph'; + +import Heading from '../typography/Heading'; +import Paragraph from '../typography/Paragraph'; +import RoadmapList from './RoadmapList'; + +interface IRoadmapColumnProps { + title: string; + description: string; + colorClass: string; + items?: any[]; + childrenCollapsed?: boolean; +} + +/** + * @description RoadmapColumn component. + * @param {string} props.title - The title of the column. + * @param {string} props.description - The description of the column. + * @param {string} props.colorClass - The color class for styling. + * @param {array} props.items - The array of items. + * @param {boolean} props.childrenCollapsed - Whether children items are collapsed. + */ +export default function RoadmapColumn({ + title, + description, + colorClass, + items = [], + childrenCollapsed = false +}: IRoadmapColumnProps): React.ReactElement { + return ( +
+
+ {title} + {description} +
+ +
+ ); +} diff --git a/components/roadmap/RoadmapItem.tsx b/components/roadmap/RoadmapItem.tsx new file mode 100644 index 00000000000..f53cf850473 --- /dev/null +++ b/components/roadmap/RoadmapItem.tsx @@ -0,0 +1,66 @@ +import { useState } from 'react'; + +// Since a roadmap item can contain nested roadmap lists, we need to import RoadmapList to display them. +/* eslint-disable import/no-cycle*/ +import RoadmapList from './RoadmapList'; +import Pill from './RoadmapPill'; + +export interface IRoadmapItemProps { + item: { + solutions?: any[]; + implementations?: any[]; + done?: boolean; + url?: string; + description?: string; + title: string; + }; + colorClass: string; + showConnector?: boolean; + collapsed?: boolean; +} + +/** + * @description RoadmapItem is a component to display a roadmap item. + * @param {object} props.item - The roadmap item. + * @param {boolean} props.item.done - Whether the item is done. + * @param {string} props.item.url - The URL associated with the item. + * @param {string} props.item.description - The description of the item. + * @param {string} props.item.title - The title of the item. + * @param {string} props.colorClass - The color class for styling. + * @param {boolean} props.showConnector - Whether to show the connector. + * @param {boolean} props.collapsed - Whether the list is collapsed. + */ +export default function RoadmapItem({ item, colorClass, showConnector = true, collapsed = true }: IRoadmapItemProps) { + const [isCollapsed, setIsCollapsed] = useState(collapsed); + const isCollapsible = item.solutions !== null || item.implementations !== null; + + const connectorClasses = 'border-l-2 border-dashed border-gray-300'; + const classNames = `pt-2 ${showConnector && connectorClasses}`; + + return ( +
  • +
    + {showConnector && ( +
    +
    +
    + )} + setIsCollapsed(!isCollapsed)} + /> +
    + + {!isCollapsed && item?.solutions?.length && ( + + )} + + {!isCollapsed && item?.implementations?.length && ( + + )} +
  • + ); +} diff --git a/components/roadmap/RoadmapList.tsx b/components/roadmap/RoadmapList.tsx new file mode 100644 index 00000000000..fbe9b6f4ca5 --- /dev/null +++ b/components/roadmap/RoadmapList.tsx @@ -0,0 +1,56 @@ +// Since a RoadmapList may contain other RoadmapItems, we need to import RoadmapItem to display them. +/* eslint-disable import/no-cycle*/ +import RoadmapItem from './RoadmapItem'; + +interface IRoadmapListProps { + colorClass: string; + className?: string; + items?: { + solutions?: any[]; + implementations?: any[]; + done?: boolean; + url?: string; + description?: string; + title: string; + }[]; + showConnector?: boolean; + collapsed?: boolean; + childrenCollapsed?: boolean; +} + +/** + * @description RoadmapList is a component to display a list of roadmap items. + * @param {string} props.colorClass - The color class for styling. + * @param {string} props.className='mt-3' - The optional CSS class name. + * @param {object[]} props.items=[] - The array of roadmap items. + * @param {boolean} props.showConnector=true - Whether to show the connector. + * @param {boolean} props.collapsed=false - Whether the list is collapsed. + * @param {boolean} props.childrenCollapsed=true - Whether children items are collapsed. + */ +export default function RoadmapList({ + colorClass, + className = 'mt-3', + items = [], + showConnector = true, + collapsed = false, + childrenCollapsed = true +}: IRoadmapListProps) { + return ( + <> + items && items.length && ( +
      + {!collapsed && + items.map((item, index) => ( + + ))} +
    + ) + + ); +} diff --git a/components/roadmap/RoadmapPill.tsx b/components/roadmap/RoadmapPill.tsx new file mode 100644 index 00000000000..322fbaed627 --- /dev/null +++ b/components/roadmap/RoadmapPill.tsx @@ -0,0 +1,104 @@ +import { useState } from 'react'; + +import IconArrowRight from '../icons/ArrowRight'; +import Modal from '../Modal'; + +/** + * @description Icon for Done (Tick) + */ +function DoneIcon() { + return ( + + + + ); +} + +interface IPillProps { + item: { + done?: boolean; + url?: string; + description?: string; + title: string; + }; + colorClass?: string; + isCollapsible?: boolean; + isCollapsed?: boolean; + onClickCollapse?: () => void; +} + +/** + * @description Pill is a component to display a roadmap item. + * @param {object} props.item - The roadmap item. + * @param {boolean} props.item.done - Whether the item is done. + * @param {string} props.item.url - The URL associated with the item. + * @param {string} props.item.description - The description of the item. + * @param {string} props.item.title - The title of the item. + * @param {string} props.colorClass - The color class for styling. + * @param {boolean} props.isCollapsible - Whether the item is collapsible. + * @param {boolean} props.isCollapsed - Whether the item is collapsed. + * @param {function} props.onClickCollapse - Function to handle click on collapse. + */ +export default function Pill({ + item, + colorClass = '', + isCollapsible = false, + isCollapsed = false, + onClickCollapse = () => {} +}: IPillProps) { + const [isDescriptionVisible, setIsDescriptionVisible] = useState(false); + + return ( + <> + + {isDescriptionVisible && ( + setIsDescriptionVisible(false)}> +
    {item.description}
    +
    + )} + + ); +} diff --git a/components/slack/Message.tsx b/components/slack/Message.tsx new file mode 100644 index 00000000000..26b0014db16 --- /dev/null +++ b/components/slack/Message.tsx @@ -0,0 +1,59 @@ +import React from 'react'; + +interface ISlackMessageProps { + className?: string; + avatar: string; + name: string; + text: React.ReactNode; + reactions?: { + emoji?: string; + icon?: string; + count: number; + mine?: boolean; + name?: string; + }[]; + } + +/** + * @description Component to render a Slack message. + * @param {Object} props - The component props. + * @param {string} props.className - Additional CSS classes for styling. + * @param {string} props.avatar - URL of the user's avatar. + * @param {string} props.name - Name of the user. + * @param {React.ReactNode} props.text - Text of the message. + * @param {Object[]} props.reactions - Array of reaction objects. + * @param {string} props.reactions[].emoji - Emoji representing the reaction. + * @param {string} props.reactions[].icon - URL of the reaction icon. + * @param {string} props.reactions[].name - Name of the reaction icon. + * @param {number} props.reactions[].count - Number of reactions. + * @param {boolean} props.reactions[].mine - Indicates if the reaction is from the current user. + */ +export default function SlackMessage({ + className = '', + avatar, + name, + text, + reactions = [] +}: ISlackMessageProps) { + return ( +
    + {name} +
    +
    {name}
    +

    + {text} +

    +
    + { + reactions.map((reaction, index) => ( +
    + { reaction.icon ? ({reaction.name}) : ({reaction.emoji}) } + {reaction.count} +
    + )) + } +
    +
    +
    + ); +} diff --git a/components/slack/index.tsx b/components/slack/index.tsx new file mode 100644 index 00000000000..3c49b2eee58 --- /dev/null +++ b/components/slack/index.tsx @@ -0,0 +1,84 @@ +import SlackMessage from './Message'; + +interface SlackProps { + className?: string; +} + +/** + * @description Slack component for displaying Slack-like UI. + * @param {string} props.className - Additional CSS classes for styling. + */ +export default function Slack({ + className = '' +}: SlackProps) { + return ( +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + Good Morning + grain + coffee + + } + reactions={[ + { icon: '/img/homepage/coffee.png', name: 'coffee', count: 6, mine: true }, + { icon: '/img/homepage/coffee-parrot.gif', name: 'coffee-parrot', count: 4, mine: true }, + { icon: '/img/homepage/coffee-bean.png', name: 'coffee-bean', count: 6, mine: true }, + { icon: '/img/homepage/parrotsleep.gif', name: 'parrotsleep', count: 1, mine: false } + ]} + /> + + Joined #general. + + } + /> + + Hey folks! 👋 + + } + reactions={[ + { emoji: '👋', count: 21, mine: true } + ]} + /> + + Hey Eve & Chan! Welcome to the AsyncAPI workspace! + + } + /> +
    +
    + ); +}