diff --git a/package-lock.json b/package-lock.json index 5ab0eeb9..f9a6ea0f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,8 @@ "d3-force-3d": "^3.0.5", "easymde": "^2.19.0", "glob": "^11.0.0", + "html-to-image": "^1.11.13", + "html2canvas": "^1.4.1", "htmx.org": "^2.0.3", "jquery": "^3.7.1", "js-cookie": "^3.0.5", @@ -3083,6 +3085,15 @@ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -3471,6 +3482,15 @@ "node": ">= 8" } }, + "node_modules/css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/cytoscape": { "version": "3.32.0", "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.32.0.tgz", @@ -4149,6 +4169,25 @@ "node": ">= 0.4" } }, + "node_modules/html-to-image": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.13.tgz", + "integrity": "sha512-cuOPoI7WApyhBElTTb9oqsawRvZ0rHhaHwghRLlTuffoD1B2aDemlCruLeZrUIIdvG7gs9xeELEPm6PhuASqrg==", + "license": "MIT" + }, + "node_modules/html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "license": "MIT", + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/htmx.org": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-2.0.4.tgz", @@ -5451,6 +5490,15 @@ "dev": true, "license": "ISC" }, + "node_modules/text-segmentation": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/three": { "version": "0.177.0", "resolved": "https://registry.npmjs.org/three/-/three-0.177.0.tgz", @@ -5708,6 +5756,15 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "license": "MIT", + "dependencies": { + "base64-arraybuffer": "^1.0.2" + } + }, "node_modules/vite": { "version": "6.3.5", "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", diff --git a/package.json b/package.json index 3abaca2b..4a2584ab 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "d3-force-3d": "^3.0.5", "easymde": "^2.19.0", "glob": "^11.0.0", + "html2canvas": "^1.4.1", "htmx.org": "^2.0.3", "jquery": "^3.7.1", "js-cookie": "^3.0.5", diff --git a/timetable/static/bundled/timetable/generator-index.ts b/timetable/static/bundled/timetable/generator-index.ts index 19d716c0..1ed6e5c2 100644 --- a/timetable/static/bundled/timetable/generator-index.ts +++ b/timetable/static/bundled/timetable/generator-index.ts @@ -1,3 +1,5 @@ +import html2canvas from "html2canvas"; + // see https://regex101.com/r/QHSaPM/2 const TIMETABLE_ROW_RE: RegExp = /^(?[A-Z\d]{4}(?:\+[A-Z\d]{4})?)\s+(?[A-Z]{2}\d)\s+((?[AB])\s+)?(?(lundi)|(mardi)|(mercredi)|(jeudi)|(vendredi)|(samedi)|(dimanche))\s+(?\d{2}:\d{2})\s+(?\d{2}:\d{2})\s+[\dA-B]\s+(?:[\wé]*\s+)?(?\w+(?:, \w+)?)$/; @@ -117,5 +119,16 @@ document.addEventListener("alpine:init", () => { left: `${this.displayedWeekdays.indexOf(slot.weekday) * SLOT_WIDTH}px`, }; }, + + async savePng() { + const elem = document.getElementById("timetable"); + const img = (await html2canvas(elem)).toDataURL(); + const downloadLink = document.createElement("a"); + downloadLink.href = img; + downloadLink.download = "edt.png"; + document.body.appendChild(downloadLink); + downloadLink.click(); + downloadLink.remove(); + }, })); }); diff --git a/timetable/static/timetable/css/generator.scss b/timetable/static/timetable/css/generator.scss index ae5fb83f..3197ebeb 100644 --- a/timetable/static/timetable/css/generator.scss +++ b/timetable/static/timetable/css/generator.scss @@ -1,4 +1,6 @@ #timetable { + display: block; + margin: 2em auto ; .header { background-color: white; box-shadow: none; diff --git a/timetable/templates/timetable/generator.jinja b/timetable/templates/timetable/generator.jinja index 81f72707..66dfb614 100644 --- a/timetable/templates/timetable/generator.jinja +++ b/timetable/templates/timetable/generator.jinja @@ -28,7 +28,7 @@
+ {% endblock content %}