Skip to content

Commit

Permalink
fixed word index printing
Browse files Browse the repository at this point in the history
  • Loading branch information
Leo-Nicolle committed Jan 26, 2024
1 parent 51f550c commit 0979d5f
Show file tree
Hide file tree
Showing 11 changed files with 129 additions and 163 deletions.
6 changes: 6 additions & 0 deletions client/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,10 @@ h2 {
body {
overflow: hidden;
}
@media print {
#outisde {
display: none;
}
}
</style>
41 changes: 27 additions & 14 deletions client/src/components/ExportButton.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<template>
<div ref="exporter" class="exporter">
<n-button @click="print" round>{{$t('buttons.print')}}</n-button>
<iframe class="print" :src="iframeUrl"></iframe>
<n-button @click="print" round>{{ $t('buttons.print') }}</n-button>
<iframe ref="iframe" class="print" :src="iframeUrl"></iframe>
</div>
</template>

<script setup lang="ts">
import { defineProps, computed } from "vue";
import { defineProps, computed, ref, onMounted } from "vue";
import router from "../router";
/**
* Component to print. It uses an iframe to print the page.
Expand All @@ -22,25 +22,38 @@ const props = defineProps<{
*/
query: any;
}>();
const enabled = ref<boolean>(false);
const iframe = ref<HTMLIFrameElement>();
const shouldPrint = ref(false);
function print() {
const iframe = document.querySelector("iframe.print") as HTMLIFrameElement;
if (!iframe || !iframe.contentWindow) return;
iframe.contentWindow.postMessage('print', '*');
enabled.value = true;
shouldPrint.value = true;
}
const iframeUrl = computed(() => {
const url = `${window.location.origin}${
router.resolve({
name: props.route,
query: props.query,
}).href
}`;
const url = `${window.location.origin}${router.resolve({
name: props.route,
query: { ...props.query, enabled: enabled.value },
}).href
}`;
return url;
});
onMounted(() => {
window.addEventListener("message", ({ data }) => {
if (data === 'print-ready' && shouldPrint.value) {
const iframe = document.querySelector("iframe.print") as HTMLIFrameElement;
if (!iframe || !iframe.contentWindow) return;
shouldPrint.value = false;
iframe.contentWindow.postMessage('print', '*');
}
else if (data === 'print-done') {
enabled.value = false;
}
});
});
</script>

