fetta 1.0.0
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/LICENSE +21 -0
- package/README.md +267 -0
- package/dist/chunk-KGMU2B53.js +764 -0
- package/dist/index.d.ts +115 -0
- package/dist/index.js +1 -0
- package/dist/react.d.ts +121 -0
- package/dist/react.js +181 -0
- package/package.json +85 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom splitText implementation with built-in kerning compensation.
|
|
3
|
+
* Measures character positions before splitting, applies compensation,
|
|
4
|
+
* then detects lines based on actual rendered positions.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Configuration options for the splitText function.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const options: SplitTextOptions = {
|
|
12
|
+
* type: "chars,words,lines",
|
|
13
|
+
* charClass: "char",
|
|
14
|
+
* mask: "lines",
|
|
15
|
+
* autoSplit: true,
|
|
16
|
+
* };
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
interface SplitTextOptions {
|
|
20
|
+
/** Split type: chars, words, lines, or combinations like "chars,words" */
|
|
21
|
+
type?: "chars" | "words" | "lines" | "chars,words" | "words,lines" | "chars,lines" | "chars,words,lines";
|
|
22
|
+
charClass?: string;
|
|
23
|
+
wordClass?: string;
|
|
24
|
+
lineClass?: string;
|
|
25
|
+
/** Apply overflow mask wrapper to elements for reveal animations */
|
|
26
|
+
mask?: "lines" | "words" | "chars";
|
|
27
|
+
/** Auto-split on resize (observes parent element) */
|
|
28
|
+
autoSplit?: boolean;
|
|
29
|
+
/** Callback when resize triggers re-split (does not re-trigger initial animations) */
|
|
30
|
+
onResize?: (result: Omit<SplitTextResult, "revert" | "dispose">) => void;
|
|
31
|
+
/** Callback fired after text is split, receives split elements. Return animation for revertOnComplete. */
|
|
32
|
+
onSplit?: (result: {
|
|
33
|
+
chars: HTMLSpanElement[];
|
|
34
|
+
words: HTMLSpanElement[];
|
|
35
|
+
lines: HTMLSpanElement[];
|
|
36
|
+
}) => void | {
|
|
37
|
+
finished: Promise<unknown>;
|
|
38
|
+
} | Array<{
|
|
39
|
+
finished: Promise<unknown>;
|
|
40
|
+
}> | Promise<unknown>;
|
|
41
|
+
/** Auto-revert when onSplit animation completes */
|
|
42
|
+
revertOnComplete?: boolean;
|
|
43
|
+
/** Add CSS custom properties (--char-index, --word-index, --line-index) */
|
|
44
|
+
propIndex?: boolean;
|
|
45
|
+
/** Add will-change: transform, opacity to split elements for better animation performance */
|
|
46
|
+
willChange?: boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Result returned by splitText containing arrays of split elements and a revert function.
|
|
50
|
+
*
|
|
51
|
+
* Each array contains the created span elements. Empty arrays are returned for
|
|
52
|
+
* split types not requested (e.g., if `type: "words"`, chars and lines will be empty).
|
|
53
|
+
*/
|
|
54
|
+
interface SplitTextResult {
|
|
55
|
+
/** Array of character span elements (empty if chars not in type) */
|
|
56
|
+
chars: HTMLSpanElement[];
|
|
57
|
+
/** Array of word span elements (empty if words not in type) */
|
|
58
|
+
words: HTMLSpanElement[];
|
|
59
|
+
/** Array of line span elements (empty if lines not in type) */
|
|
60
|
+
lines: HTMLSpanElement[];
|
|
61
|
+
/** Revert the element to its original HTML and cleanup all observers/timers */
|
|
62
|
+
revert: () => void;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Split text into characters, words, and lines with kerning compensation.
|
|
66
|
+
*
|
|
67
|
+
* Fetta measures character positions before splitting, then applies margin adjustments
|
|
68
|
+
* after splitting to preserve the original kerning (letter spacing). This prevents
|
|
69
|
+
* the visual "jumping" that occurs with naive text splitting.
|
|
70
|
+
*
|
|
71
|
+
* @param element - The HTML element containing text to split. Must have text content.
|
|
72
|
+
* @param options - Configuration options for splitting behavior
|
|
73
|
+
* @returns Object containing arrays of split elements and a revert function
|
|
74
|
+
*
|
|
75
|
+
* @throws {Error} If element is not an HTMLElement
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* import { splitText } from "fetta";
|
|
80
|
+
* import { animate, stagger } from "motion";
|
|
81
|
+
*
|
|
82
|
+
* // Basic usage
|
|
83
|
+
* const { chars, words, lines, revert } = splitText(element);
|
|
84
|
+
*
|
|
85
|
+
* // Animate words
|
|
86
|
+
* animate(words, { opacity: [0, 1], y: [20, 0] }, { delay: stagger(0.05) });
|
|
87
|
+
*
|
|
88
|
+
* // Revert to original HTML when done
|
|
89
|
+
* revert();
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```typescript
|
|
94
|
+
* // Auto-revert after animation completes
|
|
95
|
+
* splitText(element, {
|
|
96
|
+
* onSplit: ({ words }) => animate(words, { opacity: [0, 1] }),
|
|
97
|
+
* revertOnComplete: true,
|
|
98
|
+
* });
|
|
99
|
+
* ```
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* // Responsive re-splitting
|
|
104
|
+
* splitText(element, {
|
|
105
|
+
* autoSplit: true,
|
|
106
|
+
* onResize: ({ lines }) => {
|
|
107
|
+
* // Re-animate after resize
|
|
108
|
+
* animate(lines, { opacity: [0, 1] });
|
|
109
|
+
* },
|
|
110
|
+
* });
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
declare function splitText(element: HTMLElement, { type, charClass, wordClass, lineClass, mask, autoSplit, onResize, onSplit, revertOnComplete, propIndex, willChange, }?: SplitTextOptions): SplitTextResult;
|
|
114
|
+
|
|
115
|
+
export { type SplitTextOptions, type SplitTextResult, splitText };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { splitText } from './chunk-KGMU2B53.js';
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactElement } from 'react';
|
|
3
|
+
export { SplitTextOptions, SplitTextResult } from './index.js';
|
|
4
|
+
|
|
5
|
+
interface SplitTextOptions {
|
|
6
|
+
type?: "chars" | "words" | "lines" | "chars,words" | "words,lines" | "chars,lines" | "chars,words,lines";
|
|
7
|
+
charClass?: string;
|
|
8
|
+
wordClass?: string;
|
|
9
|
+
lineClass?: string;
|
|
10
|
+
/** Apply overflow mask wrapper to elements for reveal animations */
|
|
11
|
+
mask?: "lines" | "words" | "chars";
|
|
12
|
+
propIndex?: boolean;
|
|
13
|
+
willChange?: boolean;
|
|
14
|
+
}
|
|
15
|
+
interface InViewOptions {
|
|
16
|
+
/** How much of the element must be visible (0-1). Default: 0 */
|
|
17
|
+
amount?: number;
|
|
18
|
+
/** Root margin for IntersectionObserver. Default: "0px" */
|
|
19
|
+
margin?: string;
|
|
20
|
+
/** Only trigger once. Default: false */
|
|
21
|
+
once?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Result passed to SplitText callbacks (onSplit, onInView, onLeaveView, onResize).
|
|
25
|
+
*
|
|
26
|
+
* Contains arrays of split elements and a revert function for manual control.
|
|
27
|
+
* Empty arrays are returned for split types not requested in options.
|
|
28
|
+
*/
|
|
29
|
+
interface SplitTextElements {
|
|
30
|
+
/** Array of character span elements */
|
|
31
|
+
chars: HTMLSpanElement[];
|
|
32
|
+
/** Array of word span elements */
|
|
33
|
+
words: HTMLSpanElement[];
|
|
34
|
+
/** Array of line span elements */
|
|
35
|
+
lines: HTMLSpanElement[];
|
|
36
|
+
/** Revert to original HTML and cleanup observers */
|
|
37
|
+
revert: () => void;
|
|
38
|
+
}
|
|
39
|
+
/** Return type for callbacks - void, single animation, array of animations, or promise */
|
|
40
|
+
type CallbackReturn = void | {
|
|
41
|
+
finished: Promise<unknown>;
|
|
42
|
+
} | Array<{
|
|
43
|
+
finished: Promise<unknown>;
|
|
44
|
+
}> | Promise<unknown>;
|
|
45
|
+
interface SplitTextProps {
|
|
46
|
+
children: ReactElement;
|
|
47
|
+
/**
|
|
48
|
+
* Called after text is split.
|
|
49
|
+
* Return an animation or promise to enable revert (requires revertOnComplete).
|
|
50
|
+
* If inView is enabled, this is called immediately but animation typically runs in onInView.
|
|
51
|
+
*/
|
|
52
|
+
onSplit?: (result: SplitTextElements) => CallbackReturn;
|
|
53
|
+
/** Called when autoSplit triggers a re-split on resize */
|
|
54
|
+
onResize?: (result: SplitTextElements) => void;
|
|
55
|
+
options?: SplitTextOptions;
|
|
56
|
+
autoSplit?: boolean;
|
|
57
|
+
/** When true, reverts to original HTML after animation promise resolves */
|
|
58
|
+
revertOnComplete?: boolean;
|
|
59
|
+
/** Enable viewport detection. Pass true for defaults or InViewOptions for customization */
|
|
60
|
+
inView?: boolean | InViewOptions;
|
|
61
|
+
/** Called when element enters viewport. Return animation for revertOnComplete support */
|
|
62
|
+
onInView?: (result: SplitTextElements) => CallbackReturn;
|
|
63
|
+
/** Called when element leaves viewport */
|
|
64
|
+
onLeaveView?: (result: SplitTextElements) => CallbackReturn;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* React component wrapper for text splitting with kerning compensation.
|
|
68
|
+
*
|
|
69
|
+
* Wraps a single child element and splits its text content into characters,
|
|
70
|
+
* words, and/or lines. Handles lifecycle cleanup automatically on unmount.
|
|
71
|
+
*
|
|
72
|
+
* @param props - Component props including callbacks and options
|
|
73
|
+
* @returns The child element wrapped in a container div
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```tsx
|
|
77
|
+
* import { SplitText } from "fetta/react";
|
|
78
|
+
* import { animate, stagger } from "motion";
|
|
79
|
+
*
|
|
80
|
+
* // Basic animation
|
|
81
|
+
* <SplitText
|
|
82
|
+
* onSplit={({ words }) => {
|
|
83
|
+
* animate(words, { opacity: [0, 1], y: [20, 0] }, { delay: stagger(0.05) });
|
|
84
|
+
* }}
|
|
85
|
+
* >
|
|
86
|
+
* <h1>Animated Text</h1>
|
|
87
|
+
* </SplitText>
|
|
88
|
+
* ```
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```tsx
|
|
92
|
+
* // Scroll-triggered with auto-revert
|
|
93
|
+
* <SplitText
|
|
94
|
+
* onSplit={({ chars }) => {
|
|
95
|
+
* chars.forEach(c => c.style.opacity = "0");
|
|
96
|
+
* }}
|
|
97
|
+
* inView={{ amount: 0.5, once: true }}
|
|
98
|
+
* onInView={({ chars }) =>
|
|
99
|
+
* animate(chars, { opacity: 1 }, { delay: stagger(0.02) })
|
|
100
|
+
* }
|
|
101
|
+
* revertOnComplete
|
|
102
|
+
* >
|
|
103
|
+
* <p>Reveals on scroll, reverts after animation</p>
|
|
104
|
+
* </SplitText>
|
|
105
|
+
* ```
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```tsx
|
|
109
|
+
* // Responsive re-splitting
|
|
110
|
+
* <SplitText
|
|
111
|
+
* autoSplit
|
|
112
|
+
* onSplit={({ lines }) => animate(lines, { opacity: [0, 1] })}
|
|
113
|
+
* onResize={({ lines }) => animate(lines, { opacity: [0, 1] })}
|
|
114
|
+
* >
|
|
115
|
+
* <p>Re-animates when container resizes</p>
|
|
116
|
+
* </SplitText>
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
declare const SplitText: react.ForwardRefExoticComponent<SplitTextProps & react.RefAttributes<HTMLDivElement>>;
|
|
120
|
+
|
|
121
|
+
export { SplitText, type SplitTextElements };
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { splitText, __spreadProps, __spreadValues, normalizeToPromise } from './chunk-KGMU2B53.js';
|
|
2
|
+
import { forwardRef, useRef, useCallback, useState, useLayoutEffect, useEffect, isValidElement, cloneElement } from 'react';
|
|
3
|
+
import { jsx } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
var SplitText = forwardRef(
|
|
6
|
+
function SplitText2({
|
|
7
|
+
children,
|
|
8
|
+
onSplit,
|
|
9
|
+
onResize,
|
|
10
|
+
options,
|
|
11
|
+
autoSplit = false,
|
|
12
|
+
revertOnComplete = false,
|
|
13
|
+
inView,
|
|
14
|
+
onInView,
|
|
15
|
+
onLeaveView
|
|
16
|
+
}, forwardedRef) {
|
|
17
|
+
const containerRef = useRef(null);
|
|
18
|
+
const mergedRef = useCallback(
|
|
19
|
+
(node) => {
|
|
20
|
+
containerRef.current = node;
|
|
21
|
+
if (typeof forwardedRef === "function") {
|
|
22
|
+
forwardedRef(node);
|
|
23
|
+
} else if (forwardedRef) {
|
|
24
|
+
forwardedRef.current = node;
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
[forwardedRef]
|
|
28
|
+
);
|
|
29
|
+
const [childElement, setChildElement] = useState(null);
|
|
30
|
+
const [isInView, setIsInView] = useState(false);
|
|
31
|
+
const onSplitRef = useRef(onSplit);
|
|
32
|
+
const onResizeRef = useRef(onResize);
|
|
33
|
+
const optionsRef = useRef(options);
|
|
34
|
+
const revertOnCompleteRef = useRef(revertOnComplete);
|
|
35
|
+
const inViewRef = useRef(inView);
|
|
36
|
+
const onInViewRef = useRef(onInView);
|
|
37
|
+
const onLeaveViewRef = useRef(onLeaveView);
|
|
38
|
+
useLayoutEffect(() => {
|
|
39
|
+
onSplitRef.current = onSplit;
|
|
40
|
+
onResizeRef.current = onResize;
|
|
41
|
+
optionsRef.current = options;
|
|
42
|
+
revertOnCompleteRef.current = revertOnComplete;
|
|
43
|
+
inViewRef.current = inView;
|
|
44
|
+
onInViewRef.current = onInView;
|
|
45
|
+
onLeaveViewRef.current = onLeaveView;
|
|
46
|
+
});
|
|
47
|
+
const hasSplitRef = useRef(false);
|
|
48
|
+
const hasRevertedRef = useRef(false);
|
|
49
|
+
const revertFnRef = useRef(null);
|
|
50
|
+
const splitResultRef = useRef(null);
|
|
51
|
+
const observerRef = useRef(null);
|
|
52
|
+
const hasTriggeredOnceRef = useRef(false);
|
|
53
|
+
const childRefCallback = useCallback((node) => {
|
|
54
|
+
setChildElement(node);
|
|
55
|
+
}, []);
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (!childElement) return;
|
|
58
|
+
if (hasSplitRef.current) return;
|
|
59
|
+
let isMounted = true;
|
|
60
|
+
document.fonts.ready.then(() => {
|
|
61
|
+
var _a, _b;
|
|
62
|
+
if (!isMounted || hasSplitRef.current) return;
|
|
63
|
+
if (!containerRef.current) return;
|
|
64
|
+
const result = splitText(childElement, __spreadProps(__spreadValues({}, optionsRef.current), {
|
|
65
|
+
autoSplit,
|
|
66
|
+
onResize: (resizeResult) => {
|
|
67
|
+
var _a2;
|
|
68
|
+
const newSplitTextElements = {
|
|
69
|
+
chars: resizeResult.chars,
|
|
70
|
+
words: resizeResult.words,
|
|
71
|
+
lines: resizeResult.lines,
|
|
72
|
+
revert: result.revert
|
|
73
|
+
};
|
|
74
|
+
splitResultRef.current = newSplitTextElements;
|
|
75
|
+
(_a2 = onResizeRef.current) == null ? void 0 : _a2.call(onResizeRef, newSplitTextElements);
|
|
76
|
+
}
|
|
77
|
+
}));
|
|
78
|
+
revertFnRef.current = result.revert;
|
|
79
|
+
hasSplitRef.current = true;
|
|
80
|
+
const splitElements = {
|
|
81
|
+
chars: result.chars,
|
|
82
|
+
words: result.words,
|
|
83
|
+
lines: result.lines,
|
|
84
|
+
revert: result.revert
|
|
85
|
+
};
|
|
86
|
+
splitResultRef.current = splitElements;
|
|
87
|
+
containerRef.current.style.visibility = "visible";
|
|
88
|
+
if (onSplitRef.current) {
|
|
89
|
+
const callbackResult = onSplitRef.current(splitElements);
|
|
90
|
+
if (!inViewRef.current && revertOnCompleteRef.current) {
|
|
91
|
+
const promise = normalizeToPromise(callbackResult);
|
|
92
|
+
if (promise) {
|
|
93
|
+
promise.then(() => {
|
|
94
|
+
if (!isMounted || hasRevertedRef.current) return;
|
|
95
|
+
result.revert();
|
|
96
|
+
hasRevertedRef.current = true;
|
|
97
|
+
}).catch(() => {
|
|
98
|
+
console.warn("[fetta] Animation rejected, text not reverted");
|
|
99
|
+
});
|
|
100
|
+
} else if (callbackResult === void 0) ; else {
|
|
101
|
+
console.warn(
|
|
102
|
+
"SplitText: revertOnComplete is enabled but onSplit did not return an animation or promise."
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (inViewRef.current && containerRef.current) {
|
|
108
|
+
const inViewOptions = typeof inViewRef.current === "object" ? inViewRef.current : {};
|
|
109
|
+
const threshold = (_a = inViewOptions.amount) != null ? _a : 0;
|
|
110
|
+
const rootMargin = (_b = inViewOptions.margin) != null ? _b : "0px";
|
|
111
|
+
observerRef.current = new IntersectionObserver(
|
|
112
|
+
(entries) => {
|
|
113
|
+
const entry = entries[0];
|
|
114
|
+
if (!entry) return;
|
|
115
|
+
const isOnce = typeof inViewRef.current === "object" && inViewRef.current.once;
|
|
116
|
+
if (entry.isIntersecting) {
|
|
117
|
+
if (isOnce && hasTriggeredOnceRef.current) return;
|
|
118
|
+
hasTriggeredOnceRef.current = true;
|
|
119
|
+
setIsInView(true);
|
|
120
|
+
} else {
|
|
121
|
+
if (!isOnce) {
|
|
122
|
+
setIsInView(false);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
{ threshold, rootMargin }
|
|
127
|
+
);
|
|
128
|
+
observerRef.current.observe(containerRef.current);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
return () => {
|
|
132
|
+
isMounted = false;
|
|
133
|
+
if (observerRef.current) {
|
|
134
|
+
observerRef.current.disconnect();
|
|
135
|
+
observerRef.current = null;
|
|
136
|
+
}
|
|
137
|
+
if (revertFnRef.current) {
|
|
138
|
+
revertFnRef.current();
|
|
139
|
+
}
|
|
140
|
+
hasSplitRef.current = false;
|
|
141
|
+
};
|
|
142
|
+
}, [childElement, autoSplit]);
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
if (!splitResultRef.current) return;
|
|
145
|
+
if (hasRevertedRef.current) return;
|
|
146
|
+
if (isInView && onInViewRef.current) {
|
|
147
|
+
const callbackResult = onInViewRef.current(splitResultRef.current);
|
|
148
|
+
const promise = normalizeToPromise(callbackResult);
|
|
149
|
+
if (revertOnCompleteRef.current && promise) {
|
|
150
|
+
promise.then(() => {
|
|
151
|
+
var _a;
|
|
152
|
+
if (hasRevertedRef.current) return;
|
|
153
|
+
(_a = splitResultRef.current) == null ? void 0 : _a.revert();
|
|
154
|
+
hasRevertedRef.current = true;
|
|
155
|
+
}).catch(() => {
|
|
156
|
+
console.warn("[fetta] Animation rejected, text not reverted");
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
} else if (!isInView && onLeaveViewRef.current && splitResultRef.current) {
|
|
160
|
+
onLeaveViewRef.current(splitResultRef.current);
|
|
161
|
+
}
|
|
162
|
+
}, [isInView]);
|
|
163
|
+
if (!isValidElement(children)) {
|
|
164
|
+
console.error("SplitText: children must be a single valid React element");
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
const clonedChild = cloneElement(children, {
|
|
168
|
+
ref: childRefCallback
|
|
169
|
+
});
|
|
170
|
+
return /* @__PURE__ */ jsx(
|
|
171
|
+
"div",
|
|
172
|
+
{
|
|
173
|
+
ref: mergedRef,
|
|
174
|
+
style: { visibility: "hidden", position: "relative" },
|
|
175
|
+
children: clonedChild
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
export { SplitText };
|
package/package.json
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fetta",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Text splitting library with kerning compensation for animations",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./core": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./react": {
|
|
17
|
+
"types": "./dist/react.d.ts",
|
|
18
|
+
"import": "./dist/react.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"main": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md",
|
|
26
|
+
"LICENSE"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsup",
|
|
30
|
+
"dev": "tsup --watch",
|
|
31
|
+
"typecheck": "tsc --noEmit",
|
|
32
|
+
"prepublishOnly": "pnpm run build",
|
|
33
|
+
"test": "vitest",
|
|
34
|
+
"test:ui": "vitest --ui",
|
|
35
|
+
"test:coverage": "vitest run --coverage",
|
|
36
|
+
"test:e2e": "playwright test",
|
|
37
|
+
"test:all": "vitest run --coverage && playwright test"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"react": ">=18.0.0"
|
|
41
|
+
},
|
|
42
|
+
"peerDependenciesMeta": {
|
|
43
|
+
"react": {
|
|
44
|
+
"optional": true
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@playwright/test": "^1.49.0",
|
|
49
|
+
"@testing-library/dom": "^10.4.0",
|
|
50
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
51
|
+
"@testing-library/react": "^16.1.0",
|
|
52
|
+
"@types/react": "^19",
|
|
53
|
+
"@vitest/coverage-v8": "^2.1.8",
|
|
54
|
+
"@vitest/ui": "^2.1.8",
|
|
55
|
+
"jsdom": "^25.0.1",
|
|
56
|
+
"react": "^19.2.3",
|
|
57
|
+
"react-dom": "^19.0.0",
|
|
58
|
+
"serve": "^14.2.4",
|
|
59
|
+
"tsup": "^8.0.0",
|
|
60
|
+
"typescript": "^5",
|
|
61
|
+
"vitest": "^2.1.8"
|
|
62
|
+
},
|
|
63
|
+
"keywords": [
|
|
64
|
+
"text",
|
|
65
|
+
"split",
|
|
66
|
+
"animation",
|
|
67
|
+
"motion",
|
|
68
|
+
"kerning",
|
|
69
|
+
"typography",
|
|
70
|
+
"gsap"
|
|
71
|
+
],
|
|
72
|
+
"license": "MIT",
|
|
73
|
+
"author": "dimi",
|
|
74
|
+
"repository": {
|
|
75
|
+
"type": "git",
|
|
76
|
+
"url": "git+https://github.com/dimicx/fetta.git"
|
|
77
|
+
},
|
|
78
|
+
"bugs": {
|
|
79
|
+
"url": "https://github.com/dimicx/fetta/issues"
|
|
80
|
+
},
|
|
81
|
+
"homepage": "https://fetta.dimi.me",
|
|
82
|
+
"publishConfig": {
|
|
83
|
+
"access": "public"
|
|
84
|
+
}
|
|
85
|
+
}
|