summaryrefslogtreecommitdiff
path: root/packages/frontend/src/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/frontend/src/scripts')
-rw-r--r--packages/frontend/src/scripts/use-form.ts55
1 files changed, 55 insertions, 0 deletions
diff --git a/packages/frontend/src/scripts/use-form.ts b/packages/frontend/src/scripts/use-form.ts
new file mode 100644
index 0000000000..0d505fe466
--- /dev/null
+++ b/packages/frontend/src/scripts/use-form.ts
@@ -0,0 +1,55 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { computed, Reactive, reactive, watch } 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,
+ };
+}