refactor: フォームの型を改善
This commit is contained in:
parent
3cf640c2a0
commit
e484954f93
|
@ -21,37 +21,37 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<MkSpacer :marginMin="20" :marginMax="32">
|
<MkSpacer :marginMin="20" :marginMax="32">
|
||||||
<div v-if="Object.keys(form).filter(item => !form[item].hidden).length > 0" class="_gaps_m">
|
<div v-if="Object.keys(form).filter(item => !form[item].hidden).length > 0" class="_gaps_m">
|
||||||
<template v-for="item in Object.keys(form).filter(item => !form[item].hidden)">
|
<template v-for="(v, k) in Object.fromEntries(Object.entries(form).filter(([_, v]) => !('hidden' in v) || 'hidden' in v && !v.hidden))">
|
||||||
<MkInput v-if="form[item].type === 'number'" v-model="values[item]" type="number" :step="form[item].step || 1">
|
<MkInput v-if="v.type === 'number'" v-model="values[k]" type="number" :step="v.step || 1">
|
||||||
<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
|
<template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
|
||||||
<template v-if="form[item].description" #caption>{{ form[item].description }}</template>
|
<template v-if="v.description" #caption>{{ v.description }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkInput v-else-if="form[item].type === 'string' && !form[item].multiline" v-model="values[item]" type="text" :mfmAutocomplete="form[item].treatAsMfm">
|
<MkInput v-else-if="v.type === 'string' && !v.multiline" v-model="values[k]" type="text" :mfmAutocomplete="v.treatAsMfm">
|
||||||
<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
|
<template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
|
||||||
<template v-if="form[item].description" #caption>{{ form[item].description }}</template>
|
<template v-if="v.description" #caption>{{ v.description }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkTextarea v-else-if="form[item].type === 'string' && form[item].multiline" v-model="values[item]" :mfmAutocomplete="form[item].treatAsMfm" :mfmPreview="form[item].treatAsMfm">
|
<MkTextarea v-else-if="v.type === 'string' && v.multiline" v-model="values[k]" :mfmAutocomplete="v.treatAsMfm" :mfmPreview="v.treatAsMfm">
|
||||||
<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
|
<template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
|
||||||
<template v-if="form[item].description" #caption>{{ form[item].description }}</template>
|
<template v-if="v.description" #caption>{{ v.description }}</template>
|
||||||
</MkTextarea>
|
</MkTextarea>
|
||||||
<MkSwitch v-else-if="form[item].type === 'boolean'" v-model="values[item]">
|
<MkSwitch v-else-if="v.type === 'boolean'" v-model="values[k]">
|
||||||
<span v-text="form[item].label || item"></span>
|
<span v-text="v.label || k"></span>
|
||||||
<template v-if="form[item].description" #caption>{{ form[item].description }}</template>
|
<template v-if="v.description" #caption>{{ v.description }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkSelect v-else-if="form[item].type === 'enum'" v-model="values[item]">
|
<MkSelect v-else-if="v.type === 'enum'" v-model="values[k]">
|
||||||
<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
|
<template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
|
||||||
<option v-for="option in form[item].enum" :key="option.value" :value="option.value">{{ option.label }}</option>
|
<option v-for="option in v.enum" :key="option.value" :value="option.value">{{ option.label }}</option>
|
||||||
</MkSelect>
|
</MkSelect>
|
||||||
<MkRadios v-else-if="form[item].type === 'radio'" v-model="values[item]">
|
<MkRadios v-else-if="v.type === 'radio'" v-model="values[k]">
|
||||||
<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
|
<template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
|
||||||
<option v-for="option in form[item].options" :key="option.value" :value="option.value">{{ option.label }}</option>
|
<option v-for="option in v.options" :key="option.value" :value="option.value">{{ option.label }}</option>
|
||||||
</MkRadios>
|
</MkRadios>
|
||||||
<MkRange v-else-if="form[item].type === 'range'" v-model="values[item]" :min="form[item].min" :max="form[item].max" :step="form[item].step" :textConverter="form[item].textConverter">
|
<MkRange v-else-if="v.type === 'range'" v-model="values[k]" :min="v.min" :max="v.max" :step="v.step" :textConverter="v.textConverter">
|
||||||
<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
|
<template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
|
||||||
<template v-if="form[item].description" #caption>{{ form[item].description }}</template>
|
<template v-if="v.description" #caption>{{ v.description }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
<MkButton v-else-if="form[item].type === 'button'" @click="form[item].action($event, values)">
|
<MkButton v-else-if="v.type === 'button'" @click="v.action($event, values)">
|
||||||
<span v-text="form[item].content || item"></span>
|
<span v-text="v.content || k"></span>
|
||||||
</MkButton>
|
</MkButton>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
@ -72,19 +72,20 @@ import MkSelect from './MkSelect.vue';
|
||||||
import MkRange from './MkRange.vue';
|
import MkRange from './MkRange.vue';
|
||||||
import MkButton from './MkButton.vue';
|
import MkButton from './MkButton.vue';
|
||||||
import MkRadios from './MkRadios.vue';
|
import MkRadios from './MkRadios.vue';
|
||||||
|
import type { Form } from '@/scripts/form.js';
|
||||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { infoImageUrl } from '@/instance.js';
|
import { infoImageUrl } from '@/instance.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
title: string;
|
title: string;
|
||||||
form: any;
|
form: Form;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: 'done', v: {
|
(ev: 'done', v: {
|
||||||
canceled?: boolean;
|
canceled?: boolean;
|
||||||
result?: any;
|
result?: Record<string, any>;
|
||||||
}): void;
|
}): void;
|
||||||
(ev: 'closed'): void;
|
(ev: 'closed'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
import { Component, markRaw, Ref, ref, defineAsyncComponent } from 'vue';
|
import { Component, markRaw, Ref, ref, defineAsyncComponent } from 'vue';
|
||||||
import { EventEmitter } from 'eventemitter3';
|
import { EventEmitter } from 'eventemitter3';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
|
import { Form, GetFormResultType } from './scripts/form.js';
|
||||||
import type { ComponentProps } from 'vue-component-type-helpers';
|
import type { ComponentProps } from 'vue-component-type-helpers';
|
||||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
@ -435,9 +436,9 @@ export function waiting(): Promise<void> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function form(title: string, form: any): Promise<{ canceled?: boolean, result?: unknown }> {
|
export function form<F extends Form>(title: string, f: F): Promise<{ canceled?: boolean, result?: GetFormResultType<F> }> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
popup(defineAsyncComponent(() => import('@/components/MkFormDialog.vue')), { title, form }, {
|
popup(defineAsyncComponent(() => import('@/components/MkFormDialog.vue')), { title, form: f }, {
|
||||||
done: result => {
|
done: result => {
|
||||||
resolve(result);
|
resolve(result);
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,29 +12,37 @@ export type FormItem = {
|
||||||
label?: string;
|
label?: string;
|
||||||
type: 'string';
|
type: 'string';
|
||||||
default: string | null;
|
default: string | null;
|
||||||
|
description?: string;
|
||||||
|
required?: boolean;
|
||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
multiline?: boolean;
|
multiline?: boolean;
|
||||||
|
treatAsMfm?: boolean;
|
||||||
} | {
|
} | {
|
||||||
label?: string;
|
label?: string;
|
||||||
type: 'number';
|
type: 'number';
|
||||||
default: number | null;
|
default: number | null;
|
||||||
|
description?: string;
|
||||||
|
required?: boolean;
|
||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
step?: number;
|
step?: number;
|
||||||
} | {
|
} | {
|
||||||
label?: string;
|
label?: string;
|
||||||
type: 'boolean';
|
type: 'boolean';
|
||||||
default: boolean | null;
|
default: boolean | null;
|
||||||
|
description?: string;
|
||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
} | {
|
} | {
|
||||||
label?: string;
|
label?: string;
|
||||||
type: 'enum';
|
type: 'enum';
|
||||||
default: string | null;
|
default: string | null;
|
||||||
|
required?: boolean;
|
||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
enum: EnumItem[];
|
enum: EnumItem[];
|
||||||
} | {
|
} | {
|
||||||
label?: string;
|
label?: string;
|
||||||
type: 'radio';
|
type: 'radio';
|
||||||
default: unknown | null;
|
default: unknown | null;
|
||||||
|
required?: boolean;
|
||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
options: {
|
options: {
|
||||||
label: string;
|
label: string;
|
||||||
|
@ -44,9 +52,12 @@ export type FormItem = {
|
||||||
label?: string;
|
label?: string;
|
||||||
type: 'range';
|
type: 'range';
|
||||||
default: number | null;
|
default: number | null;
|
||||||
step: number;
|
description?: string;
|
||||||
|
required?: boolean;
|
||||||
|
step?: number;
|
||||||
min: number;
|
min: number;
|
||||||
max: number;
|
max: number;
|
||||||
|
textConverter?: (value: number) => string;
|
||||||
} | {
|
} | {
|
||||||
label?: string;
|
label?: string;
|
||||||
type: 'object';
|
type: 'object';
|
||||||
|
@ -57,6 +68,10 @@ export type FormItem = {
|
||||||
type: 'array';
|
type: 'array';
|
||||||
default: unknown[] | null;
|
default: unknown[] | null;
|
||||||
hidden: boolean;
|
hidden: boolean;
|
||||||
|
} | {
|
||||||
|
type: 'button';
|
||||||
|
content?: string;
|
||||||
|
action: (ev: MouseEvent, v: any) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Form = Record<string, FormItem>;
|
export type Form = Record<string, FormItem>;
|
||||||
|
|
Loading…
Reference in New Issue