npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@madsbrydegaard/timelines

v1.1.5

Published

Timeline(s) wrapper with support for zoom and pan

Downloads

21

Readme

@madsbrydegaard/timelines

Timeline engine for creating robust and flexible timelines with zoom and pan.

The project is designed to act as the main engine for web projects that can visualize historic events.

Code re-calculates points in time on every frame using minutes as timescale. The engine does not depent on expanding divs or any other HTML elements which is why it can zoom past browser limits and span 14B years of history.

How to use:

Module supports both ESM and UMD - please check demos.

Install ESM Module:

npm i @madsbrydegaard/timelines

ESM Setup:

<div id="timeline"></div>
<script type="module">
    import { TimelineContainer } from "@madsbrydegaard/timelines";

	const timeline = TimelineContainer("#timeline", {
        start: "100bc",
        end: "100ad"
    });
</script>

Vanilla setup:

<div id="timeline"></div>
<script src="path/to/timelines.umd.js"></script>
<script>
    const timeline = TimelineContainer("#timeline", {
        start: "100bc",
        end: "100ad"
    });
</script>

Add timeline events

<div id="timeline"></div>
<script type="module">
    import { TimelineContainer } from "@madsbrydegaard/timelines";

	const timeline = TimelineContainer("#timeline", {
        start: "100bc",
        end: "now"
    });

    const timelineEvents = [
        {
            start: [1974],
            end: 'now',
            title: "My Life",
            description:
                "I was born in the last century and spend most of my life programming computers",
        },
    ];
    timeline.add({
        events: timelineEvents.map((item) => ({
            ...item,
            type: "timeline",
        })),
    });
</script>

Capturing events

<div id="timeline"></div>
<script type="module">
    import { TimelineContainer } from "@madsbrydegaard/timelines";

    const timelineContainer = document.querySelector("#timeline");
	const timeline = TimelineContainer(timelineContainer, {
        start: "100bc",
        end: "now"
    });

    const timelineEvents = [
        "pinch.tl.container",
        "wheel.tl.container",
        "drag.tl.container",
        "click.tl.event"
    ];

    timelineEvents.forEach((eventName) => {
        timelineContainer.addEventListener(eventName, (timelineEvent) => {
            console.log(eventname, timelineEvent);
        });
    });

    const timelineEvents = [
        {
            start: [1974],
            end: 'now',
            title: "My Life",
            description:
                "I was born in the last century and spend most of my life programming computers",
        },
    ];
    timeline.add({
        events: timelineEvents.map((item) => ({
            ...item,
            type: "timeline",
        })),
    });
</script>

React & Typescript setup:

import { TimelineContainer, ITimelineContainer, ITimelineOptions, ITimelineEvent } from "@madsbrydegaard/timelines"
import { useEffect, useRef, createRef } from 'react';

