@gpa-gemstone/react-interactive
Version:
Interactive UI Components for GPA products
373 lines (372 loc) • 25.1 kB
JavaScript
"use strict";
// ******************************************************************************************************
// SearchBar.tsx - Gbtc
//
// Copyright © 2020, 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:
// ----------------------------------------------------------------------------------------------------
// 01/06/2020 - Christoph 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);
};
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 __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = SearchBar;
var React = __importStar(require("react"));
var Modal_1 = __importDefault(require("./Modal"));
var LoadingIcon_1 = __importDefault(require("./LoadingIcon"));
var react_forms_1 = require("@gpa-gemstone/react-forms");
var gpa_symbols_1 = require("@gpa-gemstone/gpa-symbols");
function SearchBar(props) {
var _a = React.useState(false), hover = _a[0], setHover = _a[1];
var _b = React.useState(false), show = _b[0], setShow = _b[1];
var _c = React.useState(false), isNew = _c[0], setIsNew = _c[1];
var _d = React.useState([]), filters = _d[0], setFilters = _d[1];
var _e = React.useState({ FieldName: props.CollumnList[0].key, SearchText: '', Operator: props.CollumnList[0].type === 'string' ? 'LIKE' : '=', Type: props.CollumnList[0].type, IsPivotColumn: props.CollumnList[0].isPivotField }), filter = _e[0], setFilter = _e[1];
var _f = React.useState(""), search = _f[0], setSearch = _f[1];
var _g = React.useState(null), searchFilter = _g[0], setSearchFilter = _g[1];
var isFirstRender = React.useRef(true);
// Handling filter storage between sessions if a storageID exists
React.useEffect(function () {
var _a, _b;
if (props.StorageID !== undefined) {
// Get Button Filters
var storedFilters = (_a = JSON.parse(localStorage.getItem("".concat(props.StorageID, ".Filters")))) !== null && _a !== void 0 ? _a : [];
setFilters(storedFilters);
// Get Bar Search
var storedSearch = (_b = localStorage.getItem("".concat(props.StorageID, ".Search"))) !== null && _b !== void 0 ? _b : "";
setSearch(storedSearch);
// If storedsearch is empty, then react won't trigger use effects for it, thus we have to search here
if (storedSearch === "")
props.SetFilter(storedFilters);
}
}, []);
React.useEffect(function () {
if (props.StorageID != null)
localStorage.setItem("".concat(props.StorageID, ".Filters"), JSON.stringify(filters));
}, [filters]);
React.useEffect(function () {
if (props.StorageID != null)
localStorage.setItem("".concat(props.StorageID, ".Search"), search);
}, [search]);
// Update SearchFilter if there are any Character and only do it every 500ms to avoid hammering the server while typing
React.useEffect(function () {
var handle = null;
if (search.length > 0 && props.defaultCollumn !== undefined)
handle = setTimeout(function () {
if (props.defaultCollumn !== undefined)
setSearchFilter({ FieldName: props.defaultCollumn.key, Operator: 'LIKE', Type: props.defaultCollumn.type, SearchText: ('*' + search + '*'), IsPivotColumn: props.defaultCollumn.isPivotField });
}, 500);
else
handle = setTimeout(function () {
setSearchFilter(null);
}, 500);
return function () { if (handle !== null)
clearTimeout(handle); };
}, [search]);
React.useEffect(function () {
// We need to skip the first render call or we will get a race condition with the props.setFilter in the blank useEffect
if (!isFirstRender.current || props.StorageID == null) {
if (searchFilter != null)
props.SetFilter(__spreadArray(__spreadArray([], filters, true), [searchFilter], false));
else
props.SetFilter(filters);
}
isFirstRender.current = false;
}, [searchFilter]);
function deleteFilter(f) {
var index = filters.findIndex(function (fs) { return fs === f; });
var filts = __spreadArray([], filters, true);
filts.splice(index, 1);
setFilters(filts);
setHover(false);
if (props.defaultCollumn !== undefined && searchFilter !== null)
props.SetFilter(__spreadArray(__spreadArray([], filts, true), [searchFilter], false));
else
props.SetFilter(filts);
}
function addFilter() {
var oldFilters = __spreadArray([], filters, true);
var adjustedFilter = __assign({}, filter);
if (adjustedFilter.Type === 'string' && (adjustedFilter.Operator === 'LIKE' || adjustedFilter.Operator === 'NOT LIKE'))
adjustedFilter.SearchText = '*' + adjustedFilter.SearchText + '*';
oldFilters.push(adjustedFilter);
setFilters(oldFilters);
setFilter({ FieldName: props.CollumnList[0].key, SearchText: '', Operator: props.CollumnList[0].type === 'string' ? 'LIKE' : '=', Type: props.CollumnList[0].type, IsPivotColumn: props.CollumnList[0].isPivotField });
if (props.defaultCollumn !== undefined && searchFilter !== null)
props.SetFilter(__spreadArray(__spreadArray([], oldFilters, true), [searchFilter], false));
else
props.SetFilter(oldFilters);
}
function editFilter(index) {
setIsNew(false);
var oldFilters = __spreadArray([], filters, true);
var filt = __assign({}, oldFilters[index]);
oldFilters.splice(index, 1);
if (filt.Type === 'string' && (filt.Operator === 'LIKE' || filt.Operator === 'NOT LIKE'))
filt.SearchText = filt.SearchText.substr(1, filt.SearchText.length - 2);
setShow(true);
setFilters(oldFilters);
setFilter(filt);
if (props.defaultCollumn !== undefined && searchFilter !== null)
props.SetFilter(__spreadArray(__spreadArray([], oldFilters, true), [searchFilter], false));
else
props.SetFilter(oldFilters);
}
function createFilter() {
setShow(!show);
setIsNew(true);
setFilter({ FieldName: props.CollumnList[0].key, SearchText: '', Operator: props.CollumnList[0].type === 'string' ? 'LIKE' : '=', Type: props.CollumnList[0].type, IsPivotColumn: props.CollumnList[0].isPivotField });
}
var content = (React.createElement(React.Fragment, null,
React.createElement("form", null,
React.createElement("div", { className: "row" },
props.defaultCollumn !== undefined ?
React.createElement("div", { className: "col" },
React.createElement("div", { className: "input-group" },
React.createElement("input", { className: "form-control mr-sm-2", type: "search", placeholder: "Search " + props.defaultCollumn.label, onChange: function (event) { return setSearch(event.target.value); }, value: search }),
props.ShowLoading !== undefined && props.ShowLoading ? React.createElement("div", { className: "input-group-append" },
" ",
React.createElement(LoadingIcon_1.default, { Show: true }),
" ") : null),
React.createElement("p", { style: { marginTop: 2, marginBottom: 2 } }, props.ResultNote)) : null,
React.createElement("div", { style: { position: 'relative', display: 'inline-block' }, className: 'col' },
React.createElement("button", { className: "btn btn-" + (filters.length > 0 ? "warning" : "primary"), onClick: function (evt) { evt.preventDefault(); createFilter(); }, onMouseEnter: function () { return setHover(true); }, onMouseLeave: function () { return setHover(false); } },
"Add Filter",
filters.length > 0 ? ("(" + filters.length + ")") : ""),
React.createElement("div", { className: "popover", style: {
display: hover ? 'block' : 'none', maxWidth: 'unset',
right: (props.Direction === 'right' ? 0 : 'unset'), left: (props.Direction === 'left' ? 0 : 'unset'),
top: 'unset'
}, onMouseEnter: function () { return setHover(true); }, onMouseLeave: function () { return setHover(false); } },
React.createElement("table", { className: 'table table-hover' },
React.createElement("thead", null,
React.createElement("tr", null,
React.createElement("th", null, "Column"),
React.createElement("th", null, "Operator"),
React.createElement("th", null, "Search Text"),
React.createElement("th", null, "Edit"),
React.createElement("th", null, "Remove"))),
React.createElement("tbody", null, filters.map(function (f, i) { return React.createElement(FilterRow, { Filter: f, Edit: function () { return editFilter(i); }, Delete: function () { return deleteFilter(f); }, key: i, Collumns: props.CollumnList }); })))))))));
return (React.createElement("div", { className: 'w-100' },
React.createElement("nav", { className: "navbar navbar-expand" },
React.createElement("div", { className: 'w-100' },
React.createElement("ul", { className: "navbar-nav mr-auto d-flex align-items-center w-100" },
props.Direction === 'right' ? props.children : null,
props.Label !== undefined ?
React.createElement("li", { className: "nav-item", style: { minWidth: (props.Width === undefined ? '150px' : undefined), width: props.Width, paddingRight: 10 } },
React.createElement("fieldset", { className: "border", style: { padding: '10px', height: '100%' } },
React.createElement("legend", { className: "w-auto", style: { fontSize: 'large' } },
props.Label,
":"),
content)) :
React.createElement("li", { className: "nav-item", style: { minWidth: (props.Width === undefined ? '150px' : undefined), width: props.Width, paddingRight: 10 } }, content),
props.Direction === 'left' ? props.children : null))),
React.createElement(Modal_1.default, { Title: 'Add Filter', Show: show, CallBack: function (conf) { if (conf)
addFilter(); setShow(false); }, ConfirmText: isNew ? 'Add' : 'Save', CancelText: isNew ? 'Close' : 'Delete' },
React.createElement(react_forms_1.Select, { Record: filter, Field: 'FieldName', Options: props.CollumnList.map(function (fl) { return ({ Value: fl.key, Label: fl.label }); }), Setter: function (record) {
var operator = "IN";
var column = props.CollumnList.find(function (fl) { return fl.key === record.FieldName; });
if (column !== undefined && column.type === 'string')
operator = "LIKE";
if (column !== undefined && (column.type === 'number' || column.type === 'integer' || column.type === 'boolean'))
operator = '=';
if (column !== undefined && column.type === 'datetime')
operator = '>';
setFilter(function (prevFilter) { return (__assign(__assign({}, prevFilter), { FieldName: record.FieldName, SearchText: '', Operator: operator, Type: (column !== undefined ? column.type : 'string'), IsPivotColumn: (column !== undefined ? column.isPivotField : true) })); });
}, Label: 'Column' }),
React.createElement(FilterCreator, { Filter: filter, Field: props.CollumnList.find(function (fl) { return fl.key === filter.FieldName; }), Setter: function (record) { return setFilter(record); }, Enum: (props.GetEnum === undefined ? undefined : props.GetEnum) }))));
}
function FilterCreator(props) {
var _a = React.useState([]), options = _a[0], setOptions = _a[1];
React.useEffect(function () {
if (props.Field === undefined)
return;
if (props.Field.enum !== undefined)
setOptions(props.Field.enum);
if (props.Enum !== undefined)
return props.Enum(setOptions, props.Field);
if (props.Field.enum === undefined)
setOptions([]);
}, [props.Field, props.Enum]);
if (props.Field === undefined)
return null;
if (props.Field.type === "string") {
return (React.createElement(React.Fragment, null,
React.createElement("label", null, "Column type is string. Wildcard (*) can be used with 'LIKE' and 'NOT LIKE'"),
React.createElement("div", { className: 'row' },
React.createElement("div", { className: 'col-4' },
React.createElement("select", { className: 'form-control', value: props.Filter.Operator, onChange: function (evt) {
var value = evt.target.value;
props.Setter(function (prevState) { return (__assign(__assign({}, prevState), { Operator: value })); });
} },
React.createElement("option", { value: 'LIKE' }, "LIKE"),
React.createElement("option", { value: '=' }, "="),
React.createElement("option", { value: 'NOT LIKE' }, "NOT LIKE"))),
React.createElement("div", { className: 'col' },
React.createElement("input", { className: 'form-control', value: props.Filter.SearchText.replace('$_', '_'), onChange: function (evt) {
var value = evt.target.value;
props.Setter(function (prevState) { return (__assign(__assign({}, prevState), { SearchText: value.replace('_', '$_') })); });
} })))));
}
else if (props.Field.type === "integer" || props.Field.type === "number") {
return (React.createElement(React.Fragment, null,
React.createElement("label", null,
"Column type is ",
props.Field.type,
"."),
React.createElement("div", { className: 'row' },
React.createElement("div", { className: 'col-4' },
React.createElement("select", { className: 'form-control', value: props.Filter.Operator, onChange: function (evt) {
var value = evt.target.value;
props.Setter(function (prevState) { return (__assign(__assign({}, prevState), { Operator: value })); });
} },
React.createElement("option", { value: '=' }, "="),
React.createElement("option", { value: '<>' }, "<>"),
React.createElement("option", { value: '>' }, ">"),
React.createElement("option", { value: '>=' }, ">="),
React.createElement("option", { value: '<' }, "<"),
React.createElement("option", { value: '<=' }, "<="))),
React.createElement("div", { className: 'col' },
React.createElement("input", { type: 'number', className: 'form-control', value: props.Filter.SearchText, onChange: function (evt) {
var value = evt.target.value;
props.Setter(function (prevState) { return (__assign(__assign({}, prevState), { SearchText: value })); });
} })))));
}
else if (props.Field.type === "datetime") {
return (React.createElement(React.Fragment, null,
React.createElement("label", null,
"Column type is ",
props.Field.type,
"."),
React.createElement("div", { className: 'row' },
React.createElement("div", { className: 'col-4' },
React.createElement("select", { className: 'form-control', value: props.Filter.Operator, onChange: function (evt) {
var value = evt.target.value;
props.Setter(function (prevState) { return (__assign(__assign({}, prevState), { Operator: value })); });
} },
React.createElement("option", { value: '>' }, ">"),
React.createElement("option", { value: '>=' }, ">="),
React.createElement("option", { value: '<' }, "<"),
React.createElement("option", { value: '<=' }, "<="))),
React.createElement("div", { className: 'col' },
React.createElement(react_forms_1.DatePicker, { Record: props.Filter, Field: "SearchText", Setter: function (r) {
var value = r.SearchText;
props.Setter(function (prevState) { return (__assign(__assign({}, prevState), { SearchText: value })); });
}, Label: '', Type: 'datetime-local', Valid: function () { return true; }, Format: 'MM/DD/YYYY HH:mm:ss.SSS' })))));
}
else if (props.Field.type === "boolean") {
return React.createElement("div", { className: "form-check" },
React.createElement("input", { type: "checkbox", className: "form-check-input", style: { zIndex: 1 }, onChange: function (evt) {
props.Setter(function (prevFilter) { return (__assign(__assign({}, prevFilter), { Operator: '=', SearchText: evt.target.checked ? "1" : "0" })); });
}, value: props.Filter.SearchText === "1" ? 'on' : 'off', checked: props.Filter.SearchText === "1" ? true : false }),
React.createElement("label", { className: "form-check-label" }, "Column type is boolean. Yes/On is checked."));
}
else {
var stripParenthesisAndSplit_1 = function (str) {
return (str.match(/^\(.*\)$/) != null ? str.slice(1, -1) : str).split(',');
};
return (React.createElement(React.Fragment, null,
React.createElement("label", null, "Column type is enumerable. Select from below."),
React.createElement("ul", { style: { listStyle: 'none' } },
React.createElement("li", null,
React.createElement("div", { className: "form-check" },
React.createElement("input", { type: "checkbox", className: "form-check-input", style: { zIndex: 1 }, onChange: function (evt) {
if (evt.target.checked)
props.Setter(function (prevSetter) { return (__assign(__assign({}, prevSetter), { SearchText: "(".concat(options.map(function (x) { return x.Value; }).join(','), ")") })); });
else
props.Setter(function (prevSetter) { return (__assign(__assign({}, prevSetter), { SearchText: '' })); });
}, defaultValue: 'off' }),
React.createElement("label", { className: "form-check-label" }, "Select All"))),
options.map(function (vli, index) { return React.createElement("li", { key: index },
React.createElement("div", { className: "form-check" },
React.createElement("input", { type: "checkbox", className: "form-check-input", style: { zIndex: 1 }, onChange: function (evt) {
if (evt.target.checked) {
var list = stripParenthesisAndSplit_1(props.Filter.SearchText);
list = list.filter(function (x) { return x !== ""; });
list.push(vli.Value);
var text_1 = "(".concat(list.join(','), ")");
props.Setter(function (prevSetter) { return (__assign(__assign({}, prevSetter), { SearchText: text_1 })); });
}
else {
var list = stripParenthesisAndSplit_1(props.Filter.SearchText);
list = list.filter(function (x) { return x !== ""; });
list = list.filter(function (x) { return x !== vli.Value; });
var text_2 = "(".concat(list.join(','), ")");
props.Setter(function (prevSetter) { return (__assign(__assign({}, prevSetter), { SearchText: text_2 })); });
}
}, value: props.Filter.SearchText.indexOf(vli.Value) >= 0 ? 'on' : 'off', checked: stripParenthesisAndSplit_1(props.Filter.SearchText).indexOf(vli.Value) >= 0 }),
React.createElement("label", { className: "form-check-label" }, vli.Label))); }))));
}
}
function FilterRow(props) {
var column = props.Collumns.find(function (c) { return c.key === props.Filter.FieldName; });
return React.createElement("tr", null,
React.createElement("td", null, column === undefined ? props.Filter.FieldName : column.label),
React.createElement("td", null, props.Filter.Operator),
React.createElement("td", null, props.Filter.SearchText),
React.createElement("td", null,
React.createElement("button", { type: 'button', className: "btn btn-sm", onClick: function (e) { return props.Edit(); } },
React.createElement("span", null,
React.createElement(gpa_symbols_1.ReactIcons.Pencil, null)))),
React.createElement("td", null,
React.createElement("button", { type: 'button', className: "btn btn-sm", onClick: function (e) { return props.Delete(); } },
React.createElement("span", null,
React.createElement(gpa_symbols_1.ReactIcons.TrashCan, { Style: { color: 'var(--danger)' } })))));
}