GithubHelp home page GithubHelp logo

untemps / svelte-readotron Goto Github PK

View Code? Open in Web Editor NEW
41.0 4.0 1.0 1.27 MB

Svelte component to display an estimated reading time

Home Page: https://svelte-readotron.vercel.app/

License: MIT License

JavaScript 58.09% HTML 2.29% Svelte 37.88% Shell 1.03% CSS 0.70%
reading-time reading-time-estimator reading-rates svelte svelte-component read-o-meter speed-reading-time component javascript

svelte-readotron's Introduction

svelte-readotron

Svelte component to display an estimated reading time


npm GitHub Workflow Status Codecov

Demo

🔴  LIVE DEMO :red_circle:

Example

Installation

yarn add @untemps/svelte-readotron

Usage

Basic usage

<script>
	import Readotron from '@untemps/svelte-readotron'
</script>

<main>
	<Readotron selector=".text" />
	<section class="text">...</section>
</main>

selector prop is mandatory as it points to which element contains the text content to parse. You can utilize any selector supported by the Web API querySelector function.
If several elements match the selector, it only parses the text of the first element in the array.

The component will wait for the element to be present in the DOM before parsing its content. If the element is not found after 1000ms, an error is raised and displayed.
This is achieved with the @untemps/dom-observer package.

Lang

'lang' is an optional prop that designates the language of the text to parse. The component implements the @untemps/read-per-minute underhand package which returns an estimation based on the lang (language).
Reading rates by lang come from "How many words do we read per minute? A review and meta-analysis of reading rate" by Marc Brysbaert - Department of Experimental Psychology Ghent University

Lang Rate
default 200
ar 181
zh 260
nl 228
en 236
fi 195
fr 214
de 260
he 224
it 285
ko 226
es 278
sv 218

If a lang is not defined or the provided lang is not listed, the default value (200) will be applied.

Example

<script>
	import Readotron from '@untemps/svelte-readotron'
</script>

<main>
	<Readotron selector=".text" lang="en" />
	<section class="text">...</section>
</main>

Template

You can customize the Readotron display by using the template prop.

  • A template can be a string with one or more tokens delimited with %

Example

<script>
	import Readotron from '@untemps/svelte-readotron'
</script>

<main>
	<Readotron selector=".text" template="Reading Time: %time% minutes (%words% words)" />
	<section class="text">...</section>
</main>
  • A template can be a function with time and words as arguments.
    The function should return a template literal with the markup to display using optionally arguments as placeholders. But it may return any displayable type as well.

⚠️ The string will be parsed with the {@html} expression: Be very careful with the content you pass or allow to pass in to this prop!

Example

<script>
    import Readotron from '@untemps/svelte-readotron'
</script>

<main>
    <Readotron selector=".text" template={(time, words) => `<Icon name='clock'> <strong>Reading Time: ${time} minutes</strong> (${words} words)`}/>
    <section class="text">
        ...
    </section>
</main>

Avalaible tokens/arguments

Token Description
time Estimated reading time (in minutes)
words Number of words

Scroll Support

You are able to track and update component values by opting in the withScroll flag. This will change the time (remaining time to read) and words (number of remaining words) as the user scroll the document.

Note: There is no support for element scrolling so far, document only.

The component uses the scrollProgress underhand package to track document scrolling.

Example

<script>
	import Readotron from '@untemps/svelte-readotron'
</script>

<main>
	<Readotron selector=".text" withScroll />
	<section class="text">...</section>
</main>

Change Event

If you need to be notified whenever values change, you may attach a listener to the change event emitted by the component.


Note: The change event will be dispatched only if the withScroll prop is set to true since this is the only use case that triggers the change event so far


The handler will be triggered for the first time during the mounting phase with the initial values.

Handler Signature

Here are the properties available inside the event.detail sent with the event:

Props Type Description
time number Estimated remaining reading time (in minutes)
words number Number of remaining words
progress number Ratio of progression (between 0 and 1)

Example

<script>
    import Readotron from '@untemps/svelte-readotron'
</script>

<main>
    <Readotron selector=".text" withScroll on:change={(event) => {
        const {detail: {time, words, progress}} = event
        console.log('Time:', time + ' minutes')
        console.log('Words:', words + ' remaining words')
        console.log('Progress:', progress * 100 + '%')
    }}/>
    <section class="text">
        ...
    </section>
</main>

Recipe

The event dispatching allows to animate a progress bar in sync with the reading status:

<script>
    import Readotron from '@untemps/svelte-readotron'

    let readingProgress = 0
</script>

<main>
    <Readotron selector=".text" withScroll on:change={(event) => {
        readingProgress = event.detail.progress
    }}/>
    <section class="text">
        ...
    </section>
    <div class="progress-bar" style="width: {readingProgress * 100}%"></div>
</main>

<style>
    .progress-bar {
        background-color: #0075ff;
        height: 20px;
        position: fixed;
        left: 0;
        bottom: 0;
    }
</style>

Slot

Another way to customize the display is to use the <slot> element.
If a <slot> is passed in as Readotron child and correctly set (see Constraints below), it will be rendered instead of the default layout. This has precedence over the template prop.
This allows to set a specific tag as parent if needed.

Constraints:

The <slot> element has to be set with the prop slot="content"

Avalaible tokens

Like template, tokens are passed back to the component to display dynamic values (see Svelte API documentation):

Token Description
time Estimated reading time (in minutes)
words Number of words

Example

<script>
	import Readotron from '@untemps/svelte-readotron'
</script>

<main>
	<Readotron selector=".text">
		<span slot="content" let:time let:words>{time} min ({words} words)</span>
	</Readotron>
	<section class="text">...</section>
</main>

Please see the Svelte API documentation to know more about the <slot> element.

