diff --git a/src/renderer/components/item-list/item-detail-list/columns/index.ts b/src/renderer/components/item-list/item-detail-list/columns/index.ts
index db5bb5285..e95b4f613 100644
--- a/src/renderer/components/item-list/item-detail-list/columns/index.ts
+++ b/src/renderer/components/item-list/item-detail-list/columns/index.ts
@@ -27,6 +27,7 @@ import { PlayCountColumn } from './play-count-column';
import { RatingColumn } from './rating-column';
import { ReleaseDateColumn } from './release-date-column';
import { RowIndexColumn } from './row-index-column';
+import { ItemDetailRowPlayControlCell } from './row-play-control-cell';
import { SampleRateColumn } from './sample-rate-column';
import { SizeColumn } from './size-column';
import { TitleArtistColumn } from './title-artist-column';
@@ -111,6 +112,7 @@ export {
GenreBadgeColumn,
GenreColumn,
ImageColumn,
+ ItemDetailRowPlayControlCell,
LastPlayedColumn,
PathColumn,
PlayCountColumn,
diff --git a/src/renderer/components/item-list/item-detail-list/columns/row-index-column.tsx b/src/renderer/components/item-list/item-detail-list/columns/row-index-column.tsx
index 817f0acec..25d1ed6f9 100644
--- a/src/renderer/components/item-list/item-detail-list/columns/row-index-column.tsx
+++ b/src/renderer/components/item-list/item-detail-list/columns/row-index-column.tsx
@@ -1,23 +1,40 @@
import styles from './row-index-column.module.css';
+import { ItemDetailRowPlayControlCell } from './row-play-control-cell';
import { ItemDetailListCellProps } from './types';
+import { useDetailRowPlayControl } from './use-detail-row-play-control';
-import { useIsCurrentSong } from '/@/renderer/features/player/hooks/use-is-current-song';
-import { usePlayerStatus } from '/@/renderer/store';
+import { isRowPlayControlColumn } from '/@/renderer/components/item-list/helpers/get-row-play-control-column';
import { Icon } from '/@/shared/components/icon/icon';
-import { PlayerStatus } from '/@/shared/types/types';
-
-export const RowIndexColumn = ({ rowIndex, song }: ItemDetailListCellProps) => {
- const status = usePlayerStatus();
- const { isActive } = useIsCurrentSong(song);
- const isPlaying = isActive && status === PlayerStatus.PLAYING;
-
- if (isActive) {
- return (
-
-
-
- );
- }
+import { TableColumn } from '/@/shared/types/types';
+const DefaultRowIndexColumn = ({ rowIndex }: ItemDetailListCellProps) => {
return <>{String((rowIndex ?? 0) + 1)}>;
};
+
+const PlayableRowIndexColumn = (props: ItemDetailListCellProps) => {
+ const { handlePlay, isActive, isPlaying, showPlayControls } = useDetailRowPlayControl(props);
+
+ const indexContent = isActive ? (
+
+
+
+ ) : (
+ String((props.rowIndex ?? 0) + 1)
+ );
+
+ return (
+
+ );
+};
+
+export const RowIndexColumn = (props: ItemDetailListCellProps) => {
+ if (!props.columns || !isRowPlayControlColumn(TableColumn.ROW_INDEX, props.columns)) {
+ return ;
+ }
+
+ return ;
+};
diff --git a/src/renderer/components/item-list/item-detail-list/columns/row-play-control-cell.module.css b/src/renderer/components/item-list/item-detail-list/columns/row-play-control-cell.module.css
new file mode 100644
index 000000000..daeb253f2
--- /dev/null
+++ b/src/renderer/components/item-list/item-detail-list/columns/row-play-control-cell.module.css
@@ -0,0 +1,30 @@
+.cell-wrapper {
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ min-height: 100%;
+}
+
+.play-target {
+ position: absolute;
+ inset: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.index-content {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+}
+
+.icon-wrapper {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
diff --git a/src/renderer/components/item-list/item-detail-list/columns/row-play-control-cell.tsx b/src/renderer/components/item-list/item-detail-list/columns/row-play-control-cell.tsx
new file mode 100644
index 000000000..3f87847f1
--- /dev/null
+++ b/src/renderer/components/item-list/item-detail-list/columns/row-play-control-cell.tsx
@@ -0,0 +1,34 @@
+import { ReactNode } from 'react';
+
+import styles from './row-play-control-cell.module.css';
+
+import { ItemRowPlayControls } from '/@/renderer/features/shared/components/item-row-play-controls';
+import { HoverCard } from '/@/shared/components/hover-card/hover-card';
+import { Play } from '/@/shared/types/types';
+
+export const ItemDetailRowPlayControlCell = ({
+ indexContent,
+ onPlay,
+ showPlayControls,
+}: {
+ indexContent: ReactNode;
+ onPlay: (playType: Play) => void;
+ showPlayControls: boolean;
+}) => {
+ if (!showPlayControls) {
+ return <>{indexContent}>;
+ }
+
+ return (
+
+
+
+ {indexContent}
+
+ e.stopPropagation()}>
+
+
+
+
+ );
+};
diff --git a/src/renderer/components/item-list/item-detail-list/columns/track-number-column.tsx b/src/renderer/components/item-list/item-detail-list/columns/track-number-column.tsx
index b2ee3f7f4..2e31e4647 100644
--- a/src/renderer/components/item-list/item-detail-list/columns/track-number-column.tsx
+++ b/src/renderer/components/item-list/item-detail-list/columns/track-number-column.tsx
@@ -1,7 +1,46 @@
+import { ItemDetailRowPlayControlCell } from './row-play-control-cell';
+import styles from './row-play-control-cell.module.css';
import { ItemDetailListCellProps } from './types';
+import { useDetailRowPlayControl } from './use-detail-row-play-control';
-export const TrackNumberColumn = ({ song }: ItemDetailListCellProps) => {
+import { isRowPlayControlColumn } from '/@/renderer/components/item-list/helpers/get-row-play-control-column';
+import { Icon } from '/@/shared/components/icon/icon';
+import { TableColumn } from '/@/shared/types/types';
+
+const formatTrackNumber = (song: ItemDetailListCellProps['song']) => {
const disc = song.discNumber ?? 1;
const track = song.trackNumber.toString().padStart(2, '0');
return `${disc}-${track}`;
};
+
+const DefaultTrackNumberColumn = ({ song }: ItemDetailListCellProps) => {
+ return <>{formatTrackNumber(song)}>;
+};
+
+const PlayableTrackNumberColumn = (props: ItemDetailListCellProps) => {
+ const { handlePlay, isActive, isPlaying, showPlayControls } = useDetailRowPlayControl(props);
+
+ const indexContent = isActive ? (
+
+
+
+ ) : (
+ formatTrackNumber(props.song)
+ );
+
+ return (
+
+ );
+};
+
+export const TrackNumberColumn = (props: ItemDetailListCellProps) => {
+ if (!props.columns || !isRowPlayControlColumn(TableColumn.TRACK_NUMBER, props.columns)) {
+ return ;
+ }
+
+ return ;
+};
diff --git a/src/renderer/components/item-list/item-detail-list/columns/types.ts b/src/renderer/components/item-list/item-detail-list/columns/types.ts
index cad0801a0..63080ec3c 100644
--- a/src/renderer/components/item-list/item-detail-list/columns/types.ts
+++ b/src/renderer/components/item-list/item-detail-list/columns/types.ts
@@ -1,8 +1,9 @@
import { ItemListStateActions } from '/@/renderer/components/item-list/helpers/item-list-state';
-import { ItemControls } from '/@/renderer/components/item-list/types';
+import { ItemControls, ItemTableListColumnConfig } from '/@/renderer/components/item-list/types';
import { Song } from '/@/shared/types/domain-types';
export interface ItemDetailListCellProps {
+ columns?: ItemTableListColumnConfig[];
controls?: ItemControls;
internalState?: ItemListStateActions;
isMutatingFavorite?: boolean;
diff --git a/src/renderer/components/item-list/item-detail-list/columns/use-detail-row-play-control.ts b/src/renderer/components/item-list/item-detail-list/columns/use-detail-row-play-control.ts
new file mode 100644
index 000000000..97d67ad9c
--- /dev/null
+++ b/src/renderer/components/item-list/item-detail-list/columns/use-detail-row-play-control.ts
@@ -0,0 +1,47 @@
+import { useCallback } from 'react';
+
+import { ItemDetailListCellProps } from './types';
+
+import { playSongFromItemListControl } from '/@/renderer/components/item-list/helpers/play-row-from-list';
+import { usePlayer } from '/@/renderer/features/player/context/player-context';
+import { useIsCurrentSong } from '/@/renderer/features/player/hooks/use-is-current-song';
+import { usePlayerStatus } from '/@/renderer/store';
+import { Song } from '/@/shared/types/domain-types';
+import { Play, PlayerStatus } from '/@/shared/types/types';
+
+export const useDetailRowPlayControl = ({
+ internalState,
+ rowIndex = 0,
+ song,
+}: Pick) => {
+ const status = usePlayerStatus();
+ const player = usePlayer();
+ const { isActive } = useIsCurrentSong(song);
+ const isPlaying = isActive && status === PlayerStatus.PLAYING;
+
+ const showPlayControls = !!song?.id;
+
+ const handlePlay = useCallback(
+ (playType: Play) => {
+ if (!song) {
+ return;
+ }
+
+ playSongFromItemListControl({
+ index: rowIndex,
+ internalState,
+ item: song as Song,
+ meta: { playType, singleSongOnly: true },
+ player,
+ });
+ },
+ [internalState, player, rowIndex, song],
+ );
+
+ return {
+ handlePlay,
+ isActive,
+ isPlaying,
+ showPlayControls,
+ };
+};
diff --git a/src/renderer/components/item-list/item-detail-list/item-detail-list.module.css b/src/renderer/components/item-list/item-detail-list/item-detail-list.module.css
index adc6e8d79..fa1f84eb4 100644
--- a/src/renderer/components/item-list/item-detail-list/item-detail-list.module.css
+++ b/src/renderer/components/item-list/item-detail-list/item-detail-list.module.css
@@ -447,6 +447,14 @@
padding-left: 0;
}
+.row .track-cell-play-control {
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ overflow: visible;
+}
+
.track-row-dragging {
opacity: 0.5;
}
diff --git a/src/renderer/components/item-list/item-detail-list/item-detail-list.tsx b/src/renderer/components/item-list/item-detail-list/item-detail-list.tsx
index 4f928ed35..3e09fb567 100644
--- a/src/renderer/components/item-list/item-detail-list/item-detail-list.tsx
+++ b/src/renderer/components/item-list/item-detail-list/item-detail-list.tsx
@@ -33,6 +33,7 @@ import styles from './item-detail-list.module.css';
import { ItemCardControls } from '/@/renderer/components/item-card/item-card-controls';
import { ItemImage } from '/@/renderer/components/item-image/item-image';
import { getDraggedItems } from '/@/renderer/components/item-list/helpers/get-dragged-items';
+import { isRowPlayControlColumn } from '/@/renderer/components/item-list/helpers/get-row-play-control-column';
import { useDefaultItemListControls } from '/@/renderer/components/item-list/helpers/item-list-controls';
import {
ItemListStateActions,
@@ -365,6 +366,7 @@ const TrackRow = memo(
const isTitleColumn = col.id === TableColumn.TITLE;
const isImageColumn = col.id === TableColumn.IMAGE;
const isIconActionColumn = isNoHorizontalPaddingColumn(col.id);
+ const isPlayControlColumn = isRowPlayControlColumn(col.id, columns);
const showHoverContent = shouldShowHoverOnlyColumnContent(
col.id,
isRowHovered,
@@ -374,6 +376,7 @@ const TrackRow = memo(
const content = isSongsLoading ? null : showHoverContent ? (