💧 Added react-select to implement dropdown actions menu

This commit is contained in:
2021-09-03 13:33:13 -07:00
parent 1f8e0d6ff6
commit e202ae45a4
5 changed files with 115 additions and 73 deletions

View File

@@ -50,6 +50,7 @@
"react-dom": "^17.0.1",
"react-fast-compare": "^3.2.0",
"react-final-form": "^6.5.3",
"react-select": "^4.3.1",
"react-sliding-pane": "^7.0.0",
"react-spinners": "^0.11.0",
"react-table": "^7.7.0",

View File

@@ -55,6 +55,10 @@ export const search = (data: SearchData) => async (dispatch) => {
"search_result_updated",
async (groupedResult) => {
// ...update properties of the existing result in the UI
dispatch({
type: AIRDCPP_SEARCH_RESULTS_RECEIVED,
groupedResult,
});
},
instance.id,
);
@@ -67,12 +71,7 @@ export const search = (data: SearchData) => async (dispatch) => {
async (searchInfo) => {
await sleep(5000);
// The search can now be considered to be "complete"
dispatch({
type: AIRDCPP_HUB_SEARCHES_SENT,
searchInfo,
instance,
});
// Check the number of received results (in real use cases we should know that even without calling the API)
const currentInstance = await SocketService.get(
@@ -82,7 +81,13 @@ export const search = (data: SearchData) => async (dispatch) => {
// ...nothing was received, show an informative message to the user
}
// The search can now be considered to be "complete"
// If there's an "in progress" indicator in the UI, that could also be disabled here
dispatch({
type: AIRDCPP_HUB_SEARCHES_SENT,
searchInfo,
instance,
});
},
instance.id,
);

View File

@@ -6,6 +6,7 @@ import ComicVineSearchForm from "./ComicVineSearchForm";
import AcquisitionPanel from "./AcquisitionPanel";
import DownloadsPanel from "./DownloadsPanel";
import SlidingPane from "react-sliding-pane";
import Select from "react-select";
import { css } from "@emotion/react";
import "react-sliding-pane/dist/react-sliding-pane.css";
@@ -37,7 +38,6 @@ type ComicDetailProps = {};
export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
const [page, setPage] = useState(1);
const [visible, setVisible] = useState(false);
const [isActionDropdownCollapsed, collapseActionDropdown] = useState(false);
const comicVineSearchResults = useSelector(
(state: RootState) => state.comicInfo.searchResults,
@@ -54,8 +54,6 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
const { comicObjectId } = useParams<{ comicObjectId: string }>();
const dispatch = useDispatch();
const toggleActionDropdown = () =>
collapseActionDropdown(!isActionDropdownCollapsed);
useEffect(() => {
dispatch(getComicBookDetailById(comicObjectId));
@@ -251,13 +249,30 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
imagePath = comicBookDetailData.sourcedMetadata.comicvine.image.small_url;
comicBookTitle = comicBookDetailData.sourcedMetadata.comicvine.name;
}
// actions menu options and handler
const actionOptions = [
{ value: "match-on-comic-vine", label: "Match on Comic Vine" },
{ value: "edit-metdata", label: "Edit Metadata" },
{ value: "delete-comic", label: "Delete Comic" },
];
const handleActionSelection = (action) => {
switch (action.value) {
case "match-on-comic-vine":
openDrawerWithCVMatches();
break;
default:
console.log("No valid action selected.");
break;
}
};
return (
<section className="container">
<div className="section">
{!isNil(comicBookDetailData) && !isEmpty(comicBookDetailData) && (
<>
<h1 className="title">{comicBookTitle}</h1>
<div className="columns">
<div className="columns is-multiline">
<div className="column is-narrow">
<Card
imageUrl={imagePath}
@@ -266,7 +281,7 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
/>
</div>
{/* raw file details */}
<div className="column">
<div className="column is-three-fifths">
{!isNil(comicBookDetailData.rawFileDetails) && (
<>
<RawFileDetails data={comicBookDetailData.rawFileDetails} />
@@ -279,63 +294,21 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
updatedAt={comicBookDetailData.updatedAt}
/>
)}
{/* action dropdown */}
<div
className={
"dropdown " + (isActionDropdownCollapsed ? "is-active" : "")
}
onBlur={() => toggleActionDropdown()}
onFocus={() => toggleActionDropdown()}
>
<div className="dropdown-trigger">
<button
className="button is-small"
aria-haspopup="true"
aria-controls="dropdown-menu2"
onClick={() => toggleActionDropdown()}
>
<span>
<i className="fas fa-sliders-h"></i> Actions
</span>
<span className="icon is-small">
<i className="fas fa-angle-down" aria-hidden="true"></i>
</span>
</button>
</div>
<div
className="dropdown-menu"
id="dropdown-menu2"
role="menu"
>
<div className="dropdown-content">
<div
className="dropdown-item"
onClick={openDrawerWithCVMatches}
>
<span className="icon">
<i className="fas fa-magic"></i>
</span>
<span>Match on ComicVine</span>
</div>
<hr className="dropdown-divider" />
<div className="dropdown-item">
<span className="icon">
<i className="fas fa-edit"></i>
</span>
<span>Edit Metdata</span>
</div>
<hr className="dropdown-divider" />
<div className="dropdown-item">
<span className="icon">
<i className="fas fa-trash"></i>
</span>
<span>Delete Comic</span>
</div>
</div>
</div>
</div>
</div>
{/* action dropdown */}
<div className="column is-one-quarter is-narrow">
<Select
className="basic-single"
classNamePrefix="select"
defaultValue={actionOptions[0]}
name="color"
isSearchable={false}
options={actionOptions}
onChange={handleActionSelection}
/>
</div>
</div>
{isComicBookMetadataAvailable ? <MetadataTabGroup /> : null}

View File

@@ -33,7 +33,7 @@ function airdcppReducer(state = initialState, action) {
case AIRDCPP_HUB_SEARCHES_SENT:
return {
...state,
isAirDCPPSearchInProgress: true,
isAirDCPPSearchInProgress: false,
searchInfo: action.searchInfo,
searchInstance: action.instance,
};
@@ -57,10 +57,10 @@ function airdcppReducer(state = initialState, action) {
return {
searchResults: [],
isAirDCPPSearchInProgress: false,
searchInfo: null,
searchInstance: null,
downloadResult: null,
bundleDBImportResult: null,
searchInfo: null,
searchInstance: null,
downloadResult: null,
bundleDBImportResult: null,
};
default:

View File

@@ -978,6 +978,13 @@
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.12.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7":
version "7.15.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a"
integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/template@^7.14.5", "@babel/template@^7.3.3":
version "7.14.5"
resolved "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz"
@@ -1057,6 +1064,19 @@
resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz"
integrity sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==
"@emotion/react@^11.1.1":
version "11.4.1"
resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.4.1.tgz#a1b0b767b5bad57515ffb0cad9349614d27f4d57"
integrity sha512-pRegcsuGYj4FCdZN6j5vqCALkNytdrKw3TZMekTzNXixRg4wkLsU5QEaBG5LC6l01Vppxlp7FE3aTHpIG5phLg==
dependencies:
"@babel/runtime" "^7.13.10"
"@emotion/cache" "^11.4.0"
"@emotion/serialize" "^1.0.2"
"@emotion/sheet" "^1.0.2"
"@emotion/utils" "^1.0.0"
"@emotion/weak-memoize" "^0.2.5"
hoist-non-react-statics "^3.3.1"
"@emotion/react@^11.1.4":
version "11.4.0"
resolved "https://registry.npmjs.org/@emotion/react/-/react-11.4.0.tgz"
@@ -1086,6 +1106,11 @@
resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.0.1.tgz"
integrity sha512-GbIvVMe4U+Zc+929N1V7nW6YYJtidj31lidSmdYcWozwoBIObXBnaJkKNDjZrLm9Nc0BR+ZyHNaRZxqNZbof5g==
"@emotion/sheet@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.0.2.tgz#1d9ffde531714ba28e62dac6a996a8b1089719d0"
integrity sha512-QQPB1B70JEVUHuNtzjHftMGv6eC3Y9wqavyarj4x4lg47RACkeSfNo5pxIOKizwS9AEFLohsqoaxGQj4p0vSIw==
"@emotion/unitless@^0.7.5":
version "0.7.5"
resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz"
@@ -4679,6 +4704,14 @@ dom-converter@^0.2.0:
dependencies:
utila "~0.4"
dom-helpers@^5.0.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==
dependencies:
"@babel/runtime" "^7.8.7"
csstype "^3.0.2"
dom-serializer@0:
version "0.2.2"
resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz"
@@ -8655,7 +8688,7 @@ mem@^4.0.0:
mimic-fn "^2.0.0"
p-is-promise "^2.0.0"
"memoize-one@>=3.1.1 <6":
"memoize-one@>=3.1.1 <6", memoize-one@^5.0.0:
version "5.2.1"
resolved "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz"
integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==
@@ -10435,7 +10468,7 @@ promzard@^0.3.0:
dependencies:
read "1"
prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@@ -10843,6 +10876,13 @@ react-hot-loader@^4.13.0:
shallowequal "^1.1.0"
source-map "^0.7.3"
react-input-autosize@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-3.0.0.tgz#6b5898c790d4478d69420b55441fcc31d5c50a85"
integrity sha512-nL9uS7jEs/zu8sqwFE5MAPx6pPkNAriACQ2rGLlqmKr2sPGtN7TXTyDdQt4lbNXVx7Uzadb40x8qotIuru6Rhg==
dependencies:
prop-types "^15.5.8"
react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1:
version "16.13.1"
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
@@ -10909,6 +10949,19 @@ react-router@5.2.0, react-router@^5.2.0:
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
react-select@^4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/react-select/-/react-select-4.3.1.tgz#389fc07c9bc7cf7d3c377b7a05ea18cd7399cb81"
integrity sha512-HBBd0dYwkF5aZk1zP81Wx5UsLIIT2lSvAY2JiJo199LjoLHoivjn9//KsmvQMEFGNhe58xyuOITjfxKCcGc62Q==
dependencies:
"@babel/runtime" "^7.12.0"
"@emotion/cache" "^11.4.0"
"@emotion/react" "^11.1.1"
memoize-one "^5.0.0"
prop-types "^15.6.0"
react-input-autosize "^3.0.0"
react-transition-group "^4.3.0"
react-sliding-pane@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/react-sliding-pane/-/react-sliding-pane-7.0.0.tgz#ed96400fce273e7a36bd26cfb8ffed4e1480cc29"
@@ -10929,6 +10982,16 @@ react-table@^7.7.0:
resolved "https://registry.npmjs.org/react-table/-/react-table-7.7.0.tgz"
integrity sha512-jBlj70iBwOTvvImsU9t01LjFjy4sXEtclBovl3mTiqjz23Reu0DKnRza4zlLtOPACx6j2/7MrQIthIK1Wi+LIA==
react-transition-group@^4.3.0:
version "4.4.2"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470"
integrity sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==
dependencies:
"@babel/runtime" "^7.5.5"
dom-helpers "^5.0.1"
loose-envify "^1.4.0"
prop-types "^15.6.2"
react-window-dynamic-list@^2.3.5:
version "2.4.2"
resolved "https://registry.npmjs.org/react-window-dynamic-list/-/react-window-dynamic-list-2.4.2.tgz"