Master-Detail Layout
- Usage
- Styling
Master-Detail Layout is component for building UIs with a horizontally or vertically split pair of a master (or primary) area and a detail (or secondary) area that can responsively switch to an overlay.
Note
|
Preview Feature
This is a preview version of Master-Detail Layout. You need to enable it with the feature flag |
Important
|
Scaled down examples
The examples on this page are scaled down so that their viewport-size-dependent behavior can be demonstrated.
Some examples also change their behavior based on your browser viewport size.
|
In the example below, clicking a row in the table reveals the detail area which is rendered next to the master area by default. Drag the splitter to change the width of the layout: the detail area will switch to an overlay when the master area’s minimum width is reached.
new tab
link:include::../../../frontend/demo/component/master-detail-layout/react/master-detail-layout-basic.tsx[]
MasterDetailLayout layout = new MasterDetailLayout();
layout.setMasterMinSize("600px");
layout.setDetailSize("300px");
PersonList personList = new PersonList(DataService.getPeople());
layout.setMaster(personList);
PersonDetail personDetail = new PersonDetail();
personList.getGrid().asSingleSelect().addValueChangeListener(event -> {
Person selectedPerson = event.getValue();
if (selectedPerson != null) {
personDetail.setPerson(selectedPerson);
layout.setDetail(personDetail);
} else {
layout.setDetail(null);
}
});
personDetail.addCloseListener(event -> personList.getGrid().deselectAll());
Orientation
By default, the Master-Detail Layout is split horizontally. This can be changed to a vertical split.
new tab
import React, { useEffect } from 'react';
import { useSignal } from '@vaadin/hilla-react-signals';
import { MasterDetailLayout, SplitLayout } from '@vaadin/react-components';
import PersonDetail from 'Frontend/demo/component/master-detail-layout/react/PersonDetail';
import PersonList from 'Frontend/demo/component/master-detail-layout/react/PersonList';
import { getPeople } from 'Frontend/demo/domain/DataService';
import type Person from 'Frontend/generated/com/vaadin/demo/domain/Person';
function Example() {
const items = useSignal<Person[]>([]);
const selectedPerson = useSignal<Person | null>(null);
useEffect(() => {
getPeople().then(({ people }) => {
items.value = people;
});
}, []);
return (
<SplitLayout orientation="vertical" style={{ height: '100%' }}>
<MasterDetailLayout
orientation="vertical"
masterMinSize="150px"
detailSize="250px"
>
<MasterDetailLayout.Master>
<PersonList
people={items.value}
selectedPerson={selectedPerson.value}
onSelect={(person) => {
selectedPerson.value = person;
}}
/>
</MasterDetailLayout.Master>
<MasterDetailLayout.Detail>
{selectedPerson.value ? (
<PersonDetail
person={selectedPerson.value}
onClose={() => {
selectedPerson.value = null;
}}
/>
) : null}
</MasterDetailLayout.Detail>
</MasterDetailLayout>
<div style={{ flex: '0 0 auto', backgroundColor: 'var(--lumo-contrast-5pct)', textAlign: 'center' }}>
<span style={{fontWeight: 'bold', minHeight: '1.75em'}}>Drag to resize</span>
</div>
</SplitLayout>
);
}
MasterDetailLayout layout = new MasterDetailLayout();
layout.setOrientation(MasterDetailLayout.Orientation.VERTICAL);
Responsive Details Area
The detail area can be rendered in three different ways:
-
Split: Side-by-side with the master area (horizontally or vertically, depending on the orientation);
-
Drawer: As an overlay partially covering the master area;
-
Stack: As a full-size overlay fully covering the master area.
Overlay Based on Available Space
For each area, either a fixed size or a minimum size can be specified. The area with a minimum size takes whatever space is available next to the fixed size area. If the available space is smaller than the minimum size, the detail area is rendered as an overlay.
The two most common configurations are:
-
A flexible master area with a minimum size, next to a fixed-size detail area;
-
A fixed-size master area, next to a flexible detail area with a minimum size.
If both areas are configured to be flexible, whichever reaches its minimum size first determines the overlay breakpoint.
If not explicitly set, the layout tries to determine an appropriate minimum size automatically based on the content.
layout.setMasterMinSize("600px");
layout.setDetailSize("300px");
Stack Mode
The layout can be configured to make the detail area in the overlay mode render as a stack (i.e. fully cover the master area):
layout.setOverlayMode(MasterDetailLayout.OverlayMode.STACK);
Router Integration
Master-Detail Layout can be used as a router layout (see Flow/Hilla), so that nested views are automatically rendered in the details area of the component. This allows showing nested views without having to manage the contents of the details area manually when the route changes, while providing the same benefits such as responsive behavior that the component normally provides.
The Flow MasterDetailLayout
component implements the RouterLayout
interface. When using a view class that extends from MasterDetailLayout
as a layout for a nested view, that view is then automatically shown in the details area of the component.
The example below shows how to set up a master and a detail view. The master view is ProductListView
, which would show a list of products, and the detail view is ProductDetailView
, which shows information about a specific product. The ProductListView
extends from MasterDetailLayout
, so that it can be used as a route layout by the detail view. It also configures a @Route
so that it can be navigated to by itself. Assuming there is a main layout for the application, for example one using AppLayout
, it configures that as a parent layout. The ProductDetailView
configures a @Route
, using the ProductListView
as the route layout.
With this setup, when navigating to /products
, the layout would only show the product list. When navigating to a product detail, for example /products/1
, it would then also show the product details next to, or on top of, the product list.
@ParentLayout(MainLayout.class)
@Route(value = "products", layout = MainLayout.class)
public class ProductListView extends MasterDetailLayout { ... }
@Route(value = "products/:productId", layout = ProductListView.class)
public class ProductDetailView extends VerticalLayout { ... }