@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.
@@ -161,13 +161,17 @@ function collectUniqueById(entries, warningScope) {
161
161
  }
162
162
  return Array.from(byId.values());
163
163
  }
164
- function resolveExportSections(config) {
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 title = section.title?.trim().length ? section.title.trim() : fallbackTitle;
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 }, "Export what you view");
203
+ addSection("view", { getUrl: config.getUrl, formats: config.formats }, viewTitle, DEFAULT_VIEW_EXPORT_TITLE);
198
204
  } else {
199
- addSection("view", config.view, "Export what you 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
- addSection(`section-${idx}`, section, section.title?.trim().length ? section.title : `Export ${idx + 1}`);
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, "Full data export");
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.title, format);
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(target.settings, target.id);
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);