https://github.com/seawind543/react-token-input
A react token (tag) input component. Allow customize data structure and Look & Feel
https://github.com/seawind543/react-token-input
customize customize-data-structure customize-render-functions inline-edit input paste preprocesss react react-component react-customize-token-input react-token-input tag tag-input token-input
Last synced: 4 months ago
JSON representation
A react token (tag) input component. Allow customize data structure and Look & Feel
- Host: GitHub
- URL: https://github.com/seawind543/react-token-input
- Owner: seawind543
- License: mit
- Created: 2018-08-15T14:41:06.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2025-06-06T23:45:16.000Z (4 months ago)
- Last Synced: 2025-06-16T20:07:13.519Z (4 months ago)
- Topics: customize, customize-data-structure, customize-render-functions, inline-edit, input, paste, preprocesss, react, react-component, react-customize-token-input, react-token-input, tag, tag-input, token-input
- Language: TypeScript
- Homepage: https://seawind543.github.io/react-token-input/
- Size: 10.6 MB
- Stars: 33
- Watchers: 0
- Forks: 7
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# React TokenInput [](https://travis-ci.org/seawind543/react-token-input) [](https://coveralls.io/github/seawind543/react-token-input?branch=master)
[](https://www.npmjs.com/package/react-customize-token-input/)
Live Demo: https://seawind543.github.io/react-token-input/
React TokenInput (react-customize-token-input)
A react token (tag) `controlled` input component, which support:
- Accept **customize data structure**.
- **Customize token (tag) Look & Feel** on the `label` [Demo](https://seawind543.github.io/react-token-input/#example-customize-label), `delete button` [Demo](https://seawind543.github.io/react-token-input/#example-customize-delete-button), or even override `the whole Token component` [Demo](https://seawind543.github.io/react-token-input/#example-customize-token-component).
- Customize **separate characters** to separate the end-user input string. [Demo](https://seawind543.github.io/react-token-input/#example-customize-separators)
- **Inline editing** on exist token.
- **Paste** values. [Demo](https://seawind543.github.io/react-token-input/#example-customize-separators)
- **Preprocessing** function to **normalized** user input value.
It could be helpful to reproduce a single value into multiple values too. [Demo](https://seawind543.github.io/react-token-input/#example-preprocess)
- **Validate** function.## Installation
1. Install the latest version of [react](https://github.com/facebook/react) and [react-customize-token-input](https://github.com/seawind543/react-token-input):
```
yarn add react react-customize-token-input
```2. At this point you can import `react-customize-token-input` and its styles in your application by:
```JavaScript
import TokenInput from 'react-customize-token-input';// Be sure to include styles at some point, probably during your bootstraping
import 'react-customize-token-input/dist/react-customize-token-input.css';// Could find the not minimize version to easily customize style from:
// 'react-customize-token-input/dist/react-customize-token-input.original.css';
```## Dev
1. Run `yarn install` to install required packages.
2. Run `yarn dev` to launch `webpack-dev-server`.
3. After step 2, you will see following message output in command console.```
「wds」: Project is running at http://0.0.0.0:8000/
「wds」: webpack output is served from /
「wds」: Content not from webpack is served from ../docs
```> Note: To stop the program, just type ```ctrl + c``` in command console.
4. After step 3 complete, you could access `http://localhost:8000/` to see result.
## Usage
See Live Examples: https://seawind543.github.io/react-token-input/
Note: Sources code of Examples in the folder `examples/`
## Props
```JavaScript
/**
* @template VT, ET
* @typedef {Object} TokenInputProps
*/
interface TokenInputProps {
/**
* @prop {CSSProperties} [style]
* @description An optional prop, for assigning style to TokenInput
*/
style?: CSSProperties;/**
* @prop {string} [className]
* @description An optional prop, for assigning class name to TokenInput
*/
className?: string;/**
* @prop {string} [placeholder]
* @description An optional prop, for assigning placeholder to TokenInput
*/
placeholder?: string;/**
* @prop {boolean} [readOnly = false]
* @description An optional prop, to control TokenInput is `readOnly mode`
*/
readOnly?: boolean;/**
* @prop {boolean} [disableCreateOnBlur]
* @description An optional prop, to control TokenInput creates a new token when blurring on the creator
*/
disableCreateOnBlur?: boolean;/**
* @prop {boolean} [autoFocus = false]
* @description
* An optional prop, to control TokenInput is `autoFocus mode`.
* Will be deprecated in the next major release. Took ref.current.focus() instead.
*/
autoFocus?: boolean;/**
* @template VT
* @prop {VT[]} tokenValues
* @description
* The array of tokenValue of TokenInput.
* This array will be used to render the tokens.
*
* Type: VT
* Description:
* Customize data structure data
* Could be string | number | object | customized data structure...etc.
*/
tokenValues: VT[];// TokenCreator props
/**
* @prop {TokenSeparator[]} [separators]
* @description
* An array of characters to split the user input string into array.
* For example,
* Split the user input string `abc;def` into `['abc', 'def']`
* by separators `[';']`
*
* @see {@link TokenSeparator}
* Note:
* It take the `String.prototype.split(separators.join('|'))`
* and `RegExp` to split the user input string.
*
* @example
* ```js
* value.split(separators.join('|'));
* ```
*
* Make sure your customized separators could be used with
* (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp)[`RegExp`]}.
*/
separators?: TokenSeparator[];/**
* @prop {SpecialKeyDownConfig} [specialKeyDown=DEFAULT_SPECIAL_KEY_DOWN_CONFIG]
* @description
* [Beta; Might be change in the future version]
* Current only apply to the `TokenCreator`
*
* The settings to control the behavior of specials keyDown's event handler.
* Recommend to use the built-in constant `KEY_DOWN_HANDLER_CONFIG_OPTION` to config the setting.
*
* @see KEY_DOWN_HANDLER_CONFIG_OPTION for the accepted config values
* @see DEFAULT_SPECIAL_KEY_DOWN_CONFIG for the default settings
*/
specialKeyDown?: SpecialKeyDownConfig;/**
* @prop {OnInputValueChange} [onInputValueChange]
* @description
* A callback function invoked when end-user typing but not become token yet
*
* @example
* ```js
* onInputValueChange(newValue, previousValue)
* ```
*
* @param {InputString} newValue
* The end-user's input string
*
* @param {InputString} previousValue
* The previous input string
*
* @returns {void}
*/
onInputValueChange?: OnInputValueChange;/**
* @prop {OnPreprocess} [onPreprocess]
* @description
* A callback function to `preprocessing` the user input string.
*
* Note: This function execute after `split by TokenSeparator[]` but before `onBuildTokenValue`
* inputString -> spilt(inputString) -> preprocess(spilt(inputString)) -> onBuildTokenValue(preprocess(spilt(inputString)))
*
* [Use case 1]
* Make your normalize process in this function, such as `String.prototype.trim()`.
*
* [Use case 2]
* Sometimes, we will want to auto-fit the user input, this function could help with it.
* For example, the user input string is `www.google.com`,
* and we want to auto-fit it into `http://www.google.com` and `https://www.google.com`.
*
* @example
* ```js
* onPreprocess(inputStringValues)
* ```
*
* @param {InputString[]} inputStringValues
* The user input string values
* (An array of string, which split from the original input string via the `separators`)
*
* @returns {InputString[]}
* An array of string
*/
onPreprocess?: OnPreprocess;/**
* @template VT, ET
* @prop {OnTokenValueValidate} [onTokenValueValidate=defaultTokenValueValidate]
* @description
* A callback function to validate a tokenValue
* (The returned result will be set into the TokenMeta & pass to `onGetTokenErrorMessage`)
*
* @example
* ```js
* onTokenValueValidate(tokenValue, index, tokenValues)
* ```
*
* @param {VT} tokenValue
* The tokenValue built by `onBuildTokenValue`
*
* @param {Index} index
* The array index of this tokenValue in tokenValues
*
* @param {VT[]} tokenValues
* The array of tokenValue of TokenInput
*
* @returns {TokenMeta['error']}
* The customized error.
* Specific the token's validate status or errorMessage.
* Could be `an error message` to display, or an error object for further operations.
*
* @see TokenMeta for more information about TokenMeta['error']
*
* Note: Return `Nullish` types means the token is valid.
* @see Nullish
*/
onTokenValueValidate?: OnTokenValueValidate;// Token related props
/**
* @template VT
* @prop {OnTokenValuesChange} [onTokenValuesChange]
* @description
* A callback function invoked when tokenValues update
*
* @example
* ```js
* onTokenValuesChange(modifiedTokenValues)
* ```
*
* @param {VT[]} modifiedTokenValues
* The new tokenValues
*
* @returns {void}
*/
onTokenValuesChange?: OnTokenValuesChange;/**
* @template VT
* @prop {OnBuildTokenValue} [onBuildTokenValue=defaultBuildTokenValue]
* @description
* A callback function to build `user input string value` into
* the `tokenValue` (customized data structure).
*
* Note: You could make your normalize process in this function too.
*
* @example
* ```js
* onBuildTokenValue(inputString)
* ```
*
* @param {InputString} inputString
* The user input value // (A value split by TokenSeparator[])
* Example:
* - Input string "ABC, DEF" and separators is `,`
* - The `onBuildTokenValue` will be called twice as
* ```
* onBuildTokenValue('ABC') and onBuildTokenValue('DEF')
* ```
*
* @returns {VT}
* The customized data structure data
* Could be string | number | object | customized data structure...etc.
*/
onBuildTokenValue?: OnBuildTokenValue;/**
* @prop {Component} [customizeTokenComponent]
* A customize react component to rendering a token
* Apply this to customize all token function.
*
* @example
* ```js
* customizeTokenComponent={MyToken}
* ```
*
* @returns {ReactElement | null}
*/
customizeTokenComponent?: (
props: TokenProps
) => ReactElement | null;/**
* @template VT, ET
* @prop {OnGetTokenClassName} [onGetTokenClassName]
* @description
* A callback function to getting customizes `className` to set on a `token`
*
* ```js
* onGetTokenClassName(tokenValue, tokenMeta)
* ```
*
* @param {VT} tokenValue
* The tokenValue built by `onBuildTokenValue`
*
* @param {TokenMeta} tokenMeta
* The token's meta data
*
* @returns {undefined | string}
* The customizes className
*/
onGetTokenClassName?: OnGetTokenClassName;/**
* @template VT, ET
* @prop {OnGetTokenDisplayLabel} [onGetTokenDisplayLabel=defaultGetTokenEditableValue]
* @description
* A callback function to getting displayable `label` of a token
* Apply this to customize the token's content
* For example, render token with `icon` or `Additional text`
*
* @example
* ```js
* onGetTokenDisplayLabel(tokenValue, tokenMeta)
* ```
*
* @param {VT} tokenValue
* The tokenValue built by `onBuildTokenValue`
*
* @param {TokenMeta} tokenMeta
* The token's meta data
*
* @returns {InputString | ReactNode}
* The token's display content.
*/
onGetTokenDisplayLabel?: OnGetTokenDisplayLabel;/**
* @prop {OnRenderTokenDeleteButtonContent} [onRenderTokenDeleteButtonContent]
* @description
* A callback function to render content of the delete button of token
* Apply this to customize the token's content of the delete button.
* For example, replace the built-in `x` by Google font material-icons
*
* @example
* ```js
* onRenderTokenDeleteButtonContent()
* ```
*
* @returns {ReactNode}
* The content of the delete button of the token.
* By default, TokenInput render a built-in `x` icon
*/
onRenderTokenDeleteButtonContent?: OnRenderTokenDeleteButtonContent;/**
* @template VT, ET
* @prop {OnGetIsTokenEditable} [onGetIsTokenEditable=defaultGetIsTokenEditable]
* @description
* A callback function to determine whether the token is `inline editable`.
*
* @example
* ```js
* onGetIsTokenEditable(tokenValue, tokenMeta)
* ```
*
* @param {VT} tokenValue
* The tokenValue built by `onBuildTokenValue`
*
* @param {TokenMeta} tokenMeta
* The token's meta data
*
* @returns {boolean}
* - `true`: Editable.
* - `false`: Not editable.
*/
onGetIsTokenEditable?: OnGetIsTokenEditable;/**
* @template VT, ET
* @prop {OnGetTokenEditableValue} [onGetTokenEditableValue=defaultGetTokenEditableValue]
* @description
* A callback function to getting `string input value`
* from `tokenValue` for the end-user to perform `inline edit`
*
* @example
* ```js
* onGetTokenEditableValue(tokenValue, tokenMeta)
* ```
*
* @param {VT} tokenValue
* The tokenValue built by `onBuildTokenValue`
*
* @param {TokenMeta} tokenMeta
* The token's meta data
*
* @returns {InputString}
* The value for end-user to `edit` in an input field
*/
onGetTokenEditableValue?: OnGetTokenEditableValue;/**
* @template VT, ET
* @prop {OnGetTokenErrorMessage} [onGetTokenErrorMessage=defaultGetTokenErrorMessage]
* @description
* A callback function to getting the `Error Message` to
* apply into the `title` attribute of the built-in Token Component
*
* @example
* ```js
* onGetTokenErrorMessage(tokenValue, tokenMeta)
* ```
*
* @param {VT} tokenValue
* The tokenValue built by `onBuildTokenValue`
*
* @param {TokenMeta} tokenMeta
* The token's meta data
*
* @returns {string | Nullish}
* The `Error Message` of the token.
* Return `string type` will let the built-in Token component apply the message
* into the `title` attribute. Otherwise, will simply be ignored
*/
onGetTokenErrorMessage?: OnGetTokenErrorMessage;/**
* @prop {React.FocusEventHandler} [onCreatorFocus]
* @description
* A callback function invoked on TokenCreator focused
*
* @example
* ```js
* onCreatorFocus(e)
* ```
*
* @param {React.FocusEvent} event
* The FocusEvent of the input of TokenCreator
*
* @returns {void}
*/
onCreatorFocus?: React.FocusEventHandler;/**
* @prop {React.FocusEventHandler} [onCreatorBlur]
* @description
* A callback function invoked on TokenCreator blur
*
* @example
* ```js
* onCreatorBlur(e)
* ```
*
* @param {React.FocusEvent} event
* The FocusEvent of the input of TokenCreator
*
* @returns {void}
*/
onCreatorBlur?: React.FocusEventHandler;/**
* @prop {React.KeyboardEventHandler} [onCreatorKeyDown]
* @description
* A callback function invoked when keyDown on TokenCreator
*
* @example
* ```js
* onCreatorKeyDown(e)
* ```
*
* @param {React.KeyboardEvent} event
* The KeyboardEvent of the input of TokenCreator
*
* @returns {void}
*/
onCreatorKeyDown?: React.KeyboardEventHandler;
}
```## Methods in ref of TokenInput
TokenInput provide the following method in the ref of it.
Method | Description | Parameter | Return
---------- | :------------ | :------------ | :------------
focus | Set focus on TokenInput. It will focus on the creator not the inline-editor | Same as [HTMLElement.focus()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) | void |
setCreatorValue | Set value of TokenCreator | value: string | void
getCreatorValue | Get value of TokenCreator | void | string
createTokens | Trigger tokens create. If param.value undefined, then apply the value of TokenCreator directly. | value?: string | voidCould reference [Demo](https://seawind543.github.io/react-token-input/#example-ref-methods), and its source code `ExampleRefMethods` in the folder `examples/`.
If you are using TypeScript, reference the code below for the typing of useRef.
```TypeScript
import TokenInput, { type TokenInputRef } from 'react-customize-token-input';
const tokenInputRef = useRef(null);
// ... omit
const handleFocusButtonClick = () => {
tokenInputRef.current?.focus();
}
// ... omit```
## Predefined KeyDown Event Handlers
TokenInput has the following **Predefined** KeyDown event handlers.
### For Token Create
KeyDown | Description | Note
---------- | :------------ | :---
Backspace | In case the current inputValue is an `empty string`, the latest **token** in the list tail will be deleted. |
Escape | Clear the input-box's value. | A.K.A. Reset.
Enter | Create a token with the inputValue and continually focused on the inputBox for the next inputting. |
Tab | Same as onEnter. |
- Default not apply
- Under Beta
### For Inline editing
KeyDown | Description | Note
---------- | :---------- | :---
Escape | End editing without change the value of the token. | A.K.A. Reset
Enter | End editing and apply the new value. In case the new value is an `empty string`, will perform the `onEscape`. |
## Default value of the optional Props
```JavaScript
style = undefined,
className = undefined,
placeholder = undefined,
readOnly = false,
disableCreateOnBlur = undefined,
autoFocus = false,
// TokenCreator
separators = DEFAULT_SEPARATORS,
/*
[
',',
';',
'\n', // for copy and paste
'\r', // for copy and paste
'\r\n', // for copy and paste
];
*/
specialKeyDown = DEFAULT_SPECIAL_KEY_DOWN_CONFIG,
/*
{
onBackspace: KEY_DOWN_HANDLER_CONFIG_OPTION.ON,
onTab: KEY_DOWN_HANDLER_CONFIG_OPTION.OFF,
onEnter: KEY_DOWN_HANDLER_CONFIG_OPTION.ON,
onEscape: KEY_DOWN_HANDLER_CONFIG_OPTION.ON,
},
*/
onInputValueChange = undefined,
onPreprocess = undefined,
onTokenValueValidate = defaultTokenValueValidate,
onTokenValuesChange = undefined,
// Token
onBuildTokenValue = defaultBuildTokenValue,
customizeTokenComponent = undefined,
onGetTokenClassName = undefined,
onGetTokenDisplayLabel = defaultGetTokenEditableValue,
onRenderTokenDeleteButtonContent = undefined,
onGetIsTokenEditable = defaultGetIsTokenEditable,
onGetTokenEditableValue = defaultGetTokenEditableValue,
onGetTokenErrorMessage = defaultGetTokenErrorMessage,
```
## Props of customizeTokenComponent
Your CustomizeTokenComponent will receive these props from TokenInput. You could decide where & how to use them to `customize` your Token component.
Could also reference this [Demo](https://seawind543.github.io/react-token-input/#example-customize-token-component) and its source code `ExampleCustomizeToken` in the folder `examples/`.
```JavaScript
/**
* @template VT, ET
* @typedef {Object} TokenProps
*/
export interface TokenProps {
/**
* @property {boolean} readOnly
* @description
* Same as TokenInputProps {@see 'TokenInputProps['readOnly']}
*/
readOnly: boolean;
/**
* @type {VT}
* @description This token's tokenValue
*/
tokenValue: VT;
/**
* @template ET
* @type {TokenMeta} tokenMeta
* @description This token's meta data
*/
tokenMeta: TokenMeta;
/**
* @template VT, ET
* @prop {OnGetTokenClassName} [onGetClassName]
* @description
* Same as TokenInputProps {@see TokenInputProps['onGetTokenClassName']}
*/
onGetClassName?: OnGetTokenClassName;
/**
* @template VT, ET
* @prop {OnGetTokenDisplayLabel} [onGetTokenDisplayLabel=defaultGetTokenEditableValue]
* @description
* Same as TokenInputProps {@see TokenInputProps['onGetTokenDisplayLabel']}
*/
onGetDisplayLabel: OnGetTokenDisplayLabel;
/**
* @callback OnRenderTokenDeleteButtonContent
* @description
* Same as TokenInputProps {@see TokenInputProps['onRenderTokenDeleteButtonContent']}
*/
onRenderDeleteButtonContent?: OnRenderTokenDeleteButtonContent;
/**
* @template VT, ET
* @callback OnGetIsTokenEditable
* @description
* Same as TokenInputProps {@see TokenInputProps['onGetIsTokenEditable']}
*/
onGetIsEditable: OnGetIsTokenEditable;
/**
* @template VT, ET
* @callback OnGetTokenEditableValue
* @description
* Same as TokenInputProps {@see TokenInputProps['onGetTokenEditableValue']}
*/
onGetEditableValue: OnGetTokenEditableValue;
/**
* @template VT
* @callback OnBuildTokenValue
* @description
* Same as TokenInputProps {@see TokenInputProps['onBuildTokenValue']}
*/
onBuildTokenValue: OnBuildTokenValue;
/**
* @template VT, ET
* @callback OnGetTokenErrorMessage
* @description
* Same as TokenInputProps {@see TokenInputProps['onGetTokenErrorMessage']}
*/
onGetErrorMessage: OnGetTokenErrorMessage;
/**
* @callback
* @description
* A callback function, which you should `call`
* when the end-user `start editing`
*
* Note:
* Call this function to tell TokenInput it is start to editing the token.
* As result, TokenInput will set `tokenMeta.activate` to `true`
*
* @example
* ```js
* onEditStart()
* ```
*
* @returns {void}
*/
onEditStart: () => void;
/**
* @callback
* @description
* A callback function, which you should `call`
* when end-user `end the edit`
*
* Note:
* Call this function to tell TokenInput to finish the `editing` of the token.
* As result, TokenInput will set `tokenMeta.activate` to `false`.
*
* Also, TokenInput will based on the value of the parameter newTokenValue to
* update the tokenValue of the token,
* and call the callback `onTokenValuesChange`
*
* @example
* ```js
* onEditEnd(newTokenValue);
* // or
* onEditEnd();
* ```
*
* @param {VT} [newTokenValue]
* The new tokenValue built by `onBuildTokenValue.
*
* Note:
* if `newTokenValue` is `undefined`,
* TokenInput will treat as `Cancel` (Edit will end without update the tokenValue).
* The callback `onTokenValuesChange` will also not be called.
*
* @returns {void}
*/
onEditEnd: (newTokenValue?: VT) => void;
/**
* @callback
* @description
* A callback function, which you should `call`
* when the end-user `delete` the token
*
* Note:
* Call this function to tell TokenInput to delete the token.
* As result, TokenInput will remove the token,
* and call `onTokenValuesChange` to update tokenValues.
*
* @example
* ```js
* onDelete()
* ```
*
* @returns {void}
*/
onDelete: () => void;
}
```
## License
[MIT](./LICENSE)