Chapter 7: Monochrome UI Components#
Welcome back to the streetscape.gl
tutorial! In our journey so far, we've learned how to load autonomous vehicle (AV) data using the XVIZ Loader Interface (Chapter 1), display the 3D scene in the LogViewer (Chapter 2), control playback with PlaybackControl (Chapter 3), show dynamic data panels with XVIZPanel (Chapter 4), understand the connectToLog
utility that ties components to the loader (Chapter 5), and peeked into the XVIZLayer that handles the 3D drawing (Chapter 6).
All these pieces work together to display your log data. But what about the actual buttons, sliders, checkboxes, and input fields you interact with? When you click 'Play' on the PlaybackControl or toggle a setting on an XVIZPanel, what are those UI elements made of?
streetscape.gl
needs a consistent look and feel for these standard user interface gadgets. To achieve this without reinventing common UI patterns, it uses a separate, specialized library: @streetscape.gl/monochrome, often just referred to as "Monochrome".
Think of Monochrome as a toolbox full of standard, well-designed UI controls. These controls are stateless, meaning they don't manage whether they are "on" or "off", or what their current value is internally. Instead, they receive their state (like value={true}
or isPlaying={false}
) as props and simply call a function (onChange
, onClick
, etc.) when a user interacts with them. This makes them flexible and easy to integrate into any React application, including streetscape.gl
.
What are Monochrome UI Components?#
@streetscape.gl/monochrome is a separate library of reusable React components specifically for building user interface controls. While developed alongside streetscape.gl
, it can actually be used independently in other React projects if you need stateless, stylable UI controls.
Its core purpose is to provide building blocks for common UI patterns found in applications like log viewers, such as:
- Buttons (Play/Pause, etc.)
- Sliders (Timeline scrubbers, Look Ahead controls)
- Checkboxes and Radio buttons (Enabling/disabling features or streams)
- Dropdowns (Selecting options)
- Text input fields (Entering values)
- Containers that float or can be dragged/dropped
- Components for displaying metrics, plots, and tables (used by XVIZPanel)
These components are designed with a focus on:
- Statelessness: They reflect the state passed down via props and report user interactions via callbacks, but don't hold onto their own state.
- Stylability: They are built to be easily customized in appearance using CSS overrides and a theming system.
Why Use Monochrome?#
Using a dedicated UI library like Monochrome (or any other UI library) offers several benefits when building applications:
- Consistency: Components share a common design language and behavior, making your UI look and feel unified.
- Efficiency: You don't have to build common controls like buttons or sliders from scratch every time.
- Maintainability: Updates to the library can provide improvements or bug fixes across all uses of the components.
- Styling Control: They provide explicit ways to apply custom styles or switch between themes (like light and dark modes).
For streetscape.gl
, using Monochrome ensures that the UI elements provided by core components like PlaybackControl and XVIZPanel have a consistent, clean look that can be easily themed to match your application's branding.
Common Monochrome Components#
Monochrome provides a variety of components. You can see a full list in its API documentation, but here are a few common examples:
Component | Description | Typical Use in streetscape.gl |
---|---|---|
Button |
A clickable button. | Play/Pause button in PlaybackControl |
Slider |
A control for selecting a value within a range by dragging a knob. | Timeline scrubber, Look Ahead control in PlaybackControl |
CheckBox |
A box that can be checked, unchecked, or indeterminate (3 states). | Toggling individual streams or features visible in LogViewer |
RadioBox |
A group of options where only one can be selected. | Selecting different visualization modes or settings |
Dropdown |
A clickable element that reveals a list of options when open. | Selecting views, coordinate systems, or other options |
TextBox |
An input field for typing text. | Searching, entering numerical values |
Toggle |
A switch to turn a feature on or off (boolean state). | Simpler alternative to CheckBox for boolean states |
FloatPanel |
A container that can be moved and resized like a window. | Used internally by XVIZPanel for panels if configured this way |
MetricCard |
A component to display a single metric with a title and description. | Used by XVIZPanel for METRIC type widgets |
MetricChart |
A component to display a line chart. | Used by XVIZPanel for METRIC or PLOT type widgets |
Table |
A component to display tabular data. | Used by XVIZPanel for TABLE /TREETABLE type widgets |
And many more! Including DragDropList
, Form
, Popover
, Tooltip
, Label
, Spinner
.
Using Monochrome in Your Application#
While core streetscape.gl
components use Monochrome internally, you can also use them directly in your application to build custom UI around the viewer.
Let's say you want to add a simple button to your application that, when clicked, prints a message to the console.
First, you would import the Button
component from @streetscape.gl/monochrome
:
import { Button } from '@streetscape.gl/monochrome';
// ... rest of your React component
Then, you can render the button and provide it with an onClick
handler:
import React from 'react';
import { Button } from '@streetscape.gl/monochrome';
// Assume other streetscape.gl imports are here
// ... Assume logLoader is created and connected ...
function MyAppControls({ logLoader }) { // Example component
const handleButtonClick = () => {
console.log('Button clicked!');
// You could interact with the logLoader here if needed, e.g., logLoader.seek(0)
};
return (
<div>
{/* ... Other controls like PlaybackControl might be here ... */}
<Button onClick={handleButtonClick} type="primary">
Do Something
</Button>
</div>
);
}
In this example:
- We import
Button
. - We define a simple
handleButtonClick
function. - We render
<Button>
, passing our function to theonClick
prop. - We set the button's
type
to"primary"
(Monochrome buttons have different types for styling). - The text "Do Something" becomes the button's label (passed as children).
When the user clicks the button, the handleButtonClick
function is called. Notice how simple it is β the Button
component is stateless; it doesn't need to know anything about where it's used or manage complex internal logic. It just receives props (onClick
, type
, children) and calls the provided callback when the user interacts.
Similarly, you could add a checkbox to toggle a setting:
import React, { useState } from 'react';
import { CheckBox } from '@streetscape.gl/monochrome';
// ... other imports
function MyAppSettings() {
const [showGrids, setShowGrids] = useState(false); // Manage state in parent
const handleCheckBoxChange = (newValue) => {
// newValue will be CheckBox.ON or CheckBox.OFF
console.log('Show grids toggled:', newValue === CheckBox.ON);
setShowGrids(newValue === CheckBox.ON);
// You might pass this state up or down to influence the LogViewer
};
return (
<div>
<CheckBox
label="Show Grid Lines"
value={showGrids ? CheckBox.ON : CheckBox.OFF} // Pass state down
onChange={handleCheckBoxChange} // Receive interaction event
/>
</div>
);
}
useState
to manage the showGrids
state in the parent component. The CheckBox
component simply receives this state via the value
prop and reports changes via the onChange
prop. This is the typical stateless pattern of Monochrome components.
Styling Monochrome Components#
Monochrome components are designed to be highly stylable. There are two primary ways to customize their appearance:
-
style
prop: Most Monochrome components accept astyle
prop. This prop is an object where keys correspond to different internal parts of the component (e.g.,wrapper
,button
,track
,knob
for a slider). The values can be CSS-in-JS objects or functions that receive component state (isHovered
,isActive
,value
,theme
, etc.) to apply dynamic styles.Example for a Slider's knob:
The specific style keys available vary per component. You would need to check the documentation for each component (like the snippets in the providedimport { Slider } from '@streetscape.gl/monochrome'; const mySliderStyle = { knob: props => ({ // This is a function style backgroundColor: props.isActive ? 'yellow' : 'blue', // Change color when active border: '2px solid white', }), track: { // This is a simple object style backgroundColor: 'grey', height: 4, } }; <Slider value={0.5} min={0} max={1} onChange={...} style={mySliderStyle} />
modules/monochrome/*/README.md
files). -
ThemeProvider
: Monochrome supports a theming system. You can wrap your application (or a part of it) in aThemeProvider
component and provide a theme object. This theme object defines common colors, fonts, spacing, etc., that all Monochrome components under the provider will use by default.Example using
ThemeProvider
:This is howimport { ThemeProvider } from '@streetscape.gl/monochrome'; const DARK_THEME = { extends: 'dark', // Start with the built-in dark theme background: '#222', textColorPrimary: '#EEE', controlColorPrimary: '#444', controlColorHovered: '#666', // ... define other theme properties }; function App() { return ( <ThemeProvider theme={DARK_THEME}> {/* All Monochrome components here will use DARK_THEME */} <CompleteLogDisplay /> {/* Contains LogViewer, PlaybackControl, XVIZPanel */} </ThemeProvider> ); }
streetscape.gl
applications typically apply a consistent visual style across all UI elements. The theme object structure is defined in the Monochrome styling documentation (seedocs/api-reference/styling-guide.md
/docs/get-started/styling.md
).
Both the style
prop and ThemeProvider
mechanisms leverage the emotion
library under the hood for styling in JavaScript.
How streetscape.gl
Uses Monochrome Internally#
Core streetscape.gl
components that provide UI controls are built using Monochrome components.
-
The PlaybackControl component (Chapter 3) internally uses:
- Monochrome
Slider
for the timeline scrubber and potentially the look ahead control. - Monochrome
Button
for the Play/Pause button. - Monochrome components for displaying time labels.
- It wires up the user interaction events from these Monochrome components (e.g.,
Slider
'sonChange
,Button
'sonClick
) to call methods on theXVIZ Loader
instance (log.seek()
,log.play()
,log.pause()
).
- Monochrome
-
The XVIZPanel component (Chapter 4) acts as a container that renders different Monochrome components based on the XVIZ Declarative UI definition:
- If the log defines a
METRIC
widget,XVIZPanel
renders a MonochromeMetricCard
containing aMetricChart
orRichMetricChart
. - If the log defines a
TABLE
orTREETABLE
widget,XVIZPanel
renders a MonochromeTable
orTreeTable
. - It might also use Monochrome
Toggle
orCheckBox
if the Declarative UI includes controls defined this way. - XVIZPanel and its child components receive log data via mechanisms like
connectToLog
(Chapter 5) and pass that data down as props to the stateless Monochrome components (e.g., passing chart data toMetricChart
'sdata
prop).
- If the log defines a
This relationship looks something like this:
sequenceDiagram
participant LogViewer as LogViewer
participant PlaybackControl as PlaybackControl
participant XVIZPanel as XVIZPanel
participant Loader as XVIZ Loader
participant MonoButton as Monochrome Button
participant MonoSlider as Monochrome Slider
participant MonoMetricChart as Monochrome MetricChart
App->>Loader: Create loader instance
App->>LogViewer: Render <LogViewer log={loader}>
App->>PlaybackControl: Render <PlaybackControl log={loader}>
App->>XVIZPanel: Render <XVIZPanel log={loader} name="...">
PlaybackControl->>MonoButton: Render <Button onClick={...}>
PlaybackControl->>MonoSlider: Render <Slider onChange={...}>
XVIZPanel->>MonoMetricChart: Render <MetricChart data={...}>
Note over XVIZPanel,MonoMetricChart: (Based on XVIZ UI config)
User->>MonoButton: Click
MonoButton->>PlaybackControl: onClick callback
PlaybackControl->>Loader: log.play() / log.pause()
Loader->>LogViewer: Notify update
Loader->>PlaybackControl: Notify update
LogViewer->>LogViewer: Re-render 3D
PlaybackControl->>PlaybackControl: Update UI (e.g. Slider position, button icon)
This diagram simplifies interactions, particularly the role of connectToLog
in receiving updates.
The key takeaway is that streetscape.gl
components handle the logic of connecting to the log and fetching the right data/state, and then they use the stateless Monochrome components as their visual, interactive presentation layer.
Conclusion#
Monochrome UI Components, provided by the @streetscape.gl/monochrome
library, are the building blocks for the user interface controls in streetscape.gl
. They offer a collection of common, stateless, and highly stylable React components like buttons, sliders, checkboxes, and components for displaying data in charts and tables.
While not directly interacting with the XVIZ Loader themselves (they get their data and state from their parent components), they are used extensively by core streetscape.gl
components like PlaybackControl and XVIZPanel to render interactive UI elements. Understanding that these components come from a separate, reusable UI library helps clarify the architecture of a streetscape.gl
application and shows how you can customize the look and feel using Monochrome's styling and theming capabilities.
With this chapter, we've covered the major concepts and components introduced in this streetscape.gl
tutorial, from loading the data to visualizing it in 3D, controlling playback, displaying auxiliary data, connecting components to the data source, and finally, understanding the UI building blocks themselves.
You now have a foundational understanding of how to build applications that load and visualize autonomous vehicle log data using streetscape.gl
.
Generated by AI Codebase Knowledge Builder. References: 1(https://github.com/aurora-opensource/streetscape.gl/blob/befae1354ca8605c9f6cb1229b494858a8690e4f/docs/api-reference/styling-guide.md), 2(https://github.com/aurora-opensource/streetscape.gl/blob/befae1354ca8605c9f6cb1229b494858a8690e4f/docs/get-started/styling.md), 3(https://github.com/aurora-opensource/streetscape.gl/blob/befae1354ca8605c9f6cb1229b494858a8690e4f/modules/monochrome/README.md), 4(https://github.com/aurora-opensource/streetscape.gl/blob/befae1354ca8605c9f6cb1229b494858a8690e4f/modules/monochrome/src/drag-drop-list/README.md), 5(https://github.com/aurora-opensource/streetscape.gl/blob/befae1354ca8605c9f6cb1229b494858a8690e4f/modules/monochrome/src/float-panel/README.md), 6(https://github.com/aurora-opensource/streetscape.gl/blob/befae1354ca8605c9f6cb1229b494858a8690e4f/modules/monochrome/src/form/README.md), 7(https://github.com/aurora-opensource/streetscape.gl/blob/befae1354ca8605c9f6cb1229b494858a8690e4f/modules/monochrome/src/index.js), 8(https://github.com/aurora-opensource/streetscape.gl/blob/befae1354ca8605c9f6cb1229b494858a8690e4f/modules/monochrome/src/metric-card/README.md), 9(https://github.com/aurora-opensource/streetscape.gl/blob/befae1354ca8605c9f6cb1229b494858a8690e4f/modules/monochrome/src/playback-control/README.md), 10(https://github.com/aurora-opensource/streetscape.gl/blob/befae1354ca8605c9f6cb1229b494858a8690e4f/modules/monochrome/src/shared/button/README.md), 11(https://github.com/aurora-opensource/streetscape.gl/blob/befae1354ca8605c9f6cb1229b494858a8690e4f/modules/monochrome/src/shared/checkbox/README.md), 12(https://github.com/aurora-opensource/streetscape.gl/blob/befae1354ca8605c9f6cb1229b494858a8690e4f/modules/monochrome/src/shared/dropdown/README.md), 13(https://github.com/aurora-opensource/streetscape.gl/blob/befae1354ca8605c9f6cb1229b494858a8690e4f/modules/monochrome/src/shared/label/README.md), 14(https://github.com/aurora-opensource/streetscape.gl/blob/befae1354ca8605c9f6cb1229b494858a8690e4f/modules/monochrome/src/shared/radio-box/README.md), 15(https://github.com/aurora-opensource/streetscape.gl/blob/befae1354ca8605c9f6cb1229b494858a8690e4f/modules/monochrome/src/shared/slider/README.md), 16(https://github.com/aurora-opensource/streetscape.gl/blob/befae1354ca8605c9f6cb1229b494858a8690e4f/modules/monochrome/src/shared/text-box/README.md), 17(https://github.com/aurora-opensource/streetscape.gl/blob/befae1354ca8605c9f6cb1229b494858a8690e4f/modules/monochrome/src/shared/toggle/README.md), 18(https://github.com/aurora-opensource/streetscape.gl/blob/befae1354ca8605c9f6cb1229b494858a8690e4f/modules/monochrome/src/table/README.md)