mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-20 00:23:58 +01:00
Optimized a few components
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user