@vuu-ui/vuu-table-extras 5.0.0-alpha.2 → 5.0.0-alpha.4

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 (29) hide show
  1. package/package.json +13 -13
  2. package/src/bulk-edit-dialog/useBulkEditDialog.js +93 -0
  3. package/src/csv-upload/parse/csv-parse.js +6 -2
  4. package/src/csv-upload/useCsvUpload.js +17 -0
  5. package/src/editable-table/EditableTable.css.js +41 -0
  6. package/src/editable-table/EditableTable.js +176 -0
  7. package/src/editable-table/EditableTableLayout.js +61 -0
  8. package/src/index.js +4 -0
  9. package/src/inline-add-row/useInlineAddRow.js +0 -8
  10. package/src/table-editor/TableEditorContext.js +9 -0
  11. package/src/table-editor/TableEditorControls.js +86 -0
  12. package/src/table-editor/TableEditorForm.js +142 -0
  13. package/src/table-editor/TableEditorLayout.css.js +16 -0
  14. package/src/table-editor/TableEditorLayout.js +30 -0
  15. package/src/table-editor/TableEditorProvider.js +434 -0
  16. package/src/table-editor/TableEditorTable.js +112 -0
  17. package/src/table-editor/index.js +5 -0
  18. package/types/bulk-edit-dialog/useBulkEditDialog.d.ts +27 -0
  19. package/types/editable-table/EditableTable.d.ts +178 -0
  20. package/types/editable-table/EditableTableLayout.d.ts +41 -0
  21. package/types/index.d.ts +4 -0
  22. package/types/inline-add-row/useInlineAddRow.d.ts +1 -0
  23. package/types/table-editor/TableEditorContext.d.ts +100 -0
  24. package/types/table-editor/TableEditorControls.d.ts +43 -0
  25. package/types/table-editor/TableEditorForm.d.ts +58 -0
  26. package/types/table-editor/TableEditorLayout.d.ts +10 -0
  27. package/types/table-editor/TableEditorProvider.d.ts +57 -0
  28. package/types/table-editor/TableEditorTable.d.ts +24 -0
  29. package/types/table-editor/index.d.ts +5 -0
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "5.0.0-alpha.2",
2
+ "version": "5.0.0-alpha.4",
3
3
  "author": "heswell",
4
4
  "main": "./src/index.js",
5
5
  "license": "Apache-2.0",
@@ -11,25 +11,25 @@
11
11
  "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"
12
12
  },
13
13
  "devDependencies": {
14
- "@vuu-ui/vuu-filter-types": "5.0.0-alpha.2",
15
- "@vuu-ui/vuu-protocol-types": "5.0.0-alpha.2"
14
+ "@vuu-ui/vuu-filter-types": "5.0.0-alpha.4",
15
+ "@vuu-ui/vuu-protocol-types": "5.0.0-alpha.4"
16
16
  },
