# Spotlight.js

Spotlight.js is a Javascript library that let developers highlight content on websites; it allows to create a tour, containing
several elements the user can browse through.

With Spotlight.js, you could:
* showcase your web app features;
* guide users wanting to accomplish a given task;
* attract users' attention on a given element.
* *do much more stuff!*

## Architecture

The library works the same way as a theatre set!

To perform a theatre play, you need actors, right? Well, same idea here! Each component you want to highlight can be 
declared as follows: 
```javascript
// actors need an asynchronous function that will return targeted HTMLElements.
const titleActor = new Actor(async () => document.getElementById('header-title'));
const menuActor = new Actor(async () => document.getElementById('right-menu'));
```

Of course, you need a spotlight to showcase them!
```javascript
const spotlight = new Spotlight();
```

Then, add your actors as targets for your spotlight:
```javascript
spotlight.addActors( titleActor, menuActor );
```

## Options

You can set options:
* for your `Spotlight` instance;
* for your `Actors`, if you want to set settings for specific actors; these will override options set up in your `Spotlight` instance.

```javascript
const spotlight = new Spotlight({ options });       // see available options below
spotlight.addActors(
    new Actor(async () => document.getElementById('paragraph-1'), { options }),
    new Actor(async () => document.getElementById('paragraph-2'))
); 
```

### `Spotlight` options

| name | description | default |
|---|---|---|
| `allowHiddenFocus` | allows Spotlight.js to focus elements even when the spot is not activated  | `false` |
| `autoScroll` | when an actor is not in the viewport (and spot is on), scrolls the document until the actor is visible | `true` |
| `centerActorOnFocus` | when an actor is focused, scrolls the view to center it, even if it is completely in the viewport | `true` | 
| `loops` | at the end of the tour, focuses on the first actor | `false` |
| `onBackgroundClick` | if `preventContentClicking` is true, set a callback to call when the user clicks the page during the tour | `(spotlight) => spotlight.toggleSpot()` |
| `onEnd` | callback triggered when calling `next()` while last actor is focused | `() => console.log("tour ended")` |
| `onLightsOff` | triggered when the spot goes off | `() => {}` | 
| `preventContentClicking` | prevents users from clicking page content when a presentation is in progress | `true` | 
| `preventFocusSpam` | blocks focus transition when another is running | `true` |
| `resetOnLightsOff` | focuses the first actor when turning the spot off | `false` |
| `resetOnEnd` | spot fades off when `next()` is called while last actor is focused | `true` |

### `Spotlight` + `Actor` options

| name | description | default |
|---|---|---|
| `delay` | transition time, in milliseconds, for the spot to switch from an actor to another | `1000` |
| `matchRadius` | reproduces the border-radius of the targeted actor | `true` |
| `onFocus` | callback triggered when the spot starts targeting a new actor | `(newActor: Actor, oldActor: Actor) => { console.log('switching focus from ${oldActor} to ${newActor}'); }` |
| `onFocused` | callback triggered when the spot targets a new actor | `(actor: Actor) => { console.log('now focusing ${actor}'); }` |
| `shadowColor` | color of the shadow (you might want to put some alpha value here) | `#ff0000dd` | 
| `showSign` | should display the sign | `true` |
| `showControls` | should display controls on the sign | `true` |
| `signHTML` | overrides sign body with custom HTML code | `''` |
| `signPosition` | defines position of the sign regarded the focused actor (can be `TOP`, `RIGHT`, `BOTTOM`, `LEFT`) | `SignPosition.RIGHT` |
| `signText` | text of the sign | `"Hello there!"` |
| `signTitle` | title of the sign | `"Title"` |
| `validScrollContainer` | check the "Limit parent scrolling" section below | `() => true` |

## Tips

### Actors with no target

If you want to center the sign on the screen with no particular target (*eg* for beginning a tour), provide `() => null` as builder to the Actor instance :

```javascript
new Actor (
    async () => null, 
    {
        signTitle: 'Welcome to Spotlight.js', 
        signText: 'Since no actor has been provided, the sign centers itself on the page by default.'
    }
)
```

### Limit parent scrolling

Under the hood, spotlight.js uses the [scrollIntoView](https://www.npmjs.com/package/scroll-into-view) library to 
scroll to an element that is offscreen.
When focusing an actor, it will attempt to scroll all elements containing the targeted element, which can lead to 
undesired behaviors, such as scrolling your app's main wrapper:

| ![Parent overscrolling example](doc/scrollingParentExample.gif) | 
|:--:| 
| *app wrapper is scrolled, revealing a white blank on the page bottom* |

To prevent this, you can control which elements scrollIntoView can scroll by providing the `validScrollContainer` option
as such:
```javascript
this.spotlight = new Spotlight ({
    // not scrolling the window nor #app container
    validScrollContainer: (target, parentsScrolled) => {
        return target !== window && !target.matches('#app');
    }
});
```
| ![No parent overscrolling with the validScrollContainer function set](doc/scrollingParentPreventExample.gif) | 
|:--:| 
| *app wrapper is not a valid scrolling target anymore!* |

Check [scrollIntoView's github](https://github.com/KoryNunn/scroll-into-view#how) for more details.