UNPKG

contact-input-component

Version:

![Tag Input Component Demo](https://github.com/ViniciusBenfica/contact-input-component/raw/main/demo.png)

301 lines (290 loc) 14.8 kB
import React__default, { createElement, useState, useRef, useEffect } from 'react'; function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; } function XIcon() { return createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, createElement("path", { d: "M18 6L6 18" }), createElement("path", { d: "M6 6l12 12" })); } function CheckIcon() { return createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, createElement("path", { d: "M20 6L9 17L4 12" })); } function styleInject(css, ref) { if ( ref === void 0 ) ref = {}; var insertAt = ref.insertAt; if (!css || typeof document === 'undefined') { return; } var head = document.head || document.getElementsByTagName('head')[0]; var style = document.createElement('style'); style.type = 'text/css'; if (insertAt === 'top') { if (head.firstChild) { head.insertBefore(style, head.firstChild); } else { head.appendChild(style); } } else { head.appendChild(style); } if (style.styleSheet) { style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); } } var css_248z = "body, html {\n\tmargin: 0;\n\tpadding: 0;\n}\n\ninput, button {\n\tall: unset;\n}\n\ninput {\n\twidth: auto;\n\tmin-width: 20px;\n}\n\nbutton {\n\tcursor: pointer;\n}\n\n.index_contact-input-wrapper__I0mwg {\n position: relative;\n width: 100%;\n}\n\n.index_contact-input-container__JWTEt {\n display: flex;\n align-items: center;\n min-height: 40px;\n width: 100%;\n border-radius: 4px;\n border: 1px solid #dbdde1;\n padding: 6px 12px;\n font-size: 14px;\n transition: all 0.2s ease;\n background-color: white;\n box-sizing: border-box;\n}\n\n.index_contact-input-container__JWTEt:focus-within {\n border-color: #1a73e8;\n box-shadow: 0 0 0 2px rgba(26, 115, 232, 0.3);\n}\n\n.index_contact-input-focused__94W3D {\n background-color: #f1f3f4;\n}\n\n.index_contact-input-label__ehkIk {\n color: #5f6368;\n margin-right: 8px;\n font-weight: 500;\n flex-shrink: 0;\n}\n\n.index_contact-input-tags__iAIum {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.index_contact-tag__K3ZEa {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n background-color: #e8eaed;\n border-radius: 16px;\n padding: 2px 4px;\n font-size: 14px;\n transition: background-color 0.2s ease;\n}\n\n.index_contact-tag__K3ZEa:hover {\n background-color: #dadce0;\n}\n\n.index_contact-avatar__uxua5 {\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n width: 24px;\n height: 24px;\n font-size: 12px;\n color: white;\n font-weight: 500;\n background-color: #4285f4;\n overflow: hidden;\n}\n\n.index_contact-tag-name__jZt-C {\n font-weight: 500;\n margin-top: -2px;\n}\n\n.index_contact-tag-remove__rcPdA {\n color: #5f6368;\n border-radius: 50%;\n padding: 2px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.index_contact-tag-remove__rcPdA:hover {\n color: #202124;\n background-color: rgba(0, 0, 0, 0.05);\n}\n\n.index_contact-input__c71ii {\n flex: 1;\n background-color: transparent;\n border: none;\n outline: none;\n min-width: 120px;\n height: 28px;\n}\n\n.index_contact-suggestions__dJuYK {\n position: absolute;\n z-index: 50;\n width: 100%;\n margin-top: 4px;\n background-color: white;\n border-radius: 4px;\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\n overflow: hidden;\n animation: index_scaleIn__YOBnR 0.2s ease;\n}\n\n@keyframes index_scaleIn__YOBnR {\n from {\n opacity: 0;\n transform: translateY(-10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n.index_contact-suggestions-list__XdJLM {\n padding: 4px 0;\n max-height: 280px;\n overflow-y: auto;\n}\n\n.index_contact-suggestion-item__5dlRN {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 16px;\n cursor: pointer;\n transition: background-color 0.2s ease;\n}\n\n.index_contact-suggestion-item__5dlRN:hover,\n.index_highlighted__juYTV {\n background-color: #f1f3f4;\n}\n\n.index_suggestion-details__qdLRu {\n display: flex;\n flex-direction: column;\n}\n\n.index_suggestion-name__2n4Gs {\n font-weight: 500;\n}\n\n.index_suggestion-contact__yj-0u {\n font-size: 13px;\n color: #5f6368;\n}\n\n.index_suggestion-check__vfUMT {\n margin-left: auto;\n color: #1a73e8;\n} "; var styles = {"contact-input-wrapper":"index_contact-input-wrapper__I0mwg","contact-input-container":"index_contact-input-container__JWTEt","contact-input-focused":"index_contact-input-focused__94W3D","contact-input-label":"index_contact-input-label__ehkIk","contact-input-tags":"index_contact-input-tags__iAIum","contact-tag":"index_contact-tag__K3ZEa","contact-avatar":"index_contact-avatar__uxua5","contact-tag-name":"index_contact-tag-name__jZt-C","contact-tag-remove":"index_contact-tag-remove__rcPdA","contact-input":"index_contact-input__c71ii","contact-suggestions":"index_contact-suggestions__dJuYK","scaleIn":"index_scaleIn__YOBnR","contact-suggestions-list":"index_contact-suggestions-list__XdJLM","contact-suggestion-item":"index_contact-suggestion-item__5dlRN","highlighted":"index_highlighted__juYTV","suggestion-details":"index_suggestion-details__qdLRu","suggestion-name":"index_suggestion-name__2n4Gs","suggestion-contact":"index_suggestion-contact__yj-0u","suggestion-check":"index_suggestion-check__vfUMT"}; styleInject(css_248z); var _excluded = ["label", "contacts", "selectedContacts", "onChange", "onInputChange", "allowNewContacts"]; var getFilteredContacts = function getFilteredContacts(inputValue, contacts) { return contacts.filter(function (_ref) { var name = _ref.name, description = _ref.description; var value = inputValue.toLowerCase(); return name.toLowerCase().includes(value) || (description == null ? void 0 : description.toLowerCase().includes(value)); }); }; var contactInput = function contactInput(_ref2) { var _ref2$label = _ref2.label, label = _ref2$label === void 0 ? 'To' : _ref2$label, _ref2$contacts = _ref2.contacts, contacts = _ref2$contacts === void 0 ? [] : _ref2$contacts, _ref2$selectedContact = _ref2.selectedContacts, selectedContacts = _ref2$selectedContact === void 0 ? [] : _ref2$selectedContact, onChange = _ref2.onChange, onInputChange = _ref2.onInputChange, _ref2$allowNewContact = _ref2.allowNewContacts, allowNewContacts = _ref2$allowNewContact === void 0 ? true : _ref2$allowNewContact, props = _objectWithoutPropertiesLoose(_ref2, _excluded); var _useState = useState(''), inputValue = _useState[0], setInputValue = _useState[1]; var _useState2 = useState(false), isFocused = _useState2[0], setIsFocused = _useState2[1]; var _useState3 = useState(false), isOpen = _useState3[0], setIsOpen = _useState3[1]; var _useState4 = useState(selectedContacts), selected = _useState4[0], setSelected = _useState4[1]; var _useState5 = useState([]), filteredContacts = _useState5[0], setFilteredContacts = _useState5[1]; var _useState6 = useState(-1), highlightedIndex = _useState6[0], setHighlightedIndex = _useState6[1]; var inputRef = useRef(null); var containerRef = useRef(null); useEffect(function () { setSelected(selectedContacts); }, [selectedContacts]); useEffect(function () { if (inputValue.trim()) { setFilteredContacts(getFilteredContacts(inputValue, contacts)); } else { setFilteredContacts([]); } }, [inputValue, contacts]); useEffect(function () { var handleClickOutside = function handleClickOutside(event) { if (containerRef.current && !containerRef.current.contains(event.target)) { setIsOpen(false); setIsFocused(false); } }; document.addEventListener('mousedown', handleClickOutside); return function () { document.removeEventListener('mousedown', handleClickOutside); }; }, []); var handleInputChange = function handleInputChange(e) { var value = e.target.value; setInputValue(value); if (value.trim()) { setIsOpen(true); setFilteredContacts(getFilteredContacts(value, contacts)); setHighlightedIndex(-1); } else { setIsOpen(false); setFilteredContacts([]); } if (onInputChange) { onInputChange(value); } }; var handleSelectContact = function handleSelectContact(contact) { var _inputRef$current; if (!selected.some(function (c) { return c.id === contact.id; })) { var newSelected = [].concat(selected, [contact]); setSelected(newSelected); if (onChange) { onChange(newSelected); } } setInputValue(''); setIsOpen(false); setFilteredContacts([]); (_inputRef$current = inputRef.current) == null || _inputRef$current.focus(); }; var handleRemoveContact = function handleRemoveContact(contactId) { var newSelected = selected.filter(function (c) { return c.id !== contactId; }); setSelected(newSelected); if (onChange) { onChange(newSelected); } }; var handleAddCustomContact = function handleAddCustomContact() { if (!inputValue.trim()) return; var newContact = { id: "" + Date.now(), name: inputValue.trim() }; var newSelected = [].concat(selected, [newContact]); setSelected(newSelected); if (onChange) { onChange(newSelected); } setInputValue(''); setIsOpen(false); setFilteredContacts([]); }; var handleKeyDown = function handleKeyDown(e) { if (e.key === 'ArrowDown' && (filteredContacts == null ? void 0 : filteredContacts.length) > 0) { e.preventDefault(); setHighlightedIndex(function (prevIndex) { return prevIndex < (filteredContacts == null ? void 0 : filteredContacts.length) - 1 ? prevIndex + 1 : 0; }); } else if (e.key === 'ArrowUp' && (filteredContacts == null ? void 0 : filteredContacts.length) > 0) { e.preventDefault(); setHighlightedIndex(function (prevIndex) { return prevIndex > 0 ? prevIndex - 1 : (filteredContacts == null ? void 0 : filteredContacts.length) - 1; }); } else if (e.key === 'Enter') { e.preventDefault(); if (highlightedIndex >= 0 && (filteredContacts == null ? void 0 : filteredContacts.length) > 0) { handleSelectContact(filteredContacts[highlightedIndex]); } else if (allowNewContacts && inputValue.trim()) { handleAddCustomContact(); } } else if (e.key === 'Escape') { setIsOpen(false); } else if (e.key === 'Backspace' && inputValue === '' && selected.length > 0) { handleRemoveContact(selected[selected.length - 1].id); } }; return React__default.createElement("div", { ref: containerRef, className: styles["contact-input-wrapper"] }, React__default.createElement("div", { className: styles["contact-input-container"] + " " + (isFocused ? styles["contact-input-focused"] : ''), onClick: function onClick() { var _inputRef$current2; return (_inputRef$current2 = inputRef.current) == null ? void 0 : _inputRef$current2.focus(); } }, label && React__default.createElement("span", { className: styles["contact-input-label"] }, label), React__default.createElement("div", { className: styles["contact-input-tags"] }, selected.map(function (contact) { return React__default.createElement("div", { key: contact.id, className: styles["contact-tag"] }, contact.image ? React__default.createElement("img", { src: contact.image, alt: contact.name, className: styles["contact-avatar"] }) : null, React__default.createElement("span", { className: styles["contact-tag-name"] }, contact.name), React__default.createElement("button", { type: "button", onClick: function onClick(e) { e.stopPropagation(); handleRemoveContact(contact.id); }, className: styles["contact-tag-remove"] }, React__default.createElement(XIcon, null))); }), React__default.createElement("input", Object.assign({ ref: inputRef, type: "text", className: styles["contact-input"], value: inputValue, onChange: handleInputChange, onFocus: function onFocus() { setIsFocused(true); if (inputValue.trim()) { setIsOpen(true); } }, onKeyDown: handleKeyDown }, props)))), isOpen && React__default.createElement("div", { className: styles["contact-suggestions"] }, React__default.createElement("div", { className: styles["contact-suggestions-list"] }, filteredContacts.length > 0 ? filteredContacts.map(function (contact, index) { return React__default.createElement("div", { key: contact.id, onClick: function onClick() { return handleSelectContact(contact); }, className: styles["contact-suggestion-item"] + " " + (highlightedIndex === index ? styles.highlighted : '') }, contact.image ? React__default.createElement("img", { src: contact.image, alt: contact.name, className: styles["contact-avatar"] }) : null, React__default.createElement("div", { className: styles["suggestion-details"] }, React__default.createElement("span", { className: styles["suggestion-name"] }, contact.name), contact.description && React__default.createElement("span", { className: styles["suggestion-contact"] }, contact.description)), highlightedIndex === index && React__default.createElement("span", { className: styles["suggestion-check"] }, React__default.createElement(CheckIcon, null))); }) : allowNewContacts && inputValue.trim() ? React__default.createElement("div", { className: styles["contact-suggestion-item"] + " " + styles.highlighted, onClick: handleAddCustomContact }, React__default.createElement("div", { className: styles["suggestion-details"] }, React__default.createElement("span", { className: styles["suggestion-name"] }, "\"", inputValue, "\"")), React__default.createElement("span", { className: styles["suggestion-check"] }, React__default.createElement(CheckIcon, null))) : null))); }; export default contactInput; //# sourceMappingURL=contact-input-component.esm.js.map