Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1346,6 +1346,9 @@
"trackNumber": "Track Number",
"year": "$t(common.year)"
},
"layout": {
"toggle": "Switch layout"
},
"view": {
"detail": "Detail",
"grid": "Grid",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { ItemTableListColumnConfig } from '/@/renderer/store';
import { TableColumn } from '/@/shared/types/types';

/**
* Flat, artwork-free column layout: index, title, artist, album, date added, duration.
*/
export const FLAT_COLUMN_ORDER: TableColumn[] = [
TableColumn.ROW_INDEX,
TableColumn.TITLE,
TableColumn.ARTIST,
TableColumn.ALBUM,
TableColumn.DATE_ADDED,
TableColumn.DURATION,
];

/**
* Enables and reorders the columns in `order`, keeping every other column in place but disabled.
* Widths, pins and alignment the user configured are preserved.
*/
export const applyColumnOrder = (
columns: ItemTableListColumnConfig[],
order: TableColumn[],
): ItemTableListColumnConfig[] => {
const byId = new Map(columns.map((column) => [column.id, column]));
const ordered = order
.map((id) => byId.get(id))
.filter((column): column is ItemTableListColumnConfig => column !== undefined)
.map((column) => ({ ...column, isEnabled: true }));

const orderedIds = new Set(ordered.map((column) => column.id));
const rest = columns
.filter((column) => !orderedIds.has(column.id))
.map((column) => ({ ...column, isEnabled: false }));

return [...ordered, ...rest];
};

/**
* True when the enabled columns are exactly `order` (minus any column this list doesn't have).
*/
export const isColumnOrderActive = (
columns: ItemTableListColumnConfig[],
order: TableColumn[],
): boolean => {
const availableIds = new Set(columns.map((column) => column.id));
const expected = order.filter((id) => availableIds.has(id));
const enabled = columns.filter((column) => column.isEnabled).map((column) => column.id);

return (
expected.length > 0 &&
enabled.length === expected.length &&
enabled.every((id, index) => id === expected[index])
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ const hasRequiredStateItemProperties = (
};

export enum TableItemSize {
COMPACT = 40,
COMPACT = 32,
DEFAULT = 64,
LARGE = 88,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
ListConfigMenu,
SONG_DISPLAY_TYPES,
} from '/@/renderer/features/shared/components/list-config-menu';
import { ListLayoutToggleButton } from '/@/renderer/features/shared/components/list-layout-toggle-button';
import {
CLIENT_SIDE_SONG_FILTERS,
ListSortByDropdownControlled,
Expand Down Expand Up @@ -895,6 +896,7 @@ const AlbumDetailSongsTable = ({ songs }: AlbumDetailSongsTableProps) => {
setSortOrder={(value) => setSortOrder(value as SortOrder)}
sortOrder={sortOrder}
/>
<ListLayoutToggleButton listKey={ItemListKey.ALBUM_DETAIL} />
<ListConfigMenu
displayTypes={[
{ hidden: true, value: ListDisplayType.GRID },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
ListConfigMenu,
SONG_DISPLAY_TYPES,
} from '/@/renderer/features/shared/components/list-config-menu';
import { ListLayoutToggleButton } from '/@/renderer/features/shared/components/list-layout-toggle-button';
import {
CLIENT_SIDE_ALBUM_FILTERS,
CLIENT_SIDE_SONG_FILTERS,
Expand Down Expand Up @@ -516,6 +517,7 @@ const AlbumArtistMetadataTopSongsContent = ({
size="xs"
value={topSongsQueryType}
/>
<ListLayoutToggleButton listKey={ItemListKey.SONG} />
<ListConfigMenu
displayTypes={[
{ hidden: true, value: ListDisplayType.GRID },
Expand Down Expand Up @@ -815,6 +817,7 @@ const AlbumArtistMetadataFavoriteSongs = ({
}
sortOrder={sortOrder}
/>
<ListLayoutToggleButton listKey={ItemListKey.SONG} />
<ListConfigMenu
displayTypes={[
{ hidden: true, value: ListDisplayType.GRID },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ListConfigMenu,
SONG_DISPLAY_TYPES,
} from '/@/renderer/features/shared/components/list-config-menu';
import { ListLayoutToggleButton } from '/@/renderer/features/shared/components/list-layout-toggle-button';
import { ListRefreshButton } from '/@/renderer/features/shared/components/list-refresh-button';
import { ListSortByDropdown } from '/@/renderer/features/shared/components/list-sort-by-dropdown';
import { ListSortOrderToggleButton } from '/@/renderer/features/shared/components/list-sort-order-toggle-button';
Expand Down Expand Up @@ -242,6 +243,7 @@ export const FolderListHeaderFilters = () => {
<ListRefreshButton listKey={ItemListKey.SONG} />
</Group>
<Group gap="sm" wrap="nowrap">
<ListLayoutToggleButton listKey={ItemListKey.SONG} />
<ListConfigMenu
displayTypes={[
{ hidden: true, value: ListDisplayType.GRID },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
ListConfigMenu,
SONG_DISPLAY_TYPES,
} from '/@/renderer/features/shared/components/list-config-menu';
import { ListLayoutToggleButton } from '/@/renderer/features/shared/components/list-layout-toggle-button';
import { SearchInput } from '/@/renderer/features/shared/components/search-input';
import { useCurrentServer, usePlayerStoreBase } from '/@/renderer/store';
import { hasFeature } from '/@/shared/api/utils';
Expand Down Expand Up @@ -66,7 +67,8 @@ export const PlayQueueListControls = ({
/>
</Box>
<Divider h="60%" orientation="vertical" style={{ alignSelf: 'center' }} />
<Box style={{ flexShrink: 0 }}>
<Box style={{ display: 'flex', flexShrink: 0 }}>
{type === ItemListKey.QUEUE_SONG && <ListLayoutToggleButton listKey={type} />}
<ListConfigMenu
displayTypes={[
{ hidden: true, value: ListDisplayType.GRID },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
} from '/@/renderer/features/shared/components/list-config-menu';
import { ListDisplayTypeToggleButton } from '/@/renderer/features/shared/components/list-display-type-toggle-button';
import { isFilterValueSet } from '/@/renderer/features/shared/components/list-filters';
import { ListLayoutToggleButton } from '/@/renderer/features/shared/components/list-layout-toggle-button';
import { ListRefreshButton } from '/@/renderer/features/shared/components/list-refresh-button';
import { ListSortByDropdown } from '/@/renderer/features/shared/components/list-sort-by-dropdown';
import { ListSortOrderToggleButton } from '/@/renderer/features/shared/components/list-sort-order-toggle-button';
Expand Down Expand Up @@ -234,11 +235,14 @@ export const PlaylistDetailSongListHeaderFilters = ({
tableColumnsData={ALBUM_TABLE_COLUMNS}
/>
) : (
<ListConfigMenu
displayTypes={SONG_DISPLAY_TYPES}
listKey={listKey}
tableColumnsData={PLAYLIST_SONG_TABLE_COLUMNS}
/>
<>
<ListLayoutToggleButton listKey={listKey} />
<ListConfigMenu
displayTypes={SONG_DISPLAY_TYPES}
listKey={listKey}
tableColumnsData={PLAYLIST_SONG_TABLE_COLUMNS}
/>
</>
)}
</Group>
</Flex>
Expand Down
8 changes: 7 additions & 1 deletion src/renderer/features/search/components/search-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
ListConfigMenu,
SONG_DISPLAY_TYPES,
} from '/@/renderer/features/shared/components/list-config-menu';
import { ListLayoutToggleButton } from '/@/renderer/features/shared/components/list-layout-toggle-button';
import { SearchInput } from '/@/renderer/features/shared/components/search-input';
import { AppRoute } from '/@/renderer/router/routes';
import { Button, ButtonGroup } from '/@/shared/components/button/button';
Expand Down Expand Up @@ -120,7 +121,12 @@ export const SearchHeader = ({ navigationId }: SearchHeaderProps) => {
{t('entity.artist', { count: 2 })}
</Button>
</ButtonGroup>
<ListConfigMenu {...listConfigMenuProps[itemType]} />
<Group gap="sm" wrap="nowrap">
{itemType === LibraryItem.SONG && (
<ListLayoutToggleButton listKey={ItemListKey.SONG} />
)}
<ListConfigMenu {...listConfigMenuProps[itemType]} />
</Group>
</Flex>
</FilterBar>
</Stack>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useTranslation } from 'react-i18next';

import {
applyColumnOrder,
FLAT_COLUMN_ORDER,
isColumnOrderActive,
} from '/@/renderer/components/item-list/item-table-list/column-presets';
import {
getDefaultListSettings,
useListSettings,
useSettingsStoreActions,
} from '/@/renderer/store';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { ItemListKey, ListDisplayType } from '/@/shared/types/types';

interface ListLayoutToggleButtonProps {
listKey: ItemListKey;
}

export const ListLayoutToggleButton = ({ listKey }: ListLayoutToggleButtonProps) => {
const { t } = useTranslation();
const list = useListSettings(listKey);
const { setList } = useSettingsStoreActions();

if (list?.display !== ListDisplayType.TABLE || !list.table?.columns) {
return null;
}

const isFlat = isColumnOrderActive(list.table.columns, FLAT_COLUMN_ORDER);

const handleToggle = () => {
if (isFlat) {
const defaultTable = getDefaultListSettings(listKey)?.table;
if (!defaultTable) return;
setList(listKey, {
table: { columns: defaultTable.columns, size: defaultTable.size },
});
return;
}

setList(listKey, {
table: {
columns: applyColumnOrder(list.table.columns, FLAT_COLUMN_ORDER),
size: 'compact',
},
});
};

return (
<ActionIcon
icon={isFlat ? 'layoutList' : 'layoutTable'}
iconProps={{ size: 'lg' }}
onClick={handleToggle}
tooltip={{ label: t('table.config.layout.toggle') }}
variant="subtle"
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
isFilterValueSet,
ListFiltersModal,
} from '/@/renderer/features/shared/components/list-filters';
import { ListLayoutToggleButton } from '/@/renderer/features/shared/components/list-layout-toggle-button';
import { ListRefreshButton } from '/@/renderer/features/shared/components/list-refresh-button';
import { ListSortByDropdown } from '/@/renderer/features/shared/components/list-sort-by-dropdown';
import { ListSortOrderToggleButton } from '/@/renderer/features/shared/components/list-sort-order-toggle-button';
Expand Down Expand Up @@ -94,6 +95,7 @@ export const SongListHeaderFilters = ({ toggleGenreTarget }: { toggleGenreTarget
</Group>
<Group gap="sm" wrap="nowrap">
<ListDisplayTypeToggleButton listKey={ItemListKey.SONG} />
<ListLayoutToggleButton listKey={ItemListKey.SONG} />
<ListConfigMenu
displayTypes={SONG_DISPLAY_TYPES}
listKey={ItemListKey.SONG}
Expand Down
3 changes: 3 additions & 0 deletions src/renderer/store/settings.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2822,6 +2822,9 @@ export const useSettingsForExport = (): SettingsState & { version: number } =>
export const migrateSettings = (settings: SettingsState, settingsVersion: number): SettingsState =>
useSettingsStore.persist.getOptions().migrate!(settings, settingsVersion) as SettingsState;

export const getDefaultListSettings = (type: ItemListKey) =>
initialState.lists[type as keyof typeof initialState.lists] as ItemListSettings | undefined;

export const useListSettings = (type: ItemListKey) =>
useSettingsStore(
(state) => state.lists[type as keyof typeof state.lists],
Expand Down
Loading