diff --git a/packages/desktop-client/src/components/common/Menu.tsx b/packages/desktop-client/src/components/common/Menu.tsx index 1d0258cd77..eb5efa66c6 100644 --- a/packages/desktop-client/src/components/common/Menu.tsx +++ b/packages/desktop-client/src/components/common/Menu.tsx @@ -33,19 +33,19 @@ type MenuItem = { key?: string; }; -type MenuProps = { +type MenuProps = { header?: ReactNode; footer?: ReactNode; - items: Array; - onMenuSelect: (itemName: MenuItem['name']) => void; + items: Array; + onMenuSelect: (itemName: T['name']) => void; }; -export default function Menu({ +export default function Menu({ header, footer, items: allItems, onMenuSelect, -}: MenuProps) { +}: MenuProps) { const elRef = useRef(null); const items = allItems.filter(x => x); const [hoveredIndex, setHoveredIndex] = useState(null); diff --git a/packages/desktop-client/src/components/payees/PayeeTable.tsx b/packages/desktop-client/src/components/payees/PayeeTable.tsx index b376752ae3..c7504d76cf 100644 --- a/packages/desktop-client/src/components/payees/PayeeTable.tsx +++ b/packages/desktop-client/src/components/payees/PayeeTable.tsx @@ -1,10 +1,8 @@ import { - forwardRef, useCallback, useLayoutEffect, useState, type ComponentProps, - type ComponentRef, } from 'react'; import { type PayeeEntity } from 'loot-core/src/types/models'; @@ -20,6 +18,7 @@ import PayeeTableRow from './PayeeTableRow'; type PayeeWithId = PayeeEntity & Required>; type PayeeTableProps = { + tableRef: ComponentProps>['tableRef']; payees: PayeeWithId[]; ruleCounts: Map; navigator: TableNavigator; @@ -28,56 +27,56 @@ type PayeeTableProps = { 'onUpdate' | 'onViewRules' | 'onCreateRule' >; -const PayeeTable = forwardRef< - ComponentRef>, - PayeeTableProps ->( - ( - { payees, ruleCounts, navigator, onUpdate, onViewRules, onCreateRule }, - ref, - ) => { - const [hovered, setHovered] = useState(null); - const selectedItems = useSelectedItems(); +const PayeeTable = ({ + tableRef, + payees, + ruleCounts, + navigator, + onUpdate, + onViewRules, + onCreateRule, +}: PayeeTableProps) => { + const [hovered, setHovered] = useState(null); + const selectedItems = useSelectedItems(); - useLayoutEffect(() => { - const firstSelected = [...selectedItems][0] as string; - if (typeof ref !== 'function') { - ref.current.scrollTo(firstSelected, 'center'); - } - navigator.onEdit(firstSelected, 'select'); - }, []); + useLayoutEffect(() => { + const firstSelected = [...selectedItems][0] as string; + if (typeof tableRef !== 'function') { + tableRef.current.scrollTo(firstSelected, 'center'); + } + navigator.onEdit(firstSelected, 'select'); + }, []); - const onHover = useCallback(id => { - setHovered(id); - }, []); + const onHover = useCallback((id: string) => { + setHovered(id); + }, []); - return ( - setHovered(null)}> - - ref={ref} - items={payees} - navigator={navigator} - renderItem={({ item, editing, focusedField, onEdit }) => { - return ( - - ); - }} - /> - - ); - }, -); + return ( + setHovered(null)}> + { + return ( + + ); + }} + /> + + ); +}; export default PayeeTable; diff --git a/packages/desktop-client/src/components/schedules/SchedulesTable.tsx b/packages/desktop-client/src/components/schedules/SchedulesTable.tsx index 149cb6fd18..e7c2574936 100644 --- a/packages/desktop-client/src/components/schedules/SchedulesTable.tsx +++ b/packages/desktop-client/src/components/schedules/SchedulesTable.tsx @@ -118,7 +118,7 @@ function OverflowMenu({ onClose={() => setOpen(false)} > { + onMenuSelect={name => { onAction(name, schedule.id); setOpen(false); }} diff --git a/packages/desktop-client/src/components/table.tsx b/packages/desktop-client/src/components/table.tsx index 92e2e833a4..ddf274b1b5 100644 --- a/packages/desktop-client/src/components/table.tsx +++ b/packages/desktop-client/src/components/table.tsx @@ -11,7 +11,6 @@ import React, { type ReactNode, type KeyboardEvent, type UIEvent, - type ReactElement, type Ref, } from 'react'; import { useStore } from 'react-redux'; @@ -847,17 +846,18 @@ type TableHandleRef = { type TableWithNavigatorProps = TableProps & { fields; }; -export const TableWithNavigator = forwardRef< - TableHandleRef, - TableWithNavigatorProps ->(({ fields, ...props }, ref) => { +export const TableWithNavigator = ({ + fields, + ...props +}: TableWithNavigatorProps) => { const navigator = useTableNavigator(props.items, fields); return
; -}); +}; type TableItem = { id: number | string }; type TableProps = { + tableRef?: Ref>; items: T[]; count?: number; headers?: ReactNode | TableHeaderProps['headers']; @@ -886,282 +886,276 @@ type TableProps = { saveScrollWidth?: (parent, child) => void; }; -export const Table: ( - props: TableProps & { ref?: Ref> }, -) => ReactElement = forwardRef( - ( - { - items, - count, - headers, - contentHeader, - loading, - rowHeight = ROW_HEIGHT, - backgroundColor = theme.tableHeaderBackground, - renderItem, - renderEmpty, - getItemKey, - loadMore, - style, - navigator, - onScroll, - version = 'v1', - allowPopupsEscape, - isSelected, - saveScrollWidth, - ...props +export const Table = ({ + tableRef, + items, + count, + headers, + contentHeader, + loading, + rowHeight = ROW_HEIGHT, + backgroundColor = theme.tableHeaderBackground, + renderItem, + renderEmpty, + getItemKey, + loadMore, + style, + navigator, + onScroll, + version = 'v1', + allowPopupsEscape, + isSelected, + saveScrollWidth, + ...props +}: TableProps) => { + if (!navigator) { + navigator = { + onEdit: () => {}, + editingId: null, + focusedField: null, + getNavigatorProps: props => props, + }; + } + + const { onEdit, editingId, focusedField, getNavigatorProps } = navigator; + const list = useRef(null); + const listContainer = useRef(null); + const scrollContainer = useRef(null); + const initialScrollTo = useRef(null); + const listInitialized = useRef(false); + + useImperativeHandle(tableRef, () => ({ + scrollTo: (id, alignment = 'smart') => { + const index = items.findIndex(item => item.id === id); + if (index !== -1) { + if (!list.current) { + // If the table hasn't been laid out yet, we need to wait for + // that to happen before we can scroll to something + initialScrollTo.current = index; + } else { + list.current.scrollToItem(index, alignment); + } + } }, - ref, - ) => { - if (!navigator) { - navigator = { - onEdit: () => {}, - editingId: null, - focusedField: null, - getNavigatorProps: props => props, - }; + + scrollToTop: () => { + list.current?.scrollTo(0); + }, + + getScrolledItem: () => { + if (scrollContainer.current) { + const offset = scrollContainer.current.scrollTop; + const index = list.current.getStartIndexForOffset(offset); + return items[index].id; + } + return 0; + }, + + setRowAnimation: flag => { + list.current?.setRowAnimation(flag); + }, + + edit(id, field, shouldScroll) { + onEdit(id, field); + + if (id && shouldScroll) { + // @ts-expect-error this should not be possible + ref.scrollTo(id); + } + }, + + anchor() { + list.current?.anchor(); + }, + + unanchor() { + list.current?.unanchor(); + }, + + isAnchored() { + return list.current && list.current.isAnchored(); + }, + })); + + useLayoutEffect(() => { + // We wait for the list to mount because AutoSizer needs to run + // before it's mounted + if (!listInitialized.current && listContainer.current) { + // Animation is on by default + list.current?.setRowAnimation(true); + listInitialized.current = true; } - const { onEdit, editingId, focusedField, getNavigatorProps } = navigator; - const list = useRef(null); - const listContainer = useRef(null); - const scrollContainer = useRef(null); - const initialScrollTo = useRef(null); - const listInitialized = useRef(false); + if (scrollContainer.current && saveScrollWidth) { + saveScrollWidth( + scrollContainer.current.offsetParent + ? scrollContainer.current.offsetParent.clientWidth + : 0, + scrollContainer.current ? scrollContainer.current.clientWidth : 0, + ); + } + }); - useImperativeHandle(ref, () => ({ - scrollTo: (id, alignment = 'smart') => { - const index = items.findIndex(item => item.id === id); - if (index !== -1) { - if (!list.current) { - // If the table hasn't been laid out yet, we need to wait for - // that to happen before we can scroll to something - initialScrollTo.current = index; - } else { - list.current.scrollToItem(index, alignment); - } - } - }, + function renderRow({ index, style, key }) { + const item = items[index]; + const editing = editingId === item.id; + const selected = isSelected && isSelected(item.id); - scrollToTop: () => { - list.current?.scrollTo(0); - }, - - getScrolledItem: () => { - if (scrollContainer.current) { - const offset = scrollContainer.current.scrollTop; - const index = list.current.getStartIndexForOffset(offset); - return items[index].id; - } - return 0; - }, - - setRowAnimation: flag => { - list.current?.setRowAnimation(flag); - }, - - edit(id, field, shouldScroll) { - onEdit(id, field); - - if (id && shouldScroll) { - // @ts-expect-error this should not be possible - ref.scrollTo(id); - } - }, - - anchor() { - list.current?.anchor(); - }, - - unanchor() { - list.current?.unanchor(); - }, - - isAnchored() { - return list.current && list.current.isAnchored(); - }, - })); - - useLayoutEffect(() => { - // We wait for the list to mount because AutoSizer needs to run - // before it's mounted - if (!listInitialized.current && listContainer.current) { - // Animation is on by default - list.current?.setRowAnimation(true); - listInitialized.current = true; - } - - if (scrollContainer.current && saveScrollWidth) { - saveScrollWidth( - scrollContainer.current.offsetParent - ? scrollContainer.current.offsetParent.clientWidth - : 0, - scrollContainer.current ? scrollContainer.current.clientWidth : 0, - ); - } + const row = renderItem({ + item, + editing, + focusedField: editing && focusedField, + onEdit, + index, + position: style.top, }); - function renderRow({ index, style, key }) { - const item = items[index]; - const editing = editingId === item.id; - const selected = isSelected && isSelected(item.id); + // TODO: Need to also apply zIndex if item is selected + // * Port over ListAnimation to Table + // * Move highlighted functionality into here + return ( + + {row} + + ); + } - const row = renderItem({ - item, - editing, - focusedField: editing && focusedField, - onEdit, - index, - position: style.top, - }); + function getScrollOffset(height, index) { + return ( + index * (rowHeight - 1) + + (rowHeight - 1) / 2 - + height / 2 + + (rowHeight - 1) * 2 + ); + } - // TODO: Need to also apply zIndex if item is selected - // * Port over ListAnimation to Table - // * Move highlighted functionality into here - return ( - - {row} - - ); + function onItemsRendered({ overscanStartIndex, overscanStopIndex }) { + if (loadMore && overscanStopIndex > items.length - 100) { + loadMore(); } + } - function getScrollOffset(height, index) { - return ( - index * (rowHeight - 1) + - (rowHeight - 1) / 2 - - height / 2 + - (rowHeight - 1) * 2 - ); + function getEmptyContent(empty) { + if (empty == null) { + return null; + } else if (typeof empty === 'function') { + return empty(); } - function onItemsRendered({ overscanStartIndex, overscanStopIndex }) { - if (loadMore && overscanStopIndex > items.length - 100) { - loadMore(); - } - } - - function getEmptyContent(empty) { - if (empty == null) { - return null; - } else if (typeof empty === 'function') { - return empty(); - } - - return ( - - {empty} - - ); - } - - if (loading) { - return ( - - - - ); - } - - const isEmpty = (count || items.length) === 0; - return ( - {headers && ( - - )} - - {isEmpty ? ( - getEmptyContent(renderEmpty) - ) : ( - - {({ width, height }) => { - if (width === 0 || height === 0) { - return null; - } - - return ( - - - items[index].id) - } - indexForKey={key => - items.findIndex(item => item.id === key) - } - initialScrollOffset={ - initialScrollTo.current - ? getScrollOffset(height, initialScrollTo.current) - : 0 - } - overscanCount={5} - onItemsRendered={onItemsRendered} - onScroll={onScroll} - /> - - - ); - }} - - )} - + {empty} ); - }, -); + } + + if (loading) { + return ( + + + + ); + } + + const isEmpty = (count || items.length) === 0; + + return ( + + {headers && ( + + )} + + {isEmpty ? ( + getEmptyContent(renderEmpty) + ) : ( + + {({ width, height }) => { + if (width === 0 || height === 0) { + return null; + } + + return ( + + + items[index].id) + } + indexForKey={key => + items.findIndex(item => item.id === key) + } + initialScrollOffset={ + initialScrollTo.current + ? getScrollOffset(height, initialScrollTo.current) + : 0 + } + overscanCount={5} + onItemsRendered={onItemsRendered} + onScroll={onScroll} + /> + + + ); + }} + + )} + + + ); +}; export type TableNavigator = { onEdit: (id: T['id'], field?: string) => void; diff --git a/packages/desktop-client/src/components/transactions/TransactionsTable.jsx b/packages/desktop-client/src/components/transactions/TransactionsTable.jsx index a1009c1aac..5c04a9289e 100644 --- a/packages/desktop-client/src/components/transactions/TransactionsTable.jsx +++ b/packages/desktop-client/src/components/transactions/TransactionsTable.jsx @@ -1722,7 +1722,7 @@ function TransactionTableInner({ >
>({ value, timestamp: jsc.integer(1000, 10000).smap( x => { - let clientId; + let clientId: string; switch (jsc.random(0, 1)) { case 0: clientId = clientId1; diff --git a/upcoming-release-notes/2071.md b/upcoming-release-notes/2071.md new file mode 100644 index 0000000000..e443cc99ad --- /dev/null +++ b/upcoming-release-notes/2071.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [MatissJanis] +--- + +Fixing TypeScript issues when enabling `strictFunctionTypes` (pt.4).