Compare commits

...

4 Commits

Author SHA1 Message Date
Matiss Janis Aboltins
b2de31993f Correct category name from 'Reports' to 'Themes' 2026-03-21 22:19:17 +00:00
Matiss Janis Aboltins
9da16e3137 Fix typo in Custom Reports description 2026-03-21 21:08:32 +00:00
Matiss Janis Aboltins
edc406e3c4 Refactor ColorPalette and ThemeInstaller components for improved layout and responsiveness 2026-03-21 20:44:35 +00:00
Matiss Janis Aboltins
c857fc9d0f [AI] Improve theme catalog responsive layout in ThemeInstaller 2026-03-21 20:30:21 +00:00
3 changed files with 34 additions and 25 deletions

View File

@@ -17,7 +17,8 @@ export function ColorPalette({ colors }: ColorPaletteProps) {
gridTemplateColumns: 'repeat(3, 1fr)', gridTemplateColumns: 'repeat(3, 1fr)',
gridTemplateRows: 'repeat(2, 1fr)', gridTemplateRows: 'repeat(2, 1fr)',
width: '100%', width: '100%',
height: 60, flex: 1,
minHeight: 0,
borderRadius: 4, borderRadius: 4,
overflow: 'hidden', overflow: 'hidden',
}} }}

View File

@@ -8,6 +8,7 @@ import { AnimatedLoading } from '@actual-app/components/icons/AnimatedLoading';
import { baseInputStyle } from '@actual-app/components/input'; import { baseInputStyle } from '@actual-app/components/input';
import { SpaceBetween } from '@actual-app/components/space-between'; import { SpaceBetween } from '@actual-app/components/space-between';
import { Text } from '@actual-app/components/text'; import { Text } from '@actual-app/components/text';
import { TextOneLine } from '@actual-app/components/text-one-line';
import { theme as themeStyle } from '@actual-app/components/theme'; import { theme as themeStyle } from '@actual-app/components/theme';
import { View } from '@actual-app/components/view'; import { View } from '@actual-app/components/view';
@@ -28,10 +29,10 @@ import type {
InstalledTheme, InstalledTheme,
} from '@desktop-client/style/customThemes'; } from '@desktop-client/style/customThemes';
// Theme item fixed dimensions // Theme item dimensions
const THEME_ITEM_HEIGHT = 140; const ITEMS_PER_ROW = 3;
const THEME_ITEM_WIDTH = 140;
const THEME_ITEM_GAP = 12; const THEME_ITEM_GAP = 12;
const THEME_ITEM_PADDING = 4; // horizontal padding on each side
const CATALOG_MAX_HEIGHT = 300; const CATALOG_MAX_HEIGHT = 300;
type ThemeInstallerProps = { type ThemeInstallerProps = {
@@ -78,15 +79,11 @@ export function ThemeInstaller({
} }
}, [installedTheme]); }, [installedTheme]);
// Calculate items per row based on container width // Calculate theme item width based on container width (always 3 per row)
const getItemsPerRow = useCallback((containerWidth: number) => { const getItemWidth = useCallback((containerWidth: number) => {
const padding = 8; // 4px on each side const availableWidth = containerWidth - THEME_ITEM_PADDING * 2;
const availableWidth = containerWidth - padding; return Math.floor(
return Math.max( (availableWidth - (ITEMS_PER_ROW - 1) * THEME_ITEM_GAP) / ITEMS_PER_ROW,
1,
Math.floor(
(availableWidth + THEME_ITEM_GAP) / (THEME_ITEM_WIDTH + THEME_ITEM_GAP),
),
); );
}, []); }, []);
@@ -292,10 +289,10 @@ export function ThemeInstaller({
const catalogItems = [...(catalog ?? [])] const catalogItems = [...(catalog ?? [])]
.filter(catalogTheme => !mode || catalogTheme.mode === mode) .filter(catalogTheme => !mode || catalogTheme.mode === mode)
.sort((a, b) => a.name.localeCompare(b.name)); .sort((a, b) => a.name.localeCompare(b.name));
const itemsPerRow = getItemsPerRow(width); const itemWidth = getItemWidth(width);
const rows: CatalogTheme[][] = []; const rows: CatalogTheme[][] = [];
for (let i = 0; i < catalogItems.length; i += itemsPerRow) { for (let i = 0; i < catalogItems.length; i += ITEMS_PER_ROW) {
rows.push(catalogItems.slice(i, i + itemsPerRow)); rows.push(catalogItems.slice(i, i + ITEMS_PER_ROW));
} }
return ( return (
@@ -303,17 +300,18 @@ export function ThemeInstaller({
width={width} width={width}
height={height} height={height}
itemCount={rows.length} itemCount={rows.length}
itemSize={THEME_ITEM_HEIGHT + THEME_ITEM_GAP} itemSize={itemWidth + THEME_ITEM_GAP}
itemKey={index => `row-${index}`} itemKey={index => `row-${index}`}
renderRow={({ index, style }) => { renderRow={({ index, key, style }) => {
const rowThemes = rows[index]; const rowThemes = rows[index];
return ( return (
<div <div
key={key}
style={{ style={{
...style, ...style,
display: 'flex', display: 'flex',
gap: THEME_ITEM_GAP, gap: THEME_ITEM_GAP,
padding: '0 4px', padding: `0 ${THEME_ITEM_PADDING}px ${THEME_ITEM_GAP}px`,
}} }}
> >
{rowThemes.map((theme, themeIndex) => { {rowThemes.map((theme, themeIndex) => {
@@ -334,9 +332,10 @@ export function ThemeInstaller({
aria-label={theme.name} aria-label={theme.name}
onPress={() => handleCatalogThemeClick(theme)} onPress={() => handleCatalogThemeClick(theme)}
style={{ style={{
width: THEME_ITEM_WIDTH, width: itemWidth,
height: THEME_ITEM_HEIGHT, height: itemWidth,
padding: 8, padding: 8,
overflow: 'hidden',
borderRadius: 6, borderRadius: 6,
border: `2px solid ${ border: `2px solid ${
hasError hasError
@@ -386,23 +385,26 @@ export function ThemeInstaller({
/> />
</View> </View>
<ColorPalette colors={theme.colors} /> <ColorPalette colors={theme.colors} />
<Text <TextOneLine
style={{ style={{
fontSize: 12, fontSize: 12,
fontWeight: 500, fontWeight: 500,
textAlign: 'center', textAlign: 'center',
width: '100%',
}} }}
title={theme.name}
> >
{theme.name} {theme.name}
</Text> </TextOneLine>
<SpaceBetween <SpaceBetween
direction="horizontal" direction="horizontal"
align="center" align="center"
wrap={false}
gap={4} gap={4}
style={{ fontSize: 10 }} style={{ fontSize: 10 }}
> >
<Text <TextOneLine
style={{ style={{
color: themeStyle.pageTextSubdued, color: themeStyle.pageTextSubdued,
}} }}
@@ -411,7 +413,7 @@ export function ThemeInstaller({
<Text style={{ fontWeight: 'bold' }}> <Text style={{ fontWeight: 'bold' }}>
{extractRepoOwner(theme.repo)} {extractRepoOwner(theme.repo)}
</Text> </Text>
</Text> </TextOneLine>
<Link <Link
variant="external" variant="external"
to={normalizeGitHubRepo(theme.repo)} to={normalizeGitHubRepo(theme.repo)}

View File

@@ -0,0 +1,6 @@
---
category: Bugfixes
authors: [MatissJanis]
---
Custom Themes: improved responsiveness of the theme catalog