summaryrefslogtreecommitdiff
path: root/packages/frontend/src/use/use-form.ts
blob: 26cca839c36de186b00a701581ed0423c31b59ba (plain)
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
/*
 * SPDX-FileCopyrightText: syuilo and misskey-project
 * SPDX-License-Identifier: AGPL-3.0-only
 */

import { computed, reactive, watch } from 'vue';
import type { Reactive } from 'vue';

function copy<T>(v: T): T {
	return JSON.parse(JSON.stringify(v));
}

function unwrapReactive<T>(v: Reactive<T>): T {
	return JSON.parse(JSON.stringify(v));
}

export function useForm<T extends Record<string, any>>(initialState: T, save: (newState: T) => Promise<void>) {
	const currentState = reactive<T>(copy(initialState));
	const previousState = reactive<T>(copy(initialState));

	const modifiedStates = reactive<Record<keyof T, boolean>>({} as any);
	for (const key in currentState) {
		modifiedStates[key] = false;
	}
	const modified = computed(() => Object.values(modifiedStates).some(v => v));
	const modifiedCount = computed(() => Object.values(modifiedStates).filter(v => v).length);

	watch([currentState, previousState], () => {
		for (const key in modifiedStates) {
			modifiedStates[key] = currentState[key] !== previousState[key];
		}
	}, { deep: true });

	async function _save() {
		await save(unwrapReactive(currentState));
		for (const key in currentState) {
			previousState[key] = copy(currentState[key]);
		}
	}

	function discard() {
		for (const key in currentState) {
			currentState[key] = copy(previousState[key]);
		}
	}

	return {
		state: currentState,
		savedState: previousState,
		modifiedStates,
		modified,
		modifiedCount,
		save: _save,
		discard,
	};
}