@teselagen/ui 0.8.6 → 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 (42) hide show
  1. package/DataTable/EditabelCell.d.ts +10 -0
  2. package/DataTable/defaultProps.d.ts +43 -0
  3. package/DataTable/utils/computePresets.d.ts +1 -0
  4. package/DataTable/utils/getAllRows.d.ts +1 -1
  5. package/DataTable/utils/handleCopyColumn.d.ts +1 -1
  6. package/DataTable/utils/handleCopyTable.d.ts +1 -1
  7. package/DataTable/utils/initializeHasuraWhereAndFilter.d.ts +0 -1
  8. package/DataTable/utils/queryParams.d.ts +16 -12
  9. package/DataTable/utils/tableQueryParamsToHasuraClauses.d.ts +1 -1
  10. package/README.md +1 -1
  11. package/index.cjs.js +1139 -1040
  12. package/index.d.ts +2 -0
  13. package/index.es.js +1139 -1040
  14. package/package.json +2 -2
  15. package/src/DataTable/Columns.js +2 -2
  16. package/src/DataTable/DisplayOptions.js +1 -1
  17. package/src/DataTable/EditabelCell.js +55 -0
  18. package/src/DataTable/FilterAndSortMenu.js +27 -30
  19. package/src/DataTable/defaultProps.js +45 -0
  20. package/src/DataTable/index.js +101 -84
  21. package/src/DataTable/style.css +1 -1
  22. package/src/DataTable/utils/computePresets.js +42 -0
  23. package/src/DataTable/utils/filterLocalEntitiesToHasura.js +128 -8
  24. package/src/DataTable/utils/filterLocalEntitiesToHasura.test.js +719 -21
  25. package/src/DataTable/utils/getAllRows.js +2 -6
  26. package/src/DataTable/utils/handleCopyColumn.js +2 -2
  27. package/src/DataTable/utils/handleCopyTable.js +2 -2
  28. package/src/DataTable/utils/initializeHasuraWhereAndFilter.js +1 -12
  29. package/src/DataTable/utils/queryParams.js +153 -770
  30. package/src/DataTable/utils/tableQueryParamsToHasuraClauses.js +185 -168
  31. package/src/DataTable/utils/tableQueryParamsToHasuraClauses.test.js +50 -11
  32. package/src/DataTable/utils/withTableParams.js +3 -16
  33. package/src/ExcelCell.js +38 -0
  34. package/src/FormComponents/Uploader.js +5 -1
  35. package/src/FormComponents/index.js +2 -2
  36. package/src/TgSelect/index.js +15 -0
  37. package/src/autoTooltip.js +1 -0
  38. package/src/index.js +2 -0
  39. package/src/utils/determineBlackOrWhiteTextColor.js +8 -1
  40. package/style.css +10537 -0
  41. package/ui.css +1 -1
  42. package/utils/determineBlackOrWhiteTextColor.d.ts +1 -2
@@ -11,44 +11,57 @@ export function tableQueryParamsToHasuraClauses({
11
11
  }) {
12
12
  const ccFields = getFieldsMappedByCCDisplayName(schema);
13
13
  let where = {};
14
- const order_by = {};
14
+ const order_by = [];
15
15
  const limit = pageSize || 25;
16
16
  const offset = page && pageSize ? (page - 1) * pageSize : 0;
17
17
 
18
18
  if (searchTerm) {
19
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
+
20
26
  schema.fields.forEach(field => {
21
27
  const { type, path, searchDisabled } = field;
28
+ if (uniqueFieldsByPath[path]) return; // Skip if already added
29
+ uniqueFieldsByPath[path] = true;
22
30
  if (searchDisabled || field.filterDisabled || type === "color") return;
23
- const filterValue = searchTerm; // No cleaning needed here, we're using _ilike
24
31
 
25
- if (type === "string" || type === "lookup") {
26
- const o = set({}, path, { _ilike: `%${filterValue}%` });
27
- searchTermFilters.push(o);
28
- } else if (type === "boolean") {
29
- let regex;
30
- try {
31
- regex = new RegExp("^" + searchTerm, "ig");
32
- } catch (error) {
33
- //ignore
34
- }
35
- if (regex) {
36
- if ("true".replace(regex, "") !== "true") {
37
- const o = set({}, path, { _eq: true });
38
- searchTermFilters.push(o);
39
- } else if ("false".replace(regex, "") !== "false") {
40
- const o = set({}, path, { _eq: false });
41
- searchTermFilters.push(o);
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
+ }
42
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);
43
61
  }
44
- } else if (
45
- (type === "number" || type === "integer") &&
46
- !isNaN(filterValue)
47
- ) {
48
- const o = set({}, path, { _eq: parseFloat(filterValue) });
49
- searchTermFilters.push(o);
50
- }
62
+ });
51
63
  });
