mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 22:04:56 +00:00
code: tsx,nextjs,frontends (4 files)
This commit is contained in:
@@ -1,27 +1,32 @@
|
||||
'use client'
|
||||
|
||||
import { Box, Breadcrumbs as MuiBreadcrumbs, Link, Typography } from '@mui/material'
|
||||
import { Box, Breadcrumbs, Link, Typography } from '@/fakemui'
|
||||
import { forwardRef, ReactNode } from 'react'
|
||||
|
||||
import { MoreHoriz, NavigateNext } from '@/fakemui/icons'
|
||||
|
||||
import styles from './Breadcrumb.module.scss'
|
||||
|
||||
interface BreadcrumbProps {
|
||||
children: ReactNode
|
||||
className?: string
|
||||
}
|
||||
|
||||
const Breadcrumb = forwardRef<HTMLElement, BreadcrumbProps>(({ children, ...props }, ref) => {
|
||||
return (
|
||||
<MuiBreadcrumbs
|
||||
ref={ref}
|
||||
separator={<NavigateNext size={16} />}
|
||||
aria-label="breadcrumb"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</MuiBreadcrumbs>
|
||||
)
|
||||
})
|
||||
const Breadcrumb = forwardRef<HTMLElement, BreadcrumbProps>(
|
||||
({ children, className = '', ...props }, ref) => {
|
||||
return (
|
||||
<Breadcrumbs
|
||||
ref={ref}
|
||||
separator={<NavigateNext size={16} />}
|
||||
className={`${styles.breadcrumbs} ${className}`}
|
||||
aria-label="breadcrumb"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</Breadcrumbs>
|
||||
)
|
||||
}
|
||||
)
|
||||
Breadcrumb.displayName = 'Breadcrumb'
|
||||
|
||||
interface BreadcrumbListProps {
|
||||
@@ -30,7 +35,7 @@ interface BreadcrumbListProps {
|
||||
}
|
||||
|
||||
const BreadcrumbList = forwardRef<HTMLOListElement, BreadcrumbListProps>(
|
||||
({ children, ...props }, ref) => {
|
||||
({ children, className = '', ...props }, ref) => {
|
||||
return <>{children}</>
|
||||
}
|
||||
)
|
||||
@@ -42,9 +47,9 @@ interface BreadcrumbItemProps {
|
||||
}
|
||||
|
||||
const BreadcrumbItem = forwardRef<HTMLLIElement, BreadcrumbItemProps>(
|
||||
({ children, ...props }, ref) => {
|
||||
({ children, className = '', ...props }, ref) => {
|
||||
return (
|
||||
<Box component="span" ref={ref} {...props}>
|
||||
<Box component="span" ref={ref} className={`${styles.breadcrumbItem} ${className}`} {...props}>
|
||||
{children}
|
||||
</Box>
|
||||
)
|
||||
@@ -60,14 +65,14 @@ interface BreadcrumbLinkProps {
|
||||
}
|
||||
|
||||
const BreadcrumbLink = forwardRef<HTMLAnchorElement, BreadcrumbLinkProps>(
|
||||
({ children, href, ...props }, ref) => {
|
||||
({ children, href, className = '', ...props }, ref) => {
|
||||
return (
|
||||
<Link
|
||||
ref={ref}
|
||||
href={href}
|
||||
underline="hover"
|
||||
color="inherit"
|
||||
sx={{ display: 'flex', alignItems: 'center', fontSize: '0.875rem' }}
|
||||
className={`${styles.breadcrumbLink} ${className}`}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
@@ -83,12 +88,11 @@ interface BreadcrumbPageProps {
|
||||
}
|
||||
|
||||
const BreadcrumbPage = forwardRef<HTMLSpanElement, BreadcrumbPageProps>(
|
||||
({ children, ...props }, ref) => {
|
||||
({ children, className = '', ...props }, ref) => {
|
||||
return (
|
||||
<Typography
|
||||
ref={ref}
|
||||
color="text.primary"
|
||||
sx={{ fontSize: '0.875rem', fontWeight: 500 }}
|
||||
className={`${styles.breadcrumbPage} ${className}`}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
@@ -103,10 +107,10 @@ interface BreadcrumbSeparatorProps {
|
||||
className?: string
|
||||
}
|
||||
|
||||
const BreadcrumbSeparator = ({ children, ...props }: BreadcrumbSeparatorProps) => {
|
||||
const BreadcrumbSeparator = ({ children, className = '', ...props }: BreadcrumbSeparatorProps) => {
|
||||
return (
|
||||
<Box component="span" sx={{ mx: 1, color: 'text.secondary' }} {...props}>
|
||||
{children || <NavigateNextIcon fontSize="small" />}
|
||||
<Box component="span" className={`${styles.breadcrumbSeparator} ${className}`} {...props}>
|
||||
{children || <NavigateNext size={16} />}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -116,13 +120,11 @@ interface BreadcrumbEllipsisProps {
|
||||
className?: string
|
||||
}
|
||||
|
||||
const BreadcrumbEllipsis = ({ ...props }: BreadcrumbEllipsisProps) => {
|
||||
const BreadcrumbEllipsis = ({ className = '', ...props }: BreadcrumbEllipsisProps) => {
|
||||
return (
|
||||
<Box component="span" sx={{ display: 'flex', alignItems: 'center' }} {...props}>
|
||||
<MoreHorizIcon fontSize="small" />
|
||||
<Box component="span" sx={{ position: 'absolute', width: 1, height: 1, overflow: 'hidden' }}>
|
||||
More
|
||||
</Box>
|
||||
<Box component="span" className={`${styles.breadcrumbEllipsis} ${className}`} {...props}>
|
||||
<MoreHoriz size={16} />
|
||||
<span className={styles.srOnly}>More</span>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,19 +1,12 @@
|
||||
'use client'
|
||||
|
||||
import {
|
||||
Box,
|
||||
Collapse,
|
||||
Divider,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemButton,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
} from '@mui/material'
|
||||
import { Box, Collapse, Divider, List, ListItem, ListItemIcon, ListItemText } from '@/fakemui'
|
||||
import { forwardRef, ReactNode, useState } from 'react'
|
||||
|
||||
import { ExpandLess, ExpandMore } from '@/fakemui/icons'
|
||||
|
||||
import styles from './NavGroup.module.scss'
|
||||
|
||||
export interface NavGroupProps {
|
||||
label: string
|
||||
icon?: ReactNode
|
||||
@@ -26,7 +19,7 @@ export interface NavGroupProps {
|
||||
|
||||
const NavGroup = forwardRef<HTMLDivElement, NavGroupProps>(
|
||||
(
|
||||
{ label, icon, children, defaultOpen = false, disabled = false, divider = false, ...props },
|
||||
{ label, icon, children, defaultOpen = false, disabled = false, divider = false, className = '', ...props },
|
||||
ref
|
||||
) => {
|
||||
const [open, setOpen] = useState(defaultOpen)
|
||||
@@ -37,51 +30,44 @@ const NavGroup = forwardRef<HTMLDivElement, NavGroupProps>(
|
||||
}
|
||||
}
|
||||
|
||||
const buttonClasses = [styles.groupButton, disabled && styles.groupButtonDisabled]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
|
||||
const collapseClasses = [styles.collapse, open && styles.collapseOpen].filter(Boolean).join(' ')
|
||||
|
||||
const childListClasses = [
|
||||
styles.childList,
|
||||
icon ? styles.childListWithIcon : styles.childListNoIcon,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
|
||||
return (
|
||||
<Box ref={ref} {...props}>
|
||||
{divider && <Divider sx={{ my: 1 }} />}
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton
|
||||
<Box ref={ref} className={`${styles.navGroup} ${className}`} {...props}>
|
||||
{divider && <Divider className={styles.divider} />}
|
||||
<ListItem className={styles.groupItem}>
|
||||
<button
|
||||
type="button"
|
||||
className={buttonClasses}
|
||||
onClick={handleToggle}
|
||||
disabled={disabled}
|
||||
sx={{
|
||||
borderRadius: 1,
|
||||
mx: 0.5,
|
||||
my: 0.25,
|
||||
'&:hover': {
|
||||
bgcolor: 'action.hover',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{icon && (
|
||||
<ListItemIcon
|
||||
sx={{
|
||||
minWidth: 40,
|
||||
color: 'text.secondary',
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
</ListItemIcon>
|
||||
)}
|
||||
{icon && <ListItemIcon className={styles.icon}>{icon}</ListItemIcon>}
|
||||
<ListItemText
|
||||
primary={label}
|
||||
primaryTypographyProps={{
|
||||
variant: 'body2',
|
||||
fontWeight: 600,
|
||||
color: 'text.primary',
|
||||
}}
|
||||
primary={<span className={styles.labelText}>{label}</span>}
|
||||
/>
|
||||
{open ? (
|
||||
<ExpandLess size={16} style={{ color: 'rgba(0,0,0,0.54)' }} />
|
||||
) : (
|
||||
<ExpandMore size={16} style={{ color: 'rgba(0,0,0,0.54)' }} />
|
||||
)}
|
||||
</ListItemButton>
|
||||
<span className={styles.expandIcon}>
|
||||
{open ? (
|
||||
<ExpandLess size={16} />
|
||||
) : (
|
||||
<ExpandMore size={16} />
|
||||
)}
|
||||
</span>
|
||||
</button>
|
||||
</ListItem>
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
<List component="div" disablePadding sx={{ pl: icon ? 3 : 1 }}>
|
||||
{children}
|
||||
</List>
|
||||
<Collapse in={open} className={collapseClasses}>
|
||||
<List className={childListClasses}>{children}</List>
|
||||
</Collapse>
|
||||
</Box>
|
||||
)
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
'use client'
|
||||
|
||||
import {
|
||||
Badge,
|
||||
Box,
|
||||
ListItem,
|
||||
ListItemButton,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
type ListItemProps,
|
||||
} from '@mui/material'
|
||||
import { ListItem, ListItemIcon, ListItemText } from '@/fakemui'
|
||||
import { forwardRef, ReactNode } from 'react'
|
||||
|
||||
export interface NavItemProps extends Omit<ListItemProps, 'children'> {
|
||||
import styles from './NavItem.module.scss'
|
||||
|
||||
export interface NavItemProps extends React.LiHTMLAttributes<HTMLLIElement> {
|
||||
icon?: ReactNode
|
||||
label: string
|
||||
onClick?: () => void
|
||||
@@ -25,6 +19,16 @@ export interface NavItemProps extends Omit<ListItemProps, 'children'> {
|
||||
className?: string
|
||||
}
|
||||
|
||||
const badgeColorMap: Record<string, string> = {
|
||||
default: styles.badgeDefault,
|
||||
primary: styles.badgePrimary,
|
||||
secondary: styles.badgeSecondary,
|
||||
error: styles.badgeError,
|
||||
warning: styles.badgeWarning,
|
||||
info: styles.badgeInfo,
|
||||
success: styles.badgeSuccess,
|
||||
}
|
||||
|
||||
const NavItem = forwardRef<HTMLLIElement, NavItemProps>(
|
||||
(
|
||||
{
|
||||
@@ -38,93 +42,61 @@ const NavItem = forwardRef<HTMLLIElement, NavItemProps>(
|
||||
href,
|
||||
secondaryLabel,
|
||||
dense = false,
|
||||
sx,
|
||||
className = '',
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const buttonClasses = [
|
||||
styles.navItemButton,
|
||||
active && styles.navItemButtonSelected,
|
||||
disabled && styles.navItemButtonDisabled,
|
||||
dense && styles.navItemButtonDense,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
|
||||
const iconClasses = [styles.icon, active && styles.iconActive].filter(Boolean).join(' ')
|
||||
|
||||
const primaryTextClasses = [styles.textPrimary, active && styles.textPrimaryActive]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
|
||||
const badgeClasses = [styles.badge, badgeColorMap[badgeColor]].filter(Boolean).join(' ')
|
||||
|
||||
const ButtonComponent = href ? 'a' : 'button'
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
ref={ref}
|
||||
disablePadding
|
||||
{...props}
|
||||
sx={sx}
|
||||
>
|
||||
<ListItemButton
|
||||
<ListItem ref={ref} className={`${styles.navItem} ${className}`} {...props}>
|
||||
<ButtonComponent
|
||||
className={buttonClasses}
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
selected={active}
|
||||
dense={dense}
|
||||
href={href}
|
||||
sx={{
|
||||
borderRadius: 1,
|
||||
mx: 0.5,
|
||||
my: 0.25,
|
||||
'&.Mui-selected': {
|
||||
bgcolor: 'action.selected',
|
||||
'&:hover': {
|
||||
bgcolor: 'action.hover',
|
||||
},
|
||||
},
|
||||
'&:hover': {
|
||||
bgcolor: 'action.hover',
|
||||
},
|
||||
}}
|
||||
{...(href ? { href } : { type: 'button' })}
|
||||
>
|
||||
{icon && (
|
||||
<ListItemIcon
|
||||
sx={{
|
||||
minWidth: 40,
|
||||
color: active ? 'primary.main' : 'text.secondary',
|
||||
}}
|
||||
>
|
||||
<ListItemIcon className={iconClasses}>
|
||||
{badge !== undefined ? (
|
||||
<Badge
|
||||
badgeContent={badge}
|
||||
color={badgeColor}
|
||||
sx={{
|
||||
'& .MuiBadge-badge': {
|
||||
fontSize: '0.625rem',
|
||||
height: 16,
|
||||
minWidth: 16,
|
||||
padding: '0 4px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<span className={styles.badgeWrapper}>
|
||||
{icon}
|
||||
</Badge>
|
||||
<span className={badgeClasses}>{badge}</span>
|
||||
</span>
|
||||
) : (
|
||||
icon
|
||||
)}
|
||||
</ListItemIcon>
|
||||
)}
|
||||
<ListItemText
|
||||
primary={label}
|
||||
secondary={secondaryLabel}
|
||||
primaryTypographyProps={{
|
||||
variant: 'body2',
|
||||
fontWeight: active ? 600 : 400,
|
||||
color: active ? 'primary.main' : 'text.primary',
|
||||
}}
|
||||
secondaryTypographyProps={{
|
||||
variant: 'caption',
|
||||
}}
|
||||
className={styles.text}
|
||||
primary={<span className={primaryTextClasses}>{label}</span>}
|
||||
secondary={secondaryLabel && <span className={styles.textSecondary}>{secondaryLabel}</span>}
|
||||
/>
|
||||
{badge !== undefined && !icon && (
|
||||
<Box sx={{ ml: 1 }}>
|
||||
<Badge
|
||||
badgeContent={badge}
|
||||
color={badgeColor}
|
||||
sx={{
|
||||
'& .MuiBadge-badge': {
|
||||
position: 'static',
|
||||
transform: 'none',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<span className={styles.badgeWrapper}>
|
||||
<span className={`${badgeClasses} ${styles.badgeStatic}`}>{badge}</span>
|
||||
</span>
|
||||
)}
|
||||
</ListItemButton>
|
||||
</ButtonComponent>
|
||||
</ListItem>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
'use client'
|
||||
|
||||
import { Box, Link as MuiLink, LinkProps as MuiLinkProps } from '@mui/material'
|
||||
import { Box, Link } from '@/fakemui'
|
||||
import { forwardRef, ReactNode } from 'react'
|
||||
|
||||
export interface NavLinkProps extends Omit<MuiLinkProps, 'component'> {
|
||||
import styles from './NavLink.module.scss'
|
||||
|
||||
export interface NavLinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
||||
href: string
|
||||
active?: boolean
|
||||
disabled?: boolean
|
||||
@@ -13,56 +15,33 @@ export interface NavLinkProps extends Omit<MuiLinkProps, 'component'> {
|
||||
}
|
||||
|
||||
const NavLink = forwardRef<HTMLAnchorElement, NavLinkProps>(
|
||||
({ href, active = false, disabled = false, children, icon, sx, ...props }, ref) => {
|
||||
({ href, active = false, disabled = false, children, icon, className = '', ...props }, ref) => {
|
||||
const linkClasses = [
|
||||
styles.navLink,
|
||||
active && styles.navLinkActive,
|
||||
disabled && styles.navLinkDisabled,
|
||||
className,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
|
||||
const iconClasses = [styles.icon, active && styles.iconActive].filter(Boolean).join(' ')
|
||||
|
||||
return (
|
||||
<MuiLink
|
||||
<Link
|
||||
ref={ref}
|
||||
href={disabled ? undefined : href}
|
||||
underline="none"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 1,
|
||||
px: 2,
|
||||
py: 1,
|
||||
borderRadius: 1,
|
||||
fontSize: '0.875rem',
|
||||
fontWeight: active ? 600 : 500,
|
||||
color: active ? 'primary.main' : 'text.primary',
|
||||
bgcolor: active ? 'action.selected' : 'transparent',
|
||||
cursor: disabled ? 'not-allowed' : 'pointer',
|
||||
opacity: disabled ? 0.5 : 1,
|
||||
transition: 'background-color 0.2s, color 0.2s',
|
||||
'&:hover': disabled
|
||||
? {}
|
||||
: {
|
||||
bgcolor: active ? 'action.selected' : 'action.hover',
|
||||
color: active ? 'primary.main' : 'text.primary',
|
||||
},
|
||||
'&:focus-visible': {
|
||||
outline: '2px solid',
|
||||
outlineColor: 'primary.main',
|
||||
outlineOffset: 2,
|
||||
},
|
||||
...sx,
|
||||
}}
|
||||
className={linkClasses}
|
||||
{...props}
|
||||
>
|
||||
{icon && (
|
||||
<Box
|
||||
component="span"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
fontSize: '1.25rem',
|
||||
color: active ? 'primary.main' : 'text.secondary',
|
||||
}}
|
||||
>
|
||||
<Box component="span" className={iconClasses}>
|
||||
{icon}
|
||||
</Box>
|
||||
)}
|
||||
{children}
|
||||
</MuiLink>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user