Dark mode 2 (#100)
* 🗂️ Added tab icons and styles * 🪑 Styled download panel table * 🪑 Cleaned up the DC++ search results table * 🪑 Many changes to DC++ downloads table * 🏗️ Wired up search with RQ * 🏗️ Changes to ComicDetail section * 🔧 Fixing table styes * 🏗 Fixed the archive ops panel * 🔧 Tweaked Archive ops further * 🃏 Styling the action menu * 🧩 CV Match panel refactor WIP * 🏗️ Refactored the action menu * 🤼 Cleaning up CV match panel * 🏗️ Refactored the scored matches * 🤼 Revamped CV match panel UX * 🖌️ Styling tweaks to the side panel * 🖌️ Cleaned up the form * 🏗️ Refactored the search form * 🤐 Added a uncompress indicator * 🏗️ Fix for encoding # in URIs * 🔧 Fixed # symbol handling in URLs * 🔧 Started work on Edit Metadata panel * 🔎 Added a check for existing uncompressed archives * 🏗️ Settings styling tweaks * 🏗️ Fixed invalidation of archiveOps * 🏗️ Fixed an invalidation query on DC++ download panel * 🏗️ Fixed CV-sourced Volume info panel * 🏗️ Fixed volume group card stacks on Dashboard * 🔍 Fixing CV search page * 🏗️ Refactoring Volume groups and wanted panel * 🏗️ Cleaning up useless files * 🛝 Added keen-slider for pull list * 🏗️ Abstracted heading/subheading into Header * 🏗️ Continued refactoring of PullList, Volumes etc. * 📆 Wired up the datepicker to LoCG pull list
This commit was merged in pull request #100.
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
import React, { useEffect, useRef } from "react";
|
||||
|
||||
export const Canvas = (data) => {
|
||||
const { colorHistogramData } = data.data;
|
||||
console.log(data);
|
||||
export const Canvas = ({ data }) => {
|
||||
const { colorHistogramData } = data;
|
||||
const width = 559;
|
||||
const height = 200;
|
||||
const pixelRatio = window.devicePixelRatio;
|
||||
@@ -10,7 +9,11 @@ export const Canvas = (data) => {
|
||||
const canvas = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const context = canvas.current.getContext("2d");
|
||||
const context = canvas.current?.getContext("2d");
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
|
||||
context.scale(pixelRatio, pixelRatio);
|
||||
const guideHeight = 8;
|
||||
const startY = height - guideHeight;
|
||||
@@ -46,18 +49,24 @@ export const Canvas = (data) => {
|
||||
context.stroke();
|
||||
|
||||
// Guide
|
||||
context.strokeStyle = "rgb(" + i + ", " + i + ", " + i + ")";
|
||||
context.strokeStyle = `rgb(${i}, ${i}, ${i})`;
|
||||
context.beginPath();
|
||||
context.moveTo(x, startY);
|
||||
context.lineTo(x, height);
|
||||
context.closePath();
|
||||
context.stroke();
|
||||
}
|
||||
});
|
||||
|
||||
// Cleanup function
|
||||
return () => {
|
||||
// Perform cleanup actions here
|
||||
};
|
||||
}, [colorHistogramData, pixelRatio]);
|
||||
|
||||
const dw = Math.floor(pixelRatio * width);
|
||||
const dh = Math.floor(pixelRatio * height);
|
||||
const style = { width, height };
|
||||
|
||||
return <canvas ref={canvas} width={dw} height={dh} style={style} />;
|
||||
};
|
||||
|
||||
|
||||
@@ -83,24 +83,26 @@ const renderCard = (props: ICardProps): ReactElement => {
|
||||
|
||||
case "vertical-2":
|
||||
return (
|
||||
<div className="block rounded-md w-fit h-fit shadow-md shadow-white-400 bg-gray-200 dark:bg-slate-500">
|
||||
<div className="block rounded-md w-64 h-fit shadow-md shadow-white-400 bg-gray-200 dark:bg-slate-500">
|
||||
<img
|
||||
alt="Home"
|
||||
src={props.imageUrl}
|
||||
className="rounded-t-md object-cover"
|
||||
/>
|
||||
|
||||
<div className="mt-2 px-2">
|
||||
<dl>
|
||||
<div>
|
||||
<dd className="text-md text-slate-500 dark:text-black">
|
||||
{props.title}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
{props.title ? (
|
||||
<div className="px-3 pt-3 mb-2">
|
||||
<dd className="text-sm text-slate-500 dark:text-black">
|
||||
{props.title}
|
||||
</dd>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{props.hasDetails && <>{props.children}</>}
|
||||
</div>
|
||||
{props.hasDetails ? (
|
||||
<div className="px-2">
|
||||
<>{props.children}</>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
128
src/client/components/shared/DatePicker.tsx
Normal file
128
src/client/components/shared/DatePicker.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
import React, { ChangeEventHandler, useRef, useState } from "react";
|
||||
|
||||
import { format, isValid, parse, parseISO } from "date-fns";
|
||||
import FocusTrap from "focus-trap-react";
|
||||
import { DayPicker, SelectSingleEventHandler } from "react-day-picker";
|
||||
import { usePopper } from "react-popper";
|
||||
|
||||
export const DatePickerDialog = (props) => {
|
||||
console.log(props);
|
||||
const { setter, apiAction } = props;
|
||||
const [selected, setSelected] = useState<Date>();
|
||||
const [isPopperOpen, setIsPopperOpen] = useState(false);
|
||||
|
||||
const popperRef = useRef<HTMLDivElement>(null);
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
const customStyles = {
|
||||
container: {
|
||||
// Style for the entire container
|
||||
border: "1px solid #ccc",
|
||||
borderRadius: "4px",
|
||||
padding: "10px",
|
||||
width: "300px",
|
||||
},
|
||||
day: {
|
||||
// Style for individual days
|
||||
|
||||
padding: "5px",
|
||||
margin: "2px",
|
||||
},
|
||||
selected: {
|
||||
// Style for selected days
|
||||
backgroundColor: "#007bff",
|
||||
color: "#fff",
|
||||
},
|
||||
disabled: {
|
||||
// Style for disabled days
|
||||
color: "#ccc",
|
||||
},
|
||||
today: {
|
||||
// Style for today's date
|
||||
backgroundColor: "#f0f0f0",
|
||||
},
|
||||
dayWrapper: {
|
||||
// Style for the wrapper around each day
|
||||
display: "inline-block",
|
||||
},
|
||||
};
|
||||
|
||||
const popper = usePopper(popperRef.current, popperElement, {
|
||||
placement: "bottom-start",
|
||||
});
|
||||
|
||||
const closePopper = () => {
|
||||
setIsPopperOpen(false);
|
||||
buttonRef?.current?.focus();
|
||||
};
|
||||
|
||||
const handleButtonClick = () => {
|
||||
setIsPopperOpen(true);
|
||||
};
|
||||
|
||||
const handleDaySelect: SelectSingleEventHandler = (date) => {
|
||||
setSelected(date);
|
||||
if (date) {
|
||||
setter(format(date, "M-dd-yyyy"));
|
||||
apiAction();
|
||||
closePopper();
|
||||
} else {
|
||||
setter("");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div ref={popperRef}>
|
||||
<button
|
||||
ref={buttonRef}
|
||||
type="button"
|
||||
aria-label="Pick a date"
|
||||
onClick={handleButtonClick}
|
||||
className="flex space-x-1 sm:flex-row sm:items-center rounded-lg border border-green-400 dark:border-green-200 bg-green-200 px-2 py-1 text-gray-500 hover:bg-transparent hover:text-green-600 focus:outline-none focus:ring active:text-indigo-500"
|
||||
>
|
||||
<span className="pr-1 pt-0.5 h-8">
|
||||
<span className="icon-[solar--calendar-date-bold-duotone] w-6 h-6"></span>
|
||||
</span>
|
||||
Pick a date
|
||||
</button>
|
||||
</div>
|
||||
{isPopperOpen && (
|
||||
<FocusTrap
|
||||
active
|
||||
focusTrapOptions={{
|
||||
initialFocus: false,
|
||||
allowOutsideClick: true,
|
||||
clickOutsideDeactivates: true,
|
||||
onDeactivate: closePopper,
|
||||
fallbackFocus: buttonRef.current || undefined,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
tabIndex={-1}
|
||||
style={popper.styles.popper}
|
||||
className="bg-slate-200 mt-3 p-2 rounded-lg z-50"
|
||||
{...popper.attributes.popper}
|
||||
ref={setPopperElement}
|
||||
role="dialog"
|
||||
aria-label="DayPicker calendar"
|
||||
>
|
||||
<DayPicker
|
||||
initialFocus={isPopperOpen}
|
||||
mode="single"
|
||||
defaultMonth={selected}
|
||||
selected={selected}
|
||||
onSelect={handleDaySelect}
|
||||
styles={customStyles}
|
||||
/>
|
||||
</div>
|
||||
</FocusTrap>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DatePickerDialog;
|
||||
@@ -64,7 +64,7 @@ export const DnD = (data) => {
|
||||
className="mt-2 mb-2"
|
||||
onClick={(e) => data.onClickHandler(url)}
|
||||
>
|
||||
<div className="box p-2 pl-3 control-palette">
|
||||
<div className="box p-2 control-palette">
|
||||
<span className="tag is-warning mr-2">{index}</span>
|
||||
<span className="icon is-small mr-2">
|
||||
<i className="fa-solid fa-vial"></i>
|
||||
|
||||
@@ -8,7 +8,6 @@ export function Grid({ children, columns }) {
|
||||
gridTemplateColumns: `repeat(${columns}, 200px)`,
|
||||
columnGap: 1,
|
||||
gridGap: 10,
|
||||
padding: 10,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -1,19 +1,32 @@
|
||||
import React, { ReactElement } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
type IHeaderProps = {
|
||||
headerContent: string;
|
||||
subHeaderContent: string;
|
||||
iconClassNames: string;
|
||||
link?: string;
|
||||
};
|
||||
|
||||
export const Header = (props: IHeaderProps): ReactElement => {
|
||||
return (
|
||||
<>
|
||||
<h4 className="title is-4">
|
||||
<i className={props.iconClassNames}></i> {props.headerContent}
|
||||
</h4>
|
||||
<p className="subtitle is-7">{props.subHeaderContent}</p>
|
||||
</>
|
||||
<div className="mt-7">
|
||||
<div className="">
|
||||
{props.link ? (
|
||||
<Link to={props.link}>
|
||||
<span className="text-xl">
|
||||
<span className="underline">
|
||||
{props.headerContent}{" "}
|
||||
<i className="icon-[solar--arrow-right-up-outline] w-4 h-4" />
|
||||
</span>
|
||||
</span>
|
||||
</Link>
|
||||
) : (
|
||||
<div className="text-xl">{props.headerContent}</div>
|
||||
)}
|
||||
<p className="">{props.subHeaderContent}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ interface IMetadatPanelProps {
|
||||
containerStyle: any;
|
||||
}
|
||||
export const MetadataPanel = (props: IMetadatPanelProps): ReactElement => {
|
||||
console.log(props);
|
||||
const {
|
||||
rawFileDetails,
|
||||
inferredMetadata,
|
||||
@@ -78,6 +79,15 @@ export const MetadataPanel = (props: IMetadatPanelProps): ReactElement => {
|
||||
{prettyBytes(rawFileDetails.fileSize)}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
{/* Uncompressed version available? */}
|
||||
{rawFileDetails.archive?.uncompressed && (
|
||||
<span className="inline-flex items-center bg-slate-50 text-slate-800 text-xs px-2 rounded-md dark:text-slate-900 dark:bg-slate-400">
|
||||
<span className="pr-1 pt-1">
|
||||
<i className="icon-[solar--bookmark-bold-duotone] w-3.5 h-3.5"></i>
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
</dd>
|
||||
</dl>
|
||||
),
|
||||
@@ -105,7 +115,6 @@ export const MetadataPanel = (props: IMetadatPanelProps): ReactElement => {
|
||||
</span>
|
||||
</span>
|
||||
</dd>
|
||||
|
||||
<dd className="is-size-7">
|
||||
<span>
|
||||
{ellipsize(
|
||||
@@ -118,42 +127,13 @@ export const MetadataPanel = (props: IMetadatPanelProps): ReactElement => {
|
||||
)}
|
||||
</span>
|
||||
</dd>
|
||||
|
||||
<dd className="is-size-7 mt-2">
|
||||
<div className="field is-grouped is-grouped-multiline">
|
||||
<div className="control">
|
||||
<span className="tags">
|
||||
<span
|
||||
className="tag is-success is-light has-text-weight-semibold"
|
||||
style={props.tagsStyle}
|
||||
>
|
||||
{comicvine.volumeInformation.start_year}
|
||||
</span>
|
||||
<span
|
||||
className="tag is-success is-light"
|
||||
style={props.tagsStyle}
|
||||
>
|
||||
{comicvine.volumeInformation.count_of_issues}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div className="control">
|
||||
<div className="tags has-addons">
|
||||
<span
|
||||
className="tag is-primary is-light"
|
||||
style={props.tagsStyle}
|
||||
>
|
||||
ComicVine ID
|
||||
</span>
|
||||
<span
|
||||
className="tag is-info is-light"
|
||||
style={props.tagsStyle}
|
||||
>
|
||||
{comicvine.id}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span className="my-3 mx-2">
|
||||
{comicvine.volumeInformation.start_year}
|
||||
</span>
|
||||
{comicvine.volumeInformation.count_of_issues}
|
||||
ComicVine ID
|
||||
{comicvine.id}
|
||||
</dd>
|
||||
</dl>
|
||||
),
|
||||
|
||||
@@ -52,12 +52,12 @@ export const Navbar2 = (): ReactElement => {
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a
|
||||
<Link
|
||||
to="/volumes"
|
||||
className="text-gray-500 transition hover:text-gray-500/75 dark:text-white dark:hover:text-white/75"
|
||||
href="/"
|
||||
>
|
||||
Volumes
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
@@ -68,6 +68,14 @@ export const Navbar2 = (): ReactElement => {
|
||||
Downloads
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
className="text-gray-500 transition hover:text-gray-500/75 dark:text-white dark:hover:text-white/75"
|
||||
to="/search"
|
||||
>
|
||||
Comicvine Search
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import React, { ReactElement, useMemo, useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import SearchBar from "../Library/SearchBar";
|
||||
import { Link } from "react-router-dom";
|
||||
import {
|
||||
ColumnDef,
|
||||
flexRender,
|
||||
@@ -72,10 +70,9 @@ export const T2Table = (tableOptions): ReactElement => {
|
||||
return (
|
||||
<div className="container max-w-fit mx-14">
|
||||
<div>
|
||||
{/* Search bar */}
|
||||
<div className="flex flex-row gap-2 justify-between mt-5">
|
||||
<SearchBar />
|
||||
|
||||
<div className="flex flex-row gap-2 justify-between mt-7">
|
||||
{/* Search bar */}
|
||||
{tableOptions.children}
|
||||
{/* pagination controls */}
|
||||
<div>
|
||||
Page {pageIndex} of {Math.ceil(totalPages / pageSize)}
|
||||
@@ -154,5 +151,6 @@ T2Table.propTypes = {
|
||||
previousPage: PropTypes.func,
|
||||
}),
|
||||
rowClickHandler: PropTypes.func,
|
||||
children: PropTypes.any,
|
||||
};
|
||||
export default T2Table;
|
||||
|
||||
Reference in New Issue
Block a user