From a10387262d09ffac43d94c8659adf14fbc07041c Mon Sep 17 00:00:00 2001 From: VINEETH ASOK KUMAR Date: Mon, 16 Oct 2023 09:21:56 +0200 Subject: [PATCH 1/4] Add flyout component --- package-lock.json | 2 +- package.json | 2 +- src/App.tsx | 46 +++++- src/components/Flyout/Flyout.test.tsx | 0 src/components/Flyout/Flyout.tsx | 200 ++++++++++++++++++++++++++ src/components/index.ts | 1 + 6 files changed, 242 insertions(+), 9 deletions(-) create mode 100644 src/components/Flyout/Flyout.test.tsx create mode 100644 src/components/Flyout/Flyout.tsx diff --git a/package-lock.json b/package-lock.json index e6bd0dd0..b963fd45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@radix-ui/react-avatar": "^1.0.3", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-context-menu": "^2.1.4", - "@radix-ui/react-dialog": "^1.0.4", + "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.5", "@radix-ui/react-hover-card": "^1.0.6", "@radix-ui/react-popover": "^1.0.6", diff --git a/package.json b/package.json index 60d0aad8..a6846727 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "@radix-ui/react-avatar": "^1.0.3", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-context-menu": "^2.1.4", - "@radix-ui/react-dialog": "^1.0.4", + "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.5", "@radix-ui/react-hover-card": "^1.0.6", "@radix-ui/react-popover": "^1.0.6", diff --git a/src/App.tsx b/src/App.tsx index d8a7258b..b2ab68c8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useRef, useState } from "react"; import "@/styles/globals.css"; import "./styles/variables.css"; @@ -28,6 +28,8 @@ import { Tabs, WarningAlert, CardPrimary, + Flyout, + Select, } from "@/components"; import { Dialog } from "@/components/Dialog/Dialog"; @@ -37,6 +39,7 @@ const App = () => { const [checked, setChecked] = useState(false); const [disabled] = useState(false); const [open, setOpen] = useState(true); + const ref = useRef(null); return ( { onClick={() => console.log("click")} /> -
- console.log("click")} - /> +
console.log("click")} /> + + + console.log("click")} + /> + + + + + hadksjhadksjhaskdjhaksdjhkajsdhkajshdkjashdkjashd + + + + + + + { + return ; +}; + +const Trigger = ({ children, ...props }: Dialog.DialogTriggerProps) => { + return ( + +
{children}
+
+ ); +}; +Trigger.displayName = "Flyout.Trigger"; +Flyout.Trigger = Trigger; + +type FlyoutSizeType = "narrow" | "wide"; + +interface DialogContentProps extends Dialog.DialogContentProps { + container?: HTMLElement | null; + showOverlay?: boolean; + showClose?: boolean; + size?: FlyoutSizeType; +} + +const FlyoutContent = styled(Dialog.Content)<{ $size?: FlyoutSizeType }>` + display: flex; + flex-direction: column; + align-items: center; + height: 100%; + overflow: hidden; + ${({ theme, $size = "narrow" }) => ` + width: ${theme.click.flyout.size[$size].width}; + padding: ${theme.click.flyout.space.y} ${theme.click.flyout.space.x}; + gap: ${theme.click.flyout.space.gap}; + border-left: 1px solid ${theme.click.flyout.color.stroke.default}; + background: ${theme.click.flyout.color.background.default}; + box-shadow: -6px 0px 10px 0px ${theme.click.flyout.shadow.default}, -5px 0px 20px 0px ${theme.click.flyout.shadow.default}; + `} +`; + +const Content = ({ + showOverlay = false, + children, + container, + size, + ...props +}: DialogContentProps) => { + return ( + + {showOverlay && } + + {children} + + + ); +}; +Content.displayName = "Flyout.Content"; +Flyout.Content = Content; + +interface TitleHeaderProps extends Omit, "children"> { + title: string; + description: string; + children?: never; +} + +interface ChildrenHeaderProps extends HTMLAttributes { + title?: never; + description?: never; +} + +type HeaderProps = TitleHeaderProps | ChildrenHeaderProps; + +const FlyoutHeaderContainer = styled.div<{ $showBorder?: boolean }>` + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 1rem; + padding: 0rem 1.5rem 1rem; + ${({ theme, $showBorder = true }) => ` + border-bottom: ${ + $showBorder ? `1px solid ${theme.click.flyout.color.stroke.default}` : "none" + }; + `} +`; + +const FlexGrow = styled.div` + display: flex; + flex-direction: column; + flex: 1; +`; + +const FlyoutTitle = styled(Dialog.Title)` + ${({ theme }) => ` + color: ${theme.click.flyout.color.title.default}; + font: ${theme.typography.styles.product.titles.xl}; + `} +`; + +const FlyoutDescription = styled(Dialog.Description)` + ${({ theme }) => ` + color: ${theme.click.flyout.color.description.default}; + font: ${theme.typography.styles.product.text.normal.md}; + `} +`; + +const Header = ({ title, description, children, ...props }: HeaderProps) => { + if (children) { + return ( + + {children} + + + + + ); + } + + return ( + + + {title} + {description && ( + + Make changes to your profile here. Click save when you're done. + + )} + + + + + + ); +}; +Header.displayName = "Flyout.Header"; +Flyout.Header = Header; + +const FlyoutBody = styled.div` + display: flex; + flex-direction: column; + flex: 1; + ${({ theme }) => ` + gap: ${theme.click.flyout.space.gap}; + `} + padding: 0 1.5rem; +`; + +const Body = (props: HTMLAttributes) => ; + +Body.displayName = "Flyout.Body"; +Flyout.Body = Body; + +interface FooterProps extends HTMLAttributes { + cancelText?: string; + showClose?: boolean; +} + +const FlyoutFooter = styled.div` + margin-top: 1rem; + display: flex; + justify-content: flex-end; + align-items: center; + width: 100%; + padding: 1rem 1.5rem 0; + ${({ theme }) => ` + border-top: 1px solid ${theme.click.flyout.color.stroke.default}; + `} +`; + +const Footer = ({ cancelText, showClose, children, ...props }: FooterProps) => { + return ( + + {showClose && ( + + + + )} + {children} + + ); +}; +Footer.displayName = "Flyout.Footer"; +Flyout.Footer = Footer; diff --git a/src/components/index.ts b/src/components/index.ts index 82713560..36a26ed5 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -16,6 +16,7 @@ export { CardPrimary } from "./CardPrimary/CardPrimary"; export { Checkbox } from "./Checkbox/Checkbox"; export { CodeBlock } from "./CodeBlock/CodeBlock"; export { EllipsisContent } from "./EllipsisContent/EllipsisContent"; +export { Flyout } from "./Flyout/Flyout"; export { InlineCodeBlock } from "./CodeBlock/InlineCodeBlock"; export { ContextMenu } from "./ContextMenu/ContextMenu"; export { default as Flags } from "./icons/Flags"; From 42a941e3ac12343c1022d83b209251d6660f2d32 Mon Sep 17 00:00:00 2001 From: VINEETH ASOK KUMAR Date: Fri, 20 Oct 2023 15:36:54 +0200 Subject: [PATCH 2/4] Add flyout changes --- src/App.module.css | 1 + src/App.tsx | 163 +++++++++++++++++++++--------- src/components/Flyout/Flyout.tsx | 167 +++++++++++++++++++++---------- 3 files changed, 230 insertions(+), 101 deletions(-) diff --git a/src/App.module.css b/src/App.module.css index 6037d2b4..dfa87c66 100644 --- a/src/App.module.css +++ b/src/App.module.css @@ -232,4 +232,5 @@ display: flex; flex-flow: row wrap; gap: 10px; + padding: 10px 0; } diff --git a/src/App.tsx b/src/App.tsx index b2ab68c8..755f4cde 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -30,6 +30,7 @@ import { CardPrimary, Flyout, Select, + Text, } from "@/components"; import { Dialog } from "@/components/Dialog/Dialog"; @@ -64,86 +65,149 @@ const App = () => { onClick={() => console.log("click")} />
-
+
console.log("click")} /> - - - console.log("click")} - /> - - - - - hadksjhadksjhaskdjhaksdjhkajsdhkajshdkjashdkjashd - - - - - - - console.log("click")} - /> -
-
- console.log("click")} /> console.log("click")} - /> - console.log("click")} />
console.log("click")} /> console.log("click")} /> console.log("click")} />
+
+ Flyout +
+ + + + + + + + hadksjhadksjhaskdjhaksdjhkajsdhkajshdkjashdkjashd + + + + + + + + + + + + + + + hadksjhadksjhaskdjhaksdjhkajsdhkajshdkjashdkjashd + + + + + + + + + + + + + + + hadksjhadksjhaskdjhaksdjhkajsdhkajshdkjashdkjashd + + + + + + + +
+
{ { @@ -21,35 +21,74 @@ Trigger.displayName = "Flyout.Trigger"; Flyout.Trigger = Trigger; type FlyoutSizeType = "narrow" | "wide"; - +type Strategy = "relative" | "absolute" | "fixed"; interface DialogContentProps extends Dialog.DialogContentProps { container?: HTMLElement | null; showOverlay?: boolean; showClose?: boolean; size?: FlyoutSizeType; + strategy?: Strategy; + closeOnInteractOutside?: boolean; } -const FlyoutContent = styled(Dialog.Content)<{ $size?: FlyoutSizeType }>` +const FlyoutContent = styled(Dialog.Content)<{ + $size?: FlyoutSizeType; + $strategy: Strategy; +}>` display: flex; flex-direction: column; align-items: center; + width: 100%; height: 100%; overflow: hidden; - ${({ theme, $size = "narrow" }) => ` + flex: 1; + top: 0; + right: 0; + ${({ theme, $size = "narrow", $strategy }) => ` + position: ${$strategy}; width: ${theme.click.flyout.size[$size].width}; padding: ${theme.click.flyout.space.y} ${theme.click.flyout.space.x}; gap: ${theme.click.flyout.space.gap}; border-left: 1px solid ${theme.click.flyout.color.stroke.default}; background: ${theme.click.flyout.color.background.default}; - box-shadow: -6px 0px 10px 0px ${theme.click.flyout.shadow.default}, -5px 0px 20px 0px ${theme.click.flyout.shadow.default}; + box-shadow: -6px 0px 10px 0px ${ + theme.click.flyout.shadow.default + }, -5px 0px 20px 0px ${theme.click.flyout.shadow.default}; + ${ + $strategy === "relative" + ? ` + @media (max-width: 1024px) { + position: absolute !important; + overflow: hidden; + transform: translateX(calc(100% - 50px)); + transition: 0.3s ease-in-out; + &:hover, + &.active, + &:focus-within { + transform: translateX(0); + left: auto; + } + } + ` + : "" + } `} `; +const FlyoutContainer = styled.div` + display: flex; + gap: 0; + width: 100%; + flex-flow: column nowrap; +`; const Content = ({ showOverlay = false, children, container, + strategy = "relative", size, + closeOnInteractOutside = false, + onInteractOutside, ...props }: DialogContentProps) => { return ( @@ -57,7 +96,15 @@ const Content = ({ {showOverlay && } { + if (!closeOnInteractOutside) { + e.preventDefault(); + } + if (typeof onInteractOutside === "function") { + onInteractOutside(e); + } + }} {...props} > {children} @@ -68,6 +115,20 @@ const Content = ({ Content.displayName = "Flyout.Content"; Flyout.Content = Content; +const FlyoutElement = styled.div` + display: flex; + flex-direction: column; + ${({ theme }) => ` + gap: ${theme.click.flyout.space.gap}; + padding: 0 ${theme.click.flyout.space.content.x}; + `} +`; + +const Element = (props: HTMLAttributes) => ; + +Element.displayName = "Flyout.Element"; +Flyout.Element = Element; + interface TitleHeaderProps extends Omit, "children"> { title: string; description: string; @@ -81,16 +142,14 @@ interface ChildrenHeaderProps extends HTMLAttributes { type HeaderProps = TitleHeaderProps | ChildrenHeaderProps; -const FlyoutHeaderContainer = styled.div<{ $showBorder?: boolean }>` +const FlyoutHeaderContainer = styled.div` display: flex; justify-content: space-between; align-items: flex-start; - margin-bottom: 1rem; - padding: 0rem 1.5rem 1rem; - ${({ theme, $showBorder = true }) => ` - border-bottom: ${ - $showBorder ? `1px solid ${theme.click.flyout.color.stroke.default}` : "none" - }; + ${({ theme }) => ` + row-gap: ${theme.click.flyout.space.content["row-gap"]}; + column-gap: ${theme.click.flyout.space.content["column-gap"]}; + padding: 0 ${theme.click.flyout.space.content.x}; `} `; @@ -117,12 +176,30 @@ const FlyoutDescription = styled(Dialog.Description)` const Header = ({ title, description, children, ...props }: HeaderProps) => { if (children) { return ( - - {children} - + + + {children} + + + + + + + ); + } + + return ( + + + + {title} + {description && {description}} + + { /> - ); - } - - return ( - - - {title} - {description && ( - - Make changes to your profile here. Click save when you're done. - - )} - - - - - + + ); }; Header.displayName = "Flyout.Header"; @@ -156,10 +218,8 @@ const FlyoutBody = styled.div` display: flex; flex-direction: column; flex: 1; - ${({ theme }) => ` - gap: ${theme.click.flyout.space.gap}; - `} - padding: 0 1.5rem; + width: 100%; + overflow: auto; `; const Body = (props: HTMLAttributes) => ; @@ -173,27 +233,30 @@ interface FooterProps extends HTMLAttributes { } const FlyoutFooter = styled.div` - margin-top: 1rem; display: flex; justify-content: flex-end; align-items: center; width: 100%; - padding: 1rem 1.5rem 0; ${({ theme }) => ` - border-top: 1px solid ${theme.click.flyout.color.stroke.default}; + row-gap: ${theme.click.flyout.space.content["row-gap"]}; + column-gap: ${theme.click.flyout.space.content["column-gap"]}; + padding: 0 ${theme.click.flyout.space.content.x}; `} `; const Footer = ({ cancelText, showClose, children, ...props }: FooterProps) => { return ( - - {showClose && ( - - - - )} - {children} - + + + + {showClose && ( + + + + )} + {children} + + ); }; Footer.displayName = "Flyout.Footer"; From c8380c2879f0da11f57827e55a530ba712593a19 Mon Sep 17 00:00:00 2001 From: VINEETH ASOK KUMAR Date: Fri, 20 Oct 2023 17:29:38 +0200 Subject: [PATCH 3/4] Fix Flyout --- src/App.tsx | 6 +- src/components/Flyout/Flyout.stories.tsx | 79 +++++++++++++++++++ src/components/Flyout/Flyout.test.tsx | 69 ++++++++++++++++ src/components/Flyout/Flyout.tsx | 37 +++++---- src/components/Select/MultiSelect.stories.tsx | 2 +- src/components/types.ts | 1 + 6 files changed, 175 insertions(+), 19 deletions(-) create mode 100644 src/components/Flyout/Flyout.stories.tsx diff --git a/src/App.tsx b/src/App.tsx index 755f4cde..73750375 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -119,7 +119,7 @@ const App = () => { description="test2" /> - hadksjhadksjhaskdjhaksdjhkajsdhkajshdkjashdkjashd + Flyout Text test test2 @@ -189,7 +189,7 @@ const App = () => { description="test2" /> - hadksjhadksjhaskdjhaksdjhkajsdhkajshdkjashdkjashd + Flyout Text + ` : "" }`; diff --git a/src/components/types.ts b/src/components/types.ts index 1447f041..a005926b 100644 --- a/src/components/types.ts +++ b/src/components/types.ts @@ -31,6 +31,7 @@ export type { ToastProps } from "./Toast/Toast"; export type { SelectOptionListItem } from "./Select/common/types"; export type { MultiSelectProps } from "./Select/MultiSelect"; export type { PanelProps } from "./Panel/Panel"; +export type { FlyoutProps, FlyoutFooterProps, FlyoutHeaderProps } from "./Flyout/Flyout"; export type States = "default" | "active" | "disabled" | "error" | "hover"; export type HorizontalDirection = "start" | "end"; From e031d828149fb51f216a09f7d46bceace3f17a21 Mon Sep 17 00:00:00 2001 From: VINEETH ASOK KUMAR Date: Fri, 20 Oct 2023 17:44:59 +0200 Subject: [PATCH 4/4] Update dialog import --- src/components/Flyout/Flyout.tsx | 50 ++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/src/components/Flyout/Flyout.tsx b/src/components/Flyout/Flyout.tsx index 22a69c63..a6c4f238 100644 --- a/src/components/Flyout/Flyout.tsx +++ b/src/components/Flyout/Flyout.tsx @@ -1,22 +1,34 @@ import { HTMLAttributes } from "react"; -import * as Dialog from "@radix-ui/react-dialog"; +import { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogOverlay, + DialogPortal, + DialogProps, + DialogTitle, + DialogTrigger, + DialogTriggerProps, + DialogContentProps as RadixDialogContentProps, +} from "@radix-ui/react-dialog"; import { Button, IconButton, Separator } from ".."; import styled from "styled-components"; -export type FlyoutProps = Dialog.DialogProps; +export type FlyoutProps = DialogProps; export const Flyout = (props: FlyoutProps) => { - return ; + return ; }; -const Trigger = ({ children, ...props }: Dialog.DialogTriggerProps) => { +const Trigger = ({ children, ...props }: DialogTriggerProps) => { return ( -
{children}
-
+ ); }; Trigger.displayName = "Flyout.Trigger"; @@ -24,7 +36,7 @@ Flyout.Trigger = Trigger; type FlyoutSizeType = "narrow" | "wide"; type Strategy = "relative" | "absolute" | "fixed"; -export interface DialogContentProps extends Dialog.DialogContentProps { +export interface DialogContentProps extends RadixDialogContentProps { container?: HTMLElement | null; showOverlay?: boolean; showClose?: boolean; @@ -33,7 +45,7 @@ export interface DialogContentProps extends Dialog.DialogContentProps { closeOnInteractOutside?: boolean; } -const FlyoutContent = styled(Dialog.Content)<{ +const FlyoutContent = styled(DialogContent)<{ $size?: FlyoutSizeType; $strategy: Strategy; }>` @@ -94,8 +106,8 @@ const Content = ({ ...props }: DialogContentProps) => { return ( - - {showOverlay && } + + {showOverlay && } {children} - + ); }; Content.displayName = "Flyout.Content"; @@ -161,7 +173,7 @@ const FlexGrow = styled.div` flex: 1; `; -const FlyoutTitle = styled(Dialog.Title)` +const FlyoutTitle = styled(DialogTitle)` ${({ theme }) => ` color: ${theme.click.flyout.color.title.default}; font: ${theme.typography.styles.product.titles.xl}; @@ -170,7 +182,7 @@ const FlyoutTitle = styled(Dialog.Title)` `} `; -const FlyoutDescription = styled(Dialog.Description)` +const FlyoutDescription = styled(DialogDescription)` ${({ theme }) => ` color: ${theme.click.flyout.color.description.default}; font: ${theme.typography.styles.product.text.normal.md}; @@ -185,14 +197,14 @@ const Header = ({ title, description, children, ...props }: FlyoutHeaderProps) = {children} - + - + @@ -206,14 +218,14 @@ const Header = ({ title, description, children, ...props }: FlyoutHeaderProps) = {title} {description && {description}} - + - +
@@ -257,9 +269,9 @@ const Footer = ({ cancelText, showClose, children, ...props }: FlyoutFooterProps {showClose && ( - + - + )} {children}