New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

react-tag-autocomplete

Package Overview
Dependencies
Maintainers
1
Versions
86
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-tag-autocomplete - npm Package Compare versions

Comparing version 7.0.0-rc.6 to 7.0.0-rc.7

1

changelog.md

@@ -39,2 +39,3 @@ # Changelog

- Added support for rendering a custom input component using `renderInput` prop
- Added support for rendering a custom text highlight component using `renderHighlight` prop

@@ -41,0 +42,0 @@ Other changes:

92

dist/ReactTags.cjs.js

@@ -58,3 +58,3 @@ "use strict";

function findSelectedOption(state) {
const tag = getNewTag(state.activeOption, state.value) || state.activeOption || findSuggestionExact(state.value, state.options);
const tag = getNewTag(state.activeOption, state.value) || state.activeOption || matchTagExact(state.value, state.options);
return tag && !tag.disabled ? tag : void 0;

@@ -76,2 +76,15 @@ }

}
function highlightText(text, query) {
const regexp = partialRegExp(query);
const matches = text.match(regexp);
if (matches) {
const match = matches[0];
const lastIndex = matches.index + match.length;
return [
text.slice(0, matches.index),
text.slice(matches.index, lastIndex),
text.slice(lastIndex)
];
}
}
const Whitespace = /\s+/g;

@@ -92,3 +105,3 @@ function tagToKey(tag) {

function partialRegExp(query) {
return new RegExp(`${escapeForRegExp(query)}`, "i");
return new RegExp(escapeForRegExp(query), "i");
}

@@ -98,21 +111,13 @@ function exactRegExp(query) {

}
function matchPartial(query) {
const regexp = partialRegExp(query);
return (value) => regexp.test(value);
}
function matchExact(query) {
const regexp = exactRegExp(query);
return (value) => regexp.test(value);
}
function matchSuggestionsPartial(query, suggestions) {
if (query === "") {
function matchTagsPartial(query, suggestions) {
if (query) {
const regexp = partialRegExp(query);
return suggestions.filter((item) => regexp.test(item.label));
} else {
return [].concat(suggestions);
} else {
const matcher = matchPartial(query);
return suggestions.filter((item) => matcher(item.label));
}
}
function findSuggestionExact(query, suggestions) {
const matcher = matchExact(query);
return suggestions.find((item) => matcher(item.label)) || null;
function matchTagExact(query, suggestions) {
const regexp = exactRegExp(query);
return suggestions.find((item) => regexp.test(item.label)) || null;
}

@@ -594,2 +599,21 @@ const DisableAutoCompleteAttrs = {

}
const DefaultHighlight = ({ classNames, text }) => {
return /* @__PURE__ */ React.createElement("mark", { className: classNames.highlight }, text);
};
function Highlight({ option, query, render = DefaultHighlight }) {
const { classNames } = React.useContext(GlobalContext);
let contents = option.label;
if (option.value === NewOptionValue || option.value === NoOptionsValue) {
contents = replacePlaceholder(option.label, query);
}
if (query) {
const result = highlightText(option.label, query);
if (result) {
const highlighted = render({ text: result[1], classNames });
contents = [result[0], highlighted, result[2]];
}
}
return /* @__PURE__ */ React.createElement(React.Fragment, null, contents);
}
const MemoizedHighlight = React.memo(Highlight);
const DefaultInput = ({ classNames, inputWidth, ...inputProps }) => {

@@ -644,20 +668,7 @@ return /* @__PURE__ */ React.createElement("input", { className: classNames.input, style: { width: inputWidth }, ...inputProps });

};
function Option({ index, render = DefaultOption }) {
const { classNames, managerRef } = React.useContext(GlobalContext);
function Option({ children, index, render = DefaultOption }) {
const { classNames } = React.useContext(GlobalContext);
const { option, optionProps } = useOption(index);
const children = /* @__PURE__ */ React.createElement(MemoisedOptionText, { option, query: managerRef.current.state.value });
return render({ classNames, children, option, ...optionProps });
}
function markText(name, value) {
const regexp = partialRegExp(value);
return name.replace(regexp, "<mark>$&</mark>");
}
function OptionText({ option, query }) {
if (option.value === NewOptionValue || option.value === NoOptionsValue) {
return /* @__PURE__ */ React.createElement("span", null, replacePlaceholder(option.label, query));
} else {
return /* @__PURE__ */ React.createElement("span", { dangerouslySetInnerHTML: { __html: markText(option.label, query) } });
}
}
const MemoisedOptionText = React.memo(OptionText);
const DefaultRoot = ({

@@ -712,3 +723,4 @@ children,

option: "react-tags__listbox-option",
optionIsActive: "is-active"
optionIsActive: "is-active",
highlight: "react-tags__listbox-option-highlight"
};

@@ -744,2 +756,3 @@ const DefaultDelimiterKeys = [KeyNames.Enter];

placeholderText = "Add a tag",
renderHighlight,
renderInput,

@@ -752,3 +765,3 @@ renderLabel,

suggestions = [],
suggestionsTransform = matchSuggestionsPartial,
suggestionsTransform = matchTagsPartial,
tagListLabelText = "Selected tags"

@@ -808,3 +821,10 @@ }, ref) {

}
), /* @__PURE__ */ React.createElement(ListBox, null, managerRef.current.state.options.map((tag, index) => /* @__PURE__ */ React.createElement(Option, { key: tagToKey(tag), index, render: renderOption })))), /* @__PURE__ */ React.createElement(Announcements, { ariaAddedText, ariaDeletedText }))
), /* @__PURE__ */ React.createElement(ListBox, null, managerRef.current.state.options.map((option, index) => /* @__PURE__ */ React.createElement(Option, { key: tagToKey(option), index, render: renderOption }, /* @__PURE__ */ React.createElement(
MemoizedHighlight,
{
option,
query: managerRef.current.state.value,
render: renderHighlight
}
))))), /* @__PURE__ */ React.createElement(Announcements, { ariaAddedText, ariaDeletedText }))
);

