From 2132f929684652041ce00bb7f754a872d7e04abf Mon Sep 17 00:00:00 2001 From: Vineeth Asok Kumar Date: Wed, 1 Nov 2023 17:05:16 +0100 Subject: [PATCH] Fix common styling and functionality fixes (#193) * Add onCopy to CodeBlock * Fix button group cursor * Add fillWidth option to button group * Flyout should be interactable by default * Add Ellipsis to be allowed to have different component * Fix Link type * Add ref to typography * Add onCopyError * Fix type error * Add container element to select * Add Flyout animation * Update themes values * Add Button Group color fix --- src/App.tsx | 30 +++++++++++++ src/components/ButtonGroup/ButtonGroup.tsx | 24 +++++++---- src/components/CodeBlock/CodeBlock.tsx | 42 +++++++++++++++---- .../EllipsisContent/EllipsisContent.tsx | 18 ++++++-- src/components/Flyout/Flyout.tsx | 13 ++++-- src/components/Link/Link.tsx | 2 +- .../Select/common/InternalSelect.tsx | 3 +- src/components/Select/common/types.ts | 1 + src/components/Typography/Text/Text.tsx | 25 ++++++----- src/components/Typography/Title/Title.tsx | 25 ++++++----- src/styles/types.ts | 8 +++- src/styles/variables.classic.json | 8 +++- src/styles/variables.dark.json | 10 +++-- src/styles/variables.json | 8 +++- src/styles/variables.light.json | 8 +++- 15 files changed, 169 insertions(+), 56 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index d926d355..7e1299aa 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -31,6 +31,7 @@ import { Flyout, Select, Text, + EllipsisContent, } from "@/components"; import { Dialog } from "@/components/Dialog/Dialog"; @@ -386,6 +387,35 @@ const App = () => {

I'm a dialog

+ + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec euismod dolor + vehicula tortor condimentum blandit. Quisque eget efficitur nisi, sit amet + facilisis felis. Sed malesuada ut dui vel hendrerit. Nulla at neque libero. Ut id + fringilla nisl. Nulla semper a sem a molestie. Vestibulum consequat feugiat magna, + vitae pellentesque lacus gravida non. Vestibulum bibendum gravida felis ac + elementum. In suscipit risus a sollicitudin molestie. Ut id cursus felis, vel + auctor ex. Cras vel metus ipsum. Sed finibus, ligula ut convallis maximus, turpis + ex imperdiet enim, ac finibus nunc ante non est. Ut mattis ex magna, ac faucibus + mi egestas interdum. + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec euismod dolor + vehicula tortor condimentum blandit. Quisque eget efficitur nisi, sit amet + facilisis felis. Sed malesuada ut dui vel hendrerit. Nulla at neque libero. Ut id + fringilla nisl. Nulla semper a sem a molestie. Vestibulum consequat feugiat magna, + vitae pellentesque lacus gravida non. Vestibulum bibendum gravida felis ac + elementum. In suscipit risus a sollicitudin molestie. Ut id cursus felis, vel + auctor ex. Cras vel metus ipsum. Sed finibus, ligula ut convallis maximus, turpis + ex imperdiet enim, ac finibus nunc ante non est. Ut mattis ex magna, ac faucibus + mi egestas interdum. + ); }; diff --git a/src/components/ButtonGroup/ButtonGroup.tsx b/src/components/ButtonGroup/ButtonGroup.tsx index 1b796298..9b908bf0 100644 --- a/src/components/ButtonGroup/ButtonGroup.tsx +++ b/src/components/ButtonGroup/ButtonGroup.tsx @@ -5,6 +5,7 @@ export interface ButtonGroupElementProps extends Omit, "children"> { value: string; label?: ReactNode; + fillWidth?: boolean; } export interface ButtonGroupProps @@ -21,7 +22,7 @@ export const ButtonGroup = ({ ...props }: ButtonGroupProps) => { const lastIndex = options.length - 1; - const btns = options.map(({ value, label, ...props }, index) => { + const btns = options.map(({ value, label, fillWidth, ...props }, index) => { const position: ButtonPosition = index === 0 ? "left" : index === lastIndex ? "right" : "center"; return ( @@ -29,6 +30,7 @@ export const ButtonGroup = ({ key={value} $active={value === selected} $position={position} + $fillWidth={fillWidth} onClick={() => onClick?.(value)} role="button" {...props} @@ -46,6 +48,7 @@ interface ButtonProps { $active: boolean; $position: ButtonPosition; theme: DefaultTheme; + $fillWidth?: boolean; } const ButtonGroupWrapper = styled.div` @@ -82,21 +85,28 @@ const Button = styled.button` padding: ${({ theme }) => theme.click.button.basic.space.y} ${({ theme }) => theme.click.button.basic.space.x}; gap: ${({ theme }) => theme.click.button.basic.space.group}; + ${({ $fillWidth = false }) => ($fillWidth ? "flex: 1;" : "")} cursor: pointer; &:hover { background: ${({ theme }) => theme.click.button.group.color.background.hover}; } + &:disabled { + cursor: not-allowed; + background: ${({ theme, $active }) => + theme.click.button.group.color.background[ + $active ? "disabled-active" : "disabled" + ]}; + } + &:active, &:focus { background: ${({ theme }) => theme.click.button.group.color.background.active}; - } - - &:disabled { - cursor: disabled; - background: ${({ theme }) => - theme.click.button.basic.color.primary.background.disabled}; + &:disabled { + background: ${({ theme }) => + theme.click.button.group.color.background["disabled-active"]}; + } } border-radius: ${({ $position }: ButtonProps) => diff --git a/src/components/CodeBlock/CodeBlock.tsx b/src/components/CodeBlock/CodeBlock.tsx index ed32a888..e5f5dfc4 100644 --- a/src/components/CodeBlock/CodeBlock.tsx +++ b/src/components/CodeBlock/CodeBlock.tsx @@ -13,13 +13,15 @@ SyntaxHighlighter.registerLanguage("bash", bash); SyntaxHighlighter.registerLanguage("json", json); export type CodeThemeType = "light" | "dark"; -interface Props extends Omit, "children"> { +interface Props extends Omit, "children" | "onCopy"> { language?: string; children: string; theme?: CodeThemeType; showLineNumbers?: boolean; showWrapButton?: boolean; wrapLines?: boolean; + onCopy?: (value: string) => void | Promise; + onCopyError?: (error: string) => void | Promise; } interface RendererNodeType { @@ -52,9 +54,15 @@ const CodeBlockContainer = styled.div<{ $theme?: CodeThemeType }>` }} `; -const CodeButton = styled(EmptyButton)<{ $copied: boolean }>` - ${({ $copied }) => ` - color: ${$copied ? "green" : "inherit"}; +const CodeButton = styled(EmptyButton)<{ $copied: boolean; $error: boolean }>` + ${({ $copied, $error, theme }) => ` + color: ${ + $copied + ? theme.click.alert.color.text.success + : $error + ? theme.click.alert.color.text.danger + : "inherit" + }; padding: 0; border: 0; `} @@ -88,18 +96,34 @@ export const CodeBlock = ({ showLineNumbers, showWrapButton = false, wrapLines = false, + onCopy, + onCopyError, ...props }: Props) => { const [copied, setCopied] = useState(false); + const [errorCopy, setErrorCopy] = useState(false); const [wrap, setWrap] = useState(wrapLines); const customStyle = useColorStyle(theme); const ref = useRef(null); const copyCodeToClipboard = async () => { if (ref.current?.textContent) { - await navigator.clipboard.writeText(ref.current.textContent); - setCopied(true); - setTimeout(() => setCopied(false), 2000); + try { + await navigator.clipboard.writeText(ref.current.textContent); + if (typeof onCopy == "function") { + onCopy(ref.current.textContent); + } + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } catch (error) { + let message = "Unable to copy code"; + if (error instanceof Error) message = error.message; + setErrorCopy(true); + if (typeof onCopyError === "function") { + onCopyError(message); + } + setTimeout(() => setErrorCopy(false), 2000); + } } }; const wrapElement = () => { @@ -122,6 +146,7 @@ export const CodeBlock = ({ @@ -129,7 +154,8 @@ export const CodeBlock = ({ diff --git a/src/components/EllipsisContent/EllipsisContent.tsx b/src/components/EllipsisContent/EllipsisContent.tsx index 358c0918..c4d32d0f 100644 --- a/src/components/EllipsisContent/EllipsisContent.tsx +++ b/src/components/EllipsisContent/EllipsisContent.tsx @@ -1,4 +1,9 @@ -import { HTMLAttributes, forwardRef } from "react"; +import { + ComponentPropsWithRef, + ComponentPropsWithoutRef, + ElementType, + forwardRef, +} from "react"; import { mergeRefs } from "@/utils/mergeRefs"; import styled from "styled-components"; @@ -17,11 +22,18 @@ const EllipsisContainer = styled.div` text-overflow: ellipsis; } `; +export interface EllipsisContentProps { + component?: T; +} -export const EllipsisContent = forwardRef>( - (props, ref) => { +export const EllipsisContent = forwardRef( + ( + { component, ...props }: EllipsisContentProps & ComponentPropsWithoutRef, + ref: ComponentPropsWithRef["ref"] + ) => { return ( { diff --git a/src/components/Flyout/Flyout.tsx b/src/components/Flyout/Flyout.tsx index bf820c95..14e85f55 100644 --- a/src/components/Flyout/Flyout.tsx +++ b/src/components/Flyout/Flyout.tsx @@ -15,6 +15,7 @@ import { import { Button, ButtonProps, Icon, Separator } from ".."; import styled from "styled-components"; import { CrossButton } from "../commonElement"; +import { keyframes } from "styled-components"; export type FlyoutProps = DialogProps; @@ -51,6 +52,11 @@ export interface DialogContentProps extends RadixDialogContentProps { closeOnInteractOutside?: boolean; } +const animationWidth = keyframes({ + "0%": { width: 0 }, + "100%": { width: "var(--flyout-width, 100%)" }, +}); + const FlyoutContent = styled(DialogContent)<{ $size?: FlyoutSizeType; $strategy: Strategy; @@ -58,16 +64,17 @@ const FlyoutContent = styled(DialogContent)<{ display: flex; flex-direction: column; align-items: center; - width: 100%; overflow: hidden; flex: 1; top: 0; right: 0; bottom: 0; - ${({ theme, $size = "default", $strategy }) => ` + --flyout-width: ${({ theme, $size = "default" }) => + theme.click.flyout.size[$size].width}; + animation: ${animationWidth} 500ms cubic-bezier(0.16, 1, 0.3, 1) forwards; + ${({ theme, $strategy }) => ` position: ${$strategy}; height: ${$strategy === "relative" ? "100%" : "auto"}; - 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}; diff --git a/src/components/Link/Link.tsx b/src/components/Link/Link.tsx index 3af943d5..3cc4bcba 100644 --- a/src/components/Link/Link.tsx +++ b/src/components/Link/Link.tsx @@ -58,7 +58,7 @@ const IconWrapper = styled.span<{ $size: TextSize }>` `; /** Component for linking to other pages or sections from with body text */ -export const Link = ({ +export const Link = ({ size = "md", weight = "normal", className, diff --git a/src/components/Select/common/InternalSelect.tsx b/src/components/Select/common/InternalSelect.tsx index 31b7c9c9..c1be4108 100644 --- a/src/components/Select/common/InternalSelect.tsx +++ b/src/components/Select/common/InternalSelect.tsx @@ -135,6 +135,7 @@ export const InternalSelect = ({ placeholder = "Select an option", multiple, showSearch = false, + container, ...props }: SelectContainerProps) => { const defaultId = useId(); @@ -383,7 +384,7 @@ export const InternalSelect = ({ ))} )} - + { } /** Component for writing blocks of body copy */ -const _Text = ({ color, size, weight, className, children, ...props }: TextProps) => ( - - {children} - +const _Text = forwardRef( + ({ color, size, weight, className, children, ...props }, ref) => ( + + {children} + + ) ); const CuiText = styled.p<{ $color?: TextColor; $size?: TextSize; $weight?: TextWeight }>` diff --git a/src/components/Typography/Title/Title.tsx b/src/components/Typography/Title/Title.tsx index a77d86c2..c4bb19ea 100644 --- a/src/components/Typography/Title/Title.tsx +++ b/src/components/Typography/Title/Title.tsx @@ -1,4 +1,4 @@ -import { HTMLAttributes } from "react"; +import { HTMLAttributes, forwardRef } from "react"; import styled from "styled-components"; export type TitleColor = "default" | "muted"; export type TitleSize = "xs" | "sm" | "md" | "lg" | "xl"; @@ -13,16 +13,19 @@ export interface TitleProps extends HTMLAttributes { } /** The `title` component allows you to easily add headings to your pages. They do not include built in margins. */ -export const Title = ({ size, family, type, color, children, ...props }: TitleProps) => ( - - {children} - +export const Title = forwardRef( + ({ size, family, type, color, children, ...props }, ref) => ( + + {children} + + ) ); const CuiTitle = styled.div<{ diff --git a/src/styles/types.ts b/src/styles/types.ts index bb118b75..0b3d4ac7 100644 --- a/src/styles/types.ts +++ b/src/styles/types.ts @@ -604,12 +604,16 @@ "default": string, "hover": string, "active": string, - "panel": string + "panel": string, + "disabled": string, + "disabled-active": string }, "text": { "default": string, "hover": string, - "active": string + "active": string, + "disabled": string, + "disabled-active": string }, "stroke": { "panel": string diff --git a/src/styles/variables.classic.json b/src/styles/variables.classic.json index 7b12d36f..6ee1912b 100644 --- a/src/styles/variables.classic.json +++ b/src/styles/variables.classic.json @@ -222,12 +222,16 @@ "default": "lch(100 0 0)", "hover": "lch(97.4 1.44 272)", "active": "lch(95.3 1.54 272)", - "panel": "lch(100 0 0)" + "panel": "lch(100 0 0)", + "disabled": "#dfdfdf", + "disabled-active": "lch(97.9 0 0)" }, "text": { "default": "#161517", "hover": "#161517", - "active": "#151515" + "active": "#151515", + "disabled": "#a0a0a0", + "disabled-active": "#808080" }, "stroke": { "panel": "lch(91.6 1.1 266)" diff --git a/src/styles/variables.dark.json b/src/styles/variables.dark.json index 296287fa..0a79a68d 100644 --- a/src/styles/variables.dark.json +++ b/src/styles/variables.dark.json @@ -324,13 +324,17 @@ "background": { "default": "lch(18.2 0 0)", "hover": "lch(22.8 0 0)", - "active": "#414141", - "panel": "lch(18.2 0 0)" + "active": "#1F1F1C", + "panel": "lch(18.2 0 0)", + "disabled": "#414141", + "disabled-active": "lch(22 0 0)" }, "text": { "default": "#ffffff", "hover": "#ffffff", - "active": "#ffffff" + "active": "#ffffff", + "disabled": "#808080", + "disabled-active": "#808080" }, "stroke": { "panel": "lch(27.5 0 0 / 0.3)" diff --git a/src/styles/variables.json b/src/styles/variables.json index b65677db..c5b4bcee 100644 --- a/src/styles/variables.json +++ b/src/styles/variables.json @@ -603,12 +603,16 @@ "default": "lch(94.5 0.72 266)", "hover": "lch(97.4 1.44 272)", "active": "#ffffff", - "panel": "lch(94.5 0.72 266)" + "panel": "lch(94.5 0.72 266)", + "disabled": "#dfdfdf", + "disabled-active": "lch(88.9 0 0)" }, "text": { "default": "#161517", "hover": "#161517", - "active": "#151515" + "active": "#151515", + "disabled": "#a0a0a0", + "disabled-active": "#808080" }, "stroke": { "panel": "lch(91.6 1.1 266)" diff --git a/src/styles/variables.light.json b/src/styles/variables.light.json index 5a92b9a5..04cfeb59 100644 --- a/src/styles/variables.light.json +++ b/src/styles/variables.light.json @@ -325,12 +325,16 @@ "default": "lch(94.5 0.72 266)", "hover": "lch(97.4 1.44 272)", "active": "#ffffff", - "panel": "lch(94.5 0.72 266)" + "panel": "lch(94.5 0.72 266)", + "disabled": "#dfdfdf", + "disabled-active": "lch(88.9 0 0)" }, "text": { "default": "#161517", "hover": "#161517", - "active": "#151515" + "active": "#151515", + "disabled": "#a0a0a0", + "disabled-active": "#808080" }, "stroke": { "panel": "lch(91.6 1.1 266)"