Remove text splitting and make text matching an option with a prop

This commit is contained in:
Tom Southall
2022-02-23 22:59:20 +00:00
parent a1242eec5d
commit 76bf7f2f99
9 changed files with 60 additions and 67 deletions

View File

@@ -7,7 +7,6 @@ import Item from './components/item/item'
const maxItems = 10
const placeholder = 'Enter a city or airport'
const separator = ','
const noItemsMessage = 'We found no places that match your search'
const listbox = [
@@ -70,7 +69,6 @@ const App = () => {
onEnter={onEnter}
onTab={onTab}
placeholder={placeholder}
separator={separator}
styles={autocompleteStyles}
/>
</div>

View File

@@ -39,7 +39,7 @@ export default function Item(props) {
globalSplit={false}
caseSensitiveMatch={false}
caseSensitiveSplit={false}
separator=", "
separator=","
includeSeparator={includeSeparator}
MatchComponent={MatchComponent}
SplitComponent={SplitComponent}>{item.name}</SplitMatch>

View File

@@ -69,17 +69,3 @@
background-color: #f0f0f0;
}
/* Get rid */
.topItem {
font-size: 18px;
}
/* Get rid */
.split:not(:first-child) {
font-size: 15px;
}
.match {
font-weight: bold
}

View File

@@ -1,32 +1,62 @@
import React from 'react'
import Turnstone from './lib'
import { fruits } from './data'
import { fruits, vegetables } from './data'
const styles = {
highlightedItem: 'highlightedItem'
}
const listbox = {
const listbox1 = {
data: fruits,
dataSearchType: 'startswith'
}
const listbox2 = [
{
data: fruits,
dataSearchType: 'startswith',
name: 'Fruits'
},
{
data: vegetables,
dataSearchType: 'contains',
name: 'Vegetables'
}
]
const App = () => {
return (
<>
<label htmlFor="autocomplete">Search:</label>&nbsp;
<Turnstone
autoFocus={true}
clearButton={true}
debounceWait={0}
listbox={listbox}
listboxIsImmutable={true}
maxItems={10}
name={'search'}
noItemsMessage={'No matching fruit found'}
placeholder={'Type something fruity'}
styles={styles}
/>
<div>
<label htmlFor="autocomplete">Search Fruits:</label>&nbsp;
<Turnstone
autoFocus={true}
clearButton={true}
debounceWait={0}
listbox={listbox1}
listboxIsImmutable={true}
maxItems={10}
name={'search'}
noItemsMessage={'No matching fruit found'}
placeholder={'Type something fruity'}
styles={styles}
/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<label htmlFor="autocomplete">Search Fruits &amp; Veg:</label>&nbsp;
<Turnstone
autoFocus={true}
clearButton={true}
debounceWait={0}
listbox={listbox2}
listboxIsImmutable={true}
matchText={true}
maxItems={10}
name={'search'}
noItemsMessage={'No matches found'}
placeholder={'Type something'}
styles={styles}
/>
</div>
</>
)
}

View File

@@ -2,7 +2,6 @@ import React, { useContext } from 'react'
import defaultStyles from './styles/item.styles.js'
import MatchingText from './matchingText'
import { StateContext } from '../context/state'
import escapeStringRegExp from 'escape-string-regexp'
import { setHighlighted, setSelected } from '../actions/actions'
export default function Item(props) {
@@ -13,20 +12,9 @@ export default function Item(props) {
dispatch
} = useContext(StateContext)
const { customStyles, highlighted, separator, query } = state
const { customStyles, highlighted, query } = state
const CustomItem = state.props.itemComponent
const startsWith = item.dataSearchType !== 'contains'
const split = (str, separator) => {
if(!separator) return [str]
const regex = new RegExp(`(${escapeStringRegExp(separator)})`, 'g')
return str.split(regex).filter(part => part.length)
}
const splitText = split(item.text, separator)
const splitQuery = split(query, separator)
const globalMatch = item.dataSearchType === 'contains'
const isHighlighted = highlighted && index === highlighted.index
const divClassName = () => {
@@ -49,11 +37,6 @@ export default function Item(props) {
dispatch(setSelected(index))
}
const matchedText = splitText.map((part, index) => {
const match = startsWith ? (splitQuery[index] || '') : query
return <MatchingText key={`split${index}`} text={part} match={match} startsWith={startsWith} />
})
const itemContents = (CustomItem)
? <CustomItem
appearsInDefaultListbox={item.defaultListbox}
@@ -64,7 +47,9 @@ export default function Item(props) {
searchType={item.dataSearchType}
totalItems={state.items.length}
/>
: matchedText
: (state.props.matchText
? <MatchingText text={item.text} match={query} global={globalMatch} />
: <>{item.text}</>)
return (
<div

View File

@@ -9,8 +9,7 @@ const customStyles = {
item: 'item-class',
highlightedItem: 'highlighted-item-class',
topItem: 'top-item-class',
split: 'split-class',
separator: 'separator-class'
split: 'split-class'
}
const item = {
text: 'Chicago, Illinois, United States',

View File

@@ -1,23 +1,19 @@
import React, { useContext } from 'react'
import { StateContext } from '../context/state'
import React from 'react'
import escapeStringRegExp from 'escape-string-regexp'
export default function MatchingText(props) {
const { text, match, startsWith } = props
const { state } = useContext(StateContext)
const { customStyles } = state
const patternPrefix = startsWith ? '^' : ''
const { text, match, global } = props
const patternPrefix = global ? '' : '^'
const pattern = `${patternPrefix}(${escapeStringRegExp(match)})`
const regex = new RegExp(pattern, 'i')
const partsRaw = match ? text.split(regex) : [text]
const parts = partsRaw.filter(part => part.length)
const parts = match ? text.split(regex).filter(part => part.length) : [text]
const matchingText = parts.map((part, index) => {
const isMatch = part.toLowerCase() === match.toLowerCase()
return (isMatch)
? <span key={`part${index}`} className={customStyles.match}>{parts[index]}</span>
? <strong key={`part${index}`}>{parts[index]}</strong>
: <React.Fragment key={`part${index}`}>{parts[index]}</React.Fragment>
})
return <span className={customStyles.split}>{matchingText}</span>
return <>{matchingText}</>
}

View File

@@ -6,7 +6,7 @@ import undef from '../utils/undef'
const StateContext = createContext()
const StateContextProvider = (props) => {
const { separator, styles = {}, text = '', items = [] } = props
const { styles = {}, text = '', items = [] } = props
const { children, ...propsMinusChildren} = props
const [state, dispatch] = useReducer(reducer, {
query: text,
@@ -15,7 +15,6 @@ const StateContextProvider = (props) => {
highlighted: undef,
selected: undef,
customStyles: styles,
separator,
props: propsMinusChildren
})

View File

@@ -76,6 +76,7 @@ Turnstone.propTypes = {
itemComponent: PropTypes.elementType,
listbox: listboxRules.isRequired,
listboxIsImmutable: PropTypes.bool,
matchText: PropTypes.bool,
minQueryLength: (props) => {
PropTypes.checkPropTypes(
{minQueryLength: PropTypes.number},
@@ -95,7 +96,6 @@ Turnstone.propTypes = {
onTab: PropTypes.func,
placeholder: PropTypes.string,
maxItems: PropTypes.number,
separator: PropTypes.string,
styles: PropTypes.object,
tabIndex: PropTypes.number,
text: PropTypes.string