More Apps
Attention đź’Ą
Support since v0.2.14.
Good to know đź’ˇ
Custom templates acts like dynamic reusable components. If you want to just copy static content, you should use the include app instead.
You may have some other apps that preprocessor doesn’t support yet. However, it’s very easy to add a new app based on this project custom template engine.
In this section, I will show you how to add custom app to this preprocessor.
Create a new app
Template folder
First we need to put a new app template in the assets/templates folder (which is relative to book.toml
file).
You can change the template folder path by setting custom-templates-folder
value in the preprocessor section. The default value is assets/templates
.
[preprocessor.embedify]
custom-templates-folder = "assets/templates"
The template folder path shoulde be relative to the book root directory, which is the directory where the book.toml
file is located.
Template file
Now let’s create a new app called canvas. Which is a simple drawable canvas app.
The template file name will be the app name. For example, we want to add a new app called canvas, then we should create a canvas.html under templates folder.
If your custom app name is the same as the built-in app name, the custom app will override the built-in app while rendering.
First we add some basic html structure and some styles to the canvas.html
file:
<style>
.canvas-container {
width: 100%;
background: white;
border-radius: 1rem;
border: 1px solid #ccc;
background-size: 20px 20px;
background-image: linear-gradient(to right, #eee 1px, transparent 1px), linear-gradient(to bottom, #eee 1px, transparent
1px);
}
</style>
<div class="canvas-container">
<canvas height="600"></canvas>
</div>
And then add some js code to make it drawable:
<script>
document.addEventListener("DOMContentLoaded", () => {
const container = document.querySelector(".canvas-container");
const canvas = container.querySelector("canvas");
const ctx = canvas.getContext("2d");
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) canvas.width = entry.contentRect.width;
});
resizeObserver.observe(container);
let drawing = false;
const lastPos = { x: 0, y: 0 };
canvas.addEventListener("mousedown", (e) => {
drawing = true;
lastPos.x = e.offsetX;
lastPos.y = e.offsetY;
});
canvas.addEventListener("mousemove", (e) => {
if (!drawing) return;
ctx.beginPath();
ctx.moveTo(lastPos.x, lastPos.y);
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
lastPos.x = e.offsetX;
lastPos.y = e.offsetY;
});
canvas.addEventListener("mouseup", () => (drawing = false));
canvas.addEventListener("mouseout", () => (drawing = false));
});
</script>
Good to know đź’ˇ
You can add css and js content to the template file which should be put inside
style
andscript
blocks.
However, we want to the canvas height to be dynamic. We can do this by using placeholder syntax:
<canvas height="{% height=600 %}"></canvas>
Which means the height of the canvas will be replaced by the value of height key. If user doesn’t provide the value, the default value 600 will be used.
Placeholder syntax
Syntax
There are two ways of adding dynamic values to the template file:
- Put key name in the placeholder, like {% key %}, and you can add default value after the key name, like {% key=default %}. The default value will be used if user doesn’t provide the value.
- Wrapped with preprocessor name, like {% processor(key=default) %}. The processor name acts like function name, it will be used to process the inner value and replace the placeholder.
Placeholder
The inner value is key follwed by a default value in the form of key=default. If the key is not provided, the default value will be used.
Preprocessor
Now only markdown is supported, markdown will treat the inner value as markdown content and render it to be html.
Examples
- {% height %} means the placeholder will be replaced by the value of height key and height is not optional because it doesn’t have a default value.
- {% height=600 %} means the placeholder will be replaced by the value of height key. If user doesn’t provide the value, the default value 600 will be used.
- {% markdown(message) %} means the placeholder will be replaced by the value of message processed by markdown processor.
Final template file
Here is the final template file for the canvas app:
<style>
.canvas-container {
width: 100%;
background: white;
border-radius: 1rem;
border: 1px solid #ccc;
background-size: 20px 20px;
background-image: linear-gradient(to right, #eee 1px, transparent 1px),
linear-gradient(to bottom, #eee 1px, transparent 1px);
}
</style>
<div class="canvas-container">
<canvas height="{% height=600 %}"></canvas>
</div>
<script>
document.addEventListener("DOMContentLoaded", () => {
const container = document.querySelector(".canvas-container");
const canvas = container.querySelector("canvas");
const ctx = canvas.getContext("2d");
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) canvas.width = entry.contentRect.width;
});
resizeObserver.observe(container);
let drawing = false;
const lastPos = { x: 0, y: 0 };
canvas.addEventListener("mousedown", (e) => {
drawing = true;
lastPos.x = e.offsetX;
lastPos.y = e.offsetY;
});
canvas.addEventListener("mousemove", (e) => {
if (!drawing) return;
ctx.beginPath();
ctx.moveTo(lastPos.x, lastPos.y);
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
lastPos.x = e.offsetX;
lastPos.y = e.offsetY;
});
canvas.addEventListener("mouseup", () => (drawing = false));
canvas.addEventListener("mouseout", () => (drawing = false));
});
</script>
Use the new app
After creating the template file, we can use the new app in our book:
{% embed canvas height=600 %}
Because the height has default value of 600, we can omit it:
{% embed canvas %}
Test canvas app by drawing something on it:
Conclusion
That’s it.
You can also use the same method to add your own custom apps to this preprocessor. Just clone this repository and add your own app template to the src/assets/templates folder.
Welcome to contribute to this project by adding more apps. If you have any questions or suggestions, feel free to open an issue or pull request on the GitHub repository.