Skip to content

Commit 360a027

Browse files
committed
refactor(pages): reorganize note properties
1 parent 71a690e commit 360a027

File tree

4 files changed

+847
-710
lines changed

4 files changed

+847
-710
lines changed
Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
<template>
2+
<div style="padding: 20px; display: flex; flex-direction: column">
3+
<div style="display: flex">
4+
<Checkbox
5+
label="Container"
6+
class="container-checkbox"
7+
:disable="page.react.readOnly"
8+
:model-value="note.react.collab.container.enabled"
9+
@update:model-value="
10+
(event) => {
11+
changeProp(event, (selectedNote, value) => {
12+
selectedNote.react.collab.body.enabled ||=
13+
selectedNote.react.numEnabledSections === 1 && !value;
14+
selectedNote.react.collab.container.enabled = value;
15+
});
16+
17+
if (internals.pages.react.tutorialStep === 3) {
18+
internals.pages.react.tutorialStep++;
19+
}
20+
}
21+
"
22+
>
23+
<TutorialTooltip
24+
v-if="
25+
internals.pages.react.isNewUser &&
26+
internals.pages.react.tutorialStep === 3
27+
"
28+
ref="containerTooltip"
29+
pos="bottom"
30+
>
31+
<div v-html="containerTooltipHTML"></div>
32+
</TutorialTooltip>
33+
</Checkbox>
34+
35+
<Gap style="width: 16px" />
36+
37+
<Checkbox
38+
label="Spatial"
39+
:model-value="note.react.collab.container.spatial"
40+
@update:model-value="
41+
changeProp($event, (selectedNote, value) => {
42+
selectedNote.react.collab.container.spatial = value;
43+
})
44+
"
45+
:disable="page.react.readOnly || !note.react.collab.container.enabled"
46+
/>
47+
</div>
48+
49+
<Gap style="height: 16px" />
50+
51+
<div style="display: flex">
52+
<Checkbox
53+
label="Horizontal"
54+
:model-value="note.react.collab.container.horizontal"
55+
@update:model-value="
56+
changeProp($event, (selectedNote, value) => {
57+
selectedNote.react.collab.container.horizontal = value;
58+
})
59+
"
60+
:disable="
61+
page.react.readOnly ||
62+
!note.react.collab.container.enabled ||
63+
note.react.collab.container.spatial
64+
"
65+
/>
66+
67+
<Gap style="width: 16px" />
68+
69+
<Checkbox
70+
label="Stretch children"
71+
:model-value="note.react.collab.container.stretchChildren"
72+
@update:model-value="
73+
changeProp($event, (selectedNote, value) => {
74+
selectedNote.react.collab.container.stretchChildren = value;
75+
})
76+
"
77+
:disable="
78+
page.react.readOnly ||
79+
!note.react.collab.container.enabled ||
80+
note.react.collab.container.horizontal ||
81+
note.react.collab.container.spatial
82+
"
83+
/>
84+
</div>
85+
86+
<Gap style="height: 16px" />
87+
88+
<Checkbox
89+
label="Wrap children"
90+
:model-value="note.react.collab.container.wrapChildren"
91+
@update:model-value="
92+
changeProp($event, (selectedNote, value) => {
93+
selectedNote.react.collab.container.wrapChildren = value;
94+
})
95+
"
96+
:disable="
97+
page.react.readOnly ||
98+
!note.react.collab.container.enabled ||
99+
note.react.collab.container.spatial
100+
"
101+
/>
102+
103+
<Gap style="height: 16px" />
104+
105+
<Checkbox
106+
label="Force color inheritance"
107+
:model-value="note.react.collab.container.forceColorInheritance"
108+
@update:model-value="
109+
changeProp($event, (selectedNote, value) => {
110+
selectedNote.react.collab.container.forceColorInheritance = value;
111+
})
112+
"
113+
:disable="page.react.readOnly || !note.react.collab.container.enabled"
114+
/>
115+
116+
<Gap style="height: 24px" />
117+
118+
<DeepBtn
119+
label="Reverse children"
120+
icon="mdi-swap-vertical"
121+
:disable="
122+
page.react.readOnly ||
123+
note.react.collab.container.spatial ||
124+
note.react.notes.length < 2
125+
"
126+
color="primary"
127+
@click="
128+
changeProp($event, (selectedNote, value) => {
129+
selectedNote.reverseChildren();
130+
})
131+
"
132+
/>
133+
134+
<Gap style="height: 16px" />
135+
136+
<input
137+
ref="fileInput"
138+
style="display: none"
139+
type="file"
140+
accept=".txt, .md"
141+
multiple
142+
@change="onFileChange"
143+
/>
144+
145+
<DeepBtn
146+
label="Import children from files"
147+
icon="mdi-import"
148+
:disable="
149+
page.react.readOnly ||
150+
(note.react.collab.container.enabled && note.react.container.spatial)
151+
"
152+
color="primary"
153+
@click="importChildrenFromFiles"
154+
/>
155+
</div>
156+
</template>
157+
158+
<script setup lang="ts">
159+
import { useIntervalFn } from '@vueuse/core';
160+
import showdown from 'showdown';
161+
import type { PageNote } from 'src/code/pages/page/notes/note';
162+
import type { Page } from 'src/code/pages/page/page';
163+
import TutorialTooltip from 'src/components/TutorialTooltip.vue';
164+
import type { Ref } from 'vue';
165+
166+
const page = inject<Ref<Page>>('page')!;
167+
168+
const note = computed(() => page.value.activeElem.react.value as PageNote);
169+
170+
const containerTooltipHTML =
171+
'Click here to enable<br/> the <b>note container</b>.<br/>This allows to place<br/>notes within eachother.';
172+
173+
const containerTooltip = ref<InstanceType<typeof TutorialTooltip>>();
174+
175+
useIntervalFn(() => {
176+
if (internals.pages.react.isNewUser) {
177+
containerTooltip.value?.updatePosition();
178+
}
179+
}, 1);
180+
181+
const fileInput = ref<HTMLInputElement>();
182+
183+
function changeProp(value: any, func: (note: PageNote, value: any) => void) {
184+
page.value.collab.doc.transact(() => {
185+
for (const selectedNote of page.value.selection.react.notes) {
186+
func(selectedNote, value);
187+
}
188+
});
189+
}
190+
191+
async function createChildFromFile(file: File) {
192+
const fileReader = new FileReader();
193+
194+
fileReader.onload = async (event) => {
195+
const content = String(event.target?.result ?? '');
196+
197+
const childNote = await page.value.notes.create({
198+
region: note.value,
199+
center: false,
200+
edit: false,
201+
});
202+
203+
if (childNote != null) {
204+
childNote.react.collab.head.enabled = true;
205+
childNote.react.collab.body.enabled = false;
206+
childNote.react.collab.container.enabled = false;
207+
208+
if (file.name.endsWith('.md')) {
209+
const converter = new showdown.Converter({
210+
emoji: true,
211+
parseImgDimensions: true,
212+
strikethrough: true,
213+
tables: true,
214+
underline: true,
215+
});
216+
217+
converter.addExtension(() => [
218+
{
219+
type: 'output',
220+
regex: /\$(.+?)\$/g,
221+
replace: '<inline-math>$1</inline-math>',
222+
},
223+
{
224+
type: 'output',
225+
regex: /\$\$((?:.|\n)+?)\$\$/g,
226+
replace: '<math-block>$1</math-block>',
227+
},
228+
]);
229+
230+
const initialHTML = converter
231+
.makeHtml(content)
232+
.replaceAll('\n', '')
233+
.replaceAll(/<br \/> +/g, '<br />');
234+
235+
const parser = new DOMParser();
236+
const parsedDoc = parser.parseFromString(initialHTML, 'text/html');
237+
238+
// Fix math blocks
239+
240+
for (const mathBlock of Array.from(
241+
parsedDoc.querySelectorAll('p > math-block'),
242+
)) {
243+
mathBlock.parentElement!.outerHTML = mathBlock.outerHTML;
244+
}
245+
246+
// Fix blockquotes
247+
248+
for (const paragraph of Array.from(
249+
parsedDoc.querySelectorAll('blockquote > p'),
250+
)) {
251+
paragraph.parentElement!.innerHTML = paragraph.outerHTML;
252+
}
253+
254+
const finalHTML = parsedDoc.body.innerHTML;
255+
256+
childNote.react.editors[0]?.commands.insertContent(finalHTML);
257+
} else {
258+
childNote.react.editors[0]?.commands.insertContent(content);
259+
}
260+
}
261+
};
262+
263+
fileReader.readAsText(file);
264+
}
265+
266+
async function importChildrenFromFilesAux(files: File[]) {
267+
note.value.react.collab.container.enabled = true;
268+
note.value.react.collab.container.spatial = false;
269+
270+
for (const file of files) {
271+
await createChildFromFile(file);
272+
}
273+
}
274+
275+
async function onFileChange() {
276+
if (fileInput.value?.files == null || fileInput.value.files.length === 0) {
277+
return;
278+
}
279+
280+
await importChildrenFromFilesAux(Array.from(fileInput.value.files));
281+
282+
fileInput.value = undefined;
283+
}
284+
285+
async function importChildrenFromFiles() {
286+
if ((window as any).showOpenFilePicker == null) {
287+
fileInput.value?.click();
288+
} else {
289+
const fileHandles = await (window as any).showOpenFilePicker({
290+
multiple: true,
291+
292+
types: [
293+
{
294+
description: 'Markdown file',
295+
accept: { 'text/markdown': ['.md'] },
296+
},
297+
],
298+
});
299+
300+
const files = await Promise.all(
301+
fileHandles.map((fileHandle: any) => fileHandle.getFile()),
302+
);
303+
304+
await importChildrenFromFilesAux(files);
305+
}
306+
}
307+
</script>

0 commit comments

Comments
 (0)