<commit_before># Usage

The router provides a global navigation system for client-side applications. Rather than just matching URLs with the corresponding components, it depends on a tree-like structure of views, which have their own routing configuration in the component definition.

The router state is represented by the stack of views. If a user go deeper in the tree, the view is pushed to the stack. If the navigation goes up, the stack is cleared. However, the result stack array returns only the current view and dialogs (on the top of the current view). The rest of the views on the stack are kept in the memory with the current state, scroll positions and the last focused element.

The stack of views is connected with the browser history, so the current stack and browser history is always in-sync. Because of the relations between the views, the router knows when to navigate forward or backward to reflect the structure.

To start using the router, just add a property defined with the router factory to your main component of the application, and display the current stack of views:

```javascript
import { define, html, router } from "hybrids";

import Home from "./views/Home.js";

export define({
  tag: "my-app",
  views: router(Home),
  content: ({ views }) => html`
    <my-app-layout>
      ${views}
    </my-app-layout>
  `,
});
```

## Factory

```typescript
router(views: component | component[] | () => ..., options?: object): object
```

* **arguments**:
  * `views` - a defined component or an array of defined components. You can wrap `views` in a function to avoid using imports from uninitialized ES modules
  * `options` - an object with following options:
    * `url` - a string base URL used for views without own `url` option, defaults to current URL
    * `params` - an array of property names of the element, which are passed to every view as a parameter
* **returns**:
  * a hybrid property descriptor, which resolves to an array of elements

### `views`

Views passed to the router factory create the roots of the view structures. Usually, there is only one root view, but you can pass a list of them (navigating between roots always replaces the whole stack). You can find a deeper explanation of the view concept in the [View](/router/view.md) section.

### `url`

If your application uses a mixed approach - views with and without URLs, you should specify a base URL to avoid not deterministic behavior of the router for views without the URL. Otherwise, the router will use an entry point as a base URL (which can be different according to the use case).

```javascript
define({
  tag: "my-app",  
  views: router(Home, { url: "/" }),
  ...
})
```

### `params`

Regardless of the explicit parameters when navigating to the view, you can specify an array of properties of the component, which are passed to every view as a parameter. They bypass the URL generation, so they are set by the reference, and they are not included in the URL.

```javascript
import { define, store, router } from "hybrids";

const Home = define({
  tag: "my-app-home",
  user: store(User),
  ...
});

define({
  tag: "my-app",
  user: store(User),
  views: router(Home, { params: ["settings"] }),
  ...
});
```

```html
<my-app user="123"></my-app>
```

### Nested Routers

For complex layouts, the router factory can be used inside of the views already connected to the parent router. This feature differs from setting views in the `stack` option of the view. The nested router displays content inside of the host view as content, not a separate view in the stack.

```javascript
import { define, html, router } from "hybrids";

import TabOneView from "./TabOneView.js";
import TabTwoView from "./TabTwoView.js";

export default define({
  tag: "my-module-view",
  tabs: router([TabOneView, TabTwoView]),
  content: ({ tabs }) => html`
    <my-tabs-group>
      <a href="${router.url(TabOneView)}">Tab One</a>
      <a href="${router.url(TabTwoView)}">Tab Two</a>
    </my-tabs-group>

    ${tabs}
  `,
});
```

### Events

The router dispatches the `navigate` event on the host element, which can be used for the external purposes:

```javascript
function onNavigate(event) {
  ga("send", "pageview", event.detail.url);
}

const app = document.getElementById("my-app");
app.addEventListener("navigate", onNavigate);
```

## Navigating

The router provides a set of methods to navigate to the views. Generally, those methods generate an URL instance, which can be used in anchors as `href` attribute and forms as an `action` attribute.

The component with the router listens to `click` and `submit` events, so when a user clicks on an anchor or submits the form, the router resolves the URL and navigates to the target view (going forward or backward in the history using the structure of views).

> The `submit` event does not cross the Shadow DOM boundary, so the recommended way is to use the `content` property in component definitions for the templates.

### `scrollToTop`

Besides specific parameters for the view, the router navigation methods support the `scrollToTop` option in the params, which clears the main scroll position of the view when it is navigated. As the views on the stack hold scroll positions, it might be useful, if you want to force the view to scroll to the top, but it is a parent view, which might be already in the memory. However, if the new view is pushed to the stack, the main vertical scroll position is cleared automatically, so you don't need to use this option then.

### `router.url()`

```typescript
router.url(view: component, params?: object): URL | ""
```

* **arguments**:
  * `view` - a component definition
  * `params` - an object with parameters to pass to the view
* **returns**:
  * an URL instance or an empty string

Use the definition reference to identify the target view, and eventually pass the parameters. As the result is an URL instance, the parameters are serialized to the query string - the complex object structures are not supported (use the [store](/store/usage.md) and pass the model identifier).

Not connected view results in an empty string. It allows running unit tests over the component content property, without a need to run the whole application. It means, that the method does not throw, so if you forget to add the view to the stack option, it will silently return an empty string.

### `router.backUrl()`

```typescript
router.backUrl(options?: { nested?: boolean, scrollToTop?: boolean }): URL | ""
```

* **arguments**:
  * `options` - an object with `nested` or `scrollToTop` options, both defaults to `false`
* **returns**:
  * an URL instance or an empty string

The `backUrl` method is useful if you have views, which can be navigated from multiple points (the target view is listed in more than one `stack` option in the view definitions), or you create a shared layout element for the views. Then, you can use this method to generate an URL to the previous view from the stack.

If the current view is the only one on the stack, the first parent of the target view is used (going from left to right in the view hierarchy). The root views return an empty string.

The `nested` option only applies if the previous and current views use nested routing. By default, the method will skip entries in the stack, where the main router view is the same, so it always returns the URL for another view. If the `nested` option is set to `true`, it generates the URL for the views from the deepest nested router.

```javascript
import { define, html, router } from "hybrids";

define({
  tag: "my-ui-view-layout",
  title: "",
  render: ({ title }) => {
    const backUrl = router.backUrl();

    return html`
      <header>
        ${backUrl && html`<a href="${backUrl}">Go back</a>`}
        <h1>${title}</h1>
      </header>
      <slot></slot>
    `;
  },
})<commit_msg>docs: fix the default parent description<commit_after># Usage

The router provides a global navigation system for client-side applications. Rather than just matching URLs with the corresponding components, it depends on a tree-like structure of views, which have their own routing configuration in the component definition.

The router state is represented by the stack of views. If a user go deeper in the tree, the view is pushed to the stack. If the navigation goes up, the stack is cleared. However, the result stack array returns only the current view and dialogs (on the top of the current view). The rest of the views on the stack are kept in the memory with the current state, scroll positions and the last focused element.

The stack of views is connected with the browser history, so the current stack and browser history is always in-sync. Because of the relations between the views, the router knows when to navigate forward or backward to reflect the structure.

To start using the router, just add a property defined with the router factory to your main component of the application, and display the current stack of views:

```javascript
import { define, html, router } from "hybrids";

import Home from "./views/Home.js";

export define({
  tag: "my-app",
  views: router(Home),
  content: ({ views }) => html`
    <my-app-layout>
      ${views}
    </my-app-layout>
  `,
});
```

## Factory

```typescript
router(views: component | component[] | () => ..., options?: object): object
```

* **arguments**:
  * `views` - a defined component or an array of defined components. You can wrap `views` in a function to avoid using imports from uninitialized ES modules
  * `options` - an object with following options:
    * `url` - a string base URL used for views without own `url` option, defaults to current URL
    * `params` - an array of property names of the element, which are passed to every view as a parameter
* **returns**:
  * a hybrid property descriptor, which resolves to an array of elements

### `views`

Views passed to the router factory create the roots of the view structures. Usually, there is only one root view, but you can pass a list of them (navigating between roots always replaces the whole stack). You can find a deeper explanation of the view concept in the [View](/router/view.md) section.

### `url`

If your application uses a mixed approach - views with and without URLs, you should specify a base URL to avoid not deterministic behavior of the router for views without the URL. Otherwise, the router will use an entry point as a base URL (which can be different according to the use case).

```javascript
define({
  tag: "my-app",  
  views: router(Home, { url: "/" }),
  ...
})
```

### `params`

Regardless of the explicit parameters when navigating to the view, you can specify an array of properties of the component, which are passed to every view as a parameter. They bypass the URL generation, so they are set by the reference, and they are not included in the URL.

```javascript
import { define, store, router } from "hybrids";

const Home = define({
  tag: "my-app-home",
  user: store(User),
  ...
});

define({
  tag: "my-app",
  user: store(User),
  views: router(Home, { params: ["settings"] }),
  ...
});
```

```html
<my-app user="123"></my-app>
```

### Nested Routers

For complex layouts, the router factory can be used inside of the views already connected to the parent router. This feature differs from setting views in the `stack` option of the view. The nested router displays content inside of the host view as content, not a separate view in the stack.

```javascript
import { define, html, router } from "hybrids";

import TabOneView from "./TabOneView.js";
import TabTwoView from "./TabTwoView.js";

export default define({
  tag: "my-module-view",
  tabs: router([TabOneView, TabTwoView]),
  content: ({ tabs }) => html`
    <my-tabs-group>
      <a href="${router.url(TabOneView)}">Tab One</a>
      <a href="${router.url(TabTwoView)}">Tab Two</a>
    </my-tabs-group>

    ${tabs}
  `,
});
```

### Events

The router dispatches the `navigate` event on the host element, which can be used for the external purposes:

```javascript
function onNavigate(event) {
  ga("send", "pageview", event.detail.url);
}

const app = document.getElementById("my-app");
app.addEventListener("navigate", onNavigate);
```

## Navigating

The router provides a set of methods to navigate to the views. Generally, those methods generate an URL instance, which can be used in anchors as `href` attribute and forms as an `action` attribute.

The component with the router listens to `click` and `submit` events, so when a user clicks on an anchor or submits the form, the router resolves the URL and navigates to the target view (going forward or backward in the history using the structure of views).

> The `submit` event does not cross the Shadow DOM boundary, so the recommended way is to use the `content` property in component definitions for the templates.

### `scrollToTop`

Besides specific parameters for the view, the router navigation methods support the `scrollToTop` option in the params, which clears the main scroll position of the view when it is navigated. As the views on the stack hold scroll positions, it might be useful, if you want to force the view to scroll to the top, but it is a parent view, which might be already in the memory. However, if the new view is pushed to the stack, the main vertical scroll position is cleared automatically, so you don't need to use this option then.

### `router.url()`

```typescript
router.url(view: component, params?: object): URL | ""
```

* **arguments**:
  * `view` - a component definition
  * `params` - an object with parameters to pass to the view
* **returns**:
  * an URL instance or an empty string

Use the definition reference to identify the target view, and eventually pass the parameters. As the result is an URL instance, the parameters are serialized to the query string - the complex object structures are not supported (use the [store](/store/usage.md) and pass the model identifier).

Not connected view results in an empty string. It allows running unit tests over the component content property, without a need to run the whole application. It means, that the method does not throw, so if you forget to add the view to the stack option, it will silently return an empty string.

### `router.backUrl()`

```typescript
router.backUrl(options?: { nested?: boolean, scrollToTop?: boolean }): URL | ""
```

* **arguments**:
  * `options` - an object with `nested` or `scrollToTop` options, both defaults to `false`
* **returns**:
  * an URL instance or an empty string

The `backUrl` method is useful if you have views, which can be navigated from multiple points (the target view is listed in more than one `stack` option in the view definitions), or you create a shared layout element for the views. Then, you can use this method to generate an URL to the previous view from the stack.

If the current view is the only one on the stack, the last parent of the target view is used (going from left to right in the view hierarchy). The root views always return an empty string.

The `nested` option only applies if the previous and current view uses nested routing. By default, the method will skip entries in the stack, where the main router view is the same, so it always returns the URL for another view. If the `nested` option is set to `true`, it generates the URL for another view from the deepest nested router.

```javascript
import { define, html, router } from "hybrids";

define({
  tag: "my-ui-view-layout",
  title: "",
  render: ({ title }) => {
    const backUrl = router.backUrl();

    return html`
      <header>
        ${backUrl && html`<a href="${backUrl}">Go back</a>`}
        <h1>${title}</h1>
      </header>
      <slot></slot>
    `;
  },
})
{'fraction_non_alphanumeric': 0.09106463878326997, 'fraction_numerical': 0.0006337135614702154, 'mean_word_length': 4.033811802232855, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3907475', 'n_tokens_mistral': 4136, 'n_tokens_neox': 4027, 'n_words': 2282}