lisn.js
Version:
Simply handle user gestures and actions. Includes widgets.
465 lines • 18.3 kB
TypeScript
/**
* @module Widgets
*/
import { GestureDevice, CommaSeparatedStr } from "../globals/types.cjs";
import { Widget, WidgetHandler } from "../widgets/widget.cjs";
/**
* Configures the given element as a {@link Pager} widget.
*
* The Pager widget sets up the elements that make up its pages to be overlayed
* on top of each other with only one of them visible at a time. When a user
* performs a scroll-like gesture (see {@link GestureWatcher}), the pages are
* flicked through: gestures, whose direction is down (or left) result in the
* next page being shown, otherwise the previous.
*
* The widget should have more than one page.
*
* Optionally, the widget can have a set of "switch" elements and a set of
* "toggle" elements which correspond one-to-one to each page. Switches go to
* the given page and toggles toggle the enabled/disabled state of the page.
*
* **IMPORTANT:** Unless the {@link PagerConfig.style} is set to "carousel", the
* page elements will be positioned absolutely, and therefore the pager likely
* needs to have an explicit height. If you enable
* {@link PagerConfig.fullscreen}, then the element will get `height: 100vh`
* set. Otherwise, you need to set its height in your CSS.
*
* **IMPORTANT:** You should not instantiate more than one {@link Pager}
* widget on a given element. Use {@link Pager.get} to get an existing
* instance if any. If there is already a widget instance, it will be destroyed!
*
* -----
*
* You can use the following dynamic attributes or CSS properties in your
* stylesheet:
*
* The following dynamic attributes are set on the pager element:
* - `data-lisn-orientation`: `"horizontal"` or `"vertical"`
* - `data-lisn-use-parallax`: `"true"` or `"false"`
* - `data-lisn-total-pages`: the number of pages
* - `data-lisn-visible-pages`: **for carousel only** the number of visible pages;
* can be fractional if {@link PagerConfig.peek} is enabled
* - `data-lisn-current-page`: the current page number
* - `data-lisn-current-page-is-last`: `"true"` or `"false"`
* - `data-lisn-current-page-is-first-enabled`: `"true"` or `"false"`
* - `data-lisn-current-page-is-last-enabled`: `"true"` or `"false"`
*
* The following dynamic CSS properties are also set on the pager element's style:
* - `--lisn-js--total-pages`: the number of pages
* - `--lisn-js--visible-pages`: **for carousel only** the number of visible pages
* - `--lisn-js--current-page`: the current page number
*
* The following dynamic attributes are set on each page, toggle or switch element:
* - `data-lisn-page-state`: `"current"`, `"next"`, `"covered"` or `"disabled"`
* - `data-lisn-page-number`: the corresponding page's number
*
* The following analogous dynamic CSS properties are also set on each page,
* toggle or switch element's style:
* - `--lisn-js--page-number`: the corresponding page's number
*
* -----
*
* To use with auto-widgets (HTML API) (see
* {@link Settings.settings.autoWidgets | settings.autoWidgets}), the following
* CSS classes or data attributes are recognized:
* - `lisn-pager` class or `data-lisn-pager` attribute set on the container
* element that constitutes the pager.
* - `lisn-pager-page` class or `data-lisn-pager-page` attribute set on
* elements that should act as the pages.
* - `lisn-pager-toggle` class or `data-lisn-pager-toggle` attribute set on
* elements that should act as the toggles.
* - `lisn-pager-switch` class or `data-lisn-pager-switch` attribute set on
* elements that should act as the switches.
*
* When using auto-widgets, the elements that will be used as pages are
* discovered in the following way:
* 1. The top-level element that constitutes the widget is searched for any
* elements that contain the `lisn-pager-page` class or
* `data-lisn-pager-page` attribute. They do not have to be immediate
* children of the root element, but they **should** all be siblings.
* 2. If there are no such elements, all of the immediate children of the
* widget element except any `script` or `style` elements are taken as the
* pages.
*
* The toggles and switches are descendants of the top-level element that
* constitutes the widget, that contain the
* `lisn-pager-toggle`/`lisn-pager-switch` class or `data-lisn-pager-toggle`/
* `data-lisn-pager-switch` attribute. They do not have to be immediate
* children of the root element.
*
* See below examples for what values you can use set for the data attributes
* in order to modify the configuration of the automatically created widget.
*
* @example
* This defines a simple pager with default settings.
*
* ```html
* <div class="lisn-pager">
* <div>Vertical: Page 1</div>
* <div>Vertical: Page 2</div>
* <div>Vertical: Page 3</div>
* <div>Vertical: Page 4</div>
* </div>
* ```
*
* @example
* As above but with all settings explicitly set to their default (`false`).
*
* ```html
* <div data-lisn-pager="fullscreen=false | parallax=false | horizontal=false">
* <div>Vertical: Page 1</div>
* <div>Vertical: Page 2</div>
* <div>Vertical: Page 3</div>
* <div>Vertical: Page 4</div>
* </div>
* ```
*
* @example
* This defines a pager with custom settings.
*
* ```html
* <div data-lisn-pager="fullscreen | parallax | horizontal | gestures=false">
* <div>Horizontal: Page 1</div>
* <div>Horizontal: Page 2</div>
* <div>Horizontal: Page 3</div>
* <div>Horizontal: Page 4</div>
* </div>
* ```
*
* @example
* This defines a pager with custom settings, as well as toggle and switch buttons.
*
* ```html
* <div data-lisn-pager="fullscreen | parallax | horizontal | gestures=false">
* <div>
* <div data-lisn-pager-page>Horizontal: Page 1</div>
* <div data-lisn-pager-page>Horizontal: Page 2</div>
* <div data-lisn-pager-page>Horizontal: Page 3</div>
* <div data-lisn-pager-page>Horizontal: Page 4</div>
* </div>
*
* <div>
* <button data-lisn-pager-toggle>Toggle page 1</button>
* <button data-lisn-pager-toggle>Toggle page 2</button>
* <button data-lisn-pager-toggle>Toggle page 3</button>
* <button data-lisn-pager-toggle>Toggle page 4</button>
* </div>
*
* <div>
* <button data-lisn-pager-switch>Go to page 1</button>
* <button data-lisn-pager-switch>Go to page 2</button>
* <button data-lisn-pager-switch>Go to page 3</button>
* <button data-lisn-pager-switch>Go to page 4</button>
* </div>
* </div>
* ```
*/
export declare class Pager extends Widget {
/**
* Advances the widget's page by 1. If the current page is the last one,
* nothing is done, unless {@link PagerConfig.fullscreen} is true in which
* case the pager's scrollable ancestor will be scrolled down/right
* (depending on the gesture direction).
*/
readonly nextPage: () => Promise<void>;
/**
* Reverses the widget's page by 1. If the current page is the first one,
* nothing is done, unless {@link PagerConfig.fullscreen} is true in which
* case the pager's scrollable ancestor will be scrolled up/left
* (depending on the gesture direction).
*/
readonly prevPage: () => Promise<void>;
/**
* Advances the widget to the given page. Note that page numbers start at 1.
*
* If this page is disabled, nothing is done.
*/
readonly goToPage: (pageNum: number) => Promise<void>;
/**
* Disables the given page number. Note that page numbers start at 1.
*
* The page will be skipped during transitioning to previous/next.
*
* If the page is the current one, then the current page will be switched to
* the previous one (that's not disabled), or if this is the first enabled
* page, then it will switch to the next one that's not disabled.
*
* If this is the last enabled page, nothing is done.
*/
readonly disablePage: (pageNum: number) => Promise<void>;
/**
* Re-enables the given page number. Note that page numbers start at 1.
*/
readonly enablePage: (pageNum: number) => Promise<void>;
/**
* Re-enables the given page if it is disabled, otherwise disables it. Note
* that page numbers start at 1.
*/
readonly togglePage: (pageNum: number) => Promise<void>;
/**
* Returns true if the given page number is disabled. Note that page
* numbers start at 1.
*/
readonly isPageDisabled: (pageNum: number) => boolean;
/**
* Returns the current page.
*/
readonly getCurrentPage: () => Element;
/**
* Returns the previous page, before the last transition.
*
* If there has been no change of page, returns the first page, same as
* {@link getCurrentPageNum}.
*/
readonly getPreviousPage: () => Element;
/**
* Returns the number of the current page. Note that numbers start at 1.
*/
readonly getCurrentPageNum: () => number;
/**
* Returns the number of the previous page, before the last transition. Note
* that numbers start at 1.
*
* If there has been no change of page, returns the number of the first page,
* same as {@link getCurrentPageNum}.
*/
readonly getPreviousPageNum: () => number;
/**
* The given handler will be called whenever there is a change of page. It
* will be called after the current page number is updated internally (so
* {@link getPreviousPageNum} and {@link getCurrentPageNum} will return the
* updated numbers), but before the page transition styles are updated.
*
* If the handler returns a promise, it will be awaited upon.
*/
readonly onTransition: (handler: WidgetHandler) => void;
/**
* Returns the page elements.
*/
readonly getPages: () => Element[];
/**
* Returns the toggle elements if any.
*/
readonly getToggles: () => Element[];
/**
* Returns the switch elements if any.
*/
readonly getSwitches: () => Element[];
static get(element: Element): Pager | null;
static register(): void;
/**
* @throws {@link Errors.LisnUsageError | LisnUsageError}
* If there are less than 2 pages given or found, or if any
* page is not a descendant of the main pager element.
*/
constructor(element: Element, config?: PagerConfig);
}
/**
* @interface
*/
export type PagerConfig = {
/**
* The elements that will make up the pages.
*
* They do not have to be immediate children of the root element, but they
* should all be siblings.
*
* The widget should have more than one page.
*
* If this is not specified, then
* 1. The top-level element that constitutes the widget is searched for any
* elements that contain the `lisn-pager-page` class or the
* `data-lisn-pager-page` attribute, and these are used as pages.
* 2. If there are no such elements, all of the immediate children of the
* widget element (other than `script` and `style` elements) are taken as
* the pages.
*
* **IMPORTANT:** Unless the {@link style} is set to "carousel", the page
* elements will be positioned absolutely, and therefore the pager likely
* needs to have an explicit height. If you enable {@link fullscreen}, then
* the element will get `height: 100vh` set. Otherwise, you need to set its
* height in your CSS.
*
* @defaultValue undefined
*/
pages?: Element[];
/**
* If given, these elements should match one-to-one the page elements.
*
* Each toggle element will be assigned a click listener that toggles the
* enabled/disabled state of the page.
*
* If this is not specified, then the top-level element that constitutes the
* widget is searched for any elements that contain the `lisn-pager-toggle`
* class or the `data-lisn-pager-toggle` attribute, and these are used as
* toggles.
*
* @defaultValue undefined
*/
toggles?: Element[];
/**
* If given, these elements should match one-to-one the page elements.
*
* Each toggle element will be assigned a click listener that switches the
* pager to the page.
*
* If this is not specified, then the top-level element that constitutes the
* widget is searched for any elements that contain the `lisn-pager-switch`
* class or the `data-lisn-pager-switch` attribute, and these are used as
* switches.
*
* @defaultValue undefined
*/
switches?: Element[];
/**
* This element will be assigned a click listener that goes to the next page.
*
* If this is not given, then the top-level element that constitutes the
* widget is searched for the first element that contains the
* `lisn-pager-next-switch` class or the `data-lisn-pager-next-switch`
* attribute, and this is used.
*
* @defaultValue undefined
*/
nextSwitch?: Element;
/**
* This element will be assigned a click listener that goes to the previous
* page.
*
* If this is not given, then the top-level element that constitutes the
* widget is searched for the first element that contains the
* `lisn-pager-prev-switch` class or the `data-lisn-pager-prev-switch`
* attribute, and this is used.
*
* @defaultValue undefined
*/
prevSwitch?: Element;
/**
* Set the initial page number.
*
* @defaultValue 1
*/
initialPage?: number;
/**
* Set the style of the widget. This determines the basic CSS applied.
*
* When importing the stylesheets in your project, if not using the full
* stylesheet (lisn.css) you can import either pager.css which contains all
* three pager styles, or only `pager-<style>.css`.
*
* **NOTE:** The base css only includes the minimum required for positioning
* and transitioning pages. The switches and toggles are intentionally not
* styled for flexibility. You should style those in your CSS.
*
* **IMPORTANT:** Unless the {@link style} is set to "carousel", the page
* elements will be positioned absolutely, and therefore the pager likely
* needs to have an explicit height. If you enable {@link fullscreen}, then
* the element will get `height: 100vh` set. Otherwise, you need to set its
* height in your CSS.
*
* @since v1.1.0
*
* @defaultValue "slider"
*/
style?: "slider" | "carousel" | "tabs";
/**
* Only relevant is {@link style} is "carousel".
*
* The *minimum* page height (or width in {@link horizontal} mode) in pixels.
* This will determine the number of visible slides at any one width of the
* pager. Pages will still grow to fill the size of the carousel itself.
*
* @since Introduced in v1.1.0.
*
* @defaultValue 300
*/
pageSize?: number;
/**
* Only relevant is {@link style} is "carousel".
*
* Whether to show a bit of the upcoming and/or previous pages that are
* hidden when not all fit.
*
* @since Introduced in v1.1.0.
*
* @defaultValue false
*/
peek?: boolean;
/**
* If true, then:
* - if the pager {@link style} is "slider", the pager's height will be set
* to the viewport height (100vh)
* - the pager's scrolling ancestor will be scrolled to the top of the pager
* when 30% of it is in view
* - scrolling beyond the first or last page will initiate scroll up/left or
* down/right the pager's scrolling ancestor in order to allow "leaving"
* the pager
*
* Note that except in "carousel" {@link style} the pager's pages will be
* positioned absolutely, and so if you do _not_ enable this option, you will
* need to manually set the height of the page parent element via CSS.
*
* @defaultValue false
*/
fullscreen?: boolean;
/**
* Only relevant is {@link style} is "slider" (default) or "carousel".
*
* Use a parallax effect for switching pages.
*
* @defaultValue false
*/
parallax?: boolean;
/**
* Only relevant is {@link style} is "slider" (default) or "carousel".
*
* Transition pages sideways instead of vertically.
*
* @defaultValue false
*/
horizontal?: boolean;
/**
* Transition pages upon user scroll-like and drag-like gestures via the
* given {@link GestureDevice}s. If set to true, then gestures using all
* device types are supported.
*
* Note that drag gesture is only supported by the "pointer" device and also
* the "pointer" device only supports drag gestures, so if you want to
* disable drag gestures, simply pass "wheel,key,touch" as this option.
*
* @defaultValue true
*/
useGestures?: boolean | CommaSeparatedStr<GestureDevice> | GestureDevice[];
/**
* If true, then pages will be advanced backwards/forwards regardless if the
* gesture direction is horizontal or vertical.
*
* If false then, a gesture will go to the next page only if its direction is
* down if {@link horizontal} is false/right if {@link horizontal} is true,
* and to the previous page only if the gesture direction is up if
* {@link horizontal} is false/left if {@link horizontal} is true.
*
* **IMPORTANT:**
* If {@link fullscreen}, {@link preventDefault} and
* {@link alignGestureDirection} are all true, then the pager's scrollable
* parent must be scrollable in the same direction as the pager orientation,
* otherwise automatic scroll beyond the last/first page won't work.
*
* @defaultValue false
*/
alignGestureDirection?: boolean;
/**
* Whether to prevent the default action for events that would result in
* gestures.
*
* **NOTE:**
* If true (default), then all events that originate from a device given in
* {@link useGestures} and that could result in a gesture will be prevented
* regardless of their direction and whether {@link alignGestureDirection} is
* true.
*
* @defaultValue true
*/
preventDefault?: boolean;
};
//# sourceMappingURL=pager.d.ts.map