### Turnstone Usage - Barebones Example Source: https://github.com/tomsouthall/turnstone/blob/main/README.md A basic example of how to use the Turnstone component with minimal configuration. ```APIDOC ## Usage - Barebones Example This example demonstrates a barebones, unstyled Turnstone component. ### Code ```jsx import React from 'react' import Turnstone from 'turnstone' const App = () => { const listbox = { data: ['Peach', 'Pear', 'Pineapple', 'Plum', 'Pomegranate', 'Prune'] } return ( ) } ``` ``` -------------------------------- ### Turnstone Installation Source: https://github.com/tomsouthall/turnstone/blob/main/README.md Install Turnstone using npm. ```APIDOC ## Installation Install Turnstone using npm: ```bash $ npm install --save turnstone ``` ``` -------------------------------- ### Implement Production Search Component with Turnstone Source: https://context7.com/tomsouthall/turnstone/llms.txt This example demonstrates a full-featured Turnstone implementation, including asynchronous data fetching from multiple sources, custom styling via Tailwind CSS classes, and event handling for selection and submission. It utilizes React hooks for state management and provides a robust configuration for search behavior. ```jsx import React, { useState, useCallback, useRef } from 'react' import Turnstone from 'turnstone' const styles = { container: 'relative w-full max-w-xl', input: 'w-full h-12 px-4 text-lg border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500', listbox: 'absolute w-full mt-1 bg-white border border-gray-200 rounded-lg shadow-lg max-h-80 overflow-y-auto z-50', groupHeading: 'px-4 py-2 text-xs font-semibold text-gray-500 uppercase bg-gray-50', item: 'px-4 py-3 cursor-pointer hover:bg-gray-100', highlightedItem: 'px-4 py-3 cursor-pointer bg-blue-50', clearButton: 'absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600', noItems: 'px-4 py-3 text-gray-500 italic' } const App = () => { const ref = useRef() const [selected, setSelected] = useState(null) const listbox = [ { id: 'cities', name: 'Cities', ratio: 7, displayField: 'name', data: async (query) => { const response = await fetch(`/api/cities?q=${encodeURIComponent(query)}&limit=10`) if (!response.ok) throw new Error('API Error') return response.json() }, searchType: 'startswith' }, { id: 'airports', name: 'Airports', ratio: 3, displayField: 'name', data: async (query) => { const response = await fetch(`/api/airports?q=${encodeURIComponent(query)}&limit=10`) if (!response.ok) throw new Error('API Error') return response.json() }, searchType: 'contains' } ] const defaultListbox = [ { name: 'Popular Destinations', displayField: 'name', data: [ { name: 'New York, USA', code: 'NYC' }, { name: 'London, UK', code: 'LON' }, { name: 'Paris, France', code: 'PAR' }, { name: 'Tokyo, Japan', code: 'TYO' } ] } ] const handleSelect = useCallback((item, displayField) => { if (item) { setSelected(item) console.log('Selected:', item[displayField]) } }, []) const handleEnter = useCallback((query, selectedItem) => { if (selectedItem) { console.log('Search submitted:', selectedItem.name) } }, []) return (
{selected && (
Selected: {selected.name}
)}
) } export default App ``` -------------------------------- ### Turnstone Usage - Styled Example with Grouped Results Source: https://github.com/tomsouthall/turnstone/blob/main/README.md An example of a styled Turnstone component fetching grouped search results from two API sources. ```APIDOC ## Usage - Styled Example with Grouped Results This example shows a styled Turnstone component configured to fetch and display grouped search results from multiple API endpoints. ### Code ```jsx import React, { useState } from 'react' import Turnstone from 'turnstone' const styles = { input, inputFocus, query, typeahead, cancelButton, clearButton, listbox, groupHeading, item, highlightedItem } const maxItems = 10 const listbox = [ { id: 'cities', name: 'Cities', ratio: 8, displayField: 'name', data: (query) => fetch(`/api/cities?q=${encodeURIComponent(query)}&limit=${maxItems}`).then(response => response.json()), searchType: 'startswith' }, { id: 'airports', name: 'Airports', ratio: 2, displayField: 'name', data: (query) => fetch(`/api/airports?q=${encodeURIComponent(query)}&limit=${maxItems}`).then(response => response.json()), searchType: 'contains' } ] export default function Example() { return ( ) } ``` ``` -------------------------------- ### Dynamic Listbox with Function in Turnstone Source: https://context7.com/tomsouthall/turnstone/llms.txt This example shows how to use a function to dynamically generate the listbox content in Turnstone. This is useful for scenarios involving GraphQL or when combining results from multiple API endpoints, allowing for complex data structuring. ```jsx import React from 'react' import Turnstone from 'turnstone' const listbox = (query) => { return fetch(`/api/search/locations?q=${encodeURIComponent(query)}`) .then(response => response.json()) .then(locations => { const { cities, airports, hotels } = locations return [ { id: 'cities', name: 'Cities', ratio: 5, displayField: 'name', data: cities, searchType: 'startswith' }, { id: 'airports', name: 'Airports', ratio: 3, displayField: 'name', data: airports, searchType: 'contains' }, { id: 'hotels', name: 'Hotels', ratio: 2, displayField: 'name', data: hotels, searchType: 'contains' } ] }) } const App = () => ( ) export default App ``` -------------------------------- ### Install Turnstone via NPM Source: https://context7.com/tomsouthall/turnstone/llms.txt The command to install the Turnstone package into your React project using the npm package manager. ```bash npm install --save turnstone ``` -------------------------------- ### Using Turnstone Component Methods in React Source: https://github.com/tomsouthall/turnstone/blob/main/README.md This snippet demonstrates how to use the `useRef` hook in React to get a reference to the Turnstone component and call its methods like `query` and `clear`. It shows how to attach event handlers to buttons to trigger these methods. ```jsx import React, { useRef } from 'react' import Turnstone from 'turnstone' import data from './data' const App = () => { const listbox = { data } const turnstoneRef = useRef() const handleQuery = () => { turnstoneRef.current?.query('new') } const handleClear = () => { turnstoneRef.current?.clear() } return ( <> ) } ``` -------------------------------- ### Customizing Clear and Cancel Buttons in Turnstone Source: https://context7.com/tomsouthall/turnstone/llms.txt This example demonstrates how to replace the default clear and cancel buttons in Turnstone with custom React components. It shows how to define custom SVG components and pass them as props to the Turnstone component for a personalized UI. ```jsx import React from 'react' import Turnstone from 'turnstone' const CustomClearButton = () => ( ) const CustomCancelButton = () => ( ) const listbox = { data: ['Apple', 'Banana', 'Cherry', 'Date'], searchType: 'contains' } const App = () => ( ) export default App ``` -------------------------------- ### Configure Turnstone Element Styles with Tailwind CSS Source: https://github.com/tomsouthall/turnstone/blob/main/README.md This snippet demonstrates how to define custom CSS classes for different Turnstone UI elements using Tailwind CSS. It shows an example object where keys map to specific elements like 'input', 'listbox', and 'item', and values are strings of Tailwind class names to style these elements. This allows for extensive visual customization of the search input and dropdown. ```jsx { input: 'w-full h-12 border border-slate-300 py-2 pl-10 pr-7 text-xl outline-none rounded', inputFocus: 'w-full h-12 border-x-0 border-t-0 border-b border-blue-300 py-2 pl-10 pr-7 text-xl outline-none sm:rounded sm:border', query: 'text-slate-800 placeholder-slate-400', typeahead: 'text-blue-300 border-white', cancelButton: `absolute w-10 h-12 inset-y-0 left-0 items-center justify-center z-10 text-blue-400 inline-flex sm:hidden`, clearButton: 'absolute inset-y-0 right-0 w-8 inline-flex items-center justify-center text-slate-400 hover:text-rose-400', listbox: 'w-full bg-white sm:border sm:border-blue-300 sm:rounded text-left sm:mt-2 p-2 sm:drop-shadow-xl', groupHeading: 'cursor-default mt-2 mb-0.5 px-1.5 uppercase text-sm text-rose-300', item: 'cursor-pointer p-1.5 text-lg overflow-ellipsis overflow-hidden text-slate-700', highlightedItem: 'cursor-pointer p-1.5 text-lg overflow-ellipsis overflow-hidden text-slate-700 rounded bg-blue-50' } ``` -------------------------------- ### Implement Basic Static Autocomplete Source: https://context7.com/tomsouthall/turnstone/llms.txt Demonstrates the simplest implementation of Turnstone using a static array of strings for local filtering. ```jsx import React from 'react' import Turnstone from 'turnstone' const App = () => { const listbox = { data: ['Peach', 'Pear', 'Pineapple', 'Plum', 'Pomegranate', 'Prune'], searchType: 'startswith' } return } export default App ``` -------------------------------- ### Configure Turnstone Plugins Source: https://github.com/tomsouthall/turnstone/blob/main/README.md Demonstrates how to register plugins for the Turnstone component. Plugins can be passed as simple strings or as arrays containing the plugin name and an options object. ```jsx [ ['plugin1', { option1: true, option2: 'foo' }], 'plugin2' ] ``` -------------------------------- ### Basic Turnstone Usage in React Source: https://github.com/tomsouthall/turnstone/blob/main/README.md Demonstrates a barebones, unstyled implementation of the Turnstone search component in a React application. It shows the basic import and rendering of the component with a simple list of data. ```jsx import React from 'react' import Turnstone from 'turnstone' const App = () => { const listbox = { data: ['Peach', 'Pear', 'Pineapple', 'Plum', 'Pomegranate', 'Prune'] } return ( ) } ``` -------------------------------- ### Configuring Listbox Data Source Source: https://github.com/tomsouthall/turnstone/blob/main/README.md This section explains how to provide data to the listbox, either as a static array or dynamically via a function. ```APIDOC ## Providing Data to the Listbox ### Description Configure the listbox's data source. This can be a static object for ungrouped items or a function for dynamic data fetching. ### Method Configuration Object ### Endpoint N/A ### Parameters #### Request Body - **`displayField`** (string) - Required - The field to display for each item. - **`data`** (array | function) - Required - An array of items or a function that returns a promise resolving to an array of items. - **`searchType`** (string) - Optional - The type of search to perform ('startswith' or 'contains'). Defaults to 'startswith'. ### Request Example (Ungrouped Items) ```json { "displayField": "name", "data": (query) => fetch(`/api/cities?q=${encodeURIComponent(query)}&limit=10`).then(res => res.json()), "searchType": "startswith" } ``` ### Request Example (Grouped Items via Function) ```javascript (query) => fetch(`/api/locations?q=${encodeURIComponent(query)}`) .then(res => res.json()) .then(locations => { const {cities, airports} = locations; return [ { id: 'cities', name: 'Cities', ratio: 8, displayField: 'name', data: cities, searchType: 'startswith' }, { id: 'airports', name: 'Airports', ratio: 2, displayField: 'name', data: airports, searchType: 'contains' } ]; }); ``` ### Response #### Success Response (200) An array of items or an array of group objects, each containing an array of items. #### Response Example (Ungrouped) ```json [ {"id": 1, "name": "New York"}, {"id": 2, "name": "Los Angeles"} ] ``` #### Response Example (Grouped) ```json [ { "id": "cities", "name": "Cities", "ratio": 8, "displayField": "name", "data": [ {"id": 1, "name": "New York"}, {"id": 2, "name": "Los Angeles"} ], "searchType": "startswith" }, { "id": "airports", "name": "Airports", "ratio": 2, "displayField": "name", "data": [ {"id": 101, "name": "JFK"}, {"id": 102, "name": "LAX"} ], "searchType": "contains" } ] ``` ``` -------------------------------- ### Configure Default Listbox with Popular Items Source: https://context7.com/tomsouthall/turnstone/llms.txt Demonstrates how to define a default listbox that displays popular or recent items when the search input is focused but empty. It uses a combination of static data and asynchronous local storage lookups. ```jsx import React from 'react' import Turnstone from 'turnstone' const defaultListbox = [ { name: 'Recent Searches', displayField: 'name', id: 'recent', ratio: 1, data: () => Promise.resolve(JSON.parse(localStorage.getItem('recent')) || []) }, { name: 'Popular Cities', displayField: 'name', id: 'popular', ratio: 1, data: [ { name: 'Paris, France', coords: '48.86425, 2.29416' }, { name: 'Rome, Italy', coords: '41.89205, 12.49209' }, { name: 'Orlando, Florida, United States', coords: '28.53781, -81.38592' }, { name: 'London, England', coords: '51.50420, -0.12426' }, { name: 'Barcelona, Spain', coords: '41.40629, 2.17555' } ] } ] const listbox = { displayField: 'name', data: (query) => fetch(`/api/cities?q=${encodeURIComponent(query)}`).then(res => res.json()), searchType: 'startswith' } const App = () => ( ) export default App ``` -------------------------------- ### Control Turnstone Programmatically via Refs Source: https://context7.com/tomsouthall/turnstone/llms.txt Demonstrates how to use React refs to access internal Turnstone methods like focus, blur, clear, and query. This is useful for building custom UI controls that interact with the search component. ```jsx import React, { useRef } from 'react' import Turnstone from 'turnstone' const App = () => { const turnstoneRef = useRef() const listbox = { data: ['New York', 'Los Angeles', 'Chicago', 'Houston', 'Phoenix'], searchType: 'contains' } return (
) } export default App ``` -------------------------------- ### Configure Grouped API Results Source: https://context7.com/tomsouthall/turnstone/llms.txt Shows how to fetch and display results from multiple API endpoints with weighted display ratios and custom selection handling. ```jsx import React from 'react' import Turnstone from 'turnstone' const maxItems = 10 const listbox = [ { id: 'cities', name: 'Cities', ratio: 8, displayField: 'name', data: (query) => fetch(`/api/cities?q=${encodeURIComponent(query)}&limit=${maxItems}`) .then(response => response.json()), searchType: 'startswith' }, { id: 'airports', name: 'Airports', ratio: 2, displayField: 'name', data: (query) => fetch(`/api/airports?q=${encodeURIComponent(query)}&limit=${maxItems}`) .then(response => response.json()), searchType: 'contains' } ] const App = () => { const handleSelect = (selectedItem, displayField) => { console.log('Selected:', selectedItem[displayField]) } return ( ) } export default App ``` -------------------------------- ### Apply Custom Styles with Tailwind CSS Source: https://context7.com/tomsouthall/turnstone/llms.txt Demonstrates how to customize the component's appearance using the styles prop with Tailwind CSS utility classes. ```jsx import React from 'react' import Turnstone from 'turnstone' const styles = { input: 'w-full h-12 border border-slate-300 py-2 pl-10 pr-7 text-xl outline-none rounded', inputFocus: 'w-full h-12 border-x-0 border-t-0 border-b border-blue-300 py-2 pl-10 pr-7 text-xl outline-none sm:rounded sm:border', query: 'text-slate-800 placeholder-slate-400', typeahead: 'text-blue-300 border-white', cancelButton: 'absolute w-10 h-12 inset-y-0 left-0 items-center justify-center z-10 text-blue-400 inline-flex sm:hidden', clearButton: 'absolute inset-y-0 right-0 w-8 inline-flex items-center justify-center text-slate-400 hover:text-rose-400', listbox: 'w-full bg-white sm:border sm:border-blue-300 sm:rounded text-left sm:mt-2 p-2 sm:drop-shadow-xl', groupHeading: 'cursor-default mt-2 mb-0.5 px-1.5 uppercase text-sm text-rose-300', item: 'cursor-pointer p-1.5 text-lg overflow-ellipsis overflow-hidden text-slate-700', highlightedItem: 'cursor-pointer p-1.5 text-lg overflow-ellipsis overflow-hidden text-slate-700 rounded bg-blue-50', match: 'font-bold' } const listbox = { data: ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry'], searchType: 'contains' } const App = () => ( ) export default App ``` -------------------------------- ### Extending Turnstone with Plugins Source: https://context7.com/tomsouthall/turnstone/llms.txt This snippet illustrates how to extend Turnstone's functionality using plugins, specifically the 'turnstone-recent-searches' plugin. It demonstrates how to integrate plugins to add features like storing and displaying recent search history. ```jsx import React from 'react' import Turnstone from 'turnstone' import recentSearchesPlugin from 'turnstone-recent-searches' const listbox = { displayField: 'name', data: (query) => fetch(`/api/cities?q=${query}`).then(res => res.json()), searchType: 'startswith' } const defaultListbox = [ { name: 'Popular Cities', displayField: 'name', data: [ { name: 'Paris, France' }, { name: 'London, England' }, { name: 'Tokyo, Japan' } ] } ] const App = () => ( ) export default App ``` -------------------------------- ### Configure Listbox with Multiple Data Groups (JavaScript/JSX) Source: https://github.com/tomsouthall/turnstone/blob/main/README.md This snippet demonstrates how to configure the 'listbox' prop using an array of objects. Each object represents a group of search results, specifying its data source (as a function returning a Promise), display field, search type, ratio, and name. This allows for multiple, distinct groups of results to be displayed in the search listbox, drawing from different API endpoints. ```jsx [ { id: 'cities', name: 'Cities', ratio: 8, displayField: 'name', data: (query) => fetch(`/api/cities?q=${encodeURIComponent(query)}&limit=10`).then(res => res.json()), searchType: 'startswith' }, { id: 'airports', name: 'Airports', ratio: 2, displayField: 'name', data: (query) => fetch(`/api/airports?q=${encodeURIComponent(query)}&limit=10`).then(res => res.json()), searchType: 'contains' } ] ``` -------------------------------- ### Implement Custom Item Component Source: https://context7.com/tomsouthall/turnstone/llms.txt Shows how to create a custom Item component to render search results with specific styling, icons, and conditional formatting. This allows for rich visual representation of data within the Turnstone dropdown. ```jsx import React from 'react' import Turnstone from 'turnstone' const CustomItem = (props) => { const { groupId, item, query } = props const highlightMatch = (text) => { if (!query) return text const regex = new RegExp(`(${query})`, 'gi') return text.split(regex).map((part, i) => regex.test(part) ? {part} : part ) } return (
{groupId === 'cities' ? '🏙️' : '✈️'}
{highlightMatch(item.name)}
{item.country && (
{item.country}
)}
) } const listbox = [ { id: 'cities', name: 'Cities', ratio: 7, displayField: 'name', data: (query) => fetch(`/api/cities?q=${query}`).then(res => res.json()), searchType: 'startswith' }, { id: 'airports', name: 'Airports', ratio: 3, displayField: 'name', data: (query) => fetch(`/api/airports?q=${query}`).then(res => res.json()), searchType: 'contains' } ] const App = () => ( ) export default App ``` -------------------------------- ### Handling User Events in Turnstone Source: https://context7.com/tomsouthall/turnstone/llms.txt This snippet demonstrates how to handle various user interaction events in Turnstone, such as select, change, enter, tab, focus, and blur. It logs these events to the console, allowing developers to react to user input and selections. ```jsx import React, { useState, useCallback } from 'react' import Turnstone from 'turnstone' const App = () => { const [events, setEvents] = useState([]) const logEvent = (name, data) => { setEvents(prev => [...prev, { name, data, time: new Date().toISOString() }]) } const listbox = { displayField: 'name', data: (query) => fetch(`/api/cities?q=${query}`).then(res => res.json()), searchType: 'startswith' } const onSelect = useCallback((item, displayField) => { logEvent('onSelect', item ? item[displayField] : null) }, []) const onChange = useCallback((query) => { logEvent('onChange', query) }, []) const onEnter = useCallback((query, selectedItem) => { logEvent('onEnter', { query, selected: selectedItem?.name }) }, []) const onTab = useCallback((query, selectedItem) => { logEvent('onTab', { query, selected: selectedItem?.name }) }, []) const onFocus = useCallback(() => { logEvent('onFocus', null) }, []) const onBlur = useCallback(() => { logEvent('onBlur', null) }, []) return (

Events:

    {events.slice(-5).map((e, i) => (
  • {e.name}: {JSON.stringify(e.data)}
  • ))}
) } export default App ``` -------------------------------- ### Turnstone Component Methods API Source: https://github.com/tomsouthall/turnstone/blob/main/README.md This section lists and describes the available methods for the Turnstone component. These methods allow for programmatic control over the component's state and behavior, such as managing focus, clearing input, and performing searches. ```javascript blur(): Removes keyboard focus from the search box. clear(): Clears the contents of the search box. focus(): Sets keyboard focus on the search box. query(): Sets the search box contents to the string argument supplied to the function. select(): Selects the contents of the search box. ``` -------------------------------- ### Configure Default Listbox with Object Data Source: https://github.com/tomsouthall/turnstone/blob/main/README.md Sets up the default listbox with a single, ungrouped list of items. This configuration is suitable when you have a flat data structure or a single source for your default suggestions. The data is typically fetched from an API endpoint. ```jsx { displayField: 'name', data: () => fetch(`/api/cities/popular`).then(res => res.json()) } ``` -------------------------------- ### Listbox Behavior Props Source: https://github.com/tomsouthall/turnstone/blob/main/README.md Configuration options that control the behavior and appearance of the listbox. ```APIDOC ## Listbox Behavior Properties ### Description These properties allow fine-grained control over the listbox's behavior, including immutability, text matching, item limits, and event handling. ### Method Configuration Props ### Endpoint N/A ### Parameters #### Query Parameters - **`listboxIsImmutable`** (boolean) - Optional - Default: `true` - If `true`, listbox contents never change between queries. Set to `false` if results can vary for the same query. - **`matchText`** (boolean) - Optional - Default: `false` - If `true`, matching text in list items is wrapped in `` tags. Behavior depends on `searchType`. - **`maxItems`** (number) - Optional - Default: `10` - Maximum number of items to display in the listbox. Applies to total items across all groups. - **`minQueryLength`** (number) - Optional - Default: `1` - Minimum characters required in the search box before results are fetched. Must be greater than 0. - **`name`** (string) - Optional - Default: `undefined` - The HTML `name` attribute for the search box. - **`noItemsMessage`** (string) - Optional - Default: `undefined` - Message displayed when no items match the query. If not provided, the listbox is hidden. #### Event Handlers - **`onBlur`** (function) - Optional - Default: `undefined` - Callback executed when the search box loses focus. No arguments. - **`onChange`** (function) - Optional - Default: `undefined` - Callback executed when the search box value changes. Receives `query` (string) as an argument. - **`onEnter`** (function) - Optional - Default: `undefined` - Callback executed when the Enter key is pressed in the search box. Receives `query` (string) and `selectedItem` as arguments. - **`onFocus`** (function) - Optional - Default: `undefined` - Callback executed when the search box gains focus. No arguments. ### Request Example ```javascript { listboxIsImmutable: false, matchText: true, maxItems: 15, minQueryLength: 2, name: "citySearch", noItemsMessage: "No cities found.", onBlur: () => console.log('Blurred'), onChange: (query) => console.log(`Query changed: ${query}`), onEnter: (query, selectedItem) => console.log(`Entered: ${query}, Selected: ${selectedItem}`), onFocus: () => console.log('Focused') } ``` ### Response #### Success Response (200) N/A (These are configuration props, not an API endpoint response) #### Response Example N/A ``` -------------------------------- ### Configure Dynamic Grouped Listbox Data Source Source: https://github.com/tomsouthall/turnstone/blob/main/README.md Uses a function to dynamically build grouped listbox contents. This is useful for complex data sources like GraphQL where results are returned in categories. ```jsx (query) => fetch(`/api/locations?q=${encodeURIComponent(query)}`) .then(res => res.json()) .then(locations => { const {cities, airports} = locations return [ { id: 'cities', name: 'Cities', ratio: 8, displayField: 'name', data: cities, searchType: 'startswith' }, { id: 'airports', name: 'Airports', ratio: 2, displayField: 'name', data: airports, searchType: 'contains' } ] }) ``` -------------------------------- ### Custom Renderable Components Source: https://github.com/tomsouthall/turnstone/blob/main/README.md Details on how to provide custom React components for rendering elements within the Turnstone component, such as cancel buttons, clear buttons, list items, and group names. ```APIDOC ## Custom Components The following custom components can also be supplied as props: ### `Cancel` - Type: React component - Default: `() => 'Cancel'` - This component is rendered within the cancel `