@open-mercato/ui 0.7.1-develop.7186.1.6e080a5017 → 0.7.1-develop.7193.1.910a5b0a1e
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/backend/DataTable.js +59 -13
- package/dist/backend/DataTable.js.map +2 -2
- package/dist/backend/PerspectiveSidebar.js +60 -21
- package/dist/backend/PerspectiveSidebar.js.map +2 -2
- package/dist/backend/views/NewViewForm.js +46 -38
- package/dist/backend/views/NewViewForm.js.map +2 -2
- package/package.json +3 -3
- package/src/backend/DataTable.tsx +117 -14
- package/src/backend/PerspectiveSidebar.tsx +80 -21
- package/src/backend/__tests__/DataTable.exportSectionTitles.test.tsx +134 -0
- package/src/backend/__tests__/DataTable.perspectiveReconcile.test.tsx +312 -0
- package/src/backend/__tests__/PerspectiveSidebar.roleAutosave.test.tsx +252 -0
- package/src/backend/views/NewViewForm.tsx +46 -32
|
@@ -161,13 +161,17 @@ function collectUniqueById(entries, warningScope) {
|
|
|
161
161
|
}
|
|
162
162
|
return Array.from(byId.values());
|
|
163
163
|
}
|
|
164
|
-
|
|
164
|
+
const DEFAULT_VIEW_EXPORT_TITLE = "Export what you view";
|
|
165
|
+
const DEFAULT_FULL_EXPORT_TITLE = "Full data export";
|
|
166
|
+
function resolveExportSections(config, t) {
|
|
165
167
|
if (!config) return [];
|
|
166
168
|
const sections = [];
|
|
167
169
|
const baseFormats = config.formats && config.formats.length > 0 ? config.formats : DEFAULT_EXPORT_FORMATS;
|
|
168
|
-
const addSection = (key, section, fallbackTitle) => {
|
|
170
|
+
const addSection = (key, section, fallbackTitle, fallbackFilenameBase) => {
|
|
169
171
|
if (!section || !section.getUrl && !section.prepare) return;
|
|
170
|
-
const
|
|
172
|
+
const explicitTitle = section.title?.trim().length ? section.title.trim() : null;
|
|
173
|
+
const title = explicitTitle ?? fallbackTitle;
|
|
174
|
+
const filenameBase = explicitTitle ?? fallbackFilenameBase;
|
|
171
175
|
const seen = /* @__PURE__ */ new Set();
|
|
172
176
|
const formatsSource = section.formats && section.formats.length > 0 ? section.formats : baseFormats;
|
|
173
177
|
const formats = formatsSource.filter((format) => {
|
|
@@ -179,6 +183,7 @@ function resolveExportSections(config) {
|
|
|
179
183
|
sections.push({
|
|
180
184
|
key,
|
|
181
185
|
title,
|
|
186
|
+
filenameBase,
|
|
182
187
|
description: section.description,
|
|
183
188
|
formats,
|
|
184
189
|
getUrl: section.getUrl,
|
|
@@ -193,17 +198,19 @@ function resolveExportSections(config) {
|
|
|
193
198
|
});
|
|
194
199
|
};
|
|
195
200
|
const hasExplicitSections = Array.isArray(config.sections) && config.sections.length > 0;
|
|
201
|
+
const viewTitle = t("ui.dataTable.export.viewTitle", DEFAULT_VIEW_EXPORT_TITLE);
|
|
196
202
|
if (!config.view && !config.full && !hasExplicitSections && config.getUrl) {
|
|
197
|
-
addSection("view", { getUrl: config.getUrl, formats: config.formats },
|
|
203
|
+
addSection("view", { getUrl: config.getUrl, formats: config.formats }, viewTitle, DEFAULT_VIEW_EXPORT_TITLE);
|
|
198
204
|
} else {
|
|
199
|
-
addSection("view", config.view,
|
|
205
|
+
addSection("view", config.view, viewTitle, DEFAULT_VIEW_EXPORT_TITLE);
|
|
200
206
|
}
|
|
201
207
|
if (hasExplicitSections) {
|
|
202
208
|
config.sections.forEach((section, idx) => {
|
|
203
|
-
|
|
209
|
+
const numberedTitle = t("ui.dataTable.export.sectionTitle", "Export {index}", { index: idx + 1 });
|
|
210
|
+
addSection(`section-${idx}`, section, numberedTitle, `Export ${idx + 1}`);
|
|
204
211
|
});
|
|
205
212
|
}
|
|
206
|
-
addSection("full", config.full, "
|
|
213
|
+
addSection("full", config.full, t("ui.dataTable.export.fullTitle", DEFAULT_FULL_EXPORT_TITLE), DEFAULT_FULL_EXPORT_TITLE);
|
|
207
214
|
return sections;
|
|
208
215
|
}
|
|
209
216
|
const COLUMN_MIN_WIDTH = 60;
|
|
@@ -426,7 +433,7 @@ function ExportMenu({ config, sections }) {
|
|
|
426
433
|
if (!preparedResult) return;
|
|
427
434
|
const prepared = preparedResult.prepared;
|
|
428
435
|
const serialized = serializeExport(prepared, format);
|
|
429
|
-
const filename = preparedResult.filename ?? section.filename?.(format) ?? config.filename?.(format) ?? defaultExportFilename(section.
|
|
436
|
+
const filename = preparedResult.filename ?? section.filename?.(format) ?? config.filename?.(format) ?? defaultExportFilename(section.filenameBase, format);
|
|
430
437
|
if (typeof window !== "undefined") {
|
|
431
438
|
const blob = new Blob([serialized.body], { type: serialized.contentType });
|
|
432
439
|
const href = URL.createObjectURL(blob);
|
|
@@ -831,6 +838,8 @@ function DataTable({
|
|
|
831
838
|
const perspectiveEnabled = Boolean(perspectiveTableId);
|
|
832
839
|
const initialSnapshotRef = React.useRef(null);
|
|
833
840
|
const snapshotHydratedTableRef = React.useRef(null);
|
|
841
|
+
const serverReconciledTableRef = React.useRef(null);
|
|
842
|
+
const hydratedSnapshotRef = React.useRef(null);
|
|
834
843
|
const initialSettingsSource = perspectiveConfig?.initialState?.initialSettings ?? null;
|
|
835
844
|
const mergedInitialSettings = React.useMemo(
|
|
836
845
|
() => sanitizePerspectiveSettings(initialSettingsSource),
|
|
@@ -1399,6 +1408,7 @@ function DataTable({
|
|
|
1399
1408
|
const snapshot = readPerspectiveSnapshot(perspectiveTableId);
|
|
1400
1409
|
if (!snapshot) return;
|
|
1401
1410
|
initialSnapshotRef.current = snapshot;
|
|
1411
|
+
hydratedSnapshotRef.current = snapshot;
|
|
1402
1412
|
const preserveAdvancedFilter = !!advancedFilter?.onApplyTree;
|
|
1403
1413
|
applyPerspectiveSettings(
|
|
1404
1414
|
snapshot.settings,
|
|
@@ -1818,14 +1828,42 @@ function DataTable({
|
|
|
1818
1828
|
React.useLayoutEffect(() => {
|
|
1819
1829
|
if (!canUsePerspectives) return;
|
|
1820
1830
|
if (!perspectiveTableId) return;
|
|
1821
|
-
if (initialSnapshotRef.current) return;
|
|
1822
|
-
if (initialPerspectiveAppliedRef.current) return;
|
|
1823
1831
|
const source = perspectiveData ?? perspectiveConfig?.initialState?.response;
|
|
1824
1832
|
if (!source) return;
|
|
1833
|
+
let orphanedSnapshotDropped = false;
|
|
1825
1834
|
const tryResolve = (id) => {
|
|
1826
1835
|
if (!id) return void 0;
|
|
1827
1836
|
return source.perspectives.find((p) => p.id === id) ?? source.rolePerspectives.find((p) => p.id === id);
|
|
1828
1837
|
};
|
|
1838
|
+
if (initialSnapshotRef.current || initialPerspectiveAppliedRef.current) {
|
|
1839
|
+
if (serverReconciledTableRef.current === perspectiveTableId) return;
|
|
1840
|
+
serverReconciledTableRef.current = perspectiveTableId;
|
|
1841
|
+
const hydrated = hydratedSnapshotRef.current;
|
|
1842
|
+
const snapshot = hydrated && (!activePerspectiveId || activePerspectiveId === hydrated.perspectiveId) ? hydrated : null;
|
|
1843
|
+
const localId = snapshot?.perspectiveId ?? activePerspectiveId;
|
|
1844
|
+
if (!localId) return;
|
|
1845
|
+
const local = tryResolve(localId);
|
|
1846
|
+
if (local) {
|
|
1847
|
+
const serverUpdatedAt = local.updatedAt ? Date.parse(local.updatedAt) : NaN;
|
|
1848
|
+
const serverIsNewer = snapshot != null && Number.isFinite(serverUpdatedAt) && serverUpdatedAt > snapshot.updatedAt;
|
|
1849
|
+
const settingsDiffer = snapshot != null && diffPerspectiveSettings(
|
|
1850
|
+
sanitizePerspectiveSettings(local.settings) ?? {},
|
|
1851
|
+
sanitizePerspectiveSettings(snapshot.settings) ?? {}
|
|
1852
|
+
).length > 0;
|
|
1853
|
+
if (serverIsNewer && settingsDiffer) {
|
|
1854
|
+
applyPerspectiveSettings(local.settings, local.id, {
|
|
1855
|
+
preserveAdvancedFilter: !!advancedFilter?.onApplyTree
|
|
1856
|
+
});
|
|
1857
|
+
initialPerspectiveAppliedRef.current = true;
|
|
1858
|
+
}
|
|
1859
|
+
return;
|
|
1860
|
+
}
|
|
1861
|
+
writePerspectiveSnapshot(perspectiveTableId, null);
|
|
1862
|
+
initialSnapshotRef.current = null;
|
|
1863
|
+
hydratedSnapshotRef.current = null;
|
|
1864
|
+
initialPerspectiveAppliedRef.current = false;
|
|
1865
|
+
orphanedSnapshotDropped = true;
|
|
1866
|
+
}
|
|
1829
1867
|
let target;
|
|
1830
1868
|
if (activePerspectiveId) {
|
|
1831
1869
|
target = tryResolve(activePerspectiveId);
|
|
@@ -1842,10 +1880,18 @@ function DataTable({
|
|
|
1842
1880
|
target = source.perspectives[0];
|
|
1843
1881
|
}
|
|
1844
1882
|
if (target) {
|
|
1845
|
-
applyPerspectiveSettings(
|
|
1883
|
+
applyPerspectiveSettings(
|
|
1884
|
+
target.settings,
|
|
1885
|
+
target.id,
|
|
1886
|
+
orphanedSnapshotDropped ? { preserveAdvancedFilter: !!advancedFilter?.onApplyTree } : void 0
|
|
1887
|
+
);
|
|
1888
|
+
} else if (orphanedSnapshotDropped) {
|
|
1889
|
+
applyPerspectiveSettings({}, null, {
|
|
1890
|
+
preserveAdvancedFilter: !!advancedFilter?.onApplyTree
|
|
1891
|
+
});
|
|
1846
1892
|
}
|
|
1847
1893
|
initialPerspectiveAppliedRef.current = true;
|
|
1848
|
-
}, [canUsePerspectives, perspectiveData, perspectiveTableId, perspectiveConfig, applyPerspectiveSettings, activePerspectiveId]);
|
|
1894
|
+
}, [canUsePerspectives, perspectiveData, perspectiveTableId, perspectiveConfig, applyPerspectiveSettings, activePerspectiveId, advancedFilter?.onApplyTree]);
|
|
1849
1895
|
const scrollTableIntoView = React.useCallback(() => {
|
|
1850
1896
|
const rect = containerRef.current?.getBoundingClientRect();
|
|
1851
1897
|
if (!rect || rect.top >= 0) return;
|
|
@@ -2367,7 +2413,7 @@ function DataTable({
|
|
|
2367
2413
|
const hasActions = actions !== void 0 && actions !== null && actions !== false;
|
|
2368
2414
|
const shouldReserveActionsSpace = actions === null || actions === false;
|
|
2369
2415
|
const exportConfig = exporter === false ? null : exporter || null;
|
|
2370
|
-
const resolvedExportSections = React.useMemo(() => resolveExportSections(exportConfig), [exportConfig]);
|
|
2416
|
+
const resolvedExportSections = React.useMemo(() => resolveExportSections(exportConfig, t), [exportConfig, t]);
|
|
2371
2417
|
const hasExport = resolvedExportSections.length > 0;
|
|
2372
2418
|
const refreshButtonConfig = refreshButton;
|
|
2373
2419
|
const hasRefreshButton = Boolean(refreshButtonConfig);
|