heartraite 1.0.18 → 1.0.19

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.
@@ -0,0 +1 @@
1
+ export * from "./useTypewriter";
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./useTypewriter"), exports);
@@ -0,0 +1,12 @@
1
+ interface UseTypewriterOptions {
2
+ text: string;
3
+ typingSpeed?: number;
4
+ initialDelay?: number;
5
+ blinkingSpeed?: number;
6
+ }
7
+ export declare const useTypewriter: ({ text, typingSpeed, initialDelay, blinkingSpeed, }: UseTypewriterOptions) => {
8
+ displayedText: string;
9
+ showDot: boolean;
10
+ isComplete: boolean;
11
+ };
12
+ export {};
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useTypewriter = void 0;
4
+ const react_1 = require("react");
5
+ const useTypewriter = ({ text, typingSpeed = 50, initialDelay = 1000, blinkingSpeed = 500, }) => {
6
+ const [isComplete, setIsComplete] = (0, react_1.useState)(false);
7
+ const [displayedText, setDisplayedText] = (0, react_1.useState)("");
8
+ const [showDot, setShowDot] = (0, react_1.useState)(false);
9
+ (0, react_1.useEffect)(() => {
10
+ if (!text || text.length === 0) {
11
+ setDisplayedText(""); // No text to display
12
+ setIsComplete(true); // Mark typing as complete immediately
13
+ setShowDot(false); // No blinking dot for empty text
14
+ return; // Exit early if text is empty
15
+ }
16
+ let blinkingInterval = null;
17
+ let typingTimeout = null;
18
+ // Initialize state
19
+ setDisplayedText(""); // Clear the displayed text
20
+ setShowDot(true); // Start with a blinking dot
21
+ setIsComplete(false); // Reset completion flag
22
+ // Start blinking dot effect
23
+ blinkingInterval = setInterval(() => {
24
+ setShowDot((prev) => !prev);
25
+ }, blinkingSpeed);
26
+ // Start typing after the initial delay
27
+ typingTimeout = setTimeout(() => {
28
+ if (blinkingInterval) {
29
+ clearInterval(blinkingInterval); // Stop blinking before typing
30
+ setShowDot(false); // Hide blinking dot
31
+ }
32
+ // Typing effect
33
+ let currentIndex = 0;
34
+ const typeNextChar = () => {
35
+ if (currentIndex < text.length) {
36
+ const charToAdd = text[currentIndex];
37
+ setDisplayedText((prev) => prev + charToAdd); // Append character
38
+ currentIndex++;
39
+ setTimeout(typeNextChar, typingSpeed); // Schedule next character
40
+ }
41
+ else {
42
+ setIsComplete(true); // Mark as complete
43
+ }
44
+ };
45
+ // Trigger the first character manually
46
+ setDisplayedText(text.charAt(0));
47
+ currentIndex = 1;
48
+ // Schedule the rest of the characters
49
+ setTimeout(typeNextChar, typingSpeed);
50
+ }, initialDelay);
51
+ // Cleanup on unmount or dependencies change
52
+ return () => {
53
+ if (blinkingInterval)
54
+ clearInterval(blinkingInterval);
55
+ if (typingTimeout)
56
+ clearTimeout(typingTimeout);
57
+ };
58
+ }, [text, typingSpeed, initialDelay, blinkingSpeed]);
59
+ return { displayedText, showDot, isComplete };
60
+ };
61
+ exports.useTypewriter = useTypewriter;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const react_1 = require("@testing-library/react");
4
+ const useTypewriter_1 = require("./useTypewriter");
5
+ jest.useFakeTimers();
6
+ describe("useTypewriter", () => {
7
+ const sampleText = "Hello, world!";
8
+ const defaultOptions = {
9
+ text: sampleText,
10
+ typingSpeed: 50,
11
+ initialDelay: 500,
12
+ blinkingSpeed: 300,
13
+ };
14
+ it("should start with an empty displayedText", () => {
15
+ const { result } = (0, react_1.renderHook)(() => (0, useTypewriter_1.useTypewriter)(defaultOptions));
16
+ expect(result.current.displayedText).toBe("");
17
+ expect(result.current.isComplete).toBe(false);
18
+ expect(result.current.showDot).toBe(true);
19
+ });
20
+ it("should handle empty text input gracefully", () => {
21
+ const { result } = (0, react_1.renderHook)(() => (0, useTypewriter_1.useTypewriter)({ ...defaultOptions, text: "" }));
22
+ // Assert initial state for empty text input
23
+ expect(result.current.displayedText).toBe(""); // Should be an empty string
24
+ expect(result.current.isComplete).toBe(true); // Typing is complete immediately
25
+ expect(result.current.showDot).toBe(false); // No blinking dot
26
+ });
27
+ it("should display the correct text after typing is complete", async () => {
28
+ const text = "Du beskriver dig själv som en person som värderar kroppstyper, engagemang, kompetens i livet.";
29
+ const { result } = (0, react_1.renderHook)(() => (0, useTypewriter_1.useTypewriter)({
30
+ ...defaultOptions,
31
+ text,
32
+ }));
33
+ // Simulate the initial delay (before typing starts)
34
+ (0, react_1.act)(() => {
35
+ jest.advanceTimersByTime(defaultOptions.initialDelay);
36
+ });
37
+ // Simulate the full typing duration (based on the text length and typing speed)
38
+ const totalTypingTime = text.length * defaultOptions.typingSpeed;
39
+ (0, react_1.act)(() => {
40
+ jest.advanceTimersByTime(totalTypingTime);
41
+ });
42
+ // Assert that after typing is complete, the correct text is displayed
43
+ expect(result.current.displayedText).toBe(text);
44
+ expect(result.current.isComplete).toBe(true);
45
+ expect(result.current.showDot).toBe(false); // No more blinking dot
46
+ });
47
+ it("should respect typing speed and initial delay", () => {
48
+ const customOptions = {
49
+ ...defaultOptions,
50
+ typingSpeed: 100,
51
+ initialDelay: 1000,
52
+ };
53
+ const { result } = (0, react_1.renderHook)(() => (0, useTypewriter_1.useTypewriter)(customOptions));
54
+ // Before initial delay
55
+ (0, react_1.act)(() => {
56
+ jest.advanceTimersByTime(900);
57
+ });
58
+ expect(result.current.displayedText).toBe("");
59
+ expect(result.current.isComplete).toBe(false);
60
+ // After initial delay
61
+ (0, react_1.act)(() => {
62
+ jest.advanceTimersByTime(100);
63
+ });
64
+ expect(result.current.displayedText).toBe(sampleText.charAt(0));
65
+ // Verify typing speed
66
+ (0, react_1.act)(() => {
67
+ jest.advanceTimersByTime(customOptions.typingSpeed);
68
+ });
69
+ expect(result.current.displayedText).toBe(sampleText.slice(0, 2));
70
+ });
71
+ });
package/dist/index.d.ts CHANGED
@@ -2,3 +2,4 @@ export * from "./constants";
2
2
  export * from "./enum";
