# Pearl-Chain

Komponente des SBB Design Systems Lyne.

- Paket: `@sbb-esta/lyne-elements` (Angular: `@sbb-esta/lyne-angular`)
- Aufbau: Hauptelement mit Bestandteilen; Bestandteile nur innerhalb des jeweiligen Elements verwenden
- Kategorien: Display
- Status: Gestaltungsrichtlinien vollständig

## Links

- Seite auf digital.sbb.ch: https://digital.sbb.ch/de/design-system/lyne/components/pearl-chain/
- Storybook: https://lyne-elements.app.sbb.ch/?path=/docs/elements-pearl-chain--docs
- Angular (`@sbb-esta/lyne-angular`): https://lyne-angular.app.sbb.ch/angular/components/pearl-chain/overview (API: https://lyne-angular.app.sbb.ch/angular/components/pearl-chain/api)
- GitHub: https://github.com/sbb-design-systems/lyne-components

## Elemente

- `sbb-pearl-chain`: It displays a chain of `sbb-pearl-chain-node` elements connected by lines, representing a journey with its stops.
  - `sbb-pearl-chain-node`, nur innerhalb von `sbb-pearl-chain`: Represents a station/point of interest within a `sbb-pearl-chain`.

## Übersicht

### Was macht die Komponente?
Die Pearl-Chain stellt eine Reise als Kette von Punkten dar, die mit Linien verbunden sind. Jeder Punkt steht für einen Halt. Sie zeigt den Fortschritt der Reise mit der aktuellen Position sowie Störungen, Fusswege und unsichere Abschnitte. Sie kann horizontal oder vertikal eingesetzt werden.

### Wann soll die Komponente eingesetzt werden?
* Um den Verlauf einer Verbindung mit ihren Halten darzustellen, z.B. in einer Verbindungsübersicht oder einem Reiseplan.
* Um Reisenden zu zeigen, wo sie sich auf ihrer Reise gerade befinden.

### Regeln.
* Die Halte werden in der Reihenfolge der Reise angeordnet.
* Die Kette selbst ist für Screenreader nicht lesbar. Fortschritt, Störungen und Fusswege müssen deshalb zusätzlich als Text vorhanden sein, z.B. versteckt für Screenreader.

## Implementation (Web Components, Englisch)

The `<sbb-pearl-chain>` component visualizes a journey as a chain of points connected by lines, typically used to
display the stops of a public-transport connection along with its progress.

### Positioning nodes

Place a `<sbb-pearl-chain-node>` for every stop of the journey inside the `<sbb-pearl-chain>`, in the order they
occur. `<sbb-pearl-chain>` connects them with lines automatically, following the DOM order.

Consumers can slot the nodes alongside any other content (times, station names, icons, ...) and lay everything out
however they prefer (e.g. table, flexbox, css-grid). The `<sbb-pearl-chain>` will draw the lines that connect the nodes.

```html

<sbb-pearl-chain>
  <div style="display: flex;">
    <sbb-pearl-chain-node type="start"></sbb-pearl-chain-node>
    <span style="flex: 1;"></span>
    <sbb-pearl-chain-node type="stop"></sbb-pearl-chain-node>
    <span style="flex: 1;"></span>
    <sbb-pearl-chain-node type="end"></sbb-pearl-chain-node>
  </div>
</sbb-pearl-chain>
```

```html

<sbb-pearl-chain>
  <table>
    <tr>
      <td>
        <time datetime="2026-04-10T10:00:00">
          <span class="sbb-screen-reader-only">Departure:&nbsp;</span>10:00
        </time>
      </td>
      <td>
        <sbb-pearl-chain-node type="start" departure="..."></sbb-pearl-chain-node>
      </td>
      <td>Bern</td>
    </tr>
    <tr>
      <td>
        <time datetime="2026-04-10T12:00:00">
          <span class="sbb-screen-reader-only">Departure:&nbsp;</span>12:00
        </time>
      </td>
      <td>
        <sbb-pearl-chain-node type="stop" arrival="..." departure="..."></sbb-pearl-chain-node>
      </td>
      <td>Olten</td>
    </tr>
    <tr>
      <td>
        <time datetime="2026-04-10T15:00:00">
          <span class="sbb-screen-reader-only">Arrival:&nbsp;</span>15:00
        </time>
      </td>
      <td>
        <sbb-pearl-chain-node type="end" arrival="..."></sbb-pearl-chain-node>
      </td>
      <td>Zürich HB</td>
    </tr>
  </table>
</sbb-pearl-chain>
```

### Now / progress

Setting `arrival`/`departure` on the `<sbb-pearl-chain-node>` elements allows the `<sbb-pearl-chain>` to display the current
progress of the journey with a pulsing dot marking the current position.
For testing or demo purposes, the current time can be overridden using the `now` property of `<sbb-pearl-chain>`.

