🌁 Generalizing the card component Part 1

This commit is contained in:
2021-08-19 15:38:18 -07:00
parent 2184b20887
commit 7aa3db125b
5 changed files with 196 additions and 92 deletions

View File

@@ -49,6 +49,19 @@ $border-color: red;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.partial-rounded-card-image {
img {
border-top-left-radius: 0.3rem;
border-top-right-radius: 0.3rem;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
}
.rounded-card-image {
img {
border-radius: 0.3rem;
}
}
} }
.card-container { .card-container {
display: grid; display: grid;
@@ -59,7 +72,7 @@ $border-color: red;
.card { .card {
// max-width: 500px; // max-width: 500px;
margin: 0 0 15px 0; margin: 0 0 15px 0;
.card-image { .partial-rounded-card-image {
img { img {
border-top-left-radius: 0.3rem; border-top-left-radius: 0.3rem;
border-top-right-radius: 0.3rem; border-top-right-radius: 0.3rem;
@@ -67,6 +80,9 @@ $border-color: red;
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
} }
} }
.rounded-card-image {
border-radius: 0.3rem;
}
.is-horizontal { .is-horizontal {
flex-direction: row; flex-direction: row;
@@ -126,18 +142,18 @@ $border-color: red;
// Search // Search
.search { .search {
.main-search-bar { .main-search-bar {
border: 0; border: 0;
border-bottom: 1px solid #999; border-bottom: 1px solid #999;
border-radius: 0; border-radius: 0;
outline: 0; outline: 0;
background: transparent; background: transparent;
box-shadow: none; box-shadow: none;
} }
} }
// Library // Library
.library { .library {
table { table {
tr{ tr {
td { td {
border: 0 none; border: 0 none;
.card { .card {
@@ -147,8 +163,7 @@ $border-color: red;
} }
} }
} }
}
}
tbody { tbody {
padding: 10px 0 0 0; padding: 10px 0 0 0;
} }

View File

@@ -0,0 +1,52 @@
import React, { ReactElement } from "react";
import PropTypes from "prop-types";
import { isEmpty, isNil } from "lodash";
interface ICardProps {
orientation: string;
imageUrl: string;
hasDetails: boolean;
title?: PropTypes.ReactElementLike | null;
children?: PropTypes.ReactNodeLike;
}
const renderCard = (props): ReactElement => {
switch (props.orientation) {
case "horizontal":
return <>horiztonal</>;
case "vertical":
return (
<div>
<div className="card generic-card">
<div>
<div
className={
props.hasDetails
? "partial-rounded-card-image"
: "rounded-card-image"
}
>
<figure>
<img src={props.imageUrl} alt="Placeholder image" />
</figure>
</div>
{props.hasDetails && (
<div className="card-content">
{isNil(props.title) ? "No Name" : props.title}
{props.children}
</div>
)}
</div>
</div>
</div>
);
default:
return <></>;
}
};
export const Card = (props: ICardProps): ReactElement => {
return renderCard(props);
};
export default Card;

View File

@@ -1,13 +1,13 @@
import React, { useState, useEffect, useCallback, ReactElement } from "react"; import React, { useState, useEffect, useCallback, ReactElement } from "react";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import Card from "./Card"; import Card from "./Carda";
import MatchResult from "./MatchResult"; import MatchResult from "./MatchResult";
import ComicVineSearchForm from "./ComicVineSearchForm"; import ComicVineSearchForm from "./ComicVineSearchForm";
import { css } from "@emotion/react"; import { css } from "@emotion/react";
import PuffLoader from "react-spinners/PuffLoader"; import PuffLoader from "react-spinners/PuffLoader";
import { isEmpty, isUndefined, isNil } from "lodash"; import { isEmpty, isUndefined, isNil } from "lodash";
import { IExtractedComicBookCoverFile, RootState } from "threetwo-ui-typings"; import { RootState } from "threetwo-ui-typings";
import { fetchComicVineMatches } from "../actions/fileops.actions"; import { fetchComicVineMatches } from "../actions/fileops.actions";
import { getComicBookDetailById } from "../actions/comicinfo.actions"; import { getComicBookDetailById } from "../actions/comicinfo.actions";
import { Drawer, Divider } from "antd"; import { Drawer, Divider } from "antd";
@@ -16,6 +16,10 @@ const prettyBytes = require("pretty-bytes");
import "antd/dist/antd.css"; import "antd/dist/antd.css";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import {
removeLeadingPeriod,
escapePoundSymbol,
} from "../shared/utils/formatting.utils";
type ComicDetailProps = {}; type ComicDetailProps = {};
/** /**
@@ -60,7 +64,7 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
setVisible(false); setVisible(false);
}; };
const [active, setActive] = useState(0); const [active, setActive] = useState(1);
const createDescriptionMarkup = (html) => { const createDescriptionMarkup = (html) => {
return { __html: html }; return { __html: html };
}; };
@@ -71,7 +75,7 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
// Tab groups for ComicVine metadata // Tab groups for ComicVine metadata
const tabGroup = [ const tabGroup = [
{ {
id: 0, id: 1,
name: "Volume Information", name: "Volume Information",
content: isComicBookMetadataAvailable ? ( content: isComicBookMetadataAvailable ? (
<> <>
@@ -130,7 +134,7 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
) : null, ) : null,
}, },
{ {
id: 1, id: 2,
name: "Other Metadata", name: "Other Metadata",
content: <div>bastard</div>, content: <div>bastard</div>,
}, },
@@ -158,85 +162,96 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
); );
}; };
const RawFileDetails = (props) => (
<div className="content comic-detail">
<dl>
<dt>Raw File Details</dt>
<dd>{props.data.containedIn}</dd>
<dd>{prettyBytes(props.data.fileSize)}</dd>
<dd>{props.data.path}</dd>
<dd>
<span className="tag is-primary">{props.data.extension}</span>
</dd>
</dl>
</div>
);
const ComicVineDetails = (props) => (
<div className="content comic-detail">
<dl>
<dt>ComicVine Metadata</dt>
<dd className="is-size-7">
Last scraped on{" "}
{dayjs(props.updatedAt).format("MMM D YYYY [at] h:mm a")}
</dd>
<dd>
<h6>{props.data.name}</h6>
</dd>
<dd>{props.data.number}</dd>
<dd>
<div className="field is-grouped is-grouped-multiline">
<div className="control">
<div className="tags has-addons">
<span className="tag is-light">Type</span>
<span className="tag is-warning">
{props.data.resource_type}
</span>
</div>
</div>
<div className="control">
<div className="tags has-addons">
<span className="tag is-light">ComicVine Issue ID</span>
<span className="tag is-success">{props.data.id}</span>
</div>
</div>
</div>
</dd>
</dl>
</div>
);
let imagePath = "";
let comicBookTitle = "";
if (!isNil(comicBookDetailData.rawFilDetails)) {
const encodedFilePath = encodeURI(
"http://localhost:3000" +
removeLeadingPeriod(comicBookDetailData.rawFilDetails.path),
);
imagePath = escapePoundSymbol(encodedFilePath);
comicBookTitle = comicBookDetailData.rawFilDetails.name;
} else if (
!isNil(comicBookDetailData.sourcedMetadata) &&
!isNil(comicBookDetailData.sourcedMetadata.comicvine)
) {
imagePath = comicBookDetailData.sourcedMetadata.comicvine.image.small_url;
comicBookTitle = comicBookDetailData.sourcedMetadata.comicvine.name;
}
return ( return (
<section className="container"> <section className="container">
<div className="section"> <div className="section">
{!isNil(comicBookDetailData) && !isEmpty(comicBookDetailData) && ( {!isNil(comicBookDetailData) && !isEmpty(comicBookDetailData) && (
<> <>
<h1 className="title">{comicBookDetailData.rawFileDetails.name}</h1> <h1 className="title">{comicBookTitle}</h1>
<div className="columns"> <div className="columns">
<div className="column is-narrow"> <div className="column is-narrow">
<Card <Card
comicBookCoversMetadata={comicBookDetailData.rawFileDetails} imageUrl={imagePath}
hasTitle={false} orientation={"vertical"}
isHorizontal={false} hasDetails={false}
/> />
</div> </div>
<div className="column"> <div className="column">
<div className="content comic-detail"> {!isNil(comicBookDetailData.rawFileDetails) && (
<dl> <>
<dt>Raw File Details</dt> <RawFileDetails data={comicBookDetailData.rawFileDetails} />
<dd>{comicBookDetailData.rawFileDetails.containedIn}</dd>
<dd>
{prettyBytes(comicBookDetailData.rawFileDetails.fileSize)}
</dd>
<dd>{comicBookDetailData.rawFileDetails.path}</dd>
<dd>
<span className="tag is-primary">
{comicBookDetailData.rawFileDetails.extension}
</span>
</dd>
</dl>
</div>
{!isNil(comicBookDetailData.sourcedMetadata.comicvine) && (
<div className="content comic-detail">
<Divider /> <Divider />
<dl> </>
<dt>ComicVine Metadata</dt> )}
<dd className="is-size-7"> {!isNil(comicBookDetailData.sourcedMetadata.comicvine) && (
Last scraped on{" "} <ComicVineDetails
{dayjs(comicBookDetailData.updatedAt).format( data={comicBookDetailData.sourcedMetadata.comicvine}
"MMM D YYYY [at] h:mm a", updatedAt={comicBookDetailData.updatedAt}
)} />
</dd>
<dd>
<h6>
{comicBookDetailData.sourcedMetadata.comicvine.name}
</h6>
</dd>
<dd>
{comicBookDetailData.sourcedMetadata.comicvine.number}
</dd>
<dd>
<div className="field is-grouped is-grouped-multiline">
<div className="control">
<div className="tags has-addons">
<span className="tag is-light">Type</span>
<span className="tag is-warning">
{
comicBookDetailData.sourcedMetadata.comicvine
.resource_type
}
</span>
</div>
</div>
<div className="control">
<div className="tags has-addons">
<span className="tag is-light">
ComicVine Issue ID
</span>
<span className="tag is-success">
{
comicBookDetailData.sourcedMetadata.comicvine
.id
}
</span>
</div>
</div>
</div>
</dd>
</dl>
</div>
)} )}
<button <button
className="button is-small" className="button is-small"

View File

@@ -56,6 +56,7 @@ const mapDispatchToProps = (dispatch) => ({
paginationOptions: { paginationOptions: {
page: 0, page: 0,
limit: 5, limit: 5,
sort: { updatedAt: "-1" },
}, },
}), }),
); );

View File

@@ -1,6 +1,12 @@
import React from "react"; import React, { ReactElement } from "react";
import Card from "./Card"; import Card from "./Carda";
import { map } from "lodash"; import { Link } from "react-router-dom";
import ellipsize from "ellipsize";
import {
removeLeadingPeriod,
escapePoundSymbol,
} from "../shared/utils/formatting.utils";
import { isNil, map } from "lodash";
type RecentlyImportedProps = { type RecentlyImportedProps = {
comicBookCovers: any; comicBookCovers: any;
@@ -8,17 +14,32 @@ type RecentlyImportedProps = {
export const RecentlyImported = ({ export const RecentlyImported = ({
comicBookCovers, comicBookCovers,
}: RecentlyImportedProps) => ( }: RecentlyImportedProps): ReactElement => (
<section className="card-container"> <section className="card-container">
{map(comicBookCovers.docs, ({ _id, rawFileDetails }) => { {map(comicBookCovers.docs, ({ _id, rawFileDetails, sourcedMetadata }) => {
let imagePath = "";
let comicName = "";
if (!isNil(rawFileDetails)) {
const encodedFilePath = encodeURI(
"http://localhost:3000" + removeLeadingPeriod(rawFileDetails.path),
);
imagePath = escapePoundSymbol(encodedFilePath);
comicName = rawFileDetails.name;
} else if (!isNil(sourcedMetadata)) {
imagePath = sourcedMetadata.comicvine.image.small_url;
comicName = sourcedMetadata.comicvine.name;
}
const titleElement = (
<Link to={"/comic/details/" + _id}>{ellipsize(comicName, 18)}</Link>
);
return ( return (
<Card <Card
key={_id} key={_id}
comicBookCoversMetadata={rawFileDetails} orientation={"vertical"}
mongoObjId={_id} imageUrl={imagePath}
hasTitle hasDetails
isHorizontal={false} title={comicName ? titleElement : null}
/> ></Card>
); );
})} })}
</section> </section>