3
3
  export * from "./firebase";
4
4
  export * from "./types";
5
+ export * from "./hooks";
package/dist/index.js CHANGED
@@ -18,3 +18,4 @@ __exportStar(require("./constants"), exports);
18
18
  __exportStar(require("./enum"), exports);
19
19
  __exportStar(require("./firebase"), exports);
20
20
  __exportStar(require("./types"), exports);
21
+ __exportStar(require("./hooks"), exports);
package/jest.config.js ADDED
@@ -0,0 +1,9 @@
1
+ module.exports = {
2
+ preset: "ts-jest", // Use ts-jest preset to handle TypeScript
3
+ testEnvironment: "jest-environment-jsdom", // Use jsdom for React tests
4
+ moduleFileExtensions: ["js", "jsx", "ts", "tsx"], // Support for js, jsx, ts, tsx files
5
+ testMatch: [
6
+ "**/*.test.[jt]s?(x)", // Match any .test.js, .test.ts, .test.jsx, .test.tsx files
7
+ ],
8
+ collectCoverageFrom: ["src/**/*.{js,jsx,ts,tsx}", "!src/**/*.d.ts"], // Collect coverage for all files except d.ts
9
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "heartraite",
3
- "version": "1.0.18",
3
+ "version": "1.0.19",
4
4
  "description": "Heartraite npm package for common functionality",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -20,12 +20,24 @@
20
20
  "url": "https://github.com/agottfredsson/heartraite-npm/issues"
21
21
  },
