Sharkey/src/client/app/common/views/components/reaction-picker.vue

343 lines
8.6 KiB
Vue
Raw Normal View History

2018-02-07 08:16:01 +02:00
<template>
2018-12-29 18:32:58 +02:00
<div class="rdfaahpb" v-hotkey.global="keymap">
2018-02-07 08:16:01 +02:00
<div class="backdrop" ref="backdrop" @click="close"></div>
2018-12-29 18:32:58 +02:00
<div class="popover" :class="{ isMobile: $root.isMobile }" ref="popover">
<p v-if="!$root.isMobile">{{ title }}</p>
<div class="buttons" ref="buttons" :class="{ showFocus }">
2018-12-16 03:20:46 +02:00
<button @click="react('like')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="1" :title="$t('@.reactions.like')" v-particle><mk-reaction-icon reaction="like"/></button>
<button @click="react('love')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="2" :title="$t('@.reactions.love')" v-particle><mk-reaction-icon reaction="love"/></button>
<button @click="react('laugh')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="3" :title="$t('@.reactions.laugh')" v-particle><mk-reaction-icon reaction="laugh"/></button>
<button @click="react('hmm')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="4" :title="$t('@.reactions.hmm')" v-particle><mk-reaction-icon reaction="hmm"/></button>
<button @click="react('surprise')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="5" :title="$t('@.reactions.surprise')" v-particle><mk-reaction-icon reaction="surprise"/></button>
<button @click="react('congrats')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="6" :title="$t('@.reactions.congrats')" v-particle><mk-reaction-icon reaction="congrats"/></button>
<button @click="react('angry')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="7" :title="$t('@.reactions.angry')" v-particle><mk-reaction-icon reaction="angry"/></button>
<button @click="react('confused')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="8" :title="$t('@.reactions.confused')" v-particle><mk-reaction-icon reaction="confused"/></button>
<button @click="react('rip')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="9" :title="$t('@.reactions.rip')" v-particle><mk-reaction-icon reaction="rip"/></button>
<button @click="react('pudding')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="10" :title="$t('@.reactions.pudding')" v-particle><mk-reaction-icon reaction="pudding"/></button>
2018-02-07 08:16:01 +02:00
</div>
<div v-if="enableEmojiReaction" class="text">
<input v-model="text" placeholder="または絵文字を入力" @keyup.enter="reactText" @input="tryReactText" v-autocomplete="{ model: 'text' }">
</div>
2018-02-07 08:16:01 +02:00
</div>
</div>
</template>
2018-02-13 01:11:10 +02:00
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
2019-01-18 06:06:11 +02:00
import anime from 'animejs';
import { emojiRegex } from '../../../../../misc/emoji-regex';
2018-02-13 01:11:10 +02:00
export default Vue.extend({
i18n: i18n('common/views/components/reaction-picker.vue'),
2018-06-26 12:25:27 +03:00
props: {
note: {
type: Object,
required: true
},
2018-06-26 12:25:27 +03:00
source: {
required: true
},
2018-06-26 12:25:27 +03:00
cb: {
required: false
},
2018-09-18 03:11:52 +03:00
showFocus: {
type: Boolean,
required: false,
default: false
2018-09-18 06:42:56 +03:00
},
animation: {
type: Boolean,
required: false,
default: true
2018-06-26 12:25:27 +03:00
}
},
2018-02-13 01:11:10 +02:00
data() {
return {
title: this.$t('choose-reaction'),
text: null,
enableEmojiReaction: false,
2018-09-18 03:11:52 +03:00
focus: null
2018-02-13 01:11:10 +02:00
};
},
computed: {
keymap(): any {
return {
2018-09-18 02:19:45 +03:00
'esc': this.close,
2018-09-18 06:42:56 +03:00
'enter|space|plus': this.choose,
2018-09-18 08:43:54 +03:00
'up|k': this.focusUp,
'left|h|shift+tab': this.focusLeft,
'right|l|tab': this.focusRight,
'down|j': this.focusDown,
'1': () => this.react('like'),
'2': () => this.react('love'),
'3': () => this.react('laugh'),
'4': () => this.react('hmm'),
'5': () => this.react('surprise'),
'6': () => this.react('congrats'),
'7': () => this.react('angry'),
'8': () => this.react('confused'),
'9': () => this.react('rip'),
'0': () => this.react('pudding'),
};
}
},
2018-09-18 03:11:52 +03:00
watch: {
focus(i) {
2018-09-18 10:45:20 +03:00
this.$refs.buttons.children[i].focus();
2018-09-18 03:20:06 +03:00
if (this.showFocus) {
2018-09-18 10:45:20 +03:00
this.title = this.$refs.buttons.children[i].title;
2018-09-18 03:20:06 +03:00
}
2018-09-18 03:11:52 +03:00
}
},
2018-02-13 01:11:10 +02:00
mounted() {
this.$root.getMeta().then(meta => {
this.enableEmojiReaction = meta.enableEmojiReaction;
});
2018-02-13 05:50:42 +02:00
this.$nextTick(() => {
2018-09-18 03:11:52 +03:00
this.focus = 0;
2018-02-13 05:50:42 +02:00
const popover = this.$refs.popover as any;
2018-02-13 01:11:10 +02:00
2018-02-13 05:50:42 +02:00
const rect = this.source.getBoundingClientRect();
const width = popover.offsetWidth;
const height = popover.offsetHeight;
2018-02-13 01:11:10 +02:00
2018-12-29 18:32:58 +02:00
if (this.$root.isMobile) {
2018-02-13 05:50:42 +02:00
const x = rect.left + window.pageXOffset + (this.source.offsetWidth / 2);
const y = rect.top + window.pageYOffset + (this.source.offsetHeight / 2);
popover.style.left = (x - (width / 2)) + 'px';
popover.style.top = (y - (height / 2)) + 'px';
} else {
const x = rect.left + window.pageXOffset + (this.source.offsetWidth / 2);
const y = rect.top + window.pageYOffset + this.source.offsetHeight;
popover.style.left = (x - (width / 2)) + 'px';
popover.style.top = y + 'px';
}
2018-02-07 08:34:08 +02:00
2018-02-13 05:50:42 +02:00
anime({
targets: this.$refs.backdrop,
opacity: 1,
2018-09-18 06:42:56 +03:00
duration: this.animation ? 100 : 0,
2018-02-13 05:50:42 +02:00
easing: 'linear'
});
2018-02-13 01:11:10 +02:00
2018-02-13 05:50:42 +02:00
anime({
targets: this.$refs.popover,
opacity: 1,
scale: [0.5, 1],
2018-09-18 06:42:56 +03:00
duration: this.animation ? 500 : 0
2018-02-13 05:50:42 +02:00
});
2018-02-13 01:11:10 +02:00
});
},
2018-02-13 01:11:10 +02:00
methods: {
react(reaction) {
2018-11-09 01:13:34 +02:00
this.$root.api('notes/reactions/create', {
2018-04-07 20:30:37 +03:00
noteId: this.note.id,
2018-02-13 01:11:10 +02:00
reaction: reaction
}).then(() => {
if (this.cb) this.cb();
this.$emit('closed');
2018-09-15 15:53:04 +03:00
this.destroyDom();
2018-02-13 01:11:10 +02:00
});
2018-02-08 07:25:49 +02:00
},
reactText() {
if (!this.text) return;
this.react(this.text);
},
tryReactText() {
if (!this.text) return;
if (!this.text.match(emojiRegex)) return;
this.reactText();
},
2018-02-13 01:11:10 +02:00
onMouseover(e) {
this.title = e.target.title;
2018-02-07 08:34:08 +02:00
},
2018-02-13 01:11:10 +02:00
onMouseout(e) {
this.title = this.$t('choose-reaction');
2018-02-13 01:11:10 +02:00
},
2018-02-13 01:11:10 +02:00
close() {
(this.$refs.backdrop as any).style.pointerEvents = 'none';
2018-02-07 11:17:59 +02:00
anime({
targets: this.$refs.backdrop,
2018-02-13 01:11:10 +02:00
opacity: 0,
2018-09-18 06:42:56 +03:00
duration: this.animation ? 200 : 0,
2018-02-07 11:17:59 +02:00
easing: 'linear'
});
2018-02-13 01:11:10 +02:00
(this.$refs.popover as any).style.pointerEvents = 'none';
2018-02-07 11:17:59 +02:00
anime({
targets: this.$refs.popover,
2018-02-13 01:11:10 +02:00
opacity: 0,
scale: 0.5,
2018-09-18 06:42:56 +03:00
duration: this.animation ? 200 : 0,
2018-02-13 01:11:10 +02:00
easing: 'easeInBack',
complete: () => {
this.$emit('closed');
this.destroyDom();
}
2018-02-07 11:17:59 +02:00
});
2018-09-18 03:11:52 +03:00
},
focusUp() {
this.focus = this.focus == 0 ? 9 : this.focus < 5 ? (this.focus + 4) : (this.focus - 5);
},
focusDown() {
this.focus = this.focus == 9 ? 0 : this.focus >= 5 ? (this.focus - 4) : (this.focus + 5);
},
focusRight() {
this.focus = this.focus == 9 ? 0 : (this.focus + 1);
},
focusLeft() {
this.focus = this.focus == 0 ? 9 : (this.focus - 1);
},
choose() {
this.$refs.buttons.childNodes[this.focus].click();
2018-02-07 08:16:01 +02:00
}
2018-02-13 01:11:10 +02:00
}
});
2018-02-07 08:16:01 +02:00
</script>
2018-02-07 11:30:17 +02:00
<style lang="stylus" scoped>
2018-12-29 18:32:58 +02:00
.rdfaahpb
2018-02-13 01:11:10 +02:00
position initial
> .backdrop
position fixed
top 0
left 0
z-index 10000
width 100%
height 100%
2018-09-26 19:32:04 +03:00
background var(--modalBackdrop)
2018-02-13 01:11:10 +02:00
opacity 0
> .popover
2018-09-26 19:32:04 +03:00
$bgcolor = var(--popupBg)
2018-02-13 01:11:10 +02:00
position absolute
z-index 10001
2018-04-20 01:45:37 +03:00
background $bgcolor
2018-02-13 01:11:10 +02:00
border-radius 4px
box-shadow 0 3px 12px rgba(27, 31, 35, 0.15)
transform scale(0.5)
opacity 0
2018-12-29 18:32:58 +02:00
&.isMobile
2018-06-26 12:25:27 +03:00
> div
width 280px
> button
width 50px
height 50px
font-size 28px
border-radius 4px
2018-12-29 18:32:58 +02:00
&:not(.isMobile)
$arrow-size = 16px
margin-top $arrow-size
transform-origin center -($arrow-size)
&:before
content ""
display block
position absolute
top -($arrow-size * 2)
left s('calc(50% - %s)', $arrow-size)
border-top solid $arrow-size transparent
border-left solid $arrow-size transparent
border-right solid $arrow-size transparent
border-bottom solid $arrow-size $bgcolor
2018-02-13 01:11:10 +02:00
> p
display block
margin 0
padding 8px 10px
font-size 14px
2018-09-26 19:32:04 +03:00
color var(--popupFg)
2018-12-30 07:00:57 +02:00
border-bottom solid var(--lineWidth) var(--faceDivider)
2018-02-13 01:11:10 +02:00
> .buttons
2019-03-18 10:37:29 +02:00
padding 4px 4px 8px 4px
width 216px
2018-02-13 01:11:10 +02:00
text-align center
2018-09-18 03:11:52 +03:00
&.showFocus
> button:focus
z-index 1
&:after
content ""
pointer-events none
position absolute
top 0
right 0
bottom 0
left 0
2018-09-26 14:19:35 +03:00
border 2px solid var(--primaryAlpha03)
2018-09-18 03:11:52 +03:00
border-radius 4px
2018-02-13 01:11:10 +02:00
> button
2018-03-03 09:59:00 +02:00
padding 0
2018-02-13 01:11:10 +02:00
width 40px
height 40px
font-size 24px
border-radius 2px
> *
height 1em
2018-02-13 01:11:10 +02:00
&:hover
2018-09-26 19:32:04 +03:00
background var(--reactionPickerButtonHoverBg)
2018-02-13 01:11:10 +02:00
&:active
2018-09-26 14:19:35 +03:00
background var(--primary)
2018-02-13 01:11:10 +02:00
box-shadow inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15)
2018-02-07 11:30:17 +02:00
> .text
width 216px
2019-03-18 10:37:29 +02:00
padding 0 8px 8px 8px
> input
width 100%
padding 10px
margin 0
text-align center
font-size 16px
color var(--desktopPostFormTextareaFg)
background var(--desktopPostFormTextareaBg)
outline none
border solid 1px var(--primaryAlpha01)
border-radius 4px
transition border-color .2s ease
&:hover
border-color var(--primaryAlpha02)
transition border-color .1s ease
&:focus
border-color var(--primaryAlpha05)
transition border-color 0s ease
2018-02-07 11:30:17 +02:00
</style>