diff --git a/README.md b/README.md index b7c5744..480c1be 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,57 @@ -Thymeleaf Component Dialect -=========================== +

+ Thymeleaf Component Dialect +

-[![CI](https://github.com/Serbroda/thymeleaf-component-dialect/actions/workflows/ci.yml/badge.svg)](https://github.com/Serbroda/thymeleaf-component-dialect/actions/workflows/ci.yml) -[![jitpack](https://jitpack.io/v/Serbroda/thymeleaf-component-dialect.svg)](https://jitpack.io/#Serbroda/thymeleaf-component-dialect) -[![license](https://img.shields.io/github/license/Serbroda/thymeleaf-component-dialect.svg)](https://github.com/Serbroda/thymeleaf-component-dialect/blob/main/LICENSE.txt) +

Thymeleaf Component Dialect

-A dialect for creating reusable Thymeleaf components, similar to React or Vue components. +

+ CI + jitpack + license +

-Requirements ------- +

A dialect for creating reusable Thymeleaf components with named slots, similar to React or Vue components.

-- Java 17+ -- Thymeleaf 3.1+ +## Quick Example -Installation ------- +**Define** a component (`templates/components/panel.html`): -Add the jitpack repository. +```html +
+
+
+
+``` + +**Use** it in your template: + +```html + +

Hello world

+
+``` + +**Result:** + +```html +
+
My Title
+

Hello world

+
+``` + +## Features + +- **Reusable Components** - Define with `th:fragment`, use with `` tags +- **[Named Slots](docs/named-slots.md)** - Multiple content areas per component +- **[Fallback Content](docs/named-slots.md#fallback-content)** - Default content when no slot is provided +- **[tc:once](docs/once-attribute.md)** - Deduplicate scripts and styles +- **[Attribute Replacement](docs/components.md#attribute-replacement)** - Dynamic placeholder values +- **Spring Boot Auto-Configuration** - Just add the dependency, no setup needed + +## Installation + +Requires **Java 17+** and **Thymeleaf 3.1+**. ```xml @@ -25,11 +60,7 @@ Add the jitpack repository. https://jitpack.io -``` -Add the dependency (for all available versions see [https://jitpack.io/#Serbroda/thymeleaf-component-dialect](https://jitpack.io/#Serbroda/thymeleaf-component-dialect)). - -```xml com.github.Serbroda thymeleaf-component-dialect @@ -37,88 +68,18 @@ Add the dependency (for all available versions see [https://jitpack.io/#Serbroda ``` -**With Spring Boot 3.x:** No configuration needed. The dialect is auto-configured and scans `templates/components/` for component fragments. - -**Custom configuration:** To customize the component directory or register components manually, define your own `ComponentDialect` bean (the auto-configuration will back off): - -```java -@Bean -public ComponentDialect componentDialect() { - ComponentDialect dialect = new ComponentDialect(); - dialect.addParser(new StandardThymeleafComponentParser("templates/", ".html", "my-components")); - return dialect; -} -``` +**Spring Boot 3.x:** No configuration needed - auto-configured out of the box. -**Without Spring Boot:** Register the dialect manually with your `TemplateEngine`. +See [Getting Started](docs/getting-started.md) for Gradle setup, custom configuration, and non-Spring Boot usage. -Usage ------ +## Documentation -### Create a thymeleaf component - -Thymeleaf components uses the standard `th:fragment` attribute to register components. Just create a fragment with a `` tag which will be replaced with specific contents. - -```html -
-
- -
-
- -
-
-``` - -### Use the component - -Add the namespace `xmlns:tc="https://github.com/Serbroda/thymeleaf-component-dialect"` and use the component in your application. - -```html - - - - - - -

Hello world

-

This is my first thymeleaf component

-
- - -``` - -### The result will be - -```html -
-
- A title -
-
-

Hello world

-

This is my first thymeleaf component

-
-
-``` - -### The `tc:once` Attribute - -Use `tc:once` to ensure an element (e.g. a script tag) is only rendered once, even if the component is used multiple times on the same page: - -```html -
- - -
-``` +- [Getting Started](docs/getting-started.md) - Installation and setup +- [Components](docs/components.md) - Defining and using components +- [Named Slots](docs/named-slots.md) - Multi-slot content projection +- [tc:once Attribute](docs/once-attribute.md) - Deduplicate scripts and styles -Contributing ------- +## Contributing Contributions are welcome! Feel free to open an [issue](https://github.com/Serbroda/thymeleaf-component-dialect/issues) or submit a [pull request](https://github.com/Serbroda/thymeleaf-component-dialect/pulls). @@ -126,7 +87,6 @@ Before submitting a PR, please make sure: - All tests pass: `./mvnw clean verify` - Code is formatted: `./mvnw spotless:apply` -License ------- +## License -This project is licensed under the [Apache License, Version 2.0](LICENSE.txt). \ No newline at end of file +This project is licensed under the [Apache License, Version 2.0](LICENSE.txt). diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000..22ab2c5 Binary files /dev/null and b/assets/logo.png differ diff --git a/docs/components.md b/docs/components.md new file mode 100644 index 0000000..e52182e --- /dev/null +++ b/docs/components.md @@ -0,0 +1,226 @@ +# Components + +## Defining a Component + +Components are standard Thymeleaf fragments. Place them in your components directory (default: `templates/components/`). Use `` to mark where child content will be inserted. + +```html + +
+
+ +
+
+ +
+
+``` + +## Using a Component + +Add the `tc` namespace to your template and use the component by its fragment name: + +```html + + + + +

This content replaces the tc:content tag.

+
+ + +``` + +**Result:** + +```html +
+
+ My Panel Title +
+
+

This content replaces the tc:content tag.

+
+
+``` + +## Constructor Parameters + +Use `tc:constructor` to pass parameters to the fragment. The values are Thymeleaf expressions: + +```html + +
+ + +
+``` + +```html + + +

Something went wrong.

+
+``` + +**Result:** + +```html +
+ Error! +

Something went wrong.

+
+``` + +## Passing Variables + +Attributes on the component tag (that are not `th:*` or `tc:*`) are available as variables inside the component: + +```html + + +``` + +```html + +Click me +``` + +**Result:** + +```html + +``` + +Boolean attributes (without a value) are set to `true`: + +```html + + +``` + +```html + + +``` + +**Result:** + +```html + +``` + +## Custom Selectors + +By default, the component tag name matches the fragment name. Use `tc:selector` to define a different tag name: + +```html + +
+ +
+``` + +```html + + + + + +``` + +**Result:** + +```html +
+ + +
+``` + +## Attribute Replacement + +Use `tc:repl-*` attributes to replace placeholder values inside the component. In the component definition, use `?[name]` as placeholder: + +```html + +
+ + + +
+``` + +```html + +
+ + + +``` + +**Result** (with a user object having `name` and `email` fields): + +```html +
+
+ + +
+
+ + +
+
+``` + +## Multiple Components per File + +A single HTML file can contain multiple fragment definitions: + +```html + + + + +``` + +```html + + + + + +``` + +**Result:** + +```html + +``` diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 0000000..519947f --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,78 @@ +# Getting Started + +## Requirements + +- Java 17+ +- Thymeleaf 3.1+ + +## Installation + +Add the JitPack repository and the dependency to your `pom.xml`: + +```xml + + + jitpack.io + https://jitpack.io + + + + + com.github.Serbroda + thymeleaf-component-dialect + VERSION + +``` + +For Gradle: + +```groovy +repositories { + maven { url 'https://jitpack.io' } +} + +dependencies { + implementation 'com.github.Serbroda:thymeleaf-component-dialect:VERSION' +} +``` + +## Setup + +### Spring Boot 3.x (auto-configured) + +No configuration needed. The dialect is auto-configured and scans `templates/components/` for component fragments. + +You can customize the defaults in `application.yml`: + +```yaml +thymeleaf-component-dialect: + template-prefix: templates/ + template-suffix: .html + component-directory: components +``` + +To fully customize the dialect, define your own bean (the auto-configuration will back off): + +```java +@Bean +public ComponentDialect componentDialect() { + var dialect = new ComponentDialect(); + dialect.addParser(new StandardThymeleafComponentParser( + "templates/", ".html", "my-components")); + return dialect; +} +``` + +### Without Spring Boot + +Register the dialect manually with your `TemplateEngine`: + +```java +TemplateEngine engine = new TemplateEngine(); +engine.setTemplateResolver(templateResolver); + +ComponentDialect dialect = new ComponentDialect(); +dialect.addParser(new StandardThymeleafComponentParser( + "templates/", ".html", "components")); +engine.addDialect(dialect.getPrefix(), dialect); +``` diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..9efdad3 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,51 @@ +

+ Thymeleaf Component Dialect +

+ +# Thymeleaf Component Dialect + +A Thymeleaf dialect for creating reusable UI components, similar to React or Vue components. + +## Features + +- **Reusable Components** – Define components with `th:fragment`, use them with `` tags +- **Named Slots** – Multiple content areas per component with `` and `` +- **Fallback Content** – Default content for slots when none is provided +- **Constructor Parameters** – Pass parameters via `tc:constructor` +- **tc:once** – Deduplicate scripts and styles across multiple component instances +- **Attribute Replacement** – Dynamic attribute values with `tc:repl-*` +- **Spring Boot Auto-Configuration** – Just add the dependency, no setup needed + +## Quick Example + +**Component definition** (`templates/components/alert.html`): + +```html +
+ +
+``` + +**Usage:** + +```html + + Done! Your changes have been saved. + +``` + +**Result:** + +```html +
+ Done! Your changes have been saved. +
+``` + +## Next Steps + +- [Getting Started](getting-started.md) – Installation and setup +- [Components](components.md) – Defining and using components +- [Named Slots](named-slots.md) – Multi-slot content projection +- [tc:once Attribute](once-attribute.md) – Deduplicate scripts and styles diff --git a/docs/named-slots.md b/docs/named-slots.md new file mode 100644 index 0000000..749212a --- /dev/null +++ b/docs/named-slots.md @@ -0,0 +1,188 @@ +# Named Slots + +Named slots allow components to define multiple content areas that can be filled independently, similar to Vue's `` or React's `props.children` with render props. + +## Basic Concept + +- **``** in the component definition marks a content insertion point (slot) +- **``** defines a named slot +- **``** in the usage fills a named slot +- Content not wrapped in `` goes into the default (unnamed) slot + +## Example + +### Component Definition + +```html + +
+
+ +
+
+ +
+
+ +
+
+``` + +### Usage + +```html + + +

My Page Title

+
+ + Copyright 2026 + + +

This goes into the default slot (main).

+
+``` + +### Result + +```html +
+
+

My Page Title

+
+
+

This goes into the default slot (main).

+
+
+ Copyright 2026 +
+
+``` + +## Fallback Content + +Slots can define fallback content that is rendered when no matching `` is provided: + +```html + +
+
+ Default Header +
+
+ Default Body +
+
+``` + +```html + + + Custom Header + +``` + +Result: + +```html +
+
+ Custom Header +
+
+ Default Body +
+
+``` + +## Empty Slots + +An empty component (or empty slots) will use the fallback content: + +```html + + +``` + +**Result:** + +```html +
+
+ Default Header +
+
+ Default Body +
+
+``` + +## Combining with Constructor Parameters + +Named slots work alongside `tc:constructor`: + +```html + +
+
+ +
+
+ +
+ +
+``` + +```html + + + + 4 countries registered + + +

Body content here

+
+``` + +**Result:** + +```html +
+
+ Countries +
+
+

Body content here

+
+ +
+``` + +## Unmatched Slots + +If a `` references a name that doesn't exist in the component, the slot content is silently ignored. The default content still renders normally. + +```html + + This will be ignored +

This goes into the default slot

+
+``` + +**Result:** + +```html +
+
+ Default Header +
+
+

This goes into the default slot

+
+
+``` diff --git a/docs/once-attribute.md b/docs/once-attribute.md new file mode 100644 index 0000000..6ac5fa0 --- /dev/null +++ b/docs/once-attribute.md @@ -0,0 +1,68 @@ +# The `tc:once` Attribute + +## Problem + +When a component includes a ` + + + +``` + +### Usage + +```html + + + +``` + +### Result + +```html + +
+ + + +
+
+ + +
+
+ + +
+``` + +## How It Works + +The `tc:once` attribute uses Thymeleaf's ID sequence mechanism. The first occurrence with a given value is rendered. All subsequent occurrences with the same value are removed from the output. + +Use a unique, descriptive identifier for each `tc:once` value (e.g. `component-name.script`, `component-name.css`).