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

@twreporter/timeline

v2.1.2

Published

The Reporter timeline component

Downloads

7

Readme

timeline

npm (latest) npm (rc)

What is this

This is a tool for building timeline graphs. It is published as an npm package.

The timeline graphs are used in the news articles of The Reporter Taiwan.

Here are some examples:

We also provide a servcie to convert your timeline data on Google Spreadsheet into embedded code. See the User Guide (zh-tw).

Data Structure

timeline example

sections

A timeline graph is divided into several sections. There are two levels of section for now:

  • group: one unit section or several ones may constitute a group section. Which means unit is the subsection of group
  • unit: the lowest level of section

The background blocks are divided according to one of section types.

A section contain one heading element (optional), and may have multiple subsections or sub-elements.

elements

Elements are the basic items in sections. In principle, one element corresponds to one row of the data sheet.

There are three types of element for now:

  • group-flag: the heading element of a group section
  • unit-flag: the heading element of a unit section
  • record: basic content element which may contain text and image

How to use this package

Install

yarn add @twreporter/timeline

Usage

Use NodeJS Code Builder

Example:

const timelineUtils = require('@twreporter/timeline').default
const path = require('path')
const fs = require('fs')

function handleSuccess(result) {
  return [result, undefined]
}

function handleFailure(error) {
  return [undefined, error]
}

// Setup Authentication for fetching data from spreadsheet
const keyFilePath = 'your-key-file-path' // ex: path.resolve(__dirname, './service-account.json')
const auth = new google.auth.GoogleAuth({
  keyFile: keyFilePath,
  scopes: ['https://www.googleapis.com/auth/spreadsheets.readonly'],
})

async function timeline() {
  // Use timelineUtils.Sheets to fetch data from spreadsheet
  const sheets = await new timelineUtils.Sheets({
    spreadsheetId: 'your target spreadsheet id',
    auth,
  })
  const jsonData = await sheets.getJSONData()
  // Use timelineUtils.Sheets to validate fetched data
  const [result, error] = await sheets
    .validate(jsonData)
    .then(handleSuccess, handleFailure)
  if (error) {
    /* handle the validation error here */
    /*
      We use `yup` to validate. Here's its error format:
      https://github.com/jquense/yup#validationerrorerrors-string--arraystring-value-any-path-string
    */
  }

  // You can build the embedded code (as HTML string) with fetched data
  const embeddedCode = timelineUtils.buildEmbeddedCode(
    jsonData.elements,
    jsonData.theme,
    jsonData.appProps
  )

  // Or render the Timeline component with fetched data
  const Timeline = timelineUtils.Component
  const ReactDOMServer = require('react-dom/server')
  const React = require('react')
  const html = ReactDOMServer.renderToStaticMarkup(
    <Timeline
      content={timelineUtils.buildContent(jsonData.elements)}
      theme={jsonData.theme}
      {...jsonData.appProps}
    />
  )
}

Use Timeline React Component

Props
  • content: See the content format below
  • theme: Custom theme. See the theme schema and default values in src/constants/default-theme.js
  • maxHeadingTagLevel: If it's set to 3, the heading element will start with html tag h3. The default value is 3.
  • emphasizedLevel: unit or group. It will apply blocks with white background to the content of that section level (Not include the heading element). The default value is unit.
  • showRecordBullet: Show the bullet of record or not. The default value is true.

See details of the component in src/components/timeline.js

Content Format

The content is data with tree structure. The tree is composed with nodes.

We can use buildContent to transform flat spreadsheet elements to tree content. Each element will be a leaf node in the content tree.

For example, given elements:

const elements = [
  { type: 'group-flag' /* ... */ },
  { type: 'unit-flag' /* ... */ },
  { type: 'record' /* ... */ },
  { type: 'record' /* ... */ },
  { type: 'group-flag' /* ... */ },
  { type: 'unit-flag' /* ... */ },
  { type: 'record' /* ... */ },
]

The tree structure of buildContent(elements) will be:

# each line represents a node
root
└── group-section
    ├── group-flag
    ├── unit-section
    |   ├── unit-flag
    |   ├── record
    |   └── record
    └── unit-section
        ├── unit-flag
        └── record

There are some principles applied in buildContent:

  1. Every element node is a leaf node (without child), and every leaf node is an element node.

  2. Every section node is a branch node (with at least one child), and every branch node is a section node or the root node.

  3. Every heading element node(group-flag, unit-flag) will have a section parent, and the heading element will be the first child of that section node.

  4. Nodes with the same depth should have the same type.

  5. When appending element to the tree, buildContent will try to append the new branch node into the ancestors of previous element. If there's no accurate position in the ancestors of previous element, it will create a new one. For example, given elements:

    const elements[
      { type: 'record' /* ... */ }, // there's no previous branch when appending this first record, so we will create a new branch for it
      { type: 'record' /* ... */ },
      { type: 'group-flag' /* ... */ },
      { type: 'unit-flag' /* ... */ },
      { type: 'record' /* ... */ },
    ]

    The tree structure of buildContent(elements) will be:

    # each line represents a node
    root
    ├── group-section
    │   └── unit-section
    │       ├── record
    │       └── record
    └── group-section
        ├── group-flag
        └── unit-section
            ├── unit-flag
            └── record

How to develop this package

Fetch data for dev

Use built-in scripts (save your google key file in dev/sheets-api.json):

make dev-fetch-data SHEET=[target spreadsheet id]

The data will be saved at dev/data.json

Build code for dev

Should prepare dev/data.json first.

make dev-build-code

The code will be saved at dev/output.txt

Test dev code

We use webpack-dev-server to render a mock article with all elements for development.

You should prepare dev/data.json first.

make dev-server

If you need to change the hostname (usually due to the CORS reasons), add DEV_HOST=[your-custom-hostname] for giving webpack-dev-server the hostname. Example:

# Start the webpack-dev-server with custom hostname
DEV_HOST=testtest.twreporter.org make dev-server