```html
<sbb-pearl-chain>
  <sbb-pearl-chain-node type="start" departure="2026-04-10T10:30:00"></sbb-pearl-chain-node>
  <sbb-pearl-chain-node
    type="stop"
    arrival="2026-04-10T10:50:00"
    departure="2026-04-10T10:55:00"
  ></sbb-pearl-chain-node>
  <sbb-pearl-chain-node type="end" arrival="2026-04-10T11:30:00"></sbb-pearl-chain-node>
</sbb-pearl-chain>
```

### Orientation

`<sbb-pearl-chain>` adapts its bullets depending on whether the nodes are laid out horizontally or vertically, so it looks correct in both horizontal and vertical layouts without any explicit configuration.

### Rendering

`<sbb-pearl-chain>` re-renders automatically when:

- a `<sbb-pearl-chain-node>` is added or removed;
- the pearl-chain element is resized (e.g. on window resize);
- the `IntersectionObserver` triggers, e.g. when the pearl-chain becomes visible.

Changes that are not detected automatically (e.g. a layout change caused by a CSS property) require a manual update.
In this case, call the `requestUpdate()` method on the `<sbb-pearl-chain>` element.

```js
document.querySelector('sbb-pearl-chain').requestUpdate();
```

### Utility

#### Node weights

The `<sbb-pearl-chain>` does not position the nodes: distributing them proportionally to the journey's duration is up to the consumer.
The `calculateNodeWeights()` method helps with that: it returns, for each node in DOM order, a value between `0` (first node) and `1` (last node),
proportional to its `departure` time (or `arrival`, if there is no `departure`).

```js
const chain = document.querySelector('sbb-pearl-chain');
const weights = chain.calculateNodeWeights(); // e.g. [0, 0.43, 1]
```

### Accessibility

`<sbb-pearl-chain>` and `<sbb-pearl-chain-node>` have **no built-in accessibility**: the rendered lines and bullets
are purely decorative and not exposed to assistive technology. If the information conveyed by the pearl chain
(e.g. progress, disruptions) needs to be accessible, it must be provided separately, through accessible markup e.g. via hidden text.

## API (Web Components, Englisch)

#### class: `SbbPearlChainElement`, `sbb-pearl-chain`

##### Properties

| Name    | Attribute | Privacy | Type                                  | Default | Description                                                                                                               |
| ------- | --------- | ------- | ------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------- |
| `nodes` | -         | public  | `readonly SbbPearlChainNodeElement[]` | `[]`    | Returns the registered `sbb-pearl-chain-node` elements, in DOM order.                                                     |
| `now`   | `now`     | public  | `Date`                                |         | A configured date which acts as the current date instead of the real current date. Only recommended for testing purposes. |

##### Methods

| Name                   | Privacy | Description                                                                                                                                                                          | Parameters | Return     | Inherited From |
| ---------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- | ---------- | -------------- |
| `calculateNodeWeights` | public  | Returns the relative position (0 for the first node, 1 for the last one) of each node, in DOM order, proportional to its `departure` time (or `arrival` if there is no `departure`). |            | `number[]` |                |

#### class: `SbbPearlChainNodeElement`, `sbb-pearl-chain-node`

##### Properties

| Name         | Attribute    | Privacy | Type                                                                                                                                                                                                                                                                        | Default | Description                                                                                   |
| ------------ | ------------ | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------- |
| `arrival`    | `arrival`    | public  | `Date \| null`                                                                                                                                                                                                                                                              | `null`  | The arrival date/time at this node. Accepts ISO 8601 datetime strings.                        |
| `departure`  | `departure`  | public  | `Date \| null`                                                                                                                                                                                                                                                              | `null`  | The departure date/time at this node. Accepts ISO 8601 datetime strings.                      |
| `disrupted`  | `disrupted`  | public  | `'arrival' \| 'departure' \| true \| null`                                                                                                                                                                                                                                  | `null`  | Marks the node as disrupted, coloring the bullet red (if rendered).                           |
| `irrelevant` | `irrelevant` | public  | `'arrival' \| 'departure' \| true \| null`                                                                                                                                                                                                                                  | `null`  | Marks the node as irrelevant, coloring the bullet gray (if rendered); supersedes `disrupted`. |
| `type`       | `type`       | public  | `'start' \| 'end' \| 'stop' \| 'skip' \| 'commercial' \| 'boarding' \| 'alighting' \| 'exceptional' \| 'exceptional-skip' \| 'exceptional-boarding' \| 'exceptional-alighting' \| 'stop-duty' \| 'stop-on-demand' \| 'boarding-on-demand' \| 'alighting-on-demand' \| null` | `null`  | Decides how the bullet is rendered. Defaults to \_empty\_ (no bullet).                        |
| `unsure`     | `unsure`     | public  | `'arrival' \| 'departure' \| true \| null`                                                                                                                                                                                                                                  | `null`  | Marks the connection as unsure, rendering the line dashed. Does not affect the bullet.        |
| `walk`       | `walk`       | public  | `'arrival' \| 'departure' \| true \| null`                                                                                                                                                                                                                                  | `null`  | Marks the node as a walk connection. Does not affect the bullet color.                        |
