@teselagen/ui 0.8.8 → 0.9.1

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.
Files changed (43) hide show
  1. package/DataTable/utils/filterLocalEntitiesToHasura.d.ts +5 -0
  2. package/DataTable/utils/getAllRows.d.ts +1 -1
  3. package/DataTable/utils/getRowCopyText.d.ts +1 -3
  4. package/DataTable/utils/handleCopyColumn.d.ts +1 -1
  5. package/DataTable/utils/handleCopyRows.d.ts +1 -5
  6. package/DataTable/utils/handleCopyTable.d.ts +1 -1
  7. package/DataTable/utils/index.d.ts +0 -1
  8. package/DataTable/utils/initializeHasuraWhereAndFilter.d.ts +1 -0
  9. package/DataTable/utils/queryParams.d.ts +16 -12
  10. package/DataTable/utils/rowClick.d.ts +1 -1
  11. package/DataTable/utils/tableQueryParamsToHasuraClauses.d.ts +26 -0
  12. package/FormComponents/Uploader.d.ts +1 -3
  13. package/FormComponents/tryToMatchSchemas.d.ts +1 -1
  14. package/MenuBar/index.d.ts +1 -3
  15. package/README.md +1 -1
  16. package/ResizableDraggableDialog/index.d.ts +1 -3
  17. package/TagSelect/index.d.ts +1 -1
  18. package/index.cjs.js +39715 -36506
  19. package/index.d.ts +2 -0
  20. package/index.es.js +38714 -35505
  21. package/package.json +2 -2
  22. package/src/DataTable/Columns.js +2 -2
  23. package/src/DataTable/DisplayOptions.js +1 -1
  24. package/src/DataTable/FilterAndSortMenu.js +27 -30
  25. package/src/DataTable/index.js +101 -84
  26. package/src/DataTable/style.css +1 -1
  27. package/src/DataTable/utils/filterLocalEntitiesToHasura.js +356 -0
  28. package/src/DataTable/utils/filterLocalEntitiesToHasura.test.js +1285 -0
  29. package/src/DataTable/utils/getAllRows.js +2 -6
  30. package/src/DataTable/utils/handleCopyColumn.js +2 -2
  31. package/src/DataTable/utils/handleCopyTable.js +2 -2
  32. package/src/DataTable/utils/initializeHasuraWhereAndFilter.js +15 -0
  33. package/src/DataTable/utils/queryParams.js +153 -770
  34. package/src/DataTable/utils/tableQueryParamsToHasuraClauses.js +277 -0
  35. package/src/DataTable/utils/tableQueryParamsToHasuraClauses.test.js +245 -0
  36. package/src/DataTable/utils/withTableParams.js +3 -16
  37. package/src/FormComponents/index.js +2 -2
  38. package/src/TgSelect/index.js +15 -0
  39. package/src/index.js +2 -0
  40. package/src/utils/determineBlackOrWhiteTextColor.js +8 -1
  41. package/ui.css +10537 -0
  42. package/utils/determineBlackOrWhiteTextColor.d.ts +1 -2
  43. package/utils/hotkeyUtils.d.ts +1 -3