@@ -811,0 +831,0 @@ }

@@ -56,3 +56,3 @@ import React, { useContext, useMemo, useRef, useState, useEffect, useLayoutEffect, useCallback } from "react";

function findSelectedOption(state) {
const tag = getNewTag(state.activeOption, state.value) || state.activeOption || findSuggestionExact(state.value, state.options);
const tag = getNewTag(state.activeOption, state.value) || state.activeOption || matchTagExact(state.value, state.options);
return tag && !tag.disabled ? tag : void 0;

@@ -74,2 +74,15 @@ }

}
function highlightText(text, query) {
const regexp = partialRegExp(query);
const matches = text.match(regexp);
if (matches) {
const match = matches[0];
const lastIndex = matches.index + match.length;
return [
text.slice(0, matches.index),
text.slice(matches.index, lastIndex),
text.slice(lastIndex)
];
}
}
const Whitespace = /\s+/g;

@@ -90,3 +103,3 @@ function tagToKey(tag) {

function partialRegExp(query) {
return new RegExp(`${escapeForRegExp(query)}`, "i");
return new RegExp(escapeForRegExp(query), "i");
}

@@ -96,21 +109,13 @@ function exactRegExp(query) {

}
function matchPartial(query) {
const regexp = partialRegExp(query);
return (value) => regexp.test(value);
}
function matchExact(query) {
const regexp = exactRegExp(query);
return (value) => regexp.test(value);
}
function matchSuggestionsPartial(query, suggestions) {
if (query === "") {
function matchTagsPartial(query, suggestions) {
if (query) {
const regexp = partialRegExp(query);
return suggestions.filter((item) => regexp.test(item.label));
} else {
return [].concat(suggestions);
} else {
const matcher = matchPartial(query);
return suggestions.filter((item) => matcher(item.label));
}
}
function findSuggestionExact(query, suggestions) {
const matcher = matchExact(query);
return suggestions.find((item) => matcher(item.label)) || null;
function matchTagExact(query, suggestions) {
const regexp = exactRegExp(query);
return suggestions.find((item) => regexp.test(item.label)) || null;
}

@@ -592,2 +597,21 @@ const DisableAutoCompleteAttrs = {

}
const DefaultHighlight = ({ classNames, text }) => {
return /* @__PURE__ */ React.createElement("mark", { className: classNames.highlight }, text);
};
function Highlight({ option, query, render = DefaultHighlight }) {
const { classNames } = useContext(GlobalContext);
let contents = option.label;
if (option.value === NewOptionValue || option.value === NoOptionsValue) {
contents = replacePlaceholder(option.label, query);
}
if (query) {
const result = highlightText(option.label, query);
if (result) {
const highlighted = render({ text: result[1], classNames });
contents = [result[0], highlighted, result[2]];
}
}
return /* @__PURE__ */ React.createElement(React.Fragment, null, contents);
}
const MemoizedHighlight = React.memo(Highlight);
const DefaultInput = ({ classNames, inputWidth, ...inputProps }) => {

@@ -642,20 +666,7 @@ return /* @__PURE__ */ React.createElement("input", { className: classNames.input, style: { width: inputWidth }, ...inputProps });

};
function Option({ index, render = DefaultOption }) {
const { classNames, managerRef } = useContext(GlobalContext);
function Option({ children, index, render = DefaultOption }) {
const { classNames } = useContext(GlobalContext);
const { option, optionProps } = useOption(index);
const children = /* @__PURE__ */ React.createElement(MemoisedOptionText, { option, query: managerRef.current.state.value });
return render({ classNames, children, option, ...optionProps });
}
function markText(name, value) {
const regexp = partialRegExp(value);
return name.replace(regexp, "<mark>$&</mark>");
}
function OptionText({ option, query }) {
if (option.value === NewOptionValue || option.value === NoOptionsValue) {
return /* @__PURE__ */ React.createElement("span", null, replacePlaceholder(option.label, query));
} else {
return /* @__PURE__ */ React.createElement("span", { dangerouslySetInnerHTML: { __html: markText(option.label, query) } });
}
}
const MemoisedOptionText = React.memo(OptionText);
const DefaultRoot = ({

@@ -710,3 +721,4 @@ children,

option: "react-tags__listbox-option",
optionIsActive: "is-active"
optionIsActive: "is-active",
highlight: "react-tags__listbox-option-highlight"
};

@@ -742,2 +754,3 @@ const DefaultDelimiterKeys = [KeyNames.Enter];

placeholderText = "Add a tag",
renderHighlight,
renderInput,

@@ -750,3 +763,3 @@ renderLabel,

suggestions = [],
suggestionsTransform = matchSuggestionsPartial,
suggestionsTransform = matchTagsPartial,
tagListLabelText = "Selected tags"

@@ -806,3 +819,10 @@ }, ref) {

}
), /* @__PURE__ */ React.createElement(ListBox, null, managerRef.current.state.options.map((tag, index) => /* @__PURE__ */ React.createElement(Option, { key: tagToKey(tag), index, render: renderOption })))), /* @__PURE__ */ React.createElement(Announcements, { ariaAddedText, ariaDeletedText }))
), /* @__PURE__ */ React.createElement(ListBox, null, managerRef.current.state.options.map((option, index) => /* @__PURE__ */ React.createElement(Option, { key: tagToKey(option), index, render: renderOption }, /* @__PURE__ */ React.createElement(
MemoizedHighlight,
{
option,
query: managerRef.current.state.value,
render: renderHighlight
}
))))), /* @__PURE__ */ React.createElement(Announcements, { ariaAddedText, ariaDeletedText }))
);