function App() {
    const container = createRef<HTMLDivElement>();
    const timeline = useRef<ITimelineContainer>();
    const events = [
        "pinch.tl.container",
        "wheel.tl.container",
        "drag.tl.container",
        "click.tl.event"
    ];

    useEffect(()=>{
        if (container.current) {
            // Initialize a timeline
			timeline.current = TimelineContainer(container.current, {
				start: "100bc",
				end: "2030ac",
			} as ITimelineOptions) as ITimelineContainer;

            // Listening for interaction event(s)
            events.forEach((eventName) => {
                container.current.addEventListener(eventName, (evt: CustomEvent) => {
                    if (evt.detail) {
                        // See details on event that has been clicked
                        console.log(evt.detail)
                    }
                });
            });

            // Load timeline events into timeline
            timeline.current.add(async () => {
				return await gofetch("path/to/some.json") as ITimelineEvent;
            }
        }
    }, [])

    return (
        <div className="App">
            <div ref={myRef}></div>
        </div>
    );
}

export default App;

Documentation:

// Constructor
TimelineContainer(element: HTMLElement | string, options: object)

First argument is the page container for the timeline. Eg. '#timelineContainer' | document.querySelector("#timelineContainer");

Zooming and panning is automatically activated on the whole container so that large page areas can be part of timeline.

The timeline works with 2 different date sets.

  • Overall date range of allowed zooming and panning. Zooming and Panning is not allowed outside these dates.
  • Specific date range for initialization. These acts as the boundaries when the timeline initalizes and creates a 'view' on the overall timeline

Second argument is a configuration object. Default configuration looks like this:

{
    // Defines number of 'time' labels in the timeline
    labelCount: 5,
    // How fast zooming happens
    zoomSpeed: 0.04,
    // How fast panning happens
    dragSpeed: 0.001,
    // When timeline should start on init. (See below for valid input)
    start: "-100y",
    // When timeline should end on init. (See below for valid input)
    end: "now",
    // When overall timeline boundary should start. (See below for valid input)
    timelineStart: "-1B",
    // When overall timeline boundary should end. (See below for valid input)
    timelineEnd: "1M",
    // How far out zoom is allowed
    minZoom: 1,
    // How far in zoom is allowed
    maxZoom: 1e11,
    // Where to vertically place the timeline in the container. 'bottom' | 'top'
    position: "bottom",
    // Height of timeline events
    eventHeight: 5,
    // Spacing between timeline events
    eventSpacing: 3,
    // Whether zoom and pan is automatically initialized when clicking a timeline event
    autoZoom: false,
    // Margin on each side when using auto zoom
    zoomMargin: 0.1,
    // Whether events are automatically selected on mouse click
    autoSelect: false,
    // Default color for each timeline event
    defaultColor: "#aaa",
    // Default color for each timeline event when selected
    defaultHighlightedColor: "#444",
    // Default color for each timeline background event
    defaultBackgroundColor: "#0000",
    // Default color for each timeline background event when selected
    defaultBackgroundHightligtedColor: "#eee7",
    // Duration in ms when auto zoom is enabled
    zoomDuration: 200,
    // Auto zoom easing. "easeOutCubic" | "easeOutExpo" | "easeLinear"
	easing: "easeOutCubic",
    // How many previews to display
	numberOfHighscorePreviews: 5,
    // Delay befort displaying previews in ms
	highscorePreviewDelay: 500,
    // Preview width in px
	highscorePreviewWidth: 100,
    // Default classnames for generated HTML Elements
    classNames: {
        timeline: "tl",
        timelineEvent: "tl__event",
        timelinePreview: "tl__preview",
        timelineEventTitle: "tl__event__title",
        timelineLabels: "tl__labels",
        timelineDividers: "tl__dividers",
        timelineEvents: "tl__events",
        timelinePreviews: "tl__previews",
        timelineIo: "tl__io",
        timelineLabel: "tl__label",
        timelineDivider: "tl__divider",
    }
}

Allowed values for dates:
Engine has its own Date string parser which supports the following specials:

  • XXX -> any positive or negative number translates to now plus/minus number of minutes.
  • 'YYYY-MM-DDTHH:mm:ss.sssZ' -> ISO 8601 string
  • 'now' -> Translates to current time = new Date()
  • 'XXXy' -> any positive or negative number with a trailing 'y'. Translates relative to now. Eg. '-1000y' = Minus 1000 years from now.
  • 'XXXbc' -> any positive or negative number with a trailing 'bc'. Translates into that specific year - BC. Eg. '100bc' = Year -100
  • 'XXXad' -> any positive or negative number with a trailing 'ad'. Translates into that specific year - AD. Eg. '100ad' = Year 100
  • 'XXXK' -> any positive or negative number with a trailing 'K'. Translates into year * 1000. Eg. '100K' = Year 100.000
  • 'XXXM' -> any positive or negative number with a trailing 'M'. Translates into year * 1.000.000. Eg. '100M' = Year 100.000.000
  • 'XXXB' -> any positive or negative number with a trailing 'B'. Translates into year * 1.000.000.000. Eg. '100B' = Year 100.000.000.000

Events Engine dispatches events on interactions. Use addEventListener to capture them

timelineContainer.addEventListener(eventName, (timelineEvent) => {
    // Handle timelineEvent...
});

Timeline Event object

{
    // Name of event. See list below.
    name: string;
    // Configurations object. See structure above.
    options: ITimelineOptions;
    // Current timeline event if IO event is related to any timeline event. See structure below.
    timelineEvent: ITimelineEventWithDetails;
    // Where is viewStart now. formatted.
    viewStartDate: string;
    // Where is viewEnd now. formatted.
    viewEndDate: string;
    // View duration now. In minutes.
    viewDuration: number;
    // Current timeline / view ratio (z-axix offset)
    ratio: number;
    // Current timeline / view pivot (x-axix offset)
    pivot: number;
},

Complete IO event list:

  • update.tl.container -> When engine updates (re-draws)
  • pinch.tl.container
  • wheel.tl.container
  • drag.tl.container
  • touchstart.tl.container
  • touchend.tl.container
  • touchmove.tl.container
  • mousemove.tl.container
  • mousedown.tl.container
  • mouseup.tl.container
  • resize.tl.container
  • click.tl.event
  • hover.tl.event
  • selected.tl.event

TimelineEvent

{
    // Title of event
	title: string;
    // Optional Render function for event HTML in timeline. Must return HTML Div Element.
	renderEventNode?: (timelineEvent: ITimelineEventWithDetails) => HTMLDivElement;
    // Optional Render function for event preview HTML in timeline. Must return HTML Div Element.
	renderPreviewNode?: (timelineEvent: ITimelineEventWithDetails) => HTMLDivElement;
    // Type of event. Supports "timeline" | "background"
    type?: string;
    // Color of event element. array of numbers stranslates to #XXXXXXXX hex code
	color?: number[];
    // Color of event element when selected. array of numbers stranslates to #XXXXXXXX hex code
	highlightedColor?: number[];
    // Start of timeline event.
    start?: number[] | string | number | Date;
    // End of timeline event.
	end?: number[] | string | number | Date;
    // Optional duration of timeline event. Used when 'end' is not defined.
	duration?: number | string;
    // Child events
	events?: ITimelineEvent[];
    // Prevents preview rendering on next frame
    preventNextPreviewRender?: boolean;
}

TimelineContainer

{
    // Add events to container
	add: (...timelineEvents: ITimelineEvent[]) => void;
    // auto zoom specific event
	zoom: (timelineEvent: ITimelineEvent, useAnimation?: boolean, onzoomend?: (timelineEvent: ITimelineEvent) => void) => void;
    // focus specific event
	focus: (timelineEvent: ITimelineEvent, useAnimation?: boolean, onfocused?: (timelineEvent: ITimelineEvent) => void) => void;
    // reset container to initial config
	reset: () => void;
    // select specific event
	select: (timelineEventIdentifier?: string) => void;
    // Prevents global preview rendering on next frame
	preventNextPreviewRender: () => void;
}