Files
react-test/packages/mui-joy/src/Card/Card.tsx
how2ice 005cf56baf
Some checks failed
No response / noResponse (push) Has been cancelled
CI / Continuous releases (push) Has been cancelled
CI / test-dev (macos-latest) (push) Has been cancelled
CI / test-dev (ubuntu-latest) (push) Has been cancelled
CI / test-dev (windows-latest) (push) Has been cancelled
Maintenance / main (push) Has been cancelled
Scorecards supply-chain security / Scorecards analysis (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled
init project
2025-12-12 14:26:25 +09:00

265 lines
8.6 KiB
TypeScript

'use client';
import * as React from 'react';
import clsx from 'clsx';
import PropTypes from 'prop-types';
import { unstable_composeClasses as composeClasses } from '@mui/base';
import { OverridableComponent } from '@mui/types';
import capitalize from '@mui/utils/capitalize';
import isMuiElement from '@mui/utils/isMuiElement';
import { useThemeProps } from '../styles';
import { applySolidInversion, applySoftInversion } from '../colorInversion';
import styled from '../styles/styled';
import { getCardUtilityClass } from './cardClasses';
import { CardProps, CardOwnerState, CardTypeMap } from './CardProps';
import { resolveSxValue } from '../styles/styleUtils';
import useSlot from '../utils/useSlot';
import { DividerProps } from '../Divider';
const useUtilityClasses = (ownerState: CardOwnerState) => {
const { size, variant, color, orientation } = ownerState;
const slots = {
root: [
'root',
orientation,
variant && `variant${capitalize(variant)}`,
color && `color${capitalize(color)}`,
size && `size${capitalize(size)}`,
],
};
return composeClasses(slots, getCardUtilityClass, {});
};
export const StyledCardRoot = styled('div')<{ ownerState: CardOwnerState }>(({
theme,
ownerState,
}) => {
const { p, padding, borderRadius } = resolveSxValue({ theme, ownerState }, [
'p',
'padding',
'borderRadius',
]);
return [
{
'--Icon-color':
ownerState.color !== 'neutral' || ownerState.variant === 'solid'
? 'currentColor'
: theme.vars.palette.text.icon,
// a context variable for any child component
'--Card-childRadius':
'max((var(--Card-radius) - var(--variant-borderWidth, 0px)) - var(--Card-padding), min(var(--Card-padding) / 2, (var(--Card-radius) - var(--variant-borderWidth, 0px)) / 2))',
// AspectRatio integration
'--AspectRatio-radius': 'var(--Card-childRadius)',
// Link integration
'--unstable_actionMargin': 'calc(-1 * var(--variant-borderWidth, 0px))',
// Link, Radio, Checkbox integration
'--unstable_actionRadius': 'var(--Card-radius)',
// CardCover integration
'--CardCover-radius': 'calc(var(--Card-radius) - var(--variant-borderWidth, 0px))',
// CardOverflow integration
'--CardOverflow-offset': `calc(-1 * var(--Card-padding))`,
'--CardOverflow-radius': 'calc(var(--Card-radius) - var(--variant-borderWidth, 0px))',
// Divider integration
'--Divider-inset': 'calc(-1 * var(--Card-padding))',
...(ownerState.size === 'sm' && {
'--Card-radius': theme.vars.radius.sm,
'--Card-padding': '0.625rem',
gap: '0.5rem',
}),
...(ownerState.size === 'md' && {
'--Card-radius': theme.vars.radius.md,
'--Card-padding': '1rem',
gap: '0.75rem 1rem',
}),
...(ownerState.size === 'lg' && {
'--Card-radius': theme.vars.radius.lg,
'--Card-padding': '1.5rem',
gap: '1rem 1.5rem',
}),
padding: 'var(--Card-padding)',
borderRadius: 'var(--Card-radius)',
backgroundColor: theme.vars.palette.background.surface,
position: 'relative',
display: 'flex',
flexDirection: ownerState.orientation === 'horizontal' ? 'row' : 'column',
...theme.typography[`body-${ownerState.size!}`],
...(ownerState.variant === 'solid' &&
ownerState.color &&
ownerState.invertedColors &&
applySolidInversion(ownerState.color)(theme)),
...(ownerState.variant === 'soft' &&
ownerState.color &&
ownerState.invertedColors &&
applySoftInversion(ownerState.color)(theme)),
...theme.variants[ownerState.variant!]?.[ownerState.color!],
} as const,
p !== undefined && { '--Card-padding': p },
padding !== undefined && { '--Card-padding': padding },
borderRadius !== undefined && { '--Card-radius': borderRadius },
];
});
const CardRoot = styled(StyledCardRoot, {
name: 'JoyCard',
slot: 'Root',
overridesResolver: (props, styles) => styles.root,
})<{ ownerState: CardOwnerState }>({});
/**
*
* Demos:
*
* - [Card](https://mui.com/joy-ui/react-card/)
*
* API:
*
* - [Card API](https://mui.com/joy-ui/api/card/)
*/
const Card = React.forwardRef(function Card(inProps, ref) {
const props = useThemeProps<typeof inProps & CardProps>({
props: inProps,
name: 'JoyCard',
});
const {
className,
color = 'neutral',
component = 'div',
invertedColors = false,
size = 'md',
variant = 'outlined',
children,
orientation = 'vertical',
slots = {},
slotProps = {},
...other
} = props;
const ownerState = {
...props,
color,
component,
orientation,
size,
variant,
invertedColors,
};
const classes = useUtilityClasses(ownerState);
const externalForwardedProps = { ...other, component, slots, slotProps };
const [SlotRoot, rootProps] = useSlot('root', {
ref,
className: clsx(classes.root, className),
elementType: CardRoot,
externalForwardedProps,
ownerState,
});
return (
<SlotRoot {...rootProps}>
{React.Children.map(children, (child, index) => {
if (!React.isValidElement(child)) {
return child;
}
const extraProps: Record<string, any> = {};
if (isMuiElement(child, ['Divider'])) {
const childProps = child.props as DividerProps;
extraProps.inset = childProps?.inset ?? 'context';
const dividerOrientation = orientation === 'vertical' ? 'horizontal' : 'vertical';
extraProps.orientation = childProps?.orientation ?? dividerOrientation;
}
if (index === 0) {
extraProps['data-first-child'] = '';
}
if (index === React.Children.count(children) - 1) {
extraProps['data-last-child'] = '';
}
return React.cloneElement(child, extraProps);
})}
</SlotRoot>
);
}) as OverridableComponent<CardTypeMap>;
Card.propTypes /* remove-proptypes */ = {
// ┌────────────────────────────── Warning ──────────────────────────────┐
// │ These PropTypes are generated from the TypeScript type definitions. │
// │ To update them, edit the TypeScript types and run `pnpm proptypes`. │
// └─────────────────────────────────────────────────────────────────────┘
/**
* Used to render icon or text elements inside the Card if `src` is not set.
* This can be an element, or just a string.
*/
children: PropTypes.node,
/**
* @ignore
*/
className: PropTypes.string,
/**
* The color of the component. It supports those theme colors that make sense for this component.
* @default 'neutral'
*/
color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([
PropTypes.oneOf(['danger', 'neutral', 'primary', 'success', 'warning']),
PropTypes.string,
]),
/**
* The component used for the root node.
* Either a string to use a HTML element or a component.
*/
component: PropTypes.elementType,
/**
* If `true`, the children with an implicit color prop invert their colors to match the component's variant and color.
* @default false
*/
invertedColors: PropTypes.bool,
/**
* The component orientation.
* @default 'vertical'
*/
orientation: PropTypes.oneOf(['horizontal', 'vertical']),
/**
* The size of the component.
* It accepts theme values between 'sm' and 'lg'.
* @default 'md'
*/
size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([
PropTypes.oneOf(['lg', 'md', 'sm']),
PropTypes.string,
]),
/**
* The props used for each slot inside.
* @default {}
*/
slotProps: PropTypes.shape({
root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
}),
/**
* The components used for each slot inside.
* @default {}
*/
slots: PropTypes.shape({
root: PropTypes.elementType,
}),
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])),
PropTypes.func,
PropTypes.object,
]),
/**
* The [global variant](https://mui.com/joy-ui/main-features/global-variants/) to use.
* @default 'outlined'
*/
variant: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([
PropTypes.oneOf(['outlined', 'plain', 'soft', 'solid']),
PropTypes.string,
]),
} as any;
export default Card;