commit
6da9da0e8f
8 changed files with 670 additions and 323 deletions
|
@ -178,6 +178,11 @@ auth/views/index.vue:
|
||||||
sign-in: "サインインしてください"
|
sign-in: "サインインしてください"
|
||||||
|
|
||||||
common/views/components/games/reversi/reversi.vue:
|
common/views/components/games/reversi/reversi.vue:
|
||||||
|
matching:
|
||||||
|
waiting-for: "{}を待っています"
|
||||||
|
cancel: "キャンセル"
|
||||||
|
|
||||||
|
common/views/components/games/reversi/reversi.index.vue:
|
||||||
title: "Misskey Reversi"
|
title: "Misskey Reversi"
|
||||||
sub-title: "他のMisskeyユーザーとリバーシで対戦しよう"
|
sub-title: "他のMisskeyユーザーとリバーシで対戦しよう"
|
||||||
invite: "招待"
|
invite: "招待"
|
||||||
|
@ -192,9 +197,6 @@ common/views/components/games/reversi/reversi.vue:
|
||||||
game-state:
|
game-state:
|
||||||
ended: "終了"
|
ended: "終了"
|
||||||
playing: "進行中"
|
playing: "進行中"
|
||||||
matching:
|
|
||||||
waiting-for: "{}を待っています"
|
|
||||||
cancel: "キャンセル"
|
|
||||||
|
|
||||||
common/views/components/games/reversi/reversi.room.vue:
|
common/views/components/games/reversi/reversi.room.vue:
|
||||||
settings-of-the-game: "ゲームの設定"
|
settings-of-the-game: "ゲームの設定"
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="root">
|
<div class="xqnhankfuuilcwvhgsopeqncafzsquya">
|
||||||
<header><b>{{ blackUser | userName }}</b>(%i18n:common.reversi.black%) vs <b>{{ whiteUser | userName }}</b>(%i18n:common.reversi.white%)</header>
|
<header><b>{{ blackUser | userName }}</b>(%i18n:common.reversi.black%) vs <b>{{ whiteUser | userName }}</b>(%i18n:common.reversi.white%)</header>
|
||||||
|
|
||||||
<div style="overflow: hidden">
|
<div style="overflow: hidden">
|
||||||
<p class="turn" v-if="!iAmPlayer && !game.isEnded">{{ '%i18n:common.reversi.turn-of%'.replace('{}', Vue.filter('userName')(turnUser)) }}<mk-ellipsis/></p>
|
<p class="turn" v-if="!iAmPlayer && !game.isEnded">{{ '%i18n:common.reversi.turn-of%'.replace('{}', $options.filters.userName(turnUser)) }}<mk-ellipsis/></p>
|
||||||
<p class="turn" v-if="logPos != logs.length">{{ '%i18n:common.reversi.past-turn-of%'.replace('{}', Vue.filter('userName')(turnUser)) }}</p>
|
<p class="turn" v-if="logPos != logs.length">{{ '%i18n:common.reversi.past-turn-of%'.replace('{}', $options.filters.userName(turnUser)) }}</p>
|
||||||
<p class="turn1" v-if="iAmPlayer && !game.isEnded && !isMyTurn">%i18n:common.reversi.opponent-turn%<mk-ellipsis/></p>
|
<p class="turn1" v-if="iAmPlayer && !game.isEnded && !isMyTurn">%i18n:common.reversi.opponent-turn%<mk-ellipsis/></p>
|
||||||
<p class="turn2" v-if="iAmPlayer && !game.isEnded && isMyTurn" v-animate-css="{ classes: 'tada', iteration: 'infinite' }">%i18n:common.reversi.my-turn%</p>
|
<p class="turn2" v-if="iAmPlayer && !game.isEnded && isMyTurn" v-animate-css="{ classes: 'tada', iteration: 'infinite' }">%i18n:common.reversi.my-turn%</p>
|
||||||
<p class="result" v-if="game.isEnded && logPos == logs.length">
|
<p class="result" v-if="game.isEnded && logPos == logs.length">
|
||||||
<template v-if="game.winner">{{ '%i18n:common.reversi.won%'.replace('{}', Vue.filter('userName')(game.winner)) }}{{ game.settings.isLlotheo ? ' (ロセオ)' : '' }}</template>
|
<template v-if="game.winner">{{ '%i18n:common.reversi.won%'.replace('{}', $options.filters.userName(game.winner)) }}{{ game.settings.isLlotheo ? ' (ロセオ)' : '' }}</template>
|
||||||
<template v-else>%i18n:common.reversi.drawn%</template>
|
<template v-else>%i18n:common.reversi.drawn%</template>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -258,12 +258,12 @@ export default Vue.extend({
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
@import '~const.styl'
|
@import '~const.styl'
|
||||||
|
|
||||||
.root
|
root(isDark)
|
||||||
text-align center
|
text-align center
|
||||||
|
|
||||||
> header
|
> header
|
||||||
padding 8px
|
padding 8px
|
||||||
border-bottom dashed 1px #c4cdd4
|
border-bottom dashed 1px isDark ? #4c5761 : #c4cdd4
|
||||||
|
|
||||||
> .board
|
> .board
|
||||||
width calc(100% - 16px)
|
width calc(100% - 16px)
|
||||||
|
@ -327,16 +327,16 @@ export default Vue.extend({
|
||||||
user-select none
|
user-select none
|
||||||
|
|
||||||
&.empty
|
&.empty
|
||||||
border solid 2px #eee
|
border solid 2px isDark ? #51595f : #eee
|
||||||
|
|
||||||
&.empty.can
|
&.empty.can
|
||||||
background #eee
|
background isDark ? #51595f : #eee
|
||||||
|
|
||||||
&.empty.myTurn
|
&.empty.myTurn
|
||||||
border-color #ddd
|
border-color isDark ? #6a767f : #ddd
|
||||||
|
|
||||||
&.can
|
&.can
|
||||||
background #eee
|
background isDark ? #51595f : #eee
|
||||||
cursor pointer
|
cursor pointer
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
|
@ -350,7 +350,7 @@ export default Vue.extend({
|
||||||
box-shadow 0 0 0 4px rgba($theme-color, 0.7)
|
box-shadow 0 0 0 4px rgba($theme-color, 0.7)
|
||||||
|
|
||||||
&.isEnded
|
&.isEnded
|
||||||
border-color #ddd
|
border-color isDark ? #6a767f : #ddd
|
||||||
|
|
||||||
&.none
|
&.none
|
||||||
border-color transparent !important
|
border-color transparent !important
|
||||||
|
@ -388,4 +388,11 @@ export default Vue.extend({
|
||||||
display inline-block
|
display inline-block
|
||||||
margin 0 8px
|
margin 0 8px
|
||||||
min-width 70px
|
min-width 70px
|
||||||
|
|
||||||
|
.xqnhankfuuilcwvhgsopeqncafzsquya[data-darkmode]
|
||||||
|
root(true)
|
||||||
|
|
||||||
|
.xqnhankfuuilcwvhgsopeqncafzsquya:not([data-darkmode])
|
||||||
|
root(false)
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,258 @@
|
||||||
|
<template>
|
||||||
|
<div class="phgnkghfpyvkrvwiajkiuoxyrdaqpzcx">
|
||||||
|
<h1>%i18n:@title%</h1>
|
||||||
|
<p>%i18n:@sub-title%</p>
|
||||||
|
<div class="play">
|
||||||
|
<!--<el-button round>フリーマッチ(準備中)</el-button>-->
|
||||||
|
<form-button primary round @click="match">%i18n:@invite%</form-button>
|
||||||
|
<details>
|
||||||
|
<summary>%i18n:@rule%</summary>
|
||||||
|
<div>
|
||||||
|
<p>%i18n:@rule-desc%</p>
|
||||||
|
<dl>
|
||||||
|
<dt><b>%i18n:@mode-invite%</b></dt>
|
||||||
|
<dd>%i18n:@mode-invite-desc%</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
<section v-if="invitations.length > 0">
|
||||||
|
<h2>%i18n:@invitations%</h2>
|
||||||
|
<div class="invitation" v-for="i in invitations" tabindex="-1" @click="accept(i)">
|
||||||
|
<mk-avatar class="avatar" :user="i.parent"/>
|
||||||
|
<span class="name"><b>{{ i.parent | userName }}</b></span>
|
||||||
|
<span class="username">@{{ i.parent.username }}</span>
|
||||||
|
<mk-time :time="i.createdAt"/>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section v-if="myGames.length > 0">
|
||||||
|
<h2>%i18n:@my-games%</h2>
|
||||||
|
<a class="game" v-for="g in myGames" tabindex="-1" @click.prevent="go(g)" :href="`/reversi/${g.id}`">
|
||||||
|
<mk-avatar class="avatar" :user="g.user1"/>
|
||||||
|
<mk-avatar class="avatar" :user="g.user2"/>
|
||||||
|
<span><b>{{ g.user1 | userName }}</b> vs <b>{{ g.user2 | userName }}</b></span>
|
||||||
|
<span class="state">{{ g.isEnded ? '%i18n:@game-state.ended%' : '%i18n:@game-state.playing%' }}</span>
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
<section v-if="games.length > 0">
|
||||||
|
<h2>%i18n:@all-games%</h2>
|
||||||
|
<a class="game" v-for="g in games" tabindex="-1" @click.prevent="go(g)" :href="`/reversi/${g.id}`">
|
||||||
|
<mk-avatar class="avatar" :user="g.user1"/>
|
||||||
|
<mk-avatar class="avatar" :user="g.user2"/>
|
||||||
|
<span><b>{{ g.user1 | userName }}</b> vs <b>{{ g.user2 | userName }}</b></span>
|
||||||
|
<span class="state">{{ g.isEnded ? '%i18n:@game-state.ended%' : '%i18n:@game-state.playing%' }}</span>
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
games: [],
|
||||||
|
gamesFetching: true,
|
||||||
|
gamesMoreFetching: false,
|
||||||
|
myGames: [],
|
||||||
|
matching: null,
|
||||||
|
invitations: [],
|
||||||
|
connection: null,
|
||||||
|
connectionId: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
if (this.$store.getters.isSignedIn) {
|
||||||
|
this.connection = (this as any).os.streams.reversiStream.getConnection();
|
||||||
|
this.connectionId = (this as any).os.streams.reversiStream.use();
|
||||||
|
|
||||||
|
this.connection.on('invited', this.onInvited);
|
||||||
|
|
||||||
|
(this as any).api('games/reversi/games', {
|
||||||
|
my: true
|
||||||
|
}).then(games => {
|
||||||
|
this.myGames = games;
|
||||||
|
});
|
||||||
|
|
||||||
|
(this as any).api('games/reversi/invitations').then(invitations => {
|
||||||
|
this.invitations = this.invitations.concat(invitations);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
(this as any).api('games/reversi/games').then(games => {
|
||||||
|
this.games = games;
|
||||||
|
this.gamesFetching = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.connection) {
|
||||||
|
this.connection.off('invited', this.onInvited);
|
||||||
|
(this as any).os.streams.reversiStream.dispose(this.connectionId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
go(game) {
|
||||||
|
(this as any).api('games/reversi/games/show', {
|
||||||
|
gameId: game.id
|
||||||
|
}).then(game => {
|
||||||
|
this.$emit('go', game);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
match() {
|
||||||
|
(this as any).apis.input({
|
||||||
|
title: '%i18n:@enter-username%'
|
||||||
|
}).then(username => {
|
||||||
|
(this as any).api('users/show', {
|
||||||
|
username
|
||||||
|
}).then(user => {
|
||||||
|
(this as any).api('games/reversi/match', {
|
||||||
|
userId: user.id
|
||||||
|
}).then(res => {
|
||||||
|
if (res == null) {
|
||||||
|
this.$emit('matching', user);
|
||||||
|
} else {
|
||||||
|
this.$emit('go', res);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
accept(invitation) {
|
||||||
|
(this as any).api('games/reversi/match', {
|
||||||
|
userId: invitation.parent.id
|
||||||
|
}).then(game => {
|
||||||
|
if (game) {
|
||||||
|
this.$emit('go', game);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onInvited(invite) {
|
||||||
|
this.invitations.unshift(invite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
@import '~const.styl'
|
||||||
|
|
||||||
|
root(isDark)
|
||||||
|
> h1
|
||||||
|
margin 0
|
||||||
|
padding 24px
|
||||||
|
font-size 24px
|
||||||
|
text-align center
|
||||||
|
font-weight normal
|
||||||
|
color #fff
|
||||||
|
background linear-gradient(to bottom, isDark ? #45730e : #8bca3e, isDark ? #464300 : #d6cf31)
|
||||||
|
|
||||||
|
& + p
|
||||||
|
margin 0
|
||||||
|
padding 12px
|
||||||
|
margin-bottom 12px
|
||||||
|
text-align center
|
||||||
|
font-size 14px
|
||||||
|
border-bottom solid 1px isDark ? #535f65 : #d3d9dc
|
||||||
|
|
||||||
|
> .play
|
||||||
|
margin 0 auto
|
||||||
|
padding 0 16px
|
||||||
|
max-width 500px
|
||||||
|
text-align center
|
||||||
|
|
||||||
|
> details
|
||||||
|
margin 8px 0
|
||||||
|
|
||||||
|
> div
|
||||||
|
padding 16px
|
||||||
|
font-size 14px
|
||||||
|
text-align left
|
||||||
|
background isDark ? #282c37 : #f5f5f5
|
||||||
|
border-radius 8px
|
||||||
|
|
||||||
|
> section
|
||||||
|
margin 0 auto
|
||||||
|
padding 0 16px 16px 16px
|
||||||
|
max-width 500px
|
||||||
|
border-top solid 1px isDark ? #535f65 : #d3d9dc
|
||||||
|
|
||||||
|
> h2
|
||||||
|
margin 0
|
||||||
|
padding 16px 0 8px 0
|
||||||
|
font-size 16px
|
||||||
|
font-weight bold
|
||||||
|
|
||||||
|
.invitation
|
||||||
|
margin 8px 0
|
||||||
|
padding 8px
|
||||||
|
color isDark ? #fff : #677f84
|
||||||
|
background isDark ? #282c37 : #fff
|
||||||
|
box-shadow 0 2px 16px rgba(#000, isDark ? 0.7 : 0.15)
|
||||||
|
border-radius 6px
|
||||||
|
cursor pointer
|
||||||
|
|
||||||
|
*
|
||||||
|
pointer-events none
|
||||||
|
user-select none
|
||||||
|
|
||||||
|
&:focus
|
||||||
|
border-color $theme-color
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
background isDark ? #313543 : #f5f5f5
|
||||||
|
|
||||||
|
&:active
|
||||||
|
background isDark ? #1e222b : #eee
|
||||||
|
|
||||||
|
> .avatar
|
||||||
|
width 32px
|
||||||
|
height 32px
|
||||||
|
border-radius 100%
|
||||||
|
|
||||||
|
> span
|
||||||
|
margin 0 8px
|
||||||
|
line-height 32px
|
||||||
|
|
||||||
|
.game
|
||||||
|
display block
|
||||||
|
margin 8px 0
|
||||||
|
padding 8px
|
||||||
|
color isDark ? #fff : #677f84
|
||||||
|
background isDark ? #282c37 : #fff
|
||||||
|
box-shadow 0 2px 16px rgba(#000, isDark ? 0.7 : 0.15)
|
||||||
|
border-radius 6px
|
||||||
|
cursor pointer
|
||||||
|
|
||||||
|
*
|
||||||
|
pointer-events none
|
||||||
|
user-select none
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
background isDark ? #313543 : #f5f5f5
|
||||||
|
|
||||||
|
&:active
|
||||||
|
background isDark ? #1e222b : #eee
|
||||||
|
|
||||||
|
> .avatar
|
||||||
|
width 32px
|
||||||
|
height 32px
|
||||||
|
border-radius 100%
|
||||||
|
|
||||||
|
> span
|
||||||
|
margin 0 8px
|
||||||
|
line-height 32px
|
||||||
|
|
||||||
|
.phgnkghfpyvkrvwiajkiuoxyrdaqpzcx[data-darkmode]
|
||||||
|
root(true)
|
||||||
|
|
||||||
|
.phgnkghfpyvkrvwiajkiuoxyrdaqpzcx:not([data-darkmode])
|
||||||
|
root(false)
|
||||||
|
|
||||||
|
</style>
|
|
@ -1,78 +1,94 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="root">
|
<div class="urbixznjwwuukfsckrwzwsqzsxornqij">
|
||||||
<header><b>{{ game.user1 | userName }}</b> vs <b>{{ game.user2 | userName }}</b></header>
|
<header><b>{{ game.user1 | userName }}</b> vs <b>{{ game.user2 | userName }}</b></header>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<p>%i18n:@settings-of-the-game%</p>
|
<p>%i18n:@settings-of-the-game%</p>
|
||||||
|
|
||||||
<el-card class="map">
|
<div class="card map">
|
||||||
<div slot="header">
|
<header>
|
||||||
<el-select :class="$style.mapSelect" v-model="mapName" placeholder="%i18n:@choose-map%" @change="onMapChange">
|
<select v-model="mapName" placeholder="%i18n:@choose-map%" @change="onMapChange">
|
||||||
<el-option label="%i18n:@random%" :value="null"/>
|
<option label="-Custom-" :value="mapName" v-if="mapName == '-Custom-'"/>
|
||||||
<el-option-group v-for="c in mapCategories" :key="c" :label="c">
|
<option label="%i18n:@random%" :value="null"/>
|
||||||
<el-option v-for="m in maps" v-if="m.category == c" :key="m.name" :label="m.name" :value="m.name">
|
<optgroup v-for="c in mapCategories" :key="c" :label="c">
|
||||||
<span style="float: left">{{ m.name }}</span>
|
<option v-for="m in maps" v-if="m.category == c" :key="m.name" :label="m.name" :value="m.name">{{ m.name }}</option>
|
||||||
<span style="float: right; color: #8492a6; font-size: 13px" v-if="m.author">(by <i>{{ m.author }}</i>)</span>
|
</optgroup>
|
||||||
</el-option>
|
</select>
|
||||||
</el-option-group>
|
</header>
|
||||||
</el-select>
|
|
||||||
</div>
|
<div>
|
||||||
<div :class="$style.board" v-if="game.settings.map != null" :style="{ 'grid-template-rows': `repeat(${ game.settings.map.length }, 1fr)`, 'grid-template-columns': `repeat(${ game.settings.map[0].length }, 1fr)` }">
|
<div class="random" v-if="game.settings.map == null">%fa:dice%</div>
|
||||||
|
<div class="board" v-else :style="{ 'grid-template-rows': `repeat(${ game.settings.map.length }, 1fr)`, 'grid-template-columns': `repeat(${ game.settings.map[0].length }, 1fr)` }">
|
||||||
<div v-for="(x, i) in game.settings.map.join('')"
|
<div v-for="(x, i) in game.settings.map.join('')"
|
||||||
:data-none="x == ' '"
|
:data-none="x == ' '"
|
||||||
@click="onPixelClick(i, x)"
|
@click="onPixelClick(i, x)">
|
||||||
>
|
<template v-if="x == 'b'"><template v-if="$store.state.device.darkmode">%fa:circle R%</template><template v-else>%fa:circle%</template></template>
|
||||||
<template v-if="x == 'b'">%fa:circle%</template>
|
<template v-if="x == 'w'"><template v-if="$store.state.device.darkmode">%fa:circle%</template><template v-else>%fa:circle R%</template></template>
|
||||||
<template v-if="x == 'w'">%fa:circle R%</template>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<el-card class="bw">
|
<div class="card">
|
||||||
<div slot="header">
|
<header>
|
||||||
<span>%i18n:@black-or-white%</span>
|
<span>%i18n:@black-or-white%</span>
|
||||||
</div>
|
</header>
|
||||||
<el-radio v-model="game.settings.bw" label="random" @change="updateSettings">%i18n:@random%</el-radio>
|
|
||||||
<el-radio v-model="game.settings.bw" :label="1" @change="updateSettings">{{ '%i18n:@black-is%'.split('{}')[0] }}{{ game.user1 | userName }}{{ '%i18n:@black-is%'.split('{}')[1] }}</el-radio>
|
|
||||||
<el-radio v-model="game.settings.bw" :label="2" @change="updateSettings">{{ '%i18n:@black-is%'.split('{}')[0] }}{{ game.user2 | userName }}{{ '%i18n:@black-is%'.split('{}')[1] }}</el-radio>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<el-card class="rules">
|
<div>
|
||||||
<div slot="header">
|
<form-radio v-model="game.settings.bw" value="random" @change="updateSettings">%i18n:@random%</form-radio>
|
||||||
<span>%i18n:@rules%</span>
|
<form-radio v-model="game.settings.bw" :value="1" @change="updateSettings">{{ '%i18n:@black-is%'.split('{}')[0] }}<b>{{ game.user1 | userName }}</b>{{ '%i18n:@black-is%'.split('{}')[1] }}</form-radio>
|
||||||
|
<form-radio v-model="game.settings.bw" :value="2" @change="updateSettings">{{ '%i18n:@black-is%'.split('{}')[0] }}<b>{{ game.user2 | userName }}</b>{{ '%i18n:@black-is%'.split('{}')[1] }}</form-radio>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<header>
|
||||||
|
<span>%i18n:@rules%</span>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div>
|
||||||
<mk-switch v-model="game.settings.isLlotheo" @change="updateSettings" text="%i18n:@is-llotheo%"/>
|
<mk-switch v-model="game.settings.isLlotheo" @change="updateSettings" text="%i18n:@is-llotheo%"/>
|
||||||
<mk-switch v-model="game.settings.loopedBoard" @change="updateSettings" text="%i18n:@looped-map%"/>
|
<mk-switch v-model="game.settings.loopedBoard" @change="updateSettings" text="%i18n:@looped-map%"/>
|
||||||
<mk-switch v-model="game.settings.canPutEverywhere" @change="updateSettings" text="%i18n:@can-put-everywhere%"/>
|
<mk-switch v-model="game.settings.canPutEverywhere" @change="updateSettings" text="%i18n:@can-put-everywhere%"/>
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<el-card class="bot-form" v-if="form">
|
|
||||||
<div slot="header">
|
|
||||||
<span>%i18n:@settings-of-the-bot%</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card" v-if="form">
|
||||||
|
<header>
|
||||||
|
<span>%i18n:@settings-of-the-bot%</span>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div>
|
||||||
<el-alert v-for="message in messages"
|
<el-alert v-for="message in messages"
|
||||||
:title="message.text"
|
:title="message.text"
|
||||||
:type="message.type"
|
:type="message.type"
|
||||||
:key="message.id"
|
:key="message.id"/>
|
||||||
/>
|
|
||||||
<template v-for="item in form">
|
<template v-for="item in form">
|
||||||
<mk-switch v-if="item.type == 'button'" v-model="item.value" :key="item.id" :text="item.label" @change="onChangeForm($event, item)">{{ item.desc || '' }}</mk-switch>
|
<mk-switch v-if="item.type == 'button'" v-model="item.value" :key="item.id" :text="item.label" @change="onChangeForm($event, item)">{{ item.desc || '' }}</mk-switch>
|
||||||
|
|
||||||
<el-card v-if="item.type == 'radio'" :key="item.id">
|
<div class="card" v-if="item.type == 'radio'" :key="item.id">
|
||||||
<div slot="header">
|
<header>
|
||||||
<span>{{ item.label }}</span>
|
<span>{{ item.label }}</span>
|
||||||
</div>
|
</header>
|
||||||
<el-radio v-for="(r, i) in item.items" :key="item.id + ':' + i" v-model="item.value" :label="r.value" @change="onChangeForm($event, item)">{{ r.label }}</el-radio>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<el-card v-if="item.type == 'textbox'" :key="item.id">
|
<div>
|
||||||
<div slot="header">
|
<el-radio v-for="(r, i) in item.items" :key="item.id + ':' + i" v-model="item.value" :label="r.value" @change="onChangeForm($event, item)">{{ r.label }}</el-radio>
|
||||||
<span>{{ item.label }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card" v-if="item.type == 'textbox'" :key="item.id">
|
||||||
|
<header>
|
||||||
|
<span>{{ item.label }}</span>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div>
|
||||||
<el-input v-model="item.value" @change="onChangeForm($event, item)"/>
|
<el-input v-model="item.value" @change="onChangeForm($event, item)"/>
|
||||||
</el-card>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-card>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
|
@ -84,9 +100,9 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<el-button @click="exit">%i18n:@cancel%</el-button>
|
<form-button @click="exit">%i18n:@cancel%</form-button>
|
||||||
<el-button type="primary" @click="accept" v-if="!isAccepted">%i18n:@ready%</el-button>
|
<form-button primary @click="accept" v-if="!isAccepted">%i18n:@ready%</form-button>
|
||||||
<el-button type="primary" @click="cancel" v-if="isAccepted">%i18n:@cancel-ready%</el-button>
|
<form-button primary @click="cancel" v-if="isAccepted">%i18n:@cancel-ready%</form-button>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
@ -202,11 +218,11 @@ export default Vue.extend({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onMapChange(v) {
|
onMapChange() {
|
||||||
if (v == null) {
|
if (this.mapName == null) {
|
||||||
this.game.settings.map = null;
|
this.game.settings.map = null;
|
||||||
} else {
|
} else {
|
||||||
this.game.settings.map = Object.values(maps).find(x => x.name == v).data;
|
this.game.settings.map = Object.values(maps).find(x => x.name == this.mapName).data;
|
||||||
}
|
}
|
||||||
this.$forceUpdate();
|
this.$forceUpdate();
|
||||||
this.updateSettings();
|
this.updateSettings();
|
||||||
|
@ -233,9 +249,9 @@ export default Vue.extend({
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
@import '~const.styl'
|
@import '~const.styl'
|
||||||
|
|
||||||
.root
|
root(isDark)
|
||||||
text-align center
|
text-align center
|
||||||
background #f9f9f9
|
background isDark ? #191b22 : #f9f9f9
|
||||||
|
|
||||||
> header
|
> header
|
||||||
padding 8px
|
padding 8px
|
||||||
|
@ -244,38 +260,45 @@ export default Vue.extend({
|
||||||
> div
|
> div
|
||||||
padding 0 16px
|
padding 0 16px
|
||||||
|
|
||||||
> .map
|
> .card
|
||||||
> .bw
|
|
||||||
> .rules
|
|
||||||
> .bot-form
|
|
||||||
max-width 400px
|
|
||||||
margin 0 auto 16px auto
|
margin 0 auto 16px auto
|
||||||
|
|
||||||
> footer
|
&.map
|
||||||
position sticky
|
> header
|
||||||
bottom 0
|
> select
|
||||||
padding 16px
|
|
||||||
background rgba(255, 255, 255, 0.9)
|
|
||||||
border-top solid 1px #c4cdd4
|
|
||||||
|
|
||||||
> .status
|
|
||||||
margin 0 0 16px 0
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang="stylus" module>
|
|
||||||
.mapSelect
|
|
||||||
width 100%
|
width 100%
|
||||||
|
padding 12px 14px
|
||||||
|
background isDark ? #282C37 : #fff
|
||||||
|
border 1px solid isDark ? #6a707d : #dcdfe6
|
||||||
|
border-radius 4px
|
||||||
|
color isDark ? #fff : #606266
|
||||||
|
cursor pointer
|
||||||
|
transition border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1)
|
||||||
|
|
||||||
.board
|
&:hover
|
||||||
|
border-color isDark ? #a7aebd : #c0c4cc
|
||||||
|
|
||||||
|
&:focus
|
||||||
|
&:active
|
||||||
|
border-color $theme-color
|
||||||
|
|
||||||
|
> div
|
||||||
|
> .random
|
||||||
|
padding 32px 0
|
||||||
|
font-size 64px
|
||||||
|
color isDark ? #4e5961 : #d8d8d8
|
||||||
|
|
||||||
|
> .board
|
||||||
display grid
|
display grid
|
||||||
grid-gap 4px
|
grid-gap 4px
|
||||||
width 300px
|
width 300px
|
||||||
height 300px
|
height 300px
|
||||||
margin 0 auto
|
margin 0 auto
|
||||||
|
color isDark ? #fff : #444
|
||||||
|
|
||||||
> div
|
> div
|
||||||
background transparent
|
background transparent
|
||||||
border solid 2px #ddd
|
border solid 2px isDark ? #6a767f : #ddd
|
||||||
border-radius 6px
|
border-radius 6px
|
||||||
overflow hidden
|
overflow hidden
|
||||||
cursor pointer
|
cursor pointer
|
||||||
|
@ -289,9 +312,35 @@ export default Vue.extend({
|
||||||
&[data-none]
|
&[data-none]
|
||||||
border-color transparent
|
border-color transparent
|
||||||
|
|
||||||
</style>
|
.card
|
||||||
|
max-width 400px
|
||||||
|
border-radius 4px
|
||||||
|
background isDark ? #282C37 : #fff
|
||||||
|
color isDark ? #fff : #303133
|
||||||
|
box-shadow 0 2px 12px 0 rgba(#000, 0.1)
|
||||||
|
|
||||||
|
> header
|
||||||
|
padding 18px 20px
|
||||||
|
border-bottom 1px solid isDark ? #1c2023 : #ebeef5
|
||||||
|
|
||||||
|
> div
|
||||||
|
padding 20px
|
||||||
|
color isDark ? #fff : #606266
|
||||||
|
|
||||||
|
> footer
|
||||||
|
position sticky
|
||||||
|
bottom 0
|
||||||
|
padding 16px
|
||||||
|
background rgba(isDark ? #191b22 : #fff, 0.9)
|
||||||
|
border-top solid 1px isDark ? #606266 : #c4cdd4
|
||||||
|
|
||||||
|
> .status
|
||||||
|
margin 0 0 16px 0
|
||||||
|
|
||||||
|
.urbixznjwwuukfsckrwzwsqzsxornqij[data-darkmode]
|
||||||
|
root(true)
|
||||||
|
|
||||||
|
.urbixznjwwuukfsckrwzwsqzsxornqij:not([data-darkmode])
|
||||||
|
root(false)
|
||||||
|
|
||||||
<style lang="stylus">
|
|
||||||
.el-alert__content
|
|
||||||
position initial !important
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,58 +1,16 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mk-reversi">
|
<div class="vchtoekanapleubgzioubdtmlkribzfd">
|
||||||
<div v-if="game">
|
<div v-if="game">
|
||||||
<x-gameroom :game="game"/>
|
<x-gameroom :game="game"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="matching" v-else-if="matching">
|
<div class="matching" v-else-if="matching">
|
||||||
<h1>{{ '%i18n:@matching.waiting-for%'.split('{}')[0] }}<b>{{ matching | userName }}</b>{{ '%i18n:@matching.waiting-for%'.split('{}')[1] }}<mk-ellipsis/></h1>
|
<h1>{{ '%i18n:@matching.waiting-for%'.split('{}')[0] }}<b>{{ matching | userName }}</b>{{ '%i18n:@matching.waiting-for%'.split('{}')[1] }}<mk-ellipsis/></h1>
|
||||||
<div class="cancel">
|
<div class="cancel">
|
||||||
<el-button round @click="cancel">%i18n:@matching.cancel%</el-button>
|
<form-button round @click="cancel">%i18n:@matching.cancel%</form-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="index" v-else>
|
<div class="index" v-else>
|
||||||
<h1>%i18n:@title%</h1>
|
<x-index @go="onGo" @matching="onMatching"/>
|
||||||
<p>%i18n:@sub-title%</p>
|
|
||||||
<div class="play">
|
|
||||||
<!--<el-button round>フリーマッチ(準備中)</el-button>-->
|
|
||||||
<el-button type="primary" round @click="match">%i18n:@invite%</el-button>
|
|
||||||
<details>
|
|
||||||
<summary>%i18n:@rule%</summary>
|
|
||||||
<div>
|
|
||||||
<p>%i18n:@rule-desc%</p>
|
|
||||||
<dl>
|
|
||||||
<dt><b>%i18n:@mode-invite%</b></dt>
|
|
||||||
<dd>%i18n:@mode-invite-desc%</dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</details>
|
|
||||||
</div>
|
|
||||||
<section v-if="invitations.length > 0">
|
|
||||||
<h2>%i18n:@invitations%</h2>
|
|
||||||
<div class="invitation" v-for="i in invitations" tabindex="-1" @click="accept(i)">
|
|
||||||
<mk-avatar class="avatar" :user="i.parent"/>
|
|
||||||
<span class="name"><b>{{ i.parent | userName }}</b></span>
|
|
||||||
<span class="username">@{{ i.parent.username }}</span>
|
|
||||||
<mk-time :time="i.createdAt"/>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section v-if="myGames.length > 0">
|
|
||||||
<h2>%i18n:@my-games%</h2>
|
|
||||||
<a class="game" v-for="g in myGames" tabindex="-1" @click.prevent="go(g)" :href="`/reversi/${g.id}`">
|
|
||||||
<mk-avatar class="avatar" :user="g.user1"/>
|
|
||||||
<mk-avatar class="avatar" :user="g.user2"/>
|
|
||||||
<span><b>{{ g.user1 | userName }}</b> vs <b>{{ g.user2 | userName }}</b></span>
|
|
||||||
<span class="state">{{ g.isEnded ? '%i18n:@game-state.ended%' : '%i18n:@game-state.playing%' }}</span>
|
|
||||||
</a>
|
|
||||||
</section>
|
|
||||||
<section v-if="games.length > 0">
|
|
||||||
<h2>%i18n:@all-games%</h2>
|
|
||||||
<a class="game" v-for="g in games" tabindex="-1" @click.prevent="go(g)" :href="`/reversi/${g.id}`">
|
|
||||||
<mk-avatar class="avatar" :user="g.user1"/>
|
|
||||||
<mk-avatar class="avatar" :user="g.user2"/>
|
|
||||||
<span><b>{{ g.user1 | userName }}</b> vs <b>{{ g.user2 | userName }}</b></span>
|
|
||||||
<span class="state">{{ g.isEnded ? '%i18n:@game-state.ended%' : '%i18n:@game-state.playing%' }}</span>
|
|
||||||
</a>
|
|
||||||
</section>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -60,10 +18,12 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import XGameroom from './reversi.gameroom.vue';
|
import XGameroom from './reversi.gameroom.vue';
|
||||||
|
import XIndex from './reversi.index.vue';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
components: {
|
components: {
|
||||||
XGameroom
|
XGameroom,
|
||||||
|
XIndex
|
||||||
},
|
},
|
||||||
|
|
||||||
props: ['initGame'],
|
props: ['initGame'],
|
||||||
|
@ -71,12 +31,7 @@ export default Vue.extend({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
game: null,
|
game: null,
|
||||||
games: [],
|
|
||||||
gamesFetching: true,
|
|
||||||
gamesMoreFetching: false,
|
|
||||||
myGames: [],
|
|
||||||
matching: null,
|
matching: null,
|
||||||
invitations: [],
|
|
||||||
connection: null,
|
connection: null,
|
||||||
connectionId: null,
|
connectionId: null,
|
||||||
pingClock: null
|
pingClock: null
|
||||||
|
@ -101,17 +56,6 @@ export default Vue.extend({
|
||||||
this.connectionId = (this as any).os.streams.reversiStream.use();
|
this.connectionId = (this as any).os.streams.reversiStream.use();
|
||||||
|
|
||||||
this.connection.on('matched', this.onMatched);
|
this.connection.on('matched', this.onMatched);
|
||||||
this.connection.on('invited', this.onInvited);
|
|
||||||
|
|
||||||
(this as any).api('games/reversi/games', {
|
|
||||||
my: true
|
|
||||||
}).then(games => {
|
|
||||||
this.myGames = games;
|
|
||||||
});
|
|
||||||
|
|
||||||
(this as any).api('games/reversi/invitations').then(invitations => {
|
|
||||||
this.invitations = this.invitations.concat(invitations);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.pingClock = setInterval(() => {
|
this.pingClock = setInterval(() => {
|
||||||
if (this.matching) {
|
if (this.matching) {
|
||||||
|
@ -122,17 +66,11 @@ export default Vue.extend({
|
||||||
}
|
}
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
(this as any).api('games/reversi/games').then(games => {
|
|
||||||
this.games = games;
|
|
||||||
this.gamesFetching = false;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
if (this.connection) {
|
if (this.connection) {
|
||||||
this.connection.off('matched', this.onMatched);
|
this.connection.off('matched', this.onMatched);
|
||||||
this.connection.off('invited', this.onInvited);
|
|
||||||
(this as any).os.streams.reversiStream.dispose(this.connectionId);
|
(this as any).os.streams.reversiStream.dispose(this.connectionId);
|
||||||
|
|
||||||
clearInterval(this.pingClock);
|
clearInterval(this.pingClock);
|
||||||
|
@ -140,33 +78,13 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
go(game) {
|
onGo(game) {
|
||||||
(this as any).api('games/reversi/games/show', {
|
|
||||||
gameId: game.id
|
|
||||||
}).then(game => {
|
|
||||||
this.matching = null;
|
this.matching = null;
|
||||||
this.game = game;
|
this.game = game;
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
match() {
|
onMatching(user) {
|
||||||
(this as any).apis.input({
|
|
||||||
title: '%i18n:@enter-username%'
|
|
||||||
}).then(username => {
|
|
||||||
(this as any).api('users/show', {
|
|
||||||
username
|
|
||||||
}).then(user => {
|
|
||||||
(this as any).api('games/reversi/match', {
|
|
||||||
userId: user.id
|
|
||||||
}).then(res => {
|
|
||||||
if (res == null) {
|
|
||||||
this.matching = user;
|
this.matching = user;
|
||||||
} else {
|
|
||||||
this.game = res;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
cancel() {
|
cancel() {
|
||||||
|
@ -188,10 +106,6 @@ export default Vue.extend({
|
||||||
onMatched(game) {
|
onMatched(game) {
|
||||||
this.matching = null;
|
this.matching = null;
|
||||||
this.game = game;
|
this.game = game;
|
||||||
},
|
|
||||||
|
|
||||||
onInvited(invite) {
|
|
||||||
this.invitations.unshift(invite);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -200,9 +114,9 @@ export default Vue.extend({
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
@import '~const.styl'
|
@import '~const.styl'
|
||||||
|
|
||||||
.mk-reversi
|
root(isDark)
|
||||||
color #677f84
|
color isDark ? #fff : #677f84
|
||||||
background #fff
|
background isDark ? #191b22 : #fff
|
||||||
|
|
||||||
> .matching
|
> .matching
|
||||||
> h1
|
> h1
|
||||||
|
@ -219,109 +133,10 @@ export default Vue.extend({
|
||||||
text-align center
|
text-align center
|
||||||
border-top dashed 1px #c4cdd4
|
border-top dashed 1px #c4cdd4
|
||||||
|
|
||||||
> .index
|
.vchtoekanapleubgzioubdtmlkribzfd[data-darkmode]
|
||||||
> h1
|
root(true)
|
||||||
margin 0
|
|
||||||
padding 24px
|
|
||||||
font-size 24px
|
|
||||||
text-align center
|
|
||||||
font-weight normal
|
|
||||||
color #fff
|
|
||||||
background linear-gradient(to bottom, #8bca3e, #d6cf31)
|
|
||||||
|
|
||||||
& + p
|
.vchtoekanapleubgzioubdtmlkribzfd:not([data-darkmode])
|
||||||
margin 0
|
root(false)
|
||||||
padding 12px
|
|
||||||
margin-bottom 12px
|
|
||||||
text-align center
|
|
||||||
font-size 14px
|
|
||||||
border-bottom solid 1px #d3d9dc
|
|
||||||
|
|
||||||
> .play
|
|
||||||
margin 0 auto
|
|
||||||
padding 0 16px
|
|
||||||
max-width 500px
|
|
||||||
text-align center
|
|
||||||
|
|
||||||
> details
|
|
||||||
margin 8px 0
|
|
||||||
|
|
||||||
> div
|
|
||||||
padding 16px
|
|
||||||
font-size 14px
|
|
||||||
text-align left
|
|
||||||
background #f5f5f5
|
|
||||||
border-radius 8px
|
|
||||||
|
|
||||||
> section
|
|
||||||
margin 0 auto
|
|
||||||
padding 0 16px 16px 16px
|
|
||||||
max-width 500px
|
|
||||||
border-top solid 1px #d3d9dc
|
|
||||||
|
|
||||||
> h2
|
|
||||||
margin 0
|
|
||||||
padding 16px 0 8px 0
|
|
||||||
font-size 16px
|
|
||||||
font-weight bold
|
|
||||||
|
|
||||||
.invitation
|
|
||||||
margin 8px 0
|
|
||||||
padding 8px
|
|
||||||
border solid 1px #e1e5e8
|
|
||||||
border-radius 6px
|
|
||||||
cursor pointer
|
|
||||||
|
|
||||||
*
|
|
||||||
pointer-events none
|
|
||||||
user-select none
|
|
||||||
|
|
||||||
&:focus
|
|
||||||
border-color $theme-color
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
background #f5f5f5
|
|
||||||
|
|
||||||
&:active
|
|
||||||
background #eee
|
|
||||||
|
|
||||||
> .avatar
|
|
||||||
width 32px
|
|
||||||
height 32px
|
|
||||||
border-radius 100%
|
|
||||||
|
|
||||||
> span
|
|
||||||
margin 0 8px
|
|
||||||
line-height 32px
|
|
||||||
|
|
||||||
.game
|
|
||||||
display block
|
|
||||||
margin 8px 0
|
|
||||||
padding 8px
|
|
||||||
color #677f84
|
|
||||||
border solid 1px #e1e5e8
|
|
||||||
border-radius 6px
|
|
||||||
cursor pointer
|
|
||||||
|
|
||||||
*
|
|
||||||
pointer-events none
|
|
||||||
user-select none
|
|
||||||
|
|
||||||
&:focus
|
|
||||||
border-color $theme-color
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
background #f5f5f5
|
|
||||||
|
|
||||||
&:active
|
|
||||||
background #eee
|
|
||||||
|
|
||||||
> .avatar
|
|
||||||
width 32px
|
|
||||||
height 32px
|
|
||||||
border-radius 100%
|
|
||||||
|
|
||||||
> span
|
|
||||||
margin 0 8px
|
|
||||||
line-height 32px
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -37,6 +37,8 @@ import uiTextarea from './ui/textarea.vue';
|
||||||
import uiSwitch from './ui/switch.vue';
|
import uiSwitch from './ui/switch.vue';
|
||||||
import uiRadio from './ui/radio.vue';
|
import uiRadio from './ui/radio.vue';
|
||||||
import uiSelect from './ui/select.vue';
|
import uiSelect from './ui/select.vue';
|
||||||
|
import formButton from './ui/form/button.vue';
|
||||||
|
import formRadio from './ui/form/radio.vue';
|
||||||
|
|
||||||
Vue.component('mk-analog-clock', analogClock);
|
Vue.component('mk-analog-clock', analogClock);
|
||||||
Vue.component('mk-menu', menu);
|
Vue.component('mk-menu', menu);
|
||||||
|
@ -75,3 +77,5 @@ Vue.component('ui-textarea', uiTextarea);
|
||||||
Vue.component('ui-switch', uiSwitch);
|
Vue.component('ui-switch', uiSwitch);
|
||||||
Vue.component('ui-radio', uiRadio);
|
Vue.component('ui-radio', uiRadio);
|
||||||
Vue.component('ui-select', uiSelect);
|
Vue.component('ui-select', uiSelect);
|
||||||
|
Vue.component('form-button', formButton);
|
||||||
|
Vue.component('form-radio', formRadio);
|
||||||
|
|
86
src/client/app/common/views/components/ui/form/button.vue
Normal file
86
src/client/app/common/views/components/ui/form/button.vue
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
<template>
|
||||||
|
<div class="nvemkhtwcnnpkdrwfcbzuwhfulejhmzg" :class="{ round, primary }">
|
||||||
|
<button @click="$emit('click')">
|
||||||
|
<slot></slot>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
export default Vue.extend({
|
||||||
|
props: {
|
||||||
|
round: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
primary: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
@import '~const.styl'
|
||||||
|
|
||||||
|
root(isDark)
|
||||||
|
display inline-block
|
||||||
|
|
||||||
|
& + .nvemkhtwcnnpkdrwfcbzuwhfulejhmzg
|
||||||
|
margin-left 12px
|
||||||
|
|
||||||
|
> button
|
||||||
|
display inline-block
|
||||||
|
margin 0
|
||||||
|
padding 12px 20px
|
||||||
|
font-size 14px
|
||||||
|
border 1px solid isDark ? #6d727d : #dcdfe6
|
||||||
|
border-radius 4px
|
||||||
|
outline none
|
||||||
|
box-shadow none
|
||||||
|
color isDark ? #fff : #606266
|
||||||
|
transition 0.1s
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
&:focus
|
||||||
|
color $theme-color
|
||||||
|
background rgba($theme-color, isDark ? 0.2 : 0.12)
|
||||||
|
border-color rgba($theme-color, isDark ? 0.5 : 0.3)
|
||||||
|
|
||||||
|
&:active
|
||||||
|
color darken($theme-color, 20%)
|
||||||
|
background rgba($theme-color, 0.12)
|
||||||
|
border-color $theme-color
|
||||||
|
transition all 0s
|
||||||
|
|
||||||
|
&.primary
|
||||||
|
> button
|
||||||
|
border 1px solid $theme-color
|
||||||
|
background $theme-color
|
||||||
|
color $theme-color-foreground
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
&:focus
|
||||||
|
background lighten($theme-color, 20%)
|
||||||
|
border-color lighten($theme-color, 20%)
|
||||||
|
|
||||||
|
&:active
|
||||||
|
background darken($theme-color, 20%)
|
||||||
|
border-color darken($theme-color, 20%)
|
||||||
|
transition all 0s
|
||||||
|
|
||||||
|
&.round
|
||||||
|
> button
|
||||||
|
border-radius 64px
|
||||||
|
|
||||||
|
.nvemkhtwcnnpkdrwfcbzuwhfulejhmzg[data-darkmode]
|
||||||
|
root(true)
|
||||||
|
|
||||||
|
.nvemkhtwcnnpkdrwfcbzuwhfulejhmzg:not([data-darkmode])
|
||||||
|
root(false)
|
||||||
|
|
||||||
|
</style>
|
126
src/client/app/common/views/components/ui/form/radio.vue
Normal file
126
src/client/app/common/views/components/ui/form/radio.vue
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="uywduthvrdnlpsvsjkqigicixgyfctto"
|
||||||
|
:class="{ disabled, checked }"
|
||||||
|
:aria-checked="checked"
|
||||||
|
:aria-disabled="disabled"
|
||||||
|
@click="toggle"
|
||||||
|
>
|
||||||
|
<input type="radio"
|
||||||
|
:disabled="disabled"
|
||||||
|
>
|
||||||
|
<span class="button">
|
||||||
|
<span></span>
|
||||||
|
</span>
|
||||||
|
<span class="label"><slot></slot></span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
export default Vue.extend({
|
||||||
|
model: {
|
||||||
|
prop: 'model',
|
||||||
|
event: 'change'
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
model: {
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
checked(): boolean {
|
||||||
|
return this.model === this.value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggle() {
|
||||||
|
this.$emit('change', this.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
@import '~const.styl'
|
||||||
|
|
||||||
|
root(isDark)
|
||||||
|
display inline-flex
|
||||||
|
margin 0 16px 0 0
|
||||||
|
cursor pointer
|
||||||
|
transition all 0.3s
|
||||||
|
|
||||||
|
> *
|
||||||
|
user-select none
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
> .button
|
||||||
|
border solid 2px isDark ? rgba(#fff, 0.7) : rgba(#000, 0.54)
|
||||||
|
|
||||||
|
&.disabled
|
||||||
|
opacity 0.6
|
||||||
|
cursor not-allowed
|
||||||
|
|
||||||
|
&.checked
|
||||||
|
> .button
|
||||||
|
border-color $theme-color
|
||||||
|
|
||||||
|
&:after
|
||||||
|
background-color $theme-color
|
||||||
|
transform scale(1)
|
||||||
|
opacity 1
|
||||||
|
|
||||||
|
> .label
|
||||||
|
color $theme-color
|
||||||
|
|
||||||
|
> input
|
||||||
|
position absolute
|
||||||
|
width 0
|
||||||
|
height 0
|
||||||
|
opacity 0
|
||||||
|
margin 0
|
||||||
|
|
||||||
|
> .button
|
||||||
|
display inline-block
|
||||||
|
flex-shrink 0
|
||||||
|
width 20px
|
||||||
|
height 20px
|
||||||
|
background none
|
||||||
|
border solid 2px isDark ? rgba(#fff, 0.6) : rgba(#000, 0.4)
|
||||||
|
border-radius 100%
|
||||||
|
transition inherit
|
||||||
|
|
||||||
|
&:after
|
||||||
|
content ''
|
||||||
|
display block
|
||||||
|
position absolute
|
||||||
|
top 3px
|
||||||
|
right 3px
|
||||||
|
bottom 3px
|
||||||
|
left 3px
|
||||||
|
border-radius 100%
|
||||||
|
opacity 0
|
||||||
|
transform scale(0)
|
||||||
|
transition 0.4s cubic-bezier(0.25, 0.8, 0.25, 1)
|
||||||
|
|
||||||
|
> .label
|
||||||
|
margin-left 8px
|
||||||
|
display block
|
||||||
|
font-size 14px
|
||||||
|
line-height 20px
|
||||||
|
cursor pointer
|
||||||
|
|
||||||
|
.uywduthvrdnlpsvsjkqigicixgyfctto[data-darkmode]
|
||||||
|
root(true)
|
||||||
|
|
||||||
|
.uywduthvrdnlpsvsjkqigicixgyfctto:not([data-darkmode])
|
||||||
|
root(false)
|
||||||
|
|
||||||
|
</style>
|
Loading…
Reference in a new issue