@megha-ui/react 1.3.102 → 1.3.105

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.
@@ -18,7 +18,7 @@ import { getTotalPages } from "./utils/pagination";
18
18
  import { useSort } from "./hooks/useSort";
19
19
  import { usePagination } from "./hooks/usePagination";
20
20
  import { useBulkSelect } from "./hooks/useBulkSelect";
21
- import { createRegexFromWildcard, isValidDateFormat } from "./utils/regexUtils";
21
+ import { createRegexFromWildcard, isValidDateFormat, matchSqlLikePattern, } from "./utils/regexUtils";
22
22
  import { FiEye, FiShare, FiChevronsLeft, FiChevronsRight, } from "react-icons/fi";
23
23
  import { MdFilterAltOff, MdGroupOff, MdSave, MdLayers, MdLayersClear, } from "react-icons/md";
24
24
  import GlobalSearchChiProps from "./utils/globalSearchChips";
@@ -186,6 +186,9 @@ withCard = false, cardClassName, cardHeader, title, headerLeft, headerRight, sub
186
186
  const regex = createRegexFromWildcard(query);
187
187
  return regex.test(value);
188
188
  }
189
+ if (query.includes("%") || query.includes("_")) {
190
+ return matchSqlLikePattern(query, value);
191
+ }
189
192
  switch ((_c = searchQueries[column.key]) === null || _c === void 0 ? void 0 : _c.type) {
190
193
  case "contains":
191
194
  return value.toLowerCase().includes(query.toLowerCase());
@@ -255,7 +258,7 @@ withCard = false, cardClassName, cardHeader, title, headerLeft, headerRight, sub
255
258
  : orData.length > 0
256
259
  ? orData.some((column) => {
257
260
  var _a, _b, _c;
258
- const query = (_a = searchQueries[column.key]) === null || _a === void 0 ? void 0 : _a.text;
261
+ const query = ((_a = searchQueries[column.key]) === null || _a === void 0 ? void 0 : _a.text) || "";
259
262
  const queryData = query.toLowerCase().split(",");
260
263
  const value = item[column.key] && item[column.key].value
261
264
  ? (_b = item[column.key].value) === null || _b === void 0 ? void 0 : _b.toString()
@@ -264,6 +267,9 @@ withCard = false, cardClassName, cardHeader, title, headerLeft, headerRight, sub
264
267
  const regex = createRegexFromWildcard(query);
265
268
  return regex.test(value);
266
269
  }
270
+ if (query.includes("%") || query.includes("_")) {
271
+ return matchSqlLikePattern(query, value);
272
+ }
267
273
  switch ((_c = searchQueries[column.key]) === null || _c === void 0 ? void 0 : _c.type) {
268
274
  case "contains":
269
275
  return value.toLowerCase().includes(query.toLowerCase());
@@ -160,41 +160,41 @@ const GridHeader = ({ columns, onSearch, searchQueries, sortable, search, resiza
160
160
  };
161
161
  const menuOptions = [{ label: "Group by", groupBy: setGroupBy }];
162
162
  const searchOptions = [
163
- { label: "Contains", value: "contains", action: handleSearchOptionSelect },
163
+ { label: "", value: "contains", action: handleSearchOptionSelect },
164
164
  {
165
- label: "Does Not Contain",
165
+ label: "",
166
166
  value: "doesNotContain",
167
167
  action: handleSearchOptionSelect,
168
168
  },
169
- { label: "Equals", value: "equals", action: handleSearchOptionSelect },
169
+ { label: "=", value: "equals", action: handleSearchOptionSelect },
170
170
  {
171
- label: "Does Not Equal",
171
+ label: "",
172
172
  value: "doesNotEqual",
173
173
  action: handleSearchOptionSelect,
174
174
  },
175
- { label: "After", value: "after", action: handleSearchOptionSelect },
176
- { label: "Before", value: "before", action: handleSearchOptionSelect },
177
- { label: "Greater than", value: "gt", action: handleSearchOptionSelect },
175
+ { label: ">", value: "after", action: handleSearchOptionSelect },
176
+ { label: "<", value: "before", action: handleSearchOptionSelect },
177
+ { label: ">", value: "gt", action: handleSearchOptionSelect },
178
178
  {
179
- label: "Greater than or equal to",
179
+ label: "",
180
180
  value: "gte",
181
181
  action: handleSearchOptionSelect,
182
182
  },
183
- { label: "Lesser than", value: "lt", action: handleSearchOptionSelect },
183
+ { label: "<", value: "lt", action: handleSearchOptionSelect },
184
184
  {
185
- label: "Lesser than or equals to",
185
+ label: "",
186
186
  value: "lte",
187
187
  action: handleSearchOptionSelect,
188
188
  },
189
- { label: "Between", value: "between", action: handleSearchOptionSelect },
189
+ { label: "↔︎", value: "between", action: handleSearchOptionSelect },
190
190
  {
191
- label: "Starts With",
191
+ label: "",
192
192
  value: "startsWith",
193
193
  action: handleSearchOptionSelect,
194
194
  },
195
- { label: "Ends With", value: "endsWith", action: handleSearchOptionSelect },
196
- { label: "Blank", value: "blank", action: handleSearchOptionSelect },
197
- { label: "Not Blank", value: "notBlank", action: handleSearchOptionSelect },
195
+ { label: "", value: "endsWith", action: handleSearchOptionSelect },
196
+ { label: "Ø", value: "blank", action: handleSearchOptionSelect },
197
+ { label: "≠Ø", value: "notBlank", action: handleSearchOptionSelect },
198
198
  ];
199
199
  const textTypeSearch = [
200
200
  "contains",
@@ -402,17 +402,36 @@ const GridHeader = ({ columns, onSearch, searchQueries, sortable, search, resiza
402
402
  var _a;
403
403
  return setActiveSearchColumn(((_a = headerColumns.find((column) => column.key === _groupBy)) === null || _a === void 0 ? void 0 : _a.key) || "");
404
404
  }, onChange: (e) => {
405
- var _a, _b, _c;
406
- let searchType = ((_b = searchQueries[((_a = headerColumns.find((column) => column.key === _groupBy)) === null || _a === void 0 ? void 0 : _a.key) || ""]) === null || _b === void 0 ? void 0 : _b.type) ||
405
+ var _a, _b;
406
+ const currentKey = ((_a = headerColumns.find((column) => column.key === _groupBy)) === null || _a === void 0 ? void 0 : _a.key) || "";
407
+ const input = e.target.value;
408
+ let searchType = ((_b = searchQueries[currentKey]) === null || _b === void 0 ? void 0 : _b.type) ||
407
409
  defaultSearchOperation ||
408
410
  "contains";
409
- if (e.target.value.includes("to")) {
411
+ if (input.includes("to")) {
410
412
  searchType = "between";
411
413
  }
412
414
  else if (searchType === "between") {
413
415
  searchType = "contains";
414
416
  }
415
- onSearch(((_c = headerColumns.find((column) => column.key === _groupBy)) === null || _c === void 0 ? void 0 : _c.key) || "", e.target.value, searchType);
417
+ else if (input.includes("%")) {
418
+ const startsWithPercent = input.startsWith("%");
419
+ const endsWithPercent = input.endsWith("%");
420
+ if (startsWithPercent &&
421
+ endsWithPercent &&
422
+ input.length > 2) {
423
+ searchType = "contains";
424
+ }
425
+ else if (!startsWithPercent &&
426
+ endsWithPercent) {
427
+ searchType = "startsWith";
428
+ }
429
+ else if (startsWithPercent &&
430
+ !endsWithPercent) {
431
+ searchType = "endsWith";
432
+ }
433
+ }
434
+ onSearch(currentKey, input, searchType);
416
435
  }, placeholder: "Search", extraWrapperStyle: {
417
436
  background: ((_r = headerColumns.find((column) => column.key === _groupBy)) === null || _r === void 0 ? void 0 : _r.search)
418
437
  ? "var(--background)"
@@ -528,17 +547,36 @@ const GridHeader = ({ columns, onSearch, searchQueries, sortable, search, resiza
528
547
  var _a;
529
548
  return setActiveSearchColumn(((_a = headerColumns.find((column) => column.key === _groupBy)) === null || _a === void 0 ? void 0 : _a.key) || "");
530
549
  }, onChange: (e) => {
531
- var _a, _b, _c;
532
- let searchType = ((_b = searchQueries[((_a = headerColumns.find((column) => column.key === _groupBy)) === null || _a === void 0 ? void 0 : _a.key) || ""]) === null || _b === void 0 ? void 0 : _b.type) ||
550
+ var _a, _b;
551
+ const currentKey = ((_a = headerColumns.find((column) => column.key === _groupBy)) === null || _a === void 0 ? void 0 : _a.key) || "";
552
+ const input = e.target.value;
553
+ let searchType = ((_b = searchQueries[currentKey]) === null || _b === void 0 ? void 0 : _b.type) ||
533
554
  defaultSearchOperation ||
534
555
  "contains";
535
- if (e.target.value.includes("to")) {
556
+ if (input.includes("to")) {
536
557
  searchType = "between";
537
558
  }
538
559
  else if (searchType === "between") {
539
560
  searchType = "contains";
540
561
  }
541
- onSearch(((_c = headerColumns.find((column) => column.key === _groupBy)) === null || _c === void 0 ? void 0 : _c.key) || "", e.target.value, searchType);
562
+ else if (input.includes("%")) {
563
+ const startsWithPercent = input.startsWith("%");
564
+ const endsWithPercent = input.endsWith("%");
565
+ if (startsWithPercent &&
566
+ endsWithPercent &&
567
+ input.length > 2) {
568
+ searchType = "contains";
569
+ }
570
+ else if (!startsWithPercent &&
571
+ endsWithPercent) {
572
+ searchType = "startsWith";
573
+ }
574
+ else if (startsWithPercent &&
575
+ !endsWithPercent) {
576
+ searchType = "endsWith";
577
+ }
578
+ }
579
+ onSearch(currentKey, input, searchType);
542
580
  }, placeholder: "Search", extraWrapperStyle: {
543
581
  background: ((_24 = headerColumns.find((column) => column.key === _groupBy)) === null || _24 === void 0 ? void 0 : _24.search)
544
582
  ? "var(--background)"
@@ -654,17 +692,58 @@ const GridHeader = ({ columns, onSearch, searchQueries, sortable, search, resiza
654
692
  var _a;
655
693
  return setActiveSearchColumn(((_a = headerColumns.find((column) => column.key === _groupBy)) === null || _a === void 0 ? void 0 : _a.key) || "");
656
694
  }, onChange: (e) => {
657
- var _a, _b, _c;
658
- let searchType = ((_b = searchQueries[((_a = headerColumns.find((column) => column.key === _groupBy)) === null || _a === void 0 ? void 0 : _a.key) || ""]) === null || _b === void 0 ? void 0 : _b.type) ||
695
+ var _a, _b;
696
+ const currentKey = ((_a = headerColumns.find((column) => column.key === _groupBy)) === null || _a === void 0 ? void 0 : _a.key) || "";
697
+ const input = e.target.value;
698
+ const searchType = ((_b = searchQueries[currentKey]) === null || _b === void 0 ? void 0 : _b.type) ||
699
+ defaultSearchOperation ||
700
+ "contains";
701
+ onSearch(currentKey, input, searchType);
702
+ }, onBlur: (e) => {
703
+ var _a, _b;
704
+ const currentKey = ((_a = headerColumns.find((column) => column.key === _groupBy)) === null || _a === void 0 ? void 0 : _a.key) || "";
705
+ const raw = e.target.value;
706
+ const input = raw.replace(/\?/g, "%");
707
+ let searchType = ((_b = searchQueries[currentKey]) === null || _b === void 0 ? void 0 : _b.type) ||
659
708
  defaultSearchOperation ||
660
709
  "contains";
661
- if (e.target.value.includes("to")) {
710
+ if (input.includes("to")) {
662
711
  searchType = "between";
663
712
  }
664
713
  else if (searchType === "between") {
665
714
  searchType = "contains";
666
715
  }
667
- onSearch(((_c = headerColumns.find((column) => column.key === _groupBy)) === null || _c === void 0 ? void 0 : _c.key) || "", e.target.value, searchType);
716
+ else if (input.includes("%")) {
717
+ const startsWithPercent = input.startsWith("%");
718
+ const endsWithPercent = input.endsWith("%");
719
+ if (startsWithPercent &&
720
+ endsWithPercent &&
721
+ input.length > 2) {
722
+ searchType = "contains";
723
+ }
724
+ else if (!startsWithPercent && endsWithPercent) {
725
+ searchType = "startsWith";
726
+ }
727
+ else if (startsWithPercent &&
728
+ !endsWithPercent) {
729
+ searchType = "endsWith";
730
+ }
731
+ }
732
+ let query = input;
733
+ if (query &&
734
+ !query.includes("%") &&
735
+ !query.includes("_")) {
736
+ if (searchType === "contains") {
737
+ query = `%${query}%`;
738
+ }
739
+ else if (searchType === "startsWith") {
740
+ query = `${query}%`;
741
+ }
742
+ else if (searchType === "endsWith") {
743
+ query = `%${query}`;
744
+ }
745
+ }
746
+ onSearch(currentKey, query, searchType);
668
747
  }, placeholder: "Search", extraWrapperStyle: {
669
748
  background: ((_57 = headerColumns.find((column) => column.key === _groupBy)) === null || _57 === void 0 ? void 0 : _57.search)
670
749
  ? "var(--background)"
@@ -826,16 +905,34 @@ const GridHeader = ({ columns, onSearch, searchQueries, sortable, search, resiza
826
905
  alignItems: "center",
827
906
  }, children: [_jsx(TextInput, { onFocus: () => setActiveSearchColumn(column.key), onChange: (e) => {
828
907
  var _a;
908
+ const input = e.target.value;
829
909
  let searchType = ((_a = searchQueries[column.key]) === null || _a === void 0 ? void 0 : _a.type) ||
830
910
  defaultSearchOperation ||
831
911
  "contains";
832
- if (e.target.value.includes("to")) {
912
+ if (input.includes("to")) {
833
913
  searchType = "between";
834
914
  }
835
915
  else if (searchType === "between") {
836
916
  searchType = "contains";
837
917
  }
838
- onSearch(column.key, e.target.value, searchType);
918
+ else if (input.includes("%")) {
919
+ const startsWithPercent = input.startsWith("%");
920
+ const endsWithPercent = input.endsWith("%");
921
+ if (startsWithPercent &&
922
+ endsWithPercent &&
923
+ input.length > 2) {
924
+ searchType = "contains";
925
+ }
926
+ else if (!startsWithPercent &&
927
+ endsWithPercent) {
928
+ searchType = "startsWith";
929
+ }
930
+ else if (startsWithPercent &&
931
+ !endsWithPercent) {
932
+ searchType = "endsWith";
933
+ }
934
+ }
935
+ onSearch(column.key, input, searchType);
839
936
  }, placeholder: "Search", extraWrapperStyle: {
840
937
  background: column.search
841
938
  ? "var(--background)"
@@ -914,16 +1011,34 @@ const GridHeader = ({ columns, onSearch, searchQueries, sortable, search, resiza
914
1011
  alignItems: "center",
915
1012
  }, children: [_jsx(TextInput, { onFocus: () => setActiveSearchColumn(column.key), onChange: (e) => {
916
1013
  var _a;
1014
+ const input = e.target.value;
917
1015
  let searchType = ((_a = searchQueries[column.key]) === null || _a === void 0 ? void 0 : _a.type) ||
918
1016
  defaultSearchOperation ||
919
1017
  "contains";
920
- if (e.target.value.includes("to")) {
1018
+ if (input.includes("to")) {
921
1019
  searchType = "between";
922
1020
  }
923
1021
  else if (searchType === "between") {
924
1022
  searchType = "contains";
925
1023
  }
926
- onSearch(column.key, e.target.value, searchType);
1024
+ else if (input.includes("%")) {
1025
+ const startsWithPercent = input.startsWith("%");
1026
+ const endsWithPercent = input.endsWith("%");
1027
+ if (startsWithPercent &&
1028
+ endsWithPercent &&
1029
+ input.length > 2) {
1030
+ searchType = "contains";
1031
+ }
1032
+ else if (!startsWithPercent &&
1033
+ endsWithPercent) {
1034
+ searchType = "startsWith";
1035
+ }
1036
+ else if (startsWithPercent &&
1037
+ !endsWithPercent) {
1038
+ searchType = "endsWith";
1039
+ }
1040
+ }
1041
+ onSearch(column.key, input, searchType);
927
1042
  }, placeholder: "Search", extraWrapperStyle: {
928
1043
  background: column.search
929
1044
  ? "var(--background)"
@@ -1000,16 +1115,56 @@ const GridHeader = ({ columns, onSearch, searchQueries, sortable, search, resiza
1000
1115
  alignItems: "center",
1001
1116
  }, children: [_jsx(TextInput, { onFocus: () => setActiveSearchColumn(column.key), onChange: (e) => {
1002
1117
  var _a;
1118
+ const input = e.target.value;
1119
+ const searchType = ((_a = searchQueries[column.key]) === null || _a === void 0 ? void 0 : _a.type) ||
1120
+ defaultSearchOperation ||
1121
+ "contains";
1122
+ onSearch(column.key, input, searchType);
1123
+ }, onBlur: (e) => {
1124
+ var _a;
1125
+ const raw = e.target.value;
1126
+ const input = raw.replace(/\?/g, "%");
1003
1127
  let searchType = ((_a = searchQueries[column.key]) === null || _a === void 0 ? void 0 : _a.type) ||
1004
1128
  defaultSearchOperation ||
1005
1129
  "contains";
1006
- if (e.target.value.includes("to")) {
1130
+ if (input.includes("to")) {
1007
1131
  searchType = "between";
1008
1132
  }
1009
1133
  else if (searchType === "between") {
1010
1134
  searchType = "contains";
1011
1135
  }
1012
- onSearch(column.key, e.target.value, searchType);
1136
+ else if (input.includes("%")) {
1137
+ const startsWithPercent = input.startsWith("%");
1138
+ const endsWithPercent = input.endsWith("%");
1139
+ if (startsWithPercent &&
1140
+ endsWithPercent &&
1141
+ input.length > 2) {
1142
+ searchType = "contains";
1143
+ }
1144
+ else if (!startsWithPercent &&
1145
+ endsWithPercent) {
1146
+ searchType = "startsWith";
1147
+ }
1148
+ else if (startsWithPercent &&
1149
+ !endsWithPercent) {
1150
+ searchType = "endsWith";
1151
+ }
1152
+ }
1153
+ let query = input;
1154
+ if (query &&
1155
+ !query.includes("%") &&
1156
+ !query.includes("_")) {
1157
+ if (searchType === "contains") {
1158
+ query = `%${query}%`;
1159
+ }
1160
+ else if (searchType === "startsWith") {
1161
+ query = `${query}%`;
1162
+ }
1163
+ else if (searchType === "endsWith") {
1164
+ query = `%${query}`;
1165
+ }
1166
+ }
1167
+ onSearch(column.key, query, searchType);
1013
1168
  }, placeholder: "Search", extraWrapperStyle: {
1014
1169
  background: column.search
1015
1170
  ? "var(--background)"
@@ -1,2 +1,3 @@
1
1
  export declare const createRegexFromWildcard: (query: string) => RegExp;
2
+ export declare const matchSqlLikePattern: (pattern: string, value: string) => boolean;
2
3
  export declare function isValidDateFormat(dateStr: string): boolean;
@@ -3,6 +3,37 @@ export const createRegexFromWildcard = (query) => {
3
3
  const regexPattern = "^" + escapedQuery.replace(/\*/g, ".*").replace(/\?/g, ".") + ".*$";
4
4
  return new RegExp(regexPattern, "i");
5
5
  };
6
+ // SQL-like pattern matcher for column search.
7
+ // Supports:
8
+ // - %value% -> substring match
9
+ // - %value -> endsWith
10
+ // - value% -> startsWith
11
+ // - _ -> single digit wildcard (0-9) when used in pattern
12
+ // Falls back to case-insensitive "contains" when no wildcards present.
13
+ export const matchSqlLikePattern = (pattern, value) => {
14
+ if (!value)
15
+ return false;
16
+ let v = value.toLowerCase();
17
+ let p = pattern.toLowerCase();
18
+ // Percent-based wildcards
19
+ if (p.startsWith("%") && p.endsWith("%") && p.length > 2) {
20
+ return v.includes(p.slice(1, -1));
21
+ }
22
+ if (p.startsWith("%")) {
23
+ return v.endsWith(p.slice(1));
24
+ }
25
+ if (p.endsWith("%")) {
26
+ return v.startsWith(p.slice(0, -1));
27
+ }
28
+ // Underscore: single digit wildcard
29
+ if (p.includes("_")) {
30
+ const escaped = p.replace(/([.+^${}()|[\]\\])/g, "\\$1");
31
+ const regexPattern = "^" + escaped.replace(/_/g, "\\d") + "$";
32
+ return new RegExp(regexPattern, "i").test(value);
33
+ }
34
+ // Default: simple contains
35
+ return v.includes(p);
36
+ };
6
37
  export function isValidDateFormat(dateStr) {
7
38
  if (!dateStr || typeof dateStr !== "string")
8
39
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@megha-ui/react",
3
- "version": "1.3.102",
3
+ "version": "1.3.105",
4
4
  "description": "A collection of reusable UI components for React applications, built with TypeScript.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",