Skip to content

Commit 2b1a565

Browse files
committed
Input: add fake-stream story
1 parent 5e5b762 commit 2b1a565

File tree

4 files changed

+658
-5
lines changed

4 files changed

+658
-5
lines changed

.storybook/utils.ts

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type * as CEM from '@ui5/webcomponents-tools/lib/cem/types-internal';
2-
import { useMemo } from 'react';
2+
import { useMemo, useRef, useState, useTransition } from 'react';
33
// @ts-expect-error: storybook can handle this
44
import cemAi from './custom-element-manifests/ai.json';
55
// @ts-expect-error: storybook can handle this
@@ -85,3 +85,79 @@ export function useGetSubComponentsOfModule(moduleName: string, tags: string[])
8585
return findSubComponentsRecursively(moduleName, cem);
8686
}, [cem, moduleName]);
8787
}
88+
89+
type StartStreamOptions = {
90+
text: string;
91+
onComplete?: (fullText: string) => void;
92+
onProcessingComplete?: () => void;
93+
};
94+
export function useFakeStream(typingDelay = 10, startingDelay = 1500) {
95+
const [value, setValue] = useState('');
96+
const [transitionIsPending, startTransition] = useTransition(); // active character updates
97+
const [isProcessing, setIsProcessing] = useState(false); // starting delay
98+
const [isTyping, setIsTyping] = useState(false); // actively typing characters
99+
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
100+
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
101+
102+
const startStream = ({ text, onComplete, onProcessingComplete }: StartStreamOptions) => {
103+
// Stop previous stream and timeout
104+
if (intervalRef.current) {
105+
clearInterval(intervalRef.current);
106+
intervalRef.current = null;
107+
}
108+
if (timeoutRef.current) {
109+
clearTimeout(timeoutRef.current);
110+
timeoutRef.current = null;
111+
}
112+
113+
setValue('');
114+
setIsProcessing(true);
115+
116+
timeoutRef.current = setTimeout(() => {
117+
setIsProcessing(false);
118+
119+
if (onProcessingComplete) {
120+
onProcessingComplete();
121+
}
122+
123+
setIsTyping(true);
124+
let index = 0;
125+
126+
intervalRef.current = setInterval(() => {
127+
if (index < text.length) {
128+
const nextChar = text[index];
129+
index++;
130+
131+
startTransition(() => {
132+
setValue((prev) => prev + nextChar);
133+
});
134+
} else {
135+
if (intervalRef.current) {
136+
clearInterval(intervalRef.current);
137+
intervalRef.current = null;
138+
}
139+
setIsTyping(false);
140+
141+
if (onComplete) {
142+
onComplete(text);
143+
}
144+
}
145+
}, typingDelay);
146+
}, startingDelay);
147+
};
148+
149+
const stopStream = () => {
150+
if (intervalRef.current) {
151+
clearInterval(intervalRef.current);
152+
intervalRef.current = null;
153+
}
154+
if (timeoutRef.current) {
155+
clearTimeout(timeoutRef.current);
156+
timeoutRef.current = null;
157+
}
158+
setIsProcessing(false);
159+
setIsTyping(false);
160+
};
161+
162+
return { value, transitionIsPending, isProcessing, isTyping, setValue, startStream, stopStream };
163+
}

0 commit comments

Comments
 (0)