UNPKG

@gpa-gemstone/react-forms

Version:
152 lines (151 loc) 9.06 kB
"use strict"; // ****************************************************************************************************** // SearchableSelect.tsx - Gbtc // // Copyright © 2024, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this // file except in compliance with the License. You may obtain a copy of the License at: // // http://opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 03/17/2024 - C. Lackner // Generated original version of source code. // // ****************************************************************************************************** var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = SearchableSelect; var React = require("react"); var StylableSelect_1 = require("./StylableSelect"); var gpa_symbols_1 = require("@gpa-gemstone/gpa-symbols"); var getInitialSearchText = function (useBlankString, recordValue) { return useBlankString ? '' : recordValue; }; function SearchableSelect(props) { var _a = React.useState(function () { var _a, _b, _c; return getInitialSearchText((_a = props.ResetSearchOnSelect) !== null && _a !== void 0 ? _a : false, (_c = (_b = props.Record[props.Field]) === null || _b === void 0 ? void 0 : _b.toString()) !== null && _c !== void 0 ? _c : ''); }), search = _a[0], setSearch = _a[1]; var _b = React.useState([]), searchOptions = _b[0], setSearchOptions = _b[1]; var _c = React.useState(false), loading = _c[0], setLoading = _c[1]; var _d = React.useState(null), currentLabel = _d[0], setCurrentLabel = _d[1]; var inputRef = React.useRef(null); var setter = React.useCallback(function (record, selectedOption) { var selectedOptionCasted = selectedOption; //we can safely cast here because we control the options going in.. handleSetSearch(selectedOptionCasted); props.Setter(record, { Label: selectedOptionCasted.Label, Value: selectedOptionCasted.Value }); }, [props.Setter, props.Field]); var handleSetSearch = React.useCallback(function (selectedOption) { var _a, _b, _c, _d; if ((_a = props.ResetSearchOnSelect) !== null && _a !== void 0 ? _a : false) { setSearch(''); return; } if (currentLabel != null) { setSearch(currentLabel); return; } var newSearch = (_d = (_b = selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.Label) !== null && _b !== void 0 ? _b : (_c = props.Record[props.Field]) === null || _c === void 0 ? void 0 : _c.toString()) !== null && _d !== void 0 ? _d : ''; setSearch(newSearch); }, [props.ResetSearchOnSelect, currentLabel, props.Record[props.Field]]); //Effect to set the label for the current value using GetLabel. React.useEffect(function () { if (props.GetLabel == null) { setCurrentLabel(null); return; } var handle = props.GetLabel(); handle.then(function (lab) { return setCurrentLabel(lab); }, function () { return setCurrentLabel(null); }); return function () { if ((handle === null || handle === void 0 ? void 0 : handle.abort) != null) handle.abort(); }; }, [props.Record[props.Field], props.GetLabel]); //Effect to set search when props.Record[props.Field] changes externally React.useEffect(function () { handleSetSearch(); }, [props.Record[props.Field], handleSetSearch]); // Call props.Search every 500ms to avoid hammering the server while typing React.useEffect(function () { setLoading(true); var searchHandle; var timeoutHandle = setTimeout(function () { searchHandle = props.Search(search); searchHandle.then(function (d) { setSearchOptions(d.map(function (o) { return ({ Value: o.Value, Element: o.Label, Label: o.Label }); })); setLoading(false); }, function () { setLoading(false); }); }, 500); return function () { if ((searchHandle === null || searchHandle === void 0 ? void 0 : searchHandle.abort) != null) searchHandle.abort(); if (timeoutHandle != null) clearTimeout(timeoutHandle); }; }, [search]); var options = React.useMemo(function () { var _a, _b, _c, _d, _e, _f; var ops = []; ops.push({ Value: props.Record[props.Field], Label: "", //Label doesnt matter in this case because you cant select this option Element: React.createElement("div", { className: 'input-group' }, React.createElement("input", { ref: inputRef, type: "text", className: "form-control ".concat(((_b = (_a = props.Valid) === null || _a === void 0 ? void 0 : _a.call(props, props.Field)) !== null && _b !== void 0 ? _b : true) ? '' : 'border-danger'), value: search, onChange: function (d) { return setSearch(d.target.value); }, onBlur: function () { return handleSetSearch(); }, disabled: (_c = props.Disabled) !== null && _c !== void 0 ? _c : false }), loading ? React.createElement("div", { className: "input-group-append" }, React.createElement("span", { className: "input-group-text" }, React.createElement(gpa_symbols_1.ReactIcons.SpiningIcon, null))) : null) }); if ((_d = props.AllowCustom) !== null && _d !== void 0 ? _d : false) ops.push({ Value: search, Element: React.createElement(React.Fragment, null, search, " ", React.createElement("span", { className: "badge badge-info", style: { fontSize: '0.75em' } }, "Entered Value")), Label: search }); //Ensure selectedOption is always at top of the list var selected = searchOptions.find(function (f) { return f.Value === props.Record[props.Field]; }); if (selected != null) ops.push(__assign(__assign({}, selected), { RowClass: 'table-primary' })); else if (props.Record[props.Field] != null && props.Record[props.Field] !== '') { var fallbackLabel = (_f = currentLabel !== null && currentLabel !== void 0 ? currentLabel : (_e = props.Record[props.Field]) === null || _e === void 0 ? void 0 : _e.toString()) !== null && _f !== void 0 ? _f : ''; ops.push({ Value: props.Record[props.Field], Label: fallbackLabel, Element: React.createElement(React.Fragment, null, fallbackLabel), RowClass: 'table-primary' }); } ops.push.apply(ops, searchOptions.filter(function (f) { return f.Value !== search && f.Value !== props.Record[props.Field]; })); // Only show no-results when there's no search results and we arent loading if (searchOptions.length === 0 && !loading) ops.push({ Value: '', Label: '', Element: React.createElement("span", { className: "text-muted" }, "No results found"), RowClass: 'disabled', Disabled: true }); return ops; }, [search, props.Record[props.Field], props.Field, searchOptions, props.Disabled, loading, props.Valid, handleSetSearch, currentLabel, props.AllowCustom]); return React.createElement(StylableSelect_1.default, { Record: props.Record, Field: props.Field, Setter: setter, Label: props.Label, Disabled: props.Disabled, Help: props.Help, Style: props.Style, Options: options, BtnStyle: props.BtnStyle, Valid: props.Valid, Feedback: props.Feedback, OnDropdownOpen: function () { var _a; setSearch(''); (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); }, OnDropdownClose: function () { var _a; return (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.blur(); } }); }