@agilant/toga-blox 1.0.51 → 1.0.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ // SearchInput.tsx
2
3
  import { useEffect, useRef } from "react";
3
4
  import SearchTextInput from "./SearchTextInput";
4
5
  import SearchNumberInput from "./SearchNumberInput";
@@ -17,7 +18,7 @@ const SearchInput = ({ bgColor = "bg-sky-500", textHighlight = "text-sky-500", i
17
18
  return (_jsx("div", { ref: containerRef, className: "", children: (() => {
18
19
  switch (inputType) {
19
20
  case "text":
20
- return (_jsx(SearchTextInput, { dropdownIconProp: dropdownIconProp, dropdownOptions: dropdownOptions, selectedDropdownOption: selectedDropdownOption, onDropdownOptionSelect: onDropdownOptionSelect, searchItems: searchItems, setSearchItems: setSearchItems, handleFilter: handleFilter }));
21
+ return (_jsx(SearchTextInput, { dropdownIconProp: dropdownIconProp, dropdownOptions: dropdownOptions, selectedDropdownOption: selectedDropdownOption, onDropdownOptionSelect: onDropdownOptionSelect, searchItems: searchItems, setSearchItems: setSearchItems, handleFilter: handleFilter, column: undefined }));
21
22
  case "number":
22
23
  return (_jsx(SearchNumberInput, { dropdownIconProp: dropdownIconProp, dropdownOptions: dropdownOptions, selectedDropdownOption: selectedDropdownOption, onDropdownOptionSelect: onDropdownOptionSelect, toggleStatus: toggleStatus, setToggleStatus: setToggleStatus, minValue: minValue, maxValue: maxValue, setMinValue: setMinValue, setMaxValue: setMaxValue, handleFilter: handleFilter }));
23
24
  case "multiSelect":
@@ -1,5 +1,10 @@
1
1
  import React from "react";
2
+ import { ColumnInstance } from "react-table";
2
3
  import { searchDropdownIconProps } from "./SearchInput.types";
4
+ export type SearchCriterion<T extends object> = {
5
+ searchColumn: ColumnInstance<T>;
6
+ submittedSearchText: string;
7
+ };
3
8
  type SearchTextInputProps<T extends object> = {
4
9
  pillColor?: string;
5
10
  textHighlight?: string;
@@ -9,7 +14,11 @@ type SearchTextInputProps<T extends object> = {
9
14
  dropdownOptions?: string[];
10
15
  searchItems?: string[];
11
16
  setSearchItems?: React.Dispatch<React.SetStateAction<string[]>>;
17
+ searchCriteria?: SearchCriterion<T>[];
18
+ setSearchCriteria?: React.Dispatch<React.SetStateAction<SearchCriterion<T>[]>>;
19
+ column?: ColumnInstance<T>;
12
20
  handleFilter?: () => void;
21
+ localStorageKey?: string;
13
22
  };
14
- declare const SearchTextInput: <T extends object>({ pillColor, textHighlight, dropdownIconProp, dropdownOptions, selectedDropdownOption, onDropdownOptionSelect, searchItems, setSearchItems, handleFilter, }: SearchTextInputProps<T>) => import("react/jsx-runtime").JSX.Element;
23
+ declare const SearchTextInput: <T extends object>({ pillColor, textHighlight, dropdownIconProp, dropdownOptions, selectedDropdownOption, onDropdownOptionSelect, searchItems, setSearchItems, searchCriteria, setSearchCriteria, column, handleFilter, localStorageKey, }: SearchTextInputProps<T>) => import("react/jsx-runtime").JSX.Element;
15
24
  export default SearchTextInput;
@@ -1,46 +1,139 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // SearchTextInput.tsx
2
3
  import { useEffect, useRef, useState } from "react";
3
4
  import { AnimatePresence, motion } from "framer-motion";
4
5
  import { Input } from "../Input";
5
- import { getFontAwesomeIcon } from "../../utils/getFontAwesomeIcon";
6
6
  import Dropdown from "../Dropdown/Dropdown";
7
7
  import Badge from "../Badge/Badge";
8
8
  import Text from "../Text";
9
+ import { getFontAwesomeIcon } from "../../utils/getFontAwesomeIcon";
9
10
  const SearchTextInput = ({ pillColor = "bg-sky-500", textHighlight = "text-sky-500", dropdownIconProp = {
10
11
  iconClasses: textHighlight,
11
12
  name: "chevronDown",
12
13
  weight: "solid",
13
- }, dropdownOptions = [], selectedDropdownOption = "", onDropdownOptionSelect, searchItems = [], setSearchItems, handleFilter, }) => {
14
+ }, dropdownOptions = [], selectedDropdownOption = "", onDropdownOptionSelect, searchItems, setSearchItems, searchCriteria, setSearchCriteria, column, handleFilter,
15
+ // Use a default key for simple mode if no column is provided.
16
+ localStorageKey = column ? "zu-table-store" : "searchItems", }) => {
14
17
  const containerRef = useRef(null);
15
18
  const [localSearchText, setLocalSearchText] = useState("");
16
19
  const inputRef = useRef(null);
17
- // Focus the input when editing starts (if needed)
20
+ // On mount, focus input and load from local storage
18
21
  useEffect(() => {
19
22
  inputRef.current?.focus();
20
- }, []);
23
+ const stored = localStorage.getItem(localStorageKey);
24
+ if (stored) {
25
+ try {
26
+ const parsed = JSON.parse(stored);
27
+ // For advanced mode, expect an object with state.searchCriteria
28
+ if (column &&
29
+ parsed?.state?.searchCriteria &&
30
+ setSearchCriteria) {
31
+ setSearchCriteria(parsed.state.searchCriteria);
32
+ }
33
+ // Otherwise, for simple mode, expect a plain array
34
+ else if (!column && setSearchItems) {
35
+ setSearchItems(parsed);
36
+ }
37
+ }
38
+ catch (e) {
39
+ console.error("Error parsing local storage:", e);
40
+ }
41
+ }
42
+ }, [localStorageKey, column, setSearchCriteria, setSearchItems]);
21
43
  const handleInputChange = (event) => {
22
44
  setLocalSearchText(event.target.value);
23
45
  };
24
- const handleIconClick = (item) => {
25
- // Remove criterion
26
- const filteredItems = searchItems.filter((ele) => ele !== item);
27
- setSearchItems(filteredItems);
46
+ const updateLocalStorage = (data) => {
47
+ localStorage.setItem(localStorageKey, JSON.stringify(data));
48
+ };
49
+ // Advanced mode: use searchCriteria with column info
50
+ const handleSubmitAdvanced = () => {
51
+ const trimmed = localSearchText.trim();
52
+ if (!trimmed || !column || !setSearchCriteria || !searchCriteria)
53
+ return;
54
+ let newCriteria;
55
+ const exists = searchCriteria.some((c) => c.searchColumn.id === column.id);
56
+ if (exists) {
57
+ newCriteria = searchCriteria.map((c) => c.searchColumn.id === column.id
58
+ ? { searchColumn: column, submittedSearchText: trimmed }
59
+ : c);
60
+ }
61
+ else {
62
+ newCriteria = [
63
+ ...searchCriteria,
64
+ { searchColumn: column, submittedSearchText: trimmed },
65
+ ];
66
+ }
67
+ setSearchCriteria(newCriteria);
68
+ updateLocalStorage({
69
+ state: { searchCriteria: newCriteria },
70
+ version: 0,
71
+ });
72
+ setLocalSearchText("");
73
+ if (handleFilter)
74
+ handleFilter();
75
+ };
76
+ // Simple mode: use searchItems as plain string array
77
+ const handleSubmitSimple = () => {
78
+ const trimmed = localSearchText.trim();
79
+ if (!trimmed || !setSearchItems || !searchItems)
80
+ return;
81
+ const newItems = [...searchItems, trimmed];
82
+ setSearchItems(newItems);
83
+ updateLocalStorage(newItems);
84
+ setLocalSearchText("");
85
+ if (handleFilter)
86
+ handleFilter();
87
+ };
88
+ const handleSubmit = () => {
89
+ if (column) {
90
+ handleSubmitAdvanced();
91
+ }
92
+ else {
93
+ handleSubmitSimple();
94
+ }
28
95
  };
29
96
  const handleKeyDown = (e) => {
30
- if (e.key === "Enter" && localSearchText.length > 0) {
97
+ if (e.key === "Enter") {
31
98
  e.preventDefault();
32
- setSearchItems([...searchItems, localSearchText]);
33
- setLocalSearchText("");
34
- handleFilter();
99
+ handleSubmit();
100
+ }
101
+ };
102
+ // Clear functions for each mode
103
+ const handleClearAdvanced = () => {
104
+ if (!column || !setSearchCriteria || !searchCriteria)
105
+ return;
106
+ const newCriteria = searchCriteria.filter((c) => c.searchColumn.id !== column.id);
107
+ setSearchCriteria(newCriteria);
108
+ updateLocalStorage({
109
+ state: { searchCriteria: newCriteria },
110
+ version: 0,
111
+ });
112
+ setLocalSearchText("");
113
+ };
114
+ const handleClearSimple = () => {
115
+ if (!setSearchItems || !searchItems)
116
+ return;
117
+ const newItems = searchItems.filter((item) => item !== localSearchText);
118
+ setSearchItems(newItems);
119
+ updateLocalStorage(newItems);
120
+ setLocalSearchText("");
121
+ };
122
+ const handleClear = () => {
123
+ if (column) {
124
+ handleClearAdvanced();
125
+ }
126
+ else {
127
+ handleClearSimple();
35
128
  }
36
129
  };
37
130
  const pillClassnames = `${pillColor} p-1 max-w-fit min-w-20 rounded-full flex justify-between items-center text-white text-xs px-4 border-none mr-4 mb-1`;
38
- return (_jsx("div", { ref: containerRef, className: "w-[425px]", children: _jsxs("div", { className: "flex flex-col border-2 border-navy-200 rounded-md", children: [_jsxs("div", { className: `flex ${searchItems.length ? "border-b-2" : ""} h-full`, children: [_jsx(Dropdown, { options: dropdownOptions, selectedOption: selectedDropdownOption, onOptionSelect: onDropdownOptionSelect, optionClasses: "px-4 py-1 h-full flex items-center", menuClasses: "bg-white min-w-32", icon: dropdownIconProp, dropdownClasses: "border-0 border-r-2 w-auto" }), _jsx(Input, { focusRingColor: "focus:ring-transparent", hasAutoFocus: true, value: localSearchText, iconColor: "text-navy-400", onKeyDown: handleKeyDown, required: false, id: "", name: "", type: "text", onChange: handleInputChange, additionalClasses: " min-w-[250px] min-h-full text-gray flex", placeholder: "Search", hasIcons: true, firstIcon: localSearchText === "" ? (_jsx(AnimatePresence, { children: _jsx(motion.div, { initial: "initial", animate: "animate", exit: "exit", className: "text-navy-400", children: getFontAwesomeIcon("search", "regular") }) })) : undefined, iconPosition: "both", secondIcon: _jsx("div", { className: "border-transparent text-white min-w-9", children: _jsx(AnimatePresence, { children: localSearchText !== "" && (_jsxs(motion.div, { className: "flex justify-between items-center min-w-4 text-navy-400 hover:cursor-pointer hover:text-primary", initial: "initial", animate: "animate", exit: "exit", children: [_jsx("div", { className: "bg-navy-50 pr-2 pl-1 text-gray-500", onClick: () => {
39
- setLocalSearchText("");
40
- }, "data-testid": "clear-icon", children: getFontAwesomeIcon("xmark", "regular") }), _jsx("div", { className: `${textHighlight} text-sm hover:text-primary`, children: getFontAwesomeIcon("search", "solid") })] })) }) }), onIconClick: () => {
41
- setLocalSearchText("");
42
- } })] }), searchItems?.length ? (_jsx("div", { className: " flex flex-wrap bg-white py-2 px-2 rounded-md ", children: searchItems?.map((item, index) => {
43
- return (_jsx(Badge, { backgroundColor: pillColor, borderRadius: "rounded-full", hasRightIcon: true, icon: _jsx("div", { className: "text-white text-xxs", "data-testid": "item-clear-icon", children: getFontAwesomeIcon("xmark", "solid") }), iconSize: "text-sm", mobileIconLabel: item, onClick: () => handleIconClick(item), text: _jsx(Text, { color: "text-white", fontFamily: "font-serif", size: "text-sm", tag: "span", text: item }), badgeContainerClasses: pillClassnames, type: "span" }));
44
- }) })) : null] }) }));
131
+ return (_jsx("div", { ref: containerRef, className: "w-[425px]", children: _jsxs("div", { className: "flex flex-col border-2 border-navy-200 rounded-md", children: [_jsxs("div", { className: `flex ${(column ? searchCriteria?.length : searchItems?.length)
132
+ ? "border-b-2"
133
+ : ""} h-full`, children: [_jsx(Dropdown, { options: dropdownOptions, selectedOption: selectedDropdownOption, onOptionSelect: onDropdownOptionSelect, optionClasses: "px-4 py-1 h-full flex items-center", menuClasses: "bg-white min-w-32", icon: dropdownIconProp, dropdownClasses: "border-0 border-r-2 w-auto" }), _jsx(Input, { ref: inputRef, focusRingColor: "focus:ring-transparent", hasAutoFocus: true, value: localSearchText, iconColor: "text-navy-400", onKeyDown: handleKeyDown, required: false, id: "", name: "", type: "text", onChange: handleInputChange, additionalClasses: "min-w-[250px] min-h-full text-gray flex", placeholder: "Search", hasIcons: true, firstIcon: localSearchText === "" ? (_jsx(AnimatePresence, { children: _jsx(motion.div, { initial: "initial", animate: "animate", exit: "exit", className: "text-navy-400", children: getFontAwesomeIcon("search", "regular") }) })) : undefined, iconPosition: "both", secondIcon: _jsx("div", { className: "border-transparent text-white min-w-9", children: _jsx(AnimatePresence, { children: localSearchText !== "" && (_jsxs(motion.div, { className: "flex justify-between items-center min-w-4 text-navy-400 hover:cursor-pointer hover:text-primary", initial: "initial", animate: "animate", exit: "exit", onClick: handleSubmit, children: [_jsx("div", { className: "bg-navy-50 pr-2 pl-1 text-gray-500", onClick: handleClear, "data-testid": "clear-icon", children: getFontAwesomeIcon("xmark", "regular") }), _jsx("div", { className: `${textHighlight} text-sm hover:text-primary`, children: getFontAwesomeIcon("search", "solid") })] })) }) }), onIconClick: handleClear })] }), (column ? searchCriteria?.length : searchItems?.length) ? (_jsx("div", { className: "flex flex-wrap bg-white py-2 px-2 rounded-md", children: column
134
+ ? searchCriteria
135
+ ?.filter((c) => c.searchColumn.id === column.id)
136
+ .map((criterion, index) => (_jsx(Badge, { backgroundColor: pillColor, borderRadius: "rounded-full", hasRightIcon: true, icon: _jsx("div", { className: "text-white text-xxs", "data-testid": "item-clear-icon", children: getFontAwesomeIcon("xmark", "solid") }), iconSize: "text-sm", mobileIconLabel: criterion.submittedSearchText, onClick: handleClear, text: _jsx(Text, { color: "text-white", fontFamily: "font-serif", size: "text-sm", tag: "span", text: criterion.submittedSearchText }), badgeContainerClasses: pillClassnames, type: "span" }, index)))
137
+ : searchItems?.map((item, index) => (_jsx(Badge, { backgroundColor: pillColor, borderRadius: "rounded-full", hasRightIcon: true, icon: _jsx("div", { className: "text-white text-xxs", "data-testid": "item-clear-icon", children: getFontAwesomeIcon("xmark", "solid") }), iconSize: "text-sm", mobileIconLabel: item, onClick: handleClear, text: _jsx(Text, { color: "text-white", fontFamily: "font-serif", size: "text-sm", tag: "span", text: item }), badgeContainerClasses: pillClassnames, type: "span" }, index))) })) : null] }) }));
45
138
  };
46
139
  export default SearchTextInput;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agilant/toga-blox",
3
3
  "private": false,
4
- "version": "1.0.51",
4
+ "version": "1.0.54",
5
5
  "description": "",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",