Skip to content

Commit

Permalink
feat: implement add manga
Browse files Browse the repository at this point in the history
  • Loading branch information
oae committed Oct 9, 2022
1 parent 127382d commit 8c45e0c
Show file tree
Hide file tree
Showing 12 changed files with 855 additions and 93 deletions.
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"plugin:react/jsx-runtime"
],
"rules": {
"no-nested-ternary": "off",
"react/jsx-props-no-spreading": "off",
"no-underscore-dangle": "off",
"import/extensions": [
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"@mantine/nprogress": "^5.5.4",
"@mantine/spotlight": "^5.5.4",
"@prisma/client": "4.4.0",
"@tabler/icons": "^1.101.0",
"@tanstack/react-query": "^4.9.0",
"@trpc/client": "^10.0.0-proxy-beta.13",
"@trpc/next": "^10.0.0-proxy-beta.13",
Expand Down
Binary file added public/cover-not-found.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions src/components/addLibrary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { Box, Button, Code, LoadingOverlay, Text, TextInput } from '@mantine/cor
import { useForm } from '@mantine/form';
import { useModals } from '@mantine/modals';
import { showNotification } from '@mantine/notifications';
import { IconCheck, IconX } from '@tabler/icons';
import { useState } from 'react';
import { IoCloseCircle } from 'react-icons/io5';
import { MdCheckCircleOutline, MdOutlineCreateNewFolder } from 'react-icons/md';
import { MdOutlineCreateNewFolder } from 'react-icons/md';
import { trpc } from '../utils/trpc';

function Form({ onClose }: { onClose: () => void }) {
Expand Down Expand Up @@ -35,7 +35,7 @@ function Form({ onClose }: { onClose: () => void }) {
});
} catch (err) {
showNotification({
icon: <IoCloseCircle size={48} />,
icon: <IconX size={18} />,
color: 'red',
autoClose: true,
title: 'Library',
Expand All @@ -55,7 +55,7 @@ function Form({ onClose }: { onClose: () => void }) {
onClose();
setVisible((v) => !v);
showNotification({
icon: <MdCheckCircleOutline size={48} />,
icon: <IconCheck size={18} />,
color: 'teal',
autoClose: true,
title: 'Library',
Expand Down
17 changes: 12 additions & 5 deletions src/components/mangaCard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Badge, Button, createStyles, Paper, Title } from '@mantine/core';
import { IconExternalLink } from '@tabler/icons';

const useStyles = createStyles((theme) => ({
card: {
Expand Down Expand Up @@ -35,7 +36,7 @@ const useStyles = createStyles((theme) => ({
interface ArticleCardImageProps {
image: string;
title: string;
category: string;
category?: string;
}

export function MangaCard({ image, title, category }: ArticleCardImageProps) {
Expand All @@ -50,16 +51,22 @@ export function MangaCard({ image, title, category }: ArticleCardImageProps) {
className={classes.card}
>
<div>
<Badge color="teal" variant="filled" className={classes.category} size="md">
{category}
</Badge>
{category && (
<Badge color="teal" variant="filled" className={classes.category} size="md">
{category}
</Badge>
)}
<Title order={3} className={classes.title}>
{title}
</Title>
</div>
<Button variant="filled" color="indigo" size="xs">
<Button leftIcon={<IconExternalLink size={16} />} variant="filled" color="indigo" size="xs">
Read
</Button>
</Paper>
);
}

MangaCard.defaultProps = {
category: '',
};
142 changes: 142 additions & 0 deletions src/components/mangaSearchResult.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { createStyles, Image, SimpleGrid, Text, UnstyledButton } from '@mantine/core';
import { useUncontrolled } from '@mantine/hooks';
import { useState } from 'react';

const useStyles = createStyles((theme, { checked, disabled }: { checked: boolean; disabled: boolean }) => ({
button: {
display: 'flex',
alignItems: 'center',
width: '100%',
transition: 'background-color 150ms ease, border-color 150ms ease',
border: `1px solid ${
checked
? theme.fn.variant({ variant: 'outline', color: theme.primaryColor }).border
: theme.colorScheme === 'dark'
? theme.colors.dark[8]
: theme.colors.gray[3]
}`,
borderRadius: theme.radius.sm,
padding: theme.spacing.sm,
backgroundColor: checked
? theme.fn.variant({ variant: 'light', color: theme.primaryColor }).background
: disabled
? theme.colors.gray[3]
: theme.white,
},

body: {
flex: 1,
marginLeft: theme.spacing.md,
},
}));

interface ImageCheckboxProps {
checked?: boolean;
defaultChecked?: boolean;
onChange?(checked: boolean): void;
title: string;
description: string;
image: string;
}

export function ImageCheckbox({
checked,
defaultChecked,
onChange,
title,
description,
className,
disabled,
image,
...others
}: ImageCheckboxProps & Omit<React.ComponentPropsWithoutRef<'button'>, keyof ImageCheckboxProps>) {
const [value, handleChange] = useUncontrolled({
value: checked,
defaultValue: defaultChecked,
finalValue: false,
onChange,
});

const { classes, cx } = useStyles({ checked: value, disabled: disabled || false });

return (
<UnstyledButton
{...others}
onClick={() => {
if (!disabled) {
handleChange(!value);
}
}}
className={cx(classes.button, className)}
>
<Image
withPlaceholder
placeholder={<Image src="/cover-not-found.jpg" alt={title} width={42} height={64} />}
src={image}
width={42}
height={64}
/>

<div className={classes.body}>
<Text color="dimmed" size="xs" sx={{ lineHeight: 1 }} mb={5}>
{description}
</Text>
<Text weight={500} size="sm" sx={{ lineHeight: 1 }}>
{title}
</Text>
</div>
</UnstyledButton>
);
}

ImageCheckbox.defaultProps = {
checked: undefined,
defaultChecked: undefined,
onChange: () => {},
};

type IMangaSearchResult = {
status: string;
title: string;
order: number;
cover: string;
};

export function MangaSearchResult({
items,
onSelect,
}: {
items: IMangaSearchResult[];
onSelect: (selected: IMangaSearchResult | undefined) => void;
}) {
const [selected, setSelected] = useState<IMangaSearchResult>();

return (
<SimpleGrid
cols={2}
breakpoints={[
{ maxWidth: 'md', cols: 2 },
{ maxWidth: 'sm', cols: 1 },
]}
>
{items.map((m) => (
<ImageCheckbox
key={m.title}
image={m.cover || '/cover-not-found.jpg'}
title={m.title}
disabled={selected && m.title !== selected.title}
description={m.status}
onChange={(checked) => {
if (checked) {
setSelected(m);
onSelect(m);
} else {
setSelected(undefined);
onSelect(undefined);
}
}}
/>
))}
</SimpleGrid>
);
}
Loading

0 comments on commit 8c45e0c

Please sign in to comment.