Option to toggle fields in songs, albums & artists (#923)
* Add toggleColumns - Add logic for toggling columns - Add MenuComponent + useSelectedFields hook * Refactoring * eslint-fixes * Typo * skip menu in albumGridView * add omittedFields * Add toggling for playlists and albumSong * Refactoring * defaultProps - fix * Add toggling for PlaylistSongs * remove accidental console log * Refactoring for future compatibility * Hide ToggleMenu in albumGridView * Add TopBarComponent in ToggleFieldsMenu * Add defaultOff for useSelectedFields * Fix edge case * eslint fix * Refactoring * Add propType for forwardRef * Fix issues * add translation for grid and table * add translation for grid and table * Ignore menuBtn for spotify-ish and Ligera themes * hide bpm by default in playlistSongs * Add memoization * Default album view must be Grid Co-authored-by: Deluan <deluan@navidrome.org>
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
import React, { useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import IconButton from '@material-ui/core/IconButton'
|
||||
import Menu from '@material-ui/core/Menu'
|
||||
import MenuItem from '@material-ui/core/MenuItem'
|
||||
import { makeStyles, Typography } from '@material-ui/core'
|
||||
import MoreVertIcon from '@material-ui/icons/MoreVert'
|
||||
import Checkbox from '@material-ui/core/Checkbox'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { useTranslate } from 'react-admin'
|
||||
import { setToggleableFields } from '../actions'
|
||||
|
||||
const useStyles = makeStyles({
|
||||
menuIcon: {
|
||||
position: 'relative',
|
||||
top: '-0.5em',
|
||||
},
|
||||
menu: {
|
||||
width: '24ch',
|
||||
},
|
||||
columns: {
|
||||
maxHeight: '21rem',
|
||||
overflow: 'auto',
|
||||
},
|
||||
title: {
|
||||
margin: '1rem',
|
||||
},
|
||||
})
|
||||
|
||||
const ToggleFieldsMenu = ({ resource, topbarComponent: TopBarComponent }) => {
|
||||
const [anchorEl, setAnchorEl] = useState(null)
|
||||
const dispatch = useDispatch()
|
||||
const translate = useTranslate()
|
||||
const toggleableColumns = useSelector(
|
||||
(state) => state.settings.toggleableFields[resource]
|
||||
)
|
||||
const omittedColumns =
|
||||
useSelector((state) => state.settings.omittedFields[resource]) || []
|
||||
|
||||
const classes = useStyles()
|
||||
const open = Boolean(anchorEl)
|
||||
|
||||
const handleOpen = (event) => {
|
||||
setAnchorEl(event.currentTarget)
|
||||
}
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null)
|
||||
}
|
||||
|
||||
const handleClick = (selectedColumn) => {
|
||||
dispatch(
|
||||
setToggleableFields({
|
||||
[resource]: {
|
||||
...toggleableColumns,
|
||||
[selectedColumn]: !toggleableColumns[selectedColumn],
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.menuIcon}>
|
||||
<IconButton
|
||||
aria-label="more"
|
||||
aria-controls="long-menu"
|
||||
aria-haspopup="true"
|
||||
onClick={handleOpen}
|
||||
>
|
||||
<MoreVertIcon />
|
||||
</IconButton>
|
||||
<Menu
|
||||
id="long-menu"
|
||||
anchorEl={anchorEl}
|
||||
keepMounted
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
classes={{
|
||||
paper: classes.menu,
|
||||
}}
|
||||
>
|
||||
{TopBarComponent && <TopBarComponent />}
|
||||
{toggleableColumns ? (
|
||||
<div>
|
||||
<Typography className={classes.title}>
|
||||
{translate('ra.toggleFieldsMenu.columnsToDisplay')}
|
||||
</Typography>
|
||||
<div className={classes.columns}>
|
||||
{Object.entries(toggleableColumns).map(([key, val]) =>
|
||||
!omittedColumns.includes(key) ? (
|
||||
<MenuItem key={key} onClick={() => handleClick(key)}>
|
||||
<Checkbox checked={val} />
|
||||
{translate(`resources.${resource}.fields.${key}`)}
|
||||
</MenuItem>
|
||||
) : null
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</Menu>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ToggleFieldsMenu
|
||||
|
||||
ToggleFieldsMenu.propTypes = {
|
||||
resource: PropTypes.string.isRequired,
|
||||
topbarComponent: PropTypes.elementType,
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { setOmittedFields, setToggleableFields } from '../actions'
|
||||
|
||||
const useSelectedFields = ({
|
||||
resource,
|
||||
columns,
|
||||
omittedColumns = [],
|
||||
defaultOff = [],
|
||||
}) => {
|
||||
const dispatch = useDispatch()
|
||||
const resourceFields = useSelector(
|
||||
(state) => state.settings.toggleableFields
|
||||
)?.[resource]
|
||||
const omittedFields = useSelector((state) => state.settings.omittedFields)?.[
|
||||
resource
|
||||
]
|
||||
|
||||
const [filteredComponents, setFilteredComponents] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!resourceFields ||
|
||||
Object.keys(resourceFields).length !== Object.keys(columns).length
|
||||
) {
|
||||
const obj = {}
|
||||
for (const key of Object.keys(columns)) {
|
||||
obj[key] = !defaultOff.includes(key)
|
||||
}
|
||||
dispatch(setToggleableFields({ [resource]: obj }))
|
||||
}
|
||||
if (!omittedFields) {
|
||||
dispatch(setOmittedFields({ [resource]: omittedColumns }))
|
||||
}
|
||||
}, [
|
||||
columns,
|
||||
defaultOff,
|
||||
dispatch,
|
||||
omittedColumns,
|
||||
omittedFields,
|
||||
resource,
|
||||
resourceFields,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
if (resourceFields) {
|
||||
const filtered = []
|
||||
const omitted = omittedColumns
|
||||
for (const [key, val] of Object.entries(columns)) {
|
||||
if (!val) omitted.push(key)
|
||||
else if (resourceFields[key]) filtered.push(val)
|
||||
}
|
||||
if (filteredComponents.length !== filtered.length)
|
||||
setFilteredComponents(filtered)
|
||||
if (omittedFields.length !== omitted.length)
|
||||
dispatch(setOmittedFields({ [resource]: omitted }))
|
||||
}
|
||||
}, [
|
||||
resourceFields,
|
||||
columns,
|
||||
dispatch,
|
||||
omittedColumns,
|
||||
omittedFields,
|
||||
resource,
|
||||
filteredComponents.length,
|
||||
])
|
||||
|
||||
return React.Children.toArray(filteredComponents)
|
||||
}
|
||||
|
||||
export default useSelectedFields
|
||||
|
||||
useSelectedFields.propTypes = {
|
||||
resource: PropTypes.string,
|
||||
columns: PropTypes.object,
|
||||
omittedColumns: PropTypes.arrayOf(PropTypes.string),
|
||||
defaultOff: PropTypes.arrayOf(PropTypes.string),
|
||||
}
|
||||
Reference in New Issue
Block a user