@marimo-team/islands 0.23.2-dev61 → 0.23.2-dev63
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/dist/{glide-data-editor-CVtY_KYw.js → glide-data-editor-DXti2axL.js} +20 -14
- package/dist/main.js +6 -6
- package/dist/{reveal-component-CA7oaSt2.js → reveal-component-CATTJBrD.js} +1 -1
- package/dist/{slide-BiyYep36.js → slide-Du4Jfizg.js} +9 -5
- package/package.json +1 -1
- package/src/components/data-table/__tests__/chart-spec-model.test.ts +14 -14
- package/src/components/data-table/__tests__/types.test.ts +34 -1
- package/src/components/data-table/column-summary/chart-spec-model.tsx +6 -3
- package/src/components/data-table/column-summary/column-summary.tsx +1 -1
- package/src/components/data-table/types.ts +9 -7
- package/src/plugins/impl/DataEditorPlugin.tsx +7 -3
- package/src/plugins/impl/data-editor/__tests__/data-utils.test.ts +147 -149
- package/src/plugins/impl/data-editor/data-utils.ts +12 -11
- package/src/plugins/impl/data-editor/glide-data-editor.tsx +4 -4
|
@@ -10596,7 +10596,9 @@ let __tla = (async () => {
|
|
|
10596
10596
|
switch (i) {
|
|
10597
10597
|
case "insert": {
|
|
10598
10598
|
if (!o) return Logger.error("newName is required for insert"), t;
|
|
10599
|
-
let e2 =
|
|
10599
|
+
let e2 = [
|
|
10600
|
+
...t.entries()
|
|
10601
|
+
], i2 = [
|
|
10600
10602
|
...e2.slice(0, r),
|
|
10601
10603
|
[
|
|
10602
10604
|
o,
|
|
@@ -10604,21 +10606,25 @@ let __tla = (async () => {
|
|
|
10604
10606
|
],
|
|
10605
10607
|
...e2.slice(r)
|
|
10606
10608
|
];
|
|
10607
|
-
return
|
|
10609
|
+
return new Map(i2);
|
|
10608
10610
|
}
|
|
10609
10611
|
case "remove": {
|
|
10610
|
-
if (r < 0 || r >=
|
|
10611
|
-
let e2 = (_a =
|
|
10612
|
+
if (r < 0 || r >= t.size) return t;
|
|
10613
|
+
let e2 = (_a = [
|
|
10614
|
+
...t.entries()
|
|
10615
|
+
][r]) == null ? void 0 : _a[0];
|
|
10612
10616
|
if (e2) {
|
|
10613
|
-
let
|
|
10614
|
-
return
|
|
10617
|
+
let n = new Map(t);
|
|
10618
|
+
return n.delete(e2), n;
|
|
10615
10619
|
}
|
|
10616
10620
|
return t;
|
|
10617
10621
|
}
|
|
10618
10622
|
case "rename": {
|
|
10619
10623
|
if (!o) return Logger.error("newName is required for rename"), t;
|
|
10620
|
-
if (r < 0 || r >=
|
|
10621
|
-
let e2 =
|
|
10624
|
+
if (r < 0 || r >= t.size) return t;
|
|
10625
|
+
let e2 = [
|
|
10626
|
+
...t.entries()
|
|
10627
|
+
], i2 = [
|
|
10622
10628
|
...e2.slice(0, r),
|
|
10623
10629
|
[
|
|
10624
10630
|
o,
|
|
@@ -10626,7 +10632,7 @@ let __tla = (async () => {
|
|
|
10626
10632
|
],
|
|
10627
10633
|
...e2.slice(r + 1)
|
|
10628
10634
|
];
|
|
10629
|
-
return
|
|
10635
|
+
return new Map(i2);
|
|
10630
10636
|
}
|
|
10631
10637
|
}
|
|
10632
10638
|
}
|
|
@@ -10642,7 +10648,7 @@ let __tla = (async () => {
|
|
|
10642
10648
|
let [H, U] = (0, import_react.useState)(Wr), Gr = useNonce(), W = (0, import_react.useRef)(false), G;
|
|
10643
10649
|
if (t[2] !== o || t[3] !== H || t[4] !== f || t[5] !== k) {
|
|
10644
10650
|
G = [];
|
|
10645
|
-
for (let [e2, t2] of
|
|
10651
|
+
for (let [e2, t2] of o) {
|
|
10646
10652
|
let n2 = f === "all" || f.includes(e2);
|
|
10647
10653
|
G.push({
|
|
10648
10654
|
id: e2,
|
|
@@ -10765,8 +10771,8 @@ let __tla = (async () => {
|
|
|
10765
10771
|
}, t[24] = Qr) : Qr = t[24];
|
|
10766
10772
|
let $r = Qr, ei;
|
|
10767
10773
|
t[25] !== o || t[26] !== K ? (ei = (e2, t2, n2) => {
|
|
10768
|
-
let [r] = e2, i =
|
|
10769
|
-
bb134: switch (
|
|
10774
|
+
let [r] = e2, i = K[r].title, a2 = o.get(i);
|
|
10775
|
+
bb134: switch (a2) {
|
|
10770
10776
|
case "number":
|
|
10771
10777
|
case "integer":
|
|
10772
10778
|
if (Number.isNaN(Number(t2.data))) return false;
|
|
@@ -10854,7 +10860,7 @@ let __tla = (async () => {
|
|
|
10854
10860
|
t[48] !== o || t[49] !== K || t[50] !== N || t[51] !== x || t[52] !== u || t[53] !== a ? (pi = (e2) => {
|
|
10855
10861
|
if (N) {
|
|
10856
10862
|
let t2 = K[N.col].title;
|
|
10857
|
-
if (o
|
|
10863
|
+
if (o.has(e2)) {
|
|
10858
10864
|
fi(e2);
|
|
10859
10865
|
return;
|
|
10860
10866
|
}
|
|
@@ -10881,7 +10887,7 @@ let __tla = (async () => {
|
|
|
10881
10887
|
let { direction: t2, columnName: n2, dataType: r } = e2;
|
|
10882
10888
|
if (N) {
|
|
10883
10889
|
let e3 = N.col + (t2 === "left" ? 0 : 1), i = Math.max(0, Math.min(e3, K.length));
|
|
10884
|
-
if (o
|
|
10890
|
+
if (o.has(n2)) {
|
|
10885
10891
|
fi(n2);
|
|
10886
10892
|
return;
|
|
10887
10893
|
}
|
package/dist/main.js
CHANGED
|
@@ -26,7 +26,7 @@ import { An as FileText, B as safeExtractSetUIElementMessageBuffers, C as Accord
|
|
|
26
26
|
import { __tla as __tla_1 } from "./chunk-5FQGJX7Z-CO1e63h_.js";
|
|
27
27
|
import { o as useSize, s as Root$3, u as createLucideIcon } from "./dist-ESg7xyoD.js";
|
|
28
28
|
import { A as SquareFunction, C as DEFAULT_COLOR_SCHEME, D as SCALE_TYPE_DESCRIPTIONS, E as EMPTY_VALUE$1, O as TIME_UNIT_DESCRIPTIONS, S as DEFAULT_AGGREGATION, T as DEFAULT_TIME_UNIT, _ as AGGREGATION_TYPE_DESCRIPTIONS, a as AGGREGATION_FNS$1, b as COLOR_SCHEMES, c as COLOR_BY_FIELDS, d as NONE_VALUE, f as SELECTABLE_DATA_TYPES, g as TIME_UNITS, h as STRING_AGGREGATION_FNS, i as convertDataTypeToSelectable, j as ChartColumn, k as escapeFieldName, l as COMBINED_TIME_UNITS, m as SORT_TYPES, n as createSpecWithoutData, o as BIN_AGGREGATION, p as SINGLE_TIME_UNITS, r as isFieldSet, s as CHART_TYPES, t as augmentSpecWithData, u as ChartType, v as AGGREGATION_TYPE_ICON, w as DEFAULT_MAX_BINS_FACET, x as COUNT_FIELD, y as CHART_TYPE_ICON } from "./spec-BKWq0wn2.js";
|
|
29
|
-
import { $ as CommandItem, A as TableHeader, B as usePrevious$1, C as Toggle, Ct as Download, D as TableBody, Dt as ChevronsDownUp, E as Table, Et as ChevronsLeft, F as renderCellValue, G as SELECT_COLUMN_ID, H as loadTableAndRawData, I as ColumnChartContext, J as getMimeValues, K as TOO_MANY_ROWS, L as ColumnChartSpecModel, M as NAMELESS_COLUMN_PREFIX, N as generateColumns, O as TableCell, Ot as ChevronLeft, P as inferFieldTypes, Q as CommandInput, R as DelayMount, S as slotsController, St as Ellipsis, T as Provider$1, Tt as ChevronsRight, U as loadTableData, V as getPageIndexForRow, W as INDEX_COLUMN_NAME, X as Command, Y as filtersToFilterGroup, Z as CommandEmpty, _ as contextAwarePanelOpen, _t as isStaticNotebook, a as DataTable, at as TabsList, b as isCellAwareAtom, bt as Funnel, c as downloadBlob, ct as ChartInfoState, d as Filenames, dt as useOverflowDetection, et as CommandList, f as prettifyRowColumnCount, ft as RenderTextWithLinks, g as PANEL_TYPES, gt as getStaticVirtualFiles, h as ContextAwarePanelItem, ht as EmotionCacheProvider, i as OutputRenderer, it as TabsContent, j as TableRow, k as TableHead, kt as ArrowDownWideNarrow, l as downloadByURL, lt as ChartLoadingState, m as useInternalStateWithSync, mt as HtmlOutput, n as JsonOutput, nt as Maps, o as InstallPackageButton, ot as TabsTrigger, p as prettifyRowCount, pt as Kbd, q as toFieldTypes, r as OutputArea, rt as Tabs, s as ADD_PRINTING_CLASS, st as ChartErrorState, t as Slide, tt as CommandSeparator, u as downloadHTMLAsImage, ut as LazyVegaEmbed, v as contextAwarePanelOwner, vt as TextWrap, w as Fill, wt as ChevronsUpDown, x as SlotNames, y as contextAwarePanelType, yt as GripHorizontal, z as useIntersectionObserver, __tla as __tla_2 } from "./slide-
|
|
29
|
+
import { $ as CommandItem, A as TableHeader, B as usePrevious$1, C as Toggle, Ct as Download, D as TableBody, Dt as ChevronsDownUp, E as Table, Et as ChevronsLeft, F as renderCellValue, G as SELECT_COLUMN_ID, H as loadTableAndRawData, I as ColumnChartContext, J as getMimeValues, K as TOO_MANY_ROWS, L as ColumnChartSpecModel, M as NAMELESS_COLUMN_PREFIX, N as generateColumns, O as TableCell, Ot as ChevronLeft, P as inferFieldTypes, Q as CommandInput, R as DelayMount, S as slotsController, St as Ellipsis, T as Provider$1, Tt as ChevronsRight, U as loadTableData, V as getPageIndexForRow, W as INDEX_COLUMN_NAME, X as Command, Y as filtersToFilterGroup, Z as CommandEmpty, _ as contextAwarePanelOpen, _t as isStaticNotebook, a as DataTable, at as TabsList, b as isCellAwareAtom, bt as Funnel, c as downloadBlob, ct as ChartInfoState, d as Filenames, dt as useOverflowDetection, et as CommandList, f as prettifyRowColumnCount, ft as RenderTextWithLinks, g as PANEL_TYPES, gt as getStaticVirtualFiles, h as ContextAwarePanelItem, ht as EmotionCacheProvider, i as OutputRenderer, it as TabsContent, j as TableRow, k as TableHead, kt as ArrowDownWideNarrow, l as downloadByURL, lt as ChartLoadingState, m as useInternalStateWithSync, mt as HtmlOutput, n as JsonOutput, nt as Maps, o as InstallPackageButton, ot as TabsTrigger, p as prettifyRowCount, pt as Kbd, q as toFieldTypes, r as OutputArea, rt as Tabs, s as ADD_PRINTING_CLASS, st as ChartErrorState, t as Slide, tt as CommandSeparator, u as downloadHTMLAsImage, ut as LazyVegaEmbed, v as contextAwarePanelOwner, vt as TextWrap, w as Fill, wt as ChevronsUpDown, x as SlotNames, y as contextAwarePanelType, yt as GripHorizontal, z as useIntersectionObserver, __tla as __tla_2 } from "./slide-Du4Jfizg.js";
|
|
30
30
|
import { c as Calendar, i as createReducerAndAtoms, n as useOnUnmount, o as ToggleLeft, r as Badge, t as useOnMount } from "./useLifecycle-smVfjLNI.js";
|
|
31
31
|
import { n as $fb18d541ea1ad717$export$ad991b66133851cf, r as $5a387cc49350e6db$export$722debc0e56fea39, t as $896ba0a80a8f4d36$export$85fd5fdf27bacc79 } from "./useDateFormatter-B3mCQMP3.js";
|
|
32
32
|
import { t as Check } from "./check-CFM2mVDr.js";
|
|
@@ -14012,7 +14012,7 @@ Defaulting to \`null\`.`;
|
|
|
14012
14012
|
"time",
|
|
14013
14013
|
"unknown"
|
|
14014
14014
|
];
|
|
14015
|
-
var import_compiler_runtime$88 = require_compiler_runtime(), LazyDataEditor = import_react.lazy(() => import("./glide-data-editor-
|
|
14015
|
+
var import_compiler_runtime$88 = require_compiler_runtime(), LazyDataEditor = import_react.lazy(() => import("./glide-data-editor-DXti2axL.js").then(async (m) => {
|
|
14016
14016
|
await m.__tla;
|
|
14017
14017
|
return m;
|
|
14018
14018
|
}));
|
|
@@ -14060,12 +14060,12 @@ Defaulting to \`null\`.`;
|
|
|
14060
14060
|
let r = (0, import_compiler_runtime$88.c)(31), c;
|
|
14061
14061
|
r[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel") ? (c = [], r[0] = c) : c = r[0];
|
|
14062
14062
|
let [l, u] = (0, import_react.useState)(c), d;
|
|
14063
|
-
r[1] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel") ? (d =
|
|
14063
|
+
r[1] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel") ? (d = /* @__PURE__ */ new Map(), r[1] = d) : d = r[1];
|
|
14064
14064
|
let [f, p] = (0, import_react.useState)(d), m, h;
|
|
14065
14065
|
r[2] !== e.data || r[3] !== e.fieldTypes ? (m = async () => {
|
|
14066
14066
|
let r2 = toFieldTypes(e.fieldTypes ?? []), c2 = Array.isArray(e.data) ? e.data : await vegaLoadData(e.data, {
|
|
14067
14067
|
type: "csv",
|
|
14068
|
-
parse: getVegaFieldTypes(r2)
|
|
14068
|
+
parse: getVegaFieldTypes(Object.fromEntries(r2))
|
|
14069
14069
|
}, {
|
|
14070
14070
|
handleBigIntAndNumberLike: true
|
|
14071
14071
|
});
|
|
@@ -44992,7 +44992,7 @@ ${c}
|
|
|
44992
44992
|
function asCellId(e) {
|
|
44993
44993
|
return typeof e == "string" ? e : null;
|
|
44994
44994
|
}
|
|
44995
|
-
var import_compiler_runtime$10 = require_compiler_runtime(), LazySlidesComponent = import_react.lazy(() => import("./reveal-component-
|
|
44995
|
+
var import_compiler_runtime$10 = require_compiler_runtime(), LazySlidesComponent = import_react.lazy(() => import("./reveal-component-CATTJBrD.js"));
|
|
44996
44996
|
const SlidesLayoutRenderer = (e) => {
|
|
44997
44997
|
var _a3;
|
|
44998
44998
|
let r = (0, import_compiler_runtime$10.c)(20), { cells: c, mode: l } = e, u = l === "read", d = useAtomValue(numColumnsAtom) > 1, [f, p] = (0, import_react.useState)(null), m = (0, import_react.useRef)(null), h, g;
|
|
@@ -45525,7 +45525,7 @@ ${c}
|
|
|
45525
45525
|
return Logger.warn("Failed to get version from mount config"), null;
|
|
45526
45526
|
}
|
|
45527
45527
|
}
|
|
45528
|
-
const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.23.2-
|
|
45528
|
+
const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.23.2-dev63"), showCodeInRunModeAtom = atom(true);
|
|
45529
45529
|
atom(null);
|
|
45530
45530
|
var VIRTUAL_FILE_REGEX = /\/@file\/([^\s"&'/]+)\.([\dA-Za-z]+)/g, VirtualFileTracker = class e {
|
|
45531
45531
|
constructor() {
|
|
@@ -8,7 +8,7 @@ import { t as require_react } from "./react-DA-nE2FX.js";
|
|
|
8
8
|
import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
|
|
9
9
|
import "./html-to-image-BdsDysfl.js";
|
|
10
10
|
import "./chunk-5FQGJX7Z-CO1e63h_.js";
|
|
11
|
-
import { t as Slide, xt as Expand } from "./slide-
|
|
11
|
+
import { t as Slide, xt as Expand } from "./slide-Du4Jfizg.js";
|
|
12
12
|
import "./input-Drx1pguW.js";
|
|
13
13
|
import "./toDate-yqOcZ_tY.js";
|
|
14
14
|
import "./react-dom-BWRJ_g_k.js";
|
|
@@ -15548,7 +15548,10 @@ Database schema: ${n}`), (_a3 = t2.aiFix) == null ? void 0 : _a3.setAiCompletion
|
|
|
15548
15548
|
if (Array.isArray(e) && e.every(isMimeValue)) return e.map((e2) => e2);
|
|
15549
15549
|
};
|
|
15550
15550
|
toFieldTypes = function(e) {
|
|
15551
|
-
return
|
|
15551
|
+
return new Map(e.map(([e2, [t]]) => [
|
|
15552
|
+
e2,
|
|
15553
|
+
t
|
|
15554
|
+
]));
|
|
15552
15555
|
};
|
|
15553
15556
|
SELECT_COLUMN_ID = "__select__";
|
|
15554
15557
|
INDEX_COLUMN_NAME = "_marimo_row_id";
|
|
@@ -16818,7 +16821,7 @@ Database schema: ${n}`), (_a3 = t2.aiFix) == null ? void 0 : _a3.setAiCompletion
|
|
|
16818
16821
|
getHeaderSummary(e) {
|
|
16819
16822
|
return {
|
|
16820
16823
|
stats: this.columnStats.get(e),
|
|
16821
|
-
type: this.fieldTypes
|
|
16824
|
+
type: this.fieldTypes.get(e),
|
|
16822
16825
|
spec: this.opts.includeCharts ? this.getVegaSpec(e) : void 0
|
|
16823
16826
|
};
|
|
16824
16827
|
}
|
|
@@ -16850,7 +16853,8 @@ Database schema: ${n}`), (_a3 = t2.aiFix) == null ? void 0 : _a3.setAiCompletion
|
|
|
16850
16853
|
values: t,
|
|
16851
16854
|
name: "bin_values"
|
|
16852
16855
|
});
|
|
16853
|
-
let s = this.createBase(a), c = this.fieldTypes
|
|
16856
|
+
let s = this.createBase(a), c = this.fieldTypes.get(e);
|
|
16857
|
+
if (c === void 0) return null;
|
|
16854
16858
|
switch (e = e.replaceAll(".", "\\."), e = e.replaceAll("[", "\\[").replaceAll("]", "\\]"), e = e.replaceAll(":", "\\:"), c) {
|
|
16855
16859
|
case "date":
|
|
16856
16860
|
case "datetime":
|
|
@@ -17427,7 +17431,7 @@ Database schema: ${n}`), (_a3 = t2.aiFix) == null ? void 0 : _a3.setAiCompletion
|
|
|
17427
17431
|
return logNever(c), null;
|
|
17428
17432
|
}
|
|
17429
17433
|
}
|
|
17430
|
-
}, __publicField(_a, "EMPTY", new _a([],
|
|
17434
|
+
}, __publicField(_a, "EMPTY", new _a([], /* @__PURE__ */ new Map(), {}, {}, {}, {
|
|
17431
17435
|
includeCharts: false
|
|
17432
17436
|
})), _a);
|
|
17433
17437
|
import_compiler_runtime$20 = require_compiler_runtime();
|
|
@@ -17484,7 +17488,7 @@ Database schema: ${n}`), (_a3 = t2.aiFix) == null ? void 0 : _a3.setAiCompletion
|
|
|
17484
17488
|
}), t[15] = i, t[16] = v);
|
|
17485
17489
|
let y = v;
|
|
17486
17490
|
s = "flex flex-col items-center text-xs text-muted-foreground align-end", c = _, d = (() => {
|
|
17487
|
-
if (!h) return null;
|
|
17491
|
+
if (!h || f2 === void 0) return null;
|
|
17488
17492
|
switch (f2) {
|
|
17489
17493
|
case "date":
|
|
17490
17494
|
case "datetime":
|
package/package.json
CHANGED
|
@@ -22,14 +22,14 @@ vi.mock("@/core/runtime/config", () => ({
|
|
|
22
22
|
|
|
23
23
|
describe("ColumnChartSpecModel", () => {
|
|
24
24
|
const mockData = "http://example.com/data.json";
|
|
25
|
-
const mockFieldTypes: FieldTypes =
|
|
26
|
-
date
|
|
27
|
-
number
|
|
28
|
-
integer
|
|
29
|
-
boolean
|
|
30
|
-
string
|
|
31
|
-
datetime
|
|
32
|
-
|
|
25
|
+
const mockFieldTypes: FieldTypes = new Map([
|
|
26
|
+
["date", "date"],
|
|
27
|
+
["number", "number"],
|
|
28
|
+
["integer", "integer"],
|
|
29
|
+
["boolean", "boolean"],
|
|
30
|
+
["string", "string"],
|
|
31
|
+
["datetime", "datetime"],
|
|
32
|
+
]);
|
|
33
33
|
const mockStats: Record<ColumnName, Partial<ColumnHeaderStats>> = {
|
|
34
34
|
date: { min: "2023-01-01", max: "2023-12-31" },
|
|
35
35
|
number: { min: 0, max: 100 },
|
|
@@ -120,9 +120,9 @@ describe("ColumnChartSpecModel", () => {
|
|
|
120
120
|
});
|
|
121
121
|
|
|
122
122
|
it("should handle special characters in column names", () => {
|
|
123
|
-
const specialFieldTypes: FieldTypes =
|
|
124
|
-
"column.with[special:chars]"
|
|
125
|
-
|
|
123
|
+
const specialFieldTypes: FieldTypes = new Map([
|
|
124
|
+
["column.with[special:chars]", "time"],
|
|
125
|
+
]);
|
|
126
126
|
const specialStats: Record<ColumnName, Partial<ColumnHeaderStats>> = {
|
|
127
127
|
"column.with[special:chars]": { min: "2023-01-01", max: "2023-12-31" },
|
|
128
128
|
};
|
|
@@ -267,10 +267,10 @@ describe("ColumnChartSpecModel", () => {
|
|
|
267
267
|
});
|
|
268
268
|
|
|
269
269
|
describe("snapshot with legacy data spec", () => {
|
|
270
|
-
const fieldTypes: FieldTypes =
|
|
270
|
+
const fieldTypes: FieldTypes = new Map([
|
|
271
271
|
...mockFieldTypes,
|
|
272
|
-
a
|
|
273
|
-
|
|
272
|
+
["a", "number"],
|
|
273
|
+
]);
|
|
274
274
|
|
|
275
275
|
it("url data", () => {
|
|
276
276
|
const model = new ColumnChartSpecModel(
|
|
@@ -1,6 +1,39 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
import { describe, expect, it } from "vitest";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
extractTimezone,
|
|
5
|
+
type FieldTypesWithExternalType,
|
|
6
|
+
toFieldTypes,
|
|
7
|
+
} from "../types";
|
|
8
|
+
|
|
9
|
+
describe("toFieldTypes", () => {
|
|
10
|
+
// Regression: https://github.com/marimo-team/marimo/issues/9269.
|
|
11
|
+
// When `FieldTypes` was a plain `Record<string, DataType>`, column
|
|
12
|
+
// names that look like non-negative integers (e.g. "2000", "2010")
|
|
13
|
+
// were hoisted to the front in numeric order by ECMAScript's
|
|
14
|
+
// `OrdinaryOwnPropertyKeys` algorithm. `Map` preserves insertion
|
|
15
|
+
// order for all keys.
|
|
16
|
+
it("preserves insertion order for digit-string column names", () => {
|
|
17
|
+
const input: FieldTypesWithExternalType = [
|
|
18
|
+
["here", ["string", ""]],
|
|
19
|
+
["is", ["string", ""]],
|
|
20
|
+
["a", ["string", ""]],
|
|
21
|
+
["2010", ["number", ""]],
|
|
22
|
+
["column", ["string", ""]],
|
|
23
|
+
["2000", ["number", ""]],
|
|
24
|
+
["set", ["string", ""]],
|
|
25
|
+
];
|
|
26
|
+
expect([...toFieldTypes(input).keys()]).toEqual([
|
|
27
|
+
"here",
|
|
28
|
+
"is",
|
|
29
|
+
"a",
|
|
30
|
+
"2010",
|
|
31
|
+
"column",
|
|
32
|
+
"2000",
|
|
33
|
+
"set",
|
|
34
|
+
]);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
4
37
|
|
|
5
38
|
describe("extractTimezone", () => {
|
|
6
39
|
it("should return undefined when dtype is undefined", () => {
|
|
@@ -41,7 +41,7 @@ export class ColumnChartSpecModel<T> {
|
|
|
41
41
|
|
|
42
42
|
public static readonly EMPTY = new ColumnChartSpecModel(
|
|
43
43
|
[],
|
|
44
|
-
|
|
44
|
+
new Map(),
|
|
45
45
|
{},
|
|
46
46
|
{},
|
|
47
47
|
{},
|
|
@@ -97,7 +97,7 @@ export class ColumnChartSpecModel<T> {
|
|
|
97
97
|
public getHeaderSummary(column: string) {
|
|
98
98
|
return {
|
|
99
99
|
stats: this.columnStats.get(column),
|
|
100
|
-
type: this.fieldTypes
|
|
100
|
+
type: this.fieldTypes.get(column),
|
|
101
101
|
spec: this.opts.includeCharts ? this.getVegaSpec(column) : undefined,
|
|
102
102
|
};
|
|
103
103
|
}
|
|
@@ -141,7 +141,10 @@ export class ColumnChartSpecModel<T> {
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
const base = this.createBase(data);
|
|
144
|
-
const type = this.fieldTypes
|
|
144
|
+
const type = this.fieldTypes.get(column);
|
|
145
|
+
if (type === undefined) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
145
148
|
|
|
146
149
|
// https://github.com/vega/altair/blob/32990a597af7c09586904f40b3f5e6787f752fa5/doc/user_guide/encodings/index.rst#escaping-special-characters-in-column-names
|
|
147
150
|
// escape periods in column names
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import type { RowData } from "@tanstack/react-table";
|
|
4
4
|
import type { DataType } from "@/core/kernel/messages";
|
|
5
|
-
import { Objects } from "@/utils/objects";
|
|
6
5
|
|
|
7
6
|
declare module "@tanstack/react-table" {
|
|
8
7
|
interface TableMeta<TData extends RowData> {
|
|
@@ -56,16 +55,19 @@ export type FieldTypesWithExternalType = [
|
|
|
56
55
|
columnName: string,
|
|
57
56
|
[dataType: DataType, externalType: string],
|
|
58
57
|
][];
|
|
59
|
-
|
|
58
|
+
// Ordered map of column name -> data type.
|
|
59
|
+
//
|
|
60
|
+
// `Map` (not `Record`) because JS objects reorder integer-string keys
|
|
61
|
+
// to the front in numeric order per `OrdinaryOwnPropertyKeys`; a
|
|
62
|
+
// DataFrame with a `"2010"` column alongside `"here"` would otherwise
|
|
63
|
+
// lose its column order on iteration (#9269). `Map` preserves insertion
|
|
64
|
+
// order for all keys.
|
|
65
|
+
export type FieldTypes = Map<string, DataType>;
|
|
60
66
|
|
|
61
67
|
export function toFieldTypes(
|
|
62
68
|
fieldTypes: FieldTypesWithExternalType,
|
|
63
69
|
): FieldTypes {
|
|
64
|
-
return
|
|
65
|
-
fieldTypes,
|
|
66
|
-
([columnName]) => columnName,
|
|
67
|
-
([, [type]]) => type,
|
|
68
|
-
);
|
|
70
|
+
return new Map(fieldTypes.map(([columnName, [type]]) => [columnName, type]));
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
interface BinValue {
|
|
@@ -83,19 +83,23 @@ interface Props extends Omit<
|
|
|
83
83
|
|
|
84
84
|
const LoadingDataEditor = (props: Props) => {
|
|
85
85
|
const [data, setData] = useState<unknown[]>([]);
|
|
86
|
-
const [columnFields, setColumnFields] = useState<FieldTypes>(
|
|
86
|
+
const [columnFields, setColumnFields] = useState<FieldTypes>(new Map());
|
|
87
87
|
|
|
88
88
|
// Load the data
|
|
89
89
|
const { error } = useAsyncData(async () => {
|
|
90
90
|
const withoutExternalTypes = toFieldTypes(props.fieldTypes ?? []);
|
|
91
91
|
|
|
92
92
|
// If we already have the data, return it
|
|
93
|
-
// Otherwise, load the data from the URL
|
|
93
|
+
// Otherwise, load the data from the URL. Vega's CSV parser takes a
|
|
94
|
+
// plain `Record`; column order doesn't matter for parsing.
|
|
94
95
|
const localData = Array.isArray(props.data)
|
|
95
96
|
? props.data
|
|
96
97
|
: await vegaLoadData(
|
|
97
98
|
props.data,
|
|
98
|
-
{
|
|
99
|
+
{
|
|
100
|
+
type: "csv",
|
|
101
|
+
parse: getVegaFieldTypes(Object.fromEntries(withoutExternalTypes)),
|
|
102
|
+
},
|
|
99
103
|
{ handleBigIntAndNumberLike: true },
|
|
100
104
|
);
|
|
101
105
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { describe, expect, it } from "vitest";
|
|
4
4
|
import type { FieldTypes } from "@/components/data-table/types";
|
|
5
|
+
import type { DataType } from "@/core/kernel/messages";
|
|
5
6
|
import {
|
|
6
7
|
insertColumn,
|
|
7
8
|
modifyColumnFields,
|
|
@@ -9,6 +10,11 @@ import {
|
|
|
9
10
|
renameColumn,
|
|
10
11
|
} from "../data-utils";
|
|
11
12
|
|
|
13
|
+
// Fixtures are written as object literals for readability; `FieldTypes`
|
|
14
|
+
// is a `Map` (#9269).
|
|
15
|
+
const asFieldTypes = (obj: Record<string, DataType>): FieldTypes =>
|
|
16
|
+
new Map(Object.entries(obj));
|
|
17
|
+
|
|
12
18
|
describe("removeColumn", () => {
|
|
13
19
|
const testData = [
|
|
14
20
|
{ int: 1, string: "a", bool: "True", datetime: "2025-07-12 00:07:13" },
|
|
@@ -481,12 +487,12 @@ describe("renameColumn", () => {
|
|
|
481
487
|
});
|
|
482
488
|
|
|
483
489
|
describe("modifyColumnFields", () => {
|
|
484
|
-
const testFieldTypes: FieldTypes = {
|
|
490
|
+
const testFieldTypes: FieldTypes = asFieldTypes({
|
|
485
491
|
int: "integer",
|
|
486
492
|
string: "string",
|
|
487
493
|
bool: "boolean",
|
|
488
494
|
datetime: "datetime",
|
|
489
|
-
};
|
|
495
|
+
});
|
|
490
496
|
|
|
491
497
|
it("should insert a new column at index 0", () => {
|
|
492
498
|
const result = modifyColumnFields({
|
|
@@ -496,16 +502,15 @@ describe("modifyColumnFields", () => {
|
|
|
496
502
|
newColumnName: "newColumn",
|
|
497
503
|
});
|
|
498
504
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
expect(result).toEqual(expected);
|
|
505
|
+
expect(result).toMatchInlineSnapshot(`
|
|
506
|
+
Map {
|
|
507
|
+
"newColumn" => "string",
|
|
508
|
+
"int" => "integer",
|
|
509
|
+
"string" => "string",
|
|
510
|
+
"bool" => "boolean",
|
|
511
|
+
"datetime" => "datetime",
|
|
512
|
+
}
|
|
513
|
+
`);
|
|
509
514
|
});
|
|
510
515
|
|
|
511
516
|
it("should insert a new column at index 1", () => {
|
|
@@ -516,16 +521,15 @@ describe("modifyColumnFields", () => {
|
|
|
516
521
|
newColumnName: "newColumn",
|
|
517
522
|
});
|
|
518
523
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
expect(result).toEqual(expected);
|
|
524
|
+
expect(result).toMatchInlineSnapshot(`
|
|
525
|
+
Map {
|
|
526
|
+
"int" => "integer",
|
|
527
|
+
"newColumn" => "string",
|
|
528
|
+
"string" => "string",
|
|
529
|
+
"bool" => "boolean",
|
|
530
|
+
"datetime" => "datetime",
|
|
531
|
+
}
|
|
532
|
+
`);
|
|
529
533
|
});
|
|
530
534
|
|
|
531
535
|
it("should insert a new column at index 2", () => {
|
|
@@ -536,16 +540,15 @@ describe("modifyColumnFields", () => {
|
|
|
536
540
|
newColumnName: "newColumn",
|
|
537
541
|
});
|
|
538
542
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
expect(result).toEqual(expected);
|
|
543
|
+
expect(result).toMatchInlineSnapshot(`
|
|
544
|
+
Map {
|
|
545
|
+
"int" => "integer",
|
|
546
|
+
"string" => "string",
|
|
547
|
+
"newColumn" => "string",
|
|
548
|
+
"bool" => "boolean",
|
|
549
|
+
"datetime" => "datetime",
|
|
550
|
+
}
|
|
551
|
+
`);
|
|
549
552
|
});
|
|
550
553
|
|
|
551
554
|
it("should insert a new column at the end", () => {
|
|
@@ -557,16 +560,15 @@ describe("modifyColumnFields", () => {
|
|
|
557
560
|
dataType: "datetime",
|
|
558
561
|
});
|
|
559
562
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
expect(result).toEqual(expected);
|
|
563
|
+
expect(result).toMatchInlineSnapshot(`
|
|
564
|
+
Map {
|
|
565
|
+
"int" => "integer",
|
|
566
|
+
"string" => "string",
|
|
567
|
+
"bool" => "boolean",
|
|
568
|
+
"datetime" => "datetime",
|
|
569
|
+
"newColumn" => "datetime",
|
|
570
|
+
}
|
|
571
|
+
`);
|
|
570
572
|
});
|
|
571
573
|
|
|
572
574
|
it("should insert a new column beyond the array length", () => {
|
|
@@ -577,16 +579,15 @@ describe("modifyColumnFields", () => {
|
|
|
577
579
|
newColumnName: "newColumn",
|
|
578
580
|
});
|
|
579
581
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
expect(result).toEqual(expected);
|
|
582
|
+
expect(result).toMatchInlineSnapshot(`
|
|
583
|
+
Map {
|
|
584
|
+
"int" => "integer",
|
|
585
|
+
"string" => "string",
|
|
586
|
+
"bool" => "boolean",
|
|
587
|
+
"datetime" => "datetime",
|
|
588
|
+
"newColumn" => "string",
|
|
589
|
+
}
|
|
590
|
+
`);
|
|
590
591
|
});
|
|
591
592
|
|
|
592
593
|
it("should remove column at index 0", () => {
|
|
@@ -596,14 +597,13 @@ describe("modifyColumnFields", () => {
|
|
|
596
597
|
type: "remove",
|
|
597
598
|
});
|
|
598
599
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
expect(result).toEqual(expected);
|
|
600
|
+
expect(result).toMatchInlineSnapshot(`
|
|
601
|
+
Map {
|
|
602
|
+
"string" => "string",
|
|
603
|
+
"bool" => "boolean",
|
|
604
|
+
"datetime" => "datetime",
|
|
605
|
+
}
|
|
606
|
+
`);
|
|
607
607
|
});
|
|
608
608
|
|
|
609
609
|
it("should remove column at index 1", () => {
|
|
@@ -613,14 +613,13 @@ describe("modifyColumnFields", () => {
|
|
|
613
613
|
type: "remove",
|
|
614
614
|
});
|
|
615
615
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
expect(result).toEqual(expected);
|
|
616
|
+
expect(result).toMatchInlineSnapshot(`
|
|
617
|
+
Map {
|
|
618
|
+
"int" => "integer",
|
|
619
|
+
"bool" => "boolean",
|
|
620
|
+
"datetime" => "datetime",
|
|
621
|
+
}
|
|
622
|
+
`);
|
|
624
623
|
});
|
|
625
624
|
|
|
626
625
|
it("should remove column at index 2", () => {
|
|
@@ -630,14 +629,13 @@ describe("modifyColumnFields", () => {
|
|
|
630
629
|
type: "remove",
|
|
631
630
|
});
|
|
632
631
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
expect(result).toEqual(expected);
|
|
632
|
+
expect(result).toMatchInlineSnapshot(`
|
|
633
|
+
Map {
|
|
634
|
+
"int" => "integer",
|
|
635
|
+
"string" => "string",
|
|
636
|
+
"datetime" => "datetime",
|
|
637
|
+
}
|
|
638
|
+
`);
|
|
641
639
|
});
|
|
642
640
|
|
|
643
641
|
it("should remove column at index 3", () => {
|
|
@@ -647,14 +645,13 @@ describe("modifyColumnFields", () => {
|
|
|
647
645
|
type: "remove",
|
|
648
646
|
});
|
|
649
647
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
expect(result).toEqual(expected);
|
|
648
|
+
expect(result).toMatchInlineSnapshot(`
|
|
649
|
+
Map {
|
|
650
|
+
"int" => "integer",
|
|
651
|
+
"string" => "string",
|
|
652
|
+
"bool" => "boolean",
|
|
653
|
+
}
|
|
654
|
+
`);
|
|
658
655
|
});
|
|
659
656
|
|
|
660
657
|
it("should handle removing non-existent column index", () => {
|
|
@@ -685,15 +682,14 @@ describe("modifyColumnFields", () => {
|
|
|
685
682
|
newColumnName: "number",
|
|
686
683
|
});
|
|
687
684
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
expect(result).toEqual(expected);
|
|
685
|
+
expect(result).toMatchInlineSnapshot(`
|
|
686
|
+
Map {
|
|
687
|
+
"number" => "string",
|
|
688
|
+
"string" => "string",
|
|
689
|
+
"bool" => "boolean",
|
|
690
|
+
"datetime" => "datetime",
|
|
691
|
+
}
|
|
692
|
+
`);
|
|
697
693
|
});
|
|
698
694
|
|
|
699
695
|
it("should rename column at index 1", () => {
|
|
@@ -704,15 +700,14 @@ describe("modifyColumnFields", () => {
|
|
|
704
700
|
newColumnName: "text",
|
|
705
701
|
});
|
|
706
702
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
expect(result).toEqual(expected);
|
|
703
|
+
expect(result).toMatchInlineSnapshot(`
|
|
704
|
+
Map {
|
|
705
|
+
"int" => "integer",
|
|
706
|
+
"text" => "string",
|
|
707
|
+
"bool" => "boolean",
|
|
708
|
+
"datetime" => "datetime",
|
|
709
|
+
}
|
|
710
|
+
`);
|
|
716
711
|
});
|
|
717
712
|
|
|
718
713
|
it("should rename column at index 3", () => {
|
|
@@ -724,15 +719,14 @@ describe("modifyColumnFields", () => {
|
|
|
724
719
|
newColumnName: "timestamp",
|
|
725
720
|
});
|
|
726
721
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
expect(result).toEqual(expected);
|
|
722
|
+
expect(result).toMatchInlineSnapshot(`
|
|
723
|
+
Map {
|
|
724
|
+
"int" => "integer",
|
|
725
|
+
"string" => "string",
|
|
726
|
+
"bool" => "boolean",
|
|
727
|
+
"timestamp" => "datetime",
|
|
728
|
+
}
|
|
729
|
+
`);
|
|
736
730
|
});
|
|
737
731
|
|
|
738
732
|
it("should handle renaming non-existent column index", () => {
|
|
@@ -758,7 +752,7 @@ describe("modifyColumnFields", () => {
|
|
|
758
752
|
});
|
|
759
753
|
|
|
760
754
|
it("should preserve original field types structure", () => {
|
|
761
|
-
const originalFieldTypes =
|
|
755
|
+
const originalFieldTypes = new Map(testFieldTypes);
|
|
762
756
|
modifyColumnFields({
|
|
763
757
|
columnFields: testFieldTypes,
|
|
764
758
|
columnIdx: 1,
|
|
@@ -769,7 +763,7 @@ describe("modifyColumnFields", () => {
|
|
|
769
763
|
});
|
|
770
764
|
|
|
771
765
|
it("should handle empty field types object", () => {
|
|
772
|
-
const emptyFieldTypes: FieldTypes =
|
|
766
|
+
const emptyFieldTypes: FieldTypes = new Map();
|
|
773
767
|
|
|
774
768
|
// Insert
|
|
775
769
|
const insertResult = modifyColumnFields({
|
|
@@ -778,7 +772,11 @@ describe("modifyColumnFields", () => {
|
|
|
778
772
|
type: "insert",
|
|
779
773
|
newColumnName: "newColumn",
|
|
780
774
|
});
|
|
781
|
-
expect(insertResult).
|
|
775
|
+
expect(insertResult).toMatchInlineSnapshot(`
|
|
776
|
+
Map {
|
|
777
|
+
"newColumn" => "string",
|
|
778
|
+
}
|
|
779
|
+
`);
|
|
782
780
|
|
|
783
781
|
// Remove
|
|
784
782
|
const removeResult = modifyColumnFields({
|
|
@@ -786,16 +784,16 @@ describe("modifyColumnFields", () => {
|
|
|
786
784
|
columnIdx: 0,
|
|
787
785
|
type: "remove",
|
|
788
786
|
});
|
|
789
|
-
expect(removeResult).
|
|
787
|
+
expect(removeResult).toMatchInlineSnapshot(`Map {}`);
|
|
790
788
|
});
|
|
791
789
|
|
|
792
790
|
it("should handle field types with special characters in column names", () => {
|
|
793
|
-
const specialFieldTypes: FieldTypes = {
|
|
791
|
+
const specialFieldTypes: FieldTypes = asFieldTypes({
|
|
794
792
|
"column-with-dash": "integer",
|
|
795
793
|
column_with_underscore: "string",
|
|
796
794
|
"column.with.dot": "boolean",
|
|
797
795
|
"column with space": "datetime",
|
|
798
|
-
};
|
|
796
|
+
});
|
|
799
797
|
|
|
800
798
|
// Insert
|
|
801
799
|
const insertResult = modifyColumnFields({
|
|
@@ -804,15 +802,15 @@ describe("modifyColumnFields", () => {
|
|
|
804
802
|
type: "insert",
|
|
805
803
|
newColumnName: "new-column",
|
|
806
804
|
});
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
805
|
+
expect(insertResult).toMatchInlineSnapshot(`
|
|
806
|
+
Map {
|
|
807
|
+
"column-with-dash" => "integer",
|
|
808
|
+
"new-column" => "string",
|
|
809
|
+
"column_with_underscore" => "string",
|
|
810
|
+
"column.with.dot" => "boolean",
|
|
811
|
+
"column with space" => "datetime",
|
|
812
|
+
}
|
|
813
|
+
`);
|
|
816
814
|
|
|
817
815
|
// Remove
|
|
818
816
|
const removeResult = modifyColumnFields({
|
|
@@ -820,13 +818,13 @@ describe("modifyColumnFields", () => {
|
|
|
820
818
|
columnIdx: 1,
|
|
821
819
|
type: "remove",
|
|
822
820
|
});
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
821
|
+
expect(removeResult).toMatchInlineSnapshot(`
|
|
822
|
+
Map {
|
|
823
|
+
"column-with-dash" => "integer",
|
|
824
|
+
"column.with.dot" => "boolean",
|
|
825
|
+
"column with space" => "datetime",
|
|
826
|
+
}
|
|
827
|
+
`);
|
|
830
828
|
|
|
831
829
|
// Rename
|
|
832
830
|
const renameResult = modifyColumnFields({
|
|
@@ -835,14 +833,14 @@ describe("modifyColumnFields", () => {
|
|
|
835
833
|
type: "rename",
|
|
836
834
|
newColumnName: "renamed-column",
|
|
837
835
|
});
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
836
|
+
expect(renameResult).toMatchInlineSnapshot(`
|
|
837
|
+
Map {
|
|
838
|
+
"column-with-dash" => "integer",
|
|
839
|
+
"renamed-column" => "string",
|
|
840
|
+
"column.with.dot" => "boolean",
|
|
841
|
+
"column with space" => "datetime",
|
|
842
|
+
}
|
|
843
|
+
`);
|
|
846
844
|
});
|
|
847
845
|
|
|
848
846
|
it("should handle multiple operations in sequence", () => {
|
|
@@ -864,13 +862,13 @@ describe("modifyColumnFields", () => {
|
|
|
864
862
|
newColumnName: "renamed",
|
|
865
863
|
});
|
|
866
864
|
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
865
|
+
expect(result).toMatchInlineSnapshot(`
|
|
866
|
+
Map {
|
|
867
|
+
"renamed" => "string",
|
|
868
|
+
"newColumn" => "string",
|
|
869
|
+
"bool" => "boolean",
|
|
870
|
+
"datetime" => "datetime",
|
|
871
|
+
}
|
|
872
|
+
`);
|
|
875
873
|
});
|
|
876
874
|
});
|
|
@@ -79,24 +79,25 @@ export function modifyColumnFields(opts: {
|
|
|
79
79
|
return columnFields;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
const entries =
|
|
83
|
-
const newEntries = [
|
|
82
|
+
const entries = [...columnFields.entries()];
|
|
83
|
+
const newEntries: Array<[string, DataType]> = [
|
|
84
84
|
...entries.slice(0, columnIdx),
|
|
85
85
|
[newColumnName, dataType || "string"],
|
|
86
86
|
...entries.slice(columnIdx),
|
|
87
87
|
];
|
|
88
|
-
return
|
|
88
|
+
return new Map(newEntries);
|
|
89
89
|
}
|
|
90
90
|
case "remove": {
|
|
91
|
-
if (columnIdx < 0 || columnIdx >=
|
|
91
|
+
if (columnIdx < 0 || columnIdx >= columnFields.size) {
|
|
92
92
|
return columnFields;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
const entries =
|
|
95
|
+
const entries = [...columnFields.entries()];
|
|
96
96
|
const columnName = entries[columnIdx]?.[0];
|
|
97
97
|
if (columnName) {
|
|
98
|
-
const
|
|
99
|
-
|
|
98
|
+
const next = new Map(columnFields);
|
|
99
|
+
next.delete(columnName);
|
|
100
|
+
return next;
|
|
100
101
|
}
|
|
101
102
|
return columnFields;
|
|
102
103
|
}
|
|
@@ -106,18 +107,18 @@ export function modifyColumnFields(opts: {
|
|
|
106
107
|
return columnFields;
|
|
107
108
|
}
|
|
108
109
|
|
|
109
|
-
if (columnIdx < 0 || columnIdx >=
|
|
110
|
+
if (columnIdx < 0 || columnIdx >= columnFields.size) {
|
|
110
111
|
return columnFields;
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
// Rename at the right index
|
|
114
|
-
const entries =
|
|
115
|
-
const newEntries = [
|
|
115
|
+
const entries = [...columnFields.entries()];
|
|
116
|
+
const newEntries: Array<[string, DataType]> = [
|
|
116
117
|
...entries.slice(0, columnIdx),
|
|
117
118
|
[newColumnName, dataType || "string"],
|
|
118
119
|
...entries.slice(columnIdx + 1),
|
|
119
120
|
];
|
|
120
|
-
return
|
|
121
|
+
return new Map(newEntries);
|
|
121
122
|
}
|
|
122
123
|
}
|
|
123
124
|
}
|
|
@@ -102,7 +102,7 @@ export const GlideDataEditor = <T,>({
|
|
|
102
102
|
|
|
103
103
|
const columns: ModifiedGridColumn[] = useMemo(() => {
|
|
104
104
|
const columns: ModifiedGridColumn[] = [];
|
|
105
|
-
for (const [columnName, fieldType] of
|
|
105
|
+
for (const [columnName, fieldType] of columnFields) {
|
|
106
106
|
const editable =
|
|
107
107
|
editableColumns === "all" || editableColumns.includes(columnName);
|
|
108
108
|
|
|
@@ -311,7 +311,7 @@ export const GlideDataEditor = <T,>({
|
|
|
311
311
|
const [col, _row] = cell;
|
|
312
312
|
const key = columns[col].title;
|
|
313
313
|
|
|
314
|
-
const columnType = columnFields
|
|
314
|
+
const columnType = columnFields.get(key);
|
|
315
315
|
// Verify the new value is of the correct type
|
|
316
316
|
switch (columnType) {
|
|
317
317
|
case "number":
|
|
@@ -445,7 +445,7 @@ export const GlideDataEditor = <T,>({
|
|
|
445
445
|
const oldColumnName = columns[menu.col].title;
|
|
446
446
|
|
|
447
447
|
// Validate the new column name
|
|
448
|
-
if (columnFields
|
|
448
|
+
if (columnFields.has(newName)) {
|
|
449
449
|
toastColumnExists(newName);
|
|
450
450
|
return;
|
|
451
451
|
}
|
|
@@ -498,7 +498,7 @@ export const GlideDataEditor = <T,>({
|
|
|
498
498
|
const clampedColumnIdx = Math.max(0, Math.min(columnIdx, columns.length));
|
|
499
499
|
|
|
500
500
|
// Validate the new column name
|
|
501
|
-
if (columnFields
|
|
501
|
+
if (columnFields.has(columnName)) {
|
|
502
502
|
toastColumnExists(columnName);
|
|
503
503
|
return;
|
|
504
504
|
}
|