mirror of
https://github.com/m1k1o/neko.git
synced 2026-11-03 08:21:50 -06:00
emoji component (wip)
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 1.0 MiB |
@@ -12,6 +12,7 @@
|
|||||||
@import "vendor/swal";
|
@import "vendor/swal";
|
||||||
@import "vendor/tooltip";
|
@import "vendor/tooltip";
|
||||||
@import "vendor/github";
|
@import "vendor/github";
|
||||||
|
@import "vendor/emoji_20";
|
||||||
|
|
||||||
html, body {
|
html, body {
|
||||||
-webkit-font-smoothing: subpixel-antialiased;
|
-webkit-font-smoothing: subpixel-antialiased;
|
||||||
@@ -23,4 +24,4 @@ html, body {
|
|||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
min-width: 320px;
|
min-width: 320px;
|
||||||
}
|
}
|
||||||
|
|||||||
+5185
File diff suppressed because it is too large
Load Diff
@@ -36,7 +36,7 @@
|
|||||||
<div class="accent" />
|
<div class="accent" />
|
||||||
<div class="text-container">
|
<div class="text-container">
|
||||||
<textarea ref="chat" placeholder="Send a message" @keydown="onKeyDown" v-model="content" />
|
<textarea ref="chat" placeholder="Send a message" @keydown="onKeyDown" v-model="content" />
|
||||||
<div class="emoji"></div>
|
<div class="emoji"><neko-emoji /></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -272,6 +272,7 @@
|
|||||||
height: 20px;
|
height: 20px;
|
||||||
// background: #fff;
|
// background: #fff;
|
||||||
margin: 3px 3px 0 0;
|
margin: 3px 3px 0 0;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
@@ -322,6 +323,7 @@
|
|||||||
|
|
||||||
import Markdown from './markdown'
|
import Markdown from './markdown'
|
||||||
import Content from './context.vue'
|
import Content from './context.vue'
|
||||||
|
import Emoji from './emoji.vue'
|
||||||
|
|
||||||
const length = 512 // max length of message
|
const length = 512 // max length of message
|
||||||
|
|
||||||
@@ -330,6 +332,7 @@
|
|||||||
components: {
|
components: {
|
||||||
'neko-markdown': Markdown,
|
'neko-markdown': Markdown,
|
||||||
'neko-context': Content,
|
'neko-context': Content,
|
||||||
|
'neko-emoji': Emoji,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
export default class extends Vue {
|
export default class extends Vue {
|
||||||
|
|||||||
@@ -0,0 +1,271 @@
|
|||||||
|
<template>
|
||||||
|
<div class="neko-emoji">
|
||||||
|
<div class="search">
|
||||||
|
<input type="text" ref="search" v-model="search" />
|
||||||
|
</div>
|
||||||
|
<div class="list" ref="scroll" @scroll="onScroll">
|
||||||
|
<ul :class="[search === '' ? 'group-list' : 'emoji-list']">
|
||||||
|
<template v-if="search === ''">
|
||||||
|
<li v-for="(group, index) in groups" :key="index" class="group" ref="groups">
|
||||||
|
<span class="label">{{ group.name }}</span>
|
||||||
|
<ul class="emoji-list">
|
||||||
|
<li
|
||||||
|
v-for="emoji in group.list"
|
||||||
|
:key="`${group.id}-${emoji}`"
|
||||||
|
:class="['emoji', hovered === emoji ? 'active' : '']"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
:class="['emoji-20', `e-${emoji}`]"
|
||||||
|
@mouseenter.stop.prevent="onMouseEnter($event, emoji)"
|
||||||
|
@click.stop.prevent="onClick($event, emoji)"
|
||||||
|
></span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<li v-for="emoji in filtered" :key="emoji" :class="['emoji', hovered === emoji ? 'active' : '']">
|
||||||
|
<span
|
||||||
|
:class="['emoji-20', `e-${emoji}`]"
|
||||||
|
@mouseenter.stop.prevent="onMouseEnter($event, emoji)"
|
||||||
|
@click.stop.prevent="onClick($event, emoji)"
|
||||||
|
></span>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="details">
|
||||||
|
<template v-if="hovered !== ''">
|
||||||
|
<span :class="['icon', 'emoji-20', `e-${hovered}`]"></span><span class="emoji">:{{ hovered }}:</span>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<div class="groups">
|
||||||
|
<ul>
|
||||||
|
<li
|
||||||
|
v-for="(group, index) in groups"
|
||||||
|
:key="index"
|
||||||
|
:class="[group.id, active === group.id && search === '' ? 'active' : '']"
|
||||||
|
@click.stop.prevent="scrollTo($event, index)"
|
||||||
|
>
|
||||||
|
<i class="fas fa-angry"></i>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
$emoji-width: 350px;
|
||||||
|
|
||||||
|
.neko-emoji {
|
||||||
|
position: absolute;
|
||||||
|
width: $emoji-width;
|
||||||
|
height: 200px;
|
||||||
|
background: $background-secondary;
|
||||||
|
bottom: 30px;
|
||||||
|
right: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border-radius: 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.search {
|
||||||
|
flex-shrink: 0;
|
||||||
|
height: 38px;
|
||||||
|
border-bottom: 1px solid $background-tertiary;
|
||||||
|
|
||||||
|
input {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.list {
|
||||||
|
position: relative;
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow-y: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: $background-tertiary transparent;
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
padding: 5px;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background-color: $background-tertiary;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: $background-floating;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-list {
|
||||||
|
width: $emoji-width;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
li {
|
||||||
|
&.group {
|
||||||
|
.label {
|
||||||
|
z-index: 2;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 12px;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-list {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
li {
|
||||||
|
&.emoji {
|
||||||
|
padding: 2px;
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.details {
|
||||||
|
flex-shrink: 0;
|
||||||
|
height: 36px;
|
||||||
|
background: $background-tertiary;
|
||||||
|
|
||||||
|
span {
|
||||||
|
&.icon {
|
||||||
|
}
|
||||||
|
|
||||||
|
&.emoji {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.groups {
|
||||||
|
flex-shrink: 0;
|
||||||
|
height: 28px;
|
||||||
|
background: $background-floating;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
li {
|
||||||
|
flex-grow: 1;
|
||||||
|
height: 23px;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-bottom: 2px solid $style-primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Ref, Watch, Vue } from 'vue-property-decorator'
|
||||||
|
|
||||||
|
import { list } from './emoji/list'
|
||||||
|
import { keywords } from './emoji/keywords'
|
||||||
|
import { groups } from './emoji/groups'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
name: 'neko-emoji',
|
||||||
|
})
|
||||||
|
export default class extends Vue {
|
||||||
|
@Ref('scroll') readonly _scroll!: HTMLElement
|
||||||
|
@Ref('search') readonly _search!: HTMLInputElement
|
||||||
|
@Ref('groups') readonly _groups!: HTMLElement[]
|
||||||
|
|
||||||
|
waitingForPaint = false
|
||||||
|
search = ''
|
||||||
|
active = groups[0].id
|
||||||
|
hovered = ''
|
||||||
|
|
||||||
|
get groups() {
|
||||||
|
return groups
|
||||||
|
}
|
||||||
|
|
||||||
|
get filtered() {
|
||||||
|
const filtered = []
|
||||||
|
for (const emoji of list) {
|
||||||
|
if (
|
||||||
|
emoji.includes(this.search) || typeof keywords[emoji] !== 'undefined'
|
||||||
|
? keywords[emoji].some(keyword => keyword.includes(this.search))
|
||||||
|
: false
|
||||||
|
) {
|
||||||
|
filtered.push(emoji)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filtered
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollTo(event: MouseEvent, index: number) {
|
||||||
|
const ele = this._groups[index]
|
||||||
|
if (!ele) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let top = ele.offsetTop
|
||||||
|
if (index == 0) {
|
||||||
|
top = 0
|
||||||
|
}
|
||||||
|
this._scroll.scrollTop = top
|
||||||
|
}
|
||||||
|
|
||||||
|
onScroll() {
|
||||||
|
if (!this.waitingForPaint) {
|
||||||
|
this.waitingForPaint = true
|
||||||
|
window.requestAnimationFrame(this.onScrollPaint.bind(this))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onScrollPaint() {
|
||||||
|
this.waitingForPaint = false
|
||||||
|
let scrollTop = this._scroll.scrollTop
|
||||||
|
let active = this.groups[0]
|
||||||
|
for (let i = 0, l = this.groups.length; i < l; i++) {
|
||||||
|
let group = this.groups[i]
|
||||||
|
let component = this._groups[i]
|
||||||
|
if (component && component.offsetTop > scrollTop) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
active = group
|
||||||
|
}
|
||||||
|
this.active = active.id
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseExit(event: MouseEvent, emoji: string) {
|
||||||
|
this.hovered = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseEnter(event: MouseEvent, emoji: string) {
|
||||||
|
this.hovered = emoji
|
||||||
|
this._search.placeholder = `:${emoji}:`
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick(event: MouseEvent, emoji: string) {
|
||||||
|
this.$emit('done', emoji)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user