17
17
  "dependencies": {
18
- "@vuu-ui/vuu-codemirror": "5.0.0-alpha.2",
19
- "@vuu-ui/vuu-data-editing": "5.0.0-alpha.2",
20
- "@vuu-ui/vuu-data-react": "5.0.0-alpha.2",
21
- "@vuu-ui/vuu-data-types": "5.0.0-alpha.2",
22
- "@vuu-ui/vuu-table-types": "5.0.0-alpha.2",
23
- "@vuu-ui/vuu-popups": "5.0.0-alpha.2",
24
- "@vuu-ui/vuu-table": "5.0.0-alpha.2",
25
- "@vuu-ui/vuu-utils": "5.0.0-alpha.2",
26
- "@vuu-ui/vuu-ui-controls": "5.0.0-alpha.2",
18
+ "@vuu-ui/vuu-codemirror": "5.0.0-alpha.4",
19
+ "@vuu-ui/vuu-data-editing": "5.0.0-alpha.4",
20
+ "@vuu-ui/vuu-data-react": "5.0.0-alpha.4",
21
+ "@vuu-ui/vuu-data-types": "5.0.0-alpha.4",
22
+ "@vuu-ui/vuu-table-types": "5.0.0-alpha.4",
23
+ "@vuu-ui/vuu-popups": "5.0.0-alpha.4",
24
+ "@vuu-ui/vuu-table": "5.0.0-alpha.4",
25
+ "@vuu-ui/vuu-utils": "5.0.0-alpha.4",
26
+ "@vuu-ui/vuu-ui-controls": "5.0.0-alpha.4",
27
27
  "@lezer/lr": "1.4.2",
28
28
  "@salt-ds/core": "1.74.0",
29
29
  "@salt-ds/styles": "0.4.2",
30
30
  "@salt-ds/window": "0.1.2",
31
31
  "csv-parse": "^7.0.0",
32
- "@vuu-ui/core": "5.0.0-alpha.2"
32
+ "@vuu-ui/core": "5.0.0-alpha.4"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "@floating-ui/react": "^0.27.19",
@@ -0,0 +1,93 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { DataEditingProvider, EditButtons, useEditableTable } from "@vuu-ui/vuu-data-editing";
3
+ import { BulkEditPanel } from "@vuu-ui/vuu-table";
4
+ import { useModal } from "@vuu-ui/core";
5
+ import { useCallback, useEffect, useRef, useState } from "react";
6
+ const NOT_EDITING = {
7
+ copyOption: "Selected",
8
+ editing: false
9
+ };
10
+ const useBulkEditDialog = ({ bulkEditPanelProps, confirmCancel, dataSource: sourceTableDataSource, deleteMode, editSessionApi, onCancelled, onError, onSaved, rowDefaults, saveLabel = "Save", title = "Edit rows" })=>{
11
+ const { closeDialog, showDialog } = useModal();
12
+ const [{ copyOption, editing }, setDialogState] = useState(NOT_EDITING);
13
+ const callbacksRef = useRef({
14
+ onCancelled,
15
+ onSaved
16
+ });
17
+ callbacksRef.current = {
18
+ onCancelled,
19
+ onSaved
20
+ };
21
+ const handleCancelled = useCallback(()=>{
22
+ setDialogState(NOT_EDITING);
23
+ callbacksRef.current.onCancelled?.();
24
+ }, []);
25
+ const handleSaved = useCallback(()=>{
26
+ setDialogState(NOT_EDITING);
27
+ callbacksRef.current.onSaved?.();
28
+ }, []);
29
+ const { editSession, onCancel, onSave, rowClassNameGenerators, sessionDataSource, sourceDataSource } = useEditableTable({
30
+ copyOption,
31
+ dataSource: sourceTableDataSource,
32
+ deleteMode,
33
+ editSessionApi,
34
+ isEditMode: editing,
35
+ onCancel: handleCancelled,
36
+ onError,
37
+ onSave: handleSaved,
38
+ rowDefaults
39
+ });
40
+ const editSelectedRows = useCallback(()=>{
41
+ setDialogState({
42
+ copyOption: "Selected",
43
+ editing: true
44
+ });
45
+ }, []);
46
+ const insertRows = useCallback(()=>{
47
+ setDialogState({
48
+ copyOption: "Empty",
49
+ editing: true
50
+ });
51
+ }, []);
52
+ useEffect(()=>{
53
+ if (editing && sessionDataSource) showDialog(/*#__PURE__*/ jsx(DataEditingProvider, {
54
+ editSession: editSession,
55
+ children: /*#__PURE__*/ jsx(BulkEditPanel, {
56
+ ...bulkEditPanelProps,
57
+ parentDs: sourceDataSource,
58
+ rowClassNameGenerators: rowClassNameGenerators,
59
+ sessionDs: sessionDataSource
60
+ })
61
+ }), title, [
62
+ /*#__PURE__*/ jsx(EditButtons, {
63
+ confirmCancel: confirmCancel,
64
+ editSession: editSession,
65
+ onCancel: onCancel,
66
+ onSave: onSave,
67
+ saveLabel: saveLabel
68
+ }, "edit-buttons")
69
+ ], true);
70
+ else closeDialog();
71
+ }, [
72
+ bulkEditPanelProps,
73
+ closeDialog,
74
+ confirmCancel,
75
+ editSession,
76
+ editing,
77
+ onCancel,
78
+ onSave,
79
+ rowClassNameGenerators,
80
+ saveLabel,
81
+ sessionDataSource,
82
+ showDialog,
83
+ sourceDataSource,
84
+ title
85
+ ]);
86
+ return {
87
+ editSelectedRows,
88
+ editSession,
89
+ insertRows,
90
+ isEditing: editing
91
+ };
92
+ };
93
+ export { useBulkEditDialog };
@@ -47,8 +47,8 @@ const parseCsv = (csvText, options)=>{
47
47
  relax_column_count: true,
48
48
  skip_empty_lines: true
49
49
  });