@@ -0,0 +1,277 @@
1
+ import { camelCase, set } from "lodash-es";
2
+
3
+ export function tableQueryParamsToHasuraClauses({
4
+ page,
5
+ pageSize,
6
+ searchTerm,
7
+ filters,
8
+ order,
9
+ schema, // Add schema as a parameter
10
+ additionalFilter
11
+ }) {
12
+ const ccFields = getFieldsMappedByCCDisplayName(schema);
13
+ let where = {};
14
+ const order_by = [];
15
+ const limit = pageSize || 25;
16
+ const offset = page && pageSize ? (page - 1) * pageSize : 0;
17
+
18
+ if (searchTerm) {
19
+ const searchTermFilters = [];
20
+ // Create a map to deduplicate fields by path
21
+ const uniqueFieldsByPath = {};
22
+
23
+ // Split the search term by comma to support multi-term searching
24
+ const searchTerms = searchTerm.split(",");
25
+
26
+ schema.fields.forEach(field => {
27
+ const { type, path, searchDisabled } = field;
28
+ if (uniqueFieldsByPath[path]) return; // Skip if already added
29
+ uniqueFieldsByPath[path] = true;
30
+ if (searchDisabled || field.filterDisabled || type === "color") return;
31
+
32
+ // Process each search term
33
+ searchTerms.forEach(term => {
34
+ const filterValue = term.trim(); // Trim the term to handle spaces after commas
35
+
36
+ if (type === "string" || type === "lookup") {
37
+ const o = set({}, path, { _ilike: `%${filterValue}%` });
38
+ searchTermFilters.push(o);
39
+ } else if (type === "boolean") {
40
+ let regex;
41
+ try {
42
+ regex = new RegExp("^" + filterValue, "ig");
43
+ } catch (error) {
44
+ //ignore
45
+ }
46
+ if (regex) {
47
+ if ("true".replace(regex, "") !== "true") {
48
+ const o = set({}, path, { _eq: true });
49
+ searchTermFilters.push(o);
50
+ } else if ("false".replace(regex, "") !== "false") {
51
+ const o = set({}, path, { _eq: false });
52
+ searchTermFilters.push(o);
53
+ }
54
+ }
55
+ } else if (
56
+ (type === "number" || type === "integer") &&
57
+ !isNaN(filterValue)
58
+ ) {
59
+ const o = set({}, path, { _eq: parseFloat(filterValue) });
60
+ searchTermFilters.push(o);
61
+ }
62
+ });
63
+ });
64
+
65
+ if (searchTermFilters.length > 0) {
66
+ if (Object.keys(where).length > 0) {
67
+ where = { _and: [where, { _or: searchTermFilters }] };
68
+ } else {
69
+ where = { _or: searchTermFilters };
70
+ }
71
+ }
72
+ }
73
+
74
+ if (filters && filters.length > 0) {
75
+ const filterClauses = filters.map(filter => {
76
+ let { selectedFilter, filterOn, filterValue } = filter;
77
+ const fieldSchema = ccFields[filterOn] || {};
78
+
79
+ const { path, reference, type, customColumnFilter } = fieldSchema;
80
+ if (customColumnFilter) {
81
+ return customColumnFilter(filterValue);
82
+ }
83
+ let stringFilterValue =
84
+ filterValue && filterValue.toString
85
+ ? filterValue.toString()
86
+ : filterValue;
87
+ if (stringFilterValue === false) {
88
+ // we still want to be able to search for the string "false" which will get parsed to false
89
+ stringFilterValue = "false";
90
+ } else {
91
+ stringFilterValue = stringFilterValue || "";
92
+ }
93
+ const arrayFilterValue = Array.isArray(filterValue)
94
+ ? filterValue
95
+ : stringFilterValue.split(";");
96
+
97
+ if (type === "number" || type === "integer") {
98
+ filterValue = Array.isArray(filterValue)
99
+ ? filterValue.map(val => Number(val))
100
+ : Number(filterValue);
101
+ }
102
+
103
+ if (fieldSchema.normalizeFilter) {
104
+ filterValue = fieldSchema.normalizeFilter(
105
+ filterValue,
106
+ selectedFilter,
107
+ filterOn
108
+ );
109
+ }
110
+
111
+ if (reference) {
112
+ filterOn = reference.sourceField;
113
+ } else {
114
+ filterOn = path || filterOn;
115
+ }
116
+ switch (selectedFilter) {
117
+ case "none":
118
+ return {};
119
+ case "startsWith":
120
+ return { [filterOn]: { _ilike: `${filterValue}%` } };
121
+ case "endsWith":
122
+ return { [filterOn]: { _ilike: `%${filterValue}` } };
123
+ case "contains":
124
+ return { [filterOn]: { _ilike: `%${filterValue}%` } };
125
+ case "notContains":
126
+ return { [filterOn]: { _nilike: `%${filterValue}%` } };
127
+ case "isExactly":
128
+ return { [filterOn]: { _eq: filterValue } };
129
+ case "isEmpty":
130
+ if (filterOn.includes(".")) {
131
+ // if we're filtering on a nested field, like a sequence table with parts.name
132
+ // we really want to just query on the top level field's existence
133
+ return {
134
+ _not: {
135
+ [filterOn.split(".")[0]]: {}
136
+ }
137
+ };
138
+ }
139
+ return {
140
+ _or: [
141
+ { [filterOn]: { _eq: "" } },
142
+ { [filterOn]: { _is_null: true } }
143
+ ]
144
+ };
145
+ case "notEmpty":
146
+ return {
147
+ _and: [
148
+ { [filterOn]: { _neq: "" } },
149
+ { [filterOn]: { _is_null: false } }
150
+ ]
151
+ };
152
+ case "inList":
153
+ return { [filterOn]: { _in: filterValue } };
154
+ case "notInList":
155
+ return { [filterOn]: { _nin: filterValue } };
156
+ case "true":
157
+ return { [filterOn]: { _eq: true } };
158
+ case "false":
159
+ return { [filterOn]: { _eq: false } };
160
+ case "dateIs":
161
+ return { [filterOn]: { _eq: filterValue } };
162
+ case "notBetween":
163
+ return {
164
+ _or: [
165
+ {
166
+ [filterOn]: {
167
+ _lt: new Date(arrayFilterValue[0])
168
+ }
169
+ },
170
+ {
171
+ [filterOn]: {
172
+ _gt: new Date(new Date(arrayFilterValue[1]).setHours(23, 59))
173
+ }
174
+ }
175
+ ]
176
+ };
177
+ case "isBetween":
178
+ return {
179
+ [filterOn]: {
180
+ _gte: new Date(arrayFilterValue[0]),
181
+ _lte: new Date(new Date(arrayFilterValue[1]).setHours(23, 59))
182
+ }
183
+ };
184
+ case "isBefore":
185
+ return { [filterOn]: { _lt: new Date(filterValue) } };
186
+ case "isAfter":
187
+ return { [filterOn]: { _gt: new Date(filterValue) } };
188
+ case "greaterThan":
189
+ return { [filterOn]: { _gt: parseFloat(filterValue) } };
190
+ case "lessThan":
191
+ return { [filterOn]: { _lt: parseFloat(filterValue) } };
192
+ case "inRange":
193
+ return {
194
+ [filterOn]: {
195
+ _gte: parseFloat(arrayFilterValue[0]),
196
+ _lte: parseFloat(arrayFilterValue[1])
197
+ }
198
+ };
199
+ case "outsideRange":
200
+ return {
201
+ _or: [
202
+ {
203
+ [filterOn]: {
204
+ _lt: parseFloat(arrayFilterValue[0])
205
+ }
206
+ },
207
+ {
208
+ [filterOn]: {
209
+ _gt: parseFloat(arrayFilterValue[1])
210
+ }
211
+ }
212
+ ]
213
+ };
214
+ case "equalTo":
215
+ return {
216
+ [filterOn]: {
217
+ _eq:
218
+ type === "number" || type === "integer"
219
+ ? parseFloat(filterValue)
220
+ : filterValue
221
+ }
222
+ };
223
+ case "regex":
224
+ return { [filterOn]: { _regex: filterValue } };
225
+ default:
226
+ console.warn(`Unsupported filter type: ${selectedFilter}`);
227
+ return {};
228
+ }
229
+ });
230
+
231
+ if (filterClauses.length > 0) {
232
+ if (Object.keys(where).length > 0) {
233
+ where = { _and: [where, ...filterClauses] };
234
+ } else {
235
+ where = { _and: filterClauses };
236
+ }
237
+ }
238
+ }
239
+
240
+ if (order && order.length > 0) {
241
+ order.forEach(item => {
242
+ const field = item.startsWith("-") ? item.substring(1) : item;
243
+ const direction = item.startsWith("-") ? "desc" : "asc";
244
+ order_by.push({ [field]: direction });
245
+ });
246
+ }
247
+
248
+ if (additionalFilter) {
249
+ where = { _and: [where, additionalFilter] };
250
+ }
251
+ return { where, order_by, limit, offset };
252
+ }
253
+
254
+ /**
255
+ * Takes a schema and returns an object with the fields mapped by their camelCased display name.
256
+ * If the displayName is not set or is a jsx element, the path is used instead.
257
+ * The same conversion must be done when using the result of this method
258
+ */
259
+ export function getFieldsMappedByCCDisplayName(schema) {
260
+ if (!schema || !schema.fields) return {};
261
+ return schema.fields.reduce((acc, field) => {
262
+ const ccDisplayName = getCCDisplayName(field);
263
+ acc[ccDisplayName] = field;
264
+ return acc;
265
+ }, {});
266
+ }
267
+
268
+ /**
269
+ *
270
+ * @param {object} field
271
+ * @returns the camelCase display name of the field, to be used for filters, sorting, etc
272
+ */
273
+ export function getCCDisplayName(field) {
274
+ return camelCase(
275
+ typeof field.displayName === "string" ? field.displayName : field.path
276
+ );
277
+ }
@@ -0,0 +1,245 @@
1
+ import { tableQueryParamsToHasuraClauses } from "./tableQueryParamsToHasuraClauses";
2
+
3
+ describe("tableQueryParamsToHasuraClauses", () => {
4
+ const schema = {
5
+ fields: [
6
+ { path: "name", type: "string" },
7
+ { path: "age", type: "number" },
8
+ { path: "isActive", type: "boolean" },
9
+ { path: "email", type: "string" }
10
+ ]
11
+ };
12
+
13
+ it("should handle empty query params", () => {
14
+ const result = tableQueryParamsToHasuraClauses({});
15
+ expect(result).toEqual({
16
+ where: {},
17
+ order_by: [],
18
+ limit: 25,
19
+ offset: 0
20
+ });
21
+ });
22
+
23
+ it("should handle page and pageSize", () => {
24
+ const result = tableQueryParamsToHasuraClauses({ page: 2, pageSize: 10 });
25
+ expect(result).toEqual({
26
+ where: {},
27
+ order_by: [],
28
+ limit: 10,
29
+ offset: 10
30
+ });
31
+ });
32
+
33
+ it("should handle searchTerm with string fields", () => {
34
+ const result = tableQueryParamsToHasuraClauses({
35
+ searchTerm: "test",
36
+ schema
37
+ });
38
+ expect(result).toEqual({
39
+ where: {
40
+ _or: [{ name: { _ilike: "%test%" } }, { email: { _ilike: "%test%" } }]
41
+ },
42
+ order_by: [],
43
+ limit: 25,
44
+ offset: 0
45
+ });
46
+ });
47
+ it("should handle searchTerm with string fields with a comma in them", () => {
48
+ const result = tableQueryParamsToHasuraClauses({
49
+ searchTerm: "test,test2",
50
+ schema
51
+ });
52
+ expect(result).toEqual({
53
+ where: {
54
+ _or: [
55
+ { name: { _ilike: "%test%" } },
56
+ { name: { _ilike: "%test2%" } },
57
+ { email: { _ilike: "%test%" } },
58
+ { email: { _ilike: "%test2%" } }
59
+ ]
60
+ },
61
+ order_by: [],
62
+ limit: 25,
63
+ offset: 0
64
+ });
65
+ });
66
+ it("should flatten queries with dup paths", () => {
67
+ const result = tableQueryParamsToHasuraClauses({
68
+ searchTerm: "test",
69
+ schema: {
70
+ fields: [
71
+ ...schema.fields,
72
+ { path: "name", type: "string" },
73
+ { path: "name", type: "string" }
74
+ ]
75
+ }
76
+ });
77
+ expect(result).toEqual({
78
+ where: {
79
+ _or: [{ name: { _ilike: "%test%" } }, { email: { _ilike: "%test%" } }]
80
+ },
81
+ order_by: [],
82
+ limit: 25,
83
+ offset: 0
84
+ });
85
+ });
86
+
87
+ it("should handle searchTerm with number fields", () => {
88
+ const result = tableQueryParamsToHasuraClauses({
89
+ searchTerm: "30",
90
+ schema
91
+ });
92
+ expect(result).toEqual({
93
+ where: {
94
+ _or: [
95
+ { name: { _ilike: "%30%" } },
96
+ { age: { _eq: 30 } },
97
+ { email: { _ilike: "%30%" } }
98
+ ]
99
+ },
100
+ order_by: [],
101
+ limit: 25,
102
+ offset: 0
103
+ });
104
+ });
105
+
106
+ it("should handle searchTerm with boolean fields", () => {
107
+ const result = tableQueryParamsToHasuraClauses({
108
+ searchTerm: "true",
109
+ schema
110
+ });
111
+ expect(result).toEqual({
112
+ where: {
113
+ _or: [
114
+ { name: { _ilike: "%true%" } },
115
+ { isActive: { _eq: true } },
116
+ { email: { _ilike: "%true%" } }
117
+ ]
118
+ },
119
+ order_by: [],
120
+ limit: 25,
121
+ offset: 0
122
+ });
123
+ });
124
+
125
+ it("should handle searchTerm with multiple field types", () => {
126
+ const result = tableQueryParamsToHasuraClauses({
127
+ searchTerm: "test",
128
+ schema
129
+ });
130
+ expect(result).toEqual({
131
+ where: {
132
+ _or: [{ name: { _ilike: "%test%" } }, { email: { _ilike: "%test%" } }]
133
+ },
134
+ order_by: [],
135
+ limit: 25,
136
+ offset: 0
137
+ });
138
+ });
139
+
140
+ it("should handle contains filter", () => {
141
+ const result = tableQueryParamsToHasuraClauses({
142
+ filters: [
143
+ {
144
+ selectedFilter: "contains",
145
+ filterOn: "name",
146
+ filterValue: "test"
147
+ }
148
+ ]
149
+ });
150
+ expect(result).toEqual({
151
+ where: { _and: [{ name: { _ilike: "%test%" } }] },
152
+ order_by: [],
153
+ limit: 25,
154
+ offset: 0
155
+ });
156
+ });
157
+
158
+ it("should handle equalTo filter for number", () => {
159
+ const result = tableQueryParamsToHasuraClauses({
160
+ filters: [
161
+ { selectedFilter: "equalTo", filterOn: "age", filterValue: "30" }
162
+ ],
163
+ schema
164
+ });
165
+ expect(result).toEqual({
166
+ where: { _and: [{ age: { _eq: 30 } }] },
167
+ order_by: [],
168
+ limit: 25,
169
+ offset: 0
170
+ });
171
+ });
172
+
173
+ it("should handle order", () => {
174
+ const result = tableQueryParamsToHasuraClauses({ order: ["name", "-age"] });
175
+ expect(result).toEqual({
176
+ where: {},
177
+ order_by: [{ name: "asc" }, { age: "desc" }],
178
+ limit: 25,
179
+ offset: 0
180
+ });
181
+ });
182
+
183
+ it("should combine all params", () => {
184
+ const result = tableQueryParamsToHasuraClauses({
185
+ page: 2,
186
+ pageSize: 10,
187
+ searchTerm: "test",
188
+ filters: [
189
+ {
190
+ selectedFilter: "greaterThan",
191
+ filterOn: "age",
192
+ filterValue: "30"
193
+ }
194
+ ],
195
+ order: ["name"],
196
+ schema
197
+ });
198
+ expect(result).toEqual({
199
+ where: {
200
+ _and: [
201
+ {
202
+ _or: [
203
+ { name: { _ilike: "%test%" } },
204
+ { email: { _ilike: "%test%" } }
205
+ ]
206
+ },
207
+ { age: { _gt: 30 } }
208
+ ]
209
+ },
210
+ order_by: [{ name: "asc" }],
211
+ limit: 10,
212
+ offset: 10
213
+ });
214
+ });
215
+
216
+ it("should combine searchTerm and filters", () => {
217
+ const result = tableQueryParamsToHasuraClauses({
218
+ searchTerm: "test",
219
+ filters: [
220
+ {
221
+ selectedFilter: "greaterThan",
222
+ filterOn: "age",
223
+ filterValue: "30"
224
+ }
225
+ ],
226
+ schema
227
+ });
228
+ expect(result).toEqual({
229
+ where: {
230
+ _and: [
231
+ {
232
+ _or: [
233
+ { name: { _ilike: "%test%" } },
234
+ { email: { _ilike: "%test%" } }
235
+ ]
236
+ },
237
+ { age: { _gt: 30 } }
238
+ ]
239
+ },
240
+ order_by: [],
241
+ limit: 25,
242
+ offset: 0
243
+ });
244
+ });
245
+ });
@@ -6,13 +6,13 @@ import {
6
6
  makeDataTableHandlers,
7
7
  getQueryParams,
8
8
  setCurrentParamsOnUrl,
9
- getCurrentParamsFromUrl,
10
- getCCDisplayName
9
+ getCurrentParamsFromUrl
11
10
  } from "./queryParams";
