@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
JavaScript
"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 })));
}