Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .github/workflows/release-package.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Node.js Package

on:
release:
types: [created]

workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- run: npm ci
- run: npm test

publish-gpr:
needs: build
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
registry-url: https://npm.pkg.github.com/
- run: npm ci
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ setup-tests.js
coverage/
*tags*
*.test.js
.github
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
save-exact=true
@salesp07:registry=https://npm.pkg.github.com
35 changes: 17 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,26 @@ Inspired by [react-typewriter].
## Installation

```
$ yarn add react-native-typewriter
$ yarn add @salesp07/react-native-typewriter
```

OR

```
$ npm install --save react-native-typewriter
$ npm install @salesp07/react-native-typewriter
```

## Usage

Pass text and a typing direction into the component to control its animation.

```javascript

import React, { Component } from 'react'
import TypeWriter from 'react-native-typewriter'
import React, { Component } from "react";
import TypeWriter from "react-native-typewriter";

class TypingText extends Component {
render() {
return <TypeWriter typing={1}>Hello World!</TypeWriter>
return <TypeWriter typing={1}>Hello World!</TypeWriter>;
}
}
```
Expand All @@ -49,17 +48,11 @@ type: `Boolean` default: `false`
This flag will ensure the enclosing container's size and shape is fixed.
Prevents the text from shifting around as it grows into its container.

### maxDelay
### delay

type: `Number` default: `100`

The maximum delay between each typed token in milliseconds.

### minDelay

type: `Number` default: `20`

The minimum delay between each typed token in milliseconds.
The delay between each typed token in milliseconds.

### delayMap