12
11
  import { withRouter } from "react-router-dom";
13
12
  import getTableConfigFromStorage from "./getTableConfigFromStorage";
14
13
  import { useDeepEqualMemo } from "../../utils/hooks/useDeepEqualMemo";
15
14
  import { branch, compose } from "recompose";
15
+ import { getCCDisplayName } from "./tableQueryParamsToHasuraClauses";
16
16
 
17
17
  /**
18
18
  * Note all these options can be passed at Design Time or at Runtime (like reduxForm())
@@ -32,7 +32,6 @@ import { branch, compose } from "recompose";
32
32
  export const useTableParams = props => {
33
33
  const {
34
34
  additionalFilter,
35
- additionalOrFilter,
36
35
  controlled_pageSize,
37
36
  defaults: _defaults,
38
37
  doNotCoercePageSize,
@@ -166,16 +165,6 @@ export const useTableParams = props => {
166
165
  );
167
166
 
168
167
  const queryParams = useMemo(() => {
169
- const additionalFilterToUse =
170
- typeof additionalFilter === "function"
171
- ? additionalFilter
172
- : () => additionalFilter;
173
-
174
- const additionalOrFilterToUse =
175
- typeof additionalOrFilter === "function"
176
- ? additionalOrFilter
177
- : () => additionalOrFilter;
178
-
179
168
  return getQueryParams({
180
169
  doNotCoercePageSize,
181
170
  currentParams,
@@ -185,8 +174,7 @@ export const useTableParams = props => {
185
174
  schema: convertedSchema,
186
175
  isInfinite: isInfinite || (isSimple && !withPaging),
187
176
  isLocalCall,
188
- additionalFilter: additionalFilterToUse,
189
- additionalOrFilter: additionalOrFilterToUse,
177
+ additionalFilter,
190
178
  noOrderError,
191
179
  isCodeModel,
192
180
  ownProps: passingProps
@@ -194,7 +182,6 @@ export const useTableParams = props => {
194
182
  }, [
195
183
  additionalFilter,
196
184
  passingProps,
197
- additionalOrFilter,
198
185
  doNotCoercePageSize,
199
186
  currentParams,
200
187
  entities,
@@ -114,14 +114,14 @@ function removeUnwantedProps(props) {
114
114
 
115
115
  const LabelWithTooltipInfo = ({ label, tooltipInfo, labelStyle }) =>
116
116
  tooltipInfo ? (
117
- <div style={{ display: "flex", alignItems: "center", ...labelStyle }}>
117
+ <span style={{ display: "flex", alignItems: "center", ...labelStyle }}>
118
118
  {label}{" "}
119
119
  <InfoHelper
120
120
  style={{ marginLeft: "5px", marginTop: "-6px" }}
121
121
  size={12}
122
122
  content={tooltipInfo}
123
123
  />
124
- </div>
124
+ </span>
125
125
  ) : (
126
126
  label || null
127
127
  );
@@ -390,6 +390,21 @@ class TgSelect extends React.Component {
390
390
  onKeyDown: e => {
391
391
  const { which } = e;
392
392
  e.persist();
393
+ // Handle Cmd+Enter (macOS) or Ctrl+Enter (Windows/Linux)
394
+ if ((e.metaKey || e.ctrlKey) && which === Keys.ENTER) {
395
+ e.preventDefault();
396
+ // Try to select the active item, or the first enabled item
397
+ const items = options || [];
398
+ let activeItem = this.state.activeItem;
399
+ if (!activeItem) {
400
+ // Find the first enabled item
401
+ activeItem = items.find(opt => !itemDisabled(opt));
402
+ }
403
+ if (activeItem) {
404
+ this.handleItemSelect(activeItem, e);
405
+ }
406
+ return;
407
+ }
393
408
  if (which === Keys.ENTER) {
394
409
  e.preventDefault();
395
410
  // e.stopPropagation();
package/src/index.js CHANGED
@@ -41,6 +41,7 @@ export { default as popoverOverflowModifiers } from "./utils/popoverOverflowModi
41
41
  export * from "./utils/tgFormValues";
42
42
  export { default as tgFormValues } from "./utils/tgFormValues";
43
43
  export { default as withStore } from "./utils/withStore";
44
+ export { default as determineBlackOrWhiteTextColor } from "./utils/determineBlackOrWhiteTextColor";
44
45
  export {
45
46
  default as withTableParams,
46
47
  useTableParams
@@ -85,3 +86,4 @@ const noop = () => undefined;
85
86
  export { noop };
86
87
  export { default as showDialogOnDocBody } from "./showDialogOnDocBody";
87
88
  export { default as TableFormTrackerContext } from "./DataTable/TableFormTrackerContext";
89
+ export { initializeHasuraWhereAndFilter } from "./DataTable/utils/initializeHasuraWhereAndFilter";
@@ -1,4 +1,11 @@
1
1
  /* Copyright (C) 2018 TeselaGen Biotechnology, Inc. */
2
2
  import Color from "color";
3
3
 
4
- export default c => (Color(c).isLight() ? "#000000" : "#FFFFFF");
4
+ export default function determineBlackOrWhiteTextColor(c) {
5
+ try {
6
+ return Color(c).isLight() ? "#000000" : "#FFFFFF";
7
+ } catch (e) {
8
+ console.error("Error in color parsing:", e);
9
+ return "#000000"; // Fallback to black if color parsing fails
10
+ }
11
+ }