Optimized a few components

This commit is contained in:
Gregory Schier
2023-03-18 18:49:01 -07:00
parent afcf630443
commit c0d9740a7d
17 changed files with 200 additions and 156 deletions

View File

@@ -3,7 +3,14 @@ import { CheckIcon } from '@radix-ui/react-icons';
import classnames from 'classnames';
import { motion } from 'framer-motion';
import type { ForwardedRef, ReactElement, ReactNode } from 'react';
import { forwardRef, memo, useImperativeHandle, useLayoutEffect, useState } from 'react';
import {
forwardRef,
memo,
useCallback,
useImperativeHandle,
useLayoutEffect,
useState,
} from 'react';
export interface DropdownMenuRadioItem {
label: string;
@@ -18,19 +25,22 @@ export interface DropdownMenuRadioProps {
items: DropdownMenuRadioItem[];
}
export function DropdownMenuRadio({
export const DropdownMenuRadio = memo(function DropdownMenuRadio({
children,
items,
onValueChange,
label,
value,
}: DropdownMenuRadioProps) {
const handleChange = (value: string) => {
const item = items.find((item) => item.value === value);
if (item && onValueChange) {
onValueChange(item);
}
};
const handleChange = useCallback(
(value: string) => {
const item = items.find((item) => item.value === value);
if (item && onValueChange) {
onValueChange(item);
}
},
[items, onValueChange],
);
return (
<D.Root>
@@ -49,7 +59,7 @@ export function DropdownMenuRadio({
</DropdownMenuPortal>
</D.Root>
);
}
});
export interface DropdownProps {
children: ReactElement<typeof DropdownMenuTrigger>;
@@ -212,7 +222,11 @@ function DropdownMenuItem({
type DropdownMenuRadioItemProps = Omit<D.DropdownMenuRadioItemProps & ItemInnerProps, 'leftSlot'>;
function DropdownMenuRadioItem({ rightSlot, children, ...props }: DropdownMenuRadioItemProps) {
const DropdownMenuRadioItem = memo(function DropdownMenuRadioItem({
rightSlot,
children,
...props
}: DropdownMenuRadioItemProps) {
return (
<D.RadioItem asChild {...props}>
<ItemInner
@@ -227,7 +241,7 @@ function DropdownMenuRadioItem({ rightSlot, children, ...props }: DropdownMenuRa
</ItemInner>
</D.RadioItem>
);
}
});
// const DropdownMenuSubContent = forwardRef<HTMLDivElement, DropdownMenu.DropdownMenuSubContentProps>(
// function DropdownMenuSubContent(
@@ -270,13 +284,17 @@ type DropdownMenuTriggerProps = D.DropdownMenuTriggerProps & {
className?: string;
};
export function DropdownMenuTrigger({ children, className, ...props }: DropdownMenuTriggerProps) {
export const DropdownMenuTrigger = memo(function DropdownMenuTrigger({
children,
className,
...props
}: DropdownMenuTriggerProps) {
return (
<D.Trigger asChild className={classnames(className)} {...props}>
{children}
</D.Trigger>
);
}
});
interface ItemInnerProps {
leftSlot?: ReactNode;

View File

@@ -28,6 +28,7 @@ import {
UpdateIcon,
} from '@radix-ui/react-icons';
import classnames from 'classnames';
import { memo } from 'react';
const icons = {
archive: ArchiveIcon,
@@ -67,7 +68,7 @@ export interface IconProps {
spin?: boolean;
}
export function Icon({ icon, spin, size = 'md', className }: IconProps) {
export const Icon = memo(function Icon({ icon, spin, size = 'md', className }: IconProps) {
const Component = icons[icon] ?? icons.question;
return (
<Component
@@ -81,4 +82,4 @@ export function Icon({ icon, spin, size = 'md', className }: IconProps) {
)}
/>
);
}
});

View File

@@ -1,85 +1,46 @@
import classnames from 'classnames';
import type { ComponentType, ReactNode } from 'react';
import { Children, Fragment } from 'react';
const spaceClassesX = {
0: 'pr-0',
1: 'pr-1',
2: 'pr-2',
3: 'pr-3',
4: 'pr-4',
5: 'pr-5',
6: 'pr-6',
};
const spaceClassesY = {
0: 'pt-0',
1: 'pt-1',
2: 'pt-2',
3: 'pt-3',
4: 'pt-4',
5: 'pt-5',
6: 'pt-6',
const gapClasses = {
0: 'gap-0',
1: 'gap-1',
2: 'gap-2',
3: 'gap-3',
4: 'gap-4',
5: 'gap-5',
6: 'gap-6',
};
interface HStackProps extends BaseStackProps {
space?: keyof typeof spaceClassesX;
children?: ReactNode;
}
export function HStack({ className, space, children, ...props }: HStackProps) {
return (
<BaseStack className={classnames(className, 'flex-row')} {...props}>
{space
? Children.toArray(children)
.filter(Boolean) // Remove null/false/undefined children
.map((c, i) => (
<Fragment key={i}>
{i > 0 ? (
<div
className={classnames(spaceClassesX[space], 'pointer-events-none')}
data-spacer=""
aria-hidden
/>
) : null}
{c}
</Fragment>
))
: children}
<BaseStack className={classnames(className, 'flex-row', space && gapClasses[space])} {...props}>
{children}
</BaseStack>
);
}
export interface VStackProps extends BaseStackProps {
space?: keyof typeof spaceClassesY;
children: ReactNode;
}
export function VStack({ className, space, children, ...props }: VStackProps) {
return (
<BaseStack className={classnames(className, 'w-full h-full flex-col')} {...props}>
{space
? Children.toArray(children)
.filter(Boolean) // Remove null/false/undefined children
.map((c, i) => (
<Fragment key={i}>
{i > 0 ? (
<div
className={classnames(spaceClassesY[space], 'pointer-events-none')}
data-spacer=""
aria-hidden
/>
) : null}
{c}
</Fragment>
))
: children}
<BaseStack
className={classnames(className, 'w-full h-full flex-col', space && gapClasses[space])}
{...props}
>
{children}
</BaseStack>
);
}
interface BaseStackProps {
as?: ComponentType | 'ul';
space?: keyof typeof gapClasses;
alignItems?: 'start' | 'center';
justifyContent?: 'start' | 'center' | 'end';
className?: string;

View File

@@ -9,19 +9,21 @@ import { HStack } from '../Stacks';
import './Tabs.css';
export type TabItem = {
value: string;
label: string;
options?: {
onValueChange: DropdownMenuRadioProps['onValueChange'];
value: string;
items: DropdownMenuRadioItem[];
};
};
interface Props {
label: string;
onChangeValue: (value: string) => void;
value: string;
tabs: {
value: string;
label: string;
options?: {
onValueChange: DropdownMenuRadioProps['onValueChange'];
value: string;
items: DropdownMenuRadioItem[];
};
}[];
tabs: TabItem[];
tabListClassName?: string;
className?: string;
children: ReactNode;