Styles

All HTML attributes are automatically passed to the parent element of the component (span).
That means you can query the class attribute to customize the style of the component.

Example

<script>
	import Readotron from '@untemps/svelte-readotron'
</script>

<main>
	<Readotron selector=".text" class="readotron" />
	<section class="text">...</section>
</main>

<style>
	.readotron {
		color: #0075ff;
		font-weight: 600;
	}
</style>

If you use a <slot> element, as it will replace the original layout, you have to switch the class name from the <Readotron> element to the <slot> element.

<script>
	import Readotron from '@untemps/svelte-readotron'
</script>

<main>
	<Readotron selector=".text">
		<span class="readotron" slot="content" let:time>{time} min</span>
	</Readotron>
	<section class="text">...</section>
</main>

Error

If an error occurs during the parsing phase, the component catches and exposes it through an error variable which is displayed as is it by default. That means the error message uses the exact same styles as time value.

Default error display

You may want to customize this error message, so the component provides a slot, named error, which replaces the default display if passed in.

Slot error display

Example

<script>
	import Readotron from '@untemps/svelte-readotron'
</script>

<main>
	<Readotron selector=".text">
		<span class="readotron" slot="content" let:time>{time} min</span>
		<span class="error" slot="error" let:error>Oops!</span>
	</Readotron>
	<section class="text">...</section>
</main>

<style>
	.readotron {
		color: #0075ff;
		font-weight: 600;
	}

	.error {
		color: #ff0000;
		font-weight: 600;
	}
</style>

Constraints:

The <slot> element has to be set with the prop slot="error"

Avalaible tokens

Error message is passed back to the component for display purpose if needed (see Svelte API documentation):

Token Description
error Original error message

API

Props Type Default Description
selector string (required) Selector of the element which contains the content to parse. See document.querySelector
lang string 'en' Lang of the content [""ar', 'zh', 'nl', 'en', 'fi', 'fr', 'de', 'he', 'it', 'ko', 'es', 'sv']
template string or function '%time% min read' Display template which contains dynamic tokens to be replaced by the parsed values. See Template
withScroll boolean false Enable updates on scroll. If true, time and words values will reflect the document scroll position

Events

Props Arguments Type Description
change Dispatches whenever time and words have changed
time number Estimated remaining reading time (in minutes) Estimated remaining reading time (in minutes)
words number Number of remaining words
progress number Ratio of progression (between 0 and 1)

Development

The component can be served for development purpose on http://localhost:10001/ running:

yarn dev

Contributing

Contributions are warmly welcomed:

  • Fork the repository
  • Create a feature branch
  • Develop the feature AND write the tests (or write the tests AND develop the feature)
  • Commit your changes using Angular Git Commit Guidelines
  • Submit a Pull Request

svelte-readotron's People

Contributors

gage117 avatar semantic-release-bot avatar untemps avatar vincentljn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

Forkers

gage117

svelte-readotron's Issues

Allow to pass html as template down

Format the component rendering precisely with plain html or custom components may seem difficult in some situations. The template prop is limited to string so far and the content slot may be quite verbose for some cases. Therefore, it would be great to allow a function to be passed to it.

The function would accept time ans words as arguments and would return a template literal that would be escaped with a {@html ...} expression.

<Readotron template={() => `<strong>${time}</strong> minutes`} />

Change the default behaviour of the mode

Running the dev script launches a demo of the component with the error status. Even if the behaviour is prefectly normal and intends to demonstrate the component error management, it may make think it doesn't work properly.
We should provide a working status as default behaviour.

Polish README

The documentation stood in the README file, like this issue description, has been written by a typical french guy who strongly need help to make it right.
If a native english dude wants to contribute, s.he would be warmly thanked.

Allow error styling

For now on error is just a text into the root node. We may style the root node when an error is raised but it is not convenient.
Alternatively we may wrap the error in a dedicated node and add a prop to style that node.

Switch unit tests to e2e tests

For such of component, unit tests are not the best bet as it depends on quite a lot of user behaviours.
We should switch to e2e tests to mimic those interactions.
It could be a good opportunity to give a try to Playwright.

Fix parsing when the content is updated

In the dev demo, when we manipulate the paragraph count selector, an error is raised in the console and neither the text nor the paragraph count value are updated

index.js:657 Uncaught (in promise) TypeError: Cannot read property 'c' of undefined
    at transition_out$1 (index.js:657)
    at Object.o (index.js:1221)
    at transition_out (index.js:170)
    at Object.o (index.js:1332)
    at transition_out (index.js:170)
    at index.js:192
    at Array.forEach (<anonymous>)
    at update (index.js:189)
    at handle_promise (index.js:230)
    at Object.p (index.js:1558)
transition_out$1 @ index.js:657
o @ index.js:1221
transition_out @ index.js:170
o @ index.js:1332
transition_out @ index.js:170
(anonymous) @ index.js:192
update @ index.js:189
handle_promise @ index.js:230
p @ index.js:1558
update @ index.js:132
flush @ index.js:100
Promise.then (async)
schedule_update @ index.js:82
make_dirty @ index.js:277
(anonymous) @ index.js:313
onParagraphCountChange @ index.js:1615

Seems like something goes wrong with Readotron update.

Need a way to obtain the estimated time without the need of the change event

Hello, and thank you for your efforts.

I would like to know if there is any way in which I can obtain the word count and estimated reading time in cases where I there will be no scrolling. I basically just need the reading time calculation. As a matter of fact, I don't even need a user interface or display of any kind.

Add progression management

The goal is to add support for scroll events and calculate the remaining text to read when scrolling.

It may optionally update the display to reflect the scrolling status. That requires to create a new prop : scrollUpdate: boolean for example.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.