From 8ec595f290a36e10ab7166a42d79272478b2f507 Mon Sep 17 00:00:00 2001 From: Eric Handtke Date: Thu, 17 Jul 2025 14:25:23 +0200 Subject: [PATCH 1/5] added new sections to the element-composite article. --- docs/docs/building-ui/element-composite.md | 158 +++++++++++++++++---- 1 file changed, 132 insertions(+), 26 deletions(-) diff --git a/docs/docs/building-ui/element-composite.md b/docs/docs/building-ui/element-composite.md index 3ad167d32..93c42390c 100644 --- a/docs/docs/building-ui/element-composite.md +++ b/docs/docs/building-ui/element-composite.md @@ -7,15 +7,86 @@ slug: element_composite -The `ElementComposite` class serves as a versatile foundation for managing composite elements in webforJ applications. Its primary purpose is to facilitate the interaction with HTML elements, represented by the `Element` class, by providing a structured approach to handle properties, attributes, and event listeners. It allows for implementation and reuse of elements in an application. Use the `ElementComposite` class when implementing Web Components for use in webforJ applications. + +The `ElementComposite` class serves as a versatile foundation for managing composite elements in webforJ apps. Its primary purpose is to facilitate the interaction with HTML elements, represented by the `Element` class, by providing a structured approach to handle properties, attributes, and event listeners. It allows for implementation and reuse of elements in an app. Use the `ElementComposite` class when implementing Web Components for use in webforJ apps. While using the `ElementComposite` class, using the `getElement()` method will give you access to the underlying `Element` component. Similarly, the `getNodeName()` method gives you the name of that node in the DOM. :::tip -It is possible to do everything with the `Element` class itself, without using `ElementComposite` class. However, the provided methods in the `ElementComposite` give users a way to reuse the work that's being done. +It's possible to do everything with the `Element` class itself, without using `ElementComposite` class. However, the provided methods in the `ElementComposite` give users a way to reuse the work that's being done. ::: -Throughout this guide, we'll be implementing the [Shoelace QR code web component](https://shoelace.style/components/qr-code) using the `ElementComposite` class. + +## Annotations + +The `ElementComposite` class supports several annotations to simplify integration with web components: + +- **@NodeName**: Defines the custom HTML tag for your component. For example, `@NodeName("sl-qr-code")` will create a `` element in the DOM. +- **@JavaScript**: Loads external JavaScript resources (such as third-party web components). Example: + ```java + @JavaScript(value = "https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace/dist/shoelace.js", attributes = {@Attribute(name = "type", value = "module")}) + ``` +- **@StyleSheet**: Loads external CSS files for your component. Example: + ```java + @StyleSheet("https://cdn.example.com/library.css") + ``` + +These annotations are typically placed on your `ElementComposite` subclass to make sure are loaded automatically when the component is used. + + +This guide demonstrates integration of the [Shoelace QR code web component](https://shoelace.style/components/qr-code) using the `ElementComposite` class. +## Concern Interfaces + +To add common behaviors to your custom element, implement the appropriate concern interfaces. For example: + +- **HasText**: Adds support for getting and setting text content. +- **HasClassName**: Adds support for manipulating CSS class names. +- **HasStyle**: Adds support for inline styles. + +You can implement multiple interfaces to compose the desired capability. Example: +```java +@NodeName("my-component") +public final class MyComponent extends ElementComposite + implements HasText, HasClassName, HasStyle { + // ... +} +``` +## `ElementCompositeContainer` for components with slots + +For components that need to manage child components in named slots (such as headers, footers, or content areas), extend `ElementCompositeContainer` instead of `ElementComposite`. + +`ElementCompositeContainer` provides a structured way to add, remove, and manage components in specific slots. For example: + +```java +@NodeName("my-container") +public final class MyContainer extends ElementCompositeContainer + implements HasClassName, HasStyle { + private static final String HEADER_SLOT = "header"; + private static final String CONTENT_SLOT = "content"; + private static final String FOOTER_SLOT = "footer"; + + public MyContainer() { + super(); + } + + public MyContainer addToHeader(Component... components) { + getElement().add(HEADER_SLOT, components); + return this; + } + + public MyContainer addToContent(Component... components) { + add(components); // Default slot + return this; + } + + public MyContainer addToFooter(Component... components) { + getElement().add(FOOTER_SLOT, components); + return this; + } +} +``` + +Use `ElementCompositeContainer` whenever your component needs to support multiple content areas or slots, such as layouts, dialogs, or toolbars. + ## Property and attribute descriptors -Properties and attributes in web components represent the state of the component. They are often used to manage data or configuration. The `ElementComposite` class provides a convenient way to work with properties and attributes. +Properties and attributes in web components represent the state and configuration of a component. In webforJ, the `ElementComposite` class provides a structured way to define, set, and get properties and attributes using `PropertyDescriptor`. -Properties and attributes can be declared and initialized as `PropertyDescriptor` members of the `ElementComposite` class being written, and then used in the code. To define properties and attributes, use the `set()` method to set the value of a property. For example, `set(PropertyDescriptor property, V value)` sets a property to a specified value. +### Defining properties and attributes -:::info -Properties are accessed and manipulated internally within the component's code and do not reflect in the DOM. Attributes on the other hand are part of the component's external interface and can be used to pass information into a component from the outside, providing a way for external elements or scripts to configure the component. -::: +To define a property or attribute, declare a `PropertyDescriptor` as a field in your `ElementComposite` subclass. Use `PropertyDescriptor.property("name", defaultValue)` for properties and `PropertyDescriptor.attribute("name", defaultValue)` for attributes. +**Syntax:** +```java +// For a property (not reflected in the DOM) +private final PropertyDescriptor PROPERTY_NAME = PropertyDescriptor.property("property-name", defaultValue); + +// For an attribute (reflected in the DOM) +private final PropertyDescriptor ATTRIBUTE_NAME = PropertyDescriptor.attribute("attribute-name", defaultValue); +``` + +### Defining the type of properties + +The generic type parameter `` specifies the Java type of the property or attribute. This guarantees type safety when setting or getting values. + +**Example:** ```java -// Example property called TITLE in an ElementComposite class private final PropertyDescriptor TITLE = PropertyDescriptor.property("title", ""); -// Example attribute called VALUE in an ElementComposite class -private final PropertyDescriptor VALUE = PropertyDescriptor.attribute("value", ""); -//... +private final PropertyDescriptor COUNT = PropertyDescriptor.attribute("count", 0); +``` + +### Setting and getting values + +Use the `set()` method to assign a value, and the `get()` method to retrieve it: +```java set(TITLE, "My Title"); -set(VALUE, "My Value"); +String title = get(TITLE); ``` -In addition to setting a property, utilize the `get()` method in the `ElementComposite` class to access and read properties. The `get()` method can be passed an optional `boolean` value, which is false by default, to dictate whether the method should make a trip to the client to retrieve the value. This impacts performance, but might be necessary if the property can be modified purely in the client. +You can also use the overloaded `get()` method to specify whether to fetch the value from the client, and to cast to a specific type: +```java +String title = get(TITLE, false, String.class); +``` -A `Type` can also be passed to the method, which dictates what to cast retrieved result to. +### Best practices for validating properties -:::tip -This `Type` is not overtly necessary, and adds an extra layer of specification as the data is retrieved. -::: +To guarantee valid values, add validation logic in your setter methods or before calling `set()`. For example: +```java +public void setCount(int count) { + if (count < 0) { + throw new IllegalArgumentException("Count must be non-negative"); + } + set(COUNT, count); +} +``` +This approach helps prevent invalid state. + +### Enum-style properties + +For properties that should only accept a fixed set of values, use Java enums. Define the enum, then use it as the type parameter for your `PropertyDescriptor`: ```java -// Example property called TITLE in an ElementComposite class -private final PropertyDescriptor TITLE = PropertyDescriptor.property("title", ""); -//... -String title = get(TITLE, false, String); +public enum Status { + ACTIVE, INACTIVE, DISABLED +} + +private final PropertyDescriptor STATUS = PropertyDescriptor.attribute("status", Status.ACTIVE); + +// Usage +set(STATUS, Status.INACTIVE); +Status status = get(STATUS); ``` -In the demo below, properties have been added for the QR code based on the documentation for the web component. Methods have then been implemented which allow users to get and set the various properties that have been implemented. +This pattern guarantees valid values are used and improves code readability. { The `ElementComposite` events are different than `Element` events, in that this doesn't allow any class, but only specified `Event` classes. ::: -In the demonstration below, a click event has been created and then added to the QR code component. This event, when fired, will display the "X" coordinate of the mouse at the time of clicking the component, which is provided to the Java event as data. A method is then implemented to allow the user to access this data, which is how it is displayed in the application. +In the demonstration below, a click event has been created and then added to the QR code component. This event, when fired, will display the "X" coordinate of the mouse at the time of clicking the component, which is provided to the Java event as data. A method is then implemented to allow the user to access this data, which is how it is displayed in the app. -## Interacting with Slots +## Interacting with slots Web components often use slots to allow developers to define the structure of a component from the outside. A slot is a placeholder inside a web component that can be filled with content when using the component. In the context of the `ElementComposite` class, slots provide a way to customize the content within a component. The following methods are provided to allow developers to interact with and manipulate slots: From 4ca234bc8211588f65684c69859e447696e634f7 Mon Sep 17 00:00:00 2001 From: Eric Handtke Date: Thu, 17 Jul 2025 14:28:05 +0200 Subject: [PATCH 2/5] Removed vale issues. --- docs/docs/building-ui/element-composite.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs/building-ui/element-composite.md b/docs/docs/building-ui/element-composite.md index 93c42390c..e0a37a571 100644 --- a/docs/docs/building-ui/element-composite.md +++ b/docs/docs/building-ui/element-composite.md @@ -187,7 +187,7 @@ addEventListener(ClickEvent.class, event -> { The `ElementComposite` events are different than `Element` events, in that this doesn't allow any class, but only specified `Event` classes. ::: -In the demonstration below, a click event has been created and then added to the QR code component. This event, when fired, will display the "X" coordinate of the mouse at the time of clicking the component, which is provided to the Java event as data. A method is then implemented to allow the user to access this data, which is how it is displayed in the app. +In the demonstration below, a click event has been created and then added to the QR code component. This event, when fired, will display the "X" coordinate of the mouse at the time of clicking the component, which is provided to the Java event as data. A method is then implemented to allow the user to access this data, which is how it's displayed in the app. Date: Wed, 30 Jul 2025 15:18:25 +0200 Subject: [PATCH 3/5] Addressed feedback. --- docs/docs/building-ui/element-composite.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/docs/building-ui/element-composite.md b/docs/docs/building-ui/element-composite.md index e0a37a571..0022c0047 100644 --- a/docs/docs/building-ui/element-composite.md +++ b/docs/docs/building-ui/element-composite.md @@ -7,8 +7,7 @@ slug: element_composite - -The `ElementComposite` class serves as a versatile foundation for managing composite elements in webforJ apps. Its primary purpose is to facilitate the interaction with HTML elements, represented by the `Element` class, by providing a structured approach to handle properties, attributes, and event listeners. It allows for implementation and reuse of elements in an app. Use the `ElementComposite` class when implementing Web Components for use in webforJ apps. +The `ElementComposite` class provides a structured base for building reusable web components in webforJ. It streamlines interaction with underlying HTML elements, enabling you to define properties, attributes, and event listeners in a type-safe, maintainable way. Use `ElementComposite` to encapsulate and integrate custom elements or third-party web components within your app. While using the `ElementComposite` class, using the `getElement()` method will give you access to the underlying `Element` component. Similarly, the `getNodeName()` method gives you the name of that node in the DOM. @@ -31,10 +30,12 @@ The `ElementComposite` class supports several annotations to simplify integratio @StyleSheet("https://cdn.example.com/library.css") ``` +:::tip These annotations are typically placed on your `ElementComposite` subclass to make sure are loaded automatically when the component is used. - +::: This guide demonstrates integration of the [Shoelace QR code web component](https://shoelace.style/components/qr-code) using the `ElementComposite` class. + ## Concern Interfaces To add common behaviors to your custom element, implement the appropriate concern interfaces. For example: @@ -174,7 +175,7 @@ height='250px' ## Event registration -Events are a crucial part of web components, allowing communication between different parts of an app. The `ElementComposite` class simplifies event registration and handling. To register an event listener, use the `addEventListener()` method to register event listeners for specific event types. Specify the event class, the listener, and optional event options. +Events enable communication between parts of your app and are essential for interactive components. `ElementComposite` makes event handling straightforward: register listeners for specific event types using `addEventListener()` with the event class, your listener, and optional options. ```java // Example: Adding a click event listener From ea26f06df9fb688e086b6f518c9d583f58a38a7e Mon Sep 17 00:00:00 2001 From: Ben Brennan Date: Mon, 25 Aug 2025 11:30:51 -0600 Subject: [PATCH 4/5] chore: Revision edits to PR #429 --- docs/docs/building-ui/element-composite.md | 72 ++++++++++++---------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/docs/docs/building-ui/element-composite.md b/docs/docs/building-ui/element-composite.md index 0022c0047..271b5e149 100644 --- a/docs/docs/building-ui/element-composite.md +++ b/docs/docs/building-ui/element-composite.md @@ -7,44 +7,57 @@ slug: element_composite -The `ElementComposite` class provides a structured base for building reusable web components in webforJ. It streamlines interaction with underlying HTML elements, enabling you to define properties, attributes, and event listeners in a type-safe, maintainable way. Use `ElementComposite` to encapsulate and integrate custom elements or third-party web components within your app. +The `ElementComposite` class provides a structured base for building reusable web components in webforJ. It allows you to easily define properties, attributes, and event listeners of the underlying HTML elements in a type-safe, maintainable way. Use `ElementComposite` to encapsulate and integrate custom elements or third-party web components within your app. While using the `ElementComposite` class, using the `getElement()` method will give you access to the underlying `Element` component. Similarly, the `getNodeName()` method gives you the name of that node in the DOM. :::tip -It's possible to do everything with the `Element` class itself, without using `ElementComposite` class. However, the provided methods in the `ElementComposite` give users a way to reuse the work that's being done. +While it's possible to do everything with the `Element` class itself, the provided methods in the `ElementComposite` give you a way to reuse the work that's being done. ::: +:::info +This guide uses the [Shoelace QR code web component](https://shoelace.style/components/qr-code) to show how to integrate third-party web components. +::: ## Annotations The `ElementComposite` class supports several annotations to simplify integration with web components: - **@NodeName**: Defines the custom HTML tag for your component. For example, `@NodeName("sl-qr-code")` will create a `` element in the DOM. -- **@JavaScript**: Loads external JavaScript resources (such as third-party web components). Example: + +- **@JavaScript**: Loads external JavaScript resources (such as third-party web components): + ```java - @JavaScript(value = "https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace/dist/shoelace.js", attributes = {@Attribute(name = "type", value = "module")}) + @JavaScript(value = "https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.0.0-beta.87/dist/shoelace.js", + attributes = {@Attribute(name = "type", value = "module")}) ``` -- **@StyleSheet**: Loads external CSS files for your component. Example: + +- **@StyleSheet**: Loads external CSS files for your component: + ```java - @StyleSheet("https://cdn.example.com/library.css") + @StyleSheet(url = "https://cdn.example.com/library.css") ``` :::tip -These annotations are typically placed on your `ElementComposite` subclass to make sure are loaded automatically when the component is used. +These annotations are typically placed on the subclass extending the `ElementComposite` class to make sure it's loaded automatically when the component is used. ::: -This guide demonstrates integration of the [Shoelace QR code web component](https://shoelace.style/components/qr-code) using the `ElementComposite` class. + ## Concern Interfaces To add common behaviors to your custom element, implement the appropriate concern interfaces. For example: -- **HasText**: Adds support for getting and setting text content. -- **HasClassName**: Adds support for manipulating CSS class names. -- **HasStyle**: Adds support for inline styles. +- **`HasText`**: Adds support for getting and setting text content. +- **`HasClassName`**: Adds support for manipulating CSS class names. +- **`HasStyle`**: Adds support for inline styles. + +You can implement multiple interfaces to compose the desired capability: -You can implement multiple interfaces to compose the desired capability. Example: ```java @NodeName("my-component") public final class MyComponent extends ElementComposite @@ -52,11 +65,12 @@ public final class MyComponent extends ElementComposite // ... } ``` + ## `ElementCompositeContainer` for components with slots -For components that need to manage child components in named slots (such as headers, footers, or content areas), extend `ElementCompositeContainer` instead of `ElementComposite`. +For components that need to manage child components in named slots (such as headers, footers, or content areas), extend ElementCompositeContainer instead of `ElementComposite`. -`ElementCompositeContainer` provides a structured way to add, remove, and manage components in specific slots. For example: +`ElementCompositeContainer` provides a structured way to add, remove, and manage components in specific slots: ```java @NodeName("my-container") @@ -89,16 +103,10 @@ public final class MyContainer extends ElementCompositeContainer Use `ElementCompositeContainer` whenever your component needs to support multiple content areas or slots, such as layouts, dialogs, or toolbars. - - ## Property and attribute descriptors -Properties and attributes in web components represent the state and configuration of a component. In webforJ, the `ElementComposite` class provides a structured way to define, set, and get properties and attributes using `PropertyDescriptor`. +Properties and attributes in web components represent the state and configuration of a component. You can define, set, and get properties and attributes using the PropertyDescriptor class. ### Defining properties and attributes @@ -117,7 +125,6 @@ private final PropertyDescriptor ATTRIBUTE_NAME = PropertyDescriptor.attri The generic type parameter `` specifies the Java type of the property or attribute. This guarantees type safety when setting or getting values. -**Example:** ```java private final PropertyDescriptor TITLE = PropertyDescriptor.property("title", ""); private final PropertyDescriptor COUNT = PropertyDescriptor.attribute("count", 0); @@ -125,20 +132,23 @@ private final PropertyDescriptor COUNT = PropertyDescriptor.attribute(" ### Setting and getting values -Use the `set()` method to assign a value, and the `get()` method to retrieve it: +Use the `set()` method to assign a value, and the `get()` method to retrieve it. + ```java set(TITLE, "My Title"); String title = get(TITLE); ``` -You can also use the overloaded `get()` method to specify whether to fetch the value from the client, and to cast to a specific type: +You can also use the overloaded `get()` method to specify whether to fetch the value from the client, and to cast to a specific type. + ```java String title = get(TITLE, false, String.class); ``` ### Best practices for validating properties -To guarantee valid values, add validation logic in your setter methods or before calling `set()`. For example: +To guarantee valid values, add validation logic in your setter methods or before calling `set()`: + ```java public void setCount(int count) { if (count < 0) { @@ -147,11 +157,11 @@ public void setCount(int count) { set(COUNT, count); } ``` -This approach helps prevent invalid state. +This approach helps prevent an invalid state. ### Enum-style properties -For properties that should only accept a fixed set of values, use Java enums. Define the enum, then use it as the type parameter for your `PropertyDescriptor`: +For properties that should only accept a fixed set of values, use Java enums. Define the enum, then use it as the type parameter for your `PropertyDescriptor`. ```java public enum Status { @@ -165,7 +175,6 @@ set(STATUS, Status.INACTIVE); Status status = get(STATUS); ``` -This pattern guarantees valid values are used and improves code readability. { ``` :::info -The `ElementComposite` events are different than `Element` events, in that this doesn't allow any class, but only specified `Event` classes. +The `ElementComposite` events are different from `Element` events, in that this doesn't allow any class, but only specified `Event` classes. ::: -In the demonstration below, a click event has been created and then added to the QR code component. This event, when fired, will display the "X" coordinate of the mouse at the time of clicking the component, which is provided to the Java event as data. A method is then implemented to allow the user to access this data, which is how it's displayed in the app. +In the following demonstration, a click event has been created and then added to the QR code component. This event, when fired, will display the "X" coordinate of the mouse at the time of clicking the component, which is provided to the Java event as data. A method is then implemented to allow the user to access this data, which is how it's displayed in the app. + Date: Thu, 4 Sep 2025 11:11:22 +0200 Subject: [PATCH 5/5] Adjusted according to feedback with the rebased version. --- docs/docs/building-ui/element-composite.md | 75 ++++++++++------------ 1 file changed, 34 insertions(+), 41 deletions(-) diff --git a/docs/docs/building-ui/element-composite.md b/docs/docs/building-ui/element-composite.md index 738830069..486042e25 100644 --- a/docs/docs/building-ui/element-composite.md +++ b/docs/docs/building-ui/element-composite.md @@ -66,44 +66,6 @@ public final class MyComponent extends ElementComposite } ``` -## `ElementCompositeContainer` for components with slots - -For components that need to manage child components in named slots (such as headers, footers, or content areas), extend ElementCompositeContainer instead of `ElementComposite`. - -`ElementCompositeContainer` provides a structured way to add, remove, and manage components in specific slots: - -```java -@NodeName("my-container") -public final class MyContainer extends ElementCompositeContainer - implements HasClassName, HasStyle { - private static final String HEADER_SLOT = "header"; - private static final String CONTENT_SLOT = "content"; - private static final String FOOTER_SLOT = "footer"; - - public MyContainer() { - super(); - } - - public MyContainer addToHeader(Component... components) { - getElement().add(HEADER_SLOT, components); - return this; - } - - public MyContainer addToContent(Component... components) { - add(components); // Default slot - return this; - } - - public MyContainer addToFooter(Component... components) { - getElement().add(FOOTER_SLOT, components); - return this; - } -} -``` - -Use `ElementCompositeContainer` whenever your component needs to support multiple content areas or slots, such as layouts, dialogs, or toolbars. - - ## Property and attribute descriptors {#property-and-attribute-descriptors} Properties and attributes in web components represent the state and configuration of a component. You can define, set, and get properties and attributes using the PropertyDescriptor class. @@ -205,9 +167,13 @@ javaE='https://raw.githubusercontent.com/webforj/webforj-documentation/refs/head height='300px' /> -## Interacting with Slots {#interacting-with-slots} +## `ElementCompositeContainer` for components with slots -Web components often use slots to allow developers to define the structure of a component from the outside. A slot is a placeholder inside a web component that can be filled with content when using the component. In the context of the `ElementComposite` class, slots provide a way to customize the content within a component. The following methods are provided to allow developers to interact with and manipulate slots: +Web components often use slots to allow developers to define the structure of a component from the outside. A slot is a placeholder inside a web component that can be filled with content when using the component. + +For web components that have slots (such as headers, footers, or content areas), extend ElementCompositeContainer instead of `ElementComposite` to manage the child components within the named slots. + +`ElementCompositeContainer` provides a structured way to add, remove, and manage components in specific slots: 1. **`findComponentSlot()`**: This method is used to search for a specific component across all slots in a component system. It returns the name of the slot where the component is located. If the component isn't found in any slot, an empty string is returned. @@ -217,4 +183,31 @@ Web components often use slots to allow developers to define the structure of a It's also possible to use the `add()` method with a `String` parameter to specify the desired slot in which to add the passed component. -These interactions allow developers to harness the power of web components by providing a clean and straightforward API for manipulating slots, properties, and handling events within the `ElementComposite` class. \ No newline at end of file +```java +@NodeName("my-container") +public final class MyContainer extends ElementCompositeContainer + implements HasClassName, HasStyle { + private static final String HEADER_SLOT = "header"; + private static final String CONTENT_SLOT = "content"; + private static final String FOOTER_SLOT = "footer"; + + public MyContainer() { + super(); + } + + public MyContainer addToHeader(Component... components) { + getElement().add(HEADER_SLOT, components); + return this; + } + + public MyContainer addToContent(Component... components) { + add(components); // Default slot + return this; + } + + public MyContainer addToFooter(Component... components) { + getElement().add(FOOTER_SLOT, components); + return this; + } +} +``` \ No newline at end of file