feat: consistent table styling (#6697)

* feat: consistent table styling

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #6697

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #6697

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #6697

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Alexis Vielma
2026-01-22 12:43:39 -08:00
committed by GitHub
parent 9a3415adab
commit 4986e433a5
62 changed files with 52 additions and 46 deletions

View File

@@ -162,4 +162,11 @@ export const styles: Record<string, any> = {
padding: 16,
cursor: 'pointer',
},
tableContainer: {
flex: 1,
border: '1px solid ' + theme.tableBorder,
borderTopLeftRadius: 6,
borderTopRightRadius: 6,
overflow: 'hidden',
},
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 KiB

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 81 KiB

View File

@@ -11,6 +11,7 @@ import { Trans, useTranslation } from 'react-i18next';
import { Button } from '@actual-app/components/button';
import { SpaceBetween } from '@actual-app/components/space-between';
import { styles } from '@actual-app/components/styles';
import { Text } from '@actual-app/components/text';
import { theme } from '@actual-app/components/theme';
import { View } from '@actual-app/components/view';
@@ -342,7 +343,7 @@ export function ManageRules({
onChange={onSearchChange}
/>
</View>
<View style={{ flex: 1 }}>
<View style={styles.tableContainer}>
<RulesHeader />
<InfiniteScrollWrapper loadMore={loadMore}>
{filteredRules.length === 0 ? (

View File

@@ -11,6 +11,7 @@ import { Trans, useTranslation } from 'react-i18next';
import { Button } from '@actual-app/components/button';
import { SvgLockOpen } from '@actual-app/components/icons/v1';
import { SvgLockClosed } from '@actual-app/components/icons/v2';
import { styles } from '@actual-app/components/styles';
import { Text } from '@actual-app/components/text';
import { theme } from '@actual-app/components/theme';
import { View } from '@actual-app/components/view';
@@ -168,7 +169,7 @@ function UserAccessContent({ isModal }: ManageUserAccessContentProps) {
onChange={onSearchChange}
/>
</View>
<View style={{ flex: 1 }}>
<View style={styles.tableContainer}>
<UserAccessHeader />
<InfiniteScrollWrapper loadMore={loadMore}>
<UserAccessList

View File

@@ -10,6 +10,7 @@ import { Trans, useTranslation } from 'react-i18next';
import { Button } from '@actual-app/components/button';
import { SpaceBetween } from '@actual-app/components/space-between';
import { styles } from '@actual-app/components/styles';
import { Text } from '@actual-app/components/text';
import { theme } from '@actual-app/components/theme';
import { View } from '@actual-app/components/view';
@@ -279,7 +280,7 @@ function UserDirectoryContent({ isModal }: ManageUserDirectoryContentProps) {
/>
</View>
<View style={{ flex: 1 }}>
<View style={styles.tableContainer}>
<UserDirectoryHeader />
<InfiniteScrollWrapper loadMore={loadMore}>
{filteredUsers.length === 0 ? (

View File

@@ -31,6 +31,7 @@ export function AccountsList({
<View
style={{
minHeight: 'initial',
marginBottom: -1,
}}
>
{accounts.map(account => {

View File

@@ -4,6 +4,7 @@ import { Trans, useTranslation } from 'react-i18next';
import { SvgRightArrow2 } from '@actual-app/components/icons/v0';
import { SvgEquals } from '@actual-app/components/icons/v1';
import { Select } from '@actual-app/components/select';
import { styles } from '@actual-app/components/styles';
import { Text } from '@actual-app/components/text';
import { theme } from '@actual-app/components/theme';
import { View } from '@actual-app/components/view';
@@ -103,7 +104,7 @@ export function FieldMapping({
</Trans>
</Text>
) : (
<>
<View style={styles.tableContainer}>
<TableHeader>
<Cell
width={calculatedActualFieldWidth}
@@ -136,11 +137,11 @@ export function FieldMapping({
key={field.actualField}
style={{
fontSize: 13,
backgroundColor: theme.tableRowBackgroundHover,
backgroundColor: theme.tableBackground,
display: 'flex',
alignItems: 'center',
border: '1px solid var(--color-tableBorder)',
minHeight: '40px',
borderTop: '1px solid ' + theme.tableBorder,
}}
collapsed
>
@@ -225,7 +226,7 @@ export function FieldMapping({
</Row>
);
})}
</>
</View>
)}
</>
);

View File

@@ -2,6 +2,7 @@ import { useCallback, useMemo, useState } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { useResponsive } from '@actual-app/components/hooks/useResponsive';
import { styles } from '@actual-app/components/styles';
import { Text } from '@actual-app/components/text';
import { View } from '@actual-app/components/view';
@@ -128,7 +129,7 @@ export function BankSync() {
)}
{Object.entries(groupedAccounts).map(([syncProvider, accounts]) => {
return (
<View key={syncProvider} style={{ minHeight: 'initial' }}>
<View key={syncProvider} style={styles.tableContainer}>
{Object.keys(groupedAccounts).length > 1 && (
<Text
style={{ fontWeight: 500, fontSize: 20, margin: '.5em 0' }}

View File

@@ -11,6 +11,7 @@ import { Button, ButtonWithLoading } from '@actual-app/components/button';
import { Input } from '@actual-app/components/input';
import { Select } from '@actual-app/components/select';
import { SpaceBetween } from '@actual-app/components/space-between';
import { styles } from '@actual-app/components/styles';
import { Text } from '@actual-app/components/text';
import { theme } from '@actual-app/components/theme';
import { View } from '@actual-app/components/view';
@@ -828,11 +829,7 @@ export function ImportTransactionsModal({
)}
{(!error || !error.parsed) && (
<View
style={{
flex: 'unset',
height: 300,
border: '1px solid ' + theme.tableBorder,
}}
style={{ ...styles.tableContainer, height: 300, flex: 'unset' }}
>
<TableHeader headers={headers} />

View File

@@ -4,6 +4,7 @@ import { Trans, useTranslation } from 'react-i18next';
import { Button } from '@actual-app/components/button';
import { useResponsive } from '@actual-app/components/hooks/useResponsive';
import { SpaceBetween } from '@actual-app/components/space-between';
import { styles } from '@actual-app/components/styles';
import { Text } from '@actual-app/components/text';
import { theme } from '@actual-app/components/theme';
import { Tooltip } from '@actual-app/components/tooltip';
@@ -329,11 +330,7 @@ export function SelectLinkedAccountsModal({
</View>
) : (
<View
style={{
flex: 'unset',
height: 300,
border: '1px solid ' + theme.tableBorder,
}}
style={{ ...styles.tableContainer, height: 300, flex: 'unset' }}
>
<TableHeader>
<Cell value={t('Institution to Sync')} width={175} />

View File

@@ -10,6 +10,7 @@ import { Trans, useTranslation } from 'react-i18next';
import { Button } from '@actual-app/components/button';
import { SvgExpandArrow, SvgSubtract } from '@actual-app/components/icons/v0';
import { Popover } from '@actual-app/components/popover';
import { styles } from '@actual-app/components/styles';
import { theme } from '@actual-app/components/theme';
import { View } from '@actual-app/components/view';
import memoizeOne from 'memoize-one';
@@ -46,15 +47,7 @@ function PayeeTableHeader() {
return (
<View>
<TableHeader
style={{
backgroundColor: theme.tableBackground,
color: theme.pageTextLight,
zIndex: 200,
userSelect: 'none',
}}
collapsed
>
<TableHeader collapsed>
<SelectCell
exposed
focused={false}
@@ -288,15 +281,7 @@ export const ManagePayees = ({
</View>
<SelectedProvider instance={selected} fetchAllIds={getSelectableIds}>
<View
style={{
flex: 1,
border: '1px solid ' + theme.tableBorder,
borderTopLeftRadius: 4,
borderTopRightRadius: 4,
overflow: 'hidden',
}}
>
<View style={styles.tableContainer}>
<PayeeTableHeader />
{filteredPayees.length === 0 ? (
<View

View File

@@ -8,6 +8,7 @@ import {
type ComponentRef,
} from 'react';
import { theme } from '@actual-app/components/theme';
import { View } from '@actual-app/components/view';
import { type PayeeEntity } from 'loot-core/types/models';
@@ -64,6 +65,7 @@ export const PayeeTable = forwardRef<
navigator={tableNavigator}
ref={ref}
items={payees}
backgroundColor={theme.tableBackground}
renderItem={({ item, editing, focusedField, onEdit }) => {
return (
<PayeeTableRow

View File

@@ -5,6 +5,7 @@ import { Trans, useTranslation } from 'react-i18next';
import { ButtonWithLoading } from '@actual-app/components/button';
import { Paragraph } from '@actual-app/components/paragraph';
import { SpaceBetween } from '@actual-app/components/space-between';
import { styles } from '@actual-app/components/styles';
import { theme } from '@actual-app/components/theme';
import { View } from '@actual-app/components/view';
@@ -117,7 +118,7 @@ function DiscoverSchedulesTable({
}
return (
<View style={{ flex: 1 }}>
<View style={styles.tableContainer}>
<TableHeader height={ROW_HEIGHT} inset={15}>
<SelectCell
exposed={!loading}

View File

@@ -7,6 +7,7 @@ import { useResponsive } from '@actual-app/components/hooks/useResponsive';
import { InitialFocus } from '@actual-app/components/initial-focus';
import { Input } from '@actual-app/components/input';
import { SpaceBetween } from '@actual-app/components/space-between';
import { styles } from '@actual-app/components/styles';
import { Text } from '@actual-app/components/text';
import { theme } from '@actual-app/components/theme';
import { View } from '@actual-app/components/view';
@@ -470,9 +471,7 @@ export function ScheduleEditForm({
transactions={transactions}
fields={['date', 'payee', 'notes', 'amount']}
style={{
border: '1px solid ' + theme.tableBorder,
borderRadius: 4,
overflow: 'hidden',
...styles.tableContainer,
marginTop: 5,
maxHeight: 200,
}}

View File

@@ -7,6 +7,7 @@ import { SvgDotsHorizontalTriple } from '@actual-app/components/icons/v1';
import { SvgCheck } from '@actual-app/components/icons/v2';
import { Menu } from '@actual-app/components/menu';
import { Popover } from '@actual-app/components/popover';
import { styles } from '@actual-app/components/styles';
import { Text } from '@actual-app/components/text';
import { theme } from '@actual-app/components/theme';
import { View } from '@actual-app/components/view';
@@ -428,7 +429,7 @@ export function SchedulesTable({
}
return (
<View style={{ flex: 1, ...tableStyle }}>
<View style={{ ...styles.tableContainer, ...tableStyle }}>
<TableHeader height={ROW_HEIGHT} inset={15}>
<Field width="flex">
<Trans>Name</Trans>

View File

@@ -789,7 +789,6 @@ export function TableHeader({
return (
<View
style={{
borderRadius: '6px 6px 0 0',
overflow: 'hidden',
flexShrink: 0,
}}
@@ -956,7 +955,7 @@ export const Table = forwardRef(
contentHeader,
loading,
rowHeight = ROW_HEIGHT,
backgroundColor = theme.tableHeaderBackground,
backgroundColor = theme.tableBackground,
renderItem,
renderEmpty,
getItemKey,
@@ -1088,6 +1087,7 @@ export const Table = forwardRef(
...rowStyle,
zIndex: editing || selected ? 101 : 'auto',
transform: 'translateY(var(--pos))',
backgroundColor,
}}
nativeStyle={{ '--pos': `${style.top - 1}px` }}
data-focus-key={item.id}
@@ -1156,6 +1156,7 @@ export const Table = forwardRef(
style={{
flex: 1,
outline: 'none',
overflow: 'hidden',
...style,
}}
tabIndex={0}

View File

@@ -5,6 +5,7 @@ import { Button } from '@actual-app/components/button';
import { SvgAdd } from '@actual-app/components/icons/v1';
import { SvgSearchAlternate } from '@actual-app/components/icons/v2';
import { SpaceBetween } from '@actual-app/components/space-between';
import { styles } from '@actual-app/components/styles';
import { Text } from '@actual-app/components/text';
import { theme } from '@actual-app/components/theme';
import { View } from '@actual-app/components/view';
@@ -89,7 +90,7 @@ export function ManageTags() {
onChange={setFilter}
/>
</SpaceBetween>
<View style={{ flex: 1, marginTop: 12 }}>
<View style={{ marginTop: 12, ...styles.tableContainer }}>
<TagsHeader />
{create && (
<TagCreationRow onClose={() => setCreate(false)} tags={tags} />

View File

@@ -1,5 +1,7 @@
import React from 'react';
import { theme } from '@actual-app/components/theme';
import { type TagEntity } from 'loot-core/types/models';
import { TagRow } from './TagRow';
@@ -29,7 +31,7 @@ export function TagsList({
<Table
navigator={tableNavigator}
items={tags}
backgroundColor="none"
backgroundColor={theme.tableBackground}
renderItem={({ item: tag, focusedField, onEdit }) => {
const hovered = hoveredTag === tag.id;
const selected = selectedItems.has(tag.id);

View File

@@ -208,6 +208,7 @@ export function SimpleTransactionsTable({
return (
<Table
style={style}
backgroundColor={theme.tableBackground}
items={serializedTransactions}
renderEmpty={renderEmpty}
headers={

View File

@@ -184,7 +184,7 @@ const TransactionHeader = memo(
fontWeight: 300,
zIndex: 200,
color: theme.tableHeaderText,
backgroundColor: theme.tableBackground,
backgroundColor: theme.tableHeaderBackground,
paddingRight: `${5 + (scrollWidth ?? 0)}px`,
borderTopWidth: 1,
borderBottomWidth: 1,

View File

@@ -0,0 +1,6 @@
---
category: Enhancements
authors: [aelxxs]
---
Adds consistent styling (border, border radius, background colors) to tables