fix: mkradiosを動的slotsに対応させる
This commit is contained in:
parent
7e00a6794c
commit
19eae3ade0
|
|
@ -14,6 +14,10 @@ import { bindThis } from '@/decorators.js';
|
|||
import { IdService } from '@/core/IdService.js';
|
||||
import { UserEntityService } from './UserEntityService.js';
|
||||
|
||||
function assertBw(bw: string): bw is Packed<'ReversiGameDetailed'>['bw'] {
|
||||
return ['random', '1', '2'].includes(bw);
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReversiGameEntityService {
|
||||
constructor(
|
||||
|
|
@ -58,7 +62,7 @@ export class ReversiGameEntityService {
|
|||
surrenderedUserId: game.surrenderedUserId,
|
||||
timeoutUserId: game.timeoutUserId,
|
||||
black: game.black,
|
||||
bw: game.bw,
|
||||
bw: assertBw(game.bw) ? game.bw : 'random',
|
||||
isLlotheo: game.isLlotheo,
|
||||
canPutEverywhere: game.canPutEverywhere,
|
||||
loopedBoard: game.loopedBoard,
|
||||
|
|
@ -116,7 +120,7 @@ export class ReversiGameEntityService {
|
|||
surrenderedUserId: game.surrenderedUserId,
|
||||
timeoutUserId: game.timeoutUserId,
|
||||
black: game.black,
|
||||
bw: game.bw,
|
||||
bw: assertBw(game.bw) ? game.bw : 'random',
|
||||
isLlotheo: game.isLlotheo,
|
||||
canPutEverywhere: game.canPutEverywhere,
|
||||
loopedBoard: game.loopedBoard,
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ export const packedReversiGameLiteSchema = {
|
|||
bw: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
enum: ['random', '1', '2'],
|
||||
},
|
||||
noIrregularRules: {
|
||||
type: 'boolean',
|
||||
|
|
@ -199,6 +200,7 @@ export const packedReversiGameDetailedSchema = {
|
|||
bw: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
enum: ['random', '1', '2'],
|
||||
},
|
||||
noIrregularRules: {
|
||||
type: 'boolean',
|
||||
|
|
|
|||
|
|
@ -19,8 +19,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<div :class="$style.optionContent">
|
||||
<i v-if="option.icon" :class="[$style.optionIcon, option.icon]" :style="option.iconStyle"></i>
|
||||
<div>
|
||||
<div :style="option.labelStyle">{{ option.label ?? option.value }}</div>
|
||||
<div v-if="option.caption" :class="$style.optionCaption">{{ option.caption }}</div>
|
||||
<slot v-if="option.slotId != null" :name="`option-${option.slotId}`"></slot>
|
||||
<template v-else>
|
||||
<div :style="option.labelStyle">{{ option.label ?? option.value }}</div>
|
||||
<div v-if="option.caption" :class="$style.optionCaption">{{ option.caption }}</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</MkRadio>
|
||||
|
|
@ -35,8 +38,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
import type { StyleValue } from 'vue';
|
||||
import type { OptionValue } from '@/types/option-value.js';
|
||||
|
||||
export type RadioOption<T = OptionValue> = {
|
||||
export type RadioOption<T = OptionValue, S = string> = {
|
||||
value: T;
|
||||
slotId?: S;
|
||||
label?: string;
|
||||
labelStyle?: StyleValue;
|
||||
icon?: string;
|
||||
|
|
@ -54,6 +58,13 @@ defineProps<{
|
|||
vertical?: boolean;
|
||||
}>();
|
||||
|
||||
defineSlots<{
|
||||
label?: () => any;
|
||||
caption?: () => any
|
||||
} & {
|
||||
[K in `option-${NonNullable<T['slotId']>}`]: () => any;
|
||||
}>();
|
||||
|
||||
const model = defineModel<T['value']>({ required: true });
|
||||
|
||||
function getKey(value: OptionValue): PropertyKey {
|
||||
|
|
|
|||
|
|
@ -1,107 +0,0 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<!--
|
||||
古いMkRadiosの実装。特殊な使い方をしていたため移行できなかった game.setting.vue でのみ使用
|
||||
TODO: 移行して廃止
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, h, ref, watch } from 'vue';
|
||||
import MkRadio from './MkRadio.vue';
|
||||
import type { VNode } from 'vue';
|
||||
import type { OptionValue } from '@/types/option-value';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
modelValue: {
|
||||
required: false,
|
||||
},
|
||||
vertical: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
setup(props, context) {
|
||||
const value = ref(props.modelValue);
|
||||
watch(value, () => {
|
||||
context.emit('update:modelValue', value.value);
|
||||
});
|
||||
watch(() => props.modelValue, v => {
|
||||
value.value = v;
|
||||
});
|
||||
if (!context.slots.default) return null;
|
||||
let options = context.slots.default();
|
||||
const label = context.slots.label && context.slots.label();
|
||||
const caption = context.slots.caption && context.slots.caption();
|
||||
|
||||
// なぜかFragmentになることがあるため
|
||||
if (options.length === 1 && options[0].props == null) options = options[0].children as VNode[];
|
||||
|
||||
// vnodeのうちv-if=falseなものを除外する(trueになるものはoptionなど他typeになる)
|
||||
options = options.filter(vnode => !(typeof vnode.type === 'symbol' && vnode.type.description === 'v-cmt' && vnode.children === 'v-if'));
|
||||
|
||||
return () => h('div', {
|
||||
class: [
|
||||
'novjtcto',
|
||||
...(props.vertical ? ['vertical'] : []),
|
||||
],
|
||||
}, [
|
||||
...(label ? [h('div', {
|
||||
class: 'label',
|
||||
}, label)] : []),
|
||||
h('div', {
|
||||
class: 'body',
|
||||
}, options.map(option => h(MkRadio, {
|
||||
key: option.key as string,
|
||||
value: option.props?.value,
|
||||
disabled: option.props?.disabled,
|
||||
modelValue: value.value as OptionValue,
|
||||
'onUpdate:modelValue': _v => value.value = _v,
|
||||
}, () => option.children)),
|
||||
),
|
||||
...(caption ? [h('div', {
|
||||
class: 'caption',
|
||||
}, caption)] : []),
|
||||
]);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.novjtcto {
|
||||
> .label {
|
||||
font-size: 0.85em;
|
||||
padding: 0 0 8px 0;
|
||||
user-select: none;
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
> .body {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
> .caption {
|
||||
font-size: 0.85em;
|
||||
padding: 8px 0 0 0;
|
||||
color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.vertical {
|
||||
> .body {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -36,23 +36,29 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<template #label>{{ i18n.ts._reversi.blackOrWhite }}</template>
|
||||
|
||||
<!-- TODO: 移行 -->
|
||||
<MkRadios2 v-model="game.bw">
|
||||
<option value="random">{{ i18n.ts.random }}</option>
|
||||
<option :value="'1'">
|
||||
<MkRadios
|
||||
:options="[
|
||||
{ value: 'random', label: i18n.ts.random },
|
||||
{ value: '1', slotId: 'user1' },
|
||||
{ value: '2', slotId: 'user2' },
|
||||
]"
|
||||
v-model="game.bw"
|
||||
>
|
||||
<template #option-user1>
|
||||
<I18n :src="i18n.ts._reversi.blackIs" tag="span">
|
||||
<template #name>
|
||||
<b><MkUserName :user="game.user1"/></b>
|
||||
</template>
|
||||
</I18n>
|
||||
</option>
|
||||
<option :value="'2'">
|
||||
</template>
|
||||
<template #option-user2>
|
||||
<I18n :src="i18n.ts._reversi.blackIs" tag="span">
|
||||
<template #name>
|
||||
<b><MkUserName :user="game.user2"/></b>
|
||||
</template>
|
||||
</I18n>
|
||||
</option>
|
||||
</MkRadios2>
|
||||
</template>
|
||||
</MkRadios>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder :defaultOpen="true">
|
||||
|
|
@ -106,7 +112,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, watch, ref, onMounted, shallowRef, onUnmounted } from 'vue';
|
||||
import { computed, watch, ref, onUnmounted } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import * as Reversi from 'misskey-reversi';
|
||||
import { i18n } from '@/i18n.js';
|
||||
|
|
@ -114,7 +120,6 @@ import { ensureSignin } from '@/i.js';
|
|||
import { deepClone } from '@/utility/clone.js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkRadios from '@/components/MkRadios.vue';
|
||||
import MkRadios2 from '@/components/MkRadios2.vue';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
import * as os from '@/os.js';
|
||||
|
|
|
|||
|
|
@ -5337,7 +5337,8 @@ export type components = {
|
|||
/** Format: id */
|
||||
timeoutUserId: string | null;
|
||||
black: number | null;
|
||||
bw: string;
|
||||
/** @enum {string} */
|
||||
bw: 'random' | '1' | '2';
|
||||
noIrregularRules: boolean;
|
||||
isLlotheo: boolean;
|
||||
canPutEverywhere: boolean;
|
||||
|
|
@ -5373,7 +5374,8 @@ export type components = {
|
|||
/** Format: id */
|
||||
timeoutUserId: string | null;
|
||||
black: number | null;
|
||||
bw: string;
|
||||
/** @enum {string} */
|
||||
bw: 'random' | '1' | '2';
|
||||
noIrregularRules: boolean;
|
||||
isLlotheo: boolean;
|
||||
canPutEverywhere: boolean;
|
||||
|
|
|
|||
Loading…
Reference in New Issue