<style scoped>
.exporter > iframe {
.exporter>iframe {
position: fixed;
top: 100%;
}
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/Solutions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div v-if="grids && solutionStyle">
<FontLoader :value="solutionStyle.grids.gridN" />
<Paper v-for="(gs, i) in gridsPerPage" :key="i" :format="solutionStyle.paper" :showMargins="exportOptions.margins"
:page-number="page" :showPagination="exportOptions.pagination" :pagination="solutionStyle.pagination">
:page-number="page + i" :showPagination="exportOptions.pagination" :pagination="solutionStyle.pagination">
<div class="grids">
<div v-for="(grid, j) in gs" :key="j" class="grid-c">
<span class="gridN">{{ j + solutionStyle.pagination.startIdx }}</span>
Expand Down
116 changes: 65 additions & 51 deletions client/src/components/WordsIndex.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
</span>
</Paper>
<Teleport to="#outside">
<Paper class="paper ruler" :format="solutionStyle.paper" :showMargins="false"
:showPagination="exportOptions.pagination" :pageNumber="0">
<Paper class="paper ruler" :format="solutionStyle.paper" :showMargins="true" :showPagination="true" :pageNumber="1">
<span class="words ruler" ref="ruler"> </span>
</Paper>
</Teleport>
Expand Down Expand Up @@ -61,20 +60,40 @@ const layout = ref<{ wordsPerPage: (number | string)[][]; heights: string[]; }>(
wordsPerPage: [],
heights: [],
});
const columnGap = 10;
const gap = computed(() => `${columnGap}px`);
const tolerance = 2;
watch([props.grids, ruler, wordFont, sizeFont, props], () => {
function nodesBSStart(
nodes: HTMLDivElement[], start: number, end: number, query: number
) {
while (start <= end) {
const middle = (start + end) >>> 1;
const node = nodes[middle];
if (!node) break;
const { x, width } = node.getBoundingClientRect();
const right = x + width;
if (isNaN(right) || right < query) start = middle + 1;
else end = middle - 1;
}
return start;
}
watch([props.grids, ruler, wordFont, sizeFont, props.solutionStyle,
props.exportOptions], () => {
if (!props.grids || !ruler.value) {
layout.value = { wordsPerPage: [], heights: [] };
return;
}
const words = Array.from(getAllWords(props.grids))
.sort((a, b) => a.length - b.length)
.sort((a, b) => {
const l = a.length - b.length;
return l === 0 ? a.localeCompare(b) : l;
})
.filter((w) => w.length > 1);
const r = ruler.value as HTMLDivElement;
r.innerHTML = "";
let bb = r.getBoundingClientRect();
const maxX = bb.x + bb.width;
const { x: startX, width: W, height: H } = r.parentElement!.getBoundingClientRect();
const wordsMap = words.reduce((acc, word) => {
if (acc[word.length]) {
acc[word.length].push(word);
Expand All @@ -83,56 +102,50 @@ watch([props.grids, ruler, wordFont, sizeFont, props], () => {
}
return acc;
}, {} as WordMap);
const allTexts: (string | number)[] = [];
Object.entries(wordsMap)
.forEach(([size, words]) => {
allTexts.push(+size);
const span = document.createElement("span");
span.classList.add("size");
span.innerHTML = size.toString();
r.appendChild(span);
words.forEach((word) => {
const span = document.createElement("span");
span.classList.add("word");
span.innerHTML = word;
r.appendChild(span);
allTexts.push(word);
});
});
// find the first node outside boundaries:
const nodes = Array.from(r.children) as HTMLDivElement[];
const maxX = nodes[nodes.length - 1].getBoundingClientRect().x;
const pages = Math.ceil(maxX / W);
const indexes = [0];
const wordsPerPage = [];
for (let i = 0; i < pages; i++) {
indexes.push(nodesBSStart(nodes, 0, nodes.length - 1, startX + (i + 1) * W - tolerance));
wordsPerPage.push(allTexts.slice(indexes[i], indexes[i + 1]));
}
if (indexes[indexes.length - 1] < allTexts.length - 1) {
wordsPerPage.push(wordsPerPage.slice(indexes.length - 1));
}
r.innerHTML = "";
layout.value = {
wordsPerPage,
heights: ['100%']
};
emit('pageCount', wordsPerPage.length);
let wordsOnCurrentPage: (string | number)[] = [];
const heights: string[] = [];
// cut the list into multiple pages using the ruler. When a word overflows the ruler => pagebreak
const res = Object.entries(wordsMap).reduce(
(wordsPerPage, [size, words], i, arr) => {
[+size, ...words.sort((a, b) => a.localeCompare(b))].forEach(
(word, j, arr1) => {
const span = document.createElement("span");
span.innerHTML = `${word}`;
span.classList.add(typeof word === "number" ? "size" : "word");
r.appendChild(span);
const { x, y, width, height } = span.getBoundingClientRect();
if (x + width > maxX - tolerance) {
r.innerHTML = "";
r.appendChild(span);
wordsPerPage.push(wordsOnCurrentPage);
wordsOnCurrentPage = [word];
heights.push("100%");
} else {
wordsOnCurrentPage.push(word);
}
}
);
return wordsPerPage;
},
[] as (string | number)[][]
);
res.push(wordsOnCurrentPage);
bb = r.getBoundingClientRect();
// TODO: this is not working properly
// attempt to make the page take the less height possible
// see https://stackoverflow.com/questions/75994487/css-how-to-make-flex-column-layout-grow-on-x-axis-first?noredirect=1#comment134032653_75994487
const { x, y, width, height } = r.lastChild
? r.lastChild.getBoundingClientRect()
: { x: 0, y: 0, width: 0, height: 0 };
const area =
(x - bb.x + width) * (y - bb.y + height) +
(bb.height - y + bb.y) * (x - bb.x);
const totalArea = bb.width * bb.height;
const ratio = area / totalArea;
heights.push(ratio * 100 + "%");
emit("pageCount", res.length);
layout.value = { wordsPerPage: res, heights: heights };
return;
});
</script>

<style lang="less">
@media print {
#outisde,
.ruler {
display: none;
}
Expand All @@ -150,7 +163,7 @@ watch([props.grids, ruler, wordFont, sizeFont, props], () => {
justify-content: flex-start;
align-items: center;
page-break-inside: auto;
gap: 10px;
gap: v-bind(gap);
flex: 2;
}
Expand Down Expand Up @@ -184,6 +197,7 @@ watch([props.grids, ruler, wordFont, sizeFont, props], () => {
.body.body-index {
align-content: flex-start;
margin: 0;
}
.pushup {
Expand Down
7 changes: 6 additions & 1 deletion client/src/js/usePrintMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ const onMessage = (event: MessageEvent) => {
export const usePrintMessage = () => {
window.addEventListener("message", onMessage);
};

export const sendReadyMessage = () => {
window.parent.postMessage("print-ready", location.origin);
};
export const sendDoneMessage = () => {
window.parent.postMessage("print-done", location.origin);
};
export const cleanupPrintMessage = () => {
window.removeEventListener("message", onMessage);
};
1 change: 1 addition & 0 deletions client/src/layouts/Main.vue
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ nav {
}
#outside {
position: absolute;
transform: translate(100vw, 100vh);
}
</style>
4 changes: 3 additions & 1 deletion client/src/views/Grids.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ const solutionsStyle = ref<SolutionStyle>();
const selected = ref<Grid[]>([]);
const thumbnails = ref<string[]>([]);
const exportQuery = computed(() => {
return { ids: selected.value.map((s) => s.id).join(",") };
const res = { ids: selected.value.map((s) => s.id).join(",") };
return res;
});
function fetch() {
return api
Expand Down Expand Up @@ -104,6 +105,7 @@ function onUpload(filesContents: [string, string][]) {
function createGrid() {
const newGrid = new Grid(10, 10);
newGrid.title = "Nouvelle Grille";
console.log('LA');
workerController
.getDistribution()
.then((distribution) => {
Expand Down
81 changes: 0 additions & 81 deletions client/src/views/editors/BookEditor.vue

This file was deleted.

4 changes: 2 additions & 2 deletions client/src/views/editors/SolutionsEditor.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<Layout>
<template #left-panel v-if="grids.length && style">
<OptionsForm v-model="style" grid format>
<template #left-panel>
<OptionsForm v-if="style" v-model="style" grid format>
<SolutionsForm v-model="style" />
</OptionsForm>
</template>
Expand Down
Loading

0 comments on commit 0979d5f

Please sign in to comment.