mirror of
https://github.com/fergalmoran/turnstone.git
synced 2026-03-07 00:06:43 +00:00
Remove text splitting and make text matching an option with a prop
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
60
src/App.jsx
60
src/App.jsx
@@ -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>
|
||||
<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>
|
||||
<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}
|
||||
/>
|
||||
|
||||
<label htmlFor="autocomplete">Search Fruits & Veg:</label>
|
||||
<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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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}</>
|
||||
}
|
||||
|
||||
@@ -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
|
||||
})
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user