diff --git a/articles/flow/advanced/server-client-communication.adoc b/articles/flow/advanced/server-client-communication.adoc index ead7bbd87f..24315b92f7 100644 --- a/articles/flow/advanced/server-client-communication.adoc +++ b/articles/flow/advanced/server-client-communication.adoc @@ -74,3 +74,28 @@ Re-synchronization is initiated by the client when it can't find a message with - It clears all old pending messages, updates the last known `syncId` to the newly received value, and rebuilds the DOM tree from scratch based on the server's current state. This process ensures UI consistency, even in the presence of errors or unexpected behavior. The application remains robust against transient network problems or client-side interruptions. Additionally, the user experience is preserved without requiring a full page reload, as Vaadin rebuilds the UI without reloading the entire bundle from the server. + + +[role="since:com.vaadin:vaadin@V25.2"] +== Deferring Server-Side Callbacks + +The [methodname]`UI.triggerAfter(Duration, SerializableRunnable)` method runs a task on the server after a given delay, without enabling <>. + +The delay is measured by the browser: a one-shot timer is armed on the client when the response to the current request is sent, and when the timer elapses, the client makes a round trip back to the server that runs the callback. The task runs in the same way as any other event handler, with the session locked. This makes it possible to defer a single piece of server-side work without configuring push for that purpose alone. + +[source,java] +---- +Span status = new Span("Saved"); +add(status); + +// Hide the status message after five seconds +Registration registration = UI.getCurrent() + .triggerAfter(Duration.ofSeconds(5), + () -> status.setVisible(false)); +---- + +The returned [classname]`Registration` cancels the task by clearing the client-side timer, so a task that's cancelled before its delay elapses doesn't run. A cancellation that races an already-elapsed timer, whose round trip is in flight, may still run once. + +Because the timer lives in the browser, the task isn't run if the page is reloaded, navigated away from, or closed before the delay elapses. + +[methodname]`triggerAfter()` must be called while holding the session lock, that is, from a regular event handler or from inside [methodname]`UI.access()`. The same applies to calling [methodname]`Registration.remove()` on the returned registration. diff --git a/articles/flow/component-internals/container.adoc b/articles/flow/component-internals/container.adoc index 9afe342a53..48b016592e 100644 --- a/articles/flow/component-internals/container.adoc +++ b/articles/flow/component-internals/container.adoc @@ -74,6 +74,27 @@ The component hierarchy is directly based on the element hierarchy in the DOM tr If you need access to the components themselves and not only their elements, use the [methodname]`Component.getParent()` and [methodname]`Component.getChildren()` methods. +[role="since:com.vaadin:vaadin@V25.2"] +=== Including Virtual Children + +The [methodname]`Component.getChildren()` method doesn't include virtually attached children, such as overlays attached to the [classname]`UI` or slotted components added through helper methods on web component wrappers. To traverse the component tree including virtual children, use the static helper methods in the [classname]`ComponentUtil` class: + +[source,java] +---- +// Direct children, including virtual children +Stream children = ComponentUtil.getAllChildren(layout); + +// All descendants in pre-order, including virtual children +Stream descendants = ComponentUtil.streamDescendants(layout); +---- + +The [methodname]`ComponentUtil.getAllChildren(Component)` method returns the child components of the given parent, like [methodname]`Component.getChildren()`, but also descends into virtually attached elements. Regular DOM children are returned first, in DOM order, followed by virtual children in their attachment order. + +The [methodname]`ComponentUtil.streamDescendants(Component)` method streams all descendant components in pre-order, that is, each component before its own descendants, using [methodname]`getAllChildren()` at every level. The parent itself isn't included. + +Neither method traverses shadow root contents, so children injected into a template via `@Id` are excluded. + + == Removing Child Components diff --git a/articles/flow/component-internals/dnd/drop-target.adoc b/articles/flow/component-internals/dnd/drop-target.adoc index 8a279b4788..059d66d9bb 100644 --- a/articles/flow/component-internals/dnd/drop-target.adoc +++ b/articles/flow/component-internals/dnd/drop-target.adoc @@ -99,6 +99,38 @@ dropTarget.addDropListener(event -> { See the <> reference guide for information about setting server-side drag data. At the moment, there is no way to retrieve any client-side drag data from the drop event. +[role="since:com.vaadin:vaadin@V25.2"] +== Drop Position + +The [classname]`DropEvent` carries the coordinates of the drop, so you can use the drop position, for example, to place an item where it was dropped. + +The [methodname]`getOffsetX()` and [methodname]`getOffsetY()` methods return the coordinates of the drop position relative to the drop target element. This is useful for positioning dropped items within the drop target container using absolute or relative positioning. + +The [methodname]`getDragStartOffsetX()` and [methodname]`getDragStartOffsetY()` methods return the coordinates of the mouse pointer relative to the drag source element when the drag started. If the drag source isn't in the same UI, these methods return zero. Subtract these values from the drop position to make the dropped item appear where it was grabbed, instead of where the pointer is. + +.Placing the dragged component at the drop position +[example] +==== +[source,java] +---- +dropTarget.addDropListener(event -> { + event.getDragSourceComponent().ifPresent(dragged -> { + // Maintain the relative grab position, so that the component + // appears where it was grabbed, not where the pointer is + int x = event.getOffsetX() - event.getDragStartOffsetX(); + int y = event.getOffsetY() - event.getDragStartOffsetY(); + + dragged.getElement().getStyle() + .setPosition(Style.Position.ABSOLUTE) + .setLeft(x + "px") + .setTop(y + "px"); + box.add(dragged); + }); +}); +---- +==== + + == Controlling the Drop with Drop Effect The [classname]`DropEffect` property determines which operation (copy, move, link) the drop target wants to perform. For a drop to succeed, this must be compatible with the drag source's `effectAllowed` property. diff --git a/articles/flow/component-internals/mixins.adoc b/articles/flow/component-internals/mixins.adoc index 76ca30f5d4..17d428583d 100644 --- a/articles/flow/component-internals/mixins.adoc +++ b/articles/flow/component-internals/mixins.adoc @@ -30,6 +30,28 @@ If your component implements the [interfacename]`HasComponents` interface, you c https://vaadin.com/api/platform/{moduleMavenVersion:com.vaadin:vaadin}/com/vaadin/flow/component/HasComponents.html#method-summary[Full method summary of the [interfacename]`HasComponents` interface]. +[role="since:com.vaadin:vaadin@V25.2"] +== `HasComponentsOfType` Interface + +The [interfacename]`HasComponentsOfType` interface is the typed counterpart of [interfacename]`HasComponents`. Implement it when your component should only accept child components of a specific type, so that the API statically prevents callers from adding unrelated components. For example, a breadcrumb trail component could implement `HasComponentsOfType` to only accept breadcrumb items as children. + +[interfacename]`HasComponents` is equivalent to `HasComponentsOfType` and extends it; use [interfacename]`HasComponents` for containers that accept any component. Like [interfacename]`HasComponents`, this interface extends the [interfacename]`HasElement` and [interfacename]`HasEnabled` mixins, and its default implementations assume that children are attached to the component's root element. + +[source,java] +---- +@Tag("nav") +public class Breadcrumb extends Component + implements HasComponentsOfType { +} + +Breadcrumb breadcrumb = new Breadcrumb(); +breadcrumb.add(new BreadcrumbItem("Home")); +breadcrumb.add(new Span("Not allowed")); // Doesn't compile +---- + +https://vaadin.com/api/platform/{moduleMavenVersion:com.vaadin:vaadin}/com/vaadin/flow/component/HasComponentsOfType.html#method-summary[Full method summary of the [interfacename]`HasComponentsOfType` interface]. + + == `HasStyle` Interface The [interfacename]`HasStyle` interface adds a CSS class attribute and supports inline styles. [classname]`Component` implements it by default. It extends the [interfacename]`HasElement` mixin. @@ -47,7 +69,7 @@ The following list has mixins depending directly on a root element -- all of whi - [interfacename]`HasEnabled` is for components and other UI objects that can be enabled or disabled. - [interfacename]`HasHelper` is for field components that have helper text as property and slots for inserting components. - [interfacename]`HasLabel` is for components that support label definition. -- [interfacename]`HasOrderedComponents` supports ordered child components, with an index for the layout. +- [interfacename]`HasOrderedComponents` supports ordered child components, with an index for the layout. This interface is deprecated for removal: its methods ([methodname]`replace()`, [methodname]`indexOf()`, [methodname]`getComponentCount()`, and [methodname]`getComponentAt()`) are now available directly on [interfacename]`HasComponents`. - [interfacename]`HasText` is for components that support text content. - [interfacename]`HasTheme` is for components that have a theme DOM attribute. - [interfacename]`HasValueAndElement` is the same as [interfacename]`HasValue`. It also extends [interfacename]`HasElement` and [interfacename]`HasEnabled`.