npm package: @statistikzh/charts
Description: Each chart function takes an object with the following properties:
Latest version: 0.6.0

# Charts

## Basic Usage

Each chart function takes an object with the following properties:

- `el`: The root element where the chart will be rendered into.
- `data`: A list of data objects that will be rendered. Each object must be a single observation.
- `options`: Options for the chart (depends on the chart type).

TypeScript doesn't work yet. You can look at the [src/types.ts](src/types.ts) folder to see the available options.

```js
import { BarChart } from "@statistikzh/charts";

BarChart({
  el: document.querySelector("#chart"),
  data: [],
  options: {},
});
```

## TypeScript

The library is written in TypeScript. The types are not yet fully implemented. You can look at the [src/types.ts](src/types.ts) file to see the available options.

Sadly there are quite a few of type issues that are not solved yet.

## Used libraries

We use redux as a state management solution. Every chart contains a separate redux store. The store contains the complete state of each chart.

Every element that lives inside the root SVG element is created with d3 and lit-html.
lit-html provides easy solutions to improve the performance.

As a rule of thumb:

- Use d3 for all elements that are bound to data (lines, bars, etc.).
- Use lit-html for container, group, once only elmements etc.

The tooltip and the legend are web components created with lit.

## Architecture

Each chart type is exported as a single function. Each chart can be called with the same object as an argument.:

```js
BarChart({
  el: document.querySelector("#chart"),
  data: [],
  options: {},
});
```

The options can vary from type to type. Look up the `src/types.ts` file for the available options.

### Chart creator function

The [`makeChart`](./src/charts/Chart.ts) function contains shared functionality for all chart types. Each chart type should be created with this function.

```ts
export const BarChart = makeChart(
  optionsSlice, // redux slice that contains the options of this chart type
  constructOptions, // function that merges the default options with the user options
  renderBarChart, // function that renders the components of the chart
);
```

It contains the following parts:

- Creating and subscribing to the redux store
- Building the chart context object
- Creating the root svg element
- Rendering the chart, legend and tooltip with lit-html
- Initializing the service controller, with…
  - LayoutObserver
  - ChartResizeObserver
  - Events
- Preparing and providing cleanup function

### Chart context

The chart context object contains everything that needs to avaiable "globally" in the chart.

At the moment it contains:

- `store`: The redux store. Used to access the state and dispatch actions.
- `services`: Service controller. Used to handle all sorts of effects.

The chart context is created in the `makeChart` function and is passed to the specific chart render function. From there it is passed to every componend or other function that needs to access "global" state.

Example: The functions of `src/lib/scales.ts` determine the scale type, domain etc. based on the context.

### Components

The components are the building blocks of every chart type and live in the `src/components` directory. Every component receives the chart context as an argument and returns either an `HTMLElement` or a `TemplateResult`. The returned value is then rendered inside the root svg element.

Every componenet will be rendererd after each state change. To improve the performance and to prevent unnecessary re-renders a single component can be wrapped with the [`guard`](https://lit.dev/docs/templates/directives/#guard) directive.

### Services

Services are used to handle all sorts of logic / effects that can't be solved by rendering alone. Each service can hook into lifecycle events of the chart. They're described in the `src/services/services.ts` file.

#### LayoutObserver

[`./src/services/layoutObserver.ts`](./src/services/layoutObserver.ts)

The layout observer handles the calculation of the margins thar are needed in order to display all parts inside the viewport of the chart.
This is necessary because we don't know how an axis label is. This depends on the defined format specifier, the tick values the axis renders and the domain.
After every render the observer checks if any of the elements overflows the viewport by retrieving the bounding box (`getBBox()`).
It would be to expensive to that with every single element. If an element has to be observed, the `observeLayout` directive can be applied on it.

#### ChartResizeObserver

[`./src/services/chartResizeObserver.ts`](./src/services/chartResizeObserver.ts)

The chart resize observer simply creates a `ResizeObserver` and observes the root element of the chart.
When the width changes, the dimeinsions slice of the state will be updated.

This is only applied when the `responsive` option flag is set to `true`.

#### Events

[`./src/services/events.ts`](./src/services/events.ts)

The event handling is still a very rough implementation. The event service exposes a `removeListener` and a `addListener` method for consumers outside of the chart to use. Inside the chart the `getListeners` method can be used the retrieve the listeners for a specific event type. It is only used by the tooltip at the moment.