From ea330948b634036a964028754e838428da734bd6 Mon Sep 17 00:00:00 2001 From: gokulkrishh Date: Wed, 7 Aug 2019 22:37:51 +0530 Subject: [PATCH 1/6] Add option to compare packages in search --- .../AutocompleteInput/AutocompleteInput.js | 57 ++++++++++++++++--- .../AutocompleteInput/AutocompleteInput.scss | 15 ++++- pages/index.js | 12 +++- utils/common.utils.js | 7 ++- 4 files changed, 78 insertions(+), 13 deletions(-) diff --git a/client/components/AutocompleteInput/AutocompleteInput.js b/client/components/AutocompleteInput/AutocompleteInput.js index 92055495..cd1be8f6 100644 --- a/client/components/AutocompleteInput/AutocompleteInput.js +++ b/client/components/AutocompleteInput/AutocompleteInput.js @@ -5,7 +5,7 @@ import cx from 'classnames' import './AutocompleteInput.scss' import debounce from 'debounce' -import { parsePackageString } from 'utils/common.utils' +import { parsePackageString, isComparingPkgs } from 'utils/common.utils' export default class AutocompleteInput extends PureComponent { @@ -21,14 +21,30 @@ export default class AutocompleteInput extends PureComponent { getSuggestions = debounce( value => { - API.getSuggestions(value) + if (isComparingPkgs(value)) { + const values = value.split(','); + const lastValue = values[values.length - 1]; + if (!lastValue) return; + API.getSuggestions(lastValue) .then(result => { this.setState({ suggestions: result }) }) + } + else { + API.getSuggestions(value) + .then(result => { + this.setState({ suggestions: result }) + }) + } }, 150, ) + hasPackageResult(item) { + var regex = new RegExp(`\\b${item.package.name}\\b`, "g"); + return this.state.value.match(regex); + } + renderSuggestionItem = (item, isHighlighted) => (
{ item.package.description }
+ + {item.package.name && !this.hasPackageResult(item) && } ) @@ -53,15 +80,27 @@ export default class AutocompleteInput extends PureComponent { onSearchSubmit(value || this.state.value) } + comparePackages = (item) => { + const { value } = this.state; + if (value === item.package.name) return; + this.setState({ value: `${value},${item.package.name}` }) + } + handleInputChange = ({ target }) => { - this.setState({ value: target.value }) - const trimmedValue = target.value.trim() - const { name } = parsePackageString(trimmedValue) + const { value } = target; + this.setState({ value: target.value }); + const trimmedValue = value.trim(); + const { name } = parsePackageString(trimmedValue); if (trimmedValue.length > 1) { - this.getSuggestions(name) - } - } + if (isComparingPkgs(name)) { + this.getSuggestions(name); + } + else { + this.getSuggestions(name); + } + } + }; handleMenuVisibilityChange = (isOpen) => { this.setState({ isMenuVisible: isOpen }) @@ -102,7 +141,7 @@ export default class AutocompleteInput extends PureComponent { ref={ s => this.searchInput = s } value={ value } items={ suggestions } - onSelect={ (value, item) => { + onSelect={ (value, item, e) => { this.setState({ value, suggestions: [item] }) this.handleSubmit(null, null, value) } } diff --git a/client/components/AutocompleteInput/AutocompleteInput.scss b/client/components/AutocompleteInput/AutocompleteInput.scss index 6c47e5b7..9390e6e1 100644 --- a/client/components/AutocompleteInput/AutocompleteInput.scss +++ b/client/components/AutocompleteInput/AutocompleteInput.scss @@ -150,7 +150,7 @@ .autocomplete-input__suggestion-description { @include font-size-xs; - width: 100%; + max-width: 380px; min-width: 260px; overflow: hidden; white-space: nowrap; @@ -209,3 +209,16 @@ } } + +.autocomplete-input__compare-btn { + border: 1px solid; + height: 23px; + float: right; + margin-top: -30px; + border-radius: 6px; + cursor: pointer; + + &:hover { + background-color: #ccc; + } +} diff --git a/pages/index.js b/pages/index.js index 323d4f8a..957ea6e9 100644 --- a/pages/index.js +++ b/pages/index.js @@ -6,6 +6,7 @@ import Link from 'next/link' import Analytics from 'react-ga' import './index.scss' import AutocompleteInputBox from 'client/components/AutocompleteInputBox/AutocompleteInputBox' +import { isComparingPkgs } from 'utils/common.utils' export default class Home extends PureComponent { @@ -20,7 +21,14 @@ export default class Home extends PureComponent { label: value.trim().replace(/@/g, '[at]'), }) - Router.push(`/result?p=${value.trim()}`) + console.log('value --->', value); + if (isComparingPkgs(value)) { + Router.push(`/scan-results?packages=${value.split(', ').join(',')}`) + } + else { + Router.push(`/result?p=${value.trim()}`) + } + } render() { @@ -137,4 +145,4 @@ export default class Home extends PureComponent { ) } -} \ No newline at end of file +} diff --git a/utils/common.utils.js b/utils/common.utils.js index 48c89c31..2b44f6a2 100644 --- a/utils/common.utils.js +++ b/utils/common.utils.js @@ -28,4 +28,9 @@ function parsePackageString(packageString) { return { name, version, scoped } } -module.exports = { parsePackageString } +function isComparingPkgs(searchText = []) { + return searchText.split(',').length > 1; +} + +module.exports = { parsePackageString, isComparingPkgs } + From 130d1985afdd4a295b8ec40c037124ea000edc03 Mon Sep 17 00:00:00 2001 From: gokulkrishh Date: Thu, 8 Aug 2019 22:24:33 +0530 Subject: [PATCH 2/6] Add option to compare packages in search --- .../AutocompleteInput/AutocompleteInput.js | 36 ++++++++++++------- .../AutocompleteInput/AutocompleteInput.scss | 4 +-- pages/index.js | 9 ++--- utils/common.utils.js | 4 +-- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/client/components/AutocompleteInput/AutocompleteInput.js b/client/components/AutocompleteInput/AutocompleteInput.js index cd1be8f6..0afcb4e2 100644 --- a/client/components/AutocompleteInput/AutocompleteInput.js +++ b/client/components/AutocompleteInput/AutocompleteInput.js @@ -5,7 +5,7 @@ import cx from 'classnames' import './AutocompleteInput.scss' import debounce from 'debounce' -import { parsePackageString, isComparingPkgs } from 'utils/common.utils' +import { parsePackageString, isComparingPackages } from 'utils/common.utils' export default class AutocompleteInput extends PureComponent { @@ -21,11 +21,10 @@ export default class AutocompleteInput extends PureComponent { getSuggestions = debounce( value => { - if (isComparingPkgs(value)) { - const values = value.split(','); - const lastValue = values[values.length - 1]; - if (!lastValue) return; - API.getSuggestions(lastValue) + if (isComparingPackages(value)) { + const lastSearchedValue = this.getLastSearchedValue(value) + if (!lastSearchedValue) return; + API.getSuggestions(lastSearchedValue) .then(result => { this.setState({ suggestions: result }) }) @@ -40,9 +39,14 @@ export default class AutocompleteInput extends PureComponent { 150, ) - hasPackageResult(item) { - var regex = new RegExp(`\\b${item.package.name}\\b`, "g"); - return this.state.value.match(regex); + hasCompared(item) { + var packageName = new RegExp(`\\b${item.package.name}\\b`, "g"); + return this.state.value.match(packageName); + } + + getLastSearchedValue(value) { + const commaSeperatedValue = value.split(','); + return commaSeperatedValue[commaSeperatedValue.length - 1]; } renderSuggestionItem = (item, isHighlighted) => ( @@ -57,7 +61,7 @@ export default class AutocompleteInput extends PureComponent { { item.package.description } - {item.package.name && !this.hasPackageResult(item) && } +
+ {this.shouldShowCompareBtn(item) && } +
) diff --git a/client/components/AutocompleteInput/AutocompleteInput.scss b/client/components/AutocompleteInput/AutocompleteInput.scss index bd1903bb..a75b2989 100644 --- a/client/components/AutocompleteInput/AutocompleteInput.scss +++ b/client/components/AutocompleteInput/AutocompleteInput.scss @@ -163,6 +163,8 @@ @media screen and (max-width: 40em) { font-weight: $font-weight-light; + width: calc(100% - 80px); + min-width: calc(100% - 80px); } } @@ -209,12 +211,11 @@ } } - .autocomplete-input__compare-btn { border: 1px solid; height: 22px; float: right; - margin-top: -30px; + margin-top: -25px; border-radius: 4px; cursor: pointer; @@ -222,3 +223,8 @@ background-color: #ccc; } } + +.autocomplete-input__suggestion-detail { + display: flex; + flex-direction: column; +} From 2c2ce2483fbfd0d3cf31e0107db577f1082a4f5a Mon Sep 17 00:00:00 2001 From: gokulkrishh Date: Mon, 26 Aug 2019 18:42:01 +0530 Subject: [PATCH 6/6] Fix to highlight multiple packages @ version --- .../AutocompleteInput/AutocompleteInput.js | 399 +++++++++--------- .../AutocompleteInput/AutocompleteInput.scss | 4 +- utils/common.utils.js | 14 +- 3 files changed, 204 insertions(+), 213 deletions(-) diff --git a/client/components/AutocompleteInput/AutocompleteInput.js b/client/components/AutocompleteInput/AutocompleteInput.js index d7960261..6dfdcd6b 100644 --- a/client/components/AutocompleteInput/AutocompleteInput.js +++ b/client/components/AutocompleteInput/AutocompleteInput.js @@ -1,224 +1,203 @@ -import React, { PureComponent } from 'react' -import API from 'client/api' -import AutoComplete from 'react-autocomplete' -import cx from 'classnames' -import './AutocompleteInput.scss' -import debounce from 'debounce' +import React, { PureComponent } from 'react'; +import API from 'client/api'; +import AutoComplete from 'react-autocomplete'; +import cx from 'classnames'; +import './AutocompleteInput.scss'; +import debounce from 'debounce'; -import { parsePackageString, getComparisonCount, isComparingPackages } from 'utils/common.utils' +import { parsePackageString, parseComparedPackageString, getComparisonCount, isComparingPackages } from 'utils/common.utils'; export default class AutocompleteInput extends PureComponent { + static defaultProps = { + initialValue: '' + }; - static defaultProps = { - initialValue: '' - } - - state = { - value: this.props.initialValue, - suggestions: [], - isMenuVisible: false, - } - - getSuggestions = debounce( - value => { - if (isComparingPackages(value)) { - const lastSearchedValue = this.getLastSearchedValue(value) - if (!lastSearchedValue) return; - API.getSuggestions(lastSearchedValue) - .then(result => { - this.setState({ suggestions: result }) - }) - } - else { - API.getSuggestions(value) - .then(result => { - this.setState({ suggestions: result }) - }) - } - }, - 150, - ) - - hasCompared(item) { - var packageName = new RegExp(`\\b${item.package.name}\\b`, "g"); - return this.state.value.match(packageName); - } - - getLastSearchedValue(value) { - const commaSeperatedValue = value.split(','); - return commaSeperatedValue[commaSeperatedValue.length - 1]; - } - - shouldShowCompareBtn(item) { - const packageComparisonCount = getComparisonCount(this.state.value); - return packageComparisonCount > 1 && packageComparisonCount <= 3; - } - - renderSuggestionItem = (item, isHighlighted) => ( -
-
-
- -
- { item.package.description } -
-
- -
- {this.shouldShowCompareBtn(item) && } -
-
- ) - - handleSubmit = (e, e2, value) => { - const { onSearchSubmit } = this.props - - if (e) { - e.preventDefault(); - } + state = { + value: this.props.initialValue, + suggestions: [], + isMenuVisible: false + }; - onSearchSubmit(value || this.state.value) - } + getSuggestions = debounce(value => { + if (isComparingPackages(value)) { + const lastSearchedValue = this.getLastSearchedValue(value); + if (!lastSearchedValue) return; + API.getSuggestions(lastSearchedValue).then(result => { + this.setState({ suggestions: result }); + }); + } else { + API.getSuggestions(value).then(result => { + this.setState({ suggestions: result }); + }); + } + }, 150); + + hasCompared(item) { + var packageName = new RegExp(`\\b${item.package.name}\\b`, 'g'); + return this.state.value.match(packageName); + } + + getLastSearchedValue(value) { + const commaSeperatedValue = value.split(','); + return commaSeperatedValue[commaSeperatedValue.length - 1]; + } + + canComparePackages() { + const packageComparisonCount = getComparisonCount(this.state.value); + return packageComparisonCount > 1 && packageComparisonCount <= 3; + } + + renderSuggestionItem = (item, isHighlighted) => ( +
+
+
+ +
{item.package.description}
+
+ +
+ {this.canComparePackages() && ( + + )} +
+
+ ); + + handleSubmit = (e, e2, value) => { + const { onSearchSubmit } = this.props; + + if (e) { + e.preventDefault(); + } - comparePackages = (item) => { - const { value, suggestions } = this.state; + onSearchSubmit(value || this.state.value); + }; - if (value === item.package.name) return; + comparePackages = item => { + const { value, suggestions } = this.state; - if (isComparingPackages(value)) { - const commaSeperatedValue = value.split(','); - const lastSearchedValue = commaSeperatedValue[commaSeperatedValue.length - 1]; - if (lastSearchedValue === "") { - this.setState({ value: value.concat(item.package.name) }); - } - else { - commaSeperatedValue.pop(); - this.setState({ value: commaSeperatedValue.join(',').concat(`,${item.package.name}`) }); - } - } - } + if (value === item.package.name) return; - handleInputChange = ({ target }) => { - const { value } = target; - this.setState({ value }); + if (isComparingPackages(value)) { + const commaSeperatedValue = value.split(','); + const lastSearchedValue = commaSeperatedValue[commaSeperatedValue.length - 1]; + if (lastSearchedValue === '') { + this.setState({ value: value.concat(item.package.name) }); + } else { + commaSeperatedValue.pop(); + this.setState({ value: commaSeperatedValue.join(',').concat(`,${item.package.name}`) }); + } + } + }; + + handleInputChange = ({ target }) => { + const { value } = target; + this.setState({ value }); const trimmedValue = value.trim(); - const { name } = parsePackageString(trimmedValue); - - if (trimmedValue.length > 1) { - if (isComparingPackages(name)) { - this.getSuggestions(name); - } - else { - this.getSuggestions(name); - } + const { name } = parsePackageString(trimmedValue); + + if (trimmedValue.length > 1) { + if (isComparingPackages(name)) { + this.getSuggestions(name); + } else { + this.getSuggestions(name); + } } }; - handleMenuVisibilityChange = (isOpen) => { - this.setState({ isMenuVisible: isOpen }) - } - - render() { - const { className, containerClass, autoFocus } = this.props - const { suggestions, value, isMenuVisible } = this.state - const { name, version } = parsePackageString(value) - const baseFontSize = (typeof window !== 'undefined' && window.innerWidth < 640) ? - 22 : 35 - const maxFullSizeChars = (typeof window !== 'undefined' && window.innerWidth < 640) ? - 15 : 20 - const searchFontSize = value.length < maxFullSizeChars ? null : - `${baseFontSize - (value.length - maxFullSizeChars) * 0.8}px` - - return ( -
-
- item.package.name } - inputProps={ { - placeholder: 'find package', - className: 'autocomplete-input', - autoCorrect: 'off', - autoFocus: autoFocus, - autoCapitalize: 'off', - spellCheck: false, - style: { fontSize: searchFontSize }, - } } - onMenuVisibilityChange={ this.handleMenuVisibilityChange } - onChange={ this.handleInputChange } - ref={ s => this.searchInput = s } - value={ value } - items={ suggestions } - onSelect={ (value, item, e) => { - this.setState({ value, suggestions: [item] }) - this.handleSubmit(null, null, value) - } } - renderMenu={ - (items, value, inbuiltStyles) => { - return ( -
- ) - } - } - wrapperStyle={ { - display: 'inline-block', - width: '100%', - position: 'relative', - } } - renderItem={ this.renderSuggestionItem } - /> -
- - { name } - - { - version !== null && ( - - @ - - ) - } - - { version } - -
-
-
- - - -
- - ) - } + handleMenuVisibilityChange = isOpen => { + this.setState({ isMenuVisible: isOpen }); + }; + + render() { + const { className, containerClass, autoFocus } = this.props; + const { suggestions, value, isMenuVisible } = this.state; + const { name, version } = parsePackageString(value); + let parsedPackageString = []; + if (isComparingPackages(value)) { + parsedPackageString = parseComparedPackageString(value); + } + + const baseFontSize = typeof window !== 'undefined' && window.innerWidth < 640 ? 22 : 35; + const maxFullSizeChars = typeof window !== 'undefined' && window.innerWidth < 640 ? 15 : 20; + const searchFontSize = value.length < maxFullSizeChars ? null : `${baseFontSize - (value.length - maxFullSizeChars) * 0.8}px`; + + return ( +
+
+ item.package.name} + inputProps={{ + placeholder: 'find package', + className: 'autocomplete-input', + autoCorrect: 'off', + autoFocus: autoFocus, + autoCapitalize: 'off', + spellCheck: false, + style: { fontSize: searchFontSize } + }} + onMenuVisibilityChange={this.handleMenuVisibilityChange} + onChange={this.handleInputChange} + ref={s => (this.searchInput = s)} + value={value} + items={suggestions} + onSelect={(value, item, e) => { + this.setState({ value, suggestions: [item] }); + this.handleSubmit(null, null, value); + }} + renderMenu={(items, value, inbuiltStyles) => { + return
; + }} + wrapperStyle={{ + display: 'inline-block', + width: '100%', + position: 'relative' + }} + renderItem={this.renderSuggestionItem} + /> +
+ {parsedPackageString.length > 0 && + parsedPackageString.map((parsedPackage, index) => { + return ( +
+ {parsedPackage.name} + {parsedPackage.hasAt} + {parsedPackage.version} +
+ ); + })} + {!parsedPackageString.length && ( + <> + {name} + {version !== null && @} + {version} + + )} +
+
+
+ + + +
+ + ); + } } diff --git a/client/components/AutocompleteInput/AutocompleteInput.scss b/client/components/AutocompleteInput/AutocompleteInput.scss index a75b2989..cee41367 100644 --- a/client/components/AutocompleteInput/AutocompleteInput.scss +++ b/client/components/AutocompleteInput/AutocompleteInput.scss @@ -44,7 +44,7 @@ .autocomplete-input, .autocomplete-input__dummy-input { @include font-size-lg; - padding: $global-spacing * 1.5 (25px + $global-spacing * 2) $global-spacing * 1.5 $global-spacing * 3; + padding: $global-spacing * 1.5 (35px + $global-spacing * 2) $global-spacing * 1.5 $global-spacing * 3; font-family: $font-family-code; font-weight: $font-weight-thin; width: 100%; @@ -215,7 +215,7 @@ border: 1px solid; height: 22px; float: right; - margin-top: -25px; + margin-top: -30px; border-radius: 4px; cursor: pointer; diff --git a/utils/common.utils.js b/utils/common.utils.js index 694243d2..f81f0965 100644 --- a/utils/common.utils.js +++ b/utils/common.utils.js @@ -36,5 +36,17 @@ function parsePackageString(packageString) { return { name, version, scoped } } -module.exports = { parsePackageString, isComparingPackages, getComparisonCount } +function parseComparedPackageString(packageString) { + const commaSeperatedStr = packageString.split(','); + const commas = Array(commaSeperatedStr.length - 1).fill(','); + return commaSeperatedStr.map(str => { + const [name, versionNumber] = str.split('@'); + const hasAt = str.match(/@/) ? `@` : ''; + const comma = commas.length ? commas.pop() : ''; + const version = `${versionNumber ? versionNumber : ''}${comma}`; + return { name, version, hasAt }; + }); +} + +module.exports = { parsePackageString, isComparingPackages, getComparisonCount, parseComparedPackageString }