Skip to content
This repository has been archived by the owner on Oct 25, 2021. It is now read-only.

Commit

Permalink
Allow children to fully render before initializing.
Browse files Browse the repository at this point in the history
  • Loading branch information
alexmacarthur committed Feb 7, 2020
1 parent 7197af4 commit ee151f1
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 27 deletions.
95 changes: 91 additions & 4 deletions README.md
Expand Up @@ -25,17 +25,42 @@ import TypeIt from "typeit-react";
export default () => {
return (
<div className="App">
<TypeIt>This will be typed in a `span` element!</TypeIt>
<TypeIt>
This will be typed in a `span` element!
</TypeIt>
</div>
);
};
```

Note: This approach will cause the string to first be rendered in the markup and _then_ picked up by TypeIt, which might be desired if you're using it with a statically rendered application (ex: GatsbyJS). However, it may also cause a brief flash of text when the page loads. For an alternative way to define strings, see below.
### A More Complex Example

The component will allow its children to fully render, and then type whatever HTML is generated. So, in addition to simple strings, you can nest HTML and components like below.

```javascript
import TypeIt from "typeit-react";

// This could be any component that generates HTML.
const SuperStrong = ({children}) => {
return (
<strong style={{fontSize: "80px"}}>{children}</strong>
)
}

export default () => {
return (
<div className="App">
<TypeIt>
Weak text. <SuperStrong>Super strong text.</SuperStrong>
</TypeIt>
</div>
);
};
```

### Customizing Your Options

To tweak the animation to your liking, pass an object as the `options` prop. All options supported by TypeIt can be used here. Using this prop, you can also set strings without passing them as children. To view all options, see [TypeIt's documentation](https://typeitjs.com/docs#options).
To tweak the animation to your liking, pass an object as the `options` prop. All options supported by the core TypeIt library can be used here. Using this prop, you can also set strings without passing them as children. See [TypeIt's documentation](https://typeitjs.com/docs#options) for more details on what's available.

```javascript
import TypeIt from "typeit-react";
Expand All @@ -55,7 +80,7 @@ export default () => {
};
```

### Choose Your Own Element
### Choosing Your Own Element

Out of the box, a `span` element is used to contain the typing animation. To choose your own element, use the `element` prop.

Expand All @@ -71,6 +96,68 @@ export default () => {
};
```

### Fine-Tuning the Instance w/ Companion Methods

TypeIt comes with a set of [special methods](https://typeitjs.com/docs#instance-methods) that let you fine-tune an animation down to the smallest detail. To leverage them here, pass a function as the `onBeforeInit` prop, which will give you access to the instance you can modify with these methods, and then return back to the component before the animation is initialized.

```javascript
import TypeIt from "typeit-react";

<TypeIt
getBeforeInit={(instance) => {
instance
.type("Hi, I'm Alxe")
.pause(750)
.delete(2)
.pause(500)
.type("ex!");

// Remember to return it!
return instance;
}}
/>
```

### Accessing the Instance After Initalization

Similarly, the `getAfterInit` prop allows you to access the instance _after_ it's been kicked off, so you'll be able to leverage methods like `.freeze()`, `.unfreeze()`, and `.is()`. Read more about those [here](https://typeitjs.com/docs#non-chainable-instance-methods).

```javascript
export default () => {
const [buttonText, setButtonText] = useState("Freeze");
const [instance, setInstance] = useState(null);

const toggleFreeze = () => {
if(instance.is('frozen')) {
instance.unfreeze();
setButtonText('Freeze');
return;
}

instance.freeze();
setButtonText('Unfreeze');
}

return (
<div className="App">
<button onClick={toggleFreeze}>
{buttonText}
</button>

<TypeIt
options={{ loop: true }}
getAfterInit={(instance) => {
setInstance(instance);
return instance;
}}
>
This will just keep on going.
</TypeIt>
</div>
);
}
```

## Need Help?

If you're working with a custom implementation of TypeIt and would like some help, I'm available for hire. [Get in touch!](https://macarthur.me/contact)
Expand Down
6 changes: 3 additions & 3 deletions dist/typeit-react.es.min.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions dist/typeit-react.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
@@ -1,6 +1,6 @@
{
"name": "typeit-react",
"version": "0.0.1",
"version": "0.0.2",
"description": "React component for the most versatile JavaScript animated typing utility on the planet.",
"main": "dist/typeit-react.min.js",
"module": "dist/typeit-react.es.min.js",
Expand All @@ -20,7 +20,7 @@
"typing effect",
"typewriter effect",
"type effect",
"text effects",
"text effects",
"react"
],
"repository": {
Expand Down
67 changes: 53 additions & 14 deletions src/index.tsx
@@ -1,37 +1,76 @@
import * as React from 'react';
import { default as TypeItCore } from 'typeit';
const { useRef, useEffect } = React;
const { useRef, useEffect, useState } = React;

export interface TypeItOptions {
strings?: Array<string> | string
}

export interface TypeItProps {
element?: string,
options?: object,
children?: React.ReactNode
}
options?: TypeItOptions,
children?: React.ReactNode,
getBeforeInit?: any,
getAfterInit?: any
}

const defaultProps: TypeItProps = {
element: 'span',
options: {}
options: {},
getBeforeInit: (instance: object) => instance,
getAfterInit: (instance: object) => instance
}

const TypeIt: React.FunctionComponent = (props: TypeItProps) => {
const ref = useRef<HTMLElement>(null);
const {options, element, children, ...remainingProps} = props;
const [shouldRenderChildren, setShouldRenderChildren] = useState<boolean>(true);
const ref = useRef<HTMLElement|null>(null);
const {options, element, children, getBeforeInit, getAfterInit, ...remainingProps} = props;
const DynamicElement = element;

/**
* After the component mounts (and any children are rendered),
* we can safely set the strings of the instance using the rendered HTML
* from those optionally-defined children. Otherwise, we'll just use the strings
* defined via the options prop.
*/
useEffect(() => {
if(children) {
options.strings = ref.current.innerHTML;
}

setShouldRenderChildren(false);
}, []);

/**
* Once options (and strings) have been defined, we can hide any children we might
* have rendered to make room for the TypeIt animation. On cleanup, destroy
* that instance.
*/
useEffect(() => {
const instance = (new TypeItCore(ref.current, {
if(shouldRenderChildren) {
return;
}

let i = (new TypeItCore(ref.current, {
...options
})).go();
}));

i = getBeforeInit(i);
i.go();
i = getAfterInit(i);

return () => {
instance.destroy();
// @ts-ignore
i.destroy();
}
}, []);
}, [shouldRenderChildren]);

return (
<DynamicElement ref={ref} {...remainingProps}>
{children}
</DynamicElement>
<div style={{ opacity: shouldRenderChildren ? 0 : 1}}>
<DynamicElement ref={ref} {...remainingProps}>
{shouldRenderChildren && children}
</DynamicElement>
</div>
)
}

Expand Down
3 changes: 2 additions & 1 deletion types/index.d.ts
@@ -1,5 +1,6 @@
declare namespace JSX {
interface IntrinsicElements {
DynamicElement: any
DynamicElement: any,
ref: HTMLElement | null
}
}

0 comments on commit ee151f1

Please sign in to comment.