{"id":13422125,"url":"https://github.com/yairEO/tagify","last_synced_at":"2025-03-15T10:31:40.832Z","repository":{"id":37430913,"uuid":"70848287","full_name":"yairEO/tagify","owner":"yairEO","description":"🔖 lightweight, efficient Tags input component in Vanilla JS / React / Angular / Vue","archived":false,"fork":false,"pushed_at":"2025-01-26T10:55:12.000Z","size":15764,"stargazers_count":3683,"open_issues_count":79,"forks_count":445,"subscribers_count":22,"default_branch":"master","last_synced_at":"2025-03-06T20:48:44.747Z","etag":null,"topics":["angular-component","html","input","javascript","react","react-component","reactjs","tagging","tagify","tags"],"latest_commit_sha":null,"homepage":"https://yaireo.github.io/tagify/","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yairEO.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"roadmap.md","authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-10-13T21:08:08.000Z","updated_at":"2025-03-05T00:12:34.000Z","dependencies_parsed_at":"2022-08-08T20:15:50.535Z","dependency_job_id":"d279ef8f-0db8-4f4a-aac8-d23689ff4c47","html_url":"https://github.com/yairEO/tagify","commit_stats":{"total_commits":1708,"total_committers":72,"mean_commits":23.72222222222222,"dds":"0.34250585480093676","last_synced_commit":"98fba808060394914eb4a056140a965ebedd3351"},"previous_names":[],"tags_count":305,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yairEO%2Ftagify","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yairEO%2Ftagify/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yairEO%2Ftagify/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yairEO%2Ftagify/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yairEO","download_url":"https://codeload.github.com/yairEO/tagify/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242921560,"owners_count":20207069,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular-component","html","input","javascript","react","react-component","reactjs","tagging","tagify","tags"],"created_at":"2024-07-30T23:00:37.469Z","updated_at":"2025-03-15T10:31:40.808Z","avatar_url":"https://github.com/yairEO.png","language":"HTML","readme":"\u003ch1 align=\"center\"\u003e\n  \u003ca href='https://yaireo.github.io/tagify'\u003e\u003cimg src=\"/docs/readme-header.svg\" width=\"320\" height=\"160\"\u003e\u003ca/\u003e\n  \u003cbr\u003e\u003cbr\u003e\n  \u003ca href='https://yaireo.github.io/tagify'\u003eTagify\u003c/a\u003e - \u003cem\u003etags\u003c/em\u003e input component\n\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  Transforms an input field or a textarea into a \u003cem\u003eTags component\u003c/em\u003e, in an easy, customizable way,\n  with great performance and small code footprint, exploded with features.\n  \u003cbr\u003e\n  \u003cstrong\u003eVanilla\u003c/strong\u003e ⚡ \u003cstrong\u003eReact\u003c/strong\u003e ⚡ \u003cstrong\u003eVue\u003c/strong\u003e ⚡ \u003cstrong\u003eAngular\u003c/strong\u003e\n\u003cp\u003e\n\n\u003ch3 align=\"center\"\u003e\n  👉 \u003ca href=\"https://yaireo.github.io/tagify\"\u003eSee Many Examples\u003c/a\u003e 👈\n  \u003cbr/\u003e\u003cbr/\u003e\n\u003c/h3\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href='https://www.npmjs.com/package/@yaireo/tagify'\u003e\n      \u003cimg src=\"https://img.shields.io/npm/v/@yaireo/tagify.svg\" /\u003e\n  \u003c/a\u003e\n  \u003ca href='https://simple.wikipedia.org/wiki/MIT_License'\u003e\n      \u003cimg src=\"https://img.shields.io/badge/license-MIT-lightgrey\" /\u003e\n  \u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/bundlephobia/minzip/@yaireo/tagify\" /\u003e\n  \u003cimg src=\"https://img.shields.io/npm/dw/@yaireo/tagify\" /\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://yaireo.github.io/tagify/#section-mix\"\u003e\n        \u003cimg src=\"/docs/mix3.gif?sanitize=true\" /\u003e\n    \u003c/a\u003e\n   \u003ca href=\"https://yaireo.github.io/tagify/#users-list\"\u003e\n      \u003cimg src=\"/docs/demo2.apng?sanitize=true\" /\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n## Table of Contents\n\n\u003c!--ts--\u003e\n- [Table of Contents](#table-of-contents)\n- [Installation](#installation)\n  - [Option 1 - import from CDN:](#option-1---import-from-cdn)\n  - [option 2 - import as a *Node module*:](#option-2---import-as-a-node-module)\n- [Basic Usage Examples](#basic-usage-examples)\n  - [Debugging](#debugging)\n- [Features](#features)\n- [Building the project](#building-the-project)\n  - [Output files:](#output-files)\n- [Adding tags dynamically](#adding-tags-dynamically)\n- [Output value](#output-value)\n  - [Modify original input value format](#modify-original-input-value-format)\n- [Ajax whitelist](#ajax-whitelist)\n- [Persisted data](#persisted-data)\n- [Edit tags](#edit-tags)\n- [Validations](#validations)\n- [Drag \\\u0026 Sort](#drag--sort)\n  - [Integration example:](#integration-example)\n- [DOM Templates](#dom-templates)\n  - [Example of overriding the `tag` template:](#example-of-overriding-the-tag-template)\n- [Suggestions list](#suggestions-list)\n  - [Example for a suggestion item alias](#example-for-a-suggestion-item-alias)\n  - [Example whitelist:](#example-whitelist)\n- [Mixed-Content](#mixed-content)\n- [Single-Value](#single-value)\n- [React](#react)\n  - [Update regarding `onChange` prop:](#update-regarding-onchange-prop)\n    - [Updating the component's state](#updating-the-components-state)\n- [Vue](#vue)\n- [jQuery version](#jquery-version)\n    - [`jQuery.tagify.js`](#jquerytagifyjs)\n- [HTML input \\\u0026 textarea attributes](#html-input--textarea-attributes)\n- [Caveats](#caveats)\n- [FAQ](#faq)\n- [CSS Variables](#css-variables)\n  - [Suggestions Dropdown CSS variables](#suggestions-dropdown-css-variables)\n  - [Full list of Tagify's SCSS variables](#full-list-of-tagifys-scss-variables)\n- [Methods](#methods)\n- [Events](#events)\n- [Hooks](#hooks)\n- [Settings](#settings)\n\u003c!--te--\u003e\n\n## Installation\n\n### Option 1 - import from CDN:\n\nPlace these lines before any other code which is (or will be) using *Tagify* ([Example here](https://jsbin.com/jekuqap/edit?html))\n```html\n\u003cscript src=\"https://cdn.jsdelivr.net/npm/@yaireo/tagify\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://cdn.jsdelivr.net/npm/@yaireo/tagify/dist/tagify.polyfills.min.js\"\u003e\u003c/script\u003e\n\u003clink href=\"https://cdn.jsdelivr.net/npm/@yaireo/tagify/dist/tagify.css\" rel=\"stylesheet\" type=\"text/css\" /\u003e\n```\n\n`Tagify` will then be available globally.\nTo load specific version use `@` - for example: `unpkg.com/@yaireo/tagify@3.1.0`\n\n### option 2 - import as a *Node module*:\n```sh\nnpm i @yaireo/tagify --save\n```\n\n## Basic Usage Examples\n\n- Many demos with code examples can be [seen here](https://yaireo.github.io/tagify/)\n- [CodeSandbox live demo](https://codesandbox.io/s/simple-tagify-setup-6pfi2)\n\n\n\n```js\nimport Tagify from '@yaireo/tagify'\n\nvar inputElem = document.querySelector('input') // the 'input' element which will be transformed into a Tagify component\nvar tagify = new Tagify(inputElem, {\n  // A list of possible tags. This setting is optional if you want to allow\n  // any possible tag to be added without suggesting any to the user.\n  whitelist: ['foo', 'bar', 'and baz', 0, 1, 2]\n})\n```\n\nThe above example shows the most basic `whitelist` array setting possible, with a mix\nof *Strings* and *Numbers* but the array also support Objects whic a must-have property of `value`:\n\n```js\nwhitelist: [{value: 'foo', id: '123', email: 'foo@whatever.com'}, ...]\n```\n\nThe `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\ninto 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).\n\nFor selected tags to show a different text than what is defined in `value` for a whitelist item, see the `tagTextProp` [setting](#settings)\n\n⚠️ Important:\nDon't forget to **include `tagify.css`** file in your project.\nCSS location: `@yaireo/tagify/dist/tagify.css`\nSCSS location: `@yaireo/tagify/src/tagify.scss`\n[See SCSS usecase \u0026 example](https://github.com/yairEO/tagify/pull/282)\n\n### Debugging\nThere are several places in the source code which emits `console.warn` logs to help identify issues.\n\u003cdel\u003eThose will only work if `Tagify.logger.enabled` flag is set to `true`\u003c/del\u003e.\nTo disable the default logging, set the following global variable:\n\n\n```js\nwindow.TAGIFY_DEBUG = false\n\nvar tagify = new Tagify(...)\n```\n\n\n## Features\n* Can be applied to input \u0026 textarea elements\n* Supports [mix content](#mixed-content) (text and tags together)\n* Supports [single-value](#single-value) mode (like `\u003cselect\u003e`)\n* Supports whitelist/blacklist\n* Customizable HTML templates for the different areas of the component (wrapper, tags, dropdown, dropdown item, dropdown header, dropdown footer)\n* Shows suggestions list (flexiable settings \u0026 styling) at *full (component) width* or *next to* the typed texted (caret)\n* Allows setting suggestions' [aliases](#example-for-a-suggestion-item-alias) for easier fuzzy-searching\n* Auto-suggest input as-you-type with the ability to auto-complete\n* Can paste in multiple values: `tag 1, tag 2, tag 3` or even newline-separated tags\n* Tags can be created by Regex delimiter or by pressing the \"Enter\" key / focusing of the input\n* Validate tags by Regex *pattern* or by function\n* Tags may be [editable](#edit-tags) (double-click)\n* \u003cdel\u003eARIA accessibility support\u003c/del\u003e(Component too generic for any meaningful ARIA)\n* Supports read-only mode to the whole component or per-tag\n* Each tag can have any properties desired (class, data-whatever, readonly...)\n* Automatically disallow duplicate tags (vis \"settings\" object)\n* Has built-in CSS loader, if needed (Ex. \u003cem\u003eAJAX\u003c/em\u003e whitelist pulling)\n* Tags can be trimmed via `hellip` by giving `max-width` to the `tag` element in your `CSS`\n* RTL alignment ([See demo](https://yaireo.github.io/tagify/#section-rtl))\n* \u003cdel\u003eInternet Explorer - A polyfill script should be used: `tagify.polyfills.min.js` (in `/dist`)\u003c/del\u003e ***(IE support has been dropped)***\n* Many useful custom [events](#events)\n* Original input/textarea element values kept in sync with Tagify\n\n## Building the project\nSimply run `gulp` in your terminal, from the project's path ([Gulp](https://gulpjs.com) should be installed first).\n\nSource files are this path: `/src/`\n\nOutput files, which are automatically generated using Gulp, are in: `/dist/`\n\n### Output files:\n\nFilename                             | Info\n------------------------------------ | -----------------------------------------------------------\n`tagify.esm.js`                      | ESM version. [see jsbin demo](https://jsbin.com/sulijap/edit?html,output)\n`tagify.js`                          | *minified* UMD version, including its souremaps. This is the **main** file the package exports.\n`tagify.polyfills.min.js`            | Used for old Internet Explorer browser support\n`react.tagify.js`                    | Wrapper-only for React. [Read more](#react)\n\u003cdel\u003e`jQuery.tagify.min.js`\u003c/del\u003e    | jQuery wrapper - same as `tagify.js`. Might be removed in the future. **(Deprecaded as of APR 24')**\n`tagify.css`                         |\n\n\n## Adding tags dynamically\n```javascript\nvar tagify = new Tagify(...);\n\ntagify.addTags([\"banana\", \"orange\", \"apple\"])\n\n// or add tags with pre-defined properties\n\ntagify.addTags([{value:\"banana\", color:\"yellow\"}, {value:\"apple\", color:\"red\"}, {value:\"watermelon\", color:\"green\"}])\n```\n\n## Output value\nThere are two possible ways to get the value of the tags:\n\n1. Access the tagify's instance's `value` prop: `tagify.value` (Array of tags)\n2. Access the *original* input's value: `inputElm.value` (Stringified Array of tags)\n\nThe most common way is to simply listen to the `change` event on the *original input*\n\n```javascript\nvar inputElm = document.querySelector,\n    tagify = new Tagify (inputElm);\n\ninputElm.addEventListener('change', onChange)\n\nfunction onChange(e){\n  // outputs a String\n  console.log(e.target.value)\n}\n\n```\n\n### [Modify original input value format](https://jsbin.com/paxijaj/edit?html,js,output)\n\nDefault format is a JSON string:\u003cbr\u003e\n`'[{\"value\":\"cat\"}, {\"value\":\"dog\"}]'`\n\nI **recommend** keeping this because some situations might have values such as addresses (tags contain commas):\u003cbr\u003e\n`'[{\"value\":\"Apt. 2A, Jacksonville, FL 39404\"}, {\"value\":\"Forrest Ray, 191-103 Integer Rd., Corona New Mexico\"}]'`\n\nAnother example for complex tags state might be disabled tags, or ones with custom identifier *class*:\u003cbr\u003e\n*(tags can be clicked, so delevopers can choose to use this to disable/enable tags)*\u003cbr\u003e\n`'[{\"value\":\"cat\", \"disabled\":true}, {\"value\":\"dog\"}, {\"value\":\"bird\", \"class\":\"color-green\"}]'`\n\nTo change the format, assuming your tags have no commas and are fairly simple:\n\n```js\nvar tagify = new Tagify(inputElm, {\n  originalInputValueFormat: valuesArr =\u003e valuesArr.map(item =\u003e item.value).join(',')\n})\n```\n\n**Output:**\u003cbr\u003e\n`\"cat,dog\"`\n\n\n## Ajax whitelist\nDynamically-loaded suggestions list (*whitelist*) from the server (as the user types) is a frequent need to many.\n\nTagify comes with its own loading animation, which is a very lightweight CSS-only code, and the \u003cem\u003eloading\u003c/em\u003e\nstate is controlled by the method `tagify.loading` which accepts `true` or `false` as arguments.\n\nBelow is a basic example using the `fetch` API. I advise aborting the last request on any input before starting a new request.\n\n\u003cdetails\u003e\n  \u003csummary\u003eExample:\u003c/summary\u003e\n\n```javascript\nvar input = document.querySelector('input'),\n    tagify = new Tagify(input, {whitelist:[]}),\n    controller; // for aborting the call\n\n// listen to any keystrokes which modify tagify's input\ntagify.on('input', onInput)\n\nfunction onInput( e ){\n  var value = e.detail.value\n  tagify.whitelist = null // reset the whitelist\n\n  // https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort\n  controller \u0026\u0026 controller.abort()\n  controller = new AbortController()\n\n  // show loading animation.\n  tagify.loading(true)\n\n  fetch('http://get_suggestions.com?value=' + value, {signal:controller.signal})\n    .then(RES =\u003e RES.json())\n    .then(function(newWhitelist){\n      tagify.whitelist = newWhitelist // update whitelist Array in-place\n      tagify.loading(false).dropdown.show(value) // render the suggestions dropdown\n    })\n}\n```\n\u003c/details\u003e\n\n## Persisted data\n\nSometimes the whitelist might be loaded asynchronously, and so any pre-filled value in the original input field\nwill be removed if the `enforceWhitelist` is set to `true`.\n\nTagify can automatically restore the last used `whitelist` by setting a ***unique id*** to the Tagify instance,\nby using the *localstorage* to persist the `whitelist` \u0026 `value` data:\n\n```js\nvar input = document.querySelector('input'),\n    tagify = new Tagify(input, {\n      id: 'test1',  // must be unique (per-tagify instance)\n      enforceWhitelist: true,\n    }),\n```\n\n## Edit tags\nTags that aren't `read-only` can be edited by double-clicking them (by default)\nor by changing the `editTags` *setting* to `1`, making tags editable by single-clicking them.\n\nThe value is saved on `blur` or by pressing `enter` key. Pressing `Escape` will revert the change trigger `blur`.\n\u003ckbd\u003ectrl\u003c/kbd\u003e\u003ckbd\u003ez\u003c/kbd\u003e will revert the change if an edited tag was marked as not valid (perhaps duplicate or blacklisted)\n\nTo prevent *all* tags from being allowed to be editable, set the `editTags` setting to `false` (or `null`).\u003cbr\u003e\nTo do the same but for specific tag(s), set those tags' data with `editable` property set to `false`:\n\n```html\n\u003cinput value='[{\"value\":\"foo\", \"editable\":false}, {\"value\":\"bar\"}]'\u003e\n```\n\n\n## Validations\nFor \"regular\" tags (not *mix-mode* or *select-mode*) the easiest way is to use the `pattern` setting and use a Regex, or\napply 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).\n\nIf 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.\nA *string* may be returned as the reason of the validation failure so it would be printed as the `title` attribute of the invalid tag.\n\n[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,\nand 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`.\nIf the custom async validation failed, call the `replaceTag` Tagify method and set the `__isValid` tag data property to the error string which will\nbe shown when hovering the tag.\n\n\nNote - 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\nhovering the tag and see the browser's native tooltip via the `title` attribute:\n\n```js\n{\n  empty      : \"empty\",\n  exceed     : \"number of tags exceeded\",\n  pattern    : \"pattern mismatch\",\n  duplicate  : \"already exists\",\n  notAllowed : \"not allowed\"\n}\n```\n\nThe texts for those (invalid tags) *titles* can be customized from the settings:\n\n```js\nnew Tagify(inputElement, {\n  texts: {\n    duplicate: \"Duplicates are not allowed\"\n  }\n})\n```\n\nOr by directly manipulating the *Tagify* function *prototype*:\n\n```js\nTagify.prototype.TEXTS = {...Tagify.prototype.TEXTS, {duplicate: \"Duplicates are not allowed\"}}\n```\n\n## Drag \u0026 Sort\n\nTo be able to sort tags by dragging, a 3rd-party script is needed.\n\nI have made a very simple *drag \u0026 drop* (~`11kb` *unminified*) script which uses [HTML5 native API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API) and\nit is available to download via [NPM](https://www.npmjs.com/package/@yaireo/dragsort) or [Github](https://github.com/yairEO/dragsort)\nbut any other *drag \u0026 drop* script may work. I could not find on the whole internet a decent lightweight script.\n\n### [Integration example](https://codepen.io/vsync/pen/jOqYOVJ):\n\n```js\nvar tagify = new Tagify(inputElement)\n\n// bind \"DragSort\" to Tagify's main element and tell\n// it that all the items with the below \"selector\" are \"draggable\"\nvar dragsort = new DragSort(tagify.DOM.scope, {\n    selector: '.'+tagify.settings.classNames.tag,\n    callbacks: {\n        dragEnd: onDragEnd\n    }\n})\n\n// must update Tagify's value according to the re-ordered nodes in the DOM\nfunction onDragEnd(elm){\n    tagify.updateValueByDOMTags()\n}\n```\n\n\n## DOM Templates\nIt's possible to control the templates for some of the HTML elements Tagify is using by\nmodifying the `settings.templates` Object with your own custom functions which **must return** an *HTML string*.\n\nAvailable templates are: `wrapper`, `input`, `tag`, `dropdown`, `dropdownItem`, `dropdownContent`, `dropdownHeader`, `dropdownFooter` and the optional `dropdownItemNoMatch`\nwhich 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:\n\n```js\n// ...more tagify settings...\ntemplates: {\n  dropdownItemNoMatch: data =\u003e\n    `\u003cdiv class='${tagify.settings.classNames.dropdownItem}' value=\"noMatch\" tabindex=\"0\" role=\"option\"\u003e\n        No suggestion found for: \u003cstrong\u003e${data.value}\u003c/strong\u003e\n    \u003c/div\u003e`\n}\n```\n\n[View templates](https://github.com/yairEO/tagify/blob/master/src/parts/templates.js)\n\n### Example of overriding the `tag` template:\n\nEach template function is automatically binded with `this` pointing to the current *Tagify* instance.\nIt is imperative to preserve the class names and also the `this.getAttributes(tagData)` for proper functionality.\n\n```js\nnew Tagify(inputElem, {\n  templates: {\n    tag(tagData, tagify){\n      return `\u003ctag title=\"${(tagData.title || tagData.value)}\"\n              contenteditable='false'\n              spellcheck='false'\n              tabIndex=\"${this.settings.a11y.focusableTags ? 0 : -1}\"\n              class=\"${this.settings.classNames.tag} ${tagData.class ? tagData.class : \"\"}\"\n              ${this.getAttributes(tagData)}\u003e\n      \u003cx title='' class=\"${this.settings.classNames.tagX}\" role='button' aria-label='remove tag'\u003e\u003c/x\u003e\n      \u003cdiv\u003e\n          \u003cspan class=\"${this.settings.classNames.tagText}\"\u003e${tagData[this.settings.tagTextProp] || tagData.value}\u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/tag\u003e`,\n\n    dropdownFooter(suggestions){\n      var hasMore = suggestions.length - this.settings.dropdown.maxItems;\n\n      return hasMore \u003e 0\n        ? `\u003cfooter data-selector='tagify-suggestions-footer' class=\"${this.settings.classNames.dropdownFooter}\"\u003e\n            ${hasMore} more items. Refine your search.\n          \u003c/footer\u003e`\n        : '';\n    }\n  }\n})\n```\n\n## Suggestions list\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"/docs/suggestions-list.apng\" alt='suggestions list dropdown'/\u003e\n\u003c/p\u003e\n\nThe 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\", ...]`.\n\nThe suggestions dropdown will be appended to the document's `\u003cbody\u003e` element and will be rendered by default in a position below (bottom of) the Tagify element.\nUsing the keyboard arrows up/down will highlight an option from the list, and hitting the Enter key to select.\n\nIt is possible to tweak the list dropdown via 2 settings:\n\n - `enabled` - this is a numeral value that tells Tagify when to show the suggestions dropdown, when a minimum of N characters were typed.\n - `maxItems` - Limits the number of items the suggestions list will render\n\n```javascript\nvar input = document.querySelector('input'),\n    tagify = new Tagify(input, {\n        whitelist : ['aaa', 'aaab', 'aaabb', 'aaabc', 'aaabd', 'aaabe', 'aaac', 'aaacc'],\n        dropdown : {\n            classname     : \"color-blue\",\n            enabled       : 0,              // show the dropdown immediately on focus\n            maxItems      : 5,\n            position      : \"text\",         // place the dropdown near the typed text\n            closeOnSelect : false,          // keep the dropdown open after selecting a suggestion\n            highlightFirst: true\n        }\n    });\n```\n\n\u003cp align=\"center\"\u003e\u003cb\u003eWill render\u003c/b\u003e\u003c/p\u003e\n\n```html\n\u003cdiv class=\"tagify__dropdown tagify__dropdown--text\" style=\"left:993.5px; top:106.375px; width:616px;\"\u003e\n    \u003cdiv class=\"tagify__dropdown__wrapper\"\u003e\n      \u003cdiv class=\"tagify__dropdown__item tagify__dropdown__item--active\" value=\"aaab\"\u003eaaab\u003c/div\u003e\n      \u003cdiv class=\"tagify__dropdown__item\" value=\"aaabb\"\u003eaaabb\u003c/div\u003e\n      \u003cdiv class=\"tagify__dropdown__item\" value=\"aaabc\"\u003eaaabc\u003c/div\u003e\n      \u003cdiv class=\"tagify__dropdown__item\" value=\"aaabd\"\u003eaaabd\u003c/div\u003e\n      \u003cdiv class=\"tagify__dropdown__item\" value=\"aaabe\"\u003eaaabe\u003c/div\u003e\n    \u003c/div\u003e\n\u003c/div\u003e\n```\n\nBy default searching the suggestions is using [fuzzy-search](https://en.wikipedia.org/wiki/Approximate_string_matching) (see [settings](#settings)).\n\nIf you wish to assign *alias* to items (in your suggestion list), add the `searchBy` property to *whitelist* items you wish\nto have an *alias* for.\n\nIn the below example, typing a part of a string which is included in the `searchBy` property, for example *`land midd\"`* -\nthe suggested item which matches the value \"Israel\" will be rendered in the suggestions (dropdown) list.\n\n### [Example](https://yaireo.github.io/tagify/#section-extra-properties) for a suggestion item alias\n\n```javascript\nwhitelist = [\n    ...\n    { value:'Israel', code:'IL', searchBy:'holy land, desert, middle east' },\n    ...\n]\n```\n\nAnother handy setting is `dropdown.searchKeys` which, like the above `dropdown.searchBy` setting, allows\nexpanding the search of any typed terms to more than the `value` property of the whitelist items (if items are a *Collection*).\n\n### Example whitelist:\n\n```javascript\n[\n  {\n    value    : 123456,\n    nickname : \"foo\",\n    email    : \"foo@mail.com\"\n  },\n  {\n    value    : 987654,\n    nickname : \"bar\",\n    email    : \"bar@mail.com\"\n  },\n  ...more..\n]\n```\n\nModified `searchKeys` setting to also search in other keys:\n```javascript\n{\n  dropdown: {\n    searchKeys: [\"nickname\", \"email\"] //  fuzzy-search matching for those whitelist items' properties\n  }\n}\n```\n\n## Mixed-Content\n\n[See demo here](https://yaireo.github.io/tagify/#section-mix)\n\nThis feature must be toggled using these [settings](#settings):\n\n```js\n{\n  //  mixTagsInterpolator: [\"{{\", \"}}\"],  // optional: interpolation before \u0026 after string\n  mode: 'mix',    // \u003c--  Enable mixed-content\n  pattern: /@|#/  // \u003c--  Text starting with @ or # (if single, String can be used here instead of Regex)\n}\n```\n\nWhen mixing text with tags, the original textarea (or input) element will have a value as follows:\n\n    [[cartman]]⁠ and [[kyle]]⁠ do not know [[Homer simpson]]⁠\n\nIf the initial value of the textarea or input is formatted as the above example, Tagify will try to\nautomatically convert everything between `[[` \u0026 `]]` to a tag, if tag exists in the *whitelist*, so make\nsure when the Tagify instance is initialized, that it has tags with the correct `value` property that match\nthe same values that appear between `[[` \u0026 `]]`.\n\nApplying the setting `dropdown.position:\"text\"` is encouraged for mixed-content tags, because the suggestions list\nweird when there is already a lot of content on multiple lines.\n\nIf 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.\n\n## Single-Value\n\nSimilar to native `\u003cSelect\u003e` element, but allows typing text as value.\n\n## React\n\nSee [**live demo**](https://codesandbox.io/s/tagify-react-wrapper-oempc) for React integration examples.\n⚠️ Tagify is **not** a [controlled component](https://github.com/yairEO/tagify/issues/489#issuecomment-629316093).\n\nA none-minified and raw source-code Tagify React component is exported from [`react.tagify.jsx`](https://github.com/yairEO/tagify/blob/master/src/react.tagify.jsx) and you can import it as seen in the below code example.\nThis React port will only work if your bundler can handle raw source-code in ES2015+ which is better for tree-shaking.\n\n---\n### Update regarding `onChange` prop:\n\nI have changed how the `onChange` works internally within the Wrapper of Tagify\nso as of *March 30, 2021* the `e` argument will include a `detail` parameter with the value as string.\nThere is no more `e.target`, and to access the original DOM input element, do this: `e.detail.tagify.DOM.originalInput`.\n\n----\n\n\u003e Note: You will need to import Tagify's CSS also, either by JavaScript or by SCSS `@import` (which is preferable)\n\u003e Also note that you will need to use [*dart-sass*](https://www.npmjs.com/package/sass) and not *node-sass* in order to compile the file.\n\n```javascript\nimport { useCallback, useRef } from 'react'\nimport Tags from '@yaireo/tagify/react' // React-wrapper file\nimport '@yaireo/tagify/dist/tagify.css' // Tagify CSS\n\nconst App = () =\u003e {\n    // on tag add/edit/remove\n    const onChange = useCallback((e) =\u003e {\n        console.log(\"CHANGED:\"\n            , e.detail.tagify.value // Array where each tag includes tagify's (needed) extra properties\n            , e.detail.tagify.getCleanValue() // Same as above, without the extra properties\n            , e.detail.value // a string representing the tags\n        )\n    }, [])\n\n    return (\n        \u003cTags\n            whitelist={['item 1', 'another item', 'item 3']}\n            placeholder='Add some tags'\n            settings={{\n                blacklist: [\"xxx\"],\n                maxTags: 4,\n                dropdown: {\n                    enabled: 0 // always show suggestions dropdown\n                }\n            }}\n            defaultValue=\"a,b,c\" // initial value\n            onChange={onChange}\n        /\u003e\n    )\n}\n```\n\nTo gain full access to Tagify's (instance) inner methods, A custom `ref` can be used:\n\n```jsx\nimport Tags, {MixedTags} from \"@yaireo/tagify/react\";\n\n...\nconst tagifyRef = useRef()\n...\n\u003cTags tagifyRef={tagifyRef} ... /\u003e\n\n// or mix-mode\n\u003cMixedTags\n  settings={...}\n  onChange={...}\n  defaultValue={`This is a textarea which mixes text with [[{\"value\":\"tags\"}]].`}\n/\u003e\n```\n\n`\u003cMixedTags\u003e` component is a shorthand for `\u003cTags InputMode=\"textarea\"\u003e`\n\n#### Updating the component's state\n\nThe `settings` prop is **only used once** in the initialization process, please do not update it afterwards.\n\n---\n\u003cdetails\u003e\n  \u003csummary\u003e📖 List of (React) props for the \u003ccode\u003e\u0026lt;Tags/\u0026gt;\u003c/code\u003e component\u003c/summary\u003e\n\n\nProp                    | Type                      | Updatable | Info\n----------------------- | ------------------------- |:---------:| -----------------------------------------------------------\nsettings                | \u003csub\u003eObject\u003c/sub\u003e         |           | See [*settings* section](#settings)\nname                    | \u003csub\u003eString\u003c/sub\u003e         | ✔         | `\u003cinput\u003e`'s element `name` attribute\nvalue                   | \u003csub\u003eString/Array\u003c/sub\u003e   | ✔         | Initial value.\ndefaultValue            | \u003csub\u003eString/Array\u003c/sub\u003e   |           | Same as `value prop\nplaceholder             | \u003csub\u003eString\u003c/sub\u003e         | ✔         | placeholder text for the component\nreadOnly                | \u003csub\u003eBoolean\u003c/sub\u003e        | ✔         | Toggles `readonly` state. With capital `O`.\ntagifyRef               | \u003csub\u003eObject\u003c/sub\u003e         |           | `useRef` hook refference for the component inner instance of vanilla *Tagify* (for methods access)\nshowDropdown            | \u003csub\u003eBoolean/String\u003c/sub\u003e | ✔         | if `true` shows the suggestions dropdown. if assigned a String, show the dropdown pre-filtered.\nloading                 | \u003csub\u003eBoolean\u003c/sub\u003e        | ✔         | Toggles `loading` state for the whole component\nwhitelist               | \u003csub\u003eArray\u003c/sub\u003e          | ✔         | Sets the `whitelist` which is the basis for the suggestions dropdown \u0026 autocomplete\nclassName               | \u003csub\u003eString\u003c/sub\u003e         |           | Component's optional class name to be added\nInputMode               | \u003csub\u003eString\u003c/sub\u003e         |           | `\"textarea\"` will create a `\u003ctextarea\u003e` (hidden) element instead of the default `\u003cinput\u003e` and automatically make Tagify act as [*\"mix mode\"*](#mixed-content)\nautoFocus               | \u003csub\u003eBoolean\u003c/sub\u003e        |           | Should the component have focus on mount. Must be unique, per-page.\nchildren                | \u003csub\u003eString/Array\u003c/sub\u003e   |           | `value`/`defaultValue` props are prefered\nonChange                | \u003csub\u003eFunction\u003c/sub\u003e       |           | See [*events* section](#events)\nonInput                 | \u003csub\u003eFunction\u003c/sub\u003e       |           | See [*events* section](#events)\nonAdd                   | \u003csub\u003eFunction\u003c/sub\u003e       |           | See [*events* section](#events)\nonRemove                | \u003csub\u003eFunction\u003c/sub\u003e       |           | See [*events* section](#events)\nonInvalid               | \u003csub\u003eFunction\u003c/sub\u003e       |           | See [*events* section](#events)\nonClick                 | \u003csub\u003eFunction\u003c/sub\u003e       |           | See [*events* section](#events)\nonKeydown               | \u003csub\u003eFunction\u003c/sub\u003e       |           | See [*events* section](#events)\nonFocus                 | \u003csub\u003eFunction\u003c/sub\u003e       |           | See [*events* section](#events)\nonBlur                  | \u003csub\u003eFunction\u003c/sub\u003e       |           | See [*events* section](#events)\nonEditInput             | \u003csub\u003eFunction\u003c/sub\u003e       |           | See [*events* section](#events)\nonEditBeforeUpdate      | \u003csub\u003eFunction\u003c/sub\u003e       |           | See [*events* section](#events)\nonEditUpdated           | \u003csub\u003eFunction\u003c/sub\u003e       |           | See [*events* section](#events)\nonEditStart             | \u003csub\u003eFunction\u003c/sub\u003e       |           | See [*events* section](#events)\nonEditKeydown           | \u003csub\u003eFunction\u003c/sub\u003e       |           | See [*events* section](#events)\nonDropdownShow          | \u003csub\u003eFunction\u003c/sub\u003e       |           | See [*events* section](#events)\nonDropdownHide          | \u003csub\u003eFunction\u003c/sub\u003e       |           | See [*events* section](#events)\nonDropdownSelect        | \u003csub\u003eFunction\u003c/sub\u003e       |           | See [*events* section](#events)\nonDropdownScroll        | \u003csub\u003eFunction\u003c/sub\u003e       |           | See [*events* section](#events)\nonDropdownNoMatch       | \u003csub\u003eFunction\u003c/sub\u003e       |           | See [*events* section](#events)\nonDropdownUpdated       | \u003csub\u003eFunction\u003c/sub\u003e       |           | See [*events* section](#events)\n\u003c/details\u003e\n\n---\n\n## Vue\n\nI don't know *Vue* at all and this thin wrapper was built by a friend who knows.\nTo import the [wrapper file](https://github.com/yairEO/tagify/blob/master/dist/tagify.vue), use the import path `@yaireo/tagify/vue`\n\n\n## jQuery version\n\nThis variant of Tagify code has been **deprecated** because it doesn't really add much in terms of ease-of-use.\nI only made it so it would be possible to use jQuery selectors \u0026 chaining but the (jQuery) port really isn't needed\nwhen implementing Tagify within a jQuery code.\n\nBelow is the documentation for previous Tagify packages versions which included support:\n\n#### `jQuery.tagify.js`\n\nA jQuery wrapper version is also available, but I advise not using it because it's basically the exact same as the \"normal\"\nscript (non-jqueryfied) and all the jQuery's wrapper does is allowing to chain the event listeners for ('add', 'remove', 'invalid')\n\n```javascript\n$('[name=tags]')\n    .tagify()\n    .on('add', function(e, tagData){\n        console.log('added', ...tagData)  // data, index, and DOM node\n    });\n```\n\nAccessing methods can be done via the [`.data('tagify')`](https://api.jquery.com/data):\n\n```javascript\n$('[name=tags]').tagify();\n// get tags from the server (ajax) and add them:\n$('[name=tags]').data('tagify').addTags('aaa, bbb, ccc')\n````\n\n## HTML input \u0026 textarea attributes\n\nThe below list of *attributes* affect *Tagify*.\u003cbr\u003e\nThese can also be set by Tagify [settings](#settings) Object manually, and not *declerativly* (via attributes).\n\nAttribute         | Example                                               | Info\n----------------- | ----------------------------------------------------- | --------------------\n[pattern](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/pattern) | \u003cpre lang=html\u003e`\u003cinput pattern='^[A-Za-z_✲ ]{1,15}$'\u003e`\u003c/pre\u003e               | Tag Regex pattern which tag input is validated by.\n[placeholder](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#htmlattrdefplaceholder) | \u003cpre lang=html\u003e`\u003cinput placeholder='please type your tags'\u003e`\u003c/pre\u003e        | This attribute's value will be used as a constant placeholder, which is visible unless something is being typed.\nreadOnly          | \u003cpre lang=html\u003e`\u003cinput readOnly\u003e`\u003c/pre\u003e | No user-interaction (add/remove/edit) allowed.\nautofocus         | \u003cpre lang=html\u003e`\u003cinput autofocus\u003e`\u003c/pre\u003e | Automatically focus the the Tagify component when the component is loaded\nrequired          | \u003cpre lang=html\u003e`\u003cinput required\u003e`\u003c/pre\u003e | Adds a `required` attribute to the Tagify wrapper element. Does nothing more.\n\n\n\n## Caveats\n- `\u003cinput\u003e` wrapped in a `\u003clabel\u003e` doesn't work - [#1219](https://github.com/yairEO/tagify/issues/1219) and so Tagify internally sets the label's `for` attribute to an empty string, so clicking the Tagify component will not blur it and re-focus on the hidden input/textarea element Tagify is \"connected\" to\n\n## FAQ\nList of questions \u0026 scenarios which might come up during development with Tagify:\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eDynamic whitelist\u003c/strong\u003e\u003c/summary\u003e\nThe whitelist initial value is set like so:\n\n```js\nconst tagify = new Tagify(tagNode, {\n  whitelist: [\"a\", \"b\", \"c\"]\n})\n```\n\nIf changes to the whitelist are needed, they should be done like so:\n\n**Incorrect:**\n\n```js\ntagify.settings.whitelist = [\"foo\", \"bar\"]\n```\n\n**Correct:**\n```js\n// set the whitelist directly on the instance and not on the \"settings\" property\ntagify.whitelist = [\"foo\", \"bar\"]\n```\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003etags/whitelist data structure\u003c/strong\u003e\u003c/summary\u003e\n\nTagify does not accept just *any* kind of data structure.\u003cbr\u003e\nIf a tag data is represented as an `Object`, it **must** contain a **unique** property `value`\nwhich Tagify uses to check if a tag already exists, among other things, so make sure it is present.\n\n**Incorrect:**\n\n```javascript\n[{ \"id\":1, \"name\":\"foo bar\" }]\n```\n\n**Correct:**\n\n```javascript\n[{ \"id\":1, \"value\": 1, \"name\":\"foo bar\" }]\n```\n\n```javascript\n[{ \"value\":1, \"name\":\"foo bar\" }]\n```\n\n```javascript\n[{ \"value\":\"foo bar\" }]\n```\n\n```javascript\n// ad a simple array of Strings\n[\"foo bar\"]\n```\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eSave changes (Ex. to a server)\u003c/strong\u003e\u003c/summary\u003e\n\nIn framework-less projects, the developer should save the state of the Tagify component (somewhere), and\nthe question is:\u003cbr/\u003e\n**when should the state be saved?**\nOn every change made to *Tagify's* internal state (`tagify.value` via the `update()` method).\u003cbr\u003e\n\n\n```javascript\nvar tagify = new Tagify(...)\n\n// listen to \"change\" events on the \"original\" input/textarea element\ntagify.DOM.originalInput.addEventListener('change', onTagsChange)\n\n// This example uses async/await but you can use Promises, of course, if you prefer.\nasync function onTagsChange(e){\n  const {name, value} = e.target\n  // \"imaginary\" async function \"saveToServer\" should get the field's name \u0026 value\n  await saveToServer(name, value)\n}\n```\n\nIf you are using *React/Vue/Angular* or any \"modern\" framework, then you already know how to\nattach \"onChange\" event listeners to your `\u003cinput\u003e`/`\u003ctextarea\u003e` elements, so the above is irrelevant.\n\u003c/details\u003e\n\n----\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eRender tags in one single line\u003c/strong\u003e\u003c/summary\u003e\n\nStopping tags from wrapping to new lines, add this to your `.tagify` *selector CSS Rule*:\n\n```css\nflex-wrap: nowrap;\n````\n\u003c/details\u003e\n\n----\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eSubmit on `Enter` key\u003c/strong\u003e\u003c/summary\u003e\n\nTagify internally has `state` property, per `Tagify` instance\nand this may be useful for a variety of things when implementing a specific scenario.\n\n```js\nvar tagify = new Tagify(...)\nvar formElm = document.forms[0]; // just an example\n\ntagify.on('keydown', onTagifyKeyDown)\n\nfunction onTagifyKeyDown(e){\n  if( e.detail.event.key == 'Enter' \u0026\u0026  // \"enter\" key pressed\n      !tagify.state.inputText \u0026\u0026        // assuming user is not in the middle or adding a tag\n      !tagify.state.editing             // user not editing a tag\n    ){\n    setTimeout(() =\u003e formElm.submit())  // put some buffer to make sure tagify has done with whatever, to be on the safe-side\n  }\n\n}\n```\n\u003c/details\u003e\n\n---\n* [Disable the automatic whitelist (suggestions) filtering](https://github.com/yairEO/tagify/issues/1114)\n* [Double-click tag fires both \"edit\" \u0026 \"click\" custom events](https://github.com/yairEO/tagify/issues/247)\n* [Manualy open the suggestions dropdown](https://github.com/yairEO/tagify/issues/254)\n* [Render your own suggestions dropdown](https://github.com/yairEO/tagify/issues/244)\n* [Allow max length on mix mode](https://github.com/yairEO/tagify/issues/252)\n* [Always show dropdown](https://github.com/yairEO/tagify/issues/253)\n* [Limit the length of a tag value (minimum \u0026 maximum)](https://github.com/yairEO/tagify/issues/245)\n* [*Mixed mode* initial value](https://github.com/yairEO/tagify/issues/237)\n* [Random colors for each tag](https://github.com/yairEO/tagify/issues/223)\n* [Format input value for server side](https://github.com/yairEO/tagify/issues/220)\n* [Writing to tagify textarea](https://github.com/yairEO/tagify/issues/294)\n* [Scroll all tags within one line, instead of growing vertically](https://github.com/yairEO/tagify/issues/145)\n* [Insert emoji at caret location when editing a tag](https://github.com/yairEO/tagify/issues/365)\n* [propagate `change` event](https://github.com/yairEO/tagify/issues/413)\n* [Manually update tag data after it was added](https://github.com/yairEO/tagify/issues/433)\n* [Ajax Whitelist with \"enforceWhitelist\" setting enabled](https://github.com/yairEO/tagify/issues/465)\n* [Custom (multiple) tag validation \u0026 AJAX](https://github.com/yairEO/tagify/issues/474)\n* [Make tags from pasted multi-line text](https://github.com/yairEO/tagify/issues/160)\n* [Add a tag at *caret* position in *mixed mode*](https://github.com/yairEO/tagify/issues/524#issuecomment-699140465)\n* [Change automatic title tooltips for invalid tags](https://github.com/yairEO/tagify/issues/862)\n* [Create a submenu for the suggestions dropdown](https://github.com/yairEO/tagify/issues/1016#issuecomment-1106910803)\n* [Hide placeholder if tags exist](https://github.com/yairEO/tagify/issues/495#issuecomment-620498047)\n\n## CSS Variables\n\n\u003e Learn more about [CSS Variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties)) (custom properties)\n\nTagify's utilizes *CSS variables* which allow easy customization without the need to manually write CSS.\nIf you do wish to heavily style your Tagify components, then you can (and should) use the below variables within\nyour modified styles as much as you can.\n\nFor a *live* example, see the [demos page](https://yaireo.github.io/tagify/#section-different-look).\n\nName                            | Info\n------------------------------- | --------------------------------\n--tags-disabled-bg              | Tag background color when *disabled*\n--tags-border-color             | The outer border color which surrounds tagify\n--tags-hover-border-color       | *hover* state\n--tags-focus-border-color       | *focus* state\n--tag-border-radius             | Tag border radius\n--tag-bg                        | Tag background color\n--tag-hover                     | Tag background color on hover (mouse)\n--tag-text-color                | Tag text color\n--tag-text-color--edit          | Tag text color when a Tag is being edited\n--tag-pad                       | Tag padding, from all sides. Ex. `.3em .5em`\n--tag--min-width                | Minimum Tag width\n--tag--max-width                | Maximum tag width, which gets trimmed with *hellip* after\n--tag-inset-shadow-size         | This is the inner shadow size, which dictates the color of the Tags.\u003cbr\u003eIt's important the size fits *exactly* to the tag.\u003cbr\u003eChange this if you change the `--tag-pad` or fontsize.\n--tag-invalid-color             | For border color of edited tags with invalid value being typed into them\n--tag-invalid-bg                | Background color for invalid Tags.\n--tag-remove-bg                 | Tag background color when hovering the `×` button.\n--tag-remove-btn-color          | Remove (`×`) button text color\n--tag-remove-btn-bg             | Remove (`×`) button background color\n--tag-remove-btn-bg--hover      | Remove (`×`) button hover background color\n--input-color                   | Input text color\n--tag-hide-transition           | Controls the transition property when a tag is removed. default is '.3s'\n--placeholder-color             | Placeholder text color\n--placeholder-color-focus       | Placeholder text color when Tagify has focus and no input was typed\n--loader-size                   | Loading animation size. `1em` is pretty big, default is a bit less.\n--readonly-striped              | Either a value `1` or `0` can be used to toggle the striped diagonal background in *readonly*\n\n### Suggestions Dropdown CSS variables\n\nshould be appiled on the `:root {...}` selector\n\nName                              | Info\n--------------------------------- | --------------------------------\n--tagify-dd-color-primary         | Sugegstion's background color on hover\n--tagify-dd-text-color            | Sugegstion's text color\n--tagify-dd-bg-color              | The suggestion's dropdown background color\n--tagify-dd-item--hidden-duration | When selecting a suggestion, this is the duration for it to become hidden (shrink)\n--tagify-dd-item-pad              | Suggestion item padding\n--tagify-dd-max-height            | Maximum height of the suggestions dropdown (`300px` by default)\n\n### Full list of Tagify's [SCSS variables](https://github.com/yairEO/tagify/blob/master/src/tagify.scss#L9-L24)\n\n\n## Methods\n\n`Tagify` is [prototype](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes) based and There are many methods, but I've chosen to list the most relevant ones:\n\nName                       | Parameters                                                                              | Info\n-------------------------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------\n`destroy`                  |                                                                                         | Reverts the input element back as it was before Tagify was applied\n`removeAllTags`            |                                                                                         | Removes all tags and resets the original input tag's value property\n`addTags`                  | \u003col\u003e\u003cli\u003e`Array`/`String`/`Object` tag(s) to add\u003c/li\u003e\u003cli\u003e`Boolean` clear input after adding\u003c/li\u003e\u003cli\u003e`Boolean` - skip adding invalids\u003c/li\u003e\u003col\u003e  | Accepts a String (word, single or multiple with a delimiter), an Array of Objects (see above) or Strings.\n`addMixTags`               | `Array`/`String`                                                                        | Bypasses the normalization process in `addTags`, forcefully adding tags at the last caret location or at the end, if there's no last caret location saved (at `tagify.state.selection`)\n`removeTags`               | \u003col\u003e\u003cli\u003e`Array`/`HTMLElement`/`String` tag(s) to remove\u003c/li\u003e\u003cli\u003e`silent` does not update the component's value\u003c/li\u003e\u003cli\u003e`tranDuration` Transition duration (in `ms`)\u003c/li\u003e\u003c/ul\u003e | (#502) Remove single/multiple Tags. When nothing passed, removes last tag. \u003cul\u003e\u003cli\u003e`silent` - A flag, which when turned on, does not remove any value and does not update the original input value but simply removes the tag from tagify\u003c/li\u003e\u003cli\u003e`tranDuration` - delay for animation, after which the tag will be removed from the DOM\u003c/li\u003e\u003c/ul\u003e\n`addEmptyTag`              | `Object` \u003csub\u003e(`tagData`)\u003c/sub\u003e                                                         | Create an empty tag (optionally with pre-defined data) and enters \"edit\" mode directly. [See demo](https://yaireo.github.io/tagify#section-different-look)\n`loadOriginalValues`       | `String`/`Array`                                                                        | Converts the input's value into tags. This method gets called automatically when instansiating Tagify. Also works for mixed-tags\n`getWhitelistItemsByValue` | `Object`                                                                                | `{value}` - return an Array of found matching items (case-insensitive)\n`getTagIndexByValue`       | `String`                                                                                | Returns the index of a specific tag, by value\n`getTagElmByValue`         | `String`                                                                                | Returns the first matched tag node, if found\n`isTagDuplicate`           | `String`                                                                                | Returns how many tags already exists with that value\n`parseMixTags`             | `String`                                                                                | Converts a String argument (`[[foo]]⁠ and [[bar]]⁠ are..`) into HTML with mixed tags \u0026 texts\n`getTagElms`               |                                                                                         | Returns a DOM nodes list of all the tags\n`getTagElmByValue`         | `String`                                                                                | Returns a specific tag DOM node by value\n`getSetTagData`            | `HTMLElement`, `Object`                                                                 | set/get tag data on a tag element (has`.tagify__tag` class by default)\n`editTag`                  | `HTMLElement`                                                                           | Goes to edit-mode in a specific tag\n`getTagTextNode`           | `HTMLElement`                                                                           | Get the node which has the actual tag's content\n`setTagTextNode`           | `HTMLElement`, `String`                                                                 | Sets the text of a tag (DOM only, does not affect actual data)\n`replaceTag`               | `tagElm`, `Object` \u003csub\u003e(`tagData`)\u003c/sub\u003e                                               | Exit a tag's edit-mode. if \"tagData\" exists, replace the tag element with new data and update Tagify value\n`loading`                  | `Boolean`                                                                               | toggle loading state on/off (Ex. AJAX whitelist pulling)\n`tagLoading`               | `HTMLElement`, `Boolean`                                                                | same as above but for a specific tag element\n`createTagElem`            | `Object` \u003csub\u003e(`tagData`)\u003c/sub\u003e                                                         | Returns a tag element from the supplied tag data\n`injectAtCaret`            | `HTMLElement` \u003csub\u003e(`injectedNode`)\u003c/sub\u003e, `Object` \u003csub\u003e(`range`)\u003c/sub\u003e                | Injects text or HTML node at last caret position. `range` parameter is *optional*\n`placeCaretAfterNode`      | `HTMLElement`                                                                           | Places the caret after a given node\n`setRangeAtStartEnd`       | `Boolean`, `HTMLElement`                                                                | Places the caret at the start or the end of a node.\n`insertAfterTag`           | `HTMLElement` \u003csub\u003e(tag element)\u003c/sub\u003e, `HTMLElement`/`String` \u003csub\u003e(whatever to insert after)\u003c/sub\u003e |\n`toggleClass`              | `Boolean`                                                                               | Toggles `class` on the main *tagify* container (`scope`)\n`dropdown.selectAll`       |                                                                                         | Add **all** whitelist items as tags and close the suggestion dropdown\n`dropdown.show`            | `String`                                                                                | Shows the suggestions list dropdown. A string parameter allows filtering the results\n`dropdown.hide`            | `Boolean`                                                                               | Hides the suggestions list dropdown (if it's not managed manually by the developer)\n`dropdown.toggle`          | `Boolean`                                                                               | Toggles dropdown show/hide. the boolean parameter will force-show\n`updateValueByDOMTags`     |                                                                                         | Iterate tag DOM nodes and re-build  the `tagify.value` array (call this if tags get sorted manually)\n`parseTemplate`            | `String`/`Function` \u003csub\u003e(template name or function)\u003c/sub\u003e, `Array` \u003csub\u003e(data)\u003c/sub\u003e   | converts a template string (by selecting one from the `settings.templates` by name or supplying a template function which returns a String) into a DOM node\n`setReadonly`              | `Boolean`                                                                               | Toggles \"readonly\" mode on/off\n`setDisabled`              | `Boolean`                                                                               | Toggles \"disabled\" mode on/off\n`getPersistedData`         | `String`                                                                                | Get data for the specific instance by parameter\n`setPersistedData`         | `*`, `String`                                                                           | Set data for the specific instance. Must supply a second parameter which will be the key to save the data in the localstorage (under the tagify namespace)\n`clearPersistedData`       | `String`                                                                                | Clears data for the specific instance, by parameter. If the parameter is ommited, clears all persisted data related to this instance (by its `id` which was set in the instance's settings)\n`setPlaceholder`           | `String`                                                                                | Sets the placeholder's value. [See demo](https://yaireo.github.io/tagify/#section-advance-options)\n\n## Events\n\nTo listen to `tagify` events use the `.on(EVENT_NAME, EVENT_CALLBACK_REFERENCE)` method and stop listening use the `.off(EVENT_NAME, EVENT_CALLBACK_REFERENCE)`\n\nAll triggered events return the instance's scope (tagify).\u003cbr\u003e\nSee `e.detail` for custom-event additional data.\n\n\u003cdetails\u003e\n  \u003csummary\u003eExample 1\u003c/summary\u003e\n\n```javascript\nvar tagify = new Tagify(...)\n\n// events can be chainable, and multiple events may be binded for the same callback\ntagify\n  .on('input', onInput)\n  .on('edit:input edit:updated edit:start edit:keydown', e =\u003e console.log(e.type, e.detail))\n\nfunction onInput(e) {\n  console.log(e.detail)\n}\n\n// later in the code you might do to unsubscribe the event listener with a specific callback\ntagify.off('input', onInput)\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eExample 2\u003c/summary\u003e\n\n```javascript\nvar tagify = new Tagify(inputNode, {\n  callbacks: {\n    \"change\": (e) =\u003e console.log(e.detail),\n    \"dropdown:show\": (e) =\u003e console.log(e.detail)\n  }\n})\n```\n\u003c/details\u003e\n\nName               | Info\n------------------ | --------------------------------------------------------------------------\nchange             | Any change to the value has occurred. `e.detail.value` callback listener argument is a *String*\nadd                | A tag has been added\nremove             | A tag has been removed ([use `removeTag`](https://github.com/yairEO/tagify/issues/222) instead with *jQuery*)\ninvalid            | A tag has been added but did not pass validation. See [event detail](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events)\ninput              | [Input](https://developer.mozilla.org/en-US/docs/Web/Events/input) event, when a tag is being typed/edited. `e.detail` exposes `value`, `inputElm` \u0026 `isValid`\npaste              | Text pasted (not while editing a tag). The pasted text might or might not have been converted into tags, depneding if `pasteAsTags` setting is set to `false`\nclick              | Clicking a tag. Exposes the tag element, its index \u0026 data\ndblclick           | Double-clicking a tag\nkeydown            | When Tagify input has focus and a key was pressed\nfocus              | The component currently has focus\nblur               | The component lost focus\nedit:input         | Typing inside an edited tag\nedit:beforeUpdate  | Just before a tag has been updated, while still in \"edit\" mode\nedit:updated       | A tag as been updated (changed view editing or by directly calling the `replaceTag()` method)\nedit:start         | A tag is now in \"edit mode\"\nedit:keydown       | keydown event while an edited tag is in focus\ndropdown:show      | Suggestions dropdown is to be rendered. The dropdown DOM node is passed in the callback, [see demo](https://yaireo.github.io/tagify/#section-basic).\ndropdown:hide      | Suggestions dropdown has been removed from the DOM\ndropdown:select    | Suggestions dropdown item selected (by mouse/keyboard/touch)\ndropdown:scroll    | Tells the percentage scrolled. (`event.detail.percentage`)\ndropdown:noMatch   | No whitelist suggestion item matched for the typed input. At this point it is possible to manually set `tagify.suggestedListItems` to any possible custom value, for example: `[{ value:\"default\" }]`\ndropdown:updated   | Fired when the dropdown list is re-filtered while suggestions list is visible and a tag was removed so it was re-added as a suggestion\n\n## Hooks\n\n**Promise**-based hooks for *async* program flow scenarios.\n\nAllows to \"hook\" (intervene) at certain points of the program, which were selected as a suitable place to\n**pause** the program flow and wait for further instructions on how/if to proceed.\n\n\u003cdetails\u003e\n  \u003csummary\u003eFor example, if a developer wishes to add a (native) confirmation popup before a tag is removed (by a user action):\n\u003c/summary\u003e\n\n```javascript\nvar input = document.querySelector('input')\nvar tagify = new Tagify(input,{\n    hooks: {\n        /**\n         * Removes a tag\n         * @param  {Array}  tags [Array of Objects [{node:..., data:...}, {...}, ...]]\n         */\n        beforeRemoveTag : function( tags ){\n            return new Promise((resolve, reject) =\u003e {\n                confirm(\"Remove \" + tags[0].data.value + \"?\")\n                    ? resolve()\n                    : reject()\n            })\n        }\n    }\n})\n```\n\u003c/details\u003e\n\n\nName                   | Parameters                                  | Info\n---------------------- | ------------------------------------------- | --------------------------------------------------------------------------\nbeforeRemoveTag        | Array \u003csub\u003e(of Objects)\u003c/sub\u003e               | [Example](https://jsbin.com/xoseyux/edit?html,js,output)\nsuggestionClick        | Object \u003csub\u003e(click event data)\u003c/sub\u003e        | [Example](https://jsbin.com/tuwihuf/edit?html,js,output)\nbeforePaste            | `tagify`, `pastedText`, `clipboardData`     | Before pasted text was added to Tagify. *Resolve* with new paste value if needed\nbeforeKeyDown          |                                             | On any browser keydown event, but called after `keydown` Tagify event\n\n## [Settings](https://github.com/yairEO/tagify/blob/master/src/parts/defaults.js#L1)\n\nName                           | Type                         | Default                                     | Info\n------------------------------ | ---------------------------- | ------------------------------------------- | --------------------------------------------------------------------------\nid                             | \u003csub\u003eString\u003c/sub\u003e            |                                             | See [*Persisted data*](https://github.com/yairEO/tagify/#persisted-data)\ntagTextProp                    | \u003csub\u003eString\u003c/sub\u003e            | `\"value\"`                                   | Tag data Object property which will be displayed as the tag's text. Remember to keep \"value\" property \u003cem\u003eunique\u003c/em\u003e. See Also: `dropdown.mapValueTo`, `dropdown.searchKeys`\nplaceholder                    | \u003csub\u003eString\u003c/sub\u003e            |                                             | Placeholder text. If this attribute is set on an input/textarea element it will override this setting\ndelimiters                     | \u003csub\u003eString\u003c/sub\u003e            | `\",\"`                                       | [RegEx **string**] split tags by any of these delimiters. Example delimeters: \",\u0026#124;.\u0026#124; \" (*comma*, *dot* or *whitespace*)\npattern                        | \u003csub\u003eString/RegEx\u003c/sub\u003e      | null                                        | Validate input by RegEx pattern (can also be applied on the input itself as an attribute) Ex: `/[1-9]/`\nmode                           | \u003csub\u003eString\u003c/sub\u003e            | null                                        | Use `select` for single-value dropdown-like select box. See `mix` as value to allow mixed-content. The 'pattern' setting must be set to some character.\nmixTagsInterpolator            | \u003csub\u003eArray\u003c/sub\u003e             | \u003csub\u003e`['[[', ']]']`\u003c/sub\u003e                   | Interpolation for mix mode. Everything between these will become a tag\nmixTagsAllowedAfter            | \u003csub\u003eRegEx\u003c/sub\u003e             | \u003csub\u003e`/,\\|\\.\\|\\:\\|\\s/`\u003c/sub\u003e                | Define conditions in which typed mix-tags content is allowing a tag to be created after.\nduplicates                     | \u003csub\u003eBoolean\u003c/sub\u003e           | false                                       | Should duplicate tags be allowed or not\ntrim                           | \u003csub\u003eBoolean\u003c/sub\u003e           | true                                        | If `true` trim the tag's value (remove before/after whitespaces)\nenforceWhitelist               | \u003csub\u003eBoolean\u003c/sub\u003e           | false                                       | Should ONLY use tags allowed in whitelist.\u003cbr\u003eIn `mix-mode`, setting it  to `false` will not allow creating new tags.\nuserInput                      | \u003csub\u003eBoolean\u003c/sub\u003e           | true                                        | Disable manually typing/pasting/editing tags (tags may only be added from the whitelist). Can also use the `disabled` attribute on the original input element. To update this after initialization use the setter `tagify.userInput`\nfocusable                      | \u003csub\u003eBoolean\u003c/sub\u003e           | true                                        | Allow the component as a whole to recieve focus. Implementations of Tagify without an external border should not allow 'focusability' which causes unwanted behaviour. ([use-case example](file:///C:/Users/vsync/projects/tagify/index.html#section-different-look))\nautoComplete.enabled           | \u003csub\u003eBoolean\u003c/sub\u003e           | true                                        | Tries to suggest the input's value while typing (match from whitelist) by adding the rest of term as grayed-out text\nautoComplete.rightKey          | \u003csub\u003eBoolean\u003c/sub\u003e           | false                                       | If `true`, when `→` is pressed, use the suggested value to create a tag, else just auto-completes the input. In mixed-mode this is ignored and treated as \"true\"\nautoComplete.tabKey            | \u003csub\u003eBoolean\u003c/sub\u003e           | false                                       | If `true`, pressing `tab` key would only auto-complete (if a suggesiton is highlighted) but will not convert to a tag (like `rightKey` does) also, unless clicked again (considering the `addTagOn` setting).\nwhitelist                      | \u003csub\u003eArray\u003c/sub\u003e             | `[]`                                        | An array of allowed tags (*Strings* or *Objects*). When using *Objects* in the *whitelist* array a `value` property is a must \u0026 should be unique. \u003cbr/\u003eAlso, the *whitelist used for auto-completion when `autoCompletion.enabled` is `true`\nblacklist                      | \u003csub\u003eArray\u003c/sub\u003e             | `[]`                                        | An array of tags which aren't allowed\naddTagOnBlur                   | \u003csub\u003eBoolean\u003c/sub\u003e           | true                                        | Automatically adds the text which was inputed as a tag when blur event happens\naddTagOn                       | \u003csub\u003eArray\u003c/sub\u003e             | `['blur', 'tab', 'enter']`                  | If the tagify field (in a normal mode) has any non-tag input in it, convert it to a tag on any of these \"events\": blur away from the field, click \"tab\"/\"enter\" key\nonChangeAfterBlur              | \u003csub\u003eBoolean\u003c/sub\u003e           | true                                        | By default, the native way of inputs' `onChange` events is kept, and it only fires when the field is blured.\npasteAsTags                    | \u003csub\u003eBoolean\u003c/sub\u003e           | true                                        | Automatically converts pasted text into tags\ncallbacks                      | \u003csub\u003eObject\u003c/sub\u003e            | `{}`                                        | Exposed callbacks object to be triggered on events: `'add'` / `'remove'` tags\nmaxTags                        | \u003csub\u003eNumber\u003c/sub\u003e            | Infinity                                    | Maximum number of allowed tags. when reached, adds a class \"tagify--hasMaxTags\" to `\u003cTags\u003e`\neditTags                       | \u003csub\u003eObject/Number\u003c/sub\u003e     | `{}`                                        | `false` or `null` will disallow editing\neditTags.*clicks*              | \u003csub\u003eNumber\u003c/sub\u003e            | 2                                           | Number of clicks to enter \"edit-mode\": 1 for single click. Any other value is considered as double-click\neditTags.*keepInvalid*         | \u003csub\u003eBoolean\u003c/sub\u003e           | true                                        | keeps invalid edits as-is until `esc` is pressed while in focus\ntemplates                      | \u003csub\u003eObject\u003c/sub\u003e            | \u003csub\u003e`wrapper`, `tag`, `dropdownItem`\u003c/sub\u003e | Object consisting of functions which return template strings\nvalidate                       | \u003csub\u003eFunction\u003c/sub\u003e          |                                             | If the `pattern` setting does not meet your needs, use this function, which receives *tag data object* as an argument and should return `true` if validation passed or `false`/`string` if not. A *string* may be returned as the reason for the validation failure.\ntransformTag                   | \u003csub\u003eFunction\u003c/sub\u003e          |                                             | Takes a tag data as argument and allows mutating it before a tag is created or edited and also before validation.\u003cbr\u003eShould not `return` anything, only **mutate** the argument.\nkeepInvalidTags                | \u003csub\u003eBoolean\u003c/sub\u003e           | false                                       | If `true`, do not remove tags which did not pass validation\ncreateInvalidTags              | \u003csub\u003eBoolean\u003c/sub\u003e           | true                                        | If `true`, create invalid-tags. Otherwise, keep the editable input and do not create tags from it\nskipInvalid                    | \u003csub\u003eBoolean\u003c/sub\u003e           | false                                       | If `true`, do not add invalid, temporary, tags before automatically removing them\nbackspace                      | \u003csub\u003e*\u003c/sub\u003e                 | true                                        | On pressing backspace key:\u003cbr\u003e `true` - remove last tag \u003cbr\u003e`edit` - edit last tag\u003cbr\u003e`false` - do nothing (useful for outside style)\noriginalInputValueFormat       | \u003csub\u003eFunction\u003c/sub\u003e          |                                             | If you wish your original input/textarea `value` property format to other than the default (which I recommend keeping) you may use this and make sure it returns a *string*.\nmixMode.*insertAfterTag*       | \u003csub\u003eNode/String\u003c/sub\u003e       | `\\u00A0`                                    | `node` or `string` to add after a tag added |\na11y.*focusableTags*           | \u003csub\u003eBoolean\u003c/sub\u003e           | false                                       | allows tags to get focus, and also to be deleted via \u003ckbd\u003eBackspace\u003c/kbd\u003e\ndropdown.*enabled*             | \u003csub\u003eNumber\u003c/sub\u003e            | 2                                           | Minimum characters input for showing a suggestions list. `false` will not render a suggestions list.\ndropdown.*caseSensitive*       | \u003csub\u003eBoolean\u003c/sub\u003e           | false                                       | if `true`, match **exact** item when a suggestion is selected (from the dropdown) and also more strict matching for dulpicate items. **Ensure** `fuzzySearch` is `false` for this to work.\ndropdown.*maxItems*            | \u003csub\u003eNumber\u003c/sub\u003e            | 10                                          | Maximum items to show in the suggestions list\ndropdown.*classname*           | \u003csub\u003eString\u003c/sub\u003e            | `\"\"`                                        | Custom *classname* for the dropdown suggestions list\ndropdown.*fuzzySearch*         | \u003csub\u003eBoolean\u003c/sub\u003e           | true                                        | Enables filtering dropdown items values' by string *containing* and not only *beginning*\ndropdown.*sortby*              | \u003csub\u003eString/Function\u003c/sub\u003e   |                                             | If set as `startsWith` string, the suggestions list will be sorted with matched items which starts with the query shown first, and *exact* matches shown before all.\u003cbr\u003e\u003cbr\u003e If this setting is defined as a `function`, it receives two arguments: the array of filtered items and the query and it must return an Array.\u003cbr\u003e\u003cbr\u003e(*default sorting order is same as the whitelist's*)\ndropdown.*accentedSearch*      | \u003csub\u003eBoolean\u003c/sub\u003e           | true                                        | Enable searching for \u003cem\u003eaccented\u003c/em\u003e items in the whitelist without typing exact match (#491)\ndropdown.*includeSelectedTags* | \u003csub\u003eBoolean\u003c/sub\u003e           | false                                       | Should the suggestions list Include already-selected tags (after filtering), which will be marked with a checkmark `✓`\ndropdown.*escapeHTML*          | \u003csub\u003eBoolean\u003c/sub\u003e           | true                                        | Escapes HTML entities in the suggestions' rendered text\ndropdown.*position*            | \u003csub\u003eString\u003c/sub\u003e            | `\"all\"`                                     | \u003cul\u003e\u003cli\u003e`manual` - will not render the dropdown, and you would need to do it yourself. [See demo](https://yaireo.github.io/tagify/#section-manual-suggestions)\u003c/li\u003e\u003cli\u003e`text` - places the dropdown next to the caret\u003c/li\u003e\u003cli\u003e`input` - places the dropdown next to the input (useful in rare situations)\u003c/li\u003e\u003cli\u003e`all` - normal, full-width design\u003c/li\u003e\u003c/ul\u003e\ndropdown.*RTL*                 | \u003csub\u003eBoolean\u003c/sub\u003e           | false                                       | Dictates the dropdown's horizontal starting position. By default it would be aligned with the left side of the *Tagify* component.\ndropdown.*highlightFirst*      | \u003csub\u003eBoolean\u003c/sub\u003e           | false                                       | When a suggestions list is shown, highlight the first item, and also suggest it in the input (The suggestion can be accepted with \u003ckbd\u003e→\u003c/kbd\u003e key)\ndropdown.*closeOnSelect*       | \u003csub\u003eBoolean\u003c/sub\u003e           | true                                        | close the dropdown after selecting an item, if `enabled:0` is set (which means always show dropdown on focus)\ndropdown.*clearOnSelect*       | \u003csub\u003eBoolean\u003c/sub\u003e           | true                                        | Keep typed text after selecting a suggestion\ndropdown.*mapValueTo*          | \u003csub\u003eFunction/String\u003c/sub\u003e   |                                             | If whitelist is an Array of Objects:\u003cbr\u003eEx. `[{value:'foo', email:'foo@a.com'},...]`)\u003cbr\u003e this setting controlls which data \u003cem\u003ekey\u003c/em\u003e will be printed in the dropdown.\u003cbr\u003e Ex.1: `mapValueTo: data =\u003e \"To:\" + data.email`\u003cbr\u003eEx.2: `mapValueTo: \"email\"`\ndropdown.*searchKeys*          | \u003csub\u003eArray\u003c/sub\u003e             | \u003csub\u003e`[\"value\", \"searchBy\"]`\u003c/sub\u003e          | When a user types something and trying to match the whitelist items for suggestions, this setting allows matching other keys of a whitelist objects\ndropdown.*appendTarget*        | \u003csub\u003eHTMLNode/Function\u003c/sub\u003e | `document.body`                             | Target-Node which the *suggestions dropdown* is appended to (*only when rendered*). If used as a function, should return a DOM node.\ndropdown.*placeAbove*          | \u003csub\u003eBoolean\u003c/sub\u003e           |                                             | If defined, will force the placement of the dropdown in respect to the Boolean value: `true` will always show the suggestions dropdown above the input field and `false` will always show it below. By default this setting it not defined and the placement of the dropdown is automatically decided according to the space availble, where opening it *below* the input is preferred.\n","funding_links":[],"categories":["UI Components","HTML","Uncategorized"],"sub_categories":["Form Components","Uncategorized"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FyairEO%2Ftagify","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FyairEO%2Ftagify","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FyairEO%2Ftagify/lists"}