diff --git a/demo/main.js b/demo/main.js index e8f42e1bd6..b750befcb5 100644 --- a/demo/main.js +++ b/demo/main.js @@ -210,6 +210,53 @@ class Demo extends Component { icon: "o-spreadsheet-Icon.IMPORT_XLSX", }); + topbarMenuRegistry.addChild("osheetExport", ["file"], { + name: "Download as OSHEET", + sequence: 28, + execute: async (env) => { + const date = new Date(); + const name = `${date.getFullYear()}${date.getMonth()}${date.getDate()}${date.getHours()}${date.getMinutes()}.osheet.json`; + + const data = await env.model.exportData(); + saveAs( + new Blob([JSON.stringify(data)], { + type: "application/json", + }), + name + ); + }, + icon: "o-spreadsheet-Icon.EXPORT_XLSX", + }); + + topbarMenuRegistry.addChild("osheetImport", ["file"], { + name: "Import OSHEET", + sequence: 30, + execute: async (env) => { + const input = document.createElement("input"); + input.setAttribute("type", "file"); + input.setAttribute("style", "display: none"); + document.body.appendChild(input); + input.addEventListener("change", async () => { + if (input.files.length <= 0) { + return false; + } + const file = await input.files[0].text(); + const data = JSON.parse(file); + this.leaveCollaborativeSession(); + await fetch("http://localhost:9090/clear"); + await this.initiateConnection(data); + stores.resetStores(); + this.state.key = this.state.key + 1; + + input.oncancel = input.remove; + // It's kinda annoying (or not possible?) to fire an event on close, so the hidden input will just stay there + input.remove(); + }); + input.click(); + }, + icon: "o-spreadsheet-Icon.IMPORT_XLSX", + }); + const stores = useStoreProvider(); useExternalListener(window, "beforeunload", this.leaveCollaborativeSession.bind(this)); diff --git a/src/migrations/migration_steps.ts b/src/migrations/migration_steps.ts index f692e59add..441b38d966 100644 --- a/src/migrations/migration_steps.ts +++ b/src/migrations/migration_steps.ts @@ -1,11 +1,11 @@ import { BACKGROUND_CHART_COLOR, FORMULA_REF_IDENTIFIER } from "../constants"; -import { getItemId, getUniqueText, sanitizeSheetName } from "../helpers"; +import { getItemId, getUniqueText, recomputeZones, sanitizeSheetName } from "../helpers"; import { toXC } from "../helpers/coordinates"; import { getMaxObjectId } from "../helpers/pivot/pivot_helpers"; import { DEFAULT_TABLE_CONFIG } from "../helpers/table_presets"; import { overlap, toZone, zoneToXc } from "../helpers/zones"; import { Registry } from "../registries/registry"; -import { CustomizedDataSet, DEFAULT_LOCALE, Format, WorkbookData, Zone } from "../types"; +import { CustomizedDataSet, DEFAULT_LOCALE, Format, UID, WorkbookData, Zone } from "../types"; import { normalizeV9 } from "./legacy_tools"; import { WEEK_START } from "./locale"; @@ -548,6 +548,32 @@ migrationStepRegistry } return data; }, + }) + .add("19.0", { + migrate(data: WorkbookData): any { + for (const sheet of data.sheets || []) { + const borderType: Record = {}; + for (const zoneXc in sheet.borders) { + const borderId = sheet.borders[zoneXc]; + if (!borderId) continue; + if (!(borderId in borderType)) borderType[borderId] = []; + borderType[borderId].push(toZone(zoneXc)); + } + const borders = {}; + for (const borderId in borderType) { + const border = data.borders[borderId]; + if (!border) continue; + const zones = recomputeZones(borderType[borderId]); + if (border.left || border.right) border.vertical = border.left || border.right; + if (border.top || border.bottom) border.horizontal = border.top || border.bottom; + for (const zone of zones) { + borders[zoneToXc(zone)] = borderId; + } + } + sheet.borders = borders; + } + return data; + }, }); function fixOverlappingFilters(data: any): any { diff --git a/src/plugins/core/borders.ts b/src/plugins/core/borders.ts index d9e32a1710..f202ccf2da 100644 --- a/src/plugins/core/borders.ts +++ b/src/plugins/core/borders.ts @@ -462,9 +462,17 @@ export class BordersPlugin extends CorePlugin implements Bor import(data: WorkbookData) { if (Object.keys(data.borders || {}).length) { for (const sheet of data.sheets) { + const borderType: Record = {}; for (const zoneXc in sheet.borders) { const borderId = sheet.borders[zoneXc]; - this.addBorder(sheet.id, toZone(zoneXc), data.borders[borderId]); + if (!borderId) continue; + if (!(borderId in borderType)) borderType[borderId] = []; + borderType[borderId].push(toZone(zoneXc)); + } + for (const borderId in borderType) { + const zones = recomputeZones(borderType[borderId]); + const border = data.borders[borderId]; + this.addBorders(sheet.id, zones, border); } } } diff --git a/tests/model/model_import_export.test.ts b/tests/model/model_import_export.test.ts index a346fc7b8e..fb17abb11d 100644 --- a/tests/model/model_import_export.test.ts +++ b/tests/model/model_import_export.test.ts @@ -438,6 +438,10 @@ describe("Migrations", () => { style: "thin", color: "#000", }, + horizontal: { + style: "thin", + color: "#000", + }, }, }); }); @@ -494,7 +498,9 @@ describe("Migrations", () => { test("migrate version 21: style,format and borders by zones", () => { const style = { bold: true }; - const border = { top: { style: "thin", color: "#000" } as BorderDescr }; + const border = { + top: { style: "thin", color: "#000" } as BorderDescr, + }; const model = new Model({ version: 20, sheets: [ @@ -511,7 +517,7 @@ describe("Migrations", () => { }); expect(getCell(model, "A1")?.format).toBe("0.00%"); expect(getCell(model, "A1")?.style).toEqual(style); - expect(getBorder(model, "A1")).toEqual(border); + expect(getBorder(model, "A1")).toEqual({ top: { style: "thin", color: "#000" } }); const data = model.exportData(); expect(data.version).toBe(getCurrentVersion()); expect(data.sheets[0].cells).toEqual({ A1: "hi" }); @@ -520,7 +526,9 @@ describe("Migrations", () => { expect(data.sheets[0].borders).toEqual({ A1: 1 }); expect(data.formats).toEqual({ 1: "0.00%" }); expect(data.styles).toEqual({ 1: style }); - expect(data.borders).toEqual({ 1: border }); + expect(data.borders).toEqual({ + 1: { top: { style: "thin", color: "#000" }, horizontal: { style: "thin", color: "#000" } }, + }); }); test("Migrate version 22: add inflection operator to gauge chart", () => { diff --git a/tests/xlsx/__snapshots__/xlsx_export.test.ts.snap b/tests/xlsx/__snapshots__/xlsx_export.test.ts.snap index 2d905bdce2..0ca3e6c58d 100644 --- a/tests/xlsx/__snapshots__/xlsx_export.test.ts.snap +++ b/tests/xlsx/__snapshots__/xlsx_export.test.ts.snap @@ -27248,8 +27248,7 @@ exports[`Test XLSX export Generic sheets (style, hidden, size, cf) Simple model - - + @@ -27263,7 +27262,8 @@ exports[`Test XLSX export Generic sheets (style, hidden, size, cf) Simple model - + + @@ -27292,10 +27292,10 @@ exports[`Test XLSX export Generic sheets (style, hidden, size, cf) Simple model - + - +