yairEO / tagify

πŸ”– lightweight, efficient Tags input component in Vanilla JS / React / Angular / Vue
https://yaireo.github.io/tagify/
Other
3.52k stars 433 forks source link
angular-component html input javascript react react-component reactjs tagging tagify tags



Tagify - tags input component

Transforms an input field or a textarea into a Tags component, in an easy, customizable way, with great performance and small code footprint, exploded with features.
Vanilla ⚑ React ⚑ Vue ⚑ Angular

πŸ‘‰ See Many Examples πŸ‘ˆ

## Table of Contents - [Table of Contents](#table-of-contents) - [Installation](#installation) - [Option 1 - import from CDN:](#option-1---import-from-cdn) - [option 2 - import as a *Node module*:](#option-2---import-as-a-node-module) - [Basic Usage Examples](#basic-usage-examples) - [Debugging](#debugging) - [Features](#features) - [Building the project](#building-the-project) - [Output files:](#output-files) - [Adding tags dynamically](#adding-tags-dynamically) - [Output value](#output-value) - [Modify original input value format](#modify-original-input-value-format) - [Ajax whitelist](#ajax-whitelist) - [Persisted data](#persisted-data) - [Edit tags](#edit-tags) - [Validations](#validations) - [Drag \& Sort](#drag--sort) - [Integration example:](#integration-example) - [DOM Templates](#dom-templates) - [Example of overriding the `tag` template:](#example-of-overriding-the-tag-template) - [Suggestions list](#suggestions-list) - [Example for a suggestion item alias](#example-for-a-suggestion-item-alias) - [Example whitelist:](#example-whitelist) - [Mixed-Content](#mixed-content) - [Single-Value](#single-value) - [React](#react) - [Update regarding `onChange` prop:](#update-regarding-onchange-prop) - [Updating the component's state](#updating-the-components-state) - [Vue](#vue) - [jQuery version](#jquery-version) - [`jQuery.tagify.js`](#jquerytagifyjs) - [HTML input \& textarea attributes](#html-input--textarea-attributes) - [Caveats](#caveats) - [FAQ](#faq) - [CSS Variables](#css-variables) - [Suggestions Dropdown CSS variables](#suggestions-dropdown-css-variables) - [Full list of Tagify's SCSS variables](#full-list-of-tagifys-scss-variables) - [Methods](#methods) - [Events](#events) - [Hooks](#hooks) - [Settings](#settings) ## Installation ### Option 1 - import from CDN: Place these lines before any other code which is (or will be) using *Tagify* ([Example here](https://jsbin.com/jekuqap/edit?html)) ```html ``` `Tagify` will then be available globally. To load specific version use `@` - for example: `unpkg.com/@yaireo/tagify@3.1.0` ### option 2 - import as a *Node module*: ```sh npm i @yaireo/tagify --save ``` ## Basic Usage Examples - Many demos with code examples can be [seen here](https://yaireo.github.io/tagify/) - [CodeSandbox live demo](https://codesandbox.io/s/simple-tagify-setup-6pfi2) ```js import Tagify from '@yaireo/tagify' var inputElem = document.querySelector('input') // the 'input' element which will be transformed into a Tagify component var tagify = new Tagify(inputElem, { // A list of possible tags. This setting is optional if you want to allow // any possible tag to be added without suggesting any to the user. whitelist: ['foo', 'bar', 'and baz', 0, 1, 2] }) ``` The above example shows the most basic `whitelist` array setting possible, with a mix of *Strings* and *Numbers* but the array also support Objects whic a must-have property of `value`: ```js whitelist: [{value: 'foo', id: '123', email: 'foo@whatever.com'}, ...] ``` The `value` property is what will be used when actually defining the `value` property of the original input element (`inputElem` in the example above) which was transformed into a *Tagify* component, and so when the form data is sent to the server, it will contain all the values (which are the selected tags in the component). For selected tags to show a different text than what is defined in `value` for a whitelist item, see the `tagTextProp` [setting](#settings) ⚠️ Important: Don't forget to **include `tagify.css`** file in your project. CSS location: `@yaireo/tagify/dist/tagify.css` SCSS location: `@yaireo/tagify/src/tagify.scss` [See SCSS usecase & example](https://github.com/yairEO/tagify/pull/282) ### Debugging There are several places in the source code which emits `console.warn` logs to help identify issues. Those will only work if `Tagify.logger.enabled` flag is set to `true`. To disable the default logging, set the following global variable: ```js window.TAGIFY_DEBUG = false var tagify = new Tagify(...) ``` ## Features * Can be applied to input & textarea elements * Supports [mix content](#mixed-content) (text and tags together) * Supports [single-value](#single-value) mode (like ` ``` ## Validations For "regular" tags (not *mix-mode* or *select-mode*) the easiest way is to use the `pattern` setting and use a Regex, or apply the `pattern` attribute directly on the `input` which will be "transformed" into a *Tagify* component (for vanilla code where the `input` tag is fully accessible to developers). If the `pattern` setting does not meet your needs, use the [`validate` setting](#settings), which recieves a *tag data object* as an argument and should return `true` if validaiton is passing, or `false`/`string` of not. A *string* may be returned as the reason of the validation failure so it would be printed as the `title` attribute of the invalid tag. [Here's an example](https://jsbin.com/rojixul/edit?js,output) for async validation for an added tag. The idea is to listen to `"add"` event, and when it fires, first set the tag to "loading" state, run an async call, and then set the *loading* state (of the tag) back to `false`. If the custom async validation failed, call the `replaceTag` Tagify method and set the `__isValid` tag data property to the error string which will be shown when hovering the tag. Note - there is a setting to keep invalid tags ([`keepInvalidTags`](#settings)) and if it's set to `true`, the user can see the reason for the invalidation by hovering the tag and see the browser's native tooltip via the `title` attribute: ```js { empty : "empty", exceed : "number of tags exceeded", pattern : "pattern mismatch", duplicate : "already exists", notAllowed : "not allowed" } ``` The texts for those (invalid tags) *titles* can be customized from the settings: ```js new Tagify(inputElement, { texts: { duplicate: "Duplicates are not allowed" } }) ``` Or by directly manipulating the *Tagify* function *prototype*: ```js Tagify.prototype.TEXTS = {...Tagify.prototype.TEXTS, {duplicate: "Duplicates are not allowed"}} ``` ## Drag & Sort To be able to sort tags by dragging, a 3rd-party script is needed. I have made a very simple *drag & drop* (~`11kb` *unminified*) script which uses [HTML5 native API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API) and it is available to download via [NPM](https://www.npmjs.com/package/@yaireo/dragsort) or [Github](https://github.com/yairEO/dragsort) but any other *drag & drop* script may work. I could not find on the whole internet a decent lightweight script. ### [Integration example](https://codepen.io/vsync/pen/jOqYOVJ): ```js var tagify = new Tagify(inputElement) // bind "DragSort" to Tagify's main element and tell // it that all the items with the below "selector" are "draggable" var dragsort = new DragSort(tagify.DOM.scope, { selector: '.'+tagify.settings.classNames.tag, callbacks: { dragEnd: onDragEnd } }) // must update Tagify's value according to the re-ordered nodes in the DOM function onDragEnd(elm){ tagify.updateValueByDOMTags() } ``` ## DOM Templates It's possible to control the templates for some of the HTML elements Tagify is using by modifying the `settings.templates` Object with your own custom functions which **must return** an *HTML string*. Available templates are: `wrapper`, `tag`, `dropdown`, `dropdownItem`, `dropdownContent`, `dropdownHeader`, `dropdownFooter` and the optional `dropdownItemNoMatch` which is a special template for rendering a suggestion item (in the dropdown list) only if there were no matches found for the typed input, for example: ```js // ...more tagify settings... templates: { dropdownItemNoMatch: data => `
No suggestion found for: ${data.value}
` } ``` [View templates](https://github.com/yairEO/tagify/blob/master/src/parts/templates.js) ### Example of overriding the `tag` template: Each template function is automatically binded with `this` pointing to the current *Tagify* instance. It is imperative to preserve the class names and also the `this.getAttributes(tagData)` for proper functionality. ```js new Tagify(inputElem, { templates: { tag(tagData, tagify){ return `
${tagData[this.settings.tagTextProp] || tagData.value}
`, dropdownFooter(suggestions){ var hasMore = suggestions.length - this.settings.dropdown.maxItems; return hasMore > 0 ? `
${hasMore} more items. Refine your search.
` : ''; } } }) ``` ## Suggestions list

suggestions list dropdown

The suggestions list is a *whitelist Array* of *Strings* or *Objects* which was set in the [settings](#settings) Object when the Tagify instance was created, and can be set later directly on the instance: `tagifyInstance.whitelist = ["tag1", "tag2", ...]`. The suggestions dropdown will be appended to the document's `` element and will be rendered by default in a position below (bottom of) the Tagify element. Using the keyboard arrows up/down will highlight an option from the list, and hitting the Enter key to select. It is possible to tweak the list dropdown via 2 settings: - `enabled` - this is a numeral value that tells Tagify when to show the suggestions dropdown, when a minimum of N characters were typed. - `maxItems` - Limits the number of items the suggestions list will render ```javascript var input = document.querySelector('input'), tagify = new Tagify(input, { whitelist : ['aaa', 'aaab', 'aaabb', 'aaabc', 'aaabd', 'aaabe', 'aaac', 'aaacc'], dropdown : { classname : "color-blue", enabled : 0, // show the dropdown immediately on focus maxItems : 5, position : "text", // place the dropdown near the typed text closeOnSelect : false, // keep the dropdown open after selecting a suggestion highlightFirst: true } }); ```

Will render

```html
aaab
aaabb
aaabc
aaabd
aaabe
``` By default searching the suggestions is using [fuzzy-search](https://en.wikipedia.org/wiki/Approximate_string_matching) (see [settings](#settings)). If you wish to assign *alias* to items (in your suggestion list), add the `searchBy` property to *whitelist* items you wish to have an *alias* for. In the below example, typing a part of a string which is included in the `searchBy` property, for example *`land midd"`* - the suggested item which matches the value "Israel" will be rendered in the suggestions (dropdown) list. ### [Example](https://yaireo.github.io/tagify/#section-extra-properties) for a suggestion item alias ```javascript whitelist = [ ... { value:'Israel', code:'IL', searchBy:'holy land, desert, middle east' }, ... ] ``` Another handy setting is `dropdown.searchKeys` which, like the above `dropdown.searchBy` setting, allows expanding the search of any typed terms to more than the `value` property of the whitelist items (if items are a *Collection*). ### Example whitelist: ```javascript [ { value : 123456, nickname : "foo", email : "foo@mail.com" }, { value : 987654, nickname : "bar", email : "bar@mail.com" }, ...more.. ] ``` Modified `searchKeys` setting to also search in other keys: ```javascript { dropdown: { searchKeys: ["nickname", "email"] // fuzzy-search matching for those whitelist items' properties } } ``` ## Mixed-Content [See demo here](https://yaireo.github.io/tagify/#section-mix) This feature must be toggled using these [settings](#settings): ```js { // mixTagsInterpolator: ["{{", "}}"], // optional: interpolation before & after string mode: 'mix', // <-- Enable mixed-content pattern: /@|#/ // <-- Text starting with @ or # (if single, String can be used here instead of Regex) } ``` When mixing text with tags, the original textarea (or input) element will have a value as follows: [[cartman]]⁠ and [[kyle]]⁠ do not know [[Homer simpson]]⁠ If the initial value of the textarea or input is formatted as the above example, Tagify will try to automatically convert everything between `[[` & `]]` to a tag, if tag exists in the *whitelist*, so make sure when the Tagify instance is initialized, that it has tags with the correct `value` property that match the same values that appear between `[[` & `]]`. Applying the setting `dropdown.position:"text"` is encouraged for mixed-content tags, because the suggestions list weird when there is already a lot of content on multiple lines. If a tag does not exist in the *whitelist*, it may be created by the user and all you should do is listen to the `add` event and update your local/remote state. ## Single-Value Similar to native ``'s element `name` attribute value | String/Array | βœ” | Initial value. defaultValue | String/Array | | Same as `value prop placeholder | String | βœ” | placeholder text for the component readOnly | Boolean | βœ” | Toggles `readonly` state. With capital `O`. tagifyRef | Object | | `useRef` hook refference for the component inner instance of vanilla *Tagify* (for methods access) showDropdown | Boolean/String | βœ” | if `true` shows the suggestions dropdown. if assigned a String, show the dropdown pre-filtered. loading | Boolean | βœ” | Toggles `loading` state for the whole component whitelist | Array | βœ” | Sets the `whitelist` which is the basis for the suggestions dropdown & autocomplete className | String | | Component's optional class name to be added InputMode | String | | `"textarea"` will create a `