64
+
52
65
  if (searchTermFilters.length > 0) {
53
66
  if (Object.keys(where).length > 0) {
54
67
  where = { _and: [where, { _or: searchTermFilters }] };
@@ -59,157 +72,161 @@ export function tableQueryParamsToHasuraClauses({
59
72
  }
60
73
 
61
74
  if (filters && filters.length > 0) {
62
- const filterClauses = filters
63
- .map(filter => {
64
- let { selectedFilter, filterOn, filterValue } = filter;
65
- const fieldSchema = ccFields[filterOn] || {};
75
+ const filterClauses = filters.map(filter => {
76
+ let { selectedFilter, filterOn, filterValue } = filter;
77
+ const fieldSchema = ccFields[filterOn] || {};
66
78
 
67
- const { path, reference, type } = fieldSchema;
68
- let stringFilterValue =
69
- filterValue && filterValue.toString
70
- ? filterValue.toString()
71
- : filterValue;
72
- if (stringFilterValue === false) {
73
- // we still want to be able to search for the string "false" which will get parsed to false
74
- stringFilterValue = "false";
75
- } else {
76
- stringFilterValue = stringFilterValue || "";
77
- }
78
- const arrayFilterValue = Array.isArray(filterValue)
79
- ? filterValue
80
- : stringFilterValue.split(";");
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(";");
81
96
 
82
- if (type === "number" || type === "integer") {
83
- filterValue = Array.isArray(filterValue)
84
- ? filterValue.map(val => Number(val))
85
- : Number(filterValue);
86
- }
97
+ if (type === "number" || type === "integer") {
98
+ filterValue = Array.isArray(filterValue)
99
+ ? filterValue.map(val => Number(val))
100
+ : Number(filterValue);
101
+ }
87
102
 
88
- if (fieldSchema.normalizeFilter) {
89
- filterValue = fieldSchema.normalizeFilter(
90
- filterValue,
91
- selectedFilter,
92
- filterOn
93
- );
94
- }
103
+ if (fieldSchema.normalizeFilter) {
104
+ filterValue = fieldSchema.normalizeFilter(
105
+ filterValue,
106
+ selectedFilter,
107
+ filterOn
108
+ );
109
+ }
95
110
 
96
- if (reference) {
97
- filterOn = reference.sourceField;
98
- } else {
99
- filterOn = path || filterOn;
100
- }
101
- switch (selectedFilter) {
102
- case "none":
103
- return {};
104
- case "startsWith":
105
- return { [filterOn]: { _ilike: `${filterValue}%` } };
106
- case "endsWith":
107
- return { [filterOn]: { _ilike: `%${filterValue}` } };
108
- case "contains":
109
- return { [filterOn]: { _ilike: `%${filterValue}%` } };
110
- case "notContains":
111
- return { [filterOn]: { _not_ilike: `%${filterValue}%` } };
112
- case "isExactly":
113
- return { [filterOn]: { _eq: filterValue } };
114
- case "isEmpty":
115
- return {
116
- _or: [
117
- { [filterOn]: { _eq: "" } },
118
- { [filterOn]: { _is_null: true } }
119
- ]
120
- };
121
- case "notEmpty":
122
- return {
123
- _and: [
124
- { [filterOn]: { _neq: "" } },
125
- { [filterOn]: { _is_null: false } }
126
- ]
127
- };
128
- case "inList":
129
- return { [filterOn]: { _in: filterValue } };
130
- case "notInList":
131
- return { [filterOn]: { _nin: filterValue } };
132
- case "true":
133
- return { [filterOn]: { _eq: true } };
134
- case "false":
135
- return { [filterOn]: { _eq: false } };
136
- case "dateIs":
137
- return { [filterOn]: { _eq: filterValue } };
138
- case "notBetween":
139
- return {
140
- _or: [
141
- {
142
- [filterOn]: {
143
- _lt: new Date(arrayFilterValue[0])
144
- }
145
- },
146
- {
147
- [filterOn]: {
148
- _gt: new Date(
149
- new Date(arrayFilterValue[1]).setHours(23, 59)
150
- )
151
- }
152
- }
153
- ]
154
- };
155
- case "isBetween":
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
156
133
  return {
157
- [filterOn]: {
158
- _gte: new Date(arrayFilterValue[0]),
159
- _lte: new Date(new Date(arrayFilterValue[1]).setHours(23, 59))
134
+ _not: {
135
+ [filterOn.split(".")[0]]: {}
160
136
  }
161
137
  };
162
- case "isBefore":
163
- return { [filterOn]: { _lt: new Date(filterValue) } };
164
- case "isAfter":
165
- return { [filterOn]: { _gt: new Date(filterValue) } };
166
- case "greaterThan":
167
- return { [filterOn]: { _gt: parseFloat(filterValue) } };
168
- case "lessThan":
169
- return { [filterOn]: { _lt: parseFloat(filterValue) } };
170
- case "inRange":
171
- return {
172
- [filterOn]: {
173
- _gte: parseFloat(arrayFilterValue[0]),
174
- _lte: parseFloat(arrayFilterValue[1])
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
+ }
175
174
  }
176
- };
177
- case "outsideRange":
178
- return {
179
- _or: [
180
- {
181
- [filterOn]: {
182
- _lt: parseFloat(arrayFilterValue[0])
183
- }
184
- },
185
- {
186
- [filterOn]: {
187
- _gt: parseFloat(arrayFilterValue[1])
188
- }
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])
189
210
  }
190
- ]
191
- };
192
- case "equalTo":
193
- return {
194
- [filterOn]: {
195
- _eq:
196
- type === "number" || type === "integer"
197
- ? parseFloat(filterValue)
198
- : filterValue
199
211
  }
200
- };
201
- case "regex":
202
- return { [filterOn]: { _regex: filterValue } };
203
- default:
204
- console.warn(`Unsupported filter type: ${selectedFilter}`);
205
- return {};
206
- }
207
- })
208
- .map(filter => {
209
- const o = {};
210
- set(o, Object.keys(filter)[0], filter[Object.keys(filter)[0]]);
211
- return o;
212
- });
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
+ });
213
230
 
214
231
  if (filterClauses.length > 0) {
215
232
  if (Object.keys(where).length > 0) {
@@ -224,7 +241,7 @@ export function tableQueryParamsToHasuraClauses({
224
241
  order.forEach(item => {
225
242
  const field = item.startsWith("-") ? item.substring(1) : item;
226
243
  const direction = item.startsWith("-") ? "desc" : "asc";
227
- order_by[field] = direction;
244
+ order_by.push({ [field]: direction });
228
245
  });
229
246
  }
230
247
 
@@ -14,7 +14,7 @@ describe("tableQueryParamsToHasuraClauses", () => {
14
14
  const result = tableQueryParamsToHasuraClauses({});
15
15
  expect(result).toEqual({
16
16
  where: {},
17
- order_by: {},
17
+ order_by: [],
18
18
  limit: 25,
19
19
  offset: 0
20
20
  });
@@ -24,7 +24,7 @@ describe("tableQueryParamsToHasuraClauses", () => {
24
24
  const result = tableQueryParamsToHasuraClauses({ page: 2, pageSize: 10 });
25
25
  expect(result).toEqual({
26
26
  where: {},
27
- order_by: {},
27
+ order_by: [],
28
28
  limit: 10,
29
29
  offset: 10
30
30
  });
@@ -39,7 +39,46 @@ describe("tableQueryParamsToHasuraClauses", () => {
39
39
  where: {
40
40
  _or: [{ name: { _ilike: "%test%" } }, { email: { _ilike: "%test%" } }]
41
41
  },
42
- order_by: {},
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: [],
43
82
  limit: 25,
44
83
  offset: 0
45
84
  });
@@ -58,7 +97,7 @@ describe("tableQueryParamsToHasuraClauses", () => {
58
97
  { email: { _ilike: "%30%" } }
59
98
  ]
60
99
  },
61
- order_by: {},
100
+ order_by: [],
62
101
  limit: 25,
63
102
  offset: 0
64
103
  });
@@ -77,7 +116,7 @@ describe("tableQueryParamsToHasuraClauses", () => {
77
116
  { email: { _ilike: "%true%" } }
78
117
  ]
79
118
  },
80
- order_by: {},
119
+ order_by: [],
81
120
  limit: 25,
82
121
  offset: 0
83
122
  });
@@ -92,7 +131,7 @@ describe("tableQueryParamsToHasuraClauses", () => {
92
131
  where: {
93
132
  _or: [{ name: { _ilike: "%test%" } }, { email: { _ilike: "%test%" } }]
94
133
  },
95
- order_by: {},
134
+ order_by: [],
96
135
  limit: 25,
97
136
  offset: 0
98
137
  });
@@ -110,7 +149,7 @@ describe("tableQueryParamsToHasuraClauses", () => {
110
149
  });
111
150
  expect(result).toEqual({
112
151
  where: { _and: [{ name: { _ilike: "%test%" } }] },
113
- order_by: {},
152
+ order_by: [],
114
153
  limit: 25,
115
154
  offset: 0
116
155
  });
@@ -125,7 +164,7 @@ describe("tableQueryParamsToHasuraClauses", () => {
125
164
  });
126
165
  expect(result).toEqual({
127
166
  where: { _and: [{ age: { _eq: 30 } }] },
128
- order_by: {},
167
+ order_by: [],
129
168
  limit: 25,
130
169
  offset: 0
131
170
  });
@@ -135,7 +174,7 @@ describe("tableQueryParamsToHasuraClauses", () => {
135
174
  const result = tableQueryParamsToHasuraClauses({ order: ["name", "-age"] });
136
175
  expect(result).toEqual({
137
176
  where: {},
138
- order_by: { name: "asc", age: "desc" },
177
+ order_by: [{ name: "asc" }, { age: "desc" }],
139
178
  limit: 25,
140
179
  offset: 0
141
180
  });
@@ -168,7 +207,7 @@ describe("tableQueryParamsToHasuraClauses", () => {
168
207
  { age: { _gt: 30 } }
169
208
  ]
170
209
  },
171
- order_by: { name: "asc" },
210
+ order_by: [{ name: "asc" }],
172
211
  limit: 10,
173
212
  offset: 10
174
213
  });
@@ -198,7 +237,7 @@ describe("tableQueryParamsToHasuraClauses", () => {
198
237
  { age: { _gt: 30 } }
199
238
  ]
200
239
  },
201
- order_by: {},
240
+ order_by: [],
202
241
  limit: 25,
203
242
  offset: 0
204
243
  });
@@ -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,
@@ -0,0 +1,38 @@
1
+ /* eslint react/jsx-no-bind: 0 */
2
+ import { Popover } from "@blueprintjs/core";
3
+ import React, { useState } from "react";
4
+
5
+ export default function ExcelCell() {
6
+ const [v, setV] = useState("");
7
+ const [isPopoverOpen, setIsPopoverOpen] = useState(false);
8
+ return (
9
+ <Popover
10
+ onClose={() => {
11
+ setIsPopoverOpen(false);
12
+ }}
13
+ isOpen={isPopoverOpen}
14
+ content={<div>Sum</div>}
15
+ >
16
+ <div
17
+ style={{
18
+ border: "1px solid #ccc",
19
+ padding: 5,
20
+ width: 100,
21
+ height: 30
22
+ }}
23
+ contentEditable
24
+ onInput={e => {
25
+ const text = e.currentTarget.textContent;
26
+
27
+ if (text === "=") {
28
+ // open a popover
29
+ setIsPopoverOpen(true);
30
+ }
31
+ setV(text);
32
+ }}
33
+ >
34
+ {v}
35
+ </div>
36
+ </Popover>
37
+ );
38
+ }
@@ -788,7 +788,11 @@ const Uploader = ({
788
788
  .join(", ")
789
789
  : undefined
790
790
  }
791
- onDrop={async (_acceptedFiles, rejectedFiles) => {
791
+ onDrop={async (_acceptedFiles, rejectedFiles, e) => {
792
+ const parentDropzone = e.target.closest(".tg-dropzone");
793
+ if (parentDropzone) {
794
+ parentDropzone.blur();
795
+ }
792
796
  let acceptedFiles = [];
793
797
  for (const file of _acceptedFiles) {
794
798
  if ((validateAgainstSchema || autoUnzip) && isZipFile(file)) {
@@ -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
  );