summaryrefslogtreecommitdiff
path: root/apps/web/src/components/editor/panels/properties/hooks/use-property-draft.ts
blob: 7e8f6e40b7e7557073f11cbba37f74997c064f25 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import { useState } from "react";
import { evaluateMathExpression } from "@/utils/math";

function looksLikeExpression({ input }: { input: string }): boolean {
	const trimmed = input.trim();
	if (!trimmed) return false;
	if (/[+*/]/.test(input)) return true;
	const minusIndex = trimmed.indexOf("-");
	return minusIndex > 0;
}

export function usePropertyDraft<T>({
	displayValue: sourceDisplay,
	parse,
	onPreview,
	onCommit,
	onStartEditing,
	supportsExpressions = true,
}: {
	displayValue: string;
	parse: (input: string) => T | null;
	onPreview: (value: T) => void;
	onCommit: () => void;
	onStartEditing?: () => void;
	supportsExpressions?: boolean;
}) {
	const [isEditing, setIsEditing] = useState(false);
	const [draft, setDraft] = useState("");

	return {
		displayValue: isEditing ? draft : sourceDisplay,
		scrubTo: (value: number) => {
			const parsed = parse(String(value));
			if (parsed !== null) onPreview(parsed);
		},
		commitScrub: onCommit,
		onFocus: () => {
			setIsEditing(true);
			setDraft(sourceDisplay);
			onStartEditing?.();
		},
		onChange: (
			event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
		) => {
			const nextDraft = event.target.value;
			setDraft(nextDraft);

			const parsed = parse(nextDraft);
			if (parsed !== null) {
				onPreview(parsed);
			}
		},
		onBlur: (
			event: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>,
		) => {
			const nextDraft = event.target.value;
			if (supportsExpressions && looksLikeExpression({ input: nextDraft })) {
				const evaluated = evaluateMathExpression({ input: nextDraft });
				if (evaluated !== null) {
					const parsed = parse(String(evaluated));
					if (parsed !== null) onPreview(parsed);
				}
			}
			onCommit();
			setIsEditing(false);
			setDraft("");
		},
	};
}