50
- } catch (err) {
51
- addCsvFileError(errorState, "*", CsvParseErrorEnum.INVALID_FORMAT, err instanceof Error ? err.message : "Invalid CSV format.");
50
+ } catch {
51
+ addCsvFileError(errorState, "*", CsvParseErrorEnum.INVALID_FORMAT, "Invalid CSV format.");
52
52
  return withError("Invalid CSV format.", errorState.errorMap, errorState.errors);
53
53
  }
54
54
  if (0 === records.length) {
@@ -75,6 +75,10 @@ const parseCsv = (csvText, options)=>{
75
75
  addCsvFileError(errorState, duplicateHeader, CsvParseErrorEnum.DUPLICATE_HEADER_COLUMN, `CSV header contains duplicate column '${duplicateHeader}'.`, duplicateHeader);
76
76
  return withError(`CSV header contains duplicate column '${duplicateHeader}'.`, errorState.errorMap, errorState.errors, header, []);
77
77
  }
78
+ if (0 === rows.length) {
79
+ addCsvFileError(errorState, "*", CsvParseErrorEnum.EMPTY_FILE, "CSV file is empty.");
80
+ return withError("CSV file is empty.", errorState.errorMap, errorState.errors, header, []);
81
+ }
78
82
  const firstDataRowNumber = options?.customHeaders && false === options.hasHeaderRow ? 1 : CSV_FIRST_DATA_ROW_NUMBER;
79
83
  rows.forEach((row, rowIndex)=>{
80
84
  if (row.length !== header.length) addCsvRowError(errorState, rowIndex + firstDataRowNumber, "*", CsvParseErrorEnum.ROW_COLUMN_COUNT_MISMATCH, "This row has too many or too few columns compared to the header line. Check for extra or missing commas.", row.join(","));
@@ -5,6 +5,14 @@ import { CsvValidationErrorEnum, validateCsvAgainstSchema } from "./parse/csv-sc
5
5
  import { createUploadError, hasFileParseErrors, isCsvParseError, mapCsvHeaders, mergeValidationWithParseErrors, toErrorMessage } from "./parse/csv-upload-utils.js";
6
6
  import { useImportSchema } from "./session/useImportSchema.js";
7
7
  import { useImportSession } from "./session/useImportSession.js";
8
+ const CSV_MIME_TYPES = new Set([
9
+ "",
10
+ "text/csv",
11
+ "text/plain",
12
+ "application/csv",
13
+ "application/vnd.ms-excel"
14
+ ]);
15
+ const isCsvFile = (file)=>file.name.toLowerCase().endsWith(".csv") && CSV_MIME_TYPES.has(file.type.toLowerCase());
8
16
  const useCsvUpload = ({ dataSource, importMode = "direct", importSchema, importTable, onImportSessionEnded, onImportSessionReady, onError, onImported, onPreview, onProcessingStarted, maxRows, parseOptions, customHeaders, hasHeaderRow, headerMapping, rowDefaults, validators })=>{
9
17
  const [validation, setValidation] = useState();
10
18
  const [isProcessingFile, setIsProcessingFile] = useState(false);
@@ -58,6 +66,15 @@ const useCsvUpload = ({ dataSource, importMode = "direct", importSchema, importT
58
66
  }
59
67
  if (void 0 === schema) throw Error("Table schema is not yet available.");
60
68
  if (void 0 === table) throw Error("CsvUpload requires dataSource.table to be defined.");
69
+ if (!isCsvFile(file)) {
70
+ const errorMessage = "Invalid file type. Only .csv files are supported.";
71
+ handleError({
72
+ errors: {
73
+ validationError: createUploadError("validation", errorMessage)
74
+ }
75
+ });
76
+ return;
77
+ }
61
78
  const effectiveParseOptions = {
62
79
  ...parseOptions,
63
80
  ...void 0 !== customHeaders ? {
@@ -0,0 +1,41 @@
1
+ const css = `
2
+ .vuuEditableTable {
3
+ --vuuMeasuredContainer-flex: 1 1 1px;
4
+ flex-direction: column;
5
+ flex: auto;
6
+ min-width: 0;
7
+ height: 100%;
8
+ min-height: 0;
9
+ display: flex;
10
+ }
11
+
12
+ .vuuEditableTable-withPanel {
13
+ flex-direction: row;
14
+ }
15
+
16
+ .vuuEditableTable-main {
17
+ --vuuMeasuredContainer-flex: 1 1 1px;
18
+ flex-direction: column;
19
+ flex: auto;
20
+ min-width: 0;
21
+ min-height: 0;
22
+ display: flex;
23
+ }
24
+
25
+ .vuuEditableTable-panel {
26
+ --saltSidePanel-width: 320px;
27
+ }
28
+
29
+ .vuuEditableTable-panelHeader > :first-child {
30
+ flex: 1;
31
+ min-width: 0;
32
+ }
33
+
34
+ .vuuEditableTable-detail {
35
+ min-height: 0;
36
+ overflow: auto;
37
+ }
38
+
39
+
40
+ `;
41
+ export default css;
@@ -0,0 +1,176 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { SidePanelContent, SidePanelTitle } from "@salt-ds/core";
3
+ import { useComponentCssInjection } from "@salt-ds/styles";
4
+ import { useWindow } from "@salt-ds/window";
5
+ import clsx from "clsx";
6
+ import { TableEditorCreateForm, TableEditorEditButton, TableEditorForm, TableEditorModeToggle, TableEditorNewButton, TableEditorPlaceholder, TableEditorTable, TableEditorToolbar, useTableEditor } from "../table-editor/index.js";
7
+ import { TableEditorProvider } from "../table-editor/TableEditorProvider.js";
8
+ import EditableTable from "./EditableTable.css";
9
+ import { EditableTableFooter, EditableTableMain, EditableTablePanel, EditableTablePanelHeader } from "./EditableTableLayout.js";
10
+ const classBase = "vuuEditableTable";
11
+ const PanelTitle = ({ createTitle, formTitle })=>{
12
+ const { mode, selectedRow } = useTableEditor();
13
+ const title = "create" === mode ? createTitle : selectedRow ? "function" == typeof formTitle ? formTitle(selectedRow) : formTitle : null;
14
+ return title ? /*#__PURE__*/ jsx(SidePanelTitle, {
15
+ children: title
16
+ }) : null;
17
+ };
18
+ const InlineContent = ({ createFields, createTitle = "New", panelClassName, saveLabel, showEditModeToggle, testId, validateCreate, ...tableProps })=>{
19
+ const { createStrategy, editModel } = useTableEditor();
20
+ const hasCreateForm = "form" === createStrategy;
21
+ const showToggle = showEditModeToggle ?? "session" === editModel;
22
+ const table = /*#__PURE__*/ jsxs(Fragment, {
23
+ children: [
24
+ showToggle || hasCreateForm ? /*#__PURE__*/ jsxs(TableEditorToolbar, {
25
+ children: [
26
+ showToggle ? /*#__PURE__*/ jsx(TableEditorModeToggle, {
27
+ testId: testId
28
+ }) : null,
29
+ hasCreateForm ? /*#__PURE__*/ jsx(TableEditorNewButton, {
30
+ "data-testid": `new-button${testId}`
31
+ }) : null
32
+ ]
33
+ }) : null,
34
+ /*#__PURE__*/ jsx(TableEditorTable, {
35
+ "data-testid": `table${testId}`,
36
+ ...tableProps
37
+ }),
38
+ /*#__PURE__*/ jsx(EditableTableFooter, {
39
+ saveLabel: saveLabel
40
+ })
41
+ ]
42
+ });
43
+ return hasCreateForm ? /*#__PURE__*/ jsxs(Fragment, {
44
+ children: [
45
+ /*#__PURE__*/ jsx(EditableTableMain, {
46
+ children: table
47
+ }),
48
+ /*#__PURE__*/ jsxs(EditableTablePanel, {
49
+ className: panelClassName,
50
+ "data-testid": `edit-panel${testId}`,
51
+ children: [
52
+ /*#__PURE__*/ jsx(EditableTablePanelHeader, {
53
+ children: /*#__PURE__*/ jsx(PanelTitle, {
54
+ createTitle: createTitle
55
+ })
56
+ }),
57
+ /*#__PURE__*/ jsx(SidePanelContent, {
58
+ className: `${classBase}-detail`,
59
+ children: /*#__PURE__*/ jsx(TableEditorCreateForm, {
60
+ "data-testid": `create-form${testId}`,
61
+ fields: createFields,
62
+ validate: validateCreate
63
+ })
64
+ })
65
+ ]
66
+ })
67
+ ]
68
+ }) : table;
69
+ };
70
+ const FormContent = ({ allowCreate = true, createFields, createTitle = "New", fields, formTitle, panelClassName, placeholder = "Select a row to see its details", testId, validate, validateCreate = validate, ...tableProps })=>/*#__PURE__*/ jsxs(Fragment, {
71
+ children: [
72
+ /*#__PURE__*/ jsxs(EditableTableMain, {
73
+ children: [
74
+ /*#__PURE__*/ jsxs(TableEditorToolbar, {
75
+ children: [
76
+ /*#__PURE__*/ jsx(TableEditorEditButton, {
77
+ "data-testid": `edit-button${testId}`
78
+ }),
79
+ allowCreate ? /*#__PURE__*/ jsx(TableEditorNewButton, {
80
+ "data-testid": `new-button${testId}`
81
+ }) : null
82
+ ]
83
+ }),
84
+ /*#__PURE__*/ jsx(TableEditorTable, {
85
+ "data-testid": `table${testId}`,
86
+ ...tableProps
87
+ })
88
+ ]
89
+ }),
90
+ /*#__PURE__*/ jsxs(EditableTablePanel, {
91
+ className: panelClassName,
92
+ "data-testid": `edit-panel${testId}`,
93
+ children: [
94
+ /*#__PURE__*/ jsx(EditableTablePanelHeader, {
95
+ children: /*#__PURE__*/ jsx(PanelTitle, {
96
+ createTitle: createTitle,
97
+ formTitle: formTitle
98
+ })
99
+ }),
100
+ /*#__PURE__*/ jsxs(SidePanelContent, {
101
+ className: `${classBase}-detail`,
102
+ children: [
103
+ /*#__PURE__*/ jsx(TableEditorPlaceholder, {
104
+ children: placeholder
105
+ }),
106
+ /*#__PURE__*/ jsx(TableEditorForm, {
107
+ "data-testid": `edit-form${testId}`,
108
+ fields: fields,
109
+ validate: validate
110
+ }),
111
+ allowCreate ? /*#__PURE__*/ jsx(TableEditorCreateForm, {
112
+ "data-testid": `create-form${testId}`,
113
+ fields: createFields ?? fields,
114
+ validate: validateCreate
115
+ }) : null
116
+ ]
117
+ })
118
+ ]
119
+ })
120
+ ]
121
+ });
122
+ const Container = ({ children, className, style, testId })=>{
123
+ const targetWindow = useWindow();
124
+ useComponentCssInjection({
125
+ testId: "vuu-editable-table",
126
+ css: EditableTable,
127
+ window: targetWindow
128
+ });
129
+ const { createStrategy, editStrategy, mode, panelOpen } = useTableEditor();
130
+ return /*#__PURE__*/ jsx("div", {
131
+ className: clsx(classBase, className, `${classBase}-${mode}`, {
132
+ [`${classBase}-editing`]: "edit" === mode,
133
+ [`${classBase}-panelOpen`]: panelOpen,
134
+ [`${classBase}-withPanel`]: "form" === editStrategy || "form" === createStrategy
135
+ }),
136
+ "data-testid": `edit-table${testId}`,
137
+ style: style,
138
+ children: children
139
+ });
140
+ };
141
+ const EditableTable_EditableTable = (props)=>{
142
+ const { allowDelete = false, children, className, confirmCancel, copyOption, createStrategy, dataSource, defaultMode, deleteMode, editModel = "session", editSessionApi, editStrategy = "inline", mode, onCancelled, onError, onModeChange, onSaved, panelVisibility, rowDefaults, style, testId = "", ...contentProps } = props;
143
+ const isInlineDirect = "inline" === editStrategy && "direct" === editModel;
144
+ return /*#__PURE__*/ jsx(TableEditorProvider, {
145
+ allowDelete: allowDelete,
146
+ confirmCancel: confirmCancel,
147
+ copyOption: copyOption,
148
+ createStrategy: createStrategy,
149
+ dataSource: dataSource,
150
+ defaultMode: defaultMode ?? (isInlineDirect ? "edit" : "view"),
151
+ deleteMode: deleteMode,
152
+ editModel: editModel,
153
+ editSessionApi: editSessionApi,
154
+ editStrategy: editStrategy,
155
+ mode: mode,
156
+ onCancelled: onCancelled,
157
+ onError: onError,
158
+ onModeChange: onModeChange,
159
+ onSaved: onSaved,
160
+ panelVisibility: panelVisibility,
161
+ rowDefaults: rowDefaults,
162
+ children: /*#__PURE__*/ jsx(Container, {
163
+ className: className,
164
+ style: style,
165
+ testId: testId,
166
+ children: children ?? ("form" === editStrategy ? /*#__PURE__*/ jsx(FormContent, {
167
+ ...contentProps,
168
+ testId: testId
169
+ }) : /*#__PURE__*/ jsx(InlineContent, {
170
+ ...contentProps,
171
+ testId: testId
172
+ }))
173
+ })
174
+ });
175
+ };
176
+ export { EditableTable_EditableTable as EditableTable };
@@ -0,0 +1,61 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { SidePanel, SidePanelHeader, useForkRef } from "@salt-ds/core";
3
+ import clsx from "clsx";
4
+ import { forwardRef } from "react";
5
+ import { DataSourceStats } from "../datasource-stats/DatasourceStats.js";
6
+ import { TableEditorButtons, TableEditorPanelCloseButton } from "../table-editor/index.js";
7
+ import { useTableEditorContext } from "../table-editor/TableEditorContext.js";
8
+ import { TableFooter, TableFooterTray } from "../table-footer/TableFooter.js";
9
+ const EditableTableFooter = ({ children, saveLabel, ...htmlAttributes })=>{
10
+ const { dataSource, editableTable } = useTableEditorContext("EditableTableFooter");
11
+ return /*#__PURE__*/ jsx(TableFooter, {
12
+ ...htmlAttributes,
13
+ children: editableTable?.isEditSessionReady ? /*#__PURE__*/ jsx(TableFooterTray, {
14
+ position: "center",
15
+ children: /*#__PURE__*/ jsx(TableEditorButtons, {
16
+ saveLabel: saveLabel
17
+ })
18
+ }) : children ?? /*#__PURE__*/ jsx(DataSourceStats, {
19
+ dataSource: dataSource
20
+ })
21
+ });
22
+ };
23
+ const classBase = "vuuEditableTable";
24
+ const EditableTableLayout_EditableTableMain = /*#__PURE__*/ forwardRef(function({ className, ...htmlAttributes }, forwardedRef) {
25
+ return /*#__PURE__*/ jsx("div", {
26
+ ...htmlAttributes,
27
+ className: clsx(`${classBase}-main`, className),
28
+ ref: forwardedRef
29
+ });
30
+ });
31
+ const animationWidth = "--saltSidePanel-animation-width";
32
+ const measureAnimationWidth = (el)=>{
33
+ if (el?.classList.contains("saltSidePanel-enterAnimation") && !el.style.getPropertyValue(animationWidth)) {
34
+ const { animation } = el.style;
35
+ el.style.animation = "none";
36
+ const { width } = el.getBoundingClientRect();
37
+ el.style.animation = animation;
38
+ el.style.setProperty(animationWidth, `${width}px`);
39
+ }
40
+ };
41
+ const EditableTableLayout_EditableTablePanel = /*#__PURE__*/ forwardRef(function({ className, ...props }, forwardedRef) {
42
+ const ref = useForkRef(measureAnimationWidth, forwardedRef);
43
+ return /*#__PURE__*/ jsx(SidePanel, {
44
+ ...props,
45
+ className: clsx(`${classBase}-panel`, className),
46
+ position: "right",
47
+ ref: ref
48
+ });
49
+ });
50
+ const EditableTableLayout_EditableTablePanelHeader = /*#__PURE__*/ forwardRef(function({ canClose = true, children, className, ...props }, forwardedRef) {
51
+ return /*#__PURE__*/ jsxs(SidePanelHeader, {
52
+ ...props,
53
+ className: clsx(`${classBase}-panelHeader`, className),
54
+ ref: forwardedRef,
55
+ children: [
56
+ children,
57
+ canClose ? /*#__PURE__*/ jsx(TableEditorPanelCloseButton, {}) : null
58
+ ]
59
+ });
60
+ });
61
+ export { EditableTableFooter, EditableTableLayout_EditableTableMain as EditableTableMain, EditableTableLayout_EditableTablePanel as EditableTablePanel, EditableTableLayout_EditableTablePanelHeader as EditableTablePanelHeader };
package/src/index.js CHANGED
@@ -5,6 +5,7 @@ export * from "./cell-renderers/index.js";
5
5
  export * from "./column-expression-input/index.js";
6
6
  export * from "./column-formatting-settings/index.js";
7
7
  export * from "./inline-add-row/index.js";
8
+ export * from "./table-editor/index.js";
8
9
  export { CalculatedColumnPanel } from "./calculated-column/CalculatedColumnPanel.js";
9
10
  export { ColumnMenu } from "./column-menu/ColumnMenu.js";
10
11
  export { useColumnActions } from "./column-menu/useColumnActions.js";
@@ -22,3 +23,6 @@ export { TabbedTableSettingsAction } from "./tabbed-table-config-panel/TabbedTab
22
23
  export { TableFooter, TableFooterTray } from "./table-footer/TableFooter.js";
23
24
  export { TableProvider, useTableContext } from "./table-provider/TableProvider.js";
24
25
  export { TableSettingsPanel, defaultTableSettingsPermissions } from "./table-settings-panel/TableSettingsPanel.js";
26
+ export { useBulkEditDialog } from "./bulk-edit-dialog/useBulkEditDialog.js";
27
+ export { EditableTable } from "./editable-table/EditableTable.js";
28
+ export { EditableTableFooter, EditableTableMain, EditableTablePanel, EditableTablePanelHeader } from "./editable-table/EditableTableLayout.js";
@@ -66,14 +66,6 @@ const useInlineAddRow = ({ columns })=>{
66
66
  newRowState.values,
67
67
  visibleInsertColumns
68
68
  ]);
69
- useEffect(()=>{
70
- const firstInvalidIndex = visibleColumns.findIndex(({ name })=>void 0 !== newRowState.errors[name]);
71
- if (-1 !== firstInvalidIndex) focusEditor(firstInvalidIndex);
72
- }, [
73
- focusEditor,
74
- newRowState.errors,
75
- visibleColumns
76
- ]);
77
69
  useEffect(()=>{
78
70
  const previousValues = previousValuesRef.current;
79
71
  previousValuesRef.current = newRowState.values;
@@ -0,0 +1,9 @@
1
+ import { createContext, useContext } from "react";
2
+ const TableEditorContext = createContext(void 0);
3
+ const useTableEditorContext = (componentName)=>{
4
+ const context = useContext(TableEditorContext);
5
+ if (void 0 === context) throw Error(`${componentName} must be used within an EditableTable`);
6
+ return context;
7
+ };
8
+ const useTableEditor = ()=>useTableEditorContext("useTableEditor");
9
+ export { TableEditorContext, useTableEditor, useTableEditorContext };
@@ -0,0 +1,86 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Button, SidePanelCloseButton, ToggleButton, ToggleButtonGroup } from "@salt-ds/core";
3
+ import { EditButtons, EditFormButtons, useEditFormContext } from "@vuu-ui/vuu-data-editing";
4
+ import { forwardRef, useCallback } from "react";
5
+ import { useTableEditorContext } from "./TableEditorContext.js";
6
+ const TableEditorButtons = ({ hideCancel, saveLabel })=>{
7
+ const { allowDelete, confirmCancel, editStrategy, editableTable } = useTableEditorContext("TableEditorButtons");
8
+ const form = useEditFormContext();
9
+ if (form || "form" === editStrategy) return form ? /*#__PURE__*/ jsx(EditFormButtons, {
10
+ confirmCancel: confirmCancel,
11
+ hideCancel: hideCancel,
12
+ saveLabel: saveLabel
13
+ }) : null;
14
+ if (editableTable) {
15
+ const { canCancel, canSave, editSession, hasSelection, onCancel, onDelete, onSave } = editableTable;
16
+ return /*#__PURE__*/ jsx(EditButtons, {
17
+ canCancel: canCancel,
18
+ canSave: canSave,
19
+ confirmCancel: confirmCancel,
20
+ editSession: editSession,
21
+ hasSelection: allowDelete ? hasSelection : void 0,
22
+ onCancel: hideCancel ? void 0 : onCancel,
23
+ onDelete: allowDelete ? onDelete : void 0,
24
+ onSave: onSave,
25
+ saveLabel: saveLabel
26
+ });
27
+ }
28
+ return null;
29
+ };
30
+ const TableEditorModeToggle = ({ "aria-label": ariaLabel = "Mode", disabled, testId = "", ...props })=>{
31
+ const { cancel, editStrategy, mode, selectedRow, startEdit } = useTableEditorContext("TableEditorModeToggle");
32
+ const handleChange = useCallback((e)=>{
33
+ if ("edit" === e.currentTarget.value) startEdit();
34
+ else cancel();
35
+ }, [
36
+ cancel,
37
+ startEdit
38
+ ]);
39
+ return /*#__PURE__*/ jsxs(ToggleButtonGroup, {
40
+ ...props,
41
+ "aria-label": ariaLabel,
42
+ disabled: disabled || "create" === mode || "form" === editStrategy && void 0 === selectedRow,
43
+ onChange: handleChange,
44
+ value: "edit" === mode ? "edit" : "view",
45
+ children: [
46
+ /*#__PURE__*/ jsx(ToggleButton, {
47
+ "data-testid": `toggle-view${testId}`,
48
+ value: "view",
49
+ children: "View"
50
+ }),
51
+ /*#__PURE__*/ jsx(ToggleButton, {
52
+ "data-testid": `toggle-edit${testId}`,
53
+ value: "edit",
54
+ children: "Edit"
55
+ })
56
+ ]
57
+ });
58
+ };
59
+ const TableEditorControls_TableEditorEditButton = /*#__PURE__*/ forwardRef(function({ children = "Edit", disabled, ...props }, ref) {
60
+ const { isEditing, selectedRow, startEdit } = useTableEditorContext("TableEditorEditButton");
61
+ return /*#__PURE__*/ jsx(Button, {
62
+ ...props,
63
+ disabled: disabled || isEditing || void 0 === selectedRow,
64
+ onClick: startEdit,
65
+ ref: ref,
66
+ children: children
67
+ });
68
+ });
69
+ const TableEditorControls_TableEditorNewButton = /*#__PURE__*/ forwardRef(function({ children = "New", disabled, ...props }, ref) {
70
+ const { canCreate, startCreate } = useTableEditorContext("TableEditorNewButton");
71
+ return /*#__PURE__*/ jsx(Button, {
72
+ ...props,
73
+ disabled: disabled || !canCreate,
74
+ onClick: startCreate,
75
+ ref: ref,
76
+ children: children
77
+ });
78
+ });
79
+ const TableEditorControls_TableEditorPanelCloseButton = /*#__PURE__*/ forwardRef(function(props, ref) {
80
+ const { canClosePanel } = useTableEditorContext("TableEditorPanelCloseButton");
81
+ return canClosePanel ? /*#__PURE__*/ jsx(SidePanelCloseButton, {
82
+ ...props,
83
+ ref: ref
84
+ }) : null;
85
+ });
86
+ export { TableEditorButtons, TableEditorControls_TableEditorEditButton as TableEditorEditButton, TableEditorControls_TableEditorNewButton as TableEditorNewButton, TableEditorControls_TableEditorPanelCloseButton as TableEditorPanelCloseButton, TableEditorModeToggle };