From a19eb6bcdaf826afe9f957995b2787927e688fa2 Mon Sep 17 00:00:00 2001 From: Aarnav Tale Date: Tue, 28 Jan 2025 16:04:42 -0500 Subject: [PATCH] feat: add new menu --- app/components/Button.tsx | 5 +- app/components/Code.tsx | 7 +- app/components/Dialog.tsx | 2 +- app/components/Header.tsx | 122 +++++++++---------- app/components/IconButton.tsx | 37 +++--- app/components/Input.tsx | 11 +- app/components/Menu.tsx | 213 +++++++++++++++++++++------------ app/components/NumberField.tsx | 46 ------- app/components/NumberInput.tsx | 3 +- app/components/Popover.tsx | 49 ++++++++ app/components/Select.tsx | 211 ++++++++++++++++++++++---------- app/components/TextField.tsx | 30 ----- app/components/Toaster.tsx | 91 -------------- 13 files changed, 428 insertions(+), 399 deletions(-) delete mode 100644 app/components/NumberField.tsx create mode 100644 app/components/Popover.tsx delete mode 100644 app/components/TextField.tsx delete mode 100644 app/components/Toaster.tsx diff --git a/app/components/Button.tsx b/app/components/Button.tsx index 3f1c337..ffcc81d 100644 --- a/app/components/Button.tsx +++ b/app/components/Button.tsx @@ -1,4 +1,3 @@ -import type { Dispatch, SetStateAction } from 'react'; import React, { useRef } from 'react'; import { type AriaButtonOptions, useButton } from 'react-aria'; import { cn } from '~/utils/cn'; @@ -7,10 +6,12 @@ export interface ButtonProps extends AriaButtonOptions<'button'> { variant?: 'heavy' | 'light' | 'danger'; className?: string; children?: React.ReactNode; + ref?: React.RefObject; } export default function Button({ variant = 'light', ...props }: ButtonProps) { - const ref = useRef(null); + // In case the button is used as a trigger ref + const ref = props.ref ?? useRef(null); const { buttonProps } = useButton(props, ref); return ( diff --git a/app/components/Code.tsx b/app/components/Code.tsx index 4fbb2b4..79834c4 100644 --- a/app/components/Code.tsx +++ b/app/components/Code.tsx @@ -1,7 +1,7 @@ -import { useState, HTMLProps } from 'react'; -import { CopyIcon, CheckIcon } from '@primer/octicons-react'; +import { CheckIcon, CopyIcon } from '@primer/octicons-react'; +import { HTMLProps, useState } from 'react'; import { cn } from '~/utils/cn'; -import { toast } from '~/components/Toaster'; +import toast from '~/utils/toast'; interface Props extends HTMLProps { isCopyable?: boolean; @@ -22,6 +22,7 @@ export default function Code(props: Props) { {props.isCopyable ? ( - - - + { + if (key === 'logout') { + submit( + {}, + { + method: 'POST', + action: '/logout', + }, + ); + } + }} + disabledKeys={['profile']} + > + + +
+

{data.user.name}

+

{data.user.email}

+
+
+ +

Logout

+
+
+
) : undefined} @@ -131,29 +125,21 @@ export default function Header(data: Props) { } - /> - } + icon={} /> + } /> } + icon={} /> {data.config.read ? ( <> - } - /> + } /> } + icon={} /> ) : undefined} diff --git a/app/components/IconButton.tsx b/app/components/IconButton.tsx index a16a88e..4223535 100644 --- a/app/components/IconButton.tsx +++ b/app/components/IconButton.tsx @@ -1,17 +1,21 @@ -import type { Dispatch, SetStateAction } from 'react'; import React, { useRef } from 'react'; -import { useButton, type AriaButtonOptions } from 'react-aria'; +import { type AriaButtonOptions, useButton } from 'react-aria'; import { cn } from '~/utils/cn'; export interface IconButtonProps extends AriaButtonOptions<'button'> { - variant?: 'heavy' | 'light' - className?: string - children: React.ReactNode - label: string + variant?: 'heavy' | 'light'; + className?: string; + children: React.ReactNode; + label: string; + ref?: React.RefObject; } -export default function IconButton({ variant = 'light', ...props }: IconButtonProps) { - const ref = useRef(null); +export default function IconButton({ + variant = 'light', + ...props +}: IconButtonProps) { + // In case the button is used as a trigger ref + const ref = props.ref ?? useRef(null); const { buttonProps } = useButton(props, ref); return ( @@ -25,17 +29,18 @@ export default function IconButton({ variant = 'light', ...props }: IconButtonPr props.isDisabled && 'opacity-60 cursor-not-allowed', ...(variant === 'heavy' ? [ - 'bg-headplane-900 dark:bg-headplane-50 font-semibold', - 'hover:bg-headplane-900/90 dark:hover:bg-headplane-50/90', - 'text-headplane-200 dark:text-headplane-800' - ] : [ - 'bg-headplane-100 dark:bg-headplane-700/30 font-medium', - 'hover:bg-headplane-200/90 dark:hover:bg-headplane-800/30', - ]), + 'bg-headplane-900 dark:bg-headplane-50 font-semibold', + 'hover:bg-headplane-900/90 dark:hover:bg-headplane-50/90', + 'text-headplane-200 dark:text-headplane-800', + ] + : [ + 'bg-headplane-100 dark:bg-headplane-700/30 font-medium', + 'hover:bg-headplane-200/90 dark:hover:bg-headplane-800/30', + ]), props.className, )} > {props.children} - ) + ); } diff --git a/app/components/Input.tsx b/app/components/Input.tsx index 5bc50ee..4422a53 100644 --- a/app/components/Input.tsx +++ b/app/components/Input.tsx @@ -1,14 +1,17 @@ import { useRef } from 'react'; -import { type AriaTextFieldProps, useTextField } from 'react-aria'; +import { type AriaTextFieldProps, useId, useTextField } from 'react-aria'; import cn from '~/utils/cn'; export interface InputProps extends AriaTextFieldProps { isRequired?: boolean; + className?: string; } export default function Input(props: InputProps) { - const { label } = props; + const { label, className } = props; const ref = useRef(null); + const id = useId(props.id); + const { labelProps, inputProps, @@ -22,7 +25,7 @@ export default function Input(props: InputProps) {