Custom Layout Example

Overview

HNOD supports defining custom UI layouts in a service package. The feature enables the integrator to position individual UI components in one or multiple dispays. In addition, some layout- and component specific properties are exposed, and can be used to further modify the user interface.

The layout WebRunnableBundle

Like all service packages, the layout package needs at least one entry point that exposes the service package's contents to the HNOD runtime environment. In this example, only a single layout component is exposed. The layout feature is enabled through the runnable bundle's components function. The layout AppComponent must have the viewId property set to ViewId.Layout and name property set to a layout specific identifier. The identifier enables HNOD application to support multiple physical displays. Each physical display has an instance of the HNOD client running in a webview, and the layout identifier is passed to each client via displayId URL parameter. The client picks up the correct layout according to the passed id value. The service package can export multiple layouts.

import { webBundleWith, AppComponent, ViewId } from "@here/hnod-sdk";
import { CustomLayout } from "./CustomLayout";

const layoutComponents: AppComponent[] = [
{
name: "layout.display.main", // should match with the displayId passed with URL parameters
component: CustomLayout,
viewType: { priority: 0, viewId: ViewId.Layout }
}
];

export default webBundleWith({
components(): AppComponent[] {
return layoutComponents;
}
});


In this example, the exposed CustomLayout is the actual layout component that defines the high level layout of the application. The layout component doesn't render anything by itself, but provides "renderers" for different UI components, that are loaded at runtime (and plugged into their respective renderers). These renderers can be positioned anywhere on the screen using the standard functionality provided by React. The SDK provides renderers for all standard HNOD components like the dock, map and cardstack, but it is also possible to include 3rd party components in the layout.

Here is a simple example of a layout that renders the dock component using the DockRenderer component. The dock is positioned to the top left corner of the screen and is given the width of dockItemSize from the currently active theme. The order of the buttons inside the dock component can be changed via the standard flexDirection property. A LayoutContext can be used to propagate additional information to the underlying components. Some renderers also provide component specific properties for the same purpose.

const DockContainer = styled(View)(({ theme }) => ({
top: 0,
left: 0,
width: theme.sizeOf.dockItemSize
}));

const Dock = styled(DockRenderer)({
flexDirection: "column-reverse"
});

export const CustomLayout: React.FC<AppProps> = (appProps: AppProps) => {
const layoutConfig: LayoutPreferences = {};
return (
<LayoutContextProvider value={layoutConfig}>
<DockContainer ignorePointerEvents>
<Dock {...appProps} />
</DockContainer>
</LayoutContextProvider>
);
};


This rather simple layout results in the dock component being displayed as shown in the image. The custom-layout-example defines a more comprehensive layout containing all the standard HNOD renderers, displaying a full navigation layout.

Layout context

The Layout component can provide a layout context to propagate layout specific preferences to underlying components. These preferences are typically not specific to a single component, but are more global in nature. An example of such a preference is keyboardSupport that the service packages can use to hide or display virtual keyboard related content.

Layout VisibleMapArea

Visible map area defines the area of the map view component that can display map markers, e.g. search results. It can be used for example to prevent map markers from being hidden behind any UI elements that are rendered on top of the map (e.g. cardstack). The map will automatically zoom to a level, where all markers are visible within the defined visible map area. The current car position will be horizontally centered to the visible map area.

Layout VisibleMapArea could be either a static object with a VisibleMapArea type or a function that returns a VisibleMapArea object.

Dynamic visibleMapArea

A function can be used if the map visible area needs to be dynamically updated depending on the cardstack visibility, an object will be provided as a param to the visibleMapArea function that contains the relative information.

This is useful in use cases where the CCP (Current Car Position) arrow must be in the middle of the screen when no cardstack is visible.

Layouting 3rd party components

HNOD SDK provides renderers for all standard HNOD components, but it is also possible to include 3rd party components inside the layout component. To accomplish this, a generic ViewRenderer component can be used. A unique viewId needs to be passed to the component. This view id must match with a view id defined in the service package that exports the 3rd party component (via the components runnable bundle interface).

<ViewRenderer {...appProps} viewId="oem.widget.custom" />


Build the Example

To build the service package, use:

yarn build


This processes assets, compiles the TypeScript code and outputs a single ZIP archive that is ready for execution in the HNOD runner and eventual upload to the HNOD portal.

Run the Example

Once the ZIP is built, use the runner to try it out locally:

yarn start


However, this is not yet enough for the custom layout to appear. The display id also needs to be provided as a URL parameter. For this example, you can add &displayId=layout.display.main to the end of the URL in the browser address bar. When the web page is now reloaded, the application is displaying the custom layout, as shown in this image.