mirror of
https://github.com/fergalmoran/turnstone.git
synced 2025-12-22 09:49:56 +00:00
Expose DOM methods via a forwarded ref
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
### 1.4.0 (6 Apr 2022)
|
||||||
|
- Expose DOM methods via a forwarded ref
|
||||||
|
|
||||||
### 1.3.0 (2 Apr 2022)
|
### 1.3.0 (2 Apr 2022)
|
||||||
- Extend listbox and defaultListbox prop types to allow functions returning a promise resolving to an array of group settings
|
- Extend listbox and defaultListbox prop types to allow functions returning a promise resolving to an array of group settings
|
||||||
|
|
||||||
|
|||||||
57
README.md
57
README.md
@@ -11,6 +11,7 @@ Turnstone is a highly customisable, easy-to-use autocomplete search component fo
|
|||||||
- [API](#api)
|
- [API](#api)
|
||||||
- [Props](#props)
|
- [Props](#props)
|
||||||
- [Component Props](#component-props)
|
- [Component Props](#component-props)
|
||||||
|
- [Methods](#methods)
|
||||||
- [Contributing](#contributing)
|
- [Contributing](#contributing)
|
||||||
- [Release Checklist](#release-checklist)
|
- [Release Checklist](#release-checklist)
|
||||||
- [License](#license)
|
- [License](#license)
|
||||||
@@ -624,6 +625,62 @@ The following custom components can also be supplied as props:
|
|||||||
- **`id`** (string) The `id` of the group supplied in the `listbox` or `defaultListbox` prop.
|
- **`id`** (string) The `id` of the group supplied in the `listbox` or `defaultListbox` prop.
|
||||||
- **`index`** (number) The index of the group. Matches the order supplied in the `listbox` or `defaultListbox` prop. Zero-indexed.
|
- **`index`** (number) The index of the group. Matches the order supplied in the `listbox` or `defaultListbox` prop. Zero-indexed.
|
||||||
|
|
||||||
|
### Methods
|
||||||
|
|
||||||
|
There are a number of methods accessible via a ref supplied to the Turnstone component.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```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 (
|
||||||
|
<>
|
||||||
|
<Turnstone ref={turnstoneRef} listbox={listbox} />
|
||||||
|
<button onClick={handleQuery}>Perform Query</button>
|
||||||
|
<button onClick={handleClear}>Clear Contents</button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The methods are as follows:
|
||||||
|
|
||||||
|
#### `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(<string>)`
|
||||||
|
|
||||||
|
Sets the search box contents to the string argument supplied to the function.
|
||||||
|
|
||||||
|
#### `select()`
|
||||||
|
|
||||||
|
Selects the contents of the search box
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
- Fork the project
|
- Fork the project
|
||||||
- Run the project in development mode: `$ npm run dev`
|
- Run the project in development mode: `$ npm run dev`
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* eslint no-console: 0 */
|
/* eslint no-console: 0 */
|
||||||
|
|
||||||
import React, { useCallback, useState } from 'react'
|
import React, { useCallback, useState, useRef } from 'react'
|
||||||
import Turnstone from '../../src/lib'
|
import Turnstone from '../../src/lib'
|
||||||
import styles from './styles/App.module.css'
|
import styles from './styles/App.module.css'
|
||||||
import autocompleteStyles from './styles/autocomplete.module.css'
|
import autocompleteStyles from './styles/autocomplete.module.css'
|
||||||
@@ -71,6 +71,7 @@ const listbox = [
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
|
const ref = useRef()
|
||||||
const [selectedItem, setSelectedItem] = useState(undef)
|
const [selectedItem, setSelectedItem] = useState(undef)
|
||||||
|
|
||||||
const onSelect = useCallback(
|
const onSelect = useCallback(
|
||||||
@@ -98,6 +99,7 @@ const App = () => {
|
|||||||
<div>
|
<div>
|
||||||
<label htmlFor="autocomplete">Search:</label>
|
<label htmlFor="autocomplete">Search:</label>
|
||||||
<Turnstone
|
<Turnstone
|
||||||
|
ref={ref}
|
||||||
autoFocus={false}
|
autoFocus={false}
|
||||||
cancelButton={true}
|
cancelButton={true}
|
||||||
clearButton={true}
|
clearButton={true}
|
||||||
|
|||||||
18
package-lock.json
generated
18
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "turnstone",
|
"name": "turnstone",
|
||||||
"version": "1.2.3",
|
"version": "1.3.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "turnstone",
|
"name": "turnstone",
|
||||||
"version": "1.2.3",
|
"version": "1.3.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"escape-string-regexp": "^5.0.0",
|
"escape-string-regexp": "^5.0.0",
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
"setify": "^1.0.4",
|
"setify": "^1.0.4",
|
||||||
"split-match": "^0.2.2",
|
"split-match": "^0.2.2",
|
||||||
"swr": "^1.1.2",
|
"swr": "^1.1.2",
|
||||||
"turnstone-recent-searches": "^0.4.0",
|
"turnstone-recent-searches": "^0.5.0",
|
||||||
"use-debounce": "^7.0.1"
|
"use-debounce": "^7.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -5008,9 +5008,9 @@
|
|||||||
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
||||||
},
|
},
|
||||||
"node_modules/turnstone-recent-searches": {
|
"node_modules/turnstone-recent-searches": {
|
||||||
"version": "0.4.0",
|
"version": "0.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/turnstone-recent-searches/-/turnstone-recent-searches-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/turnstone-recent-searches/-/turnstone-recent-searches-0.5.0.tgz",
|
||||||
"integrity": "sha512-3/ejeRHv+awlU2mI6l3dFXmbvzCsi0KgEmbJ7AD/X50d0Eu7VbJXnoH5RsMU4rFuO6swPGMhsuTx5Dl48MyBLw=="
|
"integrity": "sha512-OFGRM92OKAx467QTYeJXJY5Uj1XhPiMmqiXwL1/wPky38SLXotyxVjFpMvCb8MuGsY61fsJDkF2vsmseae42VA=="
|
||||||
},
|
},
|
||||||
"node_modules/type-check": {
|
"node_modules/type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
@@ -9061,9 +9061,9 @@
|
|||||||
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
||||||
},
|
},
|
||||||
"turnstone-recent-searches": {
|
"turnstone-recent-searches": {
|
||||||
"version": "0.4.0",
|
"version": "0.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/turnstone-recent-searches/-/turnstone-recent-searches-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/turnstone-recent-searches/-/turnstone-recent-searches-0.5.0.tgz",
|
||||||
"integrity": "sha512-3/ejeRHv+awlU2mI6l3dFXmbvzCsi0KgEmbJ7AD/X50d0Eu7VbJXnoH5RsMU4rFuO6swPGMhsuTx5Dl48MyBLw=="
|
"integrity": "sha512-OFGRM92OKAx467QTYeJXJY5Uj1XhPiMmqiXwL1/wPky38SLXotyxVjFpMvCb8MuGsY61fsJDkF2vsmseae42VA=="
|
||||||
},
|
},
|
||||||
"type-check": {
|
"type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "turnstone",
|
"name": "turnstone",
|
||||||
"version": "1.3.0",
|
"version": "1.4.0",
|
||||||
"description": "React customisable autocomplete component with typeahead and grouped results from multiple APIs.",
|
"description": "React customisable autocomplete component with typeahead and grouped results from multiple APIs.",
|
||||||
"author": "Tom Southall",
|
"author": "Tom Southall",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
"setify": "^1.0.4",
|
"setify": "^1.0.4",
|
||||||
"split-match": "^0.2.2",
|
"split-match": "^0.2.2",
|
||||||
"swr": "^1.1.2",
|
"swr": "^1.1.2",
|
||||||
"turnstone-recent-searches": "^0.4.0",
|
"turnstone-recent-searches": "^0.5.0",
|
||||||
"use-debounce": "^7.0.1"
|
"use-debounce": "^7.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useRef, useContext } from 'react'
|
import React, { useState, useRef, useContext, useImperativeHandle } from 'react'
|
||||||
import { StateContext } from '../context/state'
|
import { StateContext } from '../context/state'
|
||||||
import Listbox from './listbox'
|
import Listbox from './listbox'
|
||||||
import Errorbox from './errorbox'
|
import Errorbox from './errorbox'
|
||||||
@@ -23,7 +23,7 @@ import {
|
|||||||
clear
|
clear
|
||||||
} from '../actions/actions'
|
} from '../actions/actions'
|
||||||
|
|
||||||
export default function Container(props) {
|
const Container = React.forwardRef((props, ref) => {
|
||||||
// Destructure props
|
// Destructure props
|
||||||
const {
|
const {
|
||||||
autoFocus,
|
autoFocus,
|
||||||
@@ -202,6 +202,29 @@ export default function Container(props) {
|
|||||||
setBlockBlurHandler(false)
|
setBlockBlurHandler(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Expose methods to parent components
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
focus: () => {
|
||||||
|
queryInput.current.focus()
|
||||||
|
},
|
||||||
|
blur: () => {
|
||||||
|
queryInput.current.blur()
|
||||||
|
},
|
||||||
|
select: () => {
|
||||||
|
queryInput.current.select()
|
||||||
|
},
|
||||||
|
clear: () => {
|
||||||
|
clearState()
|
||||||
|
},
|
||||||
|
query: (query) => {
|
||||||
|
if(typeof query === 'string') {
|
||||||
|
queryInput.current.value = query
|
||||||
|
queryInput.current.focus()
|
||||||
|
handleInput()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div
|
<div
|
||||||
@@ -288,4 +311,8 @@ export default function Container(props) {
|
|||||||
</div>
|
</div>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
|
|
||||||
|
Container.displayName = 'Container'
|
||||||
|
|
||||||
|
export default Container
|
||||||
|
|||||||
@@ -28,12 +28,12 @@ const propDefaults = {
|
|||||||
Clear: () => '\u00d7'
|
Clear: () => '\u00d7'
|
||||||
}
|
}
|
||||||
|
|
||||||
const render = (Component, componentProps, pluginIndex) => {
|
const render = (Component, componentProps, pluginIndex, ref) => {
|
||||||
const p = Array.isArray(componentProps.plugins) && componentProps.plugins[pluginIndex]
|
const p = Array.isArray(componentProps.plugins) && componentProps.plugins[pluginIndex]
|
||||||
if(p) {
|
if(p) {
|
||||||
const [Plugin, pluginProps] = Array.isArray(p) ? p : [p]
|
const [Plugin, pluginProps] = Array.isArray(p) ? p : [p]
|
||||||
|
|
||||||
return <Plugin {...{
|
return <Plugin ref={ref} {...{
|
||||||
...pluginProps,
|
...pluginProps,
|
||||||
Component,
|
Component,
|
||||||
componentProps,
|
componentProps,
|
||||||
@@ -41,20 +41,24 @@ const render = (Component, componentProps, pluginIndex) => {
|
|||||||
render
|
render
|
||||||
}} />
|
}} />
|
||||||
}
|
}
|
||||||
return <Component {...componentProps} />
|
return <Component ref={ref} {...componentProps} />
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Turnstone(props) {
|
const Turnstone = React.forwardRef((props, ref) => {
|
||||||
const componentProps = {...propDefaults, ...props}
|
const componentProps = {...propDefaults, ...props}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<StateContextProvider {...componentProps}>
|
<StateContextProvider {...componentProps}>
|
||||||
{ render(Container, componentProps, 0) }
|
{ render(Container, componentProps, 0, ref) }
|
||||||
</StateContextProvider>
|
</StateContextProvider>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
|
|
||||||
|
Turnstone.displayName = 'Turnstone'
|
||||||
|
|
||||||
|
export default Turnstone
|
||||||
|
|
||||||
//////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////
|
||||||
// Prop validation //
|
// Prop validation //
|
||||||
|
|||||||
Reference in New Issue
Block a user