feat(card): structure improved

This commit is contained in:
Junior Garcia 2023-03-25 15:16:59 -03:00
parent f5b3c788e4
commit 0eaf34994a
9 changed files with 689 additions and 587 deletions

View File

@ -1,7 +1,6 @@
import {forwardRef, HTMLNextUIProps} from "@nextui-org/system";
import {useDOMRef} from "@nextui-org/dom-utils";
import {clsx} from "@nextui-org/shared-utils";
import {filterDOMProps} from "@react-aria/utils";
import {useCardContext} from "./card-context";
@ -16,11 +15,7 @@ const CardBody = forwardRef<HTMLNextUIProps, "div">((props, ref) => {
const bodyStyles = clsx(styles?.body, className);
return (
<Component
ref={domRef}
className={slots.body?.({class: bodyStyles})}
{...filterDOMProps(otherProps, {labelable: true})}
>
<Component ref={domRef} className={slots.body?.({class: bodyStyles})} {...otherProps}>
{children}
</Component>
);

View File

@ -1,7 +1,6 @@
import {forwardRef, HTMLNextUIProps} from "@nextui-org/system";
import {useDOMRef} from "@nextui-org/dom-utils";
import {clsx} from "@nextui-org/shared-utils";
import {filterDOMProps} from "@react-aria/utils";
import {useCardContext} from "./card-context";
@ -22,11 +21,7 @@ const CardFooter = forwardRef<CardFooterProps, "div">((props, ref) => {
});
return (
<Component
ref={domRef}
className={slots.footer?.({class: footerStyles})}
{...filterDOMProps(otherProps, {labelable: true})}
>
<Component ref={domRef} className={slots.footer?.({class: footerStyles})} {...otherProps}>
{children}
</Component>
);

View File

@ -1,7 +1,6 @@
import {forwardRef, HTMLNextUIProps} from "@nextui-org/system";
import {useDOMRef} from "@nextui-org/dom-utils";
import {clsx} from "@nextui-org/shared-utils";
import {filterDOMProps} from "@react-aria/utils";
import {useCardContext} from "./card-context";
@ -17,11 +16,7 @@ const CardHeader = forwardRef<HTMLNextUIProps, "div">((props, ref) => {
return (
<>
<Component
ref={domRef}
className={slots.header?.({class: headerStyles})}
{...filterDOMProps(otherProps, {labelable: true})}
>
<Component ref={domRef} className={slots.header?.({class: headerStyles})} {...otherProps}>
{children}
</Component>
</>

View File

@ -3,7 +3,7 @@ import type {SlotsToClasses, CardSlots, CardReturnType, CardVariantProps} from "
import {card} from "@nextui-org/theme";
import {MouseEvent, useCallback, useMemo} from "react";
import {filterDOMProps, mergeProps} from "@react-aria/utils";
import {mergeProps} from "@react-aria/utils";
import {useFocusRing} from "@react-aria/focus";
import {useHover} from "@react-aria/interactions";
import {useButton as useAriaButton} from "@react-aria/button";
@ -51,8 +51,8 @@ export type UseCardProps = Props & PressEvents & FocusableProps & CardVariantPro
export type ContextType = {
slots: CardReturnType;
styles?: SlotsToClasses<CardSlots>;
variant?: CardVariantProps["variant"];
isDisabled?: CardVariantProps["isDisabled"];
isBordered?: CardVariantProps["isBordered"];
isFooterBlurred?: CardVariantProps["isFooterBlurred"];
disableAnimation?: CardVariantProps["disableAnimation"];
fullWidth?: CardVariantProps["fullWidth"];
@ -120,7 +120,7 @@ export function useCard(originalProps: UseCardProps) {
const context = useMemo<ContextType>(
() => ({
variant: originalProps.variant,
isBordered: originalProps.isBordered,
isDisabled: originalProps.isDisabled,
isFooterBlurred: originalProps.isFooterBlurred,
disableAnimation: originalProps.disableAnimation,
@ -131,7 +131,7 @@ export function useCard(originalProps: UseCardProps) {
[
slots,
styles,
originalProps.variant,
originalProps.isBordered,
originalProps.isDisabled,
originalProps.isFooterBlurred,
originalProps.disableAnimation,
@ -153,8 +153,8 @@ export function useCard(originalProps: UseCardProps) {
...mergeProps(
originalProps.isPressable ? {...buttonProps, ...focusProps} : {},
originalProps.isHoverable ? hoverProps : {},
filterDOMProps(otherProps, {labelable: true}),
filterDOMProps(props, {labelable: true}),
otherProps,
props,
),
};
},

View File

@ -1,33 +1,36 @@
import React from "react";
import {ComponentMeta, ComponentStory} from "@storybook/react";
import {Link} from "@nextui-org/link";
import {Code} from "@nextui-org/code";
import {styled} from "@nextui-org/system";
import {ComponentStory, ComponentMeta} from "@storybook/react";
import {card} from "@nextui-org/theme";
import {Card, CardProps} from "../src";
import {Card, CardBody, CardProps} from "../src";
export default {
title: "General/Card",
title: "Components/Card",
component: Card,
argTypes: {
variant: {
shadow: {
control: {
type: "radio",
options: ["shadow", "bordered", "flat"],
type: "select",
options: ["none", "sm", "md", "lg", "xl", "2xl", "inner"],
},
},
borderWeight: {
radius: {
control: {
type: "radio",
options: ["light", "normal", "bold", "extrabold", "black"],
type: "select",
options: ["none", "base", "sm", "md", "lg", "xl", "2xl", "3xl", "full"],
},
},
isPressable: {
isBordered: {
control: {
type: "boolean",
},
},
disableAnimation: {
fullWidth: {
control: {
type: "boolean",
},
},
isFooterBlurred: {
control: {
type: "boolean",
},
@ -37,520 +40,50 @@ export default {
type: "boolean",
},
},
isPressable: {
control: {
type: "boolean",
},
},
isDisabled: {
control: {
type: "boolean",
},
},
disableRipple: {
control: {
type: "boolean",
},
},
disableAnimation: {
control: {
type: "boolean",
},
},
},
decorators: [
(Story) => (
<div className="flex items-center justify-center w-screen h-screen">
<div className="max-w-md w-full">
<Story />
</div>
</div>
),
],
} as ComponentMeta<typeof Card>;
const defaultProps = {
...card.defaultVariants,
disableRipple: false,
};
const Template: ComponentStory<typeof Card> = (args: CardProps) => (
<Card {...args} className="max-w-[50%]">
<Card.Body>A basic card</Card.Body>
<Card {...args}>
<CardBody>A basic card</CardBody>
</Card>
);
export const Default = Template.bind({});
Default.args = {
variant: "shadow",
...defaultProps,
};
export const Hoverable = Template.bind({});
Hoverable.args = {
variant: "bordered",
isHoverable: true,
};
export const Variants = () => (
<div className="container flex gap-2">
<div className="w-4/12">
<Card>
<Card.Body>
<p>Default card. (shadow)</p>
</Card.Body>
</Card>
</div>
<div className="w-4/12">
<Card variant="flat">
<Card.Body>
<p>Flat card.</p>
</Card.Body>
</Card>
</div>
<div className="w-4/12">
<Card variant="bordered">
<Card.Body>
<p>Bordered card.</p>
</Card.Body>
</Card>
</div>
</div>
);
export const WithFooter = () => (
<Card className="p-3 max-w-[400px]">
<Card.Header>
<img
alt="nextui logo"
height="34px"
src="https://avatars.githubusercontent.com/u/86160567?s=200&v=4"
width="34px"
/>
<div className="flex flex-col pl-3">
<h4 className="pb-2">Next UI</h4>
<p>nextui.org</p>
</div>
</Card.Header>
<Card.Body className="py-2">
<p>Make beautiful websites regardless of your design experience.</p>
</Card.Body>
<Card.Footer>
<Link isExternal color="primary" href="https://github.com/nextui-org/nextui" target="_blank">
Visit source code on GitHub.
</Link>
</Card.Footer>
</Card>
);
export const AbsImageWithHeader = () => {
return (
<div className="flex justify-center gap-2">
<Card className="w-[330px]">
<Card.Header className="absolute top-5 z-10">
<div className="grid-cols-2">
<p className="text-white/75 text-sm uppercase font-bold">What to watch</p>
<h3 className="text-white text-xl">Stream the Apple event</h3>
</div>
</Card.Header>
<Card.Image
alt="Apple event background"
autoResize={false}
height={440}
src={require("./assets/apple-event.jpeg")}
style={{objectFit: "cover"}}
width="100%"
/>
</Card>
</div>
);
};
export const AbsImgWithHeaderFooter = () => {
return (
<div className="flex justify-center items-start gap-2">
<Card className="w-[330px] bg-neutral-100">
<Card.Header className="absolute top-5 z-10">
<div className="flex flex-col">
<p className="text-xs text-neutral-400 upper font-bold">New</p>
<h2 className="text-lg text-black">HomePod mini</h2>
<p className="text-xs text-foreground pr-1.5">
Room-filling sound, Intelligent assistant. Smart home control. Works seamlessly with
iPhone. Check it out
</p>
</div>
</Card.Header>
<Card.Image
alt="Apple homedpods background"
autoResize={false}
height={440}
src={require("./assets/homepod.jpeg")}
style={{objectFit: "cover", paddingTop: "100px"}}
width="100%"
/>
<Card.Footer className="justify-between">
<div>
<p className="text-xs">Available soon.</p>
<p className="text-xs">Get notified.</p>
</div>
<div>
<div className="flex flex-wrap justify-end">
<p className="text-xs upper font-bold">Notify Me</p>
</div>
</div>
</Card.Footer>
</Card>
<Card className="w-[630px]">
<Card.Header className="absolute top-5 z-10">
<div className="flex flex-col">
<p className="text-neutral-300 upper font-bold">Your day your way</p>
<h3 className="text-white text-xl">Your checklist for better sleep</h3>
</div>
</Card.Header>
<Card.Image
alt="Apple homedpods background"
autoResize={false}
height={440}
src={require("./assets/relaxing.jpeg")}
style={{objectFit: "cover"}}
width="100%"
/>
<Card.Footer isBlurred className="absolute bottom-0 z-10 border-t border-border-dark">
<div className="flex flex-grow items-start">
<div className="w-[40px] mr-2">
<Card.Image
alt="Breathing app icon"
autoResize={false}
height={40}
src={require("./assets/breathing-app-icon.jpeg")}
style={{background: "black"}}
width={40}
/>
</div>
<div className="flex flex-col">
<p className="text-xs text-neutral-400">Breathing App</p>
<p className="text-xs text-neutral-400">Get a good night&apos;s sleep.</p>
</div>
</div>
<p className="text-white upper font-bold">Get App</p>
</Card.Footer>
</Card>
</div>
);
};
export const CoverImage = () => (
<div className="gap-2 grid grid-cols-12">
<div className="col-span-12 sm:col-span-4">
<Card>
<Card.Header className="absolute z-10 top-1 flex-col !items-start">
<p className="text-xs text-white/75 upper font-bold">What to watch</p>
<h4 className="text-white font-medium text-lg">Stream the Acme event</h4>
</Card.Header>
<Card.Image
alt="Card image background"
height={340}
objectFit="cover"
src="https://nextui.org/images/card-example-4.jpeg"
width="100%"
/>
</Card>
</div>
<div className="col-span-12 sm:col-span-4">
<Card>
<Card.Header className="absolute z-10 top-1 flex-col !items-start">
<p className="text-xs text-white/75 upper font-bold">Plant a tree</p>
<h4 className="text-white font-medium text-lg">Contribute to the planet</h4>
</Card.Header>
<Card.Image
alt="Card image background"
height={340}
objectFit="cover"
src="https://nextui.org/images/card-example-3.jpeg"
width="100%"
/>
</Card>
</div>
<div className="col-span-12 sm:col-span-4">
<Card className="bg-black">
<Card.Header className="absolute z-10 top-1 flex-col !items-start">
<p className="text-xs text-white/75 upper font-bold">Supercharged</p>
<h4 className="text-white font-medium text-lg">Creates beauty like a beast</h4>
</Card.Header>
<Card.Image
alt="Card image background"
height={340}
objectFit="cover"
src="https://nextui.org/images/card-example-2.jpeg"
width="100%"
/>
</Card>
</div>
<div className="col-span-12 sm:col-span-5">
<Card className="w-full h-[400px]">
<Card.Header className="absolute z-10 top-1 flex-col !items-start">
<p className="text-xs text-white/75 upper font-bold">New</p>
<h3 className="text-black font-medium text-2xl">Acme camera</h3>
</Card.Header>
<Card.Image
alt="Card example background"
height="100%"
objectFit="cover"
src="https://nextui.org/images/card-example-6.jpeg"
width="100%"
/>
<Card.Footer
isBlurred
className="absolute bg-white/50 bottom-0 border-t border-border-dark z-10"
>
<div>
<p className="text-black text-xs">Available soon.</p>
<p className="text-black text-xs">Get notified.</p>
</div>
<p className="text-xs upper font-bold text-inherit ml-auto">Notify Me</p>
</Card.Footer>
</Card>
</div>
<div className="col-span-12 sm:col-span-7">
<Card className="w-full h-[400px]">
<Card.Header className="absolute z-10 top-1 flex-col !items-start">
<p className="text-xs text-white/75 upper font-bold">Your day your way</p>
<h3 className="text-white font-medium text-2xl">Your checklist for better sleep</h3>
</Card.Header>
<Card.Image
alt="Relaxing app background"
height="100%"
objectFit="cover"
src="https://nextui.org/images/card-example-5.jpeg"
width="100%"
/>
<Card.Footer
isBlurred
className="absolute bottom-0 z-10 bg-black/50 border-t border-border-dark"
>
<div className="flex flex-grow items-center">
<div className="w-[40px] mr-2">
<Card.Image
alt="Breathing app icon"
autoResize={false}
height={40}
src={require("./assets/breathing-app-icon.jpeg")}
style={{background: "black"}}
width={40}
/>
</div>
<div className="flex flex-col">
<p className="text-xs text-neutral-400">Breathing App</p>
<p className="text-xs text-neutral-400">Get a good night&apos;s sleep.</p>
</div>
</div>
<p className="text-white upper font-bold">Get App</p>
</Card.Footer>
</Card>
</div>
</div>
);
export const PrimaryAction = () => {
const list = [
{
title: "Orange",
img: "/images/fruit-1.jpeg",
price: "$5.50",
},
{
title: "Tangerine",
img: "/images/fruit-2.jpeg",
price: "$3.00",
},
{
title: "Raspberry",
img: "/images/fruit-3.jpeg",
price: "$10.00",
},
{
title: "Lemon",
img: "/images/fruit-4.jpeg",
price: "$5.30",
},
{
title: "Advocato",
img: "/images/fruit-5.jpeg",
price: "$15.70",
},
{
title: "Lemon 2",
img: "/images/fruit-6.jpeg",
price: "$8.00",
},
{
title: "Banana",
img: "/images/fruit-7.jpeg",
price: "$7.50",
},
{
title: "Watermelon",
img: "/images/fruit-8.jpeg",
price: "$12.20",
},
];
return (
<div className="gap-2 grid grid-cols-2 sm:grid-cols-4">
{list.map((item, index) => (
// eslint-disable-next-line no-console
<Card key={index} isPressable onPress={() => console.log("item pressed", item)}>
<Card.Body className="!p-0">
<Card.Image
alt={item.title}
height={140}
src={"https://nextui.org" + item.img}
style={{objectFit: "cover"}}
width="100%"
/>
</Card.Body>
<Card.Footer className="justify-between">
<strong>{item.title}</strong>
<p className="font-medium text-neutral-500">{item.price}</p>
</Card.Footer>
</Card>
))}
</div>
);
};
export const CenterImgWithHeader = () => {
const list = [
{
title: "Mac",
img: require("./assets/mac.png"),
},
{
title: "iPhone",
img: require("./assets/iphone.png"),
},
{
title: "iPad",
img: require("./assets/ipad.png"),
},
{
title: "Apple Watch",
img: require("./assets/apple-watch.png"),
},
{
title: "AirPods",
img: require("./assets/airpods.png"),
},
{
title: "AirTag",
img: require("./assets/airtag.png"),
},
{
title: "Apple TV",
img: require("./assets/appletv.png"),
},
{
title: "HomePod mini",
img: require("./assets/homepod-mini.png"),
},
{
title: "Accessories",
img: require("./assets/accessories.png"),
},
];
return (
<div className="flex gap-2 justify-center flex-wrap">
{list.map((item, index) => (
<div key={index}>
<Card isHoverable isPressable className="w-[200px] h-[200px]">
<Card.Header className="!p-0">
<h5 className="pl-6 pt-2.5">{item.title}</h5>
</Card.Header>
<Card.Body className="h-full justify-center">
<Card.Image alt={item.title} autoResize={false} src={item.img} width={180} />
</Card.Body>
</Card>
</div>
))}
</div>
);
};
export const WithDivider = () => (
<Card className="w-max-[400px]" variant="bordered">
<Card.Header className="border-b border-border dark:border-border-dark">
<strong>Description</strong>
</Card.Header>
<Card.Body>
<p>The Object constructor creates an object wrapper for the given value.</p>
</Card.Body>
<Card.Footer className="border-t border-border dark:border-border-dark">
<p>
When called in a non-constructor context, Object behaves identically to{" "}
<Code>new Object()</Code>.
</p>
</Card.Footer>
</Card>
);
export const Shadows = () => {
const Box = styled("div", {
size: "120px",
dflex: "center",
bg: "$backgroundContrast",
br: "$md",
});
const shadows = ["$xs", "$sm", "$md", "$lg", "$xl"];
return (
<div className="grid grid-cols-2 sm:grid-cols-5 gap-2 justify-center">
<div className="flex justify-center col-span-2 sm:col-span-5">
<strong>Drop shadows</strong>
</div>
{shadows.map((shadow, index) => (
<div key={`${shadow}_${index}`}>
<Box css={{dropShadow: shadow}}>
<p>Shadow: {shadow}</p>
</Box>
</div>
))}
<div className="flex justify-center col-span-2 sm:col-span-5">
<strong>Box shadows</strong>
</div>
{shadows.map((shadow, index) => (
<div key={`${shadow}_${index}`}>
<Box css={{boxShadow: shadow}}>
<p>Shadow: {shadow}</p>
</Box>
</div>
))}
</div>
);
};
//TODO: Input & Button still missing
// export const withForm = () => {
// return (
// <Card css={{mw: "400px"}}>
// <Card.Header css={{justifyContent: "center"}}>
// <Text size={18}>
// Welcome to&nbsp;
// <Text b size={18}>
// NextUI
// </Text>
// </Text>
// </Card.Header>
// <Card.Body css={{px: "$10", pt: "$1", ov: "visible"}}>
// <Input
// bordered
// clearable
// fullWidth
// color="primary"
// contentLeft={<Mail fill="currentColor" />}
// placeholder="Email"
// size="lg"
// />
// <Spacer y={0.5} />
// <Input
// bordered
// clearable
// fullWidth
// color="primary"
// contentLeft={<Password />}
// placeholder="Password"
// size="lg"
// />
// <Spacer y={0.5} />
// <Row align="center" justify="space-between">
// <Checkbox>
// <Text css={{color: "$accents8"}} size={14}>
// Remember me
// </Text>
// </Checkbox>
// <Link css={{color: "$link", fontSize: "$sm"}} href="#">
// Forgot password?
// </Link>
// </Row>
// </Card.Body>
// <Card.Footer css={{pt: 0}}>
// <Grid.Container gap={1} justify="flex-end">
// <Grid>
// <Button auto flat>
// Sign Up
// </Button>
// </Grid>
// <Grid>
// <Button auto>Login</Button>
// </Grid>
// </Grid.Container>
// </Card.Footer>
// </Card>
// );
// };

View File

@ -0,0 +1,556 @@
import React from "react";
import {ComponentMeta, ComponentStory} from "@storybook/react";
import {Link} from "@nextui-org/link";
import {Code} from "@nextui-org/code";
import {styled} from "@nextui-org/system";
import {Card, CardProps} from "../src";
export default {
title: "General/Card",
component: Card,
argTypes: {
variant: {
control: {
type: "radio",
options: ["shadow", "bordered", "flat"],
},
},
borderWeight: {
control: {
type: "radio",
options: ["light", "normal", "bold", "extrabold", "black"],
},
},
isPressable: {
control: {
type: "boolean",
},
},
disableAnimation: {
control: {
type: "boolean",
},
},
isHoverable: {
control: {
type: "boolean",
},
},
},
} as ComponentMeta<typeof Card>;
const Template: ComponentStory<typeof Card> = (args: CardProps) => (
<Card {...args} className="max-w-[50%]">
<Card.Body>A basic card</Card.Body>
</Card>
);
export const Default = Template.bind({});
Default.args = {
variant: "shadow",
};
export const Hoverable = Template.bind({});
Hoverable.args = {
variant: "bordered",
isHoverable: true,
};
export const Variants = () => (
<div className="container flex gap-2">
<div className="w-4/12">
<Card>
<Card.Body>
<p>Default card. (shadow)</p>
</Card.Body>
</Card>
</div>
<div className="w-4/12">
<Card variant="flat">
<Card.Body>
<p>Flat card.</p>
</Card.Body>
</Card>
</div>
<div className="w-4/12">
<Card variant="bordered">
<Card.Body>
<p>Bordered card.</p>
</Card.Body>
</Card>
</div>
</div>
);
export const WithFooter = () => (
<Card className="p-3 max-w-[400px]">
<Card.Header>
<img
alt="nextui logo"
height="34px"
src="https://avatars.githubusercontent.com/u/86160567?s=200&v=4"
width="34px"
/>
<div className="flex flex-col pl-3">
<h4 className="pb-2">Next UI</h4>
<p>nextui.org</p>
</div>
</Card.Header>
<Card.Body className="py-2">
<p>Make beautiful websites regardless of your design experience.</p>
</Card.Body>
<Card.Footer>
<Link isExternal color="primary" href="https://github.com/nextui-org/nextui" target="_blank">
Visit source code on GitHub.
</Link>
</Card.Footer>
</Card>
);
export const AbsImageWithHeader = () => {
return (
<div className="flex justify-center gap-2">
<Card className="w-[330px]">
<Card.Header className="absolute top-5 z-10">
<div className="grid-cols-2">
<p className="text-white/75 text-sm uppercase font-bold">What to watch</p>
<h3 className="text-white text-xl">Stream the Apple event</h3>
</div>
</Card.Header>
<Card.Image
alt="Apple event background"
autoResize={false}
height={440}
src={require("./assets/apple-event.jpeg")}
style={{objectFit: "cover"}}
width="100%"
/>
</Card>
</div>
);
};
export const AbsImgWithHeaderFooter = () => {
return (
<div className="flex justify-center items-start gap-2">
<Card className="w-[330px] bg-neutral-100">
<Card.Header className="absolute top-5 z-10">
<div className="flex flex-col">
<p className="text-xs text-neutral-400 upper font-bold">New</p>
<h2 className="text-lg text-black">HomePod mini</h2>
<p className="text-xs text-foreground pr-1.5">
Room-filling sound, Intelligent assistant. Smart home control. Works seamlessly with
iPhone. Check it out
</p>
</div>
</Card.Header>
<Card.Image
alt="Apple homedpods background"
autoResize={false}
height={440}
src={require("./assets/homepod.jpeg")}
style={{objectFit: "cover", paddingTop: "100px"}}
width="100%"
/>
<Card.Footer className="justify-between">
<div>
<p className="text-xs">Available soon.</p>
<p className="text-xs">Get notified.</p>
</div>
<div>
<div className="flex flex-wrap justify-end">
<p className="text-xs upper font-bold">Notify Me</p>
</div>
</div>
</Card.Footer>
</Card>
<Card className="w-[630px]">
<Card.Header className="absolute top-5 z-10">
<div className="flex flex-col">
<p className="text-neutral-300 upper font-bold">Your day your way</p>
<h3 className="text-white text-xl">Your checklist for better sleep</h3>
</div>
</Card.Header>
<Card.Image
alt="Apple homedpods background"
autoResize={false}
height={440}
src={require("./assets/relaxing.jpeg")}
style={{objectFit: "cover"}}
width="100%"
/>
<Card.Footer isBlurred className="absolute bottom-0 z-10 border-t border-border-dark">
<div className="flex flex-grow items-start">
<div className="w-[40px] mr-2">
<Card.Image
alt="Breathing app icon"
autoResize={false}
height={40}
src={require("./assets/breathing-app-icon.jpeg")}
style={{background: "black"}}
width={40}
/>
</div>
<div className="flex flex-col">
<p className="text-xs text-neutral-400">Breathing App</p>
<p className="text-xs text-neutral-400">Get a good night&apos;s sleep.</p>
</div>
</div>
<p className="text-white upper font-bold">Get App</p>
</Card.Footer>
</Card>
</div>
);
};
export const CoverImage = () => (
<div className="gap-2 grid grid-cols-12">
<div className="col-span-12 sm:col-span-4">
<Card>
<Card.Header className="absolute z-10 top-1 flex-col !items-start">
<p className="text-xs text-white/75 upper font-bold">What to watch</p>
<h4 className="text-white font-medium text-lg">Stream the Acme event</h4>
</Card.Header>
<Card.Image
alt="Card image background"
height={340}
objectFit="cover"
src="https://nextui.org/images/card-example-4.jpeg"
width="100%"
/>
</Card>
</div>
<div className="col-span-12 sm:col-span-4">
<Card>
<Card.Header className="absolute z-10 top-1 flex-col !items-start">
<p className="text-xs text-white/75 upper font-bold">Plant a tree</p>
<h4 className="text-white font-medium text-lg">Contribute to the planet</h4>
</Card.Header>
<Card.Image
alt="Card image background"
height={340}
objectFit="cover"
src="https://nextui.org/images/card-example-3.jpeg"
width="100%"
/>
</Card>
</div>
<div className="col-span-12 sm:col-span-4">
<Card className="bg-black">
<Card.Header className="absolute z-10 top-1 flex-col !items-start">
<p className="text-xs text-white/75 upper font-bold">Supercharged</p>
<h4 className="text-white font-medium text-lg">Creates beauty like a beast</h4>
</Card.Header>
<Card.Image
alt="Card image background"
height={340}
objectFit="cover"
src="https://nextui.org/images/card-example-2.jpeg"
width="100%"
/>
</Card>
</div>
<div className="col-span-12 sm:col-span-5">
<Card className="w-full h-[400px]">
<Card.Header className="absolute z-10 top-1 flex-col !items-start">
<p className="text-xs text-white/75 upper font-bold">New</p>
<h3 className="text-black font-medium text-2xl">Acme camera</h3>
</Card.Header>
<Card.Image
alt="Card example background"
height="100%"
objectFit="cover"
src="https://nextui.org/images/card-example-6.jpeg"
width="100%"
/>
<Card.Footer
isBlurred
className="absolute bg-white/50 bottom-0 border-t border-border-dark z-10"
>
<div>
<p className="text-black text-xs">Available soon.</p>
<p className="text-black text-xs">Get notified.</p>
</div>
<p className="text-xs upper font-bold text-inherit ml-auto">Notify Me</p>
</Card.Footer>
</Card>
</div>
<div className="col-span-12 sm:col-span-7">
<Card className="w-full h-[400px]">
<Card.Header className="absolute z-10 top-1 flex-col !items-start">
<p className="text-xs text-white/75 upper font-bold">Your day your way</p>
<h3 className="text-white font-medium text-2xl">Your checklist for better sleep</h3>
</Card.Header>
<Card.Image
alt="Relaxing app background"
height="100%"
objectFit="cover"
src="https://nextui.org/images/card-example-5.jpeg"
width="100%"
/>
<Card.Footer
isBlurred
className="absolute bottom-0 z-10 bg-black/50 border-t border-border-dark"
>
<div className="flex flex-grow items-center">
<div className="w-[40px] mr-2">
<Card.Image
alt="Breathing app icon"
autoResize={false}
height={40}
src={require("./assets/breathing-app-icon.jpeg")}
style={{background: "black"}}
width={40}
/>
</div>
<div className="flex flex-col">
<p className="text-xs text-neutral-400">Breathing App</p>
<p className="text-xs text-neutral-400">Get a good night&apos;s sleep.</p>
</div>
</div>
<p className="text-white upper font-bold">Get App</p>
</Card.Footer>
</Card>
</div>
</div>
);
export const PrimaryAction = () => {
const list = [
{
title: "Orange",
img: "/images/fruit-1.jpeg",
price: "$5.50",
},
{
title: "Tangerine",
img: "/images/fruit-2.jpeg",
price: "$3.00",
},
{
title: "Raspberry",
img: "/images/fruit-3.jpeg",
price: "$10.00",
},
{
title: "Lemon",
img: "/images/fruit-4.jpeg",
price: "$5.30",
},
{
title: "Advocato",
img: "/images/fruit-5.jpeg",
price: "$15.70",
},
{
title: "Lemon 2",
img: "/images/fruit-6.jpeg",
price: "$8.00",
},
{
title: "Banana",
img: "/images/fruit-7.jpeg",
price: "$7.50",
},
{
title: "Watermelon",
img: "/images/fruit-8.jpeg",
price: "$12.20",
},
];
return (
<div className="gap-2 grid grid-cols-2 sm:grid-cols-4">
{list.map((item, index) => (
// eslint-disable-next-line no-console
<Card key={index} isPressable onPress={() => console.log("item pressed", item)}>
<Card.Body className="!p-0">
<Card.Image
alt={item.title}
height={140}
src={"https://nextui.org" + item.img}
style={{objectFit: "cover"}}
width="100%"
/>
</Card.Body>
<Card.Footer className="justify-between">
<strong>{item.title}</strong>
<p className="font-medium text-neutral-500">{item.price}</p>
</Card.Footer>
</Card>
))}
</div>
);
};
export const CenterImgWithHeader = () => {
const list = [
{
title: "Mac",
img: require("./assets/mac.png"),
},
{
title: "iPhone",
img: require("./assets/iphone.png"),
},
{
title: "iPad",
img: require("./assets/ipad.png"),
},
{
title: "Apple Watch",
img: require("./assets/apple-watch.png"),
},
{
title: "AirPods",
img: require("./assets/airpods.png"),
},
{
title: "AirTag",
img: require("./assets/airtag.png"),
},
{
title: "Apple TV",
img: require("./assets/appletv.png"),
},
{
title: "HomePod mini",
img: require("./assets/homepod-mini.png"),
},
{
title: "Accessories",
img: require("./assets/accessories.png"),
},
];
return (
<div className="flex gap-2 justify-center flex-wrap">
{list.map((item, index) => (
<div key={index}>
<Card isHoverable isPressable className="w-[200px] h-[200px]">
<Card.Header className="!p-0">
<h5 className="pl-6 pt-2.5">{item.title}</h5>
</Card.Header>
<Card.Body className="h-full justify-center">
<Card.Image alt={item.title} autoResize={false} src={item.img} width={180} />
</Card.Body>
</Card>
</div>
))}
</div>
);
};
export const WithDivider = () => (
<Card className="w-max-[400px]" variant="bordered">
<Card.Header className="border-b border-border dark:border-border-dark">
<strong>Description</strong>
</Card.Header>
<Card.Body>
<p>The Object constructor creates an object wrapper for the given value.</p>
</Card.Body>
<Card.Footer className="border-t border-border dark:border-border-dark">
<p>
When called in a non-constructor context, Object behaves identically to{" "}
<Code>new Object()</Code>.
</p>
</Card.Footer>
</Card>
);
export const Shadows = () => {
const Box = styled("div", {
size: "120px",
dflex: "center",
bg: "$backgroundContrast",
br: "$md",
});
const shadows = ["$xs", "$sm", "$md", "$lg", "$xl"];
return (
<div className="grid grid-cols-2 sm:grid-cols-5 gap-2 justify-center">
<div className="flex justify-center col-span-2 sm:col-span-5">
<strong>Drop shadows</strong>
</div>
{shadows.map((shadow, index) => (
<div key={`${shadow}_${index}`}>
<Box css={{dropShadow: shadow}}>
<p>Shadow: {shadow}</p>
</Box>
</div>
))}
<div className="flex justify-center col-span-2 sm:col-span-5">
<strong>Box shadows</strong>
</div>
{shadows.map((shadow, index) => (
<div key={`${shadow}_${index}`}>
<Box css={{boxShadow: shadow}}>
<p>Shadow: {shadow}</p>
</Box>
</div>
))}
</div>
);
};
//TODO: Input & Button still missing
// export const withForm = () => {
// return (
// <Card css={{mw: "400px"}}>
// <Card.Header css={{justifyContent: "center"}}>
// <Text size={18}>
// Welcome to&nbsp;
// <Text b size={18}>
// NextUI
// </Text>
// </Text>
// </Card.Header>
// <Card.Body css={{px: "$10", pt: "$1", ov: "visible"}}>
// <Input
// bordered
// clearable
// fullWidth
// color="primary"
// contentLeft={<Mail fill="currentColor" />}
// placeholder="Email"
// size="lg"
// />
// <Spacer y={0.5} />
// <Input
// bordered
// clearable
// fullWidth
// color="primary"
// contentLeft={<Password />}
// placeholder="Password"
// size="lg"
// />
// <Spacer y={0.5} />
// <Row align="center" justify="space-between">
// <Checkbox>
// <Text css={{color: "$accents8"}} size={14}>
// Remember me
// </Text>
// </Checkbox>
// <Link css={{color: "$link", fontSize: "$sm"}} href="#">
// Forgot password?
// </Link>
// </Row>
// </Card.Body>
// <Card.Footer css={{pt: 0}}>
// <Grid.Container gap={1} justify="flex-end">
// <Grid>
// <Button auto flat>
// Sign Up
// </Button>
// </Grid>
// <Grid>
// <Button auto>Login</Button>
// </Grid>
// </Grid.Container>
// </Card.Footer>
// </Card>
// );
// };

View File

@ -14,7 +14,7 @@ const accordion = tv({
base: "px-2",
variants: {
variant: {
shadow: "px-4 shadow-lg rounded-lg dark:bg-content1 border border-neutral-100",
shadow: "px-4 shadow-lg rounded-xl dark:bg-content1 border border-neutral-100",
bordered: "px-4 border border-neutral rounded-lg",
splitted: "group is-splitted flex flex-col gap-2", // the styles are applied in the accordion-item component
},

View File

@ -21,18 +21,14 @@ const card = tv({
base: [
"flex",
"flex-col",
"m-0",
"p-0",
"relative",
"overflow-hidden",
"w-full",
"height-auto",
"bg-white",
"text-foreground",
"rounded-xl",
"box-border",
"dark:bg-neutral-900",
"dark:text-foreground-dark",
"dark:bg-content1",
"border border-neutral-100",
],
header: [
"flex",
@ -44,6 +40,7 @@ const card = tv({
"color-inherit",
"p-3",
"z-10",
"subpixel-antialiased",
],
body: [
"relative",
@ -58,6 +55,7 @@ const card = tv({
"px-3",
"text-left",
"overflow-y-auto",
"subpixel-antialiased",
],
footer: [
"w-full",
@ -68,18 +66,60 @@ const card = tv({
"overflow-hidden",
"color-inherit",
"rounded-b-xl",
"subpixel-antialiased",
],
},
variants: {
variant: {
shadow: {
base: "drop-shadow-lg",
shadow: {
none: {
base: "shadow-none",
},
bordered: {
base: "border-border dark:border-border-dark",
sm: {
base: "shadow-sm",
},
flat: {
base: "bg-neutral-100",
md: {
base: "shadow-md",
},
lg: {
base: "shadow-lg",
},
xl: {
base: "shadow-xl",
},
"2xl": {
base: "shadow-2xl",
},
inner: {
base: "shadow-inner",
},
},
radius: {
none: {
base: "rounded-none",
},
base: {
base: "rounded",
},
sm: {
base: "rounded-sm",
},
md: {
base: "rounded-md",
},
lg: {
base: "rounded-lg",
},
xl: {
base: "rounded-xl",
},
"2xl": {
base: "rounded-2xl",
},
"3xl": {
base: "rounded-3xl",
},
full: {
base: "rounded-full",
},
},
fullWidth: {
@ -87,6 +127,11 @@ const card = tv({
base: "w-full",
},
},
isBordered: {
true: {
base: "border-2 border-neutral",
},
},
isHoverable: {
true: {
base: "hover:drop-shadow-lg",
@ -112,50 +157,32 @@ const card = tv({
},
disableAnimation: {
true: "",
false: {base: "!transition motion-reduce:transition-none"},
false: {base: "transition-transform motion-reduce:transition-none"},
},
},
compoundVariants: [
{
isHoverable: true,
disableAnimation: false,
class: "hover:-translate-y-0.5",
class: "hover:-translate-y-2",
},
{
isPressable: true,
disableAnimation: false,
class: "active:scale-95",
},
{
variant: "bordered",
borderWeight: "light",
class: "border",
},
{
variant: "bordered",
borderWeight: "normal",
class: "border-2",
},
{
variant: "bordered",
borderWeight: "bold",
class: "border-3",
},
{
variant: "bordered",
borderWeight: "extrabold",
class: "border-4",
},
{
variant: "bordered",
borderWeight: "black",
class: "border-5",
},
],
defaultVariants: {
variant: "shadow",
radius: "xl",
shadow: "lg",
fullWidth: false,
isHoverable: false,
isPressable: false,
isFocusVisible: false,
isBordered: false,
isDisabled: false,
disableAnimation: false,
isFooterBlurred: false,
},
});

View File

@ -17,6 +17,7 @@ module.exports = {
"../../components/pagination/stories/*.stories.@(js|jsx|ts|tsx)",
"../../components/switch/stories/*.stories.@(js|jsx|ts|tsx)",
"../../components/accordion/stories/*.stories.@(js|jsx|ts|tsx)",
"../../components/card/stories/*.stories.@(js|jsx|ts|tsx)",
],
staticDirs: ["../public"],
addons: [