Skip to content

Commit

Permalink
added #238, added context menu separator
Browse files Browse the repository at this point in the history
  • Loading branch information
mienaiyami committed Aug 16, 2023
1 parent 47a267d commit 6c28e3b
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 57 deletions.
9 changes: 9 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,15 @@ const App = (): ReactElement => {
// console.log("dddddddddd");
// }, 1000);
window.contextMenu.template = {
divider() {
return {
label: "",
action() {
//
},
divider: true,
};
},
open(url) {
return {
label: "Open",
Expand Down
1 change: 1 addition & 0 deletions src/Components/BookmarkHistoryListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ const BookmarkHistoryListItem = (props: ListItemE) => {
window.contextMenu.template.openInNewWindow(props.data.link),
window.contextMenu.template.showInExplorer(props.data.link),
window.contextMenu.template.copyPath(props.data.link),
window.contextMenu.template.divider(),
];
if (props.isBookmark) items.push(window.contextMenu.template.removeBookmark(props.data.link));
else items.push(window.contextMenu.template.addToBookmark(props));
Expand Down
59 changes: 36 additions & 23 deletions src/Components/ContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { useContext, useEffect, useRef, useState, useLayoutEffect } from "react"
// import { removeHistory } from "../store/history";
// import { useAppDispatch, useAppSelector } from "../store/hooks";
import { AppContext } from "../App";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCheck } from "@fortawesome/free-solid-svg-icons";

const ContextMenu = () => {
// const contextMenuData = useAppSelector((store) => store.contextMenu);
Expand Down Expand Up @@ -86,15 +88,21 @@ const ContextMenu = () => {
case "ArrowDown":
case "ArrowRight":
setFocused((init) => {
if (init + 1 >= contextMenuData.items.length) return 0;
return init + 1;
let f = init + 1;
if (f >= contextMenuData.items.length) f = 0;
if (ref.current?.querySelectorAll("ul li")[f]?.classList.contains("menu-divider"))
f++;
return f;
});
break;
case "ArrowUp":
case "ArrowLeft":
setFocused((init) => {
if (init - 1 < 0) return contextMenuData.items.length - 1;
return init - 1;
let f = init - 1;
if (f < 0) f = contextMenuData.items.length - 1;
if (ref.current?.querySelectorAll("ul li")[f]?.classList.contains("menu-divider"))
f--;
return f;
});
break;
case "Enter":
Expand All @@ -110,25 +118,30 @@ const ContextMenu = () => {
}
}}
>
<ul>
{contextMenuData.items.map((e, i) => (
<li
role="menuitem"
key={e.label}
onClick={e.action}
onContextMenu={e.action}
data-focused={i === focused}
onMouseEnter={() => {
setFocused(i);
}}
onMouseLeave={() => {
setFocused(-1);
}}
className={`${e.disabled ? "disabled " : ""}`}
>
{e.label}
</li>
))}
<ul className={contextMenuData.padLeft ? "padLeft" : ""}>
{contextMenuData.items.map((e, i) =>
e.divider ? (
<li role="menuitem" key={"divider" + i} className="menu-divider"></li>
) : (
<li
role="menuitem"
key={e.label}
onClick={e.action}
onContextMenu={e.action}
data-focused={i === focused}
onMouseEnter={() => {
setFocused(i);
}}
onMouseLeave={() => {
setFocused(-1);
}}
className={`${e.disabled ? "disabled " : ""}`}
>
{e.selected ? <FontAwesomeIcon icon={faCheck} /> : <span></span>}
{e.label}
</li>
)
)}
</ul>
</div>
)
Expand Down
131 changes: 99 additions & 32 deletions src/Components/LocationsTab.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { faAngleUp, faSort, faSyncAlt } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { ReactElement, useContext, useEffect, useRef, useState, useLayoutEffect, memo } from "react";
import { ReactElement, useContext, useEffect, useRef, useState, useLayoutEffect, memo, useMemo } from "react";
import { AppContext } from "../App";
import { setAppSettings } from "../store/appSettings";
import { useAppDispatch, useAppSelector } from "../store/hooks";
import LocationListItem from "./LocationListItem";
import { promptSelectDir } from "../MainImports";

type LocationData = { name: string; link: string };
type LocationData = { name: string; link: string; dateModified: number };

const LocationsTab = (): ReactElement => {
const { openInReader } = useContext(AppContext);
const { openInReader, setContextMenuData } = useContext(AppContext);
const history = useAppSelector((store) => store.history);
const appSettings = useAppSelector((store) => store.appSettings);
const dispatch = useAppDispatch();
Expand Down Expand Up @@ -69,27 +69,26 @@ const LocationsTab = (): ReactElement => {
);
}).length
);
const dirNames = files
.reduce((arr: LocationData[], cur) => {
if (window.fs.existsSync(window.path.join(link, cur))) {
if (
window.fs.lstatSync(window.path.join(link, cur)).isDirectory() ||
[".zip", ".cbz", ".rar", ".7z", ".epub", ".pdf"].includes(
window.path.extname(cur).toLowerCase()
)
) {
arr.push({
name: window.fs.lstatSync(window.path.join(link, cur)).isFile()
? window.app.replaceExtension(cur)
: cur,
link: window.path.join(link, cur),
});
}
const dirNames = files.reduce((arr: LocationData[], cur) => {
const stat = window.fs.lstatSync(window.path.join(link, cur));
if (window.fs.existsSync(window.path.join(link, cur))) {
if (
stat.isDirectory() ||
[".zip", ".cbz", ".rar", ".7z", ".epub", ".pdf"].includes(
window.path.extname(cur).toLowerCase()
)
) {
arr.push({
name: stat.isFile() ? window.app.replaceExtension(cur) : cur,
link: window.path.join(link, cur),
dateModified: stat.mtimeMs,
});
}
return arr;
// return (window.fs.lstatSync(window.path.join(link, e)) || false).isDirectory()
}, [])
.sort((a, b) => window.app.betterSortOrder(a.name, b.name));
}
return arr;
// return (window.fs.lstatSync(window.path.join(link, e)) || false).isDirectory()
}, []);

if (inputRef.current && !refresh) {
inputRef.current.value = "";
}
Expand Down Expand Up @@ -127,6 +126,14 @@ const LocationsTab = (): ReactElement => {
}
}, [inputRef]);

const sortedLocations = useMemo(() => {
const sorted =
appSettings.locationListSortBy === "name"
? locations.sort((a, b) => window.app.betterSortOrder(a.name, b.name))
: locations.sort((a, b) => (a.dateModified < b.dateModified ? -1 : 1));
return appSettings.locationListSortType === "inverse" ? [...sorted].reverse() : sorted;
}, [locations, appSettings.locationListSortBy, appSettings.locationListSortType]);

useEffect(() => {
if (currentLink) {
const historyIndex = history.findIndex(
Expand Down Expand Up @@ -158,15 +165,74 @@ const LocationsTab = (): ReactElement => {
<FontAwesomeIcon icon={faSyncAlt} />
</button> */}
<button
data-tooltip="Sort"
data-tooltip={
"Sort: " +
(appSettings.locationListSortType === "normal" ? "▲ " : "▼ ") +
appSettings.locationListSortBy.toUpperCase()
}
// tabIndex={-1}
onClick={() => {
dispatch(
setAppSettings({
locationListSortType:
appSettings.locationListSortType === "inverse" ? "normal" : "inverse",
})
);
onClick={(e) => {
// dispatch(
// setAppSettings({
// locationListSortType:
// appSettings.locationListSortType === "inverse" ? "normal" : "inverse",
// })
// );
const items: MenuListItem[] = [
{
label: "Name",
action() {
dispatch(
setAppSettings({
locationListSortBy: "name",
})
);
},
selected: appSettings.locationListSortBy === "name",
},
{
label: "Date Modified",
action() {
dispatch(
setAppSettings({
locationListSortBy: "date",
locationListSortType: "inverse",
})
);
},
selected: appSettings.locationListSortBy === "date",
},
window.contextMenu.template.divider(),
{
label: "Ascending",
action() {
dispatch(
setAppSettings({
locationListSortType: "normal",
})
);
},
selected: appSettings.locationListSortType === "normal",
},
{
label: "Descending",
action() {
dispatch(
setAppSettings({
locationListSortType: "inverse",
})
);
},
selected: appSettings.locationListSortType === "inverse",
},
];
setContextMenuData({
clickX: e.currentTarget.getBoundingClientRect().x,
clickY: e.currentTarget.getBoundingClientRect().bottom + 4,
padLeft: true,
items,
focusBackElem: e.currentTarget,
});
}}
>
<FontAwesomeIcon icon={faSort} />
Expand Down Expand Up @@ -260,6 +326,7 @@ const LocationsTab = (): ReactElement => {
}
if (val === ".." + window.path.sep)
return setCurrentLink(window.path.resolve(currentLink, "../"));
// check for exact match and open directly without enter
if (val[val.length - 1] === window.path.sep) {
const index = locations.findIndex(
(e) =>
Expand Down Expand Up @@ -321,7 +388,7 @@ const LocationsTab = (): ReactElement => {
<p>0 Folders, {imageCount} Images</p>
) : (
<ol>
{(appSettings.locationListSortType === "inverse" ? [...locations].reverse() : locations)
{sortedLocations
.filter((e) => new RegExp(filter, "ig") && new RegExp(filter, "ig").test(e.name))
.map(
(e, i, arr) =>
Expand Down
2 changes: 2 additions & 0 deletions src/Components/ReaderSideListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const ReaderSideListItem = memo(
const items = [
window.contextMenu.template.open(link),
window.contextMenu.template.openInNewWindow(link),
window.contextMenu.template.divider(),
];
if (inHistory[1] >= 0) {
items.push(window.contextMenu.template.unreadChapter(...inHistory));
Expand All @@ -69,6 +70,7 @@ const ReaderSideListItem = memo(
items.push(window.contextMenu.template.readAllChapter(inHistory[0], chapters));
}
items.push(window.contextMenu.template.unreadAllChapter(inHistory[0]));
items.push(window.contextMenu.template.divider());
items.push(window.contextMenu.template.showInExplorer(link));
items.push(window.contextMenu.template.copyPath(link));
setContextMenuFocused(true);
Expand Down
17 changes: 16 additions & 1 deletion src/MainImports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export const settingValidatorData = {
baseDir: "",
customStylesheet: "",
locationListSortType: ["normal", "inverse"],
locationListSortBy: ["name", "date"],
/**
* Check for new update on start of app.
*/
Expand Down Expand Up @@ -336,6 +337,7 @@ declare global {
focusBackElem?: HTMLElement | null
) => MouseEvent;
template: {
divider: () => MenuListItem;
open: (url: string) => MenuListItem;
openInNewWindow: (url: string) => MenuListItem;
showInExplorer: (url: string) => MenuListItem;
Expand Down Expand Up @@ -637,13 +639,25 @@ declare global {
type MenuListItem = {
label: string;
action: () => void;
disabled: boolean;
disabled?: boolean;
/**
* checked or enabled
*/
selected?: boolean;
style?: React.CSSProperties;
/**
* if true ignore all data and show line
*/
divider?: boolean;
};
type IContextMenuData = {
clickX: number;
clickY: number;
focusBackElem?: EventTarget | null;
/**
* leave extra space on left side, useful when have "check" items in list
*/
padLeft?: boolean;
items: MenuListItem[];
};
type IOptSelectData = {
Expand Down Expand Up @@ -1250,6 +1264,7 @@ const defaultSettings: AppSettings = {
baseDir: window.electron.app.getPath("home"),
customStylesheet: "",
locationListSortType: "normal",
locationListSortBy: "name",
updateCheckerEnabled: true,
askBeforeClosing: false,
skipMinorUpdate: false,
Expand Down
19 changes: 18 additions & 1 deletion src/styles/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1187,7 +1187,7 @@ body {
&:focus {
outline: none;
}
opacity: 0;
opacity: 1;
pointer-events: none;
&:focus-within {
opacity: 1;
Expand All @@ -1201,6 +1201,14 @@ body {
cursor: default;
line-height: normal;
border-radius: 2px;
&.menu-divider {
height: 2px;
width: 100%;
// background-color: var(--btn-color-focus);
background-color: var(--divider-color);
margin: 2px 0;
padding: 0;
}
&[data-focused="true"] {
background: var(--btn-color-focus);
}
Expand All @@ -1215,6 +1223,15 @@ body {
//todo make them # and check
.contextMenu {
@include KeyboardList();

ul {
&.padLeft {
li {
display: grid;
grid-template-columns: 1.5em auto;
}
}
}
}
.itemList {
@include KeyboardList();
Expand Down

0 comments on commit 6c28e3b

Please sign in to comment.