@matteodicristofalo/text-animations 1.1.2 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -42,22 +42,27 @@ export default function RootLayout({
42
42
 
43
43
  Once imported the CSS you can use the React components.
44
44
 
45
- ## SplitTextReveal
45
+ ## textReveal
46
46
 
47
47
  ```js
48
48
  "use client";
49
49
 
50
- import { SplitTextReveal } from "@matteodicristofalo/text-animations";
50
+ import { textReveal } from "@matteodicristofalo/text-animations";
51
51
 
52
52
  export function MyComponent() {
53
53
  return (
54
- <h1>
55
- <SplitTextReveal text="Hello World" />
56
- </h1>
54
+ <>
55
+ <textReveal.h1 text="Hello World" />
56
+ <textReveal.h2 text="Hello World" />
57
+ <textReveal.p text="Hello World" />
58
+ <textReveal.span text="Hello World" />
59
+ </>
57
60
  );
58
61
  }
59
62
  ```
60
63
 
64
+ If you're using **Next.JS** make sure the component importing `<textReveal>` is marked as **Client Component** via the `"use client"` directive otherwise it won't compile.
65
+
61
66
  You can decide how to split the text between one of this options:
62
67
 
63
68
  - char (default)
@@ -65,7 +70,7 @@ You can decide how to split the text between one of this options:
65
70
  - sentence
66
71
 
67
72
  ```js
68
- <SplitTextReveal text="Hello World" splitType="word" />
73
+ <textReveal.h1 text="Hello World" splitType="word" />
69
74
  ```
70
75
 
71
76
  You can also configure the reveal animation by specifying:
@@ -79,10 +84,10 @@ You can also configure the reveal animation by specifying:
79
84
  "use client";
80
85
 
81
86
  import { useMemo } from "react";
82
- import { SplitTextReveal } from "@matteodicristofalo/text-animations";
87
+ import { textReveal } from "@matteodicristofalo/text-animations";
83
88
 
84
89
  export function MyComponent() {
85
- const revealOptions = useMemo(
90
+ const animation = useMemo(
86
91
  () => ({
87
92
  duration: 1,
88
93
  stagger: 0.5,
@@ -92,40 +97,41 @@ export function MyComponent() {
92
97
  []
93
98
  );
94
99
 
95
- return (
96
- <h1>
97
- <SplitTextReveal text="Hello World" revealOptions={revealOptions} />
98
- </h1>
99
- );
100
+ return <textReveal.h1 text="Hello World" animation={animation} />;
100
101
  }
101
102
  ```
102
103
 
103
- ## SplitTextRotate
104
+ ## textRotate
104
105
 
105
106
  ```js
106
107
  "use client";
107
108
 
108
- import { SplitTextRotate } from "@matteodicristofalo/text-animations";
109
+ import { textRotate } from "@matteodicristofalo/text-animations";
109
110
 
110
111
  export function MyComponent() {
111
112
  return (
112
- <h1>
113
- <SplitTextRotate text="Hello World" />
114
- </h1>
113
+ <>
114
+ <textRotate.h1 text="Hello World" />
115
+ <textRotate.h1 text="Hello World" />
116
+ <textRotate.p text="Hello World" />
117
+ <textRotate.span text="Hello World" />
118
+ </>
115
119
  );
116
120
  }
117
121
  ```
118
122
 
123
+ As well as `<textReveal>`, If you're using **Next.JS** make sure the component importing `<textRotate>` is marked as **Client Component** via the `"use client"`.
124
+
119
125
  This component split always by char, the only thing you can configure is the rotate animation
120
126
 
121
127
  ```js
122
128
  "use client";
123
129
 
124
130
  import { useMemo } from "react";
125
- import { SplitTextRotate } from "@matteodicristofalo/text-animations";
131
+ import { textRotate } from "@matteodicristofalo/text-animations";
126
132
 
127
133
  export function MyComponent() {
128
- const rotateOptions = useMemo(
134
+ const animation = useMemo(
129
135
  () => ({
130
136
  duration: 1,
131
137
  stagger: 0.5,
@@ -133,10 +139,6 @@ export function MyComponent() {
133
139
  []
134
140
  );
135
141
 
136
- return (
137
- <h1>
138
- <SplitTextRotate text="Hello World" rotateOptions={rotateOptions} />
139
- </h1>
140
- );
142
+ return <textRotate.h1 text="Hello World" animation={animation} />;
141
143
  }
142
144
  ```
@@ -1,2 +1,2 @@
1
- export { SplitTextReveal } from "./split-text-reveal/split-text-reveal";
2
- export { SplitTextRotate } from "./split-text-rotate/split-text-rotate";
1
+ export * from "./text-reveal/text-reveal";
2
+ export * from "./text-rotate/text-rotate";
@@ -0,0 +1,24 @@
1
+ import React from "react";
2
+ import { WithTag } from "@utils/types";
3
+ type TextRevealProps = WithTag<{
4
+ text: string;
5
+ splitType?: "sentence" | "word" | "char";
6
+ animation?: Partial<{
7
+ duration: number;
8
+ delay: number;
9
+ stagger: number;
10
+ threshold: number;
11
+ once: boolean;
12
+ }>;
13
+ }>;
14
+ declare const textReveal: {
15
+ h1: React.FC<import("@utils/types").WithoutTag<TextRevealProps>>;
16
+ h2: React.FC<import("@utils/types").WithoutTag<TextRevealProps>>;
17
+ h3: React.FC<import("@utils/types").WithoutTag<TextRevealProps>>;
18
+ h4: React.FC<import("@utils/types").WithoutTag<TextRevealProps>>;
19
+ h5: React.FC<import("@utils/types").WithoutTag<TextRevealProps>>;
20
+ h6: React.FC<import("@utils/types").WithoutTag<TextRevealProps>>;
21
+ p: React.FC<import("@utils/types").WithoutTag<TextRevealProps>>;
22
+ span: React.FC<import("@utils/types").WithoutTag<TextRevealProps>>;
23
+ };
24
+ export { textReveal };
@@ -1,9 +1,20 @@
1
1
  import React from "react";
2
2
  import type { StoryObj } from "@storybook/react";
3
- import { SplitTextReveal } from "./split-text-reveal";
4
3
  declare const meta: {
5
4
  title: string;
6
- component: typeof SplitTextReveal;
5
+ component: React.FC<import("../../utils/types").WithoutTag<{
6
+ text: string;
7
+ splitType?: "sentence" | "word" | "char";
8
+ animation?: Partial<{
9
+ duration: number;
10
+ delay: number;
11
+ stagger: number;
12
+ threshold: number;
13
+ once: boolean;
14
+ }>;
15
+ } & {
16
+ tag: import("../../utils/jsx").HtmlTextElementTag;
17
+ }>>;
7
18
  argTypes: {
8
19
  text: {
9
20
  control: "text";
@@ -12,7 +23,7 @@ declare const meta: {
12
23
  control: "radio";
13
24
  options: string[];
14
25
  };
15
- revealOptions: {
26
+ animation: {
16
27
  control: {
17
28
  type: "object";
18
29
  };
@@ -22,7 +33,6 @@ declare const meta: {
22
33
  text: string;
23
34
  splitType: "char";
24
35
  };
25
- render: (args: import("./split-test-reveal.types").SplitTextRevealProps) => React.JSX.Element;
26
36
  };
27
37
  export default meta;
28
38
  type Story = StoryObj<typeof meta>;
@@ -33,4 +43,4 @@ export declare const Duration: Story;
33
43
  export declare const Delay: Story;
34
44
  export declare const Stagger: Story;
35
45
  export declare const Threshold: Story;
36
- export declare const NotOnce: Story;
46
+ export declare const MultipleTimes: Story;
@@ -0,0 +1,20 @@
1
+ import React from "react";
2
+ import { WithTag } from "@utils/types";
3
+ type TextRotateProps = WithTag<{
4
+ text: string;
5
+ animation?: Partial<{
6
+ duration: number;
7
+ stagger: number;
8
+ }>;
9
+ }>;
10
+ declare const textRotate: {
11
+ h1: React.FC<import("@utils/types").WithoutTag<TextRotateProps>>;
12
+ h2: React.FC<import("@utils/types").WithoutTag<TextRotateProps>>;
13
+ h3: React.FC<import("@utils/types").WithoutTag<TextRotateProps>>;
14
+ h4: React.FC<import("@utils/types").WithoutTag<TextRotateProps>>;
15
+ h5: React.FC<import("@utils/types").WithoutTag<TextRotateProps>>;
16
+ h6: React.FC<import("@utils/types").WithoutTag<TextRotateProps>>;
17
+ p: React.FC<import("@utils/types").WithoutTag<TextRotateProps>>;
18
+ span: React.FC<import("@utils/types").WithoutTag<TextRotateProps>>;
19
+ };
20
+ export { textRotate };
@@ -1,14 +1,20 @@
1
- import React from "react";
2
1
  import type { StoryObj } from "@storybook/react";
3
- import { SplitTextRotate } from "./split-text-rotate";
4
2
  declare const meta: {
5
3
  title: string;
6
- component: typeof SplitTextRotate;
4
+ component: import("react").FC<import("../../utils/types").WithoutTag<{
5
+ text: string;
6
+ animation?: Partial<{
7
+ duration: number;
8
+ stagger: number;
9
+ }>;
10
+ } & {
11
+ tag: import("../../utils/jsx").HtmlTextElementTag;
12
+ }>>;
7
13
  argTypes: {
8
14
  text: {
9
15
  control: "text";
10
16
  };
11
- rotateOptions: {
17
+ animation: {
12
18
  control: {
13
19
  type: "object";
14
20
  };
@@ -17,7 +23,6 @@ declare const meta: {
17
23
  args: {
18
24
  text: string;
19
25
  };
20
- render: (args: import("./split-text-rotate.types").SplitTextRotateProps) => React.JSX.Element;
21
26
  };
22
27
  export default meta;
23
28
  type Story = StoryObj<typeof meta>;
@@ -1 +1,2 @@
1
- export { useIntersectionObserver } from "./intersection-overser/intersection-observer";
1
+ export { useIntersectionObserver } from "./use-intersection-observer";
2
+ export { useIsHydrated } from "./use-is-hydrated";
@@ -0,0 +1 @@
1
+ export declare function useIsHydrated(): boolean;
package/dist/index.css CHANGED
@@ -1,60 +1,46 @@
1
- .split-text-reveal-module_text__-9O5o {
1
+ .text-reveal-module_text__YrhhT {
2
2
  position: relative;
3
3
  }
4
- .split-text-reveal-module_text__-9O5o .split-text-reveal-module_is-hidden__2J8-i {
5
- opacity: 0;
6
- }
7
- .split-text-reveal-module_text__-9O5o .split-text-reveal-module_sr-only__luZuY {
8
- height: 0;
9
- overflow: hidden;
10
- position: absolute;
11
- width: 0;
12
- }
13
- .split-text-reveal-module_text__-9O5o .split-text-reveal-module_container__mkD-P {
14
- display: inline-flex;
4
+ .text-reveal-module_text__YrhhT .text-reveal-module_container__HD1Pu {
5
+ display: inline-block;
15
6
  overflow: hidden;
16
7
  }
17
- .split-text-reveal-module_text__-9O5o .split-text-reveal-module_container__mkD-P span {
18
- min-width: 0.25em;
8
+ .text-reveal-module_text__YrhhT .text-reveal-module_container__HD1Pu span {
9
+ display: inline-block;
19
10
  transform: translateY(100%);
20
11
  transition-delay: calc(var(--var-delay) + var(--var-stagger));
21
12
  transition-duration: var(--var-duration);
22
13
  transition-property: transform;
23
14
  transition-timing-function: ease-out;
15
+ white-space: pre;
24
16
  will-change: transform;
25
17
  }
26
- .split-text-reveal-module_text__-9O5o.split-text-reveal-module_animate__y6o78 .split-text-reveal-module_container__mkD-P span {
18
+ .text-reveal-module_text__YrhhT.text-reveal-module_animate__kbOPX .text-reveal-module_container__HD1Pu span {
27
19
  transform: translateY(0);
28
20
  }
29
- .split-text-rotate-module_text__xzran {
21
+ .text-rotate-module_text__WKSY- {
30
22
  display: block;
31
23
  overflow: hidden;
32
24
  position: relative;
33
25
  }
34
- .split-text-rotate-module_text__xzran .split-text-rotate-module_sr-only__Dbb9O {
35
- height: 0;
36
- overflow: hidden;
37
- position: absolute;
38
- width: 0;
39
- }
40
- .split-text-rotate-module_text__xzran .split-text-rotate-module_container__EAJkE {
41
- display: flex;
26
+ .text-rotate-module_text__WKSY- .text-rotate-module_container__2qsrm {
27
+ display: block;
42
28
  }
43
- .split-text-rotate-module_text__xzran .split-text-rotate-module_container__EAJkE:nth-of-type(2) {
29
+ .text-rotate-module_text__WKSY- .text-rotate-module_container__2qsrm:nth-of-type(2) {
44
30
  position: absolute;
45
31
  top: 0;
46
32
  transform: translateY(100%);
47
33
  }
48
- .split-text-rotate-module_text__xzran .split-text-rotate-module_container__EAJkE .split-text-rotate-module_char__PYJ-k {
34
+ .text-rotate-module_text__WKSY- .text-rotate-module_container__2qsrm .text-rotate-module_char__LG3ps {
49
35
  display: inline-block;
50
- min-width: 0.25em;
51
36
  transition-delay: var(--var-delay-out);
52
37
  transition-duration: var(--var-duration);
53
38
  transition-property: transform;
54
39
  transition-timing-function: ease-out;
40
+ white-space: pre;
55
41
  will-change: transform;
56
42
  }
57
- .split-text-rotate-module_text__xzran:hover .split-text-rotate-module_container__EAJkE .split-text-rotate-module_char__PYJ-k {
43
+ .text-rotate-module_text__WKSY-:hover .text-rotate-module_container__2qsrm .text-rotate-module_char__LG3ps {
58
44
  transform: translateY(-100%);
59
45
  transition-delay: var(--var-delay-in);
60
46
  }
package/dist/index.js CHANGED
@@ -1,27 +1,5 @@
1
- 'use client';
2
1
  import React, { useState, useEffect, useRef, useMemo, useCallback } from 'react';
3
2
 
4
- function round(num, digits) {
5
- return parseFloat(num.toFixed(digits));
6
- }
7
- function clamp(n, min, max) {
8
- return Math.min(Math.max(min, n), max);
9
- }
10
-
11
- function sentences(text) {
12
- return segment(text, "sentence");
13
- }
14
- function words(text) {
15
- return segment(text, "word");
16
- }
17
- function characters(text) {
18
- return segment(text, "grapheme");
19
- }
20
- function segment(text, granularity) {
21
- var segmenter = new Intl.Segmenter(undefined, { granularity: granularity });
22
- return Array.from(segmenter.segment(text), function (segment) { return segment.segment; });
23
- }
24
-
25
3
  /******************************************************************************
26
4
  Copyright (c) Microsoft Corporation.
27
5
 
@@ -55,6 +33,27 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
55
33
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
56
34
  };
57
35
 
36
+ function round(num, digits) {
37
+ return parseFloat(num.toFixed(digits));
38
+ }
39
+ function clamp(n, min, max) {
40
+ return Math.min(Math.max(min, n), max);
41
+ }
42
+
43
+ function sentences(text) {
44
+ return segment(text, "sentence");
45
+ }
46
+ function words(text) {
47
+ return segment(text, "word");
48
+ }
49
+ function characters(text) {
50
+ return segment(text, "grapheme");
51
+ }
52
+ function segment(text, granularity) {
53
+ var segmenter = new Intl.Segmenter(undefined, { granularity: granularity });
54
+ return Array.from(segmenter.segment(text), function (segment) { return segment.segment; });
55
+ }
56
+
58
57
  function useIntersectionObserver(ref, options) {
59
58
  var _a = useState(false), isIntersecting = _a[0], setIntersecting = _a[1];
60
59
  useEffect(function () {
@@ -78,34 +77,69 @@ function useIntersectionObserver(ref, options) {
78
77
  return isIntersecting;
79
78
  }
80
79
 
80
+ function useIsHydrated() {
81
+ var _a = useState(false), isHydrated = _a[0], setIsHydrated = _a[1];
82
+ useEffect(function () {
83
+ setIsHydrated(true);
84
+ }, []);
85
+ return isHydrated;
86
+ }
87
+
88
+ var htmlTextElementTags = [
89
+ "h1",
90
+ "h2",
91
+ "h3",
92
+ "h4",
93
+ "h5",
94
+ "h6",
95
+ "p",
96
+ "span",
97
+ ];
98
+ function generateJsxVariations(Component) {
99
+ return htmlTextElementTags.reduce(function (variations, tag) {
100
+ variations[tag] = function (props) { return (React.createElement(Component, __assign({}, props, { tag: tag }))); };
101
+ return variations;
102
+ }, {});
103
+ }
104
+
81
105
  function r(e){var t,f,n="";if("string"==typeof e||"number"==typeof e)n+=e;else if("object"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=" "),n+=f);}else for(f in e)e[f]&&(n&&(n+=" "),n+=f);return n}function clsx(){for(var e,t,f=0,n="",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=" "),n+=t);return n}
82
106
 
83
- var styles$1 = {"text":"split-text-reveal-module_text__-9O5o","is-hidden":"split-text-reveal-module_is-hidden__2J8-i","sr-only":"split-text-reveal-module_sr-only__luZuY","container":"split-text-reveal-module_container__mkD-P","animate":"split-text-reveal-module_animate__y6o78"};
107
+ var styles$1 = {"text":"text-reveal-module_text__YrhhT","container":"text-reveal-module_container__HD1Pu","animate":"text-reveal-module_animate__kbOPX"};
84
108
 
85
109
  var DEFAULT_DURATION$1 = 0.5;
86
110
  var DEFAULT_STAGGER$1 = 0.01;
87
111
  var MIN_THRESHOLD = 0;
88
112
  var MAX_THRESHOLD = 1;
89
113
  var DEFAULT_THRESHOLD = 0.25;
90
- function SplitTextReveal(_a) {
114
+ function TextReveal(props) {
115
+ var isHydrated = useIsHydrated();
116
+ if (isHydrated === false) {
117
+ return React.createElement(TextRevealServer, __assign({}, props));
118
+ }
119
+ return React.createElement(TextRevealClient, __assign({}, props));
120
+ }
121
+ function TextRevealServer(_a) {
122
+ var Tag = _a.tag, text = _a.text;
123
+ return React.createElement(Tag, { style: { opacity: 0 } }, text);
124
+ }
125
+ function TextRevealClient(_a) {
91
126
  var _b;
92
- var text = _a.text, _c = _a.splitType, splitType = _c === void 0 ? "char" : _c, revealOptions = _a.revealOptions;
127
+ var Tag = _a.tag, text = _a.text, _c = _a.splitType, splitType = _c === void 0 ? "char" : _c, animation = _a.animation;
93
128
  var ref = useRef(null);
94
- var _d = useState(false), hasBeenMounted = _d[0], setHasBeenMounted = _d[1];
95
- var memoizedOptions = useMemo(function () {
129
+ var intersectionObserverOptions = useMemo(function () {
96
130
  var _a;
97
- var once = (_a = revealOptions === null || revealOptions === void 0 ? void 0 : revealOptions.once) !== null && _a !== void 0 ? _a : true;
98
- var threshold = (revealOptions === null || revealOptions === void 0 ? void 0 : revealOptions.threshold)
99
- ? clamp(revealOptions.threshold, MIN_THRESHOLD, MAX_THRESHOLD)
131
+ var once = (_a = animation === null || animation === void 0 ? void 0 : animation.once) !== null && _a !== void 0 ? _a : true;
132
+ var threshold = (animation === null || animation === void 0 ? void 0 : animation.threshold)
133
+ ? clamp(animation.threshold, MIN_THRESHOLD, MAX_THRESHOLD)
100
134
  : DEFAULT_THRESHOLD;
101
135
  return {
102
136
  once: once,
103
137
  threshold: threshold,
104
138
  };
105
- }, [revealOptions]);
106
- var isInView = useIntersectionObserver(ref, memoizedOptions);
107
- var transitionDuration = (revealOptions === null || revealOptions === void 0 ? void 0 : revealOptions.duration) || DEFAULT_DURATION$1;
108
- var transitionStagger = (revealOptions === null || revealOptions === void 0 ? void 0 : revealOptions.stagger) || DEFAULT_STAGGER$1;
139
+ }, [animation]);
140
+ var isInView = useIntersectionObserver(ref, intersectionObserverOptions);
141
+ var duration = (animation === null || animation === void 0 ? void 0 : animation.duration) || DEFAULT_DURATION$1;
142
+ var stagger = (animation === null || animation === void 0 ? void 0 : animation.stagger) || DEFAULT_STAGGER$1;
109
143
  var splittedText = useMemo(function () {
110
144
  switch (splitType) {
111
145
  case "sentence":
@@ -116,56 +150,58 @@ function SplitTextReveal(_a) {
116
150
  return characters(text);
117
151
  }
118
152
  }, [splitType, text]);
119
- useEffect(function () {
120
- setHasBeenMounted(true);
121
- }, []);
122
- return (React.createElement("span", { ref: ref, className: clsx(styles$1["text"], (_b = {}, _b[styles$1["animate"]] = isInView, _b)), style: {
123
- "--var-duration": "".concat(transitionDuration, "s"),
124
- } }, hasBeenMounted ? (React.createElement(React.Fragment, null,
125
- React.createElement("span", { className: styles$1["sr-only"] }, text),
126
- splittedText.map(function (el, i) {
127
- var _a;
128
- return (React.createElement("span", { key: i, className: styles$1["container"], "aria-hidden": "true" },
129
- React.createElement("span", { "aria-hidden": "true", style: {
130
- "--var-delay": "".concat((_a = revealOptions === null || revealOptions === void 0 ? void 0 : revealOptions.delay) !== null && _a !== void 0 ? _a : 0, "s"),
131
- "--var-stagger": "".concat(round(transitionStagger * i, 3), "s"),
132
- } }, el)));
133
- }))) : (React.createElement("span", { className: styles$1["is-hidden"] }, text))));
153
+ return (React.createElement(Tag, { ref: ref, className: clsx(styles$1["text"], (_b = {}, _b[styles$1["animate"]] = isInView, _b)), style: {
154
+ "--var-duration": "".concat(duration, "s"),
155
+ }, "aria-label": text }, splittedText.map(function (el, i) {
156
+ var _a;
157
+ return (React.createElement("span", { key: i, className: styles$1["container"], "aria-hidden": "true" },
158
+ React.createElement("span", { "aria-hidden": "true", style: {
159
+ "--var-delay": "".concat((_a = animation === null || animation === void 0 ? void 0 : animation.delay) !== null && _a !== void 0 ? _a : 0, "s"),
160
+ "--var-stagger": "".concat(round(stagger * i, 3), "s"),
161
+ } }, el)));
162
+ })));
134
163
  }
164
+ var textReveal = generateJsxVariations(TextReveal);
135
165
 
136
- var styles = {"text":"split-text-rotate-module_text__xzran","sr-only":"split-text-rotate-module_sr-only__Dbb9O","container":"split-text-rotate-module_container__EAJkE","char":"split-text-rotate-module_char__PYJ-k"};
166
+ var styles = {"text":"text-rotate-module_text__WKSY-","container":"text-rotate-module_container__2qsrm","char":"text-rotate-module_char__LG3ps"};
137
167
 
168
+ var NUMBER_OF_COPIES = 2;
138
169
  var DEFAULT_DURATION = 0.25;
139
170
  var DEFAULT_STAGGER = 0.025;
140
- function SplitTextRotate(_a) {
141
- var text = _a.text, rotateOptions = _a.rotateOptions;
142
- var _b = useState(false), hasBeenMounted = _b[0], setHasBeenMounted = _b[1];
143
- var splittedText = useMemo(function () { return characters(text); }, [text]);
144
- var _c = useMemo(function () { return ({
145
- transitionDuration: (rotateOptions === null || rotateOptions === void 0 ? void 0 : rotateOptions.duration) || DEFAULT_DURATION,
146
- transitionStagger: (rotateOptions === null || rotateOptions === void 0 ? void 0 : rotateOptions.stagger) || DEFAULT_STAGGER,
147
- }); }, [rotateOptions]), transitionDuration = _c.transitionDuration, transitionStagger = _c.transitionStagger;
171
+ function TextRotate(props) {
172
+ var isHydrated = useIsHydrated();
173
+ if (isHydrated === false) {
174
+ return React.createElement(TextRotateServer, __assign({}, props));
175
+ }
176
+ return React.createElement(TextRotateClient, __assign({}, props));
177
+ }
178
+ function TextRotateServer(_a) {
179
+ var Tag = _a.tag, text = _a.text;
180
+ return React.createElement(Tag, null, text);
181
+ }
182
+ function TextRotateClient(_a) {
183
+ var Tag = _a.tag, text = _a.text, animation = _a.animation;
184
+ var duration = (animation === null || animation === void 0 ? void 0 : animation.duration) || DEFAULT_DURATION;
185
+ var stagger = (animation === null || animation === void 0 ? void 0 : animation.stagger) || DEFAULT_STAGGER;
186
+ var chars = useMemo(function () { return characters(text); }, [text]);
148
187
  var delays = useCallback(function (index) {
149
- var noElementsZeroBased = splittedText.length - 1;
188
+ var precision = 3;
189
+ var noElementsZeroBased = chars.length - 1;
150
190
  return {
151
- in: round(transitionStagger * index, 3),
152
- out: round(transitionStagger * (noElementsZeroBased - index), 3),
191
+ in: round(stagger * index, precision),
192
+ out: round(stagger * (noElementsZeroBased - index), precision),
153
193
  };
154
- }, [splittedText, transitionStagger]);
155
- useEffect(function () {
156
- setHasBeenMounted(true);
157
- }, []);
158
- return (React.createElement("span", { className: styles["text"], style: {
159
- "--var-duration": "".concat(transitionDuration, "s"),
160
- } }, hasBeenMounted ? (React.createElement(React.Fragment, null,
161
- React.createElement("span", { className: styles["sr-only"] }, text),
162
- Array.from({ length: 2 }).map(function (_, i) { return (React.createElement("span", { key: i, className: styles["container"], "aria-hidden": "true" }, splittedText.map(function (char, i) {
163
- var charDelays = delays(i);
164
- return (React.createElement("span", { key: i, className: styles["char"], "aria-hidden": "true", style: {
165
- "--var-delay-in": "".concat(charDelays.in, "s"),
166
- "--var-delay-out": "".concat(charDelays.out, "s"),
167
- } }, char));
168
- }))); }))) : (React.createElement("span", null, text))));
194
+ }, [chars, stagger]);
195
+ return (React.createElement(Tag, { className: styles["text"], style: {
196
+ "--var-duration": "".concat(duration, "s"),
197
+ }, "aria-label": text }, Array.from({ length: NUMBER_OF_COPIES }).map(function (_, i) { return (React.createElement("span", { key: i, className: styles["container"], "aria-hidden": "true" }, chars.map(function (char, i) {
198
+ var charDelays = delays(i);
199
+ return (React.createElement("span", { key: i, className: styles["char"], "aria-hidden": "true", style: {
200
+ "--var-delay-in": "".concat(charDelays.in, "s"),
201
+ "--var-delay-out": "".concat(charDelays.out, "s"),
202
+ } }, char));
203
+ }))); })));
169
204
  }
205
+ var textRotate = generateJsxVariations(TextRotate);
170
206
 
171
- export { SplitTextReveal, SplitTextRotate };
207
+ export { textReveal, textRotate };
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import { WithoutTag } from "./types";
3
+ declare const htmlTextElementTags: readonly ["h1", "h2", "h3", "h4", "h5", "h6", "p", "span"];
4
+ export type HtmlTextElementTag = (typeof htmlTextElementTags)[number];
5
+ export declare function generateJsxVariations<Props extends {
6
+ tag: HtmlTextElementTag;
7
+ }>(Component: React.FC<Props>): {
8
+ [k in HtmlTextElementTag]: React.FC<WithoutTag<Props>>;
9
+ };
10
+ export {};
@@ -0,0 +1,5 @@
1
+ import { HtmlTextElementTag } from "./jsx";
2
+ export type WithTag<T> = T & {
3
+ tag: HtmlTextElementTag;
4
+ };
5
+ export type WithoutTag<T> = Omit<T, "tag">;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@matteodicristofalo/text-animations",
3
- "version": "1.1.2",
4
- "description": "React library to animate text",
3
+ "version": "2.0.2",
4
+ "description": "Lightweight React component library to animate text",
5
5
  "scripts": {
6
6
  "build": "rimraf dist && rollup -c",
7
7
  "storybook": "storybook dev -p 6006"
@@ -27,7 +27,6 @@
27
27
  "rollup": "^4.28.0",
28
28
  "rollup-plugin-peer-deps-external": "^2.2.4",
29
29
  "rollup-plugin-postcss": "^4.0.2",
30
- "rollup-preserve-directives": "^1.1.3",
31
30
  "sass": "^1.81.0",
32
31
  "storybook": "^8.4.6",
33
32
  "tslib": "^2.8.1",
@@ -53,5 +52,19 @@
53
52
  "repository": {
54
53
  "type": "git",
55
54
  "url": "git@github.com:matteodicristofalo/text-animations.git"
56
- }
55
+ },
56
+ "keywords": [
57
+ "react",
58
+ "react-components",
59
+ "ui-library",
60
+ "animation",
61
+ "animated-text",
62
+ "text-animation",
63
+ "text-reveal",
64
+ "hover",
65
+ "hover-effects",
66
+ "scroll-animation",
67
+ "typography",
68
+ "microinteractions"
69
+ ]
57
70
  }
@@ -1,14 +0,0 @@
1
- export type SplitTextRevealProps = {
2
- text: string;
3
- splitType?: SplitType;
4
- revealOptions?: Partial<RevealOptions>;
5
- };
6
- type SplitType = "sentence" | "word" | "char";
7
- type RevealOptions = {
8
- duration: number;
9
- delay: number;
10
- stagger: number;
11
- threshold: number;
12
- once: boolean;
13
- };
14
- export {};
@@ -1,3 +0,0 @@
1
- import React from "react";
2
- import { SplitTextRevealProps } from "./split-test-reveal.types";
3
- export declare function SplitTextReveal({ text, splitType, revealOptions, }: SplitTextRevealProps): React.JSX.Element;
@@ -1,3 +0,0 @@
1
- import React from "react";
2
- import { SplitTextRotateProps } from "./split-text-rotate.types";
3
- export declare function SplitTextRotate({ text, rotateOptions }: SplitTextRotateProps): React.JSX.Element;
@@ -1,9 +0,0 @@
1
- export type SplitTextRotateProps = {
2
- text: string;
3
- rotateOptions?: Partial<RotateOptions>;
4
- };
5
- type RotateOptions = {
6
- duration: number;
7
- stagger: number;
8
- };
9
- export {};