UNPKG

@codinglane/dropdown

Version:
186 lines (185 loc) 10.5 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Dropdown = void 0; const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = __importDefault(require("react")); const Icons = __importStar(require("react-feather")); require("./Dropdown.css"); const Components = __importStar(require("../components")); const Contracts = __importStar(require("../contracts")); /** * @param id * The current id of the dropdown. * @param value * The current chosen value for the dropdown, typeof string. * The value is not getting managed inside the component. You have to provide a function to manage the current chosen value. * @param searchable * Set this value to true, if you want to have a dropdown with an input field to search for a specific option * @param className * Set the classname of the dropdown, if you want to set your custom style. * @param closeOnSelect * Set this value to true, if you want that the options of the dropdown are getting closed as soon as you choose an option. * @param fields * These are the possible options for the dropdown. If you want to have grouped dropdown options, set the type of your fields to GroupedDropdownOptions * and set the group tag for all of the fields. * @param placeholder * The placeholder for the dropdown. When the placeholder is set and the value is undefined or not assignable to a field, the placeholder is getting showed. * @param onChange * This is the function to manage the current chosen value for the dropdown. * @param onFocus * This function is getting called on focus of the options menu. * @param onBlur * This function is getting called on blur of the options menu. * @param onFavorizeOption * The on favorize option is getting called as soon as the favorize icon in the option menu is getting clicked. * This dropdown do not manage the favorites on its own. You have to manage the favorites, to see changes in the component. * @param favoriteLabels * The labels for the favorite and non favorite group. * @param style * Custom stylesheet for the dropdown. It is possible to set the bg color, the color (for text & border), the font size and the font family. * @param data-testid * For testing purpose. * @returns {JSX.Element} */ const Dropdown = (_a) => { var _b; var { id, fields, value, searchable = false, className, closeOnSelect = false, placeholder, onChange, onBlur, onFocus, onFavorizeOption, style } = _a, props = __rest(_a, ["id", "fields", "value", "searchable", "className", "closeOnSelect", "placeholder", "onChange", "onBlur", "onFocus", "onFavorizeOption", "style"]); const dropdown = react_1.default.useRef(); const setDropdown = (ref) => (dropdown.current = ref); const input = react_1.default.useRef(); const setInput = (ref) => (input.current = ref); const menu = react_1.default.useRef(); const setMenu = (ref) => (menu.current = ref); const textContainer = react_1.default.useRef(); const setTextContainer = (ref) => (textContainer.current = ref); const text = react_1.default.useRef(); const setText = (ref) => (text.current = ref); const [currentVisibleOptions, setCurrentVisibleOptions] = react_1.default.useState([]); const grouped = fields.some((fld) => fld.group !== undefined); const favorize = fields.some((fld) => fld.favorite !== undefined); const [active, setActive] = react_1.default.useState(false); const [search, setSearch] = react_1.default.useState(null); const field = react_1.default.useMemo(() => { var _a; return (_a = fields.find((fld) => fld.value === value)) === null || _a === void 0 ? void 0 : _a.label; }, [value, fields]); const anchor = react_1.default.useMemo(() => { if (!active) return; if (!input.current) return; const height = document.body.clientHeight; const isScrollable = document.body.clientHeight < document.body.scrollHeight; const at = input.current.getBoundingClientRect().bottom + 5; if (input.current.getBoundingClientRect().bottom + Contracts.MENU_MAX_HEIGHT + 10 > height) return { at: height - input.current.getBoundingClientRect().top + 5 - (isScrollable ? Contracts.getScrollbarWidth() : 0), direction: 'UP', }; return { at, direction: 'DOWN' }; }, [active, input.current]); const handleOptionClick = (option) => { onChange(option); setSearch(null); if (!closeOnSelect) return; setActive(false); }; react_1.default.useEffect(() => { Contracts.setStyleSheet(style); }, [style]); react_1.default.useEffect(() => { if (active && onFocus) onFocus(); if (!active && onBlur) onBlur(); }, [active]); const handleSearch = (event) => setSearch(event.currentTarget.value); const toggle = () => setActive((prev) => !prev); const enterSearch = (event) => { event.persist(); if (event.key.toLowerCase() !== 'enter') return; if (search === null) return; if (currentVisibleOptions.length > 0) onChange(currentVisibleOptions[0]); setSearch(null); toggle(); }; react_1.default.useEffect(() => { if (!input.current) return; if (active) input.current.focus(); else { setSearch(null); input.current.blur(); } }, [input.current, active]); react_1.default.useEffect(() => { const clickListener = (event) => { var _a; if ((_a = dropdown.current) === null || _a === void 0 ? void 0 : _a.contains(event.target)) return; setActive(false); }; const scrollListener = (event) => { var _a; if ((_a = menu.current) === null || _a === void 0 ? void 0 : _a.contains(event.target)) return; setActive(false); }; document.addEventListener('click', clickListener); document.addEventListener('scroll', scrollListener, true); return () => { document.removeEventListener('click', clickListener); document.removeEventListener('scroll', scrollListener, true); }; }, []); react_1.default.useLayoutEffect(() => { if (!textContainer.current || !text.current || !input.current) return; textContainer.current.style.width = `${text.current.clientWidth + 17.5}px`; input.current.style.width = `${text.current.clientWidth - 4.5}px`; }, [text.current, textContainer.current, value]); return ((0, jsx_runtime_1.jsxs)("div", Object.assign({ className: `dropdown ${active ? 'active' : ''} ${className !== null && className !== void 0 ? className : ''}`, ref: setDropdown }, props, { children: [(0, jsx_runtime_1.jsxs)("div", Object.assign({ className: 'customBase dropdown-search' }, { children: [(0, jsx_runtime_1.jsxs)("div", Object.assign({ className: 'dropdown-text'.concat(!searchable ? ' dropdown-readonly' : ''), onClick: toggle, ref: setTextContainer }, { children: [!search && ((0, jsx_runtime_1.jsx)("div", Object.assign({ className: `dropdown-searchtext ${field === undefined ? 'dropdown-placeholder' : ''}`, ref: setText }, { children: field !== null && field !== void 0 ? field : placeholder }))), (0, jsx_runtime_1.jsx)(Components.BaseInput, { value: search !== null && search !== void 0 ? search : '', id: id === null || id === void 0 ? void 0 : id.concat('input'), onChange: handleSearch, className: 'dropdown-searchinput', autoComplete: 'off', autoCapitalize: 'off', autoCorrect: 'off', disabled: !searchable, ref: setInput, onKeyUp: enterSearch, "data-testid": (_b = props['data-testid']) === null || _b === void 0 ? void 0 : _b.concat('-input') })] })), (0, jsx_runtime_1.jsx)(Icons.ChevronDown, { size: 16, className: 'dropdown-searchicon', onClick: toggle })] })), grouped || favorize ? ((0, jsx_runtime_1.jsx)(Components.Grouped, Object.assign({ id: id, onOptionClick: handleOptionClick, onFilteredChange: setCurrentVisibleOptions, options: fields, current: value, anchor: anchor, ref: setMenu, filter: search, onFavorize: onFavorizeOption, grouping: grouped, favorize: favorize }, props))) : ((0, jsx_runtime_1.jsx)(Components.Standard, Object.assign({ id: id, onOptionClick: handleOptionClick, options: fields, current: value, filter: search, anchor: anchor, ref: setMenu, onFilteredChange: setCurrentVisibleOptions }, props)))] }))); }; exports.Dropdown = Dropdown;