diff --git a/package.json b/package.json index 7655d0b2..8ee361e4 100644 --- a/package.json +++ b/package.json @@ -156,7 +156,7 @@ "webpack-merge": "^5.9.0" }, "build": { - "buildVersion": "28", + "buildVersion": "29", "asar": true, "asarUnpack": "**\\*.{node,dll}", "files": [ diff --git a/src/main/util.tsx b/src/main/util.tsx index dc25a61f..5524a6bc 100644 --- a/src/main/util.tsx +++ b/src/main/util.tsx @@ -32,6 +32,14 @@ export interface InputProps { onKeyDown: (event: React.KeyboardEvent) => void; } +export interface PromptItem { + id: string, + todoObject: TodoObject, + headline: typeof i18n.t, + text: typeof i18n.t, + label: typeof i18n.t, + } + export function resolveHtmlPath(htmlFileName: string): string { try { if (process.env.NODE_ENV === 'development') { diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 4f3da418..ae1b93c8 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -14,9 +14,10 @@ import ToolBar from './ToolBar'; import ContextMenu from './ContextMenu'; import { I18nextProvider } from 'react-i18next'; import { i18n } from './LanguageSelector'; -import { Sorting, Headers, File, TodoObject, Attributes, Filters, TranslatedAttributes } from '../main/util'; +import { Sorting, Headers, File, TodoObject, Attributes, Filters, TranslatedAttributes, ContextMenuItem } from '../main/util'; import Settings from './Settings'; import { translatedAttributes } from './Shared'; +import Prompt from './Prompt'; import './App.scss'; const { ipcRenderer, store } = window.api; @@ -43,9 +44,10 @@ const App = () => { const [showFileTabs, setShowFileTabs] = useState(store.get('showFileTabs')); const [isSettingsOpen, setIsSettingsOpen] = useState(false); const [contextMenuPosition, setContextMenuPosition] = useState(null); - const [contextMenuItems, setContextMenuItems] = useState([]); + const [contextMenuItems, setContextMenuItems] = useState([]); const [attributeMapping, setAttributeMapping] = useState(translatedAttributes(i18n.t) || {}); const [textFieldValue, setTextFieldValue] = useState(null); + const [promptItem, setPromptItem] = useState(null); const searchFieldRef = useRef(null); @@ -95,6 +97,24 @@ const App = () => { if(typeof filePath === 'string') ipcRenderer.send('droppedFile', filePath); }; + const handlePromptConfirm = (item: ContextMenuItem) => { + const { id, todoObject, index } = item; + switch (id) { + case 'delete': + ipcRenderer.send('removeLineFromFile', todoObject?.id); + break; + case 'removeFile': + ipcRenderer.send('removeFile', index); + break; + default: + setContextMenuItems(null); + } + }; + + const handlePromptClose = () => { + setPromptItem(null); + }; + const handleDragOver = (event: Event) => { event.preventDefault(); }; @@ -148,6 +168,10 @@ const App = () => { store.set('isSearchOpen', isSearchOpen) }, [isSearchOpen]); + useEffect(() => { + setContextMenuItems(null); + }, [promptItem]); + useEffect(() => { store.set('', isNavigationOpen) }, [isNavigationOpen]); @@ -255,6 +279,7 @@ const App = () => { contextMenuItems={contextMenuItems} setContextMenuItems={setContextMenuItems} setTextFieldValue={setTextFieldValue} + setPromptItem={setPromptItem} /> { zoom={zoom} setZoom={setZoom} /> - + {contextMenuItems && ( + + )} { setSnackBarSeverity={setSnackBarSeverity} setSnackBarContent={setSnackBarContent} /> + {promptItem && ( + handlePromptConfirm(promptItem)} + headline={promptItem.headline || ''} + text={promptItem.text || ''} + buttonText={promptItem.label} + /> + )} ); diff --git a/src/renderer/ContextMenu.tsx b/src/renderer/ContextMenu.tsx index cc3b7873..f035f1b4 100644 --- a/src/renderer/ContextMenu.tsx +++ b/src/renderer/ContextMenu.tsx @@ -1,8 +1,7 @@ import React, { useState, useEffect } from 'react'; import { Menu, MenuItem, Button, Tooltip } from '@mui/material'; import FileOpenIcon from '@mui/icons-material/FileOpen'; -import Prompt from './Prompt'; -import { ContextMenuItem } from '../main/util'; +import { ContextMenuItem, PromptItem } from '../main/util'; interface Props { contextMenuPosition: { @@ -14,6 +13,7 @@ interface Props { setContextMenuItems: React.Dispatch>; setSnackBarSeverity: React.Dispatch>; setSnackBarContent: React.Dispatch>; + setPromptItem: React.Dispatch>; } const { ipcRenderer } = window.api; @@ -25,54 +25,27 @@ const ContextMenu: React.FC = ({ setContextMenuItems, setSnackBarSeverity, setSnackBarContent, + setPromptItem, }) => { - const [promptItem, setPromptItem] = useState(null); - - const handleCloseContextMenu = () => { - setContextMenuPosition(null); - setContextMenuItems([]); - }; - - const handlePromptClose = () => { - setPromptItem(null); - handleCloseContextMenu(); - }; - const handleContextMenuClick = (item: ContextMenuItem) => { const { id, todoObject, index} = item; - switch (id) { case 'delete': setPromptItem(item); break; case 'copy': - handleCloseContextMenu(); + setContextMenuItems(null); ipcRenderer.send('saveToClipboard', todoObject?.string); break; case 'removeFile': setPromptItem(item); break; case 'revealFile': - handleCloseContextMenu(); + setContextMenuItems(null); ipcRenderer.send('revealFile', index); break; default: - handleCloseContextMenu(); - } - }; - - const handlePromptConfirm = (item: ContextMenuItem) => { - const { id, todoObject, index } = item; - - switch (id) { - case 'delete': - ipcRenderer.send('removeLineFromFile', todoObject?.id); - break; - case 'removeFile': - ipcRenderer.send('removeFile', index); - break; - default: - handleCloseContextMenu(); + setContextMenuItems(null); } }; @@ -97,11 +70,11 @@ const ContextMenu: React.FC = ({ <> setContextMenuItems(null)} anchorReference="anchorPosition" anchorPosition={contextMenuPosition || undefined} > - {contextMenuItems.map((item) => ( + {contextMenuItems && contextMenuItems.map((item) => ( handleContextMenuClick(item)}> {item.id === 'changeDoneFilePath' ? ( @@ -115,16 +88,6 @@ const ContextMenu: React.FC = ({ ))} - {promptItem && ( - handlePromptConfirm(promptItem)} - headline={promptItem.headline || ''} - text={promptItem.text || ''} - buttonText={promptItem.label} - /> - )} ); }; diff --git a/src/renderer/DataGrid/Grid.tsx b/src/renderer/DataGrid/Grid.tsx index 87362bc5..8e8c9222 100644 --- a/src/renderer/DataGrid/Grid.tsx +++ b/src/renderer/DataGrid/Grid.tsx @@ -1,7 +1,7 @@ import React, { useState, KeyboardEvent } from 'react'; import { List } from '@mui/material'; import Row from './Row'; -import { TodoObject, Attributes, Filters, ContextMenuItem } from '../../main/util'; +import { TodoObject, Attributes, Filters, ContextMenuItem, PromptItem } from '../../main/util'; import './Grid.scss'; interface TodoDataGridProps { @@ -15,6 +15,7 @@ interface TodoDataGridProps { contextMenuItems: ContextMenuItem[]; setContextMenuItems: (items: any[]) => void; setTodoObject: (todoObject: TodoObject) => void; + setPromptItem: PromptItem; } const TodoDataGrid: React.FC = ({ @@ -28,6 +29,7 @@ const TodoDataGrid: React.FC = ({ contextMenuItems, setContextMenuItems, setTodoObject, + setPromptItem, }) => { const [visibleRowCount, setVisibleRowCount] = useState(50); const [loadMoreRows, setLoadMoreRows] = useState(true); @@ -99,6 +101,7 @@ const TodoDataGrid: React.FC = ({ setContextMenuPosition={setContextMenuPosition} contextMenuItems={contextMenuItems} setContextMenuItems={setContextMenuItems} + setPromptItem={setPromptItem} /> ))} diff --git a/src/renderer/DataGrid/Row.tsx b/src/renderer/DataGrid/Row.tsx index 84b7ea91..4ddffd5e 100644 --- a/src/renderer/DataGrid/Row.tsx +++ b/src/renderer/DataGrid/Row.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import { Checkbox, ListItem } from '@mui/material'; import CircleChecked from '@mui/icons-material/CheckCircle'; import CircleUnchecked from '@mui/icons-material/RadioButtonUnchecked'; @@ -6,6 +6,7 @@ import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import { withTranslation, WithTranslation } from 'react-i18next'; import Group from './Group'; import Elements from './Elements'; +import { PromptItem, TodoObject, Filters } from '../../main/util'; import { handleFilterSelect } from '../Shared'; import './Row.scss'; import { i18n } from '../LanguageSelector'; @@ -13,13 +14,14 @@ import { i18n } from '../LanguageSelector'; const { ipcRenderer } = window.api; interface Props extends WithTranslation { - row: any; - filters: any; + row: TodoObject; + filters: Filters; setDialogOpen: React.Dispatch>; setTextFieldValue: React.Dispatch>; setTodoObject: React.Dispatch>; setContextMenuPosition: React.Dispatch>; setContextMenuItems: React.Dispatch>; + setPromptItem: PromptItem; t: typeof i18n.t; } @@ -31,8 +33,17 @@ const Row: React.FC = ({ setTodoObject, setContextMenuPosition, setContextMenuItems, + setPromptItem, t, }) => { + const itemDelete = { + id: 'delete', + todoObject: row, + headline: t('prompt.delete.headline'), + text: t('prompt.delete.text'), + label: t('delete'), + } + const handleContextMenu = (event: React.MouseEvent) => { event.preventDefault(); setContextMenuPosition({ top: event.clientY, left: event.clientX }); @@ -40,15 +51,9 @@ const Row: React.FC = ({ { id: 'copy', todoObject: row, - label: t(`copy`), - }, - { - id: 'delete', - todoObject: row, - headline: t(`prompt.delete.headline`), - text: t(`prompt.delete.text`), - label: t(`delete`), + label: t('copy'), }, + itemDelete, ]); }; @@ -76,12 +81,14 @@ const Row: React.FC = ({ clickedElement.tagName === 'SPAN' || clickedElement.tagName === 'LI' ) { - if(row) { + if (row) { setTodoObject(row); setDialogOpen(true); } setTextFieldValue(row.string); } + } else if ((event.metaKey || event.ctrlKey) && (event.key === 'Delete' || event.key === 'Backspace')) { + setPromptItem(itemDelete); } }; @@ -101,35 +108,37 @@ const Row: React.FC = ({ } return ( - - } - checkedIcon={} + <> + + key={row.id} + className="row" + data-complete={row.complete} + data-hidden={row.hidden} + onClick={handleRowClick} + onKeyDown={handleRowClick} + onContextMenu={handleContextMenu} + data-todotxt-attribute="priority" + data-todotxt-value={row.priority} + > + } + checkedIcon={} + tabIndex={0} + checked={row.complete} + onChange={handleCheckboxChange} + /> - {row.hidden && } + {row.hidden && } - - + + + ); }; -export default withTranslation()(Row); +export default withTranslation()(Row); \ No newline at end of file