Skip to content

Commit 4fc196c

Browse files
committed
add templates
1 parent 877dc1d commit 4fc196c

File tree

7 files changed

+510
-4
lines changed

7 files changed

+510
-4
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<script type="x-tmpl-mustache">
2+
<div class="content-container">
3+
<div class="title">
4+
<h1>
5+
6+
{{{ title }}}
7+
8+
</h1>
9+
<div class="logo">
10+
<img src="{{{ metadata.logo }}}" alt="Logo">
11+
</div>
12+
</div>
13+
14+
<div class="content-wrapper">
15+
<div class="content">
16+
<div class="about-us-text">
17+
18+
{{{ content }}}
19+
20+
</div>
21+
<div class="info-section">
22+
23+
{{#metadata.aboutUs}}
24+
<div class="info-box">
25+
<h3>{{ title }}</h3>
26+
<p>{{ text }}</p>
27+
</div>
28+
<div class="divider"></div>
29+
{{/metadata.aboutUs}}
30+
31+
</div>
32+
33+
</div>
34+
</div>
35+
</div>
36+
<footer>
37+
<div class="footer-content">{{{ metadata.footer }}}</div>
38+
<div class="custom-slide-number"></div>
39+
</footer>
40+
</script>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<script type="x-tmpl-mustache">
2+
<div class="content-container">
3+
<div class="logo">
4+
<img src="{{{ metadata.logo }}}" alt="Logo">
5+
</div>
6+
7+
<div class="content">
8+
<h1>{{{ title }}}</h1>
9+
10+
<p>
11+
By: {{{ metadata.presenter }}}
12+
</p>
13+
14+
</div>
15+
</div>
16+
</script>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<script type="x-tmpl-mustache">
2+
<div class="content-container">
3+
<div class="title">
4+
<h1>
5+
6+
{{{ title }}}
7+
8+
</h1>
9+
<div class="logo">
10+
<img src="{{{ metadata.logo }}}" alt="Logo">
11+
</div>
12+
</div>
13+
14+
<div class="content-wrapper">
15+
<div class="content">
16+
17+
{{{ content }}}
18+
19+
</div>
20+
</div>
21+
</div>
22+
<footer>
23+
<div class="footer-content">{{{ metadata.footer }}}</div>
24+
<div class="custom-slide-number"></div>
25+
</footer>
26+
</script>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<script type="x-tmpl-mustache">
2+
<div class="content-container">
3+
<div class="title">
4+
<h1>
5+
6+
{{{ title }}}
7+
8+
</h1>
9+
<div class="logo">
10+
<img src="{{{ metadata.logo }}}" alt="Logo">
11+
</div>
12+
</div>
13+
14+
<div class="content-wrapper">
15+
<div class="content">
16+
17+
{{{ content }}}
18+
19+
</div>
20+
</div>
21+
</div>
22+
<footer>
23+
<div class="footer-content">{{{ metadata.footer }}}</div>
24+
<div class="custom-slide-number"></div>
25+
</footer>
26+
</script>

src/App.vue

Lines changed: 194 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
</template>
2121

2222
<script setup lang="ts">
23-
import { computed, onMounted, onBeforeUnmount, ref, unref, watch } from 'vue'
23+
import { computed, onMounted, onBeforeUnmount, ref, unref, watch, nextTick } from 'vue'
2424
import {
2525
AppLoadingSpinner,
2626
useThemeStore,
@@ -64,6 +64,9 @@ const dataSeparatorVertical = '\r?\n--\r?\n'
6464
const mdImageRegex = /!\[.*\]\((?!(?:http|data))(.*)\)/g
6565
6666
let reveal: Reveal.Api
67+
const awesoMd = RevealAwesoMD()
68+
const baseUrl = `${window.location.origin}/assets/apps/${appId}`
69+
awesoMd.setBaseUrl(baseUrl)
6770
6871
const { url } = defineProps({
6972
url: {
@@ -102,7 +105,7 @@ onMounted(async () => {
102105
})
103106
104107
reveal = new Reveal(unref(revealContainer), {
105-
plugins: [RevealAwesoMD, RevealHighlight, RevealMermaid]
108+
plugins: [awesoMd, RevealHighlight, RevealMermaid]
106109
})
107110
108111
await reveal.initialize({
@@ -113,6 +116,29 @@ onMounted(async () => {
113116
controlsLayout: 'edges'
114117
})
115118
119+
if (reveal.isReady()) {
120+
applyTemplateIfNeeded()
121+
addCustomSlideNumber()
122+
updateImageStructure()
123+
fitContent()
124+
adjustFontSize()
125+
updateLogoUrl()
126+
} else {
127+
reveal.addEventListener('ready', function () {
128+
applyTemplateIfNeeded()
129+
addCustomSlideNumber()
130+
updateImageStructure()
131+
fitContent()
132+
adjustFontSize()
133+
updateLogoUrl()
134+
})
135+
}
136+
137+
reveal.addEventListener('slidechanged', function () {
138+
fitContent()
139+
adjustFontSize()
140+
})
141+
116142
isReadyToShow.value = true
117143
})
118144
onBeforeUnmount(() => {
@@ -207,6 +233,172 @@ function dirname(path: string) {
207233
function basename(path: string) {
208234
return path.split('/').reverse()[0]
209235
}
236+
237+
// TEMPLATE RELATED
238+
function addCustomSlideNumber() {
239+
const allSlides = reveal.getSlides()
240+
for (const [slideNumber, slide] of Array.from(allSlides).entries()) {
241+
const customSlideNumber = slide.querySelector('.custom-slide-number')
242+
if (!customSlideNumber) {
243+
continue
244+
}
245+
customSlideNumber.textContent = `${slideNumber + 1}`
246+
}
247+
}
248+
function updateImageStructure() {
249+
const pTags = document.querySelectorAll('p > img')
250+
pTags.forEach((img) => {
251+
const pTag = img.parentNode
252+
const divContainer = document.createElement('div')
253+
divContainer.classList.add('image-container')
254+
const divWrapper = document.createElement('div')
255+
divWrapper.classList.add('image-wrapper')
256+
divWrapper.appendChild(img)
257+
divContainer.appendChild(divWrapper)
258+
pTag.parentNode?.replaceChild(divContainer, pTag)
259+
})
260+
}
261+
function adjustFontSize() {
262+
const currentSlide = reveal.getCurrentSlide()
263+
264+
function getTotalHeightOfChildren(container) {
265+
let totalHeight = 0
266+
for (const child of container.children) {
267+
if (currentSlide.classList.contains('title-content-image')) {
268+
if (child.className !== 'image-container') {
269+
totalHeight += getHeightWithMargin(child)
270+
}
271+
} else {
272+
totalHeight += getHeightWithMargin(child)
273+
}
274+
}
275+
return totalHeight
276+
}
277+
278+
function getHeightWithMargin(element) {
279+
const style = getComputedStyle(element)
280+
const marginTop = parseFloat(style.marginTop)
281+
const marginBottom = parseFloat(style.marginBottom)
282+
const paddingTop = parseFloat(style.paddingTop)
283+
const paddingBottom = parseFloat(style.paddingBottom)
284+
return element.offsetHeight + marginTop + marginBottom + paddingTop + paddingBottom
285+
}
286+
287+
const contentWrapper = currentSlide.querySelector('.content-wrapper') as HTMLElement
288+
const content = currentSlide.querySelector('.content') as HTMLElement
289+
290+
if (!contentWrapper || !content) return
291+
292+
const contentWrapperHeight = contentWrapper.offsetHeight
293+
let totalHeight = getTotalHeightOfChildren(content)
294+
295+
// set minimum font size from where the image starts to get reduced as well
296+
// this is to prevent the font size to get too small and becomes hard to read
297+
const fontSizeToStartReducingImage = 12
298+
let fontSize = parseFloat(getComputedStyle(content).fontSize)
299+
300+
while (totalHeight > contentWrapperHeight) {
301+
const scaleFactor = contentWrapperHeight / totalHeight
302+
fontSize = Math.floor(scaleFactor * fontSize)
303+
304+
const wrapperElements = Array.from(content.children) as HTMLElement[]
305+
wrapperElements.forEach((wrapperElement) => {
306+
wrapperElement.style.fontSize = `${fontSize}px`
307+
const style = getComputedStyle(wrapperElement)
308+
const marginTop = Math.floor(parseFloat(style.marginTop) * scaleFactor)
309+
const marginBottom = Math.floor(parseFloat(style.marginBottom) * scaleFactor)
310+
wrapperElement.style.marginTop = `${marginTop}px`
311+
wrapperElement.style.marginBottom = `${marginBottom}px`
312+
})
313+
314+
// reduce image size if font size gets smaller than minimum font size
315+
const images = content.querySelectorAll(
316+
'.image-container .image-wrapper img'
317+
) as NodeListOf<HTMLImageElement>
318+
if (fontSize <= fontSizeToStartReducingImage && images.length > 0) {
319+
images.forEach((image) => {
320+
const currentWidth = image.offsetWidth
321+
const currentHeight = image.offsetHeight
322+
image.style.width = `${Math.floor(currentWidth * scaleFactor)}px`
323+
image.style.height = `${Math.floor(currentHeight * scaleFactor)}px`
324+
})
325+
}
326+
totalHeight = getTotalHeightOfChildren(content)
327+
}
328+
329+
if (currentSlide.classList.contains('about-us')) {
330+
const content = currentSlide.querySelector('.content') as HTMLElement
331+
const infoSection = currentSlide.querySelector('.info-section') as HTMLElement
332+
const contentWidth = content.offsetWidth
333+
let infoSectionWidth = infoSection.scrollWidth
334+
while (infoSectionWidth > contentWidth) {
335+
const scaleFactor = contentWidth / infoSectionWidth
336+
fontSize = Math.floor(scaleFactor * fontSize)
337+
338+
const infoBoxes = infoSection.querySelectorAll('.info-box') as NodeListOf<HTMLElement>
339+
infoBoxes.forEach((box) => {
340+
box.style.fontSize = `${fontSize}px`
341+
const style = getComputedStyle(box)
342+
const padding = parseFloat(style.padding) * scaleFactor
343+
box.style.padding = `${Math.max(2, Math.floor(padding))}px`
344+
box.style.margin = `${Math.max(2, Math.floor(parseFloat(style.margin) * scaleFactor))}px`
345+
})
346+
infoSectionWidth = infoSection.scrollWidth
347+
}
348+
}
349+
}
350+
function fitContent() {
351+
const images = document.querySelectorAll('img')
352+
let imagesLoaded = 0
353+
354+
images.forEach((img) => {
355+
if (img.complete) {
356+
imagesLoaded++
357+
} else {
358+
img.addEventListener('load', () => {
359+
imagesLoaded++
360+
if (imagesLoaded === images.length) {
361+
adjustFontSize()
362+
}
363+
})
364+
}
365+
})
366+
367+
if (images.length === 0) {
368+
adjustFontSize()
369+
}
370+
}
371+
function getFrontMatterFromMarkdown() {
372+
const options = {}
373+
const rawMarkdown = unref(mdTextarea).value
374+
return awesoMd.parseFrontMatter(rawMarkdown, options)[1]
375+
}
376+
function setFontColor() {
377+
const frontMatter = getFrontMatterFromMarkdown()
378+
const color = frontMatter.metadata.color
379+
document.querySelectorAll('.title p, h1').forEach((el) => {
380+
el.style.color = color
381+
})
382+
}
383+
function applyTemplateIfNeeded() {
384+
const frontMatter = getFrontMatterFromMarkdown()
385+
if (frontMatter.metadata?.slide) {
386+
// dynamically import CSS file only when needed
387+
import('./css/templates.css')
388+
setFontColor()
389+
}
390+
}
391+
async function updateLogoUrl() {
392+
const frontMatter = getFrontMatterFromMarkdown()
393+
if (frontMatter.metadata?.logo) {
394+
const newLogoUrl = await updateImageUrls(frontMatter.metadata.logo)
395+
await nextTick()
396+
const imgs = document.querySelectorAll('.logo img')
397+
imgs.forEach((img) => {
398+
img.src = newLogoUrl
399+
})
400+
}
401+
}
210402
</script>
211403

212404
<style lang="scss">

0 commit comments

Comments
 (0)