npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-customize-token-input

v2.6.1

Published

A react token (tag) input component. Allow customize data structure and Look & Feel

Downloads

9,685

Readme

React TokenInput build status Coverage Status

NPM

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, delete button Demo, or even override the whole Token component Demo.
  • Customize separate characters to separate the end-user input string. Demo
  • Inline editing on exist token.
  • Paste values. Demo
  • Preprocessing function to normalized user input value. It could be helpful to reproduce a single value into multiple values too. Demo
  • Validate function.

Installation

  1. Install the latest version of react and react-customize-token-input:
yarn add react react-customize-token-input
  1. At this point you can import react-customize-token-input and its styles in your application by:
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.

  1. 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

/**
 * @template VT, ET
 * @typedef {Object} TokenInputProps
 */
interface TokenInputProps<VT = string, ET = string> {
  /**
   * @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<VT, ET>} [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<ET>['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<ET>['error']
   *
   * Note: Return `Nullish` types means the token is valid.
   * @see Nullish
   */
  onTokenValueValidate?: OnTokenValueValidate<VT, ET>;

  // Token related props

  /**
   * @template VT
   * @prop {OnTokenValuesChange<VT>} [onTokenValuesChange]
   * @description
   * A callback function invoked when tokenValues update
   *
   * @example
   * ```js
   * onTokenValuesChange(modifiedTokenValues)
   * ```
   *
   * @param {VT[]} modifiedTokenValues
   * The new tokenValues
   *
   * @returns {void}
   */
  onTokenValuesChange?: OnTokenValuesChange<VT>;

  /**
   * @template VT
   * @prop {OnBuildTokenValue<VT>} [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<VT>;

  /**
   * @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<VT, ET>
  ) => ReactElement | null;

  /**
   * @template VT, ET
   * @prop {OnGetTokenClassName<VT, ET>} [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<ET>} tokenMeta
   * The token's meta data
   *
   * @returns {undefined | string}
   * The customizes className
   */
  onGetTokenClassName?: OnGetTokenClassName<VT, ET>;

  /**
   * @template VT, ET
   * @prop  {OnGetTokenDisplayLabel<VT, ET>} [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<ET>} tokenMeta
   * The token's meta data
   *
   * @returns {InputString | ReactNode}
   * The token's display content.
   */
  onGetTokenDisplayLabel?: OnGetTokenDisplayLabel<VT, ET>;

  /**
   * @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<VT, ET>} [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<ET>} tokenMeta
   * The token's meta data
   *
   * @returns {boolean}
   * - `true`: Editable.
   * - `false`: Not editable.
   */
  onGetIsTokenEditable?: OnGetIsTokenEditable<VT, ET>;

  /**
   * @template VT, ET
   * @prop {OnGetTokenEditableValue<VT, ET>} [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<ET>} tokenMeta
   * The token's meta data
   *
   * @returns {InputString}
   * The value for end-user to `edit` in an input field
   */
  onGetTokenEditableValue?: OnGetTokenEditableValue<VT, ET>;

  /**
   * @template VT, ET
   * @prop {OnGetTokenErrorMessage<VT, ET>} [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<ET>} 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<VT, ET>;

  /**
   * @prop {React.FocusEventHandler<HTMLInputElement>} [onCreatorFocus]
   * @description
   * A callback function invoked on TokenCreator focused
   *
   * @example
   * ```js
   * onCreatorFocus(e)
   * ```
   *
   * @param {React.FocusEvent<HTMLInputElement>} event
   * The FocusEvent of the input of TokenCreator
   *
   * @returns {void}
   */
  onCreatorFocus?: React.FocusEventHandler<HTMLInputElement>;

  /**
   * @prop {React.FocusEventHandler<HTMLInputElement>} [onCreatorBlur]
   * @description
   * A callback function invoked on TokenCreator blur
   *
   * @example
   * ```js
   * onCreatorBlur(e)
   * ```
   *
   * @param {React.FocusEvent<HTMLInputElement>} event
   * The FocusEvent of the input of TokenCreator
   *
   * @returns {void}
   */
  onCreatorBlur?: React.FocusEventHandler<HTMLInputElement>;

  /**
   * @prop {React.KeyboardEventHandler<HTMLInputElement>} [onCreatorKeyDown]
   * @description
   * A callback function invoked when keyDown on TokenCreator
   *
   * @example
   * ```js
   * onCreatorKeyDown(e)
   * ```
   *
   * @param {React.KeyboardEvent<HTMLInputElement>} event
   * The KeyboardEvent of the input of TokenCreator
   *
   * @returns {void}
   */
  onCreatorKeyDown?: React.KeyboardEventHandler<HTMLInputElement>;
}

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() | 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 | void

Could reference Demo, and its source code ExampleRefMethods in the folder examples/.

If you are using TypeScript, reference the code below for the typing of useRef.

import TokenInput, { type TokenInputRef } from 'react-customize-token-input';
const tokenInputRef = useRef<TokenInputRef>(null);
// ... omit
const handleFocusButtonClick = () => {
  tokenInputRef.current?.focus();
}
// ... omit
<TokenInput
  ref={tokenInputRef}
  tokenValues={values}
  onTokenValuesChange={setValues}
/>

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

    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 and its source code ExampleCustomizeToken in the folder examples/.

/**
 * @template VT, ET
 * @typedef {Object} TokenProps
 */
export interface TokenProps<VT = string, ET = string> {
  /**
   * @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<ET>} tokenMeta
   * @description This token's meta data
   */
  tokenMeta: TokenMeta<ET>;

  /**
   * @template VT, ET
   * @prop {OnGetTokenClassName<VT, ET>} [onGetClassName]
   * @description
   * Same as TokenInputProps {@see TokenInputProps['onGetTokenClassName']}
   */
  onGetClassName?: OnGetTokenClassName<VT, ET>;

  /**
   * @template VT, ET
   * @prop  {OnGetTokenDisplayLabel<VT, ET>} [onGetTokenDisplayLabel=defaultGetTokenEditableValue]
   * @description
   * Same as TokenInputProps {@see TokenInputProps['onGetTokenDisplayLabel']}
   */
  onGetDisplayLabel: OnGetTokenDisplayLabel<VT, ET>;

  /**
   * @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<VT, ET>;

  /**
   * @template VT, ET
   * @callback OnGetTokenEditableValue
   * @description
   * Same as TokenInputProps {@see TokenInputProps['onGetTokenEditableValue']}
   */
  onGetEditableValue: OnGetTokenEditableValue<VT, ET>;

  /**
   * @template VT
   * @callback OnBuildTokenValue
   * @description
   * Same as TokenInputProps {@see TokenInputProps['onBuildTokenValue']}
   */
  onBuildTokenValue: OnBuildTokenValue<VT>;

  /**
   * @template VT, ET
   * @callback OnGetTokenErrorMessage
   * @description
   * Same as TokenInputProps {@see TokenInputProps['onGetTokenErrorMessage']}
   */
  onGetErrorMessage: OnGetTokenErrorMessage<VT, ET>;

  /**
   * @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