From aa357101fb179a0c42de95bfc7dc874fc2cf0135 Mon Sep 17 00:00:00 2001 From: Araozu Date: Fri, 19 Jul 2024 22:08:37 -0500 Subject: [PATCH] Add docs on templating --- src/components/docs/Info.astro | 4 + src/layouts/thpHighlighter.ts | 8 +- src/pages/index.astro | 2 +- src/pages/learn/index.mdx | 3 + src/pages/learn/templating/components.mdx | 194 ++++++++++++++++++++ src/pages/learn/templating/control-flow.mdx | 9 + src/pages/learn/templating/intro.mdx | 73 +++++++- src/pages/learn/templating/props.mdx | 156 ++++++++++++++++ 8 files changed, 442 insertions(+), 7 deletions(-) create mode 100644 src/components/docs/Info.astro create mode 100644 src/pages/learn/templating/components.mdx create mode 100644 src/pages/learn/templating/control-flow.mdx create mode 100644 src/pages/learn/templating/props.mdx diff --git a/src/components/docs/Info.astro b/src/components/docs/Info.astro new file mode 100644 index 0000000..7d86c24 --- /dev/null +++ b/src/components/docs/Info.astro @@ -0,0 +1,4 @@ +
+
Warning
+ +
diff --git a/src/layouts/thpHighlighter.ts b/src/layouts/thpHighlighter.ts index 7f11461..23897c2 100644 --- a/src/layouts/thpHighlighter.ts +++ b/src/layouts/thpHighlighter.ts @@ -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); } } - diff --git a/src/pages/index.astro b/src/pages/index.astro index e7638fd..2de652d 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -43,7 +43,7 @@ case Cat(name, lives) compiled to PHP -

+

Inspired by Rust, Zig and Kotlin, THP has a modern syntax, semantics, type system and stdlib.

diff --git a/src/pages/learn/index.mdx b/src/pages/learn/index.mdx index e558562..0097fcf 100644 --- a/src/pages/learn/index.mdx +++ b/src/pages/learn/index.mdx @@ -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" diff --git a/src/pages/learn/templating/components.mdx b/src/pages/learn/templating/components.mdx new file mode 100644 index 0000000..0b29cca --- /dev/null +++ b/src/pages/learn/templating/components.mdx @@ -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`. + + + We still need to determine whether the functions names + will be uppercase or not. + + For now they will be uppercase. + + + HTML +{ +

Hello templates!

+} +`} /> + +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: + + HTML +{ + val name = "John" + +

Hello {name}!

+} +`} /> + +```html +

Hello John!

+``` + +You can use any expression that can be converted to a `String` inside, +like conditionals. + + HTML +{ + val name = "John" + val is_vip = true + +

+ {if is_vip {"Welcome"} else {"Hi"}} + {name}! +

+} +`} /> + +```html +

Welcome John!

+``` + +## Raw HTML + +Text interpolated is always escaped: + + HTML +{ + val user_input = "BOLD" + +

answer: {user_input}

+} +`} /> +```html +

answer: <b>BOLD</b>

+``` + +To include raw html use the `raw-html` attribute (subject to change): + + HTML +{ + val user_input = "BOLD" + +

answer:

+} +`} /> +```html +

answer: BOLD

+``` + + +## Dynamic attributes + +TODO: boolean attributes + +Normal attributes (plain strings) work as you'd expect: + + HTML +{ + +} +`} /> +```html + +``` + +Dynamic attributes are used when you want to use values from code. +For those use brackets instead of double quotes: + + HTML +{ + val button_class = if true {"blue"} else {"yellow"} + + // Note the braces + +} +`} /> +```html + +``` + + + +## 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: + + HTML +{ +

hello

// This is an error, an ignored expression +

world

+} +`} /> + +Each `

` is a single expression, they are not grouped. +And since we cannot have unused expressions, the code will not compile. + +To have these two `

` tags as a single expression use a fragment: +`<>` + + HTML +{ + // This is the root "element" + <> +

hello

// Now these two are together +

world

+ +} +`} /> +```html +

hello

+

world

+``` + +## Composition + +To use a component inside another component call them as if it were an +html tag: + + HTML +{ + I am the user +} + +fun MyComponent() -> HTML +{ + <> +

status

+ // Here we are using the other component + +} +`} /> +```html +

status

+world +``` + + + diff --git a/src/pages/learn/templating/control-flow.mdx b/src/pages/learn/templating/control-flow.mdx new file mode 100644 index 0000000..c5303a9 --- /dev/null +++ b/src/pages/learn/templating/control-flow.mdx @@ -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 + diff --git a/src/pages/learn/templating/intro.mdx b/src/pages/learn/templating/intro.mdx index 7653f57..f8bce58 100644 --- a/src/pages/learn/templating/intro.mdx +++ b/src/pages/learn/templating/intro.mdx @@ -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: Html { +fun Button(String name) -> HTML { @@ -74,7 +75,7 @@ way around, so you can have arbitrary logic in the component. 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}!
Here are your transactions: - #for t in user.transactions { + @for t in user.transactions { } } -fun TransactionItem(Transaction t) { +fun TransactionItem(Transaction t) -> HTML {
  • {t.date} - {t.name} ({t.price})
  • @@ -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 + + + TBD: This is a draft, subject to change. + + +HTML expressions will be compiled to plain strings, and +those will be then composed. + + HTML { + val user = Model::get_user(name) + + +} +`} /> + +```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 "" +} +``` + + + diff --git a/src/pages/learn/templating/props.mdx b/src/pages/learn/templating/props.mdx new file mode 100644 index 0000000..a50eb9b --- /dev/null +++ b/src/pages/learn/templating/props.mdx @@ -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: + + HTML +{ +

    Hello {name}!

    +} +`} /> + +And to send its value type the parameter name as an attribute: + + HTML +{ +
    + +
    +} +`} /> + +```html +

    Hello Rose

    +``` + +You can have as many props as you'd like, of any datatype: + + HTML +{ + // ... +} +`} /> + +## Static props + +If the prop has a type `String` you can use a normal attribute. + + HTML +{ +
    + // name is a String, so we use "" + +
    +} +`} /> + + +## Dynamic props + +However, if the prop has any other datatype you must use a +dynamic attribute (`{}`) + + HTML +{ + // ... +} + +fun Home() -> HTML +{ + val my_cat = Cat("Michifu") + +
    + +
    +} +`} /> + + +## Components as props + +If for some reason you want to use a component as a prop +use the `HTML` datatype: + + HTML +{ + <> +

    Sup

    + {child} + +} +`} /> + +This, however, means that your prop component must be declared +as an attribute: + + HTML +{ +
    + I am the child} /> +
    +} +`} /> + +```html +
    +

    Sup

    + I am the child +
    +``` + +A better solution may be to use slots. + +## Slots + +Slots allow you to write html inside a component tag. + + HTML +{ + +} + +fun Home() -> HTML +{ +
    + + buy now! + +
    +} +`} /> +```html +
    + +
    +``` + + + +