@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teselagen/ui",
3
- "version": "0.8.8",
3
+ "version": "0.9.1",
4
4
  "main": "./src/index.js",
5
5
  "type": "module",
6
6
  "exports": {
@@ -17,7 +17,7 @@
17
17
  "@dnd-kit/core": "^6.1.0",
18
18
  "@dnd-kit/modifiers": "^7.0.0",
19
19
  "@dnd-kit/sortable": "^8.0.0",
20
- "@teselagen/react-table": "6.10.16",
20
+ "@teselagen/react-table": "6.10.18",
21
21
  "classnames": "^2.3.2",
22
22
  "color": "^3.2.1",
23
23
  "copy-to-clipboard": "^3.3.1",
@@ -33,10 +33,10 @@ import getTextFromEl from "../utils/getTextFromEl";
33
33
  import rowClick, { finalizeSelection } from "./utils/rowClick";
34
34
  import { editCellHelper } from "./editCellHelper";
35
35
  import { getCellVal } from "./getCellVal";
36
- import { getCCDisplayName } from "./utils/queryParams";
37
36
  import { useDispatch } from "react-redux";
38
37
  import { change as _change } from "redux-form";
39
38
  import { RenderCell } from "./RenderCell";
39
+ import { getCCDisplayName } from "./utils/tableQueryParamsToHasuraClauses";
40
40
 
41
41
  dayjs.extend(localizedFormat);
42
42
 
@@ -104,7 +104,7 @@ const RenderColumnHeader = ({
104
104
  if (order && order.length) {
105
105
  order.forEach(order => {
106
106
  const orderField = order.replace("-", "");
107
- if (orderField === ccDisplayName) {
107
+ if (orderField === path) {
108
108
  if (orderField === order) {
109
109
  ordering = "asc";
110
110
  } else {
@@ -10,7 +10,7 @@ import {
10
10
  Popover,
11
11
  Switch
12
12
  } from "@blueprintjs/core";
13
- import { getCCDisplayName } from "./utils/queryParams";
13
+ import { getCCDisplayName } from "./utils/tableQueryParamsToHasuraClauses";
14
14
 
15
15
  const DisplayOptions = ({
16
16
  compact,
@@ -1,6 +1,6 @@
1
1
  import React, { useState } from "react";
2
2
  import { DateInput, DateRangeInput } from "@blueprintjs/datetime";
3
- import { camelCase } from "lodash-es";
3
+ import { camelCase, startCase } from "lodash-es";
4
4
  import classNames from "classnames";
5
5
  import {
6
6
  Menu,
@@ -349,43 +349,40 @@ function getFilterMenuItems(dataType) {
349
349
  let filterMenuItems = [];
350
350
  if (dataType === "string") {
351
351
  filterMenuItems = [
352
- "Contains",
353
- "Not Contains",
354
- "Starts With",
355
- "Ends With",
356
- "Is Exactly",
357
- "Regex",
358
- "In List",
359
- "Not In List",
360
- "Is Empty",
361
- "Not Empty"
352
+ "contains",
353
+ "notContains",
354
+ "startsWith",
355
+ "endsWith",
356
+ "isExactly",
357
+ "regex",
358
+ "inList",
359
+ "notInList",
360
+ "isEmpty",
361
+ "notEmpty"
362
362
  ];
363
363
  } else if (dataType === "lookup") {
364
364
  filterMenuItems = [
365
- "Contains",
366
- "Not Contains",
367
- "Starts With",
368
- "Ends With",
369
- "Is Exactly",
370
- "Regex"
365
+ "contains",
366
+ "notContains",
367
+ "startsWith",
368
+ "endsWith",
369
+ "isExactly",
370
+ "regex"
371
371
  ];
372
372
  } else if (dataType === "boolean") {
373
- filterMenuItems = ["True", "False"];
373
+ filterMenuItems = ["true", "false"];
374
374
  } else if (dataType === "number" || dataType === "integer") {
375
- // else if (dataType === "lookup") {
376
- // filterMenuItems = ["None"];
377
- // }
378
375
  filterMenuItems = [
379
- "Greater Than",
380
- "Less Than",
381
- "In Range",
382
- "Outside Range",
383
- "Equal To",
384
- "In List",
385
- "Not In List"
376
+ "greaterThan",
377
+ "lessThan",
378
+ "inRange",
379
+ "outsideRange",
380
+ "equalTo",
381
+ "inList",
382
+ "notInList"
386
383
  ];
387
384
  } else if (dataType === "timestamp") {
388
- filterMenuItems = ["Is Between", "Not Between", "Is Before", "Is After"];
385
+ filterMenuItems = ["isBetween", "notBetween", "isBefore", "isAfter"];
389
386
  }
390
- return filterMenuItems;
387
+ return filterMenuItems.map(item => startCase(item));
391
388
  }
@@ -43,7 +43,7 @@ import {
43
43
  import { arrayMove } from "@dnd-kit/sortable";
44
44
  import classNames from "classnames";
45
45
  import scrollIntoView from "dom-scroll-into-view";
46
- import ReactTable from "@teselagen/react-table";
46
+ import ReactTable, { VIRTUALIZE_CUTOFF_LENGTH } from "@teselagen/react-table";
47
47
  import immer, { produceWithPatches, enablePatches, applyPatches } from "immer";
48
48
  import papaparse from "papaparse";
49
49
  import { useDispatch, useSelector } from "react-redux";
@@ -92,10 +92,9 @@ import { viewColumn, openColumn, multiViewColumn } from "./viewColumn";
92
92
  import convertSchema from "./utils/convertSchema";
93
93
  import TableFormTrackerContext from "./TableFormTrackerContext";
94
94
  import {
95
- getCCDisplayName,
96
- getCurrentParamsFromUrl,
97
95
  getQueryParams,
98
96
  makeDataTableHandlers,
97
+ getCurrentParamsFromUrl,
99
98
  setCurrentParamsOnUrl
100
99
  } from "./utils/queryParams";
101
100
  import { useColumns } from "./Columns";
@@ -103,6 +102,7 @@ import { formValueSelector, change as _change } from "redux-form";
103
102
  import { throwFormError } from "../throwFormError";
104
103
  import { isObservableArray, toJS } from "mobx";
105
104
  import { isBeingCalledExcessively } from "../utils/isBeingCalledExcessively";
105
+ import { getCCDisplayName } from "./utils/tableQueryParamsToHasuraClauses";
106
106
 
107
107
  enablePatches();
108
108
  const IS_LINUX = window.navigator.platform.toLowerCase().search("linux") > -1;
@@ -141,6 +141,7 @@ const DataTable = ({
141
141
  );
142
142
  const tableRef = useRef();
143
143
  const alreadySelected = useRef(false);
144
+ const [noVirtual, setNoVirtual] = useState(false);
144
145
  const [onlyShowRowsWErrors, setOnlyShowRowsWErrors] = useState(false);
145
146
  const [entitiesUndoRedoStack, setEntitiesUndoRedoStack] = useState({
146
147
  currentVersion: 0
@@ -337,16 +338,6 @@ const DataTable = ({
337
338
 
338
339
  const queryParams = useMemo(() => {
339
340
  if (!isTableParamsConnected) {
340
- const additionalFilterToUse =
341
- typeof props.additionalFilter === "function"
342
- ? props.additionalFilter
343
- : () => props.additionalFilter;
344
-
345
- const additionalOrFilterToUse =
346
- typeof props.additionalOrFilter === "function"
347
- ? props.additionalOrFilter
348
- : () => props.additionalOrFilter;
349
-
350
341
  return getQueryParams({
351
342
  doNotCoercePageSize,
352
343
  currentParams,
@@ -356,8 +347,7 @@ const DataTable = ({
356
347
  schema: convertedSchema,
357
348
  isInfinite,
358
349
  isLocalCall,
359
- additionalFilter: additionalFilterToUse,
360
- additionalOrFilter: additionalOrFilterToUse,
350
+ additionalFilter: props.additionalFilter,
361
351
  noOrderError: props.noOrderError,
362
352
  isCodeModel: props.isCodeModel,
363
353
  ownProps: props
@@ -614,7 +604,7 @@ const DataTable = ({
614
604
  : !val;
615
605
  });
616
606
  }
617
- if (noValsForField) {
607
+ if (noValsForField && entities.length) {
618
608
  return {
619
609
  ...field,
620
610
  isHidden: true,
@@ -1156,70 +1146,94 @@ const DataTable = ({
1156
1146
  updateValidation
1157
1147
  ]);
1158
1148
 
1159
- const handleCopySelectedCells = useCallback(
1160
- e => {
1161
- // if the current selection is consecutive cells then copy with
1162
- // tabs between. if not then just select primary selected cell
1163
- if (isEmpty(selectedCells)) return;
1164
- const pathToIndex = getFieldPathToIndex(schema);
1165
- const entityIdToEntity = getEntityIdToEntity(entities);
1166
- const selectionGrid = [];
1167
- let firstRowIndex;
1168
- let firstCellIndex;
1169
- Object.keys(selectedCells).forEach(key => {
1170
- const [rowId, path] = key.split(":");
1171
- const eInfo = entityIdToEntity[rowId];
1172
- if (eInfo) {
1173
- if (firstRowIndex === undefined || eInfo.i < firstRowIndex) {
1174
- firstRowIndex = eInfo.i;
1175
- }
1176
- if (!selectionGrid[eInfo.i]) {
1177
- selectionGrid[eInfo.i] = [];
1178
- }
1179
- const cellIndex = pathToIndex[path];
1180
- if (firstCellIndex === undefined || cellIndex < firstCellIndex) {
1181
- firstCellIndex = cellIndex;
1182
- }
1183
- selectionGrid[eInfo.i][cellIndex] = true;
1149
+ const waitUntilAllRowsAreRendered = useCallback(() => {
1150
+ return new Promise(resolve => {
1151
+ const interval = setInterval(() => {
1152
+ const allRowEls =
1153
+ tableRef.current?.tableRef?.querySelectorAll(".rt-tr-group");
1154
+ if (allRowEls?.length === entities.length) {
1155
+ clearInterval(interval);
1156
+ resolve();
1184
1157
  }
1185
- });
1186
- if (firstRowIndex === undefined) return;
1187
- const allRows = getAllRows(e);
1188
- let fullCellText = "";
1189
- const fullJson = [];
1190
- times(selectionGrid.length, i => {
1191
- const row = selectionGrid[i];
1192
- if (fullCellText) {
1193
- fullCellText += "\n";
1158
+ }, 50);
1159
+ });
1160
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1161
+ }, []);
1162
+
1163
+ const handleCopySelectedCells = useCallback(async () => {
1164
+ // if the current selection is consecutive cells then copy with
1165
+ // tabs between. if not then just select primary selected cell
1166
+ if (isEmpty(selectedCells)) return;
1167
+
1168
+ // Temporarily disable virtualization for large tables
1169
+ if (entities.length > VIRTUALIZE_CUTOFF_LENGTH) {
1170
+ setNoVirtual(true);
1171
+ await waitUntilAllRowsAreRendered();
1172
+ }
1173
+
1174
+ const pathToIndex = getFieldPathToIndex(schema);
1175
+ const entityIdToEntity = getEntityIdToEntity(entities);
1176
+ const selectionGrid = [];
1177
+ let firstRowIndex;
1178
+ let firstCellIndex;
1179
+ Object.keys(selectedCells).forEach(key => {
1180
+ const [rowId, path] = key.split(":");
1181
+ const eInfo = entityIdToEntity[rowId];
1182
+ if (eInfo) {
1183
+ if (firstRowIndex === undefined || eInfo.i < firstRowIndex) {
1184
+ firstRowIndex = eInfo.i;
1194
1185
  }
1195
- if (!row) {
1196
- return;
1197
- } else {
1198
- const jsonRow = [];
1199
- // ignore header
1200
- let [rowCopyText, json] = getRowCopyText(allRows[i + 1]);
1201
- rowCopyText = rowCopyText.split("\t");
1202
- times(row.length, i => {
1203
- const cell = row[i];
1204
- if (cell) {
1205
- fullCellText += rowCopyText[i];
1206
- jsonRow.push(json[i]);
1207
- }
1208
- if (i !== row.length - 1 && i >= firstCellIndex)
1209
- fullCellText += "\t";
1210
- });
1211
- fullJson.push(jsonRow);
1186
+ if (!selectionGrid[eInfo.i]) {
1187
+ selectionGrid[eInfo.i] = [];
1212
1188
  }
1213
- });
1214
- if (!fullCellText) return window.toastr.warning("No text to copy");
1189
+ const cellIndex = pathToIndex[path];
1190
+ if (firstCellIndex === undefined || cellIndex < firstCellIndex) {
1191
+ firstCellIndex = cellIndex;
1192
+ }
1193
+ selectionGrid[eInfo.i][cellIndex] = true;
1194
+ }
1195
+ });
1196
+ if (firstRowIndex === undefined) return;
1197
+ const allRows = getAllRows(tableRef);
1198
+ let fullCellText = "";
1199
+ const fullJson = [];
1200
+ times(selectionGrid.length, i => {
1201
+ const row = selectionGrid[i];
1202
+ if (fullCellText) {
1203
+ fullCellText += "\n";
1204
+ }
1205
+ if (!row) {
1206
+ return;
1207
+ } else {
1208
+ const jsonRow = [];
1209
+ // ignore header
1210
+ let [rowCopyText, json] = getRowCopyText(allRows[i + 1]);
1211
+ rowCopyText = rowCopyText.split("\t");
1212
+ times(row.length, i => {
1213
+ const cell = row[i];
1214
+ if (cell) {
1215
+ fullCellText += rowCopyText[i];
1216
+ jsonRow.push(json[i]);
1217
+ }
1218
+ if (i !== row.length - 1 && i >= firstCellIndex) fullCellText += "\t";
1219
+ });
1220
+ fullJson.push(jsonRow);
1221
+ }
1222
+ });
1223
+ if (!fullCellText) return window.toastr.warning("No text to copy");
1215
1224
 
1216
- handleCopyHelper(fullCellText, fullJson, "Selected cells copied");
1217
- },
1218
- [entities, selectedCells, schema]
1219
- );
1225
+ handleCopyHelper(fullCellText, fullJson, "Selected cells copied");
1226
+
1227
+ // Re-enable virtualization if it was disabled
1228
+ setNoVirtual(false);
1229
+ }, [entities, selectedCells, schema, waitUntilAllRowsAreRendered]);
1220
1230
 
1221
1231
  const handleCopySelectedRows = useCallback(
1222
- (selectedRecords, e) => {
1232
+ async selectedRecords => {
1233
+ if (entities.length > VIRTUALIZE_CUTOFF_LENGTH) {
1234
+ setNoVirtual(true);
1235
+ await waitUntilAllRowsAreRendered();
1236
+ }
1223
1237
  const idToIndex = entities.reduce((acc, e, i) => {
1224
1238
  acc[e.id || e.code] = i;
1225
1239
  return acc;
@@ -1234,10 +1248,10 @@ const DataTable = ({
1234
1248
  if (!rowNumbersToCopy.length) return;
1235
1249
  rowNumbersToCopy.unshift(0); //add in the header row
1236
1250
  try {
1237
- const allRowEls = getAllRows(e);
1251
+ const allRowEls = getAllRows(tableRef);
1238
1252
  if (!allRowEls) return;
1239
1253
  const rowEls = rowNumbersToCopy.map(i => allRowEls[i]);
1240
-
1254
+ if (window.Cypress) window.Cypress.__copiedRowsLength = rowEls.length;
1241
1255
  handleCopyRows(rowEls, {
1242
1256
  onFinishMsg: "Selected rows copied"
1243
1257
  });
@@ -1245,8 +1259,9 @@ const DataTable = ({
1245
1259
  console.error(`error:`, error);
1246
1260
  window.toastr.error("Error copying rows.");
1247
1261
  }
1262
+ setNoVirtual(false);
1248
1263
  },
1249
- [entities]
1264
+ [entities, waitUntilAllRowsAreRendered]
1250
1265
  );
1251
1266
 
1252
1267
  const handleCopyHotkey = useCallback(
@@ -2107,7 +2122,7 @@ const DataTable = ({
2107
2122
  <MenuItem
2108
2123
  key="copyColumn"
2109
2124
  onClick={() => {
2110
- handleCopyColumn(e, cellWrapper);
2125
+ handleCopyColumn(tableRef, cellWrapper);
2111
2126
  }}
2112
2127
  text="Column"
2113
2128
  />
@@ -2117,7 +2132,7 @@ const DataTable = ({
2117
2132
  <MenuItem
2118
2133
  key="copyColumnSelected"
2119
2134
  onClick={() => {
2120
- handleCopyColumn(e, cellWrapper, selectedRecords);
2135
+ handleCopyColumn(tableRef, cellWrapper, selectedRecords);
2121
2136
  }}
2122
2137
  text="Column (Selected)"
2123
2138
  />
@@ -2157,7 +2172,7 @@ const DataTable = ({
2157
2172
  <MenuItem
2158
2173
  key="copyFullTableRows"
2159
2174
  onClick={() => {
2160
- handleCopyTable(e);
2175
+ handleCopyTable(tableRef);
2161
2176
  // loop through each cell in the row
2162
2177
  }}
2163
2178
  text="Table"
@@ -2765,6 +2780,7 @@ const DataTable = ({
2765
2780
  <ReactTable
2766
2781
  data={filteredEnts}
2767
2782
  ref={tableRef}
2783
+ noVirtual={noVirtual}
2768
2784
  className={classNames({
2769
2785
  isCellEditable,
2770
2786
  "tg-table-loading": isLoading,
@@ -2846,7 +2862,8 @@ const DataTable = ({
2846
2862
  resizePersist,
2847
2863
  resized,
2848
2864
  rowsToShow,
2849
- style
2865
+ style,
2866
+ noVirtual
2850
2867
  ]
2851
2868
  );
2852
2869
 
@@ -3089,7 +3106,7 @@ const DataTable = ({
3089
3106
  try {
3090
3107
  const allEntities = await safeQuery(fragment, {
3091
3108
  variables: {
3092
- filter: variables.filter,
3109
+ where: variables.where,
3093
3110
  sort: variables.sort
3094
3111
  },
3095
3112
  canCancel: true
@@ -3163,8 +3180,8 @@ const DataTable = ({
3163
3180
  )}
3164
3181
  </div>
3165
3182
  <Button
3166
- onClick={e => {
3167
- handleCopyTable(e, { isDownload: true });
3183
+ onClick={() => {
3184
+ handleCopyTable(tableRef, { isDownload: true });
3168
3185
  }}
3169
3186
  data-tip="Download Table as CSV"
3170
3187
  minimal
@@ -246,7 +246,7 @@ body:not(.drag-active)
246
246
  display: flex;
247
247
  flex-wrap: wrap;
248
248
  align-items: center;
249
- margin-top: 20px;
249
+ margin-top: 10px;
250
250
  }
251
251
 
252
252
  .ReactTable {