@updog/data-editor-wc 0.1.4 → 0.1.5
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/README.md +364 -128
- package/index.d.ts +299 -52
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# @updog/data-editor-wc
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Client-side data importer and spreadsheet editor SDK, shipped as a Web Component. Your users import files, match columns to your schema, fix errors, and submit clean data. Edits happen inline, in the browser, at 1M+ rows.
|
|
4
|
+
|
|
5
|
+
Framework-agnostic — works with vanilla JS, Angular, Vue, Svelte, or any framework that renders HTML.
|
|
6
|
+
|
|
7
|
+
> **Using React?** Use the first-class React SDK instead — [`@updog/data-editor`](https://www.npmjs.com/package/@updog/data-editor) — for typed props, hooks-friendly integration, and a smaller bundle (shares your app's React).
|
|
4
8
|
|
|
5
9
|
## Requirements
|
|
6
10
|
|
|
7
|
-
- An API key — this is a **commercial SDK**. Sign up at [updog.tech](https://updog.tech) to get one. The editor will not render without it.
|
|
8
11
|
- Any modern evergreen browser (Chrome, Firefox, Safari, Edge).
|
|
12
|
+
- An API key — this is a **commercial SDK**. Sign up at [updog.tech](https://updog.tech) to get one.
|
|
9
13
|
|
|
10
14
|
## Installation
|
|
11
15
|
|
|
@@ -13,205 +17,427 @@ Web Component wrapper for the Updog Data Editor. Framework-agnostic — works wi
|
|
|
13
17
|
npm install @updog/data-editor-wc
|
|
14
18
|
```
|
|
15
19
|
|
|
16
|
-
##
|
|
20
|
+
## Register
|
|
21
|
+
|
|
22
|
+
```js
|
|
23
|
+
import "@updog/data-editor-wc";
|
|
24
|
+
import "@updog/data-editor-wc/styles.css";
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Defines `<updog-editor>` globally. Import once, typically at your app entry.
|
|
28
|
+
|
|
29
|
+
How you wire the element into a framework (Angular's `CUSTOM_ELEMENTS_SCHEMA`, Vue's `compilerOptions.isCustomElement`, Svelte's `svelte:element`, etc.) depends on your stack. Once registered, the rest of this guide is the same.
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
17
32
|
|
|
18
33
|
```html
|
|
34
|
+
<button id="open-btn">Open Editor</button>
|
|
35
|
+
<updog-editor id="editor"></updog-editor>
|
|
36
|
+
|
|
19
37
|
<script type="module">
|
|
20
38
|
import "@updog/data-editor-wc";
|
|
21
39
|
import "@updog/data-editor-wc/styles.css";
|
|
40
|
+
import { required, email } from "@updog/data-editor-wc";
|
|
41
|
+
|
|
42
|
+
const editor = document.getElementById("editor");
|
|
43
|
+
|
|
44
|
+
editor.configure({
|
|
45
|
+
apiKey: "your-license-key",
|
|
46
|
+
primaryKey: "id",
|
|
47
|
+
columns: [
|
|
48
|
+
{ id: "name", title: "Full Name", size: 200, validate: required("Name is required") },
|
|
49
|
+
{ id: "email", title: "Email", size: 250, validate: [required("Email is required"), email("Invalid email")] },
|
|
50
|
+
{ id: "role", title: "Role", editor: { type: "select", options: ["Admin", "Editor", "Viewer"] } },
|
|
51
|
+
],
|
|
52
|
+
loadData: async (onChunk) => {
|
|
53
|
+
const res = await fetch("/api/employees");
|
|
54
|
+
onChunk(await res.json());
|
|
55
|
+
},
|
|
56
|
+
onComplete: async (result, actions) => {
|
|
57
|
+
for (const source of result.sources) {
|
|
58
|
+
const inserts = source.rows.filter((r) => r.isNew && !r.isDeleted && r.isValid);
|
|
59
|
+
const updates = source.rows.filter((r) => !r.isNew && r.isChanged && !r.isDeleted && r.isValid);
|
|
60
|
+
const deletes = source.rows.filter((r) => r.isDeleted && !r.isNew);
|
|
61
|
+
await persist(source.sourceId, { inserts, updates, deletes });
|
|
62
|
+
}
|
|
63
|
+
actions.reset();
|
|
64
|
+
editor.hide();
|
|
65
|
+
},
|
|
66
|
+
});
|
|
22
67
|
|
|
23
|
-
|
|
68
|
+
document.getElementById("open-btn").addEventListener("click", () => editor.show());
|
|
69
|
+
editor.addEventListener("close", () => editor.hide());
|
|
70
|
+
</script>
|
|
71
|
+
```
|
|
24
72
|
|
|
25
|
-
|
|
26
|
-
{ id: "name", title: "Name", width: 150 },
|
|
27
|
-
{ id: "email", title: "Email", width: 260 },
|
|
28
|
-
];
|
|
29
|
-
editor.primaryKey = "id";
|
|
30
|
-
editor.apiKey = "your-api-key";
|
|
73
|
+
## Setting Props
|
|
31
74
|
|
|
32
|
-
|
|
33
|
-
const res = await fetch("/api/data");
|
|
34
|
-
onChunk(await res.json());
|
|
35
|
-
};
|
|
75
|
+
Two ways to configure the element: HTML attributes for primitives, JS properties for everything else.
|
|
36
76
|
|
|
37
|
-
|
|
38
|
-
console.log("Submitted:", result);
|
|
39
|
-
editor.hide();
|
|
40
|
-
};
|
|
77
|
+
### HTML attributes
|
|
41
78
|
|
|
42
|
-
|
|
43
|
-
document.getElementById("open-btn").addEventListener("click", () => editor.show());
|
|
44
|
-
</script>
|
|
79
|
+
Use for primitive values. The element observes these and re-renders on change.
|
|
45
80
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
81
|
+
| Attribute | Maps to | Type |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| `api-key` | `apiKey` | string |
|
|
84
|
+
| `primary-key` | `primaryKey` | string |
|
|
85
|
+
| `locale` | `locale` | string |
|
|
86
|
+
| `variant` | `variant` | `"editor"` \| `"uploader"` |
|
|
87
|
+
| `mode` | `mode` | `"modal"` \| `"inline"` |
|
|
88
|
+
| `open` | `open` | boolean (presence = true) |
|
|
89
|
+
| `rtl` | `rtl` | boolean (presence = true) |
|
|
90
|
+
| `readonly` | `readonly` | boolean (presence = true) |
|
|
49
91
|
|
|
50
|
-
|
|
92
|
+
```html
|
|
93
|
+
<updog-editor api-key="your-key" primary-key="id" variant="uploader" readonly></updog-editor>
|
|
94
|
+
```
|
|
51
95
|
|
|
52
|
-
|
|
96
|
+
### JS properties
|
|
53
97
|
|
|
54
|
-
|
|
55
|
-
|---|---|---|
|
|
56
|
-
| `api-key` | string | License key |
|
|
57
|
-
| `primary-key` | string | Row identifier column |
|
|
58
|
-
| `locale` | string | BCP 47 locale tag (default: `"en"`) |
|
|
59
|
-
| `variant` | `"editor"` \| `"uploader"` | UI mode |
|
|
60
|
-
| `mode` | `"modal"` \| `"inline"` | Rendering mode (default: `"modal"`) |
|
|
61
|
-
| `open` | boolean (presence) | Whether the editor modal is open (modal mode only) |
|
|
62
|
-
| `rtl` | boolean (presence) | Right-to-left layout |
|
|
63
|
-
| `readonly` | boolean (presence) | Hide write-oriented UI |
|
|
98
|
+
Everything else — objects, functions, arrays — must be set as a JS property or via `configure()`.
|
|
64
99
|
|
|
65
|
-
|
|
100
|
+
```js
|
|
101
|
+
const editor = document.querySelector("updog-editor");
|
|
102
|
+
editor.columns = [...];
|
|
103
|
+
editor.loadData = async (onChunk) => { ... };
|
|
104
|
+
editor.onComplete = (result, actions) => { ... };
|
|
105
|
+
```
|
|
66
106
|
|
|
67
|
-
|
|
107
|
+
### `configure(props)`
|
|
68
108
|
|
|
69
|
-
|
|
70
|
-
|---|---|
|
|
71
|
-
| `columns` | `DataEditorColumn[]` |
|
|
72
|
-
| `loadData` | `(onChunk: (rows) => void) => Promise<void>` |
|
|
73
|
-
| `onComplete` | `(result, actions) => void` |
|
|
74
|
-
| `translations` | `DataEditorTranslations` |
|
|
75
|
-
| `importFormats` | `("csv" \| "tsv" \| "xlsx" \| "json" \| "xml")[]` |
|
|
76
|
-
| `exportFormats` | `("csv" \| "tsv" \| "xlsx" \| "json" \| "xml")[]` |
|
|
77
|
-
| `enableDeleteRow` | `"all"` \| `"new"` \| `false` |
|
|
78
|
-
| `enableAddRow` | `boolean` |
|
|
79
|
-
| `localStorage` | `false \| { licenseGrant?: boolean }` |
|
|
80
|
-
| `rowHeight` | `number` (default: 34) |
|
|
81
|
-
| `headerHeight` | `number` (default: 36) |
|
|
82
|
-
|
|
83
|
-
All properties can also be set in bulk via `configure()`:
|
|
109
|
+
Set multiple properties at once. Useful during setup. Accepts every prop except `onClose`. Property updates are batched — the editor re-renders once per microtask regardless of how many properties you set.
|
|
84
110
|
|
|
85
111
|
```js
|
|
86
112
|
editor.configure({
|
|
87
|
-
|
|
113
|
+
apiKey: "your-key",
|
|
88
114
|
primaryKey: "id",
|
|
89
|
-
|
|
115
|
+
columns: [...],
|
|
116
|
+
loadData: async (onChunk) => onChunk(await fetchRows()),
|
|
117
|
+
onComplete: (result, actions) => { save(result); actions.reset(); },
|
|
90
118
|
});
|
|
91
119
|
```
|
|
92
120
|
|
|
121
|
+
## Inline Mode
|
|
122
|
+
|
|
123
|
+
Render the editor directly in the DOM without a modal overlay:
|
|
124
|
+
|
|
125
|
+
```html
|
|
126
|
+
<updog-editor mode="inline" api-key="your-key"></updog-editor>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
In inline mode, `show()` / `hide()` and the `open` attribute don't apply. The `close` event is not fired.
|
|
130
|
+
|
|
131
|
+
## Props
|
|
132
|
+
|
|
133
|
+
| Prop | Type | Required | Default | Description |
|
|
134
|
+
|---|---|---|---|---|
|
|
135
|
+
| `apiKey` | `string` | Yes | — | Your Updog license key. Validated on each open. |
|
|
136
|
+
| `columns` | `DataEditorColumn[]` | Yes | — | Column definitions. |
|
|
137
|
+
| `primaryKey` | `string` | Yes | — | Column ID that uniquely identifies each row. |
|
|
138
|
+
| `mode` | `"modal"` \| `"inline"` | No | `"modal"` | Rendering mode. |
|
|
139
|
+
| `open` | `boolean` | Modal only | — | Controlled modal visibility. Equivalent to `show()` / `hide()`. |
|
|
140
|
+
| `loadData` | `(onChunk) => Promise<void>` | No | — | Async data loader. Stream rows in chunks, optionally tagged by source. |
|
|
141
|
+
| `onComplete` | `(result, actions) => void` | No | — | Called on submit. See [Submission Result](#submission-result). |
|
|
142
|
+
| `variant` | `"editor"` \| `"uploader"` | No | `"editor"` | Initial view. `"uploader"` opens the import wizard first. |
|
|
143
|
+
| `translations` | `DataEditorTranslations` | No | — | Partial i18n overrides. |
|
|
144
|
+
| `locale` | `string` | No | `"en"` | BCP 47 locale tag. |
|
|
145
|
+
| `rtl` | `boolean` | No | `false` | Right-to-left layout. |
|
|
146
|
+
| `readonly` | `boolean` | No | `false` | Hide all editing UI. |
|
|
147
|
+
| `enableDeleteRow` | `"all"` \| `"new"` \| `false` | No | `false` | Row deletion policy. |
|
|
148
|
+
| `enableAddRow` | `boolean` | No | `true` | Show the "Add row" button. |
|
|
149
|
+
| `enableCreateColumn` | `boolean` | No | `true` | Allow creating columns for unmatched CSV headers during import. |
|
|
150
|
+
| `importFormats` | `DataEditorFormat[]` | No | all | Allowed import formats. `[]` disables import. |
|
|
151
|
+
| `exportFormats` | `DataEditorFormat[]` | No | all | Allowed export formats. `[]` disables export. |
|
|
152
|
+
| `remoteSources` | `RemoteSource[]` | No | — | Custom import buttons (Google Sheets, S3, etc.) rendered on the upload step. |
|
|
153
|
+
| `rowHeight` | `number` | No | `34` | Row height in pixels. |
|
|
154
|
+
| `headerHeight` | `number` | No | `36` | Header height in pixels. |
|
|
155
|
+
| `server` | `DataEditorServer` | No | — | Server-delegated mode: SDK renders, your backend handles queries and mutations. |
|
|
156
|
+
| `chat` | `DataEditorChat` | No | — | Bring-your-own AI chat panel. |
|
|
157
|
+
| `onColumnMatch` | `(headers, columns) => ...` | No | — | Override import column matching. |
|
|
158
|
+
| `onValueMatch` | `(valuesToMatch) => ...` | No | — | Override import value matching for `select` columns. |
|
|
159
|
+
| `synonyms` | `Record<string, string[]>` | No | — | Extra synonyms for column auto-matching. |
|
|
160
|
+
| `sampleData` | `Record<string, unknown>[]` | No | — | Rows used in the "Download Example" file. |
|
|
161
|
+
| `localStorage` | `false` \| `{ licenseGrant?: boolean }` | No | `{ licenseGrant: true }` | What the SDK caches in `localStorage`. |
|
|
162
|
+
| `onError` | `(error: UpdogError) => void` | No | — | Called on internal errors. Use for logging or Sentry. |
|
|
163
|
+
| `className` | `string` | No | — | CSS class on the wrapper element. |
|
|
164
|
+
|
|
93
165
|
## Methods
|
|
94
166
|
|
|
95
167
|
| Method | Description |
|
|
96
168
|
|---|---|
|
|
97
|
-
| `show()` | Opens the editor modal |
|
|
98
|
-
| `hide()` | Closes the editor modal |
|
|
99
|
-
| `configure(props)` | Bulk-set any props |
|
|
169
|
+
| `show()` | Opens the editor modal. Equivalent to setting `open` to `true`. No-op in `mode="inline"`. |
|
|
170
|
+
| `hide()` | Closes the editor modal. Equivalent to setting `open` to `false`. No-op in `mode="inline"`. |
|
|
171
|
+
| `configure(props)` | Bulk-set any props. See [`configure(props)`](#configureprops). |
|
|
100
172
|
|
|
101
173
|
## Events
|
|
102
174
|
|
|
103
|
-
|
|
104
|
-
|---|---|
|
|
105
|
-
| `close` | Fired when the user closes the editor from inside (X button, discard). Call `editor.hide()` in response. |
|
|
175
|
+
### `close`
|
|
106
176
|
|
|
107
|
-
|
|
177
|
+
Fires when the user closes the modal (X button or Escape key). Replaces the React `onClose` prop — the WC dispatches a bubbling `CustomEvent` instead. Not fired in `mode="inline"`.
|
|
108
178
|
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
|
|
179
|
+
```js
|
|
180
|
+
editor.addEventListener("close", () => editor.hide());
|
|
181
|
+
```
|
|
112
182
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
183
|
+
## Column Configuration
|
|
184
|
+
|
|
185
|
+
```js
|
|
186
|
+
import { required, email } from "@updog/data-editor-wc";
|
|
187
|
+
|
|
188
|
+
editor.columns = [
|
|
189
|
+
{
|
|
190
|
+
id: "email",
|
|
191
|
+
title: "Email",
|
|
192
|
+
size: 260,
|
|
193
|
+
|
|
194
|
+
// One validator, or an array. Each returns `{ level: "error", message }` or null.
|
|
195
|
+
validate: [required("Email is required"), email("Enter a valid email")],
|
|
196
|
+
|
|
197
|
+
// Flag duplicates in this column as errors.
|
|
198
|
+
unique: true,
|
|
199
|
+
|
|
200
|
+
// Revalidate these columns when this one changes.
|
|
201
|
+
dependentFields: ["confirmEmail"],
|
|
202
|
+
|
|
203
|
+
// Cell editor. Default is "text".
|
|
204
|
+
editor: { type: "select", options: ["US", "UK", "DE"] },
|
|
205
|
+
|
|
206
|
+
// Display-only formatting. Does not mutate stored data.
|
|
207
|
+
formatter: (value) => value.toLowerCase(),
|
|
208
|
+
|
|
209
|
+
// Runs when rows are uploaded to the editor. Mutates the stored value.
|
|
210
|
+
transformer: (value) => String(value).trim(),
|
|
211
|
+
|
|
212
|
+
// Sidebar filter control.
|
|
213
|
+
filter: { type: "select" },
|
|
214
|
+
|
|
215
|
+
// Lock cells in this column. `"default"` locks only default-source rows.
|
|
216
|
+
locked: "default",
|
|
217
|
+
},
|
|
218
|
+
];
|
|
116
219
|
```
|
|
117
220
|
|
|
118
|
-
|
|
119
|
-
|
|
221
|
+
**Cell editors**: `{ type: "text" }` (default), `{ type: "date", minDate?, maxDate? }`, `{ type: "select", options: string[] }`, `{ type: "number", decimalPlaces?, decimalSeparator?, thousandsSeparator?, allowChars? }`.
|
|
222
|
+
|
|
223
|
+
**Column filters**: `{ type: "select", label?, placeholder?, options?, multiple? }`, `{ type: "number-range", label? }`, `{ type: "date-range", label? }`.
|
|
224
|
+
|
|
225
|
+
## Built-in Validators
|
|
226
|
+
|
|
227
|
+
Validators are factory functions — call each one with the error message you want displayed.
|
|
228
|
+
|
|
229
|
+
```js
|
|
230
|
+
import {
|
|
231
|
+
required,
|
|
232
|
+
numeric,
|
|
233
|
+
email,
|
|
234
|
+
date,
|
|
235
|
+
oneOf,
|
|
236
|
+
endDateAfterStart,
|
|
237
|
+
} from "@updog/data-editor-wc";
|
|
238
|
+
|
|
239
|
+
editor.columns = [
|
|
240
|
+
{ id: "name", title: "Name", validate: required("Required") },
|
|
241
|
+
{ id: "email", title: "Email", validate: [required("Required"), email("Invalid email")] },
|
|
242
|
+
{ id: "salary", title: "Salary", validate: numeric("Must be a number") },
|
|
243
|
+
{ id: "status", title: "Status", validate: oneOf(["active", "inactive"], "Invalid status") },
|
|
244
|
+
{
|
|
245
|
+
id: "startDate",
|
|
246
|
+
title: "Start",
|
|
247
|
+
validate: date("Use YYYY-MM-DD or DD/MM/YYYY"),
|
|
248
|
+
editor: { type: "date" },
|
|
249
|
+
dependentFields: ["endDate"],
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
id: "endDate",
|
|
253
|
+
title: "End",
|
|
254
|
+
validate: [date("Invalid date"), endDateAfterStart("startDate", "End must be on or after start")],
|
|
255
|
+
editor: { type: "date" },
|
|
256
|
+
},
|
|
257
|
+
];
|
|
120
258
|
```
|
|
121
259
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
260
|
+
A `ValidationError` with `level: "error"` flags the cell in the grid but does **not** block submission — invalid rows are delivered to `onComplete` tagged via `isValid: false`.
|
|
261
|
+
|
|
262
|
+
### Custom validators
|
|
263
|
+
|
|
264
|
+
A validator is `(value, row) => ValidationError | null`. The only `level` is `"error"`.
|
|
265
|
+
|
|
266
|
+
```js
|
|
267
|
+
const mustContainAt = (value) => {
|
|
268
|
+
if (!value) return null;
|
|
269
|
+
if (!String(value).includes("@")) return { level: "error", message: "Must contain @" };
|
|
270
|
+
return null;
|
|
271
|
+
};
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Data Loading
|
|
275
|
+
|
|
276
|
+
`loadData` is called once when the editor opens. Call `onChunk` one or more times to stream rows. The grid renders each chunk without blocking.
|
|
277
|
+
|
|
278
|
+
```js
|
|
279
|
+
// Single source
|
|
280
|
+
editor.loadData = async (onChunk) => {
|
|
281
|
+
for (let page = 0; page < totalPages; page++) {
|
|
282
|
+
const rows = await fetch(`/api/employees?page=${page}`).then((r) => r.json());
|
|
283
|
+
onChunk(rows);
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// Multiple sources — each chunk tagged with a source
|
|
288
|
+
editor.loadData = async (onChunk) => {
|
|
289
|
+
onChunk(await fetchSalesforce(), { source: "Salesforce", done: true });
|
|
290
|
+
onChunk(await fetchHubSpot(), { source: "HubSpot", deletable: true, done: true });
|
|
291
|
+
};
|
|
132
292
|
```
|
|
133
293
|
|
|
134
|
-
|
|
294
|
+
`ChunkSourceOptions`: `{ source: string; id?: string; deletable?: boolean; done?: boolean }`. Sources are auto-registered on first encounter. Chunks without options go to "Existing Data". When `loadData` resolves, any source still loading is finalized automatically.
|
|
295
|
+
|
|
296
|
+
## Submission Result
|
|
297
|
+
|
|
298
|
+
`onComplete(result, actions)` fires when the user submits.
|
|
135
299
|
|
|
136
300
|
```ts
|
|
137
|
-
|
|
138
|
-
|
|
301
|
+
type DataEditorResult = {
|
|
302
|
+
sources: {
|
|
303
|
+
sourceId: string;
|
|
304
|
+
sourceName: string;
|
|
305
|
+
rows: {
|
|
306
|
+
row: Record<string, unknown>;
|
|
307
|
+
isNew: boolean; // imported or manually added this session
|
|
308
|
+
isChanged: boolean; // cell values differ from origin
|
|
309
|
+
isDeleted: boolean; // marked for deletion
|
|
310
|
+
isValid: boolean; // passes all validators
|
|
311
|
+
}[];
|
|
312
|
+
}[];
|
|
313
|
+
counts: { new: number; changed: number; deleted: number; invalid: number };
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
type DataEditorActions = {
|
|
317
|
+
reset: () => void; // discard changes and reload via loadData
|
|
318
|
+
};
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Pristine backend rows (unchanged, not deleted, not new) are omitted from `sources[].rows` — they're no-ops. The flags are orthogonal: a row can be new, changed, and deleted at once. You own the routing rules:
|
|
322
|
+
|
|
323
|
+
```js
|
|
324
|
+
editor.onComplete = async (result, actions) => {
|
|
325
|
+
for (const source of result.sources) {
|
|
326
|
+
const inserts = source.rows.filter((r) => r.isNew && !r.isDeleted && r.isValid);
|
|
327
|
+
const updates = source.rows.filter((r) => !r.isNew && r.isChanged && !r.isDeleted && r.isValid);
|
|
328
|
+
const deletes = source.rows.filter((r) => r.isDeleted && !r.isNew);
|
|
329
|
+
await persist(source.sourceId, { inserts, updates, deletes });
|
|
330
|
+
}
|
|
331
|
+
actions.reset();
|
|
332
|
+
};
|
|
139
333
|
```
|
|
140
334
|
|
|
141
|
-
|
|
142
|
-
<template>
|
|
143
|
-
<updog-editor ref="editor" api-key="xxx" @close="editor.hide()" />
|
|
144
|
-
</template>
|
|
335
|
+
If you never tagged sources via `loadData`, you'll get one entry with `sourceId: "backend"`. `result.counts` is an aggregate for summary UI — route with the per-row flags, not counts.
|
|
145
336
|
|
|
146
|
-
|
|
147
|
-
import { ref, onMounted } from "vue";
|
|
337
|
+
## Import Configuration
|
|
148
338
|
|
|
149
|
-
|
|
339
|
+
The import wizard guides users through uploading a file, mapping columns to your schema, and resolving value mismatches.
|
|
150
340
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
341
|
+
### Formats
|
|
342
|
+
|
|
343
|
+
Supported: `"csv"`, `"tsv"`, `"xlsx"`, `"json"`, `"xml"`.
|
|
344
|
+
|
|
345
|
+
```js
|
|
346
|
+
editor.importFormats = ["csv", "xlsx"]; // only CSV and XLSX
|
|
347
|
+
editor.exportFormats = []; // disable export entirely
|
|
158
348
|
```
|
|
159
349
|
|
|
160
|
-
|
|
350
|
+
### Override column matching
|
|
161
351
|
|
|
162
352
|
```js
|
|
163
|
-
editor.
|
|
353
|
+
editor.onColumnMatch = async (headers, columns) => {
|
|
354
|
+
const mappings = await myMatchingService.match(headers, columns);
|
|
355
|
+
return mappings; // { csvHeader: columnId | null }
|
|
356
|
+
};
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Return a map of `{ csvHeader: columnId | null }`. Entries set to `null` or omitted fall back to built-in matching.
|
|
360
|
+
|
|
361
|
+
### Override value matching
|
|
362
|
+
|
|
363
|
+
For select columns, override imported-value → option mapping:
|
|
364
|
+
|
|
365
|
+
```js
|
|
366
|
+
editor.onValueMatch = async (valuesToMatch) => {
|
|
367
|
+
// valuesToMatch = { country: { importedValues: ["espana", "fr"], options: ["Spain", "France"] } }
|
|
368
|
+
return {
|
|
369
|
+
country: { espana: "Spain", fr: "France" },
|
|
370
|
+
};
|
|
371
|
+
};
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Values set to `null` skip auto-matching. Unmapped values fall back to built-in fuzzy matching.
|
|
375
|
+
|
|
376
|
+
### Synonyms
|
|
377
|
+
|
|
378
|
+
Extra aliases for the built-in column matcher:
|
|
379
|
+
|
|
380
|
+
```js
|
|
381
|
+
editor.synonyms = {
|
|
382
|
+
productSku: ["sku", "article_no", "item_code"],
|
|
383
|
+
firstName: ["first", "given_name", "fname"],
|
|
384
|
+
};
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### Remote sources
|
|
388
|
+
|
|
389
|
+
Render custom import buttons for third-party sources. You own auth and picker logic; the SDK renders the button and processes the result.
|
|
390
|
+
|
|
391
|
+
```js
|
|
392
|
+
editor.remoteSources = [
|
|
164
393
|
{
|
|
165
|
-
id: "
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
return null;
|
|
394
|
+
id: "google-sheets",
|
|
395
|
+
label: "Google Sheets",
|
|
396
|
+
icon: "<svg>...</svg>",
|
|
397
|
+
fetch: async () => {
|
|
398
|
+
const data = await myGoogleSheetsLib.pick();
|
|
399
|
+
return data.rows; // Record<string, unknown>[] — or return a File
|
|
172
400
|
},
|
|
173
|
-
unique: true,
|
|
174
|
-
// Cell editor type: "text" (default), "date", or "select"
|
|
175
|
-
editor: { type: "text" },
|
|
176
|
-
// Visual formatting (display only)
|
|
177
|
-
formatter: (value) => value.toLowerCase(),
|
|
178
|
-
// Data transformation on input
|
|
179
|
-
transformer: (value) => String(value).trim(),
|
|
180
|
-
// Filter panel configuration
|
|
181
|
-
filter: { type: "select" },
|
|
182
401
|
},
|
|
183
402
|
];
|
|
184
403
|
```
|
|
185
404
|
|
|
186
|
-
|
|
405
|
+
Return a `File` to parse via the standard CSV/XLSX/JSON/XML pipeline, or return structured records to skip parsing.
|
|
187
406
|
|
|
188
|
-
|
|
407
|
+
## Error Handling
|
|
189
408
|
|
|
190
|
-
|
|
191
|
-
|
|
409
|
+
Log SDK-internal errors through `onError`. The SDK recovers gracefully — use this for monitoring, not recovery.
|
|
410
|
+
|
|
411
|
+
```js
|
|
412
|
+
editor.onError = (error) => {
|
|
413
|
+
Sentry.captureException(error.originalError ?? error, {
|
|
414
|
+
tags: { code: error.code, source: error.source },
|
|
415
|
+
});
|
|
416
|
+
};
|
|
192
417
|
```
|
|
193
418
|
|
|
194
|
-
|
|
419
|
+
Error codes: `PARSE_ERROR`, `RENDER_ERROR`, `TRANSFORM_ERROR`, `VALIDATION_ERROR`, `WORKER_ERROR`, `COMMAND_ERROR`, `OPERATION_ERROR`.
|
|
195
420
|
|
|
196
421
|
## Styling
|
|
197
422
|
|
|
198
|
-
`<updog-editor>` renders in **light DOM by design** — no Shadow DOM isolation. This is intentional: you can override any editor style with
|
|
423
|
+
`<updog-editor>` renders in **light DOM by design** — no Shadow DOM isolation. This is intentional: you can override any editor style with CSS targeting descendants of `updog-editor`.
|
|
199
424
|
|
|
200
425
|
### CSS variables
|
|
201
426
|
|
|
202
|
-
The editor reads colors
|
|
427
|
+
The editor reads colors from CSS custom properties. Override them on `:root` or on the element itself:
|
|
203
428
|
|
|
204
429
|
```css
|
|
205
430
|
updog-editor {
|
|
206
|
-
--updog-
|
|
207
|
-
--updog-
|
|
208
|
-
--updog-radius-md: 8px;
|
|
431
|
+
--updog-grid-cell-bg-idle: #ffffff;
|
|
432
|
+
--updog-grid-header-bg-idle: #f8f9fa;
|
|
209
433
|
}
|
|
210
434
|
```
|
|
211
435
|
|
|
436
|
+
See the [Styling reference](https://docs.updog.tech/styling/) for the full variable list.
|
|
437
|
+
|
|
212
438
|
### Class names
|
|
213
439
|
|
|
214
|
-
Internal elements use BEM-style class names prefixed with `updog__`. Target them
|
|
440
|
+
Internal elements use BEM-style class names prefixed with `updog__`. Target them for deep customization:
|
|
215
441
|
|
|
216
442
|
```css
|
|
217
443
|
updog-editor .updog__button--primary { background: purple; }
|
|
@@ -228,9 +454,19 @@ body *:not(updog-editor *) { /* your global rules */ }
|
|
|
228
454
|
## Notes
|
|
229
455
|
|
|
230
456
|
- Fonts are inherited from your page. The editor uses `var(--updog-font-family)` which defaults to system fonts.
|
|
231
|
-
- In modal mode, the editor portals to `document.body` as a full-screen overlay. In inline mode, it renders in
|
|
457
|
+
- In modal mode, the editor portals to `document.body` as a full-screen overlay. In inline mode, it renders in place with no overlay.
|
|
232
458
|
- Multiple `<updog-editor>` instances on the same page are fully independent.
|
|
233
459
|
|
|
460
|
+
## Using React?
|
|
461
|
+
|
|
462
|
+
Use the first-class React SDK instead:
|
|
463
|
+
|
|
464
|
+
```bash
|
|
465
|
+
npm install @updog/data-editor
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
See [@updog/data-editor](https://www.npmjs.com/package/@updog/data-editor).
|
|
469
|
+
|
|
234
470
|
## License
|
|
235
471
|
|
|
236
|
-
Commercial — see [LICENSE](./LICENSE) for the full terms of the Updog SDK Commercial License v1.0. Contact `admin@updog.tech` for enterprise licensing
|
|
472
|
+
Commercial — see [LICENSE](./LICENSE) for the full terms of the Updog SDK Commercial License v1.0. Contact `admin@updog.tech` for enterprise licensing. Third-party dependencies bundled with this package are listed in `THIRD_PARTY_NOTICES.txt`.
|
package/index.d.ts
CHANGED
|
@@ -516,17 +516,48 @@ type ExpandPluralKeys<T> = T extends Record<string, unknown>
|
|
|
516
516
|
*/
|
|
517
517
|
type DataEditorTranslations = DeepPartial<ExpandPluralKeys<typeof export_default>>;
|
|
518
518
|
|
|
519
|
+
/**
|
|
520
|
+
* Categories of internal errors surfaced through the `onError` callback.
|
|
521
|
+
*
|
|
522
|
+
* - `PARSE_ERROR` — file parsing failed (corrupt CSV, invalid XLSX, etc.).
|
|
523
|
+
* - `RENDER_ERROR` — component render failed (caught by error boundary).
|
|
524
|
+
* - `TRANSFORM_ERROR` — data transformation failed (column transform, value mapping).
|
|
525
|
+
* - `VALIDATION_ERROR` — validation execution failed (validator threw instead of returning).
|
|
526
|
+
* - `WORKER_ERROR` — Web Worker failed (filter worker, chat transform worker).
|
|
527
|
+
* - `COMMAND_ERROR` — undo/redo command failed.
|
|
528
|
+
* - `OPERATION_ERROR` — general operation failed (bulk mutations, imports).
|
|
529
|
+
*/
|
|
519
530
|
type UpdogErrorCode = "PARSE_ERROR" | "RENDER_ERROR" | "TRANSFORM_ERROR" | "VALIDATION_ERROR" | "WORKER_ERROR" | "COMMAND_ERROR" | "OPERATION_ERROR";
|
|
531
|
+
/**
|
|
532
|
+
* An internal error caught by the SDK and passed to `onError`. The SDK
|
|
533
|
+
* recovers gracefully where possible — `onError` is for your logging and
|
|
534
|
+
* monitoring (Sentry, Datadog, etc.).
|
|
535
|
+
*
|
|
536
|
+
* @example
|
|
537
|
+
* ```ts
|
|
538
|
+
* onError={(error) => {
|
|
539
|
+
* Sentry.captureException(error.originalError ?? error, {
|
|
540
|
+
* tags: { code: error.code, source: error.source },
|
|
541
|
+
* });
|
|
542
|
+
* }}
|
|
543
|
+
* ```
|
|
544
|
+
*/
|
|
520
545
|
type UpdogError = {
|
|
546
|
+
/** The error category. */
|
|
521
547
|
code: UpdogErrorCode;
|
|
548
|
+
/** Human-readable description. */
|
|
522
549
|
message: string;
|
|
550
|
+
/** Module or subsystem that raised the error. */
|
|
523
551
|
source: string;
|
|
552
|
+
/** The underlying thrown value, when available. */
|
|
524
553
|
originalError?: unknown;
|
|
525
554
|
};
|
|
526
555
|
|
|
527
556
|
/**
|
|
528
557
|
* Base row shape. Each key is a column ID, each value is the cell data.
|
|
529
|
-
* Extend this with your own type
|
|
558
|
+
* Extend this with your own type via the `<DataEditor<TRow>>` generic for
|
|
559
|
+
* type-safe column access. When the generic is omitted, rows are typed as
|
|
560
|
+
* `Record<string, unknown>`.
|
|
530
561
|
*
|
|
531
562
|
* @example
|
|
532
563
|
* ```ts
|
|
@@ -535,7 +566,9 @@ type UpdogError = {
|
|
|
535
566
|
* ```
|
|
536
567
|
*/
|
|
537
568
|
type DataEditorRow = Record<string, unknown>;
|
|
569
|
+
/** Sort direction. */
|
|
538
570
|
type SortDirection = "asc" | "desc";
|
|
571
|
+
/** Current sort state. `null` means no active sort. */
|
|
539
572
|
type SortState = {
|
|
540
573
|
columnId: string;
|
|
541
574
|
direction: SortDirection;
|
|
@@ -565,6 +598,14 @@ type Filters = {
|
|
|
565
598
|
}>;
|
|
566
599
|
};
|
|
567
600
|
|
|
601
|
+
/**
|
|
602
|
+
* A single chunk in the stream returned from `DataEditorChat.onMessage`.
|
|
603
|
+
*
|
|
604
|
+
* - `status` — progress message shown while processing (e.g. "Analyzing 500 rows...").
|
|
605
|
+
* - `message` — chat reply shown to the user.
|
|
606
|
+
* - `rows` — updated rows to apply to the grid. Matched by `primaryKey`.
|
|
607
|
+
* - `transform` — description of the transform applied.
|
|
608
|
+
*/
|
|
568
609
|
type ChatResponseChunk<TRow extends DataEditorRow = DataEditorRow> = {
|
|
569
610
|
type: "status";
|
|
570
611
|
content: string;
|
|
@@ -578,37 +619,105 @@ type ChatResponseChunk<TRow extends DataEditorRow = DataEditorRow> = {
|
|
|
578
619
|
type: "transform";
|
|
579
620
|
content: string;
|
|
580
621
|
};
|
|
622
|
+
/** Status of a row in the chat sample, relative to its origin snapshot. */
|
|
581
623
|
type ChatRowStatus = "new" | "edited" | "original";
|
|
624
|
+
/** A sample row handed to the chat callback, with its current status and validation errors. */
|
|
582
625
|
type ChatRow<TRow extends DataEditorRow = DataEditorRow> = {
|
|
626
|
+
/** Row data keyed by column ID. */
|
|
583
627
|
data: TRow;
|
|
628
|
+
/** Whether the row was newly added, edited, or is unchanged. */
|
|
584
629
|
status: ChatRowStatus;
|
|
630
|
+
/** Validation errors keyed by column ID. */
|
|
585
631
|
errors: Record<string, string[]>;
|
|
632
|
+
/** The source this row belongs to. */
|
|
586
633
|
source: string;
|
|
587
634
|
};
|
|
635
|
+
/** Aggregated error count across the current view, grouped by field and message. */
|
|
588
636
|
type ChatErrorSummary = {
|
|
637
|
+
/** Column ID where the error occurred. */
|
|
589
638
|
field: string;
|
|
639
|
+
/** The validation message. */
|
|
590
640
|
message: string;
|
|
641
|
+
/** How many rows hit this error. */
|
|
591
642
|
count: number;
|
|
643
|
+
/** A few example values that triggered the error. */
|
|
592
644
|
examples: string[];
|
|
593
645
|
};
|
|
646
|
+
/**
|
|
647
|
+
* Context about the current dataset, passed to `loadSuggestions` and extended
|
|
648
|
+
* into `ChatContext` for `onMessage`.
|
|
649
|
+
*/
|
|
594
650
|
type ChatDataContext<TRow extends DataEditorRow = DataEditorRow> = {
|
|
651
|
+
/** Full column definitions. */
|
|
595
652
|
columns: DataEditorColumn[];
|
|
653
|
+
/** Row identifier field. */
|
|
596
654
|
primaryKey: keyof TRow;
|
|
655
|
+
/** Total rows in the dataset. */
|
|
597
656
|
totalRowCount: number;
|
|
657
|
+
/** Rows in the current filtered view. */
|
|
598
658
|
filteredRowCount: number;
|
|
659
|
+
/** Sample rows, with status and errors. Size controlled by `sampleSize`. */
|
|
599
660
|
sample: ChatRow<TRow>[];
|
|
661
|
+
/** Aggregated error counts by field and message. */
|
|
600
662
|
errorSummary: ChatErrorSummary[];
|
|
663
|
+
/** Access all rows. Use for full-dataset operations. */
|
|
601
664
|
getRows: () => ChatRow<TRow>[];
|
|
602
665
|
};
|
|
666
|
+
/**
|
|
667
|
+
* The full context passed to `DataEditorChat.onMessage` when the user sends
|
|
668
|
+
* a prompt. Includes the prompt itself and all dataset context.
|
|
669
|
+
*/
|
|
603
670
|
type ChatContext<TRow extends DataEditorRow = DataEditorRow> = ChatDataContext<TRow> & {
|
|
671
|
+
/** The user's chat prompt. */
|
|
604
672
|
message: string;
|
|
605
673
|
};
|
|
674
|
+
/**
|
|
675
|
+
* Bring-your-own-AI chat configuration. When provided via the `chat` prop,
|
|
676
|
+
* the editor shows a chat panel alongside the grid. You own the AI
|
|
677
|
+
* integration; the SDK hands you dataset context and renders the streamed
|
|
678
|
+
* response.
|
|
679
|
+
*
|
|
680
|
+
* @example
|
|
681
|
+
* ```ts
|
|
682
|
+
* chat={{
|
|
683
|
+
* sampleSize: 50,
|
|
684
|
+
* onMessage: async function* (context) {
|
|
685
|
+
* yield { type: "status", content: "Thinking..." };
|
|
686
|
+
* const res = await fetch("/api/ai", {
|
|
687
|
+
* method: "POST",
|
|
688
|
+
* body: JSON.stringify({
|
|
689
|
+
* prompt: context.message,
|
|
690
|
+
* columns: context.columns,
|
|
691
|
+
* sample: context.sample.map(r => r.data),
|
|
692
|
+
* errors: context.errorSummary,
|
|
693
|
+
* }),
|
|
694
|
+
* }).then(r => r.json());
|
|
695
|
+
* yield { type: "rows", content: res.updatedRows };
|
|
696
|
+
* yield { type: "transform", content: res.description };
|
|
697
|
+
* yield { type: "message", content: res.reply };
|
|
698
|
+
* },
|
|
699
|
+
* }}
|
|
700
|
+
* ```
|
|
701
|
+
*/
|
|
606
702
|
type DataEditorChat<TRow extends DataEditorRow = DataEditorRow> = {
|
|
703
|
+
/**
|
|
704
|
+
* How many rows to include in the context sample. The SDK picks a
|
|
705
|
+
* representative slice including rows with errors. When omitted, the SDK decides.
|
|
706
|
+
*/
|
|
607
707
|
sampleSize?: number;
|
|
708
|
+
/** Title shown above the suggestion list when the chat is empty. */
|
|
608
709
|
emptyTitle?: string;
|
|
710
|
+
/** How many suggestions to request from `loadSuggestions`. */
|
|
609
711
|
suggestionsCount?: number;
|
|
712
|
+
/** Optional prompt generator for the empty-state suggestion chips. */
|
|
610
713
|
loadSuggestions?: (context: ChatDataContext<TRow>) => Promise<string[]>;
|
|
714
|
+
/**
|
|
715
|
+
* Called when the user sends a message. Receives full dataset context.
|
|
716
|
+
* Returns an async iterable of response chunks — the SDK streams them
|
|
717
|
+
* into the UI.
|
|
718
|
+
*/
|
|
611
719
|
onMessage: (context: ChatContext<TRow>) => AsyncIterable<ChatResponseChunk<TRow>>;
|
|
720
|
+
/** Called when the user cancels a pending request. Use to abort your API call. */
|
|
612
721
|
onCancel?: () => void;
|
|
613
722
|
};
|
|
614
723
|
|
|
@@ -627,8 +736,13 @@ type ValidationError = {
|
|
|
627
736
|
* A function that validates a single cell value.
|
|
628
737
|
* Return a `ValidationError` to flag a problem, or `null` if the value is valid.
|
|
629
738
|
*
|
|
739
|
+
* A `ValidationError` with `level: "error"` flags the cell in the grid but
|
|
740
|
+
* does not block submission — invalid rows are delivered to `onComplete`
|
|
741
|
+
* alongside valid ones, tagged via the `isValid` flag.
|
|
742
|
+
*
|
|
630
743
|
* Built-in validator factories: `required(msg)`, `numeric(msg)`, `email(msg)`,
|
|
631
|
-
* `date(msg)`, `endDateAfterStart(startField, msg)`.
|
|
744
|
+
* `date(msg)`, `oneOf(values, msg)`, `endDateAfterStart(startField, msg)`.
|
|
745
|
+
* Import them from the package root.
|
|
632
746
|
*
|
|
633
747
|
* @param value - The current cell value.
|
|
634
748
|
* @param row - The full row, useful for cross-field checks.
|
|
@@ -712,41 +826,62 @@ type ColumnLockMode = "all" | "default";
|
|
|
712
826
|
*
|
|
713
827
|
* @example
|
|
714
828
|
* ```ts
|
|
829
|
+
* import { required, email, numeric } from "@updog/data-editor";
|
|
830
|
+
*
|
|
715
831
|
* const columns: DataEditorColumn[] = [
|
|
716
|
-
* { id: "name", title: "Full Name",
|
|
717
|
-
* { id: "email", title: "Email",
|
|
718
|
-
* { id: "role", title: "Role", editor: { type: "select", options:
|
|
719
|
-
* { id: "salary", title: "Salary", validate: numeric, formatter: (v) => `$${v}` },
|
|
832
|
+
* { id: "name", title: "Full Name", size: 200, validate: required("Name is required") },
|
|
833
|
+
* { id: "email", title: "Email", size: 250, validate: [required("Email is required"), email("Invalid email")], unique: true },
|
|
834
|
+
* { id: "role", title: "Role", editor: { type: "select", options: ["Admin", "Editor", "Viewer"] } },
|
|
835
|
+
* { id: "salary", title: "Salary", validate: numeric("Must be a number"), formatter: (v) => v ? `$${v}` : "" },
|
|
720
836
|
* ];
|
|
721
837
|
* ```
|
|
722
838
|
*/
|
|
723
839
|
type DataEditorColumn = {
|
|
724
840
|
/** Unique column identifier. Must match the keys in your row data. */
|
|
725
841
|
id: string;
|
|
726
|
-
/** Column header text. */
|
|
842
|
+
/** Column header text shown to the user. */
|
|
727
843
|
title: string;
|
|
728
|
-
/**
|
|
844
|
+
/**
|
|
845
|
+
* One or more validators run on every edit. Each receives the cell value and
|
|
846
|
+
* the full row, and returns a `ValidationError` to flag a problem or `null`
|
|
847
|
+
* if valid. Errors do not block submission — see `CellValidator`.
|
|
848
|
+
*/
|
|
729
849
|
validate?: CellValidator | CellValidator[];
|
|
730
|
-
/**
|
|
850
|
+
/**
|
|
851
|
+
* When `true`, the editor flags duplicate values in this column as errors.
|
|
852
|
+
* The error message is localized via the `translations` prop
|
|
853
|
+
* (`dataEditor.validation.valueMustBeUnique`).
|
|
854
|
+
*/
|
|
731
855
|
unique?: boolean;
|
|
732
|
-
/**
|
|
856
|
+
/**
|
|
857
|
+
* Column IDs to revalidate when this column changes. Use for cross-field
|
|
858
|
+
* rules like "end date must be after start date".
|
|
859
|
+
*/
|
|
733
860
|
dependentFields?: string[];
|
|
734
861
|
/** Format the display value without changing stored data. E.g. add `$` prefix. */
|
|
735
862
|
formatter?: (value: string) => string;
|
|
736
|
-
/**
|
|
863
|
+
/**
|
|
864
|
+
* Transform a value before it enters the store. Runs when rows are uploaded
|
|
865
|
+
* to the data editor.
|
|
866
|
+
*/
|
|
737
867
|
transformer?: (value: unknown) => unknown;
|
|
738
868
|
/** How the cell is edited. Defaults to text input. */
|
|
739
869
|
editor?: CellEditor;
|
|
740
870
|
/** Adds a filter control for this column in the sidebar Filters panel. */
|
|
741
871
|
filter?: ColumnFilter;
|
|
742
|
-
/**
|
|
872
|
+
/**
|
|
873
|
+
* Whether this column can be pinned to the left (right in RTL) via the
|
|
874
|
+
* header context menu. @default true
|
|
875
|
+
*/
|
|
743
876
|
pinnable?: boolean;
|
|
744
877
|
/** Column width in pixels. @default 150 */
|
|
745
878
|
size?: number;
|
|
746
879
|
/**
|
|
747
|
-
* Controls whether cells in this column are locked.
|
|
748
|
-
*
|
|
749
|
-
* - `"
|
|
880
|
+
* Controls whether cells in this column are locked. A column locked via
|
|
881
|
+
* configuration cannot be unlocked from the UI.
|
|
882
|
+
* - `true` | `"all"` — locked for every row.
|
|
883
|
+
* - `"default"` — locked only for default-source rows; rows added manually,
|
|
884
|
+
* duplicated, or imported remain editable.
|
|
750
885
|
* - `false` | `undefined` — not locked.
|
|
751
886
|
*/
|
|
752
887
|
locked?: boolean | ColumnLockMode;
|
|
@@ -1146,9 +1281,12 @@ type DataEditorServer<TRow extends DataEditorRow = DataEditorRow, TFilters = Rec
|
|
|
1146
1281
|
scrollSensitivity?: number;
|
|
1147
1282
|
};
|
|
1148
1283
|
|
|
1149
|
-
/**
|
|
1284
|
+
/**
|
|
1285
|
+
* Actions available inside the `onComplete` callback. Call `reset()` after a
|
|
1286
|
+
* successful save to clear the editor and re-fetch via `loadData`.
|
|
1287
|
+
*/
|
|
1150
1288
|
type DataEditorActions = {
|
|
1151
|
-
/** Discard all changes and reload the original data
|
|
1289
|
+
/** Discard all changes and reload the original data via `loadData`. */
|
|
1152
1290
|
reset: () => void;
|
|
1153
1291
|
};
|
|
1154
1292
|
/**
|
|
@@ -1208,31 +1346,55 @@ type DataEditorResult<TRow extends DataEditorRow = DataEditorRow> = {
|
|
|
1208
1346
|
invalid: number;
|
|
1209
1347
|
};
|
|
1210
1348
|
};
|
|
1349
|
+
/**
|
|
1350
|
+
* Options used to tag a chunk passed to `loadData`'s `onChunk` callback.
|
|
1351
|
+
* Sources are auto-registered on first encounter. Chunks without options go
|
|
1352
|
+
* to "Existing Data".
|
|
1353
|
+
*/
|
|
1211
1354
|
type ChunkSourceOptions = {
|
|
1212
|
-
/** Display name for this data source. */
|
|
1355
|
+
/** Display name for this data source. Required when tagging a source. */
|
|
1213
1356
|
source: string;
|
|
1214
|
-
/** Stable identifier. Defaults to `source` value when omitted. */
|
|
1357
|
+
/** Stable identifier. Defaults to the `source` value when omitted. */
|
|
1215
1358
|
id?: string;
|
|
1216
|
-
/**
|
|
1359
|
+
/** Whether the user can delete this source from the editor. @default false */
|
|
1217
1360
|
deletable?: boolean;
|
|
1218
|
-
/**
|
|
1361
|
+
/**
|
|
1362
|
+
* Marks this source as finished loading. Shows a completion state in the
|
|
1363
|
+
* UI. When the `loadData` promise resolves, any source still loading is
|
|
1364
|
+
* automatically finalized.
|
|
1365
|
+
* @default false
|
|
1366
|
+
*/
|
|
1219
1367
|
done?: boolean;
|
|
1220
1368
|
};
|
|
1221
1369
|
type DataSourceId = string;
|
|
1222
|
-
/**
|
|
1370
|
+
/**
|
|
1371
|
+
* Per-column input to `onValueMatch`: the unique imported values from the
|
|
1372
|
+
* file and the allowed select options defined on the column.
|
|
1373
|
+
*/
|
|
1223
1374
|
type ValueMatchInput = {
|
|
1375
|
+
/** Distinct values seen in the imported file for this column. */
|
|
1224
1376
|
importedValues: string[];
|
|
1377
|
+
/** Allowed options from the column's `select` editor. */
|
|
1225
1378
|
options: string[];
|
|
1226
1379
|
};
|
|
1227
|
-
/**
|
|
1380
|
+
/**
|
|
1381
|
+
* Per-column result from `onValueMatch`. Outer key = column ID, inner key =
|
|
1382
|
+
* imported value, inner value = chosen option, or `null` to skip
|
|
1383
|
+
* auto-matching for that value. Unmapped values fall back to built-in fuzzy
|
|
1384
|
+
* matching.
|
|
1385
|
+
*/
|
|
1228
1386
|
type ValueMatchOutput = Record<string, Record<string, string | null>>;
|
|
1229
|
-
/** File formats supported for import and export. */
|
|
1387
|
+
/** File formats supported for import and export. Shared by `importFormats` and `exportFormats`. */
|
|
1230
1388
|
type DataEditorFormat = "csv" | "tsv" | "xlsx" | "json" | "xml";
|
|
1231
1389
|
/**
|
|
1232
1390
|
* A client-defined remote data source (e.g. Google Sheets, S3, Dropbox).
|
|
1233
1391
|
*
|
|
1234
|
-
* The SDK renders a button per source and calls `fetch()`
|
|
1235
|
-
*
|
|
1392
|
+
* The SDK renders a button per source on the upload step and calls `fetch()`
|
|
1393
|
+
* when clicked. You own all integration complexity — auth, pickers,
|
|
1394
|
+
* downloads. The SDK just processes the result.
|
|
1395
|
+
*
|
|
1396
|
+
* Return a `File` to go through the standard parse pipeline
|
|
1397
|
+
* (CSV/XLSX/JSON/XML), or return structured records to skip parsing entirely.
|
|
1236
1398
|
*
|
|
1237
1399
|
* @example
|
|
1238
1400
|
* ```ts
|
|
@@ -1256,7 +1418,10 @@ type RemoteSource = {
|
|
|
1256
1418
|
icon: string;
|
|
1257
1419
|
/** Optional tooltip text. */
|
|
1258
1420
|
description?: string;
|
|
1259
|
-
/**
|
|
1421
|
+
/**
|
|
1422
|
+
* Returns a `File` (parsed via the CSV/XLSX/JSON/XML pipeline) or structured
|
|
1423
|
+
* records (used directly, skipping parse).
|
|
1424
|
+
*/
|
|
1260
1425
|
fetch: () => Promise<File | Record<string, unknown>[]>;
|
|
1261
1426
|
};
|
|
1262
1427
|
/**
|
|
@@ -1271,8 +1436,9 @@ type DataEditorBaseProps<TRow extends DataEditorRow = DataEditorRow> = {
|
|
|
1271
1436
|
/** Column definitions. Each entry describes one column in the grid. */
|
|
1272
1437
|
columns: DataEditorColumn[];
|
|
1273
1438
|
/**
|
|
1274
|
-
* The column ID used to uniquely identify each row (e.g. `"id"` or
|
|
1275
|
-
* Used to match imported rows
|
|
1439
|
+
* The column ID used to uniquely identify each row (e.g. `"id"` or
|
|
1440
|
+
* `"employeeId"`). Used to match imported rows against existing data and to
|
|
1441
|
+
* track edits.
|
|
1276
1442
|
*/
|
|
1277
1443
|
primaryKey: keyof TRow;
|
|
1278
1444
|
/**
|
|
@@ -1326,37 +1492,78 @@ type DataEditorBaseProps<TRow extends DataEditorRow = DataEditorRow> = {
|
|
|
1326
1492
|
* ```
|
|
1327
1493
|
*/
|
|
1328
1494
|
onComplete?: (result: DataEditorResult<TRow>, actions: DataEditorActions) => void;
|
|
1329
|
-
/**
|
|
1495
|
+
/**
|
|
1496
|
+
* Controls the initial view. `"editor"` opens directly to the spreadsheet
|
|
1497
|
+
* grid. `"uploader"` opens the file import wizard first — the user uploads
|
|
1498
|
+
* a file, maps columns, fixes errors, then continues to the grid.
|
|
1499
|
+
* @default "editor"
|
|
1500
|
+
*/
|
|
1330
1501
|
variant?: DataEditorVariant;
|
|
1331
|
-
/**
|
|
1502
|
+
/**
|
|
1503
|
+
* Override any UI string. Pass a partial object — only the keys you provide
|
|
1504
|
+
* are replaced. Every key lives under `dataEditor`.
|
|
1505
|
+
*/
|
|
1332
1506
|
translations?: DataEditorTranslations;
|
|
1333
|
-
/**
|
|
1507
|
+
/**
|
|
1508
|
+
* BCP 47 locale tag (e.g. `"en"`, `"fr"`, `"ar"`). Used for plural rules
|
|
1509
|
+
* and locale-aware features.
|
|
1510
|
+
* @default "en"
|
|
1511
|
+
*/
|
|
1334
1512
|
locale?: string;
|
|
1335
1513
|
/**
|
|
1336
1514
|
* Controls row deletion via the right-click context menu.
|
|
1337
|
-
* - `false` — deletion disabled
|
|
1515
|
+
* - `false` — deletion disabled.
|
|
1338
1516
|
* - `"new"` — only manually added or imported rows can be deleted.
|
|
1339
1517
|
* - `"all"` — any row can be deleted.
|
|
1340
1518
|
* @default false
|
|
1341
1519
|
*/
|
|
1342
1520
|
enableDeleteRow?: "all" | "new" | false;
|
|
1343
|
-
/**
|
|
1521
|
+
/**
|
|
1522
|
+
* Show the "Add row" button in the data sources panel. When `false`, users
|
|
1523
|
+
* can only edit existing rows or import data — they can't manually add
|
|
1524
|
+
* blank rows.
|
|
1525
|
+
* @default true
|
|
1526
|
+
*/
|
|
1344
1527
|
enableAddRow?: boolean;
|
|
1345
|
-
/**
|
|
1528
|
+
/**
|
|
1529
|
+
* Which file formats the user can import. `undefined` allows all formats,
|
|
1530
|
+
* `[]` disables import entirely.
|
|
1531
|
+
*/
|
|
1346
1532
|
importFormats?: DataEditorFormat[];
|
|
1347
|
-
/**
|
|
1533
|
+
/**
|
|
1534
|
+
* Client-defined remote data sources rendered as buttons on the upload
|
|
1535
|
+
* step. You own the integration; the SDK renders the button and processes
|
|
1536
|
+
* the result of `fetch()`.
|
|
1537
|
+
*/
|
|
1348
1538
|
remoteSources?: RemoteSource[];
|
|
1349
|
-
/**
|
|
1539
|
+
/**
|
|
1540
|
+
* Which file formats the user can export to. `undefined` allows all
|
|
1541
|
+
* formats, `[]` disables export entirely.
|
|
1542
|
+
*/
|
|
1350
1543
|
exportFormats?: DataEditorFormat[];
|
|
1351
1544
|
/** Row height in pixels. @default 34 */
|
|
1352
1545
|
rowHeight?: number;
|
|
1353
1546
|
/** Header row height in pixels. @default 36 */
|
|
1354
1547
|
headerHeight?: number;
|
|
1355
|
-
/**
|
|
1548
|
+
/**
|
|
1549
|
+
* When `true`, hides all editing UI (submit, add row, delete, import). The
|
|
1550
|
+
* grid becomes view-only.
|
|
1551
|
+
* @default false
|
|
1552
|
+
*/
|
|
1356
1553
|
readonly?: boolean;
|
|
1357
|
-
/**
|
|
1554
|
+
/**
|
|
1555
|
+
* Enable right-to-left layout. Set to `true` for Arabic, Hebrew, and other
|
|
1556
|
+
* RTL languages. Affects grid direction, text alignment, and scrollbar
|
|
1557
|
+
* position.
|
|
1558
|
+
* @default false
|
|
1559
|
+
*/
|
|
1358
1560
|
rtl?: boolean;
|
|
1359
|
-
/**
|
|
1561
|
+
/**
|
|
1562
|
+
* Allow creating new columns for unmatched CSV headers during import. When
|
|
1563
|
+
* enabled, users can keep data from columns that don't match your schema by
|
|
1564
|
+
* creating dynamic columns on the fly.
|
|
1565
|
+
* @default true
|
|
1566
|
+
*/
|
|
1360
1567
|
enableCreateColumn?: boolean;
|
|
1361
1568
|
/** Override column matching during import. Return a map of `{ csvHeader: columnId | null }`. Unmapped or `null` entries fall back to built-in matching. */
|
|
1362
1569
|
onColumnMatch?: (headers: string[], columns: DataEditorColumn[]) => Record<string, string | null> | Promise<Record<string, string | null>>;
|
|
@@ -1377,30 +1584,63 @@ type DataEditorBaseProps<TRow extends DataEditorRow = DataEditorRow> = {
|
|
|
1377
1584
|
* ```
|
|
1378
1585
|
*/
|
|
1379
1586
|
onValueMatch?: (valuesToMatch: Record<string, ValueMatchInput>) => ValueMatchOutput | Promise<ValueMatchOutput>;
|
|
1380
|
-
/**
|
|
1587
|
+
/**
|
|
1588
|
+
* Extra synonyms for column auto-matching. Keys are column IDs, values are
|
|
1589
|
+
* alternative names the matching engine should recognize.
|
|
1590
|
+
*
|
|
1591
|
+
* @example
|
|
1592
|
+
* ```ts
|
|
1593
|
+
* synonyms={{
|
|
1594
|
+
* productSku: ["sku", "article_no", "item_code"],
|
|
1595
|
+
* firstName: ["first", "given_name", "fname"],
|
|
1596
|
+
* }}
|
|
1597
|
+
* ```
|
|
1598
|
+
*/
|
|
1381
1599
|
synonyms?: Record<string, string[]>;
|
|
1382
|
-
/**
|
|
1600
|
+
/**
|
|
1601
|
+
* Sample rows included in the "Download Example" file. When the user clicks
|
|
1602
|
+
* "Download Example" in the import wizard, the SDK generates a template
|
|
1603
|
+
* file with column headers and these rows. When omitted, a generic example
|
|
1604
|
+
* row is auto-generated from column definitions.
|
|
1605
|
+
*/
|
|
1383
1606
|
sampleData?: Record<string, unknown>[];
|
|
1384
|
-
/**
|
|
1607
|
+
/**
|
|
1608
|
+
* Bring-your-own-AI chat. When provided, a chat panel is shown alongside
|
|
1609
|
+
* the grid. You own the AI integration; the SDK hands you dataset context
|
|
1610
|
+
* and renders the streamed response.
|
|
1611
|
+
*/
|
|
1385
1612
|
chat?: DataEditorChat<TRow>;
|
|
1386
1613
|
};
|
|
1387
1614
|
/**
|
|
1388
|
-
* Controls what the editor stores in `localStorage`.
|
|
1389
|
-
*
|
|
1615
|
+
* Controls what the editor stores in `localStorage`. Set to `false` to
|
|
1616
|
+
* disable all local storage usage.
|
|
1617
|
+
*
|
|
1618
|
+
* @default { licenseGrant: true }
|
|
1390
1619
|
*/
|
|
1391
1620
|
type DataEditorLocalStorage = false | {
|
|
1392
|
-
/**
|
|
1621
|
+
/**
|
|
1622
|
+
* Cache the license validation result to skip re-validation on reload.
|
|
1623
|
+
* @default true
|
|
1624
|
+
*/
|
|
1393
1625
|
licenseGrant?: boolean;
|
|
1394
1626
|
};
|
|
1395
1627
|
/** Shared props present in both modal and inline modes. */
|
|
1396
1628
|
type DataEditorCommonProps<TRow extends DataEditorRow = DataEditorRow> = DataEditorBaseProps<TRow> & {
|
|
1397
|
-
/** Your Updog license key.
|
|
1629
|
+
/** Your Updog license key. Validated on each open. */
|
|
1398
1630
|
apiKey: string;
|
|
1399
|
-
/**
|
|
1631
|
+
/**
|
|
1632
|
+
* Controls what the editor stores in `localStorage`. Set to `false` to
|
|
1633
|
+
* disable all local storage usage.
|
|
1634
|
+
* @default { licenseGrant: true }
|
|
1635
|
+
*/
|
|
1400
1636
|
localStorage?: DataEditorLocalStorage;
|
|
1401
|
-
/** CSS class added to the wrapper element. */
|
|
1637
|
+
/** CSS class added to the wrapper element. Use for scoped styling overrides. */
|
|
1402
1638
|
className?: string;
|
|
1403
|
-
/**
|
|
1639
|
+
/**
|
|
1640
|
+
* Called when the SDK catches an internal error. The SDK recovers
|
|
1641
|
+
* gracefully where possible — use this for your logging and monitoring
|
|
1642
|
+
* (Sentry, Datadog, etc.). Client mode only.
|
|
1643
|
+
*/
|
|
1404
1644
|
onError?: (error: UpdogError) => void;
|
|
1405
1645
|
};
|
|
1406
1646
|
/**
|
|
@@ -1410,9 +1650,16 @@ type DataEditorCommonProps<TRow extends DataEditorRow = DataEditorRow> = DataEdi
|
|
|
1410
1650
|
type DataEditorModalProps<TRow extends DataEditorRow = DataEditorRow> = DataEditorCommonProps<TRow> & {
|
|
1411
1651
|
/** Rendering mode. @default "modal" */
|
|
1412
1652
|
mode?: "modal";
|
|
1413
|
-
/**
|
|
1653
|
+
/**
|
|
1654
|
+
* Controls modal visibility. This is a controlled prop — you manage the
|
|
1655
|
+
* state. Required in modal mode.
|
|
1656
|
+
*/
|
|
1414
1657
|
open: boolean;
|
|
1415
|
-
/**
|
|
1658
|
+
/**
|
|
1659
|
+
* Called when the user closes the modal (X button or Escape key). If the
|
|
1660
|
+
* user has unsaved changes, the SDK shows a confirmation dialog before
|
|
1661
|
+
* calling `onClose`. Required in modal mode.
|
|
1662
|
+
*/
|
|
1416
1663
|
onClose: () => void;
|
|
1417
1664
|
};
|
|
1418
1665
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@updog/data-editor-wc",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Web Component wrapper for @updog/data-editor. Framework-agnostic, zero dependencies.",
|
|
5
5
|
"author": "Mikhail Kutateladze <admin@updog.tech>",
|
|
6
6
|
"homepage": "https://updog.tech",
|