@@ -809,0 +829,0 @@ }

@@ -59,3 +59,3 @@ (function(global, factory) {

function findSelectedOption(state) {
const tag = getNewTag(state.activeOption, state.value) || state.activeOption || findSuggestionExact(state.value, state.options);
const tag = getNewTag(state.activeOption, state.value) || state.activeOption || matchTagExact(state.value, state.options);
return tag && !tag.disabled ? tag : void 0;

@@ -77,2 +77,15 @@ }

}
function highlightText(text, query) {
const regexp = partialRegExp(query);
const matches = text.match(regexp);
if (matches) {
const match = matches[0];
const lastIndex = matches.index + match.length;
return [
text.slice(0, matches.index),
text.slice(matches.index, lastIndex),
text.slice(lastIndex)
];
}
}
const Whitespace = /\s+/g;

@@ -93,3 +106,3 @@ function tagToKey(tag) {

function partialRegExp(query) {
return new RegExp(`${escapeForRegExp(query)}`, "i");
return new RegExp(escapeForRegExp(query), "i");
}

@@ -99,21 +112,13 @@ function exactRegExp(query) {

}
function matchPartial(query) {
const regexp = partialRegExp(query);
return (value) => regexp.test(value);
}
function matchExact(query) {
const regexp = exactRegExp(query);
return (value) => regexp.test(value);
}
function matchSuggestionsPartial(query, suggestions) {
if (query === "") {
function matchTagsPartial(query, suggestions) {
if (query) {
const regexp = partialRegExp(query);
return suggestions.filter((item) => regexp.test(item.label));
} else {
return [].concat(suggestions);
} else {
const matcher = matchPartial(query);
return suggestions.filter((item) => matcher(item.label));
}
}
function findSuggestionExact(query, suggestions) {
const matcher = matchExact(query);
return suggestions.find((item) => matcher(item.label)) || null;
function matchTagExact(query, suggestions) {
const regexp = exactRegExp(query);
return suggestions.find((item) => regexp.test(item.label)) || null;
}

@@ -595,2 +600,21 @@ const DisableAutoCompleteAttrs = {

}
const DefaultHighlight = ({ classNames, text }) => {
return /* @__PURE__ */ React.createElement("mark", { className: classNames.highlight }, text);
};
function Highlight({ option, query, render = DefaultHighlight }) {
const { classNames } = React.useContext(GlobalContext);
let contents = option.label;
if (option.value === NewOptionValue || option.value === NoOptionsValue) {
contents = replacePlaceholder(option.label, query);
}
if (query) {
const result = highlightText(option.label, query);
if (result) {
const highlighted = render({ text: result[1], classNames });
contents = [result[0], highlighted, result[2]];
}
}
return /* @__PURE__ */ React.createElement(React.Fragment, null, contents);
}
const MemoizedHighlight = React.memo(Highlight);
const DefaultInput = ({ classNames, inputWidth, ...inputProps }) => {

@@ -645,20 +669,7 @@ return /* @__PURE__ */ React.createElement("input", { className: classNames.input, style: { width: inputWidth }, ...inputProps });

};
function Option({ index, render = DefaultOption }) {
const { classNames, managerRef } = React.useContext(GlobalContext);
function Option({ children, index, render = DefaultOption }) {
const { classNames } = React.useContext(GlobalContext);
const { option, optionProps } = useOption(index);
const children = /* @__PURE__ */ React.createElement(MemoisedOptionText, { option, query: managerRef.current.state.value });
return render({ classNames, children, option, ...optionProps });
}
function markText(name, value) {
const regexp = partialRegExp(value);
return name.replace(regexp, "<mark>$&</mark>");
}
function OptionText({ option, query }) {
if (option.value === NewOptionValue || option.value === NoOptionsValue) {
return /* @__PURE__ */ React.createElement("span", null, replacePlaceholder(option.label, query));
} else {
return /* @__PURE__ */ React.createElement("span", { dangerouslySetInnerHTML: { __html: markText(option.label, query) } });
}
}
const MemoisedOptionText = React.memo(OptionText);
const DefaultRoot = ({

@@ -713,3 +724,4 @@ children,

option: "react-tags__listbox-option",
optionIsActive: "is-active"
optionIsActive: "is-active",
highlight: "react-tags__listbox-option-highlight"
};

@@ -745,2 +757,3 @@ const DefaultDelimiterKeys = [KeyNames.Enter];

placeholderText = "Add a tag",
renderHighlight,
renderInput,

@@ -753,3 +766,3 @@ renderLabel,

suggestions = [],
suggestionsTransform = matchSuggestionsPartial,
suggestionsTransform = matchTagsPartial,
tagListLabelText = "Selected tags"

@@ -809,3 +822,10 @@ }, ref) {

}
), /* @__PURE__ */ React.createElement(ListBox, null, managerRef.current.state.options.map((tag, index) => /* @__PURE__ */ React.createElement(Option, { key: tagToKey(tag), index, render: renderOption })))), /* @__PURE__ */ React.createElement(Announcements, { ariaAddedText, ariaDeletedText }))
), /* @__PURE__ */ React.createElement(ListBox, null, managerRef.current.state.options.map((option, index) => /* @__PURE__ */ React.createElement(Option, { key: tagToKey(option), index, render: renderOption }, /* @__PURE__ */ React.createElement(
MemoizedHighlight,
{
option,
query: managerRef.current.state.value,
render: renderHighlight
}
))))), /* @__PURE__ */ React.createElement(Announcements, { ariaAddedText, ariaDeletedText }))
);