22
22
  "homepage": "https://github.com/agottfredsson/heartraite-npm#readme",
23
+ "dependencies": {
24
+ "react": "^19.0.0",
25
+ "react-dom": "^19.0.0"
26
+ },
23
27
  "peerDependencies": {
24
28
  "firebase": "^11.0.1"
25
29
  },
26
30
  "devDependencies": {
31
+ "@testing-library/dom": "^10.4.0",
32
+ "@testing-library/react": "^16.1.0",
33
+ "@types/jest": "^29.5.14",
27
34
  "@types/node": "^22.9.0",
28
- "typescript": "^5.6.3",
29
- "firebase": "^11.0.1"
35
+ "@types/react": "^19.0.2",
36
+ "@types/react-dom": "^19.0.2",
37
+ "firebase": "^11.0.1",
38
+ "jest": "^29.7.0",
39
+ "jest-environment-jsdom": "^29.7.0",
40
+ "ts-jest": "^29.2.5",
41
+ "typescript": "^5.6.3"
30
42
  }
31
43
  }
@@ -0,0 +1 @@
1
+ export * from "./useTypewriter";
@@ -0,0 +1,86 @@
1
+ import { renderHook, act } from "@testing-library/react";
2
+ import { useTypewriter } from "./useTypewriter";
3
+
4
+ jest.useFakeTimers();
5
+
6
+ describe("useTypewriter", () => {
7
+ const sampleText = "Hello, world!";
8
+ const defaultOptions = {
9
+ text: sampleText,
10
+ typingSpeed: 50,
11
+ initialDelay: 500,
12
+ blinkingSpeed: 300,
13
+ };
14
+
15
+ it("should start with an empty displayedText", () => {
16
+ const { result } = renderHook(() => useTypewriter(defaultOptions));
17
+ expect(result.current.displayedText).toBe("");
18
+ expect(result.current.isComplete).toBe(false);
19
+ expect(result.current.showDot).toBe(true);
20
+ });
21
+ it("should handle empty text input gracefully", () => {
22
+ const { result } = renderHook(() =>
23
+ useTypewriter({ ...defaultOptions, text: "" })
24
+ );
25
+
26
+ // Assert initial state for empty text input
27
+ expect(result.current.displayedText).toBe(""); // Should be an empty string
28
+ expect(result.current.isComplete).toBe(true); // Typing is complete immediately
29
+ expect(result.current.showDot).toBe(false); // No blinking dot
30
+ });
31
+
32
+ it("should display the correct text after typing is complete", async () => {
33
+ const text =
34
+ "Du beskriver dig själv som en person som värderar kroppstyper, engagemang, kompetens i livet.";
35
+
36
+ const { result } = renderHook(() =>
37
+ useTypewriter({
38
+ ...defaultOptions,
39
+ text,
40
+ })
41
+ );
42
+
43
+ // Simulate the initial delay (before typing starts)
44
+ act(() => {
45
+ jest.advanceTimersByTime(defaultOptions.initialDelay);
46
+ });
47
+
48
+ // Simulate the full typing duration (based on the text length and typing speed)
49
+ const totalTypingTime = text.length * defaultOptions.typingSpeed;
50
+ act(() => {
51
+ jest.advanceTimersByTime(totalTypingTime);
52
+ });
53
+
54
+ // Assert that after typing is complete, the correct text is displayed
55
+ expect(result.current.displayedText).toBe(text);
56
+ expect(result.current.isComplete).toBe(true);
57
+ expect(result.current.showDot).toBe(false); // No more blinking dot
58
+ });
59
+ it("should respect typing speed and initial delay", () => {
60
+ const customOptions = {
61
+ ...defaultOptions,
62
+ typingSpeed: 100,
63
+ initialDelay: 1000,
64
+ };
65
+ const { result } = renderHook(() => useTypewriter(customOptions));
66
+
67
+ // Before initial delay
68
+ act(() => {
69
+ jest.advanceTimersByTime(900);
70
+ });
71
+ expect(result.current.displayedText).toBe("");
72
+ expect(result.current.isComplete).toBe(false);
73
+
74
+ // After initial delay
75
+ act(() => {
76
+ jest.advanceTimersByTime(100);
77
+ });
78
+ expect(result.current.displayedText).toBe(sampleText.charAt(0));
79
+
80
+ // Verify typing speed
81
+ act(() => {
82
+ jest.advanceTimersByTime(customOptions.typingSpeed);
83
+ });
84
+ expect(result.current.displayedText).toBe(sampleText.slice(0, 2));
85
+ });
86
+ });
@@ -0,0 +1,78 @@
1
+ import { useEffect, useState } from "react";
2
+
3
+ interface UseTypewriterOptions {
4
+ text: string;
5
+ typingSpeed?: number; // Time in ms between each character
6
+ initialDelay?: number; // Time in ms before typing starts
7
+ blinkingSpeed?: number; // Time in ms for the blinking dot toggle
8
+ }
9
+
10
+ export const useTypewriter = ({
11
+ text,
12
+ typingSpeed = 50,
13
+ initialDelay = 1000,
14
+ blinkingSpeed = 500,
15
+ }: UseTypewriterOptions) => {
16
+ const [isComplete, setIsComplete] = useState(false);
17
+ const [displayedText, setDisplayedText] = useState("");
18
+ const [showDot, setShowDot] = useState(false);
19
+
20
+ useEffect(() => {
21
+ if (!text || text.length === 0) {
22
+ setDisplayedText(""); // No text to display
23
+ setIsComplete(true); // Mark typing as complete immediately
24
+ setShowDot(false); // No blinking dot for empty text
25
+ return; // Exit early if text is empty
26
+ }
27
+
28
+ let blinkingInterval: NodeJS.Timeout | null = null;
29
+ let typingTimeout: NodeJS.Timeout | null = null;
30
+
31
+ // Initialize state
32
+ setDisplayedText(""); // Clear the displayed text
33
+ setShowDot(true); // Start with a blinking dot
34
+ setIsComplete(false); // Reset completion flag
35
+
36
+ // Start blinking dot effect
37
+ blinkingInterval = setInterval(() => {
38
+ setShowDot((prev) => !prev);
39
+ }, blinkingSpeed);
40
+
41
+ // Start typing after the initial delay
42
+ typingTimeout = setTimeout(() => {
43
+ if (blinkingInterval) {
44
+ clearInterval(blinkingInterval); // Stop blinking before typing
45
+ setShowDot(false); // Hide blinking dot
46
+ }
47
+
48
+ // Typing effect
49
+ let currentIndex = 0;
50
+
51
+ const typeNextChar = () => {
52
+ if (currentIndex < text.length) {
53
+ const charToAdd = text[currentIndex];
54
+ setDisplayedText((prev) => prev + charToAdd); // Append character
55
+ currentIndex++;
56
+ setTimeout(typeNextChar, typingSpeed); // Schedule next character
57
+ } else {
58
+ setIsComplete(true); // Mark as complete
59
+ }
60
+ };
61
+
62
+ // Trigger the first character manually
63
+ setDisplayedText(text.charAt(0));
64
+ currentIndex = 1;
65
+
66
+ // Schedule the rest of the characters
67
+ setTimeout(typeNextChar, typingSpeed);
68
+ }, initialDelay);
69
+
70
+ // Cleanup on unmount or dependencies change
71
+ return () => {
72
+ if (blinkingInterval) clearInterval(blinkingInterval);
73
+ if (typingTimeout) clearTimeout(typingTimeout);
74
+ };
75
+ }, [text, typingSpeed, initialDelay, blinkingSpeed]);
76
+
77
+ return { displayedText, showDot, isComplete };
78
+ };
package/src/index.ts CHANGED
@@ -2,3 +2,4 @@ export * from "./constants";
2
2
  export * from "./enum";
3
3
  export * from "./firebase";
4
4
  export * from "./types";
5
+ export * from "./hooks";