parent
e66725047a
commit
0219e62979
|
@ -1,7 +1,7 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import Autosuggest from 'react-autosuggest';
|
||||
import jdu from 'jdu';
|
||||
import Fuse from 'fuse.js';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import Icon from 'Components/Icon';
|
||||
import keyboardShortcuts, { shortcuts } from 'Components/keyboardShortcuts';
|
||||
|
@ -10,6 +10,21 @@ import styles from './SeriesSearchInput.css';
|
|||
|
||||
const ADD_NEW_TYPE = 'addNew';
|
||||
|
||||
const fuseOptions = {
|
||||
shouldSort: true,
|
||||
includeMatches: true,
|
||||
threshold: 0.3,
|
||||
location: 0,
|
||||
distance: 100,
|
||||
maxPatternLength: 32,
|
||||
minMatchCharLength: 1,
|
||||
keys: [
|
||||
'title',
|
||||
'alternateTitles.title',
|
||||
'tags.label'
|
||||
]
|
||||
};
|
||||
|
||||
class SeriesSearchInput extends Component {
|
||||
|
||||
//
|
||||
|
@ -69,9 +84,8 @@ class SeriesSearchInput extends Component {
|
|||
|
||||
return (
|
||||
<SeriesSearchResult
|
||||
query={query}
|
||||
cleanQuery={jdu.replace(query).toLowerCase()}
|
||||
{...item}
|
||||
{...item.item}
|
||||
match={item.matches[0]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -140,25 +154,16 @@ class SeriesSearchInput extends Component {
|
|||
}
|
||||
|
||||
onSuggestionsFetchRequested = ({ value }) => {
|
||||
const lowerCaseValue = jdu.replace(value).toLowerCase();
|
||||
|
||||
const suggestions = this.props.series.filter((series) => {
|
||||
// Check the title first and if there isn't a match fallback to
|
||||
// the alternate titles and finally the tags.
|
||||
|
||||
if (value.length === 1) {
|
||||
return (
|
||||
series.cleanTitle.startsWith(lowerCaseValue) ||
|
||||
series.alternateTitles.some((alternateTitle) => alternateTitle.cleanTitle.startsWith(lowerCaseValue)) ||
|
||||
series.tags.some((tag) => tag.cleanLabel.startsWith(lowerCaseValue))
|
||||
);
|
||||
const fuse = new Fuse(this.props.series, fuseOptions);
|
||||
const suggestions = fuse.search(value).sort((a, b) => {
|
||||
if (a.item.sortTitle < b.item.sortTitle) {
|
||||
return -1;
|
||||
}
|
||||
if (a.item.sortTitle > b.item.sortTitle) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return (
|
||||
series.cleanTitle.contains(lowerCaseValue) ||
|
||||
series.alternateTitles.some((alternateTitle) => alternateTitle.cleanTitle.contains(lowerCaseValue)) ||
|
||||
series.tags.some((tag) => tag.cleanLabel.contains(lowerCaseValue))
|
||||
);
|
||||
return 0;
|
||||
});
|
||||
|
||||
this.setState({ suggestions });
|
||||
|
|
|
@ -1,35 +1,14 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { push } from 'react-router-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import jdu from 'jdu';
|
||||
import createAllSeriesSelector from 'Store/Selectors/createAllSeriesSelector';
|
||||
import createTagsSelector from 'Store/Selectors/createTagsSelector';
|
||||
import SeriesSearchInput from './SeriesSearchInput';
|
||||
|
||||
function createCleanTagsSelector() {
|
||||
return createSelector(
|
||||
createTagsSelector(),
|
||||
(tags) => {
|
||||
return tags.map((tag) => {
|
||||
const {
|
||||
id,
|
||||
label
|
||||
} = tag;
|
||||
|
||||
return {
|
||||
id,
|
||||
label,
|
||||
cleanLabel: jdu.replace(label).toLowerCase()
|
||||
};
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function createCleanSeriesSelector() {
|
||||
return createSelector(
|
||||
createAllSeriesSelector(),
|
||||
createCleanTagsSelector(),
|
||||
createTagsSelector(),
|
||||
(allSeries, allTags) => {
|
||||
return allSeries.map((series) => {
|
||||
const {
|
||||
|
@ -46,27 +25,11 @@ function createCleanSeriesSelector() {
|
|||
titleSlug,
|
||||
sortTitle,
|
||||
images,
|
||||
cleanTitle: jdu.replace(title).toLowerCase(),
|
||||
alternateTitles: alternateTitles.map((alternateTitle) => {
|
||||
return {
|
||||
title: alternateTitle.title,
|
||||
sortTitle: alternateTitle.sortTitle,
|
||||
cleanTitle: jdu.replace(alternateTitle.title).toLowerCase()
|
||||
};
|
||||
}),
|
||||
alternateTitles,
|
||||
tags: tags.map((id) => {
|
||||
return allTags.find((tag) => tag.id === id);
|
||||
})
|
||||
};
|
||||
}).sort((a, b) => {
|
||||
if (a.sortTitle < b.sortTitle) {
|
||||
return -1;
|
||||
}
|
||||
if (a.sortTitle > b.sortTitle) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
);
|
||||
|
|
|
@ -5,38 +5,22 @@ import Label from 'Components/Label';
|
|||
import SeriesPoster from 'Series/SeriesPoster';
|
||||
import styles from './SeriesSearchResult.css';
|
||||
|
||||
function findMatchingAlternateTitle(alternateTitles, cleanQuery) {
|
||||
return alternateTitles.find((alternateTitle) => {
|
||||
return alternateTitle.cleanTitle.contains(cleanQuery);
|
||||
});
|
||||
}
|
||||
|
||||
function getMatchingTag(tags, cleanQuery) {
|
||||
return tags.find((tag) => {
|
||||
return tag.cleanLabel.contains(cleanQuery);
|
||||
});
|
||||
}
|
||||
|
||||
function SeriesSearchResult(props) {
|
||||
const {
|
||||
cleanQuery,
|
||||
match,
|
||||
title,
|
||||
cleanTitle,
|
||||
images,
|
||||
alternateTitles,
|
||||
tags
|
||||
} = props;
|
||||
|
||||
const titleContains = cleanTitle.contains(cleanQuery);
|
||||
let alternateTitle = null;
|
||||
let tag = null;
|
||||
|
||||
if (!titleContains) {
|
||||
alternateTitle = findMatchingAlternateTitle(alternateTitles, cleanQuery);
|
||||
}
|
||||
|
||||
if (!titleContains && !alternateTitle) {
|
||||
tag = getMatchingTag(tags, cleanQuery);
|
||||
if (match.key === 'alternateTitles.cleanTitle') {
|
||||
alternateTitle = alternateTitles[match.arrayIndex];
|
||||
} else if (match.key === 'tags.label') {
|
||||
tag = tags[match.arrayIndex];
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -55,14 +39,15 @@ function SeriesSearchResult(props) {
|
|||
</div>
|
||||
|
||||
{
|
||||
!!alternateTitle &&
|
||||
alternateTitle ?
|
||||
<div className={styles.alternateTitle}>
|
||||
{alternateTitle.title}
|
||||
</div>
|
||||
</div> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
!!tag &&
|
||||
tag ?
|
||||
<div className={styles.tagContainer}>
|
||||
<Label
|
||||
key={tag.id}
|
||||
|
@ -70,7 +55,8 @@ function SeriesSearchResult(props) {
|
|||
>
|
||||
{tag.label}
|
||||
</Label>
|
||||
</div>
|
||||
</div> :
|
||||
null
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -78,12 +64,11 @@ function SeriesSearchResult(props) {
|
|||
}
|
||||
|
||||
SeriesSearchResult.propTypes = {
|
||||
cleanQuery: PropTypes.string.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
cleanTitle: PropTypes.string.isRequired,
|
||||
images: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
alternateTitles: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
tags: PropTypes.arrayOf(PropTypes.object).isRequired
|
||||
tags: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
match: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
export default SeriesSearchResult;
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
"extract-text-webpack-plugin": "3.0.2",
|
||||
"file-loader": "1.1.6",
|
||||
"filesize": "3.6.1",
|
||||
"fuse.js": "3.4.2",
|
||||
"gulp": "3.9.1",
|
||||
"gulp-cached": "1.1.1",
|
||||
"gulp-clean-css": "3.10.0",
|
||||
|
|
|
@ -3529,6 +3529,11 @@ functional-red-black-tree@^1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
|
||||
integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
|
||||
|
||||
fuse.js@3.4.2:
|
||||
version "3.4.2"
|
||||
resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-3.4.2.tgz#d7a638c436ecd7b9c4c0051478c09594eb956212"
|
||||
integrity sha512-WVbrm+cAxPtyMqdtL7cYhR7aZJPhtOfjNClPya8GKMVukKDYs7pEnPINeRVX1C9WmWgU8MdYGYbUPAP2AJXdoQ==
|
||||
|
||||
gauge@~2.7.3:
|
||||
version "2.7.4"
|
||||
resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
|
||||
|
|
Loading…
Reference in New Issue