@@ -812,0 +832,0 @@ }

@@ -20,2 +20,3 @@ // Generated by dts-bundle-generator v7.2.0

optionIsActive: string;
highlight: string;
};

@@ -61,2 +62,7 @@ export type Tag = {

};
export type HighlightRendererProps = {
text: string;
classNames: ClassNames;
};
export type HighlightRenderer = React.FunctionComponent<HighlightRendererProps>;
export type InputRendererProps = React.ComponentPropsWithoutRef<"input"> & {

@@ -120,2 +126,3 @@ classNames: ClassNames;

placeholderText?: string;
renderHighlight?: HighlightRenderer;
renderInput?: InputRenderer;

@@ -122,0 +129,0 @@ renderLabel?: LabelRenderer;

@@ -32,10 +32,27 @@ import React, { useCallback, useRef, useState } from 'react'

// NOTE: if focus moves from the component input to this button then the listbox will be
// closed immediately so this avoids toggling it instantly back to its previous state
const skipToggle = useRef(false)
const toggle = useCallback(() => {
api.current.listBox.isExpanded ? api.current.listBox.collapse() : api.current.listBox.expand()
if (skipToggle.current) {
skipToggle.current = false
} else {
api.current.listBox.isExpanded ? api.current.listBox.collapse() : api.current.listBox.expand()
}
}, [api])
const toggleCapture = useCallback(
(e) => {
if (api.current.listBox.isExpanded && e.target !== document.activeElement) {
skipToggle.current = true
}
},
[api]
)
return (
<>
<p>Use the buttons below to control the component:</p>
<p>
<p style={{ display: 'flex', gap: '0.5rem' }}>
<button type="button" onClick={focus}>

@@ -47,4 +64,4 @@ Focus input

</button>
<button type="button" onClick={toggle}>
Toggle options list
<button type="button" onClick={toggle} onMouseDown={toggleCapture}>
Toggle listbox
</button>

@@ -51,0 +68,0 @@ </p>

@@ -7,3 +7,3 @@ # Migration Guide from v6 to v7

This version is for use with React v18 and above _only_. If you are using an older version of React please continue to use [the previous version of this component](https://github.com/i-like-robots/react-tags).
This version is for use with React v18 and above _only_. If you are using an older version of React please continue to use [the previous version of this component](https://github.com/i-like-robots/react-tags) which will remain supported for at least 12 months.

@@ -39,3 +39,3 @@ ## Prop name changes

The structure of suggested and selected tags has been updated. The `id` property has been renamed `value` and the `name` property has been renamed `label`.
The structure of suggested and selected tags has been updated. The `id` property has been renamed `value` and the `name` property has been renamed `label`. This change was made for consistency with the [`HTMLOptionElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLOptionElement) interface used by native listbox components.

@@ -53,3 +53,3 @@ ```diff

There are a number changes to the HTML structure of the component and some property names have also been changed. The component supports more states and more sub-components have class names defined for them than before.
There are a number changes to the HTML structure of the component and some property names have also been changed. The component supports more states and more sub-components have class names applied to them than before. This results in less stylesheet specificity and better support for using tools such as [`@emotion/css`](https://emotion.sh/docs/@emotion/css).

@@ -77,1 +77,2 @@ Please refer to the [new example styles](example/src/styles.css) for more details.

| `suggestionPrefix` | - | Removed component |
| - | `highlight` | New sub-component |
{
"name": "react-tag-autocomplete",
"version": "7.0.0-rc.6",
"version": "7.0.0-rc.7",
"description": "A simple, accessible, tagging component ready to drop into your React projects.",

@@ -43,8 +43,8 @@ "main": "dist/ReactTags.cjs.js",

"@types/node": "^16.18.0",
"@types/react": "^18.0.20",
"@types/react": "^18.2.0",
"@typescript-eslint/eslint-plugin": "^5.56.0",
"@typescript-eslint/parser": "^5.56.0",
"@vitejs/plugin-react-refresh": "^1.3.6",
"@vitest/coverage-c8": "^0.29.0",
"axe-core": "^4.5.0",
"@vitest/coverage-c8": "^0.31.0",
"axe-core": "^4.7.0",
"dts-bundle-generator": "^7.2.0",

@@ -59,5 +59,5 @@ "eslint": "^8.36.0",

"react-dom": "^18.2.0",
"typescript": "^4.9.0",
"vite": "^4.2.0",
"vitest": "^0.29.0"
"typescript": "^5.0.0",
"vite": "^4.3.0",
"vitest": "^0.31.0"
},

@@ -64,0 +64,0 @@ "peerDependencies": {

@@ -100,2 +100,3 @@ # React Tag Autocomplete

- [`placeholderText`](#placeholderText-optional)
- [`renderHighlight`](#renderHighlight-optional)
- [`renderInput`](#renderInput-optional)

@@ -163,2 +164,3 @@ - [`renderLabel`](#renderLabel-optional)

optionIsActive: 'is-active',
highlight: 'react-tags__listbox-option-highlight',
}

@@ -269,2 +271,12 @@ ```

#### renderHighlight (optional)
A custom option text highlight component to render. Receives the highlighted text and [`classNames`](#classNames-optional) as props. Defaults to `null`.
```jsx
function CustomHighlight({ classNames, text }) {
return <span className={classNames.highlight}>{text}</span>
}
```
#### renderInput (optional)

@@ -271,0 +283,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc