@vuu-ui/vuu-table-extras 4.0.0-alpha.20 → 4.0.0-alpha.22
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.
- package/package.json +16 -17
- package/src/cell-renderers/pct-progress-cell/PctProgressCell.css.js +2 -2
- package/src/cell-renderers/progress-cell/ProgressCell.css.js +2 -2
- package/src/column-expression-input/ColumnExpressionInput.css.js +5 -5
- package/src/column-expression-input/theme.js +1 -1
- package/src/column-settings-panel/ColumnNameLabel.css.js +1 -1
- package/src/csv-export/export-utils.js +13 -4
- package/src/csv-upload/CsvUpload.css.js +7 -7
- package/src/csv-upload/parse/csv-upload-utils.js +47 -2
- package/src/csv-upload/session/index.js +2 -0
- package/src/csv-upload/session/useImportSchema.js +44 -0
- package/src/csv-upload/session/useImportSession.js +204 -0
- package/src/csv-upload/useCsvUpload.js +45 -188
- package/src/freeze-control/FreezeControl.css.js +4 -4
- package/src/freeze-control/FrozenBanner.css.js +2 -2
- package/types/calculated-column/CalculatedColumnPanel.d.ts +1 -1
- package/types/cell-renderers/background-cell/BackgroundCell.d.ts +1 -1
- package/types/cell-renderers/background-cell/BackgroundCellConfigurationEditor.d.ts +1 -1
- package/types/cell-renderers/button-cell/IconButtonCell.d.ts +1 -1
- package/types/cell-renderers/dropdown-cell/DropdownCell.d.ts +1 -1
- package/types/cell-renderers/lookup-cell/LookupCell.d.ts +1 -1
- package/types/cell-renderers/pct-progress-cell/PctProgressCell.d.ts +1 -1
- package/types/column-expression-input/ColumnExpressionInput.d.ts +1 -1
- package/types/column-expression-input/column-language-parser/test.d.mts +1 -1
- package/types/column-formatting-settings/BaseNumericFormattingSettings.d.ts +1 -1
- package/types/column-formatting-settings/ColumnFormattingPanel.d.ts +1 -1
- package/types/column-formatting-settings/LongTypeFormattingSettings.d.ts +1 -2
- package/types/column-menu/ColumnMenu.d.ts +1 -1
- package/types/column-picker/ColumnPickerAction.d.ts +1 -1
- package/types/column-settings-panel/ColumnNameLabel.d.ts +1 -1
- package/types/column-settings-panel/ColumnSettingsPanel.d.ts +1 -1
- package/types/csv-upload/CsvUpload.d.ts +1 -1
- package/types/csv-upload/data-upload-preview/DataUploadPreview.d.ts +1 -1
- package/types/csv-upload/parse/csv-upload-utils.d.ts +9 -0
- package/types/csv-upload/session/index.d.ts +2 -0
- package/types/csv-upload/session/useImportSchema.d.ts +13 -0
- package/types/csv-upload/session/useImportSession.d.ts +26 -0
- package/types/csv-upload/useCsvUpload.d.ts +1 -1
- package/types/datasource-stats/DatasourceStats.d.ts +1 -1
- package/types/freeze-control/FreezeControl.d.ts +1 -1
- package/types/freeze-control/FrozenBanner.d.ts +1 -1
- package/types/inline-add-row/InlineAddRow.d.ts +1 -1
- package/types/tabbed-table-config-panel/TabbedTableConfigPanel.d.ts +1 -1
- package/types/tabbed-table-config-panel/TabbedTableSettingsAction.d.ts +1 -1
- package/types/table-footer/TableFooter.d.ts +2 -2
- package/types/table-provider/TableProvider.d.ts +1 -1
- package/types/table-settings-panel/TableSettingsPanel.d.ts +1 -1
- package/types/src/calculated-column/CalculatedColumnPanel.d.ts +0 -12
- package/types/src/calculated-column/useCalculatedColumnPanel.d.ts +0 -10
- package/types/src/calculated-column/useEditCalculatedColumn.d.ts +0 -13
- package/types/src/cell-edit-validators/CaseValidator.d.ts +0 -2
- package/types/src/cell-edit-validators/PatternValidator.d.ts +0 -2
- package/types/src/cell-edit-validators/index.d.ts +0 -2
- package/types/src/cell-renderers/background-cell/BackgroundCell.d.ts +0 -2
- package/types/src/cell-renderers/background-cell/BackgroundCellConfigurationEditor.d.ts +0 -2
- package/types/src/cell-renderers/background-cell/index.d.ts +0 -2
- package/types/src/cell-renderers/background-cell/useDirection.d.ts +0 -3
- package/types/src/cell-renderers/button-cell/IconButtonCell.d.ts +0 -6
- package/types/src/cell-renderers/dropdown-cell/DropdownCell.d.ts +0 -2
- package/types/src/cell-renderers/dropdown-cell/index.d.ts +0 -1
- package/types/src/cell-renderers/index.d.ts +0 -6
- package/types/src/cell-renderers/lookup-cell/LookupCell.d.ts +0 -2
- package/types/src/cell-renderers/lookup-cell/index.d.ts +0 -1
- package/types/src/cell-renderers/pct-progress-cell/PctProgressCell.d.ts +0 -2
- package/types/src/cell-renderers/pct-progress-cell/index.d.ts +0 -1
- package/types/src/cell-renderers/progress-cell/ProgressCell.d.ts +0 -1
- package/types/src/cell-renderers/progress-cell/index.d.ts +0 -1
- package/types/src/column-expression-input/ColumnExpressionInput.d.ts +0 -10
- package/types/src/column-expression-input/column-function-descriptors.d.ts +0 -15
- package/types/src/column-expression-input/column-language-parser/ColumnExpressionLanguage.d.ts +0 -2
- package/types/src/column-expression-input/column-language-parser/ColumnExpressionTreeWalker.d.ts +0 -57
- package/types/src/column-expression-input/column-language-parser/column-expression-parse-utils.d.ts +0 -5
- package/types/src/column-expression-input/column-language-parser/generated/column-parser.d.ts +0 -2
- package/types/src/column-expression-input/column-language-parser/generated/column-parser.terms.d.ts +0 -1
- package/types/src/column-expression-input/column-language-parser/index.d.ts +0 -3
- package/types/src/column-expression-input/column-language-parser/test.d.mts +0 -1
- package/types/src/column-expression-input/functionDocInfo.d.ts +0 -2
- package/types/src/column-expression-input/highlighting.d.ts +0 -1
- package/types/src/column-expression-input/index.d.ts +0 -4
- package/types/src/column-expression-input/theme.d.ts +0 -1
- package/types/src/column-expression-input/useColumnAutoComplete.d.ts +0 -6
- package/types/src/column-expression-input/useColumnExpressionEditor.d.ts +0 -25
- package/types/src/column-expression-input/useColumnExpressionSuggestionProvider.d.ts +0 -8
- package/types/src/column-formatting-settings/BaseNumericFormattingSettings.d.ts +0 -2
- package/types/src/column-formatting-settings/ColumnFormattingPanel.d.ts +0 -9
- package/types/src/column-formatting-settings/DateTimeFormattingSettings.d.ts +0 -3
- package/types/src/column-formatting-settings/LongTypeFormattingSettings.d.ts +0 -3
- package/types/src/column-formatting-settings/index.d.ts +0 -3
- package/types/src/column-menu/ColumnMenu.d.ts +0 -10
- package/types/src/column-menu/column-action-types.d.ts +0 -11
- package/types/src/column-menu/column-menu-utils.d.ts +0 -19
- package/types/src/column-menu/useColumnActions.d.ts +0 -16
- package/types/src/column-picker/ColumnModel.d.ts +0 -81
- package/types/src/column-picker/ColumnPicker.d.ts +0 -10
- package/types/src/column-picker/ColumnPickerAction.d.ts +0 -5
- package/types/src/column-picker/useColumnPicker.d.ts +0 -16
- package/types/src/column-picker/useTableColumnPicker.d.ts +0 -7
- package/types/src/column-picker/useTableColumnPickerDeprecated.d.ts +0 -14
- package/types/src/column-settings-panel/ColumnNameLabel.d.ts +0 -7
- package/types/src/column-settings-panel/ColumnSettingsPanel.d.ts +0 -5
- package/types/src/column-settings-panel/useColumnSettings.d.ts +0 -30
- package/types/src/csv-export/export-utils.d.ts +0 -42
- package/types/src/csv-export/index.d.ts +0 -2
- package/types/src/csv-export/useCsvExport.d.ts +0 -14
- package/types/src/csv-upload/CsvUpload.d.ts +0 -69
- package/types/src/csv-upload/data-upload-preview/DataUploadPreview.d.ts +0 -10
- package/types/src/csv-upload/data-upload-preview/useDataUploadPreview.d.ts +0 -17
- package/types/src/csv-upload/index.d.ts +0 -7
- package/types/src/csv-upload/parse/csv-constants.d.ts +0 -3
- package/types/src/csv-upload/parse/csv-errors.d.ts +0 -47
- package/types/src/csv-upload/parse/csv-parse.d.ts +0 -11
- package/types/src/csv-upload/parse/csv-schema-validation.d.ts +0 -18
- package/types/src/csv-upload/parse/csv-upload-utils.d.ts +0 -21
- package/types/src/csv-upload/parse/index.d.ts +0 -5
- package/types/src/csv-upload/useCsvUpload.d.ts +0 -43
- package/types/src/datasource-stats/DatasourceStats.d.ts +0 -17
- package/types/src/datasource-stats/useDatasourceStats.d.ts +0 -15
- package/types/src/freeze-control/FreezeControl.d.ts +0 -15
- package/types/src/freeze-control/FrozenBanner.d.ts +0 -6
- package/types/src/freeze-control/freezeControlBadge.d.ts +0 -11
- package/types/src/freeze-control/useFreezeControl.d.ts +0 -10
- package/types/src/index.d.ts +0 -26
- package/types/src/inline-add-row/InlineAddRow.d.ts +0 -3
- package/types/src/inline-add-row/index.d.ts +0 -2
- package/types/src/inline-add-row/useInlineAddRow.d.ts +0 -59
- package/types/src/tabbed-table-config-panel/TabbedTableConfigPanel.d.ts +0 -17
- package/types/src/tabbed-table-config-panel/TabbedTableSettingsAction.d.ts +0 -7
- package/types/src/tabbed-table-config-panel/useTabbedTableConfigPanel.d.ts +0 -13
- package/types/src/table-footer/TableFooter.d.ts +0 -10
- package/types/src/table-provider/TableProvider.d.ts +0 -18
- package/types/src/table-settings-panel/TableSettingsPanel.d.ts +0 -22
- package/types/src/table-settings-panel/useTableSettings.d.ts +0 -14
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "4.0.0-alpha.
|
|
2
|
+
"version": "4.0.0-alpha.22",
|
|
3
3
|
"author": "heswell",
|
|
4
4
|
"main": "./src/index.js",
|
|
5
5
|
"license": "Apache-2.0",
|
|
@@ -12,25 +12,24 @@
|
|
|
12
12
|
"lezer-generate:column": "lezer-generator --output ./src/column-expression-input/column-language-parser/generated/column-parser.ts ./src/column-expression-input/column-language-parser/grammar/column.grammar"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
|
-
"@vuu-ui/vuu-filter-types": "4.0.0-alpha.
|
|
16
|
-
"@vuu-ui/vuu-protocol-types": "4.0.0-alpha.
|
|
15
|
+
"@vuu-ui/vuu-filter-types": "4.0.0-alpha.22",
|
|
16
|
+
"@vuu-ui/vuu-protocol-types": "4.0.0-alpha.22"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@vuu-ui/core": "4.0.0-alpha.
|
|
20
|
-
"@vuu-ui/vuu-codemirror": "4.0.0-alpha.
|
|
21
|
-
"@vuu-ui/vuu-data-editing": "4.0.0-alpha.
|
|
22
|
-
"@vuu-ui/vuu-data-react": "4.0.0-alpha.
|
|
23
|
-
"@vuu-ui/vuu-data-types": "4.0.0-alpha.
|
|
24
|
-
"@vuu-ui/vuu-table-types": "4.0.0-alpha.
|
|
25
|
-
"@vuu-ui/vuu-popups": "4.0.0-alpha.
|
|
26
|
-
"@vuu-ui/vuu-table": "4.0.0-alpha.
|
|
27
|
-
"@vuu-ui/vuu-utils": "4.0.0-alpha.
|
|
28
|
-
"@vuu-ui/vuu-ui-controls": "4.0.0-alpha.
|
|
19
|
+
"@vuu-ui/core": "4.0.0-alpha.22",
|
|
20
|
+
"@vuu-ui/vuu-codemirror": "4.0.0-alpha.22",
|
|
21
|
+
"@vuu-ui/vuu-data-editing": "4.0.0-alpha.22",
|
|
22
|
+
"@vuu-ui/vuu-data-react": "4.0.0-alpha.22",
|
|
23
|
+
"@vuu-ui/vuu-data-types": "4.0.0-alpha.22",
|
|
24
|
+
"@vuu-ui/vuu-table-types": "4.0.0-alpha.22",
|
|
25
|
+
"@vuu-ui/vuu-popups": "4.0.0-alpha.22",
|
|
26
|
+
"@vuu-ui/vuu-table": "4.0.0-alpha.22",
|
|
27
|
+
"@vuu-ui/vuu-utils": "4.0.0-alpha.22",
|
|
28
|
+
"@vuu-ui/vuu-ui-controls": "4.0.0-alpha.22",
|
|
29
29
|
"@lezer/lr": "1.4.2",
|
|
30
|
-
"@salt-ds/core": "1.
|
|
31
|
-
"@salt-ds/
|
|
32
|
-
"@salt-ds/
|
|
33
|
-
"@salt-ds/window": "0.1.1",
|
|
30
|
+
"@salt-ds/core": "1.72.0",
|
|
31
|
+
"@salt-ds/styles": "0.4.1",
|
|
32
|
+
"@salt-ds/window": "0.1.2",
|
|
34
33
|
"csv-parse": "^7.0.0"
|
|
35
34
|
},
|
|
36
35
|
"peerDependencies": {
|
|
@@ -9,8 +9,8 @@ const css = `
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
.vuuPctProgressCell-progressBar {
|
|
12
|
-
background-color: var(--
|
|
13
|
-
border-right: solid var(--progress-bar-width) var(--
|
|
12
|
+
background-color: var(--salt-color-green-600-40a);
|
|
13
|
+
border-right: solid var(--progress-bar-width) var(--salt-color-green-600);
|
|
14
14
|
height: 20px;
|
|
15
15
|
width: var(--progress-bar-pct, 0);
|
|
16
16
|
display: inline-block;
|
|
@@ -13,7 +13,7 @@ const css = `
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
.vuuProgressCell-bg {
|
|
16
|
-
background-color: var(--salt-
|
|
16
|
+
background-color: var(--salt-sentiment-neutral-track);
|
|
17
17
|
width: 100%;
|
|
18
18
|
height: 2px;
|
|
19
19
|
display: inline-block;
|
|
@@ -23,7 +23,7 @@ const css = `
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
.vuuProgressCell-bar {
|
|
26
|
-
background-color: var(--salt-
|
|
26
|
+
background-color: var(--salt-sentiment-accent-background);
|
|
27
27
|
height: 100%;
|
|
28
28
|
transform: translateX(var(--progress-bar-pct, -100%));
|
|
29
29
|
width: 100%;
|
|
@@ -5,15 +5,15 @@ const css = `
|
|
|
5
5
|
--vuuFilterEditor-fontFamily: var(--salt-typography-fontFamily);
|
|
6
6
|
--vuuFilterEditor-fontSize: var(--salt-text-fontSize);
|
|
7
7
|
--vuuFilterEditor-cursorColor: var(--salt-content-secondary-foreground);
|
|
8
|
-
--vuuFilterEditor-selectionBackground: var(--salt-
|
|
8
|
+
--vuuFilterEditor-selectionBackground: var(--salt-selectable-background-selected);
|
|
9
9
|
--vuuFilterEditor-tooltipBackground: var(--salt-container-primary-background);
|
|
10
|
-
--vuuFilterEditor-tooltipBorder: solid 1px var(--vuu-color-purple
|
|
10
|
+
--vuuFilterEditor-tooltipBorder: solid 1px var(--vuu-color-purple);
|
|
11
11
|
--vuuFilterEditor-tooltipElevation: var(--salt-overlayable-shadow-popout);
|
|
12
12
|
--vuuFilterEditor-suggestion-selectedBackground: var(--vuu-color-gray-10);
|
|
13
13
|
--vuuFilterEditor-suggestion-selectedColor: var(--salt-content-primary-foreground);
|
|
14
14
|
--vuuFilterEditor-suggestion-detailColor: var(--salt-content-secondary-foreground-disabled);
|
|
15
15
|
--vuuFilterEditor-suggestion-height: 24px;
|
|
16
|
-
--vuuFilterEditor-variableColor: var(--vuu-color-purple
|
|
16
|
+
--vuuFilterEditor-variableColor: var(--vuu-color-purple);
|
|
17
17
|
box-sizing: border-box;
|
|
18
18
|
align-items: center;
|
|
19
19
|
height: 30px;
|
|
@@ -72,13 +72,13 @@ const css = `
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
.vuuFunctionDoc .example-container {
|
|
75
|
-
background-color: var(--
|
|
75
|
+
background-color: var(--salt-color-gray-100);
|
|
76
76
|
margin: 6px 0;
|
|
77
77
|
padding: 3px;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
.vuuFunctionDoc .example-expression {
|
|
81
|
-
color: var(--
|
|
81
|
+
color: var(--salt-color-gray-800);
|
|
82
82
|
font-family: var(--salt-typography-fontFamily-code);
|
|
83
83
|
padding: 8px;
|
|
84
84
|
font-weight: 500;
|
|
@@ -51,8 +51,9 @@ const isExportableColumn = (descriptor)=>{
|
|
|
51
51
|
const exportCsvTemplate = async (dataSource, options = {})=>{
|
|
52
52
|
const { filename = "template.csv", excludeColumns = [], columns, overrides, timeout = DEFAULT_TEMPLATE_TIMEOUT, onError, onSuccess } = options;
|
|
53
53
|
const targetColumnNames = columns ? columns.filter((col)=>"string" == typeof col ? true : isExportableColumn(col) && "client" !== col.source).map((col)=>"string" == typeof col ? col : col.name) : void 0;
|
|
54
|
-
const
|
|
55
|
-
|
|
54
|
+
const targetColumns = targetColumnNames ?? overrides?.columns;
|
|
55
|
+
const sessionOverrides = overrides ?? (targetColumns ? {
|
|
56
|
+
columns: targetColumns
|
|
56
57
|
} : void 0);
|
|
57
58
|
if (!isSessionTable(dataSource.table) && dataSource.createSessionDataSource && "initialising" !== dataSource.status && "unsubscribed" !== dataSource.status) try {
|
|
58
59
|
const sessionDataSource = await dataSource.createSessionDataSource("Empty", "export", sessionOverrides);
|
|
@@ -80,7 +81,16 @@ const exportCsvTemplate = async (dataSource, options = {})=>{
|
|
|
80
81
|
}, (message)=>{
|
|
81
82
|
if ("subscribed" === message.type) {
|
|
82
83
|
const { columns: subColumns } = message;
|
|
83
|
-
const exportCols = subColumns.filter((name)=>!excluded.has(name));
|
|
84
|
+
const exportCols = targetColumns ? targetColumns.filter((name)=>!excluded.has(name)) : subColumns.filter((name)=>!excluded.has(name));
|
|
85
|
+
if (0 === exportCols.length) {
|
|
86
|
+
cleanup();
|
|
87
|
+
const error = new Error("exportCsvTemplate: no columns available for export");
|
|
88
|
+
if (onError) {
|
|
89
|
+
onError(error);
|
|
90
|
+
resolve();
|
|
91
|
+
} else reject(error);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
84
94
|
const header = exportCols.map(csvCell).join(",");
|
|
85
95
|
triggerCsvDownload(`${header}\r\n`, filename);
|
|
86
96
|
cleanup();
|
|
@@ -93,7 +103,6 @@ const exportCsvTemplate = async (dataSource, options = {})=>{
|
|
|
93
103
|
console.warn("[exportCsvTemplate] createSessionDataSource failed, falling back to tableSchema", error);
|
|
94
104
|
}
|
|
95
105
|
const schema = dataSource.tableSchema;
|
|
96
|
-
const targetColumns = targetColumnNames ?? overrides?.columns;
|
|
97
106
|
if (void 0 !== targetColumns) {
|
|
98
107
|
if (schema) {
|
|
99
108
|
const schemaColumnNames = new Set(schema.columns.map((col)=>col.name));
|
|
@@ -20,8 +20,8 @@ const css = `
|
|
|
20
20
|
max-width: 480px;
|
|
21
21
|
max-height: 120px;
|
|
22
22
|
padding: var(--salt-spacing-100) var(--salt-spacing-200);
|
|
23
|
-
background-color:
|
|
24
|
-
border-radius: var(--salt-
|
|
23
|
+
background-color: var(--salt-status-error-background);
|
|
24
|
+
border-radius: var(--salt-palette-corner);
|
|
25
25
|
text-align: left;
|
|
26
26
|
overflow-y: auto;
|
|
27
27
|
}
|
|
@@ -33,9 +33,9 @@ const css = `
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
.vuuCsvUpload-errorItem {
|
|
36
|
-
font-size: var(--salt-text-fontSize
|
|
37
|
-
color: var(--salt-status-error-foreground);
|
|
38
|
-
line-height: var(--salt-text-lineHeight
|
|
36
|
+
font-size: var(--salt-text-label-fontSize);
|
|
37
|
+
color: var(--salt-status-error-foreground-informative);
|
|
38
|
+
line-height: var(--salt-text-label-lineHeight);
|
|
39
39
|
word-break: break-word;
|
|
40
40
|
margin-bottom: var(--salt-spacing-50);
|
|
41
41
|
}
|
|
@@ -50,8 +50,8 @@ const css = `
|
|
|
50
50
|
|
|
51
51
|
.vuuCsvUpload-errors {
|
|
52
52
|
border: solid 1px var(--salt-status-error-borderColor);
|
|
53
|
-
border-radius: var(--salt-
|
|
54
|
-
color: var(--salt-status-error-foreground);
|
|
53
|
+
border-radius: var(--salt-palette-corner);
|
|
54
|
+
color: var(--salt-status-error-foreground-informative);
|
|
55
55
|
gap: var(--salt-spacing-100);
|
|
56
56
|
padding: var(--salt-spacing-200);
|
|
57
57
|
display: grid;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isRpcError } from "@vuu-ui/vuu-utils";
|
|
1
|
+
import { isRpcError, metadataKeys } from "@vuu-ui/vuu-utils";
|
|
2
2
|
import { CSV_FIRST_DATA_ROW_NUMBER } from "./csv-constants.js";
|
|
3
3
|
const isRecord = (value)=>"object" == typeof value && null !== value;
|
|
4
4
|
const toErrorMessage = (error)=>error instanceof Error ? error.message : String(error);
|
|
@@ -56,4 +56,49 @@ const executeBatchRpcCalls = async (items, executor, errorFormatter)=>{
|
|
|
56
56
|
results
|
|
57
57
|
};
|
|
58
58
|
};
|
|
59
|
-
|
|
59
|
+
const getInlineRowErrorMessage = (result)=>{
|
|
60
|
+
if (isRecord(result) && "data" in result && isRecord(result.data)) {
|
|
61
|
+
const data = result.data;
|
|
62
|
+
const msg = data.vuuMsg ?? data.msg;
|
|
63
|
+
if ("string" == typeof msg && "" !== msg.trim()) return msg.trim();
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const getRowVuuMsgError = (row, vuuMsgDataIndex, vuuRowNumDataIndex)=>{
|
|
67
|
+
if (-1 === vuuMsgDataIndex) return;
|
|
68
|
+
const msg = Array.isArray(row) ? row[vuuMsgDataIndex] : row?.vuuMsg;
|
|
69
|
+
if ("string" == typeof msg && "" !== msg.trim()) {
|
|
70
|
+
const rowNum = -1 !== vuuRowNumDataIndex && void 0 !== row[vuuRowNumDataIndex] ? Number(row[vuuRowNumDataIndex]) : Number(row[metadataKeys.IDX]) + CSV_FIRST_DATA_ROW_NUMBER;
|
|
71
|
+
return {
|
|
72
|
+
rowNum,
|
|
73
|
+
message: msg.trim()
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const waitForSessionErrors = (isRemote, sessionErrors, receivedRowKeys, expectedRowCount, listeners, timeoutMs = 500)=>{
|
|
78
|
+
if (sessionErrors.size > 0 || !isRemote) return Promise.resolve([
|
|
79
|
+
...sessionErrors.values()
|
|
80
|
+
]);
|
|
81
|
+
return new Promise((resolve)=>{
|
|
82
|
+
let timeoutId;
|
|
83
|
+
const onUpdate = ()=>{
|
|
84
|
+
if (sessionErrors.size > 0 || receivedRowKeys.size >= expectedRowCount) {
|
|
85
|
+
cleanup();
|
|
86
|
+
resolve([
|
|
87
|
+
...sessionErrors.values()
|
|
88
|
+
]);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
const cleanup = ()=>{
|
|
92
|
+
clearTimeout(timeoutId);
|
|
93
|
+
listeners.delete(onUpdate);
|
|
94
|
+
};
|
|
95
|
+
listeners.add(onUpdate);
|
|
96
|
+
timeoutId = setTimeout(()=>{
|
|
97
|
+
cleanup();
|
|
98
|
+
resolve([
|
|
99
|
+
...sessionErrors.values()
|
|
100
|
+
]);
|
|
101
|
+
}, timeoutMs);
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
export { buildRowErrorMessage, createUploadError, executeBatchRpcCalls, getInlineRowErrorMessage, getRowVuuMsgError, hasFileParseErrors, isCsvParseError, mergeValidationWithParseErrors, normalizeTableData, toErrorMessage, toValidationErrorsFromParseRowErrors, waitForSessionErrors };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useData } from "@vuu-ui/vuu-utils";
|
|
2
|
+
import { useEffect, useMemo, useState } from "react";
|
|
3
|
+
const useImportSchema = ({ dataSource, importSchema, importTable })=>{
|
|
4
|
+
const { getServerAPI } = useData();
|
|
5
|
+
const [fetchedImportSchema, setFetchedImportSchema] = useState();
|
|
6
|
+
useEffect(()=>{
|
|
7
|
+
if (importSchema || void 0 === importTable) return void setFetchedImportSchema(void 0);
|
|
8
|
+
let cancelled = false;
|
|
9
|
+
(async ()=>{
|
|
10
|
+
try {
|
|
11
|
+
const server = await getServerAPI();
|
|
12
|
+
const tableSchema = await server.getTableSchema(importTable);
|
|
13
|
+
if (!cancelled) setFetchedImportSchema(tableSchema);
|
|
14
|
+
} catch (error) {
|
|
15
|
+
console.error("[useImportSchema] failed to fetch import table schema", error);
|
|
16
|
+
}
|
|
17
|
+
})();
|
|
18
|
+
return ()=>{
|
|
19
|
+
cancelled = true;
|
|
20
|
+
};
|
|
21
|
+
}, [
|
|
22
|
+
getServerAPI,
|
|
23
|
+
importSchema,
|
|
24
|
+
importTable
|
|
25
|
+
]);
|
|
26
|
+
const resolvedImportSchema = importSchema ?? fetchedImportSchema;
|
|
27
|
+
const sessionOverrides = useMemo(()=>{
|
|
28
|
+
const columns = resolvedImportSchema?.columns.map(({ name })=>name);
|
|
29
|
+
return columns || importTable ? {
|
|
30
|
+
columns,
|
|
31
|
+
table: importTable
|
|
32
|
+
} : void 0;
|
|
33
|
+
}, [
|
|
34
|
+
resolvedImportSchema,
|
|
35
|
+
importTable
|
|
36
|
+
]);
|
|
37
|
+
const schema = importTable ? resolvedImportSchema : resolvedImportSchema ?? dataSource.tableSchema;
|
|
38
|
+
return {
|
|
39
|
+
resolvedImportSchema,
|
|
40
|
+
schema,
|
|
41
|
+
sessionOverrides
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
export { useImportSchema };
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { EditSession } from "@vuu-ui/vuu-data-editing";
|
|
2
|
+
import { Range, buildColumnMap, isRpcError, isSessionTable, metadataKeys } from "@vuu-ui/vuu-utils";
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { buildRowErrorMessage, getInlineRowErrorMessage, getRowVuuMsgError, toErrorMessage, waitForSessionErrors } from "../parse/csv-upload-utils.js";
|
|
5
|
+
import { CSV_FIRST_DATA_ROW_NUMBER } from "../parse/csv-constants.js";
|
|
6
|
+
const useImportSession = ({ dataSource, sessionOverrides, rowDefaults, processingPromiseRef, onImportSessionEnded })=>{
|
|
7
|
+
const sessionErrorsRef = useRef(new Map());
|
|
8
|
+
const receivedRowKeysRef = useRef(new Set());
|
|
9
|
+
const sessionRowUpdateListenersRef = useRef(new Set());
|
|
10
|
+
const importDataSource = useMemo(()=>({
|
|
11
|
+
tableSchema: dataSource.tableSchema,
|
|
12
|
+
createSessionDataSource: async (copyOption, sessionType)=>{
|
|
13
|
+
if (!dataSource.createSessionDataSource) throw Error("[useCsvUpload] dataSource does not support createSessionDataSource");
|
|
14
|
+
const sessionDs = await dataSource.createSessionDataSource(copyOption, sessionType, sessionOverrides);
|
|
15
|
+
if (!sessionDs) throw Error("[useCsvUpload] Failed to create session datasource");
|
|
16
|
+
if (!sessionDs.columns.includes("vuuMsg")) sessionDs.columns = sessionDs.columns.concat("vuuMsg");
|
|
17
|
+
const columnMap = buildColumnMap(sessionDs.columns);
|
|
18
|
+
const vuuMsgDataIndex = columnMap.vuuMsg ?? -1;
|
|
19
|
+
const vuuRowNumDataIndex = columnMap.vuuRowNum ?? -1;
|
|
20
|
+
const handleSessionMessage = (message)=>{
|
|
21
|
+
if ("viewport-update" === message.type && message.rows) {
|
|
22
|
+
for (const row of message.rows){
|
|
23
|
+
const rowKey = row[metadataKeys.KEY] ?? row[metadataKeys.IDX];
|
|
24
|
+
receivedRowKeysRef.current.add(rowKey);
|
|
25
|
+
const error = getRowVuuMsgError(row, vuuMsgDataIndex, vuuRowNumDataIndex);
|
|
26
|
+
if (error) sessionErrorsRef.current.set(rowKey, error);
|
|
27
|
+
}
|
|
28
|
+
for (const listener of sessionRowUpdateListenersRef.current)listener();
|
|
29
|
+
} else if ("viewport-clear" === message.type) {
|
|
30
|
+
sessionErrorsRef.current.clear();
|
|
31
|
+
receivedRowKeysRef.current.clear();
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
if (sessionDs.isRemote) return new Promise((resolve, reject)=>{
|
|
35
|
+
const handleSubscribed = ()=>{
|
|
36
|
+
sessionDs.removeListener("subscribed", handleSubscribed);
|
|
37
|
+
resolve(sessionDs);
|
|
38
|
+
};
|
|
39
|
+
sessionDs.on("subscribed", handleSubscribed);
|
|
40
|
+
try {
|
|
41
|
+
sessionDs.subscribe({
|
|
42
|
+
range: Range(0, 0)
|
|
43
|
+
}, handleSessionMessage);
|
|
44
|
+
} catch (err) {
|
|
45
|
+
sessionDs.removeListener("subscribed", handleSubscribed);
|
|
46
|
+
reject(err);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
if ("function" == typeof sessionDs.subscribe && "initialising" === sessionDs.status) try {
|
|
50
|
+
sessionDs.subscribe({
|
|
51
|
+
range: Range(0, 0)
|
|
52
|
+
}, handleSessionMessage);
|
|
53
|
+
} catch {}
|
|
54
|
+
return sessionDs;
|
|
55
|
+
}
|
|
56
|
+
}), [
|
|
57
|
+
dataSource,
|
|
58
|
+
sessionOverrides
|
|
59
|
+
]);
|
|
60
|
+
const editSession = useMemo(()=>new EditSession({
|
|
61
|
+
dataSource: importDataSource,
|
|
62
|
+
editSessionApi: "createSessionDataSource",
|
|
63
|
+
rowDefaults
|
|
64
|
+
}), [
|
|
65
|
+
importDataSource,
|
|
66
|
+
rowDefaults
|
|
67
|
+
]);
|
|
68
|
+
const ownsEditSessionRef = useRef(true);
|
|
69
|
+
const sessionDataSourceRef = useRef(void 0);
|
|
70
|
+
const [sessionTable, setSessionTable] = useState();
|
|
71
|
+
useEffect(()=>()=>{
|
|
72
|
+
const processingPromise = processingPromiseRef?.current;
|
|
73
|
+
(async ()=>{
|
|
74
|
+
try {
|
|
75
|
+
await processingPromise;
|
|
76
|
+
} finally{
|
|
77
|
+
if (ownsEditSessionRef.current) try {
|
|
78
|
+
const sessionDs = sessionDataSourceRef.current ?? editSession.sessionDataSource;
|
|
79
|
+
if (sessionDs?.status !== "unsubscribed") await editSession.end(false);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
console.error("[useCsvUpload] failed to discard edit session during cleanup", error);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
})();
|
|
85
|
+
}, [
|
|
86
|
+
editSession,
|
|
87
|
+
processingPromiseRef
|
|
88
|
+
]);
|
|
89
|
+
const setActiveSessionDataSource = useCallback((sessionDataSource)=>{
|
|
90
|
+
sessionDataSourceRef.current = sessionDataSource;
|
|
91
|
+
const table = sessionDataSource?.table;
|
|
92
|
+
setSessionTable(table && isSessionTable(table) ? table : void 0);
|
|
93
|
+
}, []);
|
|
94
|
+
const endEditSessionAndNotify = useCallback(async (save, reason)=>{
|
|
95
|
+
ownsEditSessionRef.current = false;
|
|
96
|
+
const sessionDataSource = sessionDataSourceRef.current;
|
|
97
|
+
if (!sessionDataSource) throw Error("CsvUpload has no active edit session.");
|
|
98
|
+
if (!sessionDataSource.endEditSession) throw Error("CsvUpload requires the session datasource to support endEditSession.");
|
|
99
|
+
const currentSessionTable = sessionDataSource.table;
|
|
100
|
+
try {
|
|
101
|
+
await editSession.end(save);
|
|
102
|
+
} finally{
|
|
103
|
+
if ("unsubscribed" !== sessionDataSource.status && "function" == typeof sessionDataSource.unsubscribe) sessionDataSource.unsubscribe();
|
|
104
|
+
setActiveSessionDataSource(void 0);
|
|
105
|
+
}
|
|
106
|
+
onImportSessionEnded?.({
|
|
107
|
+
reason,
|
|
108
|
+
sessionTable: currentSessionTable && isSessionTable(currentSessionTable) ? currentSessionTable : void 0
|
|
109
|
+
});
|
|
110
|
+
}, [
|
|
111
|
+
editSession,
|
|
112
|
+
onImportSessionEnded,
|
|
113
|
+
setActiveSessionDataSource
|
|
114
|
+
]);
|
|
115
|
+
const beginEditSession = useCallback(async ()=>{
|
|
116
|
+
sessionErrorsRef.current.clear();
|
|
117
|
+
receivedRowKeysRef.current.clear();
|
|
118
|
+
ownsEditSessionRef.current = true;
|
|
119
|
+
const sessionDataSource = await editSession.begin("Empty", "import");
|
|
120
|
+
const sessionVuuTable = sessionDataSource?.table;
|
|
121
|
+
if (void 0 === sessionDataSource || void 0 === sessionVuuTable || !isSessionTable(sessionVuuTable)) throw Error("CsvUpload createSessionDataSource returned no session datasource.");
|
|
122
|
+
setActiveSessionDataSource(sessionDataSource);
|
|
123
|
+
return sessionDataSource;
|
|
124
|
+
}, [
|
|
125
|
+
editSession,
|
|
126
|
+
setActiveSessionDataSource
|
|
127
|
+
]);
|
|
128
|
+
const closePendingEditSession = useCallback(async (save)=>{
|
|
129
|
+
if (void 0 === sessionDataSourceRef.current) return;
|
|
130
|
+
await endEditSessionAndNotify(save, save ? "saved" : "discarded");
|
|
131
|
+
}, [
|
|
132
|
+
endEditSessionAndNotify
|
|
133
|
+
]);
|
|
134
|
+
const releaseEditSessionOwnership = useCallback(()=>{
|
|
135
|
+
ownsEditSessionRef.current = false;
|
|
136
|
+
}, []);
|
|
137
|
+
const addAllRows = useCallback(async (mergedValidation, isCancelled)=>{
|
|
138
|
+
const vuuMsgByRow = new Map();
|
|
139
|
+
for (const { rowNum, column, message } of mergedValidation.errors){
|
|
140
|
+
if (rowNum < CSV_FIRST_DATA_ROW_NUMBER) continue;
|
|
141
|
+
const existing = vuuMsgByRow.get(rowNum);
|
|
142
|
+
const columnError = `${column}: ${message}`;
|
|
143
|
+
vuuMsgByRow.set(rowNum, existing ? `${existing}; ${columnError}` : columnError);
|
|
144
|
+
}
|
|
145
|
+
const parsedRowCount = mergedValidation.rows.length;
|
|
146
|
+
const unparsedErrorRows = [
|
|
147
|
+
...vuuMsgByRow.keys()
|
|
148
|
+
].filter((rowNum)=>rowNum - CSV_FIRST_DATA_ROW_NUMBER >= parsedRowCount).map((rowNum)=>({
|
|
149
|
+
rowNum,
|
|
150
|
+
rowData: {},
|
|
151
|
+
vuuMsg: vuuMsgByRow.get(rowNum) ?? ""
|
|
152
|
+
}));
|
|
153
|
+
const allRows = [
|
|
154
|
+
...mergedValidation.rows.map((rowData, idx)=>({
|
|
155
|
+
rowNum: idx + CSV_FIRST_DATA_ROW_NUMBER,
|
|
156
|
+
rowData: rowData ?? {},
|
|
157
|
+
vuuMsg: vuuMsgByRow.get(idx + CSV_FIRST_DATA_ROW_NUMBER) ?? ""
|
|
158
|
+
})),
|
|
159
|
+
...unparsedErrorRows
|
|
160
|
+
];
|
|
161
|
+
const rpcErrors = [];
|
|
162
|
+
for (const { rowNum, rowData, vuuMsg } of allRows){
|
|
163
|
+
if (isCancelled()) return false;
|
|
164
|
+
try {
|
|
165
|
+
const payload = vuuMsg ? {
|
|
166
|
+
vuuRowNum: rowNum,
|
|
167
|
+
vuuMsg
|
|
168
|
+
} : {
|
|
169
|
+
...rowData,
|
|
170
|
+
vuuRowNum: rowNum,
|
|
171
|
+
vuuMsg
|
|
172
|
+
};
|
|
173
|
+
const result = await editSession.addRow(payload);
|
|
174
|
+
if (isRpcError(result)) throw Error(result.errorMessage);
|
|
175
|
+
if ("string" == typeof result) throw Error(result);
|
|
176
|
+
const inlineMsg = getInlineRowErrorMessage(result);
|
|
177
|
+
if (inlineMsg) sessionErrorsRef.current.set(rowNum, {
|
|
178
|
+
rowNum,
|
|
179
|
+
message: inlineMsg
|
|
180
|
+
});
|
|
181
|
+
} catch (error) {
|
|
182
|
+
rpcErrors.push(`${0 === rowNum ? "Header" : `Row ${rowNum}`}: ${toErrorMessage(error)}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (rpcErrors.length > 0) throw Error(buildRowErrorMessage("Import failed", rpcErrors));
|
|
186
|
+
return allRows.length;
|
|
187
|
+
}, [
|
|
188
|
+
editSession
|
|
189
|
+
]);
|
|
190
|
+
const checkSessionTableErrors = useCallback(async (sessionDs, expectedRowCount)=>waitForSessionErrors(sessionDs.isRemote ?? false, sessionErrorsRef.current, receivedRowKeysRef.current, expectedRowCount, sessionRowUpdateListenersRef.current), []);
|
|
191
|
+
return {
|
|
192
|
+
addAllRows,
|
|
193
|
+
beginEditSession,
|
|
194
|
+
checkSessionTableErrors,
|
|
195
|
+
closePendingEditSession,
|
|
196
|
+
editSession,
|
|
197
|
+
endEditSessionAndNotify,
|
|
198
|
+
releaseEditSessionOwnership,
|
|
199
|
+
sessionDataSourceRef,
|
|
200
|
+
sessionTable,
|
|
201
|
+
setActiveSessionDataSource
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
export { useImportSession };
|