Skip to content

Commit d0f68f1

Browse files
committed
Merge branch 'string-splitter-pull'
merged maxeth#34 & updated types
2 parents 0cfec0f + 7951e74 commit d0f68f1

File tree

6 files changed

+69
-47
lines changed

6 files changed

+69
-47
lines changed

README.md

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -74,19 +74,20 @@ Hence, whenever you make changes to the TypeAnimation component, you unfortunate
7474

7575
See [https://react-type-animation.netlify.app/options](https://react-type-animation.netlify.app/options) for more details.
7676

77-
| Prop | Required | Type | Example | Description | Default |
78-
| ----------------------- | -------- | -------------------------------------------------------------------- | ------------------------------------------------- | -------------------------------------------------------------------------------------------- | ------- |
79-
| `sequence` | yes | Array<number &#124; string &#124; (() => void &#124; Promise<void>)> | `['One', 1000, 'Two', () => console.log("done")]` | Animation sequence: [TEXT, DELAY-MS, CALLBACK] | `-` |
80-
| `wrapper` | no | string | `p`,`h2`,`div`, `strong` | HTML element tag that wraps the typing animation | `span` |
81-
| `speed` | no | 1,2,..,99 &#124; {type: "keyStrokeDelayInMs", value: number} | `45`, `{type: "keyStrokeDelayInMs", value: 100}` | Speed for the writing of the animation | `40` |
82-
| `deletionSpeed` | no | 1,2,..,99 &#124; {type: "keyStrokeDelayInMs", value: number} | `45`, `{type: "keyStrokeDelayInMs", value: 100}` | Speed for deleting of the animation | `speed` |
83-
| `omitDeletionAnimation` | no | boolean | `false`, `true` | If true, deletions will be instant and without animation | `false` |
84-
| `repeat` | no | number | `0`, `3`, `Infinity` | Amount of animation repetitions | `0` |
85-
| `cursor` | no | boolean | `false`, `true` | Display default blinking cursor css-animation | `true` |
86-
| `preRenderFirstString` | no | boolean | `false`, `true` | If true, the first string of your sequence will not be animated and initially (pre-)rendered | `true` |
87-
| `className` | no | string | `custom-class-name` | HTML class name applied to the wrapper to style the text | `-` |
88-
| `style` | no | object | `{fontSize: '2em'}` | JSX inline style object | `-` |
89-
| `ref` | no | HTMLElement &#124; null | `-` | `-` | `-` |
77+
| Prop | Required | Type | Example | Description | Default |
78+
|-------------------------|----------|--------------------------------------------------------------------|-------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------|--------------------|
79+
| `sequence` | yes | Array<number &#124; string &#124; (() => void &#124; Promise<void>)> | `['One', 1000, 'Two', () => console.log("done")]` | Animation sequence: [TEXT, DELAY-MS, CALLBACK] | `-` |
80+
| `wrapper` | no | string | `p`,`h2`,`div`, `strong` | HTML element tag that wraps the typing animation | `span` |
81+
| `speed` | no | 1,2,..,99 &#124; {type: "keyStrokeDelayInMs", value: number} | `45`, `{type: "keyStrokeDelayInMs", value: 100}` | Speed for the writing of the animation | `40` |
82+
| `deletionSpeed` | no | 1,2,..,99 &#124; {type: "keyStrokeDelayInMs", value: number} | `45`, `{type: "keyStrokeDelayInMs", value: 100}` | Speed for deleting of the animation | `speed` |
83+
| `omitDeletionAnimation` | no | boolean | `false`, `true` | If true, deletions will be instant and without animation | `false` |
84+
| `repeat` | no | number | `0`, `3`, `Infinity` | Amount of animation repetitions | `0` |
85+
| `cursor` | no | boolean | `false`, `true` | Display default blinking cursor css-animation | `true` |
86+
| `preRenderFirstString` | no | boolean | `false`, `true` | If true, the first string of your sequence will not be animated and initially (pre-)rendered | `true` |
87+
| `className` | no | string | `custom-class-name` | HTML class name applied to the wrapper to style the text | `-` |
88+
| `style` | no | object | `{fontSize: '2em'}` | JSX inline style object | `-` |
89+
| `ref` | no | HTMLElement &#124; null | `-` | `-` | `-` |
90+
| `splitter` | no | (text: string) => Array<string> | `(str) => new GraphemeSplitter().splitGraphemes(str)` | Used for splitting complex characters, see [grapheme-splitter](https://github.com/orling/grapheme-splitter) for more details | `String.split('')` |
9091

9192
---
9293

example/components/TypeAnimation.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { TypeAnimation } from 'react-type-animation';
2-
import { Speed } from 'react-type-animation/dist/esm/components/TypeAnimation/index.types';
2+
import { TypeAnimationProps } from 'react-type-animation/dist/esm/components/TypeAnimation/index.types';
33

4-
export default function _TypeAnimation(props: any) {
4+
export default function _TypeAnimation(props: TypeAnimationProps) {
55
return (
66
<span className="w-full block bg-blue-400 bg-opacity-10 p-3 rounded-lg">
77
<TypeAnimation {...props} />

example/pages/options.mdx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,13 @@ import { Callout, Tab, Tabs, Steps } from 'nextra-theme-docs';
8484
"-",
8585
'-',
8686
],
87+
[
88+
'splitter',
89+
'(text: string) => Array[string]',
90+
'Used for splitting complex characters, see the npm package "grapheme-splitter" for more details',
91+
'(str) => new GraphemeSplitter().splitGraphemes(str)',
92+
`String.split('')`,
93+
]
8794
]}
8895

8996
/>

src/components/TypeAnimation/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { TypeAnimationProps, Wrapper } from './index.types';
88
const DEFAULT_SPEED = 40;
99
const TypeAnimation = forwardRef<
1010
HTMLElementTagNameMap[Wrapper],
11-
TypeAnimationProps & HTMLAttributes<HTMLElementTagNameMap[Wrapper]>
11+
TypeAnimationProps
1212
>(
1313
(
1414
{
@@ -20,6 +20,7 @@ const TypeAnimation = forwardRef<
2020
omitDeletionAnimation = false,
2121
preRenderFirstString = false,
2222
wrapper = 'span',
23+
splitter = (text: string): ReadonlyArray<string> => [...text],
2324
cursor = true,
2425
style,
2526
...rest
@@ -86,6 +87,7 @@ const TypeAnimation = forwardRef<
8687

8788
type(
8889
typeRef.current,
90+
splitter,
8991
keyStrokeDelayTyping,
9092
keyStrokeDelayDeleting,
9193
omitDeletionAnimation,

src/components/TypeAnimation/index.types.ts

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,53 @@
1-
export interface TypeAnimationProps extends Props {
2-
ref?: React.Ref<HTMLElementTagNameMap[Wrapper]>;
3-
}
1+
import { HTMLAttributes } from 'react';
42

53
interface Props {
64
sequence: Sequence;
75
repeat?: number;
86
wrapper?: Wrapper;
97
cursor?: boolean;
8+
splitter?: StringSplitter;
109
speed?: Speed | GranularSpeed;
1110
deletionSpeed?: Speed | GranularSpeed;
1211
omitDeletionAnimation?: boolean;
1312
preRenderFirstString?: boolean;
1413
}
1514

15+
export interface TypeAnimationProps
16+
extends Props,
17+
Pick<
18+
HTMLAttributes<HTMLElementTagNameMap[Wrapper]>,
19+
'style' | 'aria-label' | 'aria-hidden' | 'role' | 'className'
20+
> {
21+
ref?: React.Ref<HTMLElementTagNameMap[Wrapper]>;
22+
}
23+
1624
export type GranularSpeed = {
1725
type: 'keyStrokeDelayInMs';
1826
value: number;
1927
};
2028

29+
export type StringSplitter = (text: string) => ReadonlyArray<string>;
30+
31+
export type Wrapper =
32+
| 'p'
33+
| 'div'
34+
| 'span'
35+
| 'strong'
36+
| 'a'
37+
| 'h1'
38+
| 'h2'
39+
| 'h3'
40+
| 'h4'
41+
| 'h5'
42+
| 'h6'
43+
| 'b';
44+
45+
export type Sequence = Array<SequenceElement>;
46+
export type SequenceElement =
47+
| string
48+
| number
49+
| ((element: HTMLElement | null) => void | Promise<void>);
50+
2151
export type Speed =
2252
| 1
2353
| 2
@@ -118,23 +148,3 @@ export type Speed =
118148
| 97
119149
| 98
120150
| 99;
121-
122-
export type Wrapper =
123-
| 'p'
124-
| 'div'
125-
| 'span'
126-
| 'strong'
127-
| 'a'
128-
| 'h1'
129-
| 'h2'
130-
| 'h3'
131-
| 'h4'
132-
| 'h5'
133-
| 'h6'
134-
| 'b';
135-
136-
export type Sequence = Array<SequenceElement>;
137-
export type SequenceElement =
138-
| string
139-
| number
140-
| ((element: HTMLElement | null) => void | Promise<void>);

src/typical.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import type { SequenceElement } from './components/TypeAnimation/index.types';
1+
import type { SequenceElement, StringSplitter } from './components/TypeAnimation/index.types';
22
import { Wrapper } from './components/TypeAnimation/index.types';
33

44
const OP_CODE_DELETION = 'DELETE';
55
const OP_CODE_WRITING = 'WRITING';
66

77
export async function type(
88
node: HTMLElementTagNameMap[Wrapper],
9+
splitter: StringSplitter,
910
speed: number,
1011
deletionSpeed: number,
1112
omitDeletionAnimation: boolean,
@@ -14,14 +15,14 @@ export async function type(
1415
for (const arg of args) {
1516
switch (typeof arg) {
1617
case 'string':
17-
await edit(node, arg, speed, deletionSpeed, omitDeletionAnimation);
18+
await edit(node, splitter, arg, speed, deletionSpeed, omitDeletionAnimation);
1819
break;
1920
case 'number':
2021
await wait(arg);
2122
break;
2223
case 'function':
2324
// when typeloop is passed from the TypeAnimation component, this causes an infinite, recursive call-loop here
24-
await arg(node, speed, deletionSpeed, omitDeletionAnimation, ...args);
25+
await arg(node, splitter, speed, deletionSpeed, omitDeletionAnimation, ...args);
2526
break;
2627
default:
2728
await arg;
@@ -31,6 +32,7 @@ export async function type(
3132

3233
async function edit(
3334
node: HTMLElementTagNameMap[Wrapper],
35+
splitter: StringSplitter,
3436
text: string,
3537
speed: number,
3638
deletionSpeed: number,
@@ -41,7 +43,7 @@ async function edit(
4143
const overlap = getOverlap(nodeContent, text);
4244
await perform(
4345
node,
44-
[...deleter(nodeContent, overlap), ...writer(text, overlap)],
46+
[...deleter(nodeContent, splitter, overlap), ...writer(text, splitter, overlap)],
4547
speed,
4648
deletionSpeed,
4749
omitDeletionAnimation
@@ -98,17 +100,17 @@ function* editor(edits: ReadonlyArray<string>) {
98100
}
99101
}
100102

101-
function* writer(text: string, startIndex = 0) {
102-
const splitText = [...text];
103+
function* writer(text: string, splitter: StringSplitter, startIndex = 0) {
104+
const splitText = splitter(text);
103105
const endIndex = splitText.length;
104106

105107
while (startIndex < endIndex) {
106108
yield splitText.slice(0, ++startIndex).join('');
107109
}
108110
}
109111

110-
function* deleter(text: string, startIndex = 0) {
111-
const splitText = [...text];
112+
function* deleter(text: string, splitter: StringSplitter, startIndex = 0) {
113+
const splitText = splitter(text);
112114
let endIndex = splitText.length;
113115

114116
while (endIndex > startIndex) {

0 commit comments

Comments
 (0)