Add docs on templating
This commit is contained in:
parent
2eb6e13d32
commit
aa357101fb
4
src/components/docs/Info.astro
Normal file
4
src/components/docs/Info.astro
Normal file
@ -0,0 +1,4 @@
|
||||
<div class="my-6 px-4 py-2 rounded bg-[#f8e287] text-[#221b00] dark:bg-[#dbc66e] dark:text-[#3a3000]">
|
||||
<div class="font-bold pt-2">Warning</div>
|
||||
<slot />
|
||||
</div>
|
@ -7,9 +7,13 @@ export function highlightOnDom() {
|
||||
|
||||
// Create a visual indicador
|
||||
const indicator = document.createElement("span");
|
||||
indicator.className = `absolute top-1 right-0 inline-block text-sm select-none opacity-75 ${language === "php" ? "bg-[#4f5b93]" : ""} px-2 rounded-full`;
|
||||
|
||||
let indicator_bg_class = "";
|
||||
if (language === "php") { indicator_bg_class = "bg-[#4f5b93]"; }
|
||||
else if (language === "html") { indicator_bg_class = "bg-[#dc4a20]"; }
|
||||
|
||||
indicator.className = `absolute top-1 right-0 inline-block text-sm select-none opacity-85 ${indicator_bg_class} px-2 rounded-full`;
|
||||
indicator.innerText = language;
|
||||
pre_el.appendChild(indicator);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ case Cat(name, lives)
|
||||
<br class="hidden lg:inline-block" />
|
||||
compiled to PHP
|
||||
</h1>
|
||||
<p class="font-display text-c-text text-xl pt-6 lg:pr-12">
|
||||
<p class="text-c-text text-lg pt-6 lg:pr-12">
|
||||
Inspired by Rust, Zig and Kotlin, THP has a modern syntax,
|
||||
semantics, type system and stdlib.
|
||||
</p>
|
||||
|
@ -52,6 +52,9 @@ pagesLayout:
|
||||
title: Templating
|
||||
children:
|
||||
- path: intro
|
||||
- path: components
|
||||
- path: props
|
||||
- path: control-flow
|
||||
---
|
||||
import InteractiveCode from "../../components/InteractiveCode.astro";
|
||||
import Code from "../../components/Code.astro"
|
||||
|
194
src/pages/learn/templating/components.mdx
Normal file
194
src/pages/learn/templating/components.mdx
Normal file
@ -0,0 +1,194 @@
|
||||
---
|
||||
layout: ../../../layouts/DocsLayout.astro
|
||||
title: Components
|
||||
---
|
||||
import Code from "../../../components/Code.astro"
|
||||
import Info from "../../../components/docs/Info.astro"
|
||||
|
||||
# Components
|
||||
|
||||
Like React, a component is any function that returns `HTML`.
|
||||
|
||||
<Info>
|
||||
We still need to determine whether the functions names
|
||||
will be uppercase or not.
|
||||
|
||||
For now they will be uppercase.
|
||||
</Info>
|
||||
|
||||
<Code thpcode={`
|
||||
fun MyComponent() -> HTML
|
||||
{
|
||||
<p>Hello templates!</p>
|
||||
}
|
||||
`} />
|
||||
|
||||
Inside the HTML tags you can (mostly) write the HTML you already
|
||||
know.
|
||||
|
||||
## Text interpolation
|
||||
|
||||
Interpolation inside HTML works the same way as string interpolation.
|
||||
Use brackets to insert an expression to the HTML:
|
||||
|
||||
<Code thpcode={`
|
||||
fun MyComponent() -> HTML
|
||||
{
|
||||
val name = "John"
|
||||
|
||||
<p>Hello {name}!</p>
|
||||
}
|
||||
`} />
|
||||
|
||||
```html
|
||||
<p>Hello John!</p>
|
||||
```
|
||||
|
||||
You can use any expression that can be converted to a `String` inside,
|
||||
like conditionals.
|
||||
|
||||
<Code thpcode={`
|
||||
fun MyComponent() -> HTML
|
||||
{
|
||||
val name = "John"
|
||||
val is_vip = true
|
||||
|
||||
<p>
|
||||
{if is_vip {"Welcome"} else {"Hi"}}
|
||||
{name}!
|
||||
</p>
|
||||
}
|
||||
`} />
|
||||
|
||||
```html
|
||||
<p>Welcome John!</p>
|
||||
```
|
||||
|
||||
## Raw HTML
|
||||
|
||||
Text interpolated is always escaped:
|
||||
|
||||
<Code thpcode={`
|
||||
fun MyComponent() -> HTML
|
||||
{
|
||||
val user_input = "<b>BOLD</b>"
|
||||
|
||||
<p>answer: {user_input}</p>
|
||||
}
|
||||
`} />
|
||||
```html
|
||||
<p>answer: <b>BOLD</b></p>
|
||||
```
|
||||
|
||||
To include raw html use the `raw-html` attribute (subject to change):
|
||||
|
||||
<Code thpcode={`
|
||||
fun MyComponent() -> HTML
|
||||
{
|
||||
val user_input = "<b>BOLD</b>"
|
||||
|
||||
<p>answer: <span raw-html={user_input}></span></p>
|
||||
}
|
||||
`} />
|
||||
```html
|
||||
<p>answer: <span><b>BOLD</b></span></p>
|
||||
```
|
||||
|
||||
|
||||
## Dynamic attributes
|
||||
|
||||
TODO: boolean attributes
|
||||
|
||||
Normal attributes (plain strings) work as you'd expect:
|
||||
|
||||
<Code thpcode={`
|
||||
fun MyComponent() -> HTML
|
||||
{
|
||||
<button class="red">hello</button>
|
||||
}
|
||||
`} />
|
||||
```html
|
||||
<button class="red">hello</button>
|
||||
```
|
||||
|
||||
Dynamic attributes are used when you want to use values from code.
|
||||
For those use brackets instead of double quotes:
|
||||
|
||||
<Code thpcode={`
|
||||
fun MyComponent() -> HTML
|
||||
{
|
||||
val button_class = if true {"blue"} else {"yellow"}
|
||||
|
||||
// Note the braces
|
||||
<button class={button_class}>hello</button>
|
||||
}
|
||||
`} />
|
||||
```html
|
||||
<button class="blue">hello</button>
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Fragments
|
||||
|
||||
An HTML expression consist of a single tag that may have children inside.
|
||||
If you need to return multiple tags at once you can use fragments.
|
||||
|
||||
|
||||
The following code doesn't work as you would expect:
|
||||
|
||||
<Code thpcode={`
|
||||
fun MyComponent() -> HTML
|
||||
{
|
||||
<p>hello</p> // This is an error, an ignored expression
|
||||
<p>world</p>
|
||||
}
|
||||
`} />
|
||||
|
||||
Each `<p>` is a single expression, they are not grouped.
|
||||
And since we cannot have unused expressions, the code will not compile.
|
||||
|
||||
To have these two `<p>` tags as a single expression use a fragment:
|
||||
`<></>`
|
||||
|
||||
<Code thpcode={`
|
||||
fun MyComponent() -> HTML
|
||||
{
|
||||
// This is the root "element"
|
||||
<>
|
||||
<p>hello</p> // Now these two are together
|
||||
<p>world</p>
|
||||
</>
|
||||
}
|
||||
`} />
|
||||
```html
|
||||
<p>hello</p>
|
||||
<p>world</p>
|
||||
```
|
||||
|
||||
## Composition
|
||||
|
||||
To use a component inside another component call them as if it were an
|
||||
html tag:
|
||||
|
||||
<Code thpcode={`
|
||||
fun User() -> HTML
|
||||
{
|
||||
<span>I am the user</span>
|
||||
}
|
||||
|
||||
fun MyComponent() -> HTML
|
||||
{
|
||||
<>
|
||||
<p>status</p>
|
||||
<User /> // Here we are using the other component
|
||||
</>
|
||||
}
|
||||
`} />
|
||||
```html
|
||||
<p>status</p>
|
||||
<span>world</span>
|
||||
```
|
||||
|
||||
|
||||
|
9
src/pages/learn/templating/control-flow.mdx
Normal file
9
src/pages/learn/templating/control-flow.mdx
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
layout: ../../../layouts/DocsLayout.astro
|
||||
title: Control flow
|
||||
---
|
||||
import Code from "../../../components/Code.astro"
|
||||
import Info from "../../../components/docs/Info.astro"
|
||||
|
||||
# Control flow
|
||||
|
@ -3,6 +3,7 @@ layout: ../../../layouts/DocsLayout.astro
|
||||
title: Introduction
|
||||
---
|
||||
import Code from "../../../components/Code.astro"
|
||||
import Info from "../../../components/docs/Info.astro"
|
||||
|
||||
# THP templating
|
||||
|
||||
@ -62,7 +63,7 @@ and compose them.
|
||||
The following would be the equivalent in THP:
|
||||
|
||||
<Code thpcode={`
|
||||
fun Button(String name) -> Html {
|
||||
fun Button(String name) -> HTML {
|
||||
<button class="some tailwind classes">
|
||||
Hello {name}!
|
||||
</button>
|
||||
@ -74,7 +75,7 @@ way around, so you can have arbitrary logic in the component.
|
||||
|
||||
|
||||
<Code thpcode={`
|
||||
fun User(String name) {
|
||||
fun User(String name) -> HTML {
|
||||
// Get info from the database
|
||||
val user = try Model::get_user(name)
|
||||
else {
|
||||
@ -87,13 +88,13 @@ fun User(String name) {
|
||||
Hello {user.name}!
|
||||
<br>
|
||||
Here are your transactions:
|
||||
#for t in user.transactions {
|
||||
@for t in user.transactions {
|
||||
<TransactionItem t={t} />
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
fun TransactionItem(Transaction t) {
|
||||
fun TransactionItem(Transaction t) -> HTML {
|
||||
<li>
|
||||
{t.date} - {t.name} ({t.price})
|
||||
</li>
|
||||
@ -101,3 +102,67 @@ fun TransactionItem(Transaction t) {
|
||||
`} />
|
||||
|
||||
|
||||
## Is this a JavaScript Front-End Framework?
|
||||
|
||||
**No**
|
||||
|
||||
This is an HTML templating solution. All the code is run in the backend,
|
||||
generates static HTML and sends it to the browser. There is no
|
||||
reactivity, hooks, signals, etc.
|
||||
|
||||
If you need reactivity on the front-end and want to use this templating
|
||||
take a look at [htmx](https://htmx.org/), [Alpine.js](https://alpinejs.dev/)
|
||||
and [hyperscript](https://hyperscript.org/).
|
||||
|
||||
|
||||
|
||||
## Styling
|
||||
|
||||
We don't provide any syntax or facility for styling.
|
||||
However, this component model is good to use with TailwindCSS.
|
||||
|
||||
|
||||
## Conversion
|
||||
|
||||
<Info>
|
||||
TBD: This is a draft, subject to change.
|
||||
</Info>
|
||||
|
||||
HTML expressions will be compiled to plain strings, and
|
||||
those will be then composed.
|
||||
|
||||
<Code thpcode={`
|
||||
fun Sample(String name) -> HTML {
|
||||
val user = Model::get_user(name)
|
||||
|
||||
<button>
|
||||
@if user != null
|
||||
{
|
||||
Hello {name} (id {user.name})!
|
||||
}
|
||||
@else
|
||||
{
|
||||
Not logged in
|
||||
}
|
||||
</button>
|
||||
}
|
||||
`} />
|
||||
|
||||
```php
|
||||
function Sample(name) {
|
||||
$user = Model::get_user(name);
|
||||
|
||||
$__expr_1 = "";
|
||||
if ($user !== null) {
|
||||
$__expr_2 = user.name;
|
||||
$__expr_1 = "Hello {$name} (id {$__expr_2})";
|
||||
} else {
|
||||
$__expr_1 = "Not logged in";
|
||||
}
|
||||
|
||||
return "<button>{$__expr_1}</button>"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
156
src/pages/learn/templating/props.mdx
Normal file
156
src/pages/learn/templating/props.mdx
Normal file
@ -0,0 +1,156 @@
|
||||
---
|
||||
layout: ../../../layouts/DocsLayout.astro
|
||||
title: Props
|
||||
---
|
||||
import Code from "../../../components/Code.astro"
|
||||
import Info from "../../../components/docs/Info.astro"
|
||||
|
||||
# Props
|
||||
|
||||
Props are used to send data to a children component.
|
||||
|
||||
THP's components can receive any datatype as props,
|
||||
and they are defined as normal parameters.
|
||||
|
||||
For example, to receive a `String` declare it as a
|
||||
parameter:
|
||||
|
||||
<Code thpcode={`
|
||||
fun Greeter(String name) -> HTML
|
||||
{
|
||||
<p>Hello {name}!</p>
|
||||
}
|
||||
`} />
|
||||
|
||||
And to send its value type the parameter name as an attribute:
|
||||
|
||||
<Code thpcode={`
|
||||
fun Home() -> HTML
|
||||
{
|
||||
<div>
|
||||
<Greeter name="Rose" />
|
||||
</div>
|
||||
}
|
||||
`} />
|
||||
|
||||
```html
|
||||
<div><p>Hello Rose</p></div>
|
||||
```
|
||||
|
||||
You can have as many props as you'd like, of any datatype:
|
||||
|
||||
<Code thpcode={`
|
||||
fun Greeter(String name, Int age, Array[String] friends) -> HTML
|
||||
{
|
||||
// ...
|
||||
}
|
||||
`} />
|
||||
|
||||
## Static props
|
||||
|
||||
If the prop has a type `String` you can use a normal attribute.
|
||||
|
||||
<Code thpcode={`
|
||||
fun Home() -> HTML
|
||||
{
|
||||
<div>
|
||||
// name is a String, so we use ""
|
||||
<Greeter name="Rose" />
|
||||
</div>
|
||||
}
|
||||
`} />
|
||||
|
||||
|
||||
## Dynamic props
|
||||
|
||||
However, if the prop has any other datatype you must use a
|
||||
dynamic attribute (`{}`)
|
||||
|
||||
<Code thpcode={`
|
||||
// This component receives a Cat object
|
||||
fun Sample(Cat cat) -> HTML
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
fun Home() -> HTML
|
||||
{
|
||||
val my_cat = Cat("Michifu")
|
||||
|
||||
<div>
|
||||
<Sample cat={my_cat} />
|
||||
</div>
|
||||
}
|
||||
`} />
|
||||
|
||||
|
||||
## Components as props
|
||||
|
||||
If for some reason you want to use a component as a prop
|
||||
use the `HTML` datatype:
|
||||
|
||||
<Code thpcode={`
|
||||
// The parameter can have any name, not only \`child\`
|
||||
fun Sample(HTML child) -> HTML
|
||||
{
|
||||
<>
|
||||
<p>Sup</p>
|
||||
{child}
|
||||
</>
|
||||
}
|
||||
`} />
|
||||
|
||||
This, however, means that your prop component must be declared
|
||||
as an attribute:
|
||||
|
||||
<Code thpcode={`
|
||||
fun Home() -> HTML
|
||||
{
|
||||
<div>
|
||||
<Sample child={<span>I am the child</span>} />
|
||||
</div>
|
||||
}
|
||||
`} />
|
||||
|
||||
```html
|
||||
<div>
|
||||
<p>Sup</p>
|
||||
<span>I am the child</span>
|
||||
</div>
|
||||
```
|
||||
|
||||
A better solution may be to use slots.
|
||||
|
||||
## Slots
|
||||
|
||||
Slots allow you to write html inside a component tag.
|
||||
|
||||
<Code thpcode={`
|
||||
fun MyButton() -> HTML
|
||||
{
|
||||
<button>
|
||||
// This is the slot, it will render the HTML inside the tag
|
||||
<Slot />
|
||||
</button>
|
||||
}
|
||||
|
||||
fun Home() -> HTML
|
||||
{
|
||||
<div>
|
||||
<MyButton>
|
||||
buy <b>now!</b>
|
||||
</MyButton>
|
||||
</div>
|
||||
}
|
||||
`} />
|
||||
```html
|
||||
<div>
|
||||
<button>
|
||||
buy <b>now!</b>
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user