Compound Timeline
A collection of composable and presentation agnostic Compound Components, Hooks and a Context Provider, to help aid in the creation of scheduling based user-interfaces.
A collection of composable and presentation agnostic Compound Components, Hooks and a Context Provider, to help aid in the creation of scheduling based user-interfaces.
Identify commonality across applications utilising scheduling related patterns, with the aim to abstract out a library that helps to ensure the integrity and future maintainability of applications with disparate sets of scheduling related use cases.
We identified commonality across applications in two key areas:
Somewhat but nuanced based on problem domain
DateTime manipulation
Positioning and sizing arbitrary components across a timeline
Generated data structures and state
Common but extensible interfaces
Interact with the Live Example, or view more stories in our Storybook.
1// npm2npm install @royalnavy/react-component-library34// pnpm5pnpm add @royalnavy/react-component-library
In React, composition is a natural pattern of the component model. It's how we build components from other components, of varying complexity and specialisation.
The consumer can pick and choose what functionality to include in their Timeline via the declarative JSX API.
We aim to empower the consumer by enabling them to override the presentation of the exposed compound components:
Full control over look and feel (no opinion about markup or styles)
Consistent underlying implementation across applications
Single set of robust automated tests
Render props allow us to provide custom presentation layers to our compound components by exposing any relevant internal state. See the example usage for the TimelineMonths
component.
1import React from 'react'23import {4 Timeline,5 TimelineMonths,6 TimelineRows7} from '@royalnavy/react-component-library'89const CustomTimelineMonth = ({ index, dayWidth, daysTotal, startDate }) => {10 return (11 <span12 style={{13 display: 'inline-block',14 width: `${dayWidth * daysTotal}px`,15 // ...16 }}17 >18 {startDate.getMonth() + 1}/{startDate.getFullYear()}19 </span>20 )21}2223const ExampleTimeline = () => {24 return (25 <Timeline startDate={new Date(2022, 0, 1)}>26 <TimelineMonths render={CustomTimelineMonth} />27 <TimelineRows>{}</TimelineRows>28 </Timeline>29 )30}
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
We expose the TimelineContext
provider so that a consumer can create their own application specific components. The context provider exposes Timeline state and a dispatch
function for dispatching reducer actions against the store.
In this example we have created a custom component that consumes Timeline-related state and dispatches a reducer action when a button is clicked.
1import React, { useContext } from 'react'23import {4 Timeline,5 TimelineDays,6 TimelineHours,7 TimelineRows,8 TimelineContext,9 TIMELINE_ACTIONS,10} from '@royalnavy/react-component-library'1112const CustomTimelineComponent = () => {13 const {14 state: { months, weeks, days, options, scaleOptions },15 dispatch,16 } = useContext(TimelineContext)1718 return (19 <div>20 <button21 onClick={(_) =>22 dispatch({ type: TIMELINE_ACTIONS.SCALE, getScaleIndex: () => 0 })23 }24 >25 Hourly26 </button>27 </div>28 )29}3031const ExampleTimeline = () => {32 return (33 <Timeline startDate={new Date(2022, 0, 1)}>34 <CustomTimelineComponent />35 <TimelineDays />36 <TimelineHours />37 <TimelineRows>{}</TimelineRows>38 </Timeline>39 )40}
We expose some hooks in order to aid in the creation of your own custom Timeline components.
This hook does not take any parameters and in return exposes moveNext
and movePrevious
which can be used to navigate between frames at the current zoom level.
This hook takes startDate
and endDate
and in return exposes the width
and position (in the form of an offset
) of an item relative to the date range currently displayed by the Timeline
.
This hook does not take any parameters and in return exposes fields for zooming in and out. canZoomIn
and canZoomOut
are booleans that return whether the Timeline
can zoom in or out further. zoomIn
and zoomOut
are functions which decrease or increase the amount of time visible in a frame of the Timeline
.
1import React from 'react'23import {4 useTimelineFrame,5 useTimelinePosition,6 useTimelineZoom,7} from '@royalnavy/react-component-library'89const CustomTimelineFrameComponent = () => {10 const { moveNext, movePrevious } = useTimelineFrame()1112 return (13 <>14 <button onClick={movePrevious}>Previous</button>15 <button onClick={moveNext}>Next</button>16 </>17 )18}1920const CustomTimelinePositionComponent = ({ startDate, endDate }) => {21 const { width, offset, endsBeforeStart, startsAfterEnd } =22 useTimelinePosition(startDate, endDate)2324 if (endsBeforeStart || startsAfterEnd) return null2526 return (27 <div28 style={{29 position: 'absolute',30 display: 'inline-block',31 width,32 left: offset,33 // ...34 }}35 />36 )37}3839const CustomTimelineZoomComponent = () => {40 const { canZoomIn, canZoomOut, zoomIn, zoomOut } = useTimelineZoom()4142 if (!canZoomIn && !canZoomOut) return null4344 return (45 <>46 <button onClick={zoomIn}>Zoom in</button>47 <button onClick={zoomOut}>Zoom out</button>48 </>49 )50}51
Through the use of clever composition and custom styling, it's possible to create layouts that are either nuanced or high in complexity. Here is an example of a custom layout that adds groupings to rows:
Here you will find comprehensive API documentation for the Timeline Hooks.
Provides the ability to navigate Timeline frames.
Object
undefined
Move to the next frame of the Timeline.
undefined
Move to the previous frame of the Timeline.
Provides fields to facilitate positioning items in the Timeline.
undefined
The start date of the event.
undefined
The end date of the event.
Object
undefined
Whether the element end date is after the end date of the current frame.
undefined
Whether the element end date is before the start date of the current frame.
undefined
Width of the element formatted as a pixel string taking into consideration the dates being displayed.
undefined
Distance of the element from the left of the Timeline as a pixel string.
undefined
Whether the element start date is after the end date of the current frame.
undefined
Whether the element start date is before the start date of the current frame.
undefined
Width of the element formatted as a pixel string.
Provides the ability to zoom in and out of the Timeline.
Object
undefined
Whether it is possible to zoom in further.
undefined
Whether it is possible to zoom out further.
undefined
View a lesser amount of time in the current frame.
undefined
View a larger amount of time in the current frame.
Here you will find comprehensive API documentation for the Timeline Components.
undefined
A month will display either side of this start date.
undefined
Bound the timeline by the specified start and end dates.
new Date()
Today's current date - default is the system date time.
1
The number of months to display at any one time.
30
The fixed width value of a single day (in pixels).
false
Specify whether or not to output sidebar headings.
false
Specify whether to hide the scaling buttons.
false
Specify whether to hide the toolbar.
undefined
Custom CSS class to add to the component.
(props: { today, offset }) => ReactNode
today
dateoffset
stringSupply a custom presentation layer.
(props: { index, dayWidth, daysTotal, startDate }) => ReactElement
index
numberdayWidth
numberdaysTotal
numberstartDate
dateSupply a custom presentation layer.
(props: { index, isOddNumber, offsetPx, widthPx, startDate }) => ReactElement
index
numberisOddNumber
booleanoffsetPx
stringwidthPx
stringstartDate
dateSupply a custom presentation layer.
(props: { index, dayWidth, date }) => ReactElement
index
numberdayWidth
numberdate
dateSupply a custom presentation layer.
Hour blocks will not be visible at the default scale level and will be visible when scaling in from the week level.
6
Number of hours per block in a day.
(props: { width, time }) => ReactElement
width
numbertime
stringSupply a custom presentation layer.
undefined
Supply children to be rendered.
(props: { index, isOddNumber, offsetPx, widthPx }) => ReactElement
index
numberisOddNumber
booleanoffsetPx
stringwidthPx
stringSupply a custom presentation layer.
undefined
Supply children to be rendered.
undefined
undefined
A styled-components css`` value to modify the CSS.
undefined
Ability to pass props to the content div of the row.
undefined
Ability to pass props to the header div of the row.
(props: { name }) => ReactElement
name
string?Supply a custom presentation layer for the row header.
undefined
Supply children to be rendered.
undefined
The start date of the event.
undefined
The end date of the event.
undefined
Supply children to be rendered.
(props: { startDate, endDate, widthPx, offsetPx, maxWidthPx, startsBeforeStart, endsAfterEnd }) => ReactNode
startDate
dateendDate
datewidthPx
stringoffsetPx
stringmaxWidthPx
stringstartsBeforeStart
booleanendsAfterEnd
booleanSupply a custom presentation layer.
ColorSuccess500
The colour of the bar.
Iterate upon default presentation (user research and design)
Investigate support for advanced features:
Infinite scroll
Lazy loading
Drag and drop
The contributing guide resource presents information about our development process.
If you have recently updated then read the release notes.
The Royal Navy Design System is licensed under the Apache License 2.0.