mirror of
https://github.com/lidarr/lidarr.git
synced 2025-07-15 09:33:52 -07:00
New: Custom Filtering for UI (#234)
This commit is contained in:
parent
c6873014c7
commit
7354e02bff
154 changed files with 3498 additions and 1370 deletions
|
@ -1,42 +1,107 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import Menu from 'Components/Menu/Menu';
|
||||
import ToolbarMenuButton from 'Components/Menu/ToolbarMenuButton';
|
||||
import FilterMenuContent from './FilterMenuContent';
|
||||
import Menu from './Menu';
|
||||
import ToolbarMenuButton from './ToolbarMenuButton';
|
||||
import styles from './FilterMenu.css';
|
||||
|
||||
function FilterMenu(props) {
|
||||
const {
|
||||
className,
|
||||
children,
|
||||
isDisabled,
|
||||
...otherProps
|
||||
} = props;
|
||||
class FilterMenu extends Component {
|
||||
|
||||
return (
|
||||
<Menu
|
||||
className={className}
|
||||
{...otherProps}
|
||||
>
|
||||
<ToolbarMenuButton
|
||||
iconName={icons.FILTER}
|
||||
text="Filter"
|
||||
isDisabled={isDisabled}
|
||||
/>
|
||||
{children}
|
||||
</Menu>
|
||||
);
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
isFilterModalOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onCustomFiltersPress = () => {
|
||||
this.setState({ isFilterModalOpen: true });
|
||||
}
|
||||
|
||||
onFiltersModalClose = () => {
|
||||
this.setState({ isFilterModalOpen: false });
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render(props) {
|
||||
const {
|
||||
className,
|
||||
isDisabled,
|
||||
selectedFilterKey,
|
||||
filters,
|
||||
customFilters,
|
||||
buttonComponent: ButtonComponent,
|
||||
filterModalConnectorComponent: FilterModalConnectorComponent,
|
||||
onFilterSelect,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
const showCustomFilters = !!FilterModalConnectorComponent;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Menu
|
||||
className={className}
|
||||
{...otherProps}
|
||||
>
|
||||
<ButtonComponent
|
||||
iconName={icons.FILTER}
|
||||
text="Filter"
|
||||
isDisabled={isDisabled}
|
||||
/>
|
||||
|
||||
<FilterMenuContent
|
||||
selectedFilterKey={selectedFilterKey}
|
||||
filters={filters}
|
||||
customFilters={customFilters}
|
||||
showCustomFilters={showCustomFilters}
|
||||
onFilterSelect={onFilterSelect}
|
||||
onCustomFiltersPress={this.onCustomFiltersPress}
|
||||
/>
|
||||
|
||||
</Menu>
|
||||
|
||||
{
|
||||
showCustomFilters &&
|
||||
<FilterModalConnectorComponent
|
||||
isOpen={this.state.isFilterModalOpen}
|
||||
selectedFilterKey={selectedFilterKey}
|
||||
filters={filters}
|
||||
customFilters={customFilters}
|
||||
onFilterSelect={onFilterSelect}
|
||||
onModalClose={this.onFiltersModalClose}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
FilterMenu.propTypes = {
|
||||
className: PropTypes.string,
|
||||
children: PropTypes.node.isRequired,
|
||||
isDisabled: PropTypes.bool.isRequired
|
||||
isDisabled: PropTypes.bool.isRequired,
|
||||
selectedFilterKey: PropTypes.string.isRequired,
|
||||
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
customFilters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
buttonComponent: PropTypes.func.isRequired,
|
||||
filterModalConnectorComponent: PropTypes.func,
|
||||
onFilterSelect: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
FilterMenu.defaultProps = {
|
||||
className: styles.filterMenu,
|
||||
isDisabled: false
|
||||
isDisabled: false,
|
||||
buttonComponent: ToolbarMenuButton
|
||||
};
|
||||
|
||||
export default FilterMenu;
|
||||
|
|
85
frontend/src/Components/Menu/FilterMenuContent.js
Normal file
85
frontend/src/Components/Menu/FilterMenuContent.js
Normal file
|
@ -0,0 +1,85 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import MenuContent from './MenuContent';
|
||||
import FilterMenuItem from './FilterMenuItem';
|
||||
import MenuItem from './MenuItem';
|
||||
import MenuItemSeparator from './MenuItemSeparator';
|
||||
|
||||
class FilterMenuContent extends Component {
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
selectedFilterKey,
|
||||
filters,
|
||||
customFilters,
|
||||
showCustomFilters,
|
||||
onFilterSelect,
|
||||
onCustomFiltersPress,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<MenuContent {...otherProps}>
|
||||
{
|
||||
filters.map((filter) => {
|
||||
return (
|
||||
<FilterMenuItem
|
||||
key={filter.key}
|
||||
filterKey={filter.key}
|
||||
selectedFilterKey={selectedFilterKey}
|
||||
onPress={onFilterSelect}
|
||||
>
|
||||
{filter.label}
|
||||
</FilterMenuItem>
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
customFilters.map((filter) => {
|
||||
return (
|
||||
<FilterMenuItem
|
||||
key={filter.key}
|
||||
filterKey={filter.key}
|
||||
selectedFilterKey={selectedFilterKey}
|
||||
onPress={onFilterSelect}
|
||||
>
|
||||
{filter.label}
|
||||
</FilterMenuItem>
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
showCustomFilters &&
|
||||
<MenuItemSeparator />
|
||||
}
|
||||
|
||||
{
|
||||
showCustomFilters &&
|
||||
<MenuItem onPress={onCustomFiltersPress}>
|
||||
Custom Filters
|
||||
</MenuItem>
|
||||
}
|
||||
</MenuContent>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
FilterMenuContent.propTypes = {
|
||||
selectedFilterKey: PropTypes.string.isRequired,
|
||||
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
customFilters: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
showCustomFilters: PropTypes.bool.isRequired,
|
||||
onFilterSelect: PropTypes.func.isRequired,
|
||||
onCustomFiltersPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
FilterMenuContent.defaultProps = {
|
||||
showCustomFilters: false
|
||||
};
|
||||
|
||||
export default FilterMenuContent;
|
|
@ -9,12 +9,11 @@ class FilterMenuItem extends Component {
|
|||
|
||||
onPress = () => {
|
||||
const {
|
||||
name,
|
||||
value,
|
||||
filterKey,
|
||||
onPress
|
||||
} = this.props;
|
||||
|
||||
onPress(name, value);
|
||||
onPress(filterKey);
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -22,18 +21,14 @@ class FilterMenuItem extends Component {
|
|||
|
||||
render() {
|
||||
const {
|
||||
name,
|
||||
value,
|
||||
filterKey,
|
||||
filterValue,
|
||||
selectedFilterKey,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
const isSelected = name === filterKey && value === filterValue;
|
||||
|
||||
return (
|
||||
<SelectedMenuItem
|
||||
isSelected={isSelected}
|
||||
isSelected={filterKey === selectedFilterKey}
|
||||
{...otherProps}
|
||||
onPress={this.onPress}
|
||||
/>
|
||||
|
@ -42,16 +37,9 @@ class FilterMenuItem extends Component {
|
|||
}
|
||||
|
||||
FilterMenuItem.propTypes = {
|
||||
name: PropTypes.string,
|
||||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]),
|
||||
filterKey: PropTypes.string,
|
||||
filterValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]),
|
||||
filterKey: PropTypes.string.isRequired,
|
||||
selectedFilterKey: PropTypes.string.isRequired,
|
||||
onPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
FilterMenuItem.defaultProps = {
|
||||
name: null,
|
||||
value: null
|
||||
};
|
||||
|
||||
export default FilterMenuItem;
|
||||
|
|
5
frontend/src/Components/Menu/MenuItemSeparator.css
Normal file
5
frontend/src/Components/Menu/MenuItemSeparator.css
Normal file
|
@ -0,0 +1,5 @@
|
|||
.separator {
|
||||
overflow: hidden;
|
||||
height: 1px;
|
||||
background-color: $themeDarkColor;
|
||||
}
|
10
frontend/src/Components/Menu/MenuItemSeparator.js
Normal file
10
frontend/src/Components/Menu/MenuItemSeparator.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
import React from 'react';
|
||||
import styles from './MenuItemSeparator.css';
|
||||
|
||||
function MenuItemSeparator() {
|
||||
return (
|
||||
<div className={styles.separator} />
|
||||
);
|
||||
}
|
||||
|
||||
export default MenuItemSeparator;
|
11
frontend/src/Components/Menu/PageMenuButton.css
Normal file
11
frontend/src/Components/Menu/PageMenuButton.css
Normal file
|
@ -0,0 +1,11 @@
|
|||
.menuButton {
|
||||
composes: menuButton from './MenuButton.css';
|
||||
|
||||
&:hover {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
margin-left: 5px;
|
||||
}
|
36
frontend/src/Components/Menu/PageMenuButton.js
Normal file
36
frontend/src/Components/Menu/PageMenuButton.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Icon from 'Components/Icon';
|
||||
import MenuButton from 'Components/Menu/MenuButton';
|
||||
import styles from './PageMenuButton.css';
|
||||
|
||||
function PageMenuButton(props) {
|
||||
const {
|
||||
iconName,
|
||||
text,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<MenuButton
|
||||
className={styles.menuButton}
|
||||
{...otherProps}
|
||||
>
|
||||
<Icon
|
||||
name={iconName}
|
||||
size={18}
|
||||
/>
|
||||
|
||||
<div className={styles.label}>
|
||||
{text}
|
||||
</div>
|
||||
</MenuButton>
|
||||
);
|
||||
}
|
||||
|
||||
PageMenuButton.propTypes = {
|
||||
iconName: PropTypes.object.isRequired,
|
||||
text: PropTypes.string
|
||||
};
|
||||
|
||||
export default PageMenuButton;
|
Loading…
Add table
Add a link
Reference in a new issue