Expand All @@ -72,12 +65,18 @@ let delayMap = [
// increase delay by 100ms at index 4
{ at: 4, delay: 100 },
// increase delay by 400ms following every '.' character
{ at: '.', delay: 400 },
// decrease delay by 200ms following every '!' character
{ at: /!/, delay: -200 }
]
{ at: ".", delay: 400 },
// increase delay by 500ms following every instance of "hello!" in the string
{ at: "hello!", delay: 500 },
// decrease delay by 200ms following every instance of '!?' in the string (`\` is an escape character)
{ at: /!\?/g, delay: -200 },
// increase delay by 1000ms following every last adjacent punctuation character
{ at: /[.!?:;](?![.!?:;])/g, delay: 1000 },
];
```

#### OBS: If you're using a Regular Expression, make sure to include the global flag `g`, since the code uses [matchAll()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/matchAll)

### initialDelay

type: `Number` default: `200`
Expand Down
80 changes: 37 additions & 43 deletions components/typewriter.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Text } from 'react-native';
import { getTokenAt, hideSubstring } from '../utils';
import React, { Component } from "react";
import PropTypes from "prop-types";
import { Text } from "react-native";
import {
getTokenAt,
hideSubstring,
getNewDelayMap,
extractTextFromChildren,
} from "../utils";

const DIRECTIONS = [-1, 0, 1];
const MAX_DELAY = 100;

export default class TypeWriter extends Component {
static propTypes = {
Expand All @@ -17,26 +21,21 @@ export default class TypeWriter extends Component {
PropTypes.instanceOf(RegExp),
]),
delay: PropTypes.number,
}),
})
),
fixed: PropTypes.bool,
initialDelay: PropTypes.number,
maxDelay: PropTypes.number,
minDelay: PropTypes.number,
delay: PropTypes.number,
onTyped: PropTypes.func,
onTypingEnd: PropTypes.func,
style: PropTypes.oneOfType([
PropTypes.object,
PropTypes.array,
]),
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
typing: PropTypes.oneOf(DIRECTIONS),
};

static defaultProps = {
fixed: false,
initialDelay: MAX_DELAY * 2,
maxDelay: MAX_DELAY,
minDelay: MAX_DELAY / 5,
initialDelay: 0,
delay: 100,
onTyped() {},
onTypingEnd() {},
style: {},
Expand All @@ -62,6 +61,8 @@ export default class TypeWriter extends Component {
visibleChars: 0,
};

const oldDelayMap = props.delayMap || [];
this.delayMap = getNewDelayMap(oldDelayMap, props.children);
this.typeNextChar = this.typeNextChar.bind(this);
}

Expand All @@ -74,20 +75,22 @@ export default class TypeWriter extends Component {
componentDidUpdate(prevProps, prevState) {
const { children, typing } = this.props;

this.clearTimeout();
if (this.halted) return;

this.clearTimeout();
if (typing === 0) return;

if (children !== prevProps.children) {
if (
children !== prevProps.children &&
extractTextFromChildren(children) !==
extractTextFromChildren(prevProps.children)
) {
this.delayMap = getNewDelayMap(this.delayMap, children);
this.reset();
return;
}

const {
delayMap,
onTyped,
onTypingEnd,
} = this.props;
const { onTyped, onTypingEnd } = this.props;
const { visibleChars } = this.state;
const currentToken = getTokenAt(this, prevState.visibleChars);
const nextToken = getTokenAt(this, visibleChars);
Expand All @@ -97,11 +100,11 @@ export default class TypeWriter extends Component {
}

if (nextToken) {
let timeout = this.getRandomTimeout();
let timeout = this.props.delay;

if (delayMap) {
delayMap.forEach(({ at, delay }) => {
if (at === visibleChars || (currentToken && currentToken.match(at)) ) {
if (this.delayMap) {
this.delayMap.forEach(({ at, delay }) => {
if (currentToken && at === visibleChars) {
timeout += delay;
}
});
Expand All @@ -119,27 +122,24 @@ export default class TypeWriter extends Component {
this.clearTimeout();
}

getRandomTimeout() {
const { maxDelay, minDelay } = this.props;

return Math.round(Math.random() * (maxDelay - minDelay) + minDelay);
}

clearTimeout() {
if (this.timeoutId != null) {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = null;
}
}

reset() {
const { initialDelay } = this.props;

this.setState({ visibleChars: 0 }, () => this.startTyping(initialDelay));
}

startTyping(delay) {
this.timeoutId = setTimeout(this.typeNextChar, delay);
this.halted = true;
this.timeoutId = setTimeout(() => {
this.halted = false;
this.typeNextChar();
}, delay);
}

typeNextChar() {
Expand All @@ -151,22 +151,16 @@ export default class TypeWriter extends Component {
render() {
const {
children,
delayMap,
fixed,
initialDelay,
maxDelay,
minDelay,
delay,
onTyped,
onTypingEnd,
typing,
...rest
} = this.props;
const { visibleChars } = this.state;
const component = (
<Text {...rest}>
{children}
</Text>
);
const component = <Text {...rest}>{children}</Text>;

return hideSubstring(component, fixed, visibleChars);
}
Expand Down
69 changes: 69 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Type definitions for react-native-typewriter 0.7.2
// Project: https://github.com/TaylorBriggs/react-native-typewriter
// Definitions by: Andréas "ScreamZ" HANSS <https://github.com/ScreamZ>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
import React = require("react");
import { TextProps } from "react-native";

interface TypeWriterProps extends TextProps {
children: React.ReactNode;

/**
* - `-1` - deleting
* - `0` - no typing (animation paused)
* - `1` - typing
*
* A value of 1 types text left to right until completion. A value of -1 erases text from right to left. A value of 0 stops the animation.
* @default 0
*/
typing: -1 | 0 | 1;

/**
* This flag will ensure the enclosing container's size and shape is fixed. Prevents the text from shifting around as it grows into its container.
* @default false
*/
fixed?: boolean;

/**
* The delay between each typed token in milliseconds.
* @default 100
*/
delay?: number;

/**
* Adds additional delay to specific characters before the next character is typed.
*
* @example
* const delayMap = [
* // increase delay by 100ms at index 4
* { at: 4, delay: 100 },
* // increase delay by 400ms following every '.' character
* { at: '.', delay: 400 },
* // decrease delay by 200ms following every '!' character
* { at: /!/, delay: -200 }
* ];
*/
delayMap?: Array<{ at: number; delay: number }>;

/**
* The time in milliseconds before the first token is typed.
* @default 200
*/
initialDelay?: number;

/**
* A callback called when each token is typed or erased during the animation.
* @param token - The token that was typed or erased.
* @param previousVisibleCharacters - The number of characters visible before the token was typed or erased.
*/
onTyped?(token: string, previousVisibleCharacters: number): void;

/**
* Called once the typing animation has completed. This callback is not called if props.typing is changed to 0 before the animation completes.
*/
onTypingEnd?(): void;
}

declare class TypeWriter extends React.PureComponent<TypeWriterProps> {}

export default TypeWriter;
Loading