npm package: @skrepr/zicht-js.toggle
Description: Provides awesome toggle sjizzle
Author: Boudewijn Schoon
Latest version: 6.0.6

# `@skrepr/zicht-js.toggle`

Provides extensive html element toggling support

## Installation

Update your project's `package.json`, this will download the `@skrepr/zicht-js.toggle` plugin
to your `node_modules` directory.

```
npm install @skrepr/zicht-js.toggle
```

Initialize the toggle code by adding the following to your project's `main.ts`.

```typescript
import {ToggleContainer} from '@skrepr/zicht-js.toggle/src/toggle-container';
import {TemplateEventInterface, TemplateEventType, TemplateView} from '@skrepr/zicht-js.util/src/view/template-view';

// Find and load all .js-toggle--container elements in `window.document.body`
ToggleContainer.findAndCreate();

// When a view updates its template, we may find elements in there that require javascript
TemplateView.templateEvent.attach((event:TemplateEventInterface) => {
    if (event.type === TemplateEventType.After) {
        ToggleContainer.findAndCreate(event.element, {includeContainer: false});
    }
});

```

## Scripts

- Unit test: `npm run test`
- Lint test: `npm run tslint`
- Publish a release to [npmjs.com](http://www.npmjs.com): `npm publish`

### Polyfill's

```javascript
    new PolyfillInjectorPlugin({
        polyfills: [
            'Element.prototype.closest',
            'Element.prototype.dataset',
            'Element.prototype.matches',
            'Map'
        ]
    })
```

### Animations sass

```sass
//
// Animations
//
@import "../../node_modules/@skrepr/zicht-js.toggle/sass/helpers/mixins";
@import "../../node_modules/@skrepr/zicht-js.toggle/sass/animations/base";
@import "../../node_modules/@skrepr/zicht-js.toggle/sass/animations/slide-enter/slideInDown";
@import "../../node_modules/@skrepr/zicht-js.toggle/sass/animations/slide-enter/slideOutUp";
```

## Quick blueprints

### Case: simple button and content

The case where you want your toggle button to change between the texts "Show" and "Hide", when a content element
is visible or hidden:

```twig
<div class="js-toggle--container">
    <div class="js-toggle--trigger">
         <script type="text/template">
            <% if (isVisible) { %>
                <button>Hide</button> <img src="arrow-down.png">
            <% } else { %>
                <button>Show</button> <img src="arrow-up.png">
            <% } %>
         </script>
    </div>
    <div class="js-toggle--content  u-hidden">
        This block will be initially hidden, and will toggle between hidden and visible when the trigger is clicked.

        <div class="js-toggle--trigger">
            <button>Hide</button> <img src="arrow-up.png">
        </div>
    </div>
</div>
```

### Case: a hidden form

The case where you want to encapsulate a `form` element, and automatically make the form visible, and scroll to it,
when it contains an error.

```twig
<div class="js-toggle--container">
    <div class="js-toggle--trigger">Show and hide</div>
    <div class="js-toggle--content  u-hidden"
        data-toggle-visible-on-match=".has-errors"
        data-toggle-scroll='{}'>

        This block will be initially hidden, but when toggled open, or when an element with `.has-errors` is found,
        the block will be visible and centered on the screen.
        <form>
            ...
        </form>
    </div>
</div>
```

### Case: animation (open by default)

The case where the toggled content is opened on initial pageload and uses an animation to slide up (closed) and down (opened) when the user clicks the toggle trigger (`.js-toggle--trigger`). See description below which classes need to be added to `.js-toggle--content`.

```twig
<div class="js-toggle--container">
    <div class="js-toggle--trigger">Show and hide</div>
    <div class="js-toggle--content" data-toggle-animated="true">
        This content will be initially visible (no specific classes need to be specified), when toggled (.js-toggle--trigger), the block will slide up (closed). When toggled again it will slide down (opened).
    </div>
</div>
```

### Case: animation (closed by default)

The case where the toggled content is closed on initial pageload and uses an animation to slide down (opened) and up (closed) when the user clicks the toggle trigger (`.js-toggle--trigger`). See description below which classes need to be added to `js-toggle--content`.

```twig
<div class="js-toggle--container">
    <div class="js-toggle--trigger">Show and hide</div>
    <div class="js-toggle--content  u-hidden  slideOutUp" data-toggle-animated="true">
        This content will be initially hidden (because of `u-hidden  slideOutUp`), when toggled (.js-toggle--trigger), the block will slide down (opened). When toggled again it will slide up (closed).
    </div>
</div>
```

## API usage

The `@skrepr/zicht-js.toggle` library provides a simple way to show or hide elements in the browser.  It ranges from
very simple, even trivial examples, to more complex nested toggled that automatically determine their state based on
criteria, persisted state, or external queues through Backbone.

The simplest example would be a `js-toggle--container` with a `js-toggle--trigger` and a `js-toggle--content`.  When the
trigger element is clicked, the content toggles the `u-hidden` class on the content element.

```html
<div class="js-toggle--container">
    <div class="js-toggle--trigger">Click HERE to toggle</div>
    <div class="js-toggle--content  u-hidden">This content is initially hidden and visible when the trigger is clicked</div>
</div>
```

The following elements are available:

- `js-toggle--container`: the wrapper element, this must encapsulate all other related elements
- `js-toggle--info`: a helper element, can be used to show or hide elements
- `js-toggle--content`: similar to the info element, except that this element also determines the visibility state
- `js-toggle--trigger`: an element that listens to a click event to toggle the visibility state
- `js-toggle--radio`: an element that mirrors the visibility state with the selected state of a radio element
- `js-toggle--select-option`: an element that mirrors the visibility state with the selected state of a select-option element

### API `js-toggle--container`

Every container maintains its own visibility state, which is a boolean stored in the `pageState` Backbone model.  All
toggle elements that are encapsulated within the container will follow this visibility state, unless they are explicitly
given a `data-toggle-group` attribute.

The following data properties are available:

- `data-toggle-state-name`: Optionally specify state name, defaults to a unique value
- `data-toggle-group`: Optionally specify a toggle group name, when specified, only encapsulated
  `js-toggle--content`, `js-toggle--trigger`, and `js-toggle--info` elements with this group name are used
- `data-toggle-only-one`: Optionally specify a current-container state name, when specified, out of
  all the containers with this name, only one will be visible at a time
- `data-toggle-persist`: Optionally specify how the visibility state should be persisted.  Values can be
  either "local-storage" or "session-storage".  Once the state is known, it will be persisted,
  and used on the next page load

For example:

```twig
<div class="js-toggle--container"
     data-toggle-state-name="navigation-state"
     data-toggle-only-one="only-one-navigation-state"
     data-toggle-group="navigation"
     data-toggle-persist="local-storage">

     ...

</div>
```

### API `js-toggle--info`

An info element can be encapsulated within a `js-toggle--container`.  An info element will toggle the classes
`u-visible` and `u-hidden` based on the visibility state, but an info element does not influence the visibility state
(for that you will need to use `js-toggle--content`).

The following data properties are available:

- `data-toggle-group`: Optionally specify a toggle group name, this should only be used when the parent
  `js-toggle--container` has a group name specified
- `data-toggle-is-inverse`: Optionally specify if this content element must behave as the inverse of the toggle
  visibility state.  Values can be either "true" or "false", and is considered "false" when not specified
- `data-toggle-hidden-class`: Optionally specify a different class to toggle on when the visibility state is false.
  This class also influences the initial visibility state.  The class name "u-hidden" is used when left unspecified
- `data-toggle-visible-class`: Optionally specify a different class to toggle on when the visibility state is true.
  This class also influences the initial visibility state The class name "u-visible" is used as default
- `data-toggle-animated`: Optionally specify this attribute with value "yes" in order to animate the content. Checkout the quick blueprints when the initial state needs to be closed or opened

The `js-toggle--info` element can optionally encapsulate a `HTMLScriptElement`.  In this case an underscore template will
be created and called whenever the visibility changes.  This can be used to, i.e. change a text based on the visibility
state.

```twig
<div class="js-toggle--container" data-toggle-group="navigation">
    <div class="js-toggle--info"
         data-toggle-group="navigation"
         data-toggle-is-inverse=""
         data-toggle-hidden-class="u-hidden"
         data-toggle-visible-class="u-visible">
         <script type="text/template">
            <% if (isVisible) { %> ... <% } else { %> ... <% } %>
         </script>
    </div>
</div>
```

The `js-toggle--info` is also possible on the `js-toggle--container` div.  In this case it behaves exactly the same.

```twig
<div class="js-toggle--container  js-toggle--info">
    ...
</div>
```

### API `js-toggle--content`

One or more content elements are usually encapsulated within a `js-toggle--container`.  A content element is very
similar to an info element, with the exception that they determine the visibility state (except when a the state is
persisted).  This is done by looking at the classes `u-visible` and `u-hidden`, and whether there are specific matches
on the optional `data-toggle-visible-on-match` and `data-toggle-hidden-on-match` css selectors.

The following data properties are available:

- `data-toggle-group`: Inherited from `js-toggle--info`
- `data-toggle-is-inverse`: Inherited from `js-toggle--info`
- `data-toggle-hidden-class`: Inherited from `js-toggle--info`
- `data-toggle-visible-class`: Inherited from `js-toggle--info`
- `data-toggle-scroll`: Optionally specify scroll rules. When this content element becomes visible, the scroll rules are
  automatically applied.  Scrolling uses the library [smooth-scroll-into-view-if-needed](https://github.com/stipsan/smooth-scroll-into-view-if-needed)
  and the json encoded data applied in `data-toggle-scroll` is given as options.
- `data-toggle-visible-on-match`: Optionally specify a css class selector that will set the initial visibility state to
  true when it is found within the content element
- `data-toggle-hidden-on-match`: Optionally specify a css class selector that will set the initial visibility state to
  false when it is found within the content element
- `data-toggle-animated`: Optionally specify this attribute with value "yes" in order to animate the content. Checkout the quick blueprints when the initial state needs to be closed or opened

The `js-toggle--content` element also inherits its ability to optionally encapsulate an `HTMLScriptElement`, as is shown
in the example below.

```twig
<div class="js-toggle--container" data-toggle-group="navigation">
    <div class="js-toggle--content"
         data-toggle-group="navigation"
         data-toggle-is-inverse=""
         data-toggle-hidden-class="u-hidden"
         data-toggle-visible-class="u-visible"
         data-toggle-scroll='{"behavior": "smooth", "duration": 300, "ease": "easeOutQuint"}'
         data-toggle-visible-on-match=".has-errors"
         data-toggle-hidden-on-match=".nothing-to-see-here">
         <script type="text/template">
            <% if (isVisible) { %> ... <% } else { %> ... <% } %>
         </script>
    </div>
</div>
```

### API `js-toggle--trigger`

One or more trigger elements are usually encapsulated within a `js-toggle--container`.  A trigger element listens to
click events and will toggle the visibility state, resulting in the various info and content elements to apply their
changes.

The following data properties are available:

- `data-toggle-group`: Optionally specify a toggle group name, this should only be used when the parent
- `data-toggle-prevent-default`: Optionally specify whether `event.preventDefault()` should be called when the click
  event is received.  Values can be either "true" or "false", and is considered "true" when not specified

The `js-toggle--trigger` element can optionally encapsulate a `HTMLScriptElement`.  In this case an underscore template
will be created and called whenever the visibility changes.  This can be used to, i.e. change a text based on the
visibility state.

```twig
<div class="js-toggle--container" data-toggle-group="navigation">
    <div class="js-toggle--trigger"
         data-toggle-group="navigation"
         data-toggle-prevent-default="true"
         <script type="text/template">
            <% if (isVisible) { %> ... <% } else { %> ... <% } %>
         </script>
    </div>
</div>
```

### API `js-toggle--radio`

A radio element can be encapsulated within a `js-toggle--container` resulting in the visibility state following the
selected state of this radio element.

The following data properties are available:

- `data-toggle-group`: Optionally specify a toggle group name, this should only be used when the parent
- `data-toggle-prevent-default`: Optionally specify whether `event.preventDefault()` should be called when the click
  event is received.  Values can be either "true" or "false", and is considered "true" when not specified

```twig
<div class="js-toggle--container" data-toggle-group="navigation">
    <input type="radio" class="js-toggle--radio"
         data-toggle-group="navigation"
         data-toggle-prevent-default="true">

        ...

    </input>
</div>
```

### API `js-toggle--checkbox`

A checkbox element can be used in the same way as the radio-element.

### API `js-toggle--select-option`

A select element can be encapsulated with it's option elements within a `js-toggle--container` resulting in the visibility state following the
selected state of the option element (state is being detected on an option's parent element which is the select element).

The following data properties are available:

- `data-toggle-group`: Optionally specify a toggle group name, this should only be used when the parent
- `data-toggle-prevent-default`: Optionally specify whether `event.preventDefault()` should be called when the click
  event is received.  Values can be either "true" or "false", and is considered "true" when not specified

```twig
<div class="js-toggle--container" data-toggle-group="navigation">
    <select>
        <option class="js-toggle--select-option"
             data-toggle-group="navigation"
             data-toggle-prevent-default="true">

            ...

        </option>
    </select>
</div>
```

## Maintainer

- Boudewijn Schoon <boudewijn@skrepr.zicht-nl>