@arudovwen/react-form-builder
Version:
A complete form builder for react.
114 lines (108 loc) • 5.69 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _clsx = _interopRequireDefault(require("clsx"));
var _react = _interopRequireWildcard(require("react"));
var _axios = _interopRequireDefault(require("axios"));
var _react2 = require("@headlessui/react");
var _lucideReact = require("lucide-react");
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
const CustomSelect = /*#__PURE__*/(0, _react.forwardRef)(({
options = [],
defaultValue,
onGetValue,
readOnly = false,
url = ''
}, ref) => {
const [query, setQuery] = (0, _react.useState)('');
const [debouncedQuery, setDebouncedQuery] = (0, _react.useState)('');
const [selectedValue, setSelectedValue] = (0, _react.useState)(defaultValue || null);
const [fetchedOptions, setFetchedOptions] = (0, _react.useState)([]);
const [loading, setLoading] = (0, _react.useState)(false);
const [error, setError] = (0, _react.useState)(null);
// Debounce user input
(0, _react.useEffect)(() => {
const timeoutId = setTimeout(() => {
setDebouncedQuery(query);
}, 300);
return () => clearTimeout(timeoutId);
}, [query]);
// Merge and deduplicate options
const mergedOptions = (0, _react.useMemo)(() => [...new Map([...options, ...fetchedOptions].map(item => [`${item.value}-${item.text}`, item])).values()], [options, fetchedOptions]);
// Filter options based on debounced query
const filteredOptions = (0, _react.useMemo)(() => {
if (debouncedQuery === '') return mergedOptions;
return mergedOptions.filter(option => option.text?.toLowerCase().includes(debouncedQuery.toLowerCase()));
}, [debouncedQuery, mergedOptions]);
// Fetch from API
const fetchData = (0, _react.useCallback)(async () => {
if (!url) return;
setLoading(true);
setError(null);
try {
const response = await _axios.default.get(url, {
headers: {
Authorization: `Bearer ${window.localStorage.getItem('token')}`
},
params: {
q: debouncedQuery
}
});
const data = Array.isArray(response.data) ? response.data : response.data?.data || [];
setFetchedOptions(data);
} catch (err) {
console.error('Failed to load options:', err);
setError('Something went wrong while fetching data.');
setFetchedOptions([]);
} finally {
setLoading(false);
}
}, [url, debouncedQuery]);
(0, _react.useEffect)(() => {
if (url) fetchData();
}, []);
// Call parent with selected value only
(0, _react.useEffect)(() => {
if (onGetValue) {
onGetValue(selectedValue);
}
}, [selectedValue]);
const getSelectedOption = value => mergedOptions.find(option => option.value === value) || null;
return /*#__PURE__*/_react.default.createElement("div", {
className: "relative w-full",
ref: ref
}, /*#__PURE__*/_react.default.createElement(_react2.Combobox, {
value: selectedValue,
onChange: setSelectedValue,
disabled: readOnly
}, /*#__PURE__*/_react.default.createElement("div", {
className: "relative"
}, /*#__PURE__*/_react.default.createElement(_react2.ComboboxInput, {
disabled: readOnly,
className: (0, _clsx.default)('w-full rounded-lg border border-gray-300 bg-white py-2 pr-10 pl-3 text-sm text-gray-900 disabled:bg-gray-200', 'focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500'),
displayValue: val => getSelectedOption(val)?.text || '',
onChange: event => setQuery(event.target.value),
placeholder: "Select an option..."
}), /*#__PURE__*/_react.default.createElement(_react2.ComboboxButton, {
className: "absolute inset-y-0 right-0 flex items-center px-2 text-gray-400 hover:text-gray-600 disabled:bg-gray-200"
}, /*#__PURE__*/_react.default.createElement(_lucideReact.ChevronDown, null))), /*#__PURE__*/_react.default.createElement(_react2.ComboboxOptions, {
className: (0, _clsx.default)('absolute z-10 mt-2 max-h-60 w-full overflow-auto rounded-md border border-gray-200 bg-white p-1 shadow-lg', 'ring-1 ring-black ring-opacity-5 focus:outline-none')
}, loading && /*#__PURE__*/_react.default.createElement("div", {
className: "px-3 py-2 text-sm text-gray-500"
}, "Loading..."), !loading && error && /*#__PURE__*/_react.default.createElement("div", {
className: "px-3 py-2 text-sm text-red-500"
}, error), !loading && !error && filteredOptions.length === 0 && /*#__PURE__*/_react.default.createElement("div", {
className: "px-3 py-2 text-sm text-gray-500"
}, "No results found."), !loading && !error && filteredOptions.map((option, index) => /*#__PURE__*/_react.default.createElement(_react2.ComboboxOption, {
key: `${option.value}-${index}`,
value: option.value,
className: ({
active,
selected
}) => (0, _clsx.default)('cursor-default select-none rounded-md px-3 py-2 text-sm', active ? 'bg-blue-100 text-blue-900' : 'text-gray-900', selected && 'font-semibold')
}, option.text)))));
});
var _default = exports.default = CustomSelect;