UNPKG

@explita/daily-toolset-components

Version:

A lightweight and versatile collection of TypeScript utility functions and form components, inspired by ShadCN UI, designed for seamless everyday development. Enhance your Node.js, React, and Next.js projects with a well-structured suite of helpers for st

125 lines (124 loc) 8.37 kB
"use client"; "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 () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.Select = Select; const react_1 = __importStar(require("react")); const lu_1 = require("react-icons/lu"); const command_1 = require("../ui/command"); const popover_1 = require("../ui/popover"); const scroll_area_1 = require("../ui/scroll-area"); const FormLabel_1 = require("./FormLabel"); const ClearInput_1 = require("./ClearInput"); const InputError_1 = require("./InputError"); const daily_toolset_hooks_1 = require("@explita/daily-toolset-hooks"); const useField_1 = require("../hooks/useField"); const Button_1 = require("./Button"); function Select({ addEmpty = false, align = "start", name = "", label, size, options, isDisabled = false, isRequired = true, isSearchable = false, isClearable = true, defaultValue, handleSelection, error, placeholder = "", classNames, }) { const { fieldError, fieldValue, setValue } = (0, useField_1.useField)(name); const [open, setOpen] = (0, react_1.useState)(false); const [search, setSearch] = (0, react_1.useState)(""); const { label: labelClass = "", trigger = "", root = "", dropdown = "", input = "", item: itemClass = "", empty = "", } = classNames || {}; const id = (0, react_1.useId)(); // Memoize form value to prevent unnecessary re-renders const formValue = (0, react_1.useMemo)(() => fieldValue !== null && fieldValue !== void 0 ? fieldValue : "", [fieldValue, name]); // Set initial state from defaultValue or formValues[name] const [inputValue, setInputValue] = (0, react_1.useState)(() => (defaultValue !== null && defaultValue !== void 0 ? defaultValue : formValue).toString()); // Ensure inputValue always stays in sync with formValues[name] (0, react_1.useEffect)(() => { setInputValue((defaultValue !== null && defaultValue !== void 0 ? defaultValue : formValue).toString()); }, [formValue, defaultValue]); const errorData = error ? error : fieldError || ""; // Format options and ensure all values are strings const formattedOptions = (0, react_1.useMemo)(() => { let optionsList = (options === null || options === void 0 ? void 0 : options.map((option) => { var _a, _b; return ({ ...option, value: ((_a = option.value) === null || _a === void 0 ? void 0 : _a.toString()) || "", disabled: (_b = option.disabled) !== null && _b !== void 0 ? _b : false, }); })) || []; if (addEmpty && options) { optionsList = [{ value: "", label: "", disabled: false }, ...optionsList]; } return optionsList; }, [options, addEmpty]); const filteredOptions = (0, react_1.useMemo)(() => { if (!isSearchable || !search.trim()) return formattedOptions; const lowerSearch = search.toLowerCase(); return formattedOptions.filter((opt) => opt.value.toLowerCase().includes(lowerSearch) || // Ensure value is lowercase (0, daily_toolset_hooks_1.stripTags)(opt.label).toLowerCase().includes(lowerSearch)); }, [formattedOptions, isSearchable, search]); function handleSelect(currentValue) { if (currentValue === inputValue && !isClearable) { setOpen(false); return; } const selected = currentValue === inputValue && isClearable ? "" : currentValue; setInputValue(selected); handleSelection === null || handleSelection === void 0 ? void 0 : handleSelection(selected); setValue(selected); setOpen(false); } const selectedOption = filteredOptions.find((item) => item.value === inputValue); return (react_1.default.createElement("div", { className: "explita-input-root" }, react_1.default.createElement(FormLabel_1.Label, { id: id, label: label, isRequired: isRequired, className: labelClass }), react_1.default.createElement(popover_1.Popover, { open: open, onOpenChange: setOpen }, react_1.default.createElement("div", null, isClearable && inputValue !== "" && !isDisabled && (react_1.default.createElement(ClearInput_1.Clear, { onClick: () => handleSelect("") })), react_1.default.createElement(popover_1.PopoverTrigger, { asChild: true }, react_1.default.createElement(Button_1.Button, { variant: "outline", role: "combobox", size: size, "aria-expanded": open, "data-error": errorData.length > 0, "data-empty": !inputValue, "data-clearable": isClearable && inputValue !== "", className: `select-trigger ${trigger}`, disabled: isDisabled }, react_1.default.createElement("span", null, inputValue ? (0, daily_toolset_hooks_1.stripTags)(selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.label) : placeholder), (!isClearable || !inputValue) && (react_1.default.createElement(lu_1.LuChevronsUpDown, { className: "chevron-icon" }))))), react_1.default.createElement(popover_1.PopoverContent, { className: `explita-popover-content ${root}`, align: align, onPointerDown: (e) => e.stopPropagation(), forceMount: true }, react_1.default.createElement(command_1.Command, { shouldFilter: false, loop: true }, isSearchable && (react_1.default.createElement(command_1.CommandInput, { placeholder: "Search...", value: search, onValueChange: setSearch, className: `${input}` })), react_1.default.createElement(command_1.CommandList, null, react_1.default.createElement(scroll_area_1.ScrollArea, { className: dropdown }, react_1.default.createElement("div", { className: `select-list` }, react_1.default.createElement(command_1.CommandEmpty, { className: `empty-list ${empty}` }, "No records found."), react_1.default.createElement(command_1.CommandGroup, null, filteredOptions.map((item, index) => (react_1.default.createElement(command_1.CommandItem, { key: index, value: item.value, onSelect: (value) => { handleSelect(value); setSearch(""); }, className: `select-list-item ${itemClass}` }, react_1.default.createElement("span", null, react_1.default.createElement("span", null, item.label), react_1.default.createElement("span", { className: "description" }, item.description)), react_1.default.createElement(lu_1.LuCheck, { "data-checked": inputValue && inputValue === item.value, className: "check-icon" }))))))))))), react_1.default.createElement(InputError_1.InputError, { message: errorData }), react_1.default.createElement("input", { type: "hidden", name: name, value: inputValue, id: id }))); }