Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Link and Image to FileCard #957

Merged
merged 4 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 43 additions & 5 deletions packages/components/src/components/FileCard/FileCard.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
border-width: var(--file-card--border-width);
border-style: var(--file-card--border-style);
border-radius: var(--file-card--corner-radius);
padding-inline: var(--file-card--padding-x);
padding-block: var(--file-card--padding-y);
display: flex;
gap: var(--file-card--spacing);
align-items: center;
max-width: 100%;
overflow: hidden;
position: relative;

&:not(:has(.link)),
.link {
display: flex;
align-items: center;
padding-inline: var(--file-card--padding-x);
padding-block: var(--file-card--padding-y);
gap: var(--file-card--spacing);
}

.text {
display: flex;
Expand All @@ -29,4 +34,37 @@
font-size: var(--file-card--sub-title-font-size);
}
}

&:has(.link) {
display: grid;
grid-template-areas: "content";

.link {
flex-grow: 1;
grid-area: content;
overflow: hidden;

&:hover {
background-color: var(--file-card--background-color--hover);
}

&[data-pressed] {
background-color: var(--file-card--background-color--pressed);
}
}

.text {
--button-width: calc(
var(--icon--size--s) + var(--button--padding-s-icon-only) * 2
);
padding-right: calc(var(--file-card--spacing) + var(--button-width));
}

.deleteButton {
grid-area: content;
margin-right: var(--file-card--padding-x);
justify-self: end;
align-self: center;
}
}
}
62 changes: 30 additions & 32 deletions packages/components/src/components/FileCard/FileCard.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import React from "react";
import { Avatar } from "@/components/Avatar";
import { Avatar } from "./components/Avatar";
import { Text } from "@/components/Text";
import { IconClose, IconFile } from "@/components/Icon/components/icons";
import IconImage from "@/components/Icon/components/icons/IconImage";
import { Button } from "@/components/Button";
import type {
PropsWithClassName,
PropsWithElementType,
} from "@/lib/types/props";
import styles from "./FileCard.module.scss";
import clsx from "clsx";
import { flowComponent } from "@/lib/componentFactory/flowComponent";
import locales from "./locales/*.locale.json";
import { useLocalizedStringFormatter } from "react-aria";
import { FileSizeText } from "@/components/FileCard/components/FileSizeText";
import { Link, type LinkProps } from "@/components/Link";
import Wrap from "@/components/Wrap";
import { DeleteButton } from "@/components/FileCard/components/DeleteButton";

export interface FileCardProps
extends PropsWithClassName,
PropsWithElementType<"div" | "li"> {
PropsWithElementType<"div" | "li">,
Pick<LinkProps, "onPress" | "href" | "target" | "download"> {
name: string;
type?: string;
onDelete?: () => void;
sizeInBytes?: number;
imageSrc?: string;
}

export const FileCard = flowComponent("FileCard", (props) => {
Expand All @@ -32,40 +32,38 @@ export const FileCard = flowComponent("FileCard", (props) => {
name,
className,
elementType = "div",
onPress,
href,
target,
download,
imageSrc,
} = props;

const rootClassName = clsx(styles.fileCard, className);

const Element = elementType;

const stringFormatter = useLocalizedStringFormatter(locales);

const avatar = (
<Avatar color="blue">
{type?.includes("image") ? <IconImage /> : <IconFile />}
</Avatar>
);

return (
<Element className={rootClassName}>
{avatar}
<span className={styles.text}>
<Text className={styles.title}>
<b>{name}</b>
</Text>
{sizeInBytes && <FileSizeText sizeInBytes={sizeInBytes} />}
</span>
{onDelete && (
<Button
aria-label={stringFormatter.format(`fileCard.delete`)}
size="s"
variant="plain"
color="secondary"
onPress={onDelete}
<Wrap if={href || onPress}>
<Link
className={styles.link}
unstyled
href={href}
onPress={onPress}
target={target}
download={download}
>
<IconClose />
</Button>
)}
<Avatar type={type} imageSrc={imageSrc} />
<span className={styles.text}>
<Text className={styles.title}>
<b>{name}</b>
</Text>
{sizeInBytes && <FileSizeText sizeInBytes={sizeInBytes} />}
</span>
</Link>
</Wrap>
{onDelete && <DeleteButton onDelete={onDelete} />}
</Element>
);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { FC } from "react";
import React from "react";
import IconImage from "../../../Icon/components/icons/IconImage";
import { IconFile } from "@/components/Icon/components/icons";
import { Avatar as AvatarComponent } from "@/components/Avatar";
import { Image } from "@/components/Image";

interface Props {
type?: string;
imageSrc?: string;
}

export const Avatar: FC<Props> = (props) => {
const { type, imageSrc } = props;

if (imageSrc) {
return (
<AvatarComponent>
<Image src={imageSrc} />
</AvatarComponent>
);
}

return (
<AvatarComponent color="blue">
{type?.startsWith("image") ? <IconImage /> : <IconFile />}
</AvatarComponent>
);
};
export default Avatar;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./Avatar";
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { FC } from "react";
import React from "react";
import { Button } from "@/components/Button";
import styles from "@/components/FileCard/FileCard.module.scss";
import { IconClose } from "@/components/Icon/components/icons";
import locales from "../../locales/*.locale.json";
import { useLocalizedStringFormatter } from "react-aria";

interface Props {
onDelete: () => void;
}

export const DeleteButton: FC<Props> = (props) => {
const { onDelete } = props;
const stringFormatter = useLocalizedStringFormatter(locales);

return (
<Button
className={styles.deleteButton}
aria-label={stringFormatter.format(`fileCard.delete`)}
size="s"
variant="plain"
color="secondary"
onPress={onDelete}
>
<IconClose />
</Button>
);
};

export default DeleteButton;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./DeleteButton";
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { Meta, StoryObj } from "@storybook/react";
import React from "react";
import { FileCard } from "@/components/FileCard";
import { dummyText } from "@/lib/dev/dummyText";
import { action } from "@storybook/addon-actions";

const meta: Meta<typeof FileCard> = {
title: "Upload/FileCard",
Expand All @@ -21,7 +23,28 @@ export const WithSize: Story = { args: { sizeInBytes: 47500 } };
export const WithOnDelete: Story = {
args: {
onDelete: () => {
console.log("deleted");
action("onDelete");
},
},
};

export const WithLink: Story = {
args: {
href: "#",
},
};

export const WithLinkAndOnDelete: Story = {
args: {
href: "#",
onDelete: () => {
action("onDelete");
},
},
};

export const WithImage: Story = {
args: {
imageSrc: dummyText.imageSrc,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Meta, StoryObj } from "@storybook/react";
import defaultMeta from "./Default.stories";
import { FileCard } from "@/components/FileCard";
import { dummyText } from "@/lib/dev/dummyText";
import { action } from "@storybook/addon-actions";

const meta: Meta<typeof FileCard> = {
...defaultMeta,
Expand All @@ -13,5 +14,22 @@ export default meta;
type Story = StoryObj<typeof FileCard>;

export const LongName: Story = {
args: { name: dummyText.long, sizeInBytes: 47500 },
args: {
name: dummyText.long,
sizeInBytes: 47500,
onDelete: () => {
action("onDelete");
},
},
};

export const LongNameAndLink: Story = {
args: {
name: dummyText.long,
sizeInBytes: 47500,
onDelete: () => {
action("onDelete");
},
href: "#",
},
};
5 changes: 5 additions & 0 deletions packages/design-tokens/src/upload/file-card.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,8 @@ file-card:
value: "{neutral-outline-border-color}"
sub-title-font-size:
value: "{font-size-text.s}"
background-color:
hover:
value: "{neutral-outline-background-color.hover}"
pressed:
value: "{neutral-outline-background-color.pressed}"
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import FileCard from "@mittwald/flow-react-components/FileCard";

<FileCard
type="image/jpg"
name="image.jpg"
sizeInBytes={47500}
imageSrc="https://mittwald.github.io/flow/assets/mittwald_logo_rgb.jpg"
/>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import FileCard from "@mittwald/flow-react-components/FileCard";

<FileCard
type="image/jpg"
name="image.jpg"
sizeInBytes={47500}
href="#"
/>;
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,15 @@
# Mit Delete Button

<LiveCodeEditor example="delete" editorCollapsed />

---

# Mit Link

<LiveCodeEditor example="link" editorCollapsed />

---

# Mit Bild

<LiveCodeEditor example="